smart_proxy_dhcp_bluecat 0.1.0 → 0.1.5

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.
metadata CHANGED
@@ -1,56 +1,56 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_proxy_dhcp_bluecat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthias Hähnel
8
8
  - The Foreman Team
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-10-17 00:00:00.000000000 Z
12
+ date: 2021-02-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: httparty
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - '>='
18
+ - - ">="
19
19
  - !ruby/object:Gem::Version
20
20
  version: '0'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - '>='
25
+ - - ">="
26
26
  - !ruby/object:Gem::Version
27
27
  version: '0'
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: ipaddress
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - '>='
32
+ - - ">="
33
33
  - !ruby/object:Gem::Version
34
34
  version: '0'
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - '>='
39
+ - - ">="
40
40
  - !ruby/object:Gem::Version
41
41
  version: '0'
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: rubocop
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - ~>
46
+ - - "~>"
47
47
  - !ruby/object:Gem::Version
48
48
  version: 0.50.0
49
49
  type: :development
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
- - - ~>
53
+ - - "~>"
54
54
  - !ruby/object:Gem::Version
55
55
  version: 0.50.0
56
56
  description: BlueCat DHCP provider plugin for Foreman's smart proxy
@@ -61,48 +61,39 @@ executables: []
61
61
  extensions: []
62
62
  extra_rdoc_files: []
63
63
  files:
64
+ - LICENSE
65
+ - README.md
66
+ - bundler.d/dhcp_bluecat.rb
64
67
  - config/dhcp_bluecat.yml.example
65
68
  - lib/smart_proxy_dhcp_bluecat.rb
69
+ - lib/smart_proxy_dhcp_bluecat/bluecat_api.rb
66
70
  - lib/smart_proxy_dhcp_bluecat/dhcp_bluecat_main.rb
67
71
  - lib/smart_proxy_dhcp_bluecat/dhcp_bluecat_plugin.rb
68
72
  - lib/smart_proxy_dhcp_bluecat/dhcp_bluecat_version.rb
69
73
  - lib/smart_proxy_dhcp_bluecat/module_loader.rb
70
74
  - lib/smart_proxy_dhcp_bluecat/plugin_configuration.rb
71
75
  - lib/smart_proxy_dhcp_bluecat/settings_validator.rb
72
- - lib/smart_proxy_dhcp_bluecat/bluecat_api.rb
73
- - lib/lib/smart_proxy_dhcp_bluecat/bluecat_api.rb
74
- - lib/lib/smart_proxy_dhcp_bluecat/dhcp_bluecat_main.rb
75
- - lib/lib/smart_proxy_dhcp_bluecat/dhcp_bluecat_plugin.rb
76
- - lib/lib/smart_proxy_dhcp_bluecat/dhcp_bluecat_version.rb
77
- - lib/lib/smart_proxy_dhcp_bluecat/module_loader.rb
78
- - lib/lib/smart_proxy_dhcp_bluecat/plugin_configuration.rb
79
- - lib/lib/smart_proxy_dhcp_bluecat/settings_validator.rb
80
- - lib/lib/smart_proxy_dhcp_bluecat.rb
81
- - bundler.d/dhcp_bluecat.rb
82
- - README.md
83
- - LICENSE
84
76
  homepage: https://github.com/theforeman/smart_proxy_dhcp_bluecat
85
77
  licenses:
86
78
  - GPL-3.0
87
79
  metadata: {}
88
- post_install_message:
80
+ post_install_message:
89
81
  rdoc_options: []
90
82
  require_paths:
91
83
  - lib
92
84
  required_ruby_version: !ruby/object:Gem::Requirement
93
85
  requirements:
94
- - - '>='
86
+ - - ">="
95
87
  - !ruby/object:Gem::Version
96
88
  version: '0'
97
89
  required_rubygems_version: !ruby/object:Gem::Requirement
98
90
  requirements:
99
- - - '>='
91
+ - - ">="
100
92
  - !ruby/object:Gem::Version
101
93
  version: '0'
102
94
  requirements: []
103
- rubyforge_project:
104
- rubygems_version: 2.0.14.1
105
- signing_key:
95
+ rubygems_version: 3.1.2
96
+ signing_key:
106
97
  specification_version: 4
107
98
  summary: BlueCat DHCP provider plugin for Foreman's smart proxy
108
99
  test_files: []
@@ -1,11 +0,0 @@
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'
@@ -1,345 +0,0 @@
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