From ae0ba33f3424e4bdc21ba5af79e02d096ec58886 Mon Sep 17 00:00:00 2001
From: Karel van Klink <karel.vanklink@geant.org>
Date: Tue, 2 Jan 2024 15:18:56 +0100
Subject: [PATCH] use conditionals in IP trunk termination workflow

---
 gso/workflows/iptrunk/terminate_iptrunk.py | 63 ++++++++++++++--------
 1 file changed, 41 insertions(+), 22 deletions(-)

diff --git a/gso/workflows/iptrunk/terminate_iptrunk.py b/gso/workflows/iptrunk/terminate_iptrunk.py
index 855c6cab..c0c47a8f 100644
--- a/gso/workflows/iptrunk/terminate_iptrunk.py
+++ b/gso/workflows/iptrunk/terminate_iptrunk.py
@@ -17,6 +17,7 @@ from orchestrator.workflows.steps import (
 )
 from orchestrator.workflows.utils import wrap_modify_initial_input_form
 
+from gso.products.product_blocks.iptrunk import IptrunkSideBlock
 from gso.products.product_blocks.router import RouterVendor
 from gso.products.product_types.iptrunk import Iptrunk
 from gso.services import infoblox
@@ -32,11 +33,12 @@ def initial_input_form_generator() -> FormGenerator:
     class TerminateForm(FormPage):
         termination_label: Label = (
             "Please confirm whether configuration should get removed from the A and B sides of the trunk, and whether "
-            "IPAM resources should be released."  # type: ignore[assignment]
+            "IPAM and Netbox resources should be released."  # type: ignore[assignment]
         )
         tt_number: str
         remove_configuration: bool = True
         clean_up_ipam: bool = True
+        clean_up_netbox: bool = True
 
     user_input = yield TerminateForm
     return user_input.dict()
@@ -88,26 +90,28 @@ def deprovision_ip_trunk_real(subscription: Iptrunk, process_id: UUIDstr, callba
     return {"subscription": subscription}
 
 
-@step("Remove IP Trunk from Netbox")
-def free_interfaces_in_netbox(subscription: Iptrunk) -> State:
-    """Mark used interfaces as free in Netbox.
+def _remove_interface_from_netbox(side_block: IptrunkSideBlock) -> None:
+    nbclient = NetboxClient()
 
-    TODO: decide on the conditionality of this step
-    """
-    for side in [0, 1]:
-        router = subscription.iptrunk.iptrunk_sides[side].iptrunk_side_node
-        router_vendor = get_router_vendor(router.owner_subscription_id)
-        router_fqdn = router.router_fqdn
-        if router_vendor == RouterVendor.NOKIA:
-            nbclient = NetboxClient()
-            # Remove physical interfaces from LAGs
-            for member in subscription.iptrunk.iptrunk_sides[side].iptrunk_side_ae_members:
-                nbclient.free_interface(router_fqdn, member.interface_name)
-            # Delete LAGs
-            nbclient.delete_interface(
-                router_fqdn,
-                subscription.iptrunk.iptrunk_sides[side].iptrunk_side_ae_iface,
-            )
+    for member in side_block.iptrunk_side_ae_members:
+        nbclient.free_interface(side_block.iptrunk_side_node.router_fqdn, member.interface_name)
+
+    if side_block.iptrunk_side_ae_iface:
+        nbclient.delete_interface(side_block.iptrunk_side_node.router_fqdn, side_block.iptrunk_side_ae_iface)
+
+
+@step("Netbox: Remove interfaces on side A")
+def netbox_remove_side_a_interfaces(subscription: Iptrunk) -> State:
+    """Mark used interfaces on side A as free in Netbox."""
+    _remove_interface_from_netbox(subscription.iptrunk.iptrunk_sides[0])
+
+    return {"subscription": subscription}
+
+
+@step("Netbox: Remove interfaces on side B")
+def netbox_remove_side_b_interfaces(subscription: Iptrunk) -> State:
+    """Mark used interfaces on side B as free in Netbox."""
+    _remove_interface_from_netbox(subscription.iptrunk.iptrunk_sides[1])
 
     return {"subscription": subscription}
 
@@ -139,12 +143,26 @@ def terminate_iptrunk() -> StepList:
     * Let the operator decide whether to remove configuration from the routers, if so:
         * Set the :term:`ISIS` metric of the IP trunk to an arbitrarily high value
         * Disable and remove configuration from the routers, first as a dry run
-    * Mark the IP trunk interfaces as free in Netbox
+    * Mark the IP trunk interfaces as free in Netbox, if selected by the operator
     * Clear :term:`IPAM` resources, if selected by the operator
     * Terminate the subscription in the service database
     """
     run_config_steps = conditional(lambda state: state["remove_configuration"])
     run_ipam_steps = conditional(lambda state: state["clean_up_ipam"])
+    side_a_is_nokia = conditional(
+        lambda state: state["clean_up_netbox"]
+        and get_router_vendor(
+            state["subscription"]["iptrunk"]["iptrunk_sides"][0]["iptrunk_side_node"]["owner_subscription_id"]
+        )
+        == RouterVendor.NOKIA
+    )
+    side_b_is_nokia = conditional(
+        lambda state: state["clean_up_netbox"]
+        and get_router_vendor(
+            state["subscription"]["iptrunk"]["iptrunk_sides"][1]["iptrunk_side_node"]["owner_subscription_id"]
+        )
+        == RouterVendor.NOKIA
+    )
 
     config_steps = (
         init
@@ -159,7 +177,8 @@ def terminate_iptrunk() -> StepList:
         >> store_process_subscription(Target.TERMINATE)
         >> unsync
         >> run_config_steps(config_steps)
-        >> free_interfaces_in_netbox
+        >> side_a_is_nokia(netbox_remove_side_a_interfaces)
+        >> side_b_is_nokia(netbox_remove_side_b_interfaces)
         >> run_ipam_steps(ipam_steps)
         >> set_status(SubscriptionLifecycle.TERMINATED)
         >> resync
-- 
GitLab