diff options
author | Sjur Fredriksen <sjurtf@ifi.uio.no> | 2025-02-12 22:27:34 +0100 |
---|---|---|
committer | Sjur Fredriksen <sjurtf@ifi.uio.no> | 2025-02-12 22:27:34 +0100 |
commit | 9dc4c4da650fdc7f13132ecf5e9feacc86c955c4 (patch) | |
tree | a0f30f4f6e4d82f269d97ea027dd48faa0c83b84 | |
parent | 7d2ed2329eefd0769f81bb92bd27c4f1df63a55b (diff) |
create cables and add interfaces to AEs
-rw-r--r-- | tools/netbox/scripts/create-switch/create-switch-tg25.py (renamed from tools/netbox/scripts/create-switch/create-switch-tg251.py) | 127 |
1 files changed, 124 insertions, 3 deletions
diff --git a/tools/netbox/scripts/create-switch/create-switch-tg251.py b/tools/netbox/scripts/create-switch/create-switch-tg25.py index 2c163d1..540ae84 100644 --- a/tools/netbox/scripts/create-switch/create-switch-tg251.py +++ b/tools/netbox/scripts/create-switch/create-switch-tg25.py @@ -1,17 +1,22 @@ from extras.scripts import * from django.core.exceptions import ValidationError +from django.contrib.contenttypes.models import ContentType -from dcim.models import Device, DeviceType, Location, DeviceRole, Site, Interface +from dcim.models import Cable, CableTermination, Device, DeviceType, Location, DeviceRole, Site, Interface from dcim.choices import InterfaceModeChoices, InterfaceTypeChoices from ipam.models import VLANGroup, VLAN, Role, Prefix, IPAddress from ipam.choices import PrefixStatusChoices - DEFAULT_SITE = Site.objects.get(name='Vikingskipet') DEFAULT_DEVICE_TYPE = DeviceType.objects.get(model='EX2200-48T-4G') DEFAULT_DEVICE_ROLE = DeviceRole.objects.get(slug='access-switch') DEFAULT_TG_DNS_SUFFIX = "tg25.tg.no" +DEFAULT_UPLINK_SWITCH = Device.objects.get(name='d1.ring') + +DEVICE_ROLE_LEAF = "leaf" +DEVICE_ROLE_DISTRO = "distro" +DEVICE_ROLE_UTSKUTT_DISTRO = "utskutt-distro" # VLAN Group to allocate VLANs from FABRIC_VLAN_GROUP = VLANGroup.objects.get(slug='client-vlans') @@ -28,9 +33,15 @@ FABRIC_V4_JUNIPER_MGMT_PREFIX = Prefix.objects.get(prefix='185.110.149.0/25') FABRIC_V6_JUNIPER_MGMT_PREFIX = Prefix.objects.get(prefix='2a06:5841:f::/64') UPLINK_PORTS = { - 'EX2200-48T-4G': ["ge-0/0/45", "ge-0/0/46", "ge-0/0/47", "ge-0/0/48"], + 'EX2200-48T-4G': ["ge-0/0/44", "ge-0/0/45", "ge-0/0/46", "ge-0/0/47"], } +UPLINK_TYPES = ( + (InterfaceTypeChoices.TYPE_10GE_SFP_PLUS, '10G SFP+'), + (InterfaceTypeChoices.TYPE_1GE_FIXED, '1G RJ45'), + (InterfaceTypeChoices.TYPE_10GE_FIXED, '10G RJ45') +) + def generatePrefix(prefix, length): firstPrefix = prefix.get_first_available_prefix() out = list(firstPrefix.subnet(length, count=1))[0] @@ -65,6 +76,40 @@ class CreateSwitch(Script): required = True, default = Location.objects.get(name="Ringen") # Default during development ) + uplink_type = ChoiceVar( + label = 'Uplink Type', + required = True, + description = "What type of interface should this switch be delivered on", + choices = UPLINK_TYPES, + default = InterfaceTypeChoices.TYPE_1GE_FIXED + ) + destination_device_a = ObjectVar( + description = "Uplink device (A)", + required = True, + model = Device, + query_params = { + 'role': [DEVICE_ROLE_LEAF, DEVICE_ROLE_DISTRO, DEVICE_ROLE_UTSKUTT_DISTRO], + }, + default = DEFAULT_UPLINK_SWITCH + ) + destination_device_b = ObjectVar( + description = "If connected to leaf pair - Uplink device (B)", + required = False, + model = Device, + query_params = { + 'role': [DEVICE_ROLE_LEAF], + }, + ) + # If leaf pair we assume same port. This input only cares about cases with single device. + destination_interfaces = MultiObjectVar( + description="Destination interface(s). \n\n IF You're looking at d1.ring: ge-{PLACEMENT}/x/x. Placements: 0 = South, 1 = Log, 2 = Swing, 3 = North, 4 = noc, 5 = tele", + model=Interface, + query_params={ + 'device_id': '$destination_device_a', + 'occupied': False, + 'type': '$uplink_type' + } + ) def run(self, data, commit): if not data['switch_name'].startswith("e") and not data['switch_name'].startswith("d"): @@ -148,6 +193,82 @@ class CreateSwitch(Script): ) self.log_info("Allocated traffic prefixes") + uplink_description = f"B: {data['destination_device_a'].name}" + if data['destination_device_b']: + uplink_description = f"B: {data['destination_device_a'].name} / {data['destination_device_b'].name} - ae{vlan.id}" + + uplink_ae = Interface.objects.create( + device=switch, + name="ae0", + description = uplink_description, + type = InterfaceTypeChoices.TYPE_LAG, + mode = InterfaceModeChoices.MODE_TAGGED, + ) + + + if data['destination_device_a'].role == DEVICE_ROLE_UTSKUTT_DISTRO: + self.log_debug(f"{ data['destination_device_a']} is utskutt-distro") + # TODO make sure we add traffic vlan on AE between distro and utskutt-distro as well. + + uplink_ae.tagged_vlans.add(FABRIC_V4_JUNIPER_MGMT_PREFIX.vlan.id) + uplink_ae.tagged_vlans.add(vlan.id) + + ## We only need this if not connected to leaf (since they are provisioned using AVD) + if data['destination_device_a'].role != DEVICE_ROLE_LEAF: + destination_ae = Interface.objects.create( + device=data['destination_device_a'], + name=f"ae{vlan.id}", + description = f'B: {switch.name} ae0', + type=InterfaceTypeChoices.TYPE_LAG, + mode=InterfaceModeChoices.MODE_TAGGED, + ) + destination_ae.save() + destination_ae.tagged_vlans.add(FABRIC_V4_JUNIPER_MGMT_PREFIX.vlan.id) + destination_ae.tagged_vlans.add(vlan.id) + self.log_debug(f"Created destination AE <a href=\"{destination_ae.get_absolute_url()}\">{destination_ae}</a>") + + ## TODO support leaf pair + num_uplinks = len(data['destination_interfaces']) + uplink_interfaces = list(Interface.objects.filter(device=switch, type=data['uplink_type'])) + if len(uplink_interfaces) < 1: + raise AbortScript(f"You chose a device type without any {data['uplink_type']} interfaces! Pick another model :)") + + interface_type = ContentType.objects.get_for_model(Interface) + interfaces_filtered = [interface for interface in uplink_interfaces if interface.name in UPLINK_PORTS.get(switch.device_type.model, [])] + for uplink_num in range(0, num_uplinks): + a_interface = data['destination_interfaces'][uplink_num] + b_interface = interfaces_filtered[uplink_num] + + self.log_debug(f"Connecting: {data['destination_device_a']} - {a_interface} to {switch} - {b_interface}") + + a_interface.description = f'G: {switch.name} {b_interface.name} (ae0)' + b_interface.description = f"G: {data['destination_device_a'].name} {a_interface.name} (ae{vlan.id})" + + b_interface.lag = uplink_ae + b_interface.save() + + a_interface.lag = destination_ae + a_interface.save() + + cable = Cable.objects.create() + a = CableTermination.objects.create( + cable=cable, + cable_end='A', + termination_id=a_interface.id, + termination_type=interface_type, + ) + b = CableTermination.objects.create( + cable_end='B', + cable=cable, + termination_id=b_interface.id, + termination_type=interface_type, + ) + cable = Cable.objects.get(id=cable.id) + # https://github.com/netbox-community/netbox/discussions/10199 + cable._terminations_modified = True + cable.save() + + self.log_success(f"✅ Script completed successfully.") self.log_success(f"🔗 Switch: <a href=\"{switch.get_absolute_url()}\">{switch}</a>") self.log_success(f"🔗 v6 Prefix: <a href=\"{v6_prefix.get_absolute_url()}\">{v6_prefix}</a>") |