-
Notifications
You must be signed in to change notification settings - Fork 7
/
migrate.py
1326 lines (982 loc) · 52.9 KB
/
migrate.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
from netbox import NetBox
import pymysql
from slugify import slugify
import pickle
import os
import time
import ipaddress
import random
import threading
# Messy script to transfer Racktables SQL to NetBox
# Set "MAX_PAGE_SIZE=0" in "env/netbox.env"
# Add the printed custom_fields to initialization/custom_fields.yaml for all the fields from Racktables
# Set all the bools to True and run once through for correct result, they were for debugging problems. Some info is cached with pickle, though
CREATE_VLAN_GROUPS = True
CREATE_VLANS = True
# This also creates the clusters, which are needed for all devices
CREATE_MOUNTED_VMS = True
CREATE_UNMOUNTED_VMS = True
CREATE_RACKED_DEVICES = True
# Non racked devices depend on racked devices being created first
CREATE_NON_RACKED_DEVICES = True
# Interfaces rely on devices being created
CREATE_INTERFACES = True
# Interface connections depend on all interfaces created
CREATE_INTERFACE_CONNECTIONS = True
CREATE_IPV4 = True
CREATE_IPV6 = True
# IP space depends on interfaces being created
CREATE_IP_NETWORKS = True
CREATE_IP_ALLOCATED = True
CREATE_IP_NOT_ALLOCATED = True
# The length to exceed for a site to be considered a location (like an address) not a site
SITE_NAME_LENGTH_THRESHOLD = 10
# Each step may cache some data relevant to the next step. This will stop that from happening in the pickle load function
STORE_DATA = False
rt_host = '127.0.0.1'
rt_port = 3306
rt_user = 'root'
rt_db = 'test1'
connection = pymysql.connect(host=rt_host,user=rt_user,db=rt_db, port=rt_port)
nb_host = '10.248.48.4'
nb_port = 8001
nb_token = '0123456789abcdef0123456789abcdef01234567'
netbox = NetBox(host=nb_host, port=nb_port, use_ssl=False, auth_token=nb_token)
# This might not be all. Used for looking up non-racked items. Key names are for reference
objtype_id_names = {
1: "BlackBox",
2: "PDU",
3: "Shelf",
4: "Server",
5: "DiskArray",
7: "Router",
8: "Network Switch",
9: "Patch Panel",
10: "CableOrganizer",
11: "spacer",
12: "UPS",
13: "Modem",
15: "console",
447: "multiplexer",
798: "Network Security",
1502: "Server Chassis",
1398: "Power supply",
1503: "Network chassis",
1644: "serial console server",
1787: "Management interface",
50003: "Circuit",
50013: "SAN",
50044: "SBC",
50064: "GSX",
50065: "EMS",
50066: "PSX",
50067: "SGX",
50083: "SBC SWE",
# Don't create these with the unracked devices
# 1504: "VM",
# 1505: "VM Cluster",
# 1560: "Rack",
# 1561: "Row",
# 1562: "Location",
}
# Manufacturer strings that exist in RT. Pulled out of "HW Type" to set as the manufacturer
racktables_manufacturers = {'Generic', 'Dell', 'MicroSoft', 'F5', 'ExtremeXOS', 'Netapp', 'Open Solaris', 'EMC', 'SlackWare', 'RH', 'FreeBSD', 'Edge-Core', 'SMC', 'Force10', 'Cyclades', 'IBM', 'Linksys', 'IronWare', 'Red', 'Promise', 'Extreme', 'QLogic', 'Marvell', 'SonicWall', 'Foundry', 'Juniper', 'APC', 'Raritan', 'Xen', 'NEC', 'Palo', 'OpenSUSE', 'Sun', 'noname/unknown', 'NetApp', 'VMware', 'Moxa', 'Tainet', 'SGI', 'Mellanox', 'Vyatta', 'Raisecom', 'Gentoo', 'Brocade', 'Enterasys', 'Dell/EMC', 'VMWare', 'Infortrend', 'OpenGear', 'Arista', 'Lantronix', 'Huawei', 'Avocent', 'SUSE', 'ALT_Linux', 'OpenBSD', 'Nortel', 'Univention', 'JunOS', 'MikroTik', 'NetBSD', 'Cronyx', 'Aten', 'Intel', 'PROXMOX', 'Ubuntu', 'Motorola', 'SciLin', 'Fujitsu', 'Fiberstore', '3Com', 'D-Link', 'Allied', 'Fortigate', 'Debian', 'HP', 'NETGEAR', 'Pica8', 'TPLink', 'Fortinet', 'RAD', 'NS-OS', 'Cisco', 'Alcatel-Lucent', 'CentOS', 'Hitachi'}
# Pairs of parent objtype_id, then child objtype_id
parent_child_objtype_id_pairs = (
(1502, 4),# Server inside a Server Chassis
(9, 9),# Patch Panel inside a Patch Panel
)
# Some interfaces might have a name including "Eth", then have an IP with name "Ethernet"
# This dict will try to eliminate the difference to clean up the number of "Virtual" and "Other" type interfaces
# Convert the short name into the long name
# These only apply to objects of type "Router", 7, and "Network switch", 8
interface_name_mappings = {
"Eth": "Ethernet",
"eth": "Ethernet",
"ethernet": "Ethernet",
"Po": "Port-Channel",
"Port-channel": "Port-Channel",
"BE": "Bundle-Ether",
"Lo": "Loopback",
"Loop": "Loopback",
"Vl": "VLAN",
"Vlan": "VLAN",
"Mg": "MgmtEth",
"Se": "Serial",
"Gi": "GigabitEthernet",
"Te": "TenGigE",
"Tw": "TwentyFiveGigE",
"Fo": "FortyGigE",
"Hu": "HundredGigE",
}
parent_objtype_ids = [pair[0] for pair in parent_child_objtype_id_pairs]
global_names = set()
global_tags = set()
global_devices = list()
global_device_roles = list()
global_manufacturers = list()
global_device_types = list()
# When looking at all physical devices, store the SQL object_id and the to use in the Port table later
global_physical_object_ids = set()
# Get the same info for non physical devices like VMs and Servers mounted in chassises to create their ports and linterfaces
# This is filled in during create_non_racked_devices function
global_non_physical_object_ids = set()
# asset_no from racktables. Used to find the duplicates and add -1
asset_tags = set()
# object_id to "Chassis Serial" number if it exists
serials = dict()
# Used for separating identical objects in different spots in the same rack
# Have not ca;lculated overflow yet, but 32-126 is a lot for one rack of 45/2 slots for items
first_ascii_character = " "
# Turn the attr_id from table "Attribute" to a slugified string name for example 3 -> "FQDN"
slugified_attributes = dict()
# Turn the uint_value for attr_id 2 in table "AttributeValue" into a string from the table "Dictionary"
hw_types = dict()
def error_log(string):
with open("errors", "a") as error_file:
error_file.write(string + "\n")
def pickleLoad(filename, default):
if os.path.exists(filename):
file = open(filename, 'rb')
data = pickle.load(file)
file.close()
return data
return default
def pickleDump(filename, data):
if STORE_DATA:
file = open(filename, 'wb')
pickle.dump(data, file)
file.close()
def getRackHeight(cursor, rackId):
cursor.execute("SELECT uint_value FROM AttributeValue WHERE object_id={} AND attr_id=27;".format(rackId))
return cursor.fetchall()[0][0]
# return the "HW Type" for the given racktables object
def get_hw_type(racktables_object_id):
global hw_types
cursor.execute("SELECT uint_value FROM AttributeValue WHERE object_id={} AND attr_id=2;".format(racktables_object_id))
uint = cursor.fetchall()
return hw_types[uint[0][0]] if uint else None
def getRowsAtSite(cursor, siteId):
rows = []
cursor.execute("SELECT child_entity_id FROM EntityLink WHERE parent_entity_type='location' AND parent_entity_id=%s AND child_entity_type='row'",siteId)
rowIds = cursor.fetchall()
for rowId in rowIds:
cursor.execute("SELECT id,name,label,asset_no,comment FROM Object WHERE id=%s",rowId[0])
rows += cursor.fetchall()
return rows
def getRacksAtRow(cursor, rowId):
racks = []
cursor.execute("SELECT child_entity_id FROM EntityLink WHERE parent_entity_type='row' AND parent_entity_id=%s AND child_entity_type='rack'",rowId)
rackIds = cursor.fetchall()
for rackId in rackIds:
cursor.execute("SELECT id,name,label,asset_no,comment FROM Object WHERE id=%s", rackId[0])
racks += cursor.fetchall()
return racks
def getAtomsAtRack(cursor, rackId):
cursor.execute("SELECT rack_id,unit_no,atom,state,object_id FROM RackSpace WHERE rack_id={};".format(rackId))
return cursor.fetchall()
def getTags(cursor, entity_realm, entity_id):
tags = []
cursor.execute("SELECT tag_id FROM TagStorage WHERE entity_id={} AND entity_realm=\"{}\";".format(entity_id, entity_realm))
for tag_id in [x[0] for x in cursor.fetchall()]:
cursor.execute("SELECT tag FROM TagTree WHERE id={};".format(tag_id))
tags += cursor.fetchall()
return [{'name': tag[0]} for tag in tags]
# Return a string
def getDeviceType(cursor, objtype_id):
cursor.execute("SELECT dict_key,dict_value FROM Dictionary WHERE dict_key={};".format(objtype_id))
return cursor.fetchall()[0][1]
def get_manufacturer_role_type(cursor, racktables_object_id, objtype_id, height, is_full_depth):
global racktables_manufacturers
original_device_type = getDeviceType(cursor, objtype_id)
manufacturer = original_device_type
# Add the height to the type model, as well as the binary full_depth or not
hw_type = get_hw_type(racktables_object_id)
if hw_type:
# print("HW:", hw_type)
device_type = hw_type
for racktables_manufacturer in racktables_manufacturers:
if device_type.startswith(racktables_manufacturer) or device_type.startswith(racktables_manufacturer+" "):
device_type = device_type.replace(racktables_manufacturer," ", 1).lstrip(" ")
manufacturer = racktables_manufacturer
else:
device_type = original_device_type
device_type_model = "{}-{}U{}".format(device_type, height, "-full" if is_full_depth else "")
return manufacturer, original_device_type, device_type_model
def create_global_tags(tags):
global global_tags
for tag in tags:
if tag not in global_tags:
try:
netbox.extras.create_tag(tag, slugify(tag))
except:
print(tag)
global_tags.add(tag)
def createDeviceAtLocationInRack(device_name, face, start_height, device_role, manufacturer, device_type_model, site_name, rack_name, asset_no, racktables_device_id):
global global_devices
global global_names
global global_device_roles
global global_manufacturers
global global_device_types
global asset_tags
name_at_location = None
id_at_location = None
for device in global_devices:
if face == device['face']['value'] and start_height == device['position'] and device_role == device['device_role']['name'] and manufacturer == device['device_type']['manufacturer']['name'] and device_type_model == device['device_type']['model'] and site_name == device['site']['name'] and rack_name == device['rack']['name']:
name_at_location = device['name']
id_at_location = device['id']
break
if name_at_location == None:
# print(device_name, "being created at", rack_name, start_height, face)
name_at_location = device_name
if device_name in global_names:
name_counter = 1
while True:
counter_name = device_name + ".{}".format(name_counter)
if counter_name not in global_names:
name_at_location = counter_name
break
else:
name_counter += 1
# Check if the device is in a VM cluster and if so add it to that when creating it in Netbox
device_in_vm_cluster, device_vm_cluster_name, parent_entity_ids = device_is_in_cluster(racktables_device_id)
custom_fields = get_custom_fields(cursor, racktables_device_id)
serial = serials[racktables_device_id] if racktables_device_id in serials else ""
asset_no = asset_no.strip() if asset_no else None
if asset_no and asset_no in asset_tags:
asset_no = asset_no+ "-1"
device = netbox.dcim.create_device(custom_fields=custom_fields,face=face,cluster={"name":device_vm_cluster_name} if device_in_vm_cluster else None,asset_tag=asset_no,serial=serial,position=start_height,name=name_at_location,device_role=device_role,manufacturer={"name":manufacturer},device_type=device_type_model,site_name=site_name,rack={"name":rack_name})
asset_tags.add(asset_no)
id_at_location = device['id']
global_names.add(name_at_location)
global_devices.append(device)
else:
print(name_at_location, "exists at location")
return name_at_location, id_at_location
# Pass the list of atoms into this and have the devices built to the appropriate size
def createObjectsInRackFromAtoms(cursor, atoms, rack_name, rack_id):
debug_splits = False
global global_physical_object_ids
# Put positions into dict based on Id
atoms_dict = {}
for atom in atoms:
key = str(atom[4])
if key not in atoms_dict:
atoms_dict[key] = [atom]
else:
atoms_dict[key].append(atom)
# Some of the same devices might exist, but not be attached:
# For example: [(1373, 18, 'rear', 'T', 1071), (1373, 19, 'rear', 'T', 1071), (1373, 35, 'front', 'T', 1071), (1373, 36, 'front', 'T', 1071)]
# Should be two separate items because they do not touch
# Iterate over the list and separate it at points where the objects do not meet.
# Because the original was dict, add a dummy value to the end of the Id key and disregard that for gettign the real id
added_atom_objects = {}
separated_Ids = False
for Id in atoms_dict:
current_counter = 0
old_counter = 0
max_counter = len(atoms_dict[Id]) - 1
current_hash_addition = first_ascii_character # The value to add onto the Id. Make sure this stays as 1 character and increment as ASCII
current_atom = atoms_dict[Id][0][2]
current_height = atoms_dict[Id][0][1]
# When separating the Ids, make sure to remove the original Id from the atoms_dict
internal_separated_Ids = False
# There could be a single item at the end of a list like:
# [(1379, 5, 'front', 'T', 1070), (1379, 6, 'front', 'T', 1070), (1379, 9, 'front', 'T', 1070), (1379, 10, 'front', 'T', 1070), (1379, 35, 'front', 'T', 1070)]
# Where the final list adds things before, but not itself, so add everything after the last_added
# Iterate over a copy of atoms_dict[Id] list of atoms so that the original lsit can have items removed to use 0 as starting place and not keep track of it
for atom in atoms_dict[Id].copy():
# Cases without overlap, where a split should be made
# [1] [1] [ ]
# [ ] [1] [1] # Disregard this case because it doesn't appear to come up and is too much to calculate horizantal or vertical
# [ ] [ ] [ ]
# [1] [1] [ ] [1] [ ] [ ]
# [ ] [ ] [ ] or [ ] [ ] [1] # Check for separation of heights here
# [ ] [1] [1] [ ] [ ] [ ]
if debug_splits:
print(atom[1], current_height)
# Look for device on a height above the last device
# Once found a split based on the last
if atom[1] > current_height + 1 and current_counter > 0: # or (internal_separated_Ids == True and current_counter == max_counter):
# Create separate Id for all the atoms in this list before the current one
if debug_splits:
print(atoms_dict[Id], current_counter, old_counter)
# Resize the original atoms_dict to remove the first atoms
added_atom_objects[Id + current_hash_addition] = atoms_dict[Id][old_counter:current_counter]
if debug_splits:
print("after", added_atom_objects[Id + current_hash_addition])
print(current_counter == max_counter)
# Inc hash addition. NO CHECK FOR OVERFLOW, although 32 to 126 should be good for one rack of ids
current_hash_addition = str(chr(ord(current_hash_addition) + 1))
internal_separated_Ids = True
separated_Ids = True
old_counter = current_counter
#Calculate the current position and determine if it touches the last position in the ordered list.
current_atom = atom[2]
current_height = atom[1]
current_counter += 1
# Add the last few items
if internal_separated_Ids == True:
added_atom_objects[Id + current_hash_addition] = atoms_dict[Id][old_counter:]
# print(added_atom_objects[Id + current_hash_addition])
# Add all the key,value pairs from added_atom_objects to the original atoms_dict and then remove the original Ids
if separated_Ids == True:
# Add the new Ids atoms lists with the hash addition to the original atoms_dict
for Id_and_addition in added_atom_objects:
atoms_dict[Id_and_addition] = added_atom_objects[Id_and_addition]
# Remove the original ids from atoms_dict since the value list should now be blank
for Id_and_addition in added_atom_objects:
original_Id = Id_and_addition[:-1]
if original_Id in atoms_dict:
atoms_dict.pop(original_Id)
if debug_splits:
print(added_atom_objects)
print("separated", atoms_dict)
# Any other Ids that did not get an added character now get first_ascii_character added to them
remove_original_Ids = []
add_new_Ids = {}
for Id in atoms_dict:
if Id not in added_atom_objects:
add_new_Ids[Id + first_ascii_character] = atoms_dict[Id]
remove_original_Ids.append(Id)
for Id in add_new_Ids:
atoms_dict[Id] = add_new_Ids[Id]
# Remove the original Ids without the hash addition to the atoms_dict
for Id in remove_original_Ids:
atoms_dict.pop(Id)
# Start to calculate sizes and add devices
for Id in atoms_dict:
# Cut off the extra character added to distinguish the same device in multiple locations in a rack
start_height = min([atom[1] for atom in atoms_dict[Id]])
height = max([atom[1] for atom in atoms_dict[Id]]) - start_height + 1
# Should this be == str or startswith if there are multiple reservation splits?
if Id == str(None) + first_ascii_character:
try:
units = list(range(start_height, start_height+height))
print("Reservation")
netbox.dcim.create_reservation(rack_num=rack_id,units=units,description=".",user='admin')
except Exception as e:
print(str(e))
continue
real_id = int(Id[:-1])
cursor.execute("SELECT id,name,label,objtype_id,has_problems,comment,asset_no FROM Object WHERE id={};".format(real_id))
info = cursor.fetchall()[0]
objtype_id = info[3]
device_name = info[1]
asset_no = info[-1]
device_tags = getTags(cursor, "object", real_id)
# Whether front only, rear only, or both
if 'rear' not in [atom[2] for atom in atoms_dict[Id]]:
face = 'front'
is_full_depth = False
elif 'front' not in [atom[2] for atom in atoms_dict[Id]]:
face = 'rear'
is_full_depth = False
else:
# face = 'both'
# There is no 'both' in netbox, so use 'front' instead
face = 'front'
is_full_depth = True
manufacturer, device_role, device_type_model = get_manufacturer_role_type(cursor, real_id, objtype_id, height, is_full_depth)
if device_role not in global_device_roles:
netbox.dcim.create_device_role(device_role,"ffffff",slugify(device_role))
global_device_roles.add(device_role)
if manufacturer not in global_manufacturers:
netbox.dcim.create_manufacturer(manufacturer, slugify(manufacturer))
global_manufacturers.add(manufacturer)
# Create a device type that takes into account the height
# If the device is a "Server Chassis", objtype_id 1502, create it as a parent device to assign children to in device bays
if objtype_id in parent_objtype_ids:
device_type_model += "-parent"
# Cannot easily check device_types, so must use a try: except: here
if device_type_model not in global_device_types:
netbox.dcim.create_device_type(model=device_type_model,manufacturer={"name":manufacturer},slug=slugify(device_type_model),u_height=height,is_full_depth=is_full_depth,tags=device_tags,subdevice_role="parent" if objtype_id in parent_objtype_ids else "")
global_device_types.add(device_type_model)
# Naming check done first, then check for existance in specific slot since lots of a racks have many devices of the same name, which is not allowed in netbox, even accross racks, sites, etc
# Try to create a device at specific location.
# Function looks for the location to be open, then tries different names since device names must be unique
device_name, device_id = createDeviceAtLocationInRack(device_name=device_name, face=face, start_height=start_height, device_role=device_role, manufacturer=manufacturer, device_type_model=device_type_model,site_name= site_name,rack_name=rack_name, asset_no=asset_no, racktables_device_id=real_id)
# Store all the device object_ids and names in the rack to later create the interfaces and ports
global_physical_object_ids.add((device_name, info[0], device_id, objtype_id))
# Necessary to split get_interfaces() calls because the current 50,000 interfaces fails to ever return
def get_interfaces():
interfaces = []
interfaces_file = "interfaces"
limit = 500
offset = 0
# Uncomment this if created interfaces successfully previously and have their data in the file
# or get_interfaces_custom was not added (likely) and you are only running the script once without error
return pickleLoad(interfaces_file, [])
while True:
# In netbox-python dcim.py I defined this as: Some issue with setting limit and offset made it necessary
# def get_interfaces_custom(self, limit, offset, **kwargs):
# return self.netbox_con.get('/dcim/interfaces', limit=limit, offset=offset, **kwargs)
ret = netbox.dcim.get_interfaces_custom(limit=limit, offset=offset)
if ret:
interfaces.extend(ret)
offset += limit
print("Added {} interfaces, total {}".format(limit, len(interfaces)))
else:
pickleDump(interfaces_file, interfaces)
return interfaces
def device_is_in_cluster(device_id):
cursor.execute("SELECT parent_entity_id FROM EntityLink WHERE parent_entity_type=\"object\" AND child_entity_id={};".format(device_id))
parent_entity_ids = [parent_entity_id[0] for parent_entity_id in cursor.fetchall()]
for parent_entity_id in parent_entity_ids:
cursor.execute("SELECT objtype_id,name FROM Object WHERE id={};".format(parent_entity_id))
parent_objtype_id,parent_name = cursor.fetchall()[0]
if parent_objtype_id == 1505:
return True, parent_name, parent_entity_ids
return False, None, parent_entity_ids
def get_custom_fields(cursor, racktables_object_id, initial_dict=None):
global slugified_attributes
custom_fields = initial_dict if initial_dict else dict()
cursor.execute("SELECT attr_id,string_value,uint_value FROM AttributeValue WHERE object_id={};".format(racktables_object_id))
attributes = cursor.fetchall()
for attr_id,string_value,uint_value in attributes:
# Skip the HW Type because this is used for the type and height and "Serial Tag"
if attr_id == 2 or attr_id == 27 or attr_id == 10014:
continue
custom_fields[slugified_attributes[attr_id]] = string_value if string_value else uint_value
return custom_fields
# Create the device in this list and return those that could not be created because the parent did not exist yet
def create_parent_child_devices(cursor, data, objtype_id):
global global_non_physical_object_ids
existing_site_names = set(site['name'] for site in netbox.dcim.get_sites())
existing_device_roles = set(device_role['name'] for device_role in netbox.dcim.get_device_roles())
existing_manufacturers = set(manufacturer['name'] for manufacturer in netbox.dcim.get_manufacturers())
existing_device_types = set(device_type['model'] for device_type in netbox.dcim.get_device_types())
existing_device_names = set(device['name'].strip() for device in netbox.dcim.get_devices() if device['name'])
# Map netbox parent device name to the names of its device bays
existing_device_bays = dict()
for device_bay in netbox.dcim.get_device_bays():
parent_name = device_bay['device']['name']
if parent_name not in existing_device_bays:
existing_device_bays[parent_name] = set()
existing_device_bays[parent_name].add(device_bay['name'])
not_created_parents = []
for racktables_device_id,object_name,label,asset_no,comment in data:
# Used for a child device whose parent isn't yet created and needs to be skipped
not_created_parent = False
# Some names in racktables have trailing or leading spaces
if not object_name:
print("No name for", racktables_device_id,object_name,label,asset_no,comment)
continue
object_name = object_name.strip()
if object_name not in existing_device_names:
# Create a "None" site, device type, role, manufacturer and finally device for this loose object
site_name = "None"
manufacturer, device_role, device_type_model = get_manufacturer_role_type(cursor, racktables_device_id, objtype_id, 0, False)
# print("Starting {}".format(object_name))
if site_name not in existing_site_names:
netbox.dcim.create_site(site_name, slugify(site_name))
existing_site_names.add(site_name)
print("Added non rack site", site_name)
if device_role not in existing_device_roles:
netbox.dcim.create_device_role(device_role,"ffffff",slugify(device_role))
existing_device_roles.add(device_role)
print("Added non rack device role", device_role)
if manufacturer not in existing_manufacturers:
netbox.dcim.create_manufacturer(manufacturer, slugify(manufacturer))
existing_manufacturers.add(manufacturer)
print("Added non rack manufacturer", manufacturer)
is_child = False
is_child_parent_id = None
is_child_parent_name = None
is_parent = False
# Check if the device is in a VM cluster and if so add it to that when creating it in Netbox
device_in_vm_cluster, device_vm_cluster_name, parent_entity_ids = device_is_in_cluster(racktables_device_id)
# Check if it is a child device that needs to be created with a child device type then created marked as mounted inside a parent device's device bay.
# The parent device might not exist yet, in which case it is skipped and retried after
for parent_from_pairs_objtype_id, child_from_pairs_objtype_id in parent_child_objtype_id_pairs:
# Server that might reside in a server chassis
if objtype_id == child_from_pairs_objtype_id:
# Got a parent id, so check that it is a Server Chassis and if so, create the device type with child and later add a device bay to that parent object with this newly created child Server object
for parent_entity_id in parent_entity_ids:
cursor.execute("SELECT objtype_id,name FROM Object WHERE id={};".format(parent_entity_id))
parent_objtype_id,parent_name = cursor.fetchall()[0]
if parent_objtype_id == parent_from_pairs_objtype_id:
parent_name = parent_name.strip()
is_child_parent_id = netbox.dcim.get_devices(name=parent_name)
# The parent is not yet created, so break creating this device and come back later
if not is_child_parent_id:
not_created_parents.append((racktables_device_id,object_name,label,asset_no,comment))
not_created_parent = True
break
else:
is_child_parent_id = is_child_parent_id[0]['id']
is_child_parent_name = parent_name
is_child = True
# print("{} child".format(object_name))
break
if is_child:
break
# Could be a loose patch panel that has child devices
if objtype_id == parent_from_pairs_objtype_id and not not_created_parent:
cursor.execute("SELECT child_entity_id FROM EntityLink WHERE parent_entity_type=\"object\" AND parent_entity_id={};".format(racktables_device_id))
child_entity_ids = cursor.fetchall()
# print(child_entity_ids)
for child_entity_id in [x[0] for x in child_entity_ids]:
cursor.execute("SELECT objtype_id,name FROM Object WHERE id={};".format(child_entity_id))
child_objtype_id,child_name = cursor.fetchall()[0]
# print(child_objtype_id, child_name)
if child_objtype_id == child_from_pairs_objtype_id:
is_parent = True
# print("{} parent".format(object_name))
break
if is_parent:
break
# Continue to next device, skipping the child with no parent yet
if not_created_parent:
continue
subdevice_role = ""
if is_child:
device_type_model += "-child"
subdevice_role = "child"
if is_parent:
device_type_model += "-parent"
subdevice_role = "parent"
if device_type_model not in existing_device_types:
netbox.dcim.create_device_type(model=device_type_model,slug=slugify(device_type_model), manufacturer={"name":manufacturer},u_height=0,subdevice_role=subdevice_role)
existing_device_types.add(device_type_model)
device_tags = getTags(cursor = cursor, entity_realm="object", entity_id = racktables_device_id)
custom_fields = get_custom_fields(cursor, racktables_device_id, {"Device_Label": label})
serial = serials[racktables_device_id] if racktables_device_id in serials else ""
asset_no = asset_no.strip() if asset_no else None
if asset_no and asset_no in asset_tags:
asset_no = asset_no+ "-1"
# print("Creating device \"{}\"".format(object_name), device_type_model, device_role, manufacturer, site_name, asset_no)
added_device = netbox.dcim.create_device(name=object_name,cluster={"name": device_vm_cluster_name} if device_in_vm_cluster else None,asset_tag=asset_no, serial=serial,custom_fields=custom_fields, device_type=device_type_model, device_role=device_role, site_name=site_name,comment=comment[:200] if comment else "",tags=device_tags)
asset_tags.add(asset_no)
# Later used for creating interfaces
global_non_physical_object_ids.add((object_name, racktables_device_id, added_device['id'], objtype_id))
# If device was a child device mounted inside a physically mounted parent device, then create a device bay relating to the parent device filled with the just created item
# Only one device can be assigned to each bay, so find the first open device bay name for the parent device, then use try and except to add the added device to it, although it should not fail since the child device was just created above
if is_child:
# Check that this parent object currently has any device bays in it,
if is_child_parent_name in existing_device_bays:
new_bay_name = "bay-" + str(max([int(device_bay_name[len("bay-"):]) for device_bay_name in existing_device_bays[is_child_parent_name]]) + 1)
else:
existing_device_bays[is_child_parent_name] = set()
new_bay_name = "bay-1"
# print(new_bay_name, is_child_parent_name)
existing_device_bays[is_child_parent_name].add(new_bay_name)
netbox.dcim.create_device_bay(new_bay_name, device_id=is_child_parent_id, installed_device_id=added_device['id'])
return not_created_parents
def change_interface_name(interface_name, objtype_id):
interface_name = interface_name.strip()
global interface_name_mappings
if objtype_id in (7, 8):
for prefix in interface_name_mappings:
# Make sure the prefix is followed by a number so Etherent doesn't become Etherneternet
if interface_name.startswith(prefix) and len(interface_name) > len(prefix) and interface_name[len(prefix)] in "0123456789- ":
new_interface_name = interface_name.replace(prefix, interface_name_mappings[prefix], 1)
# with open("prefixes", "a") as file:
# file.write("{} => {}\n".format(interface_name, new_interface_name))
interface_name = new_interface_name
return interface_name
with connection.cursor() as cursor:
# For the HW Type field: use this as the base name for the device type
cursor.execute("SELECT object_id,string_value FROM AttributeValue WHERE attr_id=10014")
for object_id,string_value in cursor.fetchall():
serials[object_id] = string_value if string_value else ""
# Turn the uint_value for attr_id 2 in table "AttributeValue" into a string from the table "Dictionary"
cursor.execute("SELECT dict_key,dict_value FROM Dictionary")
for dict_key,dict_value in cursor.fetchall():
hw_types[dict_key] = dict_value.strip("[]").split("|")[0].strip().replace("%"," ")
# Map the racktables id to the name to add to custom fields later
cursor.execute("SELECT id,type,name FROM Attribute")
yellow_attributes = cursor.fetchall()
for Id,Type,name in yellow_attributes:
slugified_attributes[Id] = name.replace(" ","_").replace("#","").replace(",","").replace("/","").replace(".","").strip("_")
# print("""{}:
# type: {}
# required: false
# weight: 0
# on_objects:
# - dcim.models.Device""".format(slugified_attributes[Id], {"string": "text", "uint":"integer", "date":"text", "float":"integer","dict":"text"}[Type]))
# print("\n\nPaste that in the intializers/custom_fields.yml file for this program to work!")
print("Make sure to also set the page limit to 0 in the conf.env file")
# Create all the tags
global_tags = set(tag['name'] for tag in netbox.extras.get_tags())
IPV4_TAG = "IPv4"
IPV6_TAG = "IPv6"
create_global_tags((IPV6_TAG, IPV4_TAG))
cursor.execute("SELECT tag FROM TagTree;")
create_global_tags(tag[0] for tag in cursor.fetchall())
print("Created tags")
# Map the vlan id domain to the name
vlan_domain_id_names = dict()
existing_vlan_groups = set()
for vlan_group in netbox.ipam.get_vlan_groups():
existing_vlan_groups.add(vlan_group['name'])
if CREATE_VLAN_GROUPS:
print("Creating VLAN Groups")
cursor.execute("SELECT id,description FROM VLANDomain")
vlans_domains = cursor.fetchall()
for Id, description in vlans_domains:
vlan_domain_id_names[Id] = description
if description not in existing_vlan_groups:
netbox.ipam.create_vlan_group(name=description, slug=slugify(description), custom_fields= {"VLAN_Domain_ID":Id})
existing_vlan_groups.add(description)
# Map the racktables network id to the vlan group and vlan names
network_id_group_name_id = pickleLoad('network_id_group_name_id', dict())
if CREATE_VLANS:
print("Creating VLANs")
vlans_for_group = dict()
for IP in ("4", "6"):
cursor.execute("SELECT domain_id,vlan_id,ipv{}net_id FROM VLANIPv{}".format(IP, IP))
vlans = cursor.fetchall()
for domain_id,vlan_id,net_id in vlans:
cursor.execute("SELECT vlan_descr FROM VLANDescription WHERE domain_id={} AND vlan_id={}".format(domain_id,vlan_id))
vlan_name = cursor.fetchall()[0][0]
if not vlan_name:
continue
vlan_group_name = vlan_domain_id_names[domain_id]
if vlan_group_name not in vlans_for_group:
vlans_for_group[vlan_group_name] = set()
name = vlan_name
# Need to get a unique name for the vlan_name if it is already in this group
if name in vlans_for_group[vlan_group_name]:
counter = 1
while True:
name = vlan_name+"-"+str(counter)
if name not in vlans_for_group[vlan_group_name]:
break
else:
counter += 1
# print(vlan_group_name, vlan_id, name)
try:
created_vlan = netbox.ipam.create_vlan(group={"name":vlan_group_name},vid=vlan_id,vlan_name=name)
network_id_group_name_id[net_id] = (vlan_group_name, vlan_name, created_vlan['id'])
# print("created", vlan_group_name,vlan_id,name)
except:
print(vlan_group_name,vlan_id,name)
print("Something went wrong here\n\n")
vlans_for_group[vlan_group_name].add(name)
pickleDump('network_id_group_name_id', network_id_group_name_id)
print("About to create Clusters and VMs")
if CREATE_MOUNTED_VMS:
# Create VM Clusters and the VMs that exist in them
existing_cluster_types = set(cluster_type['name'] for cluster_type in netbox.virtualization.get_cluster_types())
existing_cluster_names = set(cluster['name'] for cluster in netbox.virtualization.get_clusters())
existing_virtual_machines = set(virtual_machine['name'] for virtual_machine in netbox.virtualization.get_virtual_machines())
# print("Got {} existing virtual machines".format(len(existing_virtual_machines)))
vm_counter = 0
cursor.execute("SELECT id,name,asset_no,label FROM Object WHERE objtype_id=1505;")
clusters = cursor.fetchall()
for Id, cluster_name, asset_no,label in clusters:
if cluster_name not in existing_cluster_types:
netbox.virtualization.create_cluster_type(cluster_name, slugify(cluster_name))
existing_cluster_types.add(cluster_name)
if cluster_name not in existing_cluster_names:
netbox.virtualization.create_cluster(cluster_name, cluster_name)
existing_cluster_names.add(cluster_name)
# Create all the VMs that exist in this cluster and assign them to this cluster
cursor.execute("SELECT child_entity_type,child_entity_id FROM EntityLink WHERE parent_entity_id={};".format(Id))
child_virtual_machines = cursor.fetchall()
for child_entity_type,child_entity_id in child_virtual_machines:
cursor.execute("SELECT name,label,comment,objtype_id,asset_no FROM Object WHERE id={};".format(child_entity_id))
virtual_machine_name, virtual_machine_label, virtual_machine_comment, virtual_machine_objtype_id,virtual_machine_asset_no = cursor.fetchall()[0]
# Confirm that the child is VM and not a server or other to not create duplicates
if virtual_machine_objtype_id != 1504 or not virtual_machine_name:
continue
virtual_machine_name = virtual_machine_name.strip()
if virtual_machine_name not in existing_virtual_machines:
virtual_machine_tags = getTags(cursor, "object", child_entity_id)
netbox.virtualization.create_virtual_machine(virtual_machine_name, cluster_name, tags=virtual_machine_tags, comments=virtual_machine_comment[:200] if virtual_machine_comment else "",custom_fields= {"VM_Label": virtual_machine_label[:200] if virtual_machine_label else "", "VM_Asset_No": virtual_machine_asset_no if virtual_machine_asset_no else ""})
existing_virtual_machines.add(virtual_machine_name)
# print("Created", virtual_machine_name)
else:
# print(virtual_machine_name, "exists")
pass
vm_counter += 1
# print(virtual_machine_name, vm_counter)
if CREATE_UNMOUNTED_VMS:
print("Creating unmounted VMs")
# Create the VMs that are not in clusters
unmounted_cluster_name = "Unmounted Cluster"
if unmounted_cluster_name not in existing_cluster_types:
netbox.virtualization.create_cluster_type(unmounted_cluster_name, slugify(unmounted_cluster_name))
if unmounted_cluster_name not in existing_cluster_names:
netbox.virtualization.create_cluster(unmounted_cluster_name, unmounted_cluster_name)
cursor.execute("SELECT name,label,comment,objtype_id,asset_no FROM Object WHERE objtype_id=1504;")
vms = cursor.fetchall()
for virtual_machine_name, virtual_machine_label, virtual_machine_comment, virtual_machine_objtype_id, virtual_machine_asset_no in vms:
if virtual_machine_objtype_id != 1504 or not virtual_machine_name:
continue
virtual_machine_name = virtual_machine_name.strip()
if virtual_machine_name not in existing_virtual_machines:
virtual_machine_tags = getTags(cursor, "object", child_entity_id)
netbox.virtualization.create_virtual_machine(virtual_machine_name, unmounted_cluster_name, tags=virtual_machine_tags, comments=virtual_machine_comment[:200] if virtual_machine_comment else "", custom_fields={"VM_Label": virtual_machine_label[:200] if virtual_machine_label else "", "VM_Asset_No": virtual_machine_asset_no if virtual_machine_asset_no else ""})
existing_virtual_machines.add(virtual_machine_name)
else:
# print(virtual_machine_name, "exists")
pass
vm_counter += 1
# Map interface integer type to the string type
cursor.execute("SELECT id,oif_name FROM PortOuterInterface;")
PortOuterInterfaces = dict()
for k,v in cursor.fetchall():
PortOuterInterfaces[k] = v
# Fill racks with physical devices
if CREATE_RACKED_DEVICES:
print("Creating sites, racks, and filling rack space")
global_devices = netbox.dcim.get_devices()
print("Got {} devices".format(len(global_devices)))
global_names = set(device['name'] for device in global_devices)
print("Got {} names".format(len(global_names)))
global_manufacturers = set(manufacturer['name'] for manufacturer in netbox.dcim.get_manufacturers())
print("Got {} manufacturers".format(len(global_manufacturers)))
global_device_roles = set(device_role['name'] for device_role in netbox.dcim.get_device_roles())
print("Got {} device roles".format(len(global_device_roles)))
global_device_types = set(device_type['model'] for device_type in netbox.dcim.get_device_types())
print("Got {} device types".format(len(global_device_types)))
cursor.execute("SELECT id,name,label,asset_no,comment FROM Object WHERE objtype_id=1562")
sites = cursor.fetchall()
for site_id, site_name, site_label, site_asset_no, site_comment in sites:
if not netbox.dcim.get_sites(name=site_name) or True:
if len(site_name) > SITE_NAME_LENGTH_THRESHOLD:
print("This is probably a location (address)", site_name)
try:
# Create location
pass
except:
# Location exists
pass
continue
print("Creating site (datacenter)", site_name,"\n")
try:
netbox.dcim.create_site(site_name, slugify(site_name))
except:
print("Failed to create site", site_name)
pass