Skip to content

Commit 96540e8

Browse files
Feature: Removed static choices and return choices from API (#90)
1 parent de289d7 commit 96540e8

File tree

14 files changed

+623
-301
lines changed

14 files changed

+623
-301
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
[![Build Status](https://travis-ci.com/netbox-community/ansible_modules.svg?branch=devel)](https://travis-ci.com/netbox-community/ansible_modules)
22
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
3+
![Release](https://img.shields.io/github/v/release/netbox-community/ansible_modules)
34

45
# Netbox modules for Ansible using Ansible Collections
56

67
## Requirements
78

89
- Netbox 2.5+
9-
- **pynetbox 4.1.0+**
10+
- **pynetbox 4.2.5+**
1011
- Python 3.6+
1112
- Ansible 2.9+
1213

plugins/module_utils/netbox_utils.py

Lines changed: 39 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
# Import necessary packages
99
import traceback
10+
from itertools import chain
1011
from ansible.module_utils.compat import ipaddress
1112
from ansible.module_utils._text import to_text
1213

@@ -180,111 +181,6 @@
180181
"vrfs": "vrf",
181182
}
182183

183-
FACE_ID = dict(front=0, rear=1)
184-
185-
DEVICE_STATUS = dict(offline=0, active=1, planned=2, staged=3, failed=4, inventory=5)
186-
187-
IP_ADDRESS_STATUS = dict(active=1, reserved=2, deprecated=3, dhcp=5)
188-
189-
IP_ADDRESS_ROLE = dict(
190-
loopback=10, secondary=20, anycast=30, vip=40, vrrp=41, hsrp=42, glbp=43, carp=44
191-
)
192-
193-
PREFIX_STATUS = dict(container=0, active=1, reserved=2, deprecated=3)
194-
195-
SITE_STATUS = dict(active=1, planned=2, retired=4)
196-
197-
RACK_STATUS = dict(active=3, planned=2, reserved=0, available=1, deprecated=4)
198-
199-
RACK_UNIT = dict(millimeters=1000, inches=2000)
200-
201-
SUBDEVICE_ROLES = dict(parent=True, child=False)
202-
203-
VLAN_STATUS = dict(active=1, reserved=2, deprecated=3)
204-
205-
SERVICE_PROTOCOL = dict(tcp=6, udp=17)
206-
207-
RACK_TYPE = {
208-
"2-post frame": 100,
209-
"4-post frame": 200,
210-
"4-post cabinet": 300,
211-
"wall-mounted frame": 1000,
212-
"wall-mounted cabinet": 1100,
213-
}
214-
215-
INTF_FORM_FACTOR = {
216-
"virtual": 0,
217-
"link aggregation group (lag)": 200,
218-
"100base-tx (10/100me)": 800,
219-
"1000base-t (1ge)": 1000,
220-
"10gbase-t (10ge)": 1150,
221-
"10gbase-cx4 (10ge)": 1170,
222-
"gbic (1ge)": 1050,
223-
"sfp (1ge)": 1100,
224-
"2.5gbase-t (2.5ge)": 1120,
225-
"5gbase-t (5ge)": 1130,
226-
"sfp+ (10ge)": 1200,
227-
"xfp (10ge)": 1300,
228-
"xenpak (10ge)": 1310,
229-
"x2 (10ge)": 1320,
230-
"sfp28 (25ge)": 1350,
231-
"qsfp+ (40ge)": 1400,
232-
"qsfp28 (50ge)": 1420,
233-
"cfp (100ge)": 1500,
234-
"cfp2 (100ge)": 1510,
235-
"cfp2 (200ge)": 1650,
236-
"cfp4 (100ge)": 1520,
237-
"cisco cpak (100ge)": 1550,
238-
"qsfp28 (100ge)": 1600,
239-
"qsfp56 (200ge)": 1700,
240-
"qsfp-dd (400ge)": 1750,
241-
"ieee 802.11a": 2600,
242-
"ieee 802.11b/g": 2610,
243-
"ieee 802.11n": 2620,
244-
"ieee 802.11ac": 2630,
245-
"ieee 802.11ad": 2640,
246-
"gsm": 2810,
247-
"cdma": 2820,
248-
"lte": 2830,
249-
"oc-3/stm-1": 6100,
250-
"oc-12/stm-4": 6200,
251-
"oc-48/stm-16": 6300,
252-
"oc-192/stm-64": 6400,
253-
"oc-768/stm-256": 6500,
254-
"oc-1920/stm-640": 6600,
255-
"oc-3840/stm-1234": 6700,
256-
"sfp (1gfc)": 3010,
257-
"sfp (2gfc)": 3020,
258-
"sfp (4gfc)": 3040,
259-
"sfp+ (8gfc)": 3080,
260-
"sfp+ (16gfc)": 3160,
261-
"sfp28 (32gfc)": 3320,
262-
"qsfp28 (128gfc)": 3400,
263-
"t1 (1.544 mbps)": 4000,
264-
"e1 (2.048 mbps)": 4010,
265-
"t3 (45 mbps)": 4040,
266-
"e3 (34 mbps)": 4050,
267-
"cisco stackwise": 5000,
268-
"cisco stackwise plus": 5050,
269-
"cisco flexstack": 5100,
270-
"cisco flexstack plus": 5150,
271-
"juniper vcp": 5200,
272-
"extreme summitstack": 5300,
273-
"extreme summitstack-128": 5310,
274-
"extreme summitstack-256": 5320,
275-
"extreme summitstack-512": 5330,
276-
"other": 32767,
277-
}
278-
279-
INTF_MODE = {"access": 100, "tagged": 200, "tagged all": 300}
280-
281-
VIRTUAL_MACHINE_STATUS = dict(offline=0, active=1, staged=3)
282-
283-
CIRCUIT_STATUS = dict(
284-
deprovisioning=0, active=1, planned=2, provisioning=3, offline=4, decommissioned=5,
285-
)
286-
287-
# This is used when attempting to search for existing endpoints
288184
ALLOWED_QUERY_PARAMS = {
289185
"aggregate": set(["prefix", "rir"]),
290186
"circuit": set(["cid"]),
@@ -344,19 +240,18 @@
344240
]
345241
)
346242

347-
# This is used when converting static choices to an ID value acceptable to Netbox API
348243
REQUIRED_ID_FIND = {
349-
"circuits": [{"status": CIRCUIT_STATUS}],
350-
"devices": [{"status": DEVICE_STATUS, "face": FACE_ID}],
351-
"device_types": [{"subdevice_role": SUBDEVICE_ROLES}],
352-
"interfaces": [{"form_factor": INTF_FORM_FACTOR, "mode": INTF_MODE}],
353-
"ip_addresses": [{"status": IP_ADDRESS_STATUS, "role": IP_ADDRESS_ROLE}],
354-
"prefixes": [{"status": PREFIX_STATUS}],
355-
"racks": [{"status": RACK_STATUS, "outer_unit": RACK_UNIT, "type": RACK_TYPE}],
356-
"services": [{"protocol": SERVICE_PROTOCOL}],
357-
"sites": [{"status": SITE_STATUS}],
358-
"virtual_machines": [{"status": VIRTUAL_MACHINE_STATUS, "face": FACE_ID}],
359-
"vlans": [{"status": VLAN_STATUS}],
244+
"circuits": set(["status"]),
245+
"devices": set(["status", "face"]),
246+
"device_types": set(["subdevice_role"]),
247+
"interfaces": set(["form_factor", "mode"]),
248+
"ip_addresses": set(["status", "role"]),
249+
"prefixes": set(["status"]),
250+
"racks": set(["status", "outer_unit", "type"]),
251+
"services": set(["protocol"]),
252+
"sites": set(["status"]),
253+
"virtual_machines": set(["status", "face"]),
254+
"vlans": set(["status"]),
360255
}
361256

362257
# This is used to map non-clashing keys to Netbox API compliant keys to prevent bad logic in code for similar keys but different modules
@@ -416,6 +311,7 @@ def __init__(self, module, endpoint, nb_client=None):
416311
self.state = self.module.params["state"]
417312
self.check_mode = self.module.check_mode
418313
self.endpoint = endpoint
314+
self.version = None
419315

420316
if not HAS_PYNETBOX:
421317
self.module.fail_json(
@@ -443,7 +339,7 @@ def _connect_netbox_api(self, url, token, ssl_verify):
443339
try:
444340
nb = pynetbox.api(url, token=token, ssl_verify=ssl_verify)
445341
try:
446-
self.version = nb.version
342+
self.version = float(nb.version)
447343
except AttributeError:
448344
self.module.fail_json(msg="Must have pynetbox >=4.1.0")
449345
except Exception:
@@ -486,6 +382,9 @@ def _convert_identical_keys(self, data):
486382
Returns data
487383
:params data (dict): Data dictionary after _find_ids method ran
488384
"""
385+
if self.version and self.version >= 2.7:
386+
if data.get("form_factor"):
387+
data["type"] = data.pop("form_factor")
489388
for key in data:
490389
if key in CONVERT_KEYS:
491390
new_key = CONVERT_KEYS[key]
@@ -580,6 +479,23 @@ def _build_query_params(self, parent, module_data, child=None):
580479
query_dict = self._convert_identical_keys(query_dict)
581480
return query_dict
582481

482+
def _fetch_choice_value(self, search, endpoint):
483+
app = self._find_app(endpoint)
484+
nb_app = getattr(self.nb, app)
485+
nb_endpoint = getattr(nb_app, endpoint)
486+
endpoint_choices = nb_endpoint.choices()
487+
488+
choices = [x for x in chain.from_iterable(endpoint_choices.values())]
489+
490+
for item in choices:
491+
if item["display_name"].lower() == search.lower():
492+
return item["value"]
493+
elif item["value"] == search.lower():
494+
return item["value"]
495+
self._handle_errors(
496+
msg="%s was not found as a valid choice for %s" % (search, endpoint)
497+
)
498+
583499
def _change_choices_id(self, endpoint, data):
584500
"""Used to change data that is static and under _choices for the application.
585501
ex. DEVICE_STATUS
@@ -590,17 +506,11 @@ def _change_choices_id(self, endpoint, data):
590506
if REQUIRED_ID_FIND.get(endpoint):
591507
required_choices = REQUIRED_ID_FIND[endpoint]
592508
for choice in required_choices:
593-
for key, value in choice.items():
594-
if data.get(key):
595-
if isinstance(data[key], int):
596-
break
597-
try:
598-
data[key] = value[data[key].lower()]
599-
except KeyError:
600-
self._handle_errors(
601-
msg="%s may not be a valid choice. If it is valid, please submit bug report."
602-
% (key)
603-
)
509+
if data.get(choice):
510+
if isinstance(data[choice], int):
511+
continue
512+
choice_value = self._fetch_choice_value(data[choice], endpoint)
513+
data[choice] = choice_value
604514

605515
return data
606516

tests/integration/integration-tests.yml

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@
292292
device: test100
293293
name: GigabitEthernet3
294294
enabled: false
295-
form_factor: 1000Base-t (1GE)
295+
form_factor: 1000Base-T (1GE)
296296
lag:
297297
name: port-channel1
298298
mtu: 1600
@@ -325,7 +325,7 @@
325325
device: test100
326326
name: GigabitEthernet21
327327
enabled: false
328-
form_factor: 1000Base-t (1GE)
328+
form_factor: 1000Base-T (1GE)
329329
untagged_vlan:
330330
name: Wireless
331331
site: Test Site
@@ -382,7 +382,7 @@
382382
device: test100
383383
name: GigabitEthernet4
384384
enabled: false
385-
form_factor: 1000Base-t (1GE)
385+
form_factor: 1000Base-T (1GE)
386386
lag: "port-channel1"
387387
mtu: 1600
388388
mgmt_only: false
@@ -4171,11 +4171,11 @@
41714171
- test_five['msg'] == "circuit_termination test_circuit_a deleted"
41724172
-
41734173

4174-
##
4175-
##
4176-
### NETBOX_SERVICE
4177-
##
4178-
##
4174+
##
4175+
##
4176+
### NETBOX_SERVICE
4177+
##
4178+
##
41794179
- name: "1 - Device with required information needs to add new service"
41804180
netbox_device:
41814181
netbox_url: "http://localhost:32768"
@@ -4250,7 +4250,6 @@
42504250
- test_service_update['diff']['after']['protocol'] == 17
42514251
- test_service_update['msg'] == "services node-exporter updated"
42524252

4253-
42544253
- name: "NETBOX_SERVICE: Test service deletion"
42554254
netbox_service:
42564255
netbox_url: "http://localhost:32768"
@@ -4271,7 +4270,6 @@
42714270
- test_service_delete['diff']['before']['state'] == "present"
42724271
- test_service_delete['msg'] == "services node-exporter deleted"
42734272

4274-
42754273
##
42764274
##
42774275
### NETBOX_LOOKUP
@@ -4317,7 +4315,7 @@
43174315
device_role: "Core Switch"
43184316
site: "Test Site"
43194317
status: "Staged"
4320-
tags:
4318+
tags:
43214319
- "nolookup"
43224320
state: present
43234321

@@ -4331,15 +4329,12 @@
43314329
device_role: "Core Switch"
43324330
site: "Test Site"
43334331
status: "Staged"
4334-
tags:
4332+
tags:
43354333
- "lookup"
43364334
state: present
43374335

43384336
- name: "NETBOX_LOOKUP 7: Device query returns exactly the L2 device"
43394337
assert:
43404338
that: "{{ query_result|json_query('[?value.display_name==`L2`]')|count }} == 1"
43414339
vars:
4342-
query_result: "{{ query('netbox_community.ansible_modules.netbox', 'devices', api_filter='role=core-switch tag=lookup', api_endpoint='http://localhost:32768', token='0123456789abcdef0123456789abcdef01234567') }}"
4343-
4344-
4345-
4340+
query_result: "{{ query('netbox_community.ansible_modules.netbox', 'devices', api_filter='role=core-switch tag=lookup', api_endpoint='http://localhost:32768', token='0123456789abcdef0123456789abcdef01234567') }}"
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"status": [
3+
{
4+
"value": "planned",
5+
"display_name": "Planned"
6+
},
7+
{
8+
"value": "provisioning",
9+
"display_name": "Provisioning"
10+
},
11+
{
12+
"value": "active",
13+
"display_name": "Active"
14+
},
15+
{
16+
"value": "offline",
17+
"display_name": "Offline"
18+
},
19+
{
20+
"value": "deprovisioning",
21+
"display_name": "Deprovisioning"
22+
},
23+
{
24+
"value": "decommissioned",
25+
"display_name": "Decommissioned"
26+
}
27+
]
28+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"subdevice_role": [
3+
{
4+
"value": "parent",
5+
"display_name": "Parent"
6+
},
7+
{
8+
"value": "child",
9+
"display_name": "Child"
10+
}
11+
]
12+
}

0 commit comments

Comments
 (0)