From e3767e61f1e893ed45ac416e0319410f5c8fa61c Mon Sep 17 00:00:00 2001
From: Karel van Klink <karel.vanklink@geant.org>
Date: Wed, 18 Dec 2024 14:43:29 +0100
Subject: [PATCH] Add task for checking NETCONF connectivity of a Site

---
 ...ed588e_add_site_connectivity_check_task.py | 39 ++++++++++++++++
 gso/translations/en-GB.json                   |  1 +
 gso/workflows/__init__.py                     |  1 +
 .../tasks/check_site_connectivity.py          | 46 +++++++++++++++++++
 4 files changed, 87 insertions(+)
 create mode 100644 gso/migrations/versions/2024-12-18_8a65d0ed588e_add_site_connectivity_check_task.py
 create mode 100644 gso/workflows/tasks/check_site_connectivity.py

diff --git a/gso/migrations/versions/2024-12-18_8a65d0ed588e_add_site_connectivity_check_task.py b/gso/migrations/versions/2024-12-18_8a65d0ed588e_add_site_connectivity_check_task.py
new file mode 100644
index 00000000..8c3e61e9
--- /dev/null
+++ b/gso/migrations/versions/2024-12-18_8a65d0ed588e_add_site_connectivity_check_task.py
@@ -0,0 +1,39 @@
+"""Add Site connectivity check task.
+
+Revision ID: 8a65d0ed588e
+Revises: 818d4ffe65df
+Create Date: 2024-12-18 14:36:35.886366
+
+"""
+from uuid import uuid4
+
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision = '8a65d0ed588e'
+down_revision = '818d4ffe65df'
+branch_labels = None
+depends_on = None
+
+workflow = {
+    "name": "task_check_site_connectivity",
+    "target": "SYSTEM",
+    "description": "Check Site Connectivity",
+    "workflow_id": uuid4(),
+}
+
+
+def upgrade() -> None:
+    conn = op.get_bind()
+    conn.execute(
+        sa.text(
+            "INSERT INTO workflows VALUES (:workflow_id, :name, :target, :description, now()) ON CONFLICT DO NOTHING"
+        ),
+        workflow,
+    )
+
+
+def downgrade() -> None:
+    conn = op.get_bind()
+    conn.execute(sa.text("DELETE FROM workflows WHERE name = :name"), {"name": workflow["name"]})
diff --git a/gso/translations/en-GB.json b/gso/translations/en-GB.json
index c62d1c4d..4dfe8402 100644
--- a/gso/translations/en-GB.json
+++ b/gso/translations/en-GB.json
@@ -103,6 +103,7 @@
         "task_modify_partners": "Modify partner task",
         "task_delete_partners": "Delete partner task",
         "task_clean_old_tasks": "Remove old cleanup tasks",
+        "task_check_site_connectivity": "Check NETCONF connectivity of a Site",
         "promote_p_to_pe": "Promote P to PE",
         "create_layer_2_circuit": "Create Layer 2 Circuit",
         "modify_layer_2_circuit": "Modify Layer 2 Circuit",
diff --git a/gso/workflows/__init__.py b/gso/workflows/__init__.py
index cc6725d4..20e652d4 100644
--- a/gso/workflows/__init__.py
+++ b/gso/workflows/__init__.py
@@ -107,6 +107,7 @@ LazyWorkflowInstance("gso.workflows.tasks.create_partners", "task_create_partner
 LazyWorkflowInstance("gso.workflows.tasks.modify_partners", "task_modify_partners")
 LazyWorkflowInstance("gso.workflows.tasks.delete_partners", "task_delete_partners")
 LazyWorkflowInstance("gso.workflows.tasks.clean_old_tasks", "task_clean_old_tasks")
+LazyWorkflowInstance("gso.workflows.tasks.check_site_connectivity", "task_check_site_connectivity")
 
 #  Edge port workflows
 LazyWorkflowInstance("gso.workflows.edge_port.create_edge_port", "create_edge_port")
diff --git a/gso/workflows/tasks/check_site_connectivity.py b/gso/workflows/tasks/check_site_connectivity.py
new file mode 100644
index 00000000..a373d144
--- /dev/null
+++ b/gso/workflows/tasks/check_site_connectivity.py
@@ -0,0 +1,46 @@
+"""A task for checking site connectivity.
+
+When a new router is planned to be deployed, it is good practice to verify that the OOB connectivity is functioning
+correctly. This task takes a site and a port number as input, and checks whether there is reachability across the OOB
+access.
+"""
+
+from orchestrator import workflow
+from orchestrator.forms import SubmitFormPage
+from orchestrator.targets import Target
+from orchestrator.workflow import StepList, begin, done, step
+from pydantic import ConfigDict
+from pydantic_forms.types import FormGenerator, UUIDstr
+
+from gso.products.product_types.site import Site
+from gso.services.lso_client import LSOState, lso_interaction
+from gso.utils.helpers import active_site_selector
+from gso.utils.types.ip_address import PortNumber
+
+
+def _initial_input_form_generator() -> FormGenerator:
+    class CheckSiteConnectivityForm(SubmitFormPage):
+        model_config = ConfigDict(title="Verify NETCONF connectivity to a Site")
+
+        site: active_site_selector()  # type: ignore[valid-type]
+        port: PortNumber
+
+    user_input = yield CheckSiteConnectivityForm
+    return user_input.model_dump()
+
+
+@step("Check NETCONF connectivity")
+def check_netconf_connectivity(site: UUIDstr, port: PortNumber) -> LSOState:
+    """Run an Ansible playbook that validates NETCONF connectivity to a Site."""
+    site_subscription = Site.from_subscription(site).site
+    return {
+        "playbook_name": "gap_ansible/playbooks/check_netconf_connectivity.yaml",
+        "extra_vars": {"port_number": port},
+        "inventory": {"all": {"hosts": {site_subscription.site_ts_address: None}}},
+    }
+
+
+@workflow("Check Site Connectivity", _initial_input_form_generator, Target.SYSTEM)
+def task_check_site_connectivity() -> StepList:
+    """Check successful NETCONF connectivity of a Site."""
+    return begin >> lso_interaction(check_netconf_connectivity) >> done
-- 
GitLab