diff --git a/gso/migrations/versions/2024-10-30_c6b82e23297c_add_bfd_to_sbp.py b/gso/migrations/versions/2024-10-30_c6b82e23297c_add_bfd_to_sbp.py new file mode 100644 index 0000000000000000000000000000000000000000..d8fc7e6126c7258454255d08194e38e612ec82a0 --- /dev/null +++ b/gso/migrations/versions/2024-10-30_c6b82e23297c_add_bfd_to_sbp.py @@ -0,0 +1,50 @@ +"""Add BFD to SBP. + +Revision ID: c6b82e23297c +Revises: 7412c5b7ebe4 +Create Date: 2024-10-30 14:39:30.047934 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = 'c6b82e23297c' +down_revision = '7412c5b7ebe4' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + conn = op.get_bind() + conn.execute(sa.text(""" +INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('ServiceBindingPort')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('bfd_enabled'))) + """)) + conn.execute(sa.text(""" +INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('ServiceBindingPort')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('bfd_interval'))) + """)) + conn.execute(sa.text(""" +INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('ServiceBindingPort')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('bfd_multiplier'))) + """)) + + +def downgrade() -> None: + conn = op.get_bind() + conn.execute(sa.text(""" +DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('ServiceBindingPort')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('bfd_enabled')) + """)) + conn.execute(sa.text(""" +DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('ServiceBindingPort'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('bfd_enabled')) + """)) + conn.execute(sa.text(""" +DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('ServiceBindingPort')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('bfd_interval')) + """)) + conn.execute(sa.text(""" +DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('ServiceBindingPort'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('bfd_interval')) + """)) + conn.execute(sa.text(""" +DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('ServiceBindingPort')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('bfd_multiplier')) + """)) + conn.execute(sa.text(""" +DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('ServiceBindingPort'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('bfd_multiplier')) + """)) diff --git a/gso/products/product_blocks/service_binding_port.py b/gso/products/product_blocks/service_binding_port.py index bb90fcc15b0d5b634ad12dba5d048acdd928b520..19452b2e8661d7c0243e7b74aefcddf0f22d6810 100644 --- a/gso/products/product_blocks/service_binding_port.py +++ b/gso/products/product_blocks/service_binding_port.py @@ -30,6 +30,9 @@ class ServiceBindingPortInactive( geant_sid: str | None = None bgp_session_list: list[BGPSessionInactive] = Field(default_factory=list) edge_port: EdgePortBlockInactive | None = None + bfd_enabled: bool = False + bfd_interval: int | None = None + bfd_multiplier: int | None = None class ServiceBindingPortProvisioning(ServiceBindingPortInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): @@ -46,6 +49,9 @@ class ServiceBindingPortProvisioning(ServiceBindingPortInactive, lifecycle=[Subs geant_sid: str bgp_session_list: list[BGPSessionProvisioning] # type: ignore[assignment] edge_port: EdgePortBlockProvisioning + bfd_enabled: bool + bfd_interval: int | None + bfd_multiplier: int | None class ServiceBindingPort(ServiceBindingPortProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): @@ -73,3 +79,9 @@ class ServiceBindingPort(ServiceBindingPortProvisioning, lifecycle=[Subscription bgp_session_list: list[BGPSession] # type: ignore[assignment] #: The Edge Port on which this :term:`SBP` resides. edge_port: EdgePortBlock + #: Whether :term:`BFD` is enabled. + bfd_enabled: bool + #: The :term:`BFD` interval, if enabled. + bfd_interval: int | None + #: The :term:`BFD` multiplier, if enabled. + bfd_multiplier: int | None diff --git a/gso/workflows/nren_l3_core_service/create_nren_l3_core_service.py b/gso/workflows/nren_l3_core_service/create_nren_l3_core_service.py index be55ae6aa8c8b4663c7f9b8e924d435a000cab95..5c3d2a92c9860b89b46e00c67db6ff9f14df9d8e 100644 --- a/gso/workflows/nren_l3_core_service/create_nren_l3_core_service.py +++ b/gso/workflows/nren_l3_core_service/create_nren_l3_core_service.py @@ -105,6 +105,9 @@ def initial_input_form_generator(product_name: str) -> FormGenerator: geant_sid: str is_tagged: bool = False + bfd_enabled: bool = False + bfd_interval: int | None = None + bfd_multiplier: int | None = None vlan_id: VLAN_ID ipv4_address: IPv4AddressType ipv4_mask: IPV4Netmask diff --git a/gso/workflows/nren_l3_core_service/modify_nren_l3_core_service.py b/gso/workflows/nren_l3_core_service/modify_nren_l3_core_service.py index afb7d69bd214f91c8a70b2b3df682cbe163017a5..1d092aafeeee0cf8e4a17e38083156df47692c3a 100644 --- a/gso/workflows/nren_l3_core_service/modify_nren_l3_core_service.py +++ b/gso/workflows/nren_l3_core_service/modify_nren_l3_core_service.py @@ -136,7 +136,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: geant_sid: str = current_sbp.geant_sid is_tagged: bool = current_sbp.is_tagged - # The SBP model doesn't require these three fields, but in the case of GÉANT IP OR IAS this will never + # The SBP model doesn't require these five fields, but in the case of GÉANT IP OR IAS this will never # occur since it's a layer 3 service. The ignore statements are there to put our type checker at ease. vlan_id: VLAN_ID = current_sbp.vlan_id # type: ignore[assignment] ipv4_address: IPv4AddressType = current_sbp.ipv4_address # type: ignore[assignment] @@ -144,6 +144,9 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: ipv6_address: IPv6AddressType = current_sbp.ipv6_address # type: ignore[assignment] ipv6_mask: IPV6Netmask = current_sbp.ipv6_mask # type: ignore[assignment] custom_firewall_filters: bool = current_sbp.custom_firewall_filters + bfd_enabled: bool = current_sbp.bfd_enabled + bfd_interval: int | None = current_sbp.bfd_interval + bfd_multiplier: int | None = current_sbp.bfd_multiplier divider: Divider = Field(None, exclude=True) v4_bgp_peer: IPv4BGPPeer = IPv4BGPPeer( **v4_peer.model_dump(exclude=set("families")), @@ -188,6 +191,9 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: ipv4_address: IPv4AddressType ipv6_address: IPv6AddressType custom_firewall_filters: bool = False + bfd_enabled: bool = False + bfd_interval: int | None = None + bfd_multiplier: int | None = None divider: Divider = Field(None, exclude=True) v4_bgp_peer: IPv4BGPPeer v6_bgp_peer: IPv6BGPPeer @@ -248,6 +254,9 @@ def modify_existing_sbp_blocks(subscription: NRENL3CoreService, modified_sbp_lis current_sbp.ipv4_address = modified_sbp_data["ipv4_address"] current_sbp.ipv6_address = modified_sbp_data["ipv6_address"] current_sbp.custom_firewall_filters = modified_sbp_data["custom_firewall_filters"] + current_sbp.bfd_enabled = modified_sbp_data["bfd_enabled"] + current_sbp.bfd_interval = modified_sbp_data["bfd_interval"] + current_sbp.bfd_multiplier = modified_sbp_data["bfd_multiplier"] access_port.ap_type = modified_sbp_data["new_ap_type"] return {"subscription": subscription} diff --git a/test/fixtures/nren_l3_core_service_fixtures.py b/test/fixtures/nren_l3_core_service_fixtures.py index d0fd069a03b4b969e2fd286aaa7f57f5716f10c7..b3622cfda49c9c13ae493a0eeadedd5e9ebd15b6 100644 --- a/test/fixtures/nren_l3_core_service_fixtures.py +++ b/test/fixtures/nren_l3_core_service_fixtures.py @@ -69,7 +69,10 @@ def service_binding_port_factory(faker, bgp_session_subscription_factory, edge_p ipv6_mask: int | None = None, vlan_id: int | None = None, edge_port: EdgePort | None = None, + bfd_interval: int = 2, + bfd_multiplier: int = 2, *, + bfd_enabled: bool = False, custom_firewall_filters: bool = False, is_tagged: bool = False, ): @@ -90,6 +93,9 @@ def service_binding_port_factory(faker, bgp_session_subscription_factory, edge_p bgp_session_subscription_factory(families=[IPFamily.V6UNICAST], peer_address=faker.ipv6()), ], edge_port=edge_port or EdgePort.from_subscription(edge_port_subscription_factory()).edge_port, + bfd_enabled=bfd_enabled, + bfd_interval=bfd_interval, + bfd_multiplier=bfd_multiplier, ) return create_service_binding_port diff --git a/test/workflows/nren_l3_core_service/test_create_imported_nren_l3_core_service.py b/test/workflows/nren_l3_core_service/test_create_imported_nren_l3_core_service.py index 63aab373915f348d3a8e8e7f7bf161c2e1e5c190..bcc0c068580e27d92cbfc1432f2e89b9c3ca1510 100644 --- a/test/workflows/nren_l3_core_service/test_create_imported_nren_l3_core_service.py +++ b/test/workflows/nren_l3_core_service/test_create_imported_nren_l3_core_service.py @@ -27,9 +27,12 @@ def test_create_imported_nren_l3_core_service_success( "ipv6_address": faker.ipv6(), "ipv6_mask": faker.ipv6_netmask(), "custom_firewall_filters": faker.boolean(), + "bfd_enabled": True, + "bfd_interval": faker.pyint(), + "bfd_multiplier": faker.pyint(), "bgp_peers": [ { - "bfd_enabled": faker.boolean(), + "bfd_enabled": True, "bfd_interval": faker.pyint(), "bfd_multiplier": faker.pyint(), "has_custom_policies": faker.boolean(), diff --git a/test/workflows/nren_l3_core_service/test_create_nren_l3_core_service.py b/test/workflows/nren_l3_core_service/test_create_nren_l3_core_service.py index d8eac84e705567a8ef1e5fb0add47457607751f3..cd8d123a1e9a99de14be55a988a603a14bdc26dc 100644 --- a/test/workflows/nren_l3_core_service/test_create_nren_l3_core_service.py +++ b/test/workflows/nren_l3_core_service/test_create_nren_l3_core_service.py @@ -57,6 +57,9 @@ def test_create_nren_l3_core_service_success( "ipv6_address": faker.ipv6(), "ipv6_mask": faker.ipv6_netmask(), "custom_firewall_filters": faker.boolean(), + "bfd_enabled": True, + "bfd_interval": faker.pyint(), + "bfd_multiplier": faker.pyint(), "v4_bgp_peer": base_bgp_peer_input() | {"add_v4_multicast": faker.boolean(), "peer_address": faker.ipv4()}, "v6_bgp_peer": base_bgp_peer_input() | {"add_v6_multicast": faker.boolean(), "peer_address": faker.ipv6()}, }, diff --git a/test/workflows/nren_l3_core_service/test_modify_nren_l3_core_service.py b/test/workflows/nren_l3_core_service/test_modify_nren_l3_core_service.py index 197be36c55baf085b5d624bd9bcee8946c7c157e..d297649405a7d293b801d0ea88348d1d49a50669 100644 --- a/test/workflows/nren_l3_core_service/test_modify_nren_l3_core_service.py +++ b/test/workflows/nren_l3_core_service/test_modify_nren_l3_core_service.py @@ -102,6 +102,9 @@ def sbp_input_form_data(faker): "ipv4_address": faker.ipv4(), "ipv6_address": faker.ipv6(), "custom_firewall_filters": True, + "bfd_enabled": True, + "bfd_interval": faker.pyint(), + "bfd_multiplier": faker.pyint(), "v4_bgp_peer": { "bfd_enabled": True, "bfd_interval": faker.pyint(), @@ -262,3 +265,6 @@ def test_modify_nren_l3_core_service_modify_edge_port_success( ) == new_sbp_data[i]["v6_bgp_peer"]["add_v6_multicast"] ) + assert subscription.nren_l3_core_service.nren_ap_list[i].sbp.bfd_enabled == new_sbp_data[i]["bfd_enabled"] + assert subscription.nren_l3_core_service.nren_ap_list[i].sbp.bfd_interval == new_sbp_data[i]["bfd_interval"] + assert subscription.nren_l3_core_service.nren_ap_list[i].sbp.bfd_multiplier == new_sbp_data[i]["bfd_multiplier"]