Class: Proxy::DHCP::KeaApi::SubnetService

Inherits:
SubnetService
  • Object
show all
Includes:
Log
Defined in:
lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_subnet_service.rb

Overview

Manages the in-memory cache of DHCP data for the Kea provider.

This class is responsible for fetching all subnet, reservation, and lease information from the Kea API. It inherits from the core ‘DHCP::SubnetService` to get the underlying data structures (e.g. hashes for leases and hosts) and caching logic. Its primary public method, `load!`, orchestrates the population of this cache. It also maintains a mapping of Foreman subnet networks to their internal Kea API subnet IDs.

Defined Under Namespace

Classes: Staging

Constant Summary collapse

OPTION_MAP =

Maps Kea option-data names to their Foreman option keys and whether they are lists.

{
  'routers' => { key: :routers, list: true },
  'domain-name-servers' => { key: :dns_servers, list: true },
  'domain-name' => { key: :domain_name, list: false },
  'ntp-servers' => { key: :ntp_servers, list: true }
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(client, leases_by_ip, leases_by_mac, reservations_by_ip, reservations_by_mac, reservations_by_name, cache_ttl: 60, managed_subnets: nil) ⇒ void

Initialises the SubnetService.

rubocop:disable Metrics/ParameterLists

Parameters:

  • client (Proxy::DHCP::KeaApi::Client)

    The client for communicating with the Kea API.

  • leases_by_ip (Proxy::MemoryStore)

    A memory store for leases, passed to the parent class.

  • leases_by_mac (Proxy::MemoryStore)

    A memory store for leases, passed to the parent class.

  • reservations_by_ip (Proxy::MemoryStore)

    A memory store for reservations, passed to the parent class.

  • reservations_by_mac (Proxy::MemoryStore)

    A memory store for reservations, passed to the parent class.

  • reservations_by_name (Proxy::MemoryStore)

    A memory store for reservations, passed to the parent class.

  • cache_ttl (Integer) (defaults to: 60)

    Number of seconds before the cache is considered stale (default: 60).

  • managed_subnets (Array<String>, nil) (defaults to: nil)

    List of CIDR networks to manage. Nil means manage all.



65
66
67
68
69
70
71
72
73
74
75
# File 'lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_subnet_service.rb', line 65

def initialize(client, leases_by_ip, leases_by_mac, reservations_by_ip, reservations_by_mac, reservations_by_name,
               cache_ttl: 60, managed_subnets: nil)
  @client = client
  @kea_id_map = {}
  @subnet_options = {}
  @cache_ttl = cache_ttl
  @loaded_at = nil
  @reload_mutex = Mutex.new
  @managed_subnets = parse_managed_subnets(managed_subnets)
  super(leases_by_ip, leases_by_mac, reservations_by_ip, reservations_by_mac, reservations_by_name)
end

Instance Attribute Details

#kea_id_mapObject (readonly)

A hash mapping a subnet network address (e.g. “192.168.1.0”) to its internal Kea API integer ID (e.g. 1). This is crucial for making API calls that require a ‘subnet-id`.



47
48
49
# File 'lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_subnet_service.rb', line 47

def kea_id_map
  @kea_id_map
end

#subnet_optionsObject (readonly)

A hash mapping a subnet network address to its DHCP options hash. Used by the Provider to serve subnet-level options back to Foreman.



51
52
53
# File 'lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_subnet_service.rb', line 51

def subnet_options
  @subnet_options
end

Instance Method Details

#all_hosts(subnet_address = nil) ⇒ Array<Proxy::DHCP::Reservation>

Returns all host reservations, refreshing the cache if stale.

Parameters:

  • subnet_address (String, nil) (defaults to: nil)

    Optional subnet to filter by.

Returns:

  • (Array<Proxy::DHCP::Reservation>)

    All cached reservations.



116
117
118
119
# File 'lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_subnet_service.rb', line 116

def all_hosts(subnet_address = nil)
  reload_if_stale!
  super
end

#all_leases(subnet_address = nil) ⇒ Array<Proxy::DHCP::Lease>

Returns all leases, refreshing the cache if stale.

Parameters:

  • subnet_address (String, nil) (defaults to: nil)

    Optional subnet to filter by.

Returns:

  • (Array<Proxy::DHCP::Lease>)

    All cached leases.



