From e686ef94b2d01c2e093fc8c27b4c5a1b09a6c1ed Mon Sep 17 00:00:00 2001 From: Saket Agrahari <saket.agrahari@geant.org> Date: Mon, 12 May 2025 07:26:36 +0100 Subject: [PATCH 1/5] initial changes for iptrunk version --- ...7431c9ef_add_config_version_to_ip_trunk.py | 41 +++++++++++++++++++ gso/products/product_blocks/iptrunk.py | 7 +++- .../iptrunk/create_imported_iptrunk.py | 15 ++++--- gso/workflows/iptrunk/create_iptrunk.py | 22 ++++++---- .../iptrunk/test_create_imported_iptrunk.py | 1 + test/workflows/iptrunk/test_create_iptrunk.py | 1 + 6 files changed, 70 insertions(+), 17 deletions(-) create mode 100644 gso/migrations/versions/2025-05-12_54477431c9ef_add_config_version_to_ip_trunk.py diff --git a/gso/migrations/versions/2025-05-12_54477431c9ef_add_config_version_to_ip_trunk.py b/gso/migrations/versions/2025-05-12_54477431c9ef_add_config_version_to_ip_trunk.py new file mode 100644 index 000000000..401717874 --- /dev/null +++ b/gso/migrations/versions/2025-05-12_54477431c9ef_add_config_version_to_ip_trunk.py @@ -0,0 +1,41 @@ +"""add config version to ip trunk. + +Revision ID: 54477431c9ef +Revises: 465008ed496e +Create Date: 2025-05-12 04:46:47.410668 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = '54477431c9ef' +down_revision = '465008ed496e' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + conn = op.get_bind() + conn.execute(sa.text(""" +INSERT INTO resource_types (resource_type, description) VALUES ('iptrunk_config_version', 'adding option of service version controlled rollout') RETURNING resource_types.resource_type_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 ('IptrunkBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('iptrunk_config_version'))) + """)) + + +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 ('IptrunkBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('iptrunk_config_version')) + """)) + 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 ('IptrunkBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('iptrunk_config_version')) + """)) + 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 ('iptrunk_config_version')) + """)) + conn.execute(sa.text(""" +DELETE FROM resource_types WHERE resource_types.resource_type IN ('iptrunk_config_version') + """)) diff --git a/gso/products/product_blocks/iptrunk.py b/gso/products/product_blocks/iptrunk.py index 8bfc0056a..1c6cb4a66 100644 --- a/gso/products/product_blocks/iptrunk.py +++ b/gso/products/product_blocks/iptrunk.py @@ -4,8 +4,6 @@ import ipaddress from typing import Annotated from annotated_types import Len -from orchestrator.domain.base import ProductBlockModel, T -from orchestrator.types import SubscriptionLifecycle from pydantic import AfterValidator from pydantic_forms.types import strEnum from pydantic_forms.validators import validate_unique_list @@ -17,6 +15,8 @@ from gso.products.product_blocks.router import ( RouterBlockProvisioning, ) from gso.utils.types.interfaces import LAGMemberList, PhysicalPortCapacity +from orchestrator.domain.base import ProductBlockModel, T +from orchestrator.types import SubscriptionLifecycle class IptrunkType(strEnum): @@ -109,6 +109,7 @@ class IptrunkBlockInactive( iptrunk_ipv6_network: ipaddress.IPv6Network | None = None iptrunk_sides: IptrunkSides[IptrunkSideBlockInactive] iptrunk_description_suffix: str | None = None + iptrunk_config_version: str | None = None class IptrunkBlockProvisioning(IptrunkBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): @@ -124,6 +125,7 @@ class IptrunkBlockProvisioning(IptrunkBlockInactive, lifecycle=[SubscriptionLife iptrunk_ipv6_network: ipaddress.IPv6Network | None iptrunk_sides: IptrunkSides[IptrunkSideBlockProvisioning] # type: ignore[assignment] iptrunk_description_suffix: str | None + iptrunk_config_version: str | None class IptrunkBlock(IptrunkBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): @@ -152,3 +154,4 @@ class IptrunkBlock(IptrunkBlockProvisioning, lifecycle=[SubscriptionLifecycle.AC iptrunk_ipv6_network: ipaddress.IPv6Network iptrunk_sides: IptrunkSides[IptrunkSideBlock] # type: ignore[assignment] iptrunk_description_suffix: str | None + iptrunk_config_version: str | None diff --git a/gso/workflows/iptrunk/create_imported_iptrunk.py b/gso/workflows/iptrunk/create_imported_iptrunk.py index 4c45751b9..d65dee753 100644 --- a/gso/workflows/iptrunk/create_imported_iptrunk.py +++ b/gso/workflows/iptrunk/create_imported_iptrunk.py @@ -4,12 +4,6 @@ import ipaddress from typing import Annotated from uuid import uuid4 -from orchestrator import workflow -from orchestrator.forms import SubmitFormPage -from orchestrator.targets import Target -from orchestrator.types import SubscriptionLifecycle -from orchestrator.workflow import StepList, begin, done, step -from orchestrator.workflows.steps import resync, set_status, store_process_subscription from pydantic import AfterValidator, ConfigDict from pydantic_forms.types import FormGenerator, State from pydantic_forms.validators import validate_unique_list @@ -23,6 +17,12 @@ from gso.services.partners import get_partner_by_name from gso.utils.helpers import active_router_selector from gso.utils.types.geant_ids import IMPORTED_GA_ID, IMPORTED_GS_ID from gso.utils.types.interfaces import LAGMember, LAGMemberList, PhysicalPortCapacity +from orchestrator import workflow +from orchestrator.forms import SubmitFormPage +from orchestrator.targets import Target +from orchestrator.types import SubscriptionLifecycle +from orchestrator.workflow import StepList, begin, done, step +from orchestrator.workflows.steps import resync, set_status, store_process_subscription def initial_input_form_generator() -> FormGenerator: @@ -39,6 +39,7 @@ def initial_input_form_generator() -> FormGenerator: iptrunk_minimum_links: int iptrunk_isis_metric: int iptrunk_description_suffix: str | None = None + iptrunk_config_version: str | None = None side_a_node_id: active_router_selector() # type: ignore[valid-type] side_a_ae_iface: str @@ -89,6 +90,7 @@ def initialize_subscription( side_b_ae_iface: str, side_b_ga_id: IMPORTED_GA_ID | None, side_b_ae_members: LAGMemberList, + iptrunk_config_version: str | None, ) -> State: """Take all input from the user, and store it in the database.""" subscription.iptrunk.gs_id = gs_id @@ -98,6 +100,7 @@ def initialize_subscription( subscription.iptrunk.iptrunk_isis_metric = iptrunk_isis_metric subscription.iptrunk.iptrunk_minimum_links = iptrunk_minimum_links subscription.iptrunk.iptrunk_description_suffix = iptrunk_description_suffix + subscription.iptrunk.iptrunk_config_version = iptrunk_config_version subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node = Router.from_subscription(side_a_node_id).router subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_iface = side_a_ae_iface diff --git a/gso/workflows/iptrunk/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py index 54bdb6d96..72f6a6c9a 100644 --- a/gso/workflows/iptrunk/create_iptrunk.py +++ b/gso/workflows/iptrunk/create_iptrunk.py @@ -31,15 +31,6 @@ from typing import Annotated from uuid import uuid4 from annotated_types import Len -from orchestrator.forms import FormPage -from orchestrator.forms.validators import Choice, Label -from orchestrator.targets import Target -from orchestrator.types import SubscriptionLifecycle -from orchestrator.utils.errors import ProcessFailureError -from orchestrator.utils.json import json_dumps -from orchestrator.workflow import StepList, begin, conditional, done, step, step_group, workflow -from orchestrator.workflows.steps import resync, set_status, store_process_subscription -from orchestrator.workflows.utils import wrap_create_initial_input_form from ping3 import ping from pydantic import ConfigDict from pydantic_forms.types import FormGenerator, State, UUIDstr @@ -75,6 +66,15 @@ from gso.utils.types.netbox_router import NetboxEnabledRouter from gso.utils.types.tt_number import TTNumber from gso.utils.workflow_steps import prompt_sharepoint_checklist_url from gso.workflows.shared import create_summary_form +from orchestrator.forms import FormPage +from orchestrator.forms.validators import Choice, Label +from orchestrator.targets import Target +from orchestrator.types import SubscriptionLifecycle +from orchestrator.utils.errors import ProcessFailureError +from orchestrator.utils.json import json_dumps +from orchestrator.workflow import StepList, begin, conditional, done, step, step_group, workflow +from orchestrator.workflows.steps import resync, set_status, store_process_subscription +from orchestrator.workflows.utils import wrap_create_initial_input_form def initial_input_form_generator(product_name: str) -> FormGenerator: @@ -95,6 +95,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator: iptrunk_speed: PhysicalPortCapacity iptrunk_number_of_members: int iptrunk_description_suffix: str | None = None + iptrunk_config_version: str | None = None initial_user_input = yield CreateIptrunkForm recommended_minimum_links = calculate_recommended_minimum_links( @@ -203,6 +204,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator: "iptrunk_description", "iptrunk_minimum_links", "iptrunk_description_suffix", + "iptrunk_config_version", "side_a_node", "side_a_ae_iface", "side_a_ae_members", @@ -327,6 +329,7 @@ def initialize_subscription( iptrunk_speed: PhysicalPortCapacity, iptrunk_minimum_links: int, iptrunk_description_suffix: str | None, + iptrunk_config_version: str | None, side_a_node_id: str, side_a_ae_iface: str, side_a_ae_members: list[dict], @@ -346,6 +349,7 @@ def initialize_subscription( subscription.iptrunk.iptrunk_isis_metric = oss_params.GENERAL.isis_high_metric subscription.iptrunk.iptrunk_minimum_links = iptrunk_minimum_links subscription.iptrunk.iptrunk_description_suffix = iptrunk_description_suffix + subscription.iptrunk.iptrunk_config_version = iptrunk_config_version subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node = side_a subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_iface = side_a_ae_iface diff --git a/test/workflows/iptrunk/test_create_imported_iptrunk.py b/test/workflows/iptrunk/test_create_imported_iptrunk.py index 7f83775bb..7add53de9 100644 --- a/test/workflows/iptrunk/test_create_imported_iptrunk.py +++ b/test/workflows/iptrunk/test_create_imported_iptrunk.py @@ -23,6 +23,7 @@ def workflow_input_data(faker, router_subscription_factory): "iptrunk_minimum_links": 2, "iptrunk_isis_metric": 10000, "iptrunk_description_suffix": faker.word(), + "iptrunk_config_version": "1.0", "side_a_node_id": str(router_subscription_factory().subscription_id), "side_a_ae_iface": faker.nokia_lag_interface_name(), "side_a_ga_id": faker.imported_ga_id(), diff --git a/test/workflows/iptrunk/test_create_iptrunk.py b/test/workflows/iptrunk/test_create_iptrunk.py index 6996e20a0..c18577249 100644 --- a/test/workflows/iptrunk/test_create_iptrunk.py +++ b/test/workflows/iptrunk/test_create_iptrunk.py @@ -74,6 +74,7 @@ def input_form_wizard_data(request, router_subscription_factory, faker): "iptrunk_speed": PhysicalPortCapacity.HUNDRED_GIGABIT_PER_SECOND, "iptrunk_number_of_members": 2, "iptrunk_description_suffix": faker.word(), + "iptrunk_config_version": "1.0" } create_ip_trunk_confirm_step = {"iptrunk_minimum_links": 1} create_ip_trunk_side_a_router_name = {"side_a_node_id": router_side_a} -- GitLab From 1362114b48575d38581fe3d6150dc79fa1f22619 Mon Sep 17 00:00:00 2001 From: Saket Agrahari <saket.agrahari@geant.org> Date: Wed, 14 May 2025 08:44:04 +0100 Subject: [PATCH 2/5] adding config change and modify iptrunk --- gso/gso-services-config.json | 16 +++++++++++++ gso/settings.py | 21 +++++++++++++++++ .../iptrunk/modify_trunk_interface.py | 23 +++++++++++-------- 3 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 gso/gso-services-config.json diff --git a/gso/gso-services-config.json b/gso/gso-services-config.json new file mode 100644 index 000000000..dc53bbff5 --- /dev/null +++ b/gso/gso-services-config.json @@ -0,0 +1,16 @@ +{ + "IP_TRUNK": { + "VERSION": { + "1.0": "Base Version", + "1.1":" Minor Upgrade" + }, + "default_version": "1.0" + }, + "GEANT_IP": { + "VERSION": { + "1.0": "Base Version", + "2.0": "Major Upgrade" + }, + "default_version": "1.0" + } +} diff --git a/gso/settings.py b/gso/settings.py index bf5dd53e1..2059a6023 100644 --- a/gso/settings.py +++ b/gso/settings.py @@ -250,7 +250,28 @@ def load_oss_params() -> OSSParams: return OSSParams(**json.loads(file.read())) +class ServiceConfig(BaseSettings): + """Configurations for base gso service.""" + + version: dict[str, str] + default_version: str + + +class GSOServiceConfig(BaseSettings): + """Configuration for the GSO service.""" + IP_TRUNK: ServiceConfig + GEANT_IP: ServiceConfig + + +def load_gso_service_config() -> GSOServiceConfig: + """Load the GSO service configuration from the environment variable.""" + """Look for ``GSO_SERVICE_CONFIG`` in the environment and load the parameters from that file.""" + with Path(os.environ["GSO_SERVICE_CONFIG"]).open(encoding="utf-8") as file: + return GSOServiceConfig(**json.loads(file.read())) + + celery_settings = CelerySettings() if __name__ == "__main__": logger.debug(load_oss_params()) + logger.debug(load_gso_service_config()) diff --git a/gso/workflows/iptrunk/modify_trunk_interface.py b/gso/workflows/iptrunk/modify_trunk_interface.py index 58277b0c5..793c1bc00 100644 --- a/gso/workflows/iptrunk/modify_trunk_interface.py +++ b/gso/workflows/iptrunk/modify_trunk_interface.py @@ -12,12 +12,6 @@ from typing import Annotated from uuid import UUID, uuid4 from annotated_types import Len -from orchestrator.forms import FormPage, SubmitFormPage -from orchestrator.targets import Target -from orchestrator.utils.json import json_dumps -from orchestrator.workflow import StepList, begin, conditional, done, step, workflow -from orchestrator.workflows.steps import resync, store_process_subscription, unsync -from orchestrator.workflows.utils import wrap_modify_initial_input_form from pydantic import AfterValidator, ConfigDict, Field from pydantic_forms.types import FormGenerator, State, UUIDstr from pydantic_forms.validators import Label, ReadOnlyField @@ -30,6 +24,7 @@ from gso.products.product_blocks.iptrunk import ( from gso.products.product_types.iptrunk import Iptrunk from gso.services.lso_client import LSOState, lso_interaction from gso.services.netbox_client import NetboxClient +from gso.settings import ServiceConfig, load_gso_service_config from gso.utils.helpers import ( available_interfaces_choices, available_interfaces_choices_including_current_members, @@ -43,6 +38,12 @@ from gso.utils.types.tt_number import TTNumber from gso.utils.types.unique_field import validate_field_is_unique from gso.workflows.iptrunk.migrate_iptrunk import check_ip_trunk_optical_levels_pre from gso.workflows.iptrunk.validate_iptrunk import check_ip_trunk_isis +from orchestrator.forms import FormPage, SubmitFormPage +from orchestrator.targets import Target +from orchestrator.utils.json import json_dumps +from orchestrator.workflow import StepList, begin, conditional, done, step, workflow +from orchestrator.workflows.steps import resync, store_process_subscription, unsync +from orchestrator.workflows.utils import wrap_modify_initial_input_form def initialize_ae_members( @@ -94,7 +95,10 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: ] | None ) = subscription.iptrunk.gs_id + iptrunk_description: str | None = subscription.iptrunk.iptrunk_description + iptrunk_config_version: load_gso_service_config().IP_TRUNK.version | str \ + = subscription.iptrunk.iptrunk_config_version iptrunk_type: IptrunkType | str = subscription.iptrunk.iptrunk_type # FIXME: remove str workaround warning_label: Label = ( "Changing the PhyPortCapacity will result in the deletion of all AE members. " @@ -268,6 +272,7 @@ def modify_iptrunk_subscription( iptrunk_speed: PhysicalPortCapacity, iptrunk_minimum_links: int, iptrunk_description_suffix: str | None, + iptrunk_config_version: ServiceConfig.version, side_a_ga_id: str | None, side_a_ae_members: list[dict], side_b_ga_id: str | None, @@ -302,7 +307,7 @@ def modify_iptrunk_subscription( subscription.iptrunk.iptrunk_speed = iptrunk_speed subscription.iptrunk.iptrunk_minimum_links = iptrunk_minimum_links subscription.iptrunk.iptrunk_description_suffix = iptrunk_description_suffix - + subscription.iptrunk.iptrunk_config_version = iptrunk_config_version subscription.iptrunk.iptrunk_sides[0].ga_id = side_a_ga_id update_side_members(subscription, 0, side_a_ae_members) subscription.iptrunk.iptrunk_sides[1].ga_id = side_b_ga_id @@ -534,8 +539,8 @@ def modify_trunk_interface() -> StepList: >> capacity_has_changed(lso_interaction(check_ip_trunk_connectivity)) >> capacity_has_changed(lso_interaction(check_ip_trunk_isis)) >> modify_iptrunk_subscription - >> side_a_is_nokia(netbox_update_interfaces_side_a) - >> side_b_is_nokia(netbox_update_interfaces_side_b) + # >> side_a_is_nokia(netbox_update_interfaces_side_a) + # >> side_b_is_nokia(netbox_update_interfaces_side_b) >> lso_interaction(provision_ip_trunk_iface_dry) >> lso_interaction(provision_ip_trunk_iface_real) >> side_a_is_nokia(allocate_interfaces_in_netbox_side_a) -- GitLab From 4bc757f7534c96b4378d52877f7452dfc7539919 Mon Sep 17 00:00:00 2001 From: Saket Agrahari <saket.agrahari@geant.org> Date: Fri, 16 May 2025 09:13:30 +0100 Subject: [PATCH 3/5] Update drop-down menus in IPtrunk workflows changes for drop down menu ruff fix ruff ruff uncommenting steps mypy issue modify ip trunk test --- gso/gso-services-config.json | 6 ++-- gso/products/product_blocks/iptrunk.py | 4 +-- gso/settings.py | 3 +- .../iptrunk/create_imported_iptrunk.py | 15 ++++------ gso/workflows/iptrunk/create_iptrunk.py | 28 +++++++++-------- .../iptrunk/modify_trunk_interface.py | 30 ++++++++++--------- .../iptrunk/test_create_imported_iptrunk.py | 1 - test/workflows/iptrunk/test_create_iptrunk.py | 2 +- .../iptrunk/test_modify_trunk_interface.py | 1 + 9 files changed, 47 insertions(+), 43 deletions(-) diff --git a/gso/gso-services-config.json b/gso/gso-services-config.json index dc53bbff5..364ec44e6 100644 --- a/gso/gso-services-config.json +++ b/gso/gso-services-config.json @@ -1,13 +1,13 @@ { "IP_TRUNK": { - "VERSION": { + "version": { "1.0": "Base Version", - "1.1":" Minor Upgrade" + "1.1": "Minor Upgrade" }, "default_version": "1.0" }, "GEANT_IP": { - "VERSION": { + "version": { "1.0": "Base Version", "2.0": "Major Upgrade" }, diff --git a/gso/products/product_blocks/iptrunk.py b/gso/products/product_blocks/iptrunk.py index 1c6cb4a66..eaaab57fe 100644 --- a/gso/products/product_blocks/iptrunk.py +++ b/gso/products/product_blocks/iptrunk.py @@ -4,6 +4,8 @@ import ipaddress from typing import Annotated from annotated_types import Len +from orchestrator.domain.base import ProductBlockModel, T +from orchestrator.types import SubscriptionLifecycle from pydantic import AfterValidator from pydantic_forms.types import strEnum from pydantic_forms.validators import validate_unique_list @@ -15,8 +17,6 @@ from gso.products.product_blocks.router import ( RouterBlockProvisioning, ) from gso.utils.types.interfaces import LAGMemberList, PhysicalPortCapacity -from orchestrator.domain.base import ProductBlockModel, T -from orchestrator.types import SubscriptionLifecycle class IptrunkType(strEnum): diff --git a/gso/settings.py b/gso/settings.py index 2059a6023..65af78ac8 100644 --- a/gso/settings.py +++ b/gso/settings.py @@ -253,12 +253,13 @@ def load_oss_params() -> OSSParams: class ServiceConfig(BaseSettings): """Configurations for base gso service.""" - version: dict[str, str] + version: dict[str, str] default_version: str class GSOServiceConfig(BaseSettings): """Configuration for the GSO service.""" + IP_TRUNK: ServiceConfig GEANT_IP: ServiceConfig diff --git a/gso/workflows/iptrunk/create_imported_iptrunk.py b/gso/workflows/iptrunk/create_imported_iptrunk.py index d65dee753..4c45751b9 100644 --- a/gso/workflows/iptrunk/create_imported_iptrunk.py +++ b/gso/workflows/iptrunk/create_imported_iptrunk.py @@ -4,6 +4,12 @@ import ipaddress from typing import Annotated from uuid import uuid4 +from orchestrator import workflow +from orchestrator.forms import SubmitFormPage +from orchestrator.targets import Target +from orchestrator.types import SubscriptionLifecycle +from orchestrator.workflow import StepList, begin, done, step +from orchestrator.workflows.steps import resync, set_status, store_process_subscription from pydantic import AfterValidator, ConfigDict from pydantic_forms.types import FormGenerator, State from pydantic_forms.validators import validate_unique_list @@ -17,12 +23,6 @@ from gso.services.partners import get_partner_by_name from gso.utils.helpers import active_router_selector from gso.utils.types.geant_ids import IMPORTED_GA_ID, IMPORTED_GS_ID from gso.utils.types.interfaces import LAGMember, LAGMemberList, PhysicalPortCapacity -from orchestrator import workflow -from orchestrator.forms import SubmitFormPage -from orchestrator.targets import Target -from orchestrator.types import SubscriptionLifecycle -from orchestrator.workflow import StepList, begin, done, step -from orchestrator.workflows.steps import resync, set_status, store_process_subscription def initial_input_form_generator() -> FormGenerator: @@ -39,7 +39,6 @@ def initial_input_form_generator() -> FormGenerator: iptrunk_minimum_links: int iptrunk_isis_metric: int iptrunk_description_suffix: str | None = None - iptrunk_config_version: str | None = None side_a_node_id: active_router_selector() # type: ignore[valid-type] side_a_ae_iface: str @@ -90,7 +89,6 @@ def initialize_subscription( side_b_ae_iface: str, side_b_ga_id: IMPORTED_GA_ID | None, side_b_ae_members: LAGMemberList, - iptrunk_config_version: str | None, ) -> State: """Take all input from the user, and store it in the database.""" subscription.iptrunk.gs_id = gs_id @@ -100,7 +98,6 @@ def initialize_subscription( subscription.iptrunk.iptrunk_isis_metric = iptrunk_isis_metric subscription.iptrunk.iptrunk_minimum_links = iptrunk_minimum_links subscription.iptrunk.iptrunk_description_suffix = iptrunk_description_suffix - subscription.iptrunk.iptrunk_config_version = iptrunk_config_version subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node = Router.from_subscription(side_a_node_id).router subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_iface = side_a_ae_iface diff --git a/gso/workflows/iptrunk/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py index 72f6a6c9a..cb9f83231 100644 --- a/gso/workflows/iptrunk/create_iptrunk.py +++ b/gso/workflows/iptrunk/create_iptrunk.py @@ -31,6 +31,15 @@ from typing import Annotated from uuid import uuid4 from annotated_types import Len +from orchestrator.forms import FormPage +from orchestrator.forms.validators import Choice, Label +from orchestrator.targets import Target +from orchestrator.types import SubscriptionLifecycle +from orchestrator.utils.errors import ProcessFailureError +from orchestrator.utils.json import json_dumps +from orchestrator.workflow import StepList, begin, conditional, done, step, step_group, workflow +from orchestrator.workflows.steps import resync, set_status, store_process_subscription +from orchestrator.workflows.utils import wrap_create_initial_input_form from ping3 import ping from pydantic import ConfigDict from pydantic_forms.types import FormGenerator, State, UUIDstr @@ -53,7 +62,7 @@ from gso.services.subscriptions import ( generate_unique_id, get_non_terminated_iptrunk_subscriptions, ) -from gso.settings import load_oss_params +from gso.settings import load_gso_service_config, load_oss_params from gso.utils.helpers import ( available_interfaces_choices, available_lags_choices, @@ -66,15 +75,6 @@ from gso.utils.types.netbox_router import NetboxEnabledRouter from gso.utils.types.tt_number import TTNumber from gso.utils.workflow_steps import prompt_sharepoint_checklist_url from gso.workflows.shared import create_summary_form -from orchestrator.forms import FormPage -from orchestrator.forms.validators import Choice, Label -from orchestrator.targets import Target -from orchestrator.types import SubscriptionLifecycle -from orchestrator.utils.errors import ProcessFailureError -from orchestrator.utils.json import json_dumps -from orchestrator.workflow import StepList, begin, conditional, done, step, step_group, workflow -from orchestrator.workflows.steps import resync, set_status, store_process_subscription -from orchestrator.workflows.utils import wrap_create_initial_input_form def initial_input_form_generator(product_name: str) -> FormGenerator: @@ -85,6 +85,10 @@ def initial_input_form_generator(product_name: str) -> FormGenerator: ) + subscriptions.get_provisioning_router_subscriptions(includes=["subscription_id", "description"]) routers = {str(router["subscription_id"]): router["description"] for router in active_and_provisioning_routers} + # Get version choices from config + iptrunk_versions = list(load_gso_service_config().IP_TRUNK.version.keys()) + iptrunk_version_choices = Choice("Select version", [(v, v) for v in iptrunk_versions]) # type: ignore[arg-type] + class CreateIptrunkForm(FormPage): model_config = ConfigDict(title=product_name) @@ -95,7 +99,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator: iptrunk_speed: PhysicalPortCapacity iptrunk_number_of_members: int iptrunk_description_suffix: str | None = None - iptrunk_config_version: str | None = None + iptrunk_config_version: iptrunk_version_choices # type: ignore[valid-type] initial_user_input = yield CreateIptrunkForm recommended_minimum_links = calculate_recommended_minimum_links( @@ -329,7 +333,7 @@ def initialize_subscription( iptrunk_speed: PhysicalPortCapacity, iptrunk_minimum_links: int, iptrunk_description_suffix: str | None, - iptrunk_config_version: str | None, + iptrunk_config_version: str, side_a_node_id: str, side_a_ae_iface: str, side_a_ae_members: list[dict], diff --git a/gso/workflows/iptrunk/modify_trunk_interface.py b/gso/workflows/iptrunk/modify_trunk_interface.py index 793c1bc00..391832078 100644 --- a/gso/workflows/iptrunk/modify_trunk_interface.py +++ b/gso/workflows/iptrunk/modify_trunk_interface.py @@ -12,9 +12,15 @@ from typing import Annotated from uuid import UUID, uuid4 from annotated_types import Len +from orchestrator.forms import FormPage, SubmitFormPage +from orchestrator.targets import Target +from orchestrator.utils.json import json_dumps +from orchestrator.workflow import StepList, begin, conditional, done, step, workflow +from orchestrator.workflows.steps import resync, store_process_subscription, unsync +from orchestrator.workflows.utils import wrap_modify_initial_input_form from pydantic import AfterValidator, ConfigDict, Field from pydantic_forms.types import FormGenerator, State, UUIDstr -from pydantic_forms.validators import Label, ReadOnlyField +from pydantic_forms.validators import Choice, Label, ReadOnlyField from gso.products.product_blocks.iptrunk import ( IptrunkInterfaceBlock, @@ -24,7 +30,7 @@ from gso.products.product_blocks.iptrunk import ( from gso.products.product_types.iptrunk import Iptrunk from gso.services.lso_client import LSOState, lso_interaction from gso.services.netbox_client import NetboxClient -from gso.settings import ServiceConfig, load_gso_service_config +from gso.settings import load_gso_service_config from gso.utils.helpers import ( available_interfaces_choices, available_interfaces_choices_including_current_members, @@ -38,12 +44,6 @@ from gso.utils.types.tt_number import TTNumber from gso.utils.types.unique_field import validate_field_is_unique from gso.workflows.iptrunk.migrate_iptrunk import check_ip_trunk_optical_levels_pre from gso.workflows.iptrunk.validate_iptrunk import check_ip_trunk_isis -from orchestrator.forms import FormPage, SubmitFormPage -from orchestrator.targets import Target -from orchestrator.utils.json import json_dumps -from orchestrator.workflow import StepList, begin, conditional, done, step, workflow -from orchestrator.workflows.steps import resync, store_process_subscription, unsync -from orchestrator.workflows.utils import wrap_modify_initial_input_form def initialize_ae_members( @@ -87,6 +87,10 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: """Gather input from the operator on the interfaces that should be modified.""" subscription = Iptrunk.from_subscription(subscription_id) + # Get version choices from config (single-select dropdown) + iptrunk_versions = list(load_gso_service_config().IP_TRUNK.version.keys()) + iptrunk_version_choices = Choice("Select version", [(v, v) for v in iptrunk_versions]) # type: ignore[arg-type] + class ModifyIptrunkForm(FormPage): tt_number: TTNumber gs_id: ( @@ -95,10 +99,8 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: ] | None ) = subscription.iptrunk.gs_id - iptrunk_description: str | None = subscription.iptrunk.iptrunk_description - iptrunk_config_version: load_gso_service_config().IP_TRUNK.version | str \ - = subscription.iptrunk.iptrunk_config_version + iptrunk_config_version: iptrunk_version_choices = subscription.iptrunk.iptrunk_config_version # type: ignore[valid-type] iptrunk_type: IptrunkType | str = subscription.iptrunk.iptrunk_type # FIXME: remove str workaround warning_label: Label = ( "Changing the PhyPortCapacity will result in the deletion of all AE members. " @@ -272,7 +274,7 @@ def modify_iptrunk_subscription( iptrunk_speed: PhysicalPortCapacity, iptrunk_minimum_links: int, iptrunk_description_suffix: str | None, - iptrunk_config_version: ServiceConfig.version, + iptrunk_config_version: str, side_a_ga_id: str | None, side_a_ae_members: list[dict], side_b_ga_id: str | None, @@ -539,8 +541,8 @@ def modify_trunk_interface() -> StepList: >> capacity_has_changed(lso_interaction(check_ip_trunk_connectivity)) >> capacity_has_changed(lso_interaction(check_ip_trunk_isis)) >> modify_iptrunk_subscription - # >> side_a_is_nokia(netbox_update_interfaces_side_a) - # >> side_b_is_nokia(netbox_update_interfaces_side_b) + >> side_a_is_nokia(netbox_update_interfaces_side_a) + >> side_b_is_nokia(netbox_update_interfaces_side_b) >> lso_interaction(provision_ip_trunk_iface_dry) >> lso_interaction(provision_ip_trunk_iface_real) >> side_a_is_nokia(allocate_interfaces_in_netbox_side_a) diff --git a/test/workflows/iptrunk/test_create_imported_iptrunk.py b/test/workflows/iptrunk/test_create_imported_iptrunk.py index 7add53de9..7f83775bb 100644 --- a/test/workflows/iptrunk/test_create_imported_iptrunk.py +++ b/test/workflows/iptrunk/test_create_imported_iptrunk.py @@ -23,7 +23,6 @@ def workflow_input_data(faker, router_subscription_factory): "iptrunk_minimum_links": 2, "iptrunk_isis_metric": 10000, "iptrunk_description_suffix": faker.word(), - "iptrunk_config_version": "1.0", "side_a_node_id": str(router_subscription_factory().subscription_id), "side_a_ae_iface": faker.nokia_lag_interface_name(), "side_a_ga_id": faker.imported_ga_id(), diff --git a/test/workflows/iptrunk/test_create_iptrunk.py b/test/workflows/iptrunk/test_create_iptrunk.py index c18577249..33c9640aa 100644 --- a/test/workflows/iptrunk/test_create_iptrunk.py +++ b/test/workflows/iptrunk/test_create_iptrunk.py @@ -74,7 +74,7 @@ def input_form_wizard_data(request, router_subscription_factory, faker): "iptrunk_speed": PhysicalPortCapacity.HUNDRED_GIGABIT_PER_SECOND, "iptrunk_number_of_members": 2, "iptrunk_description_suffix": faker.word(), - "iptrunk_config_version": "1.0" + "iptrunk_config_version": "1.0", } create_ip_trunk_confirm_step = {"iptrunk_minimum_links": 1} create_ip_trunk_side_a_router_name = {"side_a_node_id": router_side_a} diff --git a/test/workflows/iptrunk/test_modify_trunk_interface.py b/test/workflows/iptrunk/test_modify_trunk_interface.py index 4e72fbd72..7c11c9551 100644 --- a/test/workflows/iptrunk/test_modify_trunk_interface.py +++ b/test/workflows/iptrunk/test_modify_trunk_interface.py @@ -121,6 +121,7 @@ def input_form_iptrunk_data( "iptrunk_speed": new_speed, "iptrunk_number_of_members": new_link_count, "iptrunk_description_suffix": faker.word(), + "iptrunk_config_version": "1.0", }, {}, { -- GitLab From fa7aec1936e0459567526cea43ae377b18b9839a Mon Sep 17 00:00:00 2001 From: Karel van Klink <karel.vanklink@geant.org> Date: Mon, 26 May 2025 11:35:40 +0200 Subject: [PATCH 4/5] Move GSO service version config into oss-params --- gso/gso-services-config.json | 16 -------- gso/oss-params-example.json | 16 ++++++++ gso/settings.py | 37 ++++++++----------- gso/workflows/iptrunk/create_iptrunk.py | 4 +- .../iptrunk/modify_trunk_interface.py | 4 +- 5 files changed, 35 insertions(+), 42 deletions(-) delete mode 100644 gso/gso-services-config.json diff --git a/gso/gso-services-config.json b/gso/gso-services-config.json deleted file mode 100644 index 364ec44e6..000000000 --- a/gso/gso-services-config.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "IP_TRUNK": { - "version": { - "1.0": "Base Version", - "1.1": "Minor Upgrade" - }, - "default_version": "1.0" - }, - "GEANT_IP": { - "version": { - "1.0": "Base Version", - "2.0": "Major Upgrade" - }, - "default_version": "1.0" - } -} diff --git a/gso/oss-params-example.json b/gso/oss-params-example.json index ea85bf24e..2b60d84c8 100644 --- a/gso/oss-params-example.json +++ b/gso/oss-params-example.json @@ -130,5 +130,21 @@ "MOODI": { "host": "moodi.test.gap.geant.org", "moodi_enabled": true + }, + "SERVICE_VERSIONS": { + "IP_TRUNK": { + "version": { + "1.0": "Base Version", + "1.1": "Minor Upgrade" + }, + "default_version": "1.0" + }, + "GEANT_IP": { + "version": { + "1.0": "Base Version", + "2.0": "Major Upgrade" + }, + "default_version": "1.0" + } } } diff --git a/gso/settings.py b/gso/settings.py index 65af78ac8..8c1b0ec8e 100644 --- a/gso/settings.py +++ b/gso/settings.py @@ -228,6 +228,20 @@ class MoodiParams(BaseSettings): moodi_enabled: bool = False +class ServiceConfig(BaseSettings): + """Base configuration object for setting version information of a service.""" + + version: dict[str, str] + default_version: str + + +class ServiceVersionConfig(BaseSettings): + """Services offered by GSO that support multiple versions.""" + + IP_TRUNK: ServiceConfig + GEANT_IP: ServiceConfig + + class OSSParams(BaseSettings): """The set of parameters required for running GSO.""" @@ -242,6 +256,7 @@ class OSSParams(BaseSettings): KENTIK: KentikParams SENTRY: SentryParams | None = None MOODI: MoodiParams + SERVICE_VERSIONS: ServiceVersionConfig def load_oss_params() -> OSSParams: @@ -250,29 +265,7 @@ def load_oss_params() -> OSSParams: return OSSParams(**json.loads(file.read())) -class ServiceConfig(BaseSettings): - """Configurations for base gso service.""" - - version: dict[str, str] - default_version: str - - -class GSOServiceConfig(BaseSettings): - """Configuration for the GSO service.""" - - IP_TRUNK: ServiceConfig - GEANT_IP: ServiceConfig - - -def load_gso_service_config() -> GSOServiceConfig: - """Load the GSO service configuration from the environment variable.""" - """Look for ``GSO_SERVICE_CONFIG`` in the environment and load the parameters from that file.""" - with Path(os.environ["GSO_SERVICE_CONFIG"]).open(encoding="utf-8") as file: - return GSOServiceConfig(**json.loads(file.read())) - - celery_settings = CelerySettings() if __name__ == "__main__": logger.debug(load_oss_params()) - logger.debug(load_gso_service_config()) diff --git a/gso/workflows/iptrunk/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py index cb9f83231..04b0d1459 100644 --- a/gso/workflows/iptrunk/create_iptrunk.py +++ b/gso/workflows/iptrunk/create_iptrunk.py @@ -62,7 +62,7 @@ from gso.services.subscriptions import ( generate_unique_id, get_non_terminated_iptrunk_subscriptions, ) -from gso.settings import load_gso_service_config, load_oss_params +from gso.settings import load_oss_params from gso.utils.helpers import ( available_interfaces_choices, available_lags_choices, @@ -86,7 +86,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator: routers = {str(router["subscription_id"]): router["description"] for router in active_and_provisioning_routers} # Get version choices from config - iptrunk_versions = list(load_gso_service_config().IP_TRUNK.version.keys()) + iptrunk_versions = list(load_oss_params().SERVICE_VERSIONS.IP_TRUNK.version.keys()) iptrunk_version_choices = Choice("Select version", [(v, v) for v in iptrunk_versions]) # type: ignore[arg-type] class CreateIptrunkForm(FormPage): diff --git a/gso/workflows/iptrunk/modify_trunk_interface.py b/gso/workflows/iptrunk/modify_trunk_interface.py index 391832078..1f3e9f543 100644 --- a/gso/workflows/iptrunk/modify_trunk_interface.py +++ b/gso/workflows/iptrunk/modify_trunk_interface.py @@ -30,7 +30,7 @@ from gso.products.product_blocks.iptrunk import ( from gso.products.product_types.iptrunk import Iptrunk from gso.services.lso_client import LSOState, lso_interaction from gso.services.netbox_client import NetboxClient -from gso.settings import load_gso_service_config +from gso.settings import load_oss_params from gso.utils.helpers import ( available_interfaces_choices, available_interfaces_choices_including_current_members, @@ -88,7 +88,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: subscription = Iptrunk.from_subscription(subscription_id) # Get version choices from config (single-select dropdown) - iptrunk_versions = list(load_gso_service_config().IP_TRUNK.version.keys()) + iptrunk_versions = list(load_oss_params().SERVICE_VERSIONS.IP_TRUNK.version.keys()) iptrunk_version_choices = Choice("Select version", [(v, v) for v in iptrunk_versions]) # type: ignore[arg-type] class ModifyIptrunkForm(FormPage): -- GitLab From ea5a7091b19fcd3c4d25e7889ed12843e90c1843 Mon Sep 17 00:00:00 2001 From: Karel van Klink <karel.vanklink@geant.org> Date: Mon, 26 May 2025 15:25:12 +0200 Subject: [PATCH 5/5] Abstract IP trunk version selector into utils --- gso/utils/helpers.py | 21 +++++++++++++++---- gso/workflows/iptrunk/create_iptrunk.py | 7 ++----- .../iptrunk/modify_trunk_interface.py | 10 +++------ test/workflows/iptrunk/test_create_iptrunk.py | 2 +- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/gso/utils/helpers.py b/gso/utils/helpers.py index c20e8a4e7..9a73d9c78 100644 --- a/gso/utils/helpers.py +++ b/gso/utils/helpers.py @@ -10,7 +10,6 @@ from orchestrator.types import SubscriptionLifecycle from pydantic_forms.types import UUIDstr from pydantic_forms.validators import Choice -from gso import settings from gso.products.product_blocks.layer_2_circuit import Layer2CircuitType from gso.products.product_blocks.router import RouterRole from gso.products.product_types.router import Router @@ -25,6 +24,7 @@ from gso.services.subscriptions import ( get_router_subscriptions, is_virtual_circuit_id_available, ) +from gso.settings import load_oss_params from gso.utils.shared_enums import Vendor from gso.utils.types.interfaces import PhysicalPortCapacity from gso.utils.types.ip_address import IPv4AddressType, IPv4NetworkType, IPv6NetworkType @@ -131,13 +131,13 @@ def iso_from_ipv4(ipv4_address: IPv4AddressType) -> str: def generate_fqdn(hostname: str, site_name: str, country_code: str) -> str: """Generate an FQDN from a hostname, site name, and a country code.""" - oss = settings.load_oss_params() + oss = load_oss_params() return f"{hostname}.{site_name.lower()}.{country_code.lower()}{oss.IPAM.LO.domain_name}" def generate_lan_switch_interconnect_subnet_v4(site_internal_id: int) -> IPv4NetworkType: """Generate an IPv4 network in which a LAN Switch Interconnect resides, given a Site internal ID.""" - ipam_oss = settings.load_oss_params().IPAM.LAN_SWITCH_INTERCONNECT + ipam_oss = load_oss_params().IPAM.LAN_SWITCH_INTERCONNECT result = str(ipam_oss.V4.containers[0]).split(".")[:2] # Take the first two octets from the IPv4 network. result.append(str(site_internal_id)) # Append the side ID as the third octet. @@ -148,7 +148,7 @@ def generate_lan_switch_interconnect_subnet_v4(site_internal_id: int) -> IPv4Net def generate_lan_switch_interconnect_subnet_v6(site_internal_id: int) -> IPv6NetworkType: """Generate an IPv6 network in which a LAN Switch Interconnect resides, given a Site internal ID.""" - ipam_oss = settings.load_oss_params().IPAM.LAN_SWITCH_INTERCONNECT + ipam_oss = load_oss_params().IPAM.LAN_SWITCH_INTERCONNECT result = IPv6Network(ipam_oss.V6.containers[0]).exploded[:17] # Take the first 56 bits of the network result += str(hex(site_internal_id)[2:]) # Append the site internal id for bytes 57 to 64 as hexadecimal number @@ -283,6 +283,19 @@ def active_edge_port_selector(*, partner_id: UUIDstr | None = None) -> TypeAlias ) +def ip_trunk_service_version_selector() -> TypeAlias: + """Generate a dropdown selector for choosing a service version.""" + iptrunk_versions = load_oss_params().SERVICE_VERSIONS.IP_TRUNK.version + + return cast( + type[Choice], + Choice.__call__( + "Select an IP trunk service version.", + [(k, f"Version {k} - {iptrunk_versions[k]}") for k in iptrunk_versions], + ), + ) + + def partner_choice() -> Choice: """Return a Choice object containing a list of available partners.""" partners = {partner.partner_id: partner.name for partner in get_all_partners()} diff --git a/gso/workflows/iptrunk/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py index 04b0d1459..e151068a2 100644 --- a/gso/workflows/iptrunk/create_iptrunk.py +++ b/gso/workflows/iptrunk/create_iptrunk.py @@ -68,6 +68,7 @@ from gso.utils.helpers import ( available_lags_choices, calculate_recommended_minimum_links, get_router_vendor, + ip_trunk_service_version_selector, ) from gso.utils.shared_enums import Vendor from gso.utils.types.interfaces import JuniperLAGMember, LAGMember, LAGMemberList, PhysicalPortCapacity @@ -85,10 +86,6 @@ def initial_input_form_generator(product_name: str) -> FormGenerator: ) + subscriptions.get_provisioning_router_subscriptions(includes=["subscription_id", "description"]) routers = {str(router["subscription_id"]): router["description"] for router in active_and_provisioning_routers} - # Get version choices from config - iptrunk_versions = list(load_oss_params().SERVICE_VERSIONS.IP_TRUNK.version.keys()) - iptrunk_version_choices = Choice("Select version", [(v, v) for v in iptrunk_versions]) # type: ignore[arg-type] - class CreateIptrunkForm(FormPage): model_config = ConfigDict(title=product_name) @@ -99,7 +96,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator: iptrunk_speed: PhysicalPortCapacity iptrunk_number_of_members: int iptrunk_description_suffix: str | None = None - iptrunk_config_version: iptrunk_version_choices # type: ignore[valid-type] + iptrunk_config_version: ip_trunk_service_version_selector() # type: ignore[valid-type] initial_user_input = yield CreateIptrunkForm recommended_minimum_links = calculate_recommended_minimum_links( diff --git a/gso/workflows/iptrunk/modify_trunk_interface.py b/gso/workflows/iptrunk/modify_trunk_interface.py index 1f3e9f543..9bb651f5e 100644 --- a/gso/workflows/iptrunk/modify_trunk_interface.py +++ b/gso/workflows/iptrunk/modify_trunk_interface.py @@ -20,7 +20,7 @@ from orchestrator.workflows.steps import resync, store_process_subscription, uns from orchestrator.workflows.utils import wrap_modify_initial_input_form from pydantic import AfterValidator, ConfigDict, Field from pydantic_forms.types import FormGenerator, State, UUIDstr -from pydantic_forms.validators import Choice, Label, ReadOnlyField +from pydantic_forms.validators import Label, ReadOnlyField from gso.products.product_blocks.iptrunk import ( IptrunkInterfaceBlock, @@ -30,12 +30,12 @@ from gso.products.product_blocks.iptrunk import ( from gso.products.product_types.iptrunk import Iptrunk from gso.services.lso_client import LSOState, lso_interaction from gso.services.netbox_client import NetboxClient -from gso.settings import load_oss_params from gso.utils.helpers import ( available_interfaces_choices, available_interfaces_choices_including_current_members, calculate_recommended_minimum_links, get_router_vendor, + ip_trunk_service_version_selector, ) from gso.utils.shared_enums import Vendor from gso.utils.types.interfaces import JuniperLAGMember, LAGMember, LAGMemberList, PhysicalPortCapacity @@ -87,10 +87,6 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: """Gather input from the operator on the interfaces that should be modified.""" subscription = Iptrunk.from_subscription(subscription_id) - # Get version choices from config (single-select dropdown) - iptrunk_versions = list(load_oss_params().SERVICE_VERSIONS.IP_TRUNK.version.keys()) - iptrunk_version_choices = Choice("Select version", [(v, v) for v in iptrunk_versions]) # type: ignore[arg-type] - class ModifyIptrunkForm(FormPage): tt_number: TTNumber gs_id: ( @@ -100,7 +96,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: | None ) = subscription.iptrunk.gs_id iptrunk_description: str | None = subscription.iptrunk.iptrunk_description - iptrunk_config_version: iptrunk_version_choices = subscription.iptrunk.iptrunk_config_version # type: ignore[valid-type] + iptrunk_config_version: ip_trunk_service_version_selector() | str = subscription.iptrunk.iptrunk_config_version # type: ignore[valid-type] iptrunk_type: IptrunkType | str = subscription.iptrunk.iptrunk_type # FIXME: remove str workaround warning_label: Label = ( "Changing the PhyPortCapacity will result in the deletion of all AE members. " diff --git a/test/workflows/iptrunk/test_create_iptrunk.py b/test/workflows/iptrunk/test_create_iptrunk.py index 33c9640aa..851c3e308 100644 --- a/test/workflows/iptrunk/test_create_iptrunk.py +++ b/test/workflows/iptrunk/test_create_iptrunk.py @@ -74,7 +74,7 @@ def input_form_wizard_data(request, router_subscription_factory, faker): "iptrunk_speed": PhysicalPortCapacity.HUNDRED_GIGABIT_PER_SECOND, "iptrunk_number_of_members": 2, "iptrunk_description_suffix": faker.word(), - "iptrunk_config_version": "1.0", + "iptrunk_config_version": "Version 1.0 - Base Version", } create_ip_trunk_confirm_step = {"iptrunk_minimum_links": 1} create_ip_trunk_side_a_router_name = {"side_a_node_id": router_side_a} -- GitLab