smart_proxy_dhcp_dnsmasq 0.7 → 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/bundler.d/dhcp_dnsmasq.rb +2 -0
- data/lib/smart_proxy_dhcp_dnsmasq.rb +2 -0
- data/lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_main.rb +28 -13
- data/lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_plugin.rb +10 -6
- data/lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_subnet_service.rb +74 -25
- data/lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_version.rb +3 -1
- data/lib/smart_proxy_dhcp_dnsmasq/{dhcp_dnsmasq_configuration.rb → plugin_configuration.rb} +9 -3
- data/test/dhcp_dnsmasq_default_settings_test.rb +3 -1
- data/test/dhcp_dnsmasq_production_wiring_test.rb +10 -6
- data/test/dhcp_dnsmasq_record_handling_test.rb +52 -3
- data/test/dhcp_dnsmasq_subnet_service_test.rb +41 -2
- data/test/fixtures/config/dhcp.leases +15 -0
- data/test/fixtures/config/dhcp/dhcphosts/22_12_1d_6f_46_25.conf +1 -0
- data/test/fixtures/config/dhcp/dhcpopts.conf +2 -0
- data/test/fixtures/config/dnsmasq.conf +32 -0
- data/test/fixtures/dhcphosts/01_02_03_04_05_06.conf +1 -0
- data/test/fixtures/dhcpopts.conf +14 -0
- data/test/test_helper.rb +8 -0
- metadata +19 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91ee0c71835884e0210ee0ec7b153e129786423536ca71bc67696019828e09df
|
4
|
+
data.tar.gz: 0b919d07a4f75212c0fea23a8852d2585930d6e7f693ff8e854dd11badce18eb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89feab88442bbc7c0b2c53f8a98879b036d8c903bff62e838a3318f1efe69339c61f891e301a2fd6a03d046f813aa969ea2549b83bb0fd6b2f950d76ffde98d2
|
7
|
+
data.tar.gz: a87a85b1252ac9063677967ec79d6df39c1e6c62865bfa3e25781011c4d13471694aa9166a81e9f209c2fbc2b59a8c66a3ff499e6afa7c499fde0d709a12cddb
|
data/README.md
CHANGED
@@ -8,7 +8,7 @@ This plugin adds a new DHCP provider for managing records in dnsmasq.
|
|
8
8
|
See [How_to_Install_a_Smart-Proxy_Plugin](http://projects.theforeman.org/projects/foreman/wiki/How_to_Install_a_Smart-Proxy_Plugin)
|
9
9
|
for how to install Smart Proxy plugins
|
10
10
|
|
11
|
-
This plugin is compatible with Smart Proxy 1.
|
11
|
+
This plugin is compatible with Smart Proxy 1.17 or higher.
|
12
12
|
|
13
13
|
You need to add two lines to your dnsmasq configuration to use this plugin;
|
14
14
|
```
|
data/bundler.d/dhcp_dnsmasq.rb
CHANGED
@@ -1,23 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'fileutils'
|
2
4
|
require 'tempfile'
|
3
5
|
require 'dhcp_common/server'
|
4
6
|
|
5
7
|
module Proxy::DHCP::Dnsmasq
|
6
|
-
class
|
8
|
+
class Provider < ::Proxy::DHCP::Server
|
7
9
|
attr_reader :config_dir, :reload_cmd, :subnet_service
|
8
10
|
|
9
|
-
def initialize(target_dir, reload_cmd, subnet_service)
|
11
|
+
def initialize(target_dir, reload_cmd, subnet_service, free_ips)
|
10
12
|
@config_dir = target_dir
|
11
13
|
@reload_cmd = reload_cmd
|
12
14
|
@subnet_service = subnet_service
|
13
15
|
@optsfile_content = []
|
14
16
|
|
15
17
|
Dir.mkdir @config_dir unless Dir.exist? @config_dir
|
16
|
-
cleanup_optsfile if
|
18
|
+
cleanup_optsfile if subnet_service.cleanup_time?
|
17
19
|
|
18
20
|
subnet_service.load!
|
19
21
|
|
20
|
-
super('localhost', nil, subnet_service)
|
22
|
+
super('localhost', nil, subnet_service, free_ips)
|
21
23
|
end
|
22
24
|
|
23
25
|
def add_record(options = {})
|
@@ -64,22 +66,32 @@ module Proxy::DHCP::Dnsmasq
|
|
64
66
|
|
65
67
|
private
|
66
68
|
|
69
|
+
def optsfile_path
|
70
|
+
@optsfile_path ||= File.join(@config_dir, 'dhcpopts.conf').freeze
|
71
|
+
end
|
72
|
+
|
67
73
|
def try_reload_cmd
|
68
74
|
logger.debug 'Reloading DHCP configuration...'
|
69
|
-
raise Proxy::DHCP::Error, 'Failed to reload configuration'
|
70
|
-
unless system(@reload_cmd)
|
75
|
+
raise Proxy::DHCP::Error, 'Failed to reload configuration' unless system(@reload_cmd)
|
71
76
|
end
|
72
77
|
|
73
78
|
def optsfile_content
|
74
|
-
path =
|
79
|
+
path = optsfile_path
|
80
|
+
|
81
|
+
return @optsfile_content unless @optsfile_content.empty?
|
82
|
+
return (@optsfile_content ||= []) unless File.exist?(path)
|
75
83
|
|
76
|
-
@optsfile_content = File.open(path)
|
77
|
-
|
78
|
-
|
84
|
+
@optsfile_content = File.open(path) do |file|
|
85
|
+
file.readlines
|
86
|
+
.map(&:chomp)
|
87
|
+
.reject(&:empty?)
|
88
|
+
.sort
|
89
|
+
.uniq.compact
|
90
|
+
end
|
79
91
|
end
|
80
92
|
|
81
93
|
def append_optsfile(line)
|
82
|
-
path =
|
94
|
+
path = optsfile_path
|
83
95
|
logger.debug "Appending #{line} to dhcpopts.conf"
|
84
96
|
|
85
97
|
optsfile_content << line
|
@@ -87,9 +99,11 @@ module Proxy::DHCP::Dnsmasq
|
|
87
99
|
end
|
88
100
|
|
89
101
|
def cleanup_optsfile
|
102
|
+
subnet_service.last_cleanup = Time.now
|
103
|
+
|
90
104
|
used_tags = []
|
91
105
|
Dir.glob(File.join(@config_dir, 'dhcphosts', '*.conf')) do |file|
|
92
|
-
File.read(file).scan(/set:(.*?),/m) { |tag| used_tags
|
106
|
+
File.read(file).scan(/set:(.*?),/m) { |tag| used_tags += tag }
|
93
107
|
end
|
94
108
|
used_tags = used_tags.sort.uniq
|
95
109
|
|
@@ -97,7 +111,8 @@ module Proxy::DHCP::Dnsmasq
|
|
97
111
|
tag = line[/tag:(.*?),/, 1]
|
98
112
|
used_tags.include?(tag)
|
99
113
|
end
|
100
|
-
|
114
|
+
|
115
|
+
File.write(optsfile_path, optsfile_content.join("\n") + "\n")
|
101
116
|
end
|
102
117
|
|
103
118
|
def sanitize_string(string)
|
@@ -1,19 +1,23 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_version'
|
4
|
+
require 'smart_proxy_dhcp_dnsmasq/plugin_configuration'
|
3
5
|
|
4
6
|
module Proxy::DHCP::Dnsmasq
|
5
7
|
class Plugin < ::Proxy::Provider
|
6
8
|
plugin :dhcp_dnsmasq, ::Proxy::DHCP::Dnsmasq::VERSION
|
7
9
|
|
8
|
-
requires :dhcp, '>= 1.
|
9
|
-
default_settings :
|
10
|
-
:
|
11
|
-
:
|
12
|
-
:
|
10
|
+
requires :dhcp, '>= 1.17'
|
11
|
+
default_settings config: '/etc/dnsmasq.conf',
|
12
|
+
target_dir: '/var/lib/foreman-proxy/dhcp/',
|
13
|
+
lease_file: '/var/lib/dnsmasq/dhcp.leases',
|
14
|
+
reload_cmd: 'systemctl reload dnsmasq'
|
13
15
|
|
14
16
|
validate_readable :lease_file
|
15
17
|
|
16
18
|
load_classes ::Proxy::DHCP::Dnsmasq::PluginConfiguration
|
17
19
|
load_dependency_injection_wirings ::Proxy::DHCP::Dnsmasq::PluginConfiguration
|
20
|
+
|
21
|
+
start_services :unused_ips, :subnet_service
|
18
22
|
end
|
19
23
|
end
|
@@ -1,5 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'ipaddr'
|
2
|
-
|
4
|
+
require 'pathname'
|
5
|
+
require 'rb-inotify'
|
3
6
|
require 'dhcp_common/dhcp_common'
|
4
7
|
require 'dhcp_common/subnet_service'
|
5
8
|
|
@@ -7,6 +10,9 @@ module Proxy::DHCP::Dnsmasq
|
|
7
10
|
class SubnetService < ::Proxy::DHCP::SubnetService
|
8
11
|
include Proxy::Log
|
9
12
|
|
13
|
+
OPTSFILE_CLEANUP_INTERVAL = 15 * 60 # 15 minutes
|
14
|
+
|
15
|
+
attr_accessor :last_cleanup
|
10
16
|
attr_reader :config_dir, :lease_file
|
11
17
|
|
12
18
|
def initialize(config, target_dir, lease_file, leases_by_ip, leases_by_mac, reservations_by_ip, reservations_by_mac, reservations_by_name)
|
@@ -14,46 +20,79 @@ module Proxy::DHCP::Dnsmasq
|
|
14
20
|
@target_dir = target_dir
|
15
21
|
@lease_file = lease_file
|
16
22
|
|
23
|
+
# Must be an absolute path for the inotify watch to succeed at the moment
|
24
|
+
@lease_file = File.expand_path(@lease_file) unless (Pathname.new @lease_file).absolute?
|
25
|
+
|
17
26
|
super(leases_by_ip, leases_by_mac, reservations_by_ip, reservations_by_mac, reservations_by_name)
|
18
27
|
end
|
19
28
|
|
20
29
|
def load!
|
30
|
+
# TODO: Refresh data if outdated
|
31
|
+
return true if subnets.any?
|
32
|
+
|
21
33
|
add_subnet(parse_config_for_subnet)
|
22
34
|
load_subnet_data
|
23
|
-
#add_watch # TODO
|
24
35
|
|
25
36
|
true
|
26
37
|
end
|
27
38
|
|
28
|
-
def
|
29
|
-
|
39
|
+
def watch_leases
|
40
|
+
logger.info "Watching leases in file #{lease_file}"
|
30
41
|
@inotify = INotify::Notifier.new
|
31
42
|
@inotify.watch(File.dirname(lease_file), :modify, :moved_to) do |ev|
|
32
43
|
next unless ev.absolute_name == lease_file
|
33
44
|
|
34
45
|
leases = load_leases
|
35
46
|
|
36
|
-
# FIXME: Proper method for this
|
37
47
|
m.synchronize do
|
38
48
|
leases_by_ip.clear
|
39
49
|
leases_by_mac.clear
|
50
|
+
|
51
|
+
leases.each do |record|
|
52
|
+
subnet_address = record.subnet_address
|
53
|
+
|
54
|
+
leases_by_ip[subnet_address, record.ip] = record
|
55
|
+
leases_by_mac[subnet_address, record.mac] = record
|
56
|
+
end
|
40
57
|
end
|
41
|
-
leases.each { |l| add_lease(l.subnet_address, l) }
|
42
58
|
end
|
59
|
+
|
60
|
+
@inotify.run
|
61
|
+
rescue INotify::QueueOverflowError => e
|
62
|
+
logger.warn "Queue overflow occured when monitoring #{lease_file}, restarting monitoring", e
|
63
|
+
sleep 60
|
64
|
+
retry
|
65
|
+
rescue StandardError => e
|
66
|
+
logger.error "Error occured when monitoring #{lease_file}", e
|
67
|
+
end
|
68
|
+
|
69
|
+
def start
|
70
|
+
Thread.new { watch_leases }
|
71
|
+
end
|
72
|
+
|
73
|
+
def stop
|
74
|
+
@inotify&.stop
|
75
|
+
end
|
76
|
+
|
77
|
+
def cleanup_time?
|
78
|
+
Time.now - (@last_cleanup ||= Time.now) > OPTSFILE_CLEANUP_INTERVAL
|
43
79
|
end
|
44
80
|
|
45
81
|
def parse_config_for_subnet
|
46
82
|
configuration = { options: {} }
|
47
83
|
files = []
|
48
84
|
@config_paths.each do |path|
|
49
|
-
|
50
|
-
|
85
|
+
if File.directory? path
|
86
|
+
files += Dir[File.join(path, '*')]
|
87
|
+
else
|
88
|
+
files << path
|
89
|
+
end
|
51
90
|
end
|
52
91
|
|
53
92
|
logger.debug "Starting parse of DHCP subnets from #{files}"
|
54
93
|
files.each do |file|
|
55
94
|
logger.debug " Parsing #{file}..."
|
56
|
-
open(file, 'r').each_line do |line|
|
95
|
+
File.open(file, 'r').each_line do |line|
|
57
96
|
line.strip!
|
58
97
|
next if line.empty? || line.start_with?('#') || !line.include?('=')
|
59
98
|
|
@@ -83,12 +122,13 @@ module Proxy::DHCP::Dnsmasq
|
|
83
122
|
configuration.merge! \
|
84
123
|
address: IPAddr.new("#{range_from}/#{mask}").to_s,
|
85
124
|
mask: mask,
|
86
|
-
range: [range_from, range_to],
|
87
125
|
ttl: ttl
|
126
|
+
|
127
|
+
configuration[:options][:range] = [range_from, range_to]
|
88
128
|
when 'dhcp-option'
|
89
129
|
data = value.split(',')
|
90
130
|
|
91
|
-
data.shift until data.empty? || /\A\d+\z/
|
131
|
+
data.shift until data.empty? || /\A\d+\z/ =~ data.first
|
92
132
|
next if data.empty?
|
93
133
|
|
94
134
|
code = data.shift.to_i
|
@@ -110,15 +150,19 @@ module Proxy::DHCP::Dnsmasq
|
|
110
150
|
def parse_config_for_dhcp_reservations
|
111
151
|
to_ret = {}
|
112
152
|
files = []
|
153
|
+
|
113
154
|
@config_paths.each do |path|
|
114
|
-
|
115
|
-
|
155
|
+
if File.directory? path
|
156
|
+
files += Dir[File.join(path, '*')]
|
157
|
+
else
|
158
|
+
files << path
|
159
|
+
end
|
116
160
|
end
|
117
161
|
|
118
162
|
logger.debug "Starting parse of DHCP reservations from #{files}"
|
119
163
|
files.each do |file|
|
120
164
|
logger.debug " Parsing #{file}..."
|
121
|
-
open(file, 'r').each_line do |line|
|
165
|
+
File.open(file, 'r').each_line do |line|
|
122
166
|
line.strip!
|
123
167
|
next if line.empty? || line.start_with?('#') || !line.include?('=')
|
124
168
|
|
@@ -137,7 +181,7 @@ module Proxy::DHCP::Dnsmasq
|
|
137
181
|
hostname,
|
138
182
|
ip,
|
139
183
|
mac,
|
140
|
-
subnet
|
184
|
+
subnet
|
141
185
|
# :source_file => file # TODO: Needs to overload the comparison
|
142
186
|
)
|
143
187
|
when 'dhcp-boot'
|
@@ -150,8 +194,8 @@ module Proxy::DHCP::Dnsmasq
|
|
150
194
|
|
151
195
|
file, server = data
|
152
196
|
|
153
|
-
to_ret[mac].options[:nextServer] =
|
154
|
-
to_ret[mac].options[:filename] =
|
197
|
+
to_ret[mac].options[:nextServer] = server
|
198
|
+
to_ret[mac].options[:filename] = file
|
155
199
|
end
|
156
200
|
end
|
157
201
|
end
|
@@ -161,7 +205,7 @@ module Proxy::DHCP::Dnsmasq
|
|
161
205
|
dhcpopts_path = File.join(@target_dir, 'dhcpopts.conf')
|
162
206
|
logger.debug "Parsing DHCP options from #{dhcpopts_path}"
|
163
207
|
if File.exist? dhcpopts_path
|
164
|
-
open(dhcpopts_path, 'r').each_line do |line|
|
208
|
+
File.open(dhcpopts_path, 'r').each_line do |line|
|
165
209
|
data = line.strip.split(',')
|
166
210
|
next if data.empty? || !data.first.start_with?('tag:')
|
167
211
|
|
@@ -175,14 +219,14 @@ module Proxy::DHCP::Dnsmasq
|
|
175
219
|
logger.debug "Parsing provisioned DHCP reservations from #{@target_dir}"
|
176
220
|
Dir[File.join(@target_dir, 'dhcphosts', '*')].each do |file|
|
177
221
|
logger.debug " Parsing #{file}..."
|
178
|
-
open(file, 'r').each_line do |line|
|
222
|
+
File.open(file, 'r').each_line do |line|
|
179
223
|
data = line.strip.split(',')
|
180
224
|
next if data.empty? || data.first.start_with?('#')
|
181
225
|
|
182
226
|
mac = data.first
|
183
227
|
data.shift
|
184
228
|
|
185
|
-
options = { :
|
229
|
+
options = { deletable: true }
|
186
230
|
while data.first.start_with? 'set:'
|
187
231
|
tag = data.first[4..-1]
|
188
232
|
data.shift
|
@@ -212,7 +256,7 @@ module Proxy::DHCP::Dnsmasq
|
|
212
256
|
def load_subnet_data
|
213
257
|
reservations = parse_config_for_dhcp_reservations
|
214
258
|
reservations.each do |record|
|
215
|
-
if dupe = find_host_by_mac(record.subnet_address, record.mac)
|
259
|
+
if (dupe = find_host_by_mac(record.subnet_address, record.mac))
|
216
260
|
logger.debug "Found duplicate #{dupe} when adding record #{record}, skipping"
|
217
261
|
next
|
218
262
|
end
|
@@ -223,11 +267,11 @@ module Proxy::DHCP::Dnsmasq
|
|
223
267
|
|
224
268
|
leases = load_leases
|
225
269
|
leases.each do |lease|
|
226
|
-
if dupe = find_lease_by_mac(lease.subnet_address, lease.mac)
|
270
|
+
if (dupe = find_lease_by_mac(lease.subnet_address, lease.mac))
|
227
271
|
logger.debug "Found duplicate #{dupe} by MAC when adding lease #{lease}, skipping"
|
228
272
|
next
|
229
273
|
end
|
230
|
-
if dupe = find_lease_by_ip(lease.subnet_address, lease.ip)
|
274
|
+
if (dupe = find_lease_by_ip(lease.subnet_address, lease.ip))
|
231
275
|
logger.debug "Found duplicate #{dupe} by IP when adding lease #{lease}, skipping"
|
232
276
|
next
|
233
277
|
end
|
@@ -239,7 +283,9 @@ module Proxy::DHCP::Dnsmasq
|
|
239
283
|
|
240
284
|
# Expects subnet_service to have subnet data
|
241
285
|
def load_leases
|
242
|
-
|
286
|
+
raise 'No subnets configured' unless subnets.any?
|
287
|
+
|
288
|
+
File.open(@lease_file, 'r').readlines.map do |line|
|
243
289
|
timestamp, mac, ip, _hostname, _client_id = line.split
|
244
290
|
timestamp = timestamp.to_i
|
245
291
|
|
@@ -250,7 +296,10 @@ module Proxy::DHCP::Dnsmasq
|
|
250
296
|
timestamp,
|
251
297
|
'active'
|
252
298
|
)
|
253
|
-
end
|
299
|
+
end.compact
|
300
|
+
rescue StandardError => e
|
301
|
+
logger.error 'Unable to load leases', e
|
302
|
+
[]
|
254
303
|
end
|
255
304
|
end
|
256
305
|
end
|
@@ -1,14 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ::Proxy::DHCP::Dnsmasq
|
2
4
|
class PluginConfiguration
|
3
5
|
def load_classes
|
6
|
+
require 'dhcp_common/free_ips'
|
4
7
|
require 'smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_subnet_service'
|
5
8
|
require 'smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_main'
|
6
9
|
end
|
7
10
|
|
8
11
|
def load_dependency_injection_wirings(container, settings)
|
12
|
+
container.singleton_dependency :unused_ips, -> { ::Proxy::DHCP::FreeIps.new(settings[:blacklist_duration_minutes]) }
|
13
|
+
|
9
14
|
container.dependency :memory_store, ::Proxy::MemoryStore
|
10
15
|
|
11
|
-
container.
|
16
|
+
container.singleton_dependency :subnet_service, (lambda do
|
12
17
|
::Proxy::DHCP::Dnsmasq::SubnetService.new(
|
13
18
|
settings[:config], settings[:target_dir], settings[:lease_file],
|
14
19
|
container.get_dependency(:memory_store),
|
@@ -17,9 +22,10 @@ module ::Proxy::DHCP::Dnsmasq
|
|
17
22
|
)
|
18
23
|
end)
|
19
24
|
container.dependency :dhcp_provider, (lambda do
|
20
|
-
Proxy::DHCP::Dnsmasq::
|
25
|
+
Proxy::DHCP::Dnsmasq::Provider.new(
|
21
26
|
settings[:target_dir],
|
22
|
-
settings[:reload_cmd], container.get_dependency(:subnet_service)
|
27
|
+
settings[:reload_cmd], container.get_dependency(:subnet_service),
|
28
|
+
container.get_dependency(:unused_ips)
|
23
29
|
)
|
24
30
|
end)
|
25
31
|
end
|
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'test_helper'
|
2
|
-
require 'smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_configuration'
|
3
4
|
require 'smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_plugin'
|
5
|
+
require 'smart_proxy_dhcp_dnsmasq/plugin_configuration'
|
4
6
|
|
5
7
|
class DnsDnsmasqDefaultSettingsTest < Test::Unit::TestCase
|
6
8
|
def test_default_settings
|
@@ -1,12 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'test_helper'
|
2
|
-
require '
|
4
|
+
require 'dhcp_common/dhcp_common'
|
5
|
+
require 'dhcp_common/free_ips'
|
3
6
|
require 'smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_main'
|
7
|
+
require 'smart_proxy_dhcp_dnsmasq/plugin_configuration'
|
4
8
|
|
5
9
|
class DHCPDnsmasqProductionWiringTest < Test::Unit::TestCase
|
6
10
|
def setup
|
7
11
|
@container = ::Proxy::DependencyInjection::Container.new
|
8
12
|
@config = ::Proxy::DHCP::Dnsmasq::PluginConfiguration.new
|
9
|
-
Proxy::DHCP::Dnsmasq::
|
13
|
+
Proxy::DHCP::Dnsmasq::Provider.any_instance.stubs(:cleanup_optsfile)
|
10
14
|
end
|
11
15
|
|
12
16
|
def test_dns_provider_initialization_default
|
@@ -15,10 +19,10 @@ class DHCPDnsmasqProductionWiringTest < Test::Unit::TestCase
|
|
15
19
|
|
16
20
|
@config.load_dependency_injection_wirings(
|
17
21
|
@container,
|
18
|
-
:
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
22
|
+
config: '/etc/dnsmasq.conf',
|
23
|
+
target_dir: '/etc/dnsmasq.d/dhcp',
|
24
|
+
lease_file: '/tmp/dhcp.leases',
|
25
|
+
reload_cmd: 'systemctl reload dnsmasq'
|
22
26
|
)
|
23
27
|
|
24
28
|
provider = @container.get_dependency(:dhcp_provider)
|
@@ -1,18 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'test_helper'
|
2
4
|
require 'smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_main'
|
3
5
|
|
4
6
|
class DHCPDnsmasqRecordHandlingTest < Test::Unit::TestCase
|
5
7
|
def setup
|
8
|
+
@free_ips = mock
|
6
9
|
@subnet_service = mock
|
7
10
|
@subnet_service.expects(:load!).returns(true)
|
11
|
+
@subnet_service.expects(:cleanup_time?).returns(false)
|
12
|
+
@subnet_service.stubs(:last_cleanup=)
|
8
13
|
Dir.stubs(:exist?).returns(true)
|
9
|
-
Proxy::
|
14
|
+
Proxy::LogBuffer::Decorator.any_instance.stubs(:add)
|
10
15
|
|
11
|
-
@server = ::Proxy::DHCP::Dnsmasq::
|
12
|
-
@server.instance_eval('@optsfile_content = []')
|
16
|
+
@server = ::Proxy::DHCP::Dnsmasq::Provider.new('/etc/dnsmasq.d/', '/bin/true', @subnet_service, @free_ips)
|
17
|
+
@server.instance_eval('@optsfile_content = []', __FILE__, __LINE__)
|
13
18
|
end
|
14
19
|
|
15
20
|
def test_add_record
|
21
|
+
Proxy::DHCP::Dnsmasq::Provider.any_instance.expects(:cleanup_optsfile).never
|
22
|
+
|
16
23
|
subnet = ::Proxy::DHCP::Subnet.new('10.0.0.0', '255.0.0.0')
|
17
24
|
@subnet_service.stubs(:find_subnet).with('10.0.0.0').returns(subnet)
|
18
25
|
@subnet_service.stubs(:find_hosts_by_ip).returns(nil)
|
@@ -52,4 +59,46 @@ class DHCPDnsmasqRecordHandlingTest < Test::Unit::TestCase
|
|
52
59
|
|
53
60
|
@server.del_record(host)
|
54
61
|
end
|
62
|
+
|
63
|
+
def test_optsfile_reading
|
64
|
+
@server.stubs(:optsfile_path).returns 'test/fixtures/dhcpopts.conf'
|
65
|
+
|
66
|
+
assert_equal %w[
|
67
|
+
tag:bf_pxelinux_0,option:bootfile-name,pxelinux.0
|
68
|
+
tag:bf_pxelinux_1,option:bootfile-name,pxelinux.1
|
69
|
+
tag:bf_pxelinux_2,option:bootfile-name,pxelinux.2
|
70
|
+
tag:bf_pxelinux_3,option:bootfile-name,pxelinux.3
|
71
|
+
tag:bf_pxelinux_4,option:bootfile-name,pxelinux.4
|
72
|
+
tag:ns_tftp_server,option:tftp-server,tftp.server
|
73
|
+
tag:ns_tftp_server_local,option:tftp-server,tftp.server.local
|
74
|
+
], @server.send(:optsfile_content)
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_optsfile_append
|
78
|
+
@server.stubs(:optsfile_path).returns '/dev/null'
|
79
|
+
@server.instance_variable_set :@optsfile_content, %w[tag:bf_pxelinux_0,option:bootfile-name,pxelinux.0]
|
80
|
+
|
81
|
+
File.expects(:write).with('/dev/null', "tag:bf_pxelinux_0,option:bootfile-name,pxelinux.0\ntag:ns_tftp_server_local,option:tftp-server.local\n")
|
82
|
+
|
83
|
+
@server.send :append_optsfile, 'tag:ns_tftp_server_local,option:tftp-server.local'
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_optsfile_cleanup
|
87
|
+
@server.stubs(:optsfile_path).returns '/dev/null'
|
88
|
+
@server.instance_variable_set :@optsfile_content, %w[tag:bf_pxelinux_0,option:bootfile-name,pxelinux.0]
|
89
|
+
|
90
|
+
Dir.expects(:glob).returns([])
|
91
|
+
File.expects(:write).with('/dev/null', "\n")
|
92
|
+
|
93
|
+
@server.send :cleanup_optsfile
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_optsfile_cleanup_real
|
97
|
+
@server.stubs(:optsfile_path).returns 'test/fixtures/dhcpopts.conf'
|
98
|
+
@server.instance_variable_set :@config_dir, 'test/fixtures'
|
99
|
+
|
100
|
+
File.expects(:write).with('test/fixtures/dhcpopts.conf', "tag:bf_pxelinux_0,option:bootfile-name,pxelinux.0\ntag:ns_tftp_server_local,option:tftp-server,tftp.server.local\n")
|
101
|
+
|
102
|
+
@server.send :cleanup_optsfile
|
103
|
+
end
|
55
104
|
end
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'test_helper'
|
2
4
|
require 'smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_subnet_service'
|
3
5
|
|
4
6
|
class DHCPDnsmasqSubnetServiceTest < Test::Unit::TestCase
|
5
7
|
def setup
|
6
|
-
@subnet_service = mock
|
8
|
+
@subnet_service = mock
|
7
9
|
Dir.stubs(:exist?).returns(true)
|
8
10
|
end
|
9
11
|
|
@@ -26,8 +28,45 @@ class DHCPDnsmasqSubnetServiceTest < Test::Unit::TestCase
|
|
26
28
|
initializer.expects(:parse_config_for_subnet).returns(subnet)
|
27
29
|
initializer.expects(:parse_config_for_dhcp_reservations).returns([host])
|
28
30
|
initializer.expects(:load_leases).returns([lease])
|
29
|
-
#
|
31
|
+
# initializer.expects(:add_watch)
|
30
32
|
|
31
33
|
initializer.load!
|
32
34
|
end
|
35
|
+
|
36
|
+
def test_load_fixtures
|
37
|
+
service = Proxy::DHCP::Dnsmasq::SubnetService.new(
|
38
|
+
'test/fixtures/config/dnsmasq.conf', 'test/fixtures/config/dhcp', 'test/fixtures/config/dhcp.leases',
|
39
|
+
::Proxy::MemoryStore.new, ::Proxy::MemoryStore.new,
|
40
|
+
::Proxy::MemoryStore.new, ::Proxy::MemoryStore.new,
|
41
|
+
::Proxy::MemoryStore.new
|
42
|
+
)
|
43
|
+
|
44
|
+
assert service.load!
|
45
|
+
|
46
|
+
subnet = service.subnets.first.last
|
47
|
+
assert_not_nil subnet
|
48
|
+
assert_equal '192.168.0.0', subnet.network
|
49
|
+
assert_equal '255.255.255.0', subnet.netmask
|
50
|
+
assert_equal IPAddr.new('192.168.0.0/24'), subnet.ipaddr
|
51
|
+
assert_equal ['192.168.0.200', '192.168.0.223'], subnet.options[:range]
|
52
|
+
assert_equal ['192.168.0.1'], subnet.options[:domain_name_servers]
|
53
|
+
assert_equal '192.168.0.1-192.168.0.254', subnet.range
|
54
|
+
|
55
|
+
# 3 in dnsmasq.conf
|
56
|
+
# 1 in dhcphosts/
|
57
|
+
assert_equal 4, service.reservations_by_name.values.count
|
58
|
+
|
59
|
+
reservation = service.find_host_by_hostname('host1')
|
60
|
+
assert_not_nil reservation
|
61
|
+
assert_equal '00:11:22:33:44:55', reservation.mac
|
62
|
+
assert_equal 'pxelinux.0', reservation.options[:filename]
|
63
|
+
assert_equal '192.168.0.2', reservation.options[:nextServer]
|
64
|
+
|
65
|
+
assert_equal 15, service.leases_by_ip.values.count
|
66
|
+
|
67
|
+
lease = service.find_lease_by_ip('192.168.0.0', '192.168.0.3')
|
68
|
+
assert_not_nil lease
|
69
|
+
assert_equal '44:fa:23:05:1b:8b', lease.mac
|
70
|
+
assert_equal 'active', lease.state
|
71
|
+
end
|
33
72
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
1563548437 22:12:1d:6f:46:25 192.168.0.221 no *
|
2
|
+
1563545331 2c:68:57:40:f6:50 192.168.0.217 some-host *
|
3
|
+
1563555257 28:3b:bf:dc:54:03 192.168.0.213 * *
|
4
|
+
1563545265 e8:a2:ed:ec:47:dc 192.168.0.13 important-host *
|
5
|
+
1563548921 44:fa:23:05:1b:8b 192.168.0.3 early-host fa:b2:05:1a:8b:10:01:00:01:24:b4:92:e0:64:66:b3:05:1b:8b
|
6
|
+
1563555097 a4:c3:91:a5:4f:0a 192.168.0.152 another-host *
|
7
|
+
1563546877 10:17:06:cd:02:2a 192.168.0.204 yet-more-hosts *
|
8
|
+
1563553309 1c:9a:7b:ea:18:a4 192.168.0.205 * *
|
9
|
+
1563549911 a4:43:30:66:16:71 192.168.0.115 less-important-host *
|
10
|
+
1563551583 40:29:42:00:27:49 192.168.0.9 small-host *
|
11
|
+
1563541055 20:e2:49:e5:d4:7a 192.168.0.215 big-host *
|
12
|
+
1563549214 20:7a:44:49:e6:31 192.168.0.20 most-hosts *
|
13
|
+
1563541281 b4:c7:92:b5:4f:45 192.168.0.150 another-entry *
|
14
|
+
1563545865 00:2b:16:93:9a:e0 192.168.0.138 example *
|
15
|
+
1563551346 a4:42:30:6d:82:1a 192.168.0.106 last-host *
|
@@ -0,0 +1 @@
|
|
1
|
+
22:12:1d:6f:46:25,set:bf_pxelinux_0,set:ns_tftp_server_local,192.168.0.221,no
|
@@ -0,0 +1,32 @@
|
|
1
|
+
conf-file=/etc/dnsmasq.conf
|
2
|
+
dhcp-authoritative
|
3
|
+
domain-needed
|
4
|
+
no-hosts
|
5
|
+
localise-queries
|
6
|
+
bogus-priv
|
7
|
+
expand-hosts
|
8
|
+
local-service
|
9
|
+
quiet-dhcp
|
10
|
+
port=53
|
11
|
+
domain=example.com
|
12
|
+
server=/example.com/
|
13
|
+
server=8.8.8.8
|
14
|
+
server=8.8.4.4
|
15
|
+
dhcp-leasefile=/tmp/dhcp.leases
|
16
|
+
resolv-file=/etc/resolv.conf
|
17
|
+
addn-hosts=/tmp/hosts
|
18
|
+
conf-dir=/tmp/dnsmasq.d
|
19
|
+
stop-dns-rebind
|
20
|
+
rebind-localhost-ok
|
21
|
+
dhcp-broadcast=tag:needs-broadcast
|
22
|
+
|
23
|
+
dhcp-host=00:11:22:33:44:55,192.168.0.7,host1
|
24
|
+
dhcp-host=10:20:30:40:50:60,192.168.0.20,host2
|
25
|
+
dhcp-host=52:52:52:52:52:52,192.168.0.25,host3
|
26
|
+
|
27
|
+
dhcp-boot=tag:00:11:22:33:44:55,pxelinux.0,192.168.0.2
|
28
|
+
|
29
|
+
|
30
|
+
dhcp-range=lan,192.168.0.200,192.168.0.223,255.255.255.0,12h
|
31
|
+
dhcp-option=lan,6,192.168.0.1
|
32
|
+
no-dhcp-interface=eth2
|
@@ -0,0 +1 @@
|
|
1
|
+
01:02:03:04:05:06,set:bf_pxelinux_0,set:ns_tftp_server_local,10.0.0.1,example
|
@@ -0,0 +1,14 @@
|
|
1
|
+
tag:bf_pxelinux_0,option:bootfile-name,pxelinux.0
|
2
|
+
|
3
|
+
tag:bf_pxelinux_1,option:bootfile-name,pxelinux.1
|
4
|
+
|
5
|
+
|
6
|
+
tag:bf_pxelinux_2,option:bootfile-name,pxelinux.2
|
7
|
+
tag:bf_pxelinux_3,option:bootfile-name,pxelinux.3
|
8
|
+
|
9
|
+
tag:bf_pxelinux_4,option:bootfile-name,pxelinux.4
|
10
|
+
|
11
|
+
tag:ns_tftp_server,option:tftp-server,tftp.server
|
12
|
+
|
13
|
+
tag:ns_tftp_server_local,option:tftp-server,tftp.server.local
|
14
|
+
tag:ns_tftp_server,option:tftp-server,tftp.server
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,57 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_proxy_dhcp_dnsmasq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0
|
4
|
+
version: '1.0'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Olofsson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: rake
|
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: mocha
|
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: test-unit
|
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'
|
11
|
+
date: 2019-10-29 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
55
13
|
description: dnamasq DHCP provider plugin for Foreman's smart proxy
|
56
14
|
email:
|
57
15
|
- alexander.olofsson@liu.se
|
@@ -64,15 +22,21 @@ files:
|
|
64
22
|
- bundler.d/dhcp_dnsmasq.rb
|
65
23
|
- config/dhcp_dnsmasq.yml
|
66
24
|
- lib/smart_proxy_dhcp_dnsmasq.rb
|
67
|
-
- lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_configuration.rb
|
68
25
|
- lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_main.rb
|
69
26
|
- lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_plugin.rb
|
70
27
|
- lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_subnet_service.rb
|
71
28
|
- lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_version.rb
|
29
|
+
- lib/smart_proxy_dhcp_dnsmasq/plugin_configuration.rb
|
72
30
|
- test/dhcp_dnsmasq_default_settings_test.rb
|
73
31
|
- test/dhcp_dnsmasq_production_wiring_test.rb
|
74
32
|
- test/dhcp_dnsmasq_record_handling_test.rb
|
75
33
|
- test/dhcp_dnsmasq_subnet_service_test.rb
|
34
|
+
- test/fixtures/config/dhcp.leases
|
35
|
+
- test/fixtures/config/dhcp/dhcphosts/22_12_1d_6f_46_25.conf
|
36
|
+
- test/fixtures/config/dhcp/dhcpopts.conf
|
37
|
+
- test/fixtures/config/dnsmasq.conf
|
38
|
+
- test/fixtures/dhcphosts/01_02_03_04_05_06.conf
|
39
|
+
- test/fixtures/dhcpopts.conf
|
76
40
|
- test/test_helper.rb
|
77
41
|
homepage: https://github.com/theforeman/smart_proxy_dhcp_dnsmasq
|
78
42
|
licenses:
|
@@ -93,13 +57,19 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
93
57
|
- !ruby/object:Gem::Version
|
94
58
|
version: '0'
|
95
59
|
requirements: []
|
96
|
-
rubygems_version: 3.0.
|
60
|
+
rubygems_version: 3.0.6
|
97
61
|
signing_key:
|
98
62
|
specification_version: 4
|
99
63
|
summary: dnsmasq DHCP provider plugin for Foreman's smart proxy
|
100
64
|
test_files:
|
101
|
-
- test/
|
102
|
-
- test/
|
65
|
+
- test/fixtures/config/dhcp.leases
|
66
|
+
- test/fixtures/config/dhcp/dhcphosts/22_12_1d_6f_46_25.conf
|
67
|
+
- test/fixtures/config/dhcp/dhcpopts.conf
|
68
|
+
- test/fixtures/config/dnsmasq.conf
|
69
|
+
- test/fixtures/dhcphosts/01_02_03_04_05_06.conf
|
70
|
+
- test/fixtures/dhcpopts.conf
|
103
71
|
- test/dhcp_dnsmasq_default_settings_test.rb
|
104
72
|
- test/dhcp_dnsmasq_production_wiring_test.rb
|
105
73
|
- test/dhcp_dnsmasq_record_handling_test.rb
|
74
|
+
- test/dhcp_dnsmasq_subnet_service_test.rb
|
75
|
+
- test/test_helper.rb
|