From 5f0d59e9bd53888e4ba4a6a11f18af3e65d6fc66 Mon Sep 17 00:00:00 2001 From: Karel van Klink <karel.vanklink@geant.org> Date: Fri, 2 Aug 2024 15:10:06 +0200 Subject: [PATCH] Add modification workflow for routers to change Kentik license --- ...e_add_router_modification_workflow_for_.py | 39 ++++++++++++++ gso/workflows/__init__.py | 1 + gso/workflows/router/modify_kentik_license.py | 54 +++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 gso/migrations/versions/2024-08-02_87a05eddee3e_add_router_modification_workflow_for_.py create mode 100644 gso/workflows/router/modify_kentik_license.py diff --git a/gso/migrations/versions/2024-08-02_87a05eddee3e_add_router_modification_workflow_for_.py b/gso/migrations/versions/2024-08-02_87a05eddee3e_add_router_modification_workflow_for_.py new file mode 100644 index 00000000..13162210 --- /dev/null +++ b/gso/migrations/versions/2024-08-02_87a05eddee3e_add_router_modification_workflow_for_.py @@ -0,0 +1,39 @@ +"""Add router modification workflow for kentik licenses. + +Revision ID: 87a05eddee3e +Revises: 41fd1ae225aq +Create Date: 2024-08-02 15:09:42.597063 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = '87a05eddee3e' +down_revision = '41fd1ae225aq' +branch_labels = None +depends_on = None + + +from orchestrator.migrations.helpers import create_workflow, delete_workflow + +new_workflows = [ + { + "name": "modify_router_kentik_license", + "target": "MODIFY", + "description": "Modify Kentik license", + "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/workflows/__init__.py b/gso/workflows/__init__.py index 291a9e25..96638b85 100644 --- a/gso/workflows/__init__.py +++ b/gso/workflows/__init__.py @@ -46,6 +46,7 @@ LazyWorkflowInstance("gso.workflows.router.import_router", "import_router") LazyWorkflowInstance("gso.workflows.router.create_imported_router", "create_imported_router") LazyWorkflowInstance("gso.workflows.router.validate_router", "validate_router") LazyWorkflowInstance("gso.workflows.router.promote_p_to_pe", "promote_p_to_pe") +LazyWorkflowInstance("gso.workflows.router.modify_kentik_license", "modify_router_kentik_license") # Site workflows LazyWorkflowInstance("gso.workflows.site.create_site", "create_site") diff --git a/gso/workflows/router/modify_kentik_license.py b/gso/workflows/router/modify_kentik_license.py new file mode 100644 index 00000000..3d6fd825 --- /dev/null +++ b/gso/workflows/router/modify_kentik_license.py @@ -0,0 +1,54 @@ +"""A workflow that modifies the Kentik license of a router.""" + +from orchestrator.forms import FormPage +from orchestrator.targets import Target +from orchestrator.types import FormGenerator, State, UUIDstr +from orchestrator.utils.errors import ProcessFailureError +from orchestrator.workflow import StepList, begin, done, step, workflow +from orchestrator.workflows.steps import resync, store_process_subscription, unsync +from orchestrator.workflows.utils import wrap_modify_initial_input_form +from pydantic_forms.validators import Choice + +from gso.products.product_types.router import Router +from gso.services.kentik_client import KentikClient + + +def _initial_input_form(subscription_id: UUIDstr) -> FormGenerator: + router = Router.from_subscription(subscription_id) + + class ModifyKentikLicenseForm(FormPage): + @staticmethod + def _get_available_plans() -> list[dict[str, str]]: + kentik_plans = KentikClient().get_plans() + return [{plan["name"]: plan["id"]} for plan in kentik_plans] + + new_plan_id = Choice("Select a Kentik license", _get_available_plans()) + + user_input = yield ModifyKentikLicenseForm + + return user_input.model_dump() | {"subscription": router} + + +@step("Update device license in Kentik") +def update_kentik_license(subscription: Router, new_plan_id: int) -> State: + """Update a Kentik device with a new license attached to it.""" + kentik_client = KentikClient() + kentik_device = kentik_client.get_device_by_name(subscription.router.router_fqdn) + if "id" not in kentik_device: + msg = "Failed to find Kentik device by name" + raise ProcessFailureError(msg, details=kentik_device) + + updated_kentik_device = kentik_client.update_device(kentik_device["id"], {"plan_id": new_plan_id}) + updated_kentik_device.pop("custom_column_data", None) + + return {"kentik_device": updated_kentik_device} + + +@workflow( + "Modify Kentik license", + initial_input_form=wrap_modify_initial_input_form(_initial_input_form), + target=Target.MODIFY, +) +def modify_router_kentik_license() -> StepList: + """Apply a selected Kentik license on an existing PE router.""" + return begin >> store_process_subscription(Target.MODIFY) >> unsync >> update_kentik_license >> resync >> done -- GitLab