├── .editorconfig ├── .envrc ├── .gitattributes ├── .github ├── CODEOWNERS ├── lock.yml └── workflows │ ├── ci.yml │ └── stale.yml ├── .gitignore ├── .markdownlint-cli2.yaml ├── .mdlrc ├── .overcommit.yml ├── .vscode └── extensions.json ├── .yamllint ├── Berksfile ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dangerfile ├── LICENSE ├── README.md ├── TESTING.md ├── UPGRADING.md ├── chefignore ├── documentation ├── .gitkeep ├── dhcp_class.md ├── dhcp_config.md ├── dhcp_group.md ├── dhcp_host.md ├── dhcp_package.md ├── dhcp_service.md ├── dhcp_shared_network.md └── dhcp_subnet.md ├── kitchen.dokken.yml ├── kitchen.exec.yml ├── kitchen.global.yml ├── kitchen.yml ├── libraries ├── helpers.rb ├── resource.rb └── template.rb ├── metadata.rb ├── renovate.json ├── resources ├── class.rb ├── config.rb ├── group.rb ├── host.rb ├── package.rb ├── service.rb ├── shared_network.rb └── subnet.rb ├── spec ├── spec_helper.rb └── unit │ └── recipes │ ├── class_spec.rb │ ├── config_spec.rb │ ├── default_spec.rb │ ├── group_spec.rb │ ├── host_spec.rb │ ├── service_spec.rb │ ├── shared_network_spec.rb │ └── subnet_spec.rb ├── templates └── default │ ├── class.conf.erb │ ├── dhcpd-env.erb │ ├── dhcpd.conf.erb │ ├── dhcpd.failover.conf.erb │ ├── group.conf.erb │ ├── host.conf.erb │ ├── list.conf.erb │ ├── shared_network.conf.erb │ ├── subnet.conf.erb │ └── subnet6.conf.erb └── test ├── cookbooks └── test │ ├── README.md │ ├── files │ ├── ubuntu-18.04 │ │ └── usr.sbin.dhcpd │ ├── ubuntu-20.04 │ │ └── usr.sbin.dhcpd │ └── ubuntu-22.04 │ │ └── usr.sbin.dhcpd │ ├── metadata.rb │ └── recipes │ ├── config_delete.rb │ ├── default.rb │ ├── dhcp_class.rb │ ├── dhcp_config.rb │ ├── dhcp_group.rb │ ├── dhcp_host.rb │ ├── dhcp_shared_network.rb │ ├── dhcp_subnet.rb │ ├── include_files.rb │ ├── net_setup.rb │ ├── package.rb │ └── service.rb └── integration ├── default ├── classes_spec.rb ├── default_spec.rb ├── group_spec.rb └── subnets_spec.rb └── delete ├── classes_spec.rb ├── default_spec.rb └── subnets_spec.rb /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root=true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | # 2 space indentation 12 | indent_style = space 13 | indent_size = 2 14 | 15 | # Avoid issues parsing cookbook files later 16 | charset = utf-8 17 | 18 | # Avoid cookstyle warnings 19 | trim_trailing_whitespace = true 20 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | use chefworkstation 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @sous-chefs/maintainers 2 | -------------------------------------------------------------------------------- /.github/lock.yml: -------------------------------------------------------------------------------- 1 | --- 2 | daysUntilLock: 365 3 | exemptLabels: [] 4 | lockLabel: false 5 | lockComment: > 6 | This thread has been automatically locked since there has not been 7 | any recent activity after it was closed. Please open a new issue for 8 | related bugs. 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: ci 3 | 4 | "on": 5 | pull_request: 6 | push: 7 | branches: [main] 8 | 9 | jobs: 10 | lint-unit: 11 | uses: sous-chefs/.github/.github/workflows/lint-unit.yml@3.1.1 12 | permissions: 13 | actions: write 14 | checks: write 15 | pull-requests: write 16 | statuses: write 17 | issues: write 18 | 19 | integration: 20 | needs: lint-unit 21 | runs-on: ubuntu-latest 22 | strategy: 23 | matrix: 24 | os: 25 | - "almalinux-8" 26 | - "almalinux-9" 27 | - "centos-7" 28 | - "centos-stream-8" 29 | - "centos-stream-9" 30 | - "debian-10" 31 | - "debian-11" 32 | - "fedora-latest" 33 | - "rockylinux-8" 34 | - "rockylinux-9" 35 | - "ubuntu-1804" 36 | - "ubuntu-2004" 37 | - "ubuntu-2204" 38 | suite: 39 | - "default" 40 | - "delete" 41 | fail-fast: false 42 | 43 | steps: 44 | - name: Check out code 45 | uses: actions/checkout@v4 # v4 46 | - name: Install Chef 47 | uses: actionshub/chef-install@3.0.1 48 | - name: Fix apparmor for dhcpd 49 | run: | 50 | set -x 51 | sudo apt-get -y update 52 | sudo apt-get -y install apparmor 53 | - name: Dokken 54 | uses: actionshub/test-kitchen@3.0.0 55 | env: 56 | CHEF_LICENSE: accept-no-persist 57 | KITCHEN_LOCAL_YAML: kitchen.dokken.yml 58 | with: 59 | suite: ${{ matrix.suite }} 60 | os: ${{ matrix.os }} 61 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Mark stale issues and pull requests 3 | 4 | "on": 5 | schedule: [cron: "0 0 * * *"] 6 | 7 | jobs: 8 | stale: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/stale@v9 12 | with: 13 | repo-token: ${{ secrets.GITHUB_TOKEN }} 14 | close-issue-message: > 15 | Closing due to inactivity. 16 | If this is still an issue please reopen or open another issue. 17 | Alternatively drop by the #sous-chefs channel on the [Chef Community Slack](http://community-slack.chef.io/) and we'll be happy to help! 18 | Thanks, Sous-Chefs. 19 | days-before-close: 7 20 | days-before-stale: 365 21 | stale-issue-message: > 22 | Marking stale due to inactivity. 23 | Remove stale label or comment or this will be closed in 7 days. 24 | Alternatively drop by the #sous-chefs channel on the [Chef Community Slack](http://community-slack.chef.io/) and we'll be happy to help! 25 | Thanks, Sous-Chefs. 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.rbc 2 | .config 3 | InstalledFiles 4 | pkg 5 | test/tmp 6 | test/version_tmp 7 | tmp 8 | _Store 9 | *~ 10 | *# 11 | .#* 12 | \#*# 13 | *.un~ 14 | *.tmp 15 | *.bk 16 | *.bkup 17 | 18 | # editor files 19 | .idea 20 | .*.sw[a-z] 21 | 22 | # ruby/bundler/rspec files 23 | .ruby-version 24 | .ruby-gemset 25 | .rvmrc 26 | Gemfile.lock 27 | .bundle 28 | *.gem 29 | coverage 30 | spec/reports 31 | 32 | # YARD / rdoc artifacts 33 | .yardoc 34 | _yardoc 35 | doc/ 36 | rdoc 37 | 38 | # chef infra stuff 39 | Berksfile.lock 40 | .kitchen 41 | kitchen.local.yml 42 | vendor/ 43 | .coverage/ 44 | .zero-knife.rb 45 | Policyfile.lock.json 46 | 47 | # vagrant stuff 48 | .vagrant/ 49 | .vagrant.d/ 50 | -------------------------------------------------------------------------------- /.markdownlint-cli2.yaml: -------------------------------------------------------------------------------- 1 | config: 2 | ul-indent: false # MD007 3 | line-length: false # MD013 4 | no-duplicate-heading: false # MD024 5 | reference-links-images: false # MD052 6 | -------------------------------------------------------------------------------- /.mdlrc: -------------------------------------------------------------------------------- 1 | rules "~MD024", "~MD013" 2 | -------------------------------------------------------------------------------- /.overcommit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | PreCommit: 3 | TrailingWhitespace: 4 | enabled: true 5 | YamlLint: 6 | enabled: true 7 | required_executable: "yamllint" 8 | ChefSpec: 9 | enabled: true 10 | required_executable: "chef" 11 | command: ["chef", "exec", "rspec"] 12 | Cookstyle: 13 | enabled: true 14 | required_executable: "cookstyle" 15 | command: ["cookstyle"] 16 | MarkdownLint: 17 | enabled: false 18 | required_executable: "npx" 19 | command: ["npx", "markdownlint-cli2", "'**/*.md'"] 20 | include: ["**/*.md"] 21 | 22 | CommitMsg: 23 | HardTabs: 24 | enabled: true 25 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "chef-software.chef", 4 | "rebornix.ruby", 5 | "editorconfig.editorconfig", 6 | "DavidAnson.vscode-markdownlint" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | rules: 4 | line-length: 5 | max: 256 6 | level: warning 7 | document-start: disable 8 | braces: 9 | forbid: false 10 | min-spaces-inside: 0 11 | max-spaces-inside: 1 12 | min-spaces-inside-empty: -1 13 | max-spaces-inside-empty: -1 14 | comments: 15 | min-spaces-from-content: 1 16 | -------------------------------------------------------------------------------- /Berksfile: -------------------------------------------------------------------------------- 1 | source 'https://supermarket.chef.io' 2 | 3 | metadata 4 | 5 | group :integration do 6 | cookbook 'test', path: 'test/cookbooks/test' 7 | end 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## Unreleased 4 | 5 | ## 8.1.16 - *2024-05-02* 6 | 7 | ## 8.1.15 - *2024-05-02* 8 | 9 | ## 8.1.14 - *2023-10-31* 10 | 11 | ## 8.1.13 - *2023-09-28* 12 | 13 | ## 8.1.12 - *2023-09-04* 14 | 15 | ## 8.1.11 - *2023-07-10* 16 | 17 | ## 8.1.10 - *2023-06-08* 18 | 19 | Standardise files with files in sous-chefs/repo-management 20 | 21 | ## 8.1.9 - *2023-05-17* 22 | 23 | ## 8.1.8 - *2023-05-03* 24 | 25 | ## 8.1.7 - *2023-04-01* 26 | 27 | ## 8.1.6 - *2023-03-24* 28 | 29 | ## 8.1.5 - *2023-03-23* 30 | 31 | - Update platforms to modern versions 32 | - Add support for AlmaLinux, Rocky Linux and Fedora 33 | - Update and fixes tests 34 | - Fix AppArmor issues on Ubuntu 35 | 36 | ## 8.1.4 - *2023-03-02* 37 | 38 | - Standardise files with files in sous-chefs/repo-management 39 | 40 | ## 8.1.3 - *2023-02-27* 41 | 42 | - Standardise files with files in sous-chefs/repo-management 43 | 44 | ## 8.1.2 - *2023-02-27* 45 | 46 | - Standardise files with files in sous-chefs/repo-management 47 | 48 | ## 8.1.1 - *2023-02-14* 49 | 50 | - Remove delivery folder 51 | 52 | ## 8.1.0 - *2021-08-29* 53 | 54 | - Standardise files with files in sous-chefs/repo-management 55 | 56 | ## 8.0.1 - *2021-06-01* 57 | 58 | - Standardise files with files in sous-chefs/repo-management 59 | 60 | ## 8.0.0 - *2021-05-12* 61 | 62 | - Chef 17 compatibility changes - [@bmhughes](https://github.com/bmhughes) 63 | - All resources now run with `unified_mode true` 64 | 65 | ## 7.3.0 - *2021-03-30* 66 | 67 | - Fix generating multiple actions from the service resource - [@bmhughes](https://github.com/bmhughes) 68 | 69 | ## 7.2.1 - *2020-11-19* 70 | 71 | - Enhance the pre-service action configuration test to remove compile/converge bug - [@bmhughes](https://github.com/bmhughes) 72 | 73 | ## 7.2.0 (2020-07-10) 74 | 75 | - resolved cookstyle error: libraries/helpers.rb:120:44 refactor: `ChefCorrectness/InvalidPlatformInCase` 76 | - resolved cookstyle error: libraries/helpers.rb:154:24 refactor: `ChefCorrectness/InvalidPlatformFamilyInCase` 77 | - resolved cookstyle error: libraries/helpers.rb:161:14 refactor: `ChefCorrectness/InvalidPlatformFamilyInCase` 78 | - resolved cookstyle error: libraries/helpers.rb:193:44 refactor: `ChefCorrectness/InvalidPlatformInCase` 79 | - Fix the service resource not using the unit file content property 80 | - Slightly simplified the libraries with a platform helper 81 | - Fix verification of the isc-dhcp-server configuration on Ubuntu 82 | - Ensure that a service restart does not occur upon a configuration test failure 83 | 84 | ## 7.1.1 (2020-05-20) 85 | 86 | - Fix configuration test running every chef run regardless of service action - [@bmhughes](https://github.com/bmhughes) 87 | 88 | ## 7.1.0 (2020-05-18) 89 | 90 | - Add configuration test option to the `dhcp_service` resource - [@bmhughes](https://github.com/bmhughes) 91 | 92 | ## 7.0.0 (2020-04-17) 93 | 94 | Version 7.0.0 is a **major** change! Please see [UPGRADING.md](./UPGRADING.md). 95 | 96 | - DHCPv6 server configuration support - [@bmhughes](https://github.com/bmhughes) 97 | - Migrated to github actions - [@Xorima](https://github.com/Xorima) 98 | - Remodel cookbook as resource library - [@bmhughes](https://github.com/bmhughes) 99 | - Remove - [@bmhughes](https://github.com/bmhughes) 100 | - Attributes 101 | - Recipes 102 | - Data bag functionality 103 | - Add resources to manage install and services for dhcpd/dhcpd6 - [@bmhughes](https://github.com/bmhughes) 104 | - Rewrite resources to current standard removing pure ruby code - [@bmhughes](https://github.com/bmhughes) 105 | 106 | ## 6.1.0 (2019-10-19) 107 | 108 | - Added ability to add extra custom lines to pool config 109 | - Converted all LWRPs to Custom Resources 110 | - Ran latest cookstyle fixes 111 | - Migrated testing to circleci 112 | 113 | ## 6.0.0 (2018-03-04) 114 | 115 | - Remove matchers. Breaking change. This requires ChefDK 2.0+ 116 | - Allow specifying array of `allow` and `deny` declarations in `dhcp_subnet` `pool` 117 | - Convert to custom resources 118 | - Drop support for Chef 12 119 | - Add support for Fedora 120 | 121 | ## 5.5.0 (2018-03-04) 122 | 123 | - Use Berkshelf instead of Policyfiles 124 | - Remove support for RHEL5 125 | 126 | ## 5.4.4 (2018-03-04) 127 | 128 | - Use dokken images for travis testing 129 | 130 | ## 5.4.3 (2018-03-04) 131 | 132 | - Require Chef 12.7+ for `apt_update` fix 133 | 134 | ## 5.4.2 (2017-12-02) 135 | 136 | - Remove Chef 10/11 compatibility code that resulted in Foodcritic deprecation warnings 137 | - Fix misplaced closing brace for dhcpd hooks in template file 138 | 139 | ## 5.4.1 140 | 141 | - Fix: attribute breaking Chef 13 runs. 142 | 143 | ## 5.4.0 144 | 145 | - Added hooks 146 | 147 | ## 5.3.2 148 | 149 | - bug #67: Removed blank? from helper libraries but was still calling in code. Addressed by removing blank? calls from code and using empty? instead. 150 | 151 | ## 5.3.1 152 | 153 | - bug: Update `_hosts` recipe for `Helpers::DataBags.escape_bagname` change to `Dhcp::Helpers.escape` 154 | 155 | ## 5.3.0 156 | 157 | - Remove dependencies on helper cookbooks 158 | 159 | ## 5.2.0 160 | 161 | - Add `allow` attribute for `pool` in `dhcp_subnet` provider 162 | 163 | ## 5.1.0 164 | 165 | - Allow specifying with `node['dhcp']['extra_files']` externally managed configs to load 166 | 167 | ## 5.0.3 168 | 169 | - bug: Load classes before subnets 170 | 171 | ## 5.0.2 172 | 173 | - bug: Include classes.d/list.conf in dhcp.conf 174 | - bug: Make `dhcp_subnet` names declared in `dhcp_shared_network` more unique 175 | 176 | ## 5.0.1 177 | 178 | - bug: Pin partial templates to dhcp cookbook 179 | 180 | ## 5.0.0 181 | 182 | - BREAKING feature: Allow defining multiple pools in a subnet 183 | 184 | - This moves `range` and `peer` attribtue from `dhcp_subnet` block to embedded `pool` block inside `dhcp_subnet`. See updated examples. 185 | - Currently this DOES NOT break defining subnets via data_bags or node attributes 186 | 187 | ## 4.1.2 188 | 189 | - bug: Allow defining dhcp_class with no subclasses 190 | 191 | ## 4.1.1 192 | 193 | - improve: Add dhcp_class ChefSpec matcher 194 | 195 | ## 4.1.0 196 | 197 | - feature: dhcp_class provider 198 | 199 | ## 4.0.1 200 | 201 | - fix: be able to declare a blank subnet inside a shared-network 202 | 203 | ## 4.0.0 204 | 205 | - feature: dhcp_shared_network provider to define subnets inside a shared-network block 206 | - improve: allowing as blank as possible of a subnet block 207 | - improve: setting next-server in a subnet block 208 | - improve: allow dhcp_subnet range to be set with a String 209 | - improve: documentation 210 | - fix: including dhcp_host config files 211 | - fix: Chef 12 support 212 | - fix/improve: testing 213 | 214 | ## 3.0.0 215 | 216 | - feature: allow setting multiple ranges in subnets. The range param now HAS to be an array, existing cooks will not work. 217 | - improve: hostname in group configs is no long forced instead you can specify this in paramaters you pass. 218 | 219 | ## 2.2.2 220 | 221 | - fix: Debian system have Chef with version Chef: 11.10.0.rc.1 that Chef::Version doesn't detect correctly 222 | - fix: handle case where peer is unavailable on cluster start 223 | - improve: better chef-solo support 224 | - feature: Allow setting slaves and masters in attributes. 225 | - feature: Added ability to pass eval into subnets 226 | 227 | ## 2.2.1 228 | 229 | - support attribute driven mode, where no databags are needed to operate 230 | - feature: add evals to groups 231 | - fix error in subnet provider caused by mis-merged comma :( 232 | 233 | ## 2.1.2 234 | 235 | - change write_config to write_include, write_config doesn't work on ubuntu 12.04 236 | - allow you to set ddns-domainname for each subnet 237 | - convert "not if"'s to "unles" 238 | - Cleanup Readme for readability and attribution 239 | - update contributors 240 | 241 | ## 2.1.1 242 | 243 | - add tailor cane, kitchen testing 244 | - chef specs for chefspec 3.0 245 | - allow ddns update in subnet (pull request #13 from simonjohansson) 246 | - fixes for chef11 compatibility 247 | - fix version check in DSL condition 248 | - update service notifications to new format 249 | 250 | ## 2.0.0 251 | 252 | - Initial public release 253 | - Restructure entire cookbook for better reusability 254 | - Remove Internal/non-public dependencies 255 | - Fix: master/slave replication issues 256 | - Fix: LWRP notification method 257 | - Add minimal spec's for libraries 258 | - New: examples directory with databags and environments 259 | - New: configurable bag lookups for dhcp/dns bags 260 | - New: Group host list support 261 | - New: Defaults to mac address as hostname for non registered clients 262 | - New: Defines global defaults in default attributes 263 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Community Guidelines 2 | 3 | This project follows the Chef Community Guidelines 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Please refer to 4 | [https://github.com/chef-cookbooks/community_cookbook_documentation/blob/master/CONTRIBUTING.MD](https://github.com/chef-cookbooks/community_cookbook_documentation/blob/master/CONTRIBUTING.MD) 5 | -------------------------------------------------------------------------------- /Dangerfile: -------------------------------------------------------------------------------- 1 | # Reference: http://danger.systems/reference.html 2 | 3 | # A pull request summary is required. Add a description of the pull request purpose. 4 | # Changelog must be updated for each pull request that changes code. 5 | # Warnings will be issued for: 6 | # Pull request with more than 400 lines of code changed 7 | # Pull reqest that change more than 5 lines without test changes 8 | # Failures will be issued for: 9 | # Pull request without summary 10 | # Pull requests with code changes without changelog entry 11 | 12 | def code_changes? 13 | code = %w(libraries attributes recipes resources files templates) 14 | code.each do |location| 15 | return true unless git.modified_files.grep(/#{location}/).empty? 16 | end 17 | false 18 | end 19 | 20 | def test_changes? 21 | tests = %w(spec test kitchen.yml kitchen.dokken.yml) 22 | tests.each do |location| 23 | return true unless git.modified_files.grep(/#{location}/).empty? 24 | end 25 | false 26 | end 27 | 28 | failure 'Please provide a summary of your Pull Request.' if github.pr_body.length < 10 29 | 30 | warn 'This is a big Pull Request.' if git.lines_of_code > 400 31 | 32 | warn 'This is a Table Flip.' if git.lines_of_code > 2000 33 | 34 | # Require a CHANGELOG entry for non-test changes. 35 | if !git.modified_files.include?('CHANGELOG.md') && code_changes? 36 | failure 'Please include a CHANGELOG entry.' 37 | end 38 | 39 | # Require Major Minor Patch version labels 40 | unless github.pr_labels.grep /minor|major|patch/i 41 | warn 'Please add a release label to this pull request' 42 | end 43 | 44 | # A sanity check for tests. 45 | if git.lines_of_code > 5 && code_changes? && !test_changes? 46 | warn 'This Pull Request is probably missing tests.' 47 | end 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DHCP Cookbook 2 | 3 | [![Cookbook Version](https://img.shields.io/cookbook/v/dhcp.svg)](https://supermarket.chef.io/cookbooks/dhcp) 4 | [![Build Status](https://img.shields.io/circleci/project/github/sous-chefs/dhcp/master.svg)](https://circleci.com/gh/sous-chefs/dhcp) 5 | [![OpenCollective](https://opencollective.com/sous-chefs/backers/badge.svg)](#backers) 6 | [![OpenCollective](https://opencollective.com/sous-chefs/sponsors/badge.svg)](#sponsors) 7 | [![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](https://opensource.org/licenses/Apache-2.0) 8 | 9 | Installs and configures ISC DHCP server in both DHCP and DHCPv6 mode. 10 | 11 | - Supports setting up Master/Slave ISC DHCP failover (IPv4 only). 12 | - Includes Support for DDNS 13 | - Includes resources for managing: 14 | - Package installation 15 | - Service configuration and management 16 | - Global configuration 17 | - Hosts 18 | - Groups 19 | - Subnets 20 | - Shared subnets 21 | 22 | Version 7.0.0 constitutes a major change and rewrite, please see [UPGRADING.md](./UPGRADING.md). 23 | 24 | ## Maintainers 25 | 26 | 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). 27 | 28 | ## Platforms 29 | 30 | - Debian / Ubuntu 31 | - RHEL/CentOS and derivatives 32 | - Fedora and derivatives 33 | 34 | ## Requirements 35 | 36 | - Chef 15.3+ 37 | 38 | ## Usage 39 | 40 | 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. 41 | 42 | Example of a basic server listening on and issuing leases for the subnet `192.0.2.0/24`. 43 | 44 | ```ruby 45 | dhcp_install 'isc-dhcp-server' 46 | 47 | dhcp_service 'dhcpd' do 48 | ip_version :ipv4 49 | action [:create, :enable, :start] 50 | end 51 | 52 | dhcp_config '/etc/dhcp/dhcpd.conf' do 53 | allow %w(booting bootp unknown-clients) 54 | parameters( 55 | 'default-lease-time' => 7200, 56 | 'max-lease-time' => 86400, 57 | 'update-static-leases' => true, 58 | 'one-lease-per-client' => true, 59 | 'authoritative' => '', 60 | 'ping-check' => true 61 | ) 62 | options( 63 | 'domain-name' => '"test.domain.local"', 64 | 'domain-name-servers' => '8.8.8.8, 8.8.4.4' 65 | ) 66 | action :create 67 | end 68 | 69 | dhcp_subnet '192.0.2.0' do 70 | comment 'Basic Subnet Declaration' 71 | subnet '192.0.2.0' 72 | netmask '255.255.255.0' 73 | options [ 74 | 'routers 192.168.1.1', 75 | ] 76 | pool( 77 | 'peer' => '192.168.0.2', 78 | 'range' => '192.168.1.100 192.168.1.200' 79 | ) 80 | parameters( 81 | 'ddns-domainname' => '"test.domain"' 82 | ) 83 | end 84 | ``` 85 | 86 | ## External Documentation 87 | 88 | - 89 | - 90 | - 91 | 92 | ## Examples 93 | 94 | Please check for more varied working examples in the [test cookbook](./test/cookbooks/test/). 95 | 96 | ## Resources 97 | 98 | - [dhcp_class](documentation/dhcp_class.md) 99 | - [dhcp_config](documentation/dhcp_config.md) 100 | - [dhcp_group](documentation/dhcp_group.md) 101 | - [dhcp_host](documentation/dhcp_host.md) 102 | - [dhcp_package](documentation/dhcp_package.md) 103 | - [dhcp_service](documentation/dhcp_service.md) 104 | - [dhcp_shared_network](documentation/dhcp_shared_network.md) 105 | - [dhcp_subnet](documentation/dhcp_subnet.md) 106 | 107 | ## Known Issues 108 | 109 | There are some known issues on Ubuntu when apparmor is running which may prevent the service from running properly. 110 | Please see the [test cookbook](test/cookbooks/test) for a possible work around that you can apply on your nodes. 111 | 112 | ## Contributors 113 | 114 | This project exists thanks to all the people who [contribute.](https://opencollective.com/sous-chefs/contributors.svg?width=890&button=false) 115 | 116 | ### Backers 117 | 118 | Thank you to all our backers! 119 | 120 | ![https://opencollective.com/sous-chefs#backers](https://opencollective.com/sous-chefs/backers.svg?width=600&avatarHeight=40) 121 | 122 | ### Sponsors 123 | 124 | Support this project by becoming a sponsor. Your logo will show up here with a link to your website. 125 | 126 | ![https://opencollective.com/sous-chefs/sponsor/0/website](https://opencollective.com/sous-chefs/sponsor/0/avatar.svg?avatarHeight=100) 127 | ![https://opencollective.com/sous-chefs/sponsor/1/website](https://opencollective.com/sous-chefs/sponsor/1/avatar.svg?avatarHeight=100) 128 | ![https://opencollective.com/sous-chefs/sponsor/2/website](https://opencollective.com/sous-chefs/sponsor/2/avatar.svg?avatarHeight=100) 129 | ![https://opencollective.com/sous-chefs/sponsor/3/website](https://opencollective.com/sous-chefs/sponsor/3/avatar.svg?avatarHeight=100) 130 | ![https://opencollective.com/sous-chefs/sponsor/4/website](https://opencollective.com/sous-chefs/sponsor/4/avatar.svg?avatarHeight=100) 131 | ![https://opencollective.com/sous-chefs/sponsor/5/website](https://opencollective.com/sous-chefs/sponsor/5/avatar.svg?avatarHeight=100) 132 | ![https://opencollective.com/sous-chefs/sponsor/6/website](https://opencollective.com/sous-chefs/sponsor/6/avatar.svg?avatarHeight=100) 133 | ![https://opencollective.com/sous-chefs/sponsor/7/website](https://opencollective.com/sous-chefs/sponsor/7/avatar.svg?avatarHeight=100) 134 | ![https://opencollective.com/sous-chefs/sponsor/8/website](https://opencollective.com/sous-chefs/sponsor/8/avatar.svg?avatarHeight=100) 135 | ![https://opencollective.com/sous-chefs/sponsor/9/website](https://opencollective.com/sous-chefs/sponsor/9/avatar.svg?avatarHeight=100) 136 | -------------------------------------------------------------------------------- /TESTING.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | Please refer to [the community cookbook documentation on testing](https://github.com/chef-cookbooks/community_cookbook_documentation/blob/master/TESTING.MD). 4 | -------------------------------------------------------------------------------- /UPGRADING.md: -------------------------------------------------------------------------------- 1 | # Upgrading 2 | 3 | This document will give you help on upgrading major versions of dhcp 4 | 5 | ## 7.0.0 6 | 7 | Version 7.0.0 is a major rewrite of the cookbook to current standards, remodel as a resource library cookbook and the addition of DHCPv6 server support. 8 | 9 | ### Removed 10 | 11 | - All attributes 12 | - All recipes 13 | - Enumeration of resources from data bags with this cookbook is no longer supported, you will have to write your own wrapper cookbook if you 14 | depend on this functionality. 15 | - Resource `dhcp_pool` 16 | - Pool verification is now provided by the `dhcp_subnet` resource 17 | 18 | ### Added 19 | 20 | - Resource `dhcp_config` - [Documentation](./documentation/dhcp_config.md) 21 | - Replacement for the `_config` recipe 22 | - Global configuration 23 | - DHCP failover configuration 24 | - Configuration of service environment file 25 | 26 | - Resource `dhcp_package` - [Documentation](./documentation/dhcp_package.md) 27 | - Replacement for the `_package` recipe 28 | - Installs the relevant packages for platform 29 | 30 | - Resource `dhcp_service` - [Documentation](./documentation/dhcp_service.md) 31 | - Replacement for the `_service` recipe 32 | - Creates a systemd unit file for the platform 33 | - Controls service activation 34 | 35 | ### Changed 36 | 37 | - Common 38 | - Custom resources have been rewritten in current style 39 | - Relevant resources have `options` and `parameters` properties to allow more flexible user defined configuration 40 | - Template files are overrideable with `cookbook` and `template` parameters to allow user template selection 41 | - `owner`, `group` and `mode` properties to control the resultant permissions and mode of the generated files 42 | - DHCPv6 support for all resources, use `ip_version` to select 43 | 44 | - Resource `dhcp_class` - [Documentation](./documentation/dhcp_class.md) 45 | - `:delete` action added 46 | - `subclasses` are now specified via `Hash` rather than a `Block` 47 | 48 | - Resource `dhcp_group` - [Documentation](./documentation/dhcp_group.md) 49 | - `:delete` action added 50 | - Hosts are provided via included nested `dhcp_host` resources 51 | 52 | - Resource `dhcp_host` - [Documentation](./documentation/dhcp_host.md) 53 | - `:delete` action added 54 | - `hostname` property removed, specify it via `options` 55 | - `macaddress` property is renamed to `identifier` 56 | - `ipaddress` property is renamed to `address` 57 | 58 | - Resource `dhcp_shared_network` - [Documentation](./documentation/dhcp_shared_network.md) 59 | - `:delete` action added 60 | - Subnets are provided via included nested `dhcp_subnet` resources 61 | - `subnets` are now specified via `Hash` rather than a `Block` 62 | 63 | - Resource `dhcp_subnet` - [Documentation](./documentation/dhcp_subnet.md) 64 | - `:delete` action added 65 | - `broadcast`, `routers`, `ddns` and `next-server` properties have been removed, specify them via `options` and `parameters` 66 | - `pools` are now specified via `Hash` rather than a `Block` 67 | -------------------------------------------------------------------------------- /chefignore: -------------------------------------------------------------------------------- 1 | # Put files/directories that should be ignored in this file when uploading 2 | # to a Chef Infra Server or Supermarket. 3 | # Lines that start with '# ' are comments. 4 | 5 | # OS generated files # 6 | ###################### 7 | .DS_Store 8 | ehthumbs.db 9 | Icon? 10 | nohup.out 11 | Thumbs.db 12 | .envrc 13 | 14 | # EDITORS # 15 | ########### 16 | .#* 17 | .project 18 | .settings 19 | *_flymake 20 | *_flymake.* 21 | *.bak 22 | *.sw[a-z] 23 | *.tmproj 24 | *~ 25 | \#* 26 | REVISION 27 | TAGS* 28 | tmtags 29 | .vscode 30 | .editorconfig 31 | 32 | ## COMPILED ## 33 | ############## 34 | *.class 35 | *.com 36 | *.dll 37 | *.exe 38 | *.o 39 | *.pyc 40 | *.so 41 | */rdoc/ 42 | a.out 43 | mkmf.log 44 | 45 | # Testing # 46 | ########### 47 | .circleci/* 48 | .codeclimate.yml 49 | .delivery/* 50 | .foodcritic 51 | .kitchen* 52 | .mdlrc 53 | .overcommit.yml 54 | .rspec 55 | .rubocop.yml 56 | .travis.yml 57 | .watchr 58 | .yamllint 59 | azure-pipelines.yml 60 | Dangerfile 61 | examples/* 62 | features/* 63 | Guardfile 64 | kitchen*.yml 65 | mlc_config.json 66 | Procfile 67 | Rakefile 68 | spec/* 69 | test/* 70 | 71 | # SCM # 72 | ####### 73 | .git 74 | .gitattributes 75 | .gitconfig 76 | .github/* 77 | .gitignore 78 | .gitkeep 79 | .gitmodules 80 | .svn 81 | */.bzr/* 82 | */.git 83 | */.hg/* 84 | */.svn/* 85 | 86 | # Berkshelf # 87 | ############# 88 | Berksfile 89 | Berksfile.lock 90 | cookbooks/* 91 | tmp 92 | 93 | # Bundler # 94 | ########### 95 | vendor/* 96 | Gemfile 97 | Gemfile.lock 98 | 99 | # Policyfile # 100 | ############## 101 | Policyfile.rb 102 | Policyfile.lock.json 103 | 104 | # Documentation # 105 | ############# 106 | CODE_OF_CONDUCT* 107 | CONTRIBUTING* 108 | documentation/* 109 | TESTING* 110 | UPGRADING* 111 | 112 | # Vagrant # 113 | ########### 114 | .vagrant 115 | Vagrantfile 116 | -------------------------------------------------------------------------------- /documentation/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sous-chefs/dhcp/49bc3af5352e80065204839b8a47e789ce7df16c/documentation/.gitkeep -------------------------------------------------------------------------------- /documentation/dhcp_class.md: -------------------------------------------------------------------------------- 1 | # dhcp_class 2 | 3 | [Back to resource list](../README.md#resources) 4 | 5 | Create a DHCPD class configuration. () 6 | 7 | Introduced: v7.0.0 8 | 9 | ## Actions 10 | 11 | - `:create` 12 | - `:delete` 13 | 14 | ## Properties 15 | 16 | | Name | Type | Default | Description | Allowed Values | 17 | | ---------------------- | ------------- | -------------------------------- | ------------------------------------------------------------------- | ------------------- | 18 | | `comment` | String | `nil` | Comment to add to the configuration file | | 19 | | `ip_version` | Symbol | `:ipv4` | Select DHCP or DHCPv6 server to configure | `:ipv4`, `:ipv6` | 20 | | `conf_dir` | String | `/etc/dhcp/dhcpd(6).d/classes.d` | Directory to create configuration file in | | 21 | | `cookbook` | String | `/etc/dhcp/dhcpd(6).d/classes.d` | Cookbook to source configuration file template from | | 22 | | `template` | String | `/etc/dhcp/dhcpd(6).d/classes.d` | Template to use to generate the configuration file | | 23 | | `owner` | String | Platform dependant | Owner of the generated configuration file | | 24 | | `group` | String | Platform dependant | Group of the generated configuration file | | 25 | | `mode` | String | `'0640'` | Filemode of the generated configuration file | | 26 | | `match` | String | `nil` | DHCPD match statement | | 27 | | `options` | Array, Hash | `nil` | DHCPD options for the class | | 28 | | `parameters` | Array, Hash | `nil` | DHCPD parameters for the class | | 29 | | `subclass` | Array | `nil` | Subclasses to include within the class | | 30 | 31 | ## Examples 32 | 33 | ```ruby 34 | dhcp_class 'BlankClass' do 35 | match 'hardware' 36 | end 37 | ``` 38 | 39 | ```ruby 40 | dhcp_class 'RegisteredHosts' do 41 | match 'hardware' 42 | subclass [ 43 | '1:10:bf:48:42:55:01', 44 | '1:10:bf:48:42:55:02', 45 | ] 46 | end 47 | ``` 48 | 49 | ```ruby 50 | dhcp_class 'SpecialHosts' do 51 | match 'hardware' 52 | subclass [ 53 | '1:10:bf:48:42:56:01', 54 | '1:10:bf:48:42:56:02', 55 | ] 56 | option( 57 | 'special-option' => 'value' 58 | ) 59 | end 60 | ``` 61 | -------------------------------------------------------------------------------- /documentation/dhcp_config.md: -------------------------------------------------------------------------------- 1 | # dhcp_config 2 | 3 | [Back to resource list](../README.md#resources) 4 | 5 | Create and manage the main DHCPD configuration. () 6 | 7 | Introduced: v7.0.0 8 | 9 | ## Actions 10 | 11 | - `:create` 12 | - `:delete` 13 | 14 | ## Properties 15 | 16 | | Name | Type | Default | Description | Allowed Values | 17 | | ---------------------- | ------------- | -------------------------------- | ------------------------------------------------------------------- | ------------------- | 18 | | `ip_version` | Symbol | `:ipv4` | Select DHCP or DHCPv6 server to configure | `:ipv4`, `:ipv6` | 19 | | `config_file` | String | `/etc/dhcp/dhcpd(6).conf` | 'The full path to the DHCP server configuration on disk | | 20 | | `cookbook` | String | `/etc/dhcp/dhcpd(6).d/classes.d` | Cookbook to source configuration file template from | | 21 | | `template` | String | `/etc/dhcp/dhcpd(6).d/classes.d` | Template to use to generate the configuration file | | 22 | | `owner` | String | Platform dependant | Owner of the generated configuration file | | 23 | | `group` | String | Platform dependant | Group of the generated configuration file | | 24 | | `mode` | String | `'0640'` | Filemode of the generated configuration file | | 25 | | `config_failover_file` | String | `/etc/dhcp/dhcpd.failover.conf` | DHCP failover configuration file path | | 26 | | `config_includes_directory` | String | `/etc/dhcp/dhcpd(6).d` | Directory to create included configuration files in | | 27 | | `lib_dir` | String | Platform dependant | DHCPD lib directory path | | 28 | | `lease_file` | String | Platform dependant | DHCPD lease file path | | 29 | | `allow` | Array | `nil` | | | 30 | | `deny` | Array | `nil` | | | 31 | | `ignore` | Array | `nil` | | | 32 | | `parameters` | Array, Hash | `nil` | DHCPD global parameters | | 33 | | `options` | Array, Hash | `nil` | DHCPD global options | | 34 | | `evals` | Array | `nil` | DHCPD conditional statements (see dhcp-eval(5)) | | 35 | | `keys` | Hash | `nil` | TSIG keys configuration | | 36 | | `zones` | Hash | `nil` | Dynamic DNS zone configuration | | 37 | | `hooks` | Hash | `nil` | Server event action configuration | | 38 | | `failover` | Hash | `nil` | DHCP failover configuration | | 39 | | `include_files` | Array | `nil` | Additional configuration files to include | | 40 | | `extra_lines` | String, Array | `nil` | Extra lines to append to the configuration file | | 41 | | `env_file` | String | Platform dependant | Service environment file, mostly unused by modern distributions | | 42 | | `env_file_lines` | String, Array | `nil` | Service environment file lines | | 43 | 44 | ## Examples 45 | 46 | ```ruby 47 | dhcp_config '/etc/dhcp/dhcpd.conf' do 48 | allow %w(booting bootp unknown-clients) 49 | parameters( 50 | 'default-lease-time' => 7200, 51 | 'ddns-update-style' => 'interim', 52 | 'max-lease-time' => 86400, 53 | 'update-static-leases' => true, 54 | 'one-lease-per-client' => true, 55 | 'authoritative' => '', 56 | 'ping-check' => true 57 | ) 58 | options( 59 | 'domain-name' => '"test.domain.local"', 60 | 'domain-name-servers' => '8.8.8.8', 61 | 'host-name' => ' = binary-to-ascii (16, 8, "-", substring (hardware, 1, 6))' 62 | ) 63 | hooks( 64 | 'commit' => ['use-host-decl-names on'], 65 | 'release' => ['use-host-decl-names on'] 66 | ) 67 | include_files [ 68 | '/etc/dhcp/extra1.conf', 69 | '/etc/dhcp/extra2.conf', 70 | '/etc/dhcp_override/list.conf', 71 | ] 72 | action :create 73 | end 74 | ``` 75 | 76 | ```ruby 77 | dhcp_config '/etc/dhcp/dhcpd6.conf' do 78 | ip_version :ipv6 79 | deny %w(duplicates) 80 | parameters( 81 | 'default-lease-time' => 7200, 82 | 'ddns-updates' => 'on', 83 | 'ddns-update-style' => 'interim', 84 | 'max-lease-time' => 86400, 85 | 'update-static-leases' => true, 86 | 'one-lease-per-client' => 'on', 87 | 'authoritative' => '', 88 | 'ping-check' => true 89 | ) 90 | options( 91 | 'dhcp6.name-servers' => '2001:4860:4860::8888, 2001:4860:4860::8844' 92 | ) 93 | action :create 94 | end 95 | ``` 96 | -------------------------------------------------------------------------------- /documentation/dhcp_group.md: -------------------------------------------------------------------------------- 1 | # dhcp_group 2 | 3 | [Back to resource list](../README.md#resources) 4 | 5 | Create a DHCPD group configuration. ( - *The **group** statement*) 6 | 7 | Introduced: v7.0.0 8 | 9 | ## Actions 10 | 11 | - `:create` 12 | - `:delete` 13 | 14 | ## Properties 15 | 16 | | Name | Type | Default | Description | Allowed Values | 17 | | ---------------------- | ------------- | -------------------------------- | ------------------------------------------------------------------- | ------------------- | 18 | | `comment` | String | `nil` | Comment to add to the configuration file | | 19 | | `ip_version` | Symbol | `:ipv4` | Select DHCP or DHCPv6 server to configure | `:ipv4`, `:ipv6` | 20 | | `conf_dir` | String | `/etc/dhcp/dhcpd(6).d/groups.d` | Directory to create configuration file in | | 21 | | `cookbook` | String | `/etc/dhcp/dhcpd(6).d/groups.d` | Cookbook to source configuration file template from | | 22 | | `template` | String | `/etc/dhcp/dhcpd(6).d/groups.d` | Template to use to generate the configuration file | | 23 | | `owner` | String | Platform dependant | Owner of the generated configuration file | | 24 | | `group` | String | Platform dependant | Group of the generated configuration file | | 25 | | `mode` | String | `'0640'` | Filemode of the generated configuration file | | 26 | | `parameters` | Array, Hash | `nil` | DHCPD parameters for the group | | 27 | | `options` | Array, Hash | `nil` | DHCPD options for the group | | 28 | | `evals` | Array | `nil` | DHCPD conditional statements for the group (see dhcp-eval(5)) | | 29 | | `hosts` | Hash | `nil` | Hosts configuration to include within the group | | 30 | 31 | ## Examples 32 | 33 | ```ruby 34 | dhcp_group 'IPPgones' do 35 | options( 36 | 'tftp-server-name' => '192.0.2.10' 37 | ) 38 | end 39 | ``` 40 | -------------------------------------------------------------------------------- /documentation/dhcp_host.md: -------------------------------------------------------------------------------- 1 | # dhcp_host 2 | 3 | [Back to resource list](../README.md#resources) 4 | 5 | Create a DHCPD host configuration. ( - *The **host** statement*) 6 | 7 | Introduced: v7.0.0 8 | 9 | ## Actions 10 | 11 | - `:create` 12 | - `:delete` 13 | 14 | ## Properties 15 | 16 | | Name | Type | Default | Description | Allowed Values | 17 | | ---------------------- | ------------- | -------------------------------- | ------------------------------------------------------------------- | ------------------- | 18 | | `comment` | String | `nil` | Comment to add to the configuration file | | 19 | | `ip_version` | Symbol | `:ipv4` | Select DHCP or DHCPv6 server to configure | `:ipv4`, `:ipv6` | 20 | | `conf_dir` | String | `/etc/dhcp/dhcpd(6).d/hosts.d` | Directory to create configuration file in | | 21 | | `cookbook` | String | `/etc/dhcp/dhcpd(6).d/hosts.d` | Cookbook to source configuration file template from | | 22 | | `template` | String | `/etc/dhcp/dhcpd(6).d/hosts.d` | Template to use to generate the configuration file | | 23 | | `owner` | String | Platform dependant | Owner of the generated configuration file | | 24 | | `group` | String | Platform dependant | Group of the generated configuration file | | 25 | | `mode` | String | `'0640'` | Filemode of the generated configuration file | | 26 | | `identifier` | String | `nil` | DHCPD host identifier (MAC or DHCID) | | 27 | | `address` | String | `nil` | DHCPD address to issue | | 28 | | `parameters` | Array, Hash | `nil` | DHCPD parameters for the host | | 29 | | `options` | Array, Hash | `nil` | DHCPD options for the host | | 30 | 31 | ## Examples 32 | 33 | ```ruby 34 | dhcp_host 'IPv4-Host' do 35 | identifier 'hardware ethernet 00:53:00:00:00:01' 36 | address '192.168.0.10' 37 | options( 38 | 'host-name' => 'test-ipv4-host' 39 | ) 40 | end 41 | ``` 42 | 43 | ```ruby 44 | dhcp_host 'IPv6-Host' do 45 | ip_version :ipv6 46 | identifier 'host-identifier option dhcp6.client-id 00:53:00:00:00:01:a4:65:b7:c8' 47 | address '2001:db8:1:1:0:0:1:10' 48 | options( 49 | 'host-name' => 'test-ipv6-host' 50 | ) 51 | end 52 | ``` 53 | -------------------------------------------------------------------------------- /documentation/dhcp_package.md: -------------------------------------------------------------------------------- 1 | # dhcp_package 2 | 3 | [Back to resource list](../README.md#resources) 4 | 5 | Install ISC DHCPD server from package. 6 | 7 | Introduced: v7.0.0 8 | 9 | ## Actions 10 | 11 | - `:install` 12 | - `:remove` 13 | 14 | ## Properties 15 | 16 | | Name | Type | Default | Description | Allowed Values | 17 | | ---------------------- | ------------- | ----------------------------- | ---------------------------------------------------------------------- | ------------------- | 18 | | `packages` | String, Array | Correct packages for platform | List of packages to install for server operation | | 19 | 20 | ## Examples 21 | 22 | ```ruby 23 | dhcp_package 24 | ``` 25 | 26 | ```ruby 27 | dhcp_package 'dhcpd' do 28 | packages 'isc-dhcp-server' 29 | end 30 | ``` 31 | -------------------------------------------------------------------------------- /documentation/dhcp_service.md: -------------------------------------------------------------------------------- 1 | # dhcp_service 2 | 3 | [Back to resource list](../README.md#resources) 4 | 5 | Manage the DHCPD and DHCPD6 services. 6 | 7 | Introduced: v7.0.0 8 | 9 | ## Actions 10 | 11 | - `:create` 12 | - `:delete` 13 | - `:start` 14 | - `:stop` 15 | - `:reload` 16 | - `:enable` 17 | - `:disable` 18 | 19 | ## Properties 20 | 21 | | Name | Type | Default | Description | Allowed Values | 22 | | ---------------------- | ------------- | ----------------------------- | ---------------------------------------------------------------------- | ------------------- | 23 | | `ip_version` | Symbol | `:ipv4` | Select DHCP or DHCPv6 server to configure | `:ipv4`, `:ipv6` | 24 | | `service_name` | String | `nil` | Custom service name | | 25 | | `systemd_unit_content` | String, Hash | Platform dependant | systemd unit file content for service | | 26 | | `config_file` | String | `/etc/dhcp/dhcpd(6).conf` | The full path to the DHCP server configuration on disk | | 27 | | `config_test` | True, False | `true` | Perform configuration test before starting, restarting or reload | | 28 | | `config_test_fail_action` | Symbol | `:raise` | Action to perform upon a configuration test failure | `:raise`, `:log` | 29 | 30 | ## Examples 31 | 32 | ```ruby 33 | dhcp_service 'dhcpd' do 34 | ip_version :ipv4 35 | action [:create, :enable, :start] 36 | end 37 | ``` 38 | 39 | ```ruby 40 | dhcp_service 'dhcpd6' do 41 | ip_version :ipv6 42 | action [:create, :enable, :start] 43 | end 44 | ``` 45 | -------------------------------------------------------------------------------- /documentation/dhcp_shared_network.md: -------------------------------------------------------------------------------- 1 | # dhcp_shared_network 2 | 3 | [Back to resource list](../README.md#resources) 4 | 5 | Create a DHCPD shared network configuration. ( - *The **shared-network** statement*) 6 | 7 | Introduced: v7.0.0 8 | 9 | ## Actions 10 | 11 | - `:create` 12 | - `:delete` 13 | 14 | ## Properties 15 | 16 | | Name | Type | Default | Description | Allowed Values | 17 | | ---------------------- | ------------- | -------------------------------- | ------------------------------------------------------------------- | ------------------- | 18 | | `comment` | String | `nil` | Comment to add to the configuration file | | 19 | | `ip_version` | Symbol | `:ipv4` | Select DHCP or DHCPv6 server to configure | `:ipv4`, `:ipv6` | 20 | | `conf_dir` | String | `/etc/dhcp/dhcpd(6).d/hosts.d` | Directory to create configuration file in | | 21 | | `cookbook` | String | `/etc/dhcp/dhcpd(6).d/hosts.d` | Cookbook to source configuration file template from | | 22 | | `template` | String | `/etc/dhcp/dhcpd(6).d/hosts.d` | Template to use to generate the configuration file | | 23 | | `owner` | String | Platform dependant | Owner of the generated configuration file | | 24 | | `group` | String | Platform dependant | Group of the generated configuration file | | 25 | | `mode` | String | `'0640'` | Filemode of the generated configuration file | | 26 | | `subnets` | Hash | `nil` | Subnets for the DHCPD shared network | | 27 | 28 | ## Examples 29 | 30 | ```ruby 31 | dhcp_shared_network 'single' do 32 | subnets( 33 | '192.168.1.0' => { 34 | 'subnet' => '192.168.1.0', 35 | 'netmask' => '255.255.255.0', 36 | 'options' => { 37 | 'broadcast-address' => '192.168.1.255', 38 | 'routers' => '192.168.1.1', 39 | }, 40 | 'pool' => { 41 | 'range' => '192.168.1.20 192.168.1.30', 42 | }, 43 | } 44 | ) 45 | end 46 | ``` 47 | 48 | ```ruby 49 | dhcp_shared_network 'multiple' do 50 | subnets( 51 | '192.168.2.0' => { 52 | 'subnet' => '192.168.2.0', 53 | 'netmask' => '255.255.255.0', 54 | 'options' => { 55 | 'broadcast-address' => '192.168.2.255', 56 | 'routers' => '192.168.2.1', 57 | }, 58 | 'pool' => { 59 | 'range' => '192.168.2.20 192.168.2.30', 60 | }, 61 | }, 62 | '192.168.3.0' => { 63 | 'subnet' => '192.168.3.0', 64 | 'netmask' => '255.255.255.0', 65 | 'options' => { 66 | 'broadcast-address' => '192.168.3.255', 67 | 'routers' => '192.168.3.1', 68 | }, 69 | 'pool' => { 70 | 'range' => [ 71 | '192.168.3.20 192.168.3.30', 72 | '192.168.3.40 192.168.3.50', 73 | ], 74 | }, 75 | } 76 | ) 77 | end 78 | ``` 79 | -------------------------------------------------------------------------------- /documentation/dhcp_subnet.md: -------------------------------------------------------------------------------- 1 | # dhcp_subnet 2 | 3 | [Back to resource list](../README.md#resources) 4 | 5 | Create and manage the main DHCPD configuration. () 6 | 7 | Introduced: v7.0.0 8 | 9 | ## Actions 10 | 11 | - `:create` 12 | - `:delete` 13 | 14 | ## Properties 15 | 16 | | Name | Type | Default | Description | Allowed Values | 17 | | ---------------------- | ------------- | -------------------------------- | ------------------------------------------------------------------- | ------------------- | 18 | | `comment` | String | `nil` | Comment to add to the configuration file | | 19 | | `ip_version` | Symbol | `:ipv4` | Select DHCP or DHCPv6 server to configure | `:ipv4`, `:ipv6` | 20 | | `conf_dir` | String | `/etc/dhcp/dhcpd(6).d/classes.d` | Directory to create configuration file in | | 21 | | `cookbook` | String | `/etc/dhcp/dhcpd(6).d/classes.d` | Cookbook to source configuration file template from | | 22 | | `template` | String | `/etc/dhcp/dhcpd(6).d/classes.d` | Template to use to generate the configuration file | | 23 | | `owner` | String | Platform dependant | Owner of the generated configuration file | | 24 | | `group` | String | Platform dependant | Group of the generated configuration file | | 25 | | `mode` | String | `'0640'` | Filemode of the generated configuration file | | 26 | | `shared_network` | True, False | `false` | DHCP failover configuration file path | | 27 | | `subnet` | String | `nil` | Subnet address | | 28 | | `netmask` | String | `nil` | Subnet network for IPv4 subnets | | 29 | | `prefix` | String | `nil` | Subnet prefix for IPv6 subnets | | 30 | | `subnet` | String | `nil` | Subnet address | | 31 | | `subnet` | String | `nil` | Subnet address | | 32 | | `parameters` | Array, Hash | `nil` | DHCPD parameters for the subnet | | 33 | | `options` | Array, Hash | `nil` | DHCPD options for the subnet | | 34 | | `evals` | Array | `nil` | DHCPD conditional statements for the subnet (see dhcp-eval(5)) | | 35 | | `keys` | Hash | `nil` | TSIG keys configuration | | 36 | | `zones` | Hash | `nil` | Dynamic DNS zone configuration | | 37 | | `allow` | Array | `nil` | | | 38 | | `deny` | Array | `nil` | | | 39 | | `extra_lines` | String, Array | `nil` | Extra lines to append to the configuration file | | 40 | | `pools` | Hash, Array | `nil` | Pool configuration hash(es), accepts most properties (see dhcpd.conf(5))| | 41 | 42 | ## Examples 43 | 44 | ```ruby 45 | dhcp_subnet '192.168.9.0' do 46 | comment 'Listen Subnet Declaration' 47 | subnet '192.168.9.0' 48 | netmask '255.255.255.0' 49 | end 50 | 51 | dhcp_subnet 'basic' do 52 | comment 'Basic Subnet Declaration' 53 | subnet '192.168.0.0' 54 | netmask '255.255.255.0' 55 | options [ 56 | 'routers 192.168.0.1' 57 | 'time-offset 10', 58 | ] 59 | pool 'range' => '192.168.0.100 192.168.0.200' 60 | end 61 | ``` 62 | 63 | ```ruby 64 | dhcp_subnet 'dhcpv6_listen' do 65 | ip_version :ipv6 66 | comment 'Testing DHCPv6 Basic Subnet' 67 | subnet '2001:db8:1::' 68 | prefix 64 69 | end 70 | 71 | dhcp_subnet 'dhcpv6_basic' do 72 | ip_version :ipv6 73 | comment 'Testing DHCPv6 Basic Subnet' 74 | subnet '2001:db8:2:1::' 75 | prefix 64 76 | options( 77 | 'domain-name' => '"test.domain.local"', 78 | 'dhcp6.name-servers' => '2001:4860:4860::8888, 2001:4860:4860::8844' 79 | ) 80 | parameters( 81 | 'ddns-domainname' => '"test.domain.local"', 82 | 'default-lease-time' => 28800 83 | ) 84 | range [ 85 | '2001:db8:2:1::1:0/112', 86 | ] 87 | end 88 | ``` 89 | -------------------------------------------------------------------------------- /kitchen.dokken.yml: -------------------------------------------------------------------------------- 1 | driver: 2 | name: dokken 3 | privileged: true 4 | chef_version: <%= ENV['CHEF_VERSION'] || 'current' %> 5 | 6 | transport: { name: dokken } 7 | provisioner: { name: dokken } 8 | 9 | platforms: 10 | - name: almalinux-8 11 | driver: 12 | image: dokken/almalinux-8 13 | pid_one_command: /usr/lib/systemd/systemd 14 | 15 | - name: almalinux-9 16 | driver: 17 | image: dokken/almalinux-9 18 | pid_one_command: /usr/lib/systemd/systemd 19 | 20 | - name: amazonlinux-2023 21 | driver: 22 | image: dokken/amazonlinux-2023 23 | pid_one_command: /usr/lib/systemd/systemd 24 | 25 | - name: centos-7 26 | driver: 27 | image: dokken/centos-7 28 | pid_one_command: /usr/lib/systemd/systemd 29 | 30 | - name: centos-stream-8 31 | driver: 32 | image: dokken/centos-stream-8 33 | pid_one_command: /usr/lib/systemd/systemd 34 | 35 | - name: centos-stream-9 36 | driver: 37 | image: dokken/centos-stream-9 38 | pid_one_command: /usr/lib/systemd/systemd 39 | 40 | - name: debian-9 41 | driver: 42 | image: dokken/debian-9 43 | pid_one_command: /bin/systemd 44 | 45 | - name: debian-10 46 | driver: 47 | image: dokken/debian-10 48 | pid_one_command: /bin/systemd 49 | 50 | - name: debian-11 51 | driver: 52 | image: dokken/debian-11 53 | pid_one_command: /bin/systemd 54 | 55 | - name: debian-12 56 | driver: 57 | image: dokken/debian-12 58 | pid_one_command: /bin/systemd 59 | 60 | - name: fedora-latest 61 | driver: 62 | image: dokken/fedora-latest 63 | pid_one_command: /usr/lib/systemd/systemd 64 | 65 | - name: opensuse-leap-15 66 | driver: 67 | image: dokken/opensuse-leap-15 68 | pid_one_command: /usr/lib/systemd/systemd 69 | 70 | - name: oraclelinux-7 71 | driver: 72 | image: dokken/oraclelinux-7 73 | pid_one_command: /usr/lib/systemd/systemd 74 | 75 | - name: oraclelinux-8 76 | driver: 77 | image: dokken/oraclelinux-8 78 | pid_one_command: /usr/lib/systemd/systemd 79 | 80 | - name: oraclelinux-9 81 | driver: 82 | image: dokken/oraclelinux-9 83 | pid_one_command: /usr/lib/systemd/systemd 84 | 85 | - name: rockylinux-8 86 | driver: 87 | image: dokken/rockylinux-8 88 | pid_one_command: /usr/lib/systemd/systemd 89 | 90 | - name: rockylinux-9 91 | driver: 92 | image: dokken/rockylinux-9 93 | pid_one_command: /usr/lib/systemd/systemd 94 | 95 | - name: ubuntu-18.04 96 | driver: 97 | image: dokken/ubuntu-18.04 98 | pid_one_command: /bin/systemd 99 | 100 | - name: ubuntu-20.04 101 | driver: 102 | image: dokken/ubuntu-20.04 103 | pid_one_command: /bin/systemd 104 | 105 | - name: ubuntu-22.04 106 | driver: 107 | image: dokken/ubuntu-22.04 108 | pid_one_command: /bin/systemd 109 | 110 | - name: ubuntu-23.04 111 | driver: 112 | image: dokken/ubuntu-23.04 113 | pid_one_command: /bin/systemd 114 | -------------------------------------------------------------------------------- /kitchen.exec.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: { name: exec } 3 | transport: { name: exec } 4 | 5 | platforms: 6 | - name: macos-latest 7 | - name: windows-latest 8 | -------------------------------------------------------------------------------- /kitchen.global.yml: -------------------------------------------------------------------------------- 1 | --- 2 | provisioner: 3 | name: chef_infra 4 | product_name: chef 5 | product_version: <%= ENV['CHEF_VERSION'] || 'latest' %> 6 | channel: stable 7 | install_strategy: once 8 | chef_license: accept 9 | enforce_idempotency: <%= ENV['ENFORCE_IDEMPOTENCY'] || true %> 10 | multiple_converge: <%= ENV['MULTIPLE_CONVERGE'] || 2 %> 11 | deprecations_as_errors: true 12 | log_level: <%= ENV['CHEF_LOG_LEVEL'] || 'auto' %> 13 | 14 | verifier: 15 | name: inspec 16 | 17 | platforms: 18 | - name: almalinux-8 19 | - name: almalinux-9 20 | - name: amazonlinux-2023 21 | - name: centos-7 22 | - name: centos-stream-8 23 | - name: centos-stream-9 24 | - name: debian-9 25 | - name: debian-10 26 | - name: debian-11 27 | - name: debian-12 28 | - name: fedora-latest 29 | - name: opensuse-leap-15 30 | - name: oraclelinux-7 31 | - name: oraclelinux-8 32 | - name: oraclelinux-9 33 | - name: rockylinux-8 34 | - name: rockylinux-9 35 | - name: ubuntu-18.04 36 | - name: ubuntu-20.04 37 | - name: ubuntu-22.04 38 | - name: ubuntu-23.04 39 | -------------------------------------------------------------------------------- /kitchen.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: 3 | name: vagrant 4 | 5 | provisioner: 6 | name: chef_zero 7 | deprecations_as_errors: true 8 | chef_license: accept 9 | product_name: chef 10 | product_version: <%= ENV['CHEF_VERSION'] || 'latest' %> 11 | install_strategy: always 12 | log_level: <%= ENV['CHEF_LOG_LEVEL'] || 'auto' %> 13 | 14 | verifier: 15 | name: inspec 16 | 17 | platforms: 18 | - name: almalinux-8 19 | - name: almalinux-9 20 | - name: amazonlinux-2 21 | - name: centos-7 22 | - name: centos-stream-8 23 | - name: centos-stream-9 24 | - name: debian-10 25 | - name: debian-11 26 | - name: fedora-latest 27 | - name: rockylinux-8 28 | - name: rockylinux-9 29 | - name: ubuntu-18.04 30 | - name: ubuntu-20.04 31 | - name: ubuntu-22.04 32 | 33 | suites: 34 | - name: default 35 | run_list: 36 | - recipe[test::default] 37 | - name: delete 38 | run_list: 39 | - recipe[test::default] 40 | - recipe[test::config_delete] 41 | -------------------------------------------------------------------------------- /libraries/helpers.rb: -------------------------------------------------------------------------------- 1 | module Dhcp 2 | module Cookbook 3 | module Helpers 4 | def dhcpd_user 5 | 'root' 6 | end 7 | 8 | def dhcpd_group 9 | return 'root' if platform?('debian') 10 | 11 | 'dhcpd' 12 | end 13 | 14 | def dhcpd_packages 15 | case node['platform_family'] 16 | when 'rhel', 'amazon' 17 | return %w(dhcp) if node['platform_version'].to_i < 8 18 | 19 | %w(dhcp-server) 20 | when 'fedora' 21 | %w(dhcp-server) 22 | when 'debian' 23 | %w(isc-dhcp-server isc-dhcp-server-ldap) 24 | end 25 | end 26 | 27 | def dhcpd_service_name(ip_version) 28 | case node['platform_family'] 29 | when 'amazon', 'rhel', 'fedora' 30 | { 31 | ipv4: 'dhcpd', 32 | ipv6: 'dhcpd6', 33 | } 34 | when 'debian' 35 | { 36 | ipv4: 'isc-dhcp-server', 37 | ipv6: 'isc-dhcp-server6', 38 | } 39 | end.fetch(ip_version) 40 | end 41 | 42 | def dhcpd_config_dir 43 | '/etc/dhcp' 44 | end 45 | 46 | def dhcpd_config_file(ip_version) 47 | case ip_version 48 | when :ipv4 49 | "#{dhcpd_config_dir}/dhcpd.conf" 50 | when :ipv6 51 | "#{dhcpd_config_dir}/dhcpd6.conf" 52 | else 53 | raise "IP version #{ip_version} is unknown." 54 | end 55 | end 56 | 57 | def dhcpd_failover_config_file(ip_version) 58 | return '/etc/dhcp/dhcpd.failover.conf' if ip_version.eql?(:ipv4) 59 | 60 | nil 61 | end 62 | 63 | def dhcpd_env_file 64 | case node['platform_family'] 65 | when 'rhel', 'amazon', 'fedora' 66 | '/etc/sysconfig/dhcpd' 67 | when 'debian' 68 | '/etc/default/isc-dhcp-server' 69 | else 70 | raise "dhcpd_env_file: Unsupported platform family #{node['platform_family']}." 71 | end 72 | end 73 | 74 | def dhcpd_config_includes_directory(ip_version) 75 | case ip_version 76 | when :ipv4 77 | "#{dhcpd_config_dir}/dhcpd.d" 78 | when :ipv6 79 | "#{dhcpd_config_dir}/dhcpd6.d" 80 | else 81 | raise "IP version #{ip_version} is unknown." 82 | end 83 | end 84 | 85 | def dhcpd_config_resource_directory(ip_version, resource_type) 86 | case resource_type 87 | when :dhcp_class 88 | "#{dhcpd_config_includes_directory(ip_version)}/classes.d" 89 | when :dhcp_group 90 | "#{dhcpd_config_includes_directory(ip_version)}/groups.d" 91 | when :dhcp_host 92 | "#{dhcpd_config_includes_directory(ip_version)}/hosts.d" 93 | when :dhcp_shared_network 94 | "#{dhcpd_config_includes_directory(ip_version)}/shared_networks.d" 95 | when :dhcp_subnet 96 | "#{dhcpd_config_includes_directory(ip_version)}/subnets.d" 97 | else 98 | raise "Invalid resource type #{resource_type}." 99 | end 100 | end 101 | 102 | def dhcpd_config_test_command(ip_version, config_file) 103 | "dhcpd -#{ip_version.eql?(:ipv4) ? '4' : '6'} -d -t -T -cf #{config_file}" 104 | end 105 | 106 | def dhcpd_lib_dir 107 | if platform_family?('debian') 108 | '/var/lib/dhcp' 109 | else 110 | '/var/lib/dhcpd' 111 | end 112 | end 113 | 114 | def dhcpd_lib_dir_options 115 | case node['platform_family'] 116 | when 'rhel', 'fedora', 'amazon' 117 | { 118 | 'owner' => 'dhcpd', 119 | 'group' => 'dhcpd', 120 | 'mode' => '0755', 121 | } 122 | when 'debian' 123 | case node['platform'] 124 | when 'ubuntu' 125 | { 126 | 'owner' => 'root', 127 | 'group' => 'dhcpd', 128 | 'mode' => '0775', 129 | } 130 | when 'debian' 131 | { 132 | 'owner' => 'root', 133 | 'group' => 'root', 134 | 'mode' => '0755', 135 | } 136 | end 137 | end 138 | end 139 | 140 | def dhcpd_lease_file(ip_version) 141 | case ip_version 142 | when :ipv4 143 | "#{dhcpd_lib_dir}/dhcpd.leases" 144 | when :ipv6 145 | "#{dhcpd_lib_dir}/dhcpd6.leases" 146 | else 147 | raise "#{ip_version} is unknown." 148 | end 149 | end 150 | 151 | def dhcpd_lease_file_options 152 | case node['platform_family'] 153 | when 'rhel', 'fedora', 'amazon' 154 | { 155 | 'owner' => 'dhcpd', 156 | 'group' => 'dhcpd', 157 | 'mode' => '0644', 158 | 'action' => :create_if_missing, 159 | } 160 | when 'debian' 161 | case node['platform'] 162 | when 'ubuntu' 163 | { 164 | 'owner' => 'root', 165 | 'group' => 'dhcpd', 166 | 'mode' => '0664', 167 | 'action' => :create_if_missing, 168 | } 169 | when 'debian' 170 | { 171 | 'owner' => 'root', 172 | 'group' => 'root', 173 | 'mode' => '0644', 174 | 'action' => :create_if_missing, 175 | } 176 | end 177 | end 178 | end 179 | 180 | def dhcpd_use_systemd? 181 | if (platform_family?('rhel') && node['platform_version'].to_i < 7) || 182 | (platform?('ubuntu') && node['platform_version'].to_i < 14) || 183 | (platform_family?('amazon') && node['platform_version'].to_i < 2) 184 | false 185 | else 186 | true 187 | end 188 | end 189 | 190 | def dhcpd_systemd_unit_content(ip_version, config_file) 191 | raise 'Invalid ip_version' unless ip_version.is_a?(Symbol) && %i(ipv4 ipv6).include?(ip_version) 192 | dhcp6 = ip_version.eql?(:ipv6) 193 | 194 | case node['platform'] 195 | when 'almalinux', 'amazon', 'centos', 'fedora', 'redhat', 'rocky' 196 | { 197 | 'Unit' => { 198 | 'Description' => "DHCP#{dhcp6 ? 'v6' : 'v4'} Server Daemon", 199 | 'Documentation' => 'man:dhcpd(8) man:dhcpd.conf(5)', 200 | 'Wants' => 'network-online.target', 201 | 'After' => [ 202 | 'network-online.target', 203 | 'time-sync.target', 204 | ], 205 | 'ConditionPathExists' => [ 206 | '/etc/sysconfig/dhcpd', 207 | "|#{config_file}", 208 | ], 209 | }, 210 | 'Service' => { 211 | 'Type' => 'notify', 212 | 'EnvironmentFile' => '-/etc/sysconfig/dhcpd', 213 | 'ExecStart' => "/usr/sbin/dhcpd -f #{dhcp6 ? '-6' : '-4'} -cf #{config_file} -user dhcpd -group dhcpd --no-pid $DHCPDARGS", 214 | 'StandardError' => 'null', 215 | }, 216 | 'Install' => { 217 | 'WantedBy' => 'multi-user.target', 218 | }, 219 | } 220 | when 'debian' 221 | { 222 | 'Unit' => { 223 | 'Description' => "ISC DHCP IP#{dhcp6 ? 'v6' : 'v4'} Server", 224 | 'Documentation' => 'man:dhcpd(8) man:dhcpd.conf(5)', 225 | 'Wants' => 'network-online.target', 226 | 'After' => [ 227 | 'network-online.target', 228 | 'time-sync.target', 229 | ], 230 | 'ConditionPathExists' => [ 231 | '/etc/default/isc-dhcp-server', 232 | "|#{config_file}", 233 | ], 234 | }, 235 | 'Service' => { 236 | 'EnvironmentFile' => '/etc/default/isc-dhcp-server', 237 | 'RuntimeDirectory' => 'dhcp-server', 238 | 'ExecStart' => "/usr/sbin/dhcpd -f #{dhcp6 ? '-6' : '-4'} -cf #{config_file} -pf /run/dhcp-server/#{dhcp6 ? 'dhcpd6' : 'dhcpd'}.pid $INTERFACES", 239 | }, 240 | 'Install' => { 241 | 'WantedBy' => 'multi-user.target', 242 | }, 243 | } 244 | when 'ubuntu' 245 | { 246 | 'Unit' => { 247 | 'Description' => "ISC DHCP IP#{dhcp6 ? 'v6' : 'v4'} Server", 248 | 'Documentation' => 'man:dhcpd(8) man:dhcpd.conf(5)', 249 | 'Wants' => 'network-online.target', 250 | 'After' => [ 251 | 'network-online.target', 252 | 'time-sync.target', 253 | ], 254 | 'ConditionPathExists' => [ 255 | '/etc/default/isc-dhcp-server', 256 | "|#{config_file}", 257 | ], 258 | }, 259 | 'Service' => { 260 | 'EnvironmentFile' => '/etc/default/isc-dhcp-server', 261 | 'RuntimeDirectory' => 'dhcp-server', 262 | 'ExecStart' => "/usr/sbin/dhcpd -f #{dhcp6 ? '-6' : '-4'} -cf #{config_file} -user dhcpd -group dhcpd -pf /run/dhcp-server/#{dhcp6 ? 'dhcpd6' : 'dhcpd'}.pid $INTERFACES", 263 | }, 264 | 'Install' => { 265 | 'WantedBy' => 'multi-user.target', 266 | }, 267 | } 268 | else 269 | raise "Platform #{node['platform']} is not a supported systemd OS." 270 | end 271 | end 272 | end 273 | end 274 | end 275 | -------------------------------------------------------------------------------- /libraries/resource.rb: -------------------------------------------------------------------------------- 1 | module Dhcp 2 | module Cookbook 3 | module ResourceHelpers 4 | # Create a list resource for the present conf_dir unless it already exists in the resource collection 5 | def init_list_resource(directory) 6 | create_list_resource(directory) unless list_resource_exist?(directory) 7 | end 8 | 9 | # Add the caller configuration resource to the relevant list for the directory it is being created in 10 | def add_to_list_resource(directory, config_file) 11 | manage_list_resource(directory, config_file, :add) 12 | end 13 | 14 | # Remove the caller configuration resource from the relevant list for the directory it is being created in 15 | def remove_from_list_resource(directory, config_file) 16 | manage_list_resource(directory, config_file, :remove) 17 | end 18 | 19 | private 20 | 21 | # Check if the list resource is already present in the resource collection 22 | def list_resource_exist?(directory) 23 | !find_resource!(:template, "#{directory}/list.conf").nil? 24 | rescue Chef::Exceptions::ResourceNotFound 25 | false 26 | end 27 | 28 | # Declare resources to create a list resource and it's containing directory 29 | def create_list_resource(directory) 30 | with_run_context(:root) do 31 | declare_resource(:directory, directory) 32 | declare_resource(:template, "#{directory}/list.conf") do 33 | cookbook 'dhcp' 34 | source 'list.conf.erb' 35 | 36 | owner new_resource.owner 37 | group new_resource.group 38 | mode new_resource.mode 39 | 40 | variables['files'] ||= [] 41 | 42 | action :nothing 43 | delayed_action :create 44 | end 45 | end 46 | end 47 | 48 | # Find the include list resource for the relevant directory and return it 49 | def list_resource(directory) 50 | find_resource!(:template, "#{directory}/list.conf") 51 | end 52 | 53 | # Manage addition and removal from an include list template ensuring it is already declared in the resource collection 54 | def manage_list_resource(directory, config_file, action) 55 | init_list_resource(directory) 56 | files = list_resource(directory).variables['files'] 57 | 58 | case action 59 | when :add 60 | files.push(config_file) unless files.include?(config_file) 61 | when :remove 62 | files.delete(config_file) if files.include?(config_file) 63 | end 64 | end 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /libraries/template.rb: -------------------------------------------------------------------------------- 1 | module Dhcp 2 | module Cookbook 3 | module TemplateHelpers 4 | def nil_or_empty?(*values) 5 | values.any? { |v| v.nil? || (v.respond_to?(:empty?) && v.empty?) } 6 | end 7 | 8 | def property_array(property) 9 | return property if property.is_a?(Array) 10 | 11 | [property] 12 | end 13 | 14 | def property_collection(property) 15 | property.map { |p| p.is_a?(Array) ? p : p.split(' ', 2) } 16 | end 17 | 18 | def property_collection_sorted(property) 19 | property_collection(property.sort) 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /metadata.rb: -------------------------------------------------------------------------------- 1 | name 'dhcp' 2 | maintainer 'Sous Chefs' 3 | maintainer_email 'help@sous-chefs.org' 4 | license 'Apache-2.0' 5 | description 'Installs and configures DHCP' 6 | chef_version '>= 15.3' 7 | source_url 'https://github.com/sous-chefs/dhcp' 8 | issues_url 'https://github.com/sous-chefs/dhcp/issues' 9 | version '8.1.16' 10 | 11 | supports 'amazon' 12 | supports 'centos' 13 | supports 'debian' 14 | supports 'fedora' 15 | supports 'oracle' 16 | supports 'redhat' 17 | supports 'scientific' 18 | supports 'ubuntu' 19 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:base"], 4 | "packageRules": [ 5 | { 6 | "groupName": "Actions", 7 | "matchUpdateTypes": ["minor", "patch", "pin"], 8 | "automerge": true, 9 | "addLabels": ["Release: Patch", "Skip: Announcements"] 10 | }, 11 | { 12 | "groupName": "Actions", 13 | "matchUpdateTypes": ["major"], 14 | "automerge": false, 15 | "addLabels": ["Release: Patch", "Skip: Announcements"] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /resources/class.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: dhcp 3 | # Resource:: class 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | unified_mode true 19 | 20 | include Dhcp::Cookbook::Helpers 21 | 22 | property :comment, String, 23 | description: 'Unparsed comment to add to the configuration file' 24 | 25 | property :ip_version, [Symbol, String], 26 | equal_to: [:ipv4, :ipv6, 'ipv4', 'ipv6'], 27 | coerce: proc { |ipv| ipv.is_a?(Symbol) ? ipv : ipv.to_sym }, 28 | default: :ipv4, 29 | description: 'The IP version, 4 or 6' 30 | 31 | property :conf_dir, String, 32 | default: lazy { dhcpd_config_resource_directory(ip_version, declared_type) }, 33 | description: 'Directory to create configuration file in' 34 | 35 | property :cookbook, String, 36 | default: 'dhcp', 37 | description: 'Template source cookbook' 38 | 39 | property :template, String, 40 | default: 'class.conf.erb', 41 | description: 'Template source file' 42 | 43 | property :owner, String, 44 | default: lazy { dhcpd_user }, 45 | description: 'Generated file owner' 46 | 47 | property :group, String, 48 | default: lazy { dhcpd_group }, 49 | description: 'Generated file group' 50 | 51 | property :mode, String, 52 | default: '0640', 53 | description: 'Generated file mode' 54 | 55 | property :match, String, 56 | description: 'Client match statement' 57 | 58 | property :parameters, [Hash, Array], 59 | description: 'Class client parameters' 60 | 61 | property :options, [Hash, Array], 62 | description: 'Class client options' 63 | 64 | property :subclass, Array, 65 | description: 'Class subclass match statements' 66 | 67 | action_class do 68 | include Dhcp::Cookbook::ResourceHelpers 69 | end 70 | 71 | action :create do 72 | template "#{new_resource.conf_dir}/#{new_resource.name}.conf" do 73 | cookbook new_resource.cookbook 74 | source new_resource.template 75 | 76 | owner new_resource.owner 77 | group new_resource.group 78 | mode new_resource.mode 79 | 80 | variables( 81 | name: new_resource.name, 82 | comment: new_resource.comment, 83 | match: new_resource.match, 84 | parameters: new_resource.parameters, 85 | options: new_resource.options, 86 | subclasses: new_resource.subclass 87 | ) 88 | helpers(Dhcp::Cookbook::TemplateHelpers) 89 | 90 | action :create 91 | end 92 | 93 | add_to_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf") 94 | end 95 | 96 | action :delete do 97 | file "#{new_resource.conf_dir}/#{new_resource.name}.conf" do 98 | action :delete 99 | end 100 | 101 | remove_from_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf") 102 | end 103 | -------------------------------------------------------------------------------- /resources/config.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: dhcp 3 | # Resource:: config 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | unified_mode true 19 | 20 | include Dhcp::Cookbook::Helpers 21 | 22 | property :ip_version, [Symbol, String], 23 | equal_to: [:ipv4, :ipv6, 'ipv4', 'ipv6'], 24 | coerce: proc { |ipv| ipv.is_a?(Symbol) ? ipv : ipv.to_sym }, 25 | default: :ipv4, 26 | description: 'The IP version, 4 or 6' 27 | 28 | property :config_file, String, 29 | default: lazy { dhcpd_config_file(ip_version) }, 30 | description: 'The full path to the DHCP server configuration on disk' 31 | 32 | property :cookbook, String, 33 | default: 'dhcp' 34 | 35 | property :template, String, 36 | default: 'dhcpd.conf.erb' 37 | 38 | property :owner, String, 39 | default: lazy { dhcpd_user } 40 | 41 | property :group, String, 42 | default: lazy { dhcpd_group } 43 | 44 | property :mode, String, 45 | default: '0640' 46 | 47 | property :config_failover_file, String, 48 | default: lazy { dhcpd_failover_config_file(ip_version) }, 49 | description: 'The full path to the DHCP server failover configuration on disk' 50 | 51 | property :config_includes_directory, String, 52 | default: lazy { dhcpd_config_includes_directory(ip_version) } 53 | 54 | property :lib_dir, String, 55 | default: lazy { dhcpd_lib_dir }, 56 | description: 'The full path to the DHCP lib directory on disk' 57 | 58 | property :lease_file, String, 59 | default: lazy { dhcpd_lease_file(ip_version) }, 60 | description: 'The full path to the DHCP server leases file on disk' 61 | 62 | property :allow, Array, 63 | description: 'Allow access control' 64 | 65 | property :deny, Array, 66 | description: 'Deny access control' 67 | 68 | property :ignore, Array, 69 | description: 'Ignore access control' 70 | 71 | property :parameters, [Hash, Array], 72 | description: 'Global parameters' 73 | 74 | property :options, [Hash, Array], 75 | description: 'Global options' 76 | 77 | property :evals, Array, 78 | description: 'Global conditional eval statements' 79 | 80 | property :keys, Hash, 81 | description: 'TSIG keys' 82 | 83 | property :zones, Hash, 84 | description: 'Dynamic DNS zones' 85 | 86 | property :hooks, Hash, 87 | description: 'Server event action configuration' 88 | 89 | property :failover, Hash, 90 | description: 'DHCP failover configuration' 91 | 92 | property :include_files, Array, 93 | description: 'Additional configuration files to include' 94 | 95 | property :extra_lines, [String, Array], 96 | description: 'Extra lines to append to the main configuration file' 97 | 98 | property :env_file, String, 99 | default: lazy { dhcpd_env_file }, 100 | description: 'Configuration file lines for the DHCP environment' 101 | 102 | property :env_file_lines, [String, Array], 103 | description: 'Configuration file lines for the DHCP environment file' 104 | 105 | action_class do 106 | include Dhcp::Cookbook::ResourceHelpers 107 | 108 | DHCPD_CONFIG_INCLUDES_DIRECTORIES = %w(classes.d groups.d hosts.d shared_networks.d subnets.d).freeze 109 | end 110 | 111 | action :create do 112 | raise 'DHCP failover is only supported for IPv4' if new_resource.failover && new_resource.ip_version.eql?(:ipv6) 113 | 114 | directory new_resource.config_includes_directory do 115 | owner new_resource.owner 116 | group new_resource.group 117 | 118 | action :create 119 | end 120 | 121 | DHCPD_CONFIG_INCLUDES_DIRECTORIES.each do |dir| 122 | directory "#{new_resource.config_includes_directory}/#{dir}" do 123 | owner new_resource.owner 124 | group new_resource.group 125 | 126 | action :create 127 | end 128 | 129 | init_list_resource("#{new_resource.config_includes_directory}/#{dir}") 130 | end 131 | 132 | # Pre-condition DHCPd lib directory and lease file for distros that don't take care of this 133 | dhcpd_lib_dir_options.each { |property, value| edit_resource(:directory, new_resource.lib_dir).send(property, value) } 134 | dhcpd_lease_file_options.each { |property, value| edit_resource(:file, new_resource.lease_file).send(property, value) } 135 | 136 | template new_resource.config_file do 137 | cookbook new_resource.cookbook 138 | source new_resource.template 139 | 140 | owner new_resource.owner 141 | group new_resource.group 142 | mode new_resource.mode 143 | 144 | variables( 145 | includes_dir: dhcpd_config_includes_directory(new_resource.ip_version), 146 | allow: new_resource.allow, 147 | deny: new_resource.deny, 148 | ignore: new_resource.ignore, 149 | parameters: new_resource.parameters, 150 | options: new_resource.options, 151 | evals: new_resource.evals, 152 | keys: new_resource.keys, 153 | zones: new_resource.zones, 154 | hooks: new_resource.hooks, 155 | failover: new_resource.failover, 156 | include_files: new_resource.include_files, 157 | extra_lines: new_resource.extra_lines 158 | ) 159 | helpers(Dhcp::Cookbook::TemplateHelpers) 160 | 161 | action :create 162 | end 163 | 164 | template new_resource.env_file do 165 | cookbook 'dhcp' 166 | source 'dhcpd-env.erb' 167 | 168 | owner new_resource.owner 169 | group new_resource.group 170 | mode new_resource.mode 171 | 172 | variables( 173 | lines: new_resource.env_file_lines 174 | ) 175 | helpers(Dhcp::Cookbook::TemplateHelpers) 176 | 177 | action :create 178 | end if new_resource.env_file 179 | 180 | if new_resource.failover 181 | template new_resource.config_failover_file do 182 | cookbook 'dhcp' 183 | source 'dhcpd.failover.conf.erb' 184 | 185 | owner new_resource.owner 186 | group new_resource.group 187 | mode new_resource.mode 188 | 189 | variables( 190 | failover: new_resource.failover 191 | ) 192 | helpers(Dhcp::Cookbook::TemplateHelpers) 193 | 194 | action :create 195 | end 196 | end 197 | end 198 | 199 | action :delete do 200 | file new_resource.config_file do 201 | action :delete 202 | end 203 | 204 | file new_resource.config_failover_file do 205 | action :delete 206 | end if new_resource.ip_version.eql?(:ipv4) 207 | end 208 | -------------------------------------------------------------------------------- /resources/group.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: dhcp 3 | # Resource:: group 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | unified_mode true 19 | 20 | include Dhcp::Cookbook::Helpers 21 | 22 | property :comment, String, 23 | description: 'Unparsed comment to add to the configuration file' 24 | 25 | property :ip_version, [Symbol, String], 26 | equal_to: [:ipv4, :ipv6, 'ipv4', 'ipv6'], 27 | coerce: proc { |ipv| ipv.is_a?(Symbol) ? ipv : ipv.to_sym }, 28 | default: :ipv4, 29 | description: 'The IP version, 4 or 6' 30 | 31 | property :conf_dir, String, 32 | default: lazy { dhcpd_config_resource_directory(ip_version, declared_type) }, 33 | description: 'Directory to create configuration file in' 34 | 35 | property :cookbook, String, 36 | default: 'dhcp', 37 | description: 'Template source cookbook' 38 | 39 | property :template, String, 40 | default: 'group.conf.erb', 41 | description: 'Template source file' 42 | 43 | property :owner, String, 44 | default: lazy { dhcpd_user }, 45 | description: 'Generated file owner' 46 | 47 | property :group, String, 48 | default: lazy { dhcpd_group }, 49 | description: 'Generated file group' 50 | 51 | property :mode, String, 52 | default: '0640', 53 | description: 'Generated file mode' 54 | 55 | property :parameters, [Hash, Array], 56 | description: 'Group client parameters' 57 | 58 | property :options, [Hash, Array], 59 | description: 'Group client options' 60 | 61 | property :evals, Array, 62 | description: 'Group conditional eval statements' 63 | 64 | property :hosts, Hash, 65 | description: 'Group host members' 66 | 67 | action_class do 68 | include Dhcp::Cookbook::ResourceHelpers 69 | end 70 | 71 | action :create do 72 | hosts_include = [] 73 | 74 | new_resource.hosts.each do |host, properties| 75 | declare_resource(:dhcp_host, "#{new_resource.name}_grouphost_#{host}") do 76 | owner new_resource.owner 77 | group new_resource.group 78 | mode new_resource.mode 79 | 80 | ip_version new_resource.ip_version 81 | conf_dir new_resource.conf_dir 82 | group_host true 83 | 84 | properties.each { |property, value| send(property, value) } 85 | end 86 | 87 | hosts_include.push("#{new_resource.conf_dir}/#{new_resource.name}_grouphost_#{host}.conf") 88 | end 89 | 90 | template "#{new_resource.conf_dir}/#{new_resource.name}.conf" do 91 | cookbook new_resource.cookbook 92 | source new_resource.template 93 | 94 | owner new_resource.owner 95 | group new_resource.group 96 | mode new_resource.mode 97 | 98 | variables( 99 | name: new_resource.name, 100 | comment: new_resource.comment, 101 | ip_version: new_resource.ip_version, 102 | parameters: new_resource.parameters, 103 | options: new_resource.options, 104 | evals: new_resource.evals, 105 | hosts: hosts_include 106 | ) 107 | helpers(Dhcp::Cookbook::TemplateHelpers) 108 | 109 | action :create 110 | end 111 | 112 | add_to_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf") 113 | end 114 | 115 | action :delete do 116 | file "#{new_resource.conf_dir}/#{new_resource.name}.conf" do 117 | action :delete 118 | end 119 | 120 | remove_from_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf") 121 | end 122 | -------------------------------------------------------------------------------- /resources/host.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: dhcp 3 | # Resource:: host 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | unified_mode true 19 | 20 | include Dhcp::Cookbook::Helpers 21 | 22 | property :comment, String, 23 | description: 'Unparsed comment to add to the configuration file' 24 | 25 | property :ip_version, [Symbol, String], 26 | equal_to: [:ipv4, :ipv6, 'ipv4', 'ipv6'], 27 | coerce: proc { |ipv| ipv.is_a?(Symbol) ? ipv : ipv.to_sym }, 28 | default: :ipv4, 29 | description: 'The IP version, 4 or 6' 30 | 31 | property :conf_dir, String, 32 | default: lazy { dhcpd_config_resource_directory(ip_version, declared_type) }, 33 | description: 'Directory to create configuration file in' 34 | 35 | property :cookbook, String, 36 | default: 'dhcp', 37 | description: 'Template source cookbook' 38 | 39 | property :template, String, 40 | default: 'host.conf.erb', 41 | description: 'Template source file' 42 | 43 | property :owner, String, 44 | default: lazy { dhcpd_user }, 45 | description: 'Generated file owner' 46 | 47 | property :group, String, 48 | default: lazy { dhcpd_group }, 49 | description: 'Generated file group' 50 | 51 | property :mode, String, 52 | default: '0640', 53 | description: 'Generated file mode' 54 | 55 | property :group_host, [true, false], 56 | default: false, 57 | description: 'Flag to indicate host is used inside a group resource and should not be added to list.conf' 58 | 59 | property :identifier, String, 60 | description: 'Host identifier, usually MAC or DHCID' 61 | 62 | property :address, String, 63 | description: 'Host assigned address' 64 | 65 | property :parameters, [Hash, Array], 66 | description: 'Host parameters' 67 | 68 | property :options, [Hash, Array], 69 | description: 'Host options' 70 | 71 | action_class do 72 | include Dhcp::Cookbook::ResourceHelpers 73 | end 74 | 75 | action :create do 76 | template "#{new_resource.conf_dir}/#{new_resource.name}.conf" do 77 | cookbook new_resource.cookbook 78 | source new_resource.template 79 | 80 | owner new_resource.owner 81 | group new_resource.group 82 | mode new_resource.mode 83 | 84 | variables( 85 | name: new_resource.name, 86 | comment: new_resource.comment, 87 | ip_version: new_resource.ip_version, 88 | identifier: new_resource.identifier, 89 | address: new_resource.address, 90 | parameters: new_resource.parameters, 91 | options: new_resource.options 92 | ) 93 | helpers(Dhcp::Cookbook::TemplateHelpers) 94 | 95 | action :create 96 | end 97 | 98 | add_to_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf") unless new_resource.group_host 99 | end 100 | 101 | action :delete do 102 | file "#{new_resource.conf_dir}/#{new_resource.name}.conf" do 103 | action :delete 104 | end 105 | 106 | remove_from_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf") unless new_resource.group_host 107 | end 108 | -------------------------------------------------------------------------------- /resources/package.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: dhcp 3 | # Resource:: package 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | unified_mode true 19 | 20 | include Dhcp::Cookbook::Helpers 21 | 22 | property :packages, [String, Array], 23 | default: lazy { dhcpd_packages } 24 | 25 | action_class do 26 | def do_action(package_action) 27 | package 'ISC DHCPD' do 28 | package_name new_resource.packages 29 | 30 | action package_action 31 | end 32 | end 33 | end 34 | 35 | action :install do 36 | do_action(action) 37 | end 38 | 39 | action :remove do 40 | do_action(action) 41 | end 42 | -------------------------------------------------------------------------------- /resources/service.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: dhcp 3 | # Resource:: service 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | unified_mode true 19 | 20 | include Dhcp::Cookbook::Helpers 21 | 22 | property :ip_version, [Symbol, String], 23 | equal_to: [:ipv4, :ipv6, 'ipv4', 'ipv6'], 24 | coerce: proc { |p| p.is_a?(Symbol) ? p : p.to_sym }, 25 | default: :ipv4, 26 | description: 'The IP version, 4 or 6' 27 | 28 | property :service_name, String, 29 | coerce: proc { |p| "#{p}.service" }, 30 | default: lazy { dhcpd_service_name(ip_version) }, 31 | description: 'Override the default service names' 32 | 33 | property :systemd_unit_content, [String, Hash], 34 | default: lazy { dhcpd_systemd_unit_content(ip_version, config_file) }, 35 | description: 'Override the systemd unit file contents' 36 | 37 | property :config_file, String, 38 | default: lazy { dhcpd_config_file(ip_version) }, 39 | description: 'The full path to the DHCP server configuration on disk' 40 | 41 | property :config_test, [true, false], 42 | default: true, 43 | description: 'Perform configuration file test before performing service action' 44 | 45 | property :config_test_fail_action, Symbol, 46 | equal_to: %i(raise log), 47 | default: :raise, 48 | description: 'Action to perform upon configuration test failure.' 49 | 50 | action_class do 51 | def do_service_action(resource_action) 52 | with_run_context(:root) do 53 | if %i(start restart reload).include?(resource_action) 54 | declare_resource(:ruby_block, "Run pre #{new_resource.service_name} #{resource_action} configuration test") do 55 | block do 56 | begin 57 | if new_resource.config_test 58 | cmd = Mixlib::ShellOut.new(dhcpd_config_test_command(new_resource.ip_version, new_resource.config_file)) 59 | cmd.user = dhcpd_user 60 | cmd.run_command.error! 61 | Chef::Log.info("Configuration test passed, creating #{new_resource.service_name} #{new_resource.declared_type} resource with action #{resource_action}") 62 | else 63 | Chef::Log.info("Configuration test disabled, creating #{new_resource.service_name} #{new_resource.declared_type} resource with action #{resource_action}") 64 | end 65 | 66 | declare_resource(:service, new_resource.service_name.delete_suffix('.service')) { delayed_action(resource_action) } 67 | rescue Mixlib::ShellOut::ShellCommandFailed 68 | if new_resource.config_test_fail_action.eql?(:log) 69 | Chef::Log.error("Configuration test failed, #{new_resource.service_name} #{resource_action} action aborted!\n\n"\ 70 | "Error\n-----\n#{cmd.stderr}") 71 | else 72 | raise "Configuration test failed, #{new_resource.service_name} #{resource_action} action aborted!\n\n"\ 73 | "Error\n-----\nAction: #{resource_action}\n#{cmd.stderr}" 74 | end 75 | end 76 | end 77 | 78 | only_if { ::File.exist?(new_resource.config_file) } 79 | 80 | action :nothing 81 | delayed_action :run 82 | end 83 | else 84 | declare_resource(:service, new_resource.service_name.delete_suffix('.service')) { delayed_action(resource_action) } 85 | end 86 | end 87 | end 88 | end 89 | 90 | action :create do 91 | with_run_context :root do 92 | declare_resource(:systemd_unit, new_resource.service_name) do 93 | content new_resource.systemd_unit_content 94 | triggers_reload true 95 | verify false 96 | 97 | action :create 98 | end if dhcpd_use_systemd? 99 | end 100 | end 101 | 102 | action :delete do 103 | do_service_action([:stop, :disable]) 104 | with_run_context :root do 105 | declare_resource(:systemd_unit, new_resource.service_name) { action(:delete) } if dhcpd_use_systemd? 106 | end 107 | end 108 | 109 | %i(start stop restart reload enable disable).each do |action_type| 110 | send(:action, action_type) { do_service_action(action) } 111 | end 112 | -------------------------------------------------------------------------------- /resources/shared_network.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: dhcp 3 | # Resource:: shared_network 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | unified_mode true 19 | 20 | include Dhcp::Cookbook::Helpers 21 | 22 | property :comment, String, 23 | description: 'Unparsed comment to add to the configuration file' 24 | 25 | property :ip_version, [Symbol, String], 26 | equal_to: [:ipv4, :ipv6, 'ipv4', 'ipv6'], 27 | coerce: proc { |ipv| ipv.is_a?(Symbol) ? ipv : ipv.to_sym }, 28 | default: :ipv4, 29 | description: 'The IP version, 4 or 6' 30 | 31 | property :conf_dir, String, 32 | default: lazy { dhcpd_config_resource_directory(ip_version, declared_type) }, 33 | description: 'Directory to create configuration file in' 34 | 35 | property :cookbook, String, 36 | default: 'dhcp', 37 | description: 'Template source cookbook' 38 | 39 | property :template, String, 40 | default: 'shared_network.conf.erb', 41 | description: 'Template source file' 42 | 43 | property :owner, String, 44 | default: lazy { dhcpd_user }, 45 | description: 'Generated file owner' 46 | 47 | property :group, String, 48 | default: lazy { dhcpd_group }, 49 | description: 'Generated file group' 50 | 51 | property :mode, String, 52 | default: '0640', 53 | description: 'Generated file mode' 54 | 55 | property :subnets, Hash, 56 | description: 'Shared subnets configuration hash' 57 | 58 | action_class do 59 | include Dhcp::Cookbook::ResourceHelpers 60 | end 61 | 62 | action :create do 63 | subnets_include = [] 64 | 65 | new_resource.subnets.each do |subnet, properties| 66 | declare_resource(:dhcp_subnet, "#{new_resource.name}_sharedsubnet_#{subnet}") do 67 | owner new_resource.owner 68 | group new_resource.group 69 | mode new_resource.mode 70 | 71 | ip_version new_resource.ip_version 72 | conf_dir new_resource.conf_dir 73 | shared_network true 74 | 75 | properties.each { |property, value| send(property, value) } 76 | end 77 | 78 | subnets_include.push("#{new_resource.conf_dir}/#{new_resource.name}_sharedsubnet_#{subnet}.conf") 79 | end 80 | 81 | template "#{new_resource.conf_dir}/#{new_resource.name}.conf" do 82 | cookbook new_resource.cookbook 83 | source new_resource.template 84 | 85 | owner new_resource.owner 86 | group new_resource.group 87 | mode new_resource.mode 88 | 89 | variables( 90 | name: new_resource.name, 91 | comment: new_resource.comment, 92 | subnets: subnets_include 93 | ) 94 | helpers(Dhcp::Cookbook::TemplateHelpers) 95 | 96 | action :create 97 | end 98 | 99 | add_to_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf") 100 | end 101 | 102 | action :delete do 103 | file "#{new_resource.conf_dir}/#{new_resource.name}.conf" do 104 | action :delete 105 | end 106 | 107 | remove_from_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf") 108 | end 109 | -------------------------------------------------------------------------------- /resources/subnet.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: dhcp 3 | # Resource:: subnet 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | unified_mode true 19 | 20 | include Dhcp::Cookbook::Helpers 21 | 22 | property :comment, String, 23 | description: 'Unparsed comment to add to the configuration file' 24 | 25 | property :ip_version, [Symbol, String], 26 | equal_to: [:ipv4, :ipv6, 'ipv4', 'ipv6'], 27 | coerce: proc { |ipv| ipv.is_a?(Symbol) ? ipv : ipv.to_sym }, 28 | default: :ipv4, 29 | description: 'The IP version, 4 or 6' 30 | 31 | property :conf_dir, String, 32 | default: lazy { dhcpd_config_resource_directory(ip_version, declared_type) }, 33 | description: 'Directory to create configuration file in' 34 | 35 | property :cookbook, String, 36 | default: 'dhcp', 37 | description: 'Template source cookbook' 38 | 39 | property :template, String, 40 | default: lazy { 41 | case ip_version 42 | when :ipv4 43 | 'subnet.conf.erb' 44 | when :ipv6 45 | 'subnet6.conf.erb' 46 | end 47 | }, 48 | description: 'Template source file' 49 | 50 | property :owner, String, 51 | default: lazy { dhcpd_user }, 52 | description: 'Generated file owner' 53 | 54 | property :group, String, 55 | default: lazy { dhcpd_group }, 56 | description: 'Generated file group' 57 | 58 | property :mode, String, 59 | default: '0640', 60 | description: 'Generated file mode' 61 | 62 | property :shared_network, [true, false], 63 | default: false, 64 | description: 'Flag to indicate subnet is used inside a shared_network resource and should not be added to list.conf' 65 | 66 | property :subnet, String, 67 | name_property: true, 68 | description: 'Subnet network address' 69 | 70 | property :netmask, String, 71 | description: 'Subnet network mask, required for IPv4' 72 | 73 | property :prefix, Integer, 74 | description: 'Subnet network prefix, required for IPv6' 75 | 76 | property :parameters, [Hash, Array], 77 | description: 'Subnet configuration parameters' 78 | 79 | property :options, [Hash, Array], 80 | description: 'Subnet options' 81 | 82 | property :evals, Array, 83 | description: 'Subnet conditional eval statements' 84 | 85 | property :key, Hash, 86 | description: 'Subnet TSIG keys' 87 | 88 | property :zones, Hash, 89 | description: 'Subnet dynamic DNS zones' 90 | 91 | property :allow, Array, 92 | description: 'Subnet allow access control' 93 | 94 | property :deny, Array, 95 | description: 'Subnet deny access control' 96 | 97 | property :extra_lines, Array, 98 | description: 'Subnet additional configuration lines' 99 | 100 | property :pools, [Hash, Array], 101 | description: 'Subnet pool(s) configuration' 102 | 103 | property :range, [String, Array] 104 | 105 | action_class do 106 | include Dhcp::Cookbook::ResourceHelpers 107 | end 108 | 109 | action :create do 110 | case new_resource.ip_version 111 | when :ipv4 112 | raise 'netmask is a required property for IPv4' unless new_resource.netmask 113 | when :ipv6 114 | raise 'prefix is a required property for IPv6' unless new_resource.prefix 115 | end 116 | 117 | template "#{new_resource.conf_dir}/#{new_resource.name}.conf" do 118 | cookbook new_resource.cookbook 119 | source new_resource.template 120 | 121 | owner new_resource.owner 122 | group new_resource.group 123 | mode new_resource.mode 124 | 125 | variables( 126 | name: new_resource.name, 127 | comment: new_resource.comment, 128 | subnet: new_resource.subnet, 129 | netmask: new_resource.netmask, 130 | prefix: new_resource.prefix, 131 | parameters: new_resource.parameters, 132 | options: new_resource.options, 133 | evals: new_resource.evals, 134 | key: new_resource.key, 135 | zones: new_resource.zones, 136 | allow: new_resource.allow, 137 | deny: new_resource.deny, 138 | extra_lines: new_resource.extra_lines, 139 | pools: new_resource.pools, 140 | range: new_resource.range 141 | ) 142 | helpers(Dhcp::Cookbook::TemplateHelpers) 143 | 144 | action :create 145 | end 146 | 147 | add_to_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf") unless new_resource.shared_network 148 | end 149 | 150 | action :delete do 151 | file "#{new_resource.conf_dir}/#{new_resource.name}.conf" do 152 | action :delete 153 | end 154 | 155 | remove_from_list_resource(new_resource.conf_dir, "#{new_resource.conf_dir}/#{new_resource.name}.conf") unless new_resource.shared_network 156 | end 157 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'chefspec' 2 | require 'chefspec/berkshelf' 3 | 4 | RSpec.configure do |config| 5 | config.color = true # Use color in STDOUT 6 | config.formatter = :documentation # Use the specified formatter 7 | config.log_level = :error # Avoid deprecation notice SPAM 8 | end 9 | -------------------------------------------------------------------------------- /spec/unit/recipes/class_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dhcp_class' do 4 | step_into :dhcp_class 5 | platform 'centos' 6 | 7 | context 'create a dhcpd class with no subclass and verify config is created properly' do 8 | recipe do 9 | dhcp_class 'BlankClass' do 10 | match 'hardware' 11 | end 12 | end 13 | 14 | it 'Creates the class configuration file correctly' do 15 | is_expected.to render_file('/etc/dhcp/dhcpd.d/classes.d/BlankClass.conf') 16 | .with_content(/class "BlankClass" {/) 17 | .with_content(/ match hardware;/) 18 | end 19 | end 20 | 21 | context 'create a dhcpd class with subclass and verify config is created properly' do 22 | recipe do 23 | dhcp_class 'RegisteredHosts' do 24 | match 'hardware' 25 | subclass [ 26 | '1:10:bf:48:42:55:01', 27 | '1:10:bf:48:42:55:02', 28 | ] 29 | end 30 | end 31 | 32 | it 'Creates the class configuration file correctly' do 33 | is_expected.to render_file('/etc/dhcp/dhcpd.d/classes.d/RegisteredHosts.conf') 34 | .with_content(/class "RegisteredHosts" {/) 35 | .with_content(/ match hardware;/) 36 | .with_content(/subclass "RegisteredHosts" 1:10:bf:48:42:55:01;/) 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /spec/unit/recipes/config_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dhcp_config' do 4 | step_into :dhcp_config 5 | platform 'centos' 6 | 7 | context 'create a dhcpd config and verify config is created properly' do 8 | recipe do 9 | dhcp_config '/etc/dhcp/dhcpd.conf' do 10 | allow %w(booting bootp unknown-clients) 11 | parameters( 12 | 'default-lease-time' => 7200, 13 | 'ddns-update-style' => 'interim', 14 | 'max-lease-time' => 86400, 15 | 'update-static-leases' => true, 16 | 'one-lease-per-client' => true, 17 | 'authoritative' => '', 18 | 'ping-check' => true 19 | ) 20 | options( 21 | 'domain-name' => '"test.domain.local"', 22 | 'domain-name-servers' => '8.8.8.8', 23 | 'host-name' => ' = binary-to-ascii (16, 8, "-", substring (hardware, 1, 6))' 24 | ) 25 | hooks( 26 | 'commit' => ['use-host-decl-names on'], 27 | 'release' => ['use-host-decl-names on'] 28 | ) 29 | include_files [ 30 | '/etc/dhcp/extra1.conf', 31 | '/etc/dhcp/extra2.conf', 32 | '/etc/dhcp_override/list.conf', 33 | ] 34 | action :create 35 | end 36 | end 37 | 38 | it 'Creates the main configuration file' do 39 | is_expected.to render_file('/etc/dhcp/dhcpd.conf') 40 | .with_content(/authoritative/) 41 | .with_content(/default-lease-time 7200/) 42 | .with_content(/option domain-name-servers 8.8.8.8;/) 43 | .with_content(%r{include "/etc/dhcp/dhcpd.d/classes.d/list.conf";}) 44 | end 45 | end 46 | 47 | context 'create a dhcpd6 config and verify config is created properly' do 48 | recipe do 49 | dhcp_config '/etc/dhcp/dhcpd6.conf' do 50 | ip_version :ipv6 51 | deny %w(duplicates) 52 | parameters( 53 | 'default-lease-time' => 7200, 54 | 'ddns-updates' => 'on', 55 | 'ddns-update-style' => 'interim', 56 | 'max-lease-time' => 86400, 57 | 'update-static-leases' => true, 58 | 'one-lease-per-client' => 'on', 59 | 'authoritative' => '', 60 | 'ping-check' => true 61 | ) 62 | options( 63 | 'dhcp6.name-servers' => '2001:4860:4860::8888, 2001:4860:4860::8844' 64 | ) 65 | action :create 66 | end 67 | end 68 | 69 | it 'Creates the main configuration file' do 70 | is_expected.to render_file('/etc/dhcp/dhcpd6.conf') 71 | .with_content(/authoritative/) 72 | .with_content(/default-lease-time 7200/) 73 | .with_content(/option dhcp6.name-servers 2001:4860:4860::8888, 2001:4860:4860::8844;/) 74 | .with_content(%r{include "/etc/dhcp/dhcpd6.d/classes.d/list.conf";}) 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /spec/unit/recipes/default_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'Default recipe on CentOS 7' do 4 | let(:runner) { ChefSpec::ServerRunner.new(platform: 'centos', version: '7', step_into: ['dhcp_package']) } 5 | 6 | it 'converges successfully' do 7 | expect { :chef_run }.to_not raise_error 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/unit/recipes/group_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dhcp_group' do 4 | step_into :dhcp_group, :dhcp_host 5 | platform 'centos' 6 | 7 | context 'create a dhcpd group and verify config is created properly' do 8 | recipe do 9 | dhcp_group 'ip-phones' do 10 | options( 11 | 'tftp-server-name' => '"192.0.2.10"' 12 | ) 13 | hosts( 14 | 'SEP010101010101' => { 15 | 'identifier' => 'hardware ethernet 01:01:01:01:01:01', 16 | } 17 | ) 18 | end 19 | end 20 | 21 | it 'Creates the group configuration file correctly' do 22 | is_expected.to render_file('/etc/dhcp/dhcpd.d/groups.d/ip-phones.conf') 23 | .with_content(/tftp-server-name "192.0.2.10";/) 24 | .with_content(%r{include "/etc/dhcp/dhcpd.d/groups.d/ip-phones_grouphost_SEP010101010101.conf";}) 25 | end 26 | 27 | it 'Creates the nested host configuration file correctly' do 28 | is_expected.to render_file('/etc/dhcp/dhcpd.d/groups.d/ip-phones_grouphost_SEP010101010101.conf') 29 | .with_content(/host ip-phones_grouphost_SEP010101010101 {/) 30 | .with_content(/hardware ethernet 01:01:01:01:01:01;/) 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/unit/recipes/host_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dhcp_host' do 4 | step_into :dhcp_host 5 | platform 'centos' 6 | 7 | context 'create a dhcpd host and verify config is created properly' do 8 | recipe do 9 | dhcp_host 'Test-IPv4-Host' do 10 | options 'host-name' => 'test-ipv4-host' 11 | identifier 'hardware ethernet 00:53:00:00:00:01' 12 | address '192.168.0.10' 13 | end 14 | end 15 | 16 | it 'Creates the class configuration file correctly' do 17 | is_expected.to render_file('/etc/dhcp/dhcpd.d/hosts.d/Test-IPv4-Host.conf') 18 | .with_content(/host Test-IPv4-Host {/) 19 | .with_content(/hardware ethernet 00:53:00:00:00:01;/) 20 | .with_content(/option host-name test-ipv4-host;/) 21 | end 22 | end 23 | 24 | context 'create a dhcpd6 host and verify config is created properly' do 25 | recipe do 26 | dhcp_host 'Test-IPv6-Host' do 27 | ip_version :ipv6 28 | options 'host-name' => 'test-ipv6-host' 29 | identifier 'host-identifier option dhcp6.client-id 00:53:00:00:00:01:a4:65:b7:c8' 30 | address '2001:db8:1:1:0:0:1:10' 31 | end 32 | end 33 | 34 | it 'Creates the class configuration file correctly' do 35 | is_expected.to render_file('/etc/dhcp/dhcpd6.d/hosts.d/Test-IPv6-Host.conf') 36 | .with_content(/host Test-IPv6-Host {/) 37 | .with_content(/host-identifier option dhcp6.client-id 00:53:00:00:00:01:a4:65:b7:c8;/) 38 | .with_content(/option host-name test-ipv6-host;/) 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /spec/unit/recipes/service_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dhcp_service' do 4 | step_into :dhcp_service 5 | platform 'centos' 6 | 7 | context 'create a dhcpd service' do 8 | recipe do 9 | dhcp_service 'dhcpd' do 10 | action [:create, :enable, :start] 11 | end 12 | end 13 | 14 | describe 'creates a systemd unit file' do 15 | it { is_expected.to create_systemd_unit('dhcpd.service') } 16 | end 17 | 18 | describe 'enables and starts dhcpd' do 19 | it { is_expected.to enable_service('dhcpd') } 20 | it { is_expected.to_not start_service('dhcpd') } 21 | it { is_expected.to nothing_ruby_block('Run pre dhcpd.service start configuration test') } 22 | end 23 | end 24 | 25 | context 'create a dhcpd6 service ' do 26 | recipe do 27 | dhcp_service 'dhcpd6' do 28 | ip_version :ipv6 29 | action [:create, :enable, :start] 30 | end 31 | end 32 | 33 | describe 'creates a systemd unit file' do 34 | it { is_expected.to create_systemd_unit('dhcpd6.service') } 35 | end 36 | 37 | describe 'enables and starts dhcpd6' do 38 | it { is_expected.to enable_service('dhcpd6') } 39 | it { is_expected.to_not start_service('dhcpd6') } 40 | it { is_expected.to nothing_ruby_block('Run pre dhcpd6.service start configuration test') } 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/unit/recipes/shared_network_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dhcp_shared_network' do 4 | step_into :dhcp_shared_network, :dhcp_subnet 5 | platform 'centos' 6 | 7 | context 'create a dhcpd single shared network and verify config is created properly' do 8 | recipe do 9 | dhcp_shared_network 'single' do 10 | subnets( 11 | '192.168.1.0' => { 12 | 'subnet' => '192.168.1.0', 13 | 'netmask' => '255.255.255.0', 14 | 'options' => { 15 | 'broadcast-address' => '192.168.1.255', 16 | 'routers' => '192.168.1.1', 17 | }, 18 | 'pools' => { 19 | 'range' => '192.168.1.20 192.168.1.30', 20 | }, 21 | } 22 | ) 23 | end 24 | end 25 | 26 | it 'Creates the shared network configuration file correctly' do 27 | is_expected.to render_file('/etc/dhcp/dhcpd.d/shared_networks.d/single.conf') 28 | .with_content(%r{include "/etc/dhcp/dhcpd.d/shared_networks.d/single_sharedsubnet_192.168.1.0.conf";}) 29 | end 30 | 31 | it 'Creates the nested subnet configuration file correctly' do 32 | is_expected.to render_file('/etc/dhcp/dhcpd.d/shared_networks.d/single_sharedsubnet_192.168.1.0.conf') 33 | .with_content(/subnet 192.168.1.0 netmask 255.255.255.0 {/) 34 | .with_content(/range 192.168.1.20 192.168.1.30;/) 35 | end 36 | end 37 | 38 | context 'create a dhcpd multiple shared network and verify config is created properly' do 39 | recipe do 40 | dhcp_shared_network 'multiple' do 41 | subnets( 42 | '192.168.2.0' => { 43 | 'subnet' => '192.168.2.0', 44 | 'netmask' => '255.255.255.0', 45 | 'options' => { 46 | 'broadcast-address' => '192.168.2.255', 47 | 'routers' => '192.168.2.1', 48 | }, 49 | 'pools' => { 50 | 'range' => '192.168.2.20 192.168.2.30', 51 | }, 52 | }, 53 | '192.168.3.0' => { 54 | 'subnet' => '192.168.3.0', 55 | 'netmask' => '255.255.255.0', 56 | 'options' => { 57 | 'broadcast-address' => '192.168.3.255', 58 | 'routers' => '192.168.3.1', 59 | }, 60 | 'pools' => { 61 | 'range' => [ 62 | '192.168.3.20 192.168.3.30', 63 | '192.168.3.40 192.168.3.50', 64 | ], 65 | }, 66 | } 67 | ) 68 | end 69 | end 70 | 71 | it 'Creates the shared network configuration file correctly' do 72 | is_expected.to render_file('/etc/dhcp/dhcpd.d/shared_networks.d/multiple.conf') 73 | .with_content(%r{include "/etc/dhcp/dhcpd.d/shared_networks.d/multiple_sharedsubnet_192.168.2.0.conf";}) 74 | .with_content(%r{include "/etc/dhcp/dhcpd.d/shared_networks.d/multiple_sharedsubnet_192.168.3.0.conf";}) 75 | end 76 | 77 | it 'Creates the nested subnet configuration files correctly' do 78 | is_expected.to render_file('/etc/dhcp/dhcpd.d/shared_networks.d/multiple_sharedsubnet_192.168.2.0.conf') 79 | .with_content(/subnet 192.168.2.0 netmask 255.255.255.0 {/) 80 | .with_content(/range 192.168.2.20 192.168.2.30;/) 81 | is_expected.to render_file('/etc/dhcp/dhcpd.d/shared_networks.d/multiple_sharedsubnet_192.168.3.0.conf') 82 | .with_content(/subnet 192.168.3.0 netmask 255.255.255.0 {/) 83 | .with_content(/option broadcast-address 192.168.3.255;/) 84 | .with_content(/range 192.168.3.40 192.168.3.50;/) 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /spec/unit/recipes/subnet_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'dhcp_subnet' do 4 | step_into :dhcp_subnet 5 | platform 'centos' 6 | 7 | context 'create a dhcpd subnet for listening only and verify config is created properly' do 8 | recipe do 9 | dhcp_subnet '192.168.9.0' do 10 | comment 'Listen Subnet Declaration' 11 | subnet '192.168.9.0' 12 | netmask '255.255.255.0' 13 | end 14 | end 15 | 16 | it 'Creates the subnet configuration file correctly' do 17 | is_expected.to render_file('/etc/dhcp/dhcpd.d/subnets.d/192.168.9.0.conf') 18 | .with_content(/# 192.168.9.0 - Listen Subnet Declaration/) 19 | .with_content(/subnet 192.168.9.0 netmask 255.255.255.0 {\n}/) 20 | end 21 | end 22 | 23 | context 'create a dhcpd subnet with options as hash and verify config is created properly' do 24 | recipe do 25 | dhcp_subnet 'overrides' do 26 | comment 'Overrides Subnet Declaration' 27 | subnet '192.168.1.0' 28 | netmask '255.255.255.0' 29 | options( 30 | 'routers' => '192.168.1.1', 31 | 'time-offset' => 10, 32 | 'broadcast-address' => '192.168.0.255' 33 | ) 34 | pools( 35 | 'peer' => '192.168.0.2', 36 | 'range' => '192.168.1.100 192.168.1.200', 37 | 'deny' => 'members of "RegisteredHosts"', 38 | 'allow' => ['members of "UnregisteredHosts"', 'members of "OtherHosts"'] 39 | ) 40 | parameters( 41 | 'ddns-domainname' => '"test.com"', 42 | 'next-server' => '192.168.0.3' 43 | ) 44 | evals [ 'if exists user-class and option user-class = "iPXE" { 45 | filename "bootstrap.ipxe"; 46 | } else { 47 | filename "undionly.kpxe"; 48 | }' ] 49 | key 'name' => 'test_key', 'algorithm' => 'hmac-sha256', 'secret' => 'c7nBOcB2rbJh7lYCI65/PGrS6QdlLMCPe2xunZ4dij8=' 50 | zones 'test' => { 'primary' => 'test_pri', 'key' => 'test_key' } 51 | conf_dir '/etc/dhcp_override' 52 | end 53 | end 54 | 55 | it 'Creates the subnet configuration file correctly' do 56 | is_expected.to render_file('/etc/dhcp_override/overrides.conf') 57 | .with_content(/subnet 192.168.1.0 netmask 255.255.255.0 {/) 58 | .with_content(/option routers 192.168.1.1;/) 59 | .with_content(/if exists user-class and option user-class = "iPXE" {/) 60 | .with_content(/key test_key {/) 61 | .with_content(/zone test {/) 62 | .with_content(/deny members of "RegisteredHosts";/) 63 | end 64 | end 65 | 66 | context 'create a dhcpd subnet with parameters as array and verify config is created properly' do 67 | recipe do 68 | dhcp_subnet 'overrides' do 69 | comment 'Overrides Subnet Declaration' 70 | subnet '192.168.1.0' 71 | netmask '255.255.255.0' 72 | options( 73 | 'routers' => '192.168.1.1', 74 | 'time-offset' => 10, 75 | 'broadcast-address' => '192.168.0.255' 76 | ) 77 | pools( 78 | 'peer' => '192.168.0.2', 79 | 'range' => '192.168.1.100 192.168.1.200', 80 | 'deny' => 'members of "RegisteredHosts"', 81 | 'allow' => ['members of "UnregisteredHosts"', 'members of "OtherHosts"'] 82 | ) 83 | parameters [ 84 | 'ddns-domainname "test.com"', 85 | 'next-server 192.168.0.3', 86 | ] 87 | evals [ 'if exists user-class and option user-class = "iPXE" { 88 | filename "bootstrap.ipxe"; 89 | } else { 90 | filename "undionly.kpxe"; 91 | }' ] 92 | key 'name' => 'test_key', 'algorithm' => 'hmac-sha256', 'secret' => 'c7nBOcB2rbJh7lYCI65/PGrS6QdlLMCPe2xunZ4dij8=' 93 | zones 'test' => { 'primary' => 'test_pri', 'key' => 'test_key' } 94 | conf_dir '/etc/dhcp_override' 95 | end 96 | end 97 | 98 | it 'Creates the subnet configuration file correctly' do 99 | is_expected.to render_file('/etc/dhcp_override/overrides.conf') 100 | .with_content(/subnet 192.168.1.0 netmask 255.255.255.0 {/) 101 | .with_content(/option routers 192.168.1.1;/) 102 | .with_content(/if exists user-class and option user-class = "iPXE" {/) 103 | .with_content(/key test_key {/) 104 | .with_content(/zone test {/) 105 | .with_content(/deny members of "RegisteredHosts";/) 106 | end 107 | end 108 | end 109 | -------------------------------------------------------------------------------- /templates/default/class.conf.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Generated by Chef for <%= node['fqdn'] %> 3 | # Do NOT modify this file by hand, changes will be overwritten. 4 | # 5 | 6 | # <%= @name -%><% if @comment -%> - <%= @comment -%><% end -%> 7 | class "<%= @name %>" { 8 | match <%= @match %>; 9 | <% unless nil_or_empty?(@parameters) -%> 10 | 11 | # Parameters 12 | <% property_collection_sorted(@parameters).each do |parameter, value| -%> 13 | <%= parameter %><% unless nil_or_empty?(value) -%> <%= value %><% end -%>; 14 | <% end -%> 15 | <% end -%> 16 | <% unless nil_or_empty?(@options) -%> 17 | 18 | # Options 19 | <% property_collection_sorted(@options).each do |option, value| -%> 20 | option <%= option %> <%= value %>; 21 | <% end -%> 22 | <% end -%> 23 | } 24 | <% unless nil_or_empty?(@subclasses) -%> 25 | 26 | <% @subclasses.each do |value| -%> 27 | subclass "<%= @name %>" <%= value %>; 28 | <% end -%> 29 | <% end -%> 30 | -------------------------------------------------------------------------------- /templates/default/dhcpd-env.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Generated by Chef for <%= node['fqdn'] %> 3 | # Do NOT modify this file by hand, changes will be overwritten. 4 | # 5 | 6 | <% if nil_or_empty?(@lines) -%> 7 | # WARNING: This file is NOT used anymore. 8 | 9 | # If you are here to restrict what interfaces should dhcpd listen on, 10 | # be aware that dhcpd listens *only* on interfaces for which it finds subnet 11 | # declaration in dhcpd.conf. It means that explicitly enumerating interfaces 12 | # also on command line should not be required in most cases. 13 | <% else -%> 14 | # User specified extra lines 15 | <% property_array(@lines).each do |line| -%> 16 | <%= line %> 17 | <% end -%> 18 | <% end -%> 19 | -------------------------------------------------------------------------------- /templates/default/dhcpd.conf.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Generated by Chef for <%= node['fqdn'] %> 3 | # Do NOT modify this file by hand, changes will be overwritten. 4 | # 5 | 6 | # The following line populates the lease file with the Vendor Class Identifier that the client sends. 7 | set vendor-string = option vendor-class-identifier; 8 | <% unless nil_or_empty?(@failover) -%> 9 | 10 | # DHCP Failover 11 | include "/etc/dhcp/dhcpd.failover.conf"; 12 | <% end -%> 13 | <% unless nil_or_empty?(@parameters) -%> 14 | 15 | # Global Parameters 16 | <% property_collection_sorted(@parameters).each do |parameter, value| -%> 17 | <%= parameter %><% unless nil_or_empty?(value) -%> <%= value %><% end -%>; 18 | <% end -%> 19 | <% end -%> 20 | <% unless nil_or_empty?(@options) -%> 21 | 22 | # Global Options 23 | <% property_collection(@options).each do |option, value| -%> 24 | option <%= option %> <%= value %>; 25 | <% end -%> 26 | <% end -%> 27 | <% unless nil_or_empty?(@evals) -%> 28 | 29 | # Evals 30 | <% @evals.each do |eval| -%> 31 | <%= eval %> 32 | <% end -%> 33 | <% end -%> 34 | <% unless nil_or_empty?(@allow) && nil_or_empty?(@deny) && nil_or_empty?(@ignore) -%> 35 | 36 | # Request Control 37 | <% unless nil_or_empty?(@allow) -%> 38 | 39 | # Allow 40 | <% @allow.each do |allow| -%> 41 | allow <%= allow %>; 42 | <% end -%> 43 | <% end -%> 44 | <% unless nil_or_empty?(@deny) -%> 45 | 46 | # Deny 47 | <% @deny.each do |deny| -%> 48 | deny <%= deny %>; 49 | <% end -%> 50 | <% end -%> 51 | <% unless nil_or_empty?(@ignore) -%> 52 | 53 | # Ignore 54 | <% @ignore.each do |ignore| -%> 55 | ignore <%= ignore %>; 56 | <% end -%> 57 | <% end -%> 58 | <% end -%> 59 | <% unless nil_or_empty?(@hooks) -%> 60 | 61 | # Events 62 | <% @hooks.each do |key, value| -%> 63 | on <%= key %> { 64 | <% value.each do |v| -%> 65 | <%= v %>; 66 | <% end -%> 67 | } 68 | <% end -%> 69 | <% end -%> 70 | <% unless nil_or_empty?(@keys) -%> 71 | 72 | # TSIG Keys 73 | <% @keys.each do |key, data| -%> 74 | key "<%= key %>" { 75 | algorithm <%= data['algorithm'] %>; 76 | secret "<%= data['secret'] %>"; 77 | }; 78 | <%end -%> 79 | <%end -%> 80 | <% unless nil_or_empty?(@zones) -%> 81 | 82 | # Dynamic DNS Zones 83 | <% @zones.each do |zone, data| -%> 84 | zone <%= zone %>. { 85 | primary <%= data["primary"] %>;<% if data.key?('key') -%> 86 | key "<%= data["key"] =%>";<% end -%> 87 | } 88 | <% end -%> 89 | <% end -%> 90 | 91 | # Includes 92 | include "<%= @includes_dir %>/classes.d/list.conf"; 93 | include "<%= @includes_dir %>/groups.d/list.conf"; 94 | include "<%= @includes_dir %>/subnets.d/list.conf"; 95 | include "<%= @includes_dir %>/shared_networks.d/list.conf"; 96 | include "<%= @includes_dir %>/hosts.d/list.conf"; 97 | <% unless nil_or_empty?(@include_files) -%> 98 | 99 | # Custom Includes 100 | <% @include_files.each do |file| -%> 101 | include "<%= file %>"; 102 | <% end -%> 103 | <% end -%> 104 | <% unless nil_or_empty?(@extra_lines) -%> 105 | 106 | # User specified extra lines 107 | <% property_array(@extra_lines).each do |line| -%> 108 | <%= line %> 109 | <% end -%> 110 | <% end -%> 111 | -------------------------------------------------------------------------------- /templates/default/dhcpd.failover.conf.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Generated by Chef for <%= node['fqdn'] %> 3 | # Do NOT modify this file by hand, changes will be overwritten. 4 | # 5 | 6 | failover peer "<%= @failover['peer'] %>" { 7 | <%= @failover['role'] -%>; 8 | <% property_collection_sorted(@failover['parameters']).each do |parameter, value| -%> 9 | <%= parameter %><% unless nil_or_empty?(value) -%> <%= value %><% end -%>; 10 | <% end -%> 11 | } 12 | -------------------------------------------------------------------------------- /templates/default/group.conf.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Generated by Chef for <%= node['fqdn'] %> 3 | # Do NOT modify this file by hand, changes will be overwritten. 4 | # 5 | 6 | # <%= @name -%><% if @comment -%> - <%= @comment -%><% end -%> 7 | group { 8 | <% unless nil_or_empty?(@parameters) -%> 9 | 10 | # Parameters 11 | <% property_collection_sorted(@parameters).each do |parameter, value| -%> 12 | <%= parameter %><% unless nil_or_empty?(value) -%> <%= value %><% end -%>; 13 | <% end -%> 14 | <% end -%> 15 | <% unless nil_or_empty?(@options) -%> 16 | 17 | # Options 18 | <% property_collection_sorted(@options).each do |option, value| -%> 19 | option <%= option %> <%= value %>; 20 | <% end -%> 21 | <% end -%> 22 | <% unless nil_or_empty?(@evals) -%> 23 | 24 | # Evals 25 | <% @evals.each do |eval| -%> 26 | <%= eval %> 27 | <% end -%> 28 | <% end -%> 29 | <% unless nil_or_empty?(@hosts) -%> 30 | 31 | # Hosts 32 | <% @hosts.sort.each do |host| -%> 33 | include "<%= host %>"; 34 | <% end -%> 35 | <% end -%> 36 | } 37 | -------------------------------------------------------------------------------- /templates/default/host.conf.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Generated by Chef for <%= node['fqdn'] %> 3 | # Do NOT modify this file by hand, changes will be overwritten. 4 | # 5 | 6 | # <%= @name -%><% if @comment -%> - <%= @comment -%><% end -%> 7 | host <%= @name %> { 8 | <% if @identifier -%> 9 | <%= @identifier %>; 10 | <% end -%> 11 | <% if @address -%> 12 | <%= @ip_version.eql?(:ipv6) ? 'fixed-address6' : 'fixed-address' %> <%= @address %>; 13 | <% end -%> 14 | <% unless nil_or_empty?(@parameters) -%> 15 | 16 | # Parameters 17 | <% property_collection_sorted(@parameters).each do |parameter, value| -%> 18 | <%= parameter %><% unless nil_or_empty?(value) -%> <%= value %><% end -%>; 19 | <% end -%> 20 | <% end -%> 21 | <% unless nil_or_empty?(@options) -%> 22 | 23 | # Options 24 | <% property_collection_sorted(@options).each do |option, value| -%> 25 | option <%= option -%> <%= value -%>; 26 | <% end -%> 27 | <% end -%> 28 | } 29 | -------------------------------------------------------------------------------- /templates/default/list.conf.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Generated by Chef for <%= node['fqdn'] %> 3 | # Do NOT modify this file by hand, changes will be overwritten. 4 | # 5 | 6 | <% if @files.empty? -%> 7 | # No files to include 8 | <% else -%> 9 | # Include files 10 | <% @files.sort.each do |file| -%> 11 | include "<%= file %>"; 12 | <% end -%> 13 | <% end -%> 14 | -------------------------------------------------------------------------------- /templates/default/shared_network.conf.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Generated by Chef for <%= node['fqdn'] %> 3 | # Do NOT modify this file by hand, changes will be overwritten. 4 | # 5 | 6 | # <%= @name -%><% if @comment -%> - <%= @comment -%><% end -%> 7 | shared-network <%= @name %> { 8 | <% @subnets.sort.each do |subnet| -%> 9 | include "<%= subnet %>"; 10 | <% end -%> 11 | } 12 | -------------------------------------------------------------------------------- /templates/default/subnet.conf.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Generated by Chef for <%= node['fqdn'] %> 3 | # Do NOT modify this file by hand, changes will be overwritten. 4 | # 5 | 6 | # <%= @name -%><% if @comment -%> - <%= @comment -%><% end -%> 7 | subnet <%= @subnet %> netmask <%= @netmask%> { 8 | <% unless nil_or_empty?(@parameters) -%> 9 | # Parameters 10 | <% property_collection_sorted(@parameters).each do |parameter, value| -%> 11 | <%= parameter %><% unless nil_or_empty?(value) -%> <%= value %><% end -%>; 12 | <% end -%> 13 | <% end -%> 14 | <% unless nil_or_empty?(@options) -%> 15 | 16 | # Options 17 | <% property_collection_sorted(@options).each do |option, value| -%> 18 | option <%= option %> <%= value %>; 19 | <% end -%> 20 | <% end -%> 21 | <% unless nil_or_empty?(@extra_lines) -%> 22 | 23 | # Extra Lines 24 | <% @extra_lines.each do |line| -%> 25 | <%= line %> 26 | <% end -%> 27 | <% end -%> 28 | <% unless nil_or_empty?(@evals) -%> 29 | 30 | # Evals 31 | <% @evals.each do |eval| -%> 32 | <%= eval %> 33 | <% end -%> 34 | <% end -%> 35 | <% unless nil_or_empty?(@key) -%> 36 | 37 | # Key 38 | key <%= @key['name'] %> { 39 | algorithm <%= @key['algorithm'] %>; 40 | secret "<%= @key['secret'] %>"; 41 | }; 42 | <% end -%> 43 | <% unless nil_or_empty?(@zones) -%> 44 | 45 | # Zones 46 | <% @zones.each do |zone, options| -%> 47 | zone <%= zone %> { 48 | primary <%= options['primary'] %>; 49 | key <%= options['key'] %>; 50 | } 51 | <% end -%> 52 | <% end -%> 53 | <% unless nil_or_empty?(@pools) -%> 54 | 55 | # Pools 56 | <% property_array(@pools).each do |pool| -%> 57 | pool { 58 | <% if pool['failover_peer'] -%> 59 | failover peer "<%= pool['failover_peer'] %>"; 60 | <% end -%> 61 | <% unless nil_or_empty?(pool['options']) -%> 62 | 63 | # Options 64 | <% property_collection_sorted(pool['options']).each do |option, value| -%> 65 | option <%= option %> <%= value %>; 66 | <% end -%> 67 | <% end -%> 68 | <% unless nil_or_empty?(pool['parameters']) -%> 69 | 70 | # Parameters 71 | <% property_collection_sorted(pool['parameters']).each do |parameter, value| -%> 72 | <%= parameter %> <%= value %>; 73 | <% end -%> 74 | <% end -%> 75 | <% unless nil_or_empty?(pool['range']) %> 76 | 77 | # Ranges 78 | <% property_array(pool['range']).sort.each do |range| -%> 79 | range <%= range %>; 80 | <% end -%> 81 | <% end -%> 82 | <% unless nil_or_empty?(pool['deny']) -%> 83 | 84 | # Deny Access Control 85 | <% property_array(pool['deny']).sort.each do |denied| -%> 86 | deny <%= denied %>; 87 | <% end -%> 88 | <% end -%> 89 | <% unless nil_or_empty?(pool['allow']) -%> 90 | 91 | # Allow Access Control 92 | <% property_array(pool['allow']).sort.each do |allowed| -%> 93 | allow <%= allowed %>; 94 | <% end -%> 95 | <% end -%> 96 | } 97 | <% end -%> 98 | <% end -%> 99 | } 100 | -------------------------------------------------------------------------------- /templates/default/subnet6.conf.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Generated by Chef for <%= node['fqdn'] %> 3 | # Do NOT modify this file by hand, changes will be overwritten. 4 | # 5 | 6 | # <%= @name -%><% if @comment -%> - <%= @comment -%><% end -%> 7 | subnet6 <%= @subnet %>/<%= @prefix %> { 8 | <% unless nil_or_empty?(@range) -%> 9 | # Ranges 10 | <% property_array(@range).each do |range| -%> 11 | range6 <%= range %>; 12 | <% end -%> 13 | <% end -%> 14 | <% unless nil_or_empty?(@parameters) -%> 15 | 16 | # Parameters 17 | <% property_collection_sorted(@parameters).each do |parameter, value| -%> 18 | <%= parameter %><% unless nil_or_empty?(value) -%> <%= value %><% end -%>; 19 | <% end -%> 20 | <% end -%> 21 | <% unless nil_or_empty?(@options) -%> 22 | 23 | # Options 24 | <% property_collection_sorted(@options).each do |option, value| -%> 25 | option <%= option %> <%= value %>; 26 | <% end -%> 27 | <% end -%> 28 | <% unless nil_or_empty?(@allow) -%> 29 | 30 | # Allow Access Control 31 | <% property_array(@allow).each do |allowed| -%> 32 | allow <%= allowed %>; 33 | <% end -%> 34 | <% end -%> 35 | <% unless nil_or_empty?(@deny) -%> 36 | 37 | # Deny Access Control 38 | <% property_array(@deny).each do |denied| -%> 39 | deny <%= denied %>; 40 | <% end -%> 41 | <% end -%> 42 | <% unless nil_or_empty?(@extra_lines) -%> 43 | 44 | # Extra Lines 45 | <% @extra_lines.each do |line| -%> 46 | <%= line %> 47 | <% end -%> 48 | <% end -%> 49 | <% unless nil_or_empty?(@evals) -%> 50 | 51 | # Evals 52 | <% @evals.each do |eval| -%> 53 | <%= eval %> 54 | <% end -%> 55 | <% end -%> 56 | 57 | <% unless nil_or_empty?(@pool) -%> 58 | <% property_array(@pool).each do |pool| -%> 59 | pool6 { 60 | <% unless nil_or_empty?(pool['range']) -%> 61 | # Ranges 62 | <% pool['range'].sort.each do |range| -%> 63 | range6 <%= range %>; 64 | <% end -%> 65 | <% end -%> 66 | <% unless nil_or_empty?(@allow) -%> 67 | 68 | # Allow Access Control 69 | <% pool['allow'].sort.each do |allowed| -%> 70 | allow <%= allowed %>; 71 | <% end -%> 72 | <% end -%> 73 | <% unless nil_or_empty?(@deny) -%> 74 | 75 | # Deny Access Control 76 | <% pool['deny'].sort.each do |denied| -%> 77 | deny <%= denied %>; 78 | <% end -%> 79 | <% end -%> 80 | } 81 | <% end -%> 82 | <% end -%> 83 | } 84 | -------------------------------------------------------------------------------- /test/cookbooks/test/README.md: -------------------------------------------------------------------------------- 1 | # dhcp_test 2 | 3 | This cookbook installs ISC DHCPd and tests whether the dhcp cookbook can configure a server. 4 | -------------------------------------------------------------------------------- /test/cookbooks/test/files/ubuntu-18.04/usr.sbin.dhcpd: -------------------------------------------------------------------------------- 1 | # vim:syntax=apparmor 2 | # Last Modified: Mon Jan 25 11:06:45 2016 3 | # Author: Jamie Strandboge 4 | 5 | #include 6 | 7 | /usr/sbin/dhcpd { 8 | #include 9 | #include 10 | #include 11 | 12 | capability chown, 13 | capability net_bind_service, 14 | capability net_raw, 15 | capability setgid, 16 | capability setuid, 17 | capability dac_override, 18 | 19 | network inet raw, 20 | network packet packet, 21 | network packet raw, 22 | 23 | @{PROC}/[0-9]*/net/dev r, 24 | @{PROC}/[0-9]*/net/{dev,if_inet6} r, 25 | 26 | /etc/hosts.allow r, 27 | /etc/hosts.deny r, 28 | 29 | /etc/dhcp/ r, 30 | /etc/dhcp/** r, 31 | /etc/dhcpd{,6}.conf r, 32 | /etc/dhcpd{,6}_ldap.conf r, 33 | 34 | /usr/sbin/dhcpd mr, 35 | 36 | /var/lib/dhcp/dhcpd{,6}.leases* lrw, 37 | /var/log/ r, 38 | /var/log/** rw, 39 | /{,var/}run/{,dhcp-server/}dhcpd{,6}.pid rw, 40 | 41 | # isc-dhcp-server-ldap 42 | /etc/ldap/ldap.conf r, 43 | 44 | # LTSP. See: 45 | # http://www.ltsp.org/~sbalneav/LTSPManual.html 46 | # https://wiki.edubuntu.org/ 47 | /etc/ltsp/ r, 48 | /etc/ltsp/** r, 49 | /etc/dhcpd{,6}-k12ltsp.conf r, 50 | /etc/dhcpd{,6}.leases* lrw, 51 | /ltsp/ r, 52 | /ltsp/** r, 53 | 54 | # Eucalyptus 55 | /{,var/}run/eucalyptus/net/ r, 56 | /{,var/}run/eucalyptus/net/** r, 57 | /{,var/}run/eucalyptus/net/*.pid lrw, 58 | /{,var/}run/eucalyptus/net/*.leases* lrw, 59 | /{,var/}run/eucalyptus/net/*.trace lrw, 60 | 61 | # wicd 62 | /var/lib/wicd/* r, 63 | 64 | # access to bind9 keys for dynamic update 65 | # It's expected that users will generate one key per zone and have it 66 | # stored in both /etc/bind9 (for bind to access) and /etc/dhcp/ddns-keys 67 | # (for dhcpd to access). 68 | /etc/dhcp/ddns-keys/** r, 69 | 70 | # allow packages to re-use dhcpd and provide their own specific directories 71 | #include 72 | 73 | # Site-specific additions and overrides. See local/README for details. 74 | #include 75 | } 76 | -------------------------------------------------------------------------------- /test/cookbooks/test/files/ubuntu-20.04/usr.sbin.dhcpd: -------------------------------------------------------------------------------- 1 | # vim:syntax=apparmor 2 | # Last Modified: Mon Jan 25 11:06:45 2016 3 | # Author: Jamie Strandboge 4 | 5 | #include 6 | 7 | /usr/sbin/dhcpd { 8 | #include 9 | #include 10 | #include 11 | 12 | capability chown, 13 | capability net_bind_service, 14 | capability net_raw, 15 | capability setgid, 16 | capability setuid, 17 | capability dac_override, 18 | 19 | network inet raw, 20 | network packet packet, 21 | network packet raw, 22 | 23 | @{PROC}/[0-9]*/net/dev r, 24 | @{PROC}/[0-9]*/net/{dev,if_inet6} r, 25 | owner @{PROC}/@{pid}/comm rw, 26 | owner @{PROC}/@{pid}/task/[0-9]*/comm rw, 27 | 28 | # LP: #1926139 29 | @{PROC}/cmdline r, 30 | 31 | /etc/hosts.allow r, 32 | /etc/hosts.deny r, 33 | 34 | /etc/dhcp/ r, 35 | /etc/dhcp/** r, 36 | /etc/dhcpd{,6}.conf r, 37 | /etc/dhcpd{,6}_ldap.conf r, 38 | 39 | /usr/sbin/dhcpd mr, 40 | 41 | /var/lib/dhcp/dhcpd{,6}.leases* lrw, 42 | /var/log/ r, 43 | /var/log/** rw, 44 | /{,var/}run/{,dhcp-server/}dhcpd{,6}.pid rw, 45 | 46 | # isc-dhcp-server-ldap 47 | /etc/ldap/ldap.conf r, 48 | 49 | # LTSP. See: 50 | # http://www.ltsp.org/~sbalneav/LTSPManual.html 51 | # https://wiki.edubuntu.org/ 52 | /etc/ltsp/ r, 53 | /etc/ltsp/** r, 54 | /etc/dhcpd{,6}-k12ltsp.conf r, 55 | /etc/dhcpd{,6}.leases* lrw, 56 | /ltsp/ r, 57 | /ltsp/** r, 58 | 59 | # Eucalyptus 60 | /{,var/}run/eucalyptus/net/ r, 61 | /{,var/}run/eucalyptus/net/** r, 62 | /{,var/}run/eucalyptus/net/*.pid lrw, 63 | /{,var/}run/eucalyptus/net/*.leases* lrw, 64 | /{,var/}run/eucalyptus/net/*.trace lrw, 65 | 66 | # wicd 67 | /var/lib/wicd/* r, 68 | 69 | # access to bind9 keys for dynamic update 70 | # It's expected that users will generate one key per zone and have it 71 | # stored in both /etc/bind9 (for bind to access) and /etc/dhcp/ddns-keys 72 | # (for dhcpd to access). 73 | /etc/dhcp/ddns-keys/** r, 74 | 75 | # allow packages to re-use dhcpd and provide their own specific directories 76 | #include 77 | 78 | # Site-specific additions and overrides. See local/README for details. 79 | #include 80 | } 81 | -------------------------------------------------------------------------------- /test/cookbooks/test/files/ubuntu-22.04/usr.sbin.dhcpd: -------------------------------------------------------------------------------- 1 | # vim:syntax=apparmor 2 | # Last Modified: Mon Jan 25 11:06:45 2016 3 | # Author: Jamie Strandboge 4 | 5 | #include 6 | 7 | /usr/sbin/dhcpd { 8 | #include 9 | #include 10 | #include 11 | 12 | capability chown, 13 | capability net_bind_service, 14 | capability net_raw, 15 | capability setgid, 16 | capability setuid, 17 | capability dac_override, 18 | 19 | network inet raw, 20 | network packet packet, 21 | network packet raw, 22 | 23 | @{PROC}/[0-9]*/net/dev r, 24 | @{PROC}/[0-9]*/net/{dev,if_inet6} r, 25 | owner @{PROC}/@{pid}/comm rw, 26 | owner @{PROC}/@{pid}/task/[0-9]*/comm rw, 27 | 28 | # LP: #1926139 29 | @{PROC}/cmdline r, 30 | 31 | /etc/hosts.allow r, 32 | /etc/hosts.deny r, 33 | 34 | /etc/dhcp/ r, 35 | /etc/dhcp/** r, 36 | /etc/dhcpd{,6}.conf r, 37 | /etc/dhcpd{,6}_ldap.conf r, 38 | 39 | /usr/sbin/dhcpd mr, 40 | 41 | /var/lib/dhcp/dhcpd{,6}.leases* lrw, 42 | /var/log/ r, 43 | /var/log/** rw, 44 | /{,var/}run/{,dhcp-server/}dhcpd{,6}.pid rw, 45 | 46 | # isc-dhcp-server-ldap 47 | /etc/ldap/ldap.conf r, 48 | 49 | # LTSP. See: 50 | # http://www.ltsp.org/~sbalneav/LTSPManual.html 51 | # https://wiki.edubuntu.org/ 52 | /etc/ltsp/ r, 53 | /etc/ltsp/** r, 54 | /etc/dhcpd{,6}-k12ltsp.conf r, 55 | /etc/dhcpd{,6}.leases* lrw, 56 | /ltsp/ r, 57 | /ltsp/** r, 58 | 59 | # Eucalyptus 60 | /{,var/}run/eucalyptus/net/ r, 61 | /{,var/}run/eucalyptus/net/** r, 62 | /{,var/}run/eucalyptus/net/*.pid lrw, 63 | /{,var/}run/eucalyptus/net/*.leases* lrw, 64 | /{,var/}run/eucalyptus/net/*.trace lrw, 65 | 66 | # wicd 67 | /var/lib/wicd/* r, 68 | 69 | # access to bind9 keys for dynamic update 70 | # It's expected that users will generate one key per zone and have it 71 | # stored in both /etc/bind9 (for bind to access) and /etc/dhcp/ddns-keys 72 | # (for dhcpd to access). 73 | /etc/dhcp/ddns-keys/** r, 74 | 75 | # allow packages to re-use dhcpd and provide their own specific directories 76 | #include 77 | 78 | # Site-specific additions and overrides. See local/README for details. 79 | #include 80 | } 81 | -------------------------------------------------------------------------------- /test/cookbooks/test/metadata.rb: -------------------------------------------------------------------------------- 1 | name 'test' 2 | version '0.0.1' 3 | 4 | depends 'dhcp' 5 | depends 'apparmor' 6 | -------------------------------------------------------------------------------- /test/cookbooks/test/recipes/config_delete.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: dhcp_test 3 | # Recipe:: config_delete 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | dhcp_config '/etc/dhcp/dhcpd6.conf' do 19 | ip_version :ipv6 20 | action :delete 21 | notifies :stop, 'dhcp_service[dhcpd6]', :delayed 22 | notifies :disable, 'dhcp_service[dhcpd6]', :delayed 23 | end 24 | 25 | dhcp_host 'Test-IPv6-Host' do 26 | ip_version :ipv6 27 | action :delete 28 | end 29 | 30 | dhcp_subnet 'overrides' do 31 | conf_dir '/etc/dhcp/override' 32 | action :delete 33 | notifies :restart, 'dhcp_service[dhcpd]', :delayed 34 | end 35 | 36 | dhcp_subnet 'overrides' do 37 | action :delete 38 | notifies :restart, 'dhcp_service[dhcpd]', :delayed 39 | end 40 | 41 | dhcp_subnet 'deny host from class' do 42 | action :delete 43 | notifies :restart, 'dhcp_service[dhcpd]', :delayed 44 | end 45 | 46 | dhcp_class 'UnregisteredHosts' do 47 | action :delete 48 | notifies :restart, 'dhcp_service[dhcpd]', :delayed 49 | end 50 | -------------------------------------------------------------------------------- /test/cookbooks/test/recipes/default.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: dhcp_test 3 | # Recipe:: default 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | include_recipe '::net_setup' 19 | 20 | include_recipe '::package' 21 | 22 | # Work around issue with apparmor on Ubuntu 23 | apparmor_policy 'usr.sbin.dhcpd' if platform?('ubuntu') 24 | 25 | include_recipe '::include_files' 26 | 27 | include_recipe '::dhcp_config' 28 | include_recipe '::dhcp_subnet' 29 | include_recipe '::dhcp_host' 30 | include_recipe '::dhcp_class' 31 | include_recipe '::dhcp_shared_network' 32 | include_recipe '::dhcp_group' 33 | 34 | include_recipe '::service' 35 | -------------------------------------------------------------------------------- /test/cookbooks/test/recipes/dhcp_class.rb: -------------------------------------------------------------------------------- 1 | dhcp_class 'BlankClass' do 2 | match 'hardware' 3 | end 4 | 5 | dhcp_class 'RegisteredHosts' do 6 | match 'hardware' 7 | subclass [ 8 | '1:10:bf:48:42:55:01', 9 | '1:10:bf:48:42:55:02', 10 | ] 11 | end 12 | 13 | dhcp_class 'UnregisteredHosts' do 14 | match 'hardware' 15 | subclass [ 16 | '1:10:bf:48:42:55:03', 17 | '1:10:bf:48:42:55:04', 18 | ] 19 | options( 20 | 'domain-name-servers' => '8.8.8.8' 21 | ) 22 | end 23 | 24 | dhcp_class 'OtherHosts' do 25 | match 'hardware' 26 | subclass [ 27 | '1:10:bf:48:42:55:05', 28 | '1:10:bf:48:42:55:06', 29 | ] 30 | end 31 | 32 | dhcp_subnet 'deny host from class' do 33 | subnet '192.168.4.0' 34 | netmask '255.255.255.0' 35 | options( 36 | 'broadcast-address' => '192.168.4.255', 37 | 'routers' => '192.168.4.1' 38 | ) 39 | pools( 40 | 'range' => '192.168.4.20 192.168.4.30', 41 | 'deny' => 'members of "RegisteredHosts"', 42 | 'allow' => 'members of "UnregisteredHosts"' 43 | ) 44 | end 45 | -------------------------------------------------------------------------------- /test/cookbooks/test/recipes/dhcp_config.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: dhcp_test 3 | # Recipe:: config 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | dhcp_config '/etc/dhcp/dhcpd.conf' do 19 | allow %w(booting bootp unknown-clients) 20 | parameters( 21 | 'default-lease-time' => 7200, 22 | 'ddns-update-style' => 'interim', 23 | 'max-lease-time' => 86400, 24 | 'update-static-leases' => true, 25 | 'one-lease-per-client' => true, 26 | 'authoritative' => '', 27 | 'ping-check' => true 28 | ) 29 | options( 30 | 'domain-name' => '"test.domain.local"', 31 | 'domain-name-servers' => '8.8.8.8', 32 | 'host-name' => ' = binary-to-ascii (16, 8, "-", substring (hardware, 1, 6))' 33 | ) 34 | hooks( 35 | 'commit' => ['use-host-decl-names on'], 36 | 'release' => ['use-host-decl-names on'] 37 | ) 38 | include_files [ 39 | '/etc/dhcp/extra1.conf', 40 | '/etc/dhcp/extra2.conf', 41 | '/etc/dhcp/override/list.conf', 42 | ] 43 | action :create 44 | end 45 | 46 | dhcp_config '/etc/dhcp/dhcpd6.conf' do 47 | ip_version :ipv6 48 | deny %w(duplicates) 49 | parameters( 50 | 'default-lease-time' => 7200, 51 | 'ddns-updates' => 'on', 52 | 'ddns-update-style' => 'interim', 53 | 'max-lease-time' => 86400, 54 | 'update-static-leases' => true, 55 | 'one-lease-per-client' => 'on', 56 | 'authoritative' => '', 57 | 'ping-check' => true 58 | ) 59 | options( 60 | 'dhcp6.name-servers' => '2001:4860:4860::8888, 2001:4860:4860::8844' 61 | ) 62 | action :create 63 | end 64 | -------------------------------------------------------------------------------- /test/cookbooks/test/recipes/dhcp_group.rb: -------------------------------------------------------------------------------- 1 | dhcp_group 'ip-phones' do 2 | options( 3 | 'tftp-server-name' => '"192.0.2.10"' 4 | ) 5 | hosts( 6 | 'SEP010101010101' => { 7 | 'identifier' => 'hardware ethernet 01:01:01:01:01:01', 8 | } 9 | ) 10 | end 11 | -------------------------------------------------------------------------------- /test/cookbooks/test/recipes/dhcp_host.rb: -------------------------------------------------------------------------------- 1 | dhcp_host 'Test-IPv4-Host' do 2 | options 'host-name' => 'test-ipv4-host' 3 | identifier 'hardware ethernet 00:53:00:00:00:01' 4 | address '192.168.0.10' 5 | end 6 | 7 | dhcp_host 'Test-IPv6-Host' do 8 | ip_version :ipv6 9 | options 'host-name' => 'test-ipv6-host' 10 | identifier 'host-identifier option dhcp6.client-id 00:53:00:00:00:01:a4:65:b7:c8' 11 | address '2001:db8:1:1:0:0:1:10' 12 | end 13 | -------------------------------------------------------------------------------- /test/cookbooks/test/recipes/dhcp_shared_network.rb: -------------------------------------------------------------------------------- 1 | dhcp_shared_network 'single' do 2 | subnets( 3 | '192.168.1.0' => { 4 | 'subnet' => '192.168.1.0', 5 | 'netmask' => '255.255.255.0', 6 | 'options' => { 7 | 'broadcast-address' => '192.168.1.255', 8 | 'routers' => '192.168.1.1', 9 | }, 10 | 'pools' => { 11 | 'range' => '192.168.1.20 192.168.1.30', 12 | }, 13 | } 14 | ) 15 | end 16 | 17 | dhcp_shared_network 'multiple' do 18 | subnets( 19 | '192.168.2.0' => { 20 | 'subnet' => '192.168.2.0', 21 | 'netmask' => '255.255.255.0', 22 | 'options' => { 23 | 'broadcast-address' => '192.168.2.255', 24 | 'routers' => '192.168.2.1', 25 | }, 26 | 'pools' => { 27 | 'range' => '192.168.2.20 192.168.2.30', 28 | }, 29 | }, 30 | '192.168.3.0' => { 31 | 'subnet' => '192.168.3.0', 32 | 'netmask' => '255.255.255.0', 33 | 'options' => { 34 | 'broadcast-address' => '192.168.3.255', 35 | 'routers' => '192.168.3.1', 36 | }, 37 | 'pools' => { 38 | 'range' => [ 39 | '192.168.3.20 192.168.3.30', 40 | '192.168.3.40 192.168.3.50', 41 | ], 42 | }, 43 | } 44 | ) 45 | end 46 | -------------------------------------------------------------------------------- /test/cookbooks/test/recipes/dhcp_subnet.rb: -------------------------------------------------------------------------------- 1 | # Define using defaults 2 | dhcp_subnet '192.168.9.0' do 3 | comment 'Listen Subnet Declaration' 4 | netmask '255.255.255.0' 5 | end 6 | 7 | # Basic definition 8 | dhcp_subnet 'basic' do 9 | comment 'Basic Subnet Declaration' 10 | subnet '192.168.0.0' 11 | netmask '255.255.255.0' 12 | options [ 13 | 'routers 192.168.0.1', 14 | 'time-offset 10', 15 | ] 16 | pools 'range' => '192.168.0.100 192.168.0.200' 17 | end 18 | 19 | directory '/etc/dhcp/override' 20 | 21 | # Override everything 22 | dhcp_subnet 'overrides' do 23 | comment 'Overrides Subnet Declaration' 24 | subnet '192.168.1.0' 25 | netmask '255.255.255.0' 26 | options [ 27 | 'routers 192.168.1.1', 28 | 'time-offset 10', 29 | 'broadcast-address 192.168.0.255', 30 | ] 31 | pools( 32 | 'peer' => '192.168.0.2', 33 | 'range' => '192.168.1.100 192.168.1.200', 34 | 'deny' => 'members of "RegisteredHosts"', 35 | 'allow' => ['members of "UnregisteredHosts"', 'members of "OtherHosts"'] 36 | ) 37 | parameters( 38 | 'ddns-domainname' => '"test.com"', 39 | 'next-server' => '192.168.0.3' 40 | ) 41 | evals [ 'if exists user-class and option user-class = "iPXE" { 42 | filename "bootstrap.ipxe"; 43 | } else { 44 | filename "undionly.kpxe"; 45 | }' ] 46 | key 'name' => 'test_key', 'algorithm' => 'hmac-sha256', 'secret' => 'c7nBOcB2rbJh7lYCI65/PGrS6QdlLMCPe2xunZ4dij8=' 47 | zones 'test' => { 'primary' => 'test_pri', 'key' => 'test_key' } 48 | conf_dir '/etc/dhcp/override' 49 | end 50 | 51 | # DHCPv6 listen subnet 52 | dhcp_subnet 'dhcpv6_listen' do 53 | ip_version :ipv6 54 | comment 'Testing DHCPv6 Basic Subnet' 55 | subnet '2001:db8:1::' 56 | prefix 64 57 | end 58 | 59 | # DHCPv6 basic subnet 60 | dhcp_subnet 'dhcpv6_basic' do 61 | ip_version :ipv6 62 | comment 'Testing DHCPv6 Basic Subnet' 63 | subnet '2001:db8:2:1::' 64 | prefix 64 65 | options( 66 | 'domain-name' => '"test.domain.local"', 67 | 'dhcp6.name-servers' => '2001:4860:4860::8888, 2001:4860:4860::8844' 68 | ) 69 | parameters( 70 | 'ddns-domainname' => '"test.domain.local"', 71 | 'default-lease-time' => 28800 72 | ) 73 | range [ 74 | '2001:db8:2:1::1:0/112', 75 | ] 76 | end 77 | -------------------------------------------------------------------------------- /test/cookbooks/test/recipes/include_files.rb: -------------------------------------------------------------------------------- 1 | file '/etc/dhcp/extra1.conf' 2 | file '/etc/dhcp/extra2.conf' 3 | -------------------------------------------------------------------------------- /test/cookbooks/test/recipes/net_setup.rb: -------------------------------------------------------------------------------- 1 | execute 'testing address eth0 - ipv4' do 2 | command 'ip addr add 192.168.9.1 dev eth0' 3 | not_if 'ip a show dev eth0 | grep \'inet 192.168.9.1\'' 4 | end 5 | 6 | execute 'testing - enable ipv6' do 7 | command 'sysctl net.ipv6.conf.eth0.disable_ipv6=0' 8 | end 9 | 10 | execute 'testing address eth0 - ipv6' do 11 | command 'ip -6 addr add 2001:db8:1::1/64 dev eth0' 12 | not_if 'ip a show dev eth0 | grep \'inet6 2001:db8:1::1\'' 13 | end 14 | 15 | execute 'remove ipv6 default route' do 16 | command 'ip -6 route del ::/0' 17 | only_if 'ip -6 route | grep default' 18 | action :nothing 19 | end.run_action(:run) 20 | -------------------------------------------------------------------------------- /test/cookbooks/test/recipes/package.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: dhcp_test 3 | # Recipe:: package 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | dhcp_package 'Server Test' 19 | -------------------------------------------------------------------------------- /test/cookbooks/test/recipes/service.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: dhcp_test 3 | # Recipe:: service 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | dhcp_service 'dhcpd' do 19 | ip_version :ipv4 20 | 21 | action %i(create enable) 22 | delayed_action :start 23 | end 24 | 25 | dhcp_service 'dhcpd6' do 26 | ip_version :ipv6 27 | 28 | action %i(create enable) 29 | delayed_action :start 30 | end 31 | -------------------------------------------------------------------------------- /test/integration/default/classes_spec.rb: -------------------------------------------------------------------------------- 1 | describe file('/etc/dhcp/dhcpd.d/classes.d/RegisteredHosts.conf') do 2 | it { should exist } 3 | it { should be_file } 4 | its(:content) { should match 'class "RegisteredHosts" ' } 5 | its(:content) { should match 'subclass "RegisteredHosts" 1:10:bf:48:42:55:01;' } 6 | end 7 | 8 | describe file('/etc/dhcp/dhcpd.d/classes.d/UnregisteredHosts.conf') do 9 | it { should exist } 10 | it { should be_file } 11 | its(:content) { should match 'class "UnregisteredHosts" ' } 12 | its(:content) { should match 'subclass "UnregisteredHosts" 1:10:bf:48:42:55:03;' } 13 | its(:content) { should match 'option domain-name-servers 8.8.8.8;' } 14 | end 15 | 16 | describe file('/etc/dhcp/dhcpd6.d/classes.d/list.conf') do 17 | it { should exist } 18 | it { should be_file } 19 | its(:content) { should match '# No files to include' } 20 | end 21 | -------------------------------------------------------------------------------- /test/integration/default/default_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | case os.family 3 | when 'redhat' 4 | package_name = if os.release.to_i < 8 5 | 'dhcp' 6 | else 7 | 'dhcp-server' 8 | end 9 | service_name = %w(dhcpd) 10 | service_name.push('dhcpd6') if interface('eth0').ipv6_address? 11 | process_state = %w(Ss Ss) 12 | when 'fedora' 13 | package_name = 'dhcp-server' 14 | service_name = %w(dhcpd) 15 | service_name.push('dhcpd6') if interface('eth0').ipv6_address? 16 | process_state = %w(Ss Ss) 17 | when 'debian' 18 | package_name = 'isc-dhcp-server' 19 | service_name = %w(isc-dhcp-server) 20 | service_name.push('isc-dhcp-server6') if interface('eth0').ipv6_address? 21 | 22 | case os.name 23 | when 'debian' 24 | process_state = os.release.to_i >= 11 ? %w(Ssl Ssl) : %w(Ss Ss) 25 | when 'ubuntu' 26 | process_state = os.release.to_f >= 20.04 ? %w(Ssl Ssl) : %w(Ss Ss) 27 | end 28 | end 29 | 30 | describe package(package_name) do 31 | it { should be_installed } 32 | end 33 | 34 | service_name.each do |service| 35 | describe service(service) do 36 | it { should be_enabled } 37 | it { should be_running } 38 | end 39 | end 40 | 41 | describe command('/usr/sbin/dhcpd -t -4 -cf /etc/dhcp/dhcpd.conf') do 42 | its('exit_status') { should eq 0 } 43 | end 44 | 45 | describe command('/usr/sbin/dhcpd -t -6 -cf /etc/dhcp/dhcpd6.conf') do 46 | its('exit_status') { should eq 0 } 47 | end 48 | 49 | describe processes('dhcpd') do 50 | its('states') { should eq process_state } 51 | end 52 | 53 | describe port(67) do 54 | it { should be_listening } 55 | its('protocols') { should include 'udp' } 56 | its('processes') { should include 'dhcpd' } 57 | end 58 | 59 | describe port(547) do 60 | it { should be_listening } 61 | its('protocols') { should include 'udp' } 62 | its('processes') { should include 'dhcpd' } 63 | end if interface('eth0').ipv6_address? 64 | 65 | describe file('/etc/dhcp/dhcpd.conf') do 66 | it { should exist } 67 | it { should be_file } 68 | its(:content) { should match %r{^include "/etc/dhcp/extra1.conf";} } 69 | its(:content) { should match %r{^include "/etc/dhcp/extra2.conf";} } 70 | its(:content) { should match "on commit {\n use-host-decl-names on;\n}" } 71 | its(:content) { should match "on release {\n use-host-decl-names on;\n}" } 72 | end 73 | 74 | describe file('/etc/dhcp/dhcpd6.conf') do 75 | it { should exist } 76 | it { should be_file } 77 | its(:content) { should match /option dhcp6.name-servers 2001:4860:4860::8888, 2001:4860:4860::8844;/ } 78 | its(:content) { should match "# Deny\ndeny duplicates;" } 79 | end 80 | -------------------------------------------------------------------------------- /test/integration/default/group_spec.rb: -------------------------------------------------------------------------------- 1 | describe file('/etc/dhcp/dhcpd.d/groups.d/ip-phones.conf') do 2 | it { should exist } 3 | it { should be_file } 4 | its(:content) { should match 'tftp-server-name "192.0.2.10";' } 5 | its(:content) { should match 'include "/etc/dhcp/dhcpd.d/groups.d/ip-phones_grouphost_SEP010101010101.conf";' } 6 | end 7 | 8 | describe file('/etc/dhcp/dhcpd.d/groups.d/ip-phones_grouphost_SEP010101010101.conf') do 9 | it { should exist } 10 | it { should be_file } 11 | its(:content) { should match 'host ip-phones_grouphost_SEP010101010101 {' } 12 | its(:content) { should match 'hardware ethernet 01:01:01:01:01:01;' } 13 | end 14 | -------------------------------------------------------------------------------- /test/integration/default/subnets_spec.rb: -------------------------------------------------------------------------------- 1 | describe file('/etc/dhcp/dhcpd.d/subnets.d/deny host from class.conf') do 2 | it { should exist } 3 | it { should be_file } 4 | its(:content) { should match 'subnet 192.168.4.0 netmask 255.255.255.0' } 5 | its(:content) { should match 'deny members of "RegisteredHosts"' } 6 | end 7 | 8 | describe file('/etc/dhcp/dhcpd6.d/subnets.d/dhcpv6_basic.conf') do 9 | it { should exist } 10 | it { should be_file } 11 | its(:content) { should match 'range6 2001:db8:2:1::1:0/112;' } 12 | its(:content) { should match 'default-lease-time 28800;' } 13 | end 14 | -------------------------------------------------------------------------------- /test/integration/delete/classes_spec.rb: -------------------------------------------------------------------------------- 1 | describe file('/etc/dhcp/dhcpd.d/classes.d/UnregisteredHosts.conf') do 2 | it { should_not exist } 3 | it { should_not be_file } 4 | end 5 | 6 | describe file('/etc/dhcp/dhcpd.d/classes.d/RegisteredHosts.conf') do 7 | it { should exist } 8 | it { should be_file } 9 | its(:content) { should match 'class "RegisteredHosts" ' } 10 | its(:content) { should match 'subclass "RegisteredHosts" 1:10:bf:48:42:55:01;' } 11 | end 12 | 13 | describe file('/etc/dhcp/dhcpd.d/classes.d/list.conf') do 14 | it { should exist } 15 | it { should be_file } 16 | its(:content) { should_not match 'include /etc/dhcp/dhcpd.d/classes.d/UnregisteredHosts.conf' } 17 | end 18 | 19 | describe file('/etc/dhcp/dhcpd6.d/classes.d/list.conf') do 20 | it { should exist } 21 | it { should be_file } 22 | its(:content) { should match '# No files to include' } 23 | end 24 | -------------------------------------------------------------------------------- /test/integration/delete/default_spec.rb: -------------------------------------------------------------------------------- 1 | case os.family 2 | when 'redhat', 'centos' 3 | package_name = if os.release.to_i < 8 4 | 'dhcp' 5 | else 6 | 'dhcp-server' 7 | end 8 | service_name = %w(dhcpd) 9 | service_name.push('dhcpd6') if interface('eth0').ipv6_address? 10 | process_state = %w(Ss) 11 | when 'fedora' 12 | package_name = 'dhcp-server' 13 | service_name = %w(dhcpd) 14 | service_name.push('dhcpd6') if interface('eth0').ipv6_address? 15 | process_state = %w(Ss) 16 | when 'debian', 'ubuntu' 17 | package_name = 'isc-dhcp-server' 18 | service_name = %w(isc-dhcp-server) 19 | service_name.push('isc-dhcp-server6') if interface('eth0').ipv6_address? 20 | 21 | case os.name 22 | when 'debian' 23 | process_state = os.release.to_i >= 11 ? %w(Ssl) : %w(Ss) 24 | when 'ubuntu' 25 | process_state = os.release.to_f >= 20.04 ? %w(Ssl) : %w(Ss) 26 | end 27 | end 28 | 29 | describe package(package_name) do 30 | it { should be_installed } 31 | end 32 | 33 | describe service(service_name[0]) do 34 | it { should be_enabled } 35 | it { should be_running } 36 | end 37 | 38 | describe service(service_name[1]) do 39 | it { should_not be_enabled } 40 | it { should_not be_running } 41 | end 42 | 43 | describe command('/usr/sbin/dhcpd -t -4 -cf /etc/dhcp/dhcpd.conf') do 44 | its('exit_status') { should eq 0 } 45 | end 46 | 47 | describe processes('dhcpd') do 48 | its('states') { should eq process_state } 49 | end 50 | 51 | describe port(67) do 52 | it { should be_listening } 53 | its('protocols') { should include 'udp' } 54 | its('processes') { should include 'dhcpd' } 55 | end 56 | 57 | describe port(547) do 58 | it { should_not be_listening } 59 | end 60 | 61 | describe file('/etc/dhcp/dhcpd.conf') do 62 | it { should exist } 63 | it { should be_file } 64 | its(:content) { should match %r{^include "/etc/dhcp/extra1.conf";} } 65 | its(:content) { should match %r{^include "/etc/dhcp/extra2.conf";} } 66 | its(:content) { should match "on commit {\n use-host-decl-names on;\n}" } 67 | its(:content) { should match "on release {\n use-host-decl-names on;\n}" } 68 | end 69 | 70 | describe file('/etc/dhcp/dhcpd6.conf') do 71 | it { should_not exist } 72 | it { should_not be_file } 73 | end 74 | -------------------------------------------------------------------------------- /test/integration/delete/subnets_spec.rb: -------------------------------------------------------------------------------- 1 | describe file('/etc/dhcp/override/overrides.conf') do 2 | it { should_not exist } 3 | it { should_not be_file } 4 | end 5 | --------------------------------------------------------------------------------