From 4bf38511435e10e6016ad90435ee57d25e0f0903 Mon Sep 17 00:00:00 2001
From: Karel van Klink <karel.vanklink@geant.org>
Date: Fri, 16 Feb 2024 16:12:30 +0100
Subject: [PATCH] reflect lifecycle state changes in workflow steps

---
 gso/workflows/iptrunk/create_iptrunk.py | 37 +++++++++++++------------
 gso/workflows/router/create_router.py   | 32 +++++++++++++--------
 2 files changed, 40 insertions(+), 29 deletions(-)

diff --git a/gso/workflows/iptrunk/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py
index a42b60f9..025033b1 100644
--- a/gso/workflows/iptrunk/create_iptrunk.py
+++ b/gso/workflows/iptrunk/create_iptrunk.py
@@ -17,12 +17,12 @@ from pynetbox.models.dcim import Interfaces
 
 from gso.products.product_blocks.iptrunk import (
     IptrunkInterfaceBlockInactive,
-    IptrunkSideBlockProvisioning,
+    IptrunkSideBlockInactive,
     IptrunkType,
     PhyPortCapacity,
 )
 from gso.products.product_blocks.router import RouterVendor
-from gso.products.product_types.iptrunk import IptrunkInactive, IptrunkProvisioning
+from gso.products.product_types.iptrunk import IptrunkInactive
 from gso.products.product_types.router import Router
 from gso.services import infoblox, subscriptions
 from gso.services.crm import get_customer_by_name
@@ -184,7 +184,7 @@ def create_subscription(product: UUIDstr, customer: str) -> State:
 
 
 @step("Get information from IPAM")
