From 5babd4061eef434285ac50885f1c7e7a9ffc9088 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Thu, 26 Sep 2024 14:43:41 -0500 Subject: [PATCH 1/7] feat(neutron-understack): refactor config into separate file Broke the config out into its own file for reusability. --- .../neutron_understack/config.py | 35 ++++++++++++++++ .../neutron_understack_mech.py | 41 +------------------ 2 files changed, 37 insertions(+), 39 deletions(-) create mode 100644 python/neutron-understack/neutron_understack/config.py diff --git a/python/neutron-understack/neutron_understack/config.py b/python/neutron-understack/neutron_understack/config.py new file mode 100644 index 00000000..3a686dc5 --- /dev/null +++ b/python/neutron-understack/neutron_understack/config.py @@ -0,0 +1,35 @@ +from oslo_config import cfg + +type_understack_opts = [ + cfg.StrOpt( + "provisioning_network", + help="provisioning_network ID as configured in ironic.conf", + ), + cfg.StrOpt( + "argo_workflow_sa", + default="workflow", + help="ServiceAccount to submit Workflow as", + ), + cfg.StrOpt( + "argo_api_url", + default="https://argo-server.argo.svc.cluster.local:2746", + help="URL of the Argo Server API", + ), + cfg.StrOpt( + "argo_namespace", + default="argo-events", + help="Namespace to submit the Workflows to", + ), + cfg.IntOpt( + "argo_max_attempts", + default=15, + help="Number of tries to retrieve the Workflow run result. " + "Sleeps 5 seconds between attempts.", + ), + cfg.BoolOpt("argo_dry_run", default=True, help="Call Undersync with dry-run mode"), + cfg.BoolOpt("argo_force", default=False, help="Call Undersync with force mode"), +] + + +def register_ml2_type_understack_opts(config): + config.register_opts(type_understack_opts, "ml2_type_understack") diff --git a/python/neutron-understack/neutron_understack/neutron_understack_mech.py b/python/neutron-understack/neutron_understack/neutron_understack_mech.py index 6f22b926..a9b584e1 100644 --- a/python/neutron-understack/neutron_understack/neutron_understack_mech.py +++ b/python/neutron-understack/neutron_understack/neutron_understack_mech.py @@ -14,49 +14,12 @@ ) from oslo_config import cfg +from neutron_understack import config from neutron_understack.argo.workflows import ArgoClient LOG = logging.getLogger(__name__) - -def setup_conf(): - grp = cfg.OptGroup("ml2_type_understack") - opts = [ - cfg.StrOpt( - "provisioning_network", - help="provisioning_network ID as configured in ironic.conf", - ), - cfg.StrOpt( - "argo_workflow_sa", - default="workflow", - help="ServiceAccount to submit Workflow as", - ), - cfg.StrOpt( - "argo_api_url", - default="https://argo-server.argo.svc.cluster.local:2746", - help="URL of the Argo Server API", - ), - cfg.StrOpt( - "argo_namespace", - default="argo-events", - help="Namespace to submit the Workflows to", - ), - cfg.IntOpt( - "argo_max_attempts", - default=15, - help="Number of tries to retrieve the Workflow run result. " - "Sleeps 5 seconds between attempts.", - ), - cfg.BoolOpt( - "argo_dry_run", default=True, help="Call Undersync with dry-run mode" - ), - cfg.BoolOpt("argo_force", default=False, help="Call Undersync with force mode"), - ] - cfg.CONF.register_group(grp) - cfg.CONF.register_opts(opts, group=grp) - - -setup_conf() +config.register_ml2_type_understack_opts(cfg.CONF) def dump_context( From ddd91c913af3ad1cbfa944d8e8027c517744428c Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Thu, 26 Sep 2024 15:00:40 -0500 Subject: [PATCH 2/7] feat(neutron-understack): add our own vxlan type driver This is currently a stub but in the future it should be looking up the UCVNI Groups that the physical networks will be on for these devices and making sure everything is valid. --- .../type_understack_vxlan.py | 45 +++++++++++++++++++ python/neutron-understack/pyproject.toml | 3 ++ 2 files changed, 48 insertions(+) create mode 100644 python/neutron-understack/neutron_understack/type_understack_vxlan.py diff --git a/python/neutron-understack/neutron_understack/type_understack_vxlan.py b/python/neutron-understack/neutron_understack/type_understack_vxlan.py new file mode 100644 index 00000000..46ef4d2a --- /dev/null +++ b/python/neutron-understack/neutron_understack/type_understack_vxlan.py @@ -0,0 +1,45 @@ +from neutron_lib import constants as p_const +from neutron_lib.plugins.ml2 import api +from oslo_log import log + +LOG = log.getLogger(__name__) + + +class UnderstackVxlanTypeDriver(api.ML2TypeDriver): + def __init__(self): + """Understack based type driver.""" + super().__init__() + LOG.info("ML2 Understack VXLAN Type initialization complete") + + def get_type(self): + return p_const.TYPE_VXLAN + + def initialize(self): + pass + + def initialize_network_segment_range_support(self): + pass + + def update_network_segment_range_allocations(self): + pass + + def get_network_segment_ranges(self): + pass + + def is_partial_segment(self, segment): + return False + + def validate_provider_segment(self, segment): + pass + + def reserve_provider_segment(self, context, segment, filters=None): + return segment + + def allocate_tenant_segment(self, context, filters=None): + return {api.NETWORK_TYPE: p_const.TYPE_VXLAN} + + def release_segment(self, context, segment): + pass + + def get_mtu(self, physical_network=None): + pass diff --git a/python/neutron-understack/pyproject.toml b/python/neutron-understack/pyproject.toml index 4678f890..39b9188f 100644 --- a/python/neutron-understack/pyproject.toml +++ b/python/neutron-understack/pyproject.toml @@ -71,3 +71,6 @@ convention = "google" [tool.poetry.plugins."neutron.ml2.mechanism_drivers"] understack = "neutron_understack.neutron_understack_mech:UnderstackDriver" + +[tool.poetry.plugins."neutron.ml2.type_drivers"] +understack_vxlan = "neutron_understack.type_understack_vxlan:UnderstackVxlanTypeDriver" From fbc8378866b5757219ac385bff359195651f1691 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Thu, 26 Sep 2024 15:33:32 -0500 Subject: [PATCH 3/7] feat(neutron-understack): wire in basic nautobot API helper Basic Nautobot API helper to call creating of a network --- .../neutron_understack/config.py | 15 ++++++++++ .../neutron_understack/nautobot.py | 30 +++++++++++++++++++ .../neutron_understack_mech.py | 5 +++- 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 python/neutron-understack/neutron_understack/nautobot.py diff --git a/python/neutron-understack/neutron_understack/config.py b/python/neutron-understack/neutron_understack/config.py index 3a686dc5..97d24e82 100644 --- a/python/neutron-understack/neutron_understack/config.py +++ b/python/neutron-understack/neutron_understack/config.py @@ -30,6 +30,21 @@ cfg.BoolOpt("argo_force", default=False, help="Call Undersync with force mode"), ] +mech_understack_opts = [ + cfg.StrOpt( + "nb_url", + help="Nautobot URL", + ), + cfg.StrOpt( + "nb_token", + help="Nautobot API token", + ), +] + def register_ml2_type_understack_opts(config): config.register_opts(type_understack_opts, "ml2_type_understack") + + +def register_ml2_understack_opts(config): + config.register_opts(mech_understack_opts, "ml2_understack") diff --git a/python/neutron-understack/neutron_understack/nautobot.py b/python/neutron-understack/neutron_understack/nautobot.py new file mode 100644 index 00000000..d1988ac4 --- /dev/null +++ b/python/neutron-understack/neutron_understack/nautobot.py @@ -0,0 +1,30 @@ +from urllib.parse import urljoin + +import requests +from oslo_log import log + +LOG = log.getLogger(__name__) + + +class Nautobot: + def __init__(self, nb_url: str, nb_token: str): + """Basic Nautobot wrapper because pynautobot doesn't expose plugin APIs.""" + self.base_url = nb_url + self.s = requests.Session() + self.s.headers.update({"Authorization": f"Token {nb_token}"}) + + def ucvni_create( + self, network_id: str, segment_id: int, ucvni_group: str, network_name: str + ): + payload = { + "id": network_id, + "ucvni_id": segment_id, + "name": network_name, + "ucvni_group": {"id": ucvni_group}, + } + + url = urljoin(self.base_url, "/api/plugins/undercloud-vni/ucvnis/") + LOG.debug("ucvni_create payload: %(payload)s", {"payload": payload}) + resp = self.s.post(url, json=payload, timeout=10) + LOG.debug("ucvni_create resp: %(resp)s", {"resp": resp.json()}) + resp.raise_for_status() diff --git a/python/neutron-understack/neutron_understack/neutron_understack_mech.py b/python/neutron-understack/neutron_understack/neutron_understack_mech.py index a9b584e1..4c6e6bf0 100644 --- a/python/neutron-understack/neutron_understack/neutron_understack_mech.py +++ b/python/neutron-understack/neutron_understack/neutron_understack_mech.py @@ -16,10 +16,12 @@ from neutron_understack import config from neutron_understack.argo.workflows import ArgoClient +from neutron_understack.nautobot import Nautobot LOG = logging.getLogger(__name__) config.register_ml2_type_understack_opts(cfg.CONF) +config.register_ml2_understack_opts(cfg.CONF) def dump_context( @@ -106,7 +108,8 @@ class UnderstackDriver(MechanismDriver): resource_provider_uuid5_namespace = UUID("6eae3046-4072-11ef-9bcf-d6be6370a162") def initialize(self): - pass + conf = cfg.CONF.ml2_understack + self.nb = Nautobot(conf.nb_url, conf.nb_token) def create_network_precommit(self, context): log_call("create_network_precommit", context) From de82c7108a8dde0ca4c9ad33ab5aa5ec8cd35f57 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Thu, 26 Sep 2024 15:57:06 -0500 Subject: [PATCH 4/7] feat(neutron-understack): create VNIs on network create When a network is created in neutron, the corresponding VNI will be created inside of Nautobot. --- .../neutron_understack/config.py | 4 +++ .../neutron_understack_mech.py | 27 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/python/neutron-understack/neutron_understack/config.py b/python/neutron-understack/neutron_understack/config.py index 97d24e82..03085c38 100644 --- a/python/neutron-understack/neutron_understack/config.py +++ b/python/neutron-understack/neutron_understack/config.py @@ -39,6 +39,10 @@ "nb_token", help="Nautobot API token", ), + cfg.StrOpt( + "ucvni_group", + help="hack", + ), ] diff --git a/python/neutron-understack/neutron_understack/neutron_understack_mech.py b/python/neutron-understack/neutron_understack/neutron_understack_mech.py index 4c6e6bf0..4da0ff07 100644 --- a/python/neutron-understack/neutron_understack/neutron_understack_mech.py +++ b/python/neutron-understack/neutron_understack/neutron_understack_mech.py @@ -5,6 +5,7 @@ import neutron_lib.api.definitions.portbindings as portbindings from neutron_lib import constants as p_const +from neutron_lib import exceptions as exc from neutron_lib.plugins.ml2 import api from neutron_lib.plugins.ml2.api import ( MechanismDriver, @@ -117,6 +118,32 @@ def create_network_precommit(self, context): def create_network_postcommit(self, context): log_call("create_network_postcommit", context) + network = context.current + network_id = network["id"] + network_name = network["name"] + provider_type = network.get("provider:network_type") + segmentation_id = network.get("provider:segmentation_id") + physnet = network.get("provider:physical_network") + + if provider_type == p_const.TYPE_VXLAN and segmentation_id: + conf = cfg.CONF.ml2_understack + ucvni_group = conf.ucvni_group + try: + self.nb.ucvni_create( + network_id, segmentation_id, ucvni_group, network_name + ) + except Exception as e: + LOG.exception( + "unable to create network %(net_id)s", {"net_id": network_id} + ) + raise exc.NetworkNotFound(net_id=network_id) from e + + LOG.info( + "network %(net_id)s has been added on ucvni_group %(ucvni_group) " + "/ physnet %(physnet)", + {"net_id": network_id, "ucvni_group": ucvni_group, "physnet": physnet}, + ) + def update_network_precommit(self, context): log_call("update_network_precommit", context) From a9a987131f9b4c3b7cd5e87b60d6c2cfb69f11ec Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Thu, 26 Sep 2024 18:54:44 -0500 Subject: [PATCH 5/7] active --- python/neutron-understack/neutron_understack/nautobot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/neutron-understack/neutron_understack/nautobot.py b/python/neutron-understack/neutron_understack/nautobot.py index d1988ac4..84d9894d 100644 --- a/python/neutron-understack/neutron_understack/nautobot.py +++ b/python/neutron-understack/neutron_understack/nautobot.py @@ -21,6 +21,7 @@ def ucvni_create( "ucvni_id": segment_id, "name": network_name, "ucvni_group": {"id": ucvni_group}, + "status": "Active", } url = urljoin(self.base_url, "/api/plugins/undercloud-vni/ucvnis/") From 41c1a42737c2cea70cc5e35a1335002ff5a164ae Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Thu, 26 Sep 2024 19:13:34 -0500 Subject: [PATCH 6/7] the ucvni plugin crashes here cause it works with raw data --- python/neutron-understack/neutron_understack/nautobot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/neutron-understack/neutron_understack/nautobot.py b/python/neutron-understack/neutron_understack/nautobot.py index 84d9894d..a02798de 100644 --- a/python/neutron-understack/neutron_understack/nautobot.py +++ b/python/neutron-understack/neutron_understack/nautobot.py @@ -20,7 +20,7 @@ def ucvni_create( "id": network_id, "ucvni_id": segment_id, "name": network_name, - "ucvni_group": {"id": ucvni_group}, + "ucvni_group": ucvni_group, "status": "Active", } From 5b51d17a9bb27d5a4b09ba78f2bc944b45d57a16 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Thu, 26 Sep 2024 20:36:30 -0500 Subject: [PATCH 7/7] fix log message --- .../neutron_understack/neutron_understack_mech.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/neutron-understack/neutron_understack/neutron_understack_mech.py b/python/neutron-understack/neutron_understack/neutron_understack_mech.py index 4da0ff07..4e6753aa 100644 --- a/python/neutron-understack/neutron_understack/neutron_understack_mech.py +++ b/python/neutron-understack/neutron_understack/neutron_understack_mech.py @@ -139,8 +139,8 @@ def create_network_postcommit(self, context): raise exc.NetworkNotFound(net_id=network_id) from e LOG.info( - "network %(net_id)s has been added on ucvni_group %(ucvni_group) " - "/ physnet %(physnet)", + "network %(net_id)s has been added on ucvni_group %(ucvni_group), " + "physnet %(physnet)", {"net_id": network_id, "ucvni_group": ucvni_group, "physnet": physnet}, )