smart_proxy_dhcp_kea_api 1.0.0 → 1.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 +1 -1
- data/lib/.DS_Store +0 -0
- data/lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_main.rb +50 -3
- data/lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_plugin.rb +6 -0
- data/lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_subnet_service.rb +15 -1
- 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 +53 -14
- data/lib/smart_proxy_dhcp_kea_api/plugin_configuration.rb +5 -0
- data/lib/smart_proxy_dhcp_kea_api.rb +6 -0
- data/smart_proxy_dhcp_kea_api.gemspec +15 -1
- metadata +146 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 42b1eec7524d54aa43e9f202c85cb88a92fbfcdf3dbf91a42df43e20d000ac1c
|
4
|
+
data.tar.gz: 66ae862538c0fc90345eb7a32e487ef63ef536c37cac572a07adfff9d39bb978
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5cf2dfcf2d1b5b52efa98f274b9abd4d777b5b62182f048401040a01baf3f7c32e70cf38921d7e596090a993b684ac19748257e8e2d67d9f35479ef0cc6f823c
|
7
|
+
data.tar.gz: 1c54018b7438505c0d8de97337a0363435498c29682e93d7699ffacb2933e7f4af57037c52bbe180b6273066b2c48e6712b06bf3484a83902715e585931d656b
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Foreman Smart Proxy DHCP Kea API
|
2
|
-
|
2
|
+
[](https://badge.fury.io/rb/smart_proxy_dhcp_kea_api)
|
3
3
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
4
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.
|
data/lib/.DS_Store
ADDED
Binary file
|
@@ -29,8 +29,36 @@ module Proxy
|
|
29
29
|
# `add_record` to perform initial validation and IP selection, then building
|
30
30
|
# the Kea-specific payload and sending it via the API client.
|
31
31
|
#
|
32
|
-
# @param options [Hash] A hash
|
32
|
+
# @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').
|
33
39
|
# @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
|
+
#
|
34
62
|
def add_record(options = {})
|
35
63
|
logger.debug "DHCP options received from Foreman: #{options.inspect}"
|
36
64
|
record = super
|
@@ -51,10 +79,29 @@ module Proxy
|
|
51
79
|
# Deletes a DHCP reservation from Kea.
|
52
80
|
#
|
53
81
|
# @param record [Proxy::DHCP::Reservation] The reservation object to be deleted.
|
54
|
-
#
|
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.
|
85
|
+
#
|
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'
|
89
|
+
#
|
90
|
+
# # First, find the reservation object
|
91
|
+
# record_to_delete = @provider.get_record('mac' => mac_to_delete)
|
92
|
+
#
|
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
|
98
|
+
#
|
55
99
|
def del_record(record)
|
56
100
|
logger.debug "Deleting record; #{record.inspect}"
|
57
|
-
|
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
|
104
|
+
end
|
58
105
|
|
59
106
|
subnet_id = @subnet_service.kea_id_map[record.subnet.network]
|
60
107
|
raise Proxy::DHCP::Error, "Unable to find Kea subnet-id for network #{record.subnet.network}" unless subnet_id
|
@@ -10,6 +10,8 @@ module Proxy
|
|
10
10
|
# as the entry point for the Foreman Smart Proxy to load and configure the
|
11
11
|
# plugin. It defines the plugin's name, version, dependencies on other
|
12
12
|
# modules, default settings, and hooks into the dependency injection framework.
|
13
|
+
#
|
14
|
+
# @see Proxy::DHCP::KeaApi::PluginConfiguration For how dependencies are loaded and wired.
|
13
15
|
class Plugin < ::Proxy::Provider
|
14
16
|
# Registers the provider with the Smart Proxy, giving it a unique name
|
15
17
|
# (`:dhcp_kea_api`) and sourcing the version from the VERSION constant.
|
@@ -21,8 +23,12 @@ module Proxy
|
|
21
23
|
|
22
24
|
# Defines the default settings for this provider. These values are used if
|
23
25
|
# they are not explicitly overridden in a user's settings file
|
26
|
+
# The `kea_api_username` and `kea_api_password` can be used to enable
|
27
|
+
# HTTP Basic Authentication if required by the Kea control agent.
|
24
28
|
# (e.g. `/etc/foreman-proxy/settings.d/dhcp_kea_api.yml`).
|
25
29
|
default_settings kea_api_url: 'http://127.0.0.1:8000/',
|
30
|
+
kea_api_username: nil,
|
31
|
+
kea_api_password: nil,
|
26
32
|
blacklist_duration_minutes: 5,
|
27
33
|
open_timeout: 5,
|
28
34
|
read_timeout: 10
|
@@ -31,7 +31,7 @@ module Proxy
|
|
31
31
|
# @param reservations_by_ip [Proxy::MemoryStore] A memory store for reservations, passed to the parent class.
|
32
32
|
# @param reservations_by_mac [Proxy::MemoryStore] A memory store for reservations, passed to the parent class.
|
33
33
|
# @param reservations_by_name [Proxy::MemoryStore] A memory store for reservations, passed to the parent class.
|
34
|
-
|
34
|
+
# @return [void]
|
35
35
|
# rubocop:disable Metrics/ParameterLists
|
36
36
|
def initialize(client, leases_by_ip, leases_by_mac, reservations_by_ip, reservations_by_mac, reservations_by_name)
|
37
37
|
@client = client
|
@@ -47,6 +47,8 @@ module Proxy
|
|
47
47
|
#
|
48
48
|
# @return [true] on success.
|
49
49
|
# @raise [Proxy::DHCP::Error] if any part of the loading process fails.
|
50
|
+
# @see #load_subnets_and_reservations_from_kea
|
51
|
+
# @see #load_leases_from_kea
|
50
52
|
# rubocop:disable Naming/PredicateMethod
|
51
53
|
def load!
|
52
54
|
subnets.clear
|
@@ -60,6 +62,10 @@ module Proxy
|
|
60
62
|
|
61
63
|
# Fetches all subnets and their associated reservations from the Kea API.
|
62
64
|
# This single `config-get` call is the most efficient way to get all static configuration.
|
65
|
+
# @return [void]
|
66
|
+
# @raise [Proxy::DHCP::Error] if the API call fails.
|
67
|
+
# @raise [IPAddr::InvalidAddressError] if a subnet address from Kea is invalid.
|
68
|
+
# @see Proxy::DHCP::KeaApi::Client#post_command
|
63
69
|
def load_subnets_and_reservations_from_kea
|
64
70
|
config = @client.post_command('dhcp4', 'config-get')
|
65
71
|
subnets_data = config&.dig('Dhcp4', 'subnet4')
|
@@ -79,6 +85,9 @@ module Proxy
|
|
79
85
|
end
|
80
86
|
|
81
87
|
# Fetches all active leases from the Kea API for the subnets currently in the cache.
|
88
|
+
# @return [void]
|
89
|
+
# @raise [Proxy::DHCP::Error] if the API call fails.
|
90
|
+
# @see Proxy::DHCP::KeaApi::Client#post_command
|
82
91
|
def load_leases_from_kea
|
83
92
|
# This guard is necessary because the `lease4-get-all` command requires
|
84
93
|
# a list of subnet IDs to query. If no subnets were loaded, we can't get leases.
|
@@ -112,6 +121,8 @@ module Proxy
|
|
112
121
|
# Foreman Subnet and Reservation objects, and adds them to the cache.
|
113
122
|
#
|
114
123
|
# @param subnet_data [Hash] The hash representing a single subnet from Kea's `config-get` response.
|
124
|
+
# @return [void]
|
125
|
+
# @raise [IPAddr::InvalidAddressError] if the subnet string is not a valid IP address.
|
115
126
|
def process_subnet(subnet_data)
|
116
127
|
ip_object = IPAddr.new(subnet_data['subnet'])
|
117
128
|
subnet_addr = ip_object.to_s
|
@@ -140,6 +151,7 @@ module Proxy
|
|
140
151
|
#
|
141
152
|
# @param subnet_data [Hash] The hash representing a single subnet.
|
142
153
|
# @return [Array<String>, nil] An array of router IP addresses, or nil if none are found.
|
154
|
+
# @note The router data in Kea is a single comma-separated string which must be split into an array.
|
143
155
|
def extract_routers(subnet_data)
|
144
156
|
# The router data is a comma-separated string which must be split into an array.
|
145
157
|
subnet_data['option-data']&.find { |opt| opt['name'] == 'routers' }&.[]('data')&.split(',')
|
@@ -149,6 +161,7 @@ module Proxy
|
|
149
161
|
#
|
150
162
|
# @param subnet_data [Hash] The hash representing a single subnet.
|
151
163
|
# @return [Array<String>, nil] A two-element array containing the start and end of the range, or nil.
|
164
|
+
# @note The pool range is a hyphen-separated string (e.g. "10.0.0.10-10.0.0.20").
|
152
165
|
def extract_range(subnet_data)
|
153
166
|
# The pool range is a hyphen-separated string (e.g. "10.0.0.10-10.0.0.20").
|
154
167
|
pool_string = subnet_data.dig('pools', 0, 'pool')
|
@@ -159,6 +172,7 @@ module Proxy
|
|
159
172
|
#
|
160
173
|
# @param res_data [Hash] The hash representing a single reservation.
|
161
174
|
# @param subnet [Proxy::DHCP::Subnet] The subnet object this reservation belongs to.
|
175
|
+
# @return [void]
|
162
176
|
def process_reservation(res_data, subnet)
|
163
177
|
record = ::Proxy::DHCP::Reservation.new(res_data['hostname'], res_data['ip-address'], res_data['hw-address'], subnet)
|
164
178
|
# Add the reservation to the parent class's cache, making it searchable.
|
@@ -16,10 +16,24 @@ module Proxy
|
|
16
16
|
# Initialises a new Kea API client.
|
17
17
|
#
|
18
18
|
# @param url [String] The base URL of the Kea API endpoint (e.g. 'http://127.0.0.1:8000/').
|
19
|
+
# @param username [String, nil] The username for HTTP Basic Authentication.
|
20
|
+
# @param password [String, nil] The password for HTTP Basic Authentication.
|
19
21
|
# @param open_timeout [Integer] Time in seconds to wait for the initial TCP connection to be established (defaults to 5).
|
20
22
|
# @param read_timeout [Integer] Time in seconds to wait for a response from the server after the connection is made (defaults to 10).
|
21
23
|
# @raise [ArgumentError] if the URL is blank, malformed, or not a valid HTTP/S URL.
|
22
|
-
|
24
|
+
#
|
25
|
+
# @example Basic Initialization
|
26
|
+
# client = Proxy::DHCP::KeaApi::Client.new(url: 'https://kea.example.com:8443')
|
27
|
+
#
|
28
|
+
# @example Initialization with Custom Timeouts
|
29
|
+
# client = Proxy::DHCP::KeaApi::Client.new(
|
30
|
+
# url: 'http://127.0.0.1:8000',
|
31
|
+
# username: 'myuser',
|
32
|
+
# password: 'mypassword'
|
33
|
+
# open_timeout: 2,
|
34
|
+
# read_timeout: 5
|
35
|
+
# )
|
36
|
+
def initialize(url:, username: nil, password: nil, open_timeout: 5, read_timeout: 10)
|
23
37
|
raise ArgumentError, 'Kea API URL cannot be nil or empty' if url.to_s.empty?
|
24
38
|
|
25
39
|
@uri = URI.parse(url)
|
@@ -28,6 +42,8 @@ module Proxy
|
|
28
42
|
|
29
43
|
raise ArgumentError, "Invalid Kea API URL: '#{url}' is missing a host" unless @uri.host
|
30
44
|
|
45
|
+
@username = username
|
46
|
+
@password = password
|
31
47
|
@open_timeout = open_timeout
|
32
48
|
@read_timeout = read_timeout
|
33
49
|
logger.info "Initializing Kea API client for URL: #{@uri} with timeouts (open: #{@open_timeout}s, read: #{@read_timeout}s)"
|
@@ -39,9 +55,30 @@ module Proxy
|
|
39
55
|
# @param service [String] The Kea service to target (e.g. 'dhcp4').
|
40
56
|
# @param command [String] The command to execute (e.g. 'config-get', 'reservation-add').
|
41
57
|
# @param arguments [Hash] A hash of arguments required by the command. Defaults to an empty hash.
|
42
|
-
#
|
43
58
|
# @return [Hash] The 'arguments' hash from the Kea API response on success.
|
44
59
|
# @raise [Proxy::DHCP::Error] if the API returns an error or if there's a communication issue.
|
60
|
+
# This can be caused by underlying errors like `Net::ReadTimeout`, `Net::OpenTimeout`,
|
61
|
+
# `Errno::ECONNREFUSED`, or `JSON::ParserError`.
|
62
|
+
#
|
63
|
+
# @example Get the current DHCPv4 configuration
|
64
|
+
# client = Proxy::DHCP::KeaApi::Client.new(url: 'http://localhost:8000')
|
65
|
+
# config_response = client.post_command('dhcp4', 'config-get')
|
66
|
+
# # => {"Dhcp4"=>{"subnet4"=>[{"id"=>1, "subnet"=>"192.168.1.0/24", ...}]}}
|
67
|
+
#
|
68
|
+
# @example Add a DHCPv4 reservation
|
69
|
+
# client = Proxy::DHCP::KeaApi::Client.new(url: 'http://localhost:8000')
|
70
|
+
# add_response = client.post_command('dhcp4', 'reservation-add', {
|
71
|
+
# reservation: {
|
72
|
+
# 'subnet-id': 1,
|
73
|
+
# 'ip-address': '192.168.1.100',
|
74
|
+
# 'hw-address': '00:11:22:33:44:55',
|
75
|
+
# hostname: 'my-new-host'
|
76
|
+
# }
|
77
|
+
# })
|
78
|
+
# # => {"text"=>"Reservation added successfully."}
|
79
|
+
#
|
80
|
+
# @see https://kea.readthedocs.io/en/latest/api.html General Kea Management API documentation.
|
81
|
+
# @see https://kea.readthedocs.io/en/latest/api.html#ref-reservation-add For the `reservation-add` command.
|
45
82
|
def post_command(service, command, arguments = {})
|
46
83
|
header = { 'Content-Type' => 'application/json' }
|
47
84
|
payload = {
|
@@ -60,16 +97,16 @@ module Proxy
|
|
60
97
|
http.read_timeout = @read_timeout
|
61
98
|
request = Net::HTTP::Post.new(@uri.request_uri, header)
|
62
99
|
request.body = payload.to_json
|
100
|
+
request.basic_auth(@username, @password) if @username && @password
|
63
101
|
|
64
102
|
logger.debug "Sending command to Kea: #{payload.inspect}"
|
65
103
|
response = http.request(request)
|
66
104
|
|
67
105
|
handle_response(response, command)
|
68
|
-
# This rescue block catches
|
69
|
-
#
|
70
|
-
|
71
|
-
|
72
|
-
logger.error "Failed to send command to Kea API: #{e.message}"
|
106
|
+
# This rescue block catches specific, expected network and parsing errors,
|
107
|
+
# wrapping them in a Foreman-specific error type for consistent handling.
|
108
|
+
rescue Net::ReadTimeout, Net::OpenTimeout, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, JSON::ParserError => e
|
109
|
+
logger.error "Failed to send command to Kea API: #{e.class.name} - #{e.message}"
|
73
110
|
raise Proxy::DHCP::Error, "Kea API communication error: #{e.message}"
|
74
111
|
end
|
75
112
|
|
@@ -79,9 +116,10 @@ module Proxy
|
|
79
116
|
#
|
80
117
|
# @param response [Net::HTTPResponse] The raw response object from the HTTP request.
|
81
118
|
# @param command [String] The original command that was sent, used for context-specific handling.
|
82
|
-
#
|
83
119
|
# @return [Hash] The 'arguments' hash from the response on success.
|
84
|
-
# @raise [Proxy::DHCP::Error] if the response indicates a failure or is
|
120
|
+
# @raise [Proxy::DHCP::Error] if the response indicates a failure, is malformed, or is empty.
|
121
|
+
# @raise [JSON::ParserError] if the response body is not valid JSON.
|
122
|
+
# @private
|
85
123
|
def handle_response(response, command)
|
86
124
|
body = JSON.parse(response.body)
|
87
125
|
logger.debug "Received response from Kea: #{body.inspect}"
|
@@ -91,7 +129,7 @@ module Proxy
|
|
91
129
|
|
92
130
|
# If the response is successful, return its arguments. Otherwise, raise an error.
|
93
131
|
if response_successful?(result, command)
|
94
|
-
# Provide a fallback of '{}' to prevent returning nil if 'arguments' key is missing.
|
132
|
+
# Provide a fallback of '{}' to prevent returning nil if the 'arguments' key is missing.
|
95
133
|
result['arguments'] || {}
|
96
134
|
else
|
97
135
|
error_message = result['text'] || 'Unknown error from Kea API'
|
@@ -103,15 +141,16 @@ module Proxy
|
|
103
141
|
#
|
104
142
|
# @param result [Hash] The parsed result hash from the Kea response body.
|
105
143
|
# @param command [String] The original command sent, needed for special case handling.
|
106
|
-
#
|
107
144
|
# @return [Boolean] `true` if the response is considered a success, `false` otherwise.
|
145
|
+
#
|
146
|
+
# @see https://kea.readthedocs.io/en/stable/api.html For documentation on Kea API result codes.
|
147
|
+
# @private
|
108
148
|
def response_successful?(result, command)
|
109
149
|
# Universal success is result code 0.
|
110
150
|
return true if result['result'].zero?
|
111
|
-
# Special case: 'lease4-get-all' is successful even with result code 3 (no leases found).
|
112
|
-
return true if command == 'lease4-get-all' && result['result'] == 3
|
113
151
|
|
114
|
-
|
152
|
+
# Special case: 'lease4-get-all' is successful even with result code 3 (no leases found).
|
153
|
+
command == 'lease4-get-all' && result['result'] == 3
|
115
154
|
end
|
116
155
|
end
|
117
156
|
end
|
@@ -38,9 +38,12 @@ module Proxy
|
|
38
38
|
|
39
39
|
# The custom client for communicating with the Kea API. This is registered as a singleton
|
40
40
|
# so that a single, persistent HTTP connection pool is used for all API calls.
|
41
|
+
# @see Proxy::DHCP::KeaApi::Client#initialize
|
41
42
|
container.singleton_dependency :kea_client, (lambda do
|
42
43
|
::Proxy::DHCP::KeaApi::Client.new(
|
43
44
|
url: settings[:kea_api_url],
|
45
|
+
username: settings[:kea_api_username],
|
46
|
+
password: settings[:kea_api_password],
|
44
47
|
open_timeout: settings[:open_timeout],
|
45
48
|
read_timeout: settings[:read_timeout]
|
46
49
|
)
|
@@ -50,6 +53,7 @@ module Proxy
|
|
50
53
|
# This is a singleton because we want one central, authoritative cache that all
|
51
54
|
# requests can share. It depends on the Kea client to fetch data and the memory
|
52
55
|
# stores to cache it.
|
56
|
+
# @see Proxy::DHCP::KeaApi::SubnetService#initialize
|
53
57
|
container.singleton_dependency :subnet_service, (lambda do
|
54
58
|
memory_store = container.get_dependency(:memory_store)
|
55
59
|
::Proxy::DHCP::KeaApi::SubnetService.new(
|
@@ -65,6 +69,7 @@ module Proxy
|
|
65
69
|
# The main provider class that ties everything together. This is the entry point
|
66
70
|
# for handling DHCP requests from Foreman. It depends on the subnet service,
|
67
71
|
# the API client, and the IP blacklist service to do its job.
|
72
|
+
# @see Proxy::DHCP::KeaApi::Provider#initialize
|
68
73
|
container.dependency :dhcp_provider, (lambda do
|
69
74
|
::Proxy::DHCP::KeaApi::Provider.new(
|
70
75
|
container.get_dependency(:subnet_service),
|
@@ -1,10 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# The top-level namespace for the Foreman Smart Proxy application.
|
4
|
+
# All core Smart Proxy modules and plugins are defined within this namespace.
|
3
5
|
module Proxy
|
6
|
+
# The namespace for DHCP-related functionality within the Foreman Smart Proxy.
|
7
|
+
# All DHCP providers and their associated services are defined here.
|
4
8
|
module DHCP
|
5
9
|
# The top-level namespace for this DHCP provider. All classes, modules,
|
6
10
|
# and services related to the Kea API integration will be defined within
|
7
11
|
# this KeaApi module to prevent naming conflicts with other plugins.
|
12
|
+
# @see Proxy::DHCP::KeaApi::Client
|
13
|
+
# @see Proxy::DHCP::KeaApi::SubnetService
|
8
14
|
module KeaApi; end
|
9
15
|
end
|
10
16
|
end
|
@@ -14,11 +14,25 @@ Gem::Specification.new do |s|
|
|
14
14
|
s.description = 'Provides DHCP management for Foreman via the ISC Kea API, requiring the host_cmds and lease_cmds hooks.'
|
15
15
|
s.license = 'GPL-3.0-only'
|
16
16
|
s.required_ruby_version = '>= 3.0', '< 4'
|
17
|
+
s.metadata = {
|
18
|
+
'bug_tracker_uri' => 'https://gitlab.surrey.ac.uk/sm0049/smart-proxy-dhcp-kea-api/-/issues',
|
19
|
+
'homepage_uri' => 'https://gitlab.surrey.ac.uk/sm0049/smart-proxy-dhcp-kea-api',
|
20
|
+
'rubygems_mfa_required' => 'true'
|
21
|
+
}
|
17
22
|
|
18
23
|
s.files = Dir.glob('{lib}/**/*', File::FNM_DOTMATCH).reject { |f| File.directory?(f) }
|
19
24
|
s.files += %w[LICENSE README.md smart_proxy_dhcp_kea_api.gemspec]
|
20
25
|
|
21
26
|
s.require_paths = ['lib']
|
22
27
|
|
23
|
-
s.
|
28
|
+
s.add_development_dependency "bundler"
|
29
|
+
s.add_development_dependency "concurrent-ruby"
|
30
|
+
s.add_development_dependency "rack-test"
|
31
|
+
s.add_development_dependency "rake"
|
32
|
+
s.add_development_dependency "rspec"
|
33
|
+
s.add_development_dependency "rubocop"
|
34
|
+
s.add_development_dependency "rubocop-rspec"
|
35
|
+
s.add_development_dependency "yard"
|
36
|
+
s.add_development_dependency "yard-rspec"
|
37
|
+
s.add_development_dependency "webmock"
|
24
38
|
end
|
metadata
CHANGED
@@ -1,15 +1,155 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_proxy_dhcp_kea_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam McCarthy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-07-
|
12
|
-
dependencies:
|
11
|
+
date: 2025-07-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: concurrent-ruby
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rack-test
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop-rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: yard
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: yard-rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: webmock
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
13
153
|
description: Provides DHCP management for Foreman via the ISC Kea API, requiring the
|
14
154
|
host_cmds and lease_cmds hooks.
|
15
155
|
email:
|
@@ -19,6 +159,7 @@ extra_rdoc_files: []
|
|
19
159
|
files:
|
20
160
|
- LICENSE
|
21
161
|
- README.md
|
162
|
+
- lib/.DS_Store
|
22
163
|
- lib/smart_proxy_dhcp_kea_api.rb
|
23
164
|
- lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_main.rb
|
24
165
|
- lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_plugin.rb
|
@@ -31,6 +172,8 @@ homepage: https://gitlab.surrey.ac.uk/sm0049/smart-proxy-dhcp-kea-api
|
|
31
172
|
licenses:
|
32
173
|
- GPL-3.0-only
|
33
174
|
metadata:
|
175
|
+
bug_tracker_uri: https://gitlab.surrey.ac.uk/sm0049/smart-proxy-dhcp-kea-api/-/issues
|
176
|
+
homepage_uri: https://gitlab.surrey.ac.uk/sm0049/smart-proxy-dhcp-kea-api
|
34
177
|
rubygems_mfa_required: 'true'
|
35
178
|
post_install_message:
|
36
179
|
rdoc_options: []
|