diff --git a/.circleci/config.yml b/.circleci/config.yml
index 8af810ef..93f73246 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -8,3 +8,16 @@ workflows:
- kitchen/danger:
name: danger
context: Danger-Minimal
+ kitchen-v6:
+ jobs:
+ - kitchen/dokken-single:
+ enable-ipv6: true
+ matrix:
+ parameters:
+ suite:
+ - default-centos-6
+ - default-centos-7
+ - default-centos-8
+ - default-fedora-31
+ - default-debian-10
+ - default-ubuntu-1804
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f3b35e97..341684fb 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -40,11 +40,15 @@ jobs:
strategy:
matrix:
os:
- - 'debian-8'
+ - 'debian-10'
+ - 'centos-6'
- 'centos-7'
- - 'ubuntu-1604'
+ - 'centos-8'
+ - 'fedora-31'
+ - 'ubuntu-1804'
suite:
- 'default'
+ - 'delete'
fail-fast: false
steps:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f1e46ecd..c05aab8d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,18 @@
# CHANGELOG
-## Unreleased
-
-- Migrated to github actions
+## 7.0.0 (2020-04-13)
+
+Version 7.0.0 is a **major** change! Please see [UPGRADING.md](./UPGRADING.md).
+
+- DHCPv6 server configuration support - [@bmhughes](https://github.com/bmhughes)
+- Migrated to github actions - [@Xorima](https://github.com/Xorima)
+- Remodel cookbook as resource library - [@bmhughes](https://github.com/bmhughes)
+- Remove - [@bmhughes](https://github.com/bmhughes)
+ - Attributes
+ - Recipes
+ - Data bag functionality
+- Add resources to manage install and services for dhcpd/dhcpd6 - [@bmhughes](https://github.com/bmhughes)
+- Rewrite resources to current standard removing pure ruby code - [@bmhughes](https://github.com/bmhughes)
## 6.1.0 (2019-10-19)
diff --git a/README.md b/README.md
index 2b1d07d2..01708079 100644
--- a/README.md
+++ b/README.md
@@ -6,446 +6,96 @@
[![OpenCollective](https://opencollective.com/sous-chefs/sponsors/badge.svg)](#sponsors)
[![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](https://opensource.org/licenses/Apache-2.0)
-## Description
+Installs and configuration ISC DHCP server for both DHCP and DHCPv6.
-Data bag and Attribute driven DHCP server.
-
-- Supports setting up Master/Master ISC DHCP failover.
+- Supports setting up Master/Master ISC DHCP failover (IPv4 only).
- Includes Support for DDNS
-- Includes LWRPs for managing hosts, groups and subnets.
-- Use databags or attributes + wrapper cookbooks to manage DHCP server [Example](examples/attribute_based.rb)
+- Includes resources for managing hosts, groups and subnets.
-Large parts were borrowed from work initially done by Dell, extended by Atalanta Systems and reworked by Matt Ray and Chef. Big thanks to all of them.
+Version 7.0.0 constitutes a major change and rewrite, please see [UPGRADING.md](./UPGRADING.md).
## Maintainers
This cookbook is maintained by the Sous Chefs. The Sous Chefs are a community of Chef cookbook maintainers working together to maintain important cookbooks. If you’d like to know more please visit [sous-chefs.org](https://sous-chefs.org/) or come chat with us on the Chef Community Slack in [#sous-chefs](https://chefcommunity.slack.com/messages/C2V7B88SF).
-## Requirements
+## Platforms
- Debian / Ubuntu
-- CentOS and derivatives
+- RHEL/CentOS and derivatives
- Fedora and derivatives
-## Chef-client
-
-- 13+
-
-## Limitations
-
-- only set up to handle ipv4
-
-## Recipes
-
-### server
-
-The node will install and configure the `dhcp-server` application. Configuration is through various dhcp_X bags, and the current role/environment
-
-## Node Attributes
-
-Almost every one of the parameters and options can be over-ridden in the host/group/network scope. These are general defaults for the server.
-
-Check out the [man page][1] for details on dhcpd.conf options/params.
-
-attribute | Type | Default | description
-:---------------------------------- | :-------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------
-`node[:dhcp][:use_bags]` | `Boolean` | true | When false we won't attempt to load data from bags.
-`node[:dhcp][:hosts]` | `Array` | `[]` | The list of hosts items that this server should load
-`node[:dhcp][:groups]` | `Array` | `[]` | The list of group items this server should load
-`node[:dhcp][:networks]` | `Array` | `[]` | The list of network items this node should load
-`node[:dhcp][:interfaces]` | `Array` | `[]` | The Network Interface(s) to listen on. If an empty list then we will listen on all interfaces.
-`node[:dhcp][:rndc_keys]` | `Array` | Attribute based representation of rndc keys you want to use.
-`node[:dhcp][:hosts_bag]` | `String` | dhcp_hosts | The name of the data bag that holds host items.
-`node[:dhcp][:groups_bag]` | `String` | dhcp_groups | The name of the data bag that holds group items
-`node[:dhcp][:networks_bag]` | `String` | dhcp_networks | The name of the data bag that holds network items
-`node[:dhcp][:host_data]` | `Hash` | `{}` | Hash of hosts data. The `node[dhcp][:hosts]` entries should have a corresponding entry here when not using bags
-`node[:dhcp][:group_data]` | `Hash` | `{}` | Same as host_data, but for groups
-`node[:dhcp][:network_data]` | `Hash` | `{}` | Same as host_data, but for networks
-`node[:dhcp][:failover]` | `Boolean` | `false` | Enable Failover support buy setting to `true`
-`node[:dhcp][:failover_lease_hack]` | `Boolean` | `false` | Force partner-down state on start by rewriting lease file
-`node[:dhcp][:allows]` | `Array` | ["booting", "bootp", "unknown-clients"] | Global Dhcpd allow entries
-`node[:dhcp][:my_ip]` | `String` | Nil | Set host IP address in failover setup. node[:ipaddress] is used if empty.
-`node[:dhcp][:masters]` | `Array` | Array of hashes to override node search for masters. Must have :ipaddress key.
-`node[:dhcp][:slaves]` | `Array` | Array of hashes to over-ride node search for slaves.
-`node['dhcp']['extra_files']` | `Array` | Array of file paths to also include as configs in `node['dhcp']['config_file']`
-`node['dhcp']['hooks']` | `Hash` | Ability to run scripts when events occur. More info @ and [example](spec/_config_spec.rb)
-
-### DHCP Global Options
-
-attribute | Type | Default | description
-:--------------------------------------------- | :------: | :-------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-`node[:dhcp][:options]['domain-name-servers']` | `String` | "8.8.8.8, 8.8.4.4" | List of DNS servers to send to clients in the global scope
-`node[:dhcp][:options]['host-name']` | `String` | " = binary-to-ascii (16, 8, \"-\", substring (hardware, 1, 6))" | Options for global scope host-name settings. The default here will generate a host as MAC address if the node doesn't provide a hostname or is not defined in dns/hosts/groups.
-`node[:dhcp][:options]['domain-name']` | `String` | "\"#{domain}\"" | Set domainname in the global scope to this nodes domain
-
-### DHCP Global Parameters
-
-These are all just k/v entries in the global params hash. All values are type: `String`
-
-attribute | Type | Default | description
-:------------------------------------------------- | :------: | :--------------: | :----------------------------------------------------------------------------------------------------------
-`node[:dhcp][:parameters]["default-lease-time"]` | `String` | "6400" | Set the default lease time in the global scope
-`node[:dhcp][:parameters]["ddns-domainname"]` | `String` | `"#{domain}"` | Set the DDNS domain to this nodes domain
-`node[:dhcp][:parameters]["ddns-update-style"]` | `String` | "interim" | DDNS Update style
-`node[:dhcp][:parameters]["max-lease-time"]` | `String` | "86400" | Max Lease time
-`node[:dhcp][:parameters]["update-static-leases"]` | `String` | "true" | Make sure we push static IP adresses defined in groups/hosts to the dns server
-`node[:dhcp][:parameters]["one-lease-per-client"]` | `String` | "true" | When a client requests an IP it will release any held leases.
-`node[:dhcp][:parameters]["authoritative"]` | `String` | "" | This setting has no value on purpose: that's how isc-dhcpd wants it.
-`node[:dhcp][:parameters]["ping-check"]` | `String` | "true" | Enable Address collision checking
-`node[:dhcp][:parameters]["next-server"]` | `String` | `"#{ipaddress}"` | Set this server as the global next-server
-`node[:dhcp][:parameters]["filename"]` | `String` | `'"pxelinux.0"'` | For TFTP file name **Note:** the quotes are necessary for this option in the fall-through to dhcpd config.
-
-### Failover parameters
-
-attribute | Type | Default | description
-:--------------------------------------------------- | :------: | :-----: | :--------------------------------------------------------------------
-`node[:dhcp][:failover_params]["mclt"]` | `String` | "3600" | Set Maximum Client Lead Time
-`node[:dhcp][:failover_params]["port"]` | `String` | "647" | TCP port to listen for connections from peer
-`node[:dhcp][:failover_params]["peer_port"]` | `String` | "647" | TCP port to connect to failover peer
-`node[:dhcp][:failover_params][auto_partner_down""]` | `String` | "0" | Timeout in seconds to enter partner-down state if peer is unavailable
-
-### Platform Default Attributes
-
-#### RHEL Platforms
-
-attribute | Type | Default
-:--------------------------- | :------: | :----------------------:
-`node[:dhcp][:dir]` | `String` | `"/etc/dhcpd"`
-`node[:dhcp][:package_name]` | `String` | `"dhcp"`
-`node[:dhcp][:service_name]` | `String` | `"dhcpd"`
-`node[:dhcp][:config_file]` | `String` | `"/etc/dhcp/dhcpd.conf"`
-`node[:dhcp][:init_config]` | `String` | `"/etc/sysconfig/dhcpd"`
-
-#### Debian Platforms
-
-attribute | Type | Default
-:--------------------------- | :------: | :----------------------------:
-`node[:dhcp][:dir]` | `String` | "/etc/dhcpd"
-`node[:dhcp][:package_name]` | `String` | "isc-dhcp-server"
-`node[:dhcp][:service_name]` | `String` | "isc-dhcp-server"
-`node[:dhcp][:config_file]` | `String` | "/etc/dhcp/dhcpd.conf"
-`node[:dhcp][:init_config]` | `String` | "/etc/default/isc-dhcp-server"
-
-## Data Bags
-
-Data bags drive the lion's share of the DHCP configuration, beyond the global settings. It is not required to configure any bags other than dhcp_networks to get up and running but, if you want to statically map a network or have handy Host names provisioned by DHCP, you will have to add either `dhcp_groups/dhcp_hosts` bags and items.
-
-You can generate example bags by using these handy commands
-
-```bash
-knife data bag create dhcp_networks
-knife data bag create dhcp_groups
-knife data bag create dhcp_hosts
-knife data bag from file dhcp_networks examples/data_bags/dhcp_networks
-knife data bag from file dhcp_groups examples/data_bags/dhcp_groups
-knife data bag from file dhcp_hosts examples/data_bags/dhcp_hosts
-```
-
-### dhcp_networks
-
-Looked up via `node[:dhcp][:networks_bag]`. Describes networks this dhcp server should be configured to provide services for. Per-network options can be provided as an array of strings where each string is a DHCP option. Make sure you escape `"`s properly as dhcpd is touchy about the format of values.
-
-```json
-{
- "id": "192-168-1-0_24",
- "routers": [ "192.168.1.1" ],
- "address": "192.168.1.0",
- "netmask": "255.255.255.0",
- "broadcast": "192.168.1.255",
- "range": "192.168.1.50 192.168.1.240",
- "options": [ "time-offset 10" ],
- "next_server": "192.168.1.11"
-}
-```
-
-### dhcp_groups
-
-Looked up via `node[:dhcp][:grougroups]` Items for this bag are group entries as per the [man page][1]. Groups are sets of machines that can be configured with common parameters. A group can be bare, i.e. Contains no host or parameters entries whatsover, and just defines options, if desired.
-
-The only required key in a host def is the `"mac":` key. Everything else is optional
-
-Example Group Bag
-
-```json
-{
- "id": "test",
- "pxe": "ubuntu-precise",
- "parameters": [
- "use-host-decl-names on",
- "max-lease-time 300",
- "default-lease-time 120",
- "next-server \"someplace\""
- ],
- "hosts": {
- "some-vm": {
- "parameters": [ ],
- "mac": "11:22:33:44:55:66",
- "ip": "192.168.1.111"
- },
- "another-host": {
- "mac": "22:33:44:55:66:77"
- }
- }
-}
-```
-
-There are a few keys that merit more discussion:
-
-## `"hosts":` key dhcp_group Items
-
-Groups in isc-dhcp can define lists of hosts. In this example we are using the `use-host-decl-names on` tell dhcp to use the "some-vm" and "another-host" entries as the host-name for these clients, as well as setting other per-group parameters.
-
-Each Host in the hosts Hash can have a number of settings, but the only required setting is `"mac":` the MAC address for the host.
-
-Key | Description
-:-------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------
-`"mac":` | MAC address of this host
-`"ip":` | IP address of this host
-`"parameters":` | an array of isc-dhcpd parameters as-per the global parameter setting.
-`"pxe":` | This key is used by my PXE cookbook to figure out what PXE item you want this group to boot with. That PXE cookbook should be released soon (hopefully).
-
-### dhcp_hosts
-
-DHCP hosts bag is looked up via `node[:dhcp][:hosts_bag]`, and contains Host Items.
-
-Example Most Basic host:
-
-```json
-{
- "id": "vagrant-vm",
- "hostname": "vagrant.vm",
- "mac": "08:00:27:f1:1f:b6",
-}
-```
-
-Example Complex host:
-
-```json
-{
- "id": "pxe_test-vm",
- "hostname": "pxe_test.vm",
- "mac": "08:00:27:8f:7b:db",
- "ip": "192.168.1.31",
- "parameters": [ ],
- "options": [ ],
- "pxe": "ubuntu-precise"
-}
-```
-
-## Resources
-
-### dhcp_host
-
-Manipulate host entries in dhcpd
-
-#### Actions
-
-Action | Description
-:------- | :-------------------------------
-`add` | `_default_` Add this host record
-`remove` | Delete this host record
-
-#### Paramaters
-
-Param | Type | Default
-:----------- | :------- | :------------
-`hostname` | `String`
-`macaddress` | `String`
-`ipaddress` | `String`
-`options` | `Array` | `[]`
-`parameters` | `Array` | `[]`
-`conf_dir` | `String` | `"/etc/dhcp"`
-
-#### Example
-
-Add a Node
-
-```ruby
-dhcp_host "myhost" do
- hostname "somebox.foobar.com"
- macaddress "08:00:27:f1:1f:b6"
- ipaddress "192.168.1.22"
- options [ "domain-name-servers 8.8.8.8" ]
-end
-```
-
-Remove a node
-
-```ruby
-dhcp_host "myhost" do
- action :remove
- hostname "somebox.foobar.com"
-end
-```
-
-If you undefine an entry, it will also get removed.
-
-### dhcp_group
-
-#### Actions
+## Requirements
-Action | Description
-:------- | :-------------------------------
-`add` | `_default_` Add this host record
-`remove` | Delete this host record
+- Chef 14+
-#### Paramaters
+## Usage
-Param | Type | Default | Desciption
-:----------- | :------- | :---------------- | :--------------------------------------------------------------------------------------------------------------
-`name` | `String` | `:name_attribute`
-`hosts` | `Hash` | `{}` | This is a hash of host entries that follow the host-databag format. See the example entry in examples directory
-`parameters` | `Array` | `[]`
-`evals` | `Array` | `[]` | This is an array of multiline strings of eval
-`conf_dir` | `String` | `"/etc/dhcp"` | The directory where the config files are stored
+It is recommended to create a project or organization specific [wrapper cookbook](https://www.chef.io/blog/2013/12/03/doing-wrapper-cookbooks-right/) and add the desired custom resources to the run list of a node.
-#### Example
+Example of a basic server listening on and issuing leases for the subnet `192.0.2.0/24`.
```ruby
-hosts_data = {
- "some-vm"=> {"parameters"=>[], "mac"=>"11:22:33:44:55:66", "ip"=>"192.168.1.111"},
- "another-host"=>{"mac"=>"22:33:44:55:66:77"}}
-}
+dhcp_install 'isc-dhcp-server'
-dhcp_group "some_group" do
- parameters [ "default-lease-time 120", "next-server \"someplace\""]
- hosts hosts_data
+dhcp_service 'dhcpd' do
+ ip_version :ipv4
+ action [:create, :enable, :start]
end
-```
-### dhcp_subnet
-
-Action | Description
-:------- | :-------------------------------
-`add` | `_default_` Add this host record
-`remove` | Delete this host record
-
-#### Paramaters
-
-Param | Type | Default | Desciption
-:------------ | :------- | :---------------- | :------------------------------------------------------------------------------------------------------
-`subnet` | `String` | `:name_attribute` | The network subnet
-`broadcast` | `String` | `nil` | The broadcast address for the subnet
-`netmask` | `String` | `nil` | The netmask address for the subnet
-`routers` | `Array` | `[]` | Gateways for the subnet
-`options` | `Array` | `[]` | DHCP options to set for the subnet
-`pool` | `block` | `nil` | Define a pool block for a `dhcp_subnet`. See 'Parameters for pool' below.
-`next_server` | `String` | `nil` | Next server for TFTP/PXE
-`evals` | `Array` | `[]` | This is an array of multiline strings of eval
-`ddns` | `String` | `nil` | Domain name that will be appended to the client's hostname to form a fully-qualified domain-name (FQDN)
-`key` | `Hash` | `{}` | Shared secret key for DDNS
-`zones` | `Array` | `[]` | _NOTE: Please help update with a good description_
-`conf_dir` | `String` | `"/etc/dhcp"` | Main dhcpd config directory
-
-##### Parameters for pool
-
-Param | Type | Default | Desciption
-:------ | :------------------ | :------ | :----------------------------------------------------------------------------------
-`peer` | `String` | `nil` | Peer server for this segment
-`range` | `String` or `Array` | `[]` | Range of IPs to make available for DHCP in the subnet
-`allow` | `String` or `Array` | `[]` | Only those clients that match any entries on the allow list will be eligible.
-`deny` | `String` or `Array` | `[]` | Only those clients that do not match any entries on the deny list will be eligible.
-
-#### Example
-
-```ruby
-dhcp_subnet "192.168.1.0" do
- pool do
- range ["192.168.1.100 192.168.1.200", "10.33.66.10 10.33.66.100"]
- end
- netmask "255.255.255.0"
- broadcast "192.168.1.255"
- options [ "time-offset 10" ]
- next_server "192.168.1.11"
- routers "192.168.1.1"
- evals [ %q|
- if exists user-class and option user-class = "iPXE" {
- filename "bootstrap.ipxe";
- } else {
- filename "undionly.kpxe";
- }
- | ]
+dhcp_config '/etc/dhcp/dhcpd.conf' do
+ allow %w(booting bootp unknown-clients)
+ parameters(
+ 'default-lease-time' => 7200,
+ 'max-lease-time' => 86400,
+ 'update-static-leases' => true,
+ 'one-lease-per-client' => true,
+ 'authoritative' => '',
+ 'ping-check' => true
+ )
+ options(
+ 'domain-name' => '"test.domain.local"',
+ 'domain-name-servers' => '8.8.8.8, 8.8.4.4'
+ )
+ action :create
end
-```
-
-### dhcp_shared_network
-
-Action | Description
-:------- | :----------------------------------
-`add` | `_default_` Add this shared network
-`remove` | Delete this shared network record
-
-#### Paramaters
-
-Param | Type | Default | Desciption
-:------- | :------------ | :------ | :---------------------------------------------------------------------------
-`subnet` | `dhcp_subnet` | `nil` | The network subnet to define inside the shared network. Can define multiples.
-### Example
-
-```ruby
-dhcp_shared_network 'mysharednet' do
- subnet '192.168.1.0' do
- pool do
- range ['192.168.1.100 192.168.1.200', '10.33.66.10 10.33.66.100']
- end
- netmask '255.255.255.0'
- broadcast '192.168.1.255'
- next_server '192.168.1.11'
- routers '192.168.1.1'
- evals [ %q|
- if exists user-class and option user-class = "iPXE" {
- filename "bootstrap.ipxe";
- } else {
- filename "undionly.kpxe";
- }
- | ]
- end
- subnet '10.0.2.0' do
- broadcast '10.0.2.254'
- netmask '255.255.255.0'
- pool do
- range ['10.0.2.50 10.0.2.240']
- end
- end
+dhcp_subnet '192.0.2.0' do
+ comment 'Basic Subnet Declaration'
+ subnet '192.0.2.0'
+ netmask '255.255.255.0'
+ options [
+ 'routers 192.168.1.1',
+ ]
+ pool(
+ 'peer' => '192.168.0.2',
+ 'range' => '192.168.1.100 192.168.1.200'
+ )
+ parameters(
+ 'ddns-domainname' => '"test.domain"'
+ )
end
```
-### dhcp_class
-
-Manage DHCP classes
-
-#### Actions
-
-Action | Description
-:----- | :-------------------------------
-`add` | `_default_` Add this host record
-
-#### Paramaters
+## External Documentation
-Param | Type | Default
-:----------- | :------- | :------
-`match` | `String` |
-`subclass` | `String` |
-`subclasses` | `Array` | `[]`
+-
+-
+-
-#### Example
+## Examples
-Add a Node
+Please check for more varied working examples in [TEST](./test/cookbooks/test/)
-```ruby
-dhcp_class 'ignorehosts' do
- match 'hardware'
- subclass '1:10:bf:48:42:55:01'
- subclass '1:10:bf:48:42:55:02'
-end
-```
-
-If you undefine an entry it will also get removed.
-
-## Testing
+## Resources
-Testing uses Chef Workstation
+- [dhcp_class](documentation/dhcp_class.md)
+- [dhcp_config](documentation/dhcp_config.md)
+- [dhcp_group](documentation/dhcp_group.md)
+- [dhcp_host](documentation/dhcp_host.md)
+- [dhcp_package](documentation/dhcp_package.md)
+- [dhcp_service](documentation/dhcp_service.md)
+- [dhcp_shared_network](documentation/dhcp_shared_network.md)
+- [dhcp_subnet](documentation/dhcp_subnet.md)
## Contributors
diff --git a/UPGRADING.md b/UPGRADING.md
new file mode 100644
index 00000000..9d47bb5f
--- /dev/null
+++ b/UPGRADING.md
@@ -0,0 +1,65 @@
+# Upgrading
+
+This document will give you help on upgrading major versions of dhcp
+
+## 7.0.0
+
+Version 7.0.0 is a major rewrite of the cookbook to current standards, remodelling as a resource library cookbook and the addition of DHCPv6 support.
+
+### Removed
+
+- All attributes
+- All recipes
+ - Enumeration of resources from data bags with this cookbook is not longer supported, you will have to write your own wrapper cookbook if you
+ depend on this functionality.
+- Resource `dhcp_pool`
+ - Pool verification is now provided by the `dhcp_subnet` resource
+
+### Added
+
+- Resource `dhcp_config` - [Documentation](./documentation/dhcp_config.md)
+ - Replacement for the `_config` recipe
+ - Global configuration
+ - DHCP failover configuration
+ - Configuration of service environment file
+
+- Resource `dhcp_package` - [Documentation](./documentation/dhcp_package.md)
+ - Replacement for the `_package` recipe
+ - Installs the relevant packages for platform
+
+- Resource `dhcp_service` - [Documentation](./documentation/dhcp_service.md)
+ - Replacement for the `_service` recipe
+ - Creates a systemd unit file for the platform
+ - Controls service activation
+
+### Changed
+
+- Common
+ - Custom resources have been rewritten in current style
+ - Relevant resources have `options` and `parameters` properties to allow more flexible user defined configuration
+ - Template files are overrideable with `cookbook` and `template` parameters to allow user template selection
+ - `owner`, `group` and `mode` properties to control the resultant permissions and mode of the generated files
+ - DHCPv6 support for all resources, use `ip_version` to select
+
+- Resource `dhcp_class` - [Documentation](./documentation/dhcp_class.md)
+ - `:delete` action added
+ - `subclasses` are now specified via `Hash` rather than a `Block`
+
+- Resource `dhcp_group` - [Documentation](./documentation/dhcp_group.md)
+ - `:delete` action added
+
+- Resource `dhcp_host` - [Documentation](./documentation/dhcp_host.md)
+ - `:delete` action added
+ - `hostname` property removed, specify it via `options`
+ - `macaddress` property is renamed to `identifier`
+ - `ipaddress` property is renamed to `address`
+
+- Resource `dhcp_shared_network` - [Documentation](./documentation/dhcp_shared_network.md)
+ - `:delete` action added
+ - Subnets are provided via included nested `dhcp_subnet` resources
+ - `subnets` are now specified via `Hash` rather than a `Block`
+
+- Resource `dhcp_subnet` - [Documentation](./documentation/dhcp_subnet.md)
+ - `:delete` action added
+ - `broadcast`, `routers`, `ddns` and `next-server` properties have been removed, specify them via `options` and `parameters`
+ - `pools` are now specified via `Hash` rather than a `Block`
diff --git a/attributes/default.rb b/attributes/default.rb
deleted file mode 100644
index df63006c..00000000
--- a/attributes/default.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-default['dhcp']['failover'] = nil
-default['dhcp']['failover_lease_hack'] = false
-default['dhcp']['failover_params']['mclt'] = 3600
-default['dhcp']['failover_params']['port'] = 647
-default['dhcp']['failover_params']['peer_port'] = 647
-default['dhcp']['failover_params']['auto_partner_down'] = 0
-
-default['dhcp']['allows'] = %w(booting bootp unknown-clients)
-
-# these are the arrays that dispatch to bags or attributes for actual data
-default['dhcp']['hosts'] = []
-default['dhcp']['groups'] = []
-default['dhcp']['networks'] = []
-default['dhcp']['shared_networks'] = []
-default['dhcp']['interfaces'] = []
-
-# turn bag support on/off
-default['dhcp']['use_bags'] = true
-
-# extra config files
-default['dhcp']['extra_files'] = []
-
-# these are the lookup keys for bag names
-default['dhcp']['hosts_bag'] = 'dhcp_hosts'
-default['dhcp']['groups_bag'] = 'dhcp_groups'
-default['dhcp']['networks_bag'] = 'dhcp_networks'
-
-# these are the attribute keys used for records when not using bags
-default['dhcp']['host_data'] = {}
-default['dhcp']['group_data'] = {}
-default['dhcp']['network_data'] = {}
-default['dhcp']['shared_network_data'] = {}
-
-default['domain'] = 'local'
-
-default['dhcp']['parameters']['default-lease-time'] = '6400'
-default['dhcp']['parameters']['ddns-domainname'] = "\"#{node['domain']}\""
-default['dhcp']['parameters']['ddns-update-style'] = 'interim'
-default['dhcp']['parameters']['max-lease-time'] = '86400'
-default['dhcp']['parameters']['update-static-leases'] = 'true'
-default['dhcp']['parameters']['one-lease-per-client'] = 'true'
-default['dhcp']['parameters']['authoritative'] = ''
-default['dhcp']['parameters']['ping-check'] = 'true'
-default['dhcp']['parameters']['next-server'] = node['ipaddress']
-default['dhcp']['parameters']['filename'] = '"pxelinux.0"'
-
-default['dhcp']['options']['domain-name'] = "\"#{node['domain']}\""
-default['dhcp']['options']['domain-name-servers'] = '8.8.8.8'
-default['dhcp']['options']['host-name'] = ' = binary-to-ascii (16, 8, "-", substring (hardware, 1, 6))'
-
-default['dhcp']['dir'] = '/etc/dhcp'
-default['dhcp']['init_config'] = '/etc/sysconfig/dhcpd'
-default['dhcp']['dhcpd_leases'] = '/var/lib/dhcpd/dhcpd.leases'
-default['dhcp']['config_file'] = '/etc/dhcp/dhcpd.conf'
-
-case node['platform_family']
-when 'rhel', 'fedora'
- default['dhcp']['package_name'] = 'dhcp'
- default['dhcp']['service_name'] = 'dhcpd'
- default['dhcp']['init_config'] = '/etc/sysconfig/dhcpd'
- default['dhcp']['init_iface'] = 'DHCPDARGS'
- default['dhcp']['dhcpd_leases'] = '/var/lib/dhcpd/dhcpd.leases'
-
-when 'debian'
- default['dhcp']['package_name'] = 'isc-dhcp-server'
- default['dhcp']['service_name'] = 'isc-dhcp-server'
-
- default['dhcp']['init_config'] = '/etc/default/isc-dhcp-server'
- default['dhcp']['dhcpd_leases'] = '/var/lib/dhcp/dhcpd.leases'
- default['dhcp']['init_iface'] = 'INTERFACES'
-end
-
-default['dhcp']['hooks'] = {}
diff --git a/attributes/dns.rb b/attributes/dns.rb
deleted file mode 100644
index 629e784b..00000000
--- a/attributes/dns.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-default['dns']['master'] = ''
-default['dns']['zones'] = []
-default['dns']['rndc_key'] = ''
diff --git a/documentation/dhcp_class.md b/documentation/dhcp_class.md
new file mode 100644
index 00000000..7443df44
--- /dev/null
+++ b/documentation/dhcp_class.md
@@ -0,0 +1,60 @@
+# dhcp_class
+
+[Back to resource list](../README.md#resources)
+
+Create a DHCPD class configuration. ()
+
+Introduced: v7.0.0
+
+## Actions
+
+- `:create`
+- `:delete`
+
+## Properties
+
+| Name | Type | Default | Description | Allowed Values |
+| ---------------------- | ------------- | -------------------------------- | ------------------------------------------------------------------- | ------------------- |
+| `comment` | String | `nil` | Comment to add to the configuration file | |
+| `ip_version` | Symbol | `:ipv4` | Select DHCP or DHCPv6 server to configure | `:ipv4`, `:ipv6` |
+| `conf_dir` | String | `/etc/dhcp/dhcpd(6).d/classes.d` | Directory to create configuration file in | |
+| `cookbook` | String | `/etc/dhcp/dhcpd(6).d/classes.d` | Cookbook to source configuration file template from | |
+| `template` | String | `/etc/dhcp/dhcpd(6).d/classes.d` | Template to use to generate the configuration file | |
+| `owner` | String | Platform dependant | Owner of the generated configuration file | |
+| `group` | String | Platform dependant | Group of the generated configuration file | |
+| `mode` | String | `'0640'` | Filemode of the generated configuration file | |
+| `match` | String | `nil` | DHCPD match statement | |
+| `options` | Array, Hash | `nil` | DHCPD options for the class | |
+| `parameters` | Array, Hash | `nil` | DHCPD parameters for the class | |
+| `subclass` | Array | `nil` | Subclasses to include within the class | |
+
+## Examples
+
+```ruby
+dhcp_class 'BlankClass' do
+ match 'hardware'
+end
+```
+
+```ruby
+dhcp_class 'RegisteredHosts' do
+ match 'hardware'
+ subclass [
+ '1:10:bf:48:42:55:01',
+ '1:10:bf:48:42:55:02',
+ ]
+end
+```
+
+```ruby
+dhcp_class 'SpecialHosts' do
+ match 'hardware'
+ subclass [
+ '1:10:bf:48:42:56:01',
+ '1:10:bf:48:42:56:02',
+ ]
+ option(
+ 'special-option' => 'value'
+ )
+end
+```
diff --git a/documentation/dhcp_config.md b/documentation/dhcp_config.md
new file mode 100644
index 00000000..49152128
--- /dev/null
+++ b/documentation/dhcp_config.md
@@ -0,0 +1,94 @@
+# dhcp_config
+
+[Back to resource list](../README.md#resources)
+
+Create and manage the main DHCPD configuration. ()
+
+Introduced: v7.0.0
+
+## Actions
+
+- `:create`
+- `:delete`
+
+## Properties
+
+| Name | Type | Default | Description | Allowed Values |
+| ---------------------- | ------------- | -------------------------------- | ------------------------------------------------------------------- | ------------------- |
+| `comment` | String | `nil` | Comment to add to the configuration file | |
+| `ip_version` | Symbol | `:ipv4` | Select DHCP or DHCPv6 server to configure | `:ipv4`, `:ipv6` |
+| `conf_dir` | String | `/etc/dhcp/dhcpd(6).d/classes.d` | Directory to create configuration file in | |
+| `cookbook` | String | `/etc/dhcp/dhcpd(6).d/classes.d` | Cookbook to source configuration file template from | |
+| `template` | String | `/etc/dhcp/dhcpd(6).d/classes.d` | Template to use to generate the configuration file | |
+| `owner` | String | Platform dependant | Owner of the generated configuration file | |
+| `group` | String | Platform dependant | Group of the generated configuration file | |
+| `mode` | String | `'0640'` | Filemode of the generated configuration file | |
+| `config_failover_file` | String | `/etc/dhcp/dhcpd.failover.conf` | DHCP failover configuration file path | |
+| `config_includes_directory` | String | `/etc/dhcp/dhcpd(6).d` | Directory to create included configuration files in | |
+| `lib_dir` | String | Platform dependant | DHCPD lib directory path | |
+| `lease_file` | String | Platform dependant | DHCPD lease file path | |
+| `allow` | Array | `nil` | | |
+| `deny` | Array | `nil` | | |
+| `ignore` | Array | `nil` | | |
+| `parameters` | Array, Hash | `nil` | DHCPD global parameters | |
+| `options` | Array, Hash | `nil` | DHCPD global options | |
+| `evals` | Array | `nil` | DHCPD conditional statements (see dhcp-eval(5)) | |
+| `keys` | Hash | `nil` | TSIG keys configuration | |
+| `zones` | Hash | `nil` | Dynamic DNS zone configuration | |
+| `hooks` | Hash | `nil` | Server event action configuration | |
+| `failover` | Hash | `nil` | DHCP failover configuration | |
+| `include_files` | Array | `nil` | Additional configuration files to include | |
+| `extra_lines` | String, Array | `nil` | Extra lines to append to the configuration file | |
+
+## Examples
+
+```ruby
+dhcp_config '/etc/dhcp/dhcpd.conf' do
+ allow %w(booting bootp unknown-clients)
+ parameters(
+ 'default-lease-time' => 7200,
+ 'ddns-update-style' => 'interim',
+ 'max-lease-time' => 86400,
+ 'update-static-leases' => true,
+ 'one-lease-per-client' => true,
+ 'authoritative' => '',
+ 'ping-check' => true
+ )
+ options(
+ 'domain-name' => '"test.domain.local"',
+ 'domain-name-servers' => '8.8.8.8',
+ 'host-name' => ' = binary-to-ascii (16, 8, "-", substring (hardware, 1, 6))'
+ )
+ hooks(
+ 'commit' => ['use-host-decl-names on'],
+ 'release' => ['use-host-decl-names on']
+ )
+ include_files [
+ '/etc/dhcp/extra1.conf',
+ '/etc/dhcp/extra2.conf',
+ '/etc/dhcp_override/list.conf',
+ ]
+ action :create
+end
+```
+
+```ruby
+dhcp_config '/etc/dhcp/dhcpd6.conf' do
+ ip_version :ipv6
+ deny %w(duplicates)
+ parameters(
+ 'default-lease-time' => 7200,
+ 'ddns-updates' => 'on',
+ 'ddns-update-style' => 'interim',
+ 'max-lease-time' => 86400,
+ 'update-static-leases' => true,
+ 'one-lease-per-client' => 'on',
+ 'authoritative' => '',
+ 'ping-check' => true
+ )
+ options(
+ 'dhcp6.name-servers' => '2001:4860:4860::8888, 2001:4860:4860::8844'
+ )
+ action :create
+end
+```
diff --git a/documentation/dhcp_group.md b/documentation/dhcp_group.md
new file mode 100644
index 00000000..79e3d28b
--- /dev/null
+++ b/documentation/dhcp_group.md
@@ -0,0 +1,39 @@
+# dhcp_group
+
+[Back to resource list](../README.md#resources)
+
+Create a DHCPD group configuration. ( - *The **group** statement*)
+
+Introduced: v7.0.0
+
+## Actions
+
+- `:create`
+- `:delete`
+
+## Properties
+
+| Name | Type | Default | Description | Allowed Values |
+| ---------------------- | ------------- | -------------------------------- | ------------------------------------------------------------------- | ------------------- |
+| `comment` | String | `nil` | Comment to add to the configuration file | |
+| `ip_version` | Symbol | `:ipv4` | Select DHCP or DHCPv6 server to configure | `:ipv4`, `:ipv6` |
+| `conf_dir` | String | `/etc/dhcp/dhcpd(6).d/groups.d` | Directory to create configuration file in | |
+| `cookbook` | String | `/etc/dhcp/dhcpd(6).d/groups.d` | Cookbook to source configuration file template from | |
+| `template` | String | `/etc/dhcp/dhcpd(6).d/groups.d` | Template to use to generate the configuration file | |
+| `owner` | String | Platform dependant | Owner of the generated configuration file | |
+| `group` | String | Platform dependant | Group of the generated configuration file | |
+| `mode` | String | `'0640'` | Filemode of the generated configuration file | |
+| `parameters` | Array, Hash | `nil` | DHCPD parameters for the group | |
+| `options` | Array, Hash | `nil` | DHCPD options for the group | |
+| `evals` | Array | `nil` | DHCPD conditional statements for the group (see dhcp-eval(5)) | |
+| `hosts` | Hash | `nil` | Hosts configuration to include within the group | |
+
+## Examples
+
+```ruby
+dhcp_group 'IPPgones' do
+ options(
+ 'tftp-server-name' => '192.0.2.10'
+ )
+end
+```
diff --git a/documentation/dhcp_host.md b/documentation/dhcp_host.md
new file mode 100644
index 00000000..55cecabb
--- /dev/null
+++ b/documentation/dhcp_host.md
@@ -0,0 +1,52 @@
+# dhcp_host
+
+[Back to resource list](../README.md#resources)
+
+Create a DHCPD host configuration. ( - *The **host** statement*)
+
+Introduced: v7.0.0
+
+## Actions
+
+- `:create`
+- `:delete`
+
+## Properties
+
+| Name | Type | Default | Description | Allowed Values |
+| ---------------------- | ------------- | -------------------------------- | ------------------------------------------------------------------- | ------------------- |
+| `comment` | String | `nil` | Comment to add to the configuration file | |
+| `ip_version` | Symbol | `:ipv4` | Select DHCP or DHCPv6 server to configure | `:ipv4`, `:ipv6` |
+| `conf_dir` | String | `/etc/dhcp/dhcpd(6).d/hosts.d` | Directory to create configuration file in | |
+| `cookbook` | String | `/etc/dhcp/dhcpd(6).d/hosts.d` | Cookbook to source configuration file template from | |
+| `template` | String | `/etc/dhcp/dhcpd(6).d/hosts.d` | Template to use to generate the configuration file | |
+| `owner` | String | Platform dependant | Owner of the generated configuration file | |
+| `group` | String | Platform dependant | Group of the generated configuration file | |
+| `mode` | String | `'0640'` | Filemode of the generated configuration file | |
+| `identifier` | String | `nil` | DHCPD host identifier (MAC or DHCID) | |
+| `address` | String | `nil` | DHCPD address to issue | |
+| `parameters` | Array, Hash | `nil` | DHCPD parameters for the host | |
+| `options` | Array, Hash | `nil` | DHCPD options for the host | |
+
+## Examples
+
+```ruby
+dhcp_host 'IPv4-Host' do
+ identifier 'hardware ethernet 00:53:00:00:00:01'
+ address '192.168.0.10'
+ options(
+ 'host-name' => 'test-ipv4-host'
+ )
+end
+```
+
+```ruby
+dhcp_host 'IPv6-Host' do
+ ip_version :ipv6
+ identifier 'host-identifier option dhcp6.client-id 00:53:00:00:00:01:a4:65:b7:c8'
+ address '2001:db8:1:1:0:0:1:10'
+ options(
+ 'host-name' => 'test-ipv6-host'
+ )
+end
+```
diff --git a/documentation/dhcp_package.md b/documentation/dhcp_package.md
new file mode 100644
index 00000000..d3524555
--- /dev/null
+++ b/documentation/dhcp_package.md
@@ -0,0 +1,30 @@
+# dhcp_package
+
+[Back to resource list](../README.md#resources)
+
+Install ISC DHCPD server from package.
+
+Introduced: v7.0.0
+
+## Actions
+
+- `:install`
+- `:remove`
+
+## Properties
+
+| Name | Type | Default | Description | Allowed Values |
+| ---------------------- | ------------- | ----------------------------- | ---------------------------------------------------------------------- | ------------------- |
+| `packages` | String, Array | Correct packages for platform | List of packages to install for server operation | |
+
+## Examples
+
+```ruby
+dhcp_package
+```
+
+```ruby
+dhcp_package 'dhcpd' do
+ packages 'isc-dhcp-server'
+ end
+```
diff --git a/documentation/dhcp_service.md b/documentation/dhcp_service.md
new file mode 100644
index 00000000..829c77e4
--- /dev/null
+++ b/documentation/dhcp_service.md
@@ -0,0 +1,41 @@
+# dhcp_service
+
+[Back to resource list](../README.md#resources)
+
+Manage the DHCPD and DHCPD6 services.
+
+Introduced: v7.0.0
+
+## Actions
+
+- `:create`
+- `:delete`
+- `:start`
+- `:stop`
+- `:reload`
+- `:enable`
+- `:disable`
+
+## Properties
+
+| Name | Type | Default | Description | Allowed Values |
+| ---------------------- | ------------- | ----------------------------- | ---------------------------------------------------------------------- | ------------------- |
+| `ip_version` | Symbol | `:ipv4` | Select DHCP or DHCPv6 server to configure | `:ipv4`, `:ipv6` |
+| `service_name` | String | `nil` | Custom service name | |
+| `systemd_unit_content` | String, Hash | Platform dependant | systemd unit file content for service | |
+
+## Examples
+
+```ruby
+dhcp_service 'dhcpd' do
+ ip_version :ipv4
+ action [:create, :enable, :start]
+end
+```
+
+```ruby
+dhcp_service 'dhcpd6' do
+ ip_version :ipv6
+ action [:create, :enable, :start]
+end
+```
diff --git a/documentation/dhcp_shared_network.md b/documentation/dhcp_shared_network.md
new file mode 100644
index 00000000..0591f11f
--- /dev/null
+++ b/documentation/dhcp_shared_network.md
@@ -0,0 +1,78 @@
+# dhcp_shared_network
+
+[Back to resource list](../README.md#resources)
+
+Create a DHCPD shared network configuration. ( - *The **shared-network** statement*)
+
+Introduced: v7.0.0
+
+## Actions
+
+- `:create`
+- `:delete`
+
+## Properties
+
+| Name | Type | Default | Description | Allowed Values |
+| ---------------------- | ------------- | -------------------------------- | ------------------------------------------------------------------- | ------------------- |
+| `comment` | String | `nil` | Comment to add to the configuration file | |
+| `ip_version` | Symbol | `:ipv4` | Select DHCP or DHCPv6 server to configure | `:ipv4`, `:ipv6` |
+| `conf_dir` | String | `/etc/dhcp/dhcpd(6).d/hosts.d` | Directory to create configuration file in | |
+| `cookbook` | String | `/etc/dhcp/dhcpd(6).d/hosts.d` | Cookbook to source configuration file template from | |
+| `template` | String | `/etc/dhcp/dhcpd(6).d/hosts.d` | Template to use to generate the configuration file | |
+| `owner` | String | Platform dependant | Owner of the generated configuration file | |
+| `group` | String | Platform dependant | Group of the generated configuration file | |
+| `mode` | String | `'0640'` | Filemode of the generated configuration file | |
+| `subnets` | Hash | `nil` | Subnets for the DHCPD shared network | |
+
+## Examples
+
+```ruby
+dhcp_shared_network 'single' do
+ subnets(
+ '192.168.1.0' => {
+ 'subnet' => '192.168.1.0',
+ 'netmask' => '255.255.255.0',
+ 'options' => {
+ 'broadcast-address' => '192.168.1.255',
+ 'routers' => '192.168.1.1',
+ },
+ 'pool' => {
+ 'range' => '192.168.1.20 192.168.1.30',
+ },
+ }
+ )
+end
+```
+
+```ruby
+dhcp_shared_network 'multiple' do
+ subnets(
+ '192.168.2.0' => {
+ 'subnet' => '192.168.2.0',
+ 'netmask' => '255.255.255.0',
+ 'options' => {
+ 'broadcast-address' => '192.168.2.255',
+ 'routers' => '192.168.2.1',
+ },
+ 'pool' => {
+ 'range' => '192.168.2.20 192.168.2.30',
+ },
+ },
+ '192.168.3.0' => {
+ 'subnet' => '192.168.3.0',
+ 'netmask' => '255.255.255.0',
+ 'options' => {
+ 'broadcast-address' => '192.168.3.255',
+ 'routers' => '192.168.3.1',
+ },
+ 'pool' => {
+ 'range' => [
+ '192.168.3.20 192.168.3.30',
+ '192.168.3.40 192.168.3.50',
+ ],
+ },
+ }
+ )
+end
+```
diff --git a/documentation/dhcp_subnet.md b/documentation/dhcp_subnet.md
new file mode 100644
index 00000000..bd5ca0e5
--- /dev/null
+++ b/documentation/dhcp_subnet.md
@@ -0,0 +1,88 @@
+# dhcp_subnet
+
+[Back to resource list](../README.md#resources)
+
+Create and manage the main DHCPD configuration. ()
+
+Introduced: v7.0.0
+
+## Actions
+
+- `:create`
+- `:delete`
+
+## Properties
+
+| Name | Type | Default | Description | Allowed Values |
+| ---------------------- | ------------- | -------------------------------- | ------------------------------------------------------------------- | ------------------- |
+| `comment` | String | `nil` | Comment to add to the configuration file | |
+| `ip_version` | Symbol | `:ipv4` | Select DHCP or DHCPv6 server to configure | `:ipv4`, `:ipv6` |
+| `conf_dir` | String | `/etc/dhcp/dhcpd(6).d/classes.d` | Directory to create configuration file in | |
+| `cookbook` | String | `/etc/dhcp/dhcpd(6).d/classes.d` | Cookbook to source configuration file template from | |
+| `template` | String | `/etc/dhcp/dhcpd(6).d/classes.d` | Template to use to generate the configuration file | |
+| `owner` | String | Platform dependant | Owner of the generated configuration file | |
+| `group` | String | Platform dependant | Group of the generated configuration file | |
+| `mode` | String | `'0640'` | Filemode of the generated configuration file | |
+| `shared_network` | True, False | `false` | DHCP failover configuration file path | |
+| `subnet` | String | `nil` | Subnet address | |
+| `netmask` | String | `nil` | Subnet network for IPv4 subnets | |
+| `prefix` | String | `nil` | Subnet prefix for IPv6 subnets | |
+| `subnet` | String | `nil` | Subnet address | |
+| `subnet` | String | `nil` | Subnet address | |
+| `parameters` | Array, Hash | `nil` | DHCPD parameters for the subnet | |
+| `options` | Array, Hash | `nil` | DHCPD options for the subnet | |
+| `evals` | Array | `nil` | DHCPD conditional statements for the subnet (see dhcp-eval(5)) | |
+| `keys` | Hash | `nil` | TSIG keys configuration | |
+| `zones` | Hash | `nil` | Dynamic DNS zone configuration | |
+| `allow` | Array | `nil` | | |
+| `deny` | Array | `nil` | | |
+| `extra_lines` | String, Array | `nil` | Extra lines to append to the configuration file | |
+| `pools` | Hash, Array | `nil` | Pool configuration hash(es), accepts most properties (see dhcpd.conf(5))| |
+
+## Examples
+
+```ruby
+dhcp_subnet '192.168.9.0' do
+ comment 'Listen Subnet Declaration'
+ subnet '192.168.9.0'
+ netmask '255.255.255.0'
+end
+
+dhcp_subnet 'basic' do
+ comment 'Basic Subnet Declaration'
+ subnet '192.168.0.0'
+ netmask '255.255.255.0'
+ options [
+ 'routers 192.168.0.1'
+ 'time-offset 10',
+ ]
+ pool 'range' => '192.168.0.100 192.168.0.200'
+end
+```
+
+```ruby
+dhcp_subnet 'dhcpv6_listen' do
+ ip_version :ipv6
+ comment 'Testing DHCPv6 Basic Subnet'
+ subnet '2001:db8:1::'
+ prefix 64
+end
+
+dhcp_subnet 'dhcpv6_basic' do
+ ip_version :ipv6
+ comment 'Testing DHCPv6 Basic Subnet'
+ subnet '2001:db8:2:1::'
+ prefix 64
+ options(
+ 'domain-name' => '"test.domain.local"',
+ 'dhcp6.name-servers' => '2001:4860:4860::8888, 2001:4860:4860::8844'
+ )
+ parameters(
+ 'ddns-domainname' => '"test.domain.local"',
+ 'default-lease-time' => 28800
+ )
+ range [
+ '2001:db8:2:1::1:0/112',
+ ]
+end
+```
diff --git a/kitchen.dokken.yml b/kitchen.dokken.yml
index db9f7ce1..5c3a22f5 100644
--- a/kitchen.dokken.yml
+++ b/kitchen.dokken.yml
@@ -1,10 +1,8 @@
---
driver:
name: dokken
- # because Docker and SystemD/Upstart
privileged: true
- chef_version: <%= ENV['CHEF_VERSION'] || 'current' %>
- env: [CHEF_LICENSE=accept]
+ chef_version: <%= ENV['CHEF_VERSION'] || 'latest' %>
transport:
name: dokken
@@ -13,28 +11,53 @@ provisioner:
name: dokken
platforms:
+ - name: centos-6
+ driver:
+ image: dokken/centos-6
+ pid_one_command: /sbin/init
+ - name: centos-7
+ driver:
+ image: dokken/centos-7
+ pid_one_command: /usr/lib/systemd/systemd
+ - name: centos-8
+ driver:
+ image: dokken/centos-8
+ pid_one_command: /usr/lib/systemd/systemd
+ - name: fedora-31
+ driver:
+ image: dokken/fedora-31
+ pid_one_command: /usr/lib/systemd/systemd
+ - name: amazonlinux-2
+ driver:
+ image: dokken/amazonlinux-2
+ pid_one_command: /usr/lib/systemd/systemd
- name: debian-8
driver:
image: dokken/debian-8
pid_one_command: /bin/systemd
intermediate_instructions:
- RUN /usr/bin/apt-get update
-
- - name: centos-7
+ - name: debian-10
driver:
- image: dokken/centos-7
- pid_one_command: /usr/lib/systemd/systemd
-
+ image: dokken/debian-10
+ pid_one_command: /bin/systemd
+ intermediate_instructions:
+ - RUN /usr/bin/apt-get update
- name: ubuntu-16.04
driver:
image: dokken/ubuntu-16.04
pid_one_command: /bin/systemd
intermediate_instructions:
- RUN /usr/bin/apt-get update
-
- - name: fedora-30
+ - name: ubuntu-18.04
driver:
- image: dokken/fedora-30
- pid_one_command: /usr/lib/systemd/systemd
+ image: dokken/ubuntu-18.04
+ pid_one_command: /bin/systemd
+ intermediate_instructions:
+ - RUN /usr/bin/apt-get update
+ - name: ubuntu-19.10
+ driver:
+ image: dokken/ubuntu-19.10
+ pid_one_command: /bin/systemd
intermediate_instructions:
- - RUN /usr/bin/yum install libselinux-utils -y
+ - RUN /usr/bin/apt-get update
diff --git a/kitchen.yml b/kitchen.yml
index c46bc5e0..8f42f2d9 100644
--- a/kitchen.yml
+++ b/kitchen.yml
@@ -1,24 +1,36 @@
---
driver:
name: vagrant
- chef_version: <%= ENV['CHEF_VERSION'] || 'current' %>
- env: [CHEF_LICENSE=accept]
provisioner:
name: chef_zero
deprecations_as_errors: true
chef_license: accept
+ product_name: chef
+ product_version: <%= ENV['CHEF_VERSION'] || 'latest' %>
+ install_strategy: always
+ chef_log_level: <%= ENV['CHEF_LOG_LEVEL'] || 'auto' %>
verifier:
name: inspec
platforms:
+ - name: centos-6
- name: centos-7
+ - name: centos-8
+ - name: fedora-31
+ - name: amazonlinux-2
- name: debian-8
+ - name: debian-10
- name: ubuntu-16.04
- - name: fedora-30
+ - name: ubuntu-18.04
+ - name: ubuntu-19.10
suites:
- name: default
run_list:
- recipe[test::default]
+ - name: delete
+ run_list:
+ - recipe[test::default]
+ - recipe[test::config_delete]
diff --git a/libraries/dynadns.rb b/libraries/dynadns.rb
deleted file mode 100644
index 447e9a95..00000000
--- a/libraries/dynadns.rb
+++ /dev/null
@@ -1,108 +0,0 @@
-module DHCP
- # methods for managing rndc key data and dynadns bind masters
- module DynaDns
- class << self
- include Chef::DSL::DataQuery
-
- attr_accessor :node
- attr_accessor :zones
-
- def load(node)
- @node = node
- load_zones
- end
-
- #
- # @return [Hash] of zone_name => master_addr
- #
- def masters
- @zones ||= load_zones
- masters ||= {}
- return unless @zones
- @zones.each do |zone|
- name = zone['zone_name']
- masters[name] ||= {}
-
- # set to global master by default
- if node['dns'].key?(:master) && !node['dns']['master'].empty?
- masters[name]['master'] = node['dns']['master']
- end
-
- if node['dns'].key?(:rndc_key) && !node['dns']['rndc_key'].empty?
- masters[name]['key'] = node['dns']['rndc_key']
- end
-
- # use zone bag override if it exists
- if zone.key?('master_address') && !zone['master_address'].empty?
- masters[name]['master'] = zone['master_address']
- end
-
- if zone.key?('rndc_key') && !zone['rndc_key'].empty?
- masters[name]['key'] = zone['rndc_key']
- end
-
- # validate
- unless masters[name].key?('key') && masters[name].key?('master')
- masters.delete(name)
- end
- end
-
- masters
- end
-
- #
- # Fetch all keys this node requests
- #
- # @return [Hash] of key-names containing bag data for each key
- #
- def keys
- k ||= {}
- @zones ||= load_zones
- return if @zones.nil? || @zones.empty?
-
- # global default keys if they exist
- # TODO: need to work out the namespace on dns stuff here.
- # TODO: be good to support knife-vault/encrypted bags for keys
- if node.key?(:dns) && node['dns'].key?(:rndc_key)
- default_key = node['dns']['rndc_key']
- k[default_key] = get_key(default_key)
- end
-
- @zones.each { |zone| k[zone['rndc_key']] = get_key zone['rndc_key'] if zone.key? 'rndc_key' }
- k
- end
-
- #
- # Get a key from bag or attributes
- #
- def get_key(name)
- return data_bag_item('rndc_keys', name).to_hash if node['dhcp']['use_bags']
- node['dhcp']['rndc_keys'].fetch name, ''
- end
-
- #
- # Should we load zones?
- # We only want to load zones if configured to use databags and node attribute node['dns']['zones'] is populated
- #
- def load_zones?
- return false unless node['dhcp']['use_bags']
- has_zones_attr = node['dns'] && node['dns']['zones']
- return true if has_zones_attr && !node['dns']['zones'].empty?
- end
-
- #
- # Load all zone bags this node calls out
- #
- def load_zones
- return nil unless load_zones?
-
- @zones = []
- node['dns']['zones'].each do |zone|
- bag_name = node['dns']['bag_name'] || 'dns_zones'
- zones << data_bag_item(bag_name, Dhcp::Helpers.escape(zone)).to_hash
- end
- @zones
- end
- end
- end
-end
diff --git a/libraries/failover.rb b/libraries/failover.rb
deleted file mode 100644
index 060ce018..00000000
--- a/libraries/failover.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-module DHCP
- # methods for detecthing and reporting on failover role/peers
- module Failover
- class << self
- include Chef::DSL::DataQuery
-
- attr_reader :node
-
- def load(node)
- @node = node
- end
-
- def enabled?
- case role
- when 'primary'
- return slaves.empty? ? false : true
- when 'secondary'
- return masters.empty? ? false : true
- end
- false
- end
-
- def role
- if node['dhcp'].key?(:slave) && node['dhcp']['slave'] == true
- 'secondary'
- elsif node['dhcp'].key?(:master) && node['dhcp']['master'] == true
- 'primary'
- end
- end
-
- def peer_node
- if node['dhcp'].key?(:slave) && node['dhcp']['slave']
- masters.first
- elsif node['dhcp'].key?(:master) && node['dhcp']['master']
- slaves.first
- end
- end
-
- def peer
- Chef::Log.info "Dhcp Peer: #{peer_node}"
- return nil if peer_node.nil? || peer_node.empty?
- peer_node['ipaddress']
- end
-
- def slaves
- if node['dhcp'].key?(:slaves) && !node['dhcp']['slaves'].empty?
- node['dhcp']['slaves']
- else
- search(:node, "domain:#{node['domain']} AND dhcp_slave:true")
- end
- end
-
- def masters
- if node['dhcp'].key?(:masters) && !node['dhcp']['masters'].empty?
- node['dhcp']['masters']
- else
- search(:node, "domain:#{node['domain']} AND dhcp_master:true")
- end
- end
- end
- end
-end
diff --git a/libraries/helpers.rb b/libraries/helpers.rb
index b610902d..263976bd 100644
--- a/libraries/helpers.rb
+++ b/libraries/helpers.rb
@@ -1,66 +1,268 @@
module Dhcp
- #
- # Helper methods that are used in multiple providers
- #
- module Helpers
- #
- # Determine if the resource matches this resource's type
- #
- # @param resource the resource to check
- #
- # @return [TrueClass, FalseClass] wether or not the resource matches
- #
- def resource_match?(resource)
- # Check for >= Chef 12
- return true if Gem::Version.new(Chef::VERSION) >= Gem::Version.new('12.0.0') && resource.declared_type == new_resource.declared_type
-
- false
- end
+ module Cookbook
+ module Helpers
+ def dhcpd_user
+ 'root'
+ end
+
+ def dhcpd_group
+ return 'root' if node['platform'].eql?('debian')
- #
- # List of files to include in list.conf for a collection of subconfigs (e.g. hosts, subnets, groups)
- #
- # @param [String] sub_dir - The subconfig directory for the config
- #
- # @return [Array] An array of config files for the subconfig
- def includes(sub_dir)
- with_run_context :root do
- run_context.resource_collection.map do |resource|
- next unless resource_match? resource
- next unless resource.action == :add || resource.action.include?(:add)
- "#{resource.conf_dir}/#{sub_dir}/#{resource.name}.conf"
- end.compact
+ 'dhcpd'
end
- end
- #
- # Defined resource for list.conf to include all subconfig files
- #
- # @param [String] sub_dir - The subconfig directory for the config
- #
- def write_include(sub_dir, name)
- template "#{new_resource.conf_dir}/#{sub_dir}/list.conf #{name}" do
- path "#{new_resource.conf_dir}/#{sub_dir}/list.conf"
- cookbook 'dhcp'
- source 'list.conf.erb'
- owner 'root'
- group 'root'
- mode '0644'
- variables(files: includes(sub_dir))
- notifies :restart, "service[#{node['dhcp']['service_name']}]", :delayed
+ def dhcpd_packages
+ case node['platform_family']
+ when 'rhel', 'amazon'
+ return %w(dhcp) if node['platform_version'].to_i < 8
+
+ %w(dhcp-server)
+ when 'fedora'
+ %w(dhcp-server)
+ when 'debian'
+ %w(isc-dhcp-server isc-dhcp-server-ldap)
+ end
+ end
+
+ def dhcpd_service_name(ip_version)
+ case node['platform_family']
+ when 'amazon', 'rhel', 'fedora'
+ {
+ ipv4: 'dhcpd',
+ ipv6: 'dhcpd6',
+ }
+ when 'debian'
+ {
+ ipv4: 'isc-dhcp-server',
+ ipv6: 'isc-dhcp-server6',
+ }
+ end.fetch(ip_version)
+ end
+
+ def dhcpd_config_dir
+ '/etc/dhcp'
+ end
+
+ def dhcpd_config_file(ip_version)
+ case ip_version
+ when :ipv4
+ "#{dhcpd_config_dir}/dhcpd.conf"
+ when :ipv6
+ "#{dhcpd_config_dir}/dhcpd6.conf"
+ else
+ raise "IP version #{ip_version} is unknown."
+ end
+ end
+
+ def dhcpd_failover_config_file(ip_version)
+ return '/etc/dhcp/dhcpd.failover.conf' if ip_version.eql?(:ipv4)
+
+ nil
+ end
+
+ def dhcpd_env_file
+ case node['platform_family']
+ when 'rhel', 'amazon', 'fedora'
+ '/etc/sysconfig/dhcpd'
+ when 'debian'
+ '/etc/default/isc-dhcp-server'
+ else
+ raise "dhcpd_env_file: Unsupported platform family #{node['platform_family']}."
+ end
end
- end
- module_function
+ def dhcpd_config_includes_directory(ip_version)
+ case ip_version
+ when :ipv4
+ "#{dhcpd_config_dir}/dhcpd.d"
+ when :ipv6
+ "#{dhcpd_config_dir}/dhcpd6.d"
+ else
+ raise "IP version #{ip_version} is unknown."
+ end
+ end
+
+ def dhcpd_config_resource_directory(ip_version, resource_type)
+ case resource_type
+ when :dhcp_class
+ "#{dhcpd_config_includes_directory(ip_version)}/classes.d"
+ when :dhcp_group
+ "#{dhcpd_config_includes_directory(ip_version)}/groups.d"
+ when :dhcp_host
+ "#{dhcpd_config_includes_directory(ip_version)}/hosts.d"
+ when :dhcp_shared_network
+ "#{dhcpd_config_includes_directory(ip_version)}/shared_networks.d"
+ when :dhcp_subnet
+ "#{dhcpd_config_includes_directory(ip_version)}/subnets.d"
+ else
+ raise "Invalid resource type #{resource_type}."
+ end
+ end
+
+ def dhcpd_config_includes_directories
+ %w(groups.d hosts.d subnets.d shared_networks.d classes.d)
+ end
+
+ def dhcpd_lib_dir
+ if platform_family?('debian')
+ '/var/lib/dhcp'
+ else
+ '/var/lib/dhcpd'
+ end
+ end
+
+ def dhcpd_lib_dir_options
+ case node['platform']
+ when 'amazon', 'centos', 'fedora', 'rhel'
+ {
+ 'owner' => 'dhcpd',
+ 'group' => 'dhcpd',
+ 'mode' => '0755',
+ }
+ when 'ubuntu'
+ {
+ 'owner' => 'root',
+ 'group' => 'dhcpd',
+ 'mode' => '0775',
+ }
+ when 'debian'
+ {
+ 'owner' => 'root',
+ 'group' => 'root',
+ 'mode' => '0755',
+ }
+ end
+ end
- #
- # Escape some special characters
- #
- # escape . -> - and / -> _
- # so 1.1/3 -> 1-1_3
- #
- def escape(name)
- name.tr('.', '-').tr('/', '_')
+ def dhcpd_lease_file(ip_version)
+ case ip_version
+ when :ipv4
+ "#{dhcpd_lib_dir}/dhcpd.leases"
+ when :ipv6
+ "#{dhcpd_lib_dir}/dhcpd6.leases"
+ else
+ raise "#{ip_version} is unknown."
+ end
+ end
+
+ def dhcpd_lease_file_options
+ case node['platform_family']
+ when 'amazon', 'centos', 'fedora', 'rhel'
+ {
+ 'owner' => 'dhcpd',
+ 'group' => 'dhcpd',
+ 'mode' => '0644',
+ 'action' => :create_if_missing,
+ }
+ when 'ubuntu'
+ {
+ 'owner' => 'root',
+ 'group' => 'dhcpd',
+ 'mode' => '0664',
+ 'action' => :create_if_missing,
+ }
+ when 'debian'
+ {
+ 'owner' => 'root',
+ 'group' => 'root',
+ 'mode' => '0644',
+ 'action' => :create_if_missing,
+ }
+ end
+ end
+
+ def dhcpd_use_systemd?
+ if (platform_family?('rhel') && node['platform_version'].to_i < 7) ||
+ (platform?('ubuntu') && node['platform_version'].to_i < 14) ||
+ (platform_family?('amazon') && node['platform_version'].to_i < 2)
+ false
+ else
+ true
+ end
+ end
+
+ def dhcpd_systemd_unit_content(ip_version)
+ raise 'Invalid ip_version' unless ip_version.is_a?(Symbol) && %i(ipv4 ipv6).include?(ip_version)
+ dhcp6 = ip_version.eql?(:ipv6)
+
+ case node['platform']
+ when 'amazon', 'centos', 'fedora', 'rhel'
+ {
+ 'Unit' => {
+ 'Description' => "DHCP#{dhcp6 ? 'v6' : 'v4'} Server Daemon",
+ 'Documentation' => 'man:dhcpd(8) man:dhcpd.conf(5)',
+ 'Wants' => 'network-online.target',
+ 'After' => [
+ 'network-online.target',
+ 'time-sync.target',
+ ],
+ 'ConditionPathExists' => [
+ '/etc/sysconfig/dhcpd',
+ "|/etc/dhcp/#{dhcp6 ? 'dhcpd6' : 'dhcpd'}.conf",
+ ],
+ },
+ 'Service' => {
+ 'Type' => 'notify',
+ 'EnvironmentFile' => '-/etc/sysconfig/dhcpd',
+ 'ExecStart' => "/usr/sbin/dhcpd -f #{dhcp6 ? '-6' : '-4'} -cf /etc/dhcp/#{dhcp6 ? 'dhcpd6' : 'dhcpd'}.conf -user dhcpd -group dhcpd --no-pid $DHCPDARGS",
+ 'StandardError' => 'null',
+ },
+ 'Install' => {
+ 'WantedBy' => 'multi-user.target',
+ },
+ }
+ when 'debian'
+ {
+ 'Unit' => {
+ 'Description' => "ISC DHCP IP#{dhcp6 ? 'v6' : 'v4'} Server",
+ 'Documentation' => 'man:dhcpd(8) man:dhcpd.conf(5)',
+ 'Wants' => 'network-online.target',
+ 'After' => [
+ 'network-online.target',
+ 'time-sync.target',
+ ],
+ 'ConditionPathExists' => [
+ '/etc/default/isc-dhcp-server',
+ "|/etc/dhcp/#{dhcp6 ? 'dhcpd6' : 'dhcpd'}.conf",
+ ],
+ },
+ 'Service' => {
+ 'EnvironmentFile' => '/etc/default/isc-dhcp-server',
+ 'RuntimeDirectory' => 'dhcp-server',
+ 'ExecStart' => "/usr/sbin/dhcpd -f #{dhcp6 ? '-6' : '-4'} -cf /etc/dhcp/#{dhcp6 ? 'dhcpd6' : 'dhcpd'}.conf -pf /run/dhcp-server/#{dhcp6 ? 'dhcpd6' : 'dhcpd'}.pid $INTERFACES",
+ },
+ 'Install' => {
+ 'WantedBy' => 'multi-user.target',
+ },
+ }
+ when 'ubuntu'
+ {
+ 'Unit' => {
+ 'Description' => "ISC DHCP IP#{dhcp6 ? 'v6' : 'v4'} Server",
+ 'Documentation' => 'man:dhcpd(8) man:dhcpd.conf(5)',
+ 'Wants' => 'network-online.target',
+ 'After' => [
+ 'network-online.target',
+ 'time-sync.target',
+ ],
+ 'ConditionPathExists' => [
+ '/etc/default/isc-dhcp-server',
+ "|/etc/dhcp/#{dhcp6 ? 'dhcpd6' : 'dhcpd'}.conf",
+ ],
+ },
+ 'Service' => {
+ 'EnvironmentFile' => '/etc/default/isc-dhcp-server',
+ 'RuntimeDirectory' => 'dhcp-server',
+ 'ExecStart' => "/usr/sbin/dhcpd -f #{dhcp6 ? '-6' : '-4'} -cf /etc/dhcp/#{dhcp6 ? 'dhcpd6' : 'dhcpd'}.conf -user dhcpd -group dhcpd -pf /run/dhcp-server/#{dhcp6 ? 'dhcpd6' : 'dhcpd'}.pid $INTERFACES",
+ },
+ 'Install' => {
+ 'WantedBy' => 'multi-user.target',
+ },
+ }
+ else
+ raise "Platform #{node['platform']} is not a supported systemd OS."
+ end
+ end
end
end
end
diff --git a/libraries/resource.rb b/libraries/resource.rb
new file mode 100644
index 00000000..7cdd0cd2
--- /dev/null
+++ b/libraries/resource.rb
@@ -0,0 +1,52 @@
+module Dhcp
+ module Cookbook
+ module ResourceHelpers
+ def create_list_resource(directory)
+ with_run_context(:root) do
+ edit_resource(:directory, directory)
+ edit_resource(:template, "#{directory}/list.conf") do
+ cookbook 'dhcp'
+ source 'list.conf.erb'
+
+ owner new_resource.owner
+ group new_resource.group
+ mode new_resource.mode
+
+ variables['files'] ||= []
+
+ action :nothing
+ delayed_action :create
+ end
+ end
+ end
+
+ def add_to_list_resource(directory, config_file)
+ manage_list_resource(directory, config_file, :add)
+ end
+
+ def remove_from_list_resource(directory, config_file)
+ manage_list_resource(directory, config_file, :remove)
+ end
+
+ private
+
+ def manage_list_resource(directory, config_file, action)
+ begin
+
+ list = find_resource!(:template, "#{directory}/list.conf")
+ rescue Chef::Exceptions::ResourceNotFound
+ list = create_list_resource(directory)
+ end
+
+ files = list.variables['files']
+
+ case action
+ when :add
+ files.push(config_file)
+ when :remove
+ files.delete(config_file) if files.include?(config_file)
+ end
+ end
+ end
+ end
+end
diff --git a/libraries/template.rb b/libraries/template.rb
new file mode 100644
index 00000000..615dd513
--- /dev/null
+++ b/libraries/template.rb
@@ -0,0 +1,25 @@
+module Dhcp
+ module Cookbook
+ module TemplateHelpers
+ def nil_or_empty?(property)
+ return true if property.nil? || (property.respond_to?(:empty?) && property.empty?)
+
+ false
+ end
+
+ def property_array(property)
+ return property if property.is_a?(Array)
+
+ [property]
+ end
+
+ def property_collection(property)
+ property.map { |p| p.is_a?(Array) ? p : p.split(' ', 2) }
+ end
+
+ def property_collection_sorted(property)
+ property.sort.map { |p| p.is_a?(Array) ? p : p.split(' ', 2) }
+ end
+ end
+ end
+end
diff --git a/metadata.rb b/metadata.rb
index 5ea24236..3d6b9a39 100644
--- a/metadata.rb
+++ b/metadata.rb
@@ -3,10 +3,10 @@
maintainer_email 'help@sous-chefs.org'
license 'Apache-2.0'
description 'Installs and configures DHCP'
-chef_version '>= 13.0'
+chef_version '>= 14.0'
source_url 'https://github.com/sous-chefs/dhcp'
issues_url 'https://github.com/sous-chefs/dhcp/issues'
-version '6.1.0'
+version '7.0.0'
supports 'debian'
supports 'ubuntu'
@@ -15,3 +15,4 @@
supports 'fedora'
supports 'scientific'
supports 'oracle'
+supports 'amazon'
diff --git a/recipes/_config.rb b/recipes/_config.rb
deleted file mode 100644
index f65dd631..00000000
--- a/recipes/_config.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-include_recipe 'dhcp::_service'
-
-DHCP::Failover.load(node)
-DHCP::DynaDns.load(node)
-
-#
-# Global DHCP config settings
-#
-template node['dhcp']['config_file'] do
- owner 'root'
- group 'root'
- mode '0644'
- source 'dhcpd.conf.erb'
- variables(
- allows: node['dhcp']['allows'] || [],
- parameters: node['dhcp']['parameters'] || [],
- options: node['dhcp']['options'] || [],
- hooks: node['dhcp']['hooks'],
- masters: DHCP::DynaDns.masters,
- keys: DHCP::DynaDns.keys,
- my_ip: node['dhcp']['my_ip'] || node['ipaddress'],
- role: DHCP::Failover.role,
- peer_ip: DHCP::Failover.peer,
- failover: DHCP::Failover.enabled?
- )
- action :create
- notifies :restart, "service[#{node['dhcp']['service_name']}]", :delayed
-end
-
-if node['dhcp']['failover_lease_hack']
- template node['dhcp']['dhcpd_leases'] do
- owner 'root'
- group 'root'
- mode '0644'
- source 'dhcpd.leases-hack.erb'
- action :create
- notifies :restart, "service[#{node['dhcp']['service_name']}]"
- not_if { ::File.exist?("#{node['dhcp']['dhcpd_leases']}-hack.lock") }
- end
-
- file "#{node['dhcp']['dhcpd_leases']}-hack.lock" do
- owner 'root'
- group 'root'
- mode '0755'
- action :touch
- end
-end
-
-#
-# Create the dirs and stub files for each resource type
-#
-%w(groups.d hosts.d subnets.d shared_networks.d classes.d).each do |dir|
- directory "#{node['dhcp']['dir']}/#{dir} initial creation" do
- path "#{node['dhcp']['dir']}/#{dir}"
- end
-
- file "#{node['dhcp']['dir']}/#{dir}/list.conf" do
- action :create_if_missing
- content ''
- end
-end
diff --git a/recipes/_groups.rb b/recipes/_groups.rb
deleted file mode 100644
index ad4cff2d..00000000
--- a/recipes/_groups.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-# Setup Groups
-#
-unless node['dhcp']['groups'].empty?
- node['dhcp']['groups'].each do |group|
- group_data = if node['dhcp']['use_bags'] == true
- data_bag_item(node['dhcp']['groups_bag'], group)
- else
- node['dhcp']['group_data'].fetch group, nil
- end
-
- next unless group_data
- dhcp_group group do
- parameters group_data['parameters'] || []
- evals group_data['evals'] || []
- hosts group_data['hosts'] || {}
- conf_dir node['dhcp']['dir']
- end
- end
-end
diff --git a/recipes/_hosts.rb b/recipes/_hosts.rb
deleted file mode 100644
index 70acffb9..00000000
--- a/recipes/_hosts.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# Hosts
-#
-unless node['dhcp']['hosts'].empty?
-
- # special key to just use all hosts in dhcp_hosts databag
- # figure which hosts to load
- # TODO: this should be refactored into libs
- # rubocop:disable BlockNesting
- host_list = node['dhcp']['hosts']
- if host_list.respond_to?(:downcase) && host_list.casecmp('all') == 0
- host_list = node['dhcp']['host_data'].keys
- if node['dhcp']['use_bags'] == true
- begin
- host_list = data_bag(node['dhcp']['hosts_bag'])
- # TODO: Make this more specific to the non existing bag
- rescue
- return
- end
- end
- elsif node['dhcp']['use_bags'] == true
- search(node['dhcp']['hosts_bag'], "id:#{node['dhcp']['hosts_bag']}").each do |host|
- host_list << host['id']
- end
- end
- # rubocop:enable BlockNesting
-
- host_list.each do |host|
- host_data = if node['dhcp']['use_bags'] == true
- data_bag_item(node['dhcp']['hosts_bag'], Dhcp::Helpers.escape(host))
- else
- node['dhcp']['host_data'].fetch host, nil
- end
-
- next unless host_data
- dhcp_host host do
- hostname host_data['hostname']
- macaddress host_data['mac']
- ipaddress host_data['ip']
- parameters host_data['parameters'] || []
- options host_data['options'] || []
- conf_dir node['dhcp']['dir']
- end
- end
-end
diff --git a/recipes/_networks.rb b/recipes/_networks.rb
deleted file mode 100644
index 12b9c223..00000000
--- a/recipes/_networks.rb
+++ /dev/null
@@ -1,96 +0,0 @@
-# Setup subnets
-#
-
-include_recipe 'dhcp::_service'
-
-node['dhcp']['networks'].each do |net|
- data = if node['dhcp']['use_bags'] == true
- data_bag_item(node['dhcp']['networks_bag'], Dhcp::Helpers.escape(net))
- else
- node['dhcp']['network_data'].fetch net, nil
- end
-
- next unless data
- # run the lwrp with the bag data
- dhcp_subnet data['address'] do
- broadcast data['broadcast'] if data.key? 'broadcast'
- netmask data['netmask'] if data.key? 'netmask'
- routers data['routers'] || []
- options data['options'] || []
- if data.key?('range') || node['dhcp']['failover']
- pool do
- range data['range'] if data.key? 'range'
- peer node['domain'] if node['dhcp']['failover']
- allow data['allow'] if data.key? 'allow'
- extra_pool_lines data['extra_pool_lines'] if data.key? 'extra_pool_lines'
- end
- end
- ddns data['ddns'] if data.key? 'ddns'
- conf_dir node['dhcp']['dir']
- evals data['evals'] || []
- key data['key'] || {}
- zones data['zones'] || []
- next_server data['next_server'] if data.key? 'next_server'
- end
-end
-
-# Load databag data
-if node['dhcp']['use_bags']
- node['dhcp']['shared_networks'].each do |shared_net|
- network_data = data_bag_item(node['dhcp']['networks_bag'], Dhcp::Helpers.escape(shared_net))
- dhcp_shared_network shared_net do
- network_data['subnets'].each do |_net, data|
- subnet data['address'] do
- broadcast data['broadcast'] if data.key? 'broadcast'
- netmask data['netmask'] if data.key? 'netmask'
- routers data['routers'] || []
- options data['options'] || []
- if data.key?('range') || node['dhcp']['failover']
- pool do
- range data['range'] if data.key? 'range'
- peer node['domain'] if node['dhcp']['failover']
- allow data['allow'] if data.key? 'allow'
- extra_pool_lines data['extra_pool_lines'] if data.key? 'extra_pool_lines'
- end
- end
- ddns data['ddns'] if data.key? 'ddns'
- conf_dir node['dhcp']['dir']
- evals data['evals'] || []
- key data['key'] || {}
- zones data['zones'] || []
- next_server data['next_server'] if data.key? 'next_server'
- end
- end
- end
- end
-end
-
-# Load node attribute data
-unless node['dhcp']['use_bags']
- node['dhcp']['shared_network_data'].each do |shared_net, network_data|
- dhcp_shared_network shared_net do
- network_data['subnets'].each do |_net, data|
- subnet data['address'] do
- broadcast data['broadcast'] if data.key? 'broadcast'
- netmask data['netmask'] if data.key? 'netmask'
- routers data['routers'] || []
- options data['options'] || []
- if data.key?('range') || node['dhcp']['failover']
- pool do
- range data['range'] if data.key? 'range'
- peer node['domain'] if node['dhcp']['failover']
- allow data['allow'] if data.key? 'allow'
- extra_pool_lines data['extra_pool_lines'] if data.key? 'extra_pool_lines'
- end
- end
- ddns data['ddns'] if data.key? 'ddns'
- conf_dir node['dhcp']['dir']
- evals data['evals'] || []
- key data['key'] || {}
- zones data['zones'] || []
- next_server data['next_server'] if data.key? 'next_server'
- end
- end
- end
- end
-end
diff --git a/recipes/_package.rb b/recipes/_package.rb
deleted file mode 100644
index 49e581db..00000000
--- a/recipes/_package.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-apt_update 'update'
-
-package node['dhcp']['package_name']
-directory node['dhcp']['dir']
diff --git a/recipes/_service.rb b/recipes/_service.rb
deleted file mode 100644
index 4f60012e..00000000
--- a/recipes/_service.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-service node['dhcp']['service_name'] do
- supports restart: true, status: true, reload: true
- action [:enable]
-end
-
-template node['dhcp']['init_config'] do
- owner 'root'
- group 'root'
- mode '0644'
- source 'init_config.erb'
- variables(
- interfaces: node['dhcp']['interfaces'],
- var: node['dhcp']['init_iface']
- )
- not_if { platform_family?('fedora') }
-end
diff --git a/recipes/library.rb b/recipes/library.rb
deleted file mode 100644
index 2e5df19d..00000000
--- a/recipes/library.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-# encoding: UTF-8
-# stub so you can load just the library/LWRP
diff --git a/resources/class.rb b/resources/class.rb
index 615a875c..be4aaa4f 100644
--- a/resources/class.rb
+++ b/resources/class.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
#
# Cookbook:: dhcp
# Resource:: class
@@ -16,40 +15,85 @@
# limitations under the License.
#
-default_action :add
+include Dhcp::Cookbook::Helpers
-property :match, String, required: true
-property :conf_dir, String, default: '/etc/dhcp'
+property :comment, String,
+ description: 'Unparsed comment to add to the configuration file'
-attr_accessor :subclasses
+property :ip_version, Symbol,
+ equal_to: %i(ipv4 ipv6),
+ default: :ipv4,
+ description: 'The IP version, 4 or 6'
-def subclass(value)
- @subclasses ||= []
- @subclasses << value
-end
+property :conf_dir, String,
+ default: lazy { dhcpd_config_resource_directory(ip_version, declared_type) },
+ description: 'Directory to create configuration file in'
+
+property :cookbook, String,
+ default: 'dhcp',
+ description: 'Template source cookbook'
+
+property :template, String,
+ default: 'class.conf.erb',
+ description: 'Template source file'
+
+property :owner, String,
+ default: lazy { dhcpd_user },
+ description: 'Generated file owner'
+
+property :group, String,
+ default: lazy { dhcpd_group },
+ description: 'Generated file group'
+
+property :mode, String,
+ default: '0640',
+ description: 'Generated file mode'
+
+property :match, String,
+ description: 'Client match statement'
+
+property :parameters, [Hash, Array],
+ description: 'Class client parameters'
+
+property :options, [Hash, Array],
+ description: 'Class client options'
+
+property :subclass, Array,
+ description: 'Class subclass match statements'
action_class do
- include Dhcp::Helpers
+ include Dhcp::Cookbook::ResourceHelpers
end
-action :add do
- with_run_context :root do
- run_context.include_recipe 'dhcp::_service'
-
- directory "#{new_resource.conf_dir}/classes.d #{new_resource.name}" do
- path "#{new_resource.conf_dir}/classes.d"
- end
-
- template "#{new_resource.conf_dir}/classes.d/#{new_resource.name}.conf" do
- cookbook 'dhcp'
- source 'class.conf.erb'
- variables name: new_resource.name, match: new_resource.match, subclasses: new_resource.subclasses
- owner 'root'
- group 'root'
- mode '0644'
- notifies :restart, "service[#{node['dhcp']['service_name']}]", :delayed
- end
-
- write_include 'classes.d', new_resource.name
+action :create do
+ template "#{new_resource.conf_dir}/#{new_resource.name}.conf" do
+ cookbook new_resource.cookbook
+ source new_resource.template
+
+ owner new_resource.owner
+ group new_resource.group
+ mode new_resource.mode
+
+ variables(
+ name: new_resource.name,
+ comment: new_resource.comment,
+ match: new_resource.match,
+ parameters: new_resource.parameters,
+ options: new_resource.options,
+ subclasses: new_resource.subclass
+ )
+ helpers(Dhcp::Cookbook::TemplateHelpers)
+
+ action :create
end
+
+ add_to_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf")
+end
+
+action :delete do
+ file "#{new_resource.conf_dir}/#{new_resource.name}.conf" do
+ action :delete
+ end
+
+ remove_from_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf")
end
diff --git a/resources/config.rb b/resources/config.rb
new file mode 100644
index 00000000..92d77574
--- /dev/null
+++ b/resources/config.rb
@@ -0,0 +1,202 @@
+#
+# Cookbook:: dhcp
+# Resource:: config
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+include Dhcp::Cookbook::Helpers
+
+property :ip_version, Symbol,
+ equal_to: %i(ipv4 ipv6),
+ default: :ipv4,
+ description: 'The IP version, 4 or 6'
+
+property :config_file, String,
+ default: lazy { dhcpd_config_file(ip_version) },
+ description: 'The full path to the DHCP server configuration on disk'
+
+property :cookbook, String,
+ default: 'dhcp'
+
+property :template, String,
+ default: 'dhcpd.conf.erb'
+
+property :owner, String,
+ default: lazy { dhcpd_user }
+
+property :group, String,
+ default: lazy { dhcpd_group }
+
+property :mode, String,
+ default: '0640'
+
+property :config_failover_file, String,
+ default: lazy { dhcpd_failover_config_file(ip_version) },
+ description: 'The full path to the DHCP server failover configuration on disk'
+
+property :config_includes_directory, String,
+ default: lazy { dhcpd_config_includes_directory(ip_version) }
+
+property :lib_dir, String,
+ default: lazy { dhcpd_lib_dir },
+ description: 'The full path to the DHCP lib directory on disk'
+
+property :lease_file, String,
+ default: lazy { dhcpd_lease_file(ip_version) },
+ description: 'The full path to the DHCP server leases file on disk'
+
+property :allow, Array,
+ description: 'Allow access control'
+
+property :deny, Array,
+ description: 'Deny access control'
+
+property :ignore, Array,
+ description: 'Ignore access control'
+
+property :parameters, [Hash, Array],
+ description: 'Global parameters'
+
+property :options, [Hash, Array],
+ description: 'Global options'
+
+property :evals, Array,
+ description: 'Global conditional eval statements'
+
+property :keys, Hash,
+ description: 'TSIG keys'
+
+property :zones, Hash,
+ description: 'Dynamic DNS zones'
+
+property :hooks, Hash,
+ description: 'Server event action configuration'
+
+property :failover, Hash,
+ description: 'DHCP failover configuration'
+
+property :include_files, Array,
+ description: 'Additional configuration files to include'
+
+property :extra_lines, [String, Array],
+ description: 'Extra lines to append to the main configuration file'
+
+property :env_file, String,
+ default: lazy { dhcpd_env_file },
+ description: 'Configuration file lines for the DHCP environment'
+
+property :env_file_lines, [String, Array],
+ description: 'Configuration file lines for the DHCP environment file'
+
+action_class do
+ include Dhcp::Cookbook::ResourceHelpers
+end
+
+action :create do
+ raise 'DHCP failover is only supported for IPv4' if new_resource.failover && new_resource.ip_version.eql?(:ipv6)
+
+ directory new_resource.config_includes_directory do
+ owner new_resource.owner
+ group new_resource.group
+
+ action :create
+ end
+
+ dhcpd_config_includes_directories.each do |dir|
+ directory "#{new_resource.config_includes_directory}/#{dir}" do
+ owner new_resource.owner
+ group new_resource.group
+
+ action :create
+ end
+
+ create_list_resource("#{new_resource.config_includes_directory}/#{dir}")
+ end
+
+ # Pre-condition DHCPd lib directory and lease file for distros that don't take care of this
+ dhcpd_lib_dir_options.each { |property, value| edit_resource(:directory, new_resource.lib_dir).send(property, value) }
+ dhcpd_lease_file_options.each { |property, value| edit_resource(:file, new_resource.lease_file).send(property, value) }
+
+ template new_resource.config_file do
+ cookbook new_resource.cookbook
+ source new_resource.template
+
+ owner new_resource.owner
+ group new_resource.group
+ mode new_resource.mode
+
+ variables(
+ includes_dir: dhcpd_config_includes_directory(new_resource.ip_version),
+ allow: new_resource.allow,
+ deny: new_resource.deny,
+ ignore: new_resource.ignore,
+ parameters: new_resource.parameters,
+ options: new_resource.options,
+ evals: new_resource.evals,
+ keys: new_resource.keys,
+ zones: new_resource.zones,
+ hooks: new_resource.hooks,
+ failover: new_resource.failover,
+ include_files: new_resource.include_files,
+ extra_lines: new_resource.extra_lines
+ )
+ helpers(Dhcp::Cookbook::TemplateHelpers)
+
+ action :create
+ end
+
+ template new_resource.env_file do
+ cookbook 'dhcp'
+ source 'dhcpd-env.erb'
+
+ owner new_resource.owner
+ group new_resource.group
+ mode new_resource.mode
+
+ variables(
+ lines: new_resource.env_file_lines
+ )
+ helpers(Dhcp::Cookbook::TemplateHelpers)
+
+ action :create
+ end if new_resource.env_file
+
+ if new_resource.failover
+ template new_resource.config_failover_file do
+ cookbook 'dhcp'
+ source 'dhcpd.failover.conf.erb'
+
+ owner new_resource.owner
+ group new_resource.group
+ mode new_resource.mode
+
+ variables(
+ failover: new_resource.failover
+ )
+ helpers(Dhcp::Cookbook::TemplateHelpers)
+
+ action :create
+ end
+ end
+end
+
+action :delete do
+ file new_resource.config_file do
+ action :delete
+ end
+
+ file new_resource.config_failover_file do
+ action :delete
+ end if new_resource.ip_version.eql?(:ipv4)
+end
diff --git a/resources/group.rb b/resources/group.rb
index f81a30e2..fe08a2d6 100644
--- a/resources/group.rb
+++ b/resources/group.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
#
# Cookbook:: dhcp
# Resource:: group
@@ -16,49 +15,106 @@
# limitations under the License.
#
-default_action :add
+include Dhcp::Cookbook::Helpers
-property :parameters, Array, default: []
-property :evals, Array, default: []
-property :hosts, Hash, default: {}
-property :conf_dir, String, default: '/etc/dhcp'
+property :comment, String,
+ description: 'Unparsed comment to add to the configuration file'
+
+property :ip_version, Symbol,
+ equal_to: %i(ipv4 ipv6),
+ default: :ipv4,
+ description: 'The IP version, 4 or 6'
+
+property :conf_dir, String,
+ default: lazy { dhcpd_config_resource_directory(ip_version, declared_type) },
+ description: 'Directory to create configuration file in'
+
+property :cookbook, String,
+ default: 'dhcp',
+ description: 'Template source cookbook'
+
+property :template, String,
+ default: 'group.conf.erb',
+ description: 'Template source file'
+
+property :owner, String,
+ default: lazy { dhcpd_user },
+ description: 'Generated file owner'
+
+property :group, String,
+ default: lazy { dhcpd_group },
+ description: 'Generated file group'
+
+property :mode, String,
+ default: '0640',
+ description: 'Generated file mode'
+
+property :parameters, [Hash, Array],
+ description: 'Group client parameters'
+
+property :options, [Hash, Array],
+ description: 'Group client options'
+
+property :evals, Array,
+ description: 'Group conditional eval statements'
+
+property :hosts, Hash,
+ description: 'Group host members'
action_class do
- include Dhcp::Helpers
+ include Dhcp::Cookbook::ResourceHelpers
end
-action :add do
- with_run_context :root do
- directory "#{new_resource.conf_dir}/groups.d #{new_resource.name}" do
- path "#{new_resource.conf_dir}/groups.d"
+action :create do
+ hosts_include = []
+
+ new_resource.hosts.each do |host, properties|
+ hr = edit_resource(:dhcp_host, "#{new_resource.name}_grouphost_#{host}") do
+ owner new_resource.owner
+ group new_resource.group
+ mode new_resource.mode
+
+ ip_version new_resource.ip_version
+ conf_dir new_resource.conf_dir
+ group_host true
end
- template "#{new_resource.conf_dir}/groups.d/#{new_resource.name}.conf" do
- cookbook 'dhcp'
- source 'group.conf.erb'
- variables(
- name: new_resource.name,
- parameters: new_resource.parameters,
- evals: new_resource.evals,
- hosts: new_resource.hosts
- )
- owner 'root'
- group 'root'
- mode '0644'
- notifies :restart, "service[#{node['dhcp']['service_name']}]", :delayed
+ properties.each do |property, value|
+ hr.send(property, value)
end
- write_include 'groups.d', new_resource.name
+ hosts_include.push("#{new_resource.conf_dir}/#{new_resource.name}_grouphost_#{host}.conf")
end
-end
-action :remove do
- with_run_context :root do
- file "#{new_resource.conf_dir}/groups.d/#{new_resource.name}.conf" do
- action :delete
- notifies :restart, "service[#{node['dhcp']['service_name']}]", :delayed
- end
+ template "#{new_resource.conf_dir}/#{new_resource.name}.conf" do
+ cookbook new_resource.cookbook
+ source new_resource.template
+
+ owner new_resource.owner
+ group new_resource.group
+ mode new_resource.mode
+
+ variables(
+ name: new_resource.name,
+ comment: new_resource.comment,
+ ip_version: new_resource.ip_version,
+ parameters: new_resource.parameters,
+ options: new_resource.options,
+ evals: new_resource.evals,
+ hosts: hosts_include
+ )
+ helpers(Dhcp::Cookbook::TemplateHelpers)
- write_include 'groups.d', new_resource.name
+ action :create
end
+
+ add_to_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf")
+end
+
+action :delete do
+ file "#{new_resource.conf_dir}/#{new_resource.name}.conf" do
+ action :delete
+ end
+
+ remove_from_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf")
end
diff --git a/resources/host.rb b/resources/host.rb
index bd0c6c5f..6637d76c 100644
--- a/resources/host.rb
+++ b/resources/host.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
#
# Cookbook:: dhcp
# Resource:: host
@@ -16,52 +15,90 @@
# limitations under the License.
#
-default_action :add
+include Dhcp::Cookbook::Helpers
-property :hostname, String
-property :macaddress, String
-property :ipaddress, String
-property :options, Array, default: []
-property :parameters, Array, default: []
-property :conf_dir, String, default: '/etc/dhcp'
+property :comment, String,
+ description: 'Unparsed comment to add to the configuration file'
+
+property :ip_version, Symbol,
+ equal_to: %i(ipv4 ipv6),
+ default: :ipv4,
+ description: 'The IP version, 4 or 6'
+
+property :conf_dir, String,
+ default: lazy { dhcpd_config_resource_directory(ip_version, declared_type) },
+ description: 'Directory to create configuration file in'
+
+property :cookbook, String,
+ default: 'dhcp',
+ description: 'Template source cookbook'
+
+property :template, String,
+ default: 'host.conf.erb',
+ description: 'Template source file'
+
+property :owner, String,
+ default: lazy { dhcpd_user },
+ description: 'Generated file owner'
+
+property :group, String,
+ default: lazy { dhcpd_group },
+ description: 'Generated file group'
+
+property :mode, String,
+ default: '0640',
+ description: 'Generated file mode'
+
+property :group_host, [true, false],
+ default: false,
+ description: 'Flag to indicate host is used inside a group resource and should not be added to list.conf'
+
+property :identifier, String,
+ description: 'Host identifier, usually MAC or DHCID'
+
+property :address, String,
+ description: 'Host assigned address'
+
+property :parameters, [Hash, Array],
+ description: 'Host parameters'
+
+property :options, [Hash, Array],
+ description: 'Host options'
action_class do
- include Dhcp::Helpers
+ include Dhcp::Cookbook::ResourceHelpers
end
-action :add do
- with_run_context :root do
- directory "#{new_resource.conf_dir}/hosts.d #{new_resource.name}" do
- path "#{new_resource.conf_dir}/hosts.d"
- end
-
- template "#{new_resource.conf_dir}/hosts.d/#{new_resource.name}.conf" do
- cookbook 'dhcp'
- source 'host.conf.erb'
- variables(
- name: new_resource.name,
- hostname: new_resource.hostname,
- macaddress: new_resource.macaddress,
- ipaddress: new_resource.ipaddress,
- options: new_resource.options,
- parameters: new_resource.parameters
- )
- owner 'root'
- group 'root'
- mode '0644'
- notifies :restart, "service[#{node['dhcp']['service_name']}]", :delayed
- end
- write_include 'hosts.d', new_resource.name
+action :create do
+ template "#{new_resource.conf_dir}/#{new_resource.name}.conf" do
+ cookbook new_resource.cookbook
+ source new_resource.template
+
+ owner new_resource.owner
+ group new_resource.group
+ mode new_resource.mode
+
+ variables(
+ name: new_resource.name,
+ comment: new_resource.comment,
+ ip_version: new_resource.ip_version,
+ identifier: new_resource.identifier,
+ address: new_resource.address,
+ parameters: new_resource.parameters,
+ options: new_resource.options
+ )
+ helpers(Dhcp::Cookbook::TemplateHelpers)
+
+ action :create
end
-end
-action :remove do
- with_run_context :root do
- file "#{new_resource.conf_dir}/hosts.d/#{new_resource.name}.conf" do
- action :delete
- notifies :restart, "service[#{node['dhcp']['service_name']}]", :delayed
- end
+ add_to_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf") unless new_resource.group_host
+end
- write_include 'hosts.d', new_resource.name
+action :delete do
+ file "#{new_resource.conf_dir}/#{new_resource.name}.conf" do
+ action :delete
end
+
+ remove_from_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf") unless new_resource.group_host
end
diff --git a/recipes/server.rb b/resources/package.rb
similarity index 59%
rename from recipes/server.rb
rename to resources/package.rb
index 810db1ec..c02ef86c 100644
--- a/recipes/server.rb
+++ b/resources/package.rb
@@ -1,12 +1,6 @@
-# encoding: UTF-8
#
-# Author:: Jesse Nelson
-# Author:: Matt Ray
# Cookbook:: dhcp
-# Recipe:: server
-#
-# Copyright:: 2012-2017, Jesse Nelson
-# Copyright:: 2011-2017, Chef Software, Inc
+# Resource:: package
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -21,9 +15,25 @@
# limitations under the License.
#
-include_recipe 'dhcp::_package'
-include_recipe 'dhcp::_service'
-include_recipe 'dhcp::_config'
-include_recipe 'dhcp::_networks'
-include_recipe 'dhcp::_groups'
-include_recipe 'dhcp::_hosts'
+include Dhcp::Cookbook::Helpers
+
+property :packages, [String, Array],
+ default: lazy { dhcpd_packages }
+
+action_class do
+ def do_action(package_action)
+ package 'ISC DHCPD' do
+ package_name new_resource.packages
+
+ action package_action
+ end
+ end
+end
+
+action :install do
+ do_action(action)
+end
+
+action :remove do
+ do_action(action)
+end
diff --git a/resources/pool.rb b/resources/pool.rb
deleted file mode 100644
index 191836ae..00000000
--- a/resources/pool.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-#
-# Cookbook:: dhcp
-# Resource:: pool
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-default_action :nothing
-
-attribute :range, kind_of: [Array, String]
-attribute :peer, kind_of: String, default: nil
-attribute :deny, kind_of: [Array, String], default: [], coerce: proc { |prop|
- Array(prop).flatten
-}
-attribute :allow, kind_of: [Array, String], default: [], coerce: proc { |prop|
- Array(prop).flatten
-}
-attribute :extra_pool_lines, kind_of: [Array, String], default: [], coerce: proc { |prop|
- Array(prop).flatten
-}
-
-# This resource has no actions, and is only used to verify properties
-# for the dhcp_subnet pool subresource.
diff --git a/resources/service.rb b/resources/service.rb
new file mode 100644
index 00000000..df873341
--- /dev/null
+++ b/resources/service.rb
@@ -0,0 +1,91 @@
+#
+# Cookbook:: dhcp
+# Resource:: service
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+include Dhcp::Cookbook::Helpers
+
+property :ip_version, Symbol,
+ equal_to: %i(ipv4 ipv6),
+ default: :ipv4,
+ description: 'The IP version, 4 or 6'
+
+property :service_name, String,
+ coerce: proc { |s| "#{s}.service" },
+ description: 'Override the default service names'
+
+property :systemd_unit_content, [String, Hash],
+ default: lazy { dhcpd_systemd_unit_content(ip_version) },
+ description: 'Override the systemd unit file contents'
+
+action_class do
+ def service_name
+ if new_resource.service_name
+ new_resource.service_name
+ else
+ dhcpd_service_name(new_resource.ip_version)
+ end
+ end
+
+ def do_service_action(resource_action)
+ with_run_context(:root) do
+ edit_resource(:service, service_name) do
+ delayed_action resource_action
+ end
+ end
+ end
+end
+
+action :create do
+ with_run_context :root do
+ edit_resource(:systemd_unit, "#{service_name}.service") do |new_resource|
+ content new_resource.dhcpd_systemd_unit_content(new_resource.ip_version)
+ triggers_reload true
+ verify false
+
+ action :create
+ end if dhcpd_use_systemd?
+ end
+end
+
+action :delete do
+ do_service_action([:stop, :disable])
+ with_run_context :root do
+ edit_resource(:systemd_unit, "#{service_name}.service").action(:delete) if dhcpd_use_systemd?
+ end
+end
+
+action :start do
+ do_service_action(action)
+end
+
+action :stop do
+ do_service_action(action)
+end
+
+action :restart do
+ do_service_action(action)
+end
+
+action :reload do
+ do_service_action(action)
+end
+
+action :enable do
+ do_service_action(action)
+end
+
+action :disable do
+ do_service_action(action)
+end
diff --git a/resources/shared_network.rb b/resources/shared_network.rb
index f592889b..78f9cc76 100644
--- a/resources/shared_network.rb
+++ b/resources/shared_network.rb
@@ -1,10 +1,7 @@
#
-# Author:: Jacob McCann ()
# Cookbook:: dhcp
# Resource:: shared_network
#
-# Copyright:: 2015-2018, Sous Chefs
-#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -18,56 +15,93 @@
# limitations under the License.
#
-default_action :add
+include Dhcp::Cookbook::Helpers
-property :conf_dir, String, default: '/etc/dhcp'
+property :comment, String,
+ description: 'Unparsed comment to add to the configuration file'
-include Chef::DSL::Recipe
+property :ip_version, Symbol,
+ equal_to: %i(ipv4 ipv6),
+ default: :ipv4,
+ description: 'The IP version, 4 or 6'
-attr_accessor :subnets
+property :conf_dir, String,
+ default: lazy { dhcpd_config_resource_directory(ip_version, declared_type) },
+ description: 'Directory to create configuration file in'
-def subnet(name, &block)
- @subnets ||= []
- s = dhcp_subnet("#{@name}-#{name}", &block)
- s.action :nothing
- s.subnet name
- @subnets << s
- s
-end
+property :cookbook, String,
+ default: 'dhcp',
+ description: 'Template source cookbook'
+
+property :template, String,
+ default: 'shared_network.conf.erb',
+ description: 'Template source file'
+
+property :owner, String,
+ default: lazy { dhcpd_user },
+ description: 'Generated file owner'
+
+property :group, String,
+ default: lazy { dhcpd_group },
+ description: 'Generated file group'
+
+property :mode, String,
+ default: '0640',
+ description: 'Generated file mode'
+
+property :subnets, Hash,
+ description: 'Shared subnets configuration hash'
action_class do
- include Dhcp::Helpers
+ include Dhcp::Cookbook::ResourceHelpers
end
-action :add do
- with_run_context :root do
- run_context.include_recipe 'dhcp::_service'
+action :create do
+ subnets_include = []
+
+ new_resource.subnets.each do |subnet, properties|
+ sr = edit_resource(:dhcp_subnet, "#{new_resource.name}_sharedsubnet_#{subnet}") do
+ owner new_resource.owner
+ group new_resource.group
+ mode new_resource.mode
- directory "#{new_resource.conf_dir}/shared_networks.d #{new_resource.name}" do
- path "#{new_resource.conf_dir}/shared_networks.d"
+ ip_version new_resource.ip_version
+ conf_dir new_resource.conf_dir
+ shared_network true
end
- template "#{new_resource.conf_dir}/shared_networks.d/#{new_resource.name}.conf" do
- cookbook 'dhcp'
- source 'shared_network.conf.erb'
- variables name: new_resource.name, subnets: new_resource.subnets
- owner 'root'
- group 'root'
- mode '0644'
- notifies :restart, "service[#{node['dhcp']['service_name']}]", :delayed
+ properties.each do |property, value|
+ sr.send(property, value)
end
- write_include 'shared_networks.d', new_resource.name
+ subnets_include.push("#{new_resource.conf_dir}/#{new_resource.name}_sharedsubnet_#{subnet}.conf")
end
+
+ template "#{new_resource.conf_dir}/#{new_resource.name}.conf" do
+ cookbook new_resource.cookbook
+ source new_resource.template
+
+ owner new_resource.owner
+ group new_resource.group
+ mode new_resource.mode
+
+ variables(
+ name: new_resource.name,
+ comment: new_resource.comment,
+ subnets: subnets_include
+ )
+ helpers(Dhcp::Cookbook::TemplateHelpers)
+
+ action :create
+ end
+
+ add_to_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf")
end
-action :remove do
- with_run_context :root do
- file "#{new_resource.conf_dir}/shared_networks.d/#{new_resource.name}.conf" do
- action :delete
- notifies :restart, "service[#{node['dhcp']['service_name']}]", :delayed
- notifies :send_notification, new_resource, :immediately
- end
- write_include 'shared_networks.d', new_resource.name
+action :delete do
+ file "#{new_resource.conf_dir}/#{new_resource.name}.conf" do
+ action :delete
end
+
+ remove_from_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf")
end
diff --git a/resources/subnet.rb b/resources/subnet.rb
index 19c10d6e..9e2c9963 100644
--- a/resources/subnet.rb
+++ b/resources/subnet.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
#
# Cookbook:: dhcp
# Resource:: subnet
@@ -16,78 +15,139 @@
# limitations under the License.
#
-default_action :add
-
-property :subnet, String, name_property: true
-property :broadcast, String
-property :netmask, String, required: true
-property :routers, Array, default: []
-property :options, Array, default: []
-property :ddns, String
-property :evals, Array, default: []
-property :key, Hash, default: {}
-property :zones, Array, default: []
-property :conf_dir, String, default: '/etc/dhcp'
-property :next_server, String
-
-include Chef::DSL::Recipe
-
-attr_accessor :pools
-
-def pool(&block)
- @pools ||= []
- p = dhcp_pool("#{@name}-pool#{@pools.count}", &block)
- p.action :nothing
- @pools << p
- p
-end
+include Dhcp::Cookbook::Helpers
+
+property :comment, String,
+ description: 'Unparsed comment to add to the configuration file'
+
+property :ip_version, Symbol,
+ equal_to: %i(ipv4 ipv6),
+ default: :ipv4,
+ description: 'The IP version, 4 or 6'
+
+property :conf_dir, String,
+ default: lazy { dhcpd_config_resource_directory(ip_version, declared_type) },
+ description: 'Directory to create configuration file in'
+
+property :cookbook, String,
+ default: 'dhcp',
+ description: 'Template source cookbook'
+
+property :template, String,
+ default: lazy {
+ case ip_version
+ when :ipv4
+ 'subnet.conf.erb'
+ when :ipv6
+ 'subnet6.conf.erb'
+ end
+ },
+ description: 'Template source file'
+
+property :owner, String,
+ default: lazy { dhcpd_user },
+ description: 'Generated file owner'
+
+property :group, String,
+ default: lazy { dhcpd_group },
+ description: 'Generated file group'
+
+property :mode, String,
+ default: '0640',
+ description: 'Generated file mode'
+
+property :shared_network, [true, false],
+ default: false,
+ description: 'Flag to indicate subnet is used inside a shared_network resource and should not be added to list.conf'
+
+property :subnet, String,
+ name_property: true,
+ description: 'Subnet network address'
+
+property :netmask, String,
+ description: 'Subnet network mask, required for IPv4'
+
+property :prefix, Integer,
+ description: 'Subnet network prefix, required for IPv6'
+
+property :parameters, [Hash, Array],
+ description: 'Subnet configuration parameters'
+
+property :options, [Hash, Array],
+ description: 'Subnet options'
+
+property :evals, Array,
+ description: 'Subnet conditional eval statements'
+
+property :key, Hash,
+ description: 'Subnet TSIG keys'
+
+property :zones, Hash,
+ description: 'Subnet dynamic DNS zones'
+
+property :allow, Array,
+ description: 'Subnet allow access control'
+
+property :deny, Array,
+ description: 'Subnet deny access control'
+
+property :extra_lines, Array,
+ description: 'Subnet additional configuration lines'
+
+property :pools, [Hash, Array],
+ description: 'Subnet pool(s) configuration'
+
+property :range, [String, Array]
action_class do
- include Dhcp::Helpers
+ include Dhcp::Cookbook::ResourceHelpers
end
-action :add do
- with_run_context :root do
- run_context.include_recipe 'dhcp::_service'
-
- directory "#{new_resource.conf_dir}/subnets.d #{new_resource.name}" do
- path "#{new_resource.conf_dir}/subnets.d"
- end
-
- template "#{new_resource.conf_dir}/subnets.d/#{new_resource.name}.conf" do
- cookbook 'dhcp'
- source 'subnet.conf.erb'
- variables(
- subnet: new_resource.subnet,
- netmask: new_resource.netmask,
- broadcast: new_resource.broadcast,
- pools: new_resource.pools,
- routers: new_resource.routers,
- options: new_resource.options,
- evals: new_resource.evals,
- key: new_resource.key,
- zones: new_resource.zones,
- ddns: new_resource.ddns,
- next_server: new_resource.next_server
- )
- owner 'root'
- group 'root'
- mode '0644'
- notifies :restart, "service[#{node['dhcp']['service_name']}]", :delayed
- end
-
- write_include 'subnets.d', new_resource.name
+action :create do
+ case new_resource.ip_version
+ when :ipv4
+ raise 'netmask is a required property for IPv4' unless new_resource.netmask
+ when :ipv6
+ raise 'prefix is a required property for IPv6' unless new_resource.prefix
end
-end
-action :remove do
- with_run_context :root do
- file "#{new_resource.conf_dir}/subnets.d/#{new_resource.name}.conf" do
- action :delete
- notifies :restart, "service[#{node['dhcp']['service_name']}]", :delayed
- notifies :send_notification, new_resource, :immediately
- end
+ template "#{new_resource.conf_dir}/#{new_resource.name}.conf" do
+ cookbook new_resource.cookbook
+ source new_resource.template
- write_include 'subnets.d', new_resource.name
+ owner new_resource.owner
+ group new_resource.group
+ mode new_resource.mode
+
+ variables(
+ name: new_resource.name,
+ comment: new_resource.comment,
+ subnet: new_resource.subnet,
+ netmask: new_resource.netmask,
+ prefix: new_resource.prefix,
+ parameters: new_resource.parameters,
+ options: new_resource.options,
+ evals: new_resource.evals,
+ key: new_resource.key,
+ zones: new_resource.zones,
+ allow: new_resource.allow,
+ deny: new_resource.deny,
+ extra_lines: new_resource.extra_lines,
+ pools: new_resource.pools,
+ range: new_resource.range
+ )
+ helpers(Dhcp::Cookbook::TemplateHelpers)
+
+ action :create
end
+
+ add_to_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf") unless new_resource.shared_network
+end
+
+action :delete do
+ file "#{new_resource.conf_dir}/#{new_resource.name}.conf" do
+ action :delete
+ end
+
+ remove_from_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf") unless new_resource.shared_network
end
diff --git a/spec/_config_spec.rb b/spec/_config_spec.rb
deleted file mode 100644
index b87b1d32..00000000
--- a/spec/_config_spec.rb
+++ /dev/null
@@ -1,92 +0,0 @@
-require 'spec_helper'
-
-describe 'dhcp::_config' do
- context 'when all attributes are default, on rhel 6.x' do
- cached(:chef_run) do
- ChefSpec::ServerRunner.new(platform: 'centos', version: '6.8').converge(described_recipe)
- end
-
- let(:params) do
- {
- 'default-lease-time' => '6400',
- 'ddns-domainname' => '"local"',
- 'ddns-update-style' => 'interim',
- 'max-lease-time' => '86400',
- 'update-static-leases' => 'true',
- 'one-lease-per-client' => 'true',
- 'authoritative' => '',
- 'ping-check' => 'true',
- 'next-server' => '10.0.0.2',
- 'filename' => '"pxelinux.0"',
- }
- end
-
- let(:opts) do
- {
- 'domain-name' => '"local"',
- 'domain-name-servers' => '8.8.8.8',
- 'host-name' => ' = binary-to-ascii (16, 8, "-", substring (hardware, 1, 6))',
- }
- end
-
- it 'generates a config file' do
- expect(chef_run).to create_template('/etc/dhcp/dhcpd.conf')
- .with(variables: { allows: %w(booting bootp unknown-clients),
- parameters: params, options: opts, masters: nil,
- keys: nil, my_ip: '10.0.0.2', role: nil,
- peer_ip: nil, failover: false, hooks: {} })
- expect(chef_run).to render_file('/etc/dhcp/dhcpd.conf').with_content(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'dhcpd.conf.default')))
- end
- end
-
- context 'when attributes are overridden, on rhel 6.x' do
- # Only testing overriding node['dhcp']['extra_files']
- # Need to add testing for all other attributes used by _config recipe
- cached(:chef_run) do
- ChefSpec::ServerRunner.new(platform: 'centos', version: '6.8') do |node|
- node.override['dhcp']['extra_files'] = ['/etc/dhcp/my_conf.conf', '/tmp/bad.conf']
- node.override['dhcp']['hooks'] = hooks
- end.converge(described_recipe)
- end
-
- let(:hooks) do
- {
- 'commit' => ['set clip = binary-to-ascii(10, 8, ".", leased-address);',
- 'set clhw = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6));',
- 'execute("/usr/local/sbin/dhcpevent", "commit", clip, clhw, host-decl-name);'],
- }
- end
-
- let(:params) do
- {
- 'default-lease-time' => '6400',
- 'ddns-domainname' => '"local"',
- 'ddns-update-style' => 'interim',
- 'max-lease-time' => '86400',
- 'update-static-leases' => 'true',
- 'one-lease-per-client' => 'true',
- 'authoritative' => '',
- 'ping-check' => 'true',
- 'next-server' => '10.0.0.2',
- 'filename' => '"pxelinux.0"',
- }
- end
-
- let(:opts) do
- {
- 'domain-name' => '"local"',
- 'domain-name-servers' => '8.8.8.8',
- 'host-name' => ' = binary-to-ascii (16, 8, "-", substring (hardware, 1, 6))',
- }
- end
-
- it 'generates config file with noextra config files' do
- expect(chef_run).to create_template('/etc/dhcp/dhcpd.conf')
- .with(variables: { allows: %w(booting bootp unknown-clients),
- parameters: params, options: opts, masters: nil,
- keys: nil, my_ip: '10.0.0.2', role: nil,
- peer_ip: nil, failover: false, hooks: hooks })
- expect(chef_run).to render_file('/etc/dhcp/dhcpd.conf').with_content(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'dhcpd.conf.overrides')))
- end
- end
-end
diff --git a/spec/_hosts_spec.rb b/spec/_hosts_spec.rb
deleted file mode 100644
index fd8d680f..00000000
--- a/spec/_hosts_spec.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-require 'spec_helper'
-
-describe 'dhcp::_hosts' do
- context 'when all attributes are default, on rhel 6.x' do
- cached(:chef_run) do
- ChefSpec::ServerRunner.new(platform: 'centos', version: '6.8') do |node, server|
- node.default['dhcp']['hosts'] = %w(pxe_test-vm vagrant-vm)
- server.create_data_bag('dhcp_hosts',
- 'pxe_test-vm' => parse_data_bag('dhcp_hosts/pxe_test-vm'),
- 'vagrant-vm' => parse_data_bag('dhcp_hosts/vagrant-vm'))
- end.converge(described_recipe)
- end
-
- it 'converges' do
- chef_run
- end
- end
-end
diff --git a/spec/_networks_spec.rb b/spec/_networks_spec.rb
deleted file mode 100644
index 2cd868a8..00000000
--- a/spec/_networks_spec.rb
+++ /dev/null
@@ -1,101 +0,0 @@
-require 'spec_helper'
-
-describe 'dhcp::_networks Exceptions' do
- before(:each) do
- Fauxhai.mock(platform: 'ubuntu', version: '14.04')
- @chef_run = ChefSpec::ServerRunner.new
- end
-
- it 'should not raise error unless when bags are missing' do
- @chef_run.converge 'dhcp::_networks'
- end
-end
-
-describe 'dhcp::_networks' do
- context 'driven by node attributes' do
- cached(:chef_run) do
- ChefSpec::ServerRunner.new(platform: 'centos', version: '6.8', step_into: %w(dhcp_subnet dhcp_shared_network)) do |node|
- node.default['chef_environment'] = 'production'
- node.override['dhcp']['use_bags'] = false
- node.override['dhcp']['networks'] = ['192.168.9.0/24', '192.168.11.0/24']
- node.override['dhcp']['network_data']['192.168.9.0/24'] = {
- 'routers' => ['192.168.9.1'],
- 'address' => '192.168.9.0',
- 'netmask' => '255.255.255.0',
- 'broadcast' => '192.168.9.255',
- 'range' => '192.168.9.50 192.168.9.240',
- 'options' => ['time-offset 10'],
- 'next_server' => '192.168.9.11',
- }
- node.override['dhcp']['network_data']['192.168.11.0/24'] = {
- 'address' => '192.168.11.0',
- 'netmask' => '255.255.255.0',
- }
- node.override['dhcp']['shared_network_data']['mysharednet']['subnets']['192.168.10.0/24'] = {
- 'routers' => ['192.168.10.1'],
- 'address' => '192.168.10.0',
- 'netmask' => '255.255.255.0',
- 'broadcast' => '192.168.10.255',
- 'range' => '192.168.10.50 192.168.10.240',
- 'next_server' => '192.168.10.11',
- }
- node.override['dhcp']['shared_network_data']['mysharednet']['subnets']['10.0.2.0/24'] = {
- 'address' => '10.0.2.0',
- 'netmask' => '255.255.255.0',
- }
- end.converge(described_recipe)
- end
-
- it 'declares subnet 192.168.9.0' do
- expect(chef_run).to add_dhcp_subnet('192.168.9.0')
- .with(broadcast: '192.168.9.255', netmask: '255.255.255.0', routers: ['192.168.9.1'],
- options: ['time-offset 10'], next_server: '192.168.9.11',
- conf_dir: '/etc/dhcp', evals: [], key: {}, zones: [])
-
- # Check that pools are defined for the subnet
- subnet = chef_run.dhcp_subnet '192.168.9.0'
- expect(subnet.pools.count).to eq 1
-
- # Check that the pool is defined correctly
- subnet_pool = chef_run.dhcp_pool '192.168.9.0-pool0'
- expect(subnet_pool).to do_nothing
- expect(subnet_pool.range).to eq '192.168.9.50 192.168.9.240'
- end
-
- it 'generates subnet config for 192.168.9.0' do
- expect(chef_run).to create_template '/etc/dhcp/subnets.d/192.168.9.0.conf'
- expect(chef_run).to render_file('/etc/dhcp/subnets.d/192.168.9.0.conf').with_content(File.read(File.join(File.dirname(__FILE__), 'fixtures', '192.168.9.0.conf')))
- end
-
- it 'generates blank subnet config for 192.168.11.0' do
- expect(chef_run).to create_template '/etc/dhcp/subnets.d/192.168.11.0.conf'
- expect(chef_run).to render_file('/etc/dhcp/subnets.d/192.168.11.0.conf').with_content(File.read(File.join(File.dirname(__FILE__), 'fixtures', '192.168.11.0.conf')))
- end
-
- it 'declares shared-network mysharednet' do
- expect(chef_run).to add_dhcp_shared_network('mysharednet')
- end
-
- it 'declares the subnets in the mysharednet shared-network' do
- subnet1 = chef_run.dhcp_subnet 'mysharednet-192.168.10.0'
- expect(subnet1).to do_nothing
- expect(subnet1.pools.count).to eq 1
- expect(subnet1.subnet).to eq '192.168.10.0'
- expect(subnet1.netmask).to eq '255.255.255.0'
- subnet1_pool = chef_run.dhcp_pool 'mysharednet-192.168.10.0-pool0'
- expect(subnet1_pool).to do_nothing
- expect(subnet1_pool.range).to eq '192.168.10.50 192.168.10.240'
-
- subnet2 = chef_run.dhcp_subnet 'mysharednet-10.0.2.0'
- expect(subnet2).to do_nothing
- expect(subnet2.pools).to be_nil
- expect(subnet2.subnet).to eq '10.0.2.0'
- expect(subnet2.netmask).to eq '255.255.255.0'
- end
-
- it 'generates a shared network config' do
- expect(chef_run).to create_template '/etc/dhcp/shared_networks.d/mysharednet.conf'
- expect(chef_run).to render_file('/etc/dhcp/shared_networks.d/mysharednet.conf').with_content(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'mysharednet.conf')))
- end
- end
-end
diff --git a/spec/dynadns_spec.rb b/spec/dynadns_spec.rb
deleted file mode 100644
index a2608598..00000000
--- a/spec/dynadns_spec.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-require 'spec_helper'
-require_relative '../libraries/dynadns'
-
-describe 'DHCP::DyanDns disabled' do
- cached(:chef_run) do
- ChefSpec::ServerRunner.new.converge('dhcp::library')
- end
-
- it 'should take no action if we have no zone info' do
- DHCP::DynaDns.load chef_run.node
- expect(DHCP::DynaDns.zones).to be_nil
- end
-end
-
-describe 'DHCP::DynaDns malformed' do
- cached(:chef_run) do
- ChefSpec::ServerRunner.new do |node, server|
- node.override['dns']['zones'] = %w(192.168.1.0)
-
- server.create_data_bag('dns_zones',
- '192-168-1-0' => parse_data_bag('dns_zones/192-168-1-0'),
- 'vm' => parse_data_bag('dns_zones/vm'))
- end.converge('dhcp::library')
- end
-
- it 'should not return masters without keys' do
- DHCP::DynaDns.load chef_run.node
- expect(DHCP::DynaDns.masters).to eq({})
- end
-end
-
-describe 'DHCP::DynaDns' do
- # cached does not appear to be working w/ data bags in chefspec 6+
- let(:chef_run) do
- ChefSpec::ServerRunner.new do |node, server|
- node.override['dns']['master'] = '192.168.9.9'
- node.override['dns']['zones'] = %w(vm 192.168.1.0)
- node.normal['dns']['rndc_key'] = 'dhcp-key'
-
- server.create_data_bag('dns_zones',
- '192-168-1-0' => parse_data_bag('dns_zones/192-168-1-0'),
- 'vm' => parse_data_bag('dns_zones/vm'))
- server.create_data_bag('rndc_keys', 'dhcp-key' => parse_data_bag('rndc_keys/dhcp-key'))
- end.converge('dhcp::library')
- end
-
- it 'should load defined zones' do
- DHCP::DynaDns.load(chef_run.node)
- expect(DHCP::DynaDns.load_zones.length).to eq 2
- end
-
- it 'should return masters' do
- DHCP::DynaDns.load(chef_run.node)
- expect(DHCP::DynaDns.masters).to eq('vm' => { 'master' => '192.168.1.9', 'key' => 'dhcp-key' }, '1.168.192.IN-ADDR.ARPA' => { 'master' => '192.168.9.9', 'key' => 'dhcp-key' })
- end
-
- it 'should load requested keys' do
- DHCP::DynaDns.load(chef_run.node)
- expect(DHCP::DynaDns.keys).to eq('dhcp-key' => { 'id' => 'dhcp-key', 'algorithm' => 'hmac-md5', 'secret' => 'L+Jl4+4onU4Wstfi4pdmnQ==', 'chef_type' => 'data_bag_item', 'data_bag' => 'rndc_keys' })
- end
-end
diff --git a/spec/failover_spec.rb b/spec/failover_spec.rb
deleted file mode 100644
index 5e980df8..00000000
--- a/spec/failover_spec.rb
+++ /dev/null
@@ -1,117 +0,0 @@
-require 'spec_helper'
-require_relative '../libraries/failover'
-
-describe 'DHCP::Failover' do
- cached(:chef_run) do
- ChefSpec::ServerRunner.new.converge('dhcp::library')
- end
-
- it 'should detect when disabled' do
- DHCP::Failover.load(chef_run.node)
- expect(DHCP::Failover.enabled?).to be false
- end
-end
-
-describe 'DHCP::Failover Master without slaves' do
- cached(:chef_run) do
- ChefSpec::ServerRunner.new do |node, _server|
- node.default['dhcp']['master'] = true
- node.automatic['ipaddress'] = '10.1.1.10'
- end.converge('dhcp::library')
- end
-
- it 'should disable without secondary' do
- DHCP::Failover.load(chef_run.node)
- expect(DHCP::Failover.enabled?).to be false
- end
-
- it 'should identify as primary' do
- DHCP::Failover.load(chef_run.node)
- expect(DHCP::Failover.role).to eq 'primary'
- end
-end
-
-describe 'DHCP::Failover Master with slaves' do
- let(:slave) do
- stub_node('slave', platform: 'ubuntu', version: '14.04') do |node|
- node.default['dhcp']['slave'] = true
- node.automatic['ipaddress'] = '10.1.1.20'
- end
- end
-
- let(:chef_run) do
- ChefSpec::ServerRunner.new do |node, server|
- node.default[:dhcp][:master] = true
- node.automatic[:ipaddress] = '10.1.1.10'
-
- server.create_node(slave)
- end.converge('dhcp::library')
- end
-
- it 'should identify as primary' do
- DHCP::Failover.load(chef_run.node)
- expect(DHCP::Failover.role).to eq 'primary'
- end
-
- it 'should enable when secondary found' do
- DHCP::Failover.load(chef_run.node)
- expect(DHCP::Failover.enabled?).to be true
- end
-
- it 'should find our peer' do
- DHCP::Failover.load(chef_run.node)
- expect(DHCP::Failover.peer).to eq '10.1.1.20'
- end
-end
-
-describe 'DHCP::Failover Slave no master' do
- cached(:chef_run) do
- ChefSpec::ServerRunner.new do |node|
- node.default['dhcp']['slave'] = true
- node.automatic['ipaddress'] = '10.1.1.20'
- end.converge('dhcp::library')
- end
-
- it 'should identify as secondary' do
- DHCP::Failover.load(chef_run.node)
- expect(DHCP::Failover.role).to eq 'secondary'
- end
-
- it 'should disable when no primaries found' do
- DHCP::Failover.load(chef_run.node)
- expect(DHCP::Failover.enabled?).to be false
- end
-end
-
-describe 'DHCP::Failover Slave' do
- let(:master) do
- stub_node('master', platform: 'ubuntu', version: '14.04') do |node|
- node.default['dhcp']['master'] = true
- node.automatic['ipaddress'] = '10.1.1.10'
- end
- end
-
- let(:chef_run) do
- ChefSpec::ServerRunner.new do |node, server|
- node.default[:dhcp][:slave] = true
- node.default[:ipaddress] = '10.1.1.20'
-
- server.create_node(master)
- end.converge('dhcp::library')
- end
-
- it 'should identify as secondary' do
- DHCP::Failover.load(chef_run.node)
- expect(DHCP::Failover.role).to eq 'secondary'
- end
-
- it 'should enable when primary found' do
- DHCP::Failover.load(chef_run.node)
- expect(DHCP::Failover.enabled?).to be true
- end
-
- it 'should find our peer' do
- DHCP::Failover.load(chef_run.node)
- expect(DHCP::Failover.peer).to eq '10.1.1.10'
- end
-end
diff --git a/spec/fixtures/192.168.11.0.conf b/spec/fixtures/192.168.11.0.conf
deleted file mode 100644
index 7fabbeb8..00000000
--- a/spec/fixtures/192.168.11.0.conf
+++ /dev/null
@@ -1,5 +0,0 @@
-# File managed by Chef
-
-subnet 192.168.11.0 netmask 255.255.255.0 {
- option subnet-mask 255.255.255.0;
-}
diff --git a/spec/fixtures/192.168.9.0.conf b/spec/fixtures/192.168.9.0.conf
deleted file mode 100644
index a32193b5..00000000
--- a/spec/fixtures/192.168.9.0.conf
+++ /dev/null
@@ -1,13 +0,0 @@
-# File managed by Chef
-
-subnet 192.168.9.0 netmask 255.255.255.0 {
-pool {
- range 192.168.9.50 192.168.9.240;
-}
-
- option routers 192.168.9.1;
- option subnet-mask 255.255.255.0;
- option broadcast-address 192.168.9.255;
- option time-offset 10;
- next-server 192.168.9.11;
-}
diff --git a/spec/fixtures/dhcpd.conf.default b/spec/fixtures/dhcpd.conf.default
deleted file mode 100644
index 2c784fac..00000000
--- a/spec/fixtures/dhcpd.conf.default
+++ /dev/null
@@ -1,33 +0,0 @@
-# File managed by Chef
-
-# set this to store vendor strings.
-set vendor-string = option vendor-class-identifier;
-
-allow booting;
-allow bootp;
-allow unknown-clients;
-
-authoritative ;
-ddns-domainname "local";
-ddns-update-style interim;
-default-lease-time 6400;
-filename "pxelinux.0";
-max-lease-time 86400;
-next-server 10.0.0.2;
-one-lease-per-client true;
-ping-check true;
-update-static-leases true;
-
-option domain-name "local";
-option domain-name-servers 8.8.8.8;
-option host-name = binary-to-ascii (16, 8, "-", substring (hardware, 1, 6));
-
-
-
-
-
-include "/etc/dhcp/classes.d/list.conf";
-include "/etc/dhcp/groups.d/list.conf";
-include "/etc/dhcp/subnets.d/list.conf";
-include "/etc/dhcp/shared_networks.d/list.conf";
-include "/etc/dhcp/hosts.d/list.conf";
diff --git a/spec/fixtures/dhcpd.conf.overrides b/spec/fixtures/dhcpd.conf.overrides
deleted file mode 100644
index cbac326a..00000000
--- a/spec/fixtures/dhcpd.conf.overrides
+++ /dev/null
@@ -1,41 +0,0 @@
-# File managed by Chef
-
-# set this to store vendor strings.
-set vendor-string = option vendor-class-identifier;
-
-allow booting;
-allow bootp;
-allow unknown-clients;
-
-authoritative ;
-ddns-domainname "local";
-ddns-update-style interim;
-default-lease-time 6400;
-filename "pxelinux.0";
-max-lease-time 86400;
-next-server 10.0.0.2;
-one-lease-per-client true;
-ping-check true;
-update-static-leases true;
-
-option domain-name "local";
-option domain-name-servers 8.8.8.8;
-option host-name = binary-to-ascii (16, 8, "-", substring (hardware, 1, 6));
-
-on commit {
- set clip = binary-to-ascii(10, 8, ".", leased-address);;
- set clhw = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6));;
- execute("/usr/local/sbin/dhcpevent", "commit", clip, clhw, host-decl-name);;
-}
-
-
-
-
-include "/etc/dhcp/classes.d/list.conf";
-include "/etc/dhcp/groups.d/list.conf";
-include "/etc/dhcp/subnets.d/list.conf";
-include "/etc/dhcp/shared_networks.d/list.conf";
-include "/etc/dhcp/hosts.d/list.conf";
-
-include "/etc/dhcp/my_conf.conf";
-include "/tmp/bad.conf";
diff --git a/spec/fixtures/mysharednet.conf b/spec/fixtures/mysharednet.conf
deleted file mode 100644
index 3774f001..00000000
--- a/spec/fixtures/mysharednet.conf
+++ /dev/null
@@ -1,19 +0,0 @@
-# File managed by Chef
-
-shared-network mysharednet {
- subnet 192.168.10.0 netmask 255.255.255.0 {
-pool {
- range 192.168.10.50 192.168.10.240;
-}
-
- option routers 192.168.10.1;
- option subnet-mask 255.255.255.0;
- option broadcast-address 192.168.10.255;
- next-server 192.168.10.11;
-}
-
- subnet 10.0.2.0 netmask 255.255.255.0 {
- option subnet-mask 255.255.255.0;
-}
-
-}
diff --git a/spec/providers/dhcp_class_spec.rb b/spec/providers/dhcp_class_spec.rb
deleted file mode 100644
index f6a74f32..00000000
--- a/spec/providers/dhcp_class_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require 'spec_helper'
-
-describe 'test::dhcp_class' do
- cached(:chef_run) do
- ChefSpec::ServerRunner.new(platform: 'centos', version: '6.8', step_into: ['dhcp_class']).converge(described_recipe)
- end
-
- let(:list_conf_contents) do
- '#
-# file managed by chef
-# Host entry includes
-#
-include "/etc/dhcp/classes.d/BlankClass.conf";
-include "/etc/dhcp/classes.d/RegisteredHosts.conf";'
- end
-
- it 'creates classes.d directory' do
- expect(chef_run).to create_directory '/etc/dhcp/classes.d'
- end
-
- it 'creates list.conf to include other subconfig files' do
- expect(chef_run).to create_template '/etc/dhcp/classes.d/list.conf'
- expect(chef_run).to render_file('/etc/dhcp/classes.d/list.conf').with_content(list_conf_contents)
- end
-
- it 'generates a blank class config' do
- expect(chef_run).to create_template '/etc/dhcp/classes.d/BlankClass.conf'
- expect(chef_run).to render_file('/etc/dhcp/classes.d/BlankClass.conf').with_content(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'dhcp_class_blank.conf')))
- end
-
- it 'generates class config with subclasses' do
- expect(chef_run).to create_template '/etc/dhcp/classes.d/RegisteredHosts.conf'
- expect(chef_run).to render_file('/etc/dhcp/classes.d/RegisteredHosts.conf').with_content(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'dhcp_class_registered_hosts.conf')))
- end
-end
diff --git a/spec/providers/dhcp_shared_network_spec.rb b/spec/providers/dhcp_shared_network_spec.rb
deleted file mode 100644
index 3ec3a71e..00000000
--- a/spec/providers/dhcp_shared_network_spec.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-require 'spec_helper'
-
-describe 'test::dhcp_shared_network' do
- cached(:chef_run) do
- ChefSpec::ServerRunner.new(platform: 'centos', version: '6.8', step_into: ['dhcp_shared_network']).converge(described_recipe)
- end
-
- it 'generates a shared network with a single network' do
- expect(chef_run).to create_template '/etc/dhcp/shared_networks.d/single.conf'
- expect(chef_run).to render_file('/etc/dhcp/shared_networks.d/single.conf').with_content(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'dhcp_shared_network.single')))
- end
-
- it 'generates a shared network with multiple networks' do
- expect(chef_run).to create_template '/etc/dhcp/shared_networks.d/multiple.conf'
- expect(chef_run).to render_file('/etc/dhcp/shared_networks.d/multiple.conf').with_content(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'dhcp_shared_network.multiple')))
- end
-end
diff --git a/spec/providers/dhcp_subnet_spec.rb b/spec/providers/dhcp_subnet_spec.rb
deleted file mode 100644
index 6b8b5b03..00000000
--- a/spec/providers/dhcp_subnet_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require 'spec_helper'
-
-describe 'test::dhcp_subnet' do
- cached(:chef_run) do
- ChefSpec::ServerRunner.new(platform: 'centos', version: '6.8', step_into: ['dhcp_subnet']).converge(described_recipe)
- end
-
- it 'generates a default config' do
- expect(chef_run).to create_template '/etc/dhcp/subnets.d/10.0.2.0.conf'
- expect(chef_run).to render_file('/etc/dhcp/subnets.d/10.0.2.0.conf').with_content(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'dhcp_subnet.default')))
- end
-
- it 'generates a basic subnet config' do
- expect(chef_run).to create_template '/etc/dhcp/subnets.d/basic.conf'
- expect(chef_run).to render_file('/etc/dhcp/subnets.d/basic.conf').with_content(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'dhcp_subnet.basic')))
- end
-
- it 'generates an overriden config' do
- expect(chef_run).to create_template '/etc/dhcp_override/subnets.d/overrides.conf'
- expect(chef_run).to render_file('/etc/dhcp_override/subnets.d/overrides.conf').with_content(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'dhcp_subnet.overrides')))
- end
-end
diff --git a/spec/providers/fixtures/dhcp_class_blank.conf b/spec/providers/fixtures/dhcp_class_blank.conf
deleted file mode 100644
index 512b0ec0..00000000
--- a/spec/providers/fixtures/dhcp_class_blank.conf
+++ /dev/null
@@ -1,3 +0,0 @@
-class "BlankClass" {
- match hardware;
-}
diff --git a/spec/providers/fixtures/dhcp_class_registered_hosts.conf b/spec/providers/fixtures/dhcp_class_registered_hosts.conf
deleted file mode 100644
index 3d704854..00000000
--- a/spec/providers/fixtures/dhcp_class_registered_hosts.conf
+++ /dev/null
@@ -1,5 +0,0 @@
-class "RegisteredHosts" {
- match hardware;
-}
-subclass "RegisteredHosts" 1:10:bf:48:42:55:01;
-subclass "RegisteredHosts" 1:10:bf:48:42:55:02;
diff --git a/spec/providers/fixtures/dhcp_shared_network.multiple b/spec/providers/fixtures/dhcp_shared_network.multiple
deleted file mode 100644
index b0a97709..00000000
--- a/spec/providers/fixtures/dhcp_shared_network.multiple
+++ /dev/null
@@ -1,25 +0,0 @@
-# File managed by Chef
-
-shared-network multiple {
- subnet 192.168.2.0 netmask 255.255.255.0 {
-pool {
- range 192.168.2.20 192.168.2.30;
-}
-
- option routers 192.168.2.1;
- option subnet-mask 255.255.255.0;
- option broadcast-address 192.168.2.255;
-}
-
- subnet 192.168.3.0 netmask 255.255.255.0 {
-pool {
- range 192.168.3.20 192.168.3.30;
- range 192.168.3.40 192.168.3.50;
-}
-
- option routers 192.168.3.1;
- option subnet-mask 255.255.255.0;
- option broadcast-address 192.168.3.255;
-}
-
-}
diff --git a/spec/providers/fixtures/dhcp_shared_network.single b/spec/providers/fixtures/dhcp_shared_network.single
deleted file mode 100644
index c3390ad0..00000000
--- a/spec/providers/fixtures/dhcp_shared_network.single
+++ /dev/null
@@ -1,14 +0,0 @@
-# File managed by Chef
-
-shared-network single {
- subnet 192.168.1.0 netmask 255.255.255.0 {
-pool {
- range 192.168.1.20 192.168.1.30;
-}
-
- option routers 192.168.1.1;
- option subnet-mask 255.255.255.0;
- option broadcast-address 192.168.1.255;
-}
-
-}
diff --git a/spec/providers/fixtures/dhcp_subnet.basic b/spec/providers/fixtures/dhcp_subnet.basic
deleted file mode 100644
index 9e3eed41..00000000
--- a/spec/providers/fixtures/dhcp_subnet.basic
+++ /dev/null
@@ -1,12 +0,0 @@
-# File managed by Chef
-
-subnet 192.168.0.0 netmask 255.255.255.0 {
-pool {
- range 192.168.0.100 192.168.0.200;
-}
-
- option routers 192.168.0.1;
- option subnet-mask 255.255.255.0;
- option broadcast-address 192.168.0.255;
- option time-offset 10;
-}
diff --git a/spec/providers/fixtures/dhcp_subnet.default b/spec/providers/fixtures/dhcp_subnet.default
deleted file mode 100644
index b5634793..00000000
--- a/spec/providers/fixtures/dhcp_subnet.default
+++ /dev/null
@@ -1,5 +0,0 @@
-# File managed by Chef
-
-subnet 10.0.2.0 netmask 255.255.254.0 {
- option subnet-mask 255.255.254.0;
-}
diff --git a/spec/providers/fixtures/dhcp_subnet.overrides b/spec/providers/fixtures/dhcp_subnet.overrides
deleted file mode 100644
index aa963acc..00000000
--- a/spec/providers/fixtures/dhcp_subnet.overrides
+++ /dev/null
@@ -1,35 +0,0 @@
-# File managed by Chef
-
-subnet 192.168.0.0 netmask 255.255.255.0 {
-pool {
- failover peer "local";
- range 192.168.0.100 192.168.0.200;
- deny members of "RegisteredHosts";
- allow members of "UnregisteredHosts";
- allow members of "OtherHosts";
-}
-
- ddns-domainname "test.com";
- option routers 192.168.0.1;
- option subnet-mask 255.255.255.0;
- option broadcast-address 192.168.0.255;
- option time-offset 10;
- next-server 192.168.0.3;
-
- if exists user-class and option user-class = "iPXE" {
- filename "bootstrap.ipxe";
- } else {
- filename "undionly.kpxe";
- }
-
- key test_key {
- algorithm test_algo;
- secret "test_secret";
- };
-
- zone test {
- primary test_pri;
- key test_key;
- }
-
-}
diff --git a/spec/server_spec.rb b/spec/server_spec.rb
deleted file mode 100644
index 9761e64f..00000000
--- a/spec/server_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'spec_helper'
-
-describe 'dhcp::server' do
- let(:chef_run) do
- ChefSpec::ServerRunner.new do |node|
- node.default['chef_environment'] = 'production'
- end.converge(described_recipe)
- end
-end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 04a26677..773d5579 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,15 +1,8 @@
require 'chefspec'
require 'chefspec/berkshelf'
-require 'json'
-require 'pry'
RSpec.configure do |config|
- config.platform = 'ubuntu'
- config.version = '14.04'
- config.log_level = :error
-end
-
-def parse_data_bag(path)
- data_bags_path = File.expand_path(File.join(File.dirname(__FILE__), '../test/integration/data_bags'))
- JSON.parse(File.read("#{data_bags_path}/#{path}.json"))
+ config.color = true # Use color in STDOUT
+ config.formatter = :documentation # Use the specified formatter
+ config.log_level = :error # Avoid deprecation notice SPAM
end
diff --git a/spec/unit/recipes/class_spec.rb b/spec/unit/recipes/class_spec.rb
new file mode 100644
index 00000000..6c7a1b40
--- /dev/null
+++ b/spec/unit/recipes/class_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe 'dhcp_class' do
+ step_into :dhcp_class
+ platform 'centos'
+
+ context 'create a dhcpd class with no subclass and verify config is created properly' do
+ recipe do
+ dhcp_class 'BlankClass' do
+ match 'hardware'
+ end
+ end
+
+ it 'Creates the class configuration file correctly' do
+ is_expected.to render_file('/etc/dhcp/dhcpd.d/classes.d/BlankClass.conf')
+ .with_content(/class "BlankClass" {/)
+ .with_content(/ match hardware;/)
+ end
+ end
+
+ context 'create a dhcpd class with subclass and verify config is created properly' do
+ recipe do
+ dhcp_class 'RegisteredHosts' do
+ match 'hardware'
+ subclass [
+ '1:10:bf:48:42:55:01',
+ '1:10:bf:48:42:55:02',
+ ]
+ end
+ end
+
+ it 'Creates the class configuration file correctly' do
+ is_expected.to render_file('/etc/dhcp/dhcpd.d/classes.d/RegisteredHosts.conf')
+ .with_content(/class "RegisteredHosts" {/)
+ .with_content(/ match hardware;/)
+ .with_content(/subclass "RegisteredHosts" 1:10:bf:48:42:55:01;/)
+ end
+ end
+end
diff --git a/spec/unit/recipes/config_spec.rb b/spec/unit/recipes/config_spec.rb
new file mode 100644
index 00000000..b8d916b8
--- /dev/null
+++ b/spec/unit/recipes/config_spec.rb
@@ -0,0 +1,77 @@
+require 'spec_helper'
+
+describe 'dhcp_config' do
+ step_into :dhcp_config
+ platform 'centos'
+
+ context 'create a dhcpd config and verify config is created properly' do
+ recipe do
+ dhcp_config '/etc/dhcp/dhcpd.conf' do
+ allow %w(booting bootp unknown-clients)
+ parameters(
+ 'default-lease-time' => 7200,
+ 'ddns-update-style' => 'interim',
+ 'max-lease-time' => 86400,
+ 'update-static-leases' => true,
+ 'one-lease-per-client' => true,
+ 'authoritative' => '',
+ 'ping-check' => true
+ )
+ options(
+ 'domain-name' => '"test.domain.local"',
+ 'domain-name-servers' => '8.8.8.8',
+ 'host-name' => ' = binary-to-ascii (16, 8, "-", substring (hardware, 1, 6))'
+ )
+ hooks(
+ 'commit' => ['use-host-decl-names on'],
+ 'release' => ['use-host-decl-names on']
+ )
+ include_files [
+ '/etc/dhcp/extra1.conf',
+ '/etc/dhcp/extra2.conf',
+ '/etc/dhcp_override/list.conf',
+ ]
+ action :create
+ end
+ end
+
+ it 'Creates the main configuration file' do
+ is_expected.to render_file('/etc/dhcp/dhcpd.conf')
+ .with_content(/authoritative/)
+ .with_content(/default-lease-time 7200/)
+ .with_content(/option domain-name-servers 8.8.8.8;/)
+ .with_content(%r{include "/etc/dhcp/dhcpd.d/classes.d/list.conf";})
+ end
+ end
+
+ context 'create a dhcpd6 config and verify config is created properly' do
+ recipe do
+ dhcp_config '/etc/dhcp/dhcpd6.conf' do
+ ip_version :ipv6
+ deny %w(duplicates)
+ parameters(
+ 'default-lease-time' => 7200,
+ 'ddns-updates' => 'on',
+ 'ddns-update-style' => 'interim',
+ 'max-lease-time' => 86400,
+ 'update-static-leases' => true,
+ 'one-lease-per-client' => 'on',
+ 'authoritative' => '',
+ 'ping-check' => true
+ )
+ options(
+ 'dhcp6.name-servers' => '2001:4860:4860::8888, 2001:4860:4860::8844'
+ )
+ action :create
+ end
+ end
+
+ it 'Creates the main configuration file' do
+ is_expected.to render_file('/etc/dhcp/dhcpd6.conf')
+ .with_content(/authoritative/)
+ .with_content(/default-lease-time 7200/)
+ .with_content(/option dhcp6.name-servers 2001:4860:4860::8888, 2001:4860:4860::8844;/)
+ .with_content(%r{include "/etc/dhcp/dhcpd6.d/classes.d/list.conf";})
+ end
+ end
+end
diff --git a/spec/unit/recipes/default_spec.rb b/spec/unit/recipes/default_spec.rb
new file mode 100644
index 00000000..fa23370c
--- /dev/null
+++ b/spec/unit/recipes/default_spec.rb
@@ -0,0 +1,9 @@
+require 'spec_helper'
+
+describe 'Default recipe on CentOS 7' do
+ let(:runner) { ChefSpec::ServerRunner.new(platform: 'centos', version: '7', step_into: ['dhcp_package']) }
+
+ it 'converges successfully' do
+ expect { :chef_run }.to_not raise_error
+ end
+end
diff --git a/spec/unit/recipes/group_spec.rb b/spec/unit/recipes/group_spec.rb
new file mode 100644
index 00000000..254e6abc
--- /dev/null
+++ b/spec/unit/recipes/group_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe 'dhcp_group' do
+ step_into :dhcp_group, :dhcp_host
+ platform 'centos'
+
+ context 'create a dhcpd group and verify config is created properly' do
+ recipe do
+ dhcp_group 'ip-phones' do
+ options(
+ 'tftp-server-name' => '"192.0.2.10"'
+ )
+ hosts(
+ 'SEP010101010101' => {
+ 'identifier' => 'hardware ethernet 01:01:01:01:01:01',
+ }
+ )
+ end
+ end
+
+ it 'Creates the group configuration file correctly' do
+ is_expected.to render_file('/etc/dhcp/dhcpd.d/groups.d/ip-phones.conf')
+ .with_content(/tftp-server-name "192.0.2.10";/)
+ .with_content(%r{include "/etc/dhcp/dhcpd.d/groups.d/ip-phones_grouphost_SEP010101010101.conf";})
+ end
+
+ it 'Creates the nested host configuration file correctly' do
+ is_expected.to render_file('/etc/dhcp/dhcpd.d/groups.d/ip-phones_grouphost_SEP010101010101.conf')
+ .with_content(/host ip-phones_grouphost_SEP010101010101 {/)
+ .with_content(/hardware ethernet 01:01:01:01:01:01;/)
+ end
+ end
+end
diff --git a/spec/unit/recipes/host_spec.rb b/spec/unit/recipes/host_spec.rb
new file mode 100644
index 00000000..b184613e
--- /dev/null
+++ b/spec/unit/recipes/host_spec.rb
@@ -0,0 +1,41 @@
+require 'spec_helper'
+
+describe 'dhcp_host' do
+ step_into :dhcp_host
+ platform 'centos'
+
+ context 'create a dhcpd host and verify config is created properly' do
+ recipe do
+ dhcp_host 'Test-IPv4-Host' do
+ options 'host-name' => 'test-ipv4-host'
+ identifier 'hardware ethernet 00:53:00:00:00:01'
+ address '192.168.0.10'
+ end
+ end
+
+ it 'Creates the class configuration file correctly' do
+ is_expected.to render_file('/etc/dhcp/dhcpd.d/hosts.d/Test-IPv4-Host.conf')
+ .with_content(/host Test-IPv4-Host {/)
+ .with_content(/hardware ethernet 00:53:00:00:00:01;/)
+ .with_content(/option host-name test-ipv4-host;/)
+ end
+ end
+
+ context 'create a dhcpd6 host and verify config is created properly' do
+ recipe do
+ dhcp_host 'Test-IPv6-Host' do
+ ip_version :ipv6
+ options 'host-name' => 'test-ipv6-host'
+ identifier 'host-identifier option dhcp6.client-id 00:53:00:00:00:01:a4:65:b7:c8'
+ address '2001:db8:1:1:0:0:1:10'
+ end
+ end
+
+ it 'Creates the class configuration file correctly' do
+ is_expected.to render_file('/etc/dhcp/dhcpd6.d/hosts.d/Test-IPv6-Host.conf')
+ .with_content(/host Test-IPv6-Host {/)
+ .with_content(/host-identifier option dhcp6.client-id 00:53:00:00:00:01:a4:65:b7:c8;/)
+ .with_content(/option host-name test-ipv6-host;/)
+ end
+ end
+end
diff --git a/spec/unit/recipes/shared_network_spec.rb b/spec/unit/recipes/shared_network_spec.rb
new file mode 100644
index 00000000..b2803a62
--- /dev/null
+++ b/spec/unit/recipes/shared_network_spec.rb
@@ -0,0 +1,87 @@
+require 'spec_helper'
+
+describe 'dhcp_shared_network' do
+ step_into :dhcp_shared_network, :dhcp_subnet
+ platform 'centos'
+
+ context 'create a dhcpd single shared network and verify config is created properly' do
+ recipe do
+ dhcp_shared_network 'single' do
+ subnets(
+ '192.168.1.0' => {
+ 'subnet' => '192.168.1.0',
+ 'netmask' => '255.255.255.0',
+ 'options' => {
+ 'broadcast-address' => '192.168.1.255',
+ 'routers' => '192.168.1.1',
+ },
+ 'pools' => {
+ 'range' => '192.168.1.20 192.168.1.30',
+ },
+ }
+ )
+ end
+ end
+
+ it 'Creates the shared network configuration file correctly' do
+ is_expected.to render_file('/etc/dhcp/dhcpd.d/shared_networks.d/single.conf')
+ .with_content(%r{include "/etc/dhcp/dhcpd.d/shared_networks.d/single_sharedsubnet_192.168.1.0.conf";})
+ end
+
+ it 'Creates the nested subnet configuration file correctly' do
+ is_expected.to render_file('/etc/dhcp/dhcpd.d/shared_networks.d/single_sharedsubnet_192.168.1.0.conf')
+ .with_content(/subnet 192.168.1.0 netmask 255.255.255.0 {/)
+ .with_content(/range 192.168.1.20 192.168.1.30;/)
+ end
+ end
+
+ context 'create a dhcpd multiple shared network and verify config is created properly' do
+ recipe do
+ dhcp_shared_network 'multiple' do
+ subnets(
+ '192.168.2.0' => {
+ 'subnet' => '192.168.2.0',
+ 'netmask' => '255.255.255.0',
+ 'options' => {
+ 'broadcast-address' => '192.168.2.255',
+ 'routers' => '192.168.2.1',
+ },
+ 'pools' => {
+ 'range' => '192.168.2.20 192.168.2.30',
+ },
+ },
+ '192.168.3.0' => {
+ 'subnet' => '192.168.3.0',
+ 'netmask' => '255.255.255.0',
+ 'options' => {
+ 'broadcast-address' => '192.168.3.255',
+ 'routers' => '192.168.3.1',
+ },
+ 'pools' => {
+ 'range' => [
+ '192.168.3.20 192.168.3.30',
+ '192.168.3.40 192.168.3.50',
+ ],
+ },
+ }
+ )
+ end
+ end
+
+ it 'Creates the shared network configuration file correctly' do
+ is_expected.to render_file('/etc/dhcp/dhcpd.d/shared_networks.d/multiple.conf')
+ .with_content(%r{include "/etc/dhcp/dhcpd.d/shared_networks.d/multiple_sharedsubnet_192.168.2.0.conf";})
+ .with_content(%r{include "/etc/dhcp/dhcpd.d/shared_networks.d/multiple_sharedsubnet_192.168.3.0.conf";})
+ end
+
+ it 'Creates the nested subnet configuration files correctly' do
+ is_expected.to render_file('/etc/dhcp/dhcpd.d/shared_networks.d/multiple_sharedsubnet_192.168.2.0.conf')
+ .with_content(/subnet 192.168.2.0 netmask 255.255.255.0 {/)
+ .with_content(/range 192.168.2.20 192.168.2.30;/)
+ is_expected.to render_file('/etc/dhcp/dhcpd.d/shared_networks.d/multiple_sharedsubnet_192.168.3.0.conf')
+ .with_content(/subnet 192.168.3.0 netmask 255.255.255.0 {/)
+ .with_content(/option broadcast-address 192.168.3.255;/)
+ .with_content(/range 192.168.3.40 192.168.3.50;/)
+ end
+ end
+end
diff --git a/spec/unit/recipes/subnet_spec.rb b/spec/unit/recipes/subnet_spec.rb
new file mode 100644
index 00000000..999c7246
--- /dev/null
+++ b/spec/unit/recipes/subnet_spec.rb
@@ -0,0 +1,108 @@
+require 'spec_helper'
+
+describe 'dhcp_subnet' do
+ step_into :dhcp_subnet
+ platform 'centos'
+
+ context 'create a dhcpd subnet for listening only and verify config is created properly' do
+ recipe do
+ dhcp_subnet '192.168.9.0' do
+ comment 'Listen Subnet Declaration'
+ subnet '192.168.9.0'
+ netmask '255.255.255.0'
+ end
+ end
+
+ it 'Creates the subnet configuration file correctly' do
+ is_expected.to render_file('/etc/dhcp/dhcpd.d/subnets.d/192.168.9.0.conf')
+ .with_content(/# 192.168.9.0 - Listen Subnet Declaration/)
+ .with_content(/subnet 192.168.9.0 netmask 255.255.255.0 {\n}/)
+ end
+ end
+
+ context 'create a dhcpd subnet with options as hash and verify config is created properly' do
+ recipe do
+ dhcp_subnet 'overrides' do
+ comment 'Overrides Subnet Declaration'
+ subnet '192.168.1.0'
+ netmask '255.255.255.0'
+ options(
+ 'routers' => '192.168.1.1',
+ 'time-offset' => 10,
+ 'broadcast-address' => '192.168.0.255'
+ )
+ pools(
+ 'peer' => '192.168.0.2',
+ 'range' => '192.168.1.100 192.168.1.200',
+ 'deny' => 'members of "RegisteredHosts"',
+ 'allow' => ['members of "UnregisteredHosts"', 'members of "OtherHosts"']
+ )
+ parameters(
+ 'ddns-domainname' => '"test.com"',
+ 'next-server' => '192.168.0.3'
+ )
+ evals [ 'if exists user-class and option user-class = "iPXE" {
+ filename "bootstrap.ipxe";
+ } else {
+ filename "undionly.kpxe";
+ }' ]
+ key 'name' => 'test_key', 'algorithm' => 'hmac-sha256', 'secret' => 'c7nBOcB2rbJh7lYCI65/PGrS6QdlLMCPe2xunZ4dij8='
+ zones 'test' => { 'primary' => 'test_pri', 'key' => 'test_key' }
+ conf_dir '/etc/dhcp_override'
+ end
+ end
+
+ it 'Creates the subnet configuration file correctly' do
+ is_expected.to render_file('/etc/dhcp_override/overrides.conf')
+ .with_content(/subnet 192.168.1.0 netmask 255.255.255.0 {/)
+ .with_content(/option routers 192.168.1.1;/)
+ .with_content(/if exists user-class and option user-class = "iPXE" {/)
+ .with_content(/key test_key {/)
+ .with_content(/zone test {/)
+ .with_content(/deny members of "RegisteredHosts";/)
+ end
+ end
+
+ context 'create a dhcpd subnet with parameters as array and verify config is created properly' do
+ recipe do
+ dhcp_subnet 'overrides' do
+ comment 'Overrides Subnet Declaration'
+ subnet '192.168.1.0'
+ netmask '255.255.255.0'
+ options(
+ 'routers' => '192.168.1.1',
+ 'time-offset' => 10,
+ 'broadcast-address' => '192.168.0.255'
+ )
+ pools(
+ 'peer' => '192.168.0.2',
+ 'range' => '192.168.1.100 192.168.1.200',
+ 'deny' => 'members of "RegisteredHosts"',
+ 'allow' => ['members of "UnregisteredHosts"', 'members of "OtherHosts"']
+ )
+ parameters [
+ 'ddns-domainname "test.com"',
+ 'next-server 192.168.0.3',
+ ]
+ evals [ 'if exists user-class and option user-class = "iPXE" {
+ filename "bootstrap.ipxe";
+ } else {
+ filename "undionly.kpxe";
+ }' ]
+ key 'name' => 'test_key', 'algorithm' => 'hmac-sha256', 'secret' => 'c7nBOcB2rbJh7lYCI65/PGrS6QdlLMCPe2xunZ4dij8='
+ zones 'test' => { 'primary' => 'test_pri', 'key' => 'test_key' }
+ conf_dir '/etc/dhcp_override'
+ end
+ end
+
+ it 'Creates the subnet configuration file correctly' do
+ is_expected.to render_file('/etc/dhcp_override/overrides.conf')
+ .with_content(/subnet 192.168.1.0 netmask 255.255.255.0 {/)
+ .with_content(/option routers 192.168.1.1;/)
+ .with_content(/if exists user-class and option user-class = "iPXE" {/)
+ .with_content(/key test_key {/)
+ .with_content(/zone test {/)
+ .with_content(/deny members of "RegisteredHosts";/)
+ end
+ end
+end
diff --git a/templates/default/class.conf.erb b/templates/default/class.conf.erb
index 7c248136..fe6bf2f3 100644
--- a/templates/default/class.conf.erb
+++ b/templates/default/class.conf.erb
@@ -1,9 +1,28 @@
-# File managed by Chef
+#
+# Generated by Chef for <%= node['fqdn'] %>
+# Do NOT modify this file by hand, changes will be overwritten.
+#
+# <%= @name -%><% if @comment -%> - <%= @comment -%><% end -%>
class "<%= @name %>" {
match <%= @match %>;
+ <% unless nil_or_empty?(@parameters) -%>
+
+ # Parameters
+ <% property_collection_sorted(@parameters).each do |parameter, value| -%>
+ <%= parameter %><% unless nil_or_empty?(value) -%> <%= value %><% end -%>;
+ <% end -%>
+ <% end -%>
+ <% unless nil_or_empty?(@options) -%>
+
+ # Options
+ <% property_collection_sorted(@options).each do |option, value| -%>
+ option <%= option %> <%= value %>;
+ <% end -%>
+ <% end -%>
}
-<% unless @subclasses.nil? || @subclasses.empty? -%>
+<% unless nil_or_empty?(@subclasses) -%>
+
<% @subclasses.each do |value| -%>
subclass "<%= @name %>" <%= value %>;
<% end -%>
diff --git a/templates/default/dhcp-server.erb b/templates/default/dhcp-server.erb
deleted file mode 100644
index 2caf3e8b..00000000
--- a/templates/default/dhcp-server.erb
+++ /dev/null
@@ -1,13 +0,0 @@
-# Defaults for dhcp initscript
-# sourced by /etc/init.d/dhcp
-
-#
-# This is a POSIX shell fragment
-#
-
-# On what interfaces should the DHCP server (dhcpd) serve DHCP requests?
-# Separate multiple interfaces with spaces, e.g. "eth0 eth1".
-<% unless @interfaces.empty? -%>
-INTERFACES="<%= @interfaces.map {|i| i }.join(" ") %>"
-<% end -%>
-
diff --git a/templates/default/dhcpd-env.erb b/templates/default/dhcpd-env.erb
new file mode 100644
index 00000000..4f4a45f9
--- /dev/null
+++ b/templates/default/dhcpd-env.erb
@@ -0,0 +1,18 @@
+#
+# Generated by Chef for <%= node['fqdn'] %>
+# Do NOT modify this file by hand, changes will be overwritten.
+#
+
+<% if nil_or_empty?(@lines) -%>
+# WARNING: This file is NOT used anymore.
+
+# If you are here to restrict what interfaces should dhcpd listen on,
+# be aware that dhcpd listens *only* on interfaces for which it finds subnet
+# declaration in dhcpd.conf. It means that explicitly enumerating interfaces
+# also on command line should not be required in most cases.
+<% else -%>
+# User specified extra lines
+<% property_array(@lines).each do |line| -%>
+<%= line %>
+<% end -%>
+<% end -%>
diff --git a/templates/default/dhcpd.conf.erb b/templates/default/dhcpd.conf.erb
index 55fe6df9..19f4f9a0 100644
--- a/templates/default/dhcpd.conf.erb
+++ b/templates/default/dhcpd.conf.erb
@@ -1,21 +1,64 @@
-# File managed by Chef
+#
+# Generated by Chef for <%= node['fqdn'] %>
+# Do NOT modify this file by hand, changes will be overwritten.
+#
-# set this to store vendor strings.
+# The following line populates the lease file with the Vendor Class Identifier that the client sends.
set vendor-string = option vendor-class-identifier;
+<% unless nil_or_empty?(@failover) -%>
-<% @allows.each do |allow| -%>
+# DHCP Failover
+include "/etc/dhcp/dhcpd.failover.conf";
+<% end -%>
+<% unless nil_or_empty?(@parameters) -%>
+
+# Global Parameters
+<% property_collection_sorted(@parameters).each do |parameter, value| -%>
+<%= parameter %><% unless nil_or_empty?(value) -%> <%= value %><% end -%>;
+<% end -%>
+<% end -%>
+<% unless nil_or_empty?(@options) -%>
+
+# Global Options
+<% property_collection(@options).each do |option, value| -%>
+option <%= option %> <%= value %>;
+<% end -%>
+<% end -%>
+<% unless nil_or_empty?(@evals) -%>
+
+# Evals
+<% @evals.each do |eval| -%>
+<%= eval %>
+<% end -%>
+<% end -%>
+<% unless nil_or_empty?(@allow) && nil_or_empty?(@deny) && nil_or_empty?(@ignore) -%>
+
+# Request Control
+<% unless nil_or_empty?(@allow) -%>
+
+# Allow
+<% @allow.each do |allow| -%>
allow <%= allow %>;
<% end -%>
+<% end -%>
+<% unless nil_or_empty?(@deny) -%>
-<% @parameters.sort.each do |key, value| -%>
-<%= key %> <%= value %>;
+# Deny
+<% @deny.each do |deny| -%>
+deny <%= deny %>;
+<% end -%>
<% end -%>
+<% unless nil_or_empty?(@ignore) -%>
-<% @options.sort.each do |key, value| -%>
-option <%= key %> <%= value %>;
+# Ignore
+<% @ignore.each do |ignore| -%>
+ignore <%= ignore %>;
+<% end -%>
<% end -%>
+<% end -%>
+<% unless nil_or_empty?(@hooks) -%>
-<% unless @hooks.nil? || @hooks.empty? -%>
+# Events
<% @hooks.each do |key, value| -%>
on <%= key %> {
<% value.each do |v| -%>
@@ -24,49 +67,44 @@ on <%= key %> {
}
<% end -%>
<% end -%>
+<% unless nil_or_empty?(@keys) -%>
-<% unless @keys.nil? || @keys.empty? -%>
- <% @keys.each do |key, data| -%>
+# TSIG Keys
+<% @keys.each do |key, data| -%>
key "<%= key %>" {
algorithm <%= data['algorithm'] %>;
secret "<%= data['secret'] %>";
};
- <%end -%>
<%end -%>
+<%end -%>
+<% unless nil_or_empty?(@zones) -%>
-<% unless @masters.nil? || @masters.empty? -%>
-<% @masters.each do |zone, data| -%>
+# Dynamic DNS Zones
+<% @zones.each do |zone, data| -%>
zone <%= zone %>. {
- primary <%= data["master"] %>;
- key "<%= data["key"] %>";
+ primary <%= data["primary"] %>;<% if data.key?('key') -%>
+ key "<%= data["key"] =%>";<% end -%>
}
- <% end -%>
+<% end -%>
<% end -%>
-<% if @failover %>
-failover peer "<%= node['domain'] %>" {
- <%= @role %>;
- address <%= @my_ip %>;
- port <%= node['dhcp']['failover_params']['port'] %>;
- peer address <%= @peer_ip %>;
- peer port <%= node['dhcp']['failover_params']['peer_port'] %>;
- max-response-delay 60;
- max-unacked-updates 10;
- mclt <%= node['dhcp']['failover_params']['mclt'] %>;
- auto-partner-down <%= node['dhcp']['failover_params']['auto_partner_down'] %> ;
- <% if @role =~ /primary/i -%>
- split 128;
- load balance max seconds 3;
- <% end -%>
-}
-<% end %>
-
-include "<%= node['dhcp']['dir'] %>/classes.d/list.conf";
-include "<%= node['dhcp']['dir'] %>/groups.d/list.conf";
-include "<%= node['dhcp']['dir'] %>/subnets.d/list.conf";
-include "<%= node['dhcp']['dir'] %>/shared_networks.d/list.conf";
-include "<%= node['dhcp']['dir'] %>/hosts.d/list.conf";
+# Includes
+include "<%= @includes_dir %>/classes.d/list.conf";
+include "<%= @includes_dir %>/groups.d/list.conf";
+include "<%= @includes_dir %>/subnets.d/list.conf";
+include "<%= @includes_dir %>/shared_networks.d/list.conf";
+include "<%= @includes_dir %>/hosts.d/list.conf";
+<% unless nil_or_empty?(@include_files) -%>
-<% node['dhcp']['extra_files'].each do |file| -%>
+# Custom Includes
+<% @include_files.each do |file| -%>
include "<%= file %>";
<% end -%>
+<% end -%>
+<% unless nil_or_empty?(@extra_lines) -%>
+
+# User specified extra lines
+<% property_array(@extra_lines).each do |line| -%>
+<%= line %>
+<% end -%>
+<% end -%>
diff --git a/templates/default/dhcpd.failover.conf.erb b/templates/default/dhcpd.failover.conf.erb
new file mode 100644
index 00000000..b1797f04
--- /dev/null
+++ b/templates/default/dhcpd.failover.conf.erb
@@ -0,0 +1,11 @@
+#
+# Generated by Chef for <%= node['fqdn'] %>
+# Do NOT modify this file by hand, changes will be overwritten.
+#
+
+failover peer "<%= @failover['peer'] %>" {
+ <%= @failover['role'] -%>;
+ <% property_collection_sorted(@failover['parameters']).each do |parameter, value| -%>
+ <%= parameter %><% unless nil_or_empty?(value) -%> <%= value %><% end -%>;
+ <% end -%>
+}
diff --git a/templates/default/dhcpd.leases-hack.erb b/templates/default/dhcpd.leases-hack.erb
deleted file mode 100644
index 0b255187..00000000
--- a/templates/default/dhcpd.leases-hack.erb
+++ /dev/null
@@ -1,4 +0,0 @@
-failover peer "<%= node['domain'] %>" state {
- my state partner-down;
- partner state unknown-state;
-}
diff --git a/templates/default/group.conf.erb b/templates/default/group.conf.erb
index 027915d0..244a8b87 100644
--- a/templates/default/group.conf.erb
+++ b/templates/default/group.conf.erb
@@ -1,30 +1,36 @@
-# File managed by Chef
+#
+# Generated by Chef for <%= node['fqdn'] %>
+# Do NOT modify this file by hand, changes will be overwritten.
+#
-#<%= @name %>
-group {
-<% @parameters.sort.each do |parameter| -%>
- <%= parameter %>;
-<% end -%>
-<% @evals.sort.each do |eval| -%>
-<%= eval %>
+# <%= @name -%><% if @comment -%> - <%= @comment -%><% end -%>
+group {
+<% unless nil_or_empty?(@parameters) -%>
+
+ # Parameters
+ <% property_collection_sorted(@parameters).each do |parameter, value| -%>
+ <%= parameter %><% unless nil_or_empty?(value) -%> <%= value %><% end -%>;
+ <% end -%>
<% end -%>
+<% unless nil_or_empty?(@options) -%>
-<% @hosts.sort.each do |host,data| -%>
- host <%= host %> {
-<% if data.has_key?("parameters") -%>
- <% data['parameters'].sort.each do |param| -%>
- <%= param %>;
+ # Options
+ <% property_collection_sorted(@options).each do |option, value| -%>
+ option <%= option %> <%= value %>;
<% end -%>
<% end -%>
-<% if data.has_key?("options") -%>
- <% data['options'].sort.each do |option| -%>
- option <%= option %>;
+<% unless nil_or_empty?(@evals) -%>
+
+ # Evals
+ <% @evals.each do |eval| -%>
+ <%= eval %>
<% end -%>
<% end -%>
- hardware ethernet <%= data["mac"] %>;
- <% if data.has_key?("ip") -%>
- fixed-address <%= data["ip"] %>;
- <% end -%>
- }
+<% unless nil_or_empty?(@hosts) -%>
+
+ # Hosts
+<% @hosts.sort.each do |host| -%>
+ include "<%= host %>";
+<% end -%>
<% end -%>
-}
\ No newline at end of file
+}
diff --git a/templates/default/host.conf.erb b/templates/default/host.conf.erb
index a851a9f5..12b27cdf 100644
--- a/templates/default/host.conf.erb
+++ b/templates/default/host.conf.erb
@@ -1,31 +1,28 @@
#
-# File managed by Chef
+# Generated by Chef for <%= node['fqdn'] %>
+# Do NOT modify this file by hand, changes will be overwritten.
#
+# <%= @name -%><% if @comment -%> - <%= @comment -%><% end -%>
host <%= @name %> {
-<% if @hostname -%>
- option host-name "<%= @hostname %>";
+<% if @identifier -%>
+ <%= @identifier %>;
<% end -%>
-<% if @macaddress -%>
- hardware ethernet <%= @macaddress %>;
+<% if @address -%>
+ <%= @ip_version.eql?(:ipv6) ? 'fixed-address6' : 'fixed-address' %> <%= @address %>;
<% end -%>
-<% if @servername -%>
- server-name "<%= @servername %>";
-<% end -%>
-<% if @ipaddress -%>
- fixed-address <%= @ipaddress %>;
-<% end -%>
-<% if @filename -%>
- file-name "<%= @filename %>";
-<% end -%>
-<% unless @parameters.empty? -%>
- <% @parameters.sort.each do |parameter| -%>
- <%= parameter -%>;
+<% unless nil_or_empty?(@parameters) -%>
+
+ # Parameters
+ <% property_collection_sorted(@parameters).each do |parameter, value| -%>
+ <%= parameter %><% unless nil_or_empty?(value) -%> <%= value %><% end -%>;
<% end -%>
<% end -%>
-<% unless @options.empty? -%>
- <% @options.sort.each do |option| -%>
- option <%= option -%>;
+<% unless nil_or_empty?(@options) -%>
+
+ # Options
+ <% property_collection_sorted(@options).each do |option, value| -%>
+ option <%= option -%> <%= value -%>;
<% end -%>
<% end -%>
}
diff --git a/templates/default/init_config.erb b/templates/default/init_config.erb
deleted file mode 100644
index 65eee2b9..00000000
--- a/templates/default/init_config.erb
+++ /dev/null
@@ -1,11 +0,0 @@
-# Defaults for dhcp initscript
-# sourced by /etc/init.d/dhcp
-# managed by chef dhcp recipe
-
-
-# On what interfaces should the DHCP server (dhcpd) serve DHCP requests?
-# Separate multiple interfaces with spaces, e.g. "eth0 eth1".
-<% unless @interfaces.empty? -%>
-<%= @var.upcase %>="<%= @interfaces.collect {|i| i }.join(" ") %>"
-<% end -%>
-
diff --git a/templates/default/list.conf.erb b/templates/default/list.conf.erb
index 04b00518..a68dcab5 100644
--- a/templates/default/list.conf.erb
+++ b/templates/default/list.conf.erb
@@ -1,7 +1,13 @@
#
-# file managed by chef
-# Host entry includes
+# Generated by Chef for <%= node['fqdn'] %>
+# Do NOT modify this file by hand, changes will be overwritten.
#
+
+<% if @files.empty? -%>
+# No files to include
+<% else -%>
+# Include files
<% @files.sort.each do |file| -%>
include "<%= file %>";
<% end -%>
+<% end -%>
diff --git a/templates/default/shared_network.conf.erb b/templates/default/shared_network.conf.erb
index 3b89b83c..4c1b556d 100644
--- a/templates/default/shared_network.conf.erb
+++ b/templates/default/shared_network.conf.erb
@@ -1,21 +1,11 @@
-# File managed by Chef
+#
+# Generated by Chef for <%= node['fqdn'] %>
+# Do NOT modify this file by hand, changes will be overwritten.
+#
+# <%= @name -%><% if @comment -%> - <%= @comment -%><% end -%>
shared-network <%= @name %> {
- <% @subnets.each do |subnet| -%>
- <%= render 'subnet.erb', :cookbook => 'dhcp', :variables => {
- node: @node,
- subnet: subnet.subnet,
- netmask: subnet.netmask,
- broadcast: subnet.broadcast,
- routers: subnet.routers,
- options: subnet.options,
- pools: subnet.pools,
- evals: subnet.evals,
- key: subnet.key,
- zones: subnet.zones,
- ddns: subnet.ddns,
- next_server: subnet.next_server
- }
- %>
+ <% @subnets.sort.each do |subnet| -%>
+ include "<%= subnet %>";
<% end -%>
}
diff --git a/templates/default/subnet.conf.erb b/templates/default/subnet.conf.erb
index 0d98a4af..acc68b4d 100644
--- a/templates/default/subnet.conf.erb
+++ b/templates/default/subnet.conf.erb
@@ -1,3 +1,99 @@
-# File managed by Chef
+#
+# Generated by Chef for <%= node['fqdn'] %>
+# Do NOT modify this file by hand, changes will be overwritten.
+#
-<%= render 'subnet.erb', :cookbook => 'dhcp', :variables => @variables %>
+# <%= @name -%><% if @comment -%> - <%= @comment -%><% end -%>
+subnet <%= @subnet %> netmask <%= @netmask%> {
+ <% unless nil_or_empty?(@parameters) -%>
+ # Parameters
+ <% property_collection_sorted(@parameters).each do |parameter, value| -%>
+ <%= parameter %><% unless nil_or_empty?(value) -%> <%= value %><% end -%>;
+ <% end -%>
+<% end -%>
+<% unless nil_or_empty?(@options) -%>
+
+ # Options
+ <% property_collection_sorted(@options).each do |option, value| -%>
+ option <%= option %> <%= value %>;
+ <% end -%>
+<% end -%>
+<% unless nil_or_empty?(@extra_lines) -%>
+
+ # Extra Lines
+ <% @extra_lines.each do |line| -%>
+ <%= line %>
+ <% end -%>
+<% end -%>
+<% unless nil_or_empty?(@evals) -%>
+
+ # Evals
+ <% @evals.each do |eval| -%>
+ <%= eval %>
+ <% end -%>
+<% end -%>
+<% unless nil_or_empty?(@key) -%>
+
+ # Key
+ key <%= @key['name'] %> {
+ algorithm <%= @key['algorithm'] %>;
+ secret "<%= @key['secret'] %>";
+ };
+<% end -%>
+<% unless nil_or_empty?(@zones) -%>
+
+ # Zones
+ <% @zones.each do |zone, options| -%>
+ zone <%= zone %> {
+ primary <%= options['primary'] %>;
+ key <%= options['key'] %>;
+ }
+ <% end -%>
+<% end -%>
+<% unless nil_or_empty?(@pools) -%>
+
+ # Pools
+<% property_array(@pools).each do |pool| -%>
+ pool {
+ <% if pool['failover_peer'] -%>
+ failover peer "<%= pool['failover_peer'] %>";
+ <% end -%>
+ <% unless nil_or_empty?(pool['options']) -%>
+
+ # Options
+ <% property_collection_sorted(pool['options']).each do |option, value| -%>
+ option <%= option %> <%= value %>;
+ <% end -%>
+ <% end -%>
+ <% unless nil_or_empty?(pool['parameters']) -%>
+
+ # Parameters
+ <% property_collection_sorted(pool['parameters']).each do |parameter, value| -%>
+ <%= parameter %> <%= value %>;
+ <% end -%>
+ <% end -%>
+ <% unless nil_or_empty?(pool['range']) %>
+
+ # Ranges
+ <% property_array(pool['range']).sort.each do |range| -%>
+ range <%= range %>;
+ <% end -%>
+ <% end -%>
+ <% unless nil_or_empty?(pool['deny']) -%>
+
+ # Deny Access Control
+ <% property_array(pool['deny']).sort.each do |denied| -%>
+ deny <%= denied %>;
+ <% end -%>
+ <% end -%>
+ <% unless nil_or_empty?(pool['allow']) -%>
+
+ # Allow Access Control
+ <% property_array(pool['allow']).sort.each do |allowed| -%>
+ allow <%= allowed %>;
+ <% end -%>
+ <% end -%>
+ }
+<% end -%>
+<% end -%>
+}
diff --git a/templates/default/subnet.erb b/templates/default/subnet.erb
deleted file mode 100644
index 25ec4b31..00000000
--- a/templates/default/subnet.erb
+++ /dev/null
@@ -1,47 +0,0 @@
-subnet <%= @subnet %> netmask <%= @netmask%> {
-<% unless @pools.nil? || @pools.empty? -%>
-<% @pools.each do |pool| -%>
-<%= render 'subnet_pool.erb', :cookbook => 'dhcp', :variables => { node: @node, peer: pool.peer, range: pool.range, deny: pool.deny, allow: pool.allow, extra_pool_lines: pool.extra_pool_lines } %>
-<% end -%>
-<% end -%>
-<% if @ddns -%>
- ddns-domainname "<%= @ddns %>";
-<% end -%>
-<% unless @routers.empty? -%>
- option routers <%= @routers.map {|i| i }.join(",") %>;
-<% end -%>
-<% if @netmask -%>
- option subnet-mask <%= @netmask %>;
-<% end -%>
-<% if @broadcast -%>
- option broadcast-address <%= @broadcast %>;
-<% end -%>
-<% unless @options.empty? -%>
-<% @options.sort.each do |option| -%>
- option <%= option %>;
-<% end -%>
-<% end -%>
-<% if @next_server -%>
- next-server <%= @next_server %>;
-<% end -%>
-<% @evals.sort.each do |eval| -%>
-<%= eval %>
-
-<% end -%>
-<% unless @key.empty? -%>
- key <%= @key['name'] %> {
- algorithm <%= @key['algorithm'] %>;
- secret "<%= @key['secret'] %>";
- };
-
-<% end -%>
-<% unless @zones.empty? -%>
-<% @zones.each do |zone| -%>
- zone <%= zone['zone'] %> {
- primary <%= zone['primary'] %>;
- key <%= zone['key'] %>;
- }
-<% end -%>
-
-<% end -%>
-}
diff --git a/templates/default/subnet6.conf.erb b/templates/default/subnet6.conf.erb
new file mode 100644
index 00000000..0f2327d4
--- /dev/null
+++ b/templates/default/subnet6.conf.erb
@@ -0,0 +1,83 @@
+#
+# Generated by Chef for <%= node['fqdn'] %>
+# Do NOT modify this file by hand, changes will be overwritten.
+#
+
+# <%= @name -%><% if @comment -%> - <%= @comment -%><% end -%>
+subnet6 <%= @subnet %>/<%= @prefix %> {
+<% unless nil_or_empty?(@range) -%>
+ # Ranges
+ <% property_array(@range).each do |range| -%>
+ range6 <%= range %>;
+ <% end -%>
+<% end -%>
+<% unless nil_or_empty?(@parameters) -%>
+
+ # Parameters
+ <% property_collection_sorted(@parameters).each do |parameter, value| -%>
+ <%= parameter %><% unless nil_or_empty?(value) -%> <%= value %><% end -%>;
+ <% end -%>
+<% end -%>
+<% unless nil_or_empty?(@options) -%>
+
+ # Options
+ <% property_collection_sorted(@options).each do |option, value| -%>
+ option <%= option %> <%= value %>;
+ <% end -%>
+<% end -%>
+<% unless nil_or_empty?(@allow) -%>
+
+ # Allow Access Control
+ <% property_array(@allow).each do |allowed| -%>
+ allow <%= allowed %>;
+ <% end -%>
+<% end -%>
+<% unless nil_or_empty?(@deny) -%>
+
+ # Deny Access Control
+ <% property_array(@deny).each do |denied| -%>
+ deny <%= denied %>;
+ <% end -%>
+<% end -%>
+<% unless nil_or_empty?(@extra_lines) -%>
+
+ # Extra Lines
+ <% @extra_lines.each do |line| -%>
+ <%= line %>
+ <% end -%>
+<% end -%>
+<% unless nil_or_empty?(@evals) -%>
+
+ # Evals
+ <% @evals.each do |eval| -%>
+ <%= eval %>
+ <% end -%>
+<% end -%>
+
+<% unless nil_or_empty?(@pool) -%>
+<% property_array(@pool).each do |pool| -%>
+ pool6 {
+ <% unless nil_or_empty?(pool['range']) -%>
+ # Ranges
+ <% pool['range'].sort.each do |range| -%>
+ range6 <%= range %>;
+ <% end -%>
+ <% end -%>
+ <% unless nil_or_empty?(@allow) -%>
+
+ # Allow Access Control
+ <% pool['allow'].sort.each do |allowed| -%>
+ allow <%= allowed %>;
+ <% end -%>
+ <% end -%>
+ <% unless nil_or_empty?(@deny) -%>
+
+ # Deny Access Control
+ <% pool['deny'].sort.each do |denied| -%>
+ deny <%= denied %>;
+ <% end -%>
+ <% end -%>
+ }
+<% end -%>
+<% end -%>
+}
diff --git a/templates/default/subnet_pool.erb b/templates/default/subnet_pool.erb
deleted file mode 100644
index f91a788a..00000000
--- a/templates/default/subnet_pool.erb
+++ /dev/null
@@ -1,21 +0,0 @@
-pool {
- <% if @extra_pool_lines -%>
- <% @extra_pool_lines = [@extra_pool_lines] if @extra_pool_lines.is_a?(String) -%>
- <% @extra_pool_lines.each do |line| -%>
- <%= line %>
- <% end -%>
- <% end -%>
- <% if @peer -%>
- failover peer "<%= node['domain'] %>";
- <% end -%>
- <% @range = [@range] if @range.is_a?(String) -%>
- <% @range.each do |range| -%>
- range <%= range %>;
- <% end -%>
- <% @deny.each do |denied| -%>
- deny <%= denied %>;
- <% end -%>
- <% @allow.each do |allowed| -%>
- allow <%= allowed %>;
- <% end -%>
-}
diff --git a/test/cookbooks/test/README.md b/test/cookbooks/test/README.md
new file mode 100644
index 00000000..536353bb
--- /dev/null
+++ b/test/cookbooks/test/README.md
@@ -0,0 +1,3 @@
+# dhcp_test
+
+This cookbook installs ISC DHCPd and tests whether the dhcp cookbook can configure a server.
diff --git a/test/cookbooks/test/attributes/attribute_driver.rb b/test/cookbooks/test/attributes/attribute_driver.rb
deleted file mode 100644
index 20bac07c..00000000
--- a/test/cookbooks/test/attributes/attribute_driver.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-default['dhcp']['network_data']['192.168.12.0/24'] = {
- 'id' => '192-168-12-0_24',
- 'routers' => ['192.168.12.1'],
- 'address' => '192.168.12.0',
- 'netmask' => '255.255.255.0',
- 'broadcast' => '192.168.12.255',
- 'range' => '192.168.12.50 192.168.12.240',
- 'options' => ['time-offset 10'],
- 'next_server' => '192.168.12.11',
-}
-
-default['dhcp']['shared_network_data']['mysharedattrnetwork']['subnets']['192.168.13.0/24'] = {
- 'id' => '192-168-13-0_24',
- 'routers' => ['192.168.13.1'],
- 'address' => '192.168.13.0',
- 'netmask' => '255.255.255.0',
- 'broadcast' => '192.168.13.255',
- 'range' => '192.168.13.50 192.168.13.240',
- 'next_server' => '192.168.13.11',
-}
-default['dhcp']['shared_network_data']['mysharedattrnetwork']['subnets']['192.168.14.0/24'] = {
- 'id' => '192-168-14-0_24',
- 'routers' => ['192.168.14.1'],
- 'address' => '192.168.14.0',
- 'netmask' => '255.255.255.0',
- 'broadcast' => '192.168.14.255',
- 'range' => '192.168.14.50 192.168.14.240',
- 'next_server' => '192.168.14.11',
-}
-
-default['dhcp']['hooks'] = {
- 'commit' => ['use-host-decl-names on'],
- 'release' => ['use-host-decl-names on'],
-}
-
-default['dhcp']['interfaces'] = %w(eth0 eth1)
-default['dhcp']['groups'] = []
-default['dhcp']['hosts'] = []
-default['dhcp']['networks'] = ['192.168.9.0/24']
-default['dhcp']['shared_networks'] = ['mysharednet']
-default['dhcp']['options']['domain-name'] = 'vm'
-default['dhcp']['options']['domain-name-servers'] = '192.168.9.1'
-default['dhcp']['extra_files'] = ['/etc/dhcp/extra1.conf', '/etc/dhcp/extra2.conf']
diff --git a/test/cookbooks/test/metadata.rb b/test/cookbooks/test/metadata.rb
index a1079b26..20d78501 100644
--- a/test/cookbooks/test/metadata.rb
+++ b/test/cookbooks/test/metadata.rb
@@ -1,3 +1,4 @@
name 'test'
+version '0.0.1'
depends 'dhcp'
diff --git a/test/cookbooks/test/recipes/config_delete.rb b/test/cookbooks/test/recipes/config_delete.rb
new file mode 100644
index 00000000..d98fbe71
--- /dev/null
+++ b/test/cookbooks/test/recipes/config_delete.rb
@@ -0,0 +1,49 @@
+#
+# Cookbook:: dhcp_test
+# Recipe:: config_delete
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+dhcp_config '/etc/dhcp/dhcpd6.conf' do
+ ip_version :ipv6
+ action :delete
+ notifies :stop, 'dhcp_service[dhcpd6]', :delayed
+ notifies :disable, 'dhcp_service[dhcpd6]', :delayed
+end
+
+dhcp_host 'Test-IPv6-Host' do
+ ip_version :ipv6
+ action :delete
+end
+
+dhcp_subnet 'overrides' do
+ conf_dir '/etc/dhcp_override'
+ action :delete
+ notifies :restart, 'dhcp_service[dhcpd]', :delayed
+end
+
+dhcp_subnet 'overrides' do
+ action :delete
+ notifies :restart, 'dhcp_service[dhcpd]', :delayed
+end
+
+dhcp_subnet 'deny host from class' do
+ action :delete
+ notifies :restart, 'dhcp_service[dhcpd]', :delayed
+end
+
+dhcp_class 'UnregisteredHosts' do
+ action :delete
+ notifies :restart, 'dhcp_service[dhcpd]', :delayed
+end
diff --git a/test/cookbooks/test/recipes/default.rb b/test/cookbooks/test/recipes/default.rb
index 11ce48a8..5dd79c72 100644
--- a/test/cookbooks/test/recipes/default.rb
+++ b/test/cookbooks/test/recipes/default.rb
@@ -1,5 +1,30 @@
-include_recipe 'dhcp::server'
-include_recipe 'test::net_setup'
-include_recipe 'test::extra_files'
-include_recipe 'test::dhcp_shared_network'
-include_recipe 'test::dhcp_class'
+#
+# Cookbook:: dhcp_test
+# Recipe:: default
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+include_recipe '::net_setup'
+
+include_recipe '::package'
+include_recipe '::include_files'
+
+include_recipe '::dhcp_config'
+include_recipe '::dhcp_subnet'
+include_recipe '::dhcp_host'
+include_recipe '::dhcp_class'
+include_recipe '::dhcp_shared_network'
+include_recipe '::dhcp_group'
+
+include_recipe '::service'
diff --git a/test/cookbooks/test/recipes/dhcp_class.rb b/test/cookbooks/test/recipes/dhcp_class.rb
index c1a357b6..60b25c08 100644
--- a/test/cookbooks/test/recipes/dhcp_class.rb
+++ b/test/cookbooks/test/recipes/dhcp_class.rb
@@ -4,24 +4,41 @@
dhcp_class 'RegisteredHosts' do
match 'hardware'
- subclass '1:10:bf:48:42:55:01'
- subclass '1:10:bf:48:42:55:02'
+ subclass [
+ '1:10:bf:48:42:55:01',
+ '1:10:bf:48:42:55:02',
+ ]
end
dhcp_class 'UnregisteredHosts' do
match 'hardware'
- subclass '1:10:bf:48:42:55:03'
- subclass '1:10:bf:48:42:55:04'
+ subclass [
+ '1:10:bf:48:42:55:03',
+ '1:10:bf:48:42:55:04',
+ ]
+ options(
+ 'domain-name-servers' => '8.8.8.8'
+ )
+end
+
+dhcp_class 'OtherHosts' do
+ match 'hardware'
+ subclass [
+ '1:10:bf:48:42:55:05',
+ '1:10:bf:48:42:55:06',
+ ]
end
dhcp_subnet 'deny host from class' do
subnet '192.168.4.0'
- broadcast '192.168.4.255'
netmask '255.255.255.0'
- routers ['192.168.4.1']
- pool do
- range '192.168.4.20 192.168.4.30'
- deny 'members of "RegisteredHosts"'
- allow 'members of "UnregisteredHosts"'
- end
+ options(
+ 'broadcast-address' => '192.168.4.255',
+ 'routers' => '192.168.4.1'
+ )
+ pools(
+ 'range' => '192.168.4.20 192.168.4.30',
+ 'deny' => 'members of "RegisteredHosts"',
+ 'allow' => 'members of "UnregisteredHosts"'
+ )
end
diff --git a/test/cookbooks/test/recipes/dhcp_config.rb b/test/cookbooks/test/recipes/dhcp_config.rb
new file mode 100644
index 00000000..4a52803e
--- /dev/null
+++ b/test/cookbooks/test/recipes/dhcp_config.rb
@@ -0,0 +1,63 @@
+#
+# Cookbook:: dhcp_test
+# Recipe:: config
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+dhcp_config '/etc/dhcp/dhcpd.conf' do
+ allow %w(booting bootp unknown-clients)
+ parameters(
+ 'default-lease-time' => 7200,
+ 'ddns-update-style' => 'interim',
+ 'max-lease-time' => 86400,
+ 'update-static-leases' => true,
+ 'one-lease-per-client' => true,
+ 'authoritative' => '',
+ 'ping-check' => true
+ )
+ options(
+ 'domain-name' => '"test.domain.local"',
+ 'domain-name-servers' => '8.8.8.8',
+ 'host-name' => ' = binary-to-ascii (16, 8, "-", substring (hardware, 1, 6))'
+ )
+ hooks(
+ 'commit' => ['use-host-decl-names on'],
+ 'release' => ['use-host-decl-names on']
+ )
+ include_files [
+ '/etc/dhcp/extra1.conf',
+ '/etc/dhcp/extra2.conf',
+ '/etc/dhcp_override/list.conf',
+ ]
+ action :create
+end
+
+dhcp_config '/etc/dhcp/dhcpd6.conf' do
+ ip_version :ipv6
+ deny %w(duplicates)
+ parameters(
+ 'default-lease-time' => 7200,
+ 'ddns-updates' => 'on',
+ 'ddns-update-style' => 'interim',
+ 'max-lease-time' => 86400,
+ 'update-static-leases' => true,
+ 'one-lease-per-client' => 'on',
+ 'authoritative' => '',
+ 'ping-check' => true
+ )
+ options(
+ 'dhcp6.name-servers' => '2001:4860:4860::8888, 2001:4860:4860::8844'
+ )
+ action :create
+end
diff --git a/test/cookbooks/test/recipes/dhcp_group.rb b/test/cookbooks/test/recipes/dhcp_group.rb
new file mode 100644
index 00000000..d51deebd
--- /dev/null
+++ b/test/cookbooks/test/recipes/dhcp_group.rb
@@ -0,0 +1,10 @@
+dhcp_group 'ip-phones' do
+ options(
+ 'tftp-server-name' => '"192.0.2.10"'
+ )
+ hosts(
+ 'SEP010101010101' => {
+ 'identifier' => 'hardware ethernet 01:01:01:01:01:01',
+ }
+ )
+end
diff --git a/test/cookbooks/test/recipes/dhcp_host.rb b/test/cookbooks/test/recipes/dhcp_host.rb
new file mode 100644
index 00000000..9f2d0aee
--- /dev/null
+++ b/test/cookbooks/test/recipes/dhcp_host.rb
@@ -0,0 +1,12 @@
+dhcp_host 'Test-IPv4-Host' do
+ options 'host-name' => 'test-ipv4-host'
+ identifier 'hardware ethernet 00:53:00:00:00:01'
+ address '192.168.0.10'
+end
+
+dhcp_host 'Test-IPv6-Host' do
+ ip_version :ipv6
+ options 'host-name' => 'test-ipv6-host'
+ identifier 'host-identifier option dhcp6.client-id 00:53:00:00:00:01:a4:65:b7:c8'
+ address '2001:db8:1:1:0:0:1:10'
+end
diff --git a/test/cookbooks/test/recipes/dhcp_shared_network.rb b/test/cookbooks/test/recipes/dhcp_shared_network.rb
index 5ab3c330..38a6d53e 100644
--- a/test/cookbooks/test/recipes/dhcp_shared_network.rb
+++ b/test/cookbooks/test/recipes/dhcp_shared_network.rb
@@ -1,29 +1,45 @@
dhcp_shared_network 'single' do
- subnet '192.168.1.0' do
- broadcast '192.168.1.255'
- netmask '255.255.255.0'
- routers ['192.168.1.1']
- pool do
- range '192.168.1.20 192.168.1.30'
- end
- end
+ subnets(
+ '192.168.1.0' => {
+ 'subnet' => '192.168.1.0',
+ 'netmask' => '255.255.255.0',
+ 'options' => {
+ 'broadcast-address' => '192.168.1.255',
+ 'routers' => '192.168.1.1',
+ },
+ 'pools' => {
+ 'range' => '192.168.1.20 192.168.1.30',
+ },
+ }
+ )
end
dhcp_shared_network 'multiple' do
- subnet '192.168.2.0' do
- broadcast '192.168.2.255'
- netmask '255.255.255.0'
- routers ['192.168.2.1']
- pool do
- range ['192.168.2.20 192.168.2.30']
- end
- end
- subnet '192.168.3.0' do
- broadcast '192.168.3.255'
- netmask '255.255.255.0'
- routers ['192.168.3.1']
- pool do
- range ['192.168.3.20 192.168.3.30', '192.168.3.40 192.168.3.50']
- end
- end
+ subnets(
+ '192.168.2.0' => {
+ 'subnet' => '192.168.2.0',
+ 'netmask' => '255.255.255.0',
+ 'options' => {
+ 'broadcast-address' => '192.168.2.255',
+ 'routers' => '192.168.2.1',
+ },
+ 'pools' => {
+ 'range' => '192.168.2.20 192.168.2.30',
+ },
+ },
+ '192.168.3.0' => {
+ 'subnet' => '192.168.3.0',
+ 'netmask' => '255.255.255.0',
+ 'options' => {
+ 'broadcast-address' => '192.168.3.255',
+ 'routers' => '192.168.3.1',
+ },
+ 'pools' => {
+ 'range' => [
+ '192.168.3.20 192.168.3.30',
+ '192.168.3.40 192.168.3.50',
+ ],
+ },
+ }
+ )
end
diff --git a/test/cookbooks/test/recipes/dhcp_subnet.rb b/test/cookbooks/test/recipes/dhcp_subnet.rb
index 8718dd87..d3dedfe6 100644
--- a/test/cookbooks/test/recipes/dhcp_subnet.rb
+++ b/test/cookbooks/test/recipes/dhcp_subnet.rb
@@ -1,42 +1,76 @@
# Define using defaults
-dhcp_subnet '10.0.2.0' do
- netmask '255.255.254.0' # Requried attribute
+dhcp_subnet '192.168.9.0' do
+ comment 'Listen Subnet Declaration'
+ netmask '255.255.255.0'
end
# Basic definition
dhcp_subnet 'basic' do
+ comment 'Basic Subnet Declaration'
subnet '192.168.0.0'
- broadcast '192.168.0.255'
netmask '255.255.255.0'
- routers ['192.168.0.1']
- options ['time-offset 10']
- pool do
- range '192.168.0.100 192.168.0.200'
- end
+ options [
+ 'routers 192.168.0.1',
+ 'time-offset 10',
+ ]
+ pools 'range' => '192.168.0.100 192.168.0.200'
end
+directory '/etc/dhcp_override'
+
# Override everything
dhcp_subnet 'overrides' do
- subnet '192.168.0.0'
- broadcast '192.168.0.255'
+ comment 'Overrides Subnet Declaration'
+ subnet '192.168.1.0'
netmask '255.255.255.0'
- routers ['192.168.0.1']
- options ['time-offset 10']
- pool do
- peer '192.168.0.2'
- range '192.168.0.100 192.168.0.200'
- deny 'members of "RegisteredHosts"'
- allow ['members of "UnregisteredHosts"', 'members of "OtherHosts"']
- end
- ddns 'test.com'
- evals ['
- if exists user-class and option user-class = "iPXE" {
- filename "bootstrap.ipxe";
- } else {
- filename "undionly.kpxe";
- }']
- key 'name' => 'test_key', 'algorithm' => 'test_algo', 'secret' => 'test_secret'
- zones [{ 'zone' => 'test', 'primary' => 'test_pri', 'key' => 'test_key' }]
+ options [
+ 'routers 192.168.1.1',
+ 'time-offset 10',
+ 'broadcast-address 192.168.0.255',
+ ]
+ pools(
+ 'peer' => '192.168.0.2',
+ 'range' => '192.168.1.100 192.168.1.200',
+ 'deny' => 'members of "RegisteredHosts"',
+ 'allow' => ['members of "UnregisteredHosts"', 'members of "OtherHosts"']
+ )
+ parameters(
+ 'ddns-domainname' => '"test.com"',
+ 'next-server' => '192.168.0.3'
+ )
+ evals [ 'if exists user-class and option user-class = "iPXE" {
+ filename "bootstrap.ipxe";
+ } else {
+ filename "undionly.kpxe";
+ }' ]
+ key 'name' => 'test_key', 'algorithm' => 'hmac-sha256', 'secret' => 'c7nBOcB2rbJh7lYCI65/PGrS6QdlLMCPe2xunZ4dij8='
+ zones 'test' => { 'primary' => 'test_pri', 'key' => 'test_key' }
conf_dir '/etc/dhcp_override'
- next_server '192.168.0.3'
+end
+
+# DHCPv6 listen subnet
+dhcp_subnet 'dhcpv6_listen' do
+ ip_version :ipv6
+ comment 'Testing DHCPv6 Basic Subnet'
+ subnet '2001:db8:1::'
+ prefix 64
+end
+
+# DHCPv6 basic subnet
+dhcp_subnet 'dhcpv6_basic' do
+ ip_version :ipv6
+ comment 'Testing DHCPv6 Basic Subnet'
+ subnet '2001:db8:2:1::'
+ prefix 64
+ options(
+ 'domain-name' => '"test.domain.local"',
+ 'dhcp6.name-servers' => '2001:4860:4860::8888, 2001:4860:4860::8844'
+ )
+ parameters(
+ 'ddns-domainname' => '"test.domain.local"',
+ 'default-lease-time' => 28800
+ )
+ range [
+ '2001:db8:2:1::1:0/112',
+ ]
end
diff --git a/test/cookbooks/test/recipes/extra_files.rb b/test/cookbooks/test/recipes/include_files.rb
similarity index 100%
rename from test/cookbooks/test/recipes/extra_files.rb
rename to test/cookbooks/test/recipes/include_files.rb
diff --git a/test/cookbooks/test/recipes/net_setup.rb b/test/cookbooks/test/recipes/net_setup.rb
index 9c033b9e..24020e3f 100644
--- a/test/cookbooks/test/recipes/net_setup.rb
+++ b/test/cookbooks/test/recipes/net_setup.rb
@@ -8,6 +8,12 @@
not_if 'ip a show dev eth1 | grep UP'
end
-ifconfig '192.168.9.1' do
- device 'eth1'
+execute 'address eth1' do
+ command 'ip addr add 192.168.9.1 dev eth1'
+ not_if 'ip a show dev eth1 | grep \'inet 192.168.9.1\''
+end
+
+execute 'remove ipv6 default route' do
+ command 'ip -6 route del ::/0'
+ only_if 'ip -6 route | grep default'
end
diff --git a/recipes/default.rb b/test/cookbooks/test/recipes/package.rb
similarity index 72%
rename from recipes/default.rb
rename to test/cookbooks/test/recipes/package.rb
index 5b08eb1f..e4bb0365 100644
--- a/recipes/default.rb
+++ b/test/cookbooks/test/recipes/package.rb
@@ -1,8 +1,6 @@
-# Author:: Matt Ray
-# Cookbook:: dhcp
-# Recipe:: default
#
-# Copyright:: 2011-2017, Chef Software, Inc
+# Cookbook:: dhcp_test
+# Recipe:: package
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,4 +15,4 @@
# limitations under the License.
#
-Chef::Log.warn('The dhcp::default recipe is empty and should not be applied to a node')
+dhcp_package 'Server Test'
diff --git a/test/cookbooks/test/recipes/service.rb b/test/cookbooks/test/recipes/service.rb
new file mode 100644
index 00000000..361c3a23
--- /dev/null
+++ b/test/cookbooks/test/recipes/service.rb
@@ -0,0 +1,29 @@
+#
+# Cookbook:: dhcp_test
+# Recipe:: service
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+dhcp_service 'dhcpd' do
+ ip_version :ipv4
+ action [:create, :enable, :start]
+end
+
+require 'socket'
+ipv6_available = Socket.getifaddrs.select { |addr_info| addr_info.addr.ipv6? }.count > 0
+
+dhcp_service 'dhcpd6' do
+ ip_version :ipv6
+ action ipv6_available ? [:create, :enable, :start] : [:create, :enable]
+end
diff --git a/test/integration/data_bags/dhcp_groups/test.json b/test/integration/data_bags/dhcp_groups/test.json
deleted file mode 100644
index 0f0c3b26..00000000
--- a/test/integration/data_bags/dhcp_groups/test.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "id": "test",
- "pxe": "ubuntu-precise",
- "parameters": [
- "use-host-decl-names on",
- "max-lease-time 300",
- "default-lease-time 120",
- "next-server \"someplace\""
- ],
- "hosts": {
- "some-vm": {
- "parameters": [ ],
- "mac": "11:22:33:44:55:66",
- "ip": "192.168.1.111"
- },
- "another-host": {
- "mac": "22:33:44:55:66:77"
- }
- }
-}
diff --git a/test/integration/data_bags/dhcp_hosts/pxe_test-vm.json b/test/integration/data_bags/dhcp_hosts/pxe_test-vm.json
deleted file mode 100644
index 215eea8f..00000000
--- a/test/integration/data_bags/dhcp_hosts/pxe_test-vm.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "id": "pxe_test-vm",
- "hostname": "pxe_test.vm",
- "mac": "08:00:27:8f:7b:db",
- "ip": "192.168.1.31",
- "parameters": [ ],
- "options": [ ],
- "pxe": "ubuntu-precise"
-}
diff --git a/test/integration/data_bags/dhcp_hosts/vagrant-vm.json b/test/integration/data_bags/dhcp_hosts/vagrant-vm.json
deleted file mode 100644
index 368fc2a5..00000000
--- a/test/integration/data_bags/dhcp_hosts/vagrant-vm.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "id": "vagrant-vm",
- "hostname": "vagrant.vm",
- "mac": "08:00:27:f1:1f:b6",
- "pxe": "ubuntu-precise"
-}
diff --git a/test/integration/data_bags/dhcp_networks/192-168-34-0_24.json b/test/integration/data_bags/dhcp_networks/192-168-34-0_24.json
deleted file mode 100644
index b3a3f331..00000000
--- a/test/integration/data_bags/dhcp_networks/192-168-34-0_24.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "id": "192-168-34-0_24",
- "routers": [ "192.168.34.1" ],
- "address": "192.168.34.0",
- "netmask": "255.255.255.0",
- "broadcast": "192.168.9.255",
- "range": [
- "192.168.34.50 192.168.34.100",
- "192.168.34.150 192.168.34.200",
- "192.168.34.250 192.168.34.254"
- ],
- "options": [ "time-offset 10" ],
- "next_server": "192.168.34.11"
-}
diff --git a/test/integration/data_bags/dhcp_networks/192-168-9-0_24.json b/test/integration/data_bags/dhcp_networks/192-168-9-0_24.json
deleted file mode 100644
index bdee7a70..00000000
--- a/test/integration/data_bags/dhcp_networks/192-168-9-0_24.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "id": "192-168-9-0_24",
- "routers": [ "192.168.9.1" ],
- "address": "192.168.9.0",
- "netmask": "255.255.255.0",
- "broadcast": "192.168.9.255",
- "range": "192.168.9.50 192.168.9.240",
- "options": [ "time-offset 10" ],
- "next_server": "192.168.9.11",
- "extra_pool_lines": [ "# test" ]
-}
diff --git a/test/integration/data_bags/dhcp_networks/mysharednet.json b/test/integration/data_bags/dhcp_networks/mysharednet.json
deleted file mode 100644
index e699c2f9..00000000
--- a/test/integration/data_bags/dhcp_networks/mysharednet.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "id": "mysharednet",
- "subnets": {
- "192.168.10.0/24": {
- "routers": ["192.168.10.1"],
- "address": "192.168.10.0",
- "netmask": "255.255.255.0",
- "broadcast": "192.168.10.255",
- "range": "192.168.10.50 192.168.10.240",
- "next_server": "192.168.10.11"
- },
- "10.0.2.0/24": {
- "address": "10.0.2.0",
- "netmask": "255.255.255.0",
- "broadcast": "10.0.2.255",
- "range": "10.0.2.50 10.0.2.240"
- }
- }
-}
diff --git a/test/integration/data_bags/dns_zones/192-168-1-0.json b/test/integration/data_bags/dns_zones/192-168-1-0.json
deleted file mode 100644
index 10821c0c..00000000
--- a/test/integration/data_bags/dns_zones/192-168-1-0.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "id": "192-168-1-0",
- "zone_name": "1.168.192.IN-ADDR.ARPA",
- "ttl": "2d",
- "authority": "dns01.vm",
- "email": "root",
- "refresh": "1h", "retry": "15m", "expire": "7d", "minimum": "1h",
- "allow_update": "127.0.0.1",
- "allow_query": "any;",
- "name_servers": { "dns01.vm": "192.168.1.9" },
- "resource_records": {
- "9": { "PTR": "dns01.vm." },
- "10": { "PTR": "server.vm." },
- "11": { "PTR": "client01.vm." },
- "12": { "PTR": "dhcp01.vm." }
- }
-}
diff --git a/test/integration/data_bags/dns_zones/vm.json b/test/integration/data_bags/dns_zones/vm.json
deleted file mode 100644
index 94f56094..00000000
--- a/test/integration/data_bags/dns_zones/vm.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "id": "vm",
- "zone_name": "vm",
- "ttl": "2d",
- "authority": "dns01.vm",
- "email": "root",
- "refresh": "1h", "retry": "15m", "expire": "7d", "minimum": "1h",
- "allow_update": "127.0.0.1;",
- "allow_query": "any;",
- "name_servers": { "dns01.vm": "192.168.1.9" },
- "master_address": "192.168.1.9",
- "rndc_key": "dhcp-key",
- "resource_records": {
- "server": { "A": "192.168.1.10" },
- "chef-server": { "CNAME": "server.vm." },
- "client01": { "A": "192.168.1.11" },
- "dhcp01": { "A": "192.168.1.12" }
- }
-}
diff --git a/test/integration/data_bags/rndc_keys/dhcp-key.json b/test/integration/data_bags/rndc_keys/dhcp-key.json
deleted file mode 100644
index 174b84be..00000000
--- a/test/integration/data_bags/rndc_keys/dhcp-key.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "id": "dhcp-key",
- "algorithm": "hmac-md5",
- "secret": "L+Jl4+4onU4Wstfi4pdmnQ=="
-}
diff --git a/test/integration/default/inspec/classes_spec.rb b/test/integration/default/inspec/classes_spec.rb
new file mode 100644
index 00000000..bd036780
--- /dev/null
+++ b/test/integration/default/inspec/classes_spec.rb
@@ -0,0 +1,20 @@
+describe file('/etc/dhcp/dhcpd.d/classes.d/RegisteredHosts.conf') do
+ it { should exist }
+ it { should be_file }
+ its(:content) { should match 'class "RegisteredHosts" ' }
+ its(:content) { should match 'subclass "RegisteredHosts" 1:10:bf:48:42:55:01;' }
+end
+
+describe file('/etc/dhcp/dhcpd.d/classes.d/UnregisteredHosts.conf') do
+ it { should exist }
+ it { should be_file }
+ its(:content) { should match 'class "UnregisteredHosts" ' }
+ its(:content) { should match 'subclass "UnregisteredHosts" 1:10:bf:48:42:55:03;' }
+ its(:content) { should match 'option domain-name-servers 8.8.8.8;' }
+end
+
+describe file('/etc/dhcp/dhcpd6.d/classes.d/list.conf') do
+ it { should exist }
+ it { should be_file }
+ its(:content) { should match '# No files to include' }
+end
diff --git a/test/integration/default/inspec/default_spec.rb b/test/integration/default/inspec/default_spec.rb
index 92530e64..e9810110 100644
--- a/test/integration/default/inspec/default_spec.rb
+++ b/test/integration/default/inspec/default_spec.rb
@@ -1,30 +1,65 @@
-title 'DHCP install'
-case os[:family]
-when 'redhat'
- package_name = 'dhcp'
- service_name = 'dhcpd'
+case os.family
+when 'redhat', 'centos'
+ package_name = if os.release.to_i < 8
+ 'dhcp'
+ else
+ 'dhcp-server'
+ end
+ service_name = %w(dhcpd)
+ service_name.push('dhcpd6') if interface('eth0').ipv6_address?
when 'fedora'
package_name = 'dhcp-server'
- service_name = 'dhcpd'
+ service_name = %w(dhcpd)
+ service_name.push('dhcpd6') if interface('eth0').ipv6_address?
when 'debian', 'ubuntu'
package_name = 'isc-dhcp-server'
- service_name = 'isc-dhcp-server'
+ service_name = %w(isc-dhcp-server)
+ service_name.push('isc-dhcp-server6') if interface('eth0').ipv6_address?
end
describe package(package_name) do
it { should be_installed }
end
-describe service(service_name) do
- it { should be_enabled }
- it { should be_running }
+service_name.each do |service|
+ describe service(service) do
+ it { should be_enabled }
+ it { should be_running }
+ end
end
-describe processes('dhcpd') do
- its('states') { should eq ['Ss'] }
+describe command('/usr/sbin/dhcpd -t -4 -cf /etc/dhcp/dhcpd.conf') do
+ its('exit_status') { should eq 0 }
end
+describe command('/usr/sbin/dhcpd -t -6 -cf /etc/dhcp/dhcpd6.conf') do
+ its('exit_status') { should eq 0 }
+end
+
+# if interface('eth0').ipv6_address? - Using bodge due to
+if command('ip addr show dev eth0 | grep inet6').exit_status.eql?(0)
+ describe processes('dhcpd') do
+ its('states') { should eq %w(Ss Ss) }
+ end
+else
+ describe processes('dhcpd') do
+ its('states') { should eq %w(Ss) }
+ end
+end
+
+describe port(67) do
+ it { should be_listening }
+ its('protocols') { should include 'udp' }
+ its('processes') { should include 'dhcpd' }
+end
+
+describe port(547) do
+ it { should be_listening }
+ its('protocols') { should include 'udp' }
+ its('processes') { should include 'dhcpd' }
+end if interface('eth0').ipv6_address?
+
describe file('/etc/dhcp/dhcpd.conf') do
it { should exist }
it { should be_file }
@@ -33,3 +68,10 @@
its(:content) { should match "on commit {\n use-host-decl-names on;\n}" }
its(:content) { should match "on release {\n use-host-decl-names on;\n}" }
end
+
+describe file('/etc/dhcp/dhcpd6.conf') do
+ it { should exist }
+ it { should be_file }
+ its(:content) { should match /option dhcp6.name-servers 2001:4860:4860::8888, 2001:4860:4860::8844;/ }
+ its(:content) { should match "# Deny\ndeny duplicates;" }
+end
diff --git a/test/integration/default/inspec/group_spec.rb b/test/integration/default/inspec/group_spec.rb
new file mode 100644
index 00000000..01de3f10
--- /dev/null
+++ b/test/integration/default/inspec/group_spec.rb
@@ -0,0 +1,13 @@
+describe file('/etc/dhcp/dhcpd.d/groups.d/ip-phones.conf') do
+ it { should exist }
+ it { should be_file }
+ its(:content) { should match 'tftp-server-name "192.0.2.10";' }
+ its(:content) { should match 'include "/etc/dhcp/dhcpd.d/groups.d/ip-phones_grouphost_SEP010101010101.conf";' }
+end
+
+describe file('/etc/dhcp/dhcpd.d/groups.d/ip-phones_grouphost_SEP010101010101.conf') do
+ it { should exist }
+ it { should be_file }
+ its(:content) { should match 'host ip-phones_grouphost_SEP010101010101 {' }
+ its(:content) { should match 'hardware ethernet 01:01:01:01:01:01;' }
+end
diff --git a/test/integration/default/inspec/subnets_spec.rb b/test/integration/default/inspec/subnets_spec.rb
new file mode 100644
index 00000000..3108a75c
--- /dev/null
+++ b/test/integration/default/inspec/subnets_spec.rb
@@ -0,0 +1,13 @@
+describe file('/etc/dhcp/dhcpd.d/subnets.d/deny host from class.conf') do
+ it { should exist }
+ it { should be_file }
+ its(:content) { should match 'subnet 192.168.4.0 netmask 255.255.255.0' }
+ its(:content) { should match 'deny members of "RegisteredHosts"' }
+end
+
+describe file('/etc/dhcp/dhcpd6.d/subnets.d/dhcpv6_basic.conf') do
+ it { should exist }
+ it { should be_file }
+ its(:content) { should match 'range6 2001:db8:2:1::1:0/112;' }
+ its(:content) { should match 'default-lease-time 28800;' }
+end
diff --git a/test/integration/delete/classes_spec.rb b/test/integration/delete/classes_spec.rb
new file mode 100644
index 00000000..2bf44099
--- /dev/null
+++ b/test/integration/delete/classes_spec.rb
@@ -0,0 +1,23 @@
+describe file('/etc/dhcp/dhcpd.d/classes.d/UnregisteredHosts.conf') do
+ it { should_not exist }
+ it { should_not be_file }
+end
+
+describe file('/etc/dhcp/dhcpd.d/classes.d/RegisteredHosts.conf') do
+ it { should exist }
+ it { should be_file }
+ its(:content) { should match 'class "RegisteredHosts" ' }
+ its(:content) { should match 'subclass "RegisteredHosts" 1:10:bf:48:42:55:01;' }
+end
+
+describe file('/etc/dhcp/dhcpd.d/classes.d/list.conf') do
+ it { should exist }
+ it { should be_file }
+ its(:content) { should_not match 'include /etc/dhcp/dhcpd.d/classes.d/UnregisteredHosts.conf' }
+end
+
+describe file('/etc/dhcp/dhcpd6.d/classes.d/list.conf') do
+ it { should exist }
+ it { should be_file }
+ its(:content) { should match '# No files to include' }
+end
diff --git a/test/integration/delete/default_spec.rb b/test/integration/delete/default_spec.rb
new file mode 100644
index 00000000..b80d1e2e
--- /dev/null
+++ b/test/integration/delete/default_spec.rb
@@ -0,0 +1,64 @@
+case os.family
+when 'redhat', 'centos'
+ package_name = if os.release.to_i < 8
+ 'dhcp'
+ else
+ 'dhcp-server'
+ end
+ service_name = %w(dhcpd)
+ service_name.push('dhcpd6') if interface('eth0').ipv6_address?
+when 'fedora'
+ package_name = 'dhcp-server'
+ service_name = %w(dhcpd)
+ service_name.push('dhcpd6') if interface('eth0').ipv6_address?
+when 'debian', 'ubuntu'
+ package_name = 'isc-dhcp-server'
+ service_name = %w(isc-dhcp-server)
+ service_name.push('isc-dhcp-server6') if interface('eth0').ipv6_address?
+end
+
+describe package(package_name) do
+ it { should be_installed }
+end
+
+describe service(service_name[0]) do
+ it { should be_enabled }
+ it { should be_running }
+end
+
+describe service(service_name[1]) do
+ it { should_not be_enabled }
+ it { should_not be_running }
+end
+
+describe command('/usr/sbin/dhcpd -t -4 -cf /etc/dhcp/dhcpd.conf') do
+ its('exit_status') { should eq 0 }
+end
+
+describe processes('dhcpd') do
+ its('states') { should eq %w(Ss) }
+end
+
+describe port(67) do
+ it { should be_listening }
+ its('protocols') { should include 'udp' }
+ its('processes') { should include 'dhcpd' }
+end
+
+describe port(547) do
+ it { should_not be_listening }
+end
+
+describe file('/etc/dhcp/dhcpd.conf') do
+ it { should exist }
+ it { should be_file }
+ its(:content) { should match %r{^include "/etc/dhcp/extra1.conf";} }
+ its(:content) { should match %r{^include "/etc/dhcp/extra2.conf";} }
+ its(:content) { should match "on commit {\n use-host-decl-names on;\n}" }
+ its(:content) { should match "on release {\n use-host-decl-names on;\n}" }
+end
+
+describe file('/etc/dhcp/dhcpd6.conf') do
+ it { should_not exist }
+ it { should_not be_file }
+end
diff --git a/test/integration/delete/subnets_spec.rb b/test/integration/delete/subnets_spec.rb
new file mode 100644
index 00000000..6ca1a267
--- /dev/null
+++ b/test/integration/delete/subnets_spec.rb
@@ -0,0 +1,4 @@
+describe file('/etc/dhcp_override/overrides.conf') do
+ it { should_not exist }
+ it { should_not be_file }
+end