Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Refactor mech driver #500

Merged
merged 8 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions components/neutron/aio-values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,20 +74,32 @@ pod:
- mountPath: /etc/nb-token/
name: nb-token
readOnly: true
- mountPath: /etc/undersync/
name: undersync-token
readOnly: true
volumes:
- name: nb-token
secret:
secretName: nautobot-token
- name: undersync-token
secret:
secretName: undersync-token
neutron_rpc_server:
neutron_rpc_server:
volumeMounts:
- mountPath: /etc/nb-token/
name: nb-token
readOnly: true
- mountPath: /etc/undersync/
name: undersync-token
readOnly: true
volumes:
- name: nb-token
secret:
secretName: nautobot-token
- name: undersync-token
secret:
secretName: undersync-token
# (nicholas.kuechler) updating the jobs list to remove the 'neutron-rabbit-init' job.
dependencies:
dynamic:
Expand Down
Empty file.
95 changes: 0 additions & 95 deletions python/neutron-understack/neutron_understack/argo/workflows.py

This file was deleted.

38 changes: 15 additions & 23 deletions python/neutron-understack/neutron_understack/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,8 @@
cfg.StrOpt(
"provisioning_network",
help="provisioning_network ID as configured in ironic.conf",
default="change_me",
),
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"),
mfencik marked this conversation as resolved.
Show resolved Hide resolved
]

mech_understack_opts = [
Expand All @@ -43,6 +21,20 @@
"ucvni_group",
help="hack",
),
cfg.StrOpt(
"undersync_url",
help="Undersync URL",
),
cfg.StrOpt(
"undersync_token",
help=(
"Undersync API token. If not provided, "
"the '/etc/undersync/token' will be read instead."
),
),
cfg.BoolOpt(
"undersync_dry_run", default=True, help="Call Undersync with dry-run mode"
),
]


Expand Down
114 changes: 77 additions & 37 deletions python/neutron-understack/neutron_understack/nautobot.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,65 @@
import inspect
import pathlib
from urllib.parse import urljoin

import requests
from oslo_log import log
from requests.models import HTTPError

LOG = log.getLogger(__name__)


class NautobotError(Exception):
pass


class Nautobot:
CALLER_FRAME = 1

def __init__(self, nb_url: str | None = None, nb_token: str | None = None):
"""Basic Nautobot wrapper because pynautobot doesn't expose plugin APIs."""
self.base_url = nb_url or "http://nautobot-default.nautobot.svc.cluster.local"
self.token = nb_token or self.fetch_nb_token()
self.token = nb_token or self._fetch_nb_token()
self.s = requests.Session()
self.s.headers.update({"Authorization": f"Token {self.token}"})

def fetch_nb_token(self):
def _fetch_nb_token(self):
file = pathlib.Path("/etc/nb-token/token")
with file.open() as f:
return f.read().strip()

def _log_and_raise_for_status(self, response):
try:
response.raise_for_status()
except HTTPError as error:
LOG.error("Nautobot error: %(error)s", {"error": error})
raise NautobotError() from error

def make_api_request(
self, url: str, method: str, payload: dict | None = None
) -> dict:
endpoint_url = urljoin(self.base_url, url)
caller_function = inspect.stack()[self.CALLER_FRAME].function
http_method = method.upper()

LOG.debug(
"%(caller_function)s payload: %(payload)s",
{"payload": payload, "caller_function": caller_function},
)
resp = self.s.request(http_method, endpoint_url, timeout=10, json=payload)

if resp.content:
resp_data = resp.json()
else:
resp_data = {"status_code": resp.status_code}

LOG.debug(
"%(caller_function)s resp: %(resp)s",
{"resp": resp_data, "caller_function": caller_function},
)
self._log_and_raise_for_status(resp)
return resp_data

def ucvni_create(
self,
network_id: str,
Expand All @@ -38,52 +78,52 @@ def ucvni_create(
payload["ucvni_id"] = segment_id
payload["ucvni_type"] = "INFRA"

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()
url = "/api/plugins/undercloud-vni/ucvnis/"
resp_data = self.make_api_request(url, "post", payload)
return resp_data

def ucvni_delete(self, network_id):
payload = {"id": network_id}
url = f"/api/plugins/undercloud-vni/ucvnis/{network_id}/"
return self.make_api_request(url, "delete")

ucvni_url = f"/api/plugins/undercloud-vni/ucvnis/{network_id}/"
url = urljoin(self.base_url, ucvni_url)
LOG.debug("ucvni_delete payload: %(payload)s", {"payload": payload})
resp = self.s.delete(url, json=payload, timeout=10)
LOG.debug("ucvni_delete resp: %(resp)s", {"resp": resp.status_code})
resp.raise_for_status()
def prep_switch_interface(
self, connected_interface_id: str, ucvni_uuid: str
) -> str:
"""Runs a Nautobot Job to update a switch interface for tenant mode.

def detach_port(self, network_id, mac_address):
The nautobot job will assign vlans as required and set the interface
into the correct mode for "normal" tenant operation.

The vlan group ID is returned.
"""
url = "/api/plugins/undercloud-vni/prep_switch_interface"
payload = {
"ucvni_uuid": network_id,
"server_interface_mac": mac_address,
"ucvni_id": str(ucvni_uuid),
"connected_interface_id": str(connected_interface_id),
}
resp_data = self.make_api_request(url, "post", payload)

url = urljoin(self.base_url, "/api/plugins/undercloud-vni/detach_port")
LOG.debug("detach_port payload: %(payload)s", {"payload": payload})
resp = self.s.post(url, json=payload, timeout=10)
resp_data = resp.json()
LOG.debug("detach_port resp: %(resp)s", {"resp": resp_data})
resp.raise_for_status()
return resp_data["vlan_group_id"]

def reset_port_status(self, mac_address):
intf_url = urljoin(
self.base_url, f"/api/dcim/interfaces/?mac_address={mac_address}"
)
intf_resp = self.s.get(intf_url, timeout=10)
intf_resp_data = intf_resp.json()

LOG.debug("reset_port interface resp: %(resp)s", {"resp": intf_resp_data})
intf_resp.raise_for_status()
def configure_port_status(self, interface_uuid: str, status: str) -> dict:
url = f"/api/dcim/interfaces/{interface_uuid}/"
payload = {"status": {"name": status}}
resp_data = self.make_api_request(url, "patch", payload)
return resp_data

conn_intf_url = intf_resp_data["results"][0]["connected_endpoint"]["url"]
def fetch_vlan_group_uuid(self, device_uuid: str) -> str:
url = f"/api/dcim/devices/{device_uuid}/?include=relationships"

payload = {"status": {"name": "Active"}}
resp = self.s.patch(conn_intf_url, json=payload, timeout=10)
resp_data = self.make_api_request(url, "get")
try:
vlan_group_uuid = resp_data["relationships"]["vlan_group_to_devices"][
"source"
]["objects"][0]["id"]
except (KeyError, IndexError, TypeError) as error:
LOG.error("vlan_group_uuid_error: %(error)s", {"error": error})
raise NautobotError() from error

LOG.debug(
"reset_port connected interface resp: %(resp)s", {"resp": resp.json()}
"vlan_group_uuid: %(vlan_group_uuid)s", {"vlan_group_uuid": vlan_group_uuid}
)
resp.raise_for_status()
return vlan_group_uuid
Loading