From 513f45419dfaf93898d669863cf8e54ab68347eb Mon Sep 17 00:00:00 2001 From: Karel van Klink <karel.vanklink@geant.org> Date: Tue, 9 Jan 2024 15:15:53 +0100 Subject: [PATCH] add router validation task --- ...6c986f219e3f_add_router_validation_task.py | 39 ++++++++++++++++++ ...-21_6dbd6c4c04b4_add_ip_trunk_workflows.py | 2 +- ...11-21_815033570ad7_add_router_workflows.py | 2 +- ...64c6f392c_add_twamp_deployment_workflow.py | 2 +- gso/services/netbox_client.py | 4 +- gso/translations/en-GB.json | 3 +- gso/workflows/__init__.py | 1 + gso/workflows/router/create_router.py | 13 +++--- gso/workflows/tasks/validate_router.py | 41 +++++++++++++++++++ 9 files changed, 95 insertions(+), 12 deletions(-) create mode 100644 gso/migrations/versions/2023-09-22_6c986f219e3f_add_router_validation_task.py create mode 100644 gso/workflows/tasks/validate_router.py diff --git a/gso/migrations/versions/2023-09-22_6c986f219e3f_add_router_validation_task.py b/gso/migrations/versions/2023-09-22_6c986f219e3f_add_router_validation_task.py new file mode 100644 index 00000000..3d176c13 --- /dev/null +++ b/gso/migrations/versions/2023-09-22_6c986f219e3f_add_router_validation_task.py @@ -0,0 +1,39 @@ +"""Add router validation task. + +Revision ID: 6c986f219e3f +Revises: f0764c6f392c +Create Date: 2023-09-22 17:13:01.223133 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = '6c986f219e3f' +down_revision = 'f0764c6f392c' +branch_labels = None +depends_on = None + + +from orchestrator.migrations.helpers import create_workflow, delete_workflow + +new_workflows = [ + { + "name": "validate_router", + "target": "SYSTEM", + "description": "Validate router configuration", + "product_type": "Router" + } +] + + +def upgrade() -> None: + conn = op.get_bind() + for workflow in new_workflows: + create_workflow(conn, workflow) + + +def downgrade() -> None: + conn = op.get_bind() + for workflow in new_workflows: + delete_workflow(conn, workflow["name"]) diff --git a/gso/migrations/versions/2023-11-21_6dbd6c4c04b4_add_ip_trunk_workflows.py b/gso/migrations/versions/2023-11-21_6dbd6c4c04b4_add_ip_trunk_workflows.py index b2ae1da2..e8e81feb 100644 --- a/gso/migrations/versions/2023-11-21_6dbd6c4c04b4_add_ip_trunk_workflows.py +++ b/gso/migrations/versions/2023-11-21_6dbd6c4c04b4_add_ip_trunk_workflows.py @@ -1,7 +1,7 @@ """Add IP Trunk workflows. Revision ID: 6dbd6c4c04b4 -Revises: 815033570ad7 +Revises: 0c31b60487c8 Create Date: 2023-11-21 12:55:29.689402 """ diff --git a/gso/migrations/versions/2023-11-21_815033570ad7_add_router_workflows.py b/gso/migrations/versions/2023-11-21_815033570ad7_add_router_workflows.py index 1d996616..ed03212a 100644 --- a/gso/migrations/versions/2023-11-21_815033570ad7_add_router_workflows.py +++ b/gso/migrations/versions/2023-11-21_815033570ad7_add_router_workflows.py @@ -1,7 +1,7 @@ """Add router workflows. Revision ID: 815033570ad7 -Revises: 0c31b60487c8 +Revises: 0c904cf0b66b Create Date: 2023-11-21 12:53:26.413333 """ diff --git a/gso/migrations/versions/2023-12-27_f0764c6f392c_add_twamp_deployment_workflow.py b/gso/migrations/versions/2023-12-27_f0764c6f392c_add_twamp_deployment_workflow.py index 9e978249..c0f3c473 100644 --- a/gso/migrations/versions/2023-12-27_f0764c6f392c_add_twamp_deployment_workflow.py +++ b/gso/migrations/versions/2023-12-27_f0764c6f392c_add_twamp_deployment_workflow.py @@ -1,7 +1,7 @@ """Add TWAMP deployment workflow. Revision ID: f0764c6f392c -Revises: 815033570ad7 +Revises: b689d4636694 Create Date: 2023-12-27 14:31:42.285180 """ diff --git a/gso/services/netbox_client.py b/gso/services/netbox_client.py index 212b1fb9..570ecbeb 100644 --- a/gso/services/netbox_client.py +++ b/gso/services/netbox_client.py @@ -223,9 +223,7 @@ class NetboxClient: # Assign interface to :term:`LAG`, ensuring it does not already belong to a :term:`LAG`. if iface.lag: msg = f"The interface: {iface_name} on device: {device_name} already belongs to a LAG: {iface.lag.name}." - raise WorkflowStateError( - msg, - ) + raise WorkflowStateError(msg) iface.lag = lag.id # Set description if provided diff --git a/gso/translations/en-GB.json b/gso/translations/en-GB.json index 9fdf1dfa..232bcd0a 100644 --- a/gso/translations/en-GB.json +++ b/gso/translations/en-GB.json @@ -55,6 +55,7 @@ "import_router": "NOT FOR HUMANS -- Finalize import into a Router product", "import_iptrunk": "NOT FOR HUMANS -- Finalize import into an IP trunk product", "import_office_router": "NOT FOR HUMANS -- Finalize import into an Office router product", - "import_super_pop_switch": "NOT FOR HUMANS -- Finalize import into a Super PoP switch" + "import_super_pop_switch": "NOT FOR HUMANS -- Finalize import into a Super PoP switch", + "validate_router": "Validate router configuration" } } diff --git a/gso/workflows/__init__.py b/gso/workflows/__init__.py index cf2eca0f..54d6a8a1 100644 --- a/gso/workflows/__init__.py +++ b/gso/workflows/__init__.py @@ -63,3 +63,4 @@ LazyWorkflowInstance("gso.workflows.office_router.create_imported_office_router" # Opengear workflows LazyWorkflowInstance("gso.workflows.opengear.create_imported_opengear", "create_imported_opengear") LazyWorkflowInstance("gso.workflows.opengear.import_opengear", "import_opengear") + diff --git a/gso/workflows/router/create_router.py b/gso/workflows/router/create_router.py index 700f3f9c..dfa0b27b 100644 --- a/gso/workflows/router/create_router.py +++ b/gso/workflows/router/create_router.py @@ -7,6 +7,7 @@ from orchestrator.forms import FormPage from orchestrator.forms.validators import Choice, Label from orchestrator.targets import Target from orchestrator.types import FormGenerator, State, SubscriptionLifecycle, UUIDstr +from orchestrator.utils.errors import ProcessFailureError from orchestrator.workflow import StepList, conditional, done, init, inputstep, step, workflow from orchestrator.workflows.steps import resync, set_status, store_process_subscription from orchestrator.workflows.utils import wrap_create_initial_input_form @@ -143,13 +144,15 @@ def create_netbox_device(subscription: RouterInactive) -> State: @step("Verify IPAM resources for loopback interface") -def verify_ipam_loopback(subscription: RouterInactive) -> State: - """Validate the :term:`IPAM` resources for the loopback interface.""" +def verify_ipam_loopback(subscription: RouterInactive) -> None: + """Validate the :term:`IPAM` resources for the loopback interface. + + Raises an :class:`orchestrator.utils.errors.ProcessFailureError` if IPAM is configured incorrectly. + """ host_record = infoblox.find_host_by_fqdn(f"lo0.{subscription.router.router_fqdn}") if not host_record or str(subscription.subscription_id) not in host_record.comment: - return {"ipam_warning": "Loopback record is incorrectly configured in IPAM, please investigate this manually!"} - - return {"subscription": subscription} + msg = "Loopback record is incorrectly configured in IPAM, please investigate this manually!" + raise ProcessFailureError(msg) @inputstep("Prompt to reboot", assignee=Assignee.SYSTEM) diff --git a/gso/workflows/tasks/validate_router.py b/gso/workflows/tasks/validate_router.py new file mode 100644 index 00000000..18ab6e3e --- /dev/null +++ b/gso/workflows/tasks/validate_router.py @@ -0,0 +1,41 @@ +import json + +from orchestrator.targets import Target +from orchestrator.types import State +from orchestrator.utils.json import json_dumps +from orchestrator.workflow import StepList, init, workflow, done, step +from orchestrator.workflows.steps import store_process_subscription, unsync, resync + +from gso.services.provisioning_proxy import pp_interaction, execute_playbook +from gso.workflows.router.create_router import verify_ipam_loopback +from products import Router + + +@step("Validate router configuration") +def validate_router_config(subscription: Router, callback_route: str) -> None: + extra_vars = {"wfo_router_json": json.loads(json_dumps(subscription)), "verb": "validate"} + + execute_playbook( + playbook_name="base_config.yaml", + callback_route=callback_route, + inventory=subscription.router.router_fqdn, + extra_vars=extra_vars + ) + + +@workflow("Validate router configuration", target=Target.SYSTEM) +def validate_router() -> StepList: + """Validate an existing, active Router subscription. + + * Run an Ansible playbook to verify the configuration is intact. + * Verify that the loopback interface is correctly configured in IPAM. + """ + return ( + init + >> store_process_subscription(Target.SYSTEM) + >> unsync + >> pp_interaction(validate_router_config) + >> verify_ipam_loopback + >> resync + >> done + ) -- GitLab