From c2e26d403617d8a24ca442af090a8e8f9b836f44 Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Fri, 9 May 2025 14:07:04 +0200 Subject: [PATCH 01/24] IX port PB --- gso/products/product_blocks/ix_port.py | 67 ++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 gso/products/product_blocks/ix_port.py diff --git a/gso/products/product_blocks/ix_port.py b/gso/products/product_blocks/ix_port.py new file mode 100644 index 000000000..f9fec2e6b --- /dev/null +++ b/gso/products/product_blocks/ix_port.py @@ -0,0 +1,67 @@ +"""IX Port Product Blocks.""" + +from orchestrator.domain.base import ProductBlockModel +from orchestrator.types import SubscriptionLifecycle +from pydantic_forms.types import strEnum + +from gso.products.product_blocks.service_binding_port import ServiceBindingPortInactive, ServiceBindingPortProvisioning, \ + ServiceBindingPort + + +class SessionState(strEnum): + """Session state of the IX Port.""" + + PROVISIONING = "provisioning" + """Provisioning.""" + ACTIVE = "active" + """Active.""" + DISABLED = "disabled" + """Disabled.""" + + +class PeeringConnectionInactive(ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], + product_block_name="PeeringConnectionBlock"): + """A Peering Connection that's currently inactive. See `PeeringConnectionBlock`.""" + + sbp: ServiceBindingPortInactive + prefix_limit: int | None = None + + +class PeeringConnectionProvisioning(PeeringConnectionInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): + """A Peering Connection that's currently being provisioned. See `PeeringConnectionBlock`.""" + + sbp: ServiceBindingPortProvisioning + prefix_limit: int | None = None + + +class PeeringConnection(PeeringConnectionProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): + """A Peering Connection that's currently active.""" + + sbp: ServiceBindingPort + prefix_limit: int | None = None + + +class IXPortBlockInactive( + ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="IXPortBlock" +): + """An IX Port that's not yet provisioned. See ``IXPortBlock``.""" + + peering_connection: list[PeeringConnectionInactive] + minimum_hold_timer: int | None = None + session_state: SessionState | None = None + + +class IXPortBlockProvisioning(IXPortBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): + """An IX Port that's being provisioned. See ``IXPortBlock``.""" + + peering_connection: list[PeeringConnectionProvisioning] + minimum_hold_timer: int | None = None + session_state: SessionState | None = None + + +class IXPortBlock(IXPortBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): + """An Internet Exchange Port that's active.""" + + peering_connection: list[PeeringConnection] + minimum_hold_timer: int | None = None + session_state: SessionState -- GitLab From dad058285ffc5c8608e5d8d8c6801dfb5cedcc0b Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Fri, 9 May 2025 14:07:22 +0200 Subject: [PATCH 02/24] Private Peer Port PB --- .../product_blocks/private_peer_port.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 gso/products/product_blocks/private_peer_port.py diff --git a/gso/products/product_blocks/private_peer_port.py b/gso/products/product_blocks/private_peer_port.py new file mode 100644 index 000000000..9e8d8ac8f --- /dev/null +++ b/gso/products/product_blocks/private_peer_port.py @@ -0,0 +1,27 @@ +"""Private Peer Port Product Blocks.""" + +from orchestrator.domain.base import ProductBlockModel +from orchestrator.types import SubscriptionLifecycle + +from gso.products.product_blocks.ix_port import PeeringConnection, PeeringConnectionInactive, \ + PeeringConnectionProvisioning + + +class PrivatePeerPortBlockInactive( + ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="PrivatePeerPortBlock" +): + """An Private Peer Port that's not yet provisioned. See ``PrivatePeerPortBlock``.""" + + peering_connection: list[PeeringConnectionInactive] + + +class PrivatePeerPortBlockProvisioning(PrivatePeerPortBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): + """An Private Peer Port that's being provisioned. See ``PrivatePeerPortBlock``.""" + + peering_connection: list[PeeringConnectionProvisioning] + + +class PrivatePeerPortBlock(PrivatePeerPortBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): + """A Private Provider Port that's active.""" + + peering_connection: list[PeeringConnection] -- GitLab From df016c909da2d8791bb0d630e175523f01140d17 Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Fri, 9 May 2025 14:07:45 +0200 Subject: [PATCH 03/24] Transit Provider Port PB --- .../product_blocks/transit_provider_port.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 gso/products/product_blocks/transit_provider_port.py diff --git a/gso/products/product_blocks/transit_provider_port.py b/gso/products/product_blocks/transit_provider_port.py new file mode 100644 index 000000000..3c8ac0470 --- /dev/null +++ b/gso/products/product_blocks/transit_provider_port.py @@ -0,0 +1,27 @@ +"""Transit Provider Port Product Blocks.""" + +from orchestrator.domain.base import ProductBlockModel +from orchestrator.types import SubscriptionLifecycle + +from gso.products.product_blocks.ix_port import PeeringConnection, PeeringConnectionInactive, \ + PeeringConnectionProvisioning + + +class TransitProviderPortBlockInactive( + ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="TransitProviderPortBlock" +): + """A Transit Provider Port that's not yet provisioned. See ``TransitProviderPortBlock``.""" + + peering_connection: list[PeeringConnectionInactive] + + +class TransitProviderPortBlockProvisioning(TransitProviderPortBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): + """A Transit Provider Port that's being provisioned. See ``TransitProviderPortBlock``.""" + + peering_connection: list[PeeringConnectionProvisioning] + + +class TransitProviderPortBlock(TransitProviderPortBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): + """A Transit Provider Port that's active.""" + + peering_connection: list[PeeringConnection] -- GitLab From 18eb17d181f2ba3e288a6f3bad70d816593ea240 Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Fri, 9 May 2025 14:15:45 +0200 Subject: [PATCH 04/24] Add private peer port Product Type --- .../product_types/private_peer_port.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 gso/products/product_types/private_peer_port.py diff --git a/gso/products/product_types/private_peer_port.py b/gso/products/product_types/private_peer_port.py new file mode 100644 index 000000000..680a5dda9 --- /dev/null +++ b/gso/products/product_types/private_peer_port.py @@ -0,0 +1,42 @@ +"""Product type for Private Peer Port.""" + +from orchestrator.domain import SubscriptionModel +from orchestrator.types import SubscriptionLifecycle + +from gso.products.product_blocks.private_peer_port import ( + PrivatePeerPortBlock, + PrivatePeerPortBlockInactive, + PrivatePeerPortBlockProvisioning, +) + + +class PrivatePeerPortInactive(SubscriptionModel, is_base=True): + """A Private Peering Port product that is inactive.""" + + private_peer_port: PrivatePeerPortBlockInactive + + +class PrivatePeerPortProvisioning(PrivatePeerPortInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): + """A Private Peering Port product that is being provisioned.""" + + private_peer_port: PrivatePeerPortBlockProvisioning + + +class PrivatePeerPort(PrivatePeerPortProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): + """A Private Peering Port product that is active.""" + + private_peer_port: PrivatePeerPortBlock + + +class ImportedPrivatePeerPortInactive(SubscriptionModel, is_base=True): + """An imported Private Peering Port product that is inactive.""" + + private_peer_port: PrivatePeerPortBlockInactive + + +class ImportedPrivatePeerPort( + ImportedPrivatePeerPortInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING, SubscriptionLifecycle.ACTIVE] +): + """An imported Private Peering Port product that is active.""" + + private_peer_port: PrivatePeerPortBlock \ No newline at end of file -- GitLab From 148ca2b6410cd4e77df5b59174faa97df0d6121e Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Fri, 9 May 2025 14:15:58 +0200 Subject: [PATCH 05/24] Add IX port Product Type --- gso/products/product_types/ix_port.py | 42 +++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 gso/products/product_types/ix_port.py diff --git a/gso/products/product_types/ix_port.py b/gso/products/product_types/ix_port.py new file mode 100644 index 000000000..0a84bb6c2 --- /dev/null +++ b/gso/products/product_types/ix_port.py @@ -0,0 +1,42 @@ +"""Product type for IX Port.""" + +from orchestrator.domain import SubscriptionModel +from orchestrator.types import SubscriptionLifecycle + +from gso.products.product_blocks.ix_port import ( + IXPortBlock, + IXPortBlockInactive, + IXPortBlockProvisioning, +) + + +class IXPortInactive(SubscriptionModel, is_base=True): + """An IX Port product that is inactive.""" + + ix_port: IXPortBlockInactive + + +class IXPortProvisioning(IXPortInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): + """An IX Port product that is being provisioned.""" + + ix_port: IXPortBlockProvisioning + + +class IXPort(IXPortProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): + """An IX Port product that is active.""" + + ix_port: IXPortBlock + + +class ImportedIXPortInactive(SubscriptionModel, is_base=True): + """An imported IX Port product that is inactive.""" + + ix_port: IXPortBlockInactive + + +class ImportedIXPort( + ImportedIXPortInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING, SubscriptionLifecycle.ACTIVE] +): + """An imported IX Port product that is active.""" + + ix_port: IXPortBlock -- GitLab From 3a0684b5e1c377e6281a0788f603a8f535b09932 Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Fri, 9 May 2025 14:16:18 +0200 Subject: [PATCH 06/24] Add Transit provider port Product Type --- .../product_types/transit_provider_port.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 gso/products/product_types/transit_provider_port.py diff --git a/gso/products/product_types/transit_provider_port.py b/gso/products/product_types/transit_provider_port.py new file mode 100644 index 000000000..1b2b38d98 --- /dev/null +++ b/gso/products/product_types/transit_provider_port.py @@ -0,0 +1,42 @@ +"""Product type for Transit Provider Port.""" + +from orchestrator.domain import SubscriptionModel +from orchestrator.types import SubscriptionLifecycle + +from gso.products.product_blocks.transit_provider_port import ( + TransitProviderPortBlock, + TransitProviderPortBlockInactive, + TransitProviderPortBlockProvisioning, +) + + +class TransitProviderPortInactive(SubscriptionModel, is_base=True): + """A Transit Provider Port product that is inactive.""" + + transit_provider_port: TransitProviderPortBlockInactive + + +class TransitProviderPortProvisioning(TransitProviderPortInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): + """A Transit Provider Port product that is being provisioned.""" + + transit_provider_port: TransitProviderPortBlockProvisioning + + +class TransitProviderPort(TransitProviderPortProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): + """A Transit Provider Port product that is active.""" + + transit_provider_port: TransitProviderPortBlock + + +class ImportedTransitProviderPortInactive(SubscriptionModel, is_base=True): + """An imported Transit Provider Port product that is inactive.""" + + transit_provider_port: TransitProviderPortBlockInactive + + +class ImportedTransitProviderPort( + ImportedTransitProviderPortInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING, SubscriptionLifecycle.ACTIVE] +): + """An imported Transit Provider Port product that is active.""" + + transit_provider_port: TransitProviderPortBlock \ No newline at end of file -- GitLab From 955f04b7b30fc9fc7122f65badbc6de2ad62643c Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Fri, 9 May 2025 14:21:51 +0200 Subject: [PATCH 07/24] Add new product types to the ProductType enumerator. --- gso/products/__init__.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/gso/products/__init__.py b/gso/products/__init__.py index fff581523..bc4122d3f 100644 --- a/gso/products/__init__.py +++ b/gso/products/__init__.py @@ -13,16 +13,19 @@ from gso.products.product_types.edge_port import EdgePort, ImportedEdgePort from gso.products.product_types.geant_ip import GeantIP, ImportedGeantIP from gso.products.product_types.ias import IAS, ImportedIAS from gso.products.product_types.iptrunk import ImportedIptrunk, Iptrunk +from gso.products.product_types.ix_port import IXPort, ImportedIXPort from gso.products.product_types.lan_switch_interconnect import ImportedLanSwitchInterconnect, LanSwitchInterconnect from gso.products.product_types.layer_2_circuit import ImportedLayer2Circuit, Layer2Circuit, Layer2CircuitServiceType from gso.products.product_types.lhcone import ImportedLHCOne, LHCOne from gso.products.product_types.office_router import ImportedOfficeRouter, OfficeRouter from gso.products.product_types.opengear import ImportedOpengear, Opengear from gso.products.product_types.pop_vlan import PopVlan +from gso.products.product_types.private_peer_port import PrivatePeerPort, ImportedPrivatePeerPort from gso.products.product_types.router import ImportedRouter, Router from gso.products.product_types.site import ImportedSite, Site from gso.products.product_types.super_pop_switch import ImportedSuperPopSwitch, SuperPopSwitch from gso.products.product_types.switch import ImportedSwitch, Switch +from gso.products.product_types.transit_provider_port import TransitProviderPort, ImportedTransitProviderPort from gso.products.product_types.vrf import VRF @@ -78,6 +81,18 @@ class ProductName(strEnum): IMPORTED_EXPRESSROUTE = Layer2CircuitServiceType.IMPORTED_EXPRESSROUTE VRF = "VRF" """VRFs.""" + IX_PORT = "IX Port" + """Internet Exchange Ports.""" + IMPORTED_IX_PORT = "Imported IX Port" + """Imported IX Ports.""" + PRIVATE_PEER_PORT = "Private Peer Port" + """Private Peer Ports.""" + IMPORTED_PRIVATE_PEER_PORT = "Imported Private Peer Port" + """Imported Private Peer Ports.""" + TRANSIT_PROVIDER_PORT = "Transit Provider Port" + """Transit Provider Ports.""" + IMPORTED_TRANSIT_PROVIDER_PORT = "Imported Transit Provider Port" + """Imported Transit Provider Ports.""" L2_CIRCUIT_PRODUCT_TYPE = Layer2Circuit.__name__ @@ -118,6 +133,12 @@ class ProductType(strEnum): IMPORTED_LHCONE = ImportedLHCOne.__name__ COPERNICUS = Copernicus.__name__ IMPORTED_COPERNICUS = ImportedCopernicus.__name__ + IX_PORT = IXPort.__name__ + IMPORTED_IX_PORT = ImportedIXPort.__name__ + PRIVATE_PEER_PORT = PrivatePeerPort.__name__ + IMPORTED_PRIVATE_PEER_PORT = ImportedPrivatePeerPort.__name__ + TRANSIT_PROVIDER_PORT = TransitProviderPort.__name__ + IMPORTED_TRANSIT_PROVIDER_PORT = ImportedTransitProviderPort.__name__ SUBSCRIPTION_MODEL_REGISTRY.update( @@ -154,6 +175,12 @@ SUBSCRIPTION_MODEL_REGISTRY.update( ProductName.EXPRESSROUTE.value: Layer2Circuit, ProductName.IMPORTED_EXPRESSROUTE.value: ImportedLayer2Circuit, ProductName.VRF.value: VRF, + ProductName.IX_PORT.value: IXPort, + ProductName.IMPORTED_IX_PORT.value: ImportedIXPort, + ProductName.PRIVATE_PEER_PORT.value: PrivatePeerPort, + ProductName.IMPORTED_PRIVATE_PEER_PORT.value: ImportedPrivatePeerPort, + ProductName.TRANSIT_PROVIDER_PORT.value: TransitProviderPort, + ProductName.IMPORTED_TRANSIT_PROVIDER_PORT.value: ImportedTransitProviderPort, }, ) -- GitLab From 796c0acb55fefd96cd15cb9bfbb2996207f040c2 Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Fri, 9 May 2025 14:25:06 +0200 Subject: [PATCH 08/24] Make ruff happy --- gso/products/__init__.py | 6 +++--- gso/products/product_blocks/ix_port.py | 12 ++++++++---- gso/products/product_blocks/private_peer_port.py | 7 +++++-- gso/products/product_blocks/transit_provider_port.py | 11 ++++++++--- gso/products/product_types/private_peer_port.py | 2 +- gso/products/product_types/transit_provider_port.py | 2 +- 6 files changed, 26 insertions(+), 14 deletions(-) diff --git a/gso/products/__init__.py b/gso/products/__init__.py index bc4122d3f..67749f2f8 100644 --- a/gso/products/__init__.py +++ b/gso/products/__init__.py @@ -13,19 +13,19 @@ from gso.products.product_types.edge_port import EdgePort, ImportedEdgePort from gso.products.product_types.geant_ip import GeantIP, ImportedGeantIP from gso.products.product_types.ias import IAS, ImportedIAS from gso.products.product_types.iptrunk import ImportedIptrunk, Iptrunk -from gso.products.product_types.ix_port import IXPort, ImportedIXPort +from gso.products.product_types.ix_port import ImportedIXPort, IXPort from gso.products.product_types.lan_switch_interconnect import ImportedLanSwitchInterconnect, LanSwitchInterconnect from gso.products.product_types.layer_2_circuit import ImportedLayer2Circuit, Layer2Circuit, Layer2CircuitServiceType from gso.products.product_types.lhcone import ImportedLHCOne, LHCOne from gso.products.product_types.office_router import ImportedOfficeRouter, OfficeRouter from gso.products.product_types.opengear import ImportedOpengear, Opengear from gso.products.product_types.pop_vlan import PopVlan -from gso.products.product_types.private_peer_port import PrivatePeerPort, ImportedPrivatePeerPort +from gso.products.product_types.private_peer_port import ImportedPrivatePeerPort, PrivatePeerPort from gso.products.product_types.router import ImportedRouter, Router from gso.products.product_types.site import ImportedSite, Site from gso.products.product_types.super_pop_switch import ImportedSuperPopSwitch, SuperPopSwitch from gso.products.product_types.switch import ImportedSwitch, Switch -from gso.products.product_types.transit_provider_port import TransitProviderPort, ImportedTransitProviderPort +from gso.products.product_types.transit_provider_port import ImportedTransitProviderPort, TransitProviderPort from gso.products.product_types.vrf import VRF diff --git a/gso/products/product_blocks/ix_port.py b/gso/products/product_blocks/ix_port.py index f9fec2e6b..e9a5f941e 100644 --- a/gso/products/product_blocks/ix_port.py +++ b/gso/products/product_blocks/ix_port.py @@ -4,8 +4,11 @@ from orchestrator.domain.base import ProductBlockModel from orchestrator.types import SubscriptionLifecycle from pydantic_forms.types import strEnum -from gso.products.product_blocks.service_binding_port import ServiceBindingPortInactive, ServiceBindingPortProvisioning, \ - ServiceBindingPort +from gso.products.product_blocks.service_binding_port import ( + ServiceBindingPort, + ServiceBindingPortInactive, + ServiceBindingPortProvisioning, +) class SessionState(strEnum): @@ -19,8 +22,9 @@ class SessionState(strEnum): """Disabled.""" -class PeeringConnectionInactive(ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], - product_block_name="PeeringConnectionBlock"): +class PeeringConnectionInactive( + ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="PeeringConnectionBlock" +): """A Peering Connection that's currently inactive. See `PeeringConnectionBlock`.""" sbp: ServiceBindingPortInactive diff --git a/gso/products/product_blocks/private_peer_port.py b/gso/products/product_blocks/private_peer_port.py index 9e8d8ac8f..f961af943 100644 --- a/gso/products/product_blocks/private_peer_port.py +++ b/gso/products/product_blocks/private_peer_port.py @@ -3,8 +3,11 @@ from orchestrator.domain.base import ProductBlockModel from orchestrator.types import SubscriptionLifecycle -from gso.products.product_blocks.ix_port import PeeringConnection, PeeringConnectionInactive, \ - PeeringConnectionProvisioning +from gso.products.product_blocks.ix_port import ( + PeeringConnection, + PeeringConnectionInactive, + PeeringConnectionProvisioning, +) class PrivatePeerPortBlockInactive( diff --git a/gso/products/product_blocks/transit_provider_port.py b/gso/products/product_blocks/transit_provider_port.py index 3c8ac0470..f4e8648ff 100644 --- a/gso/products/product_blocks/transit_provider_port.py +++ b/gso/products/product_blocks/transit_provider_port.py @@ -3,8 +3,11 @@ from orchestrator.domain.base import ProductBlockModel from orchestrator.types import SubscriptionLifecycle -from gso.products.product_blocks.ix_port import PeeringConnection, PeeringConnectionInactive, \ - PeeringConnectionProvisioning +from gso.products.product_blocks.ix_port import ( + PeeringConnection, + PeeringConnectionInactive, + PeeringConnectionProvisioning, +) class TransitProviderPortBlockInactive( @@ -15,7 +18,9 @@ class TransitProviderPortBlockInactive( peering_connection: list[PeeringConnectionInactive] -class TransitProviderPortBlockProvisioning(TransitProviderPortBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): +class TransitProviderPortBlockProvisioning( + TransitProviderPortBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING] +): """A Transit Provider Port that's being provisioned. See ``TransitProviderPortBlock``.""" peering_connection: list[PeeringConnectionProvisioning] diff --git a/gso/products/product_types/private_peer_port.py b/gso/products/product_types/private_peer_port.py index 680a5dda9..231f19521 100644 --- a/gso/products/product_types/private_peer_port.py +++ b/gso/products/product_types/private_peer_port.py @@ -39,4 +39,4 @@ class ImportedPrivatePeerPort( ): """An imported Private Peering Port product that is active.""" - private_peer_port: PrivatePeerPortBlock \ No newline at end of file + private_peer_port: PrivatePeerPortBlock diff --git a/gso/products/product_types/transit_provider_port.py b/gso/products/product_types/transit_provider_port.py index 1b2b38d98..33e1dfc87 100644 --- a/gso/products/product_types/transit_provider_port.py +++ b/gso/products/product_types/transit_provider_port.py @@ -39,4 +39,4 @@ class ImportedTransitProviderPort( ): """An imported Transit Provider Port product that is active.""" - transit_provider_port: TransitProviderPortBlock \ No newline at end of file + transit_provider_port: TransitProviderPortBlock -- GitLab From 5c5ef252e1a52d6b42d8bbc138b2374c6f3d04eb Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Mon, 12 May 2025 17:10:18 +0100 Subject: [PATCH 09/24] Improve the models --- .../product_blocks/commercial_peer.py | 67 +++++++++++++++ gso/products/product_blocks/ix_port.py | 81 ++++++++----------- .../product_blocks/private_peer_port.py | 42 ++++++++-- .../product_blocks/transit_provider_port.py | 46 ++++++++--- 4 files changed, 172 insertions(+), 64 deletions(-) create mode 100644 gso/products/product_blocks/commercial_peer.py diff --git a/gso/products/product_blocks/commercial_peer.py b/gso/products/product_blocks/commercial_peer.py new file mode 100644 index 000000000..583f3086d --- /dev/null +++ b/gso/products/product_blocks/commercial_peer.py @@ -0,0 +1,67 @@ +"""Commercial Peer Product Blocks.""" + +from orchestrator.domain.base import ProductBlockModel +from orchestrator.types import SubscriptionLifecycle +from pydantic_forms.types import strEnum + +from gso.products.product_blocks.bgp_session import BGPSessionInactive, BGPSessionProvisioning, BGPSession + + +class SessionState(strEnum): + """Session state of the Peering connection.""" + + PROVISIONED = "provisioning" + """Provisioning.""" + ACTIVE = "active" + """Active.""" + DISABLED = "disabled" + """Disabled.""" + + +class PeeringConnectionInactive( + ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="PeeringConnectionBlock" +): + """A Peering Connection that's currently inactive. See `PeeringConnectionBlock`.""" + + bgp_session_v4: BGPSessionInactive + bgp_session_v6: BGPSessionInactive + minimum_hold_timer: int | None = None + session_state: SessionState + + +class PeeringConnectionProvisioning(PeeringConnectionInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): + """A Peering Connection that's currently being provisioned. See `PeeringConnectionBlock`.""" + + bgp_session_v4: BGPSessionProvisioning + bgp_session_v6: BGPSessionProvisioning + minimum_hold_timer: int | None = None + session_state: SessionState + + +class PeeringConnection(PeeringConnectionProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): + """A Peering Connection that's currently active.""" + + bgp_session_v4: BGPSession + bgp_session_v6: BGPSession + minimum_hold_timer: int | None = None + session_state: SessionState + + +class CommercialPeerBlockInactive( + ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="CommercialPeerBlock" +): + """A Commercial Peer that's not yet provisioned. See ``CommercialPeerBlock``.""" + + peering_connection: list[PeeringConnectionInactive] + + +class CommercialPeerBlockProvisioning(CommercialPeerBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): + """An CommercialPeer that's being provisioned. See ``CommercialPeerBlock``.""" + + peering_connection: list[PeeringConnectionProvisioning] + + +class CommercialPeerBlock(CommercialPeerBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): + """An Internet Exchange Port that's active.""" + + peering_connection: list[PeeringConnection] diff --git a/gso/products/product_blocks/ix_port.py b/gso/products/product_blocks/ix_port.py index e9a5f941e..f3388dbd1 100644 --- a/gso/products/product_blocks/ix_port.py +++ b/gso/products/product_blocks/ix_port.py @@ -2,47 +2,13 @@ from orchestrator.domain.base import ProductBlockModel from orchestrator.types import SubscriptionLifecycle -from pydantic_forms.types import strEnum +from gso.products.product_blocks.edge_port import EdgePortBlock, EdgePortBlockProvisioning from gso.products.product_blocks.service_binding_port import ( - ServiceBindingPort, - ServiceBindingPortInactive, - ServiceBindingPortProvisioning, + BFDSettings, ) - - -class SessionState(strEnum): - """Session state of the IX Port.""" - - PROVISIONING = "provisioning" - """Provisioning.""" - ACTIVE = "active" - """Active.""" - DISABLED = "disabled" - """Disabled.""" - - -class PeeringConnectionInactive( - ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="PeeringConnectionBlock" -): - """A Peering Connection that's currently inactive. See `PeeringConnectionBlock`.""" - - sbp: ServiceBindingPortInactive - prefix_limit: int | None = None - - -class PeeringConnectionProvisioning(PeeringConnectionInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): - """A Peering Connection that's currently being provisioned. See `PeeringConnectionBlock`.""" - - sbp: ServiceBindingPortProvisioning - prefix_limit: int | None = None - - -class PeeringConnection(PeeringConnectionProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): - """A Peering Connection that's currently active.""" - - sbp: ServiceBindingPort - prefix_limit: int | None = None +from gso.utils.shared_enums import SBPType +from gso.utils.types.ip_address import IPv4AddressType, IPv4Netmask, IPv6AddressType, IPv6Netmask class IXPortBlockInactive( @@ -50,22 +16,43 @@ class IXPortBlockInactive( ): """An IX Port that's not yet provisioned. See ``IXPortBlock``.""" - peering_connection: list[PeeringConnectionInactive] - minimum_hold_timer: int | None = None - session_state: SessionState | None = None + layer_type: SBPType + ipv4_address: IPv4AddressType | None = None + ipv4_mask: IPv4Netmask | None = None + ipv6_address: IPv6AddressType | None = None + ipv6_mask: IPv6Netmask | None = None + custom_firewall_filters: bool + gs_id: str + edge_port: EdgePortBlockProvisioning + v4_bfd_settings: BFDSettings + v6_bfd_settings: BFDSettings class IXPortBlockProvisioning(IXPortBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): """An IX Port that's being provisioned. See ``IXPortBlock``.""" - peering_connection: list[PeeringConnectionProvisioning] - minimum_hold_timer: int | None = None - session_state: SessionState | None = None + layer_type: SBPType + ipv4_address: IPv4AddressType | None = None + ipv4_mask: IPv4Netmask | None = None + ipv6_address: IPv6AddressType | None = None + ipv6_mask: IPv6Netmask | None = None + custom_firewall_filters: bool + gs_id: str + edge_port: EdgePortBlockProvisioning + v4_bfd_settings: BFDSettings + v6_bfd_settings: BFDSettings class IXPortBlock(IXPortBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): """An Internet Exchange Port that's active.""" - peering_connection: list[PeeringConnection] - minimum_hold_timer: int | None = None - session_state: SessionState + layer_type: SBPType + ipv4_address: IPv4AddressType | None = None + ipv4_mask: IPv4Netmask | None = None + ipv6_address: IPv6AddressType | None = None + ipv6_mask: IPv6Netmask | None = None + custom_firewall_filters: bool + gs_id: str + edge_port: EdgePortBlock + v4_bfd_settings: BFDSettings + v6_bfd_settings: BFDSettings diff --git a/gso/products/product_blocks/private_peer_port.py b/gso/products/product_blocks/private_peer_port.py index f961af943..e69051407 100644 --- a/gso/products/product_blocks/private_peer_port.py +++ b/gso/products/product_blocks/private_peer_port.py @@ -3,11 +3,10 @@ from orchestrator.domain.base import ProductBlockModel from orchestrator.types import SubscriptionLifecycle -from gso.products.product_blocks.ix_port import ( - PeeringConnection, - PeeringConnectionInactive, - PeeringConnectionProvisioning, -) +from gso.products.product_blocks.edge_port import EdgePortBlockInactive, EdgePortBlockProvisioning +from gso.products.product_blocks.service_binding_port import BFDSettings +from gso.utils.shared_enums import SBPType +from gso.utils.types.ip_address import IPv4AddressType, IPv4Netmask, IPv6AddressType, IPv6Netmask class PrivatePeerPortBlockInactive( @@ -15,16 +14,43 @@ class PrivatePeerPortBlockInactive( ): """An Private Peer Port that's not yet provisioned. See ``PrivatePeerPortBlock``.""" - peering_connection: list[PeeringConnectionInactive] + layer_type: SBPType + ipv4_address: IPv4AddressType | None = None + ipv4_mask: IPv4Netmask | None = None + ipv6_address: IPv6AddressType | None = None + ipv6_mask: IPv6Netmask | None = None + custom_firewall_filters: bool + gs_id: str + edge_port: EdgePortBlockInactive + v4_bfd_settings: BFDSettings + v6_bfd_settings: BFDSettings class PrivatePeerPortBlockProvisioning(PrivatePeerPortBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): """An Private Peer Port that's being provisioned. See ``PrivatePeerPortBlock``.""" - peering_connection: list[PeeringConnectionProvisioning] + layer_type: SBPType + ipv4_address: IPv4AddressType | None = None + ipv4_mask: IPv4Netmask | None = None + ipv6_address: IPv6AddressType | None = None + ipv6_mask: IPv6Netmask | None = None + custom_firewall_filters: bool + gs_id: str + edge_port: EdgePortBlockProvisioning + v4_bfd_settings: BFDSettings + v6_bfd_settings: BFDSettings class PrivatePeerPortBlock(PrivatePeerPortBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): """A Private Provider Port that's active.""" - peering_connection: list[PeeringConnection] + layer_type: SBPType + ipv4_address: IPv4AddressType | None = None + ipv4_mask: IPv4Netmask | None = None + ipv6_address: IPv6AddressType | None = None + ipv6_mask: IPv6Netmask | None = None + custom_firewall_filters: bool + gs_id: str + edge_port: EdgePortBlockProvisioning + v4_bfd_settings: BFDSettings + v6_bfd_settings: BFDSettings diff --git a/gso/products/product_blocks/transit_provider_port.py b/gso/products/product_blocks/transit_provider_port.py index f4e8648ff..ae64d8962 100644 --- a/gso/products/product_blocks/transit_provider_port.py +++ b/gso/products/product_blocks/transit_provider_port.py @@ -3,11 +3,11 @@ from orchestrator.domain.base import ProductBlockModel from orchestrator.types import SubscriptionLifecycle -from gso.products.product_blocks.ix_port import ( - PeeringConnection, - PeeringConnectionInactive, - PeeringConnectionProvisioning, -) +from gso.products.product_blocks.edge_port import EdgePortBlock, EdgePortBlockInactive, EdgePortBlockProvisioning +from gso.products.product_blocks.service_binding_port import BFDSettings +from gso.utils.shared_enums import SBPType +from gso.utils.types.ip_address import IPv4AddressType, IPv4Netmask, IPv6AddressType, IPv6Netmask +from gso.utils.types.virtual_identifiers import VLAN_ID class TransitProviderPortBlockInactive( @@ -15,18 +15,46 @@ class TransitProviderPortBlockInactive( ): """A Transit Provider Port that's not yet provisioned. See ``TransitProviderPortBlock``.""" - peering_connection: list[PeeringConnectionInactive] - + is_tagged: bool + vlan_id: VLAN_ID | None = None + layer_type: SBPType + ipv4_address: IPv4AddressType | None = None + ipv4_mask: IPv4Netmask | None = None + ipv6_address: IPv6AddressType | None = None + ipv6_mask: IPv6Netmask | None = None + custom_firewall_filters: bool + gs_id: str + edge_port: EdgePortBlockInactive + v4_bfd_settings: BFDSettings + v6_bfd_settings: BFDSettings class TransitProviderPortBlockProvisioning( TransitProviderPortBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING] ): """A Transit Provider Port that's being provisioned. See ``TransitProviderPortBlock``.""" - peering_connection: list[PeeringConnectionProvisioning] + layer_type: SBPType + ipv4_address: IPv4AddressType | None = None + ipv4_mask: IPv4Netmask | None = None + ipv6_address: IPv6AddressType | None = None + ipv6_mask: IPv6Netmask | None = None + custom_firewall_filters: bool + gs_id: str + edge_port: EdgePortBlockProvisioning + v4_bfd_settings: BFDSettings + v6_bfd_settings: BFDSettings class TransitProviderPortBlock(TransitProviderPortBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): """A Transit Provider Port that's active.""" - peering_connection: list[PeeringConnection] + layer_type: SBPType + ipv4_address: IPv4AddressType | None = None + ipv4_mask: IPv4Netmask | None = None + ipv6_address: IPv6AddressType | None = None + ipv6_mask: IPv6Netmask | None = None + custom_firewall_filters: bool + gs_id: str + edge_port: EdgePortBlock + v4_bfd_settings: BFDSettings + v6_bfd_settings: BFDSettings -- GitLab From 827261aafb80d53bac6401b864d526c2f580716e Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Mon, 12 May 2025 17:19:05 +0100 Subject: [PATCH 10/24] Add Commercial Peer product types --- gso/products/product_types/commercial_peer.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 gso/products/product_types/commercial_peer.py diff --git a/gso/products/product_types/commercial_peer.py b/gso/products/product_types/commercial_peer.py new file mode 100644 index 000000000..8dcf76181 --- /dev/null +++ b/gso/products/product_types/commercial_peer.py @@ -0,0 +1,42 @@ +"""Product type for Commercial Peer.""" + +from orchestrator.domain import SubscriptionModel +from orchestrator.types import SubscriptionLifecycle + +from gso.products.product_blocks.commercial_peer import ( + CommercialPeerBlock, + CommercialPeerBlockInactive, + CommercialPeerBlockProvisioning, +) + + +class CommercialPeerInactive(SubscriptionModel, is_base=True): + """A Commercial Peer product that is inactive.""" + + commercial_peer: CommercialPeerBlockInactive + + +class CommercialPeerProvisioning(CommercialPeerInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): + """A Commercial Peer product that is being provisioned.""" + + commercial_peer: CommercialPeerBlockProvisioning + + +class CommercialPeer(CommercialPeerProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): + """A Commercial Peer product that is active.""" + + commercial_peer: CommercialPeerBlock + + +class ImportedCommercialPeerInactive(SubscriptionModel, is_base=True): + """An imported Commercial Peer product that is inactive.""" + + commercial_peer: CommercialPeerBlockInactive + + +class ImportedCommercialPeer( + ImportedCommercialPeerInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING, SubscriptionLifecycle.ACTIVE] +): + """An imported Commercial Peer product that is active.""" + + commercial_peer: CommercialPeerBlock -- GitLab From c9373390b217f0e6e7b8e3f825e823acab0bff17 Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Fri, 16 May 2025 16:17:33 +0200 Subject: [PATCH 11/24] Move the common fields for private peer, transit provider and IX ports to l3 interface block --- gso/products/product_blocks/ix_port.py | 53 ++++++++++++++----- .../product_blocks/private_peer_port.py | 41 +++----------- .../product_blocks/transit_provider_port.py | 42 ++------------- 3 files changed, 51 insertions(+), 85 deletions(-) diff --git a/gso/products/product_blocks/ix_port.py b/gso/products/product_blocks/ix_port.py index f3388dbd1..dcc3b5560 100644 --- a/gso/products/product_blocks/ix_port.py +++ b/gso/products/product_blocks/ix_port.py @@ -3,35 +3,39 @@ from orchestrator.domain.base import ProductBlockModel from orchestrator.types import SubscriptionLifecycle -from gso.products.product_blocks.edge_port import EdgePortBlock, EdgePortBlockProvisioning +from gso.products.product_blocks.edge_port import EdgePortBlock, EdgePortBlockProvisioning, EdgePortBlockInactive from gso.products.product_blocks.service_binding_port import ( BFDSettings, ) -from gso.utils.shared_enums import SBPType from gso.utils.types.ip_address import IPv4AddressType, IPv4Netmask, IPv6AddressType, IPv6Netmask +from gso.utils.types.virtual_identifiers import VLAN_ID -class IXPortBlockInactive( - ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="IXPortBlock" +class L3InterfacePortBlockInactive( + ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="L3InterfacePortBlock" ): - """An IX Port that's not yet provisioned. See ``IXPortBlock``.""" + """A Layer 3 Interface Port Block that's not yet provisioned. See ``L3InterfacePortBlock``.""" - layer_type: SBPType + is_tagged: bool + vlan_id: VLAN_ID | None = None ipv4_address: IPv4AddressType | None = None ipv4_mask: IPv4Netmask | None = None ipv6_address: IPv6AddressType | None = None ipv6_mask: IPv6Netmask | None = None custom_firewall_filters: bool gs_id: str - edge_port: EdgePortBlockProvisioning + edge_port: EdgePortBlockInactive v4_bfd_settings: BFDSettings v6_bfd_settings: BFDSettings -class IXPortBlockProvisioning(IXPortBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): - """An IX Port that's being provisioned. See ``IXPortBlock``.""" +class L3InterfacePortBlockProvisioning( + ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="L3InterfacePortBlock" +): + """A Layer 3 Interface Port Block that's being provisioned. See ``L3InterfacePortBlock``.""" - layer_type: SBPType + is_tagged: bool + vlan_id: VLAN_ID | None = None ipv4_address: IPv4AddressType | None = None ipv4_mask: IPv4Netmask | None = None ipv6_address: IPv6AddressType | None = None @@ -43,10 +47,13 @@ class IXPortBlockProvisioning(IXPortBlockInactive, lifecycle=[SubscriptionLifecy v6_bfd_settings: BFDSettings -class IXPortBlock(IXPortBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): - """An Internet Exchange Port that's active.""" +class L3InterfacePortBlock( + ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="L3InterfacePortBlock" +): + """A Layer 3 Interface Port Block that's active.""" - layer_type: SBPType + is_tagged: bool + vlan_id: VLAN_ID | None = None ipv4_address: IPv4AddressType | None = None ipv4_mask: IPv4Netmask | None = None ipv6_address: IPv6AddressType | None = None @@ -56,3 +63,23 @@ class IXPortBlock(IXPortBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTI edge_port: EdgePortBlock v4_bfd_settings: BFDSettings v6_bfd_settings: BFDSettings + + +class IXPortBlockInactive( + ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="IXPortBlock" +): + """An IX Port that's not yet provisioned. See ``IXPortBlock``.""" + + l3_interface: L3InterfacePortBlockInactive + + +class IXPortBlockProvisioning(IXPortBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): + """An IX Port that's being provisioned. See ``IXPortBlock``.""" + + l3_interface: L3InterfacePortBlockProvisioning + + +class IXPortBlock(IXPortBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): + """An Internet Exchange Port that's active.""" + + l3_interface: L3InterfacePortBlock diff --git a/gso/products/product_blocks/private_peer_port.py b/gso/products/product_blocks/private_peer_port.py index e69051407..63925253e 100644 --- a/gso/products/product_blocks/private_peer_port.py +++ b/gso/products/product_blocks/private_peer_port.py @@ -3,54 +3,25 @@ from orchestrator.domain.base import ProductBlockModel from orchestrator.types import SubscriptionLifecycle -from gso.products.product_blocks.edge_port import EdgePortBlockInactive, EdgePortBlockProvisioning -from gso.products.product_blocks.service_binding_port import BFDSettings -from gso.utils.shared_enums import SBPType -from gso.utils.types.ip_address import IPv4AddressType, IPv4Netmask, IPv6AddressType, IPv6Netmask +from gso.products.product_blocks.ix_port import L3InterfacePortBlockProvisioning, L3InterfacePortBlockInactive, \ + L3InterfacePortBlock class PrivatePeerPortBlockInactive( ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="PrivatePeerPortBlock" ): - """An Private Peer Port that's not yet provisioned. See ``PrivatePeerPortBlock``.""" + """A Private Peer Port that's not yet provisioned. See ``PrivatePeerPortBlock``.""" - layer_type: SBPType - ipv4_address: IPv4AddressType | None = None - ipv4_mask: IPv4Netmask | None = None - ipv6_address: IPv6AddressType | None = None - ipv6_mask: IPv6Netmask | None = None - custom_firewall_filters: bool - gs_id: str - edge_port: EdgePortBlockInactive - v4_bfd_settings: BFDSettings - v6_bfd_settings: BFDSettings + l3_interface: L3InterfacePortBlockInactive class PrivatePeerPortBlockProvisioning(PrivatePeerPortBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): """An Private Peer Port that's being provisioned. See ``PrivatePeerPortBlock``.""" - layer_type: SBPType - ipv4_address: IPv4AddressType | None = None - ipv4_mask: IPv4Netmask | None = None - ipv6_address: IPv6AddressType | None = None - ipv6_mask: IPv6Netmask | None = None - custom_firewall_filters: bool - gs_id: str - edge_port: EdgePortBlockProvisioning - v4_bfd_settings: BFDSettings - v6_bfd_settings: BFDSettings + l3_interface: L3InterfacePortBlockProvisioning class PrivatePeerPortBlock(PrivatePeerPortBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): """A Private Provider Port that's active.""" - layer_type: SBPType - ipv4_address: IPv4AddressType | None = None - ipv4_mask: IPv4Netmask | None = None - ipv6_address: IPv6AddressType | None = None - ipv6_mask: IPv6Netmask | None = None - custom_firewall_filters: bool - gs_id: str - edge_port: EdgePortBlockProvisioning - v4_bfd_settings: BFDSettings - v6_bfd_settings: BFDSettings + l3_interface: L3InterfacePortBlock diff --git a/gso/products/product_blocks/transit_provider_port.py b/gso/products/product_blocks/transit_provider_port.py index ae64d8962..5b3216b2e 100644 --- a/gso/products/product_blocks/transit_provider_port.py +++ b/gso/products/product_blocks/transit_provider_port.py @@ -3,11 +3,8 @@ from orchestrator.domain.base import ProductBlockModel from orchestrator.types import SubscriptionLifecycle -from gso.products.product_blocks.edge_port import EdgePortBlock, EdgePortBlockInactive, EdgePortBlockProvisioning -from gso.products.product_blocks.service_binding_port import BFDSettings -from gso.utils.shared_enums import SBPType -from gso.utils.types.ip_address import IPv4AddressType, IPv4Netmask, IPv6AddressType, IPv6Netmask -from gso.utils.types.virtual_identifiers import VLAN_ID +from gso.products.product_blocks.ix_port import L3InterfacePortBlockInactive, L3InterfacePortBlockProvisioning, \ + L3InterfacePortBlock class TransitProviderPortBlockInactive( @@ -15,46 +12,17 @@ class TransitProviderPortBlockInactive( ): """A Transit Provider Port that's not yet provisioned. See ``TransitProviderPortBlock``.""" - is_tagged: bool - vlan_id: VLAN_ID | None = None - layer_type: SBPType - ipv4_address: IPv4AddressType | None = None - ipv4_mask: IPv4Netmask | None = None - ipv6_address: IPv6AddressType | None = None - ipv6_mask: IPv6Netmask | None = None - custom_firewall_filters: bool - gs_id: str - edge_port: EdgePortBlockInactive - v4_bfd_settings: BFDSettings - v6_bfd_settings: BFDSettings + l3_interface: L3InterfacePortBlockInactive class TransitProviderPortBlockProvisioning( TransitProviderPortBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING] ): """A Transit Provider Port that's being provisioned. See ``TransitProviderPortBlock``.""" - layer_type: SBPType - ipv4_address: IPv4AddressType | None = None - ipv4_mask: IPv4Netmask | None = None - ipv6_address: IPv6AddressType | None = None - ipv6_mask: IPv6Netmask | None = None - custom_firewall_filters: bool - gs_id: str - edge_port: EdgePortBlockProvisioning - v4_bfd_settings: BFDSettings - v6_bfd_settings: BFDSettings + l3_interface: L3InterfacePortBlockProvisioning class TransitProviderPortBlock(TransitProviderPortBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): """A Transit Provider Port that's active.""" - layer_type: SBPType - ipv4_address: IPv4AddressType | None = None - ipv4_mask: IPv4Netmask | None = None - ipv6_address: IPv6AddressType | None = None - ipv6_mask: IPv6Netmask | None = None - custom_firewall_filters: bool - gs_id: str - edge_port: EdgePortBlock - v4_bfd_settings: BFDSettings - v6_bfd_settings: BFDSettings + l3_interface: L3InterfacePortBlock -- GitLab From 26d37669f241c42ca20fa126c29c199fbd872d5c Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Fri, 16 May 2025 16:19:12 +0200 Subject: [PATCH 12/24] Reorder imports --- gso/products/product_blocks/commercial_peer.py | 2 +- gso/products/product_blocks/ix_port.py | 2 +- gso/products/product_blocks/private_peer_port.py | 7 +++++-- gso/products/product_blocks/transit_provider_port.py | 8 ++++++-- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/gso/products/product_blocks/commercial_peer.py b/gso/products/product_blocks/commercial_peer.py index 583f3086d..61891dd8a 100644 --- a/gso/products/product_blocks/commercial_peer.py +++ b/gso/products/product_blocks/commercial_peer.py @@ -4,7 +4,7 @@ from orchestrator.domain.base import ProductBlockModel from orchestrator.types import SubscriptionLifecycle from pydantic_forms.types import strEnum -from gso.products.product_blocks.bgp_session import BGPSessionInactive, BGPSessionProvisioning, BGPSession +from gso.products.product_blocks.bgp_session import BGPSession, BGPSessionInactive, BGPSessionProvisioning class SessionState(strEnum): diff --git a/gso/products/product_blocks/ix_port.py b/gso/products/product_blocks/ix_port.py index dcc3b5560..3ae86a1b8 100644 --- a/gso/products/product_blocks/ix_port.py +++ b/gso/products/product_blocks/ix_port.py @@ -3,7 +3,7 @@ from orchestrator.domain.base import ProductBlockModel from orchestrator.types import SubscriptionLifecycle -from gso.products.product_blocks.edge_port import EdgePortBlock, EdgePortBlockProvisioning, EdgePortBlockInactive +from gso.products.product_blocks.edge_port import EdgePortBlock, EdgePortBlockInactive, EdgePortBlockProvisioning from gso.products.product_blocks.service_binding_port import ( BFDSettings, ) diff --git a/gso/products/product_blocks/private_peer_port.py b/gso/products/product_blocks/private_peer_port.py index 63925253e..e88974582 100644 --- a/gso/products/product_blocks/private_peer_port.py +++ b/gso/products/product_blocks/private_peer_port.py @@ -3,8 +3,11 @@ from orchestrator.domain.base import ProductBlockModel from orchestrator.types import SubscriptionLifecycle -from gso.products.product_blocks.ix_port import L3InterfacePortBlockProvisioning, L3InterfacePortBlockInactive, \ - L3InterfacePortBlock +from gso.products.product_blocks.ix_port import ( + L3InterfacePortBlock, + L3InterfacePortBlockInactive, + L3InterfacePortBlockProvisioning, +) class PrivatePeerPortBlockInactive( diff --git a/gso/products/product_blocks/transit_provider_port.py b/gso/products/product_blocks/transit_provider_port.py index 5b3216b2e..15696e215 100644 --- a/gso/products/product_blocks/transit_provider_port.py +++ b/gso/products/product_blocks/transit_provider_port.py @@ -3,8 +3,11 @@ from orchestrator.domain.base import ProductBlockModel from orchestrator.types import SubscriptionLifecycle -from gso.products.product_blocks.ix_port import L3InterfacePortBlockInactive, L3InterfacePortBlockProvisioning, \ - L3InterfacePortBlock +from gso.products.product_blocks.ix_port import ( + L3InterfacePortBlock, + L3InterfacePortBlockInactive, + L3InterfacePortBlockProvisioning, +) class TransitProviderPortBlockInactive( @@ -14,6 +17,7 @@ class TransitProviderPortBlockInactive( l3_interface: L3InterfacePortBlockInactive + class TransitProviderPortBlockProvisioning( TransitProviderPortBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING] ): -- GitLab From 4c183ddfc6cf7b10a8ad83f589a08a030c42c2de Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Fri, 16 May 2025 16:21:51 +0200 Subject: [PATCH 13/24] Add Commercial Peer and Imported Commercial Peer product types --- gso/products/__init__.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/gso/products/__init__.py b/gso/products/__init__.py index 67749f2f8..95bc3d094 100644 --- a/gso/products/__init__.py +++ b/gso/products/__init__.py @@ -8,6 +8,7 @@ from orchestrator.domain import SUBSCRIPTION_MODEL_REGISTRY from pydantic_forms.types import strEnum +from gso.products.product_types.commercial_peer import CommercialPeer, ImportedCommercialPeer from gso.products.product_types.copernicus import Copernicus, ImportedCopernicus from gso.products.product_types.edge_port import EdgePort, ImportedEdgePort from gso.products.product_types.geant_ip import GeantIP, ImportedGeantIP @@ -93,6 +94,10 @@ class ProductName(strEnum): """Transit Provider Ports.""" IMPORTED_TRANSIT_PROVIDER_PORT = "Imported Transit Provider Port" """Imported Transit Provider Ports.""" + COMMERCIAL_PEER = "Commercial Peer" + """Commercial Peers.""" + IMPORTED_COMMERCIAL_PEER = "Imported Commercial Peer" + """Imported Commercial Peers.""" L2_CIRCUIT_PRODUCT_TYPE = Layer2Circuit.__name__ @@ -139,7 +144,8 @@ class ProductType(strEnum): IMPORTED_PRIVATE_PEER_PORT = ImportedPrivatePeerPort.__name__ TRANSIT_PROVIDER_PORT = TransitProviderPort.__name__ IMPORTED_TRANSIT_PROVIDER_PORT = ImportedTransitProviderPort.__name__ - + COMMERCIAL_PEER = CommercialPeer.__name__ + IMPORTED_COMMERCIAL_PEER = ImportedCommercialPeer.__name__ SUBSCRIPTION_MODEL_REGISTRY.update( { @@ -181,6 +187,8 @@ SUBSCRIPTION_MODEL_REGISTRY.update( ProductName.IMPORTED_PRIVATE_PEER_PORT.value: ImportedPrivatePeerPort, ProductName.TRANSIT_PROVIDER_PORT.value: TransitProviderPort, ProductName.IMPORTED_TRANSIT_PROVIDER_PORT.value: ImportedTransitProviderPort, + ProductName.COMMERCIAL_PEER.value: CommercialPeer, + ProductName.IMPORTED_COMMERCIAL_PEER.value: ImportedCommercialPeer, }, ) -- GitLab From 7d900f398246af258f9ebd37566be8afd05e14f1 Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Fri, 16 May 2025 16:41:18 +0200 Subject: [PATCH 14/24] Add prefix limit to Commercial Peer models --- gso/products/product_blocks/commercial_peer.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/gso/products/product_blocks/commercial_peer.py b/gso/products/product_blocks/commercial_peer.py index 61891dd8a..9cb8878bb 100644 --- a/gso/products/product_blocks/commercial_peer.py +++ b/gso/products/product_blocks/commercial_peer.py @@ -2,6 +2,7 @@ from orchestrator.domain.base import ProductBlockModel from orchestrator.types import SubscriptionLifecycle +from pydantic import NonNegativeInt from pydantic_forms.types import strEnum from gso.products.product_blocks.bgp_session import BGPSession, BGPSessionInactive, BGPSessionProvisioning @@ -52,16 +53,19 @@ class CommercialPeerBlockInactive( ): """A Commercial Peer that's not yet provisioned. See ``CommercialPeerBlock``.""" - peering_connection: list[PeeringConnectionInactive] + peering_connection: list[PeeringConnectionInactive] # type: ignore[assignment] + prefix_limit: NonNegativeInt | None = None class CommercialPeerBlockProvisioning(CommercialPeerBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): """An CommercialPeer that's being provisioned. See ``CommercialPeerBlock``.""" - peering_connection: list[PeeringConnectionProvisioning] + peering_connection: list[PeeringConnectionProvisioning] # type: ignore[assignment] + prefix_limit: NonNegativeInt | None = None class CommercialPeerBlock(CommercialPeerBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): """An Internet Exchange Port that's active.""" - peering_connection: list[PeeringConnection] + peering_connection: list[PeeringConnection] # type: ignore[assignment] + prefix_limit: NonNegativeInt | None = None -- GitLab From f4fbf41f4671280f95c4eb15b131862992b68d58 Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Fri, 16 May 2025 17:01:20 +0200 Subject: [PATCH 15/24] Enhance documentation --- gso/products/__init__.py | 1 + gso/products/product_blocks/commercial_peer.py | 9 +++++++-- gso/products/product_blocks/ix_port.py | 12 ++++++++---- gso/products/product_blocks/private_peer_port.py | 10 +++++++--- gso/products/product_blocks/transit_provider_port.py | 10 +++++++--- 5 files changed, 30 insertions(+), 12 deletions(-) diff --git a/gso/products/__init__.py b/gso/products/__init__.py index 95bc3d094..143e36334 100644 --- a/gso/products/__init__.py +++ b/gso/products/__init__.py @@ -147,6 +147,7 @@ class ProductType(strEnum): COMMERCIAL_PEER = CommercialPeer.__name__ IMPORTED_COMMERCIAL_PEER = ImportedCommercialPeer.__name__ + SUBSCRIPTION_MODEL_REGISTRY.update( { ProductName.IP_TRUNK.value: Iptrunk, diff --git a/gso/products/product_blocks/commercial_peer.py b/gso/products/product_blocks/commercial_peer.py index 9cb8878bb..ee6cc6fcf 100644 --- a/gso/products/product_blocks/commercial_peer.py +++ b/gso/products/product_blocks/commercial_peer.py @@ -53,7 +53,7 @@ class CommercialPeerBlockInactive( ): """A Commercial Peer that's not yet provisioned. See ``CommercialPeerBlock``.""" - peering_connection: list[PeeringConnectionInactive] # type: ignore[assignment] + peering_connection: list[PeeringConnectionInactive] prefix_limit: NonNegativeInt | None = None @@ -65,7 +65,12 @@ class CommercialPeerBlockProvisioning(CommercialPeerBlockInactive, lifecycle=[Su class CommercialPeerBlock(CommercialPeerBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): - """An Internet Exchange Port that's active.""" + """An Internet Exchange Port that's active. + + Attributes: + peering_connection: The Peering connection block + prefix_limit: The prefix limit + """ peering_connection: list[PeeringConnection] # type: ignore[assignment] prefix_limit: NonNegativeInt | None = None diff --git a/gso/products/product_blocks/ix_port.py b/gso/products/product_blocks/ix_port.py index 3ae86a1b8..bf3f8b71a 100644 --- a/gso/products/product_blocks/ix_port.py +++ b/gso/products/product_blocks/ix_port.py @@ -16,7 +16,7 @@ class L3InterfacePortBlockInactive( ): """A Layer 3 Interface Port Block that's not yet provisioned. See ``L3InterfacePortBlock``.""" - is_tagged: bool + is_tagged: bool | None = None vlan_id: VLAN_ID | None = None ipv4_address: IPv4AddressType | None = None ipv4_mask: IPv4Netmask | None = None @@ -76,10 +76,14 @@ class IXPortBlockInactive( class IXPortBlockProvisioning(IXPortBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): """An IX Port that's being provisioned. See ``IXPortBlock``.""" - l3_interface: L3InterfacePortBlockProvisioning + l3_interface: L3InterfacePortBlockProvisioning # type: ignore[assignment] class IXPortBlock(IXPortBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): - """An Internet Exchange Port that's active.""" + """An Internet Exchange Port that's active. + + Attributes: + l3_interface: The Layer 3 interface block associated with this IX port. + """ - l3_interface: L3InterfacePortBlock + l3_interface: L3InterfacePortBlock # type: ignore[assignment] diff --git a/gso/products/product_blocks/private_peer_port.py b/gso/products/product_blocks/private_peer_port.py index e88974582..dde021ed8 100644 --- a/gso/products/product_blocks/private_peer_port.py +++ b/gso/products/product_blocks/private_peer_port.py @@ -21,10 +21,14 @@ class PrivatePeerPortBlockInactive( class PrivatePeerPortBlockProvisioning(PrivatePeerPortBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): """An Private Peer Port that's being provisioned. See ``PrivatePeerPortBlock``.""" - l3_interface: L3InterfacePortBlockProvisioning + l3_interface: L3InterfacePortBlockProvisioning # type: ignore[assignment] class PrivatePeerPortBlock(PrivatePeerPortBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): - """A Private Provider Port that's active.""" + """A Private Provider Port that's active. - l3_interface: L3InterfacePortBlock + Attributes: + l3_interface: The Layer 3 interface block associated with this private peer port. + """ + + l3_interface: L3InterfacePortBlock # type: ignore[assignment] diff --git a/gso/products/product_blocks/transit_provider_port.py b/gso/products/product_blocks/transit_provider_port.py index 15696e215..f2a33eb1e 100644 --- a/gso/products/product_blocks/transit_provider_port.py +++ b/gso/products/product_blocks/transit_provider_port.py @@ -23,10 +23,14 @@ class TransitProviderPortBlockProvisioning( ): """A Transit Provider Port that's being provisioned. See ``TransitProviderPortBlock``.""" - l3_interface: L3InterfacePortBlockProvisioning + l3_interface: L3InterfacePortBlockProvisioning # type: ignore[assignment] class TransitProviderPortBlock(TransitProviderPortBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): - """A Transit Provider Port that's active.""" + """A Transit Provider Port that's active. - l3_interface: L3InterfacePortBlock + Attributes: + l3_interface: The Layer 3 interface block associated with this transit provider port. + """ + + l3_interface: L3InterfacePortBlock # type: ignore[assignment] -- GitLab From 8fdced0c8c4561fb6a4cd5650a64b7fee8fb80b8 Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Fri, 16 May 2025 17:29:42 +0200 Subject: [PATCH 16/24] Add database migration for commercial peer, transit provider, IX port and private provider peer models --- ...7_add_commercial_peer_transit_provider_.py | 251 ++++++++++++++++++ 1 file changed, 251 insertions(+) create mode 100644 gso/migrations/versions/2025-05-16_fb88e4914b47_add_commercial_peer_transit_provider_.py diff --git a/gso/migrations/versions/2025-05-16_fb88e4914b47_add_commercial_peer_transit_provider_.py b/gso/migrations/versions/2025-05-16_fb88e4914b47_add_commercial_peer_transit_provider_.py new file mode 100644 index 000000000..dd7296f1d --- /dev/null +++ b/gso/migrations/versions/2025-05-16_fb88e4914b47_add_commercial_peer_transit_provider_.py @@ -0,0 +1,251 @@ +"""Add commercial peer, transit provider port, private peer port and IX port.. + +Revision ID: fb88e4914b47 +Revises: 465008ed496e +Create Date: 2025-05-16 17:28:08.257848 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = 'fb88e4914b47' +down_revision = '465008ed496e' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + conn = op.get_bind() + conn.execute(sa.text(""" +INSERT INTO products (name, description, product_type, tag, status) VALUES ('IX Port', 'Internet Exchange Port', 'IXPort', 'IX_PORT', 'active') RETURNING products.product_id + """)) + conn.execute(sa.text(""" +INSERT INTO products (name, description, product_type, tag, status) VALUES ('Imported IX Port', 'Imported Internet Exchange Port', 'ImportedIXPort', 'IMP_IX_PORT', 'active') RETURNING products.product_id + """)) + conn.execute(sa.text(""" +INSERT INTO products (name, description, product_type, tag, status) VALUES ('Private Peer Port', 'Private Peer Port', 'PrivatePeerPort', 'PP_PORT', 'active') RETURNING products.product_id + """)) + conn.execute(sa.text(""" +INSERT INTO products (name, description, product_type, tag, status) VALUES ('Imported Private Peer Port', 'Imported Private Peer Port', 'ImportedPrivatePeerPort', 'IMP_PP_PORT', 'active') RETURNING products.product_id + """)) + conn.execute(sa.text(""" +INSERT INTO products (name, description, product_type, tag, status) VALUES ('Transit Provider Port', 'Transit Provider Port', 'TransitProviderPort', 'TP_PORT', 'active') RETURNING products.product_id + """)) + conn.execute(sa.text(""" +INSERT INTO products (name, description, product_type, tag, status) VALUES ('Imported Transit Provider Port', 'Imported Transit Provider Port', 'ImportedTransitProviderPort', 'IMP_TP_PORT', 'active') RETURNING products.product_id + """)) + conn.execute(sa.text(""" +INSERT INTO products (name, description, product_type, tag, status) VALUES ('Commercial Peer', 'Commercial Peer', 'CommercialPeer', 'CP', 'active') RETURNING products.product_id + """)) + conn.execute(sa.text(""" +INSERT INTO products (name, description, product_type, tag, status) VALUES ('Imported Commercial Peer', 'Imported Commercial Peer', 'ImportedCommercialPeer', 'IMP_CP', 'active') RETURNING products.product_id + """)) + conn.execute(sa.text(""" +INSERT INTO product_blocks (name, description, tag, status) VALUES ('IXPortBlock', 'IXPortBlock', 'IX_PORT_BLK', 'active') RETURNING product_blocks.product_block_id + """)) + conn.execute(sa.text(""" +INSERT INTO product_blocks (name, description, tag, status) VALUES ('L3InterfacePortBlock', 'L3InterfacePortBlock', 'L3_IF_PORT_BLK', 'active') RETURNING product_blocks.product_block_id + """)) + conn.execute(sa.text(""" +INSERT INTO product_blocks (name, description, tag, status) VALUES ('PrivatePeerPortBlock', 'PrivatePeerPortBlock', 'PP_PORT_BLK', 'active') RETURNING product_blocks.product_block_id + """)) + conn.execute(sa.text(""" +INSERT INTO product_blocks (name, description, tag, status) VALUES ('TransitProviderPortBlock', 'TransitProviderPortBlock', 'TP_PORT_BLK', 'active') RETURNING product_blocks.product_block_id + """)) + conn.execute(sa.text(""" +INSERT INTO product_blocks (name, description, tag, status) VALUES ('CommercialPeerBlock', 'CommercialPeerBlock', 'CP_BLK', 'active') RETURNING product_blocks.product_block_id + """)) + conn.execute(sa.text(""" +INSERT INTO product_blocks (name, description, tag, status) VALUES ('PeeringConnectionBlock', 'PeeringConnectionBlock', 'PC_BLK', 'active') RETURNING product_blocks.product_block_id + """)) + conn.execute(sa.text(""" +INSERT INTO resource_types (resource_type, description) VALUES ('minimum_hold_timer', 'Minimum hold timer') RETURNING resource_types.resource_type_id + """)) + conn.execute(sa.text(""" +INSERT INTO resource_types (resource_type, description) VALUES ('session_state', 'The session state') RETURNING resource_types.resource_type_id + """)) + conn.execute(sa.text(""" +INSERT INTO product_product_blocks (product_id, product_block_id) VALUES ((SELECT products.product_id FROM products WHERE products.name IN ('IX Port')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('IXPortBlock'))), ((SELECT products.product_id FROM products WHERE products.name IN ('Imported IX Port')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('IXPortBlock'))) + """)) + conn.execute(sa.text(""" +INSERT INTO product_product_blocks (product_id, product_block_id) VALUES ((SELECT products.product_id FROM products WHERE products.name IN ('Private Peer Port')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PrivatePeerPortBlock'))), ((SELECT products.product_id FROM products WHERE products.name IN ('Imported Private Peer Port')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PrivatePeerPortBlock'))) + """)) + conn.execute(sa.text(""" +INSERT INTO product_product_blocks (product_id, product_block_id) VALUES ((SELECT products.product_id FROM products WHERE products.name IN ('Transit Provider Port')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('TransitProviderPortBlock'))), ((SELECT products.product_id FROM products WHERE products.name IN ('Imported Transit Provider Port')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('TransitProviderPortBlock'))) + """)) + conn.execute(sa.text(""" +INSERT INTO product_product_blocks (product_id, product_block_id) VALUES ((SELECT products.product_id FROM products WHERE products.name IN ('Imported Commercial Peer')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('CommercialPeerBlock'))), ((SELECT products.product_id FROM products WHERE products.name IN ('Commercial Peer')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('CommercialPeerBlock'))) + """)) + conn.execute(sa.text(""" +INSERT INTO product_block_relations (in_use_by_id, depends_on_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('TransitProviderPortBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('L3InterfacePortBlock'))), ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('IXPortBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('L3InterfacePortBlock'))), ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PrivatePeerPortBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('L3InterfacePortBlock'))) + """)) + conn.execute(sa.text(""" +INSERT INTO product_block_relations (in_use_by_id, depends_on_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('L3InterfacePortBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('EdgePortBlock'))) + """)) + conn.execute(sa.text(""" +INSERT INTO product_block_relations (in_use_by_id, depends_on_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('L3InterfacePortBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('BFDSettings'))) + """)) + conn.execute(sa.text(""" +INSERT INTO product_block_relations (in_use_by_id, depends_on_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('CommercialPeerBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PeeringConnectionBlock'))) + """)) + conn.execute(sa.text(""" +INSERT INTO product_block_relations (in_use_by_id, depends_on_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PeeringConnectionBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('BGPSession'))) + """)) + 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 ('L3InterfacePortBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('is_tagged'))) + """)) + 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 ('L3InterfacePortBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('vlan_id'))) + """)) + 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 ('L3InterfacePortBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv4_address'))) + """)) + 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 ('L3InterfacePortBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv4_mask'))) + """)) + 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 ('L3InterfacePortBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv6_address'))) + """)) + 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 ('L3InterfacePortBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv6_mask'))) + """)) + 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 ('L3InterfacePortBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('custom_firewall_filters'))) + """)) + 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 ('L3InterfacePortBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('gs_id'))) + """)) + 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 ('CommercialPeerBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('prefix_limit'))) + """)) + 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 ('PeeringConnectionBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('minimum_hold_timer'))) + """)) + 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 ('PeeringConnectionBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('session_state'))) + """)) + + +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 ('L3InterfacePortBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('is_tagged')) + """)) + 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 ('L3InterfacePortBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('is_tagged')) + """)) + 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 ('L3InterfacePortBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('vlan_id')) + """)) + 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 ('L3InterfacePortBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('vlan_id')) + """)) + 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 ('L3InterfacePortBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv4_address')) + """)) + 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 ('L3InterfacePortBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv4_address')) + """)) + 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 ('L3InterfacePortBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv4_mask')) + """)) + 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 ('L3InterfacePortBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv4_mask')) + """)) + 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 ('L3InterfacePortBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv6_address')) + """)) + 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 ('L3InterfacePortBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv6_address')) + """)) + 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 ('L3InterfacePortBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv6_mask')) + """)) + 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 ('L3InterfacePortBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv6_mask')) + """)) + 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 ('L3InterfacePortBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('custom_firewall_filters')) + """)) + 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 ('L3InterfacePortBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('custom_firewall_filters')) + """)) + 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 ('L3InterfacePortBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('gs_id')) + """)) + 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 ('L3InterfacePortBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('gs_id')) + """)) + 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 ('CommercialPeerBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('prefix_limit')) + """)) + 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 ('CommercialPeerBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('prefix_limit')) + """)) + 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 ('PeeringConnectionBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('minimum_hold_timer')) + """)) + 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 ('PeeringConnectionBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('minimum_hold_timer')) + """)) + 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 ('PeeringConnectionBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('session_state')) + """)) + 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 ('PeeringConnectionBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('session_state')) + """)) + conn.execute(sa.text(""" +DELETE FROM subscription_instance_values WHERE subscription_instance_values.resource_type_id IN (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('minimum_hold_timer', 'session_state')) + """)) + conn.execute(sa.text(""" +DELETE FROM resource_types WHERE resource_types.resource_type IN ('minimum_hold_timer', 'session_state') + """)) + conn.execute(sa.text(""" +DELETE FROM product_product_blocks WHERE product_product_blocks.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('IX Port', 'Imported IX Port')) AND product_product_blocks.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('IXPortBlock')) + """)) + conn.execute(sa.text(""" +DELETE FROM product_product_blocks WHERE product_product_blocks.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Private Peer Port', 'Imported Private Peer Port')) AND product_product_blocks.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PrivatePeerPortBlock')) + """)) + conn.execute(sa.text(""" +DELETE FROM product_product_blocks WHERE product_product_blocks.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Transit Provider Port', 'Imported Transit Provider Port')) AND product_product_blocks.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('TransitProviderPortBlock')) + """)) + conn.execute(sa.text(""" +DELETE FROM product_product_blocks WHERE product_product_blocks.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Imported Commercial Peer', 'Commercial Peer')) AND product_product_blocks.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('CommercialPeerBlock')) + """)) + conn.execute(sa.text(""" +DELETE FROM product_block_relations WHERE product_block_relations.in_use_by_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('TransitProviderPortBlock', 'IXPortBlock', 'PrivatePeerPortBlock')) AND product_block_relations.depends_on_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('L3InterfacePortBlock')) + """)) + conn.execute(sa.text(""" +DELETE FROM product_block_relations WHERE product_block_relations.in_use_by_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('L3InterfacePortBlock')) AND product_block_relations.depends_on_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('EdgePortBlock')) + """)) + conn.execute(sa.text(""" +DELETE FROM product_block_relations WHERE product_block_relations.in_use_by_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('L3InterfacePortBlock')) AND product_block_relations.depends_on_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('BFDSettings')) + """)) + conn.execute(sa.text(""" +DELETE FROM product_block_relations WHERE product_block_relations.in_use_by_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('CommercialPeerBlock')) AND product_block_relations.depends_on_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PeeringConnectionBlock')) + """)) + conn.execute(sa.text(""" +DELETE FROM product_block_relations WHERE product_block_relations.in_use_by_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PeeringConnectionBlock')) AND product_block_relations.depends_on_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('BGPSession')) + """)) + conn.execute(sa.text(""" +DELETE FROM subscription_instances WHERE subscription_instances.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('L3InterfacePortBlock', 'TransitProviderPortBlock', 'PrivatePeerPortBlock', 'CommercialPeerBlock', 'PeeringConnectionBlock', 'IXPortBlock')) + """)) + conn.execute(sa.text(""" +DELETE FROM product_blocks WHERE product_blocks.name IN ('L3InterfacePortBlock', 'TransitProviderPortBlock', 'PrivatePeerPortBlock', 'CommercialPeerBlock', 'PeeringConnectionBlock', 'IXPortBlock') + """)) + conn.execute(sa.text(""" +DELETE FROM processes WHERE processes.pid IN (SELECT processes_subscriptions.pid FROM processes_subscriptions WHERE processes_subscriptions.subscription_id IN (SELECT subscriptions.subscription_id FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('IX Port', 'Transit Provider Port', 'Imported Commercial Peer', 'Private Peer Port', 'Imported Transit Provider Port', 'Imported Private Peer Port', 'Commercial Peer', 'Imported IX Port')))) + """)) + conn.execute(sa.text(""" +DELETE FROM processes_subscriptions WHERE processes_subscriptions.subscription_id IN (SELECT subscriptions.subscription_id FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('IX Port', 'Transit Provider Port', 'Imported Commercial Peer', 'Private Peer Port', 'Imported Transit Provider Port', 'Imported Private Peer Port', 'Commercial Peer', 'Imported IX Port'))) + """)) + conn.execute(sa.text(""" +DELETE FROM subscription_instances WHERE subscription_instances.subscription_id IN (SELECT subscriptions.subscription_id FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('IX Port', 'Transit Provider Port', 'Imported Commercial Peer', 'Private Peer Port', 'Imported Transit Provider Port', 'Imported Private Peer Port', 'Commercial Peer', 'Imported IX Port'))) + """)) + conn.execute(sa.text(""" +DELETE FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('IX Port', 'Transit Provider Port', 'Imported Commercial Peer', 'Private Peer Port', 'Imported Transit Provider Port', 'Imported Private Peer Port', 'Commercial Peer', 'Imported IX Port')) + """)) + conn.execute(sa.text(""" +DELETE FROM products WHERE products.name IN ('IX Port', 'Transit Provider Port', 'Imported Commercial Peer', 'Private Peer Port', 'Imported Transit Provider Port', 'Imported Private Peer Port', 'Commercial Peer', 'Imported IX Port') + """)) -- GitLab From e5a88c8ddfc0158ebc4b419cd3897afbb0b9bebc Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Wed, 28 May 2025 10:31:33 +0200 Subject: [PATCH 17/24] Refactor L3 interface port block classes and add new L3InterfacePortBlock model --- gso/products/product_blocks/ix_port.py | 63 ++---------------- .../product_blocks/l3_interface_port.py | 65 +++++++++++++++++++ .../product_blocks/private_peer_port.py | 2 +- 3 files changed, 70 insertions(+), 60 deletions(-) create mode 100644 gso/products/product_blocks/l3_interface_port.py diff --git a/gso/products/product_blocks/ix_port.py b/gso/products/product_blocks/ix_port.py index bf3f8b71a..0e6878ddf 100644 --- a/gso/products/product_blocks/ix_port.py +++ b/gso/products/product_blocks/ix_port.py @@ -3,66 +3,11 @@ from orchestrator.domain.base import ProductBlockModel from orchestrator.types import SubscriptionLifecycle -from gso.products.product_blocks.edge_port import EdgePortBlock, EdgePortBlockInactive, EdgePortBlockProvisioning -from gso.products.product_blocks.service_binding_port import ( - BFDSettings, +from gso.products.product_blocks.l3_interface_port import ( + L3InterfacePortBlock, + L3InterfacePortBlockInactive, + L3InterfacePortBlockProvisioning, ) -from gso.utils.types.ip_address import IPv4AddressType, IPv4Netmask, IPv6AddressType, IPv6Netmask -from gso.utils.types.virtual_identifiers import VLAN_ID - - -class L3InterfacePortBlockInactive( - ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="L3InterfacePortBlock" -): - """A Layer 3 Interface Port Block that's not yet provisioned. See ``L3InterfacePortBlock``.""" - - is_tagged: bool | None = None - vlan_id: VLAN_ID | None = None - ipv4_address: IPv4AddressType | None = None - ipv4_mask: IPv4Netmask | None = None - ipv6_address: IPv6AddressType | None = None - ipv6_mask: IPv6Netmask | None = None - custom_firewall_filters: bool - gs_id: str - edge_port: EdgePortBlockInactive - v4_bfd_settings: BFDSettings - v6_bfd_settings: BFDSettings - - -class L3InterfacePortBlockProvisioning( - ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="L3InterfacePortBlock" -): - """A Layer 3 Interface Port Block that's being provisioned. See ``L3InterfacePortBlock``.""" - - is_tagged: bool - vlan_id: VLAN_ID | None = None - ipv4_address: IPv4AddressType | None = None - ipv4_mask: IPv4Netmask | None = None - ipv6_address: IPv6AddressType | None = None - ipv6_mask: IPv6Netmask | None = None - custom_firewall_filters: bool - gs_id: str - edge_port: EdgePortBlockProvisioning - v4_bfd_settings: BFDSettings - v6_bfd_settings: BFDSettings - - -class L3InterfacePortBlock( - ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="L3InterfacePortBlock" -): - """A Layer 3 Interface Port Block that's active.""" - - is_tagged: bool - vlan_id: VLAN_ID | None = None - ipv4_address: IPv4AddressType | None = None - ipv4_mask: IPv4Netmask | None = None - ipv6_address: IPv6AddressType | None = None - ipv6_mask: IPv6Netmask | None = None - custom_firewall_filters: bool - gs_id: str - edge_port: EdgePortBlock - v4_bfd_settings: BFDSettings - v6_bfd_settings: BFDSettings class IXPortBlockInactive( diff --git a/gso/products/product_blocks/l3_interface_port.py b/gso/products/product_blocks/l3_interface_port.py new file mode 100644 index 000000000..8712c4720 --- /dev/null +++ b/gso/products/product_blocks/l3_interface_port.py @@ -0,0 +1,65 @@ +"""A Layer 3 Interface Port Block.""" + +from orchestrator.domain.base import ProductBlockModel +from orchestrator.types import SubscriptionLifecycle + +from gso.products.product_blocks.edge_port import EdgePortBlock, EdgePortBlockInactive, EdgePortBlockProvisioning +from gso.products.product_blocks.service_binding_port import ( + BFDSettings, +) +from gso.utils.types.ip_address import IPv4AddressType, IPv4Netmask, IPv6AddressType, IPv6Netmask +from gso.utils.types.virtual_identifiers import VLAN_ID + + +class L3InterfacePortBlockInactive( + ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="L3InterfacePortBlock" +): + """A Layer 3 Interface Port Block that's not yet provisioned. See ``L3InterfacePortBlock``.""" + + is_tagged: bool | None = None + vlan_id: VLAN_ID | None = None + ipv4_address: IPv4AddressType | None = None + ipv4_mask: IPv4Netmask | None = None + ipv6_address: IPv6AddressType | None = None + ipv6_mask: IPv6Netmask | None = None + custom_firewall_filters: bool + gs_id: str + edge_port: EdgePortBlockInactive + v4_bfd_settings: BFDSettings + v6_bfd_settings: BFDSettings + + +class L3InterfacePortBlockProvisioning( + ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="L3InterfacePortBlock" +): + """A Layer 3 Interface Port Block that's being provisioned. See ``L3InterfacePortBlock``.""" + + is_tagged: bool + vlan_id: VLAN_ID | None = None + ipv4_address: IPv4AddressType | None = None + ipv4_mask: IPv4Netmask | None = None + ipv6_address: IPv6AddressType | None = None + ipv6_mask: IPv6Netmask | None = None + custom_firewall_filters: bool + gs_id: str + edge_port: EdgePortBlockProvisioning + v4_bfd_settings: BFDSettings + v6_bfd_settings: BFDSettings + + +class L3InterfacePortBlock( + ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="L3InterfacePortBlock" +): + """A Layer 3 Interface Port Block that's active.""" + + is_tagged: bool + vlan_id: VLAN_ID | None = None + ipv4_address: IPv4AddressType | None = None + ipv4_mask: IPv4Netmask | None = None + ipv6_address: IPv6AddressType | None = None + ipv6_mask: IPv6Netmask | None = None + custom_firewall_filters: bool + gs_id: str + edge_port: EdgePortBlock + v4_bfd_settings: BFDSettings + v6_bfd_settings: BFDSettings diff --git a/gso/products/product_blocks/private_peer_port.py b/gso/products/product_blocks/private_peer_port.py index dde021ed8..0f4368567 100644 --- a/gso/products/product_blocks/private_peer_port.py +++ b/gso/products/product_blocks/private_peer_port.py @@ -19,7 +19,7 @@ class PrivatePeerPortBlockInactive( class PrivatePeerPortBlockProvisioning(PrivatePeerPortBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): - """An Private Peer Port that's being provisioned. See ``PrivatePeerPortBlock``.""" + """A Private Peer Port that's being provisioned. See ``PrivatePeerPortBlock``.""" l3_interface: L3InterfacePortBlockProvisioning # type: ignore[assignment] -- GitLab From d876ba9236e23b90856e29644abe3795df117f4b Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Wed, 28 May 2025 11:04:16 +0200 Subject: [PATCH 18/24] Make mypy happy --- gso/products/product_blocks/private_peer_port.py | 6 ++++++ gso/products/product_blocks/transit_provider_port.py | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/gso/products/product_blocks/private_peer_port.py b/gso/products/product_blocks/private_peer_port.py index 0f4368567..0bb8e25e3 100644 --- a/gso/products/product_blocks/private_peer_port.py +++ b/gso/products/product_blocks/private_peer_port.py @@ -9,6 +9,12 @@ from gso.products.product_blocks.ix_port import ( L3InterfacePortBlockProvisioning, ) +__all__ = [ + "L3InterfacePortBlock", + "L3InterfacePortBlockInactive", + "L3InterfacePortBlockProvisioning", +] + class PrivatePeerPortBlockInactive( ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="PrivatePeerPortBlock" diff --git a/gso/products/product_blocks/transit_provider_port.py b/gso/products/product_blocks/transit_provider_port.py index f2a33eb1e..c809cd423 100644 --- a/gso/products/product_blocks/transit_provider_port.py +++ b/gso/products/product_blocks/transit_provider_port.py @@ -9,6 +9,12 @@ from gso.products.product_blocks.ix_port import ( L3InterfacePortBlockProvisioning, ) +__all__ = [ + "L3InterfacePortBlock", + "L3InterfacePortBlockInactive", + "L3InterfacePortBlockProvisioning", +] + class TransitProviderPortBlockInactive( ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="TransitProviderPortBlock" -- GitLab From 5363673ac492b4a1bdf742dd5e3d617f59253801 Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Wed, 28 May 2025 11:10:17 +0200 Subject: [PATCH 19/24] Refactor transit and private peer port imports to use L3 interface port block --- gso/products/product_blocks/commercial_peer.py | 14 ++++++++++++++ gso/products/product_blocks/private_peer_port.py | 8 +------- .../product_blocks/transit_provider_port.py | 8 +------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/gso/products/product_blocks/commercial_peer.py b/gso/products/product_blocks/commercial_peer.py index ee6cc6fcf..f0aed3925 100644 --- a/gso/products/product_blocks/commercial_peer.py +++ b/gso/products/product_blocks/commercial_peer.py @@ -6,6 +6,17 @@ from pydantic import NonNegativeInt from pydantic_forms.types import strEnum from gso.products.product_blocks.bgp_session import BGPSession, BGPSessionInactive, BGPSessionProvisioning +from gso.products.product_types.ix_port import IXPort, IXPortInactive, IXPortProvisioning +from gso.products.product_types.private_peer_port import ( + PrivatePeerPort, + PrivatePeerPortInactive, + PrivatePeerPortProvisioning, +) +from gso.products.product_types.transit_provider_port import ( + TransitProviderPort, + TransitProviderPortInactive, + TransitProviderPortProvisioning, +) class SessionState(strEnum): @@ -28,6 +39,7 @@ class PeeringConnectionInactive( bgp_session_v6: BGPSessionInactive minimum_hold_timer: int | None = None session_state: SessionState + placement_port: IXPortInactive | PrivatePeerPortInactive | TransitProviderPortInactive | None = None class PeeringConnectionProvisioning(PeeringConnectionInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): @@ -37,6 +49,7 @@ class PeeringConnectionProvisioning(PeeringConnectionInactive, lifecycle=[Subscr bgp_session_v6: BGPSessionProvisioning minimum_hold_timer: int | None = None session_state: SessionState + placement_port: IXPortProvisioning | PrivatePeerPortProvisioning | TransitProviderPortProvisioning | None = None class PeeringConnection(PeeringConnectionProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): @@ -46,6 +59,7 @@ class PeeringConnection(PeeringConnectionProvisioning, lifecycle=[SubscriptionLi bgp_session_v6: BGPSession minimum_hold_timer: int | None = None session_state: SessionState + placement_port: IXPort | PrivatePeerPort | TransitProviderPort class CommercialPeerBlockInactive( diff --git a/gso/products/product_blocks/private_peer_port.py b/gso/products/product_blocks/private_peer_port.py index 0bb8e25e3..c589912f1 100644 --- a/gso/products/product_blocks/private_peer_port.py +++ b/gso/products/product_blocks/private_peer_port.py @@ -3,18 +3,12 @@ from orchestrator.domain.base import ProductBlockModel from orchestrator.types import SubscriptionLifecycle -from gso.products.product_blocks.ix_port import ( +from gso.products.product_blocks.l3_interface_port import ( L3InterfacePortBlock, L3InterfacePortBlockInactive, L3InterfacePortBlockProvisioning, ) -__all__ = [ - "L3InterfacePortBlock", - "L3InterfacePortBlockInactive", - "L3InterfacePortBlockProvisioning", -] - class PrivatePeerPortBlockInactive( ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="PrivatePeerPortBlock" diff --git a/gso/products/product_blocks/transit_provider_port.py b/gso/products/product_blocks/transit_provider_port.py index c809cd423..3423e98d9 100644 --- a/gso/products/product_blocks/transit_provider_port.py +++ b/gso/products/product_blocks/transit_provider_port.py @@ -3,18 +3,12 @@ from orchestrator.domain.base import ProductBlockModel from orchestrator.types import SubscriptionLifecycle -from gso.products.product_blocks.ix_port import ( +from gso.products.product_blocks.l3_interface_port import ( L3InterfacePortBlock, L3InterfacePortBlockInactive, L3InterfacePortBlockProvisioning, ) -__all__ = [ - "L3InterfacePortBlock", - "L3InterfacePortBlockInactive", - "L3InterfacePortBlockProvisioning", -] - class TransitProviderPortBlockInactive( ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="TransitProviderPortBlock" -- GitLab From 3a92247810a242812be40ba588be8c719f32b422 Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Mon, 2 Jun 2025 11:33:46 +0200 Subject: [PATCH 20/24] Update down_revision for commercial peer and transit provider migration --- ...5-16_fb88e4914b47_add_commercial_peer_transit_provider_.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gso/migrations/versions/2025-05-16_fb88e4914b47_add_commercial_peer_transit_provider_.py b/gso/migrations/versions/2025-05-16_fb88e4914b47_add_commercial_peer_transit_provider_.py index dd7296f1d..0d7b7abe4 100644 --- a/gso/migrations/versions/2025-05-16_fb88e4914b47_add_commercial_peer_transit_provider_.py +++ b/gso/migrations/versions/2025-05-16_fb88e4914b47_add_commercial_peer_transit_provider_.py @@ -1,7 +1,7 @@ """Add commercial peer, transit provider port, private peer port and IX port.. Revision ID: fb88e4914b47 -Revises: 465008ed496e +Revises: 90547df711c3 Create Date: 2025-05-16 17:28:08.257848 """ @@ -10,7 +10,7 @@ from alembic import op # revision identifiers, used by Alembic. revision = 'fb88e4914b47' -down_revision = '465008ed496e' +down_revision = '90547df711c3' branch_labels = None depends_on = None -- GitLab From e7cdda2c233e06c507241c904e8c5bd033ab4506 Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Mon, 2 Jun 2025 11:34:41 +0200 Subject: [PATCH 21/24] Add IX Port creation workflow and input forms --- gso/workflows/__init__.py | 3 + gso/workflows/ix_port/create_ix_port.py | 122 ++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 gso/workflows/ix_port/create_ix_port.py diff --git a/gso/workflows/__init__.py b/gso/workflows/__init__.py index 8b1f4065d..98d3f94f8 100644 --- a/gso/workflows/__init__.py +++ b/gso/workflows/__init__.py @@ -172,3 +172,6 @@ LazyWorkflowInstance("gso.workflows.vrf.create_vrf", "create_vrf") LazyWorkflowInstance("gso.workflows.vrf.modify_vrf_router_list", "modify_vrf_router_list") LazyWorkflowInstance("gso.workflows.vrf.redeploy_vrf", "redeploy_vrf") LazyWorkflowInstance("gso.workflows.vrf.terminate_vrf", "terminate_vrf") + +# IX Port workflows +LazyWorkflowInstance("gso.workflows.ix_port.create_ix_port", "create_ix_port") \ No newline at end of file diff --git a/gso/workflows/ix_port/create_ix_port.py b/gso/workflows/ix_port/create_ix_port.py new file mode 100644 index 000000000..d5fbccd39 --- /dev/null +++ b/gso/workflows/ix_port/create_ix_port.py @@ -0,0 +1,122 @@ +"""Create IX Port Service Workflow""" +from uuid import uuid4 + +from orchestrator import step, workflow +from orchestrator.forms import FormPage, SubmitFormPage +from orchestrator.targets import Target +from orchestrator.types import SubscriptionLifecycle +from orchestrator.workflow import StepList, begin, done +from orchestrator.workflows.steps import resync, set_status, store_process_subscription +from orchestrator.workflows.utils import wrap_create_initial_input_form +from pydantic import ConfigDict, Field +from pydantic_forms.types import FormGenerator, State, UUIDstr +from pydantic_forms.validators import Divider + +from gso.products.product_blocks.service_binding_port import BFDSettings +from gso.products.product_types.edge_port import EdgePort +from gso.products.product_types.ix_port import IXPort, IXPortInactive +from gso.services.partners import get_partner_by_name +from gso.services.subscriptions import generate_unique_id +from gso.utils.helpers import active_edge_port_selector, partner_choice +from gso.utils.types.ip_address import IPv4AddressType, IPv4Netmask, IPv6AddressType, IPv6Netmask +from gso.utils.types.tt_number import TTNumber +from gso.utils.types.virtual_identifiers import VLAN_ID + + +def initial_input_generator(product_name: str) -> FormGenerator: + """Gather input from the operator about a new IX Port subscription.""" + + geant_partner_id = get_partner_by_name("GEANT").partner_id + + class InitialIXPortForm(FormPage): + model_config = ConfigDict(title=f"{product_name}") + + tt_number: TTNumber + partner: partner_choice() + initial_user_input = yield InitialIXPortForm + + class ConfigureIXPortForm(SubmitFormPage): + model_config = ConfigDict(title=f"{product_name} - Configure IX Port") + + gs_id: str = Field(default_factory=lambda: generate_unique_id("GS")) + edge_port: active_edge_port_selector( + partner_id=initial_user_input.partner if initial_user_input.partner != geant_partner_id else None + ) + is_tagged: bool = True + vlan_id: VLAN_ID | None = None + divider1: Divider = Field(None, exclude=True) + ipv4_address: IPv4AddressType | None = None + ipv4_mask: IPv4Netmask | None = None + ipv6_address: IPv6AddressType | None = None + ipv6_mask: IPv6Netmask | None = None + custom_firewall_filters: bool = False + divider2: Divider = Field(None, exclude=True) + v4_bfd_settings: BFDSettings + v6_bfd_settings: BFDSettings + + config_input = yield ConfigureIXPortForm + + return {"product_name": product_name} | initial_user_input.model_dump() | config_input.model_dump() + + +@step("Create IXPort subscription") +def create_subscription(product: UUIDstr, partner: UUIDstr) -> State: + """Create a new IXPort subscription object.""" + subscription = IXPortInactive.from_product_id(product, partner) + return {"subscription": subscription, "subscription_id": subscription.subscription_id} + + +@step("Initialize IXPort subscription") +def initialize_subscription( + subscription: IXPortInactive, + gs_id: str, + edge_port: str, + is_tagged: bool, + vlan_id: VLAN_ID | None, + ipv4_address: IPv4AddressType | None, + ipv4_mask: IPv4Netmask | None, + ipv6_address: IPv6AddressType | None, + ipv6_mask: IPv6Netmask | None, + custom_firewall_filters: bool, + v4_bfd_settings: BFDSettings, + v6_bfd_settings: BFDSettings, +) -> State: + """Initialize the IXPort subscription object.""" + + subscription.ix_port.l3_interface = subscription.ix_port.l3_interface.new( + uuid4(), + gs_id=gs_id, + edge_port=EdgePort.from_subscription(subscription_id=edge_port).edge_port, + is_tagged=is_tagged, + vlan_id=vlan_id, + ipv4_address=ipv4_address, + ipv4_mask=ipv4_mask, + ipv6_address=ipv6_address, + ipv6_mask=ipv6_mask, + custom_firewall_filters=custom_firewall_filters, + v4_bfd_settings=v4_bfd_settings, + v6_bfd_settings=v6_bfd_settings, + ) + + subscription = IXPort.from_other_lifecycle(subscription, SubscriptionLifecycle.PROVISIONING) + subscription.description = f"{subscription.product.name} - {gs_id}" + + return {"subscription": subscription} + + +@workflow( + "Create IX Port Service", + initial_input_form=wrap_create_initial_input_form(initial_input_generator), + target=Target.CREATE, +) +def create_ix_port() -> StepList: + """Create a new IX Port service subscription.""" + return ( + begin + >> create_subscription + >> store_process_subscription(Target.CREATE) + >> initialize_subscription + >> set_status(SubscriptionLifecycle.ACTIVE) + >> resync + >> done + ) -- GitLab From 818a47efdefa80b7c06e2ea51b16883f5f9e0d3f Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Mon, 2 Jun 2025 11:52:18 +0200 Subject: [PATCH 22/24] Add BFD settings form and update IX Port creation WF --- ...5_add_commertial_peers_ix_port_private_.py | 39 +++++++++++++++++++ .../product_blocks/l3_interface_port.py | 10 ++--- gso/workflows/ix_port/__init__.py | 1 + gso/workflows/ix_port/create_ix_port.py | 21 ++++++---- 4 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 gso/migrations/versions/2025-06-02_f16525338855_add_commertial_peers_ix_port_private_.py create mode 100644 gso/workflows/ix_port/__init__.py diff --git a/gso/migrations/versions/2025-06-02_f16525338855_add_commertial_peers_ix_port_private_.py b/gso/migrations/versions/2025-06-02_f16525338855_add_commertial_peers_ix_port_private_.py new file mode 100644 index 000000000..23bfb0b8a --- /dev/null +++ b/gso/migrations/versions/2025-06-02_f16525338855_add_commertial_peers_ix_port_private_.py @@ -0,0 +1,39 @@ +"""Add Commertial peers, IX port, private peer port and transit proivder port WFs. + +Revision ID: f16525338855 +Revises: fb88e4914b47 +Create Date: 2025-06-02 11:36:48.053232 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = 'f16525338855' +down_revision = 'fb88e4914b47' +branch_labels = None +depends_on = None + + +from orchestrator.migrations.helpers import create_workflow, delete_workflow + +new_workflows = [ + { + "name": "create_ix_port", + "target": "CREATE", + "description": "Create IX Port Service", + "product_type": "IXPort" + } +] + + +def upgrade() -> None: + conn = op.get_bind() + for workflow in new_workflows: + create_workflow(conn, workflow) + + +def downgrade() -> None: + conn = op.get_bind() + for workflow in new_workflows: + delete_workflow(conn, workflow["name"]) diff --git a/gso/products/product_blocks/l3_interface_port.py b/gso/products/product_blocks/l3_interface_port.py index 8712c4720..4b3914512 100644 --- a/gso/products/product_blocks/l3_interface_port.py +++ b/gso/products/product_blocks/l3_interface_port.py @@ -22,11 +22,11 @@ class L3InterfacePortBlockInactive( ipv4_mask: IPv4Netmask | None = None ipv6_address: IPv6AddressType | None = None ipv6_mask: IPv6Netmask | None = None - custom_firewall_filters: bool - gs_id: str - edge_port: EdgePortBlockInactive - v4_bfd_settings: BFDSettings - v6_bfd_settings: BFDSettings + custom_firewall_filters: bool | None = None + gs_id: str | None = None + edge_port: EdgePortBlockInactive | None = None + v4_bfd_settings: BFDSettings | None = None + v6_bfd_settings: BFDSettings | None = None class L3InterfacePortBlockProvisioning( diff --git a/gso/workflows/ix_port/__init__.py b/gso/workflows/ix_port/__init__.py new file mode 100644 index 000000000..85bbeef04 --- /dev/null +++ b/gso/workflows/ix_port/__init__.py @@ -0,0 +1 @@ +"""All workflows that can be executed on IX Port.""" diff --git a/gso/workflows/ix_port/create_ix_port.py b/gso/workflows/ix_port/create_ix_port.py index d5fbccd39..b30e1f680 100644 --- a/gso/workflows/ix_port/create_ix_port.py +++ b/gso/workflows/ix_port/create_ix_port.py @@ -8,7 +8,7 @@ from orchestrator.types import SubscriptionLifecycle from orchestrator.workflow import StepList, begin, done from orchestrator.workflows.steps import resync, set_status, store_process_subscription from orchestrator.workflows.utils import wrap_create_initial_input_form -from pydantic import ConfigDict, Field +from pydantic import ConfigDict, Field, BaseModel from pydantic_forms.types import FormGenerator, State, UUIDstr from pydantic_forms.validators import Divider @@ -35,6 +35,13 @@ def initial_input_generator(product_name: str) -> FormGenerator: partner: partner_choice() initial_user_input = yield InitialIXPortForm + class BFDSettingsForm(BaseModel): + bfd_enabled: bool = False + bfd_interval_rx: int | None = Field(default=None, examples=["BFD RX defaults"]) + bfd_interval_tx: int | None = None + bfd_multiplier: int | None = None + + class ConfigureIXPortForm(SubmitFormPage): model_config = ConfigDict(title=f"{product_name} - Configure IX Port") @@ -51,8 +58,8 @@ def initial_input_generator(product_name: str) -> FormGenerator: ipv6_mask: IPv6Netmask | None = None custom_firewall_filters: bool = False divider2: Divider = Field(None, exclude=True) - v4_bfd_settings: BFDSettings - v6_bfd_settings: BFDSettings + v4_bfd_settings: BFDSettingsForm + v6_bfd_settings: BFDSettingsForm config_input = yield ConfigureIXPortForm @@ -78,8 +85,8 @@ def initialize_subscription( ipv6_address: IPv6AddressType | None, ipv6_mask: IPv6Netmask | None, custom_firewall_filters: bool, - v4_bfd_settings: BFDSettings, - v6_bfd_settings: BFDSettings, + v4_bfd_settings: dict, + v6_bfd_settings: dict, ) -> State: """Initialize the IXPort subscription object.""" @@ -94,8 +101,8 @@ def initialize_subscription( ipv6_address=ipv6_address, ipv6_mask=ipv6_mask, custom_firewall_filters=custom_firewall_filters, - v4_bfd_settings=v4_bfd_settings, - v6_bfd_settings=v6_bfd_settings, + v4_bfd_settings=BFDSettings.new(subscription_id=uuid4(), **(v4_bfd_settings)), + v6_bfd_settings=BFDSettings.new(subscription_id=uuid4(), **(v6_bfd_settings)), ) subscription = IXPort.from_other_lifecycle(subscription, SubscriptionLifecycle.PROVISIONING) -- GitLab From 335854cf25f9844af1c001de3580dd91f031c268 Mon Sep 17 00:00:00 2001 From: Aleksandr Kurbatov <ak@geant.org> Date: Tue, 3 Jun 2025 15:54:18 +0100 Subject: [PATCH 23/24] Remove BFD settings from l3_interface_port model --- gso/products/product_blocks/l3_interface_port.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/gso/products/product_blocks/l3_interface_port.py b/gso/products/product_blocks/l3_interface_port.py index 4b3914512..3375e63f2 100644 --- a/gso/products/product_blocks/l3_interface_port.py +++ b/gso/products/product_blocks/l3_interface_port.py @@ -4,9 +4,7 @@ from orchestrator.domain.base import ProductBlockModel from orchestrator.types import SubscriptionLifecycle from gso.products.product_blocks.edge_port import EdgePortBlock, EdgePortBlockInactive, EdgePortBlockProvisioning -from gso.products.product_blocks.service_binding_port import ( - BFDSettings, -) + from gso.utils.types.ip_address import IPv4AddressType, IPv4Netmask, IPv6AddressType, IPv6Netmask from gso.utils.types.virtual_identifiers import VLAN_ID @@ -25,8 +23,6 @@ class L3InterfacePortBlockInactive( custom_firewall_filters: bool | None = None gs_id: str | None = None edge_port: EdgePortBlockInactive | None = None - v4_bfd_settings: BFDSettings | None = None - v6_bfd_settings: BFDSettings | None = None class L3InterfacePortBlockProvisioning( @@ -43,8 +39,6 @@ class L3InterfacePortBlockProvisioning( custom_firewall_filters: bool gs_id: str edge_port: EdgePortBlockProvisioning - v4_bfd_settings: BFDSettings - v6_bfd_settings: BFDSettings class L3InterfacePortBlock( @@ -61,5 +55,3 @@ class L3InterfacePortBlock( custom_firewall_filters: bool gs_id: str edge_port: EdgePortBlock - v4_bfd_settings: BFDSettings - v6_bfd_settings: BFDSettings -- GitLab From c718ca642e6252c8bd2ba67fcd0d042a42868179 Mon Sep 17 00:00:00 2001 From: Aleksandr Kurbatov <ak@geant.org> Date: Tue, 3 Jun 2025 15:54:48 +0100 Subject: [PATCH 24/24] Update create_ix_port WF --- gso/workflows/ix_port/create_ix_port.py | 37 +++++++++++++++++++------ 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/gso/workflows/ix_port/create_ix_port.py b/gso/workflows/ix_port/create_ix_port.py index b30e1f680..02ddde656 100644 --- a/gso/workflows/ix_port/create_ix_port.py +++ b/gso/workflows/ix_port/create_ix_port.py @@ -1,6 +1,9 @@ """Create IX Port Service Workflow""" +import re +from typing import Any from uuid import uuid4 +from gso.services.lso_client import LSOState from orchestrator import step, workflow from orchestrator.forms import FormPage, SubmitFormPage from orchestrator.targets import Target @@ -12,10 +15,10 @@ from pydantic import ConfigDict, Field, BaseModel from pydantic_forms.types import FormGenerator, State, UUIDstr from pydantic_forms.validators import Divider -from gso.products.product_blocks.service_binding_port import BFDSettings from gso.products.product_types.edge_port import EdgePort from gso.products.product_types.ix_port import IXPort, IXPortInactive -from gso.services.partners import get_partner_by_name +from gso.services.lso_client import LSOState, lso_interaction +from gso.services.partners import get_partner_by_id, get_partner_by_name from gso.services.subscriptions import generate_unique_id from gso.utils.helpers import active_edge_port_selector, partner_choice from gso.utils.types.ip_address import IPv4AddressType, IPv4Netmask, IPv6AddressType, IPv6Netmask @@ -58,8 +61,6 @@ def initial_input_generator(product_name: str) -> FormGenerator: ipv6_mask: IPv6Netmask | None = None custom_firewall_filters: bool = False divider2: Divider = Field(None, exclude=True) - v4_bfd_settings: BFDSettingsForm - v6_bfd_settings: BFDSettingsForm config_input = yield ConfigureIXPortForm @@ -85,8 +86,6 @@ def initialize_subscription( ipv6_address: IPv6AddressType | None, ipv6_mask: IPv6Netmask | None, custom_firewall_filters: bool, - v4_bfd_settings: dict, - v6_bfd_settings: dict, ) -> State: """Initialize the IXPort subscription object.""" @@ -101,14 +100,33 @@ def initialize_subscription( ipv6_address=ipv6_address, ipv6_mask=ipv6_mask, custom_firewall_filters=custom_firewall_filters, - v4_bfd_settings=BFDSettings.new(subscription_id=uuid4(), **(v4_bfd_settings)), - v6_bfd_settings=BFDSettings.new(subscription_id=uuid4(), **(v6_bfd_settings)), ) subscription = IXPort.from_other_lifecycle(subscription, SubscriptionLifecycle.PROVISIONING) subscription.description = f"{subscription.product.name} - {gs_id}" + partner_name = get_partner_by_id(subscription.customer_id).name - return {"subscription": subscription} + return {"subscription": subscription, "partner_name": partner_name} + + +@step("[DRY RUN] Deploy IX port") +def deploy_l3_port_dry( + subscription: dict[str, Any], process_id: UUIDstr, tt_number: str, partner_name: str +) -> LSOState: + """Perform a dry run of deploying L3 interface.""" + extra_vars = { + "subscription": subscription, + "partner_name": partner_name, + "dry_run": True, + "verb": "deploy", + "object": "l3_interface", + } + + return { + "playbook_name": "gap_ansible/playbooks/l3_interface.yaml", + "inventory": {"all": {"hosts": subscription["ix_port"]["l3_interface"]["edge_port"]["node"]["router_fqdn"]}}, + "extra_vars": extra_vars, + } @workflow( @@ -123,6 +141,7 @@ def create_ix_port() -> StepList: >> create_subscription >> store_process_subscription(Target.CREATE) >> initialize_subscription + >> lso_interaction(deploy_l3_port_dry) >> set_status(SubscriptionLifecycle.ACTIVE) >> resync >> done -- GitLab