Sync MAC addresses and schedule endpoint sync jobs

This commit is contained in:
2026-07-10 10:42:44 +02:00
parent 58af2bde5d
commit 36f158b108
7 changed files with 142 additions and 16 deletions
+71 -2
View File
@@ -6,7 +6,7 @@ from dataclasses import dataclass, field
from django.contrib.contenttypes.models import ContentType
from django.db import transaction
from django.db.models import Sum
from dcim.models import Platform
from dcim.models import MACAddress, Platform
from ipam.models import IPAddress
from virtualization.models import VMInterface, VirtualDisk, VirtualMachine
@@ -58,6 +58,9 @@ class SyncResult:
ip_addresses_created: int = 0
ip_addresses_updated: int = 0
ip_conflicts: int = 0
mac_addresses_created: int = 0
mac_addresses_updated: int = 0
mac_addresses_deleted: int = 0
virtual_disks_created: int = 0
virtual_disks_updated: int = 0
virtual_disks_deleted: int = 0
@@ -214,11 +217,13 @@ class VMwareImporter:
self.include_pattern = re.compile(endpoint.include_name_regex) if endpoint.include_name_regex else None
self.exclude_pattern = re.compile(endpoint.exclude_name_regex) if endpoint.exclude_name_regex else None
self.interface_content_type = None
self.mac_address_content_type = None
def sync(self):
result = SyncResult()
platforms = list(Platform.objects.all())
self.interface_content_type = ContentType.objects.get_for_model(VMInterface)
self.mac_address_content_type = ContentType.objects.get_for_model(VMInterface)
with VMwareClient(self.endpoint) as client:
for vm_data in client.iter_virtual_machines():
@@ -366,7 +371,6 @@ class VMwareImporter:
name=interface_data.name,
)
vm_interface.mac_address = interface_data.mac_address
vm_interface.enabled = True
vm_interface.full_clean()
vm_interface.save()
@@ -378,6 +382,8 @@ class VMwareImporter:
result.interfaces_updated += 1
self.logger.info("Updated interface %s on %s", interface_data.name, nb_vm.name)
self._sync_mac_address(vm_interface, interface_data.mac_address, result)
if not self.endpoint.sync_ip_addresses:
continue
@@ -398,6 +404,69 @@ class VMwareImporter:
return primary_ipv4, primary_ipv6
def _sync_mac_address(self, vm_interface, mac_address, result):
normalized_mac = self._normalize_mac_address(mac_address)
if normalized_mac is None:
return None
mac_obj = MACAddress.objects.filter(
assigned_object_type=self.mac_address_content_type,
assigned_object_id=vm_interface.pk,
mac_address=normalized_mac,
).first()
created = mac_obj is None
if created:
mac_obj = MACAddress(
mac_address=normalized_mac,
assigned_object=vm_interface,
)
mac_obj.description = "VMware reported MAC address"
mac_obj.full_clean()
mac_obj.save()
if created:
result.mac_addresses_created += 1
self.logger.info("Created MAC address %s on interface %s", normalized_mac, vm_interface.name)
else:
result.mac_addresses_updated += 1
self.logger.info("Updated MAC address %s on interface %s", normalized_mac, vm_interface.name)
if vm_interface.primary_mac_address_id != mac_obj.pk:
vm_interface.primary_mac_address = mac_obj
vm_interface.full_clean()
vm_interface.save(update_fields=("primary_mac_address",))
self.logger.info("Set primary MAC address %s on interface %s", normalized_mac, vm_interface.name)
stale_macs = MACAddress.objects.filter(
assigned_object_type=self.mac_address_content_type,
assigned_object_id=vm_interface.pk,
description="VMware reported MAC address",
).exclude(pk=mac_obj.pk)
stale_count = stale_macs.count()
if stale_count:
stale_macs.delete()
result.mac_addresses_deleted += stale_count
self.logger.info("Deleted %s stale VMware MAC address(es) from interface %s", stale_count, vm_interface.name)
return mac_obj
@staticmethod
def _normalize_mac_address(mac_address):
if not mac_address:
return None
value = str(mac_address).strip().replace("-", ":").lower()
parts = value.split(":")
if len(parts) != 6:
return None
try:
return ":".join(f"{int(part, 16):02x}" for part in parts)
except ValueError:
return None
def _normalize_ip_address(self, raw_ip):
if not raw_ip:
return None