From 83e3b12ab4564795ee6dfd1de5c4ac5b2980a107 Mon Sep 17 00:00:00 2001 From: Karel van Klink <karel.vanklink@geant.org> Date: Wed, 3 Jan 2024 12:10:55 +0100 Subject: [PATCH] use conditionals and reformat some code in modify iptrunk interface workflow --- .../iptrunk/modify_trunk_interface.py | 182 +++++++++++------- 1 file changed, 115 insertions(+), 67 deletions(-) diff --git a/gso/workflows/iptrunk/modify_trunk_interface.py b/gso/workflows/iptrunk/modify_trunk_interface.py index 57076988..8c58be5b 100644 --- a/gso/workflows/iptrunk/modify_trunk_interface.py +++ b/gso/workflows/iptrunk/modify_trunk_interface.py @@ -2,14 +2,14 @@ import ipaddress import json -from uuid import uuid4 +from uuid import UUID, uuid4 from orchestrator.forms import FormPage, ReadOnlyField from orchestrator.forms.validators import UniqueConstrainedList from orchestrator.targets import Target from orchestrator.types import FormGenerator, State, UUIDstr from orchestrator.utils.json import json_dumps -from orchestrator.workflow import StepList, done, init, step, workflow +from orchestrator.workflow import StepList, conditional, done, init, 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 validator @@ -17,6 +17,7 @@ from pydantic_forms.validators import Label from gso.products.product_blocks.iptrunk import ( IptrunkInterfaceBlock, + IptrunkSideBlock, IptrunkType, PhyPortCapacity, ) @@ -160,22 +161,23 @@ def modify_iptrunk_subscription( ) -> State: """Modify the subscription in the service database, reflecting the changes to the newly selected interfaces.""" # Prepare the list of removed AE members - previous_ae_members = {} - removed_ae_members = {} - for side_index in range(2): - previous_ae_members[side_index] = [ + previous_ae_members = [ + [ { "interface_name": member.interface_name, "interface_description": member.interface_description, } - for member in subscription.iptrunk.iptrunk_sides[side_index].iptrunk_side_ae_members + for member in side.iptrunk_side_ae_members ] + for side in subscription.iptrunk.iptrunk_sides + ] + removed_ae_members = [] + for side_index in range(2): previous_members = previous_ae_members[side_index] current_members = side_a_ae_members if side_index == 0 else side_b_ae_members - removed_ae_members[side_index] = [ - ae_member for ae_member in previous_members if ae_member not in current_members - ] + removed_ae_members.append([ae_member for ae_member in previous_members if ae_member not in current_members]) + subscription.iptrunk.geant_s_sid = geant_s_sid subscription.iptrunk.iptrunk_description = iptrunk_description subscription.iptrunk.iptrunk_type = iptrunk_type @@ -267,67 +269,99 @@ def provision_ip_trunk_iface_real( return {"subscription": subscription} -@step("Update interfaces in Netbox. Reserving interfaces.") -def update_interfaces_in_netbox(subscription: Iptrunk, removed_ae_members: dict, previous_ae_members: dict) -> State: - """Update Netbox such that it contains the new interfaces.""" +def _netbox_update_interfaces( + subscription_id: UUID, + side_block: IptrunkSideBlock, + removed_ae_members: list[dict], + previous_ae_members: list[dict], +) -> None: nbclient = NetboxClient() - for index, side in enumerate(subscription.iptrunk.iptrunk_sides): - if get_router_vendor(side.iptrunk_side_node.owner_subscription_id) == RouterVendor.NOKIA: - lag_interface = side.iptrunk_side_ae_iface - router_name = side.iptrunk_side_node.router_fqdn - # Free removed interfaces - for member in removed_ae_members[str(index)]: - nbclient.free_interface(router_name, member["interface_name"]) - # Attach physical interfaces to :term:`LAG` - # Update interface description to subscription ID - # Reserve interfaces - for interface in side.iptrunk_side_ae_members: - if any( - ae_member.get("interface_name") == interface.interface_name - for ae_member in previous_ae_members[str(index)] - ): - continue - nbclient.attach_interface_to_lag( - device_name=side.iptrunk_side_node.router_fqdn, - lag_name=lag_interface, - iface_name=interface.interface_name, - description=str(subscription.subscription_id), - ) - nbclient.reserve_interface( - device_name=side.iptrunk_side_node.router_fqdn, - iface_name=interface.interface_name, - ) - return { - "subscription": subscription, - } + + # Free removed interfaces + for member in removed_ae_members: + nbclient.free_interface(side_block.iptrunk_side_node.router_fqdn, member["interface_name"]) + + # Attach physical interfaces to :term:`LAG` + # Update interface description to subscription ID + # Reserve interfaces + for interface in side_block.iptrunk_side_ae_members: + if any(ae_member["interface_name"] == interface.interface_name for ae_member in previous_ae_members): + continue + if side_block.iptrunk_side_ae_iface: + nbclient.attach_interface_to_lag( + device_name=side_block.iptrunk_side_node.router_fqdn, + lag_name=side_block.iptrunk_side_ae_iface, + iface_name=interface.interface_name, + description=str(subscription_id), + ) + + nbclient.reserve_interface( + device_name=side_block.iptrunk_side_node.router_fqdn, + iface_name=interface.interface_name, + ) + + +@step("Netbox: Reserve side A interfaces") +def netbox_update_interfaces_side_a( + subscription: Iptrunk, removed_ae_members: list[list[dict]], previous_ae_members: list[list[dict]] +) -> None: + """Update Netbox such that it contains the new interfaces on side A.""" + _netbox_update_interfaces( + subscription.subscription_id, + subscription.iptrunk.iptrunk_sides[0], + removed_ae_members[0], + previous_ae_members[0], + ) + + +@step("Netbox: Reserve side B interfaces") +def netbox_update_interfaces_side_b( + subscription: Iptrunk, removed_ae_members: list[list[dict]], previous_ae_members: list[list[dict]] +) -> None: + """Update Netbox such that it contains the new interfaces on side B.""" + _netbox_update_interfaces( + subscription.subscription_id, + subscription.iptrunk.iptrunk_sides[1], + removed_ae_members[1], + previous_ae_members[1], + ) -@step("Allocate interfaces in Netbox") -def allocate_interfaces_in_netbox(subscription: Iptrunk, previous_ae_members: dict) -> State: - """Allocate the :term:`LAG` interfaces in NetBox. +def _netbox_allocate_interfaces(side_block: IptrunkSideBlock, previous_ae_members: list[dict]) -> None: + nbclient = NetboxClient() - Attach the :term:`LAG` interfaces to the physical interfaces detach old ones from the :term:`LAG`. + for interface in side_block.iptrunk_side_ae_members: + if any(ae_member["interface_name"] == interface.interface_name for ae_member in previous_ae_members): + continue + nbclient.allocate_interface( + device_name=side_block.iptrunk_side_node.router_fqdn, + iface_name=interface.interface_name, + ) + + # detach the old interfaces from lag + if side_block.iptrunk_side_ae_iface: + nbclient.detach_interfaces_from_lag( + device_name=side_block.iptrunk_side_node.router_fqdn, + lag_name=side_block.iptrunk_side_ae_iface, + ) + + +@step("Netbox: Allocate side A interfaces") +def allocate_interfaces_in_netbox_side_a(subscription: Iptrunk, previous_ae_members: list[list[dict]]) -> None: + """Allocate the :term:`LAG` interfaces on side A in Netbox. + + Attach the :term:`LAG` interface to the physical interface detach old one from the :term:`LAG`. """ - for index, side in enumerate(subscription.iptrunk.iptrunk_sides): - nbclient = NetboxClient() - if get_router_vendor(side.iptrunk_side_node.owner_subscription_id) == RouterVendor.NOKIA: - for interface in side.iptrunk_side_ae_members: - if any( - ae_member.get("interface_name") == interface.interface_name - for ae_member in previous_ae_members[str(index)] - ): - continue - nbclient.allocate_interface( - device_name=side.iptrunk_side_node.router_fqdn, - iface_name=interface.interface_name, - ) - # detach the old interfaces from lag - nbclient.detach_interfaces_from_lag( - device_name=side.iptrunk_side_node.router_fqdn, - lag_name=side.iptrunk_side_ae_iface, - ) + _netbox_allocate_interfaces(subscription.iptrunk.iptrunk_sides[0], previous_ae_members[0]) - return {"subscription": subscription} + +@step("Netbox: Allocate side B interfaces") +def allocate_interfaces_in_netbox_side_b(subscription: Iptrunk, previous_ae_members: list[list[dict]]) -> None: + """Allocate the :term:`LAG` interface on side B in Netbox. + + Attach the :term:`LAG` interface to the physical interface detach old one from the :term:`LAG`. + """ + _netbox_allocate_interfaces(subscription.iptrunk.iptrunk_sides[1], previous_ae_members[1]) @workflow( @@ -343,15 +377,29 @@ def modify_trunk_interface() -> StepList: * Provision the updated version of the IP trunk, first as a dry run * Allocate the reserved interfaces in Netbox """ + side_a_is_nokia = conditional( + lambda state: get_router_vendor( + state["subscription"]["iptrunk"]["iptrunk_sides"][0]["iptrunk_side_node"]["owner_subscription_id"] + ) + == RouterVendor.NOKIA + ) + side_b_is_nokia = conditional( + lambda state: get_router_vendor( + state["subscription"]["iptrunk"]["iptrunk_sides"][1]["iptrunk_side_node"]["owner_subscription_id"] + ) + == RouterVendor.NOKIA + ) return ( init >> store_process_subscription(Target.MODIFY) >> unsync >> modify_iptrunk_subscription - >> update_interfaces_in_netbox + >> side_a_is_nokia(netbox_update_interfaces_side_a) + >> side_b_is_nokia(netbox_update_interfaces_side_b) >> pp_interaction(provision_ip_trunk_iface_dry) >> pp_interaction(provision_ip_trunk_iface_real) - >> allocate_interfaces_in_netbox + >> side_a_is_nokia(allocate_interfaces_in_netbox_side_a) + >> side_b_is_nokia(allocate_interfaces_in_netbox_side_b) >> resync >> done ) -- GitLab