From 8d6f0d9de44e8f7f12852c50708356d58e6b67fe Mon Sep 17 00:00:00 2001
From: Karel van Klink <karel.vanklink@geant.org>
Date: Tue, 9 Jan 2024 15:18:02 +0100
Subject: [PATCH] Add nightly schedule for router validation

---
 gso/schedules/__init__.py                 |  8 ++++++++
 gso/schedules/validate_routers_nightly.py | 21 +++++++++++++++++++++
 gso/workflows/tasks/validate_router.py    | 21 ++++++++++++++-------
 3 files changed, 43 insertions(+), 7 deletions(-)
 create mode 100644 gso/schedules/validate_routers_nightly.py

diff --git a/gso/schedules/__init__.py b/gso/schedules/__init__.py
index 8257a874..f3100cf8 100644
--- a/gso/schedules/__init__.py
+++ b/gso/schedules/__init__.py
@@ -1 +1,9 @@
 """Tasks that are scheduled to run periodically in :term:`GSO`."""
+
+from orchestrator.schedules import SchedulingFunction  # type:ignore[attr-defined]
+
+from gso.schedules.validate_routers_nightly import run_validate_routers
+
+#  TODO: This list overwrites the default, and therefore leaves all default schedules unused.
+#  TODO: Consider using the default schedules.
+ALL_SCHEDULERS: list[SchedulingFunction] = [run_validate_routers]
diff --git a/gso/schedules/validate_routers_nightly.py b/gso/schedules/validate_routers_nightly.py
new file mode 100644
index 00000000..9b60df42
--- /dev/null
+++ b/gso/schedules/validate_routers_nightly.py
@@ -0,0 +1,21 @@
+"""Nightly schedule for validating all active Routers' configuration."""
+
+import logging
+
+from orchestrator.schedules.scheduling import scheduler
+from orchestrator.services.processes import start_process
+
+from gso.services.subscriptions import get_active_router_subscriptions
+
+logger = logging.getLogger(__name__)
+
+
+@scheduler(name="Validate routers", time_unit="day", at="03:00")
+def run_validate_routers() -> None:
+    """Validate configuration on all active Routers, every night at 3AM."""
+    routers = get_active_router_subscriptions(includes=["subscription_id"])
+
+    for router in routers:
+        msg = f"Validating configuration of router subscription {router['subscription_id']}"
+        logger.info(msg)
+        start_process("validate_router", [{"subscription_id": router["subscription_id"]}])
diff --git a/gso/workflows/tasks/validate_router.py b/gso/workflows/tasks/validate_router.py
index 18ab6e3e..465e4c33 100644
--- a/gso/workflows/tasks/validate_router.py
+++ b/gso/workflows/tasks/validate_router.py
@@ -1,29 +1,36 @@
+"""Router validation workflow. Used in a nightly schedule."""
+
 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 orchestrator.workflow import StepList, done, init, step, workflow
+from orchestrator.workflows.steps import resync, store_process_subscription, unsync
+from orchestrator.workflows.utils import wrap_modify_initial_input_form
+from products import Router
 
-from gso.services.provisioning_proxy import pp_interaction, execute_playbook
+from gso.services.provisioning_proxy import execute_playbook, pp_interaction
 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:
+    """Run an Ansible playbook that validates the configuration that is present on an active Router."""
     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
+        extra_vars=extra_vars,
     )
 
 
-@workflow("Validate router configuration", target=Target.SYSTEM)
+@workflow(
+    "Validate router configuration",
+    target=Target.SYSTEM,
+    initial_input_form=wrap_modify_initial_input_form(None),
+)
 def validate_router() -> StepList:
     """Validate an existing, active Router subscription.
 
-- 
GitLab