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 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