Skip to content

Commit

Permalink
WIP: Rework network_route provider
Browse files Browse the repository at this point in the history
Signed-off-by: David Hollinger <[email protected]>
  • Loading branch information
David Hollinger committed May 29, 2018
1 parent 797f1bd commit ec9599f
Show file tree
Hide file tree
Showing 9 changed files with 262 additions and 81 deletions.
2 changes: 2 additions & 0 deletions .sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ spec/spec_helper.rb:
spec_overrides: "require 'spec_helper_methods'"
Gemfile:
optional:
':development':
- gem: 'puppet-resource_api'
':test':
- gem: 'ipaddress'
- gem: 'rspec-its'
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ group :test do
gem 'rack', '~> 1.0', :require => false if RUBY_VERSION < '2.2.2'
gem 'parallel_tests', :require => false
gem 'ipaddress', :require => false
gem 'net-ip', :require => false
gem 'rspec-its', :require => false
end

Expand All @@ -39,6 +40,7 @@ group :development do
gem 'travis-lint', :require => false
gem 'guard-rake', :require => false
gem 'overcommit', '>= 0.39.1', :require => false
gem 'puppet-resource_api', :require => false
end

group :system_tests do
Expand Down
60 changes: 60 additions & 0 deletions lib/puppet/provider/network_route/network_route.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
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 < Puppet::ResourceApi::SimpleProvider
# include PuppetX::FileMapper

def routes_list
routes = []
Net::IP.routes.each do |route|
routes.push(route.instance_variables.each_with_object({}) { |var, hash| hash[var.to_s.delete("@")] = route.instance_variable_get(var) })
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)
should.delete(:prefix)
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)
should[:src] = should.delete(:source) if should[:source]
should[:scope] = should.delete(:scope)
should[:proto] = should.delete(:protocol)
should[:mtu] = should.delete(:mtu) if should[:mtu]
should
end
end
File renamed without changes.
File renamed without changes.
141 changes: 60 additions & 81 deletions lib/puppet/type/network_route.rb
Original file line number Diff line number Diff line change
@@ -1,81 +1,60 @@
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: 'String',
desc: 'table to add this route.',
default: 'local',
},
source: {
type: 'Optional[String]',
desc: 'the source address to prefer using when sending to the destinations covered by route prefix.',
},
scope: {
type: 'Enum["global", "nowhere", "host", "link", "site"]',
desc: 'scope of the destinations covered by the route prefix.',
default: 'global',
},
protocol: {
type: 'String',
desc: 'routing protocol identifier of this route.',
default: 'boot',
},
mtu: {
type: 'Optional[String]',
desc: 'the MTU along the path to destination.',
},
},
)
81 changes: 81 additions & 0 deletions lib/puppet/type/network_route_old.rb
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 spec/unit/puppet/provider/network_route/network_route_spec.rb
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
8 changes: 8 additions & 0 deletions spec/unit/puppet/type/network_route_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
require 'spec_helper'
require 'puppet/type/network_route'

RSpec.describe 'the network_route type' do
it 'loads' do
expect(Puppet::Type.type(:network_route)).not_to be_nil
end
end

0 comments on commit ec9599f

Please sign in to comment.