smart_proxy_dhcp_dnsmasq 0.7 → 1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|