aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHåkon Solbjørg <hakon@solbj.org>2023-04-01 21:14:13 +0200
committerHåkon Solbjørg <hakon@solbj.org>2023-04-02 09:42:11 +0200
commit7fec0112e5989b232755749cdcd13349a3ab77f4 (patch)
tree6a66dac8f24204cdaeded07fd4aa506cfd1a13d4
parent73f4d8d1398a815cc4644bff77c7b59976daa389 (diff)
feat(netbox2gondul): Support sending multiple/all devices+networks in one go
-rw-r--r--tools/netbox/scripts/netbox2gondul/netbox2gondul.py117
1 files changed, 67 insertions, 50 deletions
diff --git a/tools/netbox/scripts/netbox2gondul/netbox2gondul.py b/tools/netbox/scripts/netbox2gondul/netbox2gondul.py
index 42850ae..e3841e2 100644
--- a/tools/netbox/scripts/netbox2gondul/netbox2gondul.py
+++ b/tools/netbox/scripts/netbox2gondul/netbox2gondul.py
@@ -73,15 +73,25 @@ class Netbox2Gondul(Script):
If a device is selected, it will also sync the required networks as long as they are set up correctly (Primary IP addresses for the Device & VLAN configured for the Prefix of those IP Addresses).
""")
- device = ObjectVar(
- description="Device",
+ device = MultiObjectVar(
+ label="Switches",
+ description="Switches to update in Gondul. Leave empty to sync all devices and networks.",
model=Device,
- required=True,
+ required=False,
)
_gondul = None
- def network_to_gondul(self, vlan: VLAN, prefix_v4: Prefix, prefix_v6: Prefix):
+ def networks_to_gondul(self, networks):
+ self.log_info(f"Posting {len(networks)} networks to Gondul")
+ req = self._gondul.update_networks(networks)
+
+ if req.ok:
+ self.log_success(f"Gondul said (HTTP {req.status_code}): {req.text}")
+ else:
+ self.log_failure(f"Gondul said HTTP {req.status_code} and {req.text}")
+
+ def network_to_gondul_format(self, vlan: VLAN, prefix_v4: Prefix, prefix_v6: Prefix):
self.log_info(f"Preparing {vlan.name} for Gondul")
subnet4 = None
@@ -113,8 +123,7 @@ class Netbox2Gondul(Script):
override = vlan.cf['gondul_name']
self.log_info(f'Overriding management vlan name with: {override} (was: {vlan_name})')
vlan_name = override
-
- networks = [{
+ return {
"name": vlan_name,
"subnet4": subnet4,
"subnet6": subnet6,
@@ -122,18 +131,19 @@ class Netbox2Gondul(Script):
"gw6": gw6,
"router": router,
"vlan": vlan.vid,
- }]
+ }
- req = self._gondul.update_networks(networks)
+ def switches_to_gondul(self, switches):
+ self.log_info(f"Posting {len(switches)} switches to Gondul")
+
+ req = self._gondul.update_switches(switches)
if req.ok:
self.log_success(f"Gondul said (HTTP {req.status_code}): {req.text}")
else:
self.log_failure(f"Gondul said HTTP {req.status_code} and {req.text}")
- def device_to_gondul(self, device: Device):
- self.log_info(f"Posting {device.name} to Gondul")
-
+ def device_to_gondul_format(self, device: Device):
# Find distro and distro port through the cable connected on uplink ae.
# Assuming the uplink AE is always named 'ae0'.
uplink_ae: Interface = device.interfaces.get(name="ae0")
@@ -156,6 +166,7 @@ class Netbox2Gondul(Script):
self.log_info(f'Overriding management vlan name with: {override} (was: {mgmt_vlan_name})')
mgmt_vlan_name = override
+ traffic_network = None
traffic_vlan_name = None
try:
traffic_vlan = VLAN.objects.get(name=device.name)
@@ -163,11 +174,11 @@ class Netbox2Gondul(Script):
traffic_prefix_v6 = Prefix.objects.get(vlan=traffic_vlan, prefix__family=6)
traffic_vlan_name = traffic_vlan.name
- self.network_to_gondul(traffic_vlan, traffic_prefix_v4, traffic_prefix_v6)
+ traffic_network = self.network_to_gondul_format(traffic_vlan, traffic_prefix_v4, traffic_prefix_v6)
except VLAN.DoesNotExist:
pass
- switches = [{
+ return {
# "community": "", # Not implemented
"tags": [tag.slug for tag in list(device.tags.all())],
"distro_name": distro.name,
@@ -180,45 +191,51 @@ class Netbox2Gondul(Script):
"sysname": device.name,
"traffic_vlan": traffic_vlan_name,
# "deleted": False, # Not implemented
- }]
-
- req = self._gondul.update_switches(switches)
-
- if req.ok:
- self.log_success(f"Gondul said (HTTP {req.status_code}): {req.text}")
- else:
- self.log_failure(f"Gondul said HTTP {req.status_code} and {req.text}")
+ }, traffic_network
def run(self, data, commit):
- device: Device = data['device']
-
- 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
-
- 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
- else:
- self.log_warning(f'Device <a href="{device.get_absolute_url()}">{device.name}</a> is missing primary IPv4 address.')
-
- 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.')
-
- 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
-
- self._gondul = Gondul.read_config_from_file(GONDUL_CONFIG_FILE)
-
- self.network_to_gondul(vlan, prefix_v4, prefix_v6)
+ input_devices: list[Type[Device]] = data['device']
+
+ if len(input_devices) == 0:
+ input_devices = Device.objects.all()
+
+ networks = []
+ switches = []
+
+ # 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
+
+ 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
+ else:
+ self.log_warning(f'Device <a href="{device.get_absolute_url()}">{device.name}</a> is missing primary IPv4 address.')
+
+ 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.')
+
+ 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
+
+
+ networks.append(self.network_to_gondul_format(vlan, prefix_v4, prefix_v6))
+ switch, traffic_network = self.device_to_gondul_format(device)
+ if traffic_network:
+ networks.append(traffic_network)
+ switches.append(switch)
self.log_success("All good, sending to Gondul")
+ self._gondul = Gondul.read_config_from_file(GONDUL_CONFIG_FILE)
- self.device_to_gondul(device)
+ self.networks_to_gondul(networks)
+ self.switches_to_gondul(switches)