smart_proxy_dhcp_dnsmasq 0.4 → 0.6
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 +11 -3
- data/config/dhcp_dnsmasq.yml +15 -11
- data/lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_configuration.rb +9 -5
- data/lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_main.rb +77 -32
- data/lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_plugin.rb +5 -1
- data/lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_subnet_service.rb +110 -44
- data/lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_version.rb +1 -1
- data/test/dhcp_dnsmasq_default_settings_test.rb +4 -2
- data/test/dhcp_dnsmasq_production_wiring_test.rb +9 -6
- data/test/dhcp_dnsmasq_record_handling_test.rb +54 -0
- data/test/dhcp_dnsmasq_subnet_service_test.rb +33 -0
- metadata +7 -5
- data/test/dhcp_dnsmasq_record_default_test.rb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 64fb30133ada0a818bdf665a6488a4373f317144
|
4
|
+
data.tar.gz: 60dd83f0e023802e77b59d54ba75bdd1ec40e75c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 11665804f753f83922fbbabd1551b08c415b5910439f4a00319115794bbf4dd97cb472e1f75be48f5c091ab9cc3e1464c840956ecdb7bdec7da26698a4065e90
|
7
|
+
data.tar.gz: eb8ae2512a23f5924d15dd0a7b89c50b555fdbfe809f45daab0e8e3f6368a5aa8523fa85d59978c947ba3688c95cb5fe17546ed1e054001370fcc6ac5965e42e
|
data/README.md
CHANGED
@@ -10,6 +10,14 @@ for how to install Smart Proxy plugins
|
|
10
10
|
|
11
11
|
This plugin is compatible with Smart Proxy 1.15 or higher.
|
12
12
|
|
13
|
+
You need to add two lines to your dnsmasq configuration to use this plugin;
|
14
|
+
```
|
15
|
+
dhcp-optsfile=<target_dir>/dhcpopts.conf
|
16
|
+
dhcp-hostsfile=<target_dir>/dhcphosts
|
17
|
+
```
|
18
|
+
|
19
|
+
Dnsmasq will also require write privileges to the configuration file and folder specified.
|
20
|
+
|
13
21
|
## Configuration
|
14
22
|
|
15
23
|
To enable this DNS provider, edit `/etc/foreman-proxy/settings.d/dhcp.yml` and set:
|
@@ -18,10 +26,10 @@ To enable this DNS provider, edit `/etc/foreman-proxy/settings.d/dhcp.yml` and s
|
|
18
26
|
|
19
27
|
Configuration options for this plugin are in `/etc/foreman-proxy/settings.d/dhcp_dnsmasq.yml` and include:
|
20
28
|
|
21
|
-
* `
|
22
|
-
* `
|
29
|
+
* `config`: The path to the configuration directory to load, changes will be written to host-specific configuration files inside
|
30
|
+
* `target_dir`: The path of where dhcp files should be written, must have write permissions for the proxy user.
|
31
|
+
* `lease_file`: The path to the lease file. (*optional, will be auto-discovered if `dhcp-leasefile` is set in one of the config files*)
|
23
32
|
* `reload_cmd`: The command to use for reloading the dnsmasq configuration.
|
24
|
-
* `write_config_file`: The file to write any new changes to, the smart-proxy will only be able to remove any reservations made in this file.
|
25
33
|
|
26
34
|
For best results, the write config should point to a file in a dnsmasq `conf-dir` which only the smart-proxy uses.
|
27
35
|
|
data/config/dhcp_dnsmasq.yml
CHANGED
@@ -3,21 +3,25 @@
|
|
3
3
|
# Configuration file for 'dhcp_dnsmasq' dhcp provider
|
4
4
|
#
|
5
5
|
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
6
|
+
# This pluing requires that the following options are set in your dnsmasq
|
7
|
+
# configuration;
|
8
|
+
# dhcp-optsfile=<target_dir>/dhcpopts.conf
|
9
|
+
# dhcp-hostsfile=<target_dir>/dhcphosts # NB: this line should not end in a slash
|
10
|
+
|
11
|
+
# Configuration files and directories to parse
|
12
|
+
:config: /etc/dnsmasq.conf
|
13
|
+
# Directory to write dhcp files into
|
14
|
+
:target_dir: /var/lib/foreman-proxy/dhcp/
|
15
|
+
# Dnsmasq DHCP lease file location
|
13
16
|
:lease_file: /var/lib/dnsmasq/dhcp.leases
|
17
|
+
# Command to reload / SIGHUP dnsmasq
|
14
18
|
:reload_cmd: systemctl reload dnsmasq
|
15
19
|
|
16
20
|
# Example configuration for an OpenWRT router;
|
17
|
-
#:
|
21
|
+
#:config:
|
18
22
|
# - /tmp/etc/dnsmasq.conf
|
19
|
-
# - /
|
23
|
+
# - /tmp/dnsmasq.d/
|
24
|
+
# - /etc/dnsmasq.conf
|
25
|
+
#:target_dir: /etc/foreman-proxy/dnsmasq_dhcp/
|
20
26
|
#:lease_file: /tmp/dhcp.leases
|
21
27
|
#:reload_cmd: /etc/init.d/dnsmasq reload
|
22
|
-
#
|
23
|
-
# This also requires that the conf-dir option is set in /etc/dnsmasq.conf
|
@@ -6,17 +6,21 @@ module ::Proxy::DHCP::Dnsmasq
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def load_dependency_injection_wirings(container, settings)
|
9
|
-
write_config = settings[:write_config_file] || settings[:config_files].last
|
10
|
-
|
11
9
|
container.dependency :memory_store, ::Proxy::MemoryStore
|
10
|
+
|
12
11
|
container.dependency :subnet_service, (lambda do
|
13
|
-
::Proxy::DHCP::Dnsmasq::SubnetService.new(
|
12
|
+
::Proxy::DHCP::Dnsmasq::SubnetService.new(
|
13
|
+
settings[:config], settings[:target_dir], settings[:lease_file],
|
14
14
|
container.get_dependency(:memory_store),
|
15
15
|
container.get_dependency(:memory_store), container.get_dependency(:memory_store),
|
16
|
-
container.get_dependency(:memory_store), container.get_dependency(:memory_store)
|
16
|
+
container.get_dependency(:memory_store), container.get_dependency(:memory_store)
|
17
|
+
)
|
17
18
|
end)
|
18
19
|
container.dependency :dhcp_provider, (lambda do
|
19
|
-
Proxy::DHCP::Dnsmasq::Record.new(
|
20
|
+
Proxy::DHCP::Dnsmasq::Record.new(
|
21
|
+
settings[:target_dir],
|
22
|
+
settings[:reload_cmd], container.get_dependency(:subnet_service)
|
23
|
+
)
|
20
24
|
end)
|
21
25
|
end
|
22
26
|
end
|
@@ -4,62 +4,107 @@ require 'dhcp_common/server'
|
|
4
4
|
|
5
5
|
module Proxy::DHCP::Dnsmasq
|
6
6
|
class Record < ::Proxy::DHCP::Server
|
7
|
-
attr_reader :
|
7
|
+
attr_reader :config_dir, :reload_cmd, :subnet_service
|
8
8
|
|
9
|
-
def initialize(
|
10
|
-
@
|
11
|
-
@write_config_file = write_config_file
|
9
|
+
def initialize(target_dir, reload_cmd, subnet_service)
|
10
|
+
@config_dir = target_dir
|
12
11
|
@reload_cmd = reload_cmd
|
13
12
|
@subnet_service = subnet_service
|
13
|
+
@optsfile_content = []
|
14
|
+
|
15
|
+
Dir.mkdir @config_dir unless Dir.exist? @config_dir
|
14
16
|
|
15
17
|
subnet_service.load!
|
16
18
|
|
17
19
|
super('localhost', nil, subnet_service)
|
18
20
|
end
|
19
21
|
|
20
|
-
def add_record(options={})
|
22
|
+
def add_record(options = {})
|
23
|
+
logger.debug "Adding record; #{options}"
|
21
24
|
record = super(options)
|
22
25
|
options = record.options
|
23
26
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
subnet_service.add_host(record)
|
27
|
+
tags = []
|
28
|
+
tags << ensure_bootfile(options[:filename]) if options[:filename]
|
29
|
+
tags << ensure_tftpserver(options[:nextServer]) if options[:nextServer]
|
30
|
+
tagstring = ",set:#{tags.join(',set:')}" unless tags.empty?
|
31
31
|
|
32
|
-
|
32
|
+
hostspath = File.join(@config_dir, 'dhcphosts')
|
33
|
+
Dir.mkdir hostspath unless Dir.exist? hostspath
|
34
|
+
File.write(File.join(hostspath, "#{sanitize_string record.mac}.conf"),
|
35
|
+
"#{record.mac}#{tagstring},#{record.ip},#{record.name}\n")
|
36
|
+
subnet_service.add_host(record.subnet_address, record)
|
33
37
|
|
38
|
+
try_reload_cmd
|
34
39
|
record
|
35
40
|
end
|
36
41
|
|
37
42
|
def del_record(record)
|
38
|
-
|
43
|
+
logger.debug "Deleting record; #{record}"
|
44
|
+
# TODO: Removal of leases, to prevent DHCP record collisions?
|
39
45
|
return record if record.is_a? ::Proxy::DHCP::Lease
|
40
46
|
|
41
|
-
|
42
|
-
|
43
|
-
Tempfile.open('reservations') do |output|
|
44
|
-
tmppath = output.path.freeze
|
45
|
-
open(@write_config_file, 'r').each_line do |line|
|
46
|
-
output.puts line unless line.start_with?("dhcp-host=#{record.mac}") || \
|
47
|
-
line.start_with?("dhcp-host=set:#{record.mac}") || \
|
48
|
-
line.start_with?("dhcp-boot=tag:#{record.mac}")
|
49
|
-
|
50
|
-
found = true if line.start_with?("dhcp-host=#{record.mac}") || \
|
51
|
-
line.start_with?("dhcp-host=set:#{record.mac}")
|
52
|
-
end
|
53
|
-
end
|
54
|
-
FileUtils.mv(tmppath, @write_config_file) if found
|
47
|
+
path = File.join(@config_dir, 'dhcphosts', "#{sanitize_string record.mac}.conf")
|
48
|
+
File.unlink(path) if File.exist? path
|
55
49
|
|
56
50
|
subnet_service.delete_host(record)
|
57
51
|
|
58
|
-
|
59
|
-
|
52
|
+
try_reload_cmd
|
60
53
|
record
|
61
|
-
|
62
|
-
|
54
|
+
end
|
55
|
+
|
56
|
+
def find_record_by_mac(subnet_address, mac_address)
|
57
|
+
get_subnet(subnet_address)
|
58
|
+
service.find_host_by_mac(subnet_address, mac_address) ||
|
59
|
+
service.find_host_by_mac(subnet_address, mac_address.downcase) ||
|
60
|
+
service.find_lease_by_mac(subnet_address, mac_address) ||
|
61
|
+
service.find_lease_by_mac(subnet_address, mac_address.downcase)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def try_reload_cmd
|
67
|
+
logger.debug 'Reloading DHCP configuration...'
|
68
|
+
raise Proxy::DHCP::Error, 'Failed to reload configuration' \
|
69
|
+
unless system(@reload_cmd)
|
70
|
+
end
|
71
|
+
|
72
|
+
def optsfile_content
|
73
|
+
path = File.join(@config_dir, 'dhcpopts.conf').freeze
|
74
|
+
|
75
|
+
@optsfile_content = open(path).readlines \
|
76
|
+
if File.exist?(path) && @optsfile_content.empty?
|
77
|
+
@optsfile_content
|
78
|
+
end
|
79
|
+
|
80
|
+
def append_optsfile(line)
|
81
|
+
path = File.join(@config_dir, 'dhcpopts.conf').freeze
|
82
|
+
logger.debug "Appending #{line} to dhcpopts.conf"
|
83
|
+
|
84
|
+
optsfile_content << line
|
85
|
+
File.write(path, optsfile_content.join("\n") + "\n")
|
86
|
+
end
|
87
|
+
|
88
|
+
def sanitize_string(string)
|
89
|
+
string.downcase.gsub(/[^0-9a-z]/, '_')
|
90
|
+
end
|
91
|
+
|
92
|
+
def ensure_bootfile(filename)
|
93
|
+
tagname = "bf_#{sanitize_string(filename)}"
|
94
|
+
|
95
|
+
append_optsfile "tag:#{tagname},option:bootfile-name,#{filename}" \
|
96
|
+
unless optsfile_content.find { |l| l.start_with? "tag:#{tagname}" }
|
97
|
+
|
98
|
+
tagname
|
99
|
+
end
|
100
|
+
|
101
|
+
def ensure_tftpserver(address)
|
102
|
+
tagname = "ns_#{sanitize_string(address)}"
|
103
|
+
|
104
|
+
append_optsfile "tag:#{tagname},option:tftp-server,#{address}" \
|
105
|
+
unless optsfile_content.find { |l| l.start_with? "tag:#{tagname}" }
|
106
|
+
|
107
|
+
tagname
|
63
108
|
end
|
64
109
|
end
|
65
110
|
end
|
@@ -6,9 +6,13 @@ module Proxy::DHCP::Dnsmasq
|
|
6
6
|
plugin :dhcp_dnsmasq, ::Proxy::DHCP::Dnsmasq::VERSION
|
7
7
|
|
8
8
|
requires :dhcp, '>= 1.15'
|
9
|
-
default_settings :
|
9
|
+
default_settings :config=> '/etc/dnsmasq.conf',
|
10
|
+
:target_dir => '/var/lib/foreman-proxy/dhcp/',
|
11
|
+
:lease_file => '/var/lib/dnsmasq/dhcp.leases',
|
10
12
|
:reload_cmd => 'systemctl reload dnsmasq'
|
11
13
|
|
14
|
+
validate_readable :lease_file
|
15
|
+
|
12
16
|
load_classes ::Proxy::DHCP::Dnsmasq::PluginConfiguration
|
13
17
|
load_dependency_injection_wirings ::Proxy::DHCP::Dnsmasq::PluginConfiguration
|
14
18
|
end
|
@@ -7,10 +7,11 @@ module Proxy::DHCP::Dnsmasq
|
|
7
7
|
class SubnetService < ::Proxy::DHCP::SubnetService
|
8
8
|
include Proxy::Log
|
9
9
|
|
10
|
-
attr_reader :
|
10
|
+
attr_reader :config_dir, :lease_file
|
11
11
|
|
12
|
-
def initialize(
|
13
|
-
@
|
12
|
+
def initialize(config, target_dir, lease_file, leases_by_ip, leases_by_mac, reservations_by_ip, reservations_by_mac, reservations_by_name)
|
13
|
+
@config_paths = [config].flatten
|
14
|
+
@target_dir = target_dir
|
14
15
|
@lease_file = lease_file
|
15
16
|
|
16
17
|
super(leases_by_ip, leases_by_mac, reservations_by_ip, reservations_by_mac, reservations_by_name)
|
@@ -30,7 +31,7 @@ module Proxy::DHCP::Dnsmasq
|
|
30
31
|
@inotify.watch(File.dirname(lease_file), :modify, :moved_to) do |ev|
|
31
32
|
next unless ev.absolute_name == lease_file
|
32
33
|
|
33
|
-
leases = load_leases
|
34
|
+
leases = load_leases
|
34
35
|
|
35
36
|
# FIXME: Proper method for this
|
36
37
|
m.synchronize do
|
@@ -42,8 +43,16 @@ module Proxy::DHCP::Dnsmasq
|
|
42
43
|
end
|
43
44
|
|
44
45
|
def parse_config_for_subnet
|
45
|
-
configuration = {
|
46
|
-
|
46
|
+
configuration = {}
|
47
|
+
files = []
|
48
|
+
@config_paths.each do |path|
|
49
|
+
files << path if File.exist? path
|
50
|
+
files += Dir["#{path}/*"] if Dir.exist? path
|
51
|
+
end
|
52
|
+
|
53
|
+
logger.debug "Starting parse of DHCP subnets from #{files}"
|
54
|
+
files.each do |file|
|
55
|
+
logger.debug " Parsing #{file}..."
|
47
56
|
open(file, 'r').each_line do |line|
|
48
57
|
line.strip!
|
49
58
|
next if line.empty? || line.start_with?('#') || !line.include?('=')
|
@@ -62,31 +71,30 @@ module Proxy::DHCP::Dnsmasq
|
|
62
71
|
range_to = data.pop
|
63
72
|
range_from = data.pop
|
64
73
|
|
65
|
-
case ttl[-1]
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
configuration.merge!
|
75
|
-
|
76
|
-
|
77
|
-
|
74
|
+
ttl = case ttl[-1]
|
75
|
+
when 'h'
|
76
|
+
ttl[0..-2].to_i * 60 * 60
|
77
|
+
when 'm'
|
78
|
+
ttl[0..-2].to_i * 60
|
79
|
+
else
|
80
|
+
ttl.to_i
|
81
|
+
end
|
82
|
+
|
83
|
+
configuration.merge! \
|
84
|
+
address: IPAddr.new("#{range_from}/#{mask}").to_s,
|
85
|
+
mask: mask,
|
86
|
+
range: [range_from, range_to],
|
87
|
+
ttl: ttl
|
78
88
|
when 'dhcp-option'
|
79
89
|
data = value.split(',')
|
80
90
|
|
81
91
|
configuration[:options] = {} unless configuration.key? :options
|
82
92
|
|
83
|
-
until data.empty? || /\A\d+\z/ === data.first
|
84
|
-
data.shift
|
85
|
-
end
|
93
|
+
data.shift until data.empty? || /\A\d+\z/ === data.first
|
86
94
|
next if data.empty?
|
87
95
|
|
88
96
|
code = data.shift.to_i
|
89
|
-
option = ::Proxy::DHCP::Standard.select { |
|
97
|
+
option = ::Proxy::DHCP::Standard.select { |_k, v| v[:code] == code }.first.first
|
90
98
|
|
91
99
|
data = data.first unless ::Proxy::DHCP::Standard[option][:is_list]
|
92
100
|
configuration[:options][option] = data
|
@@ -94,14 +102,24 @@ module Proxy::DHCP::Dnsmasq
|
|
94
102
|
end
|
95
103
|
end
|
96
104
|
|
105
|
+
# TODO: Multiple subnets
|
106
|
+
logger.debug "Adding subnet with configuration; #{configuration}"
|
97
107
|
@ttl = configuration[:ttl]
|
98
108
|
::Proxy::DHCP::Subnet.new(configuration[:address], configuration[:mask], configuration[:options])
|
99
109
|
end
|
100
110
|
|
101
111
|
# Expects subnet_service to have subnet data
|
102
|
-
def parse_config_for_dhcp_reservations
|
112
|
+
def parse_config_for_dhcp_reservations
|
103
113
|
to_ret = {}
|
114
|
+
files = []
|
115
|
+
@config_paths.each do |path|
|
116
|
+
files << path if File.exist? path
|
117
|
+
files += Dir[File.join(path), '*'] if Dir.exist? path
|
118
|
+
end
|
119
|
+
|
120
|
+
logger.debug "Starting parse of DHCP reservations from #{files}"
|
104
121
|
files.each do |file|
|
122
|
+
logger.debug " Parsing #{file}..."
|
105
123
|
open(file, 'r').each_line do |line|
|
106
124
|
line.strip!
|
107
125
|
next if line.empty? || line.start_with?('#') || !line.include?('=')
|
@@ -112,7 +130,7 @@ module Proxy::DHCP::Dnsmasq
|
|
112
130
|
data = value.split(',')
|
113
131
|
data.shift while data.first.start_with? 'set:'
|
114
132
|
|
115
|
-
mac, ip, hostname = data[0,3]
|
133
|
+
mac, ip, hostname = data[0, 3]
|
116
134
|
|
117
135
|
# TODO: Possible ttl on end
|
118
136
|
|
@@ -122,7 +140,7 @@ module Proxy::DHCP::Dnsmasq
|
|
122
140
|
ip,
|
123
141
|
mac,
|
124
142
|
subnet,
|
125
|
-
#
|
143
|
+
# :source_file => file # TODO: Needs to overload the comparison
|
126
144
|
)
|
127
145
|
when 'dhcp-boot'
|
128
146
|
data = value.split(',')
|
@@ -140,56 +158,104 @@ module Proxy::DHCP::Dnsmasq
|
|
140
158
|
end
|
141
159
|
end
|
142
160
|
end
|
161
|
+
dhcpoptions = {}
|
162
|
+
|
163
|
+
dhcpopts_path = File.join(@target_dir, 'dhcpopts.conf')
|
164
|
+
logger.debug "Parsing DHCP options from #{dhcpopts_path}"
|
165
|
+
if File.exist? dhcpopts_path
|
166
|
+
open(dhcpopts_path, 'r').each_line do |line|
|
167
|
+
data = line.strip.split(',')
|
168
|
+
next if data.empty? || !data.first.start_with?('tag:')
|
169
|
+
|
170
|
+
tag = data.first[4..-1]
|
171
|
+
data.shift
|
172
|
+
|
173
|
+
dhcpoptions[tag] = data.last
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
logger.debug "Parsing provisioned DHCP reservations from #{@target_dir}"
|
178
|
+
Dir[File.join(@target_dir, 'dhcphosts', '*')].each do |file|
|
179
|
+
logger.debug " Parsing #{file}..."
|
180
|
+
open(file, 'r').each_line do |line|
|
181
|
+
data = line.strip.split(',')
|
182
|
+
next if data.empty? || data.first.start_with?('#')
|
183
|
+
|
184
|
+
mac = data.first
|
185
|
+
data.shift
|
186
|
+
|
187
|
+
options = { :deletable => true }
|
188
|
+
while data.first.start_with? 'set:'
|
189
|
+
tag = data.first[4..-1]
|
190
|
+
data.shift
|
191
|
+
|
192
|
+
value = dhcpoptions[tag]
|
193
|
+
next if value.nil?
|
194
|
+
|
195
|
+
options[:nextServer] = value if tag.start_with? 'ns'
|
196
|
+
options[:filename] = value if tag.start_with? 'bf'
|
197
|
+
end
|
198
|
+
|
199
|
+
ip, name = data
|
200
|
+
subnet = find_subnet(ip)
|
201
|
+
|
202
|
+
to_ret[mac] = ::Proxy::DHCP::Reservation.new(
|
203
|
+
name, ip, mac, subnet, options
|
204
|
+
)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
143
208
|
to_ret.values
|
144
|
-
rescue
|
209
|
+
rescue StandardError => e
|
145
210
|
logger.error msg = "Unable to parse reservations: #{e}"
|
146
|
-
raise Proxy::DHCP::Error, msg
|
211
|
+
raise Proxy::DHCP::Error, e, msg
|
147
212
|
end
|
148
213
|
|
149
214
|
def load_subnet_data
|
150
215
|
reservations = parse_config_for_dhcp_reservations
|
151
216
|
reservations.each do |record|
|
152
217
|
if dupe = find_host_by_mac(record.subnet_address, record.mac)
|
153
|
-
|
218
|
+
logger.debug "Found duplicate #{dupe} when adding record #{record}, skipping"
|
219
|
+
next
|
220
|
+
# delete_host(dupe)
|
154
221
|
end
|
155
222
|
|
223
|
+
logger.debug "Adding host #{record}"
|
156
224
|
add_host(record.subnet_address, record)
|
157
225
|
end
|
158
226
|
|
159
227
|
leases = [] # load_leases # FIXME: Will cause collisions if added
|
160
228
|
leases.each do |lease|
|
161
229
|
if dupe = find_lease_by_mac(lease.subnet_address, lease.mac)
|
162
|
-
|
230
|
+
logger.debug "Removing duplicate #{dupe} when adding lease #{lease}, skipping"
|
231
|
+
next
|
232
|
+
# delete_lease(dupe)
|
163
233
|
end
|
164
234
|
if dupe = find_lease_by_ip(lease.subnet_address, lease.ip)
|
165
|
-
|
235
|
+
logger.debug "Removing duplicate #{dupe} when adding lease #{lease}, skipping"
|
236
|
+
next
|
237
|
+
# delete_lease(dupe)
|
166
238
|
end
|
167
239
|
|
240
|
+
logger.debug "Adding lease #{lease}"
|
168
241
|
add_lease(lease.subnet_address, lease)
|
169
242
|
end
|
170
243
|
end
|
171
244
|
|
172
245
|
# Expects subnet_service to have subnet data
|
173
246
|
def load_leases
|
174
|
-
|
175
|
-
|
176
|
-
timestamp, mac, ip, hostname, client_id = line.split
|
177
|
-
|
247
|
+
open(@lease_file, 'r').readlines.map do |line|
|
248
|
+
timestamp, mac, ip, _hostname, _client_id = line.split
|
178
249
|
timestamp = timestamp.to_i
|
179
250
|
|
180
251
|
subnet = find_subnet(ip)
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
mac,
|
185
|
-
subnet,
|
186
|
-
timestamp - (@ttl or 24*60*60),
|
252
|
+
::Proxy::DHCP::Lease.new(
|
253
|
+
nil, ip, mac, subnet,
|
254
|
+
timestamp - (@ttl || 24 * 60 * 60),
|
187
255
|
timestamp,
|
188
|
-
'active'
|
189
|
-
deleteable: false,
|
256
|
+
'active'
|
190
257
|
)
|
191
258
|
end
|
192
|
-
leases
|
193
259
|
end
|
194
260
|
end
|
195
261
|
end
|
@@ -5,7 +5,9 @@ require 'smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_plugin'
|
|
5
5
|
class DnsDnsmasqDefaultSettingsTest < Test::Unit::TestCase
|
6
6
|
def test_default_settings
|
7
7
|
Proxy::DHCP::Dnsmasq::Plugin.load_test_settings({})
|
8
|
-
assert_equal
|
9
|
-
assert_equal
|
8
|
+
assert_equal '/etc/dnsmasq.conf', Proxy::DHCP::Dnsmasq::Plugin.settings.config
|
9
|
+
assert_equal '/var/lib/foreman-proxy/dhcp/', Proxy::DHCP::Dnsmasq::Plugin.settings.target_dir
|
10
|
+
assert_equal '/var/lib/dnsmasq/dhcp.leases', Proxy::DHCP::Dnsmasq::Plugin.settings.lease_file
|
11
|
+
assert_equal 'systemctl reload dnsmasq', Proxy::DHCP::Dnsmasq::Plugin.settings.reload_cmd
|
10
12
|
end
|
11
13
|
end
|
@@ -10,17 +10,20 @@ class DHCPDnsmasqProductionWiringTest < Test::Unit::TestCase
|
|
10
10
|
|
11
11
|
def test_dns_provider_initialization_default
|
12
12
|
Proxy::DHCP::Dnsmasq::SubnetService.any_instance.expects(:load!).returns(true)
|
13
|
+
Dir.expects(:exist?).with('/etc/dnsmasq.d/dhcp').returns(true)
|
13
14
|
|
14
|
-
@config.load_dependency_injection_wirings(
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
@config.load_dependency_injection_wirings(
|
16
|
+
@container,
|
17
|
+
:config => '/etc/dnsmasq.conf',
|
18
|
+
:target_dir => '/etc/dnsmasq.d/dhcp',
|
19
|
+
:lease_file => '/tmp/dhcp.leases',
|
20
|
+
:reload_cmd => 'systemctl reload dnsmasq'
|
21
|
+
)
|
18
22
|
|
19
23
|
provider = @container.get_dependency(:dhcp_provider)
|
20
24
|
|
21
25
|
assert_not_nil provider
|
22
|
-
assert_equal
|
23
|
-
assert_equal '/etc/dnsmasq.conf', provider.write_config_file
|
26
|
+
assert_equal '/etc/dnsmasq.d/dhcp', provider.config_dir
|
24
27
|
assert_equal 'systemctl reload dnsmasq', provider.reload_cmd
|
25
28
|
end
|
26
29
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_main'
|
3
|
+
|
4
|
+
class DHCPDnsmasqRecordHandlingTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@subnet_service = mock
|
7
|
+
@subnet_service.expects(:load!).returns(true)
|
8
|
+
Dir.stubs(:exist?).returns(true)
|
9
|
+
|
10
|
+
@server = ::Proxy::DHCP::Dnsmasq::Record.new('/etc/dnsmasq.d/', '/bin/true', @subnet_service)
|
11
|
+
@server.instance_eval('@optsfile_content = []')
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_add_record
|
15
|
+
subnet = ::Proxy::DHCP::Subnet.new('10.0.0.0', '255.0.0.0')
|
16
|
+
@subnet_service.stubs(:find_subnet).with('10.0.0.0').returns(subnet)
|
17
|
+
@subnet_service.stubs(:find_hosts_by_ip).returns(nil)
|
18
|
+
@subnet_service.stubs(:find_host_by_mac).returns(nil)
|
19
|
+
@subnet_service.stubs(:find_lease_by_ip).returns(nil)
|
20
|
+
@subnet_service.stubs(:find_lease_by_mac).returns(nil)
|
21
|
+
|
22
|
+
File.expects(:write).with(
|
23
|
+
'/etc/dnsmasq.d/dhcphosts/00_01_02_03_04_05.conf',
|
24
|
+
"00:01:02:03:04:05,set:bf_bootfile,set:ns_10_0_0_2,10.0.0.1,hostname\n"
|
25
|
+
)
|
26
|
+
@server.expects(:append_optsfile)
|
27
|
+
.with('tag:bf_bootfile,option:bootfile-name,bootfile').returns(true)
|
28
|
+
@server.expects(:append_optsfile)
|
29
|
+
.with('tag:ns_10_0_0_2,option:tftp-server,10.0.0.2').returns(true)
|
30
|
+
@subnet_service.expects(:add_host).returns(true)
|
31
|
+
@server.expects(:try_reload_cmd).returns(nil)
|
32
|
+
|
33
|
+
@server.add_record(
|
34
|
+
'hostname' => 'hostname',
|
35
|
+
'ip' => '10.0.0.1',
|
36
|
+
'network' => '10.0.0.0',
|
37
|
+
'mac' => '00:01:02:03:04:05',
|
38
|
+
filename: 'bootfile',
|
39
|
+
nextServer: '10.0.0.2'
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_del_record
|
44
|
+
subnet = ::Proxy::DHCP::Subnet.new('10.0.0.0', '255.0.0.0')
|
45
|
+
host = ::Proxy::DHCP::Reservation.new('10.0.0.0', '255.0.0.0', '00:01:02:03:04:05', subnet)
|
46
|
+
@subnet_service.expects(:delete_host).with(host).returns(true)
|
47
|
+
|
48
|
+
File.expects(:exist?).with('/etc/dnsmasq.d/dhcphosts/00_01_02_03_04_05.conf').returns(true)
|
49
|
+
File.expects(:unlink).with('/etc/dnsmasq.d/dhcphosts/00_01_02_03_04_05.conf').returns(true)
|
50
|
+
@server.expects(:try_reload_cmd).returns(nil)
|
51
|
+
|
52
|
+
@server.del_record(host)
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_subnet_service'
|
3
|
+
|
4
|
+
class DHCPDnsmasqSubnetServiceTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@subnet_service = mock()
|
7
|
+
Dir.stubs(:exist?).returns(true)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_initialize
|
11
|
+
subnet = ::Proxy::DHCP::Subnet.new('10.0.0.0', '255.255.255.0')
|
12
|
+
host = ::Proxy::DHCP::Reservation.new('test', '10.0.0.10', 'ba:be:fa:ce:ca:fe', subnet)
|
13
|
+
lease = ::Proxy::DHCP::Lease.new('test', '10.0.0.10', 'ba:be:fa:ce:ca:fe', subnet, Time.now, Time.now + 1000, 'active')
|
14
|
+
|
15
|
+
initializer = Proxy::DHCP::Dnsmasq::SubnetService.new(
|
16
|
+
'config/file', 'config/dir', 'lease/file',
|
17
|
+
::Proxy::MemoryStore.new, ::Proxy::MemoryStore.new,
|
18
|
+
::Proxy::MemoryStore.new, ::Proxy::MemoryStore.new,
|
19
|
+
::Proxy::MemoryStore.new
|
20
|
+
)
|
21
|
+
|
22
|
+
initializer.expects(:add_subnet).with(subnet)
|
23
|
+
initializer.expects(:add_host)
|
24
|
+
# initializer.expects(:add_lease)
|
25
|
+
|
26
|
+
initializer.expects(:parse_config_for_subnet).returns(subnet)
|
27
|
+
initializer.expects(:parse_config_for_dhcp_reservations).returns([host])
|
28
|
+
# initializer.expects(:load_leases).returns([lease])
|
29
|
+
# initializer.expects(:add_watch)
|
30
|
+
|
31
|
+
initializer.load!
|
32
|
+
end
|
33
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
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: '0.6'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Olofsson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-08-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -71,7 +71,8 @@ files:
|
|
71
71
|
- lib/smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_version.rb
|
72
72
|
- test/dhcp_dnsmasq_default_settings_test.rb
|
73
73
|
- test/dhcp_dnsmasq_production_wiring_test.rb
|
74
|
-
- test/
|
74
|
+
- test/dhcp_dnsmasq_record_handling_test.rb
|
75
|
+
- test/dhcp_dnsmasq_subnet_service_test.rb
|
75
76
|
- test/test_helper.rb
|
76
77
|
homepage: https://github.com/ace13/smart_proxy_dhcp_dnsmasq
|
77
78
|
licenses:
|
@@ -93,7 +94,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
93
94
|
version: '0'
|
94
95
|
requirements: []
|
95
96
|
rubyforge_project:
|
96
|
-
rubygems_version: 2.6.
|
97
|
+
rubygems_version: 2.6.12
|
97
98
|
signing_key:
|
98
99
|
specification_version: 4
|
99
100
|
summary: dnsmasq DHCP provider plugin for Foreman's smart proxy
|
@@ -101,4 +102,5 @@ test_files:
|
|
101
102
|
- test/test_helper.rb
|
102
103
|
- test/dhcp_dnsmasq_default_settings_test.rb
|
103
104
|
- test/dhcp_dnsmasq_production_wiring_test.rb
|
104
|
-
- test/
|
105
|
+
- test/dhcp_dnsmasq_record_handling_test.rb
|
106
|
+
- test/dhcp_dnsmasq_subnet_service_test.rb
|
@@ -1,26 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
require 'smart_proxy_dhcp_dnsmasq/dhcp_dnsmasq_subnet_service'
|
3
|
-
|
4
|
-
class DHCPDnsmasqSubnetServiceTest < Test::Unit::TestCase
|
5
|
-
def setup
|
6
|
-
@subnet_service = mock()
|
7
|
-
end
|
8
|
-
|
9
|
-
def test_initialize
|
10
|
-
subnet = ::Proxy::DHCP::Subnet.new('10.0.0.0','255.255.255.0')
|
11
|
-
host = ::Proxy::DHCP::Reservation.new('test','10.0.0.10','ba:be:fa:ce:ca:fe',subnet)
|
12
|
-
lease = ::Proxy::DHCP::Lease.new('test','10.0.0.10','ba:be:fa:ce:ca:fe',subnet,Time.now,Time.now + 1000,'active')
|
13
|
-
|
14
|
-
initializer = Proxy::DHCP::Dnsmasq::SubnetService.new('config/file', 'lease/file', ::Proxy::MemoryStore.new, ::Proxy::MemoryStore.new, ::Proxy::MemoryStore.new, ::Proxy::MemoryStore.new, ::Proxy::MemoryStore.new)
|
15
|
-
|
16
|
-
initializer.expects(:add_subnet).with(subnet)
|
17
|
-
initializer.expects(:add_host)
|
18
|
-
initializer.expects(:add_lease)
|
19
|
-
|
20
|
-
initializer.expects(:parse_config_for_subnet).returns(subnet)
|
21
|
-
initializer.expects(:parse_config_for_dhcp_reservations).returns([ host ])
|
22
|
-
initializer.expects(:load_leases).returns([ lease ])
|
23
|
-
initializer.expects(:add_watch)
|
24
|
-
initializer.load!
|
25
|
-
end
|
26
|
-
end
|