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