11from collections import Counter
22import pynetbox
33import requests
4+ import os
5+ import glob
46# from pynetbox import RequestError as APIRequestError
57
68class NetBox :
79 def __new__ (cls , * args , ** kwargs ):
810 return super ().__new__ (cls )
9-
11+
1012 def __init__ (self , settings ):
1113 self .counter = Counter (
1214 added = 0 ,
1315 updated = 0 ,
1416 manufacturer = 0 ,
1517 module_added = 0 ,
1618 module_port_added = 0 ,
19+ images = 0 ,
1720 )
1821 self .url = settings .NETBOX_URL
1922 self .token = settings .NETBOX_TOKEN
@@ -25,7 +28,7 @@ def __init__(self, settings):
2528 self .verify_compatibility ()
2629 self .existing_manufacturers = self .get_manufacturers ()
2730 self .device_types = DeviceTypes (self .netbox , self .handle , self .counter )
28-
31+
2932 def connect_api (self ):
3033 try :
3134 self .netbox = pynetbox .api (self .url , token = self .token )
@@ -35,13 +38,13 @@ def connect_api(self):
3538 self .netbox .http_session .verify = False
3639 except Exception as e :
3740 self .handle .exception ("Exception" , 'NetBox API Error' , e )
38-
41+
3942 def get_api (self ):
4043 return self .netbox
41-
44+
4245 def get_counter (self ):
4346 return self .counter
44-
47+
4548 def verify_compatibility (self ):
4649 # nb.version should be the version in the form '3.2'
4750 version_split = [int (x ) for x in self .netbox .version .split ('.' )]
@@ -50,10 +53,10 @@ def verify_compatibility(self):
5053 # Might want to check for the module-types entry as well?
5154 if version_split [0 ] > 3 or (version_split [0 ] == 3 and version_split [1 ] >= 2 ):
5255 self .modules = True
53-
56+
5457 def get_manufacturers (self ):
5558 return {str (item ): item for item in self .netbox .dcim .manufacturers .all ()}
56-
59+
5760 def create_manufacturers (self , vendors ):
5861 to_create = []
5962 self .existing_manufacturers = self .get_manufacturers ()
@@ -64,7 +67,7 @@ def create_manufacturers(self, vendors):
6467 except KeyError :
6568 to_create .append (vendor )
6669 self .handle .verbose_log (f"Manufacturer queued for addition: { vendor ['name' ]} " )
67-
70+
6871 if to_create :
6972 try :
7073 created_manufacturers = self .netbox .dcim .manufacturers .create (to_create )
@@ -75,9 +78,28 @@ def create_manufacturers(self, vendors):
7578 except pynetbox .RequestError as request_error :
7679 self .handle .log ("Error creating manufacturers" )
7780 self .handle .verbose_log (f"Error during manufacturer creation. - { request_error .error } " )
78-
81+
7982 def create_device_types (self , device_types_to_add ):
8083 for device_type in device_types_to_add :
84+
85+ # Remove file base path
86+ src_file = device_type ["src" ]
87+ del device_type ["src" ]
88+
89+ # Pre-process front/rear_image flag, remove it if present
90+ saved_images = {}
91+ image_base = os .path .dirname (src_file ).replace ("device-types" ,"elevation-images" )
92+ for i in ["front_image" ,"rear_image" ]:
93+ if i in device_type :
94+ if device_type [i ]:
95+ image_glob = f"{ image_base } /{ device_type ['slug' ]} .{ i .split ('_' )[0 ]} .*"
96+ images = glob .glob (image_glob , recursive = False )
97+ if images :
98+ saved_images [i ] = images [0 ]
99+ else :
100+ self .handle .log (f"Error locating image file using '{ image_glob } '" )
101+ del device_type [i ]
102+
81103 try :
82104 dt = self .device_types .existing_device_types [device_type ["model" ]]
83105 self .handle .verbose_log (f'Device Type Exists: { dt .manufacturer .name } - '
@@ -114,6 +136,10 @@ def create_device_types(self, device_types_to_add):
114136 if self .modules and 'module-bays' in device_type :
115137 self .device_types .create_module_bays (device_type ['module-bays' ], dt .id )
116138
139+ # Finally, update images if any
140+ if saved_images :
141+ self .device_types .upload_images (self .url , self .token , saved_images , dt .id )
142+
117143 def create_module_types (self , module_types ):
118144 all_module_types = {}
119145 for curr_nb_mt in self .netbox .dcim .module_types .all ():
@@ -152,46 +178,46 @@ def create_module_types(self, module_types):
152178 self .device_types .create_module_rear_ports (curr_mt ["rear-ports" ], module_type_res .id )
153179 if "front-ports" in curr_mt :
154180 self .device_types .create_module_front_ports (curr_mt ["front-ports" ], module_type_res .id )
155-
181+
156182class DeviceTypes :
157183 def __new__ (cls , * args , ** kwargs ):
158184 return super ().__new__ (cls )
159-
185+
160186 def __init__ (self , netbox , handle , counter ):
161187 self .netbox = netbox
162188 self .handle = handle
163189 self .counter = counter
164190 self .existing_device_types = self .get_device_types ()
165-
191+
166192 def get_device_types (self ):
167193 return {str (item ): item for item in self .netbox .dcim .device_types .all ()}
168194
169195 def get_power_ports (self , device_type ):
170196 return {str (item ): item for item in self .netbox .dcim .power_port_templates .filter (devicetype_id = device_type )}
171-
197+
172198 def get_rear_ports (self , device_type ):
173199 return {str (item ): item for item in self .netbox .dcim .rear_port_templates .filter (devicetype_id = device_type )}
174-
200+
175201 def get_module_power_ports (self , module_type ):
176202 return {str (item ): item for item in self .netbox .dcim .power_port_templates .filter (moduletype_id = module_type )}
177-
203+
178204 def get_module_rear_ports (self , module_type ):
179205 return {str (item ): item for item in self .netbox .dcim .rear_port_templates .filter (moduletype_id = module_type )}
180-
206+
181207 def get_device_type_ports_to_create (self , dcim_ports , device_type , existing_ports ):
182208 to_create = [port for port in dcim_ports if port ['name' ] not in existing_ports ]
183209 for port in to_create :
184210 port ['device_type' ] = device_type
185-
211+
186212 return to_create
187-
213+
188214 def get_module_type_ports_to_create (self , module_ports , module_type , existing_ports ):
189215 to_create = [port for port in module_ports if port ['name' ] not in existing_ports ]
190216 for port in to_create :
191217 port ['module_type' ] = module_type
192-
218+
193219 return to_create
194-
220+
195221 def create_interfaces (self , interfaces , device_type ):
196222 existing_interfaces = {str (item ): item for item in self .netbox .dcim .interface_templates .filter (
197223 devicetype_id = device_type )}
@@ -206,11 +232,11 @@ def create_interfaces(self, interfaces, device_type):
206232 })
207233 except pynetbox .RequestError as excep :
208234 self .handle .log (f"Error '{ excep .error } ' creating Interface" )
209-
235+
210236 def create_power_ports (self , power_ports , device_type ):
211237 existing_power_ports = self .get_power_ports (device_type )
212238 to_create = self .get_device_type_ports_to_create (power_ports , device_type , existing_power_ports )
213-
239+
214240 if to_create :
215241 try :
216242 self .counter .update ({'updated' :
@@ -219,11 +245,11 @@ def create_power_ports(self, power_ports, device_type):
219245 })
220246 except pynetbox .RequestError as excep :
221247 self .handle .log (f"Error '{ excep .error } ' creating Power Port" )
222-
248+
223249 def create_console_ports (self , console_ports , device_type ):
224250 existing_console_ports = {str (item ): item for item in self .netbox .dcim .console_port_templates .filter (devicetype_id = device_type )}
225251 to_create = self .get_device_type_ports_to_create (console_ports , device_type , existing_console_ports )
226-
252+
227253 if to_create :
228254 try :
229255 self .counter .update ({'updated' :
@@ -245,7 +271,7 @@ def create_power_outlets(self, power_outlets, device_type):
245271 outlet ['power_port' ] = power_port .id
246272 except KeyError :
247273 pass
248-
274+
249275 try :
250276 self .counter .update ({'updated' :
251277 self .handle .log_device_ports_created (
@@ -283,7 +309,7 @@ def create_rear_ports(self, rear_ports, device_type):
283309 def create_front_ports (self , front_ports , device_type ):
284310 existing_front_ports = {str (item ): item for item in self .netbox .dcim .front_port_templates .filter (devicetype_id = device_type )}
285311 to_create = self .get_device_type_ports_to_create (front_ports , device_type , existing_front_ports )
286-
312+
287313 if to_create :
288314 all_rearports = self .get_rear_ports (device_type )
289315 for port in to_create :
@@ -293,7 +319,7 @@ def create_front_ports(self, front_ports, device_type):
293319 except KeyError :
294320 self .handle .log (f'Could not find Rear Port for Front Port: { port ["name" ]} - '
295321 + f'{ port ["type" ]} - { device_type } ' )
296-
322+
297323 try :
298324 self .counter .update ({'updated' :
299325 self .handle .log_device_ports_created (
@@ -305,7 +331,7 @@ def create_front_ports(self, front_ports, device_type):
305331 def create_device_bays (self , device_bays , device_type ):
306332 existing_device_bays = {str (item ): item for item in self .netbox .dcim .device_bay_templates .filter (devicetype_id = device_type )}
307333 to_create = self .get_device_type_ports_to_create (device_bays , device_type , existing_device_bays )
308-
334+
309335 if to_create :
310336 try :
311337 self .counter .update ({'updated' :
@@ -314,11 +340,11 @@ def create_device_bays(self, device_bays, device_type):
314340 })
315341 except pynetbox .RequestError as excep :
316342 self .handle .log (f"Error '{ excep .error } ' creating Device Bay" )
317-
343+
318344 def create_module_bays (self , module_bays , device_type ):
319345 existing_module_bays = {str (item ): item for item in self .netbox .dcim .module_bay_templates .filter (devicetype_id = device_type )}
320346 to_create = self .get_device_type_ports_to_create (module_bays , device_type , existing_module_bays )
321-
347+
322348 if to_create :
323349 try :
324350 self .counter .update ({'updated' :
@@ -331,7 +357,7 @@ def create_module_bays(self, module_bays, device_type):
331357 def create_module_interfaces (self , module_interfaces , module_type ):
332358 existing_interfaces = {str (item ): item for item in self .netbox .dcim .interface_templates .filter (moduletype_id = module_type )}
333359 to_create = self .get_module_type_ports_to_create (module_interfaces , module_type , existing_interfaces )
334-
360+
335361 if to_create :
336362 try :
337363 self .counter .update ({'updated' :
@@ -340,11 +366,11 @@ def create_module_interfaces(self, module_interfaces, module_type):
340366 })
341367 except pynetbox .RequestError as excep :
342368 self .handle .log (f"Error '{ excep .error } ' creating Module Interface" )
343-
369+
344370 def create_module_power_ports (self , power_ports , module_type ):
345371 existing_power_ports = self .get_module_power_ports (module_type )
346372 to_create = self .get_module_type_ports_to_create (power_ports , module_type , existing_power_ports )
347-
373+
348374 if to_create :
349375 try :
350376 self .counter .update ({'updated' :
@@ -353,11 +379,11 @@ def create_module_power_ports(self, power_ports, module_type):
353379 })
354380 except pynetbox .RequestError as excep :
355381 self .handle .log (f"Error '{ excep .error } ' creating Module Power Port" )
356-
382+
357383 def create_module_console_ports (self , console_ports , module_type ):
358384 existing_console_ports = {str (item ): item for item in self .netbox .dcim .console_port_templates .filter (moduletype_id = module_type )}
359385 to_create = self .get_module_type_ports_to_create (console_ports , module_type , existing_console_ports )
360-
386+
361387 if to_create :
362388 try :
363389 self .counter .update ({'updated' :
@@ -366,11 +392,11 @@ def create_module_console_ports(self, console_ports, module_type):
366392 })
367393 except pynetbox .RequestError as excep :
368394 self .handle .log (f"Error '{ excep .error } ' creating Module Console Port" )
369-
395+
370396 def create_module_power_outlets (self , power_outlets , module_type ):
371397 existing_power_outlets = {str (item ): item for item in self .netbox .dcim .power_outlet_templates .filter (moduletype_id = module_type )}
372398 to_create = self .get_module_type_ports_to_create (power_outlets , module_type , existing_power_outlets )
373-
399+
374400 if to_create :
375401 existing_power_ports = self .get_module_power_ports (module_type )
376402 for outlet in to_create :
@@ -379,19 +405,19 @@ def create_module_power_outlets(self, power_outlets, module_type):
379405 outlet ['power_port' ] = power_port .id
380406 except KeyError :
381407 pass
382-
408+
383409 try :
384410 self .counter .update ({'updated' :
385411 self .handle .log_module_ports_created (
386412 self .netbox .dcim .power_outlet_templates .create (to_create ), "Module Power Outlet" )
387413 })
388414 except pynetbox .RequestError as excep :
389415 self .handle .log (f"Error '{ excep .error } ' creating Module Power Outlet" )
390-
416+
391417 def create_module_console_server_ports (self , console_server_ports , module_type ):
392418 existing_console_server_ports = {str (item ): item for item in self .netbox .dcim .console_server_port_templates .filter (moduletype_id = module_type )}
393419 to_create = self .get_module_type_ports_to_create (console_server_ports , module_type , existing_console_server_ports )
394-
420+
395421 if to_create :
396422 try :
397423 self .counter .update ({'updated' :
@@ -400,11 +426,11 @@ def create_module_console_server_ports(self, console_server_ports, module_type):
400426 })
401427 except pynetbox .RequestError as excep :
402428 self .handle .log (f"Error '{ excep .error } ' creating Module Console Server Port" )
403-
429+
404430 def create_module_rear_ports (self , rear_ports , module_type ):
405431 existing_rear_ports = self .get_module_rear_ports (module_type )
406432 to_create = self .get_module_type_ports_to_create (rear_ports , module_type , existing_rear_ports )
407-
433+
408434 if to_create :
409435 try :
410436 self .counter .update ({'updated' :
@@ -417,7 +443,7 @@ def create_module_rear_ports(self, rear_ports, module_type):
417443 def create_module_front_ports (self , front_ports , module_type ):
418444 existing_front_ports = {str (item ): item for item in self .netbox .dcim .front_port_templates .filter (moduletype_id = module_type )}
419445 to_create = self .get_module_type_ports_to_create (front_ports , module_type , existing_front_ports )
420-
446+
421447 if to_create :
422448 existing_rear_ports = self .get_module_rear_ports (module_type )
423449 for port in to_create :
@@ -427,11 +453,32 @@ def create_module_front_ports(self, front_ports, module_type):
427453 except KeyError :
428454 self .handle .log (f'Could not find Rear Port for Front Port: { port ["name" ]} - '
429455 + f'{ port ["type" ]} - { module_type } ' )
430-
456+
431457 try :
432458 self .counter .update ({'updated' :
433459 self .handle .log_module_ports_created (
434460 self .netbox .dcim .front_port_templates .create (to_create ), "Module Front Port" )
435461 })
436462 except pynetbox .RequestError as excep :
437- self .handle .log (f"Error '{ excep .error } ' creating Module Front Port" )
463+ self .handle .log (f"Error '{ excep .error } ' creating Module Front Port" )
464+
465+ def upload_images (self ,baseurl ,token ,images ,device_type ):
466+ '''Upload front_image and/or rear_image for the given device type
467+
468+ Args:
469+ baseurl: URL for Netbox instance
470+ token: Token to access Netbox instance
471+ images: map of front_image and/or rear_image filename
472+ device_type: id for the device-type to update
473+
474+ Returns:
475+ None
476+ '''
477+ url = f"{ baseurl } /api/dcim/device-types/{ device_type } /"
478+ headers = { "Authorization" : f"Token { token } " }
479+
480+ files = { i : (os .path .basename (f ), open (f ,"rb" ) ) for i ,f in images .items () }
481+ response = requests .patch (url , headers = headers , files = files , verify = False )
482+
483+ self .handle .log ( f'Images { images } updated at { url } : { response } ' )
484+ self .counter ["images" ] += len (images )
0 commit comments