nautobot / nautobot-app-ssot

Single Source of Truth for Nautobot
https://docs.nautobot.com/projects/ssot/en/latest/
Other
34 stars 28 forks source link

VLANs not properly associated with Prefixes #425

Open steven-douilliet opened 2 months ago

steven-douilliet commented 2 months ago

Environment

Expected Behavior

I expected Prefixes synchronization from Infoblox to Nautobot, including the associated VLANs.

During synchronization, Prefixes should be automatically updated or created and linked to VLANs using the VLAN field available in the Location/VLAN Assignment section (which contains fields for Locations, VLAN group, and VLAN).

Observed Behavior

The VLAN field from the Location/VLAN Assignment section is not set.

The SSoT plugin creates a Relationship Prefix -> VLAN and uses this field to associate the Prefix to VLAN, while the Prefix Model has a VLAN field which is a foreign key to the VLAN model.

So, the SSoT will update the relationship field instead of updating the VLAN field.

Workaround

Update VLAN field instead the relation ship:

class NautobotNetwork(Network):
    """Nautobot implementation of the Network Model."""

    @classmethod
    def create(cls, diffsync, ids, attrs):
        """Create Prefix object in Nautobot."""
        _prefix = OrmPrefix(
            prefix=ids["network"],
            status_id=diffsync.status_map["Active"],
            type=attrs["network_type"],
            description=attrs.get("description", ""),
        )
        prefix_ranges = attrs.get("ranges")
        if prefix_ranges:
            _prefix.cf["dhcp_ranges"] = ",".join(prefix_ranges)
        if attrs.get("vlans"):
            for _, _vlan in attrs["vlans"].items():
                index = 0
                try:
                    found_vlan = diffsync.vlan_map[_vlan["group"]][_vlan["vid"]]
                    if found_vlan:
                        if index == 0:
                            _prefix.vlan_id = found_vlan
                    index += 1
                except KeyError as err:
                    diffsync.job.logger.warning(
                        f"Unable to find VLAN {_vlan['vid']} {_vlan['name']} in {_vlan['group']}. {err}"
                    )

        if attrs.get("ext_attrs"):
            process_ext_attrs(diffsync=diffsync, obj=_prefix, extattrs=attrs["ext_attrs"])
        _prefix.validated_save()
        diffsync.prefix_map[ids["network"]] = _prefix.id
        return super().create(ids=ids, diffsync=diffsync, attrs=attrs)

    def update(self, attrs):  # pylint: disable=too-many-branches
        """Update Prefix object in Nautobot."""
        _pf = OrmPrefix.objects.get(id=self.pk)
        if self.diffsync.job.debug:
            self.diffsync.job.logger.debug(f"Attempting to update Prefix {_pf.prefix} with {attrs}.")
        if "description" in attrs:
            _pf.description = attrs["description"]
        if "network_type" in attrs:
            _pf.type = attrs["network_type"]
        if "ext_attrs" in attrs:
            process_ext_attrs(diffsync=self.diffsync, obj=_pf, extattrs=attrs["ext_attrs"])
        prefix_ranges = attrs.get("ranges")
        if prefix_ranges:
            _pf.cf["dhcp_ranges"] = ",".join(prefix_ranges)
        if "vlans" in attrs:
            _vlans = attrs["vlans"]
            if len(_vlans) == 1:
                for _, item in attrs["vlans"].items():
                    try:
                        vlan = OrmVlan.objects.get(vid=item["vid"], name=item["name"], vlan_group__name=item["group"])
                        _pf.vlan = vlan
                        self.diffsync.job.logger.debug(f"{_pf.prefix} updated with {vlan}")

                    except OrmVlan.DoesNotExist:
                        if self.diffsync.job.debug:
                            self.diffsync.job.logger.debug(
                                f"Unable to find VLAN {item['vid']} {item['name']} in {item['group']} to assign to prefix {_pf.prefix}."
                            )
                        continue
            _pf.validated_save()
        return super().update(attrs)
jdrew82 commented 1 month ago

So this isn't a bug and this particular request is already being done here. The problem is it only does this if there's a single VLAN assigned as Nautobot only supports one VLAN to be assigned to a Prefix. So what it does in create() is that it assigns the first VLAN in the list to that attribute on the Prefix and the rest have a RelationshipAssociation created. Now in the update() method I do see that we're only updating the RelationshipAssociations, so we could potentially try and update the direct VLAN assignment, but I think we'd potentially have difficulty knowing which one we should be assigning there.

steven-douilliet commented 1 month ago

Hello,

I believe I initially described the problem incorrectly.

During the Infoblox to Nautobot sync, when a prefix is created, the VLAN is associated with the prefix via the relationship field. However, if we update the VLAN in Infoblox (for example, by modifying the VID), this change is not reflected in Nautobot (the VLAN associated with the prefix remains unchanged).

The code provided in my first post (in the Workaround section) fixes this issue and updates the VLAN field directly instead of the relationship field.


steven-douilliet commented 1 month ago

This bug seems fixed after updating to version 2.2.4. I tested adding and modifying the VLAN associated with a prefix from Infoblox, and the synchronization works correctly.

However, when I remove the VLAN from the prefix in Infoblox, Nautobot does not remove the VLAN from the prefix on its side.

jdrew82 commented 2 weeks ago

Due to wanting to support earlier versions than 2.2.4 we can't update the code to do a M2M assignment of VLANs and Prefixes. When we update to SSoT 3.x and have a more recent version minimum we can look into making this change.