smart_proxy_ipam 0.0.11 → 0.0.12
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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 03e5b4db2d3b9f5ffad35e382585f0d632c7bf312c9a0b643ff24989d11283d8
|
4
|
+
data.tar.gz: 34960551000adc62f823b7d66f81d3429530b0f39cc3b8f9301d92207c6c5a7b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da0f3c2643eac27f95a041b8181097b874aadee8d1a46a8d549862870328cb9f40766d0def9688ff487cb27fc00b8a178522a75f90e2e813b60c99256e3b0068
|
7
|
+
data.tar.gz: 1f3f74603ab6822061a29018a2eb4ef98fd1071cb15c0af0e252da93cbbfe15b7fdc2e396d1a5a3ff3e9b29de97580ce46edae20b8c5357c76d317154ee4ce7d
|
data/README.md
CHANGED
@@ -17,8 +17,8 @@ for how to install Foreman plugins
|
|
17
17
|
|
18
18
|
Once plugin is installed, you can use phpIPAM to get the next available IP address for a subnet:
|
19
19
|
|
20
|
-
1. Create a subnet in Foreman of IPAM type "External IPAM". Click on the `Proxy` tab and associate the subnet with the
|
21
|
-
2. Create a host in Foreman. When adding/editing interfaces, select the above created subnet, and the next available IP
|
20
|
+
1. Create a subnet in Foreman of IPAM type "External IPAM". Click on the `Proxy` tab and associate the subnet with a Smart Proxy that has the `external_ipam` feature enabled. _NOTE: This subnet must actually exist in phpIPAM. There is no integration with subnet creation at this time._
|
21
|
+
2. Create a host in Foreman. When adding/editing interfaces, select the above created subnet, and the next available IP in the selected subnet will be pulled from phpIPAM, and displayed in the IPv4 address field. _NOTE: This is not supported for IPv6._
|
22
22
|
|
23
23
|
## Local development
|
24
24
|
|
@@ -32,29 +32,29 @@ git clone https://github.com/theforeman/smart-proxy
|
|
32
32
|
```
|
33
33
|
3. Fork both the foreman plugin and smart proxy plugin repos, then clone
|
34
34
|
|
35
|
-
|
36
|
-
|
35
|
+
foreman_ipam repo: https://github.com/grizzthedj/foreman_ipam
|
36
|
+
smart_proxy_ipam repo: https://github.com/grizzthedj/smart_proxy_ipam
|
37
37
|
|
38
38
|
```
|
39
|
-
git clone https://github.com/<GITHUB_USER>/
|
40
|
-
git clone https://github.com/<GITHUB_USER>/
|
39
|
+
git clone https://github.com/<GITHUB_USER>/foreman_ipam
|
40
|
+
git clone https://github.com/<GITHUB_USER>/smart_proxy_ipam
|
41
41
|
```
|
42
|
-
4. Add the
|
42
|
+
4. Add the foreman_ipam plugin to `Gemfile.local.rb` in the Foreman bundler.d directory
|
43
43
|
```
|
44
|
-
gem '
|
44
|
+
gem 'foreman_ipam', :path => '/path/to/foreman_ipam'
|
45
45
|
```
|
46
46
|
5. From Foreman root directory run
|
47
47
|
```
|
48
48
|
bundle install
|
49
49
|
bundle exec rails db:migrate
|
50
|
-
bundle exec rails db:seed # This adds '
|
50
|
+
bundle exec rails db:seed # This adds 'external_ipam' feature to Features table
|
51
51
|
bundle exec foreman start
|
52
52
|
```
|
53
53
|
6. Add the smart_proxy_phpipam plugin to `Gemfile.local.rb` in Smart Proxy bundler.d directory
|
54
54
|
```
|
55
|
-
gem '
|
55
|
+
gem 'smart_proxy_ipam', :path => '/path/to/smart_proxy_ipam'
|
56
56
|
```
|
57
|
-
7. Copy `config/settings.d/
|
57
|
+
7. Copy `config/settings.d/external_ipam.yml.example` to `config/settings.d/external_ipam.yml` and replace values with your phpIPAM URL and credentials.
|
58
58
|
8. From Smart Proxy root directory run ...
|
59
59
|
```
|
60
60
|
bundle install
|
@@ -62,7 +62,7 @@ bundle exec smart-proxy start
|
|
62
62
|
```
|
63
63
|
9. Navigate to Foreman UI at http://localhost:5000
|
64
64
|
10. Add a Local Smart Proxy in the Foreman UI(Infrastructure => Smart Proxies)
|
65
|
-
11. Ensure that the `external_ipam` feature is present on the proxy(http://
|
65
|
+
11. Ensure that the `external_ipam` feature is present on the proxy(http://localhost:8000/features)
|
66
66
|
12. Create a Subnet, and associate the subnet to the `external_ipam` proxy
|
67
67
|
|
68
68
|
## Contributing
|
@@ -2,6 +2,7 @@ require 'sinatra'
|
|
2
2
|
require 'smart_proxy_ipam/ipam'
|
3
3
|
require 'smart_proxy_ipam/ipam_main'
|
4
4
|
require 'smart_proxy_ipam/phpipam/phpipam_client'
|
5
|
+
require 'smart_proxy_ipam/phpipam/phpipam_helper'
|
5
6
|
|
6
7
|
# TODO: Refactor later to handle multiple IPAM providers. For now, it is
|
7
8
|
# just phpIPAM that is supported
|
@@ -9,6 +10,7 @@ module Proxy::Phpipam
|
|
9
10
|
class Api < ::Sinatra::Base
|
10
11
|
include ::Proxy::Log
|
11
12
|
helpers ::Proxy::Helpers
|
13
|
+
helpers PhpipamHelper
|
12
14
|
|
13
15
|
# Gets the next available IP address based on a given subnet
|
14
16
|
#
|
@@ -23,33 +25,23 @@ module Proxy::Phpipam
|
|
23
25
|
content_type :json
|
24
26
|
|
25
27
|
begin
|
28
|
+
err = validate_required_params(["cidr", "mac"], params)
|
29
|
+
return err if err.length > 0
|
30
|
+
|
26
31
|
cidr = params[:cidr]
|
27
32
|
mac = params[:mac]
|
28
|
-
|
29
|
-
if not cidr
|
30
|
-
return {:error => "A 'cidr' parameter for the subnet must be provided(e.g. 100.10.10.0/24)"}.to_json
|
31
|
-
end
|
32
|
-
if not mac
|
33
|
-
return {:error => "A 'mac' address must be provided(e.g. 00:0a:95:9d:68:10)"}.to_json
|
34
|
-
end
|
35
|
-
|
36
33
|
phpipam_client = PhpipamClient.new
|
37
34
|
subnet = JSON.parse(phpipam_client.get_subnet(cidr))
|
38
35
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
subnet_id = subnet[0]['id']
|
44
|
-
ip = phpipam_client.get_next_ip(subnet_id, mac, cidr)
|
36
|
+
return {:error => errors[:no_subnet]}.to_json if no_subnets_found(subnet)
|
37
|
+
|
38
|
+
ip = phpipam_client.get_next_ip(subnet[0]['id'], mac, cidr)
|
45
39
|
|
46
|
-
|
47
|
-
return {:error => "There are no more free addresses in subnet #{cidr}"}.to_json
|
48
|
-
end
|
40
|
+
return {:error => errors[:no_free_ip]}.to_json if no_free_ip_found(ip)
|
49
41
|
|
50
42
|
{:cidr => cidr, :next_ip => ip['next_ip']}.to_json
|
51
|
-
rescue Errno::ECONNREFUSED
|
52
|
-
return {:error =>
|
43
|
+
rescue Errno::ECONNREFUSED, Errno::ECONNRESET
|
44
|
+
return {:error => errors[:no_connection]}.to_json
|
53
45
|
end
|
54
46
|
end
|
55
47
|
|
@@ -76,17 +68,15 @@ module Proxy::Phpipam
|
|
76
68
|
content_type :json
|
77
69
|
|
78
70
|
begin
|
79
|
-
|
80
|
-
|
81
|
-
if not cidr
|
82
|
-
return {:error => "A 'cidr' parameter for the subnet must be provided(e.g. 100.10.10.0/24)"}.to_json
|
83
|
-
end
|
71
|
+
err = validate_required_params(["cidr"], params)
|
72
|
+
return err if err.length > 0
|
84
73
|
|
85
74
|
phpipam_client = PhpipamClient.new
|
86
|
-
subnet = phpipam_client.get_subnet(cidr)
|
75
|
+
subnet = JSON.parse(phpipam_client.get_subnet(params[:cidr]))
|
76
|
+
return {:error => errors[:no_subnet]}.to_json if no_subnets_found(subnet)
|
87
77
|
subnet.to_json
|
88
|
-
rescue Errno::ECONNREFUSED
|
89
|
-
return {:error =>
|
78
|
+
rescue Errno::ECONNREFUSED, Errno::ECONNRESET
|
79
|
+
return {:error => errors[:no_connection]}.to_json
|
90
80
|
end
|
91
81
|
end
|
92
82
|
|
@@ -107,10 +97,9 @@ module Proxy::Phpipam
|
|
107
97
|
|
108
98
|
begin
|
109
99
|
phpipam_client = PhpipamClient.new
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
return {:error => "Unable to connect to External IPAM server"}.to_json
|
100
|
+
phpipam_client.get_sections.to_json
|
101
|
+
rescue Errno::ECONNREFUSED, Errno::ECONNRESET
|
102
|
+
return {:error => errors[:no_connection]}.to_json
|
114
103
|
end
|
115
104
|
end
|
116
105
|
|
@@ -173,17 +162,19 @@ module Proxy::Phpipam
|
|
173
162
|
content_type :json
|
174
163
|
|
175
164
|
begin
|
165
|
+
err = validate_required_params(["section_name"], params)
|
166
|
+
return err if err.length > 0
|
167
|
+
|
176
168
|
section_name = URI.decode(params[:section_name])
|
177
|
-
|
178
|
-
if not section_name
|
179
|
-
return {:error => "A 'section_name' must be provided"}.to_json
|
180
|
-
end
|
181
|
-
|
182
169
|
phpipam_client = PhpipamClient.new
|
183
|
-
|
170
|
+
section = phpipam_client.get_section(section_name)
|
171
|
+
|
172
|
+
return {:error => errors[:no_section]}.to_json if section.nil?
|
173
|
+
|
174
|
+
subnets = phpipam_client.get_subnets(section['id'].to_s)
|
184
175
|
subnets.to_json
|
185
|
-
rescue Errno::ECONNREFUSED
|
186
|
-
return {:error =>
|
176
|
+
rescue Errno::ECONNREFUSED, Errno::ECONNRESET
|
177
|
+
return {:error => errors[:no_connection]}.to_json
|
187
178
|
end
|
188
179
|
end
|
189
180
|
|
@@ -201,23 +192,17 @@ module Proxy::Phpipam
|
|
201
192
|
content_type :json
|
202
193
|
|
203
194
|
begin
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
return {:error => "Missing 'cidr' parameter. A CIDR IPv4 address must be provided(e.g. 100.10.10.0/24)"}.to_json if not cidr
|
208
|
-
return {:error => "Missing 'ip' parameter. An IPv4 address must be provided(e.g. 100.10.10.22)"}.to_json if not ip
|
195
|
+
err = validate_required_params(["cidr", "ip"], params)
|
196
|
+
return err if err.length > 0
|
209
197
|
|
210
198
|
phpipam_client = PhpipamClient.new
|
211
|
-
subnet = phpipam_client.get_subnet(cidr)
|
199
|
+
subnet = JSON.parse(phpipam_client.get_subnet(params[:cidr]))
|
212
200
|
|
213
|
-
|
214
|
-
return {:error => "The specified subnet does not exist in External IPAM."}.to_json
|
215
|
-
end
|
201
|
+
return {:error => errors[:no_subnet]}.to_json if no_subnets_found(subnet)
|
216
202
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
return {:error => "Unable to connect to External IPAM server"}.to_json
|
203
|
+
phpipam_client.ip_exists(params[:ip], subnet[0]['id'])
|
204
|
+
rescue Errno::ECONNREFUSED, Errno::ECONNRESET
|
205
|
+
return {:error => errors[:no_connection]}.to_json
|
221
206
|
end
|
222
207
|
end
|
223
208
|
|
@@ -236,26 +221,21 @@ module Proxy::Phpipam
|
|
236
221
|
content_type :json
|
237
222
|
|
238
223
|
begin
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
return {:error => "Missing 'cidr' parameter. A CIDR IPv4 address must be provided(e.g. 100.10.10.0/24)"}.to_json if not cidr
|
243
|
-
return {:error => "Missing 'ip' parameter. An IPv4 address must be provided(e.g. 100.10.10.22)"}.to_json if not ip
|
224
|
+
err = validate_required_params(["cidr", "ip"], params)
|
225
|
+
return err if err.length > 0
|
244
226
|
|
227
|
+
ip = params[:ip]
|
228
|
+
cidr = params[:cidr]
|
245
229
|
phpipam_client = PhpipamClient.new
|
246
|
-
|
247
|
-
|
248
|
-
if response['message'] && response['message'].downcase == "no subnets found"
|
249
|
-
return {:error => "The specified subnet does not exist in External IPAM."}.to_json
|
250
|
-
end
|
230
|
+
subnet = JSON.parse(phpipam_client.get_subnet(cidr))
|
251
231
|
|
252
|
-
|
232
|
+
return {:error => errors[:no_subnet]}.to_json if no_subnets_found(subnet)
|
253
233
|
|
254
|
-
phpipam_client.add_ip_to_subnet(ip,
|
234
|
+
phpipam_client.add_ip_to_subnet(ip, subnet[0]['id'], 'Address auto added by Foreman')
|
255
235
|
|
256
236
|
{:message => "IP #{ip} added to subnet #{cidr} successfully."}.to_json
|
257
|
-
rescue Errno::ECONNREFUSED
|
258
|
-
return {:error =>
|
237
|
+
rescue Errno::ECONNREFUSED, Errno::ECONNRESET
|
238
|
+
return {:error => errors[:no_connection]}.to_json
|
259
239
|
end
|
260
240
|
end
|
261
241
|
|
@@ -273,28 +253,22 @@ module Proxy::Phpipam
|
|
273
253
|
content_type :json
|
274
254
|
|
275
255
|
begin
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
return {:error => "Missing 'cidr' parameter. A CIDR IPv4 address must be provided(e.g. 100.10.10.0/24)"}.to_json if not cidr
|
280
|
-
return {:error => "Missing 'ip' parameter. An IPv4 address must be provided(e.g. 100.10.10.22)"}.to_json if not ip
|
256
|
+
err = validate_required_params(["cidr", "ip"], params)
|
257
|
+
return err if err.length > 0
|
281
258
|
|
259
|
+
ip = params[:ip]
|
260
|
+
cidr = params[:cidr]
|
282
261
|
phpipam_client = PhpipamClient.new
|
283
|
-
|
284
|
-
|
285
|
-
if response['message'] && response['message'].downcase == "no subnets found"
|
286
|
-
return {:error => "The specified subnet does not exist in External IPAM."}.to_json
|
287
|
-
end
|
262
|
+
subnet = JSON.parse(phpipam_client.get_subnet(cidr))
|
288
263
|
|
289
|
-
|
264
|
+
return {:error => errors[:no_subnet]}.to_json if no_subnets_found(subnet)
|
290
265
|
|
291
|
-
phpipam_client.delete_ip_from_subnet(ip,
|
266
|
+
phpipam_client.delete_ip_from_subnet(ip, subnet[0]['id'])
|
292
267
|
|
293
268
|
{:message => "IP #{ip} deleted from subnet #{cidr} successfully."}.to_json
|
294
|
-
rescue Errno::ECONNREFUSED
|
295
|
-
return {:error =>
|
269
|
+
rescue Errno::ECONNREFUSED, Errno::ECONNRESET
|
270
|
+
return {:error => errors[:no_connection]}.to_json
|
296
271
|
end
|
297
272
|
end
|
298
|
-
|
299
273
|
end
|
300
274
|
end
|
@@ -5,10 +5,12 @@ require 'monitor'
|
|
5
5
|
require 'concurrent'
|
6
6
|
require 'smart_proxy_ipam/ipam'
|
7
7
|
require 'smart_proxy_ipam/ipam_main'
|
8
|
+
require 'smart_proxy_ipam/phpipam/phpipam_helper'
|
8
9
|
|
9
10
|
module Proxy::Phpipam
|
10
11
|
class PhpipamClient
|
11
12
|
include Proxy::Log
|
13
|
+
include PhpipamHelper
|
12
14
|
|
13
15
|
MAX_RETRIES = 5
|
14
16
|
DEFAULT_CLEANUP_INTERVAL = 120 # 2 mins
|
@@ -26,25 +28,20 @@ module Proxy::Phpipam
|
|
26
28
|
|
27
29
|
def get_subnet(cidr)
|
28
30
|
subnets = get("subnets/cidr/#{cidr.to_s}")
|
31
|
+
return {:error => errors[:no_subnet]}.to_json if no_subnets_found(subnets)
|
29
32
|
response = []
|
30
33
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
:subnet => subnet['subnet'],
|
40
|
-
:description => subnet['description'],
|
41
|
-
:mask => subnet['mask']
|
42
|
-
})
|
43
|
-
end
|
44
|
-
|
45
|
-
return response.to_json
|
46
|
-
end
|
34
|
+
# Only return the relevant fields
|
35
|
+
subnets['data'].each do |subnet|
|
36
|
+
response.push({
|
37
|
+
:id => subnet['id'],
|
38
|
+
:subnet => subnet['subnet'],
|
39
|
+
:description => subnet['description'],
|
40
|
+
:mask => subnet['mask']
|
41
|
+
})
|
47
42
|
end
|
43
|
+
|
44
|
+
response.to_json
|
48
45
|
end
|
49
46
|
|
50
47
|
def add_ip_to_subnet(ip, subnet_id, desc)
|
@@ -52,6 +49,10 @@ module Proxy::Phpipam
|
|
52
49
|
post('addresses/', data)
|
53
50
|
end
|
54
51
|
|
52
|
+
def get_section(section_name)
|
53
|
+
get("sections/#{section_name}/")["data"]
|
54
|
+
end
|
55
|
+
|
55
56
|
def get_sections
|
56
57
|
sections = get('sections/')['data']
|
57
58
|
response = []
|
@@ -69,11 +70,7 @@ module Proxy::Phpipam
|
|
69
70
|
response
|
70
71
|
end
|
71
72
|
|
72
|
-
def get_subnets(
|
73
|
-
sections = get_sections
|
74
|
-
section = sections.select {|section| section[:name] == section_name}
|
75
|
-
return {:error => "Section '#{section_name}' not found"}.to_json if section.size == 0
|
76
|
-
section_id = section[0][:id].to_s
|
73
|
+
def get_subnets(section_id)
|
77
74
|
subnets = get("sections/#{section_id}/subnets/")
|
78
75
|
response = []
|
79
76
|
|
@@ -103,8 +100,8 @@ module Proxy::Phpipam
|
|
103
100
|
return {:ip => ip, :exists => false}.to_json
|
104
101
|
else
|
105
102
|
response = get("subnets/#{subnet_id.to_s}/addresses/#{ip}/")
|
106
|
-
|
107
|
-
if response
|
103
|
+
|
104
|
+
if ip_not_found_in_ipam(response)
|
108
105
|
return {:ip => ip, :exists => false}.to_json
|
109
106
|
else
|
110
107
|
return {:ip => ip, :exists => true}.to_json
|
@@ -127,11 +124,11 @@ module Proxy::Phpipam
|
|
127
124
|
|
128
125
|
return response if response['message']
|
129
126
|
|
130
|
-
if subnet_hash
|
127
|
+
if subnet_hash && subnet_hash.key?(mac.to_sym)
|
131
128
|
response['next_ip'] = @@ip_cache[cidr.to_sym][mac.to_sym]
|
132
129
|
else
|
133
130
|
new_ip = response['data']
|
134
|
-
ip_not_in_cache = subnet_hash
|
131
|
+
ip_not_in_cache = subnet_hash && subnet_hash.key(new_ip).nil?
|
135
132
|
|
136
133
|
if ip_not_in_cache
|
137
134
|
next_ip = new_ip.to_s
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module PhpipamHelper
|
2
|
+
def validate_required_params(required_params, params)
|
3
|
+
err = []
|
4
|
+
required_params.each do |param|
|
5
|
+
if not params[param.to_sym]
|
6
|
+
err.push errors[param.to_sym]
|
7
|
+
end
|
8
|
+
end
|
9
|
+
err.length == 0 ? [] : {:error => err}.to_json
|
10
|
+
end
|
11
|
+
|
12
|
+
def no_subnets_found(subnet)
|
13
|
+
!subnet.kind_of?(Array) && subnet['message'] && subnet['message'].downcase == "no subnets found"
|
14
|
+
end
|
15
|
+
|
16
|
+
def no_free_ip_found(ip)
|
17
|
+
!ip.kind_of?(Array) && ip['message'] && ip['message'].downcase == "no free addresses found"
|
18
|
+
end
|
19
|
+
|
20
|
+
def ip_not_found_in_ipam(ip)
|
21
|
+
ip && ip['message'] && ip['message'].downcase == 'no addresses found'
|
22
|
+
end
|
23
|
+
|
24
|
+
def errors
|
25
|
+
{
|
26
|
+
:cidr => "A 'cidr' parameter for the subnet must be provided(e.g. 100.10.10.0/24)",
|
27
|
+
:mac => "A 'mac' address must be provided(e.g. 00:0a:95:9d:68:10)",
|
28
|
+
:ip => "Missing 'ip' parameter. An IPv4 address must be provided(e.g. 100.10.10.22)",
|
29
|
+
:section_name => "A 'section_name' must be provided",
|
30
|
+
:no_free_ip => "There are no more free addresses in this subnet",
|
31
|
+
:no_section => "Section not found in External IPAM.",
|
32
|
+
:no_subnet => "The specified subnet does not exist in External IPAM.",
|
33
|
+
:no_connection => "Unable to connect to External IPAM server"
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_proxy_ipam
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christopher Smith
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-09-
|
11
|
+
date: 2019-09-13 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Smart proxy plugin for IPAM integration with various IPAM providers
|
14
14
|
email: chrisjsmith001@gmail.com
|
@@ -27,6 +27,7 @@ files:
|
|
27
27
|
- lib/smart_proxy_ipam/ipam_main.rb
|
28
28
|
- lib/smart_proxy_ipam/phpipam/phpipam_api.rb
|
29
29
|
- lib/smart_proxy_ipam/phpipam/phpipam_client.rb
|
30
|
+
- lib/smart_proxy_ipam/phpipam/phpipam_helper.rb
|
30
31
|
- lib/smart_proxy_ipam/version.rb
|
31
32
|
- settings.d/external_ipam.yml
|
32
33
|
- settings.d/external_ipam.yml.example
|