smart_proxy_dhcp_bluecat 0.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.
@@ -0,0 +1,47 @@
1
+ # SmartProxyDhcpBlueCat
2
+
3
+ This plugin adds a new DHCP provider for managing records with BlueCat Address Manager.
4
+ The Provider manages dhcp reservations and A&PTR records.
5
+
6
+ ## Installation
7
+
8
+ See [How_to_Install_a_Smart-Proxy_Plugin](http://projects.theforeman.org/projects/foreman/wiki/How_to_Install_a_Smart-Proxy_Plugin)
9
+ for how to install Smart Proxy plugins
10
+
11
+ This plugin is compatible with Smart Proxy 1.16 or higher.
12
+
13
+ When installing using "gem", make sure to install the bundle file:
14
+
15
+ echo "gem 'smart_proxy_dhcp_bluecat', :git => 'https://github.com/theforeman/smart_proxy_dhcp_bluecat'" > /usr/share/foreman-proxy/bundler.d/dhcp_bluecat.rb
16
+
17
+ ## Configuration
18
+
19
+ To enable this DHCP provider, edit `/etc/foreman-proxy/settings.d/dhcp.yml` and set:
20
+
21
+ :use_provider: dhcp_bluecat
22
+ :subnets: subnets you want to use (optional)
23
+
24
+ Configuration options for this plugin are in `/etc/foreman-proxy/settings.d/dhcp_bluecat.yml` and include:
25
+
26
+ :scheme: connection mode to the Bluecat address manager
27
+ :verify: validate ssl connection
28
+ :host: FQDN or IP of the Bluecat address manager
29
+ :parent_block: parent_block Id that holds your subnets
30
+ :view_name: Bluecat DNS view name
31
+ :config_id: Bluecat configuration id
32
+ :config_name: Bluecat configuration name
33
+ :server_id: id of your dhcp server
34
+ :username: API Username
35
+ :password: API Password
36
+
37
+ ## Limitations
38
+ IPv6 Records are currently not implemented
39
+ Adresses with expired DHCP Leases are not handed out as free IPs by Bluecat
40
+
41
+ ## Contributing
42
+
43
+ Fork and send a Pull Request. Thanks!
44
+
45
+ ## Copyright
46
+
47
+ Copyright (c) 2018 Sixt GmbH & Co. Autovermietung KG
@@ -0,0 +1 @@
1
+ gem 'smart_proxy_dhcp_bluecat'
@@ -0,0 +1,35 @@
1
+ ---
2
+ #
3
+ # Configuration file for 'dhcp_bluecat' dhcp provider
4
+ #
5
+
6
+ #connection mode to the address manager
7
+ #https or http
8
+ :scheme: "https"
9
+
10
+ # validate ssl connection
11
+ # true or false
12
+ :verify: true
13
+
14
+ #fqdn or ip of your bluecat address manager
15
+ :host: "192.168.0.2"
16
+
17
+ # id of the parent_block that holds the subnets that you want to use
18
+ :parent_block: 00000
19
+
20
+ # name of your dns view
21
+ :view_name: "internal"
22
+
23
+ # id of your Bluecat configuration
24
+ :config_id: 000000
25
+
26
+ # Name of your Bluecat configuration
27
+ :config_name: "default"
28
+
29
+ # id of the server that holds your dhcp
30
+ :server_id: 000000
31
+
32
+
33
+ # credentials of your api user
34
+ :username: "username"
35
+ :password: "password"
@@ -0,0 +1,11 @@
1
+ module Proxy
2
+ module DHCP
3
+ module BlueCat; end
4
+ end
5
+ end
6
+
7
+ require 'smart_proxy_dhcp_bluecat/plugin_configuration'
8
+ require 'smart_proxy_dhcp_bluecat/module_loader'
9
+ require 'smart_proxy_dhcp_bluecat/settings_validator'
10
+ require 'smart_proxy_dhcp_bluecat/dhcp_bluecat_version'
11
+ require 'smart_proxy_dhcp_bluecat/dhcp_bluecat_plugin'
@@ -0,0 +1,345 @@
1
+ require 'httparty'
2
+ require 'ipaddress'
3
+ require 'json'
4
+
5
+ class BlueCat::Client
6
+ include ::Proxy::Log
7
+
8
+ @@token = ''
9
+
10
+ attr_reader :scheme, :verify, :host, :parent_block, :view_name, :config_name, :config_id, :server_id, :username, :password
11
+
12
+ def initialize(scheme, verify, host, parent_block, view_name, config_name, config_id, server_id, username, password)
13
+ @scheme = scheme
14
+ @verify = verify
15
+ @host = host
16
+ @parent_block = parent_block
17
+ @view_name = view_name
18
+ @config_name = config_name
19
+ @config_id = config_id
20
+ @server_id = server_id
21
+ @username = username
22
+ @password = password
23
+ end
24
+
25
+ def rest_login
26
+ # login to bam, parse the session token
27
+ logger.debug('BAM Login ' + @scheme + ' ' + @host + ' ')
28
+ response = HTTParty.get(format('%s://%s/Services/REST/v1/login?username=%s&password=%s', @scheme, @host, @username, @password),
29
+ headers: { 'Content-Type' => 'text/plain' },
30
+ verify => @verify)
31
+ if response.code != 200
32
+ logger.error('BAM Login Failed. HTTP' + response.code.to_s + ' ' + response.body.to_s)
33
+ end
34
+ body = response.body.to_s
35
+ token = body.match(/BAMAuthToken:\s+(\S+)/).captures
36
+
37
+ logger.debug('BAM Login Body ' + response.body)
38
+ logger.debug('BAM Login Token ' + token[0].to_s)
39
+ @@token = token[0].to_s
40
+ end
41
+
42
+ def rest_logout
43
+ # logout from bam,
44
+ logger.debug('BAM Logout ')
45
+ response = HTTParty.get(format('%s://%s/Services/REST/v1/logout', @scheme, @host),
46
+ headers: { 'Authorization' => 'BAMAuthToken: ' + @@token, 'Content-Type' => 'application/json' },
47
+ verify: @verify)
48
+ if response.code != 200
49
+ logger.error('BAM Logout Failed. HTTP' + response.code.to_s + ' ' + response.body.to_s)
50
+ end
51
+ end
52
+
53
+ def rest_get(endpoint, querystring)
54
+ # wrapper function to for rest get requests
55
+ logger.debug('BAM GET ' + endpoint + '?' + querystring)
56
+
57
+ response = HTTParty.get(format('%s://%s/Services/REST/v1/%s?%s', @scheme, @host, endpoint, querystring),
58
+ headers: { 'Authorization' => 'BAMAuthToken: ' + @@token, 'Content-Type' => 'application/json' },
59
+ verify: @verify)
60
+ # Session propably expired, refresh it and do the request again
61
+ if response.code == 401
62
+ rest_login
63
+ response = HTTParty.get(format('%s://%s/Services/REST/v1/%s?%s', @scheme, @host, endpoint, querystring),
64
+ headers: { 'Authorization' => 'BAMAuthToken: ' + @@token, 'Content-Type' => 'application/json' },
65
+ verify: @verify)
66
+ end
67
+
68
+ if response.code != 200
69
+ logger.error('BAM GET Failed. HTTP' + response.code.to_s + ' ' + response.body.to_s)
70
+ return nil
71
+ end
72
+
73
+ response.body
74
+ end
75
+
76
+ def rest_post(endpoint, querystring)
77
+ # wrapper function to for rest post requests
78
+ logger.debug('BAM POST ' + endpoint + '?' + querystring)
79
+ response = HTTParty.post(format('%s://%s/Services/REST/v1/%s?%s', @scheme, @host, endpoint, querystring),
80
+ headers: { 'Authorization' => 'BAMAuthToken: ' + @@token, 'Content-Type' => 'application/json' },
81
+ verify: @verify
82
+ )
83
+ # Session propably expired, refresh it and do the request again
84
+ if response.code == 401
85
+ rest_login
86
+ response = HTTParty.post(format('%s://%s/Services/REST/v1/%s?%s', @scheme, @host, endpoint, querystring),
87
+ headers: { 'Authorization' => 'BAMAuthToken: ' + @@token, 'Content-Type' => 'application/json' },
88
+ verify: @verify)
89
+ end
90
+ if response.code != 200
91
+ logger.error('BAM POST Failed. HTTP' + response.code.to_s + ' ' + response.body.to_s)
92
+ return nil
93
+ else
94
+ return response.body
95
+ end
96
+ end
97
+
98
+ def rest_put(endpoint, querystring)
99
+ # wrapper function to for rest put requests
100
+ logger.debug('BAM PUT ' + endpoint + '?' + querystring)
101
+ response = HTTParty.put(format('%s://%s/Services/REST/v1/%s?%s', @scheme, @host, endpoint, querystring),
102
+ headers: { 'Authorization' => 'BAMAuthToken: ' + @@token, 'Content-Type' => 'application/json' },
103
+ verify: @verify)
104
+ # Session propably expired, refresh it and do the request again
105
+ if response.code == 401
106
+ rest_login
107
+ response = HTTParty.put(format('%s://%s/Services/REST/v1/%s?%s', @scheme, @host, endpoint, querystring),
108
+ headers: { 'Authorization' => 'BAMAuthToken: ' + @@token, 'Content-Type' => 'application/json' },
109
+ verify: @verify)
110
+ end
111
+ if response.code != 200
112
+ logger.error('BAM PUT Failed. HTTP' + response.code.to_s + ' ' + response.body.to_s)
113
+ return nil
114
+ else
115
+ return response.body
116
+ end
117
+ end
118
+
119
+ def rest_delete(endpoint, querystring)
120
+ # wrapper function to for rest delete requests
121
+ logger.debug('BAM DELETE ' + endpoint + '?' + querystring)
122
+ response = HTTParty.delete(format('%s://%s/Services/REST/v1/%s?%s', @scheme, @host, endpoint, querystring),
123
+ headers: { 'Authorization' => 'BAMAuthToken: ' + @@token, 'Content-Type' => 'application/json' },
124
+ verify: @verify)
125
+
126
+ # Session propably expired, refresh it and do the request again
127
+ if response.code == 401
128
+ rest_login
129
+ response = HTTParty.delete(format('%s://%s/Services/REST/v1/%s?%s', @scheme, @host, endpoint, querystring),
130
+ headers: { 'Authorization' => 'BAMAuthToken: ' + @@token, 'Content-Type' => 'application/json' },
131
+ verify: @verify)
132
+ end
133
+ if response.code != 200
134
+ logger.error('BAM DELETE Failed. HTTP' + response.code.to_s + ' ' + response.body.to_s)
135
+ return nil
136
+ else
137
+ return response.body
138
+ end
139
+ end
140
+
141
+ def get_addressid_by_ip(ip)
142
+ # helper function to get the object id of a ip by an ip address
143
+ json = rest_get('getIP4Address', 'containerId=' + @config_id.to_s + '&address=' + ip)
144
+ result = JSON.parse(json)
145
+ return nil if result.empty?
146
+ result['id'].to_s
147
+ end
148
+
149
+ def get_networkid_by_ip(ip)
150
+ # helper function to get the object id of a subnet by an ip address
151
+ logger.debug('BAM get_networkid_by_ip ' + ip)
152
+ querystring = 'containerId=' + @config_id.to_s + '&type=IP4Network' + '&address=' + ip.to_s
153
+ json = rest_get('getIPRangedByIP', querystring)
154
+ result = JSON.parse(json)
155
+ return nil if result.empty?
156
+ result['id'].to_s
157
+ end
158
+
159
+ def get_network_by_ip(ip)
160
+ # helper function to get the whole subnet informarions by an ip address
161
+ logger.debug('BAM get_network_by_ip ' + ip)
162
+ querystring = 'containerId=' + @config_id.to_s + '&type=IP4Network' + '&address=' + ip.to_s
163
+ json = rest_get('getIPRangedByIP', querystring)
164
+ result = JSON.parse(json)
165
+ properties = parse_properties(result['properties'])
166
+ properties['CIDR'].to_s
167
+ end
168
+
169
+ def parse_properties(properties)
170
+ # helper function to parse the properties scheme of bluecat into a hash
171
+ # => properies: a string that contains properties for the object in attribute=value format, with each separated by a | (pipe) character.
172
+ # For example, a host record object may have a properties field such as ttl=123|comments=my comment|.
173
+ properties = properties.split('|')
174
+ h = {}
175
+ properties.each do |property|
176
+ h[property.split('=').first.to_s] = property.split('=').last.to_s
177
+ end
178
+ h
179
+ end
180
+
181
+ # public
182
+ def add_host(options)
183
+ # wrapper function to add the dhcp reservation and dns records
184
+
185
+ # add the ip and hostname and mac as static
186
+ rest_post('addDeviceInstance', 'configName=' + @config_name +
187
+ '&ipAddressMode=PASS_VALUE' \
188
+ '&ipEntity=' + options['ip'] +
189
+ '&viewName=' + @view_name +
190
+ '&zoneName=' + options['hostname'].split('.', 2).last +
191
+ '&deviceName=' + options['hostname'] +
192
+ '&recordName=' + options['hostname'] +
193
+ '&macAddressMode=PASS_VALUE' \
194
+ '&macEntity=' + options['mac'] +
195
+ '&options=AllowDuplicateHosts=true%7C')
196
+
197
+ address_id = get_addressid_by_ip(options['ip'])
198
+
199
+ # update the state of the ip from static to dhcp reserved
200
+ rest_put('changeStateIP4Address', 'addressId=' + address_id +
201
+ '&targetState=MAKE_DHCP_RESERVED' \
202
+ '&macAddress=' + options['mac'])
203
+ # deploy the config
204
+ rest_post('deployServerConfig', 'serverId=' + @server_id.to_s + '&properties=services=DHCP')
205
+ # lets wait a little bit for the complete dhcp deploy
206
+ sleep 3
207
+ rest_post('deployServerConfig', 'serverId=' + @server_id.to_s + '&properties=services=DNS')
208
+ nil
209
+ end
210
+
211
+ # public
212
+ def remove_host(ip)
213
+ # wrapper function to remove a dhcp reservation and dns records
214
+ # deploy the config, without a clean config the removal fails sometimes
215
+ rest_post('deployServerConfig', 'serverId=' + @server_id.to_s + '&properties=services=DHCP,DNS')
216
+ # remove the ip and depending records
217
+ rest_delete('deleteDeviceInstance', 'configName=' + @config_name + '&identifier=' + ip)
218
+ # deploy the config again
219
+ rest_post('deployServerConfig', 'serverId=' + @server_id.to_s + '&properties=services=DHCP,DNS')
220
+ end
221
+
222
+ # public
223
+ def get_next_ip(netadress, start_ip, _end_ip)
224
+ # fetches the next free address in a subnet
225
+ networkid = get_networkid_by_ip(netadress)
226
+
227
+ start_ip = IPAddress.parse(netadress).first if start_ip.to_s.empty?
228
+
229
+ properties = 'offset=' + start_ip.to_s + '%7CexcludeDHCPRange=false'
230
+ result = rest_get('getNextIP4Address', 'parentId=' + networkid.to_s + '&properties=' + properties)
231
+ return if result.empty?
232
+ result.tr('"', '')
233
+ end
234
+
235
+ # public
236
+ def get_subnets
237
+ # fetches all subnets under the parent_block
238
+ json = rest_get('getEntities', 'parentId=' + @parent_block.to_s + '&type=IP4Network&start=0&count=10000')
239
+ results = JSON.parse(json)
240
+ subnets = []
241
+ subnets = results.map do |result|
242
+ properties = parse_properties(result['properties'])
243
+ net = IPAddress.parse(properties['CIDR'])
244
+ opts = { routers: [properties['gateway']] }
245
+ ::Proxy::DHCP::Subnet.new(net.address, net.netmask, opts)
246
+ end
247
+ subnets.compact
248
+ end
249
+
250
+ # public
251
+ def find_mysubnet(subnet_address)
252
+ # fetches a subnet by its network address
253
+ net = IPAddress.parse(get_network_by_ip(subnet_address))
254
+ subnet = ::Proxy::DHCP::Subnet.new(net.address, net.netmask)
255
+ subnet
256
+ end
257
+
258
+ # public
259
+ def get_hosts(network_address)
260
+ # fetches all dhcp reservations in a subnet
261
+ netid = get_networkid_by_ip(network_address)
262
+ net = IPAddress.parse(get_network_by_ip(network_address))
263
+ subnet = ::Proxy::DHCP::Subnet.new(net.address, net.netmask)
264
+
265
+ json = rest_get('getNetworkLinkedProperties', 'networkId=' + netid.to_s)
266
+ results = JSON.parse(json)
267
+
268
+ hosts = []
269
+ results.each do |result|
270
+ properties = parse_properties(result['properties'])
271
+
272
+ # Static Addresses and Gateway are not needed here
273
+ # if properties.length() >= 4
274
+ # if properties["state"] == "Gateway" or properties["state"] == "Static"
275
+ # address = properties[0].split("=").last()
276
+ # macAddress = "00:00:00:00:00:00"
277
+ # hosttag = properties[3].split("=").last().split(":")
278
+ # name = hosttag[1] + "." + hosttag[3]
279
+ # opts = {:hostname => name}
280
+ # hosts.push(Proxy::DHCP::Reservation.new(name, address, macAddress, subnet, opts))
281
+ # end
282
+ # end
283
+ next unless properties.length >= 5
284
+ next unless properties['state'] == 'DHCP Reserved'
285
+ hosttag = properties['host'].split(':')
286
+ name = hosttag[1] + '.' + hosttag[3]
287
+ opts = { hostname: name }
288
+ hosts.push(Proxy::DHCP::Reservation.new(name, properties['address'], properties['macAddress'].tr('-', ':'), subnet, opts))
289
+ end
290
+ hosts.compact
291
+ end
292
+
293
+ # public
294
+ def get_hosts_by_ip(ip)
295
+ # fetches a host by its ip
296
+ hosts = []
297
+ net = IPAddress.parse(get_network_by_ip(ip))
298
+ subnet = ::Proxy::DHCP::Subnet.new(net.address, net.netmask)
299
+ ipid = get_addressid_by_ip(ip)
300
+ return nil if ipid.to_s == '0'
301
+ json = rest_get('getLinkedEntities', 'entityId=' + ipid + '&type=HostRecord&start=0&count=2')
302
+ results = JSON.parse(json)
303
+
304
+ if results.empty?
305
+ # no host record on ip, fetch mac only
306
+ json2 = rest_get('getIP4Address', 'containerId=' + @config_id.to_s + '&address=' + ip)
307
+ result2 = JSON.parse(json2)
308
+ properties2 = parse_properties(result2['properties'])
309
+ mac_address = properties2['macAddress'].tr('-', ':')
310
+ hosts.push(Proxy::DHCP::Reservation.new("", ip, mac_address, subnet, {}))
311
+ else
312
+ # host record on ip, return more infos
313
+ results.each do |result|
314
+ properties = parse_properties(result['properties'])
315
+ opts = { hostname: properties['absoluteName'] }
316
+
317
+ next unless properties['reverseRecord'].to_s == 'true'.to_s
318
+ json2 = rest_get('getEntityById', 'id=' + ipid)
319
+ result2 = JSON.parse(json2)
320
+ properties2 = parse_properties(result2['properties'])
321
+ mac_address = properties2['macAddress'].tr('-', ':')
322
+ unless mac_address.empty?
323
+ hosts.push(Proxy::DHCP::Reservation.new(properties['absoluteName'], ip, mac_address, subnet, opts))
324
+ end
325
+ end
326
+ end
327
+ hosts.compact
328
+ end
329
+
330
+ # public
331
+ def get_host_by_mac(mac)
332
+ # fetches all dhcp reservations by a mac
333
+ json = rest_get('getMACAddress', 'configurationId=' + @config_id.to_s + '&macAddress=' + mac.to_s)
334
+ result = JSON.parse(json)
335
+ macid = result['id'].to_s
336
+ return if macid == '0'
337
+ json2 = rest_get('getLinkedEntities', 'entityId=' + macid + '&type=IP4Address&start=0&count=1')
338
+ result2 = JSON.parse(json2)
339
+ return if result2.empty?
340
+ properties = parse_properties(result2[0]['properties'])
341
+ host = get_hosts_by_ip(properties['address'])
342
+ return if host.nil?
343
+ host[0]
344
+ end
345
+ end
@@ -0,0 +1,109 @@
1
+ require 'dhcp_common/server'
2
+
3
+ module Proxy::DHCP::BlueCat
4
+ class Provider < ::Proxy::DHCP::Server
5
+ include Proxy::Log
6
+ include Proxy::Util
7
+
8
+ attr_reader :connection
9
+ def initialize(connection, managed_subnets)
10
+ @connection = connection
11
+ @managed_subnets = managed_subnets
12
+ super('bluecat', managed_subnets, nil)
13
+ end
14
+
15
+ def subnets
16
+ logger.debug('START subnets')
17
+ subnets = @connection.get_subnets
18
+ logger.debug('END subnets')
19
+ logger.debug('Returned: ' + subnets.class.to_s + ': ' + subnets.to_s)
20
+ subnets
21
+ end
22
+
23
+ def all_hosts(network_address)
24
+ logger.debug('START all_hosts with network_address: ' + network_address.to_s)
25
+ hosts = @connection.get_hosts(network_address)
26
+ logger.debug('END all_hosts with network_address: ' + network_address.to_s)
27
+ logger.debug('Returned: ' + hosts.class.to_s + ': ' + hosts.to_s)
28
+ hosts
29
+ end
30
+
31
+ def all_leases(network_address)
32
+ logger.debug('START all_leases with network_address: ' + network_address.to_s)
33
+ hosts = @connection.get_hosts(network_address)
34
+ logger.debug('END all_leases with network_address: ' + network_address.to_s)
35
+ logger.debug('Returned: ' + hosts.class.to_s + ': ' + hosts.to_s)
36
+ hosts
37
+ end
38
+
39
+ def unused_ip(subnet, mac_address, from_ip_address, to_ip_address)
40
+ logger.debug('START unused_ip with subnet: ' + subnet.to_s + ' mac_address: ' + mac_address.to_s + ' from_ip_address: ' + from_ip_address.to_s + ' to_ip_address: ' + to_ip_address.to_s)
41
+ ip = @connection.get_next_ip(subnet, from_ip_address, to_ip_address)
42
+ logger.debug('END unused_ip with subnet: ' + subnet.to_s + ' mac_address: ' + mac_address.to_s + ' from_ip_address: ' + from_ip_address.to_s + ' to_ip_address: ' + to_ip_address.to_s)
43
+ logger.debug('Returned: ' + ip.class.to_s + ': ' + ip.to_s)
44
+ ip
45
+ end
46
+
47
+ def find_record(subnet_address, address)
48
+ logger.debug('START find_record with subnet_address: ' + subnet_address.to_s + ' address: ' + address.to_s)
49
+ records = if IPAddress.valid?(address)
50
+ find_records_by_ip(subnet_address, address)
51
+ else
52
+ find_record_by_mac(subnet_address, address)
53
+ end
54
+ logger.debug('END find_record with subnet_address: ' + subnet_address.to_s + ' address: ' + address.to_s)
55
+ logger.debug('Returned: ' + records.class.to_s + ': ' + records.to_s)
56
+ return [] if records.nil?
57
+ records
58
+ end
59
+
60
+ def find_records_by_ip(subnet_address, ip)
61
+ logger.debug('START find_records_by_ip with subnet_address: ' + subnet_address.to_s + ' ip: ' + ip.to_s)
62
+ records = @connection.get_hosts_by_ip(ip)
63
+ logger.debug('END find_records_by_ip with subnet_address: ' + subnet_address.to_s + ' ip: ' + ip.to_s)
64
+ logger.debug('Returned: ' + records.class.to_s + ': ' + records.to_s)
65
+ return [] if records.nil?
66
+ records
67
+ end
68
+
69
+ def find_record_by_mac(subnet_address, mac_address)
70
+ logger.debug('START find_record_by_mac with subnet_address: ' + subnet_address.to_s + ' mac_address: ' + mac_address.to_s)
71
+ record = @connection.get_host_by_mac(mac_address)
72
+ logger.debug('END find_record_by_mac with subnet_address: ' + subnet_address.to_s + ' mac_address: ' + mac_address.to_s)
73
+ logger.debug('Returned: ' + record.class.to_s + ': ' + record.to_s)
74
+ record
75
+ end
76
+
77
+ def find_subnet(subnet_address)
78
+ logger.debug('START find_subnet with subnet_address: ' + subnet_address.to_s)
79
+ net = @connection.find_mysubnet(subnet_address)
80
+ logger.debug('END find_subnet with subnet_address: ' + subnet_address.to_s)
81
+ logger.debug('Returned: ' + net.class.to_s + ': ' + net.to_s)
82
+ net
83
+ end
84
+
85
+ def get_subnet(subnet_address)
86
+ logger.debug('START get_subnet with subnet_address: ' + subnet_address.to_s)
87
+ net = @connection.find_mysubnet(subnet_address)
88
+ logger.debug('END get_subnet with subnet_address: ' + subnet_address.to_s)
89
+ logger.debug('Returned: ' + net.class.to_s + ': ' + net.to_s)
90
+ net
91
+ end
92
+
93
+ def add_record(options)
94
+ logger.debug('START add_record with options: ' + options.to_s)
95
+ @connection.add_host(options)
96
+ logger.debug('END add_record with options: ' + options.to_s)
97
+ end
98
+
99
+ def del_record(record)
100
+ logger.debug('START del_record with record: ' + record.to_s)
101
+ if record.empty?
102
+ logger.debug('record empty, nothing to do')
103
+ else
104
+ @connection.remove_host(record.ip)
105
+ end
106
+ logger.debug('END del_record with record: ' + record.to_s)
107
+ end
108
+ end
109
+ end