From 745b2537e79eca59f8e9f2f15230482fcdf140aa Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Thu, 11 Apr 2013 11:54:50 +0200 Subject: Refactor module * Use a top dhcp class with a $server boolean param * Refactor the dhcp::class to use packages/config/service classes * Don't use inheritance, put everything in dhcp::params --- manifests/init.pp | 7 +++++ manifests/params.pp | 7 +++++ manifests/server.pp | 6 ++-- manifests/server/base.pp | 65 -------------------------------------------- manifests/server/config.pp | 43 +++++++++++++++++++++++++++++ manifests/server/debian.pp | 21 -------------- manifests/server/packages.pp | 8 ++++++ manifests/server/service.pp | 10 +++++++ 8 files changed, 78 insertions(+), 89 deletions(-) create mode 100644 manifests/init.pp delete mode 100644 manifests/server/base.pp create mode 100644 manifests/server/config.pp delete mode 100644 manifests/server/debian.pp create mode 100644 manifests/server/packages.pp create mode 100644 manifests/server/service.pp diff --git a/manifests/init.pp b/manifests/init.pp new file mode 100644 index 0000000..8a14b13 --- /dev/null +++ b/manifests/init.pp @@ -0,0 +1,7 @@ +class dhcp ( + $server = true, +) { + if $server { + class { '::dhcp::server': } + } +} diff --git a/manifests/params.pp b/manifests/params.pp index d2ba584..20fbcd2 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -17,6 +17,13 @@ class dhcp::params { lenny => 'dhcp3-server', squeeze => 'isc-dhcp-server', } + + $service_pattern = $::lsbdistcodename? { + lenny => '/usr/sbin/dhcpd3', + squeeze => '/usr/sbin/dhcpd', + } + + $base_template = 'dhcp/dhcpd.conf.debian.erb' } default: { diff --git a/manifests/server.pp b/manifests/server.pp index 6bdf51f..779b305 100644 --- a/manifests/server.pp +++ b/manifests/server.pp @@ -33,7 +33,7 @@ node "dhcp.toto.ltd" { */ class dhcp::server { - case $::operatingsystem { - Debian: { include dhcp::server::debian } - } + class { '::dhcp::server::packages': } -> + class { '::dhcp::server::config': } ~> + class { '::dhcp::server::service': } } diff --git a/manifests/server/base.pp b/manifests/server/base.pp deleted file mode 100644 index 4e7d645..0000000 --- a/manifests/server/base.pp +++ /dev/null @@ -1,65 +0,0 @@ -# = Class dhcp::server::base -# -# Do NOT include this class - it won't work at all. -# Set variables for package name and so on. -# This class should be inherited in dhcp::server::$operatingsystem. -# -class dhcp::server::base { - - include dhcp::params - include concat::setup - - package {'dhcp-server': - ensure => present, - name => $dhcp::params::srv_dhcpd, - } - - service {'dhcpd': - ensure => running, - name => $dhcp::params::srv_dhcpd, - enable => true, - require => Package['dhcp-server'], - } - - concat {"${dhcp::params::config_dir}/dhcpd.conf": - owner => root, - group => root, - mode => '0644', - } - - concat::fragment {'00.dhcp.server.base': - ensure => present, - target => "${dhcp::params::config_dir}/dhcpd.conf", - require => Package['dhcp-server'], - notify => Service['dhcpd'], - } - - file {"${dhcp::params::config_dir}/dhcpd.conf.d": - ensure => directory, - mode => '0700', - recurse => true, - purge => true, - force => true, - source => 'puppet:///modules/dhcp/empty' - } - - file {"${dhcp::params::config_dir}/subnets": - ensure => directory, - recurse => true, - purge => true, - force => true, - source => 'puppet:///modules/dhcp/empty', - require => Package['dhcp-server'], - notify => Service['dhcpd'], - } - - file {"${dhcp::params::config_dir}/hosts.d": - ensure => directory, - recurse => true, - purge => true, - force => true, - source => 'puppet:///modules/dhcp/empty', - require => Package['dhcp-server'], - } - -} diff --git a/manifests/server/config.pp b/manifests/server/config.pp new file mode 100644 index 0000000..c37d044 --- /dev/null +++ b/manifests/server/config.pp @@ -0,0 +1,43 @@ +class dhcp::server::config { + include ::dhcp::params + include ::concat::setup + + concat {"${dhcp::params::config_dir}/dhcpd.conf": + owner => root, + group => root, + mode => '0644', + } + + concat::fragment {'00.dhcp.server.base': + ensure => present, + target => "${dhcp::params::config_dir}/dhcpd.conf", + content => template($dhcp::params::base_template), + } + + file {"${dhcp::params::config_dir}/dhcpd.conf.d": + ensure => directory, + mode => '0700', + recurse => true, + purge => true, + force => true, + source => "puppet:///modules/${module_name}/empty" + } + + file {"${dhcp::params::config_dir}/subnets": + ensure => directory, + recurse => true, + purge => true, + force => true, + source => "puppet:///modules/${module_name}/empty", + require => Package['dhcp-server'], + notify => Service['dhcpd'], + } + + file {"${dhcp::params::config_dir}/hosts.d": + ensure => directory, + recurse => true, + purge => true, + force => true, + source => "puppet:///modules/${module_name}/empty", + } +} diff --git a/manifests/server/debian.pp b/manifests/server/debian.pp deleted file mode 100644 index 5d23bbe..0000000 --- a/manifests/server/debian.pp +++ /dev/null @@ -1,21 +0,0 @@ -# = Class: dhcp::server::debian -# -# Installs a dhcp server on debian system. -# -# This class should not be included as is, -# please include "dhcp::server" instead. -# -class dhcp::server::debian inherits dhcp::server::base { - - Concat::Fragment['00.dhcp.server.base'] { - content => template('dhcp/dhcpd.conf.debian.erb'), - } - - Service['dhcpd'] { - pattern => $::lsbdistcodename ? { - squeeze => '/usr/sbin/dhcpd', - lenny => '/usr/sbin/dhcpd3', - } - } - -} diff --git a/manifests/server/packages.pp b/manifests/server/packages.pp new file mode 100644 index 0000000..34751e8 --- /dev/null +++ b/manifests/server/packages.pp @@ -0,0 +1,8 @@ +class dhcp::server::packages { + include ::dhcp::params + + package {'dhcp-server': + ensure => present, + name => $dhcp::params::srv_dhcpd, + } +} diff --git a/manifests/server/service.pp b/manifests/server/service.pp new file mode 100644 index 0000000..d531a7d --- /dev/null +++ b/manifests/server/service.pp @@ -0,0 +1,10 @@ +class dhcp::server::service { + include ::dhcp::params + + service {'dhcpd': + ensure => running, + name => $dhcp::params::srv_dhcpd, + enable => true, + pattern => $dhcp::params::service_pattern, + } +} -- cgit v1.2.3 From 90aa04648bf2e79890e47b8f84df8903f9958a12 Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Thu, 11 Apr 2013 11:58:37 +0200 Subject: Use # as comments --- manifests/server.pp | 65 +++++++++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/manifests/server.pp b/manifests/server.pp index 779b305..63e9f37 100644 --- a/manifests/server.pp +++ b/manifests/server.pp @@ -1,37 +1,34 @@ -/* - -= Class: dhcp::server -Simple OS wrapper. Include this to install a dhcp server on your host. - -Requires: - module "common": git://github.com/camptocamp/puppet-common.git - -facultative argument: - *$dhcpd_ddns_update* : ddns-update-style option (default to none) - *$dhcpd_authoritative* : set it if you want that your DHCP server is - authoritative (default to no) - *$dhcpd_opts* : any other DHCPD valid options - -Example: -node "dhcp.toto.ltd" { - $dhcpd_opts = ['domain-name "toto.ltd"', "domain-name-servers 192.168.21.1"] - include dhcp::server - - dhcp::subnet {"10.27.20.0": - ensure => present, - broadcast => "10.27.20.255", - other_opts => ['filename "pxelinux.0";', 'next-server 10.27.10.1;'], - } - - dhcp::host {"titi-eth0": - ensure => present, - mac => "0e:18:fa:fe:d9:00", - subnet => "10.27.20.0", - fixed_address => "10.27.10.52", - } -} -*/ - +# = Class: dhcp::server +# Simple OS wrapper. Include this to install a dhcp server on your host. +# +# Requires: +# module "common": git://github.com/camptocamp/puppet-common.git +# +# facultative argument: +# *$dhcpd_ddns_update* : ddns-update-style option (default to none) +# *$dhcpd_authoritative* : set it if you want that your DHCP server is +# authoritative (default to no) +# *$dhcpd_opts* : any other DHCPD valid options +# +# Example: +# node "dhcp.toto.ltd" { +# $dhcpd_opts = ['domain-name "toto.ltd"', "domain-name-servers 192.168.21.1"] +# include dhcp::server +# +# dhcp::subnet {"10.27.20.0": +# ensure => present, +# broadcast => "10.27.20.255", +# other_opts => ['filename "pxelinux.0";', 'next-server 10.27.10.1;'], +# } +# +# dhcp::host {"titi-eth0": +# ensure => present, +# mac => "0e:18:fa:fe:d9:00", +# subnet => "10.27.20.0", +# fixed_address => "10.27.10.52", +# } +# } +# class dhcp::server { class { '::dhcp::server::packages': } -> class { '::dhcp::server::config': } ~> -- cgit v1.2.3 From 49e0718b41881715f03b139468a28a9aadff3c3e Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Thu, 11 Apr 2013 12:02:44 +0200 Subject: Add Modulefile --- Modulefile | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 Modulefile diff --git a/Modulefile b/Modulefile new file mode 100644 index 0000000..f1d2b51 --- /dev/null +++ b/Modulefile @@ -0,0 +1,10 @@ +name 'camptocamp-dhcp' +version '0.1.0' +dependency 'puppetlabs/stdlib', '0.0.1' +dependency 'ripienaar/concat', '0.0.1' +source 'https://github.com/camptocamp/puppet-dhcp' +author 'DevOps Team / Camptocamp' +license 'GNU GPLv3' +summary 'Camptocamp DHCP Module' +description 'DHCP Module for Puppet' +project_page 'https://github.com/camptocamp/puppet-dhcp' -- cgit v1.2.3 From ea4fd867017083119a41f3a35a4b8ed6ad9fdbdc Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Thu, 11 Apr 2013 12:08:13 +0200 Subject: Prepare for rspec tests --- .fixtures.yml | 6 ++++++ .travis.yml | 15 +++++++++++++++ Gemfile | 13 +++++++++++++ Rakefile | 5 +++++ spec/.rspec | 6 ++++++ spec/fixtures/manifests/site.pp | 6 ++++++ spec/spec_helper.rb | 4 ++++ 7 files changed, 55 insertions(+) create mode 100644 .fixtures.yml create mode 100644 .travis.yml create mode 100644 Gemfile create mode 100644 Rakefile create mode 100644 spec/.rspec create mode 100644 spec/fixtures/manifests/site.pp create mode 100644 spec/spec_helper.rb diff --git a/.fixtures.yml b/.fixtures.yml new file mode 100644 index 0000000..14caa2b --- /dev/null +++ b/.fixtures.yml @@ -0,0 +1,6 @@ +fixtures: + repositories: + "stdlib": "git://github.com/puppetlabs/puppetlabs-stdlib.git" + "concat": "git://github.com/ripienaar/puppet-concat.git" + symlinks: + "dhcp": "#{source_dir}" diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..6d7c0f8 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: ruby +rvm: + - 1.8.7 + - 1.9.3 + - 2.0.0 + - ruby-head +env: + - PUPPET_GEM_VERSION=">= 3.0.0" +matrix: + allow_failures: + - rvm: 2.0.0 + - rvm: ruby-head + include: + - rvm: 1.8.7 + env: PUPPET_GEM_VERSION="~> 2.7" diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..a500d9e --- /dev/null +++ b/Gemfile @@ -0,0 +1,13 @@ +source :rubygems + +if ENV.key?('PUPPET_VERSION') + puppetversion = "= #{ENV['PUPPET_VERSION']}" +else + puppetversion = ['>= 2.7'] +end + +gem 'rake' +gem 'puppet-lint' +gem 'rspec-puppet' +gem 'puppet', puppetversion +gem 'puppetlabs_spec_helper', '>= 0.4.0' diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..4d31fec --- /dev/null +++ b/Rakefile @@ -0,0 +1,5 @@ +require 'puppet-lint/tasks/puppet-lint' +require 'puppetlabs_spec_helper/rake_tasks' + +task :default => [:spec, :lint] + diff --git a/spec/.rspec b/spec/.rspec new file mode 100644 index 0000000..91cd642 --- /dev/null +++ b/spec/.rspec @@ -0,0 +1,6 @@ +--format +s +--colour +--loadby +mtime +--backtrace diff --git a/spec/fixtures/manifests/site.pp b/spec/fixtures/manifests/site.pp new file mode 100644 index 0000000..0380291 --- /dev/null +++ b/spec/fixtures/manifests/site.pp @@ -0,0 +1,6 @@ +# Fake $::concat_basedir fact +$concat_basedir = '/var/lib/puppet/concat' + +# Default node to test defines +node default { +} diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..4edc964 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,4 @@ +require 'mocha' +require 'rspec-puppet' +require 'puppetlabs_spec_helper/module_spec_helper' + -- cgit v1.2.3 From 87096a875d4bee517e0c8c807218394a2024bd46 Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Thu, 11 Apr 2013 12:21:01 +0200 Subject: Add variable validations --- manifests/params.pp | 4 ++-- manifests/server/config.pp | 13 ++++++++++--- manifests/server/packages.pp | 7 +++++++ manifests/server/service.pp | 9 +++++++++ 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/manifests/params.pp b/manifests/params.pp index 20fbcd2..925f3f5 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -1,4 +1,4 @@ -# = Class: dhcp::params +# Class: dhcp::params # # Do NOT include this class - it won't do anything. # Set variables for names and paths @@ -23,7 +23,7 @@ class dhcp::params { squeeze => '/usr/sbin/dhcpd', } - $base_template = 'dhcp/dhcpd.conf.debian.erb' + $server_template = "${module_name}/dhcpd.conf.debian.erb" } default: { diff --git a/manifests/server/config.pp b/manifests/server/config.pp index c37d044..4898a60 100644 --- a/manifests/server/config.pp +++ b/manifests/server/config.pp @@ -1,7 +1,16 @@ +# Class: dhcp::server::config +# +# Configure the DHCP server +# class dhcp::server::config { include ::dhcp::params include ::concat::setup + validate_string($dhcp::params::config_dir) + validate_absolute_path($dhcp::params::config_dir) + validate_string($dhcp::params::server_template) + validate_re($dhcp::params::server_template, '^\S+$') + concat {"${dhcp::params::config_dir}/dhcpd.conf": owner => root, group => root, @@ -11,7 +20,7 @@ class dhcp::server::config { concat::fragment {'00.dhcp.server.base': ensure => present, target => "${dhcp::params::config_dir}/dhcpd.conf", - content => template($dhcp::params::base_template), + content => template($dhcp::params::server_template), } file {"${dhcp::params::config_dir}/dhcpd.conf.d": @@ -29,8 +38,6 @@ class dhcp::server::config { purge => true, force => true, source => "puppet:///modules/${module_name}/empty", - require => Package['dhcp-server'], - notify => Service['dhcpd'], } file {"${dhcp::params::config_dir}/hosts.d": diff --git a/manifests/server/packages.pp b/manifests/server/packages.pp index 34751e8..b89ede3 100644 --- a/manifests/server/packages.pp +++ b/manifests/server/packages.pp @@ -1,6 +1,13 @@ +# Class: dhcp::server::packages +# +# Install the DHCP server +# class dhcp::server::packages { include ::dhcp::params + validate_string($dhcp::params::srv_dhcpd) + validate_re($dhcp::params::srv_dhcpd, '^\S+$') + package {'dhcp-server': ensure => present, name => $dhcp::params::srv_dhcpd, diff --git a/manifests/server/service.pp b/manifests/server/service.pp index d531a7d..ef016c5 100644 --- a/manifests/server/service.pp +++ b/manifests/server/service.pp @@ -1,6 +1,15 @@ +# Class: dhcp::server::service +# +# Manage the DHCP server service +# class dhcp::server::service { include ::dhcp::params + validate_string($dhcp::params::srv_dhcpd) + validate_re($dhcp::params::srv_dhcpd, '^\S+$') + validate_string($dhcp::params::service_pattern) + validate_re($dhcp::params::service_pattern, '^\S+$') + service {'dhcpd': ensure => running, name => $dhcp::params::srv_dhcpd, -- cgit v1.2.3 From 4cc54cff2d04eedb672c7cc6b92a5edcccac63c9 Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Thu, 11 Apr 2013 12:34:23 +0200 Subject: Add spec tests for the dhcp class --- manifests/hosts.pp | 2 +- manifests/params.pp | 2 +- spec/classes/dhcp_spec.rb | 73 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 spec/classes/dhcp_spec.rb diff --git a/manifests/hosts.pp b/manifests/hosts.pp index b159449..426d661 100644 --- a/manifests/hosts.pp +++ b/manifests/hosts.pp @@ -42,7 +42,7 @@ define dhcp::hosts ( $template = 'dhcp/host.conf.erb', ) { - include dhcp::params + include ::dhcp::params concat::fragment {"dhcp.host.${name}": target => "${dhcp::params::config_dir}/hosts.d/${subnet}.conf", diff --git a/manifests/params.pp b/manifests/params.pp index 925f3f5..0938e4a 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -27,7 +27,7 @@ class dhcp::params { } default: { - fail "${name} is not available for ${::operatingsystem}/${::lsbdistcodename}" + fail "Unsupported OS ${::operatingsystem}/${::lsbdistcodename}" } } diff --git a/spec/classes/dhcp_spec.rb b/spec/classes/dhcp_spec.rb new file mode 100644 index 0000000..c833c52 --- /dev/null +++ b/spec/classes/dhcp_spec.rb @@ -0,0 +1,73 @@ +require 'spec_helper' + +describe 'dhcp' do + context 'When on an unsupported OS' do + let (:facts) { { + :operatingsystem => 'RedHat', + :osfamily => 'Redhat', + :lsbdistcodename => 'Santiago' + } } + + it 'should fail' do + expect { + should contain_package('dhcp-server') + }.to raise_error(Puppet::Error, /Unsupported OS RedHat\/Santiago/) + end + end + + context 'When on Debian lenny' do + let (:facts) { { + :operatingsystem => 'Debian', + :osfamily => 'Debian', + :lsbdistcodename => 'lenny' + } } + + # Package + it { should contain_package('dhcp-server').with( + :name => 'dhcp3-server' + ) } + + # Config + it { should contain_concat('/etc/dhcp3/dhcpd.conf').with( + :owner => 'root', + :group => 'root', + :mode => '0644' + ) } + + # Service + it { should contain_service('dhcpd').with( + :ensure => 'running', + :name => 'dhcp3-server', + :enable => true, + :pattern => '/usr/sbin/dhcpd3' + ) } + end + + context 'When on Debian squeeze' do + let (:facts) { { + :operatingsystem => 'Debian', + :osfamily => 'Debian', + :lsbdistcodename => 'squeeze' + } } + + # Package + it { should contain_package('dhcp-server').with( + :name => 'isc-dhcp-server' + ) } + + # Config + it { should contain_concat('/etc/dhcp/dhcpd.conf').with( + :owner => 'root', + :group => 'root', + :mode => '0644' + ) } + + # Service + it { should contain_service('dhcpd').with( + :ensure => 'running', + :name => 'isc-dhcp-server', + :enable => true, + :pattern => '/usr/sbin/dhcpd' + ) } + end +end -- cgit v1.2.3 From a3158aaca4070a4de89b49d687854d72df8741fe Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Thu, 11 Apr 2013 12:37:03 +0200 Subject: Fix trailing whitespaces --- manifests/server.pp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/manifests/server.pp b/manifests/server.pp index 63e9f37..ceffa14 100644 --- a/manifests/server.pp +++ b/manifests/server.pp @@ -1,26 +1,26 @@ # = Class: dhcp::server # Simple OS wrapper. Include this to install a dhcp server on your host. -# +# # Requires: # module "common": git://github.com/camptocamp/puppet-common.git -# +# # facultative argument: # *$dhcpd_ddns_update* : ddns-update-style option (default to none) # *$dhcpd_authoritative* : set it if you want that your DHCP server is # authoritative (default to no) # *$dhcpd_opts* : any other DHCPD valid options -# +# # Example: # node "dhcp.toto.ltd" { # $dhcpd_opts = ['domain-name "toto.ltd"', "domain-name-servers 192.168.21.1"] # include dhcp::server -# +# # dhcp::subnet {"10.27.20.0": # ensure => present, # broadcast => "10.27.20.255", # other_opts => ['filename "pxelinux.0";', 'next-server 10.27.10.1;'], # } -# +# # dhcp::host {"titi-eth0": # ensure => present, # mac => "0e:18:fa:fe:d9:00", -- cgit v1.2.3 From 65bea20e0d8e61de7519bb1ab0677597500aaac7 Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Thu, 11 Apr 2013 14:25:15 +0200 Subject: Get rid of unscoped variables in dhcp::server, fix template --- manifests/init.pp | 9 +++++- manifests/server.pp | 20 +++++++----- manifests/server/config.pp | 4 +++ spec/classes/dhcp_spec.rb | 68 +++++++++++++++++++++++++++++++++++++++++ templates/dhcpd.conf.debian.erb | 16 +++------- 5 files changed, 97 insertions(+), 20 deletions(-) diff --git a/manifests/init.pp b/manifests/init.pp index 8a14b13..e1e05b3 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -1,7 +1,14 @@ class dhcp ( $server = true, + $server_ddns_update = undef, + $server_authoritative = undef, + $server_opts = undef, ) { if $server { - class { '::dhcp::server': } + class { '::dhcp::server': + ddns_update => $server_ddns_update, + authoritative => $server_authoritative, + opts => $server_opts, + } } } diff --git a/manifests/server.pp b/manifests/server.pp index ceffa14..d83c26f 100644 --- a/manifests/server.pp +++ b/manifests/server.pp @@ -5,15 +5,17 @@ # module "common": git://github.com/camptocamp/puppet-common.git # # facultative argument: -# *$dhcpd_ddns_update* : ddns-update-style option (default to none) -# *$dhcpd_authoritative* : set it if you want that your DHCP server is -# authoritative (default to no) -# *$dhcpd_opts* : any other DHCPD valid options +# *$ddns_update* : ddns-update-style option (default to none) +# *$authoritative* : set it if you want that your DHCP server is +# authoritative (default to no) +# *$opts* : any other DHCPD valid options # # Example: # node "dhcp.toto.ltd" { -# $dhcpd_opts = ['domain-name "toto.ltd"', "domain-name-servers 192.168.21.1"] -# include dhcp::server +# class { 'dhcp::server': +# opts => ['domain-name "toto.ltd"', +# 'domain-name-servers 192.168.21.1'], +# } # # dhcp::subnet {"10.27.20.0": # ensure => present, @@ -29,7 +31,11 @@ # } # } # -class dhcp::server { +class dhcp::server ( + $ddns_update = 'none', + $authoritative = false, + $opts = [], +) { class { '::dhcp::server::packages': } -> class { '::dhcp::server::config': } ~> class { '::dhcp::server::service': } diff --git a/manifests/server/config.pp b/manifests/server/config.pp index 4898a60..0c1a74a 100644 --- a/manifests/server/config.pp +++ b/manifests/server/config.pp @@ -11,6 +11,10 @@ class dhcp::server::config { validate_string($dhcp::params::server_template) validate_re($dhcp::params::server_template, '^\S+$') + validate_string($dhcp::server::ddns_update) + validate_bool($dhcp::server::authoritative) + validate_array($dhcp::server::opts) + concat {"${dhcp::params::config_dir}/dhcpd.conf": owner => root, group => root, diff --git a/spec/classes/dhcp_spec.rb b/spec/classes/dhcp_spec.rb index c833c52..ce9228e 100644 --- a/spec/classes/dhcp_spec.rb +++ b/spec/classes/dhcp_spec.rb @@ -34,6 +34,13 @@ describe 'dhcp' do :mode => '0644' ) } + it { should contain_concat__fragment('00.dhcp.server.base').with( + :ensure => 'present', + :target => '/etc/dhcp3/dhcpd.conf', + :content => /log-facility/ + ).with_content(/ddns-update-style none;/).with_content(/#authoritative/) + } + # Service it { should contain_service('dhcpd').with( :ensure => 'running', @@ -62,6 +69,13 @@ describe 'dhcp' do :mode => '0644' ) } + it { should contain_concat__fragment('00.dhcp.server.base').with( + :ensure => 'present', + :target => '/etc/dhcp/dhcpd.conf', + :content => /log-facility/ + ).with_content(/ddns-update-style none;/).with_content(/#authoritative/) + } + # Service it { should contain_service('dhcpd').with( :ensure => 'running', @@ -70,4 +84,58 @@ describe 'dhcp' do :pattern => '/usr/sbin/dhcpd' ) } end + + context 'When passing ddns_update' do + let (:facts) { { + :operatingsystem => 'Debian', + :osfamily => 'Debian', + :lsbdistcodename => 'squeeze' + } } + let (:params) { { + :server_ddns_update => 'foo' + } } + + it { should contain_concat__fragment('00.dhcp.server.base').with( + :ensure => 'present', + :target => '/etc/dhcp/dhcpd.conf', + :content => /log-facility/ + ).with_content(/ddns-update-style foo;/).with_content(/#authoritative/) + } + end + + context 'When passing authoritative' do + let (:facts) { { + :operatingsystem => 'Debian', + :osfamily => 'Debian', + :lsbdistcodename => 'squeeze' + } } + let (:params) { { + :server_authoritative => true + } } + + it { should contain_concat__fragment('00.dhcp.server.base').with( + :ensure => 'present', + :target => '/etc/dhcp/dhcpd.conf', + :content => /log-facility/ + ).with_content(/ddns-update-style none;/).with_content(/[^#]authoritative/) + } + end + + context 'When passing opts' do + let (:facts) { { + :operatingsystem => 'Debian', + :osfamily => 'Debian', + :lsbdistcodename => 'squeeze' + } } + let (:params) { { + :server_opts => ['foo', 'bar', 'baz'] + } } + + it { should contain_concat__fragment('00.dhcp.server.base').with( + :ensure => 'present', + :target => '/etc/dhcp/dhcpd.conf', + :content => /log-facility/ + ).with_content(/ddns-update-style none;/).with_content(/#authoritative/).with_content(/foo;\nbar;\nbaz;\n/) + } + end end diff --git a/templates/dhcpd.conf.debian.erb b/templates/dhcpd.conf.debian.erb index ee95bee..7dfd168 100644 --- a/templates/dhcpd.conf.debian.erb +++ b/templates/dhcpd.conf.debian.erb @@ -4,22 +4,14 @@ # attempt to do a DNS update when a lease is confirmed. We default to the # behavior of the version 2 packages ('none', since DHCP v2 didn't # have support for DDNS.) -<% if has_variable?('dhcpd_ddns_update') -%> -ddns-update-style <%=dhcpd_ddns_update%>; -<% else -%> -ddns-update-style none; -<% end -%> +ddns-update-style <%= @ddns_update %>; # If this DHCP server is the official DHCP server for the local # network, the authoritative directive should be uncommented. -<% if has_variable?('dhcpd_authoritative') -%> -authoritative; -<% else -%> -#authoritative; -<% end -%> +<% unless @authoritative -%>#<%- end -%>authoritative; -<% if has_variable?('dhcpd_opts') and not dhcpd_opts.empty? -%> -<%= dhcpd_opts.join(";\n") %> +<% unless @opts.empty? -%> +<%= @opts.join(";\n") %>; <% end -%> # Use this to send dhcp log messages to a different log file (you also -- cgit v1.2.3 From d7efc5772d475e3ad374e6000f2e706be42c85da Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Thu, 11 Apr 2013 14:31:39 +0200 Subject: Check param validation in dhcp::server::config --- spec/classes/dhcp_spec.rb | 141 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 99 insertions(+), 42 deletions(-) diff --git a/spec/classes/dhcp_spec.rb b/spec/classes/dhcp_spec.rb index ce9228e..5eb7172 100644 --- a/spec/classes/dhcp_spec.rb +++ b/spec/classes/dhcp_spec.rb @@ -86,56 +86,113 @@ describe 'dhcp' do end context 'When passing ddns_update' do - let (:facts) { { - :operatingsystem => 'Debian', - :osfamily => 'Debian', - :lsbdistcodename => 'squeeze' - } } - let (:params) { { - :server_ddns_update => 'foo' - } } + context 'When passing wrong type' do + let (:facts) { { + :operatingsystem => 'Debian', + :osfamily => 'Debian', + :lsbdistcodename => 'squeeze' + } } + let (:params) { { + :server_ddns_update => true + } } + + it 'should fail' do + expect { + should contain_concat__fragment('00.dhcp.server.base') + }.to raise_error(Puppet::Error, /true is not a string./) + end + end - it { should contain_concat__fragment('00.dhcp.server.base').with( - :ensure => 'present', - :target => '/etc/dhcp/dhcpd.conf', - :content => /log-facility/ - ).with_content(/ddns-update-style foo;/).with_content(/#authoritative/) - } + context 'When passing valid value' do + let (:facts) { { + :operatingsystem => 'Debian', + :osfamily => 'Debian', + :lsbdistcodename => 'squeeze' + } } + let (:params) { { + :server_ddns_update => 'foo' + } } + + it { should contain_concat__fragment('00.dhcp.server.base').with( + :ensure => 'present', + :target => '/etc/dhcp/dhcpd.conf', + :content => /log-facility/ + ).with_content(/ddns-update-style foo;/).with_content(/#authoritative/) + } + end end context 'When passing authoritative' do - let (:facts) { { - :operatingsystem => 'Debian', - :osfamily => 'Debian', - :lsbdistcodename => 'squeeze' - } } - let (:params) { { - :server_authoritative => true - } } + context 'When passing wrong type' do + let (:facts) { { + :operatingsystem => 'Debian', + :osfamily => 'Debian', + :lsbdistcodename => 'squeeze' + } } + let (:params) { { + :server_authoritative => 'foo' + } } + + it 'should fail' do + expect { + should contain_concat__fragment('00.dhcp.server.base') + }.to raise_error(Puppet::Error, /"foo" is not a boolean./) + end + end - it { should contain_concat__fragment('00.dhcp.server.base').with( - :ensure => 'present', - :target => '/etc/dhcp/dhcpd.conf', - :content => /log-facility/ - ).with_content(/ddns-update-style none;/).with_content(/[^#]authoritative/) - } + context 'When passing valid value' do + let (:facts) { { + :operatingsystem => 'Debian', + :osfamily => 'Debian', + :lsbdistcodename => 'squeeze' + } } + let (:params) { { + :server_authoritative => true + } } + + it { should contain_concat__fragment('00.dhcp.server.base').with( + :ensure => 'present', + :target => '/etc/dhcp/dhcpd.conf', + :content => /log-facility/ + ).with_content(/ddns-update-style none;/).with_content(/[^#]authoritative/) + } + end end context 'When passing opts' do - let (:facts) { { - :operatingsystem => 'Debian', - :osfamily => 'Debian', - :lsbdistcodename => 'squeeze' - } } - let (:params) { { - :server_opts => ['foo', 'bar', 'baz'] - } } + context 'When passing wrong type' do + let (:facts) { { + :operatingsystem => 'Debian', + :osfamily => 'Debian', + :lsbdistcodename => 'squeeze' + } } + let (:params) { { + :server_opts => 'foo' + } } + + it 'should fail' do + expect { + should contain_concat__fragment('00.dhcp.server.base') + }.to raise_error(Puppet::Error, /"foo" is not an Array./) + end + end - it { should contain_concat__fragment('00.dhcp.server.base').with( - :ensure => 'present', - :target => '/etc/dhcp/dhcpd.conf', - :content => /log-facility/ - ).with_content(/ddns-update-style none;/).with_content(/#authoritative/).with_content(/foo;\nbar;\nbaz;\n/) - } + context 'When passing valid value' do + let (:facts) { { + :operatingsystem => 'Debian', + :osfamily => 'Debian', + :lsbdistcodename => 'squeeze' + } } + let (:params) { { + :server_opts => ['foo', 'bar', 'baz'] + } } + + it { should contain_concat__fragment('00.dhcp.server.base').with( + :ensure => 'present', + :target => '/etc/dhcp/dhcpd.conf', + :content => /log-facility/ + ).with_content(/ddns-update-style none;/).with_content(/#authoritative/).with_content(/foo;\nbar;\nbaz;\n/) + } + end end end -- cgit v1.2.3 From 2b4ea8662914c9652cfde1834bbfdc6d25e45209 Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Thu, 11 Apr 2013 14:37:04 +0200 Subject: Doc in dhcp::server --- manifests/server.pp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/manifests/server.pp b/manifests/server.pp index d83c26f..1c77cf2 100644 --- a/manifests/server.pp +++ b/manifests/server.pp @@ -5,10 +5,10 @@ # module "common": git://github.com/camptocamp/puppet-common.git # # facultative argument: -# *$ddns_update* : ddns-update-style option (default to none) -# *$authoritative* : set it if you want that your DHCP server is -# authoritative (default to no) -# *$opts* : any other DHCPD valid options +# *$ddns_update* : ddns-update-style option (defaults to 'none') +# *$authoritative* : a boolean setting whether the DHCP server is +# authoritative (defaults to false) +# *$opts* : an array of DHCPD valid options # # Example: # node "dhcp.toto.ltd" { -- cgit v1.2.3 From 0689631b833d5683bdef5885de5893450f4928ca Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Fri, 12 Apr 2013 08:52:01 +0200 Subject: small letters --- spec/classes/dhcp_spec.rb | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/spec/classes/dhcp_spec.rb b/spec/classes/dhcp_spec.rb index 5eb7172..9db478c 100644 --- a/spec/classes/dhcp_spec.rb +++ b/spec/classes/dhcp_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe 'dhcp' do - context 'When on an unsupported OS' do + context 'when on an unsupported OS' do let (:facts) { { :operatingsystem => 'RedHat', :osfamily => 'Redhat', @@ -15,7 +15,7 @@ describe 'dhcp' do end end - context 'When on Debian lenny' do + context 'when on Debian lenny' do let (:facts) { { :operatingsystem => 'Debian', :osfamily => 'Debian', @@ -50,7 +50,7 @@ describe 'dhcp' do ) } end - context 'When on Debian squeeze' do + context 'when on Debian squeeze' do let (:facts) { { :operatingsystem => 'Debian', :osfamily => 'Debian', @@ -85,8 +85,8 @@ describe 'dhcp' do ) } end - context 'When passing ddns_update' do - context 'When passing wrong type' do + context 'when passing ddns_update' do + context 'when passing wrong type' do let (:facts) { { :operatingsystem => 'Debian', :osfamily => 'Debian', @@ -103,7 +103,7 @@ describe 'dhcp' do end end - context 'When passing valid value' do + context 'when passing valid value' do let (:facts) { { :operatingsystem => 'Debian', :osfamily => 'Debian', @@ -122,8 +122,8 @@ describe 'dhcp' do end end - context 'When passing authoritative' do - context 'When passing wrong type' do + context 'when passing authoritative' do + context 'when passing wrong type' do let (:facts) { { :operatingsystem => 'Debian', :osfamily => 'Debian', @@ -140,7 +140,7 @@ describe 'dhcp' do end end - context 'When passing valid value' do + context 'when passing valid value' do let (:facts) { { :operatingsystem => 'Debian', :osfamily => 'Debian', @@ -159,8 +159,8 @@ describe 'dhcp' do end end - context 'When passing opts' do - context 'When passing wrong type' do + context 'when passing opts' do + context 'when passing wrong type' do let (:facts) { { :operatingsystem => 'Debian', :osfamily => 'Debian', @@ -177,7 +177,7 @@ describe 'dhcp' do end end - context 'When passing valid value' do + context 'when passing valid value' do let (:facts) { { :operatingsystem => 'Debian', :osfamily => 'Debian', -- cgit v1.2.3 From f4e7bdf477586e34e47a972172c15809ac92fe3c Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Fri, 12 Apr 2013 10:38:14 +0200 Subject: Improve template host.conf.erb with validations and add spec for dhcp::hosts --- manifests/hosts.pp | 26 ++- spec/defines/dhcp_hosts_spec.rb | 411 ++++++++++++++++++++++++++++++++++++++++ templates/host.conf.erb | 32 ++-- 3 files changed, 451 insertions(+), 18 deletions(-) create mode 100644 spec/defines/dhcp_hosts_spec.rb diff --git a/manifests/hosts.pp b/manifests/hosts.pp index 426d661..f8e5faf 100644 --- a/manifests/hosts.pp +++ b/manifests/hosts.pp @@ -1,4 +1,4 @@ -# = Definition: dhcp::hosts +# Definition: dhcp::hosts # # Creates a dhcp configuration for given hosts # @@ -38,15 +38,27 @@ define dhcp::hosts ( $hash_data, $subnet, - $global_options = false, - $template = 'dhcp/host.conf.erb', + $ensure = present, + $global_options = [], + $template = "${module_name}/host.conf.erb", ) { include ::dhcp::params - concat::fragment {"dhcp.host.${name}": - target => "${dhcp::params::config_dir}/hosts.d/${subnet}.conf", - content => template($template), - notify => Service['dhcpd'], + validate_string($ensure) + validate_re($ensure, ['present', 'absent'], + "\$ensure must be either 'present' or 'absent', got '${ensure}'") + validate_hash($hash_data) + validate_string($subnet) + validate_re($subnet, '^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$') + validate_array($global_options) + validate_string($template) + + if ($ensure == 'present') { + concat::fragment {"dhcp.host.${name}": + target => "${dhcp::params::config_dir}/hosts.d/${subnet}.conf", + content => template($template), + notify => Service['dhcpd'], + } } } diff --git a/spec/defines/dhcp_hosts_spec.rb b/spec/defines/dhcp_hosts_spec.rb new file mode 100644 index 0000000..658be20 --- /dev/null +++ b/spec/defines/dhcp_hosts_spec.rb @@ -0,0 +1,411 @@ +require 'spec_helper' + +describe 'dhcp::hosts' do + let (:title) { 'My hosts' } + let (:facts) { { + :operatingsystem => 'Debian', + :osfamily => 'Debian', + :lsbdistcodename => 'squeeze', + } } + + context 'when passing wrong value for ensure' do + let (:params) { { + :hash_data => {}, + :subnet => '1.2.3.4', + :ensure => 'running' + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp.host.My hosts') + }.to raise_error(Puppet::Error, /\$ensure must be either 'present' or 'absent', got 'running'/) + end + end + + context 'when hash_data is not passed' do + let (:params) { { + :subnet => '1.2.3.4', + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp.host.My hosts') + }.to raise_error(Puppet::Error, /Must pass hash_data to Dhcp::Hosts/) + end + end + + context 'when passing wrong type for hash_data' do + let (:params) { { + :hash_data => 'foo', + :subnet => '1.2.3.4', + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp.host.My hosts') + }.to raise_error(Puppet::Error, /"foo" is not a Hash\./) + end + end + + context 'when subnet is not passed' do + let (:params) { { + :hash_data => {}, + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp.host.My hosts') + }.to raise_error(Puppet::Error, /Must pass subnet to Dhcp::Hosts/) + end + end + + context 'when passing wrong type for subnet' do + let (:params) { { + :hash_data => {}, + :subnet => true + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp.host.My hosts') + }.to raise_error(Puppet::Error, /true is not a string\./) + end + end + + context 'when passing wrong value for subnet' do + let (:params) { { + :hash_data => {}, + :subnet => 'foo' + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp.host.My hosts') + }.to raise_error(Puppet::Error, /"foo" does not match/) + end + end + + context 'when passing wrong type for global_options' do + let (:params) { { + :hash_data => {}, + :subnet => '1.2.3.4', + :global_options => 'foo' + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp.host.My hosts') + }.to raise_error(Puppet::Error, /"foo" is not an Array\./) + end + end + + context 'when passing wrong type for template' do + let (:params) { { + :hash_data => {}, + :subnet => '1.2.3.4', + :template => true + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp.host.My hosts') + }.to raise_error(Puppet::Error, /true is not a string\./) + end + end + + context 'when passing one entry in hash_data' do + context 'when passing wrong type for an host data' do + let (:params) { { + :hash_data => { + 'host1' => true, + }, + :subnet => '1.2.3.4' + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp.host.My hosts') + }.to raise_error(Puppet::Error, /true is not a Hash/) + end + end + + context 'when interfaces is not passed' do + let (:params) { { + :hash_data => { + 'host1' => { + }, + }, + :subnet => '1.2.3.4' + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp.host.My hosts') + }.to raise_error(Puppet::Error, /Missing interfaces hash for host 'host1'/) + end + end + + context 'when passing wrong value for an interface' do + let (:params) { { + :hash_data => { + 'host1' => { + 'interfaces' => { + 'eth 0' => '00:11:22:33:44:55', + }, + }, + }, + :subnet => '1.2.3.4' + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp.host.My hosts') + }.to raise_error(Puppet::Error, /"eth 0" does not match/) + end + end + + context 'when passing wrong type for a mac address' do + let (:params) { { + :hash_data => { + 'host1' => { + 'interfaces' => { + 'eth0' => true, + }, + }, + }, + :subnet => '1.2.3.4' + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp.host.My hosts') + }.to raise_error(Puppet::Error, /true is not a string\./) + end + end + + context 'when passing wrong value for a mac address' do + let (:params) { { + :hash_data => { + 'host1' => { + 'interfaces' => { + 'eth0' => 'my mac', + }, + }, + }, + :subnet => '1.2.3.4' + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp.host.My hosts') + }.to raise_error(Puppet::Error, /"my mac" does not match/) + end + end + + context 'when passing wrong type for fixed_address' do + let (:params) { { + :hash_data => { + 'host1' => { + 'interfaces' => { + 'eth0' => '00:11:22:33:44:55', + }, + 'fixed_address' => true, + }, + }, + :subnet => '1.2.3.4' + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp.host.My hosts') + }.to raise_error(Puppet::Error, /true is not a string/) + end + end + + context 'when passing wrong value for fixed_address' do + let (:params) { { + :hash_data => { + 'host1' => { + 'interfaces' => { + 'eth0' => '00:11:22:33:44:55', + }, + 'fixed_address' => 'my wrong value', + }, + }, + :subnet => '1.2.3.4' + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp.host.My hosts') + }.to raise_error(Puppet::Error, /"my wrong value" does not match/) + end + end + + context 'when not passing fixed_address' do + let (:params) { { + :hash_data => { + 'host1' => { + 'interfaces' => { + 'eth0' => '00:11:22:33:44:55', + }, + }, + }, + :subnet => '1.2.3.4' + } } + + it { should contain_concat__fragment('dhcp.host.My hosts').with( + :content => /fixed-address host1;/ + ) } + end + + context 'when not passing options' do + let (:params) { { + :hash_data => { + 'host1' => { + 'interfaces' => { + 'eth0' => '00:11:22:33:44:55', + }, + }, + }, + :subnet => '1.2.3.4', + :global_options => ['foo', 'bar'], + } } + + it { should contain_concat__fragment('dhcp.host.My hosts').with( + :content => /foo;\nbar;\n/ + ) } + end + + context 'when overriding options' do + let (:params) { { + :hash_data => { + 'host1' => { + 'interfaces' => { + 'eth0' => '00:11:22:33:44:55', + }, + 'options' => ['baz'], + }, + }, + :subnet => '1.2.3.4', + :global_options => ['foo', 'bar'], + } } + + it { should contain_concat__fragment('dhcp.host.My hosts').with( + :content => /baz;\n/ + ) } + end + + context 'when passing wrong type for options' do + let (:params) { { + :hash_data => { + 'host1' => { + 'interfaces' => { + 'eth0' => '00:11:22:33:44:55', + }, + 'options' => true, + }, + }, + :subnet => '1.2.3.4', + :global_options => ['foo', 'bar'], + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp.host.My hosts') + }.to raise_error(Puppet::Error, /true is not an Array\./) + end + end + + context 'when passing wrong value for fixed_address' do + let (:params) { { + :hash_data => { + 'host1' => { + 'interfaces' => { + 'eth0' => '00:11:22:33:44:55', + }, + 'fixed_address' => 'my wrong value', + }, + }, + :subnet => '1.2.3.4' + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp.host.My hosts') + }.to raise_error(Puppet::Error, /"my wrong value" does not match/) + end + end + + context 'when not passing fixed_address' do + let (:params) { { + :hash_data => { + 'host1' => { + 'interfaces' => { + 'eth0' => '00:11:22:33:44:55', + }, + }, + }, + :subnet => '1.2.3.4' + } } + + it { should contain_concat__fragment('dhcp.host.My hosts').with( + :content => /fixed-address host1;/ + ) } + end + + context 'when not passing options' do + let (:params) { { + :hash_data => { + 'host1' => { + 'interfaces' => { + 'eth0' => '00:11:22:33:44:55', + }, + }, + }, + :subnet => '1.2.3.4', + :global_options => ['foo', 'bar'], + } } + + it { should contain_concat__fragment('dhcp.host.My hosts').with( + :content => /foo;\nbar;\n/ + ) } + end + + context 'when passing two hosts' do + let (:params) { { + :hash_data => { + 'host1' => { + 'interfaces' => { + 'eth0' => '00:11:22:33:44:55', + 'wlan0' => '00:aa:bb:44:55:ff', + }, + }, + 'host2' => { + 'interfaces' => { + 'eth1' => '00:11:af:33:44:55', + }, + 'fixed_address' => 'foo.example.com', + 'options' => ['opt1'], + }, + }, + :subnet => '1.2.3.4' + } } + + it { should contain_concat__fragment('dhcp.host.My hosts').with_content( + /host host1-eth0 \{\n hardware ethernet 00:11:22:33:44:55;\n fixed-address host1;\n\}/).with_content( + /host host1-wlan0 \{\n hardware ethernet 00:aa:bb:44:55:ff;\n fixed-address host1;\n\}/).with_content( + /host host2-eth1 \{\n hardware ethernet 00:11:af:33:44:55;\n fixed-address foo\.example\.com;\n opt1;\n\}/) + } + end + end + + context 'when passing two entries in hash_data' do + end + + context 'when passing global_options' do + end + + context 'when overriding template' do + end +end diff --git a/templates/host.conf.erb b/templates/host.conf.erb index e6a7073..398d798 100644 --- a/templates/host.conf.erb +++ b/templates/host.conf.erb @@ -1,16 +1,26 @@ -<%- @hash_data.sort.each do |host, datas| -%> -<%- datas.fetch('interfaces').sort.each do |if_name, if_mac| -%> +<%- @hash_data.sort.each do |host, data| + scope.function_validate_hash([data]) -%> +<%- + raise Puppet::ParseError, "Missing interfaces hash for host '#{host}'" unless data.has_key? 'interfaces' + data.fetch('interfaces').sort.each do |if_name, if_mac| + scope.function_validate_re([if_name, '^\S+$']) + scope.function_validate_string([if_mac]) + scope.function_validate_re([if_mac, '^[a-f0-9:.]+$']) + -%> host <%= host %>-<%= if_name %> { hardware ethernet <%= if_mac %>; -<% if datas.fetch('fixed_address', false) -%> - fixed-address <%= datas.fetch('fixed_address') %>; -<% else -%> - fixed-address <%= host %>; -<% end -%> -<% if datas.fetch('option', false) -%> - <%= datas.fetch('option') %> -<% elsif @global_options -%> - <%= @global_options %> +<% + fixed_address = data.fetch('fixed_address', nil) || host + scope.function_validate_string([fixed_address]) + scope.function_validate_re([fixed_address, '^\S+$']) + -%> + fixed-address <%= fixed_address %>; +<% + options = data.fetch('options', nil) || @global_options + scope.function_validate_array([options]) + unless options.empty? + -%> + <%= options.join(";\n") %>; <% end -%> } <% end -%> -- cgit v1.2.3 From 53346e4314aed349290661cbe70c97ab573ea9bc Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Fri, 12 Apr 2013 10:45:55 +0200 Subject: Remove duplicated tests, test template override --- spec/defines/dhcp_hosts_spec.rb | 54 +++++++++-------------------------------- 1 file changed, 11 insertions(+), 43 deletions(-) diff --git a/spec/defines/dhcp_hosts_spec.rb b/spec/defines/dhcp_hosts_spec.rb index 658be20..c8face6 100644 --- a/spec/defines/dhcp_hosts_spec.rb +++ b/spec/defines/dhcp_hosts_spec.rb @@ -317,43 +317,6 @@ describe 'dhcp::hosts' do end end - context 'when passing wrong value for fixed_address' do - let (:params) { { - :hash_data => { - 'host1' => { - 'interfaces' => { - 'eth0' => '00:11:22:33:44:55', - }, - 'fixed_address' => 'my wrong value', - }, - }, - :subnet => '1.2.3.4' - } } - - it 'should fail' do - expect { - should contain_concat__fragment('dhcp.host.My hosts') - }.to raise_error(Puppet::Error, /"my wrong value" does not match/) - end - end - - context 'when not passing fixed_address' do - let (:params) { { - :hash_data => { - 'host1' => { - 'interfaces' => { - 'eth0' => '00:11:22:33:44:55', - }, - }, - }, - :subnet => '1.2.3.4' - } } - - it { should contain_concat__fragment('dhcp.host.My hosts').with( - :content => /fixed-address host1;/ - ) } - end - context 'when not passing options' do let (:params) { { :hash_data => { @@ -400,12 +363,17 @@ describe 'dhcp::hosts' do end end - context 'when passing two entries in hash_data' do - end - - context 'when passing global_options' do - end - context 'when overriding template' do + let (:params) { { + :hash_data => {}, + :subnet => '1.2.3.4', + :template => 'wrong/path', + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp.host.My hosts') + }.to raise_error(Puppet::Error, /Could not find template 'wrong\/path'/) + end end end -- cgit v1.2.3 From f7025518512c0bf8d63f413f7be78347fedee9f4 Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Fri, 12 Apr 2013 10:50:24 +0200 Subject: Test target for concat::fragment in dhcp::hosts --- manifests/hosts.pp | 8 +++----- spec/defines/dhcp_hosts_spec.rb | 7 ++++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/manifests/hosts.pp b/manifests/hosts.pp index f8e5faf..676d507 100644 --- a/manifests/hosts.pp +++ b/manifests/hosts.pp @@ -4,15 +4,14 @@ # # Arguments # $template: dhcp host template - default: 'dhcp/host.conf.erb' -# $global_options: global options for the whole bunch of hosts. +# $global_options: an array of global options for the whole bunch of hosts. # you may override it per host, setting the host "options" # directly in the hash. # $subnet: targeted subnet # $hash_data: hash containing data - default form: # { # => { -# options => false, -# fixed_address => false, +# options => ['opt1', 'opt2'], # interfaces => { # eth0 => 'mac-address', # eth1 => 'mac-address', @@ -22,8 +21,7 @@ # } # }, # => { -# options => false, -# fixed_address => false, +# fixed_address => 'foo.example.com', # interfaces => { # eth0 => 'mac-address', # eth1 => 'mac-address', diff --git a/spec/defines/dhcp_hosts_spec.rb b/spec/defines/dhcp_hosts_spec.rb index c8face6..f5f44ca 100644 --- a/spec/defines/dhcp_hosts_spec.rb +++ b/spec/defines/dhcp_hosts_spec.rb @@ -255,6 +255,7 @@ describe 'dhcp::hosts' do } } it { should contain_concat__fragment('dhcp.host.My hosts').with( + :target => '/etc/dhcp/hosts.d/1.2.3.4.conf', :content => /fixed-address host1;/ ) } end @@ -273,6 +274,7 @@ describe 'dhcp::hosts' do } } it { should contain_concat__fragment('dhcp.host.My hosts').with( + :target => '/etc/dhcp/hosts.d/1.2.3.4.conf', :content => /foo;\nbar;\n/ ) } end @@ -292,6 +294,7 @@ describe 'dhcp::hosts' do } } it { should contain_concat__fragment('dhcp.host.My hosts').with( + :target => '/etc/dhcp/hosts.d/1.2.3.4.conf', :content => /baz;\n/ ) } end @@ -331,6 +334,7 @@ describe 'dhcp::hosts' do } } it { should contain_concat__fragment('dhcp.host.My hosts').with( + :target => '/etc/dhcp/hosts.d/1.2.3.4.conf', :content => /foo;\nbar;\n/ ) } end @@ -355,7 +359,8 @@ describe 'dhcp::hosts' do :subnet => '1.2.3.4' } } - it { should contain_concat__fragment('dhcp.host.My hosts').with_content( + it { should contain_concat__fragment('dhcp.host.My hosts').with( + :target => '/etc/dhcp/hosts.d/1.2.3.4.conf').with_content( /host host1-eth0 \{\n hardware ethernet 00:11:22:33:44:55;\n fixed-address host1;\n\}/).with_content( /host host1-wlan0 \{\n hardware ethernet 00:aa:bb:44:55:ff;\n fixed-address host1;\n\}/).with_content( /host host2-eth1 \{\n hardware ethernet 00:11:af:33:44:55;\n fixed-address foo\.example\.com;\n opt1;\n\}/) -- cgit v1.2.3 From e1f00ed6c75724251c3e205a3a50c7a3a4bcb077 Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Fri, 12 Apr 2013 10:53:21 +0200 Subject: Rename shared-network as shared_network --- manifests/shared-network.pp | 25 ------------------------- manifests/shared_network.pp | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 25 deletions(-) delete mode 100644 manifests/shared-network.pp create mode 100644 manifests/shared_network.pp diff --git a/manifests/shared-network.pp b/manifests/shared-network.pp deleted file mode 100644 index 9a02a5b..0000000 --- a/manifests/shared-network.pp +++ /dev/null @@ -1,25 +0,0 @@ -# == Definition: dhcp::shared-network -# Creates a shared-network -# -# Arguments: -# *$subnets* : subnet list to be included in the shared-network -# -# Warnings: -# - subnets must exists -# - subnets must have $is_shared set to true (default is false) -# -define dhcp::shared-network( - $ensure = present, - $subnets = [] -) { - - include dhcp::params - - concat::fragment {"shared-${name}": - ensure => $ensure, - target => "${dhcp::params::config_dir}/dhcpd.conf", - content => template('dhcp/shared-network.erb'), - require => Dhcp::Subnet[$subnets], - } - -} diff --git a/manifests/shared_network.pp b/manifests/shared_network.pp new file mode 100644 index 0000000..9a02a5b --- /dev/null +++ b/manifests/shared_network.pp @@ -0,0 +1,25 @@ +# == Definition: dhcp::shared-network +# Creates a shared-network +# +# Arguments: +# *$subnets* : subnet list to be included in the shared-network +# +# Warnings: +# - subnets must exists +# - subnets must have $is_shared set to true (default is false) +# +define dhcp::shared-network( + $ensure = present, + $subnets = [] +) { + + include dhcp::params + + concat::fragment {"shared-${name}": + ensure => $ensure, + target => "${dhcp::params::config_dir}/dhcpd.conf", + content => template('dhcp/shared-network.erb'), + require => Dhcp::Subnet[$subnets], + } + +} -- cgit v1.2.3 From f28c3616cefa8f2c53fd04cbc2b3a58b0cb4f0be Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Fri, 12 Apr 2013 10:56:56 +0200 Subject: Pass $ensure to concat::fragment in dhcp::hosts --- manifests/hosts.pp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/manifests/hosts.pp b/manifests/hosts.pp index 676d507..9e2b284 100644 --- a/manifests/hosts.pp +++ b/manifests/hosts.pp @@ -52,11 +52,10 @@ define dhcp::hosts ( validate_array($global_options) validate_string($template) - if ($ensure == 'present') { - concat::fragment {"dhcp.host.${name}": - target => "${dhcp::params::config_dir}/hosts.d/${subnet}.conf", - content => template($template), - notify => Service['dhcpd'], - } + concat::fragment {"dhcp.host.${name}": + ensure => $ensure, + target => "${dhcp::params::config_dir}/hosts.d/${subnet}.conf", + content => template($template), + notify => Service['dhcpd'], } } -- cgit v1.2.3 From 558df46dc9bec98c6c90719d9aed11ec7a338411 Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Fri, 12 Apr 2013 11:05:42 +0200 Subject: Simplify template --- templates/host.conf.erb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/templates/host.conf.erb b/templates/host.conf.erb index 398d798..1c690b4 100644 --- a/templates/host.conf.erb +++ b/templates/host.conf.erb @@ -1,6 +1,5 @@ <%- @hash_data.sort.each do |host, data| - scope.function_validate_hash([data]) -%> -<%- + scope.function_validate_hash([data]) raise Puppet::ParseError, "Missing interfaces hash for host '#{host}'" unless data.has_key? 'interfaces' data.fetch('interfaces').sort.each do |if_name, if_mac| scope.function_validate_re([if_name, '^\S+$']) -- cgit v1.2.3 From b35e1322d0a791ab22cb2c385ec913492c75026d Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Fri, 12 Apr 2013 11:10:45 +0200 Subject: Rename dhcp::shared-network to dhcp::shared_network, add specs --- manifests/shared_network.pp | 15 ++++--- spec/defines/dhcp_shared_network_spec.rb | 71 ++++++++++++++++++++++++++++++++ templates/shared-network.erb | 9 ++-- 3 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 spec/defines/dhcp_shared_network_spec.rb diff --git a/manifests/shared_network.pp b/manifests/shared_network.pp index 9a02a5b..38f14fb 100644 --- a/manifests/shared_network.pp +++ b/manifests/shared_network.pp @@ -8,17 +8,22 @@ # - subnets must exists # - subnets must have $is_shared set to true (default is false) # -define dhcp::shared-network( +define dhcp::shared_network( $ensure = present, - $subnets = [] + $subnets = [], ) { - include dhcp::params + include ::dhcp::params - concat::fragment {"shared-${name}": + validate_string($ensure) + validate_re($ensure, ['present', 'absent'], + "\$ensure must be either 'present' or 'absent', got '${ensure}'") + validate_array($subnets) + + concat::fragment {"dhcp-shared-${name}": ensure => $ensure, target => "${dhcp::params::config_dir}/dhcpd.conf", - content => template('dhcp/shared-network.erb'), + content => template("${module_name}/shared-network.erb"), require => Dhcp::Subnet[$subnets], } diff --git a/spec/defines/dhcp_shared_network_spec.rb b/spec/defines/dhcp_shared_network_spec.rb new file mode 100644 index 0000000..ff5a2c5 --- /dev/null +++ b/spec/defines/dhcp_shared_network_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper' + +describe 'dhcp::shared_network' do + let (:title) { 'My network' } + let (:facts) { { + :operatingsystem => 'Debian', + :osfamily => 'Debian', + :lsbdistcodename => 'squeeze' + } } + + context 'when passing wrong value for ensure' do + let (:params) { { + :ensure => 'running', + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp-shared-My network') + }.to raise_error(Puppet::Error, /\$ensure must be either 'present' or 'absent', got 'running'/) + end + end + + context 'when passing wrong type for subnets' do + let (:params) { { + :subnets => true, + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp-shared-My network') + }.to raise_error(Puppet::Error, /true is not an Array\./) + end + end + + context 'when passing no parameters' do + it { should contain_concat__fragment('dhcp-shared-My network').with( + :ensure => 'present', + :target => '/etc/dhcp/dhcpd.conf' + ).with_content( + /shared-network My network/ + ) + } + end + + context 'when passing wrong type for a subnet' do + let (:params) { { + :subnets => [true], + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp-shared-My network') + }.to raise_error(Puppet::Error, /true is not a string\./) + end + end + + context 'when passing wrong value for a subnet' do + let (:params) { { + :subnets => ['wrong value'], + } } + + it 'should fail' do + expect { + should contain_concat__fragment('dhcp-shared-My network') + }.to raise_error(Puppet::Error, /"wrong value" does not match/) + end + end + + context 'when passing subnets' do + end +end diff --git a/templates/shared-network.erb b/templates/shared-network.erb index 99a7aca..407106a 100644 --- a/templates/shared-network.erb +++ b/templates/shared-network.erb @@ -1,6 +1,9 @@ -#### dhcp::shared-network <%= name %> -shared-network <%= name %> { -<% subnets.each do |subnet| -%> +#### dhcp::shared_network <%= @name %> +shared-network <%= @name %> { +<% @subnets.each do |subnet| + scope.function_validate_string([subnet]) + scope.function_validate_re([subnet, '^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$']) +-%> include "<%= scope.lookupvar("dhcp::params::config_dir") %>/subnets/<%= subnet %>.conf"; <% end -%> } -- cgit v1.2.3 From e7cdc4e685a73d05be05ea9d6d0f854c4a3a6ab2 Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Fri, 12 Apr 2013 11:18:56 +0200 Subject: Test working dhcp::shared_network --- spec/defines/dhcp_shared_network_spec.rb | 12 +++++++++++- templates/shared-network.erb | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/spec/defines/dhcp_shared_network_spec.rb b/spec/defines/dhcp_shared_network_spec.rb index ff5a2c5..d59f73c 100644 --- a/spec/defines/dhcp_shared_network_spec.rb +++ b/spec/defines/dhcp_shared_network_spec.rb @@ -37,7 +37,7 @@ describe 'dhcp::shared_network' do :ensure => 'present', :target => '/etc/dhcp/dhcpd.conf' ).with_content( - /shared-network My network/ + /shared-network My network \{\n\}/ ) } end @@ -67,5 +67,15 @@ describe 'dhcp::shared_network' do end context 'when passing subnets' do + let (:params) { { + :subnets => ['1.2.3.4', '5.6.7.8'], + } } + + it { should contain_concat__fragment('dhcp-shared-My network').with( + :ensure => 'present', + :target => '/etc/dhcp/dhcpd.conf' + ).with_content( + /shared-network My network \{\n include "\/etc\/dhcp\/subnets\/1\.2\.3\.4\.conf";\n include "\/etc\/dhcp\/subnets\/5\.6\.7\.8\.conf";\n\}/) + } end end diff --git a/templates/shared-network.erb b/templates/shared-network.erb index 407106a..33810b8 100644 --- a/templates/shared-network.erb +++ b/templates/shared-network.erb @@ -7,4 +7,4 @@ shared-network <%= @name %> { include "<%= scope.lookupvar("dhcp::params::config_dir") %>/subnets/<%= subnet %>.conf"; <% end -%> } -#### END <%= name %> +#### END <%= @name %> -- cgit v1.2.3 From eda301b7ba8f8827d1c9973ffaba0c1c7a029d98 Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Fri, 12 Apr 2013 12:29:45 +0200 Subject: Add spec for dhcp::subnet and improve template and definition --- manifests/subnet.pp | 57 +++++---- spec/defines/dhcp_subnet_spec.rb | 252 +++++++++++++++++++++++++++++++++++++++ templates/subnet.conf.erb | 49 ++++---- 3 files changed, 307 insertions(+), 51 deletions(-) create mode 100644 spec/defines/dhcp_subnet_spec.rb diff --git a/manifests/subnet.pp b/manifests/subnet.pp index 18ffdc5..b9826da 100644 --- a/manifests/subnet.pp +++ b/manifests/subnet.pp @@ -27,16 +27,29 @@ # define dhcp::subnet( $broadcast, - $ensure=present, - $netmask=false, - $routers=false, - $subnet_mask=false, - $domain_name=false, - $other_opts=false, - $is_shared=false + $ensure = present, + $netmask = undef, + $routers = [], + $subnet_mask = undef, + $domain_name = undef, + $other_opts = [], + $is_shared = false ) { - include dhcp::params + include ::dhcp::params + + $ip_re = '^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$' + + validate_string($ensure) + validate_re($ensure, ['present', 'absent'], + "\$ensure must be either 'present' or 'absent', got '${ensure}'") + validate_string($broadcast) + validate_re($broadcast, $ip_re) + validate_string($netmask) + validate_array($routers) + validate_string($subnet_mask) + validate_string($domain_name) + validate_bool($is_shared) concat {"${dhcp::params::config_dir}/hosts.d/${name}.conf": owner => root, @@ -48,34 +61,30 @@ define dhcp::subnet( ensure => $ensure, owner => root, group => root, - content => template('dhcp/subnet.conf.erb'), + content => template("${module_name}/subnet.conf.erb"), notify => Service['dhcpd'], } - if ! $is_shared { - concat::fragment {"dhcp.${name}": - ensure => $ensure, - target => "${dhcp::params::config_dir}/dhcpd.conf", - content => "include \"${dhcp::params::config_dir}/subnets/${name}.conf\";\n", - } - } else { - concat::fragment {"dhcp.${name}": - ensure => absent, - target => "${dhcp::params::config_dir}/dhcpd.conf", - content => "include \"${dhcp::params::config_dir}/subnets/${name}.conf\";\n", - } - + $ensure_shared = $is_shared ? { + true => $ensure, + false => 'absent', + } + concat::fragment {"dhcp.subnet.${name}": + ensure => $ensure_shared, + target => "${dhcp::params::config_dir}/dhcpd.conf", + content => "include \"${dhcp::params::config_dir}/subnets/${name}.conf\";\n", } - concat::fragment {"subnet.${name}.hosts": + concat::fragment {"dhcp.subnet.${name}.hosts": ensure => $ensure, target => "${dhcp::params::config_dir}/dhcpd.conf", content => "include \"${dhcp::params::config_dir}/hosts.d/${name}.conf\";\n", } - concat::fragment {"00.dhcp.${name}.base": + concat::fragment {"dhcp.subnet.${name}.base": ensure => $ensure, target => "${dhcp::params::config_dir}/hosts.d/${name}.conf", content => "# File managed by puppet\n", + order => '00', } } diff --git a/spec/defines/dhcp_subnet_spec.rb b/spec/defines/dhcp_subnet_spec.rb new file mode 100644 index 0000000..48ab71b --- /dev/null +++ b/spec/defines/dhcp_subnet_spec.rb @@ -0,0 +1,252 @@ +require 'spec_helper' + +describe 'dhcp::subnet' do + let (:title) { '1.2.3.4' } + let (:facts) { { + :operatingsystem => 'Debian', + :osfamily => 'Debian', + :lsbdistcodename => 'squeeze', + :netmask_eth0 => '255.255.255.0', + :domain => 'example.com', + } } + + context 'when passing wrong value for ensure' do + let (:params) { { + :ensure => 'running', + :broadcast => '1.2.3.4', + } } + + it 'should fail' do + expect { + should contain_concat('/etc/dhcp/hosts.d/1.2.3.4.conf') + }.to raise_error(Puppet::Error, /\$ensure must be either 'present' or 'absent', got 'running'/) + end + end + + context 'when not passing broadcast' do + it 'should fail' do + expect { + should contain_concat('/etc/dhcp/hosts.d/1.2.3.4.conf') + }.to raise_error(Puppet::Error, /Must pass broadcast to Dhcp::Subnet/) + end + end + + context 'when passing wrong type for broadcast' do + let (:params) { { + :broadcast => true, + } } + + it 'should fail' do + expect { + should contain_concat('/etc/dhcp/hosts.d/1.2.3.4.conf') + }.to raise_error(Puppet::Error, /true is not a string\./) + end + end + + context 'when passing wrong value for broadcast' do + let (:params) { { + :broadcast => 'foo', + } } + + it 'should fail' do + expect { + should contain_concat('/etc/dhcp/hosts.d/1.2.3.4.conf') + }.to raise_error(Puppet::Error, /"foo" does not match/) + end + end + + context 'when passing wrong type for netmask' do + let (:params) { { + :broadcast => '1.2.3.4', + :netmask => true, + } } + + it 'should fail' do + expect { + should contain_concat('/etc/dhcp/hosts.d/1.2.3.4.conf') + }.to raise_error(Puppet::Error, /true is not a string\./) + end + end + + context 'when passing wrong value for netmask' do + let (:params) { { + :broadcast => '1.2.3.4', + :netmask => 'foo', + } } + + it 'should fail' do + expect { + should contain_concat('/etc/dhcp/hosts.d/1.2.3.4.conf') + }.to raise_error(Puppet::Error, /"foo" does not match/) + end + end + + context 'when passing wrong type for routers' do + let (:params) { { + :broadcast => '1.2.3.4', + :routers => true, + } } + + it 'should fail' do + expect { + should contain_concat('/etc/dhcp/hosts.d/1.2.3.4.conf') + }.to raise_error(Puppet::Error, /true is not an Array\./) + end + end + + context 'when passing wrong type for subnet_mask' do + let (:params) { { + :broadcast => '1.2.3.4', + :subnet_mask => true, + } } + + it 'should fail' do + expect { + should contain_concat('/etc/dhcp/hosts.d/1.2.3.4.conf') + }.to raise_error(Puppet::Error, /true is not a string\./) + end + end + + context 'when passing wrong type for domain_name' do + let (:params) { { + :broadcast => '1.2.3.4', + :domain_name => true, + } } + + it 'should fail' do + expect { + should contain_concat('/etc/dhcp/hosts.d/1.2.3.4.conf') + }.to raise_error(Puppet::Error, /true is not a string\./) + end + end + + context 'when passing wrong type for is_shared' do + let (:params) { { + :broadcast => '1.2.3.4', + :is_shared => 'foo', + } } + + it 'should fail' do + expect { + should contain_concat('/etc/dhcp/hosts.d/1.2.3.4.conf') + }.to raise_error(Puppet::Error, /"foo" is not a boolean\./) + end + end + + context 'when using defaults' do + let (:params) { { + :broadcast => '1.2.3.4', + } } + + it { should contain_concat('/etc/dhcp/hosts.d/1.2.3.4.conf').with( + :owner => 'root', + :group => 'root', + :mode => '0644' + ) } + + it { should contain_file('/etc/dhcp/subnets/1.2.3.4.conf').with( + :ensure => 'present', + :owner => 'root', + :group => 'root' + ).with_content( + /subnet 1\.2\.3\.4 netmask 255\.255\.255\.0 \{\n/ + ).with_content( + /option routers 255\.255\.255\.0;\n/ + ).with_content( + /option subnet-mask 255\.255\.255\.0;\n/ + ).with_content( + /option broadcast-address 1\.2\.3\.4;\n/ + ).with_content( + /option domain-name "example\.com";\n/ + ) } + + it { should contain_concat__fragment('dhcp.subnet.1.2.3.4').with( + :ensure => 'absent' + ) } + + it { should contain_concat__fragment('dhcp.subnet.1.2.3.4.hosts').with( + :ensure => 'present', + :target => '/etc/dhcp/dhcpd.conf', + :content => "include \"/etc/dhcp/hosts.d/1\.2\.3\.4\.conf\";\n" + ) } + + it { should contain_concat__fragment('dhcp.subnet.1.2.3.4.base').with( + :ensure => 'present', + :target => '/etc/dhcp/hosts.d/1.2.3.4.conf', + :content => "# File managed by puppet\n", + :order => '00' + ) } + end + + context 'when is_shared is true' do + let (:params) { { + :broadcast => '1.2.3.4', + :is_shared => true, + } } + + it { should contain_concat__fragment('dhcp.subnet.1.2.3.4').with( + :ensure => 'present', + :target => '/etc/dhcp/dhcpd.conf', + :content => "include \"/etc/dhcp/subnets/1.2.3.4.conf\";\n" + ) } + end + + context 'when passing other_opts as array' do + let (:params) { { + :broadcast => '1.2.3.4', + :other_opts => ['foo', 'bar'], + } } + + it { should contain_file('/etc/dhcp/subnets/1.2.3.4.conf').with( + :ensure => 'present', + :owner => 'root', + :group => 'root' + ).with_content( + / foo;\n bar;\n/ + ) } + end + + context 'when passing other_opts as string' do + let (:params) { { + :broadcast => '1.2.3.4', + :other_opts => 'bar', + } } + + it { should contain_file('/etc/dhcp/subnets/1.2.3.4.conf').with( + :ensure => 'present', + :owner => 'root', + :group => 'root' + ).with_content( + / bar;\n/ + ) } + end + + context 'when overriding all parameters' do + let (:params) { { + :broadcast => '1.2.3.4', + :netmask => '255.1.2.0', + :routers => ['2.3.4.5', '3.4.5.6'], + :subnet_mask => '255.255.1.0', + :domain_name => 'foo.io', + :other_opts => ['foo', 'bar'], + } } + + it { should contain_file('/etc/dhcp/subnets/1.2.3.4.conf').with( + :ensure => 'present', + :owner => 'root', + :group => 'root' + ).with_content( + /subnet 1\.2\.3\.4 netmask 255\.1\.2\.0 \{\n/ + ).with_content( + /option routers 2\.3\.4\.5,3\.4\.5\.6;\n/ + ).with_content( + /option subnet-mask 255\.255\.1\.0;\n/ + ).with_content( + /option broadcast-address 1\.2\.3\.4;\n/ + ).with_content( + /option domain-name "foo\.io";\n/ + ).with_content( + / foo;\n bar;\n/ + ) } + end +end diff --git a/templates/subnet.conf.erb b/templates/subnet.conf.erb index 89ca82f..11f9ed4 100644 --- a/templates/subnet.conf.erb +++ b/templates/subnet.conf.erb @@ -1,33 +1,28 @@ # File managed by puppet -<% if netmask -%> -subnet <%=name%> netmask <%=netmask%> { -<% else -%> -subnet <%=name%> netmask <%=netmask_eth0%> { -<% end -%> -<% if routers and not routers.empty? -%> - option routers <%= routers.collect! {|i| "#{i}" }.join(",") %>; -<% else -%> - option routers <%=network_eth0%>; -<% end -%> -<% if subnet_mask -%> - option subnet-mask <%=subnet_mask%>; -<% elsif netmask -%> - option subnet-mask <%=netmask%>; -<% else -%> - option subnet-mask <%=netmask_eth0%>; -<% end -%> - option broadcast-address <%=broadcast%>; -<% if domain_name -%> - option domain-name "<%=domain_name%>"; -<% else -%> - option domain-name "<%=domain%>"; -<% end -%> -<% if other_opts and not other_opts.empty? -%> - <% if other_opts.is_a?(Array) -%> -<%= other_opts.collect! {|i| " #{i};"}.join("\n") %> +<%- + _netmask = @netmask || scope.lookupvar('::netmask_eth0') + scope.function_validate_re([_netmask, @ip_re]) -%> +subnet <%= @name %> netmask <%= _netmask %> { +<%- + _routers = @routers unless @routers.empty? + _routers ||= [scope.lookupvar('::netmask_eth0')] + scope.function_validate_array([_routers]) -%> + option routers <%= _routers.collect! {|i| "#{i}" }.join(',') %>; +<%- + _subnet_mask = @subnet_mask || _netmask + scope.function_validate_re([_subnet_mask, @ip_re]) -%> + option subnet-mask <%= _subnet_mask %>; + option broadcast-address <%= @broadcast %>; +<%- + _domain_name = @domain_name || scope.lookupvar('::domain') + scope.function_validate_re([_domain_name, '^\S+$']) -%> + option domain-name "<%= _domain_name %>"; +<% unless @other_opts.empty? -%> + <% if @other_opts.is_a?(Array) -%> +<%= @other_opts.collect! {|i| " #{i};"}.join("\n") %> <% else -%> - <%=other_opts%>; + <%= @other_opts %>; <% end -%> <% end -%> } -- cgit v1.2.3 From 9567c846ddfd93f424b60fad8333d33d1d2f408c Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Fri, 12 Apr 2013 12:35:51 +0200 Subject: Add LICENSE and README.md --- LICENSE | 674 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 37 ++++ 2 files changed, 711 insertions(+) create mode 100644 LICENSE create mode 100644 README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d5848cb --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# DHCP module for Puppet + +**Manages dhcp configuration under Debian.** + +This module is provided by [Camptocamp](http://www.camptocamp.com/) + +## Classes + +## Definitions + + +## Contributing + +Please report bugs and feature request using [GitHub issue +tracker](https://github.com/camptocamp/puppet-dhcp/issues). + +For pull requests, it is very much appreciated to check your Puppet manifest +with [puppet-lint](https://github.com/camptocamp/puppet-dhcp/issues) to follow the recommended Puppet style guidelines from the +[Puppet Labs style guide](http://docs.puppetlabs.com/guides/style_guide.html). + +## License + +Copyright (c) 2013 All rights reserved. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + -- cgit v1.2.3 From b1c6314a83146a88f2d703ec99b6896bcd11c927 Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Fri, 12 Apr 2013 12:37:04 +0200 Subject: README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index d5848cb..7a54b86 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,14 @@ This module is provided by [Camptocamp](http://www.camptocamp.com/) ## Classes +* dhcp +* dhcp::server + ## Definitions +* dhcp::hosts +* dhcp::shared\_network +* dhcp::subnet ## Contributing -- cgit v1.2.3 From 928dbaa62bcc37092ddec36b1f715365576eae6d Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Fri, 12 Apr 2013 14:11:44 +0200 Subject: doc --- README.md | 8 +++++++ manifests/hosts.pp | 38 +++++++++++++++++++++++++------ manifests/init.pp | 22 ++++++++++++++++++ manifests/server.pp | 54 +++++++++++++++++++++++---------------------- manifests/shared_network.pp | 20 ++++++++++++----- manifests/subnet.pp | 34 ++++++++++++++-------------- 6 files changed, 120 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 7a54b86..eb129db 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,14 @@ This module is provided by [Camptocamp](http://www.camptocamp.com/) * dhcp * dhcp::server +### dhcp + +The `dhcp` class is a wrapper around `dhcp::server`: + + include ::dhcp + +### dhcp::server + ## Definitions * dhcp::hosts diff --git a/manifests/hosts.pp b/manifests/hosts.pp index 9e2b284..1767a90 100644 --- a/manifests/hosts.pp +++ b/manifests/hosts.pp @@ -2,13 +2,13 @@ # # Creates a dhcp configuration for given hosts # -# Arguments -# $template: dhcp host template - default: 'dhcp/host.conf.erb' -# $global_options: an array of global options for the whole bunch of hosts. -# you may override it per host, setting the host "options" -# directly in the hash. -# $subnet: targeted subnet -# $hash_data: hash containing data - default form: +# Parameters: +# ['template'] - DHCP host template - default: 'dhcp/host.conf.erb' +# ['global_options'] - An array of global options for the whole bunch of +# hosts. You may override it per host, setting the +# host "options" directly in the hash. +# ['subnet'] - Targeted subnet +# ['hash_data'] - Hash containing data - default form: # { # => { # options => ['opt1', 'opt2'], @@ -33,6 +33,30 @@ # …, # } # +# Sample usage: +# ::dhcp::hosts { 'workstations': +# subnet => '192.168.1.0', +# 'hash_data' => { +# 'host1' => { +# 'interfaces' => { +# 'eth0' => '00:11:22:33:44:55', +# 'wlan0' => '00:aa:bb:44:55:ff', +# }, +# }, +# 'host2' => { +# 'interfaces' => { +# 'eth1' => '00:11:af:33:44:55', +# }, +# 'fixed_address' => 'foo.example.com', +# 'options' => ['opt1'], +# }, +# }, +# } +# +# Requires: +# - puppetlabs/stdlib +# - ripienaar/concat +# define dhcp::hosts ( $hash_data, $subnet, diff --git a/manifests/init.pp b/manifests/init.pp index e1e05b3..9af496f 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -1,3 +1,25 @@ +# Class: dhcp +# +# This class provides a simple way to install a DHCP server +# It will install and configure the necessary packages. +# +# Parameters: +# ['server'] - Whether to install the DHCP server +# (default: true) +# ['server_ddns_update'] - Set ddns_update on dhcp::server +# ['server_authoritative'] - Set authoritative on dhcp::server +# ['server_opts'] - Set opts for dhcp::server +# +# Actions: +# - Deploys a DHCP server +# +# Sample usage: +# include ::dhcp +# +# Requires: +# - puppetlabs/stdlib +# - ripienaar/concat +# class dhcp ( $server = true, $server_ddns_update = undef, diff --git a/manifests/server.pp b/manifests/server.pp index 1c77cf2..a44cfe1 100644 --- a/manifests/server.pp +++ b/manifests/server.pp @@ -1,35 +1,37 @@ -# = Class: dhcp::server -# Simple OS wrapper. Include this to install a dhcp server on your host. +# Class: dhcp::server # -# Requires: -# module "common": git://github.com/camptocamp/puppet-common.git +# Installs and configures a DHCP server. # -# facultative argument: -# *$ddns_update* : ddns-update-style option (defaults to 'none') -# *$authoritative* : a boolean setting whether the DHCP server is -# authoritative (defaults to false) -# *$opts* : an array of DHCPD valid options +# Parameters: +# ['ddns_update'] : ddns-update-style option (defaults to 'none') +# ['authoritative'] : a boolean setting whether the DHCP server is +# authoritative (defaults to false) +# ['opts'] : an array of DHCPD valid options # -# Example: -# node "dhcp.toto.ltd" { -# class { 'dhcp::server': -# opts => ['domain-name "toto.ltd"', -# 'domain-name-servers 192.168.21.1'], -# } +# Sample usage: +# node "dhcp.toto.ltd" { +# class { 'dhcp::server': +# opts => ['domain-name "toto.ltd"', +# 'domain-name-servers 192.168.21.1'], +# } # -# dhcp::subnet {"10.27.20.0": -# ensure => present, -# broadcast => "10.27.20.255", -# other_opts => ['filename "pxelinux.0";', 'next-server 10.27.10.1;'], -# } +# dhcp::subnet {"10.27.20.0": +# ensure => present, +# broadcast => "10.27.20.255", +# other_opts => ['filename "pxelinux.0";', 'next-server 10.27.10.1;'], +# } # -# dhcp::host {"titi-eth0": -# ensure => present, -# mac => "0e:18:fa:fe:d9:00", -# subnet => "10.27.20.0", -# fixed_address => "10.27.10.52", +# dhcp::host {"titi-eth0": +# ensure => present, +# mac => "0e:18:fa:fe:d9:00", +# subnet => "10.27.20.0", +# fixed_address => "10.27.10.52", +# } # } -# } +# +# Requires: +# - puppetlabs/stdlib +# - ripienaar/concat # class dhcp::server ( $ddns_update = 'none', diff --git a/manifests/shared_network.pp b/manifests/shared_network.pp index 38f14fb..766f432 100644 --- a/manifests/shared_network.pp +++ b/manifests/shared_network.pp @@ -1,12 +1,22 @@ -# == Definition: dhcp::shared-network +# Definition: dhcp::shared-network +# # Creates a shared-network # -# Arguments: -# *$subnets* : subnet list to be included in the shared-network +# Parameters: +# ['subnets'] - An array of subnets to be included in the shared-network. +# +# Sample usage: +# ::dhcp::shared_network { 'office': +# subnets => ['192.168.1.0', '192.168.2.0'], +# } +# +# Requires: +# - puppetlabs/stdlib +# - ripienaar/concat # # Warnings: -# - subnets must exists -# - subnets must have $is_shared set to true (default is false) +# - subnets must exists +# - subnets must have $is_shared set to true (default is false) # define dhcp::shared_network( $ensure = present, diff --git a/manifests/subnet.pp b/manifests/subnet.pp index b9826da..b70a233 100644 --- a/manifests/subnet.pp +++ b/manifests/subnet.pp @@ -1,29 +1,27 @@ -# = Definition: dhcp::subnet -# Creates a subnet -# -# Arguments: -# *$broadcast* : subnet broadcast (mandatory) -# *$netmask* : subnet netmask (if not set, takes eth0 netmask) -# *$routers* : subnet routers (array) (if not set, takes eth0 IP) -# *$subnet_mask* : netmask sent to dhcp guests (if not set, takes -# $netmask, or netmask_eth0) -# *$domain_name* : subnet domain name (if not set, takes server domain) -# *$other_opts* : any other DHCPD option, as an array -# *$is_shared* : whether it's part of a shared network or not. Default: false +# Definition: dhcp::subnet # -# Example: +# Creates a subnet # -# node "dhcp.domain.ltd" { -# $dhcpd_domain_name = 'domain.ltd' -# $dhcpd_dns_servers = '10.27.21.1, 10.26.21.1' -# include dhcp +# Parameters: +# ['broadcast'] : subnet broadcast (mandatory) +# ['netmask'] : subnet netmask +# (default: $::netmask_eth0) +# ['routers'] : An array of subnet routers +# (default: $::netmask) +# ['subnet_mask'] : netmask sent to dhcp guests +# (default: the value of $netmask) +# ['domain_name'] : subnet domain name +# (default: $::domain) +# ['other_opts'] : An array of additional DHCPD options +# ['is_shared'] : whether it's part of a shared network or not +# (default: false) # +# Sample usage: # dhcp::subnet {"10.27.20.0": # ensure => present, # broadcast => "10.27.20.255", # other_opts => ['filename "pxelinux.0";', 'next-server 10.27.10.1;'], # } -# } # define dhcp::subnet( $broadcast, -- cgit v1.2.3 From 6fd559e9c99e85426d41358a5ed95c522c8031bc Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Fri, 12 Apr 2013 14:20:38 +0200 Subject: README --- README.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ manifests/hosts.pp | 2 +- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index eb129db..eb8ae5b 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,60 @@ The `dhcp` class is a wrapper around `dhcp::server`: ### dhcp::server +Installs a DHCP server: + + class { 'dhcp::server': + opts => ['domain-name "toto.ltd"', + 'domain-name-servers 192.168.21.1'], + } + ## Definitions * dhcp::hosts * dhcp::shared\_network * dhcp::subnet +### dhcp::hosts + +Creates a DHCP configuration for the given hosts: + + dhcp::hosts { 'workstations': + subnet => '192.168.1.0', + 'hash_data' => { + 'host1' => { + 'interfaces' => { + 'eth0' => '00:11:22:33:44:55', + 'wlan0' => '00:aa:bb:44:55:ff', + }, + }, + 'host2' => { + 'interfaces' => { + 'eth1' => '00:11:af:33:44:55', + }, + 'fixed_address' => 'foo.example.com', + 'options' => ['opt1'], + }, + }, + } + +### dhcp::shared\_network + +Creates a shared-network entry: + + dhcp::shared_network { 'office': + subnets => ['192.168.1.0', '192.168.2.0'], + } + +### dhcp::subnet + +Creates a subnet: + + dhcp::subnet {"10.27.20.0": + ensure => present, + broadcast => "10.27.20.255", + other_opts => ['filename "pxelinux.0";', 'next-server 10.27.10.1;'], + } + ## Contributing Please report bugs and feature request using [GitHub issue diff --git a/manifests/hosts.pp b/manifests/hosts.pp index 1767a90..9c5d1b3 100644 --- a/manifests/hosts.pp +++ b/manifests/hosts.pp @@ -1,6 +1,6 @@ # Definition: dhcp::hosts # -# Creates a dhcp configuration for given hosts +# Creates a dhcp configuration for the given hosts # # Parameters: # ['template'] - DHCP host template - default: 'dhcp/host.conf.erb' -- cgit v1.2.3 From 1ad8359dc57c99c1671d7cb41b961c451fb1c22f Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Fri, 12 Apr 2013 14:27:05 +0200 Subject: capital letters are valid in mac addresses --- spec/defines/dhcp_hosts_spec.rb | 4 ++-- templates/host.conf.erb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/defines/dhcp_hosts_spec.rb b/spec/defines/dhcp_hosts_spec.rb index f5f44ca..8170ed5 100644 --- a/spec/defines/dhcp_hosts_spec.rb +++ b/spec/defines/dhcp_hosts_spec.rb @@ -350,7 +350,7 @@ describe 'dhcp::hosts' do }, 'host2' => { 'interfaces' => { - 'eth1' => '00:11:af:33:44:55', + 'eth1' => '00:11:AF:33:44:55', }, 'fixed_address' => 'foo.example.com', 'options' => ['opt1'], @@ -363,7 +363,7 @@ describe 'dhcp::hosts' do :target => '/etc/dhcp/hosts.d/1.2.3.4.conf').with_content( /host host1-eth0 \{\n hardware ethernet 00:11:22:33:44:55;\n fixed-address host1;\n\}/).with_content( /host host1-wlan0 \{\n hardware ethernet 00:aa:bb:44:55:ff;\n fixed-address host1;\n\}/).with_content( - /host host2-eth1 \{\n hardware ethernet 00:11:af:33:44:55;\n fixed-address foo\.example\.com;\n opt1;\n\}/) + /host host2-eth1 \{\n hardware ethernet 00:11:AF:33:44:55;\n fixed-address foo\.example\.com;\n opt1;\n\}/) } end end diff --git a/templates/host.conf.erb b/templates/host.conf.erb index 1c690b4..c483d62 100644 --- a/templates/host.conf.erb +++ b/templates/host.conf.erb @@ -4,7 +4,7 @@ data.fetch('interfaces').sort.each do |if_name, if_mac| scope.function_validate_re([if_name, '^\S+$']) scope.function_validate_string([if_mac]) - scope.function_validate_re([if_mac, '^[a-f0-9:.]+$']) + scope.function_validate_re([if_mac, '^[A-Fa-f0-9:.]+$']) -%> host <%= host %>-<%= if_name %> { hardware ethernet <%= if_mac %>; -- cgit v1.2.3 From c0f46562b70777b9a4a39429d94c49e869150c64 Mon Sep 17 00:00:00 2001 From: Raphaël Pinson Date: Fri, 12 Apr 2013 15:14:55 +0200 Subject: Fix inverted logic for $is_shared in dhcp::subnet --- manifests/subnet.pp | 4 ++-- spec/defines/dhcp_subnet_spec.rb | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/manifests/subnet.pp b/manifests/subnet.pp index b70a233..e7c72dd 100644 --- a/manifests/subnet.pp +++ b/manifests/subnet.pp @@ -64,8 +64,8 @@ define dhcp::subnet( } $ensure_shared = $is_shared ? { - true => $ensure, - false => 'absent', + true => 'absent', + false => $ensure, } concat::fragment {"dhcp.subnet.${name}": ensure => $ensure_shared, diff --git a/spec/defines/dhcp_subnet_spec.rb b/spec/defines/dhcp_subnet_spec.rb index 48ab71b..b679551 100644 --- a/spec/defines/dhcp_subnet_spec.rb +++ b/spec/defines/dhcp_subnet_spec.rb @@ -161,7 +161,9 @@ describe 'dhcp::subnet' do ) } it { should contain_concat__fragment('dhcp.subnet.1.2.3.4').with( - :ensure => 'absent' + :ensure => 'present', + :target => '/etc/dhcp/dhcpd.conf', + :content => "include \"/etc/dhcp/subnets/1.2.3.4.conf\";\n" ) } it { should contain_concat__fragment('dhcp.subnet.1.2.3.4.hosts').with( @@ -185,9 +187,7 @@ describe 'dhcp::subnet' do } } it { should contain_concat__fragment('dhcp.subnet.1.2.3.4').with( - :ensure => 'present', - :target => '/etc/dhcp/dhcpd.conf', - :content => "include \"/etc/dhcp/subnets/1.2.3.4.conf\";\n" + :ensure => 'absent' ) } end -- cgit v1.2.3