From b4fedb4f76eab4e90675c3ff58c75026fad5ce8c Mon Sep 17 00:00:00 2001
From: Karel van Klink <karel.vanklink@geant.org>
Date: Fri, 20 Sep 2024 15:49:14 +0200
Subject: [PATCH] Update input form and subscription model creation for geant
 ip workflow

---
 gso/utils/helpers.py                        |  2 +-
 gso/workflows/edge_port/create_edge_port.py |  2 +-
 gso/workflows/geant_ip/create_geant_ip.py   | 99 ++++++++++++---------
 3 files changed, 61 insertions(+), 42 deletions(-)

diff --git a/gso/utils/helpers.py b/gso/utils/helpers.py
index 18d7674e..e3dda41d 100644
--- a/gso/utils/helpers.py
+++ b/gso/utils/helpers.py
@@ -214,7 +214,7 @@ def active_edge_port_selector(*, geant_only: bool | None = None) -> Choice:
             edge_port_subscriptions,
         )
 
-    edge_port_subscriptions = {port["subscription_id"]: port["description"] for port in edge_port_subscriptions}
+    edge_port_subscriptions = {str(port["subscription_id"]): port["description"] for port in edge_port_subscriptions}
 
     return Choice(
         "Select an Edge Port", zip(edge_port_subscriptions.keys(), edge_port_subscriptions.items(), strict=True)
diff --git a/gso/workflows/edge_port/create_edge_port.py b/gso/workflows/edge_port/create_edge_port.py
index 312461a0..fdf1e760 100644
--- a/gso/workflows/edge_port/create_edge_port.py
+++ b/gso/workflows/edge_port/create_edge_port.py
@@ -49,7 +49,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
         node: router_enum  # type: ignore[valid-type]
         partner: partner_choice()  # type: ignore[valid-type]
         service_type: EdgePortType
-        enable_lacp: bool
+        enable_lacp: bool = False
         speed: PhysicalPortCapacity
         encapsulation: EncapsulationType = EncapsulationType.DOT1Q
         number_of_members: int
diff --git a/gso/workflows/geant_ip/create_geant_ip.py b/gso/workflows/geant_ip/create_geant_ip.py
index cc01eee1..fdaaf38e 100644
--- a/gso/workflows/geant_ip/create_geant_ip.py
+++ b/gso/workflows/geant_ip/create_geant_ip.py
@@ -11,7 +11,7 @@ from orchestrator.utils.errors import ProcessFailureError
 from orchestrator.workflow import StepList, begin, done, step, workflow
 from orchestrator.workflows.steps import resync, set_status, store_process_subscription
 from orchestrator.workflows.utils import wrap_create_initial_input_form
-from pydantic import AfterValidator, BaseModel, ConfigDict, Field
+from pydantic import AfterValidator, BaseModel, ConfigDict, PositiveInt
 from pydantic_forms.validators import Divider, validate_unique_list
 
 from gso.products.product_blocks.bgp_session import BGPSession, IPFamily
@@ -20,13 +20,12 @@ from gso.products.product_blocks.service_binding_port import VLAN_ID, ServiceBin
 from gso.products.product_types.edge_port import EdgePort
 from gso.products.product_types.geant_ip import GeantIPInactive
 from gso.services.lso_client import execute_playbook, lso_interaction
-from gso.services.partners import get_partner_by_name
 from gso.utils.helpers import (
     active_edge_port_selector,
     partner_choice,
 )
 from gso.utils.shared_enums import APType, SBPType
-from gso.utils.types.ip_address import IPAddress, IPv4AddressType, IPv6AddressType
+from gso.utils.types.ip_address import IPv4AddressType, IPv6AddressType
 from gso.utils.types.tt_number import TTNumber
 
 
@@ -45,67 +44,87 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
         edge_port: active_edge_port_selector()
         ap_type: APType
 
+        def __hash__(self):
+            return self.edge_port.__hash__()
+
     class EdgePortSelectionForm(FormPage):
         model_config = ConfigDict(title=f"{product_name} - Select Edge Ports")
         info_label: Label = "Please select the Edge Ports where this GÉANT IP service will terminate"
 
-        edge_ports: list[EdgePortSelection] = Field(default_factory=list)
+        edge_ports: Annotated[list[EdgePortSelection], AfterValidator(validate_unique_list)]
 
     selected_edge_ports = yield EdgePortSelectionForm
-    ep_list = selected_edge_ports.edge_port_pair_list
+    ep_list = selected_edge_ports.edge_ports
     total_ep_count = len(ep_list)
     current_ep_index = 0
 
-    class BGPPeer(BaseModel):
-        peer_address: IPAddress
-        bfd_enabled: bool
-        bfd_interval: int | None = None
-        bfd_multiplier: int | None = None
-        families: Annotated[list[IPFamily], AfterValidator(validate_unique_list)]
-        has_custom_policies: bool
-        authentication_key: str
-        multipath_enabled: bool
-        send_default_route: bool
-        is_multi_hop: bool
-
-    class BindingPortsInputForm(FormPage):
-        model_config = ConfigDict(
-            title=f"{product_name} - Configure Service Binding Ports ({current_ep_index + 1}/{total_ep_count})"
-        )
-        info_label: Label = "Please configure the Service Binding Ports for each Edge Port."
-        current_ep_label: Label = f'Currently configuring Edge Port: "{ep_list[current_ep_index]["description"]}"'
-
-        geant_sid: str
-        is_tagged: bool
-        sbp_type: SBPType
-        vlan_id: VLAN_ID | None
-        ipv4_address: IPv4AddressType | None = None
-        ipv6_address: IPv6AddressType | None = None
-        custom_firewall_filters: bool
-        divider: Divider
-        bgp_peers: list[BGPPeer] = Field(default_factory=list)
-
     binding_port_inputs = []
     while current_ep_index < total_ep_count:
+        current_edge_port_description = EdgePort.from_subscription(ep_list[current_ep_index].edge_port).description
+
+        class BindingPortsInputForm(FormPage):
+            model_config = ConfigDict(
+                title=f"{product_name} - Configure Service Binding Ports ({current_ep_index + 1}/{total_ep_count})"
+            )
+            info_label: Label = "Please configure the Service Binding Ports for each Edge Port."
+            current_ep_label: Label = f"Currently configuring on {current_edge_port_description}"
+
+            geant_sid: str
+            is_tagged: bool = False
+            sbp_type: SBPType
+            vlan_id: VLAN_ID | None
+            ipv4_address: IPv4AddressType | None = None
+            ipv6_address: IPv6AddressType | None = None
+            custom_firewall_filters: bool = False
+            divider: Divider
+            bgp_peer_count: PositiveInt = 1
+
         binding_port_input_form = yield BindingPortsInputForm
-        binding_port_inputs.append(
-            binding_port_input_form.model_dump(exclude=set("info_label" "current_ep_label" "divider"))
+        binding_port_input = binding_port_input_form.model_dump(
+            exclude=set("info_label" "current_ep_label" "divider" "bgp_peer_count")
         )
+
+        bgp_peers = []
+        for bgp_peer_index in range(binding_port_input_form.bgp_peer_count):
+
+            class BGPPeerForm(FormPage):
+                model_config = ConfigDict(
+                    title=(
+                        f"Configure BGP peers for {current_edge_port_description} - "
+                        f"({bgp_peer_index + 1}/{binding_port_input_form.bgp_peer_count})"
+                    )
+                )
+                peer_address: IPv4AddressType | IPv6AddressType
+                bfd_enabled: bool = False
+                bfd_interval: int | None = None
+                bfd_multiplier: int | None = None
+                families: Annotated[list[IPFamily], AfterValidator(validate_unique_list)]
+                has_custom_policies: bool = False
+                authentication_key: str
+                multipath_enabled: bool = False
+                send_default_route: bool = False
+                is_multi_hop: bool = False
+
+            bgp_peer_input_form = yield BGPPeerForm
+            bgp_peers.append(bgp_peer_input_form.model_dump())
+
+        binding_port_input["bgp_peers"] = bgp_peers
+        binding_port_inputs.append(binding_port_input)
         current_ep_index += 1
 
     return (
         initial_user_input.model_dump()
         | selected_edge_ports.model_dump(exclude=set("info_label"))
-        | binding_port_inputs
+        | {"binding_port_inputs": binding_port_inputs}
     )
 
 
 @step("Create subscription")
 def create_subscription(product: UUIDstr, partner: str) -> State:
     """Create a new subscription object in the database."""
-    subscription = GeantIPInactive.from_product_id(product, get_partner_by_name(partner)["partner_id"])
+    subscription = GeantIPInactive.from_product_id(product, partner)
 
-    return {"subscription": subscription}
+    return {"subscription": subscription, "subscription_id": subscription.subscription_id}
 
 
 @step("Initialize subscription")
@@ -120,7 +139,7 @@ def initialize_subscription(
             NRENAccessPortInactive.new(
                 subscription_id=uuid4(),
                 nren_ap_type=edge_port_input["ap_type"],
-                geant_ip_ep=edge_port_subscription.subscription_id,
+                geant_ip_ep=edge_port_subscription.edge_port,
             )
         )
         sbp_bgp_session_list = [
-- 
GitLab