-def get_info_from_ipam(subscription: IptrunkProvisioning) -> State:
+def get_info_from_ipam(subscription: IptrunkInactive) -> State:
     """Allocate IP resources in :term:`IPAM`."""
     subscription.iptrunk.iptrunk_ipv4_network = infoblox.allocate_v4_network(
         "TRUNK",
@@ -248,7 +248,7 @@ def initialize_subscription(
 
 @step("Provision IP trunk interface [DRY RUN]")
 def provision_ip_trunk_iface_dry(
-    subscription: IptrunkProvisioning,
+    subscription: IptrunkInactive,
     callback_route: str,
     process_id: UUIDstr,
     tt_number: str,
@@ -276,7 +276,7 @@ def provision_ip_trunk_iface_dry(
 
 @step("Provision IP trunk interface [FOR REAL]")
 def provision_ip_trunk_iface_real(
-    subscription: IptrunkProvisioning,
+    subscription: IptrunkInactive,
     callback_route: str,
     process_id: UUIDstr,
     tt_number: str,
@@ -304,7 +304,7 @@ def provision_ip_trunk_iface_real(
 
 @step("Check IP connectivity of the trunk")
 def check_ip_trunk_connectivity(
-    subscription: IptrunkProvisioning,
+    subscription: IptrunkInactive,
     callback_route: str,
 ) -> State:
     """Check successful connectivity across the new trunk."""
@@ -322,7 +322,7 @@ def check_ip_trunk_connectivity(
 
 @step("Provision IP trunk ISIS interface [DRY RUN]")
 def provision_ip_trunk_isis_iface_dry(
-    subscription: IptrunkProvisioning,
+    subscription: IptrunkInactive,
     callback_route: str,
     process_id: UUIDstr,
     tt_number: str,
@@ -350,7 +350,7 @@ def provision_ip_trunk_isis_iface_dry(
 
 @step("Provision IP trunk ISIS interface [FOR REAL]")
 def provision_ip_trunk_isis_iface_real(
-    subscription: IptrunkProvisioning,
+    subscription: IptrunkInactive,
     callback_route: str,
     process_id: UUIDstr,
     tt_number: str,
@@ -378,7 +378,7 @@ def provision_ip_trunk_isis_iface_real(
 
 @step("Check ISIS adjacency")
 def check_ip_trunk_isis(
-    subscription: IptrunkProvisioning,
+    subscription: IptrunkInactive,
     callback_route: str,
 ) -> State:
     """Run an Ansible playbook to confirm :term:`ISIS` adjacency."""
@@ -395,7 +395,7 @@ def check_ip_trunk_isis(
 
 
 @step("NextBox integration")
-def reserve_interfaces_in_netbox(subscription: IptrunkProvisioning) -> State:
+def reserve_interfaces_in_netbox(subscription: IptrunkInactive) -> State:
     """Create the :term:`LAG` interfaces in NetBox and attach the lag interfaces to the physical interfaces."""
     nbclient = NetboxClient()
     for trunk_side in subscription.iptrunk.iptrunk_sides:
@@ -427,22 +427,25 @@ def reserve_interfaces_in_netbox(subscription: IptrunkProvisioning) -> State:
     }
 
 
-def _allocate_interfaces_in_netbox(iptrunk_side: IptrunkSideBlockProvisioning) -> None:
+def _allocate_interfaces_in_netbox(iptrunk_side: IptrunkSideBlockInactive) -> None:
     for interface in iptrunk_side.iptrunk_side_ae_members:
-        NetboxClient().allocate_interface(
-            device_name=iptrunk_side.iptrunk_side_node.router_fqdn,
-            iface_name=interface.interface_name,
-        )
+        fqdn = iptrunk_side.iptrunk_side_node.router_fqdn
+        iface_name = interface.interface_name
+        if not fqdn or not iface_name:
+            msg = f"FQDN and/or interface name missing in subscription {interface.owner_subscription_id}"
+            raise ValueError(msg)
+
+        NetboxClient().allocate_interface(device_name=fqdn, iface_name=iface_name)
 
 
 @step("Allocate interfaces in Netbox for side A")
-def netbox_allocate_side_a_interfaces(subscription: IptrunkProvisioning) -> None:
+def netbox_allocate_side_a_interfaces(subscription: IptrunkInactive) -> None:
     """Allocate the :term:`LAG` interfaces for the Nokia router on side A."""
     _allocate_interfaces_in_netbox(subscription.iptrunk.iptrunk_sides[0])
 
 
 @step("Allocate interfaces in Netbox for side B")
-def netbox_allocate_side_b_interfaces(subscription: IptrunkProvisioning) -> None:
+def netbox_allocate_side_b_interfaces(subscription: IptrunkInactive) -> None:
     """Allocate the :term:`LAG` interfaces for the Nokia router on side B."""
     _allocate_interfaces_in_netbox(subscription.iptrunk.iptrunk_sides[1])
 
diff --git a/gso/workflows/router/create_router.py b/gso/workflows/router/create_router.py
index af68b2d3..29a60dbb 100644
--- a/gso/workflows/router/create_router.py
+++ b/gso/workflows/router/create_router.py
@@ -19,7 +19,7 @@ from gso.products.product_blocks.router import (
     RouterVendor,
     generate_fqdn,
 )
-from gso.products.product_types.router import RouterInactive, RouterProvisioning
+from gso.products.product_types.router import RouterInactive
 from gso.products.product_types.site import Site
 from gso.services import infoblox, subscriptions
 from gso.services.crm import get_customer_by_name
@@ -127,17 +127,21 @@ def ipam_allocate_loopback(subscription: RouterInactive) -> State:
 
 
 @step("Create NetBox Device")
-def create_netbox_device(subscription: RouterProvisioning) -> State:
+def create_netbox_device(subscription: RouterInactive) -> State:
     """Create a new NOKIA device in Netbox."""
-    NetboxClient().create_device(
-        subscription.router.router_fqdn,
-        str(subscription.router.router_site.site_tier),
-    )
+    fqdn = subscription.router.router_fqdn
+    site_tier = subscription.router.router_site.site_tier if subscription.router.router_site else None
+    if not fqdn or not site_tier:
+        msg = f"FQDN and/or Site tier missing in router subscription {subscription.subscription_id}!"
+        raise ValueError(msg)
+
+    NetboxClient().create_device(fqdn, site_tier)
+
     return {"subscription": subscription}
 
 
 @step("Verify IPAM resources for loopback interface")
-def verify_ipam_loopback(subscription: RouterProvisioning) -> State:
+def verify_ipam_loopback(subscription: RouterInactive) -> State:
     """Validate the :term:`IPAM` resources for the loopback interface."""
     host_record = infoblox.find_host_by_fqdn(f"lo0.{subscription.router.router_fqdn}")
     if not host_record or str(subscription.subscription_id) not in host_record.comment:
@@ -147,17 +151,21 @@ def verify_ipam_loopback(subscription: RouterProvisioning) -> State:
 
 
 @inputstep("Prompt to reboot", assignee=Assignee.SYSTEM)
-def prompt_reboot_router(subscription: RouterProvisioning) -> FormGenerator:
+def prompt_reboot_router(subscription: RouterInactive) -> FormGenerator:
     """Wait for confirmation from an operator that the router has been rebooted."""
 
     class RebootPrompt(FormPage):
         class Config:
             title = "Please reboot before continuing"
 
-        info_label_1: Label = (
-            f"Base config has been deployed. Please log in via the console using https://"  # type: ignore[assignment]
-            f"{subscription.router.router_site.site_ts_address}."
-        )
+        if subscription.router.router_site and subscription.router.router_site.site_ts_address:
+            info_label_1: Label = (
+                f"Base config has been deployed. Please log in via the console using https://"  # type: ignore[assignment]
+                f"{subscription.router.router_site.site_ts_address}."
+            )
+        else:
+            info_label_1 = "Base config has been deployed. Please log in via the console."  # type: ignore[assignment]
+
         info_label_2: Label = "Reboot the router, and once it is up again, press submit to continue the workflow."  # type: ignore[assignment]
 
     yield RebootPrompt
-- 
GitLab