125
126
127
128
# File 'lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_subnet_service.rb', line 125

def all_leases(subnet_address = nil)
  reload_if_stale!
  super
end

#all_subnetsArray<Proxy::DHCP::Subnet>

Returns all subnets, refreshing the cache if stale.

Returns:

  • (Array<Proxy::DHCP::Subnet>)

    All cached subnets.



107
108
109
110
# File 'lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_subnet_service.rb', line 107

def all_subnets
  reload_if_stale!
  super
end

#load!true

The main entry point for loading all DHCP data from the Kea server.

All Kea API calls populate a fresh, off-to-the-side set of stores (‘staging`); only once every fetch has succeeded are the new stores swapped into the live cache under the parent’s monitor. This keeps the slow network fetch off the live cache so concurrent readers never see a half-populated state, and leaves the previous cache intact if the fetch fails partway through.

rubocop:disable Naming/PredicateMethod

Returns:

  • (true)

    on success.

Raises:

  • (Proxy::DHCP::Error)

    if any part of the loading process fails.

See Also:



92
93
94
95
96
97
98
99
100
101
# File 'lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_subnet_service.rb', line 92

def load!
  staging = Staging.new

  load_subnets_and_reservations_from_kea(staging)
  load_reservations_from_database(staging)
  load_leases_from_kea(staging)

  commit(staging)
  true
end

#load_leases_from_kea(staging) ⇒ void

This method returns an undefined value.

Fetches all active leases from the Kea API for the subnets currently in the cache.

Parameters:

  • staging (Staging)

    The buffer to populate with fetched data.

Raises:

  • (Proxy::DHCP::Error)

    if the API call fails.

See Also:



187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_subnet_service.rb', line 187

def load_leases_from_kea(staging)
  return if staging.kea_id_map.empty?

  response = @client.post_command('dhcp4', 'lease4-get-all', { subnets: staging.kea_id_map.values })
  return unless response && response['leases']

  response['leases'].each do |lease|
    process_lease(lease, staging)
  end
rescue Proxy::DHCP::Error => e
  logger.error "Failed to load all leases from Kea: #{e.message}"
  raise
end

#load_reservations_from_database(staging) ⇒ void

This method returns an undefined value.

Fetches dynamically added reservations from Kea’s hosts-database via ‘reservation-get-all`. These are not included in `config-get` which only returns static reservations from the config file.

Parameters:

  • staging (Staging)

    The buffer to populate with fetched data.



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_subnet_service.rb', line 160

def load_reservations_from_database(staging)
  return if staging.kea_id_map.empty?

  staging.kea_id_map.each do |network, subnet_id|
    response = @client.post_command('dhcp4', 'reservation-get-all', { 'subnet-id': subnet_id })
    hosts = response&.[]('hosts')
    next unless hosts

    subnet_obj = staging.service.find_subnet(network)
    next unless subnet_obj

    hosts.each do |res_data|
      next if staging.service.find_host_by_mac(network, res_data['hw-address'])

      process_reservation(res_data, subnet_obj, staging)
    end
  end
rescue Proxy::DHCP::Error => e
  logger.debug "reservation-get-all not available or failed: #{e.message}"
end

#load_subnets_and_reservations_from_kea(staging) ⇒ void

This method returns an undefined value.

Fetches all subnets and their associated reservations from the Kea API. This single ‘config-get` call is the most efficient way to get all static configuration.

Parameters:

  • staging (Staging)

    The buffer to populate with fetched data.

Raises:

  • (Proxy::DHCP::Error)

    if the API call fails.

  • (IPAddr::InvalidAddressError)

    if a subnet address from Kea is invalid.

See Also:



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/smart_proxy_dhcp_kea_api/dhcp_kea_api_subnet_service.rb', line 138

def load_subnets_and_reservations_from_kea(staging)
  config = @client.post_command('dhcp4', 'config-get')
  subnets_data = config&.dig('Dhcp4', 'subnet4')
  return unless subnets_data

  subnets_data.each do |subnet_data|
    process_subnet(subnet_data, staging)
  end
rescue Proxy::DHCP::Error => e
  logger.error "Failed to load subnets and reservations from Kea: #{e.message}"
  raise
rescue IPAddr::InvalidAddressError => e
  logger.error "Failed to parse subnet from Kea, invalid address found: #{e.message}"
  raise
end