Skip to content

Commit b38d0c4

Browse files
authored
Fix interfaces attached to modules being recreated when running the Sync Network Data job (LTM) (#483)
1 parent ad745e4 commit b38d0c4

File tree

8 files changed

+199
-154
lines changed

8 files changed

+199
-154
lines changed

changes/483.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed interfaces attached to modules being recreated when running the Sync Network Data job.

nautobot_device_onboarding/constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,6 @@
5151

5252
# The git repository data source folder name for custom command mappers.
5353
ONBOARDING_COMMAND_MAPPERS_REPOSITORY_FOLDER = "onboarding_command_mappers"
54+
55+
# Support 4 modules deep (device -> modulebay -> module -> modulebay -> module -> modulebay -> module -> modulebay -> module -> interface)
56+
ONBOARDING_DEVICE_MODULE_RECURSION_LIMIT = 4

nautobot_device_onboarding/diffsync/adapters/sync_devices_adapters.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,18 +106,14 @@ def load_devices(self):
106106
self.job.logger.debug("Loading Device data from Nautobot...")
107107

108108
for device in Device.objects.filter(primary_ip4__host__in=list(self.job.ip_address_inventory)):
109-
interface_list = []
109+
interfaces = []
110110
# Only interfaces with the device's primary ip should be considered for diff calculations
111111
# Ultimately, only the first matching interface is used but this list could support multiple
112112
# interface syncs in the future.
113-
for interface in device.interfaces.all():
113+
for interface in device.all_interfaces.order_by("name"):
114114
if device.primary_ip4 in interface.ip_addresses.all():
115-
interface_list.append(interface.name)
116-
if interface_list:
117-
interface_list.sort()
118-
interfaces = [interface_list[0]]
119-
else:
120-
interfaces = []
115+
interfaces = [interface.name]
116+
break
121117
onboarding_device = self.device(
122118
adapter=self,
123119
pk=device.pk,

nautobot_device_onboarding/diffsync/adapters/sync_network_data_adapters.py

Lines changed: 71 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -167,60 +167,63 @@ def load_tagged_vlans_to_interface(self):
167167
168168
Only Vlan assignments that were returned by the CommandGetter job should be loaded.
169169
"""
170-
for interface in Interface.objects.filter(device__in=self.job.devices_to_load):
171-
tagged_vlans = []
172-
for vlan in interface.tagged_vlans.all():
173-
vlan_dict = {}
174-
vlan_dict["name"] = vlan.name
175-
vlan_dict["id"] = str(vlan.vid)
176-
tagged_vlans.append(vlan_dict)
177-
sorted_tagged_vlans = sorted(tagged_vlans, key=lambda x: x["id"])
178-
179-
network_tagged_vlans_to_interface = self.tagged_vlans_to_interface(
180-
adapter=self,
181-
device__name=interface.device.name,
182-
name=interface.name,
183-
tagged_vlans=sorted_tagged_vlans,
184-
)
185-
network_tagged_vlans_to_interface.model_flags = DiffSyncModelFlags.SKIP_UNMATCHED_DST
186-
self.add(network_tagged_vlans_to_interface)
170+
for device in self.job.devices_to_load:
171+
for interface in device.all_interfaces:
172+
tagged_vlans = []
173+
for vlan in interface.tagged_vlans.all():
174+
vlan_dict = {}
175+
vlan_dict["name"] = vlan.name
176+
vlan_dict["id"] = str(vlan.vid)
177+
tagged_vlans.append(vlan_dict)
178+
sorted_tagged_vlans = sorted(tagged_vlans, key=lambda x: x["id"])
179+
180+
network_tagged_vlans_to_interface = self.tagged_vlans_to_interface(
181+
adapter=self,
182+
device__name=device.name,
183+
name=interface.name,
184+
tagged_vlans=sorted_tagged_vlans,
185+
)
186+
network_tagged_vlans_to_interface.model_flags = DiffSyncModelFlags.SKIP_UNMATCHED_DST
187+
self.add(network_tagged_vlans_to_interface)
187188

188189
def load_untagged_vlan_to_interface(self):
189190
"""
190191
Load UnTagged VLAN interface assignments into the Diffsync store.
191192
192193
Only UnTagged Vlan assignments that were returned by the CommandGetter job should be synced.
193194
"""
194-
for interface in Interface.objects.filter(device__in=self.job.devices_to_load):
195-
untagged_vlan = {}
196-
if interface.untagged_vlan:
197-
untagged_vlan["name"] = interface.untagged_vlan.name
198-
untagged_vlan["id"] = str(interface.untagged_vlan.vid)
195+
for device in self.job.devices_to_load:
196+
for interface in device.all_interfaces:
197+
untagged_vlan = {}
198+
if interface.untagged_vlan:
199+
untagged_vlan["name"] = interface.untagged_vlan.name
200+
untagged_vlan["id"] = str(interface.untagged_vlan.vid)
199201

200-
network_untagged_vlan_to_interface = self.untagged_vlan_to_interface(
201-
adapter=self,
202-
device__name=interface.device.name,
203-
name=interface.name,
204-
untagged_vlan=untagged_vlan,
205-
)
206-
network_untagged_vlan_to_interface.model_flags = DiffSyncModelFlags.SKIP_UNMATCHED_DST
207-
self.add(network_untagged_vlan_to_interface)
202+
network_untagged_vlan_to_interface = self.untagged_vlan_to_interface(
203+
adapter=self,
204+
device__name=device.name,
205+
name=interface.name,
206+
untagged_vlan=untagged_vlan,
207+
)
208+
network_untagged_vlan_to_interface.model_flags = DiffSyncModelFlags.SKIP_UNMATCHED_DST
209+
self.add(network_untagged_vlan_to_interface)
208210

209211
def load_lag_to_interface(self):
210212
"""
211213
Load Lag interface assignments into the Diffsync store.
212214
213215
Only Lag assignments that were returned by the CommandGetter job should be synced.
214216
"""
215-
for interface in Interface.objects.filter(device__in=self.job.devices_to_load):
216-
network_lag_to_interface = self.lag_to_interface(
217-
adapter=self,
218-
device__name=interface.device.name,
219-
name=interface.name,
220-
lag__interface__name=interface.lag.name if interface.lag else "",
221-
)
222-
network_lag_to_interface.model_flags = DiffSyncModelFlags.SKIP_UNMATCHED_DST
223-
self.add(network_lag_to_interface)
217+
for device in self.job.devices_to_load:
218+
for interface in device.all_interfaces:
219+
network_lag_to_interface = self.lag_to_interface(
220+
adapter=self,
221+
device__name=device.name,
222+
name=interface.name,
223+
lag__interface__name=interface.lag.name if interface.lag else "",
224+
)
225+
network_lag_to_interface.model_flags = DiffSyncModelFlags.SKIP_UNMATCHED_DST
226+
self.add(network_lag_to_interface)
224227

225228
def load_vrfs(self):
226229
"""
@@ -246,19 +249,20 @@ def load_vrf_to_interface(self):
246249
247250
Only Vrf assignments that were returned by the CommandGetter job should be synced.
248251
"""
249-
for interface in Interface.objects.filter(device__in=self.job.devices_to_load):
250-
vrf = {}
251-
if interface.vrf:
252-
vrf["name"] = interface.vrf.name
252+
for device in self.job.devices_to_load:
253+
for interface in device.all_interfaces:
254+
vrf = {}
255+
if interface.vrf:
256+
vrf["name"] = interface.vrf.name
253257

254-
network_vrf_to_interface = self.vrf_to_interface(
255-
adapter=self,
256-
device__name=interface.device.name,
257-
name=interface.name,
258-
vrf=vrf,
259-
)
260-
network_vrf_to_interface.model_flags = DiffSyncModelFlags.SKIP_UNMATCHED_DST
261-
self.add(network_vrf_to_interface)
258+
network_vrf_to_interface = self.vrf_to_interface(
259+
adapter=self,
260+
device__name=device.name,
261+
name=interface.name,
262+
vrf=vrf,
263+
)
264+
network_vrf_to_interface.model_flags = DiffSyncModelFlags.SKIP_UNMATCHED_DST
265+
self.add(network_vrf_to_interface)
262266

263267
def load_cables(self):
264268
"""
@@ -345,6 +349,21 @@ def load_software_version_to_device(self):
345349
network_software_version_to_device.model_flags = DiffSyncModelFlags.SKIP_UNMATCHED_DST
346350
self.add(network_software_version_to_device)
347351

352+
def _handle_single_parameter(self, parameters, parameter_name, database_object, diffsync_model):
353+
"""Overload parameter handling to add special handling for modular interfaces."""
354+
if parameter_name == "device__name":
355+
if database_object.parent:
356+
parameters["device__name"] = database_object.parent.name
357+
else:
358+
parameters["device__name"] = ""
359+
elif parameter_name == "interface__device__name":
360+
if database_object.interface.parent:
361+
parameters["interface__device__name"] = database_object.interface.parent.name
362+
else:
363+
parameters["interface__device__name"] = ""
364+
else:
365+
super()._handle_single_parameter(parameters, parameter_name, database_object, diffsync_model)
366+
348367
def load(self):
349368
"""Generic implementation of the load function."""
350369
if not hasattr(self, "top_level") or not self.top_level:
@@ -411,7 +430,7 @@ def sync_complete(self, source, diff, *args, **kwargs):
411430
)
412431
if ip_address:
413432
try:
414-
interface = Interface.objects.get(device=device, ip_addresses__in=[ip_address])
433+
interface = device.all_interfaces.get(ip_addresses__in=[ip_address])
415434
interface.mgmt_only = True
416435
interface.validated_save()
417436
self.job.logger.info(

nautobot_device_onboarding/diffsync/models/sync_devices_models.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,7 @@ def _get_or_create_interface(cls, adapter, device, ip_address, interface_name):
100100
"""Attempt to get a Device Interface, create a new one if necessary."""
101101
device_interface = None
102102
try:
103-
device_interface = Interface.objects.get(
104-
name=interface_name,
105-
device=device,
106-
)
103+
device_interface = device.all_interfaces.get(name=interface_name)
107104
except ObjectDoesNotExist:
108105
try:
109106
job_form_attrs = adapter.job.ip_address_inventory[ip_address]
@@ -156,10 +153,7 @@ def _update_device_with_attrs(cls, device, platform, ids, attrs, adapter):
156153
def _remove_old_interface_assignment(self, device, ip_address):
157154
"""Remove a device's primary IP address from an interface."""
158155
try:
159-
old_interface = Interface.objects.get(
160-
device=device,
161-
ip_addresses__in=[ip_address],
162-
)
156+
old_interface = device.all_interfaces.get(ip_addresses__in=[ip_address])
163157
old_interface_assignment = IPAddressToInterface.objects.get(
164158
interface=old_interface,
165159
ip_address=ip_address,

0 commit comments

Comments
 (0)