diff --git a/python/neutron-understack/neutron_understack/nautobot.py b/python/neutron-understack/neutron_understack/nautobot.py index ebb1833a0..aee73aa76 100644 --- a/python/neutron-understack/neutron_understack/nautobot.py +++ b/python/neutron-understack/neutron_understack/nautobot.py @@ -105,6 +105,23 @@ def prep_switch_interface( return resp_data["vlan_group_id"] + def detach_port(self, connected_interface_id: str, ucvni_uuid: str) -> str: + """Runs a Nautobot Job to cleanup a switch interface. + + The nautobot job will find a VLAN that is bound to the UCVNI, remove it + from the Interface and if the VLAN is unused it will delete it. + + The vlan group ID is returned. + """ + url = "/api/plugins/undercloud-vni/detach_port" + payload = { + "ucvni_uuid": str(ucvni_uuid), + "connected_interface_id": str(connected_interface_id), + } + resp_data = self.make_api_request(url, "post", payload) + + return resp_data["vlan_group_id"] + def configure_port_status(self, interface_uuid: str, status: str) -> dict: url = f"/api/dcim/interfaces/{interface_uuid}/" payload = {"status": {"name": status}} diff --git a/python/neutron-understack/neutron_understack/neutron_understack_mech.py b/python/neutron-understack/neutron_understack/neutron_understack_mech.py index a71bd3d84..ec294ba68 100644 --- a/python/neutron-understack/neutron_understack/neutron_understack_mech.py +++ b/python/neutron-understack/neutron_understack/neutron_understack_mech.py @@ -210,13 +210,15 @@ def update_port_precommit(self, context): def update_port_postcommit(self, context): log_call("update_port_postcommit", context) + self._delete_tenant_port_on_unbound(context) + vif_type = context.current["binding:vif_type"] if vif_type != portbindings.VIF_TYPE_OTHER: return network_id = context.current["network_id"] - connected_interface_uuid = self.fetch_connected_interface_uuid(context) + connected_interface_uuid = self.fetch_connected_interface_uuid(context.current) nb_vlan_group_id = self.update_nautobot(network_id, connected_interface_uuid) self.undersync.sync_devices( @@ -230,6 +232,23 @@ def delete_port_precommit(self, context): def delete_port_postcommit(self, context): log_call("delete_port_postcommit", context) + network_id = context.current["network_id"] + + if network_id == cfg.CONF.ml2_type_understack.provisioning_network: + connected_interface_uuid = self.fetch_connected_interface_uuid( + context.current + ) + port_status = "Active" + configure_port_status_data = self.nb.configure_port_status( + connected_interface_uuid, port_status + ) + switch_uuid = configure_port_status_data.get("device", {}).get("id") + nb_vlan_group_id = UUID(self.nb.fetch_vlan_group_uuid(switch_uuid)) + self.undersync.sync_devices( + vlan_group_uuids=str(nb_vlan_group_id), + dry_run=cfg.CONF.ml2_understack.undersync_dry_run, + ) + def bind_port(self, context): log_call("bind_port", context) for segment in context.network.network_segments: @@ -273,16 +292,14 @@ def check_segment(self, segment): def check_vlan_transparency(self, context): log_call("check_vlan_transparency", context) - def fetch_connected_interface_uuid(self, context: PortContext) -> str: + def fetch_connected_interface_uuid(self, context: dict) -> str: """Fetches the connected interface UUID from the port context. :param context: The context of the port. :return: The connected interface UUID. """ connected_interface_uuid = ( - context.current["binding:profile"] - .get("local_link_information")[0] - .get("port_id") + context["binding:profile"].get("local_link_information")[0].get("port_id") ) try: UUID(str(connected_interface_uuid)) @@ -319,3 +336,31 @@ def update_nautobot(self, network_id: str, connected_interface_uuid: str) -> UUI return UUID( self.nb.prep_switch_interface(connected_interface_uuid, network_id) ) + + def _delete_tenant_port_on_unbound(self, context): + """Tenant network port cleanup in the UnderCloud infrastructure. + + This is triggered in the update_port_postcommit call as in the + delete_port_postcommit call there is no binding profile information + anymore, hence there is no way for us to identify which baremetal port + needs cleanup. + + Only in the update_port_postcommit we have access to the original context, + from which we can access the binding information. + """ + if ( + context.current["binding:vnic_type"] == "baremetal" + and context.vif_type == portbindings.VIF_TYPE_UNBOUND + and context.original_vif_type == portbindings.VIF_TYPE_OTHER + ): + connected_interface_uuid = self.fetch_connected_interface_uuid( + context.original + ) + network_id = context.current["network_id"] + nb_vlan_group_id = UUID( + self.nb.detach_port(connected_interface_uuid, network_id) + ) + self.undersync.sync_devices( + vlan_group_uuids=str(nb_vlan_group_id), + dry_run=cfg.CONF.ml2_understack.undersync_dry_run, + ) diff --git a/python/neutron-understack/neutron_understack/tests/test_neutron_understack_mech.py b/python/neutron-understack/neutron_understack/tests/test_neutron_understack_mech.py index 965bc507d..6f632a870 100644 --- a/python/neutron-understack/neutron_understack/tests/test_neutron_understack_mech.py +++ b/python/neutron-understack/neutron_understack/tests/test_neutron_understack_mech.py @@ -34,7 +34,7 @@ def undersync_client() -> Undersync: def test_fetch_connected_interface_uuid(context): - result = driver.fetch_connected_interface_uuid(context) + result = driver.fetch_connected_interface_uuid(context.current) assert result == "03921f8d-b4de-412e-a733-f8eade4c6268"