diff options
-rw-r--r-- | tools/netbox/scripts/netbox2gondul/netbox2gondul.py | 65 | ||||
-rw-r--r-- | tools/netbox/scripts/planning2netbox/planning2netbox.py | 12 |
2 files changed, 58 insertions, 19 deletions
diff --git a/tools/netbox/scripts/netbox2gondul/netbox2gondul.py b/tools/netbox/scripts/netbox2gondul/netbox2gondul.py index 0613350..5fb4ad8 100644 --- a/tools/netbox/scripts/netbox2gondul/netbox2gondul.py +++ b/tools/netbox/scripts/netbox2gondul/netbox2gondul.py @@ -1,7 +1,7 @@ import os from django.contrib.contenttypes.models import ContentType -from django.db.models import F +from django.db.models import F, Q from django.utils.text import slugify from dcim.choices import DeviceStatusChoices, InterfaceModeChoices, InterfaceTypeChoices, SiteStatusChoices @@ -16,6 +16,9 @@ import re import requests from requests.models import HTTPBasicAuth +FLOOR = Site.objects.get(slug="floor") +RING = Site.objects.get(slug="ring") + class GondulConfigError(Exception): def __init__(self, msg): self.message = msg @@ -101,19 +104,19 @@ class Netbox2Gondul(Script): router = None if prefix_v4: - subnet4 = str(prefix_v4.prefix) + subnet4 = prefix_v4.prefix gw4 = str(ipaddress.IPv4Network(prefix_v4.prefix)[1]) else: self.log_warning(f'Network for VLAN <a href="{vlan.get_absolute_url()}">{vlan.name}</a> is missing IPv4 Prefix') if prefix_v6: - subnet6 = str(prefix_v6.prefix) + subnet6 = prefix_v6.prefix gw6 = str(ipaddress.IPv6Network(prefix_v6.prefix)[1]) else: self.log_warning(f'Network for VLAN <a href="{vlan.get_absolute_url()}">{vlan.name}</a> is missing IPv6 Prefix') try: - router = str(IPAddress.objects.get(address=gw4)) + router = str(IPAddress.objects.get(address=f"{gw4}/{subnet4.prefixlen}")) except IPAddress.DoesNotExist: self.log_warning(f'Router not found for VLAN <a href="{vlan.get_absolute_url()}">{vlan.name}</a>') router = "r1.tele" @@ -125,12 +128,13 @@ class Netbox2Gondul(Script): vlan_name = override return { "name": vlan_name, - "subnet4": subnet4, - "subnet6": subnet6, + "subnet4": str(subnet4), + "subnet6": str(subnet6), "gw4": gw4, "gw6": gw6, "router": router, "vlan": vlan.vid, + "tags": [tag.slug for tag in list(vlan.tags.all())], } def switches_to_gondul(self, switches): @@ -151,7 +155,7 @@ class Netbox2Gondul(Script): first_ae_interface: Interface = uplink_ae.member_interfaces.first() cable: Cable = first_ae_interface.cable # Assuming we only have one entry in the cable termination list. - distro_interface: Interface = cable.a_terminations[0] + distro_interface: Interface = cable.a_terminations[0] if cable.a_terminations[0].device != device else cable.b_terminations[0] distro = distro_interface.device # This is the same way as we fetch mgmt vlan in the main run() function. @@ -197,7 +201,11 @@ class Netbox2Gondul(Script): input_devices: list[Type[Device]] = data['device'] if len(input_devices) == 0: - input_devices = Device.objects.all() + input_devices = Device.objects.filter( + Q(site=FLOOR) | Q(site=RING) + ).filter( + status=DeviceStatusChoices.STATUS_ACTIVE, + ) networks = [] switches = [] @@ -205,28 +213,51 @@ class Netbox2Gondul(Script): # sanity check for device in input_devices: if not device.primary_ip4 and not device.primary_ip6: - self.log_failure(f'Device <a href="{device.get_absolute_url()}">{device.name}</a> is missing primary IPv4 and IPv6 address.') - return + self.log_warning(f'Device <a href="{device.get_absolute_url()}">{device.name}</a> is missing primary IPv4 and IPv6 address, skipping...') + continue vlan: VLAN = None prefix_v4: Prefix = None if device.primary_ip4: - prefix_v4 = Prefix.objects.get(NetHostContained(F('prefix'), str(device.primary_ip4))) - vlan = prefix_v4.vlan + try: + prefix_v4 = Prefix.objects.get(NetHostContained(F('prefix'), str(device.primary_ip4))) + vlan = prefix_v4.vlan + except Exception as e: + self.log_warning(f"Failed to configure {device} for import: {e}") + continue else: - self.log_warning(f'Device <a href="{device.get_absolute_url()}">{device.name}</a> is missing primary IPv4 address.') + self.log_warning(f'Device <a href="{device.get_absolute_url()}">{device.name}</a> is missing primary IPv4 address. Skipping.') + continue prefix_v6: Prefix = None if device.primary_ip6: prefix_v6 = Prefix.objects.get(NetHostContained(F('prefix'), str(device.primary_ip6))) vlan = prefix_v6.vlan else: - self.log_warning(f'Device <a href="{device.get_absolute_url()}">{device.name}</a> is missing primary IPv6 address.') + self.log_warning(f'Device <a href="{device.get_absolute_url()}">{device.name}</a> is missing primary IPv6 address. Skipping.') + continue - if prefix_v4 is not None and prefix_v6 is not None and prefix_v4.vlan != prefix_v6.vlan: - self.log_failure(f'VLANs differ for the IPv4 and IPv6 addresses.') - return + if not vlan: + self.log_warning(f"Skipping {device}: missing vlan") + continue + if prefix_v4 is not None and prefix_v6 is not None and prefix_v4.vlan != prefix_v6.vlan: + self.log_warning(f'VLANs differ for the IPv4 and IPv6 addresses, skipping...') + continue + + if (uplink_aes := list(device.interfaces.filter(name="ae0"))): + if len(uplink_aes) == 0: + self.log_warning(f"Skipping {device}: Missing uplink AE") + continue + + uplink_ae = uplink_aes[0] + first_uplink_interface = uplink_ae.member_interfaces.first() + if first_uplink_interface is None: + self.log_warning(f"Skipping {device}: Missing lag member for ae0") + continue + if not first_uplink_interface.cable: + self.log_warning(f"Skipping {device}: Missing netbox cable for uplink AE") + continue networks.append(self.network_to_gondul_format(vlan, prefix_v4, prefix_v6)) switch, traffic_network = self.device_to_gondul_format(device) diff --git a/tools/netbox/scripts/planning2netbox/planning2netbox.py b/tools/netbox/scripts/planning2netbox/planning2netbox.py index 5fc53a7..0df6a8f 100644 --- a/tools/netbox/scripts/planning2netbox/planning2netbox.py +++ b/tools/netbox/scripts/planning2netbox/planning2netbox.py @@ -231,9 +231,13 @@ class Planning2Netbox(Script): # Set mgmt ip mgmt_addr_v4, _ = IPAddress.objects.get_or_create( address=data['mgmt4'], + assigned_object_type=interface_type, + assigned_object_id=vlan_interface.id, ) mgmt_addr_v6, _ = IPAddress.objects.get_or_create( address=data['mgmt6'], + assigned_object_type=interface_type, + assigned_object_id=vlan_interface.id, ) switch.primary_ip4 = mgmt_addr_v4 switch.primary_ip6 = mgmt_addr_v6 @@ -266,11 +270,15 @@ class Planning2Netbox(Script): # and then some of the features of it doesn't work, # e.g. prefix.get_first_available_ip(). + subnet4 = IPNetwork(data['subnet4']) + uplink_addr_v4_raw = subnet4[1] uplink_addr_v4, _ = IPAddress.objects.get_or_create( - address=IPNetwork(data['subnet4'])[1], + address=f"{uplink_addr_v4_raw}/{subnet4.prefixlen}", ) + subnet6 = IPNetwork(data['subnet6']) + uplink_addr_v6_raw = subnet6[1] uplink_addr_v6, _ = IPAddress.objects.get_or_create( - address=IPNetwork(data['subnet6'])[1], + address=f"{uplink_addr_v6_raw}/{subnet6.prefixlen}", ) core_subinterface.ip_addresses.add(uplink_addr_v4) core_subinterface.ip_addresses.add(uplink_addr_v6) |