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