From 63683debc082ff1ca9365586b484576b30456799 Mon Sep 17 00:00:00 2001 From: pvillar_netdev Date: Tue, 18 Jun 2024 11:14:35 +0200 Subject: [PATCH 01/28] Several issues fixed --- .../integrations/aci/diffsync/adapters/aci.py | 18 ++- .../aci/diffsync/adapters/nautobot.py | 6 +- .../integrations/aci/diffsync/client.py | 139 ++++++++++++------ .../diffsync/device-types/APIC-SERVER-M3.yaml | 3 + .../device-types/N9K-C93180YC-FX3.yaml | 131 +++++++++++++++++ .../integrations/aci/diffsync/models/base.py | 5 +- .../aci/diffsync/models/nautobot.py | 94 ++++++++---- .../integrations/aci/diffsync/utils.py | 14 +- nautobot_ssot/integrations/aci/jobs.py | 3 +- nautobot_ssot/integrations/aci/signals.py | 6 + 10 files changed, 335 insertions(+), 84 deletions(-) create mode 100644 nautobot_ssot/integrations/aci/diffsync/device-types/N9K-C93180YC-FX3.yaml diff --git a/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py b/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py index cc25aa1b..627dfc43 100644 --- a/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py +++ b/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py @@ -83,11 +83,13 @@ def load_tenants(self): for _tenant in tenant_list: if not _tenant["name"] in PLUGIN_CFG.get("ignore_tenants"): tenant_name = f"{self.tenant_prefix}:{_tenant['name']}" + _msite_tag = True if ":mso" in _tenant.get("annotation").lower() else False new_tenant = self.tenant( name=tenant_name, description=_tenant["description"], comments=PLUGIN_CFG.get("comments", ""), site_tag=self.site, + msite_tag=_msite_tag, ) self.add(new_tenant) @@ -218,7 +220,7 @@ def load_ipaddresses(self): site=self.site, vrf=bd_value["vrf"], vrf_tenant=vrf_tenant, - tenant=tenant_name, + tenant=vrf_tenant or tenant_name, # BUG fix ) new_ipaddress = self.ip_address( address=subnet[0], @@ -227,8 +229,8 @@ def load_ipaddresses(self): description=f"ACI Bridge Domain: {bd_key}", device=None, interface=None, - tenant=tenant_name, - namespace=tenant_name, + tenant=vrf_tenant or tenant_name, # BUGfix + namespace=vrf_tenant or tenant_name, # BUGfix site=self.site, site_tag=self.site, ) @@ -241,7 +243,7 @@ def load_ipaddresses(self): self.add(new_ipaddress) else: self.job.logger.warning( - "Duplicate DiffSync IPAddress Object found and has not been loaded.", + f"Duplicate DiffSync IPAddress Object found: {new_ipaddress.address} in Tenant {new_ipaddress.tenant} and has not been loaded.", ) def load_prefixes(self): @@ -255,15 +257,15 @@ def load_prefixes(self): vrf_tenant = f"{self.tenant_prefix}:{bd_value['vrf_tenant']}" else: vrf_tenant = None - if tenant_name not in PLUGIN_CFG.get("ignore_tenants"): + if bd_value.get("tenant") not in PLUGIN_CFG.get("ignore_tenants"): # modified for bugfix for subnet in bd_value["subnets"]: new_prefix = self.prefix( prefix=str(ip_network(subnet[0], strict=False)), - namespace=tenant_name, + namespace=vrf_tenant or tenant_name, # BUGfix status="Active", site=self.site, description=f"ACI Bridge Domain: {bd_key}", - tenant=tenant_name, + tenant=vrf_tenant or tenant_name, vrf=bd_value["vrf"] if bd_value.get("vrf") != "" else None, vrf_tenant=vrf_tenant, site_tag=self.site, @@ -282,7 +284,7 @@ def load_prefixes(self): self.add(new_prefix) else: self.job.logger.warning( - "Duplicate DiffSync Prefix Object found and has not been loaded.", + f"Duplicate DiffSync Prefix Object found {new_prefix.prefix} in Namespace {new_prefix.namespace} and has not been loaded.", ) def load_devicetypes(self): diff --git a/nautobot_ssot/integrations/aci/diffsync/adapters/nautobot.py b/nautobot_ssot/integrations/aci/diffsync/adapters/nautobot.py index d3cf370c..bb811719 100644 --- a/nautobot_ssot/integrations/aci/diffsync/adapters/nautobot.py +++ b/nautobot_ssot/integrations/aci/diffsync/adapters/nautobot.py @@ -99,7 +99,11 @@ def load_tenants(self): """Method to load Tenants from Nautobot.""" for nbtenant in Tenant.objects.filter(tags=self.site_tag): _tenant = self.tenant( - name=nbtenant.name, description=nbtenant.description, comments=nbtenant.comments, site_tag=self.site + name=nbtenant.name, + description=nbtenant.description, + comments=nbtenant.comments, + site_tag=self.site, + msite_tag=nbtenant.tags.filter(name="ACI_MULTISITE").exists(), ) self.add(_tenant) diff --git a/nautobot_ssot/integrations/aci/diffsync/client.py b/nautobot_ssot/integrations/aci/diffsync/client.py index 9cd94fc2..79b966e9 100644 --- a/nautobot_ssot/integrations/aci/diffsync/client.py +++ b/nautobot_ssot/integrations/aci/diffsync/client.py @@ -1,17 +1,25 @@ """All interactions with ACI.""" # pylint: disable=too-many-lines, too-many-instance-attributes, too-many-arguments # pylint: disable=invalid-name - +import re import sys import logging -from datetime import datetime -from datetime import timedelta -import re -from ipaddress import ip_network +import itertools import requests import urllib3 - -from .utils import tenant_from_dn, ap_from_dn, node_from_dn, pod_from_dn, fex_id_from_dn, interface_from_dn +from datetime import datetime, timedelta +from ipaddress import ip_network +from .utils import ( + tenant_from_dn, + ap_from_dn, + node_from_dn, + pod_from_dn, + fex_id_from_dn, + interface_from_dn, + epg_from_dn, + bd_from_dn, +) +from copy import deepcopy urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) @@ -132,6 +140,7 @@ def get_tenants(self) -> list: { "name": data["fvTenant"]["attributes"]["name"], "description": data["fvTenant"]["attributes"]["descr"], + "annotation": data["fvTenant"]["attributes"]["annotation"], } for data in resp.json()["imdata"] ] @@ -327,45 +336,69 @@ def get_vrfs(self, tenant: str) -> list: ] return vrf_list - def get_bds(self, tenant: str) -> dict: + + def get_bds(self, tenant: str = "all") -> dict: """Return Bridge Domains and Subnets from the Cisco APIC.""" + # TODO: rewrite using one API call -> https://10.101.40.2/api/node/class/fvBD.json?query-target=subtree&target-subtree-class=fvBD,fvRsCtx,fvSubnet if tenant == "all": - resp = self._get("/api/node/class/fvBD.json") + resp = self._get( + "/api/node/class/fvBD.json?query-target=subtree&target-subtree-class=fvBD,fvRsCtx,fvSubnet" + ) else: - resp = self._get(f"/api/node/mo/uni/tn-{tenant}.json?query-target=children&target-subtree-class=fvBD") - + resp = self._get( + f"/api/node/mo/uni/tn-{tenant}.json?query-target=children&target-subtree-class=fvBD" + ) # test this bd_dict = {} + bd_dict_schema = { + "name": "", + "tenant": "", + "description": "", + "vrf": None, + "vrf_tenant": None, + "subnets": [], + } for data in resp.json()["imdata"]: - bd_dict.setdefault(data["fvBD"]["attributes"]["name"], {}) - bd_dict[data["fvBD"]["attributes"]["name"]]["tenant"] = tenant_from_dn(data["fvBD"]["attributes"]["dn"]) - bd_dict[data["fvBD"]["attributes"]["name"]]["description"] = data["fvBD"]["attributes"]["descr"] - bd_dict[data["fvBD"]["attributes"]["name"]]["unicast_routing"] = data["fvBD"]["attributes"]["unicastRoute"] - bd_dict[data["fvBD"]["attributes"]["name"]]["mac"] = data["fvBD"]["attributes"]["mac"] - bd_dict[data["fvBD"]["attributes"]["name"]]["l2unicast"] = data["fvBD"]["attributes"]["unkMacUcastAct"] - - for key, value in bd_dict.items(): - # get the containing VRF - resp = self._get( - f"/api/node/mo/uni/tn-{value['tenant']}/BD-{key}.json?query-target=children&target-subtree-class=fvRsCtx" - ) - for data in resp.json()["imdata"]: - value["vrf"] = data["fvRsCtx"]["attributes"].get("tnFvCtxName", "default") - vrf_tenant = data["fvRsCtx"]["attributes"].get("tDn", None) + if "fvBD" in data.keys(): + bd_tenant = tenant_from_dn(data["fvBD"]["attributes"]["dn"]) + bd_name = data["fvBD"]["attributes"]["name"] + unique_name = f"{bd_name}:{bd_tenant}" + try: + bd_dict[unique_name] + except KeyError: + bd_dict.setdefault(unique_name, deepcopy(bd_dict_schema)) + bd_dict[unique_name]["tenant"] = tenant_from_dn(data["fvBD"]["attributes"]["dn"]) + bd_dict[unique_name]["name"] = data["fvBD"]["attributes"]["name"] + bd_dict[unique_name]["description"] = data["fvBD"]["attributes"]["descr"] + + elif "fvRsCtx" in data.keys(): + bd_tenant = tenant_from_dn(data["fvRsCtx"]["attributes"]["dn"]) + bd_name = bd_from_dn(data["fvRsCtx"]["attributes"]["dn"]) + unique_name = f"{bd_name}:{bd_tenant}" + try: + bd_dict[unique_name] + except KeyError: + bd_dict.setdefault(unique_name, deepcopy(bd_dict_schema)) + # BUGfix for non existent VRF, assumes BD Tenant. + bd_dict[unique_name]["vrf"] = data["fvRsCtx"]["attributes"].get("tnFvCtxName") or "default" + vrf_tenant = data["fvRsCtx"]["attributes"].get("tDn") if vrf_tenant: - value["vrf_tenant"] = tenant_from_dn(vrf_tenant) - else: - value["vrf_tenant"] = None - # get subnets - resp = self._get( - f"/api/node/mo/uni/tn-{value['tenant']}/BD-{key}.json?query-target=children&target-subtree-class=fvSubnet" - ) - subnet_list = [ - (data["fvSubnet"]["attributes"]["ip"], data["fvSubnet"]["attributes"]["scope"]) - for data in resp.json()["imdata"] - ] - for subnet in subnet_list: - value.setdefault("subnets", []) - value["subnets"].append(subnet) + bd_dict[unique_name]["vrf_tenant"] = tenant_from_dn(vrf_tenant) + + elif "fvSubnet" in data.keys(): + bd_tenant = tenant_from_dn(data["fvSubnet"]["attributes"]["dn"]) + bd_name = bd_from_dn(data["fvSubnet"]["attributes"]["dn"]) + unique_name = f"{bd_name}:{bd_tenant}" + try: + bd_dict[unique_name] + except KeyError: + bd_dict.setdefault(unique_name, deepcopy(bd_dict_schema)) + subnet = (data["fvSubnet"]["attributes"]["ip"], data["fvSubnet"]["attributes"]["scope"]) + (bd_dict[unique_name]["subnets"]).append(subnet) + else: + logger.error( + msg=f"Failed to load Bridge Domains data, unexpected response in {data}. Skipping Record..." + ) + continue return bd_dict def get_nodes(self) -> dict: @@ -394,12 +427,19 @@ def get_nodes(self) -> dict: mgmt_addr = f"{node['topSystem']['attributes']['address']}/{ip_network(node['topSystem']['attributes']['tepPool'], strict=False).prefixlen}" else: mgmt_addr = "" - if node["topSystem"]["attributes"]["tepPool"] != "0.0.0.0": # nosec: B104 - subnet = node["topSystem"]["attributes"]["tepPool"] - elif mgmt_addr: + # BUG detected in this block: changing + if mgmt_addr: subnet = ip_network(mgmt_addr, strict=False).with_prefixlen + elif node["topSystem"]["attributes"]["tepPool"] != "0.0.0.0": # nosec: B104 + subnet = node["topSystem"]["attributes"]["tepPool"] else: subnet = "" + # if node["topSystem"]["attributes"]["tepPool"] != "0.0.0.0": # nosec: B104 + # subnet = node["topSystem"]["attributes"]["tepPool"] + # elif mgmt_addr: + # subnet = ip_network(mgmt_addr, strict=False).with_prefixlen + # else: + # subnet = "" node_id = node["topSystem"]["attributes"]["id"] node_dict[node_id]["oob_ip"] = mgmt_addr node_dict[node_id]["subnet"] = subnet @@ -447,12 +487,19 @@ def get_controllers(self) -> dict: mgmt_addr = f"{node['topSystem']['attributes']['address']}/{ip_network(node['topSystem']['attributes']['tepPool'], strict=False).prefixlen}" else: mgmt_addr = "" - if node["topSystem"]["attributes"]["tepPool"] != "0.0.0.0": # nosec: B104 - subnet = node["topSystem"]["attributes"]["tepPool"] - elif mgmt_addr: + # BUG detected in this block: changing + if mgmt_addr: subnet = ip_network(mgmt_addr, strict=False).with_prefixlen + elif node["topSystem"]["attributes"]["tepPool"] != "0.0.0.0": # nosec: B104 + subnet = node["topSystem"]["attributes"]["tepPool"] else: subnet = "" + # if node["topSystem"]["attributes"]["tepPool"] != "0.0.0.0": # nosec: B104 + # subnet = node["topSystem"]["attributes"]["tepPool"] + # elif mgmt_addr: + # subnet = ip_network(mgmt_addr, strict=False).with_prefixlen + # else: + # subnet = "" node_id = node["topSystem"]["attributes"]["id"] node_dict[node_id]["pod_id"] = node["topSystem"]["attributes"]["podId"] node_dict[node_id]["oob_ip"] = mgmt_addr diff --git a/nautobot_ssot/integrations/aci/diffsync/device-types/APIC-SERVER-M3.yaml b/nautobot_ssot/integrations/aci/diffsync/device-types/APIC-SERVER-M3.yaml index 99121665..df0bca3d 100644 --- a/nautobot_ssot/integrations/aci/diffsync/device-types/APIC-SERVER-M3.yaml +++ b/nautobot_ssot/integrations/aci/diffsync/device-types/APIC-SERVER-M3.yaml @@ -35,6 +35,9 @@ interfaces: - name: ILO type: 1000base-t mgmt_only: true + - name: mgmt0 + type: 10gbase-t + mgmt_only: true - name: LAN-1 type: 10gbase-t mgmt_only: true diff --git a/nautobot_ssot/integrations/aci/diffsync/device-types/N9K-C93180YC-FX3.yaml b/nautobot_ssot/integrations/aci/diffsync/device-types/N9K-C93180YC-FX3.yaml new file mode 100644 index 00000000..da4357b4 --- /dev/null +++ b/nautobot_ssot/integrations/aci/diffsync/device-types/N9K-C93180YC-FX3.yaml @@ -0,0 +1,131 @@ +--- +manufacturer: Cisco +model: N9K-C93180YC-FX3 +part_number: N9K-C93180YC-FX3 +slug: n9k-c93180yc-fx3 +u_height: 1 +is_full_depth: true +console-ports: + - name: console + type: rj-45 +power-ports: + - name: Power Supply 1 + type: iec-60320-c14 + maximum_draw: 425 + allocated_draw: 260 + - name: Power Supply 2 + type: iec-60320-c14 + maximum_draw: 425 + allocated_draw: 260 +interfaces: + - name: Ethernet1/1 + type: 25gbase-x-sfp28 + - name: Ethernet1/2 + type: 25gbase-x-sfp28 + - name: Ethernet1/3 + type: 25gbase-x-sfp28 + - name: Ethernet1/4 + type: 25gbase-x-sfp28 + - name: Ethernet1/5 + type: 25gbase-x-sfp28 + - name: Ethernet1/6 + type: 25gbase-x-sfp28 + - name: Ethernet1/7 + type: 25gbase-x-sfp28 + - name: Ethernet1/8 + type: 25gbase-x-sfp28 + - name: Ethernet1/9 + type: 25gbase-x-sfp28 + - name: Ethernet1/10 + type: 25gbase-x-sfp28 + - name: Ethernet1/11 + type: 25gbase-x-sfp28 + - name: Ethernet1/12 + type: 25gbase-x-sfp28 + - name: Ethernet1/13 + type: 25gbase-x-sfp28 + - name: Ethernet1/14 + type: 25gbase-x-sfp28 + - name: Ethernet1/15 + type: 25gbase-x-sfp28 + - name: Ethernet1/16 + type: 25gbase-x-sfp28 + - name: Ethernet1/17 + type: 25gbase-x-sfp28 + - name: Ethernet1/18 + type: 25gbase-x-sfp28 + - name: Ethernet1/19 + type: 25gbase-x-sfp28 + - name: Ethernet1/20 + type: 25gbase-x-sfp28 + - name: Ethernet1/21 + type: 25gbase-x-sfp28 + - name: Ethernet1/22 + type: 25gbase-x-sfp28 + - name: Ethernet1/23 + type: 25gbase-x-sfp28 + - name: Ethernet1/24 + type: 25gbase-x-sfp28 + - name: Ethernet1/25 + type: 25gbase-x-sfp28 + - name: Ethernet1/26 + type: 25gbase-x-sfp28 + - name: Ethernet1/27 + type: 25gbase-x-sfp28 + - name: Ethernet1/28 + type: 25gbase-x-sfp28 + - name: Ethernet1/29 + type: 25gbase-x-sfp28 + - name: Ethernet1/30 + type: 25gbase-x-sfp28 + - name: Ethernet1/31 + type: 25gbase-x-sfp28 + - name: Ethernet1/32 + type: 25gbase-x-sfp28 + - name: Ethernet1/33 + type: 25gbase-x-sfp28 + - name: Ethernet1/34 + type: 25gbase-x-sfp28 + - name: Ethernet1/35 + type: 25gbase-x-sfp28 + - name: Ethernet1/36 + type: 25gbase-x-sfp28 + - name: Ethernet1/37 + type: 25gbase-x-sfp28 + - name: Ethernet1/38 + type: 25gbase-x-sfp28 + - name: Ethernet1/39 + type: 25gbase-x-sfp28 + - name: Ethernet1/40 + type: 25gbase-x-sfp28 + - name: Ethernet1/41 + type: 25gbase-x-sfp28 + - name: Ethernet1/42 + type: 25gbase-x-sfp28 + - name: Ethernet1/43 + type: 25gbase-x-sfp28 + - name: Ethernet1/44 + type: 25gbase-x-sfp28 + - name: Ethernet1/45 + type: 25gbase-x-sfp28 + - name: Ethernet1/46 + type: 25gbase-x-sfp28 + - name: Ethernet1/47 + type: 25gbase-x-sfp28 + - name: Ethernet1/48 + type: 25gbase-x-sfp28 + - name: Ethernet1/49 + type: 100gbase-x-qsfp28 + - name: Ethernet1/50 + type: 100gbase-x-qsfp28 + - name: Ethernet1/51 + type: 100gbase-x-qsfp28 + - name: Ethernet1/52 + type: 100gbase-x-qsfp28 + - name: Ethernet1/53 + type: 100gbase-x-qsfp28 + - name: Ethernet1/54 + type: 100gbase-x-qsfp28 + - name: mgmt0 + type: 1000base-t + mgmt_only: true diff --git a/nautobot_ssot/integrations/aci/diffsync/models/base.py b/nautobot_ssot/integrations/aci/diffsync/models/base.py index 14c27116..f7dc8a37 100644 --- a/nautobot_ssot/integrations/aci/diffsync/models/base.py +++ b/nautobot_ssot/integrations/aci/diffsync/models/base.py @@ -1,6 +1,6 @@ """Base Shared Models for Cisco ACI integration with SSoT app.""" -from typing import List, Optional +from typing import List, Optional, Dict from diffsync import DiffSyncModel @@ -9,12 +9,13 @@ class Tenant(DiffSyncModel): _modelname = "tenant" _identifiers = ("name",) - _attributes = ("description", "comments", "site_tag") + _attributes = ("description", "comments", "site_tag", "msite_tag") name: str description: Optional[str] comments: Optional[str] site_tag: str + msite_tag: bool class Vrf(DiffSyncModel): diff --git a/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py b/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py index 86e72d7f..da956722 100644 --- a/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py +++ b/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py @@ -1,6 +1,8 @@ """Nautobot Models for Cisco ACI integration with SSoT app.""" import logging +from ipaddress import ip_network +from django.db import IntegrityError from django.contrib.contenttypes.models import ContentType from nautobot.tenancy.models import Tenant as OrmTenant from nautobot.dcim.models import DeviceType as OrmDeviceType @@ -8,11 +10,11 @@ from nautobot.dcim.models import InterfaceTemplate as OrmInterfaceTemplate from nautobot.dcim.models import Interface as OrmInterface from nautobot.dcim.models import Location, LocationType +from nautobot.dcim.models import Manufacturer from nautobot.ipam.models import IPAddress as OrmIPAddress -from nautobot.ipam.models import Namespace, IPAddressToInterface +from nautobot.ipam.models import Namespace, IPAddressToInterface, VLAN from nautobot.ipam.models import Prefix as OrmPrefix from nautobot.ipam.models import VRF as OrmVrf -from nautobot.dcim.models import Manufacturer from nautobot.extras.models import Role, Status, Tag from nautobot_ssot.integrations.aci.diffsync.models.base import ( Tenant, @@ -38,20 +40,24 @@ class NautobotTenant(Tenant): def create(cls, diffsync, ids, attrs): """Create Tenant object in Nautobot.""" _tenant = OrmTenant(name=ids["name"], description=attrs["description"], comments=attrs["comments"]) + if attrs["msite_tag"]: + _tenant.tags.add(Tag.objects.get(name="ACI_MULTISITE")) _tenant.tags.add(Tag.objects.get(name=PLUGIN_CFG.get("tag"))) _tenant.tags.add(Tag.objects.get(name=attrs["site_tag"])) _tenant.validated_save() - Namespace.objects.create(name=ids["name"]) + Namespace.objects.get_or_create(name=ids["name"]) return super().create(ids=ids, diffsync=diffsync, attrs=attrs) def update(self, attrs): """Update Tenant object in Nautobot.""" _tenant = OrmTenant.objects.get(name=self.name) - if attrs.get("description"): - _tenant.description = attrs["description"] - if attrs.get("comments"): - _tenant.comments = attrs["comments"] + # if attrs.get("description"): Fix, this does not cover empty descr update + # _tenant.description = attrs["description"] + _tenant.description = attrs.get("description", "") + # if attrs.get("comments"): + # _tenant.comments = attrs["comments"] + _tenant.comments = attrs.get("comments", "") _tenant.validated_save() return super().update(attrs) @@ -347,23 +353,44 @@ def create(cls, diffsync, ids, attrs): intf = None if attrs["device"] and attrs["interface"]: try: - intf = OrmInterface.objects.get(name=_interface, device__name=_device) + intf = OrmInterface.objects.get( + name=_interface, device__name=_device, device__location__name=ids["site"] + ) except OrmInterface.DoesNotExist: - diffsync.job.logger.warning(f"{_device} missing interface {_interface} to assign {ids['address']}") + diffsync.job.logger.warning(f"{_device} missing interface {_interface} to assign {ids['address']}.") + except OrmInterface.MultipleObjectsReturned: + diffsync.job.logger.warning(f"Found Multiple {_interface} in {_device} to assign {ids['address']}.") if ids["tenant"]: - tenant_name = OrmTenant.objects.get(name=ids["tenant"]) + _tenant = OrmTenant.objects.get(name=ids["tenant"]) else: - tenant_name = None + _tenant = None + # BUGfix here + try: + _namespace = Namespace.objects.get(name=ids["namespace"]) + _parent = OrmPrefix.objects.get(prefix=attrs["prefix"], namespace=_namespace) + except Namespace.DoesNotExist: + diffsync.job.logger.warning(f"{ids['namespace']} missing Namespace to assign IP address: {ids['address']}") + return + except OrmPrefix.DoesNotExist: + diffsync.job.logger.warning( + f"{attrs['prefix']} missing Parent Prefix to assign IP address: {ids['address']}" + ) + return + try: + _ipaddress = OrmIPAddress.objects.create( + address=ids["address"], + status=Status.objects.get(name=attrs["status"]), + description=attrs["description"], + namespace=_namespace, + parent=_parent, + tenant=_tenant, + ) + except IntegrityError: # BUGFix Multiple BDs leaking same IP address space to common tenant + diffsync.job.logger.warning( + f"Unable to create IP Address {ids['address']}. Duplicate Address or Parent Prefix: {attrs['prefix']} in Namespace: {ids['namespace']}" + ) + return - namespace = Namespace.objects.get(name=ids["namespace"]) - _ipaddress = OrmIPAddress.objects.create( - address=ids["address"], - status=Status.objects.get(name=attrs["status"]), - description=attrs["description"], - namespace=namespace, - parent=OrmPrefix.objects.get(prefix=attrs["prefix"], namespace=namespace), - tenant=tenant_name, - ) if intf: mapping = IPAddressToInterface.objects.create(ip_address=_ipaddress, interface=intf) mapping.validated_save() @@ -382,13 +409,19 @@ def create(cls, diffsync, ids, attrs): def update(self, attrs): """Update IPAddress object in Nautobot.""" - _ipaddress = OrmIPAddress.objects.get(address=self.address) + _ipaddress = OrmIPAddress.objects.get( + address=self.address, tenant__name=self.tenant, parent__namespace__name=self.namespace + ) if attrs.get("description"): _ipaddress.description = attrs["description"] if attrs.get("tenant"): _ipaddress.tenant = OrmTenant.objects.get(name=self.tenant) if attrs.get("device") and attrs.get("interface"): - intf = OrmInterface.objects.get(name=attrs["interface"], device__name=attrs["device"]) + intf = OrmInterface.objects.get( + name=attrs["interface"], + device__name=attrs["device"], + device__location__name=self.site, + ) mapping = IPAddressToInterface.objects.create(ip_address=_ipaddress, interface=intf) mapping.validated_save() if attrs.get("status"): @@ -419,8 +452,11 @@ def create(cls, diffsync, ids, attrs): try: vrf_tenant = OrmTenant.objects.get(name=attrs["vrf_tenant"]) except OrmTenant.DoesNotExist: - diffsync.job.logger.warning(f"Tenant {attrs['vrf_tenant']} not found for VRF {attrs['vrf']}") + diffsync.job.logger.warning( + f"Tenant {attrs['vrf_tenant']} not found for VRF while creating Prefix: {ids['prefix']}" + ) # modified to avoid Key Error vrf_tenant = None + return if ids["vrf"] and vrf_tenant: try: @@ -430,7 +466,9 @@ def create(cls, diffsync, ids, attrs): vrf = None else: vrf = None - _prefix = OrmPrefix.objects.create( + + # diffsync.job.logger.info(msg=f"Processing Prefix {ids['prefix']} in Namespace: {attrs['namespace']}, Tenant: {attrs['vrf_tenant']}") + _prefix, created = OrmPrefix.objects.get_or_create( # fixing adding error correction prefix=ids["prefix"], status=Status.objects.get(name=attrs["status"]), description=attrs["description"], @@ -438,6 +476,12 @@ def create(cls, diffsync, ids, attrs): tenant=OrmTenant.objects.get(name=attrs["vrf_tenant"]), location=Location.objects.get(name=ids["site"], location_type=LocationType.objects.get(name="Site")), ) + + if not created: + diffsync.job.logger.warning( + f"Prefix: {_prefix.prefix} duplicate in Namespace: {_prefix.namespace.name}. Skipping .." + ) + return if vrf: _prefix.vrfs.add(vrf) _prefix.tags.add(Tag.objects.get(name=PLUGIN_CFG.get("tag"))) @@ -479,7 +523,7 @@ def delete(self): _prefix = OrmPrefix.objects.get( prefix=self.prefix, tenant=tenant, - vrf=OrmVrf.objects.get(name=self.vrf, tenant=vrf_tenant), + vrfs=OrmVrf.objects.get(name=self.vrf, tenant=vrf_tenant), # fixed bug here ) self.diffsync.objects_to_delete["prefix"].append(_prefix) # pylint: disable=protected-access return self diff --git a/nautobot_ssot/integrations/aci/diffsync/utils.py b/nautobot_ssot/integrations/aci/diffsync/utils.py index 3f3bbe2e..c9bf072d 100644 --- a/nautobot_ssot/integrations/aci/diffsync/utils.py +++ b/nautobot_ssot/integrations/aci/diffsync/utils.py @@ -40,10 +40,22 @@ def tenant_from_dn(dn): def ap_from_dn(dn): """Match an ACI Application Profile in the Distinguished Name (DN).""" - pattern = "ap-[A-Za-z0-9\-]+" # noqa: W605 # pylint: disable=anomalous-backslash-in-string + pattern = "ap-[A-Za-z0-9\-\_]+" # noqa: W605 # pylint: disable=anomalous-backslash-in-string return re.search(pattern, dn).group().replace("ap-", "", 1).rstrip("/") +def bd_from_dn(dn): + """Match an ACI Bridge Domain in the Distinguished Name (DN).""" + pattern = "BD-[A-Za-z0-9\-\_]+" # noqa: W605 # pylint: disable=anomalous-backslash-in-string + return re.search(pattern, dn).group().replace("BD-", "", 1).rstrip("/") + + +def epg_from_dn(dn): + """Match an ACI Endpoint Group in the Distinguished Name (DN).""" + pattern = "epg-[A-Za-z0-9\-\_]+" # noqa: W605 # pylint: disable=anomalous-backslash-in-string + return re.search(pattern, dn).group().replace("epg-", "", 1).rstrip("/") + + def load_yamlfile(filename): """Load a YAML file to a Dict.""" with open(filename, "r", encoding="utf-8") as fn: diff --git a/nautobot_ssot/integrations/aci/jobs.py b/nautobot_ssot/integrations/aci/jobs.py index b013544d..8e0fd7a1 100644 --- a/nautobot_ssot/integrations/aci/jobs.py +++ b/nautobot_ssot/integrations/aci/jobs.py @@ -53,7 +53,8 @@ def __init__(self): """Initialize ExampleYAMLDataSource.""" super().__init__() self.diffsync_flags = ( - self.diffsync_flags | DiffSyncFlags.SKIP_UNMATCHED_DST # pylint: disable=unsupported-binary-operation + self.diffsync_flags + # self.diffsync_flags | DiffSyncFlags.SKIP_UNMATCHED_DST # pylint: disable=unsupported-binary-operation ) @classmethod diff --git a/nautobot_ssot/integrations/aci/signals.py b/nautobot_ssot/integrations/aci/signals.py index a29bf833..4cd29f5f 100644 --- a/nautobot_ssot/integrations/aci/signals.py +++ b/nautobot_ssot/integrations/aci/signals.py @@ -38,6 +38,10 @@ def aci_create_tag(apps, **kwargs): name=PLUGIN_CFG.get("tag_down"), color=PLUGIN_CFG.get("tag_down_color"), ) + tag.objects.update_or_create( + name="ACI_MULTISITE", + color="03a9f4", + ) apics = PLUGIN_CFG.get("apics") for key in apics: if ("SITE" in key or "STAGE" in key) and not tag.objects.filter(name=apics[key]).exists(): @@ -62,12 +66,14 @@ def aci_create_site(apps, **kwargs): Device = apps.get_model("dcim", "Device") Site = apps.get_model("dcim", "Location") Prefix = apps.get_model("ipam", "Prefix") + Vlan = apps.get_model("ipam", "VLAN") location_type = apps.get_model("dcim", "LocationType") status = apps.get_model("extras", "Status") apics = PLUGIN_CFG.get("apics") loc_type = location_type.objects.update_or_create(name="Site")[0] loc_type.content_types.add(ContentType.objects.get_for_model(Device)) loc_type.content_types.add(ContentType.objects.get_for_model(Prefix)) + loc_type.content_types.add(ContentType.objects.get_for_model(Vlan)) active_status = status.objects.update_or_create(name="Active")[0] for key in apics: if "SITE" in key: From 842182ab6f34f3d3f219bdb659de7c4b9b3d44ac Mon Sep 17 00:00:00 2001 From: pvillar_netdev Date: Thu, 25 Jul 2024 14:34:07 +0200 Subject: [PATCH 02/28] Update APIC-SERVER-L3.yaml --- .../integrations/aci/diffsync/device-types/APIC-SERVER-L3.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nautobot_ssot/integrations/aci/diffsync/device-types/APIC-SERVER-L3.yaml b/nautobot_ssot/integrations/aci/diffsync/device-types/APIC-SERVER-L3.yaml index 0fc92a74..7ff97a4b 100644 --- a/nautobot_ssot/integrations/aci/diffsync/device-types/APIC-SERVER-L3.yaml +++ b/nautobot_ssot/integrations/aci/diffsync/device-types/APIC-SERVER-L3.yaml @@ -35,6 +35,9 @@ interfaces: - name: ILO type: 1000base-t mgmt_only: true + - name: mgmt0 + type: 10gbase-t + mgmt_only: true - name: LAN-1 type: 10gbase-t mgmt_only: true From 5ea1d2927a5b781510f5e77e8e622f4f7c6d0f3e Mon Sep 17 00:00:00 2001 From: pvillar_netdev Date: Thu, 25 Jul 2024 14:40:51 +0200 Subject: [PATCH 03/28] Add files via upload --- .../device-types/N9K-C93360YC-FX2.yaml | 251 ++++++++++++++++++ .../device-types/N9K-C93600CD-GX.yaml | 91 +++++++ 2 files changed, 342 insertions(+) create mode 100644 nautobot_ssot/integrations/aci/diffsync/device-types/N9K-C93360YC-FX2.yaml create mode 100644 nautobot_ssot/integrations/aci/diffsync/device-types/N9K-C93600CD-GX.yaml diff --git a/nautobot_ssot/integrations/aci/diffsync/device-types/N9K-C93360YC-FX2.yaml b/nautobot_ssot/integrations/aci/diffsync/device-types/N9K-C93360YC-FX2.yaml new file mode 100644 index 00000000..257ad2b8 --- /dev/null +++ b/nautobot_ssot/integrations/aci/diffsync/device-types/N9K-C93360YC-FX2.yaml @@ -0,0 +1,251 @@ +--- +manufacturer: Cisco +model: Nexus 93360YC-FX2 +part_number: N9K-C93360YC-FX2 +slug: cisco-n9k-c93360yc-fx2 +front_image: true +rear_image: true +u_height: 2 +is_full_depth: true +console-ports: + - name: console + type: rj-45 + - name: usb1 + type: usb-a +interfaces: + - name: Ethernet1/1 + type: 25gbase-x-sfp28 + - name: Ethernet1/2 + type: 25gbase-x-sfp28 + - name: Ethernet1/3 + type: 25gbase-x-sfp28 + - name: Ethernet1/4 + type: 25gbase-x-sfp28 + - name: Ethernet1/5 + type: 25gbase-x-sfp28 + - name: Ethernet1/6 + type: 25gbase-x-sfp28 + - name: Ethernet1/7 + type: 25gbase-x-sfp28 + - name: Ethernet1/8 + type: 25gbase-x-sfp28 + - name: Ethernet1/9 + type: 25gbase-x-sfp28 + - name: Ethernet1/10 + type: 25gbase-x-sfp28 + - name: Ethernet1/11 + type: 25gbase-x-sfp28 + - name: Ethernet1/12 + type: 25gbase-x-sfp28 + - name: Ethernet1/13 + type: 25gbase-x-sfp28 + - name: Ethernet1/14 + type: 25gbase-x-sfp28 + - name: Ethernet1/15 + type: 25gbase-x-sfp28 + - name: Ethernet1/16 + type: 25gbase-x-sfp28 + - name: Ethernet1/17 + type: 25gbase-x-sfp28 + - name: Ethernet1/18 + type: 25gbase-x-sfp28 + - name: Ethernet1/19 + type: 25gbase-x-sfp28 + - name: Ethernet1/20 + type: 25gbase-x-sfp28 + - name: Ethernet1/21 + type: 25gbase-x-sfp28 + - name: Ethernet1/22 + type: 25gbase-x-sfp28 + - name: Ethernet1/23 + type: 25gbase-x-sfp28 + - name: Ethernet1/24 + type: 25gbase-x-sfp28 + - name: Ethernet1/25 + type: 25gbase-x-sfp28 + - name: Ethernet1/26 + type: 25gbase-x-sfp28 + - name: Ethernet1/27 + type: 25gbase-x-sfp28 + - name: Ethernet1/28 + type: 25gbase-x-sfp28 + - name: Ethernet1/29 + type: 25gbase-x-sfp28 + - name: Ethernet1/30 + type: 25gbase-x-sfp28 + - name: Ethernet1/31 + type: 25gbase-x-sfp28 + - name: Ethernet1/32 + type: 25gbase-x-sfp28 + - name: Ethernet1/33 + type: 25gbase-x-sfp28 + - name: Ethernet1/34 + type: 25gbase-x-sfp28 + - name: Ethernet1/35 + type: 25gbase-x-sfp28 + - name: Ethernet1/36 + type: 25gbase-x-sfp28 + - name: Ethernet1/37 + type: 25gbase-x-sfp28 + - name: Ethernet1/38 + type: 25gbase-x-sfp28 + - name: Ethernet1/39 + type: 25gbase-x-sfp28 + - name: Ethernet1/40 + type: 25gbase-x-sfp28 + - name: Ethernet1/41 + type: 25gbase-x-sfp28 + - name: Ethernet1/42 + type: 25gbase-x-sfp28 + - name: Ethernet1/43 + type: 25gbase-x-sfp28 + - name: Ethernet1/44 + type: 25gbase-x-sfp28 + - name: Ethernet1/45 + type: 25gbase-x-sfp28 + - name: Ethernet1/46 + type: 25gbase-x-sfp28 + - name: Ethernet1/47 + type: 25gbase-x-sfp28 + - name: Ethernet1/48 + type: 25gbase-x-sfp28 + - name: Ethernet1/49 + type: 25gbase-x-sfp28 + - name: Ethernet1/50 + type: 25gbase-x-sfp28 + - name: Ethernet1/51 + type: 25gbase-x-sfp28 + - name: Ethernet1/52 + type: 25gbase-x-sfp28 + - name: Ethernet1/53 + type: 25gbase-x-sfp28 + - name: Ethernet1/54 + type: 25gbase-x-sfp28 + - name: Ethernet1/55 + type: 25gbase-x-sfp28 + - name: Ethernet1/56 + type: 25gbase-x-sfp28 + - name: Ethernet1/57 + type: 25gbase-x-sfp28 + - name: Ethernet1/58 + type: 25gbase-x-sfp28 + - name: Ethernet1/59 + type: 25gbase-x-sfp28 + - name: Ethernet1/60 + type: 25gbase-x-sfp28 + - name: Ethernet1/61 + type: 25gbase-x-sfp28 + - name: Ethernet1/62 + type: 25gbase-x-sfp28 + - name: Ethernet1/63 + type: 25gbase-x-sfp28 + - name: Ethernet1/64 + type: 25gbase-x-sfp28 + - name: Ethernet1/65 + type: 25gbase-x-sfp28 + - name: Ethernet1/66 + type: 25gbase-x-sfp28 + - name: Ethernet1/67 + type: 25gbase-x-sfp28 + - name: Ethernet1/68 + type: 25gbase-x-sfp28 + - name: Ethernet1/69 + type: 25gbase-x-sfp28 + - name: Ethernet1/70 + type: 25gbase-x-sfp28 + - name: Ethernet1/71 + type: 25gbase-x-sfp28 + - name: Ethernet1/72 + type: 25gbase-x-sfp28 + - name: Ethernet1/73 + type: 25gbase-x-sfp28 + - name: Ethernet1/74 + type: 25gbase-x-sfp28 + - name: Ethernet1/75 + type: 25gbase-x-sfp28 + - name: Ethernet1/76 + type: 25gbase-x-sfp28 + - name: Ethernet1/77 + type: 25gbase-x-sfp28 + - name: Ethernet1/78 + type: 25gbase-x-sfp28 + - name: Ethernet1/79 + type: 25gbase-x-sfp28 + - name: Ethernet1/80 + type: 25gbase-x-sfp28 + - name: Ethernet1/81 + type: 25gbase-x-sfp28 + - name: Ethernet1/82 + type: 25gbase-x-sfp28 + - name: Ethernet1/83 + type: 25gbase-x-sfp28 + - name: Ethernet1/84 + type: 25gbase-x-sfp28 + - name: Ethernet1/85 + type: 25gbase-x-sfp28 + - name: Ethernet1/86 + type: 25gbase-x-sfp28 + - name: Ethernet1/87 + type: 25gbase-x-sfp28 + - name: Ethernet1/88 + type: 25gbase-x-sfp28 + - name: Ethernet1/89 + type: 25gbase-x-sfp28 + - name: Ethernet1/90 + type: 25gbase-x-sfp28 + - name: Ethernet1/91 + type: 25gbase-x-sfp28 + - name: Ethernet1/92 + type: 25gbase-x-sfp28 + - name: Ethernet1/93 + type: 25gbase-x-sfp28 + - name: Ethernet1/94 + type: 25gbase-x-sfp28 + - name: Ethernet1/95 + type: 25gbase-x-sfp28 + - name: Ethernet1/96 + type: 25gbase-x-sfp28 + - name: Ethernet1/97 + type: 100gbase-x-qsfp28 + - name: Ethernet1/98 + type: 100gbase-x-qsfp28 + - name: Ethernet1/99 + type: 100gbase-x-qsfp28 + - name: Ethernet1/100 + type: 100gbase-x-qsfp28 + - name: Ethernet1/101 + type: 100gbase-x-qsfp28 + - name: Ethernet1/102 + type: 100gbase-x-qsfp28 + - name: Ethernet1/103 + type: 100gbase-x-qsfp28 + - name: Ethernet1/104 + type: 100gbase-x-qsfp28 + - name: Ethernet1/105 + type: 100gbase-x-qsfp28 + - name: Ethernet1/106 + type: 100gbase-x-qsfp28 + - name: Ethernet1/107 + type: 100gbase-x-qsfp28 + - name: Ethernet1/108 + type: 100gbase-x-qsfp28 + - name: mgmt0 + type: 1000base-t + mgmt_only: true +module-bays: + - name: PS1 + label: Power Supply 1 + position: '1' + - name: PS2 + label: Power Supply 2 + position: '2' + - name: Fan 1 + position: '1' + - name: Fan 2 + position: '2' + - name: Fan 3 + position: '3' + - name: Fan 4 + position: '4' + - name: Fan 5 + position: '5' diff --git a/nautobot_ssot/integrations/aci/diffsync/device-types/N9K-C93600CD-GX.yaml b/nautobot_ssot/integrations/aci/diffsync/device-types/N9K-C93600CD-GX.yaml new file mode 100644 index 00000000..ba7f00d9 --- /dev/null +++ b/nautobot_ssot/integrations/aci/diffsync/device-types/N9K-C93600CD-GX.yaml @@ -0,0 +1,91 @@ +--- +manufacturer: Cisco +model: Nexus 93600CD-GX +part_number: N9K-C93600CD-GX +slug: cisco-n9k-c93600cd-gx +u_height: 1 +is_full_depth: true +console-ports: + - name: console + type: rj-45 +interfaces: + - name: Ethernet1/1 + type: 100gbase-x-qsfp28 + - name: Ethernet1/2 + type: 100gbase-x-qsfp28 + - name: Ethernet1/3 + type: 100gbase-x-qsfp28 + - name: Ethernet1/4 + type: 100gbase-x-qsfp28 + - name: Ethernet1/5 + type: 100gbase-x-qsfp28 + - name: Ethernet1/6 + type: 100gbase-x-qsfp28 + - name: Ethernet1/7 + type: 100gbase-x-qsfp28 + - name: Ethernet1/8 + type: 100gbase-x-qsfp28 + - name: Ethernet1/9 + type: 100gbase-x-qsfp28 + - name: Ethernet1/10 + type: 100gbase-x-qsfp28 + - name: Ethernet1/11 + type: 100gbase-x-qsfp28 + - name: Ethernet1/12 + type: 100gbase-x-qsfp28 + - name: Ethernet1/13 + type: 100gbase-x-qsfp28 + - name: Ethernet1/14 + type: 100gbase-x-qsfp28 + - name: Ethernet1/15 + type: 100gbase-x-qsfp28 + - name: Ethernet1/16 + type: 100gbase-x-qsfp28 + - name: Ethernet1/17 + type: 100gbase-x-qsfp28 + - name: Ethernet1/18 + type: 100gbase-x-qsfp28 + - name: Ethernet1/19 + type: 100gbase-x-qsfp28 + - name: Ethernet1/20 + type: 100gbase-x-qsfp28 + - name: Ethernet1/21 + type: 100gbase-x-qsfp28 + - name: Ethernet1/22 + type: 100gbase-x-qsfp28 + - name: Ethernet1/23 + type: 100gbase-x-qsfp28 + - name: Ethernet1/24 + type: 100gbase-x-qsfp28 + - name: Ethernet1/25 + type: 100gbase-x-qsfp28 + - name: Ethernet1/26 + type: 100gbase-x-qsfp28 + - name: Ethernet1/27 + type: 100gbase-x-qsfp28 + - name: Ethernet1/28 + type: 100gbase-x-qsfp28 + - name: Ethernet1/29 + type: 400gbase-x-qsfpdd + - name: Ethernet1/30 + type: 400gbase-x-qsfpdd + - name: Ethernet1/31 + type: 400gbase-x-qsfpdd + - name: Ethernet1/32 + type: 400gbase-x-qsfpdd + - name: Ethernet1/33 + type: 400gbase-x-qsfpdd + - name: Ethernet1/34 + type: 400gbase-x-qsfpdd + - name: Ethernet1/35 + type: 400gbase-x-qsfpdd + - name: Ethernet1/36 + type: 400gbase-x-qsfpdd + - name: mgmt0 + type: 1000base-t + mgmt_only: true +module-bays: + - name: PS1 + position: '1' + - name: PS2 + position: '2' From 0fb790721742e50c69b81cef9e25d352d3eba974 Mon Sep 17 00:00:00 2001 From: pvillar_netdev Date: Thu, 25 Jul 2024 14:52:17 +0200 Subject: [PATCH 04/28] Update aci.py --- .../integrations/aci/diffsync/adapters/aci.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py b/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py index 627dfc43..f4791923 100644 --- a/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py +++ b/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py @@ -212,11 +212,15 @@ def load_ipaddresses(self): vrf_tenant = f"{self.tenant_prefix}:{bd_value['vrf_tenant']}" else: vrf_tenant = None + if bd_value.get('tenant') == 'mgmt': # BUGFix + _namespace = "Global" + else: + _namespace = vrf_tenant or tenant_name for subnet in bd_value["subnets"]: prefix = ip_network(subnet[0], strict=False).with_prefixlen self.load_subnet_as_prefix( prefix=prefix, - namespace=tenant_name, + namespace=_namespace, site=self.site, vrf=bd_value["vrf"], vrf_tenant=vrf_tenant, @@ -230,7 +234,7 @@ def load_ipaddresses(self): device=None, interface=None, tenant=vrf_tenant or tenant_name, # BUGfix - namespace=vrf_tenant or tenant_name, # BUGfix + namespace=_namespace, # BUGfix site=self.site, site_tag=self.site, ) @@ -257,11 +261,15 @@ def load_prefixes(self): vrf_tenant = f"{self.tenant_prefix}:{bd_value['vrf_tenant']}" else: vrf_tenant = None + if bd_value.get('tenant') == 'mgmt': # BUGFix + _namespace = "Global" + else: + _namespace = vrf_tenant or tenant_name if bd_value.get("tenant") not in PLUGIN_CFG.get("ignore_tenants"): # modified for bugfix for subnet in bd_value["subnets"]: new_prefix = self.prefix( prefix=str(ip_network(subnet[0], strict=False)), - namespace=vrf_tenant or tenant_name, # BUGfix + namespace=_namespace, # BUGfix status="Active", site=self.site, description=f"ACI Bridge Domain: {bd_key}", From 88df9c156ac2d1faf52476d97e630702dfa732b1 Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Fri, 26 Jul 2024 11:28:48 +0200 Subject: [PATCH 05/28] Several issues fixed --- .../integrations/aci/diffsync/adapters/aci.py | 12 ++++++------ nautobot_ssot/integrations/aci/diffsync/client.py | 1 - .../integrations/aci/diffsync/models/nautobot.py | 3 +-- nautobot_ssot/integrations/aci/jobs.py | 1 - 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py b/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py index f4791923..baf0b39b 100644 --- a/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py +++ b/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py @@ -212,7 +212,7 @@ def load_ipaddresses(self): vrf_tenant = f"{self.tenant_prefix}:{bd_value['vrf_tenant']}" else: vrf_tenant = None - if bd_value.get('tenant') == 'mgmt': # BUGFix + if bd_value.get('tenant') == 'mgmt': _namespace = "Global" else: _namespace = vrf_tenant or tenant_name @@ -233,8 +233,8 @@ def load_ipaddresses(self): description=f"ACI Bridge Domain: {bd_key}", device=None, interface=None, - tenant=vrf_tenant or tenant_name, # BUGfix - namespace=_namespace, # BUGfix + tenant=vrf_tenant or tenant_name, + namespace=_namespace, site=self.site, site_tag=self.site, ) @@ -261,15 +261,15 @@ def load_prefixes(self): vrf_tenant = f"{self.tenant_prefix}:{bd_value['vrf_tenant']}" else: vrf_tenant = None - if bd_value.get('tenant') == 'mgmt': # BUGFix + if bd_value.get('tenant') == 'mgmt': _namespace = "Global" else: _namespace = vrf_tenant or tenant_name - if bd_value.get("tenant") not in PLUGIN_CFG.get("ignore_tenants"): # modified for bugfix + if bd_value.get("tenant") not in PLUGIN_CFG.get("ignore_tenants"): for subnet in bd_value["subnets"]: new_prefix = self.prefix( prefix=str(ip_network(subnet[0], strict=False)), - namespace=_namespace, # BUGfix + namespace=_namespace, status="Active", site=self.site, description=f"ACI Bridge Domain: {bd_key}", diff --git a/nautobot_ssot/integrations/aci/diffsync/client.py b/nautobot_ssot/integrations/aci/diffsync/client.py index 79b966e9..3aaf8ba2 100644 --- a/nautobot_ssot/integrations/aci/diffsync/client.py +++ b/nautobot_ssot/integrations/aci/diffsync/client.py @@ -378,7 +378,6 @@ def get_bds(self, tenant: str = "all") -> dict: bd_dict[unique_name] except KeyError: bd_dict.setdefault(unique_name, deepcopy(bd_dict_schema)) - # BUGfix for non existent VRF, assumes BD Tenant. bd_dict[unique_name]["vrf"] = data["fvRsCtx"]["attributes"].get("tnFvCtxName") or "default" vrf_tenant = data["fvRsCtx"]["attributes"].get("tDn") if vrf_tenant: diff --git a/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py b/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py index da956722..d38e0e4d 100644 --- a/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py +++ b/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py @@ -364,7 +364,6 @@ def create(cls, diffsync, ids, attrs): _tenant = OrmTenant.objects.get(name=ids["tenant"]) else: _tenant = None - # BUGfix here try: _namespace = Namespace.objects.get(name=ids["namespace"]) _parent = OrmPrefix.objects.get(prefix=attrs["prefix"], namespace=_namespace) @@ -385,7 +384,7 @@ def create(cls, diffsync, ids, attrs): parent=_parent, tenant=_tenant, ) - except IntegrityError: # BUGFix Multiple BDs leaking same IP address space to common tenant + except IntegrityError: diffsync.job.logger.warning( f"Unable to create IP Address {ids['address']}. Duplicate Address or Parent Prefix: {attrs['prefix']} in Namespace: {ids['namespace']}" ) diff --git a/nautobot_ssot/integrations/aci/jobs.py b/nautobot_ssot/integrations/aci/jobs.py index 8e0fd7a1..ede861a6 100644 --- a/nautobot_ssot/integrations/aci/jobs.py +++ b/nautobot_ssot/integrations/aci/jobs.py @@ -54,7 +54,6 @@ def __init__(self): super().__init__() self.diffsync_flags = ( self.diffsync_flags - # self.diffsync_flags | DiffSyncFlags.SKIP_UNMATCHED_DST # pylint: disable=unsupported-binary-operation ) @classmethod From 0f6cddbea90a0f63a3f87a726b7f38b20acefc50 Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Fri, 26 Jul 2024 11:55:03 +0200 Subject: [PATCH 06/28] Several issues fixed --- nautobot_ssot/integrations/aci/diffsync/adapters/aci.py | 4 ++-- nautobot_ssot/integrations/aci/diffsync/client.py | 3 --- nautobot_ssot/integrations/aci/diffsync/models/base.py | 2 +- nautobot_ssot/integrations/aci/diffsync/models/nautobot.py | 3 +-- nautobot_ssot/integrations/aci/jobs.py | 3 +-- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py b/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py index baf0b39b..01127b77 100644 --- a/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py +++ b/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py @@ -212,7 +212,7 @@ def load_ipaddresses(self): vrf_tenant = f"{self.tenant_prefix}:{bd_value['vrf_tenant']}" else: vrf_tenant = None - if bd_value.get('tenant') == 'mgmt': + if bd_value.get("tenant") == 'mgmt': _namespace = "Global" else: _namespace = vrf_tenant or tenant_name @@ -261,7 +261,7 @@ def load_prefixes(self): vrf_tenant = f"{self.tenant_prefix}:{bd_value['vrf_tenant']}" else: vrf_tenant = None - if bd_value.get('tenant') == 'mgmt': + if bd_value.get("tenant") == 'mgmt': _namespace = "Global" else: _namespace = vrf_tenant or tenant_name diff --git a/nautobot_ssot/integrations/aci/diffsync/client.py b/nautobot_ssot/integrations/aci/diffsync/client.py index 3aaf8ba2..a8c43c19 100644 --- a/nautobot_ssot/integrations/aci/diffsync/client.py +++ b/nautobot_ssot/integrations/aci/diffsync/client.py @@ -4,7 +4,6 @@ import re import sys import logging -import itertools import requests import urllib3 from datetime import datetime, timedelta @@ -16,7 +15,6 @@ pod_from_dn, fex_id_from_dn, interface_from_dn, - epg_from_dn, bd_from_dn, ) from copy import deepcopy @@ -336,7 +334,6 @@ def get_vrfs(self, tenant: str) -> list: ] return vrf_list - def get_bds(self, tenant: str = "all") -> dict: """Return Bridge Domains and Subnets from the Cisco APIC.""" # TODO: rewrite using one API call -> https://10.101.40.2/api/node/class/fvBD.json?query-target=subtree&target-subtree-class=fvBD,fvRsCtx,fvSubnet diff --git a/nautobot_ssot/integrations/aci/diffsync/models/base.py b/nautobot_ssot/integrations/aci/diffsync/models/base.py index f7dc8a37..539501aa 100644 --- a/nautobot_ssot/integrations/aci/diffsync/models/base.py +++ b/nautobot_ssot/integrations/aci/diffsync/models/base.py @@ -1,6 +1,6 @@ """Base Shared Models for Cisco ACI integration with SSoT app.""" -from typing import List, Optional, Dict +from typing import List, Optional from diffsync import DiffSyncModel diff --git a/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py b/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py index d38e0e4d..f4d9be7f 100644 --- a/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py +++ b/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py @@ -1,7 +1,6 @@ """Nautobot Models for Cisco ACI integration with SSoT app.""" import logging -from ipaddress import ip_network from django.db import IntegrityError from django.contrib.contenttypes.models import ContentType from nautobot.tenancy.models import Tenant as OrmTenant @@ -12,7 +11,7 @@ from nautobot.dcim.models import Location, LocationType from nautobot.dcim.models import Manufacturer from nautobot.ipam.models import IPAddress as OrmIPAddress -from nautobot.ipam.models import Namespace, IPAddressToInterface, VLAN +from nautobot.ipam.models import Namespace, IPAddressToInterface from nautobot.ipam.models import Prefix as OrmPrefix from nautobot.ipam.models import VRF as OrmVrf from nautobot.extras.models import Role, Status, Tag diff --git a/nautobot_ssot/integrations/aci/jobs.py b/nautobot_ssot/integrations/aci/jobs.py index ede861a6..6b5692ff 100644 --- a/nautobot_ssot/integrations/aci/jobs.py +++ b/nautobot_ssot/integrations/aci/jobs.py @@ -2,7 +2,6 @@ from django.templatetags.static import static from django.urls import reverse -from diffsync import DiffSyncFlags from nautobot.core.settings_funcs import is_truthy from nautobot.extras.jobs import BooleanVar, ChoiceVar, Job from nautobot_ssot.jobs.base import DataMapping, DataSource @@ -53,7 +52,7 @@ def __init__(self): """Initialize ExampleYAMLDataSource.""" super().__init__() self.diffsync_flags = ( - self.diffsync_flags + self.diffsync_flags, ) @classmethod From ed4f92e1f60d9de24a48cccb138555452073ba5b Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Fri, 26 Jul 2024 11:58:30 +0200 Subject: [PATCH 07/28] Several issues fixed --- nautobot_ssot/integrations/aci/jobs.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/nautobot_ssot/integrations/aci/jobs.py b/nautobot_ssot/integrations/aci/jobs.py index 6b5692ff..a3f89f1f 100644 --- a/nautobot_ssot/integrations/aci/jobs.py +++ b/nautobot_ssot/integrations/aci/jobs.py @@ -51,9 +51,7 @@ class Meta: # pylint: disable=too-few-public-methods def __init__(self): """Initialize ExampleYAMLDataSource.""" super().__init__() - self.diffsync_flags = ( - self.diffsync_flags, - ) + self.diffsync_flags = (self.diffsync_flags,) @classmethod def data_mappings(cls): From e6cd8e0b5285dad7d5103e9ef32b85e3e0f5c3d3 Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Fri, 26 Jul 2024 12:04:25 +0200 Subject: [PATCH 08/28] Several issues fixed --- nautobot_ssot/integrations/aci/diffsync/adapters/aci.py | 6 +++--- nautobot_ssot/integrations/aci/diffsync/client.py | 2 -- nautobot_ssot/integrations/aci/diffsync/models/nautobot.py | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py b/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py index 01127b77..28ae17f5 100644 --- a/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py +++ b/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py @@ -212,7 +212,7 @@ def load_ipaddresses(self): vrf_tenant = f"{self.tenant_prefix}:{bd_value['vrf_tenant']}" else: vrf_tenant = None - if bd_value.get("tenant") == 'mgmt': + if bd_value.get("tenant") == "mgmt": _namespace = "Global" else: _namespace = vrf_tenant or tenant_name @@ -224,7 +224,7 @@ def load_ipaddresses(self): site=self.site, vrf=bd_value["vrf"], vrf_tenant=vrf_tenant, - tenant=vrf_tenant or tenant_name, # BUG fix + tenant=vrf_tenant or tenant_name, ) new_ipaddress = self.ip_address( address=subnet[0], @@ -261,7 +261,7 @@ def load_prefixes(self): vrf_tenant = f"{self.tenant_prefix}:{bd_value['vrf_tenant']}" else: vrf_tenant = None - if bd_value.get("tenant") == 'mgmt': + if bd_value.get("tenant") == "mgmt": _namespace = "Global" else: _namespace = vrf_tenant or tenant_name diff --git a/nautobot_ssot/integrations/aci/diffsync/client.py b/nautobot_ssot/integrations/aci/diffsync/client.py index a8c43c19..6cd80297 100644 --- a/nautobot_ssot/integrations/aci/diffsync/client.py +++ b/nautobot_ssot/integrations/aci/diffsync/client.py @@ -423,7 +423,6 @@ def get_nodes(self) -> dict: mgmt_addr = f"{node['topSystem']['attributes']['address']}/{ip_network(node['topSystem']['attributes']['tepPool'], strict=False).prefixlen}" else: mgmt_addr = "" - # BUG detected in this block: changing if mgmt_addr: subnet = ip_network(mgmt_addr, strict=False).with_prefixlen elif node["topSystem"]["attributes"]["tepPool"] != "0.0.0.0": # nosec: B104 @@ -483,7 +482,6 @@ def get_controllers(self) -> dict: mgmt_addr = f"{node['topSystem']['attributes']['address']}/{ip_network(node['topSystem']['attributes']['tepPool'], strict=False).prefixlen}" else: mgmt_addr = "" - # BUG detected in this block: changing if mgmt_addr: subnet = ip_network(mgmt_addr, strict=False).with_prefixlen elif node["topSystem"]["attributes"]["tepPool"] != "0.0.0.0": # nosec: B104 diff --git a/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py b/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py index f4d9be7f..8cb00b3b 100644 --- a/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py +++ b/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py @@ -521,7 +521,7 @@ def delete(self): _prefix = OrmPrefix.objects.get( prefix=self.prefix, tenant=tenant, - vrfs=OrmVrf.objects.get(name=self.vrf, tenant=vrf_tenant), # fixed bug here + vrfs=OrmVrf.objects.get(name=self.vrf, tenant=vrf_tenant), ) self.diffsync.objects_to_delete["prefix"].append(_prefix) # pylint: disable=protected-access return self From 322aafa304c2273bc87a4e387485539bd3e52914 Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Fri, 26 Jul 2024 12:09:46 +0200 Subject: [PATCH 09/28] Several issues fixed --- .../integrations/aci/diffsync/models/nautobot.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py b/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py index 8cb00b3b..2a75be79 100644 --- a/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py +++ b/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py @@ -51,11 +51,7 @@ def create(cls, diffsync, ids, attrs): def update(self, attrs): """Update Tenant object in Nautobot.""" _tenant = OrmTenant.objects.get(name=self.name) - # if attrs.get("description"): Fix, this does not cover empty descr update - # _tenant.description = attrs["description"] _tenant.description = attrs.get("description", "") - # if attrs.get("comments"): - # _tenant.comments = attrs["comments"] _tenant.comments = attrs.get("comments", "") _tenant.validated_save() return super().update(attrs) @@ -452,7 +448,7 @@ def create(cls, diffsync, ids, attrs): except OrmTenant.DoesNotExist: diffsync.job.logger.warning( f"Tenant {attrs['vrf_tenant']} not found for VRF while creating Prefix: {ids['prefix']}" - ) # modified to avoid Key Error + ) vrf_tenant = None return @@ -464,9 +460,7 @@ def create(cls, diffsync, ids, attrs): vrf = None else: vrf = None - - # diffsync.job.logger.info(msg=f"Processing Prefix {ids['prefix']} in Namespace: {attrs['namespace']}, Tenant: {attrs['vrf_tenant']}") - _prefix, created = OrmPrefix.objects.get_or_create( # fixing adding error correction + _prefix, created = OrmPrefix.objects.get_or_create( prefix=ids["prefix"], status=Status.objects.get(name=attrs["status"]), description=attrs["description"], From 82180ea8c7929b0db6ca23a8f6cfe35d2980cddf Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Fri, 26 Jul 2024 12:15:03 +0200 Subject: [PATCH 10/28] Several issues fixed --- changes/491.changed | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/491.changed diff --git a/changes/491.changed b/changes/491.changed new file mode 100644 index 00000000..d4947b52 --- /dev/null +++ b/changes/491.changed @@ -0,0 +1 @@ +Fixed tenant names and introduced tag for multisite. \ No newline at end of file From 41873013396ba5e7862918fdfbc20cffeb8e8f2b Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Fri, 26 Jul 2024 12:36:55 +0200 Subject: [PATCH 11/28] Several issues fixed --- .../integrations/aci/diffsync/adapters/aci.py | 5 ++++- nautobot_ssot/integrations/aci/diffsync/client.py | 11 +++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py b/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py index 28ae17f5..269ad931 100644 --- a/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py +++ b/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py @@ -83,7 +83,10 @@ def load_tenants(self): for _tenant in tenant_list: if not _tenant["name"] in PLUGIN_CFG.get("ignore_tenants"): tenant_name = f"{self.tenant_prefix}:{_tenant['name']}" - _msite_tag = True if ":mso" in _tenant.get("annotation").lower() else False + if ":mso" in _tenant.get("annotation").lower(): + _msite_tag = True + else: + _msite_tag = False new_tenant = self.tenant( name=tenant_name, description=_tenant["description"], diff --git a/nautobot_ssot/integrations/aci/diffsync/client.py b/nautobot_ssot/integrations/aci/diffsync/client.py index 6cd80297..2f7f7e46 100644 --- a/nautobot_ssot/integrations/aci/diffsync/client.py +++ b/nautobot_ssot/integrations/aci/diffsync/client.py @@ -1,13 +1,17 @@ """All interactions with ACI.""" # pylint: disable=too-many-lines, too-many-instance-attributes, too-many-arguments # pylint: disable=invalid-name +from copy import deepcopy +from datetime import datetime, timedelta +from ipaddress import ip_network + +import logging import re import sys -import logging + import requests import urllib3 -from datetime import datetime, timedelta -from ipaddress import ip_network + from .utils import ( tenant_from_dn, ap_from_dn, @@ -17,7 +21,6 @@ interface_from_dn, bd_from_dn, ) -from copy import deepcopy urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) From 523eb18170accd9b5839977a774a761e5b9f7082 Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Fri, 26 Jul 2024 12:40:25 +0200 Subject: [PATCH 12/28] Several issues fixed --- nautobot_ssot/integrations/aci/diffsync/adapters/aci.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py b/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py index 269ad931..81655205 100644 --- a/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py +++ b/nautobot_ssot/integrations/aci/diffsync/adapters/aci.py @@ -83,7 +83,7 @@ def load_tenants(self): for _tenant in tenant_list: if not _tenant["name"] in PLUGIN_CFG.get("ignore_tenants"): tenant_name = f"{self.tenant_prefix}:{_tenant['name']}" - if ":mso" in _tenant.get("annotation").lower(): + if ":mso" in _tenant.get("annotation").lower(): # pylint: disable=simplifiable-if-statement _msite_tag = True else: _msite_tag = False From 6a2d7d528002af1a3145b5ec5b8c9218deed3508 Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Mon, 29 Jul 2024 15:05:58 +0200 Subject: [PATCH 13/28] Fix tenant annotation --- nautobot_ssot/integrations/aci/diffsync/client.py | 2 +- nautobot_ssot/tests/aci/test_api.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/nautobot_ssot/integrations/aci/diffsync/client.py b/nautobot_ssot/integrations/aci/diffsync/client.py index 2f7f7e46..fe700f83 100644 --- a/nautobot_ssot/integrations/aci/diffsync/client.py +++ b/nautobot_ssot/integrations/aci/diffsync/client.py @@ -141,7 +141,7 @@ def get_tenants(self) -> list: { "name": data["fvTenant"]["attributes"]["name"], "description": data["fvTenant"]["attributes"]["descr"], - "annotation": data["fvTenant"]["attributes"]["annotation"], + "annotation": data["fvTenant"]["attributes"].get("annotation", ""), } for data in resp.json()["imdata"] ] diff --git a/nautobot_ssot/tests/aci/test_api.py b/nautobot_ssot/tests/aci/test_api.py index 858184ee..9899c04c 100644 --- a/nautobot_ssot/tests/aci/test_api.py +++ b/nautobot_ssot/tests/aci/test_api.py @@ -29,8 +29,8 @@ def test_get_tenants(self, mocked_login, mocked_handle_request): mock_fvTenant.status_code = 200 mock_fvTenant.json.return_value = { "imdata": [ - {"fvTenant": {"attributes": {"name": "test_tenant_1", "descr": "test_desc_1"}}}, - {"fvTenant": {"attributes": {"name": "test_tenant_2", "descr": "test_desc_2"}}}, + {"fvTenant": {"attributes": {"name": "test_tenant_1", "descr": "test_desc_1", "annotation": ""}}}, + {"fvTenant": {"attributes": {"name": "test_tenant_2", "descr": "test_desc_2", "annotation": ""}}}, ] } From 18dc7fe370707422f846c7c8d359b54ac850880b Mon Sep 17 00:00:00 2001 From: pvillar_netdev Date: Fri, 26 Jul 2024 14:16:48 +0200 Subject: [PATCH 14/28] Update nautobot.py --- .../integrations/aci/diffsync/models/nautobot.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py b/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py index 2a75be79..0eb02cd4 100644 --- a/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py +++ b/nautobot_ssot/integrations/aci/diffsync/models/nautobot.py @@ -364,12 +364,12 @@ def create(cls, diffsync, ids, attrs): _parent = OrmPrefix.objects.get(prefix=attrs["prefix"], namespace=_namespace) except Namespace.DoesNotExist: diffsync.job.logger.warning(f"{ids['namespace']} missing Namespace to assign IP address: {ids['address']}") - return + return None except OrmPrefix.DoesNotExist: diffsync.job.logger.warning( f"{attrs['prefix']} missing Parent Prefix to assign IP address: {ids['address']}" ) - return + return None try: _ipaddress = OrmIPAddress.objects.create( address=ids["address"], @@ -383,7 +383,7 @@ def create(cls, diffsync, ids, attrs): diffsync.job.logger.warning( f"Unable to create IP Address {ids['address']}. Duplicate Address or Parent Prefix: {attrs['prefix']} in Namespace: {ids['namespace']}" ) - return + return None if intf: mapping = IPAddressToInterface.objects.create(ip_address=_ipaddress, interface=intf) @@ -450,7 +450,7 @@ def create(cls, diffsync, ids, attrs): f"Tenant {attrs['vrf_tenant']} not found for VRF while creating Prefix: {ids['prefix']}" ) vrf_tenant = None - return + return None if ids["vrf"] and vrf_tenant: try: @@ -473,7 +473,7 @@ def create(cls, diffsync, ids, attrs): diffsync.job.logger.warning( f"Prefix: {_prefix.prefix} duplicate in Namespace: {_prefix.namespace.name}. Skipping .." ) - return + return None if vrf: _prefix.vrfs.add(vrf) _prefix.tags.add(Tag.objects.get(name=PLUGIN_CFG.get("tag"))) From 0bbc629ac0d92239b7374d794a9b18de1a645f02 Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Mon, 29 Jul 2024 15:12:48 +0200 Subject: [PATCH 15/28] Fix docstring --- nautobot_ssot/integrations/aci/diffsync/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nautobot_ssot/integrations/aci/diffsync/client.py b/nautobot_ssot/integrations/aci/diffsync/client.py index fe700f83..ccad8a61 100644 --- a/nautobot_ssot/integrations/aci/diffsync/client.py +++ b/nautobot_ssot/integrations/aci/diffsync/client.py @@ -462,7 +462,7 @@ def get_nodes(self) -> dict: return node_dict def get_controllers(self) -> dict: - """Return list of Leaf/Spine nodes in the ACI fabric.""" + """Return list of Controller nodes in the ACI fabric.""" resp = self._get('/api/class/fabricNode.json?query-target-filter=eq(fabricNode.role,"controller")') node_dict = {} for node in resp.json()["imdata"]: From b6abc0a824026a6dc0cc386378965c697af44b6e Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Mon, 29 Jul 2024 15:18:32 +0200 Subject: [PATCH 16/28] Fix get_bds behaviour --- nautobot_ssot/tests/aci/test_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nautobot_ssot/tests/aci/test_api.py b/nautobot_ssot/tests/aci/test_api.py index 9899c04c..e54a4548 100644 --- a/nautobot_ssot/tests/aci/test_api.py +++ b/nautobot_ssot/tests/aci/test_api.py @@ -728,7 +728,7 @@ def test_get_controllers(self, mocked_login, mocked_handle_request): "fabric_ip": "10.0.0.1", "site": "ACI", "pod_id": "1", - "subnet": "10.0.0.0/24", + "subnet": "10.1.1.0/24", "oob_ip": "10.1.1.1/24", "uptime": "05:22:43:18.000", }, From 8b57f0eaae44eac70be8d19fbd6db4707ea7e926 Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Mon, 29 Jul 2024 15:22:56 +0200 Subject: [PATCH 17/28] Fix get_bds behaviour --- nautobot_ssot/tests/aci/test_api.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/nautobot_ssot/tests/aci/test_api.py b/nautobot_ssot/tests/aci/test_api.py index e54a4548..e47b8605 100644 --- a/nautobot_ssot/tests/aci/test_api.py +++ b/nautobot_ssot/tests/aci/test_api.py @@ -525,9 +525,6 @@ def test_get_bds(self, mocked_login, mocked_handle_request): "tenant": "ntc-chatops", "vrf_tenant": "ntc-chatops", "description": "WEB", - "unicast_routing": "yes", - "mac": "00:22:BD:F8:19:FF", - "l2unicast": "proxy", "vrf": "vrf1", "subnets": [("10.1.1.1/24", "public")], }, @@ -535,9 +532,6 @@ def test_get_bds(self, mocked_login, mocked_handle_request): "tenant": "ntc-chatops", "vrf_tenant": "ntc-chatops", "description": "APP", - "unicast_routing": "yes", - "mac": "00:22:BD:F8:19:FF", - "l2unicast": "proxy", "vrf": "vrf2", "subnets": [("10.2.2.2/24", "public")], }, From b409b1f225219c2872b5fcbe8e310b34cf567752 Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Mon, 29 Jul 2024 15:33:34 +0200 Subject: [PATCH 18/28] Fix get_tenants --- nautobot_ssot/tests/aci/test_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nautobot_ssot/tests/aci/test_api.py b/nautobot_ssot/tests/aci/test_api.py index e47b8605..aa8a69ac 100644 --- a/nautobot_ssot/tests/aci/test_api.py +++ b/nautobot_ssot/tests/aci/test_api.py @@ -40,8 +40,8 @@ def test_get_tenants(self, mocked_login, mocked_handle_request): self.assertEqual( self.aci_obj.get_tenants(), [ - {"name": "test_tenant_1", "description": "test_desc_1"}, - {"name": "test_tenant_2", "description": "test_desc_2"}, + {"name": "test_tenant_1", "description": "test_desc_1", "annotation": ""}, + {"name": "test_tenant_2", "description": "test_desc_2", "annotation": ""}, ], ) From 434fb40b35f77e3b043c8b590163be67cb5cdd14 Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Mon, 29 Jul 2024 15:52:26 +0200 Subject: [PATCH 19/28] Fix get_bds --- nautobot_ssot/tests/aci/test_api.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nautobot_ssot/tests/aci/test_api.py b/nautobot_ssot/tests/aci/test_api.py index aa8a69ac..9d72da16 100644 --- a/nautobot_ssot/tests/aci/test_api.py +++ b/nautobot_ssot/tests/aci/test_api.py @@ -521,14 +521,16 @@ def test_get_bds(self, mocked_login, mocked_handle_request): ] expected_data = { - "Vlan100_Web": { + "ntc-chatops:Vlan100_Web": { + "name": "Vlan100_Web", "tenant": "ntc-chatops", "vrf_tenant": "ntc-chatops", "description": "WEB", "vrf": "vrf1", "subnets": [("10.1.1.1/24", "public")], }, - "Vlan101_App": { + "ntc-chatops:Vlan101_App": { + "name": "Vlan101_App", "tenant": "ntc-chatops", "vrf_tenant": "ntc-chatops", "description": "APP", From 08dfaec5576dd285473cba9e6e0a0a169e87d36e Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Mon, 29 Jul 2024 16:14:26 +0200 Subject: [PATCH 20/28] Fix get_bds --- nautobot_ssot/tests/aci/test_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nautobot_ssot/tests/aci/test_api.py b/nautobot_ssot/tests/aci/test_api.py index 9d72da16..710ff0d9 100644 --- a/nautobot_ssot/tests/aci/test_api.py +++ b/nautobot_ssot/tests/aci/test_api.py @@ -521,7 +521,7 @@ def test_get_bds(self, mocked_login, mocked_handle_request): ] expected_data = { - "ntc-chatops:Vlan100_Web": { + "Vlan100_Web:ntc-chatops": { "name": "Vlan100_Web", "tenant": "ntc-chatops", "vrf_tenant": "ntc-chatops", @@ -529,7 +529,7 @@ def test_get_bds(self, mocked_login, mocked_handle_request): "vrf": "vrf1", "subnets": [("10.1.1.1/24", "public")], }, - "ntc-chatops:Vlan101_App": { + "Vlan101_App:ntc-chatops": { "name": "Vlan101_App", "tenant": "ntc-chatops", "vrf_tenant": "ntc-chatops", From 2b7e470974b89ed095104326dc0d23bee8da96a9 Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Mon, 29 Jul 2024 18:59:38 +0200 Subject: [PATCH 21/28] Fix get_bds --- nautobot_ssot/tests/aci/test_api.py | 76 +++++++++++++++++++---------- 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/nautobot_ssot/tests/aci/test_api.py b/nautobot_ssot/tests/aci/test_api.py index 710ff0d9..a4983d37 100644 --- a/nautobot_ssot/tests/aci/test_api.py +++ b/nautobot_ssot/tests/aci/test_api.py @@ -470,7 +470,19 @@ def test_get_bds(self, mocked_login, mocked_handle_request): "mac": "00:22:BD:F8:19:FF", "unkMacUcastAct": "proxy", } - } + }, + "fvRsCtx": { + "attributes": { + "tnFvCtxName": "vrf1", + "tDn": "uni/tn-ntc-chatops/ctx-vrf1" + } + }, + "fvSubnet": { + "attributes": { + "ip": "10.1.1.1/24", + "scope": "public" + } + }, }, { "fvBD": { @@ -482,42 +494,54 @@ def test_get_bds(self, mocked_login, mocked_handle_request): "mac": "00:22:BD:F8:19:FF", "unkMacUcastAct": "proxy", } - } + }, + "fvRsCtx": { + "attributes": { + "tnFvCtxName": "vrf2", + "tDn": "uni/tn-ntc-chatops/ctx-vrf1", + } + }, + "fvSubnet": { + "attributes": { + "ip": "10.2.2.2/24", + "scope": "public" + } + }, }, ] } - mocked_fvRsCtx_1 = Mock() - mocked_fvRsCtx_1.status_code = 200 - mocked_fvRsCtx_1.json.return_value = { - "imdata": [{"fvRsCtx": {"attributes": {"tnFvCtxName": "vrf1", "tDn": "uni/tn-ntc-chatops/ctx-vrf1"}}}] - } + # mocked_fvRsCtx_1 = Mock() + # mocked_fvRsCtx_1.status_code = 200 + # mocked_fvRsCtx_1.json.return_value = { + # "imdata": [{"fvRsCtx": {"attributes": {"tnFvCtxName": "vrf1", "tDn": "uni/tn-ntc-chatops/ctx-vrf1"}}}] + # } - mocked_fvRsCtx_2 = Mock() - mocked_fvRsCtx_2.status_code = 200 - mocked_fvRsCtx_2.json.return_value = { - "imdata": [{"fvRsCtx": {"attributes": {"tnFvCtxName": "vrf2", "tDn": "uni/tn-ntc-chatops/ctx-vrf1"}}}] - } + # mocked_fvRsCtx_2 = Mock() + # mocked_fvRsCtx_2.status_code = 200 + # mocked_fvRsCtx_2.json.return_value = { + # "imdata": [{"fvRsCtx": {"attributes": {"tnFvCtxName": "vrf2", "tDn": "uni/tn-ntc-chatops/ctx-vrf1"}}}] + # } - mocked_fvSubnet_1 = Mock() - mocked_fvSubnet_1.status_code = 200 - mocked_fvSubnet_1.json.return_value = { - "imdata": [{"fvSubnet": {"attributes": {"ip": "10.1.1.1/24", "scope": "public"}}}] - } + # mocked_fvSubnet_1 = Mock() + # mocked_fvSubnet_1.status_code = 200 + # mocked_fvSubnet_1.json.return_value = { + # "imdata": [{"fvSubnet": {"attributes": {"ip": "10.1.1.1/24", "scope": "public"}}}] + # } - mocked_fvSubnet_2 = Mock() - mocked_fvSubnet_2.status_code = 200 - mocked_fvSubnet_2.json.return_value = { - "imdata": [{"fvSubnet": {"attributes": {"ip": "10.2.2.2/24", "scope": "public"}}}] - } + # mocked_fvSubnet_2 = Mock() + # mocked_fvSubnet_2.status_code = 200 + # mocked_fvSubnet_2.json.return_value = { + # "imdata": [{"fvSubnet": {"attributes": {"ip": "10.2.2.2/24", "scope": "public"}}}] + # } mocked_login.return_value = self.mock_login mocked_handle_request.side_effect = [ mocked_fvBD, - mocked_fvRsCtx_1, - mocked_fvSubnet_1, - mocked_fvRsCtx_2, - mocked_fvSubnet_2, + # mocked_fvRsCtx_1, + # mocked_fvSubnet_1, + # mocked_fvRsCtx_2, + # mocked_fvSubnet_2, ] expected_data = { From 3fd4a1ce1f842e492cf92c467f100133061eb1ae Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Tue, 30 Jul 2024 12:01:50 +0200 Subject: [PATCH 22/28] Fix get_bds unittests --- nautobot_ssot/tests/aci/test_api.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/nautobot_ssot/tests/aci/test_api.py b/nautobot_ssot/tests/aci/test_api.py index a4983d37..df0ac4df 100644 --- a/nautobot_ssot/tests/aci/test_api.py +++ b/nautobot_ssot/tests/aci/test_api.py @@ -471,14 +471,20 @@ def test_get_bds(self, mocked_login, mocked_handle_request): "unkMacUcastAct": "proxy", } }, + }, + { "fvRsCtx": { "attributes": { + "dn": "uni/tn-ntc-chatops/BD-Vlan100_Web/rsctx", "tnFvCtxName": "vrf1", "tDn": "uni/tn-ntc-chatops/ctx-vrf1" } }, + }, + { "fvSubnet": { "attributes": { + "dn": "uni/tn-Prod/BD-Vlan100_Web/subnet-[10.1.1.1/24]", "ip": "10.1.1.1/24", "scope": "public" } @@ -495,14 +501,20 @@ def test_get_bds(self, mocked_login, mocked_handle_request): "unkMacUcastAct": "proxy", } }, + }, + { "fvRsCtx": { "attributes": { + "dn": "uni/tn-Prod/BD-Vlan101_App/rsctx", "tnFvCtxName": "vrf2", "tDn": "uni/tn-ntc-chatops/ctx-vrf1", } }, + }, + { "fvSubnet": { "attributes": { + "dn": "uni/tn-ntc-chatops/BD-Vlan101_App/subnet-[10.2.2.2/24]", "ip": "10.2.2.2/24", "scope": "public" } From 5601e5dd5e2793ae8ba0be6f4b7119538d57a2a1 Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Tue, 30 Jul 2024 12:07:07 +0200 Subject: [PATCH 23/28] Fix get_bds unittests --- nautobot_ssot/tests/aci/test_api.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nautobot_ssot/tests/aci/test_api.py b/nautobot_ssot/tests/aci/test_api.py index df0ac4df..68350a51 100644 --- a/nautobot_ssot/tests/aci/test_api.py +++ b/nautobot_ssot/tests/aci/test_api.py @@ -477,7 +477,7 @@ def test_get_bds(self, mocked_login, mocked_handle_request): "attributes": { "dn": "uni/tn-ntc-chatops/BD-Vlan100_Web/rsctx", "tnFvCtxName": "vrf1", - "tDn": "uni/tn-ntc-chatops/ctx-vrf1" + "tDn": "uni/tn-ntc-chatops/ctx-vrf1", } }, }, @@ -486,7 +486,7 @@ def test_get_bds(self, mocked_login, mocked_handle_request): "attributes": { "dn": "uni/tn-Prod/BD-Vlan100_Web/subnet-[10.1.1.1/24]", "ip": "10.1.1.1/24", - "scope": "public" + "scope": "public", } }, }, @@ -516,7 +516,7 @@ def test_get_bds(self, mocked_login, mocked_handle_request): "attributes": { "dn": "uni/tn-ntc-chatops/BD-Vlan101_App/subnet-[10.2.2.2/24]", "ip": "10.2.2.2/24", - "scope": "public" + "scope": "public", } }, }, From f691df6353b733e98d590bc6b0db4d21aecfb14e Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Tue, 30 Jul 2024 12:14:51 +0200 Subject: [PATCH 24/28] Fix get_bds unittests --- nautobot_ssot/tests/aci/test_api.py | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/nautobot_ssot/tests/aci/test_api.py b/nautobot_ssot/tests/aci/test_api.py index 68350a51..5414d2b0 100644 --- a/nautobot_ssot/tests/aci/test_api.py +++ b/nautobot_ssot/tests/aci/test_api.py @@ -523,37 +523,9 @@ def test_get_bds(self, mocked_login, mocked_handle_request): ] } - # mocked_fvRsCtx_1 = Mock() - # mocked_fvRsCtx_1.status_code = 200 - # mocked_fvRsCtx_1.json.return_value = { - # "imdata": [{"fvRsCtx": {"attributes": {"tnFvCtxName": "vrf1", "tDn": "uni/tn-ntc-chatops/ctx-vrf1"}}}] - # } - - # mocked_fvRsCtx_2 = Mock() - # mocked_fvRsCtx_2.status_code = 200 - # mocked_fvRsCtx_2.json.return_value = { - # "imdata": [{"fvRsCtx": {"attributes": {"tnFvCtxName": "vrf2", "tDn": "uni/tn-ntc-chatops/ctx-vrf1"}}}] - # } - - # mocked_fvSubnet_1 = Mock() - # mocked_fvSubnet_1.status_code = 200 - # mocked_fvSubnet_1.json.return_value = { - # "imdata": [{"fvSubnet": {"attributes": {"ip": "10.1.1.1/24", "scope": "public"}}}] - # } - - # mocked_fvSubnet_2 = Mock() - # mocked_fvSubnet_2.status_code = 200 - # mocked_fvSubnet_2.json.return_value = { - # "imdata": [{"fvSubnet": {"attributes": {"ip": "10.2.2.2/24", "scope": "public"}}}] - # } - mocked_login.return_value = self.mock_login mocked_handle_request.side_effect = [ mocked_fvBD, - # mocked_fvRsCtx_1, - # mocked_fvSubnet_1, - # mocked_fvRsCtx_2, - # mocked_fvSubnet_2, ] expected_data = { From 1b91b8f584b99d2e7d809883a98d2bfe64e03d56 Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Tue, 30 Jul 2024 12:25:49 +0200 Subject: [PATCH 25/28] Fix get_bds unittests --- nautobot_ssot/tests/aci/test_api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nautobot_ssot/tests/aci/test_api.py b/nautobot_ssot/tests/aci/test_api.py index 5414d2b0..9d301c26 100644 --- a/nautobot_ssot/tests/aci/test_api.py +++ b/nautobot_ssot/tests/aci/test_api.py @@ -20,6 +20,8 @@ def setUp(self): verify=False, site="ACI", ) # nosec + self.maxDiff = None + @patch.object(AciApi, "_handle_request") @patch.object(AciApi, "_login") From d6598bbf99a03bb352a17d12d677441978dc2757 Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Tue, 30 Jul 2024 12:29:09 +0200 Subject: [PATCH 26/28] Fix get_bds unittests --- nautobot_ssot/tests/aci/test_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nautobot_ssot/tests/aci/test_api.py b/nautobot_ssot/tests/aci/test_api.py index 9d301c26..07c12195 100644 --- a/nautobot_ssot/tests/aci/test_api.py +++ b/nautobot_ssot/tests/aci/test_api.py @@ -486,7 +486,7 @@ def test_get_bds(self, mocked_login, mocked_handle_request): { "fvSubnet": { "attributes": { - "dn": "uni/tn-Prod/BD-Vlan100_Web/subnet-[10.1.1.1/24]", + "dn": "uni/tn-ntc-chatops/BD-Vlan100_Web/subnet-[10.1.1.1/24]", "ip": "10.1.1.1/24", "scope": "public", } @@ -507,7 +507,7 @@ def test_get_bds(self, mocked_login, mocked_handle_request): { "fvRsCtx": { "attributes": { - "dn": "uni/tn-Prod/BD-Vlan101_App/rsctx", + "dn": "uni/tn-ntc-chatops/BD-Vlan101_App/rsctx", "tnFvCtxName": "vrf2", "tDn": "uni/tn-ntc-chatops/ctx-vrf1", } From b2896caef7c5f554df3f3578c8a1e514bca3ef7d Mon Sep 17 00:00:00 2001 From: Marek Zbroch Date: Tue, 30 Jul 2024 12:35:23 +0200 Subject: [PATCH 27/28] Fix get_bds unittests --- nautobot_ssot/tests/aci/test_api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nautobot_ssot/tests/aci/test_api.py b/nautobot_ssot/tests/aci/test_api.py index 07c12195..8f3a7d93 100644 --- a/nautobot_ssot/tests/aci/test_api.py +++ b/nautobot_ssot/tests/aci/test_api.py @@ -22,7 +22,6 @@ def setUp(self): ) # nosec self.maxDiff = None - @patch.object(AciApi, "_handle_request") @patch.object(AciApi, "_login") def test_get_tenants(self, mocked_login, mocked_handle_request): From cff516a6c0cfb0e5d63d59c383b053602f31a7fd Mon Sep 17 00:00:00 2001 From: Justin Drew <2396364+jdrew82@users.noreply.github.com> Date: Wed, 21 Aug 2024 12:58:50 -0500 Subject: [PATCH 28/28] =?UTF-8?q?fix:=20=F0=9F=90=9B=20Making=20suggested?= =?UTF-8?q?=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../integrations/aci/diffsync/client.py | 30 +++++-------------- nautobot_ssot/integrations/aci/jobs.py | 10 ++----- nautobot_ssot/tests/aci/test_api.py | 4 +-- 3 files changed, 13 insertions(+), 31 deletions(-) diff --git a/nautobot_ssot/integrations/aci/diffsync/client.py b/nautobot_ssot/integrations/aci/diffsync/client.py index ccad8a61..55e7f408 100644 --- a/nautobot_ssot/integrations/aci/diffsync/client.py +++ b/nautobot_ssot/integrations/aci/diffsync/client.py @@ -1,28 +1,26 @@ """All interactions with ACI.""" # pylint: disable=too-many-lines, too-many-instance-attributes, too-many-arguments # pylint: disable=invalid-name -from copy import deepcopy -from datetime import datetime, timedelta -from ipaddress import ip_network - import logging import re import sys +from copy import deepcopy +from datetime import datetime, timedelta +from ipaddress import ip_network import requests import urllib3 from .utils import ( - tenant_from_dn, ap_from_dn, - node_from_dn, - pod_from_dn, + bd_from_dn, fex_id_from_dn, interface_from_dn, - bd_from_dn, + node_from_dn, + pod_from_dn, + tenant_from_dn, ) - urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) logger = logging.getLogger(__name__) @@ -339,7 +337,7 @@ def get_vrfs(self, tenant: str) -> list: def get_bds(self, tenant: str = "all") -> dict: """Return Bridge Domains and Subnets from the Cisco APIC.""" - # TODO: rewrite using one API call -> https://10.101.40.2/api/node/class/fvBD.json?query-target=subtree&target-subtree-class=fvBD,fvRsCtx,fvSubnet + # TODO: rewrite using one API call -> https:///api/node/class/fvBD.json?query-target=subtree&target-subtree-class=fvBD,fvRsCtx,fvSubnet if tenant == "all": resp = self._get( "/api/node/class/fvBD.json?query-target=subtree&target-subtree-class=fvBD,fvRsCtx,fvSubnet" @@ -432,12 +430,6 @@ def get_nodes(self) -> dict: subnet = node["topSystem"]["attributes"]["tepPool"] else: subnet = "" - # if node["topSystem"]["attributes"]["tepPool"] != "0.0.0.0": # nosec: B104 - # subnet = node["topSystem"]["attributes"]["tepPool"] - # elif mgmt_addr: - # subnet = ip_network(mgmt_addr, strict=False).with_prefixlen - # else: - # subnet = "" node_id = node["topSystem"]["attributes"]["id"] node_dict[node_id]["oob_ip"] = mgmt_addr node_dict[node_id]["subnet"] = subnet @@ -491,12 +483,6 @@ def get_controllers(self) -> dict: subnet = node["topSystem"]["attributes"]["tepPool"] else: subnet = "" - # if node["topSystem"]["attributes"]["tepPool"] != "0.0.0.0": # nosec: B104 - # subnet = node["topSystem"]["attributes"]["tepPool"] - # elif mgmt_addr: - # subnet = ip_network(mgmt_addr, strict=False).with_prefixlen - # else: - # subnet = "" node_id = node["topSystem"]["attributes"]["id"] node_dict[node_id]["pod_id"] = node["topSystem"]["attributes"]["podId"] node_dict[node_id]["oob_ip"] = mgmt_addr diff --git a/nautobot_ssot/integrations/aci/jobs.py b/nautobot_ssot/integrations/aci/jobs.py index a3f89f1f..e31a3f37 100644 --- a/nautobot_ssot/integrations/aci/jobs.py +++ b/nautobot_ssot/integrations/aci/jobs.py @@ -4,10 +4,11 @@ from django.urls import reverse from nautobot.core.settings_funcs import is_truthy from nautobot.extras.jobs import BooleanVar, ChoiceVar, Job -from nautobot_ssot.jobs.base import DataMapping, DataSource + +from nautobot_ssot.integrations.aci.constant import PLUGIN_CFG from nautobot_ssot.integrations.aci.diffsync.adapters.aci import AciAdapter from nautobot_ssot.integrations.aci.diffsync.adapters.nautobot import NautobotAdapter -from nautobot_ssot.integrations.aci.constant import PLUGIN_CFG +from nautobot_ssot.jobs.base import DataMapping, DataSource name = "Cisco ACI SSoT" # pylint: disable=invalid-name, abstract-method @@ -48,11 +49,6 @@ class Meta: # pylint: disable=too-few-public-methods data_source_icon = static("nautobot_ssot_aci/aci.png") description = "Sync information from ACI to Nautobot" - def __init__(self): - """Initialize ExampleYAMLDataSource.""" - super().__init__() - self.diffsync_flags = (self.diffsync_flags,) - @classmethod def data_mappings(cls): """Shows mapping of models between ACI and Nautobot.""" diff --git a/nautobot_ssot/tests/aci/test_api.py b/nautobot_ssot/tests/aci/test_api.py index 8f3a7d93..04adb048 100644 --- a/nautobot_ssot/tests/aci/test_api.py +++ b/nautobot_ssot/tests/aci/test_api.py @@ -2,7 +2,8 @@ # pylint: disable=import-outside-toplevel, invalid-name import unittest -from unittest.mock import patch, Mock +from unittest.mock import Mock, patch + from nautobot_ssot.integrations.aci.diffsync.client import AciApi, RequestHTTPError @@ -20,7 +21,6 @@ def setUp(self): verify=False, site="ACI", ) # nosec - self.maxDiff = None @patch.object(AciApi, "_handle_request") @patch.object(AciApi, "_login")