diff --git a/python/neutron-understack/neutron_understack/config.py b/python/neutron-understack/neutron_understack/config.py index f48264c9..75c68f68 100644 --- a/python/neutron-understack/neutron_understack/config.py +++ b/python/neutron-understack/neutron_understack/config.py @@ -35,6 +35,14 @@ cfg.BoolOpt( "undersync_dry_run", default=True, help="Call Undersync with dry-run mode" ), + cfg.StrOpt( + "shared_nautobot_namespace_name", + default="Global", + help=( + "Nautobot namespace name that will house all external prefixes, i.e " + "prefixes that need to be routable outside of a tenant environment." + ), + ), ] diff --git a/python/neutron-understack/neutron_understack/nautobot.py b/python/neutron-understack/neutron_understack/nautobot.py index 77641543..332a3f80 100644 --- a/python/neutron-understack/neutron_understack/nautobot.py +++ b/python/neutron-understack/neutron_understack/nautobot.py @@ -109,6 +109,20 @@ def namespace_delete(self, namespace_uuid: str) -> dict: url = f"/api/ipam/namespaces/{namespace_uuid}/" return self.make_api_request(url, "delete") + def subnet_create(self, subnet_uuid: str, prefix: str, namespace_name: str) -> dict: + url = "/api/ipam/prefixes/" + payload = { + "id": subnet_uuid, + "prefix": prefix, + "status": "Active", + "namespace": {"name": namespace_name}, + } + return self.make_api_request(url, "post", payload) + + def subnet_delete(self, subnet_uuid: str) -> dict: + url = f"/api/ipam/prefixes/{subnet_uuid}/" + return self.make_api_request(url, "delete") + def prep_switch_interface( self, connected_interface_id: str, ucvni_uuid: str ) -> str: diff --git a/python/neutron-understack/neutron_understack/neutron_understack_mech.py b/python/neutron-understack/neutron_understack/neutron_understack_mech.py index f034be59..1a595d87 100644 --- a/python/neutron-understack/neutron_understack/neutron_understack_mech.py +++ b/python/neutron-understack/neutron_understack/neutron_understack_mech.py @@ -122,6 +122,7 @@ def create_network_postcommit(self, context): network = context.current network_id = network["id"] network_name = network["name"] + external = network["router:external"] provider_type = network.get("provider:network_type") segmentation_id = network.get("provider:segmentation_id") physnet = network.get("provider:physical_network") @@ -135,11 +136,7 @@ def create_network_postcommit(self, context): "physnet %(physnet)s", {"net_id": network_id, "ucvni_group": ucvni_group, "physnet": physnet}, ) - self.nb.namespace_create(name=network_id) - LOG.info( - "namespace with name %(network_id)s has been created in Nautobot", - {"network_id": network_id}, - ) + self._create_nautobot_namespace(network_id, external) def update_network_precommit(self, context): log_call("update_network_precommit", context) @@ -155,6 +152,7 @@ def delete_network_postcommit(self, context): network = context.current network_id = network["id"] + external = network["router:external"] provider_type = network.get("provider:network_type") physnet = network.get("provider:physical_network") @@ -167,7 +165,8 @@ def delete_network_postcommit(self, context): "physnet %(physnet)s", {"net_id": network_id, "ucvni_group": ucvni_group, "physnet": physnet}, ) - self._fetch_and_delete_nautobot_namespace(network_id) + if not external: + self._fetch_and_delete_nautobot_namespace(network_id) def create_subnet_precommit(self, context): log_call("create_subnet_precommit", context) @@ -175,6 +174,21 @@ def create_subnet_precommit(self, context): def create_subnet_postcommit(self, context): log_call("create_subnet_postcommit", context) + subnet = context.current + subnet_uuid = subnet["id"] + network_uuid = subnet["network_id"] + prefix = subnet["cidr"] + external = subnet["router:external"] + shared_namespace = cfg.CONF.ml2_understack.shared_nautobot_namespace_name + nautobot_namespace_name = network_uuid if not external else shared_namespace + + self.nb.subnet_create(subnet_uuid, prefix, nautobot_namespace_name) + LOG.info( + "subnet with ID: %(uuid)s and prefix %(prefix)s has been " + "created in Nautobot", + {"prefix": prefix, "uuid": subnet_uuid}, + ) + def update_subnet_precommit(self, context): log_call("update_subnet_precommit", context) @@ -187,6 +201,17 @@ def delete_subnet_precommit(self, context): def delete_subnet_postcommit(self, context): log_call("delete_subnet_postcommit", context) + subnet = context.current + subnet_uuid = subnet["id"] + prefix = subnet["cidr"] + + self.nb.subnet_delete(subnet_uuid) + LOG.info( + "subnet with ID: %(uuid)s and prefix %(prefix)s has been " + "deleted in Nautobot", + {"prefix": prefix, "uuid": subnet_uuid}, + ) + def create_port_precommit(self, context): log_call("create_port_precommit", context) @@ -366,3 +391,24 @@ def _fetch_and_delete_nautobot_namespace(self, name: str) -> None: "from nautobot", {"name": name, "ns_uuid": namespace_uuid}, ) + + def _create_nautobot_namespace( + self, network_id: str, network_is_external: bool + ) -> None: + if not network_is_external: + self.nb.namespace_create(name=network_id) + LOG.info( + "namespace with name %(network_id)s has been created in Nautobot", + {"network_id": network_id}, + ) + else: + shared_namespace = cfg.CONF.ml2_understack.shared_nautobot_namespace_name + LOG.info( + "Network %(network_id)s is external, nautobot namespace " + "%(shared_nautobot_namespace)s will be used to house all " + "prefixes in this network", + { + "network_id": network_id, + "shared_nautobot_namespace": shared_namespace, + }, + )