From 75a5248b299124381977ce400abd1f8a5da99311 Mon Sep 17 00:00:00 2001
From: Aleksandr Kurbatov <aleksandr.kurbatov@GL1342.local>
Date: Wed, 3 Jul 2024 10:15:17 +0100
Subject: [PATCH] BFD-related workflow steps

If trunk_type is Leased, then BFD must be configured on that trunk
according to the policy.
The steps that are added will update the target loopback address of BFD
config on the remaining side when trunk type is Leased, and will be
skipped when trunk type is Dark_fiber.
---
 gso/workflows/iptrunk/migrate_iptrunk.py | 92 +++++++++++++++++++++++-
 1 file changed, 91 insertions(+), 1 deletion(-)

diff --git a/gso/workflows/iptrunk/migrate_iptrunk.py b/gso/workflows/iptrunk/migrate_iptrunk.py
index 11294d9e..e8fe02ca 100644
--- a/gso/workflows/iptrunk/migrate_iptrunk.py
+++ b/gso/workflows/iptrunk/migrate_iptrunk.py
@@ -25,7 +25,7 @@ from pydantic import AfterValidator, ConfigDict, field_validator
 from pydantic_forms.validators import ReadOnlyField, validate_unique_list
 from pynetbox.models.dcim import Interfaces
 
-from gso.products.product_blocks.iptrunk import IptrunkInterfaceBlock
+from gso.products.product_blocks.iptrunk import IptrunkInterfaceBlock, IptrunkType
 from gso.products.product_types.iptrunk import Iptrunk
 from gso.products.product_types.router import Router
 from gso.services import infoblox
@@ -416,6 +416,90 @@ def deploy_new_config_real(
     return {"subscription": subscription}
 
 
+@step("[DRY RUN] Update BFD on the remaining side")
+def update_remaining_side_bfd_dry(
+    subscription: Iptrunk,
+    callback_route: str,
+    new_node: Router,
+    replace_index: int,
+    process_id: UUIDstr,
+    tt_number: str,
+) -> State:
+    """Perform a dry run of deploying configuration on the new router."""
+    extra_vars = {
+        "wfo_trunk_json": json.loads(json_dumps(subscription)),
+        "new_node": json.loads(json_dumps(new_node)),
+        "replace_index": replace_index,
+        "verb": "update",
+        "config_object": "bfd_update",
+        "dry_run": True,
+        "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} " f"- Update BFD config.",
+    }
+
+    execute_playbook(
+        playbook_name="iptrunks_migration.yaml",
+        callback_route=callback_route,
+        inventory=subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn,
+        extra_vars=extra_vars,
+    )
+
+    return {"subscription": subscription}
+
+
+@step("[FOR REAL] Update BFD on the remaining side")
+def update_remaining_side_bfd_real(
+    subscription: Iptrunk,
+    callback_route: str,
+    new_node: Router,
+    replace_index: int,
+    process_id: UUIDstr,
+    tt_number: str,
+) -> State:
+    """Perform a dry run of deploying configuration on the new router."""
+    extra_vars = {
+        "wfo_trunk_json": json.loads(json_dumps(subscription)),
+        "new_node": json.loads(json_dumps(new_node)),
+        "replace_index": replace_index,
+        "verb": "update",
+        "config_object": "bfd_update",
+        "dry_run": False,
+        "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} " f"- Update BFD config.",
+    }
+
+    execute_playbook(
+        playbook_name="iptrunks_migration.yaml",
+        callback_route=callback_route,
+        inventory=subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn,
+        extra_vars=extra_vars,
+    )
+
+    return {"subscription": subscription}
+
+
+@step("Check BFD session over trunk")
+def check_ip_trunk_bfd(
+    subscription: Iptrunk,
+    callback_route: str,
+    new_node: Router,
+    replace_index: int,
+) -> State:
+    """Check BFD session across the new trunk."""
+    extra_vars = {
+        "wfo_ip_trunk_json": json.loads(json_dumps(subscription)),
+        "new_node": json.loads(json_dumps(new_node)),
+        "check": "bfd",
+    }
+
+    execute_playbook(
+        playbook_name="iptrunks_checks.yaml",
+        callback_route=callback_route,
+        inventory=subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn,
+        extra_vars=extra_vars,
+    )
+
+    return {"subscription": subscription}
+
+
 @inputstep("Wait for confirmation", assignee=Assignee.SYSTEM)
 def confirm_continue_move_fiber() -> FormGenerator:
     """Wait for confirmation from an operator that the physical fiber has been moved."""
@@ -726,6 +810,9 @@ def migrate_iptrunk() -> StepList:
         == Vendor.NOKIA
     )
     should_restore_isis_metric = conditional(lambda state: state["restore_isis_metric"])
+    trunk_type_is_leased = conditional(
+        lambda state: state["subscription"]["iptrunk"]["iptrunk_type"] == IptrunkType.LEASED
+    )
 
     return (
         init
@@ -739,8 +826,11 @@ def migrate_iptrunk() -> StepList:
         >> lso_interaction(disable_old_config_real)
         >> lso_interaction(deploy_new_config_dry)
         >> lso_interaction(deploy_new_config_real)
+        >> trunk_type_is_leased(lso_interaction(update_remaining_side_bfd_dry))
+        >> trunk_type_is_leased(lso_interaction(update_remaining_side_bfd_real))
         >> confirm_continue_move_fiber
         >> lso_interaction(check_ip_trunk_optical_levels_post)
+        >> trunk_type_is_leased(lso_interaction(check_ip_trunk_bfd))
         >> lso_interaction(check_ip_trunk_connectivity)
         >> lso_interaction(deploy_new_isis)
         >> lso_interaction(check_ip_trunk_isis)
-- 
GitLab