From 316d2b47ea65c4537e11904b74eb63d8eb3fe015 Mon Sep 17 00:00:00 2001 From: haseeb <184114777+haseebsyed12@users.noreply.github.com> Date: Wed, 5 Mar 2025 15:47:43 +0530 Subject: [PATCH 1/2] PUC-788: assign tenant to UCVNI when the network is created --- .../neutron_understack/nautobot.py | 4 +++- .../neutron_understack_mech.py | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/python/neutron-understack/neutron_understack/nautobot.py b/python/neutron-understack/neutron_understack/nautobot.py index a85a934ae..cb4adec4c 100644 --- a/python/neutron-understack/neutron_understack/nautobot.py +++ b/python/neutron-understack/neutron_understack/nautobot.py @@ -125,12 +125,14 @@ def make_api_request( def ucvni_create( self, network_id: str, + project_id: str, ucvni_group: str, network_name: str, segment_id: int | None = None, - ): + ) -> dict: payload = { "id": network_id, + "tenant": project_id, "name": network_name, "ucvni_group": ucvni_group, "status": {"name": "Active"}, diff --git a/python/neutron-understack/neutron_understack/neutron_understack_mech.py b/python/neutron-understack/neutron_understack/neutron_understack_mech.py index 99b612029..1803e9c2b 100644 --- a/python/neutron-understack/neutron_understack/neutron_understack_mech.py +++ b/python/neutron-understack/neutron_understack/neutron_understack_mech.py @@ -65,6 +65,7 @@ def create_network_postcommit(self, context): network = context.current network_id = network["id"] network_name = network["name"] + project_id = network["project_id"] external = network["router:external"] provider_type = network.get("provider:network_type") segmentation_id = network.get("provider:segmentation_id") @@ -74,11 +75,22 @@ def create_network_postcommit(self, context): if provider_type not in [p_const.TYPE_VLAN, p_const.TYPE_VXLAN]: return ucvni_group = conf.ucvni_group - self.nb.ucvni_create(network_id, ucvni_group, network_name) + ucvni_response = self.nb.ucvni_create( + network_id=network_id, + project_id=project_id, + ucvni_group=ucvni_group, + network_name=network_name, + ) LOG.info( "network %(net_id)s has been added on ucvni_group %(ucvni_group)s, " "physnet %(physnet)s", - {"net_id": network_id, "ucvni_group": ucvni_group, "physnet": physnet}, + { + "net_id": network_id, + "nautobot_ucvni_uuid": ucvni_response.get("id"), + "nautobot_tenant_id": ucvni_response.get("tenant", {}).get("id"), + "ucvni_group": ucvni_group, + "physnet": physnet, + }, ) self._create_nautobot_namespace(network_id, external) From 84d0d40eacec3b5fac382ef6f2b23a927440d70c Mon Sep 17 00:00:00 2001 From: Milan Fencik Date: Thu, 20 Mar 2025 19:15:15 +0000 Subject: [PATCH 2/2] add tests --- .../neutron_understack/tests/conftest.py | 64 +++++++++++++++++-- .../neutron_understack/tests/helpers.py | 28 ++++++-- .../neutron_understack/tests/test_nautobot.py | 24 +++++++ .../tests/test_neutron_understack_mech.py | 25 ++++++++ 4 files changed, 133 insertions(+), 8 deletions(-) diff --git a/python/neutron-understack/neutron_understack/tests/conftest.py b/python/neutron-understack/neutron_understack/tests/conftest.py index 4ee7a3237..682ab0a99 100644 --- a/python/neutron-understack/neutron_understack/tests/conftest.py +++ b/python/neutron-understack/neutron_understack/tests/conftest.py @@ -24,11 +24,17 @@ from neutron_understack.nautobot import Nautobot from neutron_understack.neutron_understack_mech import UnderstackDriver from neutron_understack.tests.helpers import Ml2PluginNoInit +from neutron_understack.tests.helpers import extend_network_dict from neutron_understack.tests.helpers import extend_port_dict_with_trunk from neutron_understack.trunk import UnderStackTrunkDriver from neutron_understack.undersync import Undersync +@pytest.fixture +def ucvni_group_id() -> uuid.UUID: + return uuid.uuid4() + + @pytest.fixture def network_id() -> uuid.UUID: return uuid.uuid4() @@ -59,6 +65,16 @@ def network_segment_id() -> uuid.UUID: return uuid.uuid4() +@pytest.fixture +def project_id() -> str: + return uuid.uuid4().hex + + +@pytest.fixture +def network(project_id, network_id) -> Network: + return Network(id=str(network_id), project_id=project_id, external=None) + + @pytest.fixture def patch_extend_subnet(mocker) -> None: """Ml2 Plugin extend subnet patch. @@ -86,13 +102,16 @@ def subport(port_id, vlan_num) -> SubPort: @pytest.fixture -def network_dict(ml2_plugin) -> dict: - return ml2_plugin._make_network_dict(Network(), process_extensions=False) +def network_dict(ml2_plugin, network, network_segment) -> dict: + network_details = ml2_plugin._make_network_dict(network, process_extensions=False) + extend_network_dict(network_details, network) + ml2_plugin.extend_network_dict_with_segment(network_segment, network_details) + return network_details @pytest.fixture -def network_segment() -> NetworkSegment: - return NetworkSegment(network_type="vxlan") +def network_segment(network) -> NetworkSegment: + return NetworkSegment(network_type="vxlan", network=network) @pytest.fixture @@ -268,3 +287,40 @@ def trunk_payload_metadata(subport) -> dict: @pytest.fixture def trunk_payload(trunk_payload_metadata, trunk) -> DBEventPayload: return DBEventPayload("context", metadata=trunk_payload_metadata, states=[trunk]) + + +@pytest.fixture +def ml2_understack_conf(mocker, ucvni_group_id) -> None: + mocker.patch( + "neutron_understack.neutron_understack_mech.cfg.CONF.ml2_understack.ucvni_group", + str(ucvni_group_id), + ) + mocker.patch( + "neutron_understack.neutron_understack_mech.cfg.CONF.ml2_understack.network_node_switchport_uuid", + "a27f7260-a7c5-4f0c-ac70-6258b026d368", + ) + mocker.patch( + "neutron_understack.neutron_understack_mech.cfg.CONF.ml2_understack.undersync_dry_run", + False, + ) + + +@pytest.fixture +def ucvni_create_response(ucvni_group_id) -> list[dict]: + return [ + { + "id": "63a2da8b-9da5-493a-b5ac-2ae62f663e1a", + "name": "PROV-NET500", + "ucvni_id": 200054, + "ucvni_type": "TENANT", + "ucvni_group": { + "id": str(ucvni_group_id), + }, + "tenant": { + "id": "d3c2c85b-dbf2-4ff5-843f-323524b63768", + }, + "status": { + "id": "d4bcbafa-3033-433b-b21b-a20acf9d1324", + }, + } + ] diff --git a/python/neutron-understack/neutron_understack/tests/helpers.py b/python/neutron-understack/neutron_understack/tests/helpers.py index 43fd00a66..746d9215b 100644 --- a/python/neutron-understack/neutron_understack/tests/helpers.py +++ b/python/neutron-understack/neutron_understack/tests/helpers.py @@ -1,6 +1,10 @@ -from neutron.db.external_net_db import External_net_db_mixin +from neutron.db import external_net_db +from neutron.db import segments_db +from neutron.db.models.segment import NetworkSegment +from neutron.db.models_v2 import Network from neutron.db.models_v2 import Port from neutron.db.models_v2 import Subnet +from neutron.plugins.ml2.managers import TypeManager from neutron.plugins.ml2.plugin import Ml2Plugin from neutron.services.trunk.plugin import TrunkPlugin @@ -13,7 +17,7 @@ class Ml2PluginNoInit(Ml2Plugin): """ def __init__(self): - pass + self.type_manager = TypeManager() def construct_port_dict(self, port: Port) -> dict: port_dict = self._make_port_dict(port, process_extensions=False) @@ -22,14 +26,30 @@ def construct_port_dict(self, port: Port) -> dict: def construct_subnet_dict(self, subnet: Subnet) -> dict: base_subnet_dict = self._make_subnet_dict(subnet) - extended_subnet_dict = External_net_db_mixin._extend_subnet_dict_l3( - base_subnet_dict, subnet + extended_subnet_dict = ( + external_net_db.External_net_db_mixin._extend_subnet_dict_l3( + base_subnet_dict, subnet + ) ) return extended_subnet_dict + def extend_network_dict_with_segment( + self, segment: NetworkSegment, network_dict: dict + ) -> dict: + segment_dict = segments_db._make_segment_dict(segment) + self.type_manager.extend_network_with_provider_segments( + network_dict, [segment_dict] + ) + return network_dict + def extend_port_dict_with_trunk(port_dict: dict, port: Port) -> dict: port_dict["bulk"] = True TrunkPlugin._extend_port_trunk_details(port_dict, port) port_dict.pop("bulk") return port_dict + + +def extend_network_dict(network_dict: dict, network: Network = None): + external_net_db.External_net_db_mixin._extend_network_dict_l3(network_dict, network) + return network_dict diff --git a/python/neutron-understack/neutron_understack/tests/test_nautobot.py b/python/neutron-understack/neutron_understack/tests/test_nautobot.py index ea7295250..2f77a05e1 100644 --- a/python/neutron-understack/neutron_understack/tests/test_nautobot.py +++ b/python/neutron-understack/neutron_understack/tests/test_nautobot.py @@ -1,3 +1,4 @@ +import uuid from unittest.mock import MagicMock from unittest.mock import patch @@ -57,3 +58,26 @@ def test_create_vlan_and_associate_vlan_to_ucvni(nautobot, mock_pynautobot_api): nautobot.create_vlan_and_associate_vlan_to_ucvni(payload) mock_ipam.vlans.create.assert_called_once_with(expected_payload_dict) + + +def test_ucvni_create( + mocker, + network_id, + ucvni_create_response, + nautobot, +): + mocker.patch.object( + nautobot, "make_api_request", return_value=ucvni_create_response + ) + project_id = "d3c2c85bdbf24ff5843f323524b63768" + response = nautobot.ucvni_create( + network_id=network_id.hex, + project_id=project_id, + ucvni_group="f6843091-845d-4195-8132-960125e05f7b", + network_name="PROV-NET500", + ) + + assert "tenant" in response[0] + tenant_obj = response[0].get("tenant", {}) + + assert tenant_obj.get("id") == str(uuid.UUID(project_id)) 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 0f09b7740..c7f1af4fd 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 @@ -183,3 +183,28 @@ def test__delete_vlan(self, mocker, vlan_network_segment, understack_driver): mock_delete = mocker.patch.object(understack_driver.nb, "delete_vlan") understack_driver._delete_vlan(vlan_network_segment) mock_delete.assert_called_once_with(vlan_id=vlan_network_segment.get("id")) + + +class TestCreateNetworkPostCommit: + @pytest.mark.usefixtures("ml2_understack_conf") + def test_vxlan_network( + self, + mocker, + understack_driver, + network_context, + network_id, + ucvni_group_id, + project_id, + ): + mocker.patch.object(understack_driver, "_create_nautobot_namespace") + understack_driver.create_network_postcommit(network_context) + understack_driver.nb.ucvni_create.assert_called_once_with( + network_id=str(network_id), + project_id=str(project_id), + ucvni_group=str(ucvni_group_id), + network_name=network_context.current["name"], + ) + understack_driver._create_nautobot_namespace.assert_called_once_with( + str(network_id), + network_context.current["router:external"], + )