Skip to content

Commit 8ad3ed7

Browse files
danner26jbemmel
andauthored
Version 2.0.1 (#97)
* Fix image upload v2 (#91) * Add upload_image functionality to new beta script * Updated for new location of files with Boolean flag for front/rear image * fixing the requests verify missing issue #95 (#96) --------- Co-authored-by: J vanBemmel <jvb127@gmail.com>
1 parent 362056e commit 8ad3ed7

File tree

3 files changed

+96
-45
lines changed

3 files changed

+96
-45
lines changed

nb-dt-import.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ def main():
3838
settings.handle.verbose_log(
3939
f'Script took {(datetime.now() - startTime)} to run')
4040
settings.handle.log(f'{netbox.counter["added"]} devices created')
41+
settings.handle.log(f'{netbox.counter["images"]} images uploaded')
4142
settings.handle.log(
4243
f'{netbox.counter["updated"]} interfaces/ports updated')
4344
settings.handle.log(

netbox_api.py

Lines changed: 92 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
from collections import Counter
22
import pynetbox
33
import requests
4+
import os
5+
import glob
46
# from pynetbox import RequestError as APIRequestError
57

68
class 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+
156182
class 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)

repo.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ def parse_files(self, files: list, slugs: list = None):
9595
data['manufacturer'] = {
9696
'name': manufacturer, 'slug': self.slug_format(manufacturer)}
9797

98+
# Save file location to resolve any relative paths for images
99+
data['src'] = file
100+
98101
if slugs and True not in [True if s.casefold() in data['slug'].casefold() else False for s in slugs]:
99102
self.handle.verbose_log(f"Skipping {data['model']}")
100103
continue

0 commit comments

Comments
 (0)