From 5b976b14334f72a741f0714f89aaf78de02b157f Mon Sep 17 00:00:00 2001 From: Neda Moeini <neda.moeini@geant.org> Date: Tue, 18 Feb 2025 14:09:54 +0100 Subject: [PATCH] Product types and block for 10GGBS --- ...5-02-18_3f54c454f4bf_add_10ggbs_product.py | 98 +++++++++++++++++++ gso/products/__init__.py | 10 +- gso/products/product_blocks/tenggbs.py | 43 ++++---- gso/products/product_types/tenggbs.py | 2 +- 4 files changed, 130 insertions(+), 23 deletions(-) create mode 100644 gso/migrations/versions/2025-02-18_3f54c454f4bf_add_10ggbs_product.py diff --git a/gso/migrations/versions/2025-02-18_3f54c454f4bf_add_10ggbs_product.py b/gso/migrations/versions/2025-02-18_3f54c454f4bf_add_10ggbs_product.py new file mode 100644 index 000000000..1aa2ef3f9 --- /dev/null +++ b/gso/migrations/versions/2025-02-18_3f54c454f4bf_add_10ggbs_product.py @@ -0,0 +1,98 @@ +"""Add 10GGBS product. + +Revision ID: 3f54c454f4bf +Revises: efebcde91f2f +Create Date: 2025-02-18 14:02:54.974421 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = '3f54c454f4bf' +down_revision = 'efebcde91f2f' +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 ('10GGBS', '10GGBS product', 'TenGGBS', '10GGBS', 'active') RETURNING products.product_id + """)) + conn.execute(sa.text(""" +INSERT INTO products (name, description, product_type, tag, status) VALUES ('Imported 10GGBS', 'Imported 10GGBS product', 'ImportedTenGGBS', 'IMP_10GGBS', 'active') RETURNING products.product_id + """)) + conn.execute(sa.text(""" +INSERT INTO product_blocks (name, description, tag, status) VALUES ('TenGGBSBlock', '10GGBS product block', '10GGBS_BLK', 'active') RETURNING product_blocks.product_block_id + """)) + conn.execute(sa.text(""" +INSERT INTO product_blocks (name, description, tag, status) VALUES ('PathBlock', '10GGBS product path block', '10GGBS_PATH_BLK', 'active') RETURNING product_blocks.product_block_id + """)) + conn.execute(sa.text(""" +INSERT INTO resource_types (resource_type, description) VALUES ('path_role', 'The role of the path (primary or secondary) for 10GGBS service') 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 ('Imported 10GGBS')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('TenGGBSBlock'))), ((SELECT products.product_id FROM products WHERE products.name IN ('10GGBS')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('TenGGBSBlock'))) + """)) + 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 ('TenGGBSBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('Layer2CircuitBlock'))) + """)) + 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 ('TenGGBSBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PathBlock'))) + """)) + 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 ('PathBlock')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('IptrunkSideBlock'))) + """)) + 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 ('PathBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('path_role'))) + """)) + + +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 ('PathBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('path_role')) + """)) + 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 ('PathBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('path_role')) + """)) + 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 ('path_role')) + """)) + conn.execute(sa.text(""" +DELETE FROM resource_types WHERE resource_types.resource_type IN ('path_role') + """)) + 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 10GGBS', '10GGBS')) AND product_product_blocks.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('TenGGBSBlock')) + """)) + 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 ('TenGGBSBlock')) AND product_block_relations.depends_on_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('Layer2CircuitBlock')) + """)) + 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 ('TenGGBSBlock')) AND product_block_relations.depends_on_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('PathBlock')) + """)) + 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 ('PathBlock')) AND product_block_relations.depends_on_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('IptrunkSideBlock')) + """)) + 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 ('PathBlock', 'TenGGBSBlock')) + """)) + conn.execute(sa.text(""" +DELETE FROM product_blocks WHERE product_blocks.name IN ('PathBlock', 'TenGGBSBlock') + """)) + 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 ('Imported 10GGBS', '10GGBS')))) + """)) + 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 ('Imported 10GGBS', '10GGBS'))) + """)) + 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 ('Imported 10GGBS', '10GGBS'))) + """)) + conn.execute(sa.text(""" +DELETE FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Imported 10GGBS', '10GGBS')) + """)) + conn.execute(sa.text(""" +DELETE FROM products WHERE products.name IN ('Imported 10GGBS', '10GGBS') + """)) diff --git a/gso/products/__init__.py b/gso/products/__init__.py index fd8c097fb..ff736c0fb 100644 --- a/gso/products/__init__.py +++ b/gso/products/__init__.py @@ -20,8 +20,8 @@ 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.tenggbs import ImportedTenGGBS, TenGGBS from gso.products.product_types.vrf import VRF -from gso.products.product_types.tenggbs import TenGGBS, ImportedTenGGBS class ProductName(strEnum): @@ -79,9 +79,9 @@ class ProductName(strEnum): IMPORTED_EXPRESSROUTE = Layer2CircuitServiceType.IMPORTED_EXPRESSROUTE VRF = "VRF" """VRFs.""" - TenGGBS = "TenGGBS" + TENGGBS = "10GGBS" """10GGBS.""" - IMPORTED_TenGGBS = "Imported TenGGBS" + IMPORTED_TENGGBS = "Imported 10GGBS" L3_CORE_SERVICE_PRODUCT_TYPE = L3CoreService.__name__ @@ -165,8 +165,8 @@ SUBSCRIPTION_MODEL_REGISTRY.update( ProductName.EXPRESSROUTE.value: Layer2Circuit, ProductName.IMPORTED_EXPRESSROUTE.value: ImportedLayer2Circuit, ProductName.VRF.value: VRF, - ProductName.TenGGBS.value: TenGGBS, - ProductName.IMPORTED_TenGGBS.value: ImportedTenGGBS, + ProductName.TENGGBS.value: TenGGBS, + ProductName.IMPORTED_TENGGBS.value: ImportedTenGGBS, }, ) diff --git a/gso/products/product_blocks/tenggbs.py b/gso/products/product_blocks/tenggbs.py index bc9c7d15c..b22d6f677 100644 --- a/gso/products/product_blocks/tenggbs.py +++ b/gso/products/product_blocks/tenggbs.py @@ -1,10 +1,10 @@ """10GGBS product block models.""" + from typing import Annotated, TypeVar from annotated_types import Len from orchestrator.domain.base import ProductBlockModel -from orchestrator.types import SubscriptionLifecycle -from orchestrator.types import strEnum +from orchestrator.types import SubscriptionLifecycle, strEnum from pydantic import AfterValidator from pydantic_forms.validators import validate_unique_list from typing_extensions import Doc @@ -25,6 +25,7 @@ T = TypeVar("T") class PathRole(strEnum): """Defines path role types.""" + PRIMARY = "Primary" """Primary path.""" SECONDARY = "Secondary" @@ -34,37 +35,40 @@ class PathRole(strEnum): Paths = Annotated[ list[T], AfterValidator(validate_unique_list), - Len(min_length=0), Doc("A list of TrunkSideBlocks that make up a path."), ] -class PathBlockInactive( - ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="PathBlock" -): +class PathBlockInactive(ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="PathBlock"): """A path representing an ordered list of adjacent trunk sides.""" - role: PathRole | None = None - path: Paths[IptrunkSideBlockInactive] + path_role: PathRole | None = None + tenggbs_path: Paths[IptrunkSideBlockInactive] class PathBlockProvisioning(PathBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): """A path that is being provisioned.""" - role: PathRole | None = None - path: Paths[IptrunkSideBlockProvisioning] + path_role: PathRole | None = None + tenggbs_path: Paths[IptrunkSideBlockProvisioning] # type: ignore[assignment] class PathBlock(PathBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): - """An active path.""" + """An active path for a 10GGBS service. - role: PathRole - path: Paths[IptrunkSideBlock] + Attributes: + path_role: The role of the path (primary or secondary). + tenggbs_path: The list of trunk sides that make up the path. + """ + + path_role: PathRole + tenggbs_path: Paths[IptrunkSideBlock] # type: ignore[assignment] PathsList = Annotated[ list[T], AfterValidator(validate_unique_list), + Len(min_length=0), Doc("A list of paths (1 primary, multiple secondary)."), ] @@ -75,18 +79,23 @@ class TenGGBSBlockInactive( """Inactive 10GGBS service model.""" l2_circuit: Layer2CircuitBlockInactive - paths: PathsList[PathBlockInactive] | None = None + tenggbs_paths: PathsList[PathBlockInactive] class TenGGBSBlockProvisioning(TenGGBSBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): """Provisioning state of 10GGBS.""" l2_circuit: Layer2CircuitBlockProvisioning - paths: PathsList[PathBlockProvisioning] | None = None + tenggbs_paths: PathsList[PathBlockProvisioning] # type: ignore[assignment] class TenGGBSBlock(TenGGBSBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): - """Active 10GGBS service model.""" + """Active 10GGBS service model. + + Attributes: + l2_circuit: The Layer 2 circuit block. + tenggbs_paths: The list of paths for this 10GGBS service. + """ l2_circuit: Layer2CircuitBlock - paths: PathsList[PathBlock] | None = None + tenggbs_paths: PathsList[PathBlock] # type: ignore[assignment] diff --git a/gso/products/product_types/tenggbs.py b/gso/products/product_types/tenggbs.py index ca7645723..97acda866 100644 --- a/gso/products/product_types/tenggbs.py +++ b/gso/products/product_types/tenggbs.py @@ -1,6 +1,6 @@ """The product type for 10GGBS services.""" -from orchestrator.domain.base import SubscriptionModel +from orchestrator.domain import SubscriptionModel from orchestrator.types import SubscriptionLifecycle from gso.products.product_blocks.tenggbs import ( -- GitLab