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 +4 -4
- data/README.md +95 -43
- data/lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_main.rb +126 -71
- data/lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_plugin.rb +9 -4
- data/lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_subnet_service.rb +292 -48
- data/lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_version.rb +2 -1
- data/lib/smart_proxy_dhcp_kea_api/kea_api_client.rb +28 -16
- data/lib/smart_proxy_dhcp_kea_api/plugin_configuration.rb +13 -14
- data/lib/smart_proxy_dhcp_kea_api.rb +6 -0
- data/smart_proxy_dhcp_kea_api.gemspec +17 -1
- metadata +152 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 950a20554504d738a61674a78b9838c3730d4952e56fcd02d87e95242645e898
|
|
4
|
+
data.tar.gz: 7b1d8c8dfb590d4bcd898aa32204ca04b3a6c06ddbd118b3af0a0241105c36bd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
[](https://badge.fury.io/rb/smart_proxy_dhcp_kea_api)
|
|
3
4
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
4
5
|
|
|
5
|
-
A Foreman Smart Proxy plugin to provide DHCP management by interacting
|
|
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
|
-
*
|
|
17
|
-
* **ISC Kea:** 3.0.0 or newer
|
|
18
|
+
* **ISC Kea:** >= 3.0.0
|
|
18
19
|
|
|
19
20
|
## Features
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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`**
|
|
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
|
-
|
|
58
|
+
Register the gem with the Smart Proxy bundler by creating a file
|
|
59
|
+
in `/usr/share/foreman-proxy/bundler.d/`:
|
|
46
60
|
|
|
47
|
-
```
|
|
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
|
-
|
|
52
|
-
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
82
|
-
|
|
|
83
|
-
| `:enabled`
|
|
84
|
-
| `:kea_api_url`
|
|
85
|
-
| `:
|
|
86
|
-
| `:
|
|
87
|
-
| `:
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
153
|
+
Copyright (c) 2025 Sam McCarthy
|
|
105
154
|
|
|
106
|
-
This program is free software: you can redistribute it and/or
|
|
107
|
-
it under the terms of the GNU General Public Licence as
|
|
108
|
-
the Free Software Foundation, either version 3 of
|
|
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.
|
|
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.
|
|
118
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
# @
|
|
87
|
-
#
|
|
88
|
-
#
|
|
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
|
-
#
|
|
91
|
-
#
|
|
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
|
-
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
107
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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[
|
|
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[
|
|
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
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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.
|
|
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
|
|
25
|
-
# they are not
|
|
26
|
-
# (
|
|
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
|