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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9f60a51d51d8a80edf7ebd7f841ef4f5e44397c4b86bfb5c13c8872fab3d74b
4
- data.tar.gz: e528fe5bc8aef7b939ab6feea332ec2c0aedba81d5a101e2be1d063c242fb2b4
3
+ metadata.gz: 91ee0c71835884e0210ee0ec7b153e129786423536ca71bc67696019828e09df
4
+ data.tar.gz: 0b919d07a4f75212c0fea23a8852d2585930d6e7f693ff8e854dd11badce18eb
5
5
  SHA512:
6
- metadata.gz: 45ba84592849d76efe6725ec24723509ac3347d7a4ff15c1de47969aeb3cd5a9e019d51521b2bdddca27ff7bb25c861fd49ff6c17261a08cc1be60066099cd0c
7
- data.tar.gz: 9d7e177853adff920b1c98892c4a240c174afdb7ff07e848ec49065434f2f59ca9e7df890cc4a2fdcfdb7e0b426dfa09417a960986f12a8b8b66e40400a94c62
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.15 or higher.
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
  ```
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  gem 'smart_proxy_dhcp_dnsmasq'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Proxy
2
4
  module DHCP
3
5
  module Dnsmasq; end
@@ -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 Record < ::Proxy::DHCP::Server
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 true # TODO: Only cleanup occasionally
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 = File.join(@config_dir, 'dhcpopts.conf').freeze
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).readlines.map(&:chomp).reject(&:empty?) \
77
- if File.exist?(path) && @optsfile_content.empty?
78
- @optsfile_content
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 = File.join(@config_dir, 'dhcpopts.conf').freeze
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 << tag }
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
- File.write(File.join(@config_dir, 'dhcpopts.conf'), optsfile_content.join("\n") + "\n")
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
- require 'smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_configuration'
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.15'
9
- default_settings :config=> '/etc/dnsmasq.conf',
10
- :target_dir => '/var/lib/foreman-proxy/dhcp/',
11
- :lease_file => '/var/lib/dnsmasq/dhcp.leases',
12
- :reload_cmd => 'systemctl reload dnsmasq'
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
- #require 'rb-inotify'
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 add_watch
29
- # TODO: Add proper inotify listener for configs
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
- files << path if File.exist? path
50
- files += Dir["#{path}/*"] if Dir.exist? path
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/ === data.first
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
- files << path if File.exist? path
115
- files += Dir[File.join(path), '*'] if Dir.exist? path
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] = file
154
- to_ret[mac].options[:filename] = server
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 = { :deletable => true }
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
- open(@lease_file, 'r').readlines.map do |line|
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,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Proxy
2
4
  module DHCP
3
5
  module Dnsmasq
4
- VERSION = '0.7'.freeze
6
+ VERSION = '1.0'
5
7
  end
6
8
  end
7
9
  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.dependency :subnet_service, (lambda do
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::Record.new(
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 'smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_configuration'
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::Record.any_instance.stubs(:cleanup_optsfile)
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
- :config => '/etc/dnsmasq.conf',
19
- :target_dir => '/etc/dnsmasq.d/dhcp',
20
- :lease_file => '/tmp/dhcp.leases',
21
- :reload_cmd => 'systemctl reload dnsmasq'
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::DHCP::Dnsmasq::Record.any_instance.stubs(:cleanup_optsfile)
14
+ Proxy::LogBuffer::Decorator.any_instance.stubs(:add)
10
15
 
11
- @server = ::Proxy::DHCP::Dnsmasq::Record.new('/etc/dnsmasq.d/', '/bin/true', @subnet_service)
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
- # initializer.expects(:add_watch)
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,2 @@
1
+ tag:bf_pxelinux_0,option:bootfile-name,pxelinux.0
2
+ tag:ns_tftp_server_local,option:tftp-server,tftp.server.local
@@ -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
@@ -1,3 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'simplecov'
4
+ SimpleCov.start do
5
+ add_filter '/test/'
6
+ add_filter '/vendor/'
7
+ end
8
+
1
9
  require 'test/unit'
2
10
  require 'mocha/setup'
3
11
  require 'json'
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.7'
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-07-12 00:00:00.000000000 Z
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.3
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/test_helper.rb
102
- - test/dhcp_dnsmasq_subnet_service_test.rb
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