smart_proxy_dhcp_kea_api 1.0.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60b69b716ae58881effcacb442e0357e0d92a2b30b4c5be29e24439e07c9e047
4
- data.tar.gz: c698f7bdb99f3965eb1f7eb02afe908559b29c2145e3c8870b87038c3552c101
3
+ metadata.gz: 950a20554504d738a61674a78b9838c3730d4952e56fcd02d87e95242645e898
4
+ data.tar.gz: 7b1d8c8dfb590d4bcd898aa32204ca04b3a6c06ddbd118b3af0a0241105c36bd
5
5
  SHA512:
6
- metadata.gz: bd83ca3c705b98af40b57b11b9d9d10386f43277a07884d537eda07f28ef433e3939aeb5d7ab90d351e0f1f429560ea642a9a8ce0b4a98eb60867cb037c6b1e5
7
- data.tar.gz: e965386db272058aa1504d089cc69e3e39ada4610a2fe96ff4e2f1a2c5ee7632577430f781e0bffc16239f7243d94f5acb536ef0e6ebcefd0558541b080a018d
6
+ metadata.gz: 023627a6dcfb40be4e77f83fed7aebb7d96ff7c630105f967baaa4620ea6a1d403f139ecd42aa3a6ca92f92087399c5b515fb58c281da6ae36c8e1cb80bb57b0
7
+ data.tar.gz: d5b5ca744246290728a63e1a22e72b64a74ebf41e3f3ec9ead6b499e61a6b330f9655cb9d51c80c7381e71d878ccb9e479876103a693f810eff5cfdb352c6a69
data/README.md CHANGED
@@ -1,38 +1,51 @@
1
1
  # Foreman Smart Proxy DHCP Kea API
2
+
2
3
  [![Gem Version](https://badge.fury.io/rb/smart_proxy_dhcp_kea_api.svg)](https://badge.fury.io/rb/smart_proxy_dhcp_kea_api)
3
4
  [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
4
5
 
5
- A Foreman Smart Proxy plugin to provide DHCP management by interacting with the ISC Kea API. This provider allows Foreman to view subnets and leases and to create and delete host reservations directly via Kea's JSON-RPC interface.
6
+ A Foreman Smart Proxy plugin to provide DHCP management by interacting
7
+ with the ISC Kea API. This provider allows Foreman to view subnets and
8
+ leases and to create and delete host reservations directly via Kea's
9
+ JSON-RPC interface.
6
10
 
7
11
  > [!NOTE]
8
12
  > This plugin currently supports **IPv4 only**.
9
13
 
10
- ## Table of Contents
11
-
12
14
  [[_TOC_]]
13
15
 
14
16
  ## Compatibility
15
17
 
16
- * **Foreman Smart Proxy:**
17
- * **ISC Kea:** 3.0.0 or newer
18
+ * **ISC Kea:** >= 3.0.0
18
19
 
19
20
  ## Features
20
21
 
21
- - Adds ISC Kea as a DHCP provider for Foreman.
22
- - Fetches subnet and pool information directly from the Kea API.
23
- - Fetches active lease information.
24
- - Provides host reservation management (add/delete).
25
- - Suggests the next available IP address from a subnet's pool.
26
- - Passes next-server and PXE boot file to Kea
22
+ * Adds ISC Kea as a DHCP provider for Foreman.
23
+ * Fetches subnet and pool information directly from the Kea API.
24
+ * Fetches active lease information.
25
+ * Provides host reservation management (add/delete).
26
+ * Provides lease deletion via `lease4-del`.
27
+ * Suggests the next available IP address from a subnet's pool.
28
+ * Passes next-server, PXE boot file, DNS servers, NTP servers,
29
+ and domain-name to Kea.
30
+ * Round-trips reservation options (PXE settings, DNS, routers)
31
+ so Foreman can pre-fill them on edit.
32
+ * Exposes subnet-level DHCP options (next-server, boot-file-name,
33
+ DNS, NTP) to Foreman's UI.
34
+ * Automatic cache refresh with configurable TTL.
35
+ * Optional subnet filtering to manage only a subset of Kea's
36
+ subnets.
27
37
 
28
38
  ## Prerequisites
29
39
 
30
- - A running Foreman Smart Proxy instance.
31
- - A running ISC Kea DHCP server.
32
- - Network connectivity from the Smart Proxy server to the Kea server's API endpoint (default port 8000).
40
+ * A running Foreman Smart Proxy instance.
41
+ * A running ISC Kea DHCP server.
42
+ * Network connectivity from the Smart Proxy server to the Kea
43
+ server's API endpoint (default port 8000).
33
44
 
34
45
  > [!WARNING]
35
- > The Kea server **must** be configured with the **`host_cmds`** and **`lease_cmds`** hook libraries loaded for the `dhcp4` service. This plugin will not function without them.
46
+ > The Kea server **must** be configured with the **`host_cmds`**
47
+ > and **`lease_cmds`** hook libraries loaded for the `dhcp4`
48
+ > service. This plugin will not function without them.
36
49
 
37
50
  ## Installation
38
51
 
@@ -42,31 +55,39 @@ First, install the gem on your Foreman Smart Proxy server:
42
55
  gem install smart_proxy_dhcp_kea_api
43
56
  ```
44
57
 
45
- Add the gem to `/usr/share/foreman-proxy/bundler.d/Gemfile.local.rb`:
58
+ Register the gem with the Smart Proxy bundler by creating a file
59
+ in `/usr/share/foreman-proxy/bundler.d/`:
46
60
 
47
- ```ruby
48
- gem 'smart_proxy_dhcp_kea_api'
61
+ ```bash
62
+ echo "gem 'smart_proxy_dhcp_kea_api'" \
63
+ > /usr/share/foreman-proxy/bundler.d/dhcp_kea_api.rb
49
64
  ```
50
65
 
51
- After installing the gem or changing its configuration, you must restart the `foreman-proxy` service for the changes to take effect.
52
- ```bash
66
+ Then run `bundle install` and restart the proxy:
67
+
68
+ ```bash
69
+ cd /usr/share/foreman-proxy && bundle install
53
70
  systemctl restart foreman-proxy
54
- ```
71
+ ```
55
72
 
56
73
  ## Configuration
57
74
 
58
- Configuration is done in two files in the `/etc/foreman-proxy/settings.d/` directory.
75
+ Configuration is done in two files in the
76
+ `/etc/foreman-proxy/settings.d/` directory.
59
77
 
60
78
  ### 1. Enable the DHCP Module
61
79
 
62
- First, you must enable the main DHCP module and tell it to use this plugin as its provider.
80
+ First, enable the main DHCP module and tell it to use this plugin
81
+ as its provider.
63
82
 
64
83
  **File:** `/etc/foreman-proxy/settings.d/dhcp.yml`
84
+
65
85
  ```yaml
66
86
  ---
67
87
  :enabled: true
68
88
  :use_provider: dhcp_kea_api
69
- # It is highly recommended to enable the ping check for unused IPs to prevent conflicts.
89
+ # It is highly recommended to enable the ping check for unused
90
+ # IPs to prevent conflicts.
70
91
  :ping_free_ip: true
71
92
  ```
72
93
 
@@ -76,43 +97,74 @@ Next, create a configuration file for the Kea provider itself.
76
97
 
77
98
  **File:** `/etc/foreman-proxy/settings.d/dhcp_kea_api.yml`
78
99
 
79
- This file enables the provider and contains all the settings needed to connect to your Kea API server.
100
+ This file enables the provider and contains all the settings
101
+ needed to connect to your Kea API server.
102
+
80
103
 
81
- | Setting | Description | Default | Required |
82
- | -------------------------- | ---------------------------------------------------------------------------- | ------------------------ | -------- |
83
- | `:enabled` | Enables or disables this provider module. | `false` | **Yes** |
84
- | `:kea_api_url` | The full URL to your Kea API endpoint (HTTP or HTTPS). | `http://127.0.0.1:8000/` | **Yes** |
85
- | `:open_timeout` | Time in seconds to wait for the initial connection to be established. | `5` | No |
86
- | `:read_timeout` | Time in seconds to wait for a response after connecting. | `10` | No |
87
- | `:blacklist_duration_minutes` | The duration in minutes to temporarily blacklist a suggested IP. | `5` | No |
104
+ | Setting | Default | Required | Description |
105
+ | ----------------------------- | ------------------------ | -------- | ------------------------------- |
106
+ | `:enabled` | `false` | **Yes** | Enables this provider. |
107
+ | `:kea_api_url` | `http://127.0.0.1:8000/` | **Yes** | Kea API endpoint URL. |
108
+ | `:kea_api_username` | `nil` | No | HTTP Basic Auth username. |
109
+ | `:kea_api_password` | `nil` | No | HTTP Basic Auth password. |
110
+ | `:open_timeout` | `5` | No | Connect timeout (seconds). |
111
+ | `:read_timeout` | `10` | No | Response timeout (seconds). |
112
+ | `:blacklist_duration_minutes` | `5` | No | IP blacklist duration (min). |
113
+ | `:cache_ttl` | `60` | No | Cache refresh interval (sec). |
114
+ | `:managed_subnets` | `nil` | No | CIDRs to manage (all if unset). |
115
+
116
+ **Example configuration:**
117
+
118
+ ```yaml
119
+ ---
120
+ :enabled: true
121
+ :kea_api_url: http://127.0.0.1:8000/
122
+ :kea_api_username: admin
123
+ :kea_api_password: secret
124
+ :cache_ttl: 120
125
+ :managed_subnets:
126
+ - 192.168.1.0/24
127
+ - 10.0.0.0/16
128
+ ```
88
129
 
89
130
  ### Verifying the Installation
90
131
 
91
- The Smart Proxy will test its connection to the Kea API upon initialisation. Due to lazy loading, this check runs **on the first DHCP request** after a proxy restart, not during the initial boot sequence.
132
+ Due to lazy loading, the plugin connects to Kea on the **first
133
+ DHCP request** after a proxy restart, not at boot time.
92
134
 
93
- Check `/var/log/foreman-proxy/proxy.log` for a "Successfully connected to Kea API" message at that time to confirm everything is working. If the connection fails, the first DHCP request will fail with an error in the log, preventing further issues.
135
+ Check `/var/log/foreman-proxy/proxy.log` for messages like
136
+ `Loaded subnet 192.168.x.x/255.255.255.0 and mapped to Kea ID 1`
137
+ to confirm the connection is working. If the connection fails,
138
+ the first DHCP request will return an error and the log will
139
+ contain the details.
94
140
 
95
141
  ## Contributing
96
142
 
97
- Bug reports and pull requests are welcome on this project's GitLab page.
143
+ Bug reports and pull requests are welcome on this project's
144
+ GitLab page.
98
145
 
99
146
  ## Licence Information
100
147
 
148
+ <!-- markdownlint-disable MD033 -->
149
+
101
150
  <details>
102
151
  <summary>Copyright and Licence (GPLv3)</summary>
103
152
 
104
- Copyright © 2025 Sam McCarthy
153
+ Copyright (c) 2025 Sam McCarthy
105
154
 
106
- This program is free software: you can redistribute it and/or modify
107
- it under the terms of the GNU General Public Licence as published by
108
- the Free Software Foundation, either version 3 of the Licence or
109
- (at your option) any later version.
155
+ This program is free software: you can redistribute it and/or
156
+ modify it under the terms of the GNU General Public Licence as
157
+ published by the Free Software Foundation, either version 3 of
158
+ the Licence or (at your option) any later version.
110
159
 
111
160
  This program is distributed in the hope that it will be useful,
112
161
  but WITHOUT ANY WARRANTY; without even the implied warranty of
113
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
162
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
114
163
  GNU General Public Licence for more details.
115
164
 
116
165
  You should have received a copy of the GNU General Public Licence
117
- along with this program. If not, see <https://www.gnu.org/licenses/>.
118
- </details>
166
+ along with this program. If not, see
167
+ [https://www.gnu.org/licenses/](https://www.gnu.org/licenses/).
168
+
169
+ </details>
170
+ <!-- markdownlint-enable MD033 -->
@@ -8,7 +8,7 @@ module Proxy
8
8
  module KeaApi
9
9
  # The main provider class for the `dhcp_kea_api` module. This class inherits
10
10
  # from the Foreman Smart Proxy's core `DHCP::Server` and implements the
11
- # Kea-specific logic for adding and deleting DHCP reservations.
11
+ # Kea-specific logic for adding and deleting DHCP reservations and leases.
12
12
  class Provider < ::Proxy::DHCP::Server
13
13
  attr_reader :subnet_service, :client
14
14
 
@@ -25,40 +25,9 @@ module Proxy
25
25
  end
26
26
 
27
27
  # Creates a new DHCP reservation in Kea.
28
- # This method orchestrates the process by first calling the parent class's
29
- # `add_record` to perform initial validation and IP selection, then building
30
- # the Kea-specific payload and sending it via the API client.
31
28
  #
32
29
  # @param options [Hash] A hash containing the details for the new reservation.
33
- # @option options [String] 'mac' The hardware address of the host.
34
- # @option options [String] 'ip' The IP address to reserve.
35
- # @option options [String] 'hostname' The hostname for the reservation.
36
- # @option options [Proxy::DHCP::Subnet] :subnet The subnet object this reservation belongs to.
37
- # @option options [String, nil] 'nextServer' (optional) The IP of the TFTP boot server.
38
- # @option options [String, nil] 'filename' (optional) The boot filename (e.g., 'pxelinux.0').
39
30
  # @return [Proxy::DHCP::Reservation] The created reservation object.
40
- #
41
- # @example Add a new DHCP reservation for a host
42
- # # Assume @provider is an instance of Proxy::DHCP::KeaApi::Provider
43
- # # and 'subnet' is a valid Proxy::DHCP::Subnet object for '192.168.1.0/24'
44
- # options = {
45
- # 'mac' => 'aa:bb:cc:dd:ee:ff',
46
- # 'hostname' => 'test-host.example.com',
47
- # 'ip' => '192.168.1.15',
48
- # subnet: subnet,
49
- # 'nextServer' => 'tftp.example.com',
50
- # 'filename' => 'pxelinux.0',
51
- # 'routers' => ['192.168.1.1'],
52
- # 'ntp_servers' => ['192.168.1.254']
53
- # }
54
- #
55
- # reservation = @provider.add_record(options)
56
- #
57
- # reservation.mac #=> "aa:bb:cc:dd:ee:ff"
58
- # reservation.ip #=> "192.168.1.15"
59
- # reservation.name #=> "test-host.example.com"
60
- # reservation.subnet #=> #<Proxy::DHCP::Subnet ...>
61
- #
62
31
  def add_record(options = {})
63
32
  logger.debug "DHCP options received from Foreman: #{options.inspect}"
64
33
  record = super
@@ -70,43 +39,93 @@ module Proxy
70
39
  reservation_args['option-data'] = option_data unless option_data.empty?
71
40
 
72
41
  @client.post_command('dhcp4', 'reservation-add', { reservation: reservation_args })
73
- subnet_service.add_host(record.subnet.network, record)
42
+ begin
43
+ subnet_service.add_host(record.subnet.network, record)
44
+ rescue StandardError => e
45
+ logger.error "Cache update failed after successful reservation-add for MAC #{record.mac}. " \
46
+ "Kea and cache are out of sync: #{e.message}"
47
+ raise
48
+ end
74
49
 
75
50
  logger.info "Successfully added reservation for MAC #{record.mac} and IP #{record.ip}"
76
51
  record
77
52
  end
78
53
 
79
- # Deletes a DHCP reservation from Kea.
80
- #
81
- # @param record [Proxy::DHCP::Reservation] The reservation object to be deleted.
82
- # This object should be retrieved from the subnet service first.
83
- # @return [Proxy::DHCP::Reservation] The object that was successfully deleted.
84
- # @raise [Proxy::DHCP::Error] if the corresponding Kea subnet-id cannot be found or the API call fails.
54
+ # Deletes a DHCP record from Kea. Handles both reservations and leases.
85
55
  #
86
- # @example Delete a known DHCP reservation
87
- # # Assume @provider is an instance of Proxy::DHCP::KeaApi::Provider
88
- # mac_to_delete = 'aa:bb:cc:dd:ee:ff'
56
+ # @param record [Proxy::DHCP::Reservation, Proxy::DHCP::Lease] The record to be deleted.
57
+ # @return [Proxy::DHCP::Record] The object that was successfully deleted.
58
+ # @raise [Proxy::DHCP::Error] if the record type is unsupported, the corresponding
59
+ # Kea subnet-id cannot be found, or the API call fails.
60
+ def del_record(record)
61
+ logger.debug "Deleting record: #{record.inspect}"
62
+
63
+ if record.is_a?(::Proxy::DHCP::Reservation)
64
+ del_reservation(record)
65
+ elsif record.is_a?(::Proxy::DHCP::Lease)
66
+ del_lease(record)
67
+ else
68
+ raise Proxy::DHCP::Error, "Cannot delete unsupported record type: #{record.class.name}"
69
+ end
70
+
71
+ record
72
+ end
73
+
74
+ # Loads subnet-level DHCP options from the cached Kea configuration.
89
75
  #
90
- # # First, find the reservation object
91
- # record_to_delete = @provider.get_record('mac' => mac_to_delete)
76
+ # @param subnet [Proxy::DHCP::Subnet] The subnet to load options for.
77
+ # @return [void]
78
+ def load_subnet_options(subnet)
79
+ opts = @subnet_service.subnet_options[subnet.network]
80
+ return unless opts
81
+
82
+ apply_boot_subnet_options(subnet, opts)
83
+ apply_mapped_subnet_options(subnet, opts)
84
+ end
85
+
86
+ private
87
+
88
+ # Applies the boot/server fields, which are top-level Kea config fields
89
+ # rather than `option-data` entries (so they are not in OPTION_MAP).
92
90
  #
93
- # # Now, pass the entire object to del_record
94
- # if record_to_delete
95
- # result = @provider.del_record(record_to_delete)
96
- # #=> #<Proxy::DHCP::Reservation ...>
97
- # end
91
+ # @param subnet [Proxy::DHCP::Subnet] The subnet to modify.
92
+ # @param opts [Hash] The cached options hash.
93
+ # @return [void]
94
+ def apply_boot_subnet_options(subnet, opts)
95
+ subnet.options[:nextServer] = opts['next-server'] if opts['next-server']
96
+ subnet.options[:filename] = opts['boot-file-name'] if opts['boot-file-name']
97
+ end
98
+
99
+ # Applies every `option-data`-derived subnet option using OPTION_MAP as the
100
+ # single source of truth for the Kea-name -> Foreman-key (and list) mapping.
98
101
  #
99
- def del_record(record)
100
- logger.debug "Deleting record; #{record.inspect}"
101
- unless record.is_a?(::Proxy::DHCP::Reservation)
102
- logger.warn "Attempted to delete a record for MAC '#{record.mac}' but it is not a Reservation (actual type: #{record.class.name}). No action taken."
103
- return record
102
+ # @param subnet [Proxy::DHCP::Subnet] The subnet to modify.
103
+ # @param opts [Hash] The cached options hash.
104
+ # @return [void]
105
+ def apply_mapped_subnet_options(subnet, opts)
106
+ SubnetService::OPTION_MAP.each do |kea_name, mapping|
107
+ value = opts[kea_name]
108
+ next unless value
109
+
110
+ subnet.options[mapping[:key]] = mapping[:list] ? split_option(value) : value
104
111
  end
112
+ end
113
+
114
+ # Splits a comma-separated option string into a trimmed array.
115
+ #
116
+ # @param value [String] The comma-separated string.
117
+ # @return [Array<String>] The split and stripped values.
118
+ def split_option(value)
119
+ value.split(',').map(&:strip)
120
+ end
105
121
 
106
- subnet_id = @subnet_service.kea_id_map[record.subnet.network]
107
- raise Proxy::DHCP::Error, "Unable to find Kea subnet-id for network #{record.subnet.network}" unless subnet_id
122
+ # Deletes a reservation from Kea by MAC address.
123
+ #
124
+ # @param record [Proxy::DHCP::Reservation] The reservation to delete.
125
+ # @return [void]
126
+ def del_reservation(record)
127
+ subnet_id = find_subnet_id!(record.subnet.network)
108
128
 
109
- # Construct the arguments for the 'reservation-del' command using the identifier triplet.
110
129
  args = {
111
130
  'subnet-id': subnet_id,
112
131
  'identifier-type': 'hw-address',
@@ -114,21 +133,59 @@ module Proxy
114
133
  }
115
134
 
116
135
  @client.post_command('dhcp4', 'reservation-del', args)
117
- subnet_service.delete_host(record)
136
+ begin
137
+ subnet_service.delete_host(record)
138
+ rescue StandardError => e
139
+ logger.error "Cache update failed after successful reservation-del for MAC #{record.mac}. " \
140
+ "Kea and cache are out of sync: #{e.message}"
141
+ raise
142
+ end
118
143
 
119
144
  logger.info "Successfully deleted reservation for MAC #{record.mac} and IP #{record.ip}"
120
- record
121
145
  end
122
146
 
123
- private
147
+ # Deletes a lease from Kea by IP address.
148
+ #
149
+ # @param record [Proxy::DHCP::Lease] The lease to delete.
150
+ # @return [void]
151
+ def del_lease(record)
152
+ subnet_id = find_subnet_id!(record.subnet.network)
153
+
154
+ args = {
155
+ 'subnet-id': subnet_id,
156
+ 'ip-address': record.ip
157
+ }
158
+
159
+ @client.post_command('dhcp4', 'lease4-del', args)
160
+ begin
161
+ subnet_service.delete_lease(record)
162
+ rescue StandardError => e
163
+ logger.error "Cache update failed after successful lease4-del for IP #{record.ip}. " \
164
+ "Kea and cache are out of sync: #{e.message}"
165
+ raise
166
+ end
167
+
168
+ logger.info "Successfully deleted lease for IP #{record.ip}"
169
+ end
170
+
171
+ # Looks up the Kea subnet-id for a network address, raising if not found.
172
+ #
173
+ # @param network [String] The subnet network address.
174
+ # @return [Integer] The Kea subnet-id.
175
+ # @raise [Proxy::DHCP::Error] if the subnet-id is not in the map.
176
+ def find_subnet_id!(network)
177
+ subnet_id = @subnet_service.kea_id_map[network]
178
+ raise Proxy::DHCP::Error, "Unable to find Kea subnet-id for network #{network}" unless subnet_id
179
+
180
+ subnet_id
181
+ end
124
182
 
125
183
  # Builds the initial hash of arguments required for a Kea reservation.
126
184
  #
127
185
  # @param record [Proxy::DHCP::Reservation] The reservation object from the parent class.
128
186
  # @return [Hash] A hash containing the base arguments for the Kea API.
129
187
  def build_base_reservation_args(record)
130
- subnet_id = @subnet_service.kea_id_map[record.subnet.network]
131
- raise Proxy::DHCP::Error, "Unable to find Kea subnet-id for network #{record.subnet.network}" unless subnet_id
188
+ subnet_id = find_subnet_id!(record.subnet.network)
132
189
 
133
190
  {
134
191
  'subnet-id': subnet_id,
@@ -139,16 +196,15 @@ module Proxy
139
196
  end
140
197
 
141
198
  # Adds next-server and boot-file-name options to the reservation arguments hash.
142
- # This method modifies the `reservation_args` hash in place.
143
199
  #
144
200
  # @param reservation_args [Hash] The hash of arguments to be modified.
145
201
  # @param options [Hash] The original options hash from Foreman.
146
202
  # @return [void]
147
203
  def add_boot_and_server_options(reservation_args, options)
148
- next_server_value = options[:nextServer]
204
+ next_server_value = options['nextServer']
149
205
  reservation_args[:'next-server'] = resolve_hostname(next_server_value) unless next_server_value.to_s.empty?
150
206
 
151
- reservation_args[:'boot-file-name'] = options[:filename] unless options[:filename].to_s.empty?
207
+ reservation_args[:'boot-file-name'] = options['filename'] unless options['filename'].to_s.empty?
152
208
  end
153
209
 
154
210
  # Resolves a hostname to an IP address. If the provided string is already
@@ -165,27 +221,26 @@ module Proxy
165
221
  raise Proxy::DHCP::Error, "Could not resolve next-server hostname '#{hostname}': #{e.message}"
166
222
  end
167
223
 
168
- # Builds the array of DHCP options (e.g. routers, ntp-servers) for the reservation.
224
+ # Builds the array of DHCP options (e.g. routers, ntp-servers, dns-servers) for the reservation.
169
225
  #
170
226
  # @param options [Hash] The original options hash from Foreman.
171
227
  # @return [Array<Hash>] An array of option hashes for the Kea API.
172
228
  def build_option_data(options)
173
229
  option_data = []
174
- add_dhcp_option(option_data, 'routers', options[:routers])
175
- add_dhcp_option(option_data, 'ntp-servers', options[:ntp_servers])
176
- add_dhcp_option(option_data, 'domain-name', options[:domain_name])
230
+ SubnetService::OPTION_MAP.each do |kea_name, mapping|
231
+ add_dhcp_option(option_data, kea_name, options[mapping[:key].to_s])
232
+ end
177
233
  option_data
178
234
  end
179
235
 
180
236
  # A helper to add a DHCP option to the data array if the value exists.
181
- # This method modifies the `option_data` array in place.
182
237
  #
183
238
  # @param option_data [Array<Hash>] The array of options to be modified.
184
239
  # @param name [String] The name of the DHCP option (e.g. 'routers').
185
240
  # @param value [String, Array] The value of the option.
186
241
  # @return [void]
187
242
  def add_dhcp_option(option_data, name, value)
188
- return if value.to_s.empty?
243
+ return if value.nil? || (value.respond_to?(:empty?) && value.empty?)
189
244
 
190
245
  option_data << { name: name, data: Array(value).join(',') }
191
246
  end
@@ -21,13 +21,18 @@ module Proxy
21
21
  # that the base classes we inherit from (like DHCP::Server) are available.
22
22
  requires :dhcp, '>= 1.17'
23
23
 
24
- # Defines the default settings for this provider. These values are used if
25
- # they are not explicitly overridden in a user's settings file
26
- # (e.g. `/etc/foreman-proxy/settings.d/dhcp_kea_api.yml`).
24
+ # Defines the default settings for this provider. These values are used
25
+ # if they are not overridden in the user's settings file
26
+ # (`/etc/foreman-proxy/settings.d/dhcp_kea_api.yml`).
27
+ # The `kea_api_username` and `kea_api_password` enable HTTP Basic
28
+ # Authentication when the Kea control agent requires it.
27
29
  default_settings kea_api_url: 'http://127.0.0.1:8000/',
30
+ kea_api_username: nil,
31
+ kea_api_password: nil,
28
32
  blacklist_duration_minutes: 5,
29
33
  open_timeout: 5,
30
- read_timeout: 10
34
+ read_timeout: 10,
35
+ cache_ttl: 60
31
36
 
32
37
  # Hooks into the Smart Proxy's dependency injection (DI) framework. These lines
33
38
  # delegate the responsibility of loading the required classes and wiring up