smart_proxy_ipam 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -3
- data/lib/smart_proxy_ipam/ipam_http_config.ru +1 -6
- data/lib/smart_proxy_ipam/phpipam/phpipam_api.rb +149 -31
- data/lib/smart_proxy_ipam/phpipam/phpipam_client.rb +80 -14
- data/lib/smart_proxy_ipam/version.rb +1 -1
- data/settings.d/external_ipam.yml +0 -4
- data/settings.d/external_ipam.yml.example +0 -4
- metadata +2 -3
- data/lib/smart_proxy_ipam/ipam/ipam_api.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f64f77c875cf2954bfe2d9fb2e1c56e1848e402ec8259959382771da8cd7158b
|
4
|
+
data.tar.gz: a4e3900fd2ad8a2ec16e9ab3887dd6412ce567b16f7cdb98278407c80add8e9c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3608be9d60dda80356bdf9e9c59f79cf0edcd1c69210cb7c10aa818237ec85e1485e8fd12d2ee1c1d527ca3ab926d1090ef20c2a6bc4faf8f2c4b262f86df44c
|
7
|
+
data.tar.gz: a99d59aab8ce7b3f1a1b6d4b90e861fef90435c9071e0b42d3f7863834a660050d8e96af471473b4e6f0a0b8db8b1f1d212502e01ac80073e79a1542d731fff6
|
data/README.md
CHANGED
@@ -1,8 +1,12 @@
|
|
1
|
-
#
|
1
|
+
# smart_proxy_ipam
|
2
2
|
|
3
|
-
Foreman Smart Proxy plugin for IPAM integration with
|
3
|
+
Foreman Smart Proxy plugin for IPAM integration with various IPAM providers.
|
4
|
+
|
5
|
+
Currently supported Providers:
|
6
|
+
1. [phpIPAM](https://phpipam.net/).
|
7
|
+
|
8
|
+
Provides a basic Dashboard for viewing phpIPAM sections, subnets. Also supports obtaining of the next available IPv4 address for a given subnet(via [IPAM Smart Proxy Plugin](https://github.com/grizzthedj/smart_proxy_ipam)).
|
4
9
|
|
5
|
-
Used for obtaining the next available IPv4 address from phpIPAM, for a given subnet.
|
6
10
|
|
7
11
|
## Installation
|
8
12
|
|
@@ -3,11 +3,27 @@ require 'smart_proxy_ipam/ipam'
|
|
3
3
|
require 'smart_proxy_ipam/ipam_main'
|
4
4
|
require 'smart_proxy_ipam/phpipam/phpipam_client'
|
5
5
|
|
6
|
+
# TODO: Refactor later to handle multiple IPAM providers. For now, it is
|
7
|
+
# just phpIPAM that is supported
|
6
8
|
module Proxy::Phpipam
|
7
9
|
class Api < ::Sinatra::Base
|
8
10
|
include ::Proxy::Log
|
9
11
|
helpers ::Proxy::Helpers
|
10
12
|
|
13
|
+
get '/providers' do
|
14
|
+
content_type :json
|
15
|
+
{ipam_providers: ['phpIPAM']}.to_json
|
16
|
+
end
|
17
|
+
|
18
|
+
# Gets the next available IP address based on a given subnet
|
19
|
+
#
|
20
|
+
# Input: cidr(string): CIDR address in the format: "100.20.20.0/24"
|
21
|
+
# Returns: Hash with "next_ip", or hash with "error"
|
22
|
+
# Examples:
|
23
|
+
# Response if success:
|
24
|
+
# {"cidr":"100.20.20.0/24","next_ip":"100.20.20.11"}
|
25
|
+
# Response if error:
|
26
|
+
# {"error":"The specified subnet does not exist in phpIPAM."}
|
11
27
|
get '/next_ip' do
|
12
28
|
content_type :json
|
13
29
|
|
@@ -22,10 +38,10 @@ module Proxy::Phpipam
|
|
22
38
|
response = phpipam_client.get_subnet(cidr)
|
23
39
|
|
24
40
|
if response['message'] && response['message'].downcase == "no subnets found"
|
25
|
-
return {error: "The specified subnet does not exist in
|
41
|
+
return {error: "The specified subnet does not exist in External IPAM."}.to_json
|
26
42
|
end
|
27
43
|
|
28
|
-
subnet_id = response[
|
44
|
+
subnet_id = JSON.parse(response)[0]['id']
|
29
45
|
response = phpipam_client.get_next_ip(subnet_id)
|
30
46
|
|
31
47
|
if response['message'] && response['message'].downcase == "no free addresses found"
|
@@ -34,10 +50,29 @@ module Proxy::Phpipam
|
|
34
50
|
|
35
51
|
{cidr: cidr, next_ip: response['data']}.to_json
|
36
52
|
rescue Errno::ECONNREFUSED
|
37
|
-
return {error: "Unable to connect to
|
53
|
+
return {error: "Unable to connect to External IPAM server"}.to_json
|
38
54
|
end
|
39
55
|
end
|
40
56
|
|
57
|
+
# Gets the subnet from phpIPAM
|
58
|
+
#
|
59
|
+
# Input: cidr(string): CIDR address in the format: "100.20.20.0/24"
|
60
|
+
# Returns: JSON with "data" key on success, or JSON with "error" key when there is an error
|
61
|
+
# Examples:
|
62
|
+
# Response if subnet exists:
|
63
|
+
# {
|
64
|
+
# "code":200,"success":true,"data":[{"id":"12","subnet":"100.30.30.0","mask":"24",
|
65
|
+
# "sectionId":"5", "description":"Test Subnet","linked_subnet":null,"firewallAddressObject":null,
|
66
|
+
# "vrfId":"0","masterSubnetId":"0","allowRequests":"0","vlanId":"0","showName":"0","device":"0",
|
67
|
+
# "permissions":"[]","pingSubnet":"0","discoverSubnet":"0","DNSrecursive":"0","DNSrecords":"0",
|
68
|
+
# "nameserverId":"0","scanAgent":"0","isFolder":"0","isFull":"0","tag":"2","threshold":"0",
|
69
|
+
# "location":"0","editDate":null,"lastScan":null,"lastDiscovery":null}],"time":0.009
|
70
|
+
# }
|
71
|
+
#
|
72
|
+
# Response if subnet not exists):
|
73
|
+
# {
|
74
|
+
# "code":200,"success":0,"message":"No subnets found","time":0.01
|
75
|
+
# }
|
41
76
|
get '/get_subnet' do
|
42
77
|
content_type :json
|
43
78
|
|
@@ -52,10 +87,22 @@ module Proxy::Phpipam
|
|
52
87
|
subnet = phpipam_client.get_subnet(cidr)
|
53
88
|
subnet.to_json
|
54
89
|
rescue Errno::ECONNREFUSED
|
55
|
-
return {error: "Unable to connect to
|
90
|
+
return {error: "Unable to connect to External IPAM server"}.to_json
|
56
91
|
end
|
57
92
|
end
|
58
93
|
|
94
|
+
# Get a list of sections from external ipam
|
95
|
+
#
|
96
|
+
# Input: None
|
97
|
+
# Returns: An array of sections on success, hash with "error" key otherwise
|
98
|
+
# Examples
|
99
|
+
# Response if success: [
|
100
|
+
# {"id":"5","name":"Awesome Section","description":"A totally awesome Section","masterSection":"0",
|
101
|
+
# "permissions":"[]","strictMode":"1","subnetOrdering":"default","order":null,
|
102
|
+
# "editDate":"2019-04-19 21:49:55","showVLAN":"1","showVRF":"1","showSupernetOnly":"1","DNS":null}]
|
103
|
+
# ]
|
104
|
+
# Response if error:
|
105
|
+
# {"error":"Unable to connect to phpIPAM server"}
|
59
106
|
get '/sections' do
|
60
107
|
content_type :json
|
61
108
|
|
@@ -64,10 +111,65 @@ module Proxy::Phpipam
|
|
64
111
|
sections = phpipam_client.get_sections
|
65
112
|
sections.to_json
|
66
113
|
rescue Errno::ECONNREFUSED
|
67
|
-
return {error: "Unable to connect to
|
114
|
+
return {error: "Unable to connect to External IPAM server"}.to_json
|
68
115
|
end
|
69
116
|
end
|
70
117
|
|
118
|
+
# Get a list of subnets for given external ipam section
|
119
|
+
#
|
120
|
+
# Input: section_id(integer). The id of the external ipam section
|
121
|
+
# Returns: Array of subnets(as json) in "data" key on success, hash with error otherwise
|
122
|
+
# Examples:
|
123
|
+
# Response if success:
|
124
|
+
# {
|
125
|
+
# "code":200,
|
126
|
+
# "success":true,
|
127
|
+
# "data":[
|
128
|
+
# {
|
129
|
+
# "id":"24",
|
130
|
+
# "subnet":"100.10.10.0",
|
131
|
+
# "mask":"24",
|
132
|
+
# "sectionId":"10",
|
133
|
+
# "description":"wrgwgwefwefw",
|
134
|
+
# "linked_subnet":null,
|
135
|
+
# "firewallAddressObject":null,
|
136
|
+
# "vrfId":"0",
|
137
|
+
# "masterSubnetId":"0",
|
138
|
+
# "allowRequests":"0",
|
139
|
+
# "vlanId":"0",
|
140
|
+
# "showName":"0",
|
141
|
+
# "device":"0",
|
142
|
+
# "permissions":"[]",
|
143
|
+
# "pingSubnet":"0",
|
144
|
+
# "discoverSubnet":"0",
|
145
|
+
# "DNSrecursive":"0",
|
146
|
+
# "DNSrecords":"0",
|
147
|
+
# "nameserverId":"0",
|
148
|
+
# "scanAgent":"0",
|
149
|
+
# "isFolder":"0",
|
150
|
+
# "isFull":"0",
|
151
|
+
# "tag":"2",
|
152
|
+
# "threshold":"0",
|
153
|
+
# "location":"0",
|
154
|
+
# "editDate":null,
|
155
|
+
# "lastScan":null,
|
156
|
+
# "lastDiscovery":null,
|
157
|
+
# "usage":{
|
158
|
+
# "used":"0",
|
159
|
+
# "maxhosts":"254",
|
160
|
+
# "freehosts":"254",
|
161
|
+
# "freehosts_percent":100,
|
162
|
+
# "Offline_percent":0,
|
163
|
+
# "Used_percent":0,
|
164
|
+
# "Reserved_percent":0,
|
165
|
+
# "DHCP_percent":0
|
166
|
+
# }
|
167
|
+
# }
|
168
|
+
# ],
|
169
|
+
# "time":0.012
|
170
|
+
# }
|
171
|
+
# Response if error:
|
172
|
+
# {"error":"Unable to connect to phpIPAM server"}
|
71
173
|
get '/sections/:section_id/subnets' do
|
72
174
|
content_type :json
|
73
175
|
|
@@ -82,10 +184,20 @@ module Proxy::Phpipam
|
|
82
184
|
subnets = phpipam_client.get_subnets(section_id)
|
83
185
|
subnets.to_json
|
84
186
|
rescue Errno::ECONNREFUSED
|
85
|
-
return {error: "Unable to connect to
|
187
|
+
return {error: "Unable to connect to External IPAM server"}.to_json
|
86
188
|
end
|
87
189
|
end
|
88
190
|
|
191
|
+
# Checks whether an IP address has already been taken in external ipam.
|
192
|
+
#
|
193
|
+
# Inputs: 1. ip(string). IP address to be checked.
|
194
|
+
# 2. cidr(string): CIDR address in the format: "100.20.20.0/24"
|
195
|
+
# Returns: JSON object with 'exists' field being either true or false
|
196
|
+
# Example:
|
197
|
+
# Response if exists:
|
198
|
+
# {"ip":"100.20.20.18","exists":true}
|
199
|
+
# Response if not exists:
|
200
|
+
# {"ip":"100.20.20.18","exists":false}
|
89
201
|
get '/ip_exists' do
|
90
202
|
content_type :json
|
91
203
|
|
@@ -100,31 +212,27 @@ module Proxy::Phpipam
|
|
100
212
|
subnet = phpipam_client.get_subnet(cidr)
|
101
213
|
|
102
214
|
if subnet['message'] && subnet['message'].downcase == "no subnets found"
|
103
|
-
return {error: "The specified subnet does not exist in
|
215
|
+
return {error: "The specified subnet does not exist in External IPAM."}.to_json
|
104
216
|
end
|
105
217
|
|
106
|
-
subnet_id = subnet[
|
107
|
-
|
108
|
-
|
109
|
-
# We need to check subnet usage first in the case there are zero ips in the subnet. Checking
|
110
|
-
# the ip existence on an empty subnet returns a malformed response from phpIPAM, containing
|
111
|
-
# HTML in the JSON response.
|
112
|
-
if usage['data']['used'] == "0"
|
113
|
-
return {ip: ip, exists: false}.to_json
|
114
|
-
else
|
115
|
-
response = phpipam_client.ip_exists(ip, subnet_id)
|
116
|
-
|
117
|
-
if response && response['message'] && response['message'].downcase == 'no addresses found'
|
118
|
-
return {ip: ip, exists: false}.to_json
|
119
|
-
else
|
120
|
-
return {ip: ip, exists: true}.to_json
|
121
|
-
end
|
122
|
-
end
|
218
|
+
subnet_id = JSON.parse(subnet)[0]['id']
|
219
|
+
phpipam_client.ip_exists(ip, subnet_id)
|
123
220
|
rescue Errno::ECONNREFUSED
|
124
|
-
return {error: "Unable to connect to
|
221
|
+
return {error: "Unable to connect to External IPAM server"}.to_json
|
125
222
|
end
|
126
223
|
end
|
127
224
|
|
225
|
+
# Adds an IP address to the specified subnet
|
226
|
+
#
|
227
|
+
# Inputs: 1. ip(string). IP address to be added.
|
228
|
+
# 2. subnet_id(integer). The id of the external ipam subnet
|
229
|
+
# 3. description(string). IP address description
|
230
|
+
# Returns: Hash with "message" on success, or hash with "error"
|
231
|
+
# Examples:
|
232
|
+
# Response if success:
|
233
|
+
# {"message":"IP 100.10.10.123 added to subnet 100.10.10.0/24 successfully."}
|
234
|
+
# Response if error:
|
235
|
+
# {"error":"The specified subnet does not exist in phpIPAM."}
|
128
236
|
post '/add_ip_to_subnet' do
|
129
237
|
content_type :json
|
130
238
|
|
@@ -139,19 +247,29 @@ module Proxy::Phpipam
|
|
139
247
|
response = phpipam_client.get_subnet(cidr)
|
140
248
|
|
141
249
|
if response['message'] && response['message'].downcase == "no subnets found"
|
142
|
-
return {error: "The specified subnet does not exist in
|
250
|
+
return {error: "The specified subnet does not exist in External IPAM."}.to_json
|
143
251
|
end
|
144
252
|
|
145
|
-
subnet_id = response[
|
253
|
+
subnet_id = JSON.parse(response)[0]['id']
|
146
254
|
|
147
255
|
phpipam_client.add_ip_to_subnet(ip, subnet_id, 'Address auto added by Foreman')
|
148
256
|
|
149
257
|
{message: "IP #{ip} added to subnet #{cidr} successfully."}.to_json
|
150
258
|
rescue Errno::ECONNREFUSED
|
151
|
-
return {error: "Unable to connect to
|
259
|
+
return {error: "Unable to connect to External IPAM server"}.to_json
|
152
260
|
end
|
153
261
|
end
|
154
262
|
|
263
|
+
# Deletes IP address from a given subnet
|
264
|
+
#
|
265
|
+
# Inputs: 1. ip(string). IP address to be checked.
|
266
|
+
# 2. cidr(string): CIDR address in the format: "100.20.20.0/24"
|
267
|
+
# Returns: JSON object
|
268
|
+
# Example:
|
269
|
+
# Response if success:
|
270
|
+
# {"code": 200, "success": true, "message": "Address deleted", "time": 0.017}
|
271
|
+
# Response if error:
|
272
|
+
# {"code": 404, "success": 0, "message": "Address does not exist", "time": 0.008}
|
155
273
|
post '/delete_ip_from_subnet' do
|
156
274
|
content_type :json
|
157
275
|
|
@@ -166,16 +284,16 @@ module Proxy::Phpipam
|
|
166
284
|
response = phpipam_client.get_subnet(cidr)
|
167
285
|
|
168
286
|
if response['message'] && response['message'].downcase == "no subnets found"
|
169
|
-
return {error: "The specified subnet does not exist in
|
287
|
+
return {error: "The specified subnet does not exist in External IPAM."}.to_json
|
170
288
|
end
|
171
289
|
|
172
|
-
subnet_id = response[
|
290
|
+
subnet_id = JSON.parse(response)[0]['id']
|
173
291
|
|
174
292
|
phpipam_client.delete_ip_from_subnet(ip, subnet_id)
|
175
293
|
|
176
294
|
{message: "IP #{ip} deleted from subnet #{cidr} successfully."}.to_json
|
177
295
|
rescue Errno::ECONNREFUSED
|
178
|
-
return {error: "Unable to connect to
|
296
|
+
return {error: "Unable to connect to External IPAM server"}.to_json
|
179
297
|
end
|
180
298
|
end
|
181
299
|
|
@@ -6,22 +6,40 @@ require 'smart_proxy_ipam/ipam'
|
|
6
6
|
require 'smart_proxy_ipam/ipam_main'
|
7
7
|
|
8
8
|
module Proxy::Phpipam
|
9
|
-
class PhpipamClient
|
9
|
+
class PhpipamClient
|
10
10
|
def initialize
|
11
11
|
conf = Proxy::Ipam.get_config[:phpipam]
|
12
12
|
@phpipam_config = {url: conf[:url], user: conf[:user], password: conf[:password]}
|
13
|
-
@api_base = conf[:url]
|
13
|
+
@api_base = "#{conf[:url]}/api/#{conf[:user]}/"
|
14
14
|
@token = nil
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
|
19
|
-
get(url)
|
17
|
+
def init_reservations
|
18
|
+
@@ipam_reservations = {}
|
20
19
|
end
|
21
20
|
|
22
|
-
def
|
23
|
-
url =
|
24
|
-
get(url)
|
21
|
+
def get_subnet(cidr)
|
22
|
+
url = "subnets/cidr/#{cidr.to_s}"
|
23
|
+
subnets = get(url)
|
24
|
+
response = []
|
25
|
+
|
26
|
+
if subnets
|
27
|
+
if subnets['message'] && subnets['message'].downcase == 'no subnets found'
|
28
|
+
return {"message": "no subnets found"}.to_json
|
29
|
+
else
|
30
|
+
# Only return the relevant fields to Foreman
|
31
|
+
subnets['data'].each do |subnet|
|
32
|
+
response.push({
|
33
|
+
"id": subnet['id'],
|
34
|
+
"subnet": subnet['subnet'],
|
35
|
+
"description": subnet['description'],
|
36
|
+
"mask": subnet['mask']
|
37
|
+
})
|
38
|
+
end
|
39
|
+
|
40
|
+
return response.to_json
|
41
|
+
end
|
42
|
+
end
|
25
43
|
end
|
26
44
|
|
27
45
|
def add_ip_to_subnet(ip, subnet_id, desc)
|
@@ -30,26 +48,74 @@ module Proxy::Phpipam
|
|
30
48
|
end
|
31
49
|
|
32
50
|
def get_sections
|
33
|
-
get('sections/')['data']
|
51
|
+
sections = get('sections/')['data']
|
52
|
+
response = []
|
53
|
+
|
54
|
+
if sections
|
55
|
+
sections.each do |section|
|
56
|
+
response.push({
|
57
|
+
"id": section['id'],
|
58
|
+
"name": section['name'],
|
59
|
+
"description": section['description']
|
60
|
+
})
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
response
|
34
65
|
end
|
35
66
|
|
36
67
|
def get_subnets(section_id)
|
37
|
-
get(
|
68
|
+
subnets = get("sections/#{section_id.to_s}/subnets/")
|
69
|
+
response = []
|
70
|
+
|
71
|
+
if subnets && subnets['data']
|
72
|
+
# Only return the relevant data
|
73
|
+
subnets['data'].each do |subnet|
|
74
|
+
response.push({
|
75
|
+
"id": subnet['id'],
|
76
|
+
"subnet": subnet['subnet'],
|
77
|
+
"mask": subnet['mask'],
|
78
|
+
"sectionId": subnet['sectionId'],
|
79
|
+
"description": subnet['description']
|
80
|
+
})
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
response
|
38
85
|
end
|
39
86
|
|
40
87
|
def ip_exists(ip, subnet_id)
|
41
|
-
|
88
|
+
usage = get_subnet_usage(subnet_id)
|
89
|
+
|
90
|
+
# We need to check subnet usage first in the case there are zero ips in the subnet. Checking
|
91
|
+
# the ip existence on an empty subnet returns a malformed response from phpIPAM, containing
|
92
|
+
# HTML in the JSON response.
|
93
|
+
if usage['data']['used'] == "0"
|
94
|
+
return {ip: ip, exists: false}.to_json
|
95
|
+
else
|
96
|
+
response = get("subnets/#{subnet_id.to_s}/addresses/#{ip}/")
|
97
|
+
|
98
|
+
if response && response['message'] && response['message'].downcase == 'no addresses found'
|
99
|
+
return {ip: ip, exists: false}.to_json
|
100
|
+
else
|
101
|
+
return {ip: ip, exists: true}.to_json
|
102
|
+
end
|
103
|
+
end
|
42
104
|
end
|
43
105
|
|
106
|
+
|
44
107
|
def get_subnet_usage(subnet_id)
|
45
|
-
get(
|
108
|
+
get("subnets/#{subnet_id.to_s}/usage/")
|
46
109
|
end
|
47
110
|
|
48
111
|
def delete_ip_from_subnet(ip, subnet_id)
|
49
|
-
delete(
|
112
|
+
delete("addresses/#{ip}/#{subnet_id.to_s}/")
|
50
113
|
end
|
51
114
|
|
52
|
-
|
115
|
+
def get_next_ip(subnet_id)
|
116
|
+
url = "subnets/#{subnet_id.to_s}/first_free/"
|
117
|
+
get(url)
|
118
|
+
end
|
53
119
|
|
54
120
|
def get(path, body=nil)
|
55
121
|
authenticate
|
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.5
|
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-
|
11
|
+
date: 2019-07-10 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
|
@@ -23,7 +23,6 @@ files:
|
|
23
23
|
- bundler.d/ipam.rb
|
24
24
|
- lib/smart_proxy_ipam.rb
|
25
25
|
- lib/smart_proxy_ipam/ipam.rb
|
26
|
-
- lib/smart_proxy_ipam/ipam/ipam_api.rb
|
27
26
|
- lib/smart_proxy_ipam/ipam_http_config.ru
|
28
27
|
- lib/smart_proxy_ipam/ipam_main.rb
|
29
28
|
- lib/smart_proxy_ipam/phpipam/phpipam_api.rb
|
@@ -1,15 +0,0 @@
|
|
1
|
-
require 'sinatra'
|
2
|
-
require 'smart_proxy_ipam/ipam'
|
3
|
-
require 'smart_proxy_ipam/ipam_main'
|
4
|
-
|
5
|
-
module Proxy::Ipam
|
6
|
-
class Api < ::Sinatra::Base
|
7
|
-
include ::Proxy::Log
|
8
|
-
helpers ::Proxy::Helpers
|
9
|
-
|
10
|
-
get '/providers' do
|
11
|
-
content_type :json
|
12
|
-
{ipam_providers: ['phpIPAM']}.to_json
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|