-
-
Notifications
You must be signed in to change notification settings - Fork 107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Rework network_route provider with resource_api #245
Open
dhollinger
wants to merge
15
commits into
voxpupuli:master
Choose a base branch
from
dhollinger:resource_refactor
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 4 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
ec9599f
WIP: Rework network_route provider
d18dd76
Remove simple_provider
d16f67c
Fully functioning provider for network_route
dhollinger 5ab58cf
Convert string hash keys to symbolized keys
dhollinger a79572b
Add network::route defined type
dhollinger 89e8b1a
Remove legacy network route resources
dhollinger 0cbeea1
Initial test working but needs mocks
dhollinger 9a08c21
Add new type tests
dhollinger 68845cc
Add network_route provider #get test
dhollinger 039f825
Add puppet_munge test to provider tests
dhollinger d610ddc
WIP - add create tests
dhollinger f85a029
Finish unit tests for network_route provider
dhollinger 3d75ee7
Remove network facts as they cover much of the same ground as facter 3
dhollinger 94ce7eb
Remove old unused tests
dhollinger d8a407c
Puppet Strings update v1
dhollinger File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
require 'net/ip' | ||
# require_relative '../../../puppet_x/voxpupuli/utils' | ||
require 'puppet/resource_api/simple_provider' | ||
|
||
# Implementation for the network_route type using the Resource API. | ||
class Puppet::Provider::NetworkRoute::NetworkRoute | ||
# include PuppetX::FileMapper | ||
|
||
def routes_list | ||
routes = [] | ||
Net::IP.routes.each do |route| | ||
routes.push(route.to_h) | ||
end | ||
routes | ||
end | ||
|
||
def get(_context) | ||
routes_list.map do |route| | ||
default = if route[:prefix] == 'default' | ||
true | ||
else | ||
false | ||
end | ||
|
||
{ | ||
ensure: 'present', | ||
prefix: route[:prefix], | ||
default_route: default, | ||
gateway: route[:via], | ||
interface: route[:dev], | ||
metric: route[:metric], | ||
table: route[:table], | ||
source: route[:src], | ||
scope: route[:scope], | ||
protocol: route[:proto], | ||
mtu: route[:mtu] | ||
}.compact! | ||
end | ||
end | ||
|
||
def puppet_munge(should) | ||
should.delete(:ensure) | ||
if should[:default_route] | ||
should[:prefix] = 'default' | ||
should.delete(:default_route) | ||
else | ||
should[:prefix] = should.delete(:prefix) | ||
end | ||
should[:via] = should.delete(:gateway) if should[:gateway] | ||
should[:dev] = should.delete(:interface) if should[:interface] | ||
should[:metric] = should.delete(:metric) | ||
should[:table] = should.delete(:table) if should[:table] | ||
should[:src] = should.delete(:source) if should[:source] | ||
should[:scope] = should.delete(:scope) if should[:scope] | ||
should[:proto] = should.delete(:protocol) | ||
should[:mtu] = should.delete(:mtu) if should[:mtu] | ||
should | ||
end | ||
|
||
def set(context, changes) | ||
changes.each do |name, change| | ||
is = change.key?(:is) ? change[:is] : get_single(name) | ||
should = change[:should] | ||
|
||
is = { prefix: name, ensure: 'absent' } if is.nil? | ||
should = { prefix: name, ensure: 'absent' } if should.nil? | ||
|
||
if is[:ensure].to_s == 'absent' && should[:ensure].to_s == 'present' | ||
create(context, name, should) | ||
elsif is[:ensure] == should[:ensure] && is != should | ||
update(context, name, should) | ||
elsif is[:ensure].to_s == 'present' && should[:ensure].to_s == 'absent' | ||
delete(context, name, should) | ||
end | ||
end | ||
end | ||
|
||
def create(context, name, should) | ||
puppet_munge(should) | ||
route = Net::IP::Route.new(should) | ||
Net::IP.routes.add(route) | ||
end | ||
|
||
def update(context, name, should) | ||
puppet_munge(should) | ||
route = Net::IP::Route.new(should) | ||
Net::IP.routes.flush(route.prefix) | ||
Net::IP.routes.add(route) | ||
end | ||
|
||
def delete(context, name, should) | ||
puppet_munge(should) | ||
route = Net::IP::Route.new(should) | ||
Net::IP.routes.flush(route.prefix) | ||
end | ||
end |
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,81 +1,58 @@ | ||
require 'ipaddr' | ||
require_relative '../../puppet_x/voxpupuli/utils.rb' | ||
|
||
Puppet::Type.newtype(:network_route) do | ||
@doc = 'Manage non-volatile route configuration information' | ||
|
||
include PuppetX::Voxpupuli::Utils | ||
|
||
ensurable | ||
|
||
newparam(:name) do | ||
isnamevar | ||
desc 'The name of the network route' | ||
end | ||
|
||
newproperty(:network) do | ||
isrequired | ||
desc 'The target network address' | ||
validate do |value| | ||
unless value == 'default' | ||
a = PuppetX::Voxpupuli::Utils.try { IPAddr.new(value) } | ||
raise("Invalid value for network: #{value}") unless a | ||
end | ||
end | ||
end | ||
|
||
newproperty(:netmask) do | ||
isrequired | ||
desc 'The subnet mask to apply to the route' | ||
|
||
validate do |value| | ||
unless value.length <= 3 || PuppetX::Voxpupuli::Utils.try { IPAddr.new(value) } | ||
raise("Invalid value for argument netmask: #{value}") | ||
end | ||
end | ||
|
||
munge do |value| | ||
# '255.255.255.255'.to_i will return 255, so we try to convert it back: | ||
if value.to_i.to_s == value | ||
# what are the chances someone is using /16 for their IPv6 network? | ||
addr = value.to_i <= 32 ? '255.255.255.255' : 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' | ||
IPAddr.new(addr).mask(value.strip.to_i).to_s | ||
elsif PuppetX::Voxpupuli::Utils.try { IPAddr.new(value).ipv6? } | ||
IPAddr.new('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff').mask(value).to_s | ||
elsif PuppetX::Voxpupuli::Utils.try { IPAddr.new(value).ipv4? } | ||
IPAddr.new('255.255.255.255').mask(value).to_s | ||
else | ||
raise("Invalid value for argument netmask: #{value}") | ||
end | ||
end | ||
end | ||
|
||
newproperty(:gateway) do | ||
isrequired | ||
desc 'The gateway to use for the route' | ||
|
||
validate do |value| | ||
begin | ||
IPAddr.new(value) | ||
rescue ArgumentError | ||
raise("Invalid value for gateway: #{value}") | ||
end | ||
end | ||
end | ||
|
||
newproperty(:interface) do | ||
isrequired | ||
desc 'The interface to use for the route' | ||
end | ||
|
||
# `:options` provides an arbitrary passthrough for provider properties, so | ||
# that provider specific behavior doesn't clutter up the main type but still | ||
# allows for more powerful actions to be taken. | ||
newproperty(:options, required_features: :provider_options) do | ||
desc 'Provider specific options to be passed to the provider' | ||
|
||
validate do |value| | ||
raise ArgumentError, "#{self.class} requires a string for the options property" unless value.is_a?(String) | ||
end | ||
end | ||
end | ||
require 'puppet/resource_api' | ||
|
||
Puppet::ResourceApi.register_type( | ||
name: 'network_route', | ||
docs: <<-EOS, | ||
Manage non-volatile route configuration information. | ||
EOS | ||
attributes: { | ||
ensure: { | ||
type: 'Enum[present, absent]', | ||
desc: 'Whether the network route should be present or absent on the target system.', | ||
default: 'present', | ||
}, | ||
prefix: { | ||
type: 'String', | ||
desc: 'The destination prefix/network of the route.', | ||
behaviour: :namevar, | ||
}, | ||
default_route: { | ||
type: 'Optional[Boolean]', | ||
desc: 'Whether the route is default or not.', | ||
}, | ||
gateway: { | ||
type: 'Optional[String]', | ||
desc: 'The gateway to use for the route.', | ||
}, | ||
interface: { | ||
type: 'Optional[String]', | ||
desc: 'The interface to use for the route.', | ||
}, | ||
metric: { | ||
type: 'String', | ||
desc: 'preference value of the route. NUMBER is an arbitrary 32bit number.', | ||
default: '100', | ||
}, | ||
table: { | ||
type: 'Optional[String]', | ||
desc: 'table to add this route.', | ||
}, | ||
source: { | ||
type: 'Optional[String]', | ||
desc: 'the source address to prefer using when sending to the destinations covered by route prefix.', | ||
}, | ||
scope: { | ||
type: 'Optional[Enum["global", "nowhere", "host", "link", "site"]]', | ||
desc: 'scope of the destinations covered by the route prefix.', | ||
}, | ||
protocol: { | ||
type: 'String', | ||
desc: 'routing protocol identifier of this route.', | ||
default: 'static', | ||
}, | ||
mtu: { | ||
type: 'Optional[String]', | ||
desc: 'the MTU along the path to destination.', | ||
}, | ||
}, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
require 'ipaddr' | ||
require_relative '../../puppet_x/voxpupuli/utils.rb' | ||
|
||
Puppet::Type.newtype(:network_route) do | ||
@doc = 'Manage non-volatile route configuration information' | ||
|
||
include PuppetX::Voxpupuli::Utils | ||
|
||
ensurable | ||
|
||
newparam(:name) do | ||
isnamevar | ||
desc 'The name of the network route' | ||
end | ||
|
||
newproperty(:network) do | ||
isrequired | ||
desc 'The target network address' | ||
validate do |value| | ||
unless value == 'default' | ||
a = PuppetX::Voxpupuli::Utils.try { IPAddr.new(value) } | ||
raise("Invalid value for network: #{value}") unless a | ||
end | ||
end | ||
end | ||
|
||
newproperty(:netmask) do | ||
isrequired | ||
desc 'The subnet mask to apply to the route' | ||
|
||
validate do |value| | ||
unless value.length <= 3 || PuppetX::Voxpupuli::Utils.try { IPAddr.new(value) } | ||
raise("Invalid value for argument netmask: #{value}") | ||
end | ||
end | ||
|
||
munge do |value| | ||
# '255.255.255.255'.to_i will return 255, so we try to convert it back: | ||
if value.to_i.to_s == value | ||
# what are the chances someone is using /16 for their IPv6 network? | ||
addr = value.to_i <= 32 ? '255.255.255.255' : 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' | ||
IPAddr.new(addr).mask(value.strip.to_i).to_s | ||
elsif PuppetX::Voxpupuli::Utils.try { IPAddr.new(value).ipv6? } | ||
IPAddr.new('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff').mask(value).to_s | ||
elsif PuppetX::Voxpupuli::Utils.try { IPAddr.new(value).ipv4? } | ||
IPAddr.new('255.255.255.255').mask(value).to_s | ||
else | ||
raise("Invalid value for argument netmask: #{value}") | ||
end | ||
end | ||
end | ||
|
||
newproperty(:gateway) do | ||
isrequired | ||
desc 'The gateway to use for the route' | ||
|
||
validate do |value| | ||
begin | ||
IPAddr.new(value) | ||
rescue ArgumentError | ||
raise("Invalid value for gateway: #{value}") | ||
end | ||
end | ||
end | ||
|
||
newproperty(:interface) do | ||
isrequired | ||
desc 'The interface to use for the route' | ||
end | ||
|
||
# `:options` provides an arbitrary passthrough for provider properties, so | ||
# that provider specific behavior doesn't clutter up the main type but still | ||
# allows for more powerful actions to be taken. | ||
newproperty(:options, required_features: :provider_options) do | ||
desc 'Provider specific options to be passed to the provider' | ||
|
||
validate do |value| | ||
raise ArgumentError, "#{self.class} requires a string for the options property" unless value.is_a?(String) | ||
end | ||
end | ||
end |
49 changes: 49 additions & 0 deletions
49
spec/unit/puppet/provider/network_route/network_route_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
require 'spec_helper' | ||
|
||
ensure_module_defined('Puppet::Provider::NetworkRoute') | ||
require 'puppet/provider/network_route/network_route' | ||
|
||
RSpec.describe Puppet::Provider::NetworkRoute::NetworkRoute do | ||
subject(:provider) { described_class.new } | ||
|
||
let(:context) { instance_double('Puppet::ResourceApi::BaseContext', 'context') } | ||
|
||
describe '#get' do | ||
it 'processes resources' do | ||
expect(provider.get(context)).to eq [ | ||
{ | ||
name: 'foo', | ||
ensure: 'present', | ||
}, | ||
{ | ||
name: 'bar', | ||
ensure: 'present', | ||
}, | ||
] | ||
end | ||
end | ||
|
||
describe 'create(context, name, should)' do | ||
it 'creates the resource' do | ||
expect(context).to receive(:notice).with(%r{\ACreating 'a'}) | ||
|
||
provider.create(context, 'a', name: 'a', ensure: 'present') | ||
end | ||
end | ||
|
||
describe 'update(context, name, should)' do | ||
it 'updates the resource' do | ||
expect(context).to receive(:notice).with(%r{\AUpdating 'foo'}) | ||
|
||
provider.update(context, 'foo', name: 'foo', ensure: 'present') | ||
end | ||
end | ||
|
||
describe 'delete(context, name, should)' do | ||
it 'deletes the resource' do | ||
expect(context).to receive(:notice).with(%r{\ADeleting 'foo'}) | ||
|
||
provider.delete(context, 'foo') | ||
end | ||
end | ||
end |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That file is going away ;)
That said, I am looking for a way to transform a prefix length to subnet mask. Nothing seems to do it natively (plenty of libraries to transform from subnet mask to prefix length though).