diff --git a/gso/products/product_blocks/iptrunk.py b/gso/products/product_blocks/iptrunk.py index 4a726c4117af2db83889d5e4b80d748d47538088..cac186262f641fc52fdc62a4e4c556c7d924f22b 100644 --- a/gso/products/product_blocks/iptrunk.py +++ b/gso/products/product_blocks/iptrunk.py @@ -55,14 +55,14 @@ class IptrunkInterfaceBlockProvisioning(IptrunkInterfaceBlockInactive, lifecycle """An IP trunk interface that is being provisioned.""" interface_name: str - interface_description: str + interface_description: str | None = None class IptrunkInterfaceBlock(IptrunkInterfaceBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): """An active IP trunk interface.""" interface_name: str - interface_description: str + interface_description: str | None = None class IptrunkSides(UniqueConstrainedList[T_co]): # type: ignore[type-var] diff --git a/gso/utils/helpers.py b/gso/utils/helpers.py index 6c30324ed0b81064bdc4c84e862f1a0ff671b9da..0241e836ef8c9db3bcc750b357e03c65d291914d 100644 --- a/gso/utils/helpers.py +++ b/gso/utils/helpers.py @@ -25,7 +25,7 @@ class LAGMember(BaseModel): """A :term:`LAG` member interface that consists of a name and description.""" interface_name: str - interface_description: str + interface_description: str | None def __hash__(self) -> int: """Calculate the hash based on the interface name and description, so that uniqueness can be determined.""" diff --git a/gso/workflows/iptrunk/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py index 5d02b13ae275f720af9157fa168348a77e49d5ef..fed3ab25b20b27a205481ca9fc685528670284d3 100644 --- a/gso/workflows/iptrunk/create_iptrunk.py +++ b/gso/workflows/iptrunk/create_iptrunk.py @@ -61,7 +61,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator: iptrunk_description: str iptrunk_type: IptrunkType iptrunk_speed: PhysicalPortCapacity - iptrunk_minimum_links: int + iptrunk_number_of_members: int @validator("tt_number", allow_reuse=True) def validate_tt_number(cls, tt_number: str) -> str: @@ -69,6 +69,13 @@ def initial_input_form_generator(product_name: str) -> FormGenerator: initial_user_input = yield CreateIptrunkForm + class VerifyMinimumLinksForm(FormPage): + info_label: Label = ( + f"This is the calculated minimum-links for this LAG: " f"{initial_user_input.iptrunk_number_of_members - 1}" # type: ignore[assignment] + ) + info_label2: Label = "Please confirm or modify." # type: ignore[assignment] + + yield VerifyMinimumLinksForm router_enum_a = Choice("Select a router", zip(routers.keys(), routers.items(), strict=True)) # type: ignore[arg-type] class SelectRouterSideA(FormPage): @@ -86,7 +93,8 @@ def initial_input_form_generator(product_name: str) -> FormGenerator: router_a_fqdn = Router.from_subscription(router_a).router.router_fqdn class JuniperAeMembers(UniqueConstrainedList[LAGMember]): - min_items = initial_user_input.iptrunk_minimum_links + min_items = initial_user_input.iptrunk_number_of_members + max_items = initial_user_input.iptrunk_number_of_members if get_router_vendor(router_a) == Vendor.NOKIA: @@ -97,7 +105,8 @@ def initial_input_form_generator(product_name: str) -> FormGenerator: ) class NokiaAeMembersA(UniqueConstrainedList[NokiaLAGMemberA]): - min_items = initial_user_input.iptrunk_minimum_links + min_items = initial_user_input.iptrunk_number_of_members + max_items = initial_user_input.iptrunk_number_of_members ae_members_side_a = NokiaAeMembersA else: @@ -213,7 +222,7 @@ def initialize_subscription( iptrunk_type: IptrunkType, iptrunk_description: str, iptrunk_speed: PhysicalPortCapacity, - iptrunk_minimum_links: int, + iptrunk_number_of_members: int, side_a_node_id: str, side_a_ae_iface: str, side_a_ae_geant_a_sid: str | None, @@ -232,7 +241,7 @@ def initialize_subscription( subscription.iptrunk.iptrunk_type = iptrunk_type subscription.iptrunk.iptrunk_speed = iptrunk_speed subscription.iptrunk.iptrunk_isis_metric = oss_params.GENERAL.isis_high_metric - subscription.iptrunk.iptrunk_minimum_links = iptrunk_minimum_links + subscription.iptrunk.iptrunk_minimum_links = iptrunk_number_of_members - 1 subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node = side_a subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_iface = side_a_ae_iface diff --git a/gso/workflows/iptrunk/modify_trunk_interface.py b/gso/workflows/iptrunk/modify_trunk_interface.py index 816ff706b1fe28c4aff885f458c3904be4ab3c7c..27111c62cbba9a1a92c173a035a3df163ac29d3f 100644 --- a/gso/workflows/iptrunk/modify_trunk_interface.py +++ b/gso/workflows/iptrunk/modify_trunk_interface.py @@ -40,7 +40,7 @@ def initialize_ae_members(subscription: Iptrunk, initial_user_input: dict, side_ """Initialize the list of AE members.""" router = subscription.iptrunk.iptrunk_sides[side_index].iptrunk_side_node router_vendor = get_router_vendor(router.owner_subscription_id) - iptrunk_minimum_link = initial_user_input["iptrunk_minimum_links"] + iptrunk_number_of_members = initial_user_input["iptrunk_number_of_members"] if router_vendor == Vendor.NOKIA: iptrunk_speed = initial_user_input["iptrunk_speed"] @@ -61,13 +61,15 @@ def initialize_ae_members(subscription: Iptrunk, initial_user_input: dict, side_ ) class NokiaAeMembers(UniqueConstrainedList[NokiaLAGMember]): - min_items = iptrunk_minimum_link + min_items = iptrunk_number_of_members + max_items = iptrunk_number_of_members ae_members = NokiaAeMembers else: class JuniperAeMembers(UniqueConstrainedList[LAGMember]): - min_items = iptrunk_minimum_link + min_items = iptrunk_number_of_members + max_items = iptrunk_number_of_members ae_members = JuniperAeMembers # type: ignore[assignment] return ae_members # type: ignore[return-value] @@ -87,7 +89,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: "You will need to add the new AE members in the next steps." # type: ignore[assignment] ) iptrunk_speed: PhysicalPortCapacity = subscription.iptrunk.iptrunk_speed - iptrunk_minimum_links: int = subscription.iptrunk.iptrunk_minimum_links + iptrunk_number_of_members: int = subscription.iptrunk.iptrunk_minimum_links + 1 iptrunk_isis_metric: int = ReadOnlyField(subscription.iptrunk.iptrunk_isis_metric) iptrunk_ipv4_network: ipaddress.IPv4Network = ReadOnlyField(subscription.iptrunk.iptrunk_ipv4_network) iptrunk_ipv6_network: ipaddress.IPv6Network = ReadOnlyField(subscription.iptrunk.iptrunk_ipv6_network) @@ -97,6 +99,14 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: return validate_tt_number(tt_number) initial_user_input = yield ModifyIptrunkForm + + class VerifyMinimumLinksForm(FormPage): + info_label: Label = ( + f"This is the calculated minimum-links for this LAG: " f"{initial_user_input.iptrunk_number_of_members - 1}" # type: ignore[assignment] + ) + info_label2: Label = "Please confirm or modify." # type: ignore[assignment] + + yield VerifyMinimumLinksForm ae_members_side_a = initialize_ae_members(subscription, initial_user_input.dict(), 0) class ModifyIptrunkSideAForm(FormPage): @@ -158,7 +168,7 @@ def modify_iptrunk_subscription( iptrunk_type: IptrunkType, iptrunk_description: str, iptrunk_speed: PhysicalPortCapacity, - iptrunk_minimum_links: int, + iptrunk_number_of_members: int, side_a_ae_geant_a_sid: str | None, side_a_ae_members: list[dict], side_b_ae_geant_a_sid: str | None, @@ -187,7 +197,7 @@ def modify_iptrunk_subscription( subscription.iptrunk.iptrunk_description = iptrunk_description subscription.iptrunk.iptrunk_type = iptrunk_type subscription.iptrunk.iptrunk_speed = iptrunk_speed - subscription.iptrunk.iptrunk_minimum_links = iptrunk_minimum_links + subscription.iptrunk.iptrunk_minimum_links = iptrunk_number_of_members - 1 subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_geant_a_sid = side_a_ae_geant_a_sid # Flush the old list of member interfaces diff --git a/test/workflows/iptrunk/test_create_iptrunk.py b/test/workflows/iptrunk/test_create_iptrunk.py index a883f0160f3a1f023dcea5e462e354f54c9e13fb..4163f6835166d368cbda5a007d24f7043bd8e7f4 100644 --- a/test/workflows/iptrunk/test_create_iptrunk.py +++ b/test/workflows/iptrunk/test_create_iptrunk.py @@ -52,12 +52,12 @@ def input_form_wizard_data(request, juniper_router_subscription_factory, nokia_r # Set side b router to Juniper if vendor == Vendor.JUNIPER: router_side_b = juniper_router_subscription_factory() - side_b_members = faker.link_members_juniper() + side_b_members = faker.link_members_juniper()[0:2] else: router_side_b = nokia_router_subscription_factory() side_b_members = [ LAGMember(interface_name=f"Interface{interface}", interface_description=faker.sentence()) - for interface in range(5) + for interface in range(2) ] create_ip_trunk_step = { @@ -66,8 +66,9 @@ def input_form_wizard_data(request, juniper_router_subscription_factory, nokia_r "iptrunk_type": IptrunkType.DARK_FIBER, "iptrunk_description": faker.sentence(), "iptrunk_speed": PhysicalPortCapacity.HUNDRED_GIGABIT_PER_SECOND, - "iptrunk_minimum_links": 2, + "iptrunk_number_of_members": 2, } + create_ip_trunk_confirm_step = {} create_ip_trunk_side_a_router_name = {"side_a_node_id": router_side_a} create_ip_trunk_side_a_step = { "side_a_ae_iface": "lag-1", @@ -77,7 +78,7 @@ def input_form_wizard_data(request, juniper_router_subscription_factory, nokia_r interface_name=f"Interface{interface}", interface_description=faker.sentence(), ) - for interface in range(5) + for interface in range(2) ], } create_ip_trunk_side_b_router_name = {"side_b_node_id": router_side_b} @@ -89,6 +90,7 @@ def input_form_wizard_data(request, juniper_router_subscription_factory, nokia_r return [ create_ip_trunk_step, + create_ip_trunk_confirm_step, create_ip_trunk_side_a_router_name, create_ip_trunk_side_a_step, create_ip_trunk_side_b_router_name, diff --git a/test/workflows/iptrunk/test_modify_trunk_interface.py b/test/workflows/iptrunk/test_modify_trunk_interface.py index 4dca28e963ef862f182749a9ce7c1b25c418c4b7..18524cfd70ba8a61a12db319ff4cd1af921c64fe 100644 --- a/test/workflows/iptrunk/test_modify_trunk_interface.py +++ b/test/workflows/iptrunk/test_modify_trunk_interface.py @@ -29,26 +29,26 @@ def input_form_iptrunk_data( side_node = juniper_router_subscription_factory() side_a_node = iptrunk_side_subscription_factory(iptrunk_side_node=side_node) side_b_node = iptrunk_side_subscription_factory() - new_side_a_ae_members = faker.link_members_juniper() - new_side_b_ae_members = faker.link_members_nokia() + new_side_a_ae_members = faker.link_members_juniper()[0:2] + new_side_b_ae_members = faker.link_members_nokia()[0:2] elif use_juniper == UseJuniperSide.SIDE_B: side_node = juniper_router_subscription_factory() side_a_node = iptrunk_side_subscription_factory() side_b_node = iptrunk_side_subscription_factory(iptrunk_side_node=side_node) - new_side_a_ae_members = faker.link_members_nokia() - new_side_b_ae_members = faker.link_members_juniper() + new_side_a_ae_members = faker.link_members_nokia()[0:2] + new_side_b_ae_members = faker.link_members_juniper()[0:2] elif use_juniper == UseJuniperSide.SIDE_BOTH: side_node_1 = juniper_router_subscription_factory() side_node_2 = juniper_router_subscription_factory() side_a_node = iptrunk_side_subscription_factory(iptrunk_side_node=side_node_1) side_b_node = iptrunk_side_subscription_factory(iptrunk_side_node=side_node_2) - new_side_a_ae_members = faker.link_members_juniper() - new_side_b_ae_members = faker.link_members_juniper() + new_side_a_ae_members = faker.link_members_juniper()[0:2] + new_side_b_ae_members = faker.link_members_juniper()[0:2] else: side_a_node = iptrunk_side_subscription_factory() side_b_node = iptrunk_side_subscription_factory() - new_side_a_ae_members = faker.link_members_nokia() - new_side_b_ae_members = faker.link_members_nokia() + new_side_a_ae_members = faker.link_members_nokia()[0:2] + new_side_b_ae_members = faker.link_members_nokia()[0:2] product_id = iptrunk_subscription_factory(iptrunk_sides=[side_a_node, side_b_node]) @@ -70,8 +70,9 @@ def input_form_iptrunk_data( "iptrunk_description": new_description, "iptrunk_type": new_type, "iptrunk_speed": new_speed, - "iptrunk_minimum_links": new_link_count, + "iptrunk_number_of_members": new_link_count, }, + {}, { "side_a_ae_geant_a_sid": new_side_a_sid, "side_a_ae_members": new_side_a_ae_members, @@ -133,10 +134,10 @@ def test_iptrunk_modify_trunk_interface_success( assert mock_provision_ip_trunk.call_count == 2 # Assert all Netbox calls have been made new_sid = input_form_iptrunk_data[1]["geant_s_sid"] - new_side_a_sid = input_form_iptrunk_data[2]["side_a_ae_geant_a_sid"] - new_side_a_ae_members = input_form_iptrunk_data[2]["side_a_ae_members"] - new_side_b_sid = input_form_iptrunk_data[3]["side_b_ae_geant_a_sid"] - new_side_b_ae_members = input_form_iptrunk_data[3]["side_b_ae_members"] + new_side_a_sid = input_form_iptrunk_data[3]["side_a_ae_geant_a_sid"] + new_side_a_ae_members = input_form_iptrunk_data[3]["side_a_ae_members"] + new_side_b_sid = input_form_iptrunk_data[4]["side_b_ae_geant_a_sid"] + new_side_b_ae_members = input_form_iptrunk_data[4]["side_b_ae_members"] # Only Nokia interfaces are checked vendor_side_a = subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.vendor @@ -162,7 +163,7 @@ def test_iptrunk_modify_trunk_interface_success( assert subscription.iptrunk.iptrunk_description == input_form_iptrunk_data[1]["iptrunk_description"] assert subscription.iptrunk.iptrunk_type == input_form_iptrunk_data[1]["iptrunk_type"] assert subscription.iptrunk.iptrunk_speed == input_form_iptrunk_data[1]["iptrunk_speed"] - assert subscription.iptrunk.iptrunk_minimum_links == input_form_iptrunk_data[1]["iptrunk_minimum_links"] + assert subscription.iptrunk.iptrunk_minimum_links == input_form_iptrunk_data[1]["iptrunk_number_of_members"] - 1 assert subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_geant_a_sid == new_side_a_sid def _find_interface_by_name(interfaces: list[dict[str, str]], name: str):