77
88# Import necessary packages
99import traceback
10+ from itertools import chain
1011from ansible .module_utils .compat import ipaddress
1112from ansible .module_utils ._text import to_text
1213
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
288184ALLOWED_QUERY_PARAMS = {
289185 "aggregate" : set (["prefix" , "rir" ]),
290186 "circuit" : set (["cid" ]),
344240 ]
345241)
346242
347- # This is used when converting static choices to an ID value acceptable to Netbox API
348243REQUIRED_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
0 commit comments