ipfabric_netbox 4.1.0b3__py3-none-any.whl → 4.1.0b4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ipfabric_netbox might be problematic. Click here for more details.

@@ -6,7 +6,7 @@ class NetboxIPFabricConfig(PluginConfig):
6
6
  name = "ipfabric_netbox"
7
7
  verbose_name = "NetBox IP Fabric SoT Plugin"
8
8
  description = "Sync IP Fabric into NetBox"
9
- version = "4.1.0b3"
9
+ version = "4.1.0b4"
10
10
  base_url = "ipfabric"
11
11
  min_version = "4.2.4"
12
12
 
@@ -3,6 +3,7 @@ import logging
3
3
  from importlib import metadata
4
4
  from typing import Callable
5
5
  from typing import TYPE_CHECKING
6
+ from typing import TypeVar
6
7
 
7
8
  from core.choices import DataSourceStatusChoices
8
9
  from core.exceptions import SyncError
@@ -36,9 +37,12 @@ FAILED_CREATE_INSTANCE_TEMPLATE = (
36
37
 
37
38
  if TYPE_CHECKING:
38
39
  from ..models import IPFabricIngestion
40
+ from ipam.models import IPAddress
39
41
 
40
42
  logger = logging.getLogger("ipfabric_netbox.utilities.ipf_utils")
41
43
 
44
+ ModelTypeVar = TypeVar("ModelTypeVar", bound=Model)
45
+
42
46
 
43
47
  def slugify_text(value):
44
48
  return slugify(value)
@@ -98,7 +102,7 @@ class IPFabric(object):
98
102
  formatted_snapshots[snapshot_ref] = (description, snapshot.snapshot_id)
99
103
  return formatted_snapshots
100
104
 
101
- def get_sites(self, snapshot=None) -> dict():
105
+ def get_sites(self, snapshot=None) -> list:
102
106
  if snapshot:
103
107
  raw_sites = self.ipf.inventory.sites.all(snapshot_id=snapshot)
104
108
  else:
@@ -526,11 +530,29 @@ class IPFabricSyncRunner(object):
526
530
  )
527
531
 
528
532
  @handle_errors
529
- def sync_model(self, app_label: str, model: str, data: dict | None) -> Model | None:
533
+ def sync_model(
534
+ self,
535
+ app_label: str,
536
+ model: str,
537
+ data: dict | None,
538
+ stats: bool = True,
539
+ sync: bool = False,
540
+ ) -> ModelTypeVar | None:
530
541
  """Sync a single item to NetBox."""
542
+ # The `sync` param is a workaround since we need to get some models (Device...) even when not syncing them.
543
+ if not sync:
544
+ return None
545
+
531
546
  if not data:
532
547
  return None
533
- return self.get_model_or_update(app_label, model, data)
548
+
549
+ instance = self.get_model_or_update(app_label, model, data)
550
+
551
+ # Only log when we successfully synced the item and asked for it
552
+ if stats:
553
+ self.logger.increment_statistics(model=model)
554
+
555
+ return instance
534
556
 
535
557
  def sync_items(
536
558
  self,
@@ -548,11 +570,14 @@ class IPFabricSyncRunner(object):
548
570
  return
549
571
 
550
572
  for item in items:
551
- synced_object = self.sync_model(app_label=app_label, model=model, data=item)
573
+ synced_object = self.sync_model(
574
+ app_label=app_label,
575
+ model=model,
576
+ data=item,
577
+ sync=self.settings.get(model),
578
+ )
552
579
  if synced_object is None:
553
580
  continue
554
- # Only log when we successfully synced the item
555
- self.logger.increment_statistics(model=model)
556
581
 
557
582
  if cf:
558
583
  synced_object.snapshot()
@@ -588,22 +613,40 @@ class IPFabricSyncRunner(object):
588
613
  devices_total = len(devices)
589
614
 
590
615
  for device in devices:
591
- if self.sync_model("dcim", "manufacturer", device) is None:
592
- continue
593
- if self.sync_model("dcim", "devicetype", device) is None:
594
- continue
595
- if self.sync_model("dcim", "platform", device) is None:
596
- continue
597
- if self.sync_model("dcim", "devicerole", device) is None:
598
- continue
616
+ self.sync_model(
617
+ "dcim", "manufacturer", device, sync=self.settings.get("manufacturer")
618
+ )
619
+ self.sync_model(
620
+ "dcim", "devicetype", device, sync=self.settings.get("devicetype")
621
+ )
622
+ self.sync_model(
623
+ "dcim", "platform", device, sync=self.settings.get("platform")
624
+ )
625
+ self.sync_model(
626
+ "dcim", "devicerole", device, sync=self.settings.get("devicerole")
627
+ )
599
628
 
600
629
  virtual_chassis = device.get("virtual_chassis", {})
601
- self.sync_model("dcim", "virtualchassis", virtual_chassis)
630
+ self.sync_model(
631
+ "dcim",
632
+ "virtualchassis",
633
+ virtual_chassis,
634
+ False,
635
+ sync=self.settings.get("virtualchassis"),
636
+ )
602
637
 
603
- if (device_object := self.sync_model("dcim", "device", device)) is None:
604
- continue
638
+ # We need to get a Device instance even when not syncing it but syncing Interfaces, IPs or MACs
639
+ device_object: Device | None = self.sync_model(
640
+ "dcim",
641
+ "device",
642
+ device,
643
+ sync=self.settings.get("device")
644
+ or self.settings.get("interface")
645
+ or self.settings.get("ipaddress")
646
+ or self.settings.get("macaddress"),
647
+ )
605
648
 
606
- if self.settings.get("device"):
649
+ if device_object and self.settings.get("device"):
607
650
  device_object.snapshot()
608
651
  device_object.custom_field_data[
609
652
  "ipfabric_source"
@@ -612,23 +655,26 @@ class IPFabricSyncRunner(object):
612
655
  device_object.custom_field_data["ipfabric_ingestion"] = ingestion.pk
613
656
  device_object.save()
614
657
 
615
- self.logger.increment_statistics(model="device")
616
- logger.info(
617
- f"Device {self.logger.log_data.get('statistics', {}).get('device', {}).get('current')} out of {devices_total}"
618
- )
658
+ self.logger.increment_statistics(model="device")
659
+ logger.info(
660
+ f"Device {self.logger.log_data.get('statistics', {}).get('device', {}).get('current')} out of {devices_total}"
661
+ )
619
662
 
620
- # The Device exists now, so we can update master of the VC.
621
- # The logic is handled in transform maps.
622
- self.sync_model("dcim", "virtualchassis", virtual_chassis)
663
+ # The Device exists now, so we can update the master of the VC.
664
+ # The logic is handled in transform maps.
665
+ self.sync_model(
666
+ "dcim",
667
+ "virtualchassis",
668
+ virtual_chassis,
669
+ False,
670
+ sync=self.settings.get("virtualchassis"),
671
+ )
623
672
 
624
673
  device_interfaces = interface_dict.get(device.get("sn"), [])
625
674
  for device_interface in device_interfaces:
626
- interface_object = self.sync_interface(
675
+ self.sync_interface(
627
676
  device_interface, managed_ips, device_object, device
628
677
  )
629
- if interface_object is None:
630
- continue
631
- self.logger.increment_statistics(model="interface")
632
678
 
633
679
  @handle_errors
634
680
  def sync_ipaddress(
@@ -638,22 +684,20 @@ class IPFabricSyncRunner(object):
638
684
  primary_ip: str | None,
639
685
  login_ip: str | None,
640
686
  ):
641
- if not self.settings.get("ipaddress") or not managed_ip:
642
- return
643
- ip_address_obj = self.get_model_or_update(
687
+ ip_address_obj: "IPAddress | None" = self.sync_model(
644
688
  "ipam",
645
689
  "ipaddress",
646
690
  managed_ip,
691
+ sync=self.settings.get("ipaddress"),
647
692
  )
648
693
  if ip_address_obj is None:
649
- return
650
- self.logger.increment_statistics(model="ipaddress")
694
+ return None
651
695
 
652
696
  connection_name = self.get_db_connection_name()
653
697
 
654
698
  try:
655
- # Removing other IP is done in .signals.clear_other_primary_ip
656
- # But do it here too so the change is shown in StagedChange diff
699
+ # Removing another IP is done in .signals.clear_other_primary_ip
700
+ # But do it here too, so the change is shown in StagedChange diff
657
701
  other_device = Device.objects.using(connection_name).get(
658
702
  primary_ip4=ip_address_obj
659
703
  )
@@ -669,7 +713,7 @@ class IPFabricSyncRunner(object):
669
713
  device_object.snapshot()
670
714
  device_object.primary_ip4 = ip_address_obj
671
715
  device_object.save(using=connection_name)
672
- except ValueError as err:
716
+ except (ValueError, AttributeError) as err:
673
717
  self.logger.log_failure(
674
718
  f"Error assigning primary IP to device: {err}", obj=self.sync
675
719
  )
@@ -680,17 +724,17 @@ class IPFabricSyncRunner(object):
680
724
  def sync_macaddress(
681
725
  self, data: dict | None, interface_object: Interface
682
726
  ) -> MACAddress | None:
683
- if not self.settings.get("macaddress") or not data:
684
- return None
685
727
  # Need to create MAC Address object before we can assign it to Interface
686
728
  # TODO: Figure out how to do this using transform maps
687
729
  macaddress_data = {
688
730
  "mac": data,
689
- "id": interface_object.pk,
731
+ "id": getattr(interface_object, "pk", None),
690
732
  }
691
- macaddress_object = self.get_model_or_update(
692
- "dcim", "macaddress", macaddress_data
733
+ macaddress_object: MACAddress | None = self.sync_model(
734
+ "dcim", "macaddress", macaddress_data, sync=self.settings.get("macaddress")
693
735
  )
736
+ if macaddress_object is None:
737
+ return None
694
738
  try:
695
739
  interface_object.snapshot()
696
740
  interface_object.primary_mac_address = macaddress_object
@@ -711,15 +755,19 @@ class IPFabricSyncRunner(object):
711
755
  device: dict,
712
756
  ):
713
757
  device_interface["loginIp"] = device.get("loginIp")
714
- interface_object = self.get_model_or_update(
715
- "dcim", "interface", device_interface
758
+ # We need to get an Interface instance even when not syncing it but syncing IPs or MACs
759
+ interface_object: Interface | None = self.sync_model(
760
+ "dcim",
761
+ "interface",
762
+ device_interface,
763
+ sync=self.settings.get("interface")
764
+ or self.settings.get("ipaddress")
765
+ or self.settings.get("macaddress"),
716
766
  )
717
- if interface_object is None:
718
- return None
719
767
 
720
- for ipaddress in managed_ips.get(device_object.serial, {}).get(
721
- interface_object.name, []
722
- ):
768
+ for ipaddress in managed_ips.get(
769
+ getattr(device_object, "serial", None), {}
770
+ ).get(getattr(interface_object, "name", None), []):
723
771
  self.sync_ipaddress(
724
772
  ipaddress,
725
773
  device_object,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ipfabric_netbox
3
- Version: 4.1.0b3
3
+ Version: 4.1.0b4
4
4
  Summary: NetBox plugin to sync IP Fabric data into NetBox
5
5
  License: MIT
6
6
  Keywords: netbox,ipfabric,plugin,sync
@@ -1,4 +1,4 @@
1
- ipfabric_netbox/__init__.py,sha256=dNmLAwHDbmPgnzHOHoqLDisMshHihZ0VvOzkvp41n-8,674
1
+ ipfabric_netbox/__init__.py,sha256=aDj8nmvY2u_eiMD6l_7ajoVt3TM4i1CJI_ORnRR5YQE,674
2
2
  ipfabric_netbox/api/__init__.py,sha256=DOkvDAI4BoNgdCiNxfseeExEHyOrK8weG-LvjPRyK8A,101
3
3
  ipfabric_netbox/api/nested_serializers.py,sha256=F-VpAL6xhNSUipuvR9_37sVw-3IPjr-XZB5CkouwelA,2174
4
4
  ipfabric_netbox/api/serializers.py,sha256=BOhibC7IZDhaPhTAdQmt_RJtVxmgqjt-MXIo7UcNGZc,4496
@@ -63,11 +63,11 @@ ipfabric_netbox/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
63
63
  ipfabric_netbox/tests/test_models.py,sha256=4SGMX0LZ_fThkc-F1AMZIQbpmJvQNVFnS6QYZpIZkNA,14998
64
64
  ipfabric_netbox/urls.py,sha256=ok66LP09rYi01qJmwdGGlBzV9wrGWVwVAIngPcreJxg,5449
65
65
  ipfabric_netbox/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
66
- ipfabric_netbox/utilities/ipfutils.py,sha256=LNFNfwTAUkHb09PRExZHfRdiSubWvGHuZzvhBWHgA7Q,28675
66
+ ipfabric_netbox/utilities/ipfutils.py,sha256=zB3i4JXRpUQRyr0OW4Kp6WwWAin_hwCIn4OCrzURliY,30132
67
67
  ipfabric_netbox/utilities/logging.py,sha256=GYknjocMN6LQ2873_az3y0RKm29TCXaWviUIIneH-x0,3445
68
68
  ipfabric_netbox/utilities/nbutils.py,sha256=kFBEiJOGvr_49hJWCS2duXojx2-A9kVk0Xp_vj0ohfs,2641
69
69
  ipfabric_netbox/utilities/transform_map.py,sha256=QotbGc2TksINJrb62STgAigpC5Nsgi5umYHu_0rZd8k,2204
70
70
  ipfabric_netbox/views.py,sha256=tPuceZgH2veu5ls4_avUiM9fuM5pYxr1KFWp5im5b0s,35898
71
- ipfabric_netbox-4.1.0b3.dist-info/METADATA,sha256=1RxqnpWrxYvqQai2wU5lbiUzIhhSFUwXmyS5dy8eVj8,4548
72
- ipfabric_netbox-4.1.0b3.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
73
- ipfabric_netbox-4.1.0b3.dist-info/RECORD,,
71
+ ipfabric_netbox-4.1.0b4.dist-info/METADATA,sha256=QCAzNdZIJvqCHTQ3dlAhYizP9k9O9P4EEDuhY-LXcUI,4548
72
+ ipfabric_netbox-4.1.0b4.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
73
+ ipfabric_netbox-4.1.0b4.dist-info/RECORD,,