From ce8af67534fe8460c3af5d53eeba3e22e1c59910 Mon Sep 17 00:00:00 2001
From: Karel van Klink <karel.vanklink@geant.org>
Date: Wed, 14 Aug 2024 11:16:43 +0200
Subject: [PATCH] Restrict promotion workflow to Nokia P-routers

---
 gso/workflows/router/promote_p_to_pe.py       | 21 ++++++-------
 test/workflows/router/test_promote_p_to_pe.py | 31 +++++++++----------
 2 files changed, 24 insertions(+), 28 deletions(-)

diff --git a/gso/workflows/router/promote_p_to_pe.py b/gso/workflows/router/promote_p_to_pe.py
index ff0902c2..e593844d 100644
--- a/gso/workflows/router/promote_p_to_pe.py
+++ b/gso/workflows/router/promote_p_to_pe.py
@@ -10,10 +10,10 @@ from orchestrator.targets import Target
 from orchestrator.types import FormGenerator, State, UUIDstr
 from orchestrator.utils.errors import ProcessFailureError
 from orchestrator.utils.json import json_dumps
-from orchestrator.workflow import StepList, begin, conditional, done, inputstep, step, workflow
+from orchestrator.workflow import StepList, begin, done, inputstep, step, workflow
 from orchestrator.workflows.steps import resync, store_process_subscription, unsync
 from orchestrator.workflows.utils import wrap_modify_initial_input_form
-from pydantic import ConfigDict
+from pydantic import ConfigDict, model_validator
 
 from gso.products.product_blocks.router import RouterRole
 from gso.products.product_types.router import Router
@@ -35,6 +35,14 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
 
         tt_number: TTNumber
 
+        @model_validator(mode="before")
+        def router_must_be_nokia_p(cls, data: Any) -> Any:
+            if not (subscription.router.router_role == RouterRole.P and subscription.router.vendor == Vendor.NOKIA):
+                msg = "PE promotion workflow can only be run on Nokia P routers."
+                raise ValueError(msg)
+
+            return data
+
     user_input = yield PromotePToPEForm
 
     return user_input.model_dump() | {"subscription": subscription}
@@ -147,10 +155,6 @@ def create_kentik_device(subscription: Router) -> State:
     )
     kentik_device = kentik_client.create_device(new_device)
 
-    if "error" in kentik_device or "kentik_error" in kentik_device:
-        raise ProcessFailureError(str(kentik_device))
-
-    kentik_device.pop("custom_column_data", None)
     return {"kentik_device": kentik_device}
 
 
@@ -559,14 +563,9 @@ def delete_default_routes_real(
 )
 def promote_p_to_pe() -> StepList:
     """Promote a P router to a PE router."""
-    router_is_juniper = conditional(lambda state: state["subscription"]["router"]["vendor"] == Vendor.JUNIPER)
-    router_is_pe = conditional(lambda state: state["subscription"]["router"]["router_role"] == RouterRole.PE)
-
     return (
         begin
         >> store_process_subscription(Target.MODIFY)
-        >> router_is_juniper(done)
-        >> router_is_pe(done)
         >> unsync
         >> lso_interaction(set_isis_overload)
         >> lso_interaction(deploy_pe_base_config_dry)
diff --git a/test/workflows/router/test_promote_p_to_pe.py b/test/workflows/router/test_promote_p_to_pe.py
index 67f789e4..90493d7e 100644
--- a/test/workflows/router/test_promote_p_to_pe.py
+++ b/test/workflows/router/test_promote_p_to_pe.py
@@ -46,33 +46,30 @@ def test_promote_p_to_pe_success(
 
 
 @pytest.mark.workflow()
-@patch("gso.workflows.router.promote_p_to_pe.lso_client.execute_playbook")
-def test_promote_p_to_pe_juniper_router(
-    mock_execute_playbook, juniper_router_subscription_factory, data_config_filename, faker
-):
+def test_promote_p_to_pe_juniper_router(juniper_router_subscription_factory, data_config_filename, faker):
     """Test that the workflow does not run for a Juniper P router since this workflow is only for Nokia routers."""
     router_id = juniper_router_subscription_factory(router_role=RouterRole.P, status=SubscriptionLifecycle.ACTIVE)
     input_data = [{"subscription_id": router_id}, {"tt_number": faker.tt_number()}]
-    result, _, _ = run_workflow("promote_p_to_pe", input_data)
-    assert_complete(result)
-    state = extract_state(result)
-    assert mock_execute_playbook.call_count == 0
-    assert state["subscription"]["router"]["router_role"] == RouterRole.P
+    with pytest.raises(FormValidationError) as error:
+        run_workflow("promote_p_to_pe", input_data)
+    error = error.value.errors[0]
+    assert error["msg"] == "PE promotion workflow can only be run on Nokia P routers."
+    assert error["loc"][0] == "__root__"
 
 
 @pytest.mark.workflow()
 @patch("gso.workflows.router.promote_p_to_pe.lso_client.execute_playbook")
-def test_promote_p_to_pe_nokia_p_router(
+def test_promote_p_to_pe_nokia_pe_router(
     mock_execute_playbook, nokia_router_subscription_factory, data_config_filename, faker
 ):
     """Test that the workflow does not run for a Nokia PE router since it is already a PE router."""
     router_id = nokia_router_subscription_factory(router_role=RouterRole.PE, status=SubscriptionLifecycle.ACTIVE)
     input_data = [{"subscription_id": router_id}, {"tt_number": faker.tt_number()}]
-    result, _, _ = run_workflow("promote_p_to_pe", input_data)
-    assert_complete(result)
-    state = extract_state(result)
-    assert mock_execute_playbook.call_count == 0
-    assert state["subscription"]["router"]["router_role"] == RouterRole.PE
+    with pytest.raises(FormValidationError) as error:
+        run_workflow("promote_p_to_pe", input_data)
+    error = error.value.errors[0]
+    assert error["msg"] == "PE promotion workflow can only be run on Nokia P routers."
+    assert error["loc"][0] == "__root__"
 
 
 def test_promote_p_to_pe_missing_tt_number(nokia_router_subscription_factory):
@@ -85,11 +82,11 @@ def test_promote_p_to_pe_missing_tt_number(nokia_router_subscription_factory):
     assert error["loc"][0] == "tt_number"
 
 
-def test_promote_p_to_pe_with_invalid_router_life_cycle(nokia_router_subscription_factory):
+def test_promote_p_to_pe_with_invalid_router_life_cycle(nokia_router_subscription_factory, faker):
     """Test that the router life cycle must be ACTIVE to run this workflow."""
     router_id = nokia_router_subscription_factory(router_role=RouterRole.P, status=SubscriptionLifecycle.PROVISIONING)
     with pytest.raises(FormValidationError) as error:
-        run_workflow("promote_p_to_pe", [{"subscription_id": router_id}, {"tt_number": "TT123"}])
+        run_workflow("promote_p_to_pe", [{"subscription_id": router_id}, {"tt_number": faker.tt_number()}])
     error = error.value.errors[0]
     assert error["msg"] == (
         "This workflow cannot be started: This subscription can not be modified because of the status it has"
-- 
GitLab