diff --git a/.gitignore b/.gitignore index fcfacde53b94401ea65e66f7390c2dc9da6c4040..44a480afac83055105a6ed8f4b467885b95bc640 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,9 @@ docs/wf_redirects.yaml .idea .venv .env + + +# Ignore files for local development +config.json +dockerfile.dev +gso.env diff --git a/Changelog.md b/Changelog.md index 7b6914b5a5164d528a80a7d7d8d8692b3a0ebe8a..9c8c135efe07fafcdcdb69f857f8c9c236dc05b5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,11 @@ # Changelog +## [2.39] - 2025-02-24 +- Fixed L3 service data import. +- Automatically update connection method to OOB for the router + when removing (migrating) its last connecting trunk. +- Updates in `migrate_l3_core_service` WF. + ## [2.38] - 2025-02-14 - fix validate base config if there is a vprn diff --git a/gso/__init__.py b/gso/__init__.py index 610b109831a99599c7d2551fb8d1d88634cd5f8a..e07302fba15c90ffea41117b1ac971d1a0a529d3 100644 --- a/gso/__init__.py +++ b/gso/__init__.py @@ -16,7 +16,7 @@ from gso.auth.oidc import oidc_instance from gso.auth.opa import graphql_opa_instance, opa_instance from gso.graphql_api.resolvers.customer import custom_subscription_interface from gso.graphql_api.types import GSO_SCALAR_OVERRIDES -from gso.settings import load_oss_params +from gso.settings import celery_settings, load_oss_params SCALAR_OVERRIDES.update(GSO_SCALAR_OVERRIDES) @@ -36,15 +36,14 @@ def init_gso_app() -> OrchestratorCore: app.register_graphql(subscription_interface=custom_subscription_interface) app.include_router(api_router, prefix="/api") - oss_params = load_oss_params() celery = Celery( "geant-service-orchestrator", - broker=oss_params.CELERY.broker_url, - backend=oss_params.CELERY.result_backend, + broker=celery_settings.broker_url, + backend=celery_settings.result_backend, include=["orchestrator.services.tasks", "gso.tasks.start_process"], ) celery.conf.update( - result_expires=oss_params.CELERY.result_expires, + result_expires=celery_settings.result_expires, task_always_eager=app_settings.TESTING, task_eager_propagates=app_settings.TESTING, ) diff --git a/gso/cli/imports.py b/gso/cli/imports.py index 1fcb35105bcdc946d57019543dddfb025e96bad8..6444aba8963fcd8c2364eccc0afb3ee85b40d59b 100644 --- a/gso/cli/imports.py +++ b/gso/cli/imports.py @@ -25,6 +25,7 @@ from gso.products.product_blocks.layer_2_circuit import Layer2CircuitType from gso.products.product_blocks.router import RouterRole from gso.products.product_blocks.switch import SwitchModel from gso.products.product_types.edge_port import EdgePort +from gso.products.product_types.l3_core_service import L3CoreServiceType from gso.products.product_types.layer_2_circuit import Layer2CircuitServiceType from gso.services.partners import ( PartnerEmail, @@ -287,6 +288,7 @@ class L3CoreServiceImportModel(BaseModel): partner: str service_binding_ports: list[ServiceBindingPort] + service_type: L3CoreServiceType @field_validator("partner") def check_if_partner_exists(cls, value: str) -> str: diff --git a/gso/oss-params-example.json b/gso/oss-params-example.json index 1a1495e4d56813a02048fc1d0698e99f2ea1766e..5c8134c56a4f3c82e07dec89adeddbcc2380805e 100644 --- a/gso/oss-params-example.json +++ b/gso/oss-params-example.json @@ -84,11 +84,6 @@ "api_base": "localhost:44444", "api_version": 1123 }, - "CELERY": { - "broker_url": "redis://localhost:6379/0", - "result_backend": "rpc://localhost:6379/0", - "result_expires": 3600 - }, "THIRD_PARTY_API_KEYS": { "AnsibleDynamicInventoryGenerator": "REALLY_random_AND_secure_T0keN", "Application_2": "another_REALY_random_AND_3cure_T0keN" diff --git a/gso/products/product_blocks/layer_2_circuit.py b/gso/products/product_blocks/layer_2_circuit.py index 159177d5f8f3c80b63cdb5130ebfcf79eae0c4ab..08142b3f0bec47718c20e2e17286d9eafe9fe2f5 100644 --- a/gso/products/product_blocks/layer_2_circuit.py +++ b/gso/products/product_blocks/layer_2_circuit.py @@ -62,10 +62,10 @@ Layer2CircuitSides = Annotated[ class Layer2CircuitType(strEnum): """The two types of Layer 2 Circuit.""" - TAGGED = "TAGGED" - """Tagged.""" - UNTAGGED = "UNTAGGED" - """Untagged.""" + VLAN = "VLAN" + """VLAN.""" + ETHERNET = "Ethernet" + """Ethernet.""" class Layer2CircuitBlockInactive( @@ -104,8 +104,8 @@ class Layer2CircuitBlock(Layer2CircuitBlockProvisioning, lifecycle=[Subscription Attributes: layer_2_circuit_sides: The two sides that the Layer 2 Circuit is connected to. virtual_circuit_id: Virtual Circuit ID of this Layer 2 Circuit. - layer_2_circuit_type: The type of circuit, can be tagged or untagged. - vlan_range_lower_bound: If tagged, the lower and upper bounds will set the VLAN range. + layer_2_circuit_type: The type of circuit, can be Vlan or Ethernet. + vlan_range_lower_bound: If type is VLAN, the lower and upper bounds will set the VLAN range. vlan_range_upper_bound: Lower and Upper bounds are including. policer_enabled: Whether this Layer 2 Circuit is policed. policer_burst_rate: If policed, the burst rate of the policer. diff --git a/gso/services/subscriptions.py b/gso/services/subscriptions.py index 102b2ddb29ea0954cd57c6a8b55dbbde8788e76a..c09641194beed749866228cbf17d82764b4b7f26 100644 --- a/gso/services/subscriptions.py +++ b/gso/services/subscriptions.py @@ -250,6 +250,30 @@ def get_active_vrfs_linked_to_router(router_id: UUIDstr) -> list[SubscriptionMod return [SubscriptionModel.from_subscription(result.subscription_id) for result in results] +def get_active_ip_trunks_linked_to_router(router_id: UUIDstr) -> list[SubscriptionModel]: + """Retrieve all active IP trunks that are linked to the router. + + Args: + router_id: The ID of the router. + + Returns: + A list of active IP trunks that are linked to the router. + """ + results = ( + query_in_use_by_subscriptions(UUID(router_id)) + .join(ProductTable) + .filter( + and_( + ProductTable.product_type == ProductType.IP_TRUNK.value, + SubscriptionTable.status == SubscriptionLifecycle.ACTIVE, + ) + ) + .all() + ) + + return [SubscriptionModel.from_subscription(result.subscription_id) for result in results] + + def get_product_id_by_name(product_name: ProductName) -> UUID: """Retrieve the UUID of a product by its name. diff --git a/gso/settings.py b/gso/settings.py index 5c5b646d3f0ee93715aba5a675d126d3e2d9e280..e7326dbbcb27b6c0fcef7a5dc15c9b5bbc482fb3 100644 --- a/gso/settings.py +++ b/gso/settings.py @@ -44,15 +44,18 @@ class GeneralParams(BaseSettings): environment: EnvironmentEnum -class CeleryParams(BaseSettings): +class CelerySettings(BaseSettings): """Parameters for Celery.""" - broker_url: str - result_backend: str - timezone: str = "Europe/Amsterdam" - enable_utc: bool = True + broker_url: str = "redis://localhost:6379/0" + result_backend: str = "rpc://localhost:6379/0" result_expires: int = 3600 + class Config: + """The prefix for the environment variables.""" + + env_prefix = "CELERY_" + class InfoBloxParams(BaseSettings): """Parameters related to InfoBlox.""" @@ -231,7 +234,6 @@ class OSSParams(BaseSettings): NETBOX: NetBoxParams MONITORING: MonitoringParams PROVISIONING_PROXY: ProvisioningProxyParams - CELERY: CeleryParams THIRD_PARTY_API_KEYS: dict[str, str] EMAIL: EmailParams SHAREPOINT: SharepointParams @@ -246,5 +248,7 @@ def load_oss_params() -> OSSParams: return OSSParams(**json.loads(file.read())) +celery_settings = CelerySettings() + if __name__ == "__main__": logger.debug(load_oss_params()) diff --git a/gso/worker.py b/gso/worker.py index 3a28ba925d057537fee60e1d0385b6b72ca617da..ed9c01ffc5fc3edf055ad48a60100f8e1911f56c 100644 --- a/gso/worker.py +++ b/gso/worker.py @@ -17,7 +17,7 @@ from orchestrator.workflows import ALL_WORKFLOWS from structlog import get_logger from gso import gso_initialise_celery -from gso.settings import load_oss_params +from gso.settings import celery_settings logger = get_logger(__name__) @@ -72,12 +72,10 @@ class OrchestratorWorker(Celery): super().close() -settings = load_oss_params() - celery = OrchestratorWorker( "geant-service-orchestrator-worker", - broker=settings.CELERY.broker_url, - backend=settings.CELERY.result_backend, + broker=celery_settings.broker_url, + backend=celery_settings.result_backend, include=[ "gso.schedules.task_vacuum", "gso.schedules.validate_products", @@ -89,17 +87,13 @@ celery = OrchestratorWorker( ], ) -if app_settings.TESTING: - celery.conf.update(backend=settings.CELERY.result_backend, task_ignore_result=False) -else: - celery.conf.update(task_ignore_result=True) - celery.conf.update( - result_expires=settings.CELERY.result_expires, + result_expires=celery_settings.result_expires, worker_prefetch_multiplier=1, worker_send_task_event=True, task_send_sent_event=True, - redbeat_redis_url=settings.CELERY.broker_url, + redbeat_redis_url=celery_settings.broker_url, + task_ignore_result=not app_settings.TESTING, ) gso_initialise_celery(celery) diff --git a/gso/workflows/iptrunk/migrate_iptrunk.py b/gso/workflows/iptrunk/migrate_iptrunk.py index 0d3c0d242d142bab2987b4fcb0fe503be35cfb4a..60602b8e6b9034c2289948186493e8ba499c32fa 100644 --- a/gso/workflows/iptrunk/migrate_iptrunk.py +++ b/gso/workflows/iptrunk/migrate_iptrunk.py @@ -33,7 +33,7 @@ from gso.services import infoblox from gso.services.lso_client import LSOState, lso_interaction from gso.services.netbox_client import NetboxClient from gso.services.sharepoint import SharePointClient -from gso.services.subscriptions import get_active_router_subscriptions +from gso.services.subscriptions import get_active_ip_trunks_linked_to_router, get_active_router_subscriptions from gso.settings import load_oss_params from gso.utils.helpers import ( available_interfaces_choices, @@ -824,6 +824,29 @@ def create_new_sharepoint_checklist(subscription: Iptrunk, tt_number: str, proce return {"checklist_url": new_list_item_url} +@step("Update the subscription model to use the out-of-band connection") +def modify_connection_strategy_to_out_of_band(subscription: Iptrunk, replace_index: int) -> State: + """Modify the replacement side of the IP trunk to use the out-of-band connection strategy. + + This is necessary when the router is connected to only one IP trunk. + + Note: + The `router` retrieved from `subscription.iptrunk.iptrunk_sides[replace_index].iptrunk_side_node` + will be replaced and is no longer part of this subscription. Because of this, it does not get updated + automatically. To ensure consistency, we need to manually update it by calling `router.save()`. + Alternatively, we could run a separate asynchronous `modify_connection_strategy` migration, + but we have opted for the manual update approach here. + """ + iptrunk_side_node = subscription.iptrunk.iptrunk_sides[replace_index].iptrunk_side_node + iptrunk_side_node.router_access_via_ts = True + + iptrunk_side_node.save( + subscription_id=iptrunk_side_node.owner_subscription_id, status=iptrunk_side_node.subscription.status + ) + + return {"subscription": subscription} + + @workflow( "Migrate an IP Trunk", initial_input_form=wrap_modify_initial_input_form(initial_input_form_generator), @@ -855,6 +878,13 @@ def migrate_iptrunk() -> StepList: lambda state: state["subscription"]["iptrunk"]["iptrunk_type"] == IptrunkType.LEASED ) + def _is_router_connected_to_one_ip_trunk(state: State) -> bool: + iptrunk = Iptrunk.from_subscription(state["subscription"]["subscription_id"]) + router_id = iptrunk.iptrunk.iptrunk_sides[state["replace_index"]].iptrunk_side_node.owner_subscription_id + return len(get_active_ip_trunks_linked_to_router(str(router_id))) == 1 + + if_router_connected_to_one_ip_trunk = conditional(_is_router_connected_to_one_ip_trunk) + return ( begin >> store_process_subscription(Target.MODIFY) @@ -863,6 +893,7 @@ def migrate_iptrunk() -> StepList: >> calculate_old_side_data >> lso_interaction(set_isis_to_max) >> lso_interaction(check_ip_trunk_optical_levels_pre) + >> if_router_connected_to_one_ip_trunk(modify_connection_strategy_to_out_of_band) >> lso_interaction(disable_old_config_dry) >> lso_interaction(disable_old_config_real) >> lso_interaction(deploy_new_config_dry) diff --git a/gso/workflows/l2_circuit/create_imported_layer_2_circuit.py b/gso/workflows/l2_circuit/create_imported_layer_2_circuit.py index 9d7f0f7a7c04cf2a9cf92e689d9709f8efec2f24..6dbd052ebc9d9d9494e42152d4b89a1d0a85cf26 100644 --- a/gso/workflows/l2_circuit/create_imported_layer_2_circuit.py +++ b/gso/workflows/l2_circuit/create_imported_layer_2_circuit.py @@ -53,13 +53,13 @@ def initial_input_form_generator() -> FormGenerator: custom_service_name: str | None = None @model_validator(mode="after") - def tagged_layer_2_circuit_has_vlan_bounds(self) -> Self: - """If a Layer 2 Circuit is tagged, it must have a VLAN range set.""" - if self.layer_2_circuit_type == Layer2CircuitType.TAGGED and ( + def vlan_layer_2_circuit_has_vlan_bounds(self) -> Self: + """If a Layer 2 Circuit is VLAN, it must have a VLAN range set.""" + if self.layer_2_circuit_type == Layer2CircuitType.VLAN and ( self.vlan_range_lower_bound is None or self.vlan_range_upper_bound is None ): msg = ( - f"A tagged Layer 2 Circuit must have a VLAN range set. Received lower: " + f"A VLAN Layer 2 Circuit must have a VLAN range set. Received lower: " f"{self.vlan_range_lower_bound}, upper: {self.vlan_range_upper_bound}." ) raise ValueError(msg) @@ -114,7 +114,7 @@ def initialize_subscription( sbp_type=SBPType.L2, vlan_id=circuit_side_data["vlan_id"], gs_id=gs_id, - is_tagged=layer_2_circuit_type == Layer2CircuitType.TAGGED, + is_tagged=layer_2_circuit_type == Layer2CircuitType.VLAN, custom_firewall_filters=False, ) layer2_circuit_side = Layer2CircuitSideBlockInactive.new(uuid4(), sbp=sbp) diff --git a/gso/workflows/l2_circuit/create_layer_2_circuit.py b/gso/workflows/l2_circuit/create_layer_2_circuit.py index 2548639e5471aad73093481b58da78ed73cfe2cf..20cc4c866199dae6bc50b418cf61e78d18597d1b 100644 --- a/gso/workflows/l2_circuit/create_layer_2_circuit.py +++ b/gso/workflows/l2_circuit/create_layer_2_circuit.py @@ -50,9 +50,9 @@ def initial_input_generator(product_name: str) -> FormGenerator: ) vlan_id: VLAN_ID - def _vlan_range_field(*, is_tagged: bool) -> VLAN_ID: - """Return the appropriate field type based on whether the circuit is tagged.""" - return VLAN_ID if is_tagged else ReadOnlyField(None, default_type=int) + def _vlan_range_field(*, is_vlan: bool) -> VLAN_ID: + """Return the appropriate field type based on whether the circuit is VLAN.""" + return VLAN_ID if is_vlan else ReadOnlyField(None, default_type=int) def _policer_field(*, policer_enabled: bool) -> BandwidthString: """Return the appropriate field type based on whether the policer is enabled.""" @@ -63,10 +63,10 @@ def initial_input_generator(product_name: str) -> FormGenerator: vlan_range_label: Label = Field("Please set a VLAN range, bounds including.", exclude=True) vlan_range_lower_bound: _vlan_range_field( # type: ignore[valid-type] - is_tagged=initial_user_input.layer_2_circuit_type == Layer2CircuitType.TAGGED + is_vlan=initial_user_input.layer_2_circuit_type == Layer2CircuitType.VLAN ) vlan_range_upper_bound: _vlan_range_field( # type: ignore[valid-type] - is_tagged=initial_user_input.layer_2_circuit_type == Layer2CircuitType.TAGGED + is_vlan=initial_user_input.layer_2_circuit_type == Layer2CircuitType.VLAN ) vlan_divider: Divider = Field(None, exclude=True) policer_bandwidth: _policer_field(policer_enabled=initial_user_input.policer_enabled) # type: ignore[valid-type] @@ -118,7 +118,7 @@ def initialize_subscription( sbp_type=SBPType.L2, vlan_id=circuit_side_data["vlan_id"], gs_id=gs_id, - is_tagged=layer_2_circuit_type == Layer2CircuitType.TAGGED, + is_tagged=layer_2_circuit_type == Layer2CircuitType.VLAN, custom_firewall_filters=False, ) layer2_circuit_side = Layer2CircuitSideBlockInactive.new(uuid4(), sbp=sbp) diff --git a/gso/workflows/l2_circuit/modify_layer_2_circuit.py b/gso/workflows/l2_circuit/modify_layer_2_circuit.py index 6e378a779c28d9e328ce4546ec91f465acf5a750..6d7b15ff3a6d72f9e1b19e18a4f75e44c051193b 100644 --- a/gso/workflows/l2_circuit/modify_layer_2_circuit.py +++ b/gso/workflows/l2_circuit/modify_layer_2_circuit.py @@ -40,7 +40,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: model_config = ConfigDict(title=f"{product_name} - Configure Edge Ports") vlan_range_label: Label = Field("Please set a VLAN range, bounds including.", exclude=True) - if layer_2_circuit_input.layer_2_circuit_type == Layer2CircuitType.TAGGED: + if layer_2_circuit_input.layer_2_circuit_type == Layer2CircuitType.VLAN: vlan_range_lower_bound: VLAN_ID = subscription.layer_2_circuit.vlan_range_lower_bound # type: ignore[assignment] vlan_range_upper_bound: VLAN_ID = subscription.layer_2_circuit.vlan_range_upper_bound # type: ignore[assignment] else: @@ -95,7 +95,7 @@ def modify_layer_2_circuit_subscription( subscription.layer_2_circuit.policer_burst_rate = policer_burst_rate subscription.layer_2_circuit.custom_service_name = custom_service_name for layer_2_circuit_side in subscription.layer_2_circuit.layer_2_circuit_sides: - layer_2_circuit_side.sbp.is_tagged = layer_2_circuit_type == Layer2CircuitType.TAGGED + layer_2_circuit_side.sbp.is_tagged = layer_2_circuit_type == Layer2CircuitType.VLAN return {"subscription": subscription} diff --git a/gso/workflows/l3_core_service/create_imported_l3_core_service.py b/gso/workflows/l3_core_service/create_imported_l3_core_service.py index f8104c016e217369106c7c53bd1a8c857ce1dbde..ebb73fb95687fbd6b31f59861c8f2bc1e27bba6f 100644 --- a/gso/workflows/l3_core_service/create_imported_l3_core_service.py +++ b/gso/workflows/l3_core_service/create_imported_l3_core_service.py @@ -118,7 +118,7 @@ def initialize_subscription(subscription: ImportedL3CoreServiceInactive, service service_binding_port_subscription = ServiceBindingPortInactive.new( subscription_id=uuid4(), edge_port=edge_port_subscription.edge_port, - sbp_bgp_session_list=sbp_bgp_session_list, + bgp_session_list=sbp_bgp_session_list, v4_bfd_settings=BFDSettings.new(subscription_id=uuid4(), **(service_binding_port.pop("v4_bfd_settings"))), v6_bfd_settings=BFDSettings.new(subscription_id=uuid4(), **(service_binding_port.pop("v6_bfd_settings"))), **service_binding_port, @@ -132,8 +132,6 @@ def initialize_subscription(subscription: ImportedL3CoreServiceInactive, service ) ) - subscription.description = f"{subscription.product} service" - return {"subscription": subscription} diff --git a/gso/workflows/l3_core_service/import_l3_core_service.py b/gso/workflows/l3_core_service/import_l3_core_service.py index 9c8118142719685b58a337257d27c9c192d9dbb7..d7f0d06ee85ebb28e8a1dac60769d8f4014187a7 100644 --- a/gso/workflows/l3_core_service/import_l3_core_service.py +++ b/gso/workflows/l3_core_service/import_l3_core_service.py @@ -13,6 +13,7 @@ from gso.products.product_types.l3_core_service import ( L3CoreService, L3CoreServiceType, ) +from gso.services.partners import get_partner_by_id from gso.services.subscriptions import get_product_id_by_name @@ -35,7 +36,9 @@ def import_l3_core_service_subscription(subscription_id: UUIDstr) -> State: msg = f"This {old_l3_core_service.l3_core_service_type} is already imported, nothing to do." raise ProcessFailureError(message=msg, details=old_l3_core_service) new_subscription = L3CoreService.from_other_product(old_l3_core_service, new_subscription_id) # type: ignore[arg-type] - + new_subscription.description = ( + f"{new_subscription.product.name} service for {get_partner_by_id(new_subscription.customer_id).name}" + ) return {"subscription": new_subscription} diff --git a/gso/workflows/l3_core_service/migrate_l3_core_service.py b/gso/workflows/l3_core_service/migrate_l3_core_service.py index 704190fbe9c761fa80afa0ef1a266fbdd58282af..6a0ff8a8d36435e418399e8b87571be02fc5a788 100644 --- a/gso/workflows/l3_core_service/migrate_l3_core_service.py +++ b/gso/workflows/l3_core_service/migrate_l3_core_service.py @@ -27,6 +27,7 @@ from pydantic_forms.validators import Choice, Divider, Label from gso.products.product_types.edge_port import EdgePort from gso.products.product_types.l3_core_service import L3CoreService from gso.services.lso_client import LSOState, lso_interaction +from gso.services.partners import get_partner_by_id from gso.services.subscriptions import get_active_edge_port_subscriptions from gso.utils.types.tt_number import TTNumber from gso.utils.workflow_steps import IS_HUMAN_INITIATED_WF_KEY, SKIP_MOODI_KEY, start_moodi, stop_moodi @@ -101,9 +102,17 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: ) +@step("Inject Partner Name") +def inject_partner_name(subscription: L3CoreService) -> LSOState: + """Resolve and inject partner name into the state.""" + partner_name = get_partner_by_id(subscription.customer_id).name + + return {"partner_name": partner_name} + + @step("Show BGP neighbors") def show_bgp_neighbors( - subscription: L3CoreService, process_id: UUIDstr, tt_number: TTNumber, source_edge_port: EdgePort + subscription: L3CoreService, process_id: UUIDstr, tt_number: TTNumber, source_edge_port: EdgePort, partner_name: str ) -> LSOState: """List all BGP neighbors on the source router, to present an expected base-line for the new one.""" source_access_port_fqdn = source_edge_port.edge_port.node.router_fqdn @@ -114,7 +123,9 @@ def show_bgp_neighbors( "extra_vars": { "dry_run": True, "verb": "check", + "object": "bgp", "subscription": subscription, + "partner_name": partner_name, "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Show BGP neighbors.", }, "source_access_port_fqdn": source_access_port_fqdn, @@ -123,7 +134,11 @@ def show_bgp_neighbors( @step("[DRY RUN] Deactivate BGP session on the source router") def deactivate_bgp_dry( - subscription: L3CoreService, process_id: UUIDstr, tt_number: TTNumber, source_access_port_fqdn: str + subscription: L3CoreService, + process_id: UUIDstr, + tt_number: TTNumber, + source_access_port_fqdn: str, + partner_name: str, ) -> LSOState: """Perform a dry run of deactivating the BGP session on the source router.""" return { @@ -131,8 +146,10 @@ def deactivate_bgp_dry( "inventory": {"all": {"hosts": {source_access_port_fqdn: None}}}, "extra_vars": { "dry_run": True, - "verb": "disable", + "verb": "deactivate", + "object": "bgp", "subscription": subscription, + "partner_name": partner_name, "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deactivate BGP session.", }, } @@ -140,7 +157,11 @@ def deactivate_bgp_dry( @step("[FOR REAL] Deactivate BGP session on the source router") def deactivate_bgp_real( - subscription: L3CoreService, process_id: UUIDstr, tt_number: TTNumber, source_access_port_fqdn: str + subscription: L3CoreService, + process_id: UUIDstr, + tt_number: TTNumber, + source_access_port_fqdn: str, + partner_name: str, ) -> LSOState: """Deactivate the BGP session on the source router.""" return { @@ -148,8 +169,10 @@ def deactivate_bgp_real( "inventory": {"all": {"hosts": {source_access_port_fqdn: None}}}, "extra_vars": { "dry_run": False, - "verb": "disable", + "verb": "deactivate", + "object": "bgp", "subscription": subscription, + "partner_name": partner_name, "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deactivate BGP session.", }, } @@ -169,7 +192,7 @@ def inform_operator_traffic_check() -> FormGenerator: model_config = ConfigDict(title="Please confirm before continuing") info_label_1: Label = "Please verify that traffic has moved as expected." - info_label_2: Label = "If traffic is misbehaving, this is your last chance to abort this workflow cleanly." + info_label_3: Label = "If traffic is misbehaving, this is your last chance to abort this workflow cleanly." yield PreCheckPage return {} @@ -177,7 +200,11 @@ def inform_operator_traffic_check() -> FormGenerator: @step("[DRY RUN] Deactivate SBP config on the source router") def deactivate_sbp_dry( - subscription: L3CoreService, process_id: UUIDstr, tt_number: TTNumber, source_access_port_fqdn: str + subscription: L3CoreService, + process_id: UUIDstr, + tt_number: TTNumber, + source_access_port_fqdn: str, + partner_name: str, ) -> LSOState: """Perform a dry run of deactivating SBP config on the source router.""" return { @@ -185,8 +212,9 @@ def deactivate_sbp_dry( "inventory": {"all": {"hosts": {source_access_port_fqdn: None}}}, "extra_vars": { "dry_run": True, - "verb": "disable", + "verb": "deactivate", "subscription": subscription, + "partner_name": partner_name, "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deactivate BGP session.", }, } @@ -194,7 +222,11 @@ def deactivate_sbp_dry( @step("[FOR REAL] Deactivate SBP config on the source router") def deactivate_sbp_real( - subscription: L3CoreService, process_id: UUIDstr, tt_number: TTNumber, source_access_port_fqdn: str + subscription: L3CoreService, + process_id: UUIDstr, + tt_number: TTNumber, + source_access_port_fqdn: str, + partner_name: str, ) -> LSOState: """Deactivate the BGP session on the source router.""" return { @@ -202,8 +234,9 @@ def deactivate_sbp_real( "inventory": {"all": {"hosts": {source_access_port_fqdn: None}}}, "extra_vars": { "dry_run": False, - "verb": "disable", + "verb": "deactivate", "subscription": subscription, + "partner_name": partner_name, "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deactivate BGP session.", }, "__remove_keys": ["source_access_port_fqdn"], @@ -239,7 +272,11 @@ def generate_scoped_subscription_model( @step("[DRY RUN] Configure service on destination Edge Port") def deploy_destination_ep_dry( - scoped_subscription: dict[str, Any], process_id: UUIDstr, tt_number: TTNumber, destination_edge_port: EdgePort + scoped_subscription: dict[str, Any], + process_id: UUIDstr, + tt_number: TTNumber, + destination_edge_port: EdgePort, + partner_name: str, ) -> LSOState: """Deploy Access Port on the destination Edge Port, as a dry run. @@ -251,7 +288,9 @@ def deploy_destination_ep_dry( "extra_vars": { "dry_run": True, "verb": "deploy", + "object": "sbp", "subscription": scoped_subscription, + "partner_name": partner_name, "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - " "Deploying SBP and standard IDs.", }, @@ -260,7 +299,11 @@ def deploy_destination_ep_dry( @step("[FOR REAL] Configure service on destination Edge Port") def deploy_destination_ep_real( - scoped_subscription: dict[str, Any], destination_edge_port: EdgePort, process_id: UUIDstr, tt_number: TTNumber + scoped_subscription: dict[str, Any], + destination_edge_port: EdgePort, + process_id: UUIDstr, + tt_number: TTNumber, + partner_name: str, ) -> LSOState: """Deploy Access Port on the destination Edge Port.""" return { @@ -269,7 +312,9 @@ def deploy_destination_ep_real( "extra_vars": { "dry_run": False, "verb": "deploy", + "object": "sbp", "subscription": scoped_subscription, + "partner_name": partner_name, "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - " "Deploying SBP and standard IDs.", }, @@ -278,16 +323,22 @@ def deploy_destination_ep_real( @step("[DRY RUN] Deploy BGP session") def deploy_bgp_session_dry( - scoped_subscription: dict[str, Any], destination_edge_port: EdgePort, process_id: UUIDstr, tt_number: TTNumber + scoped_subscription: dict[str, Any], + destination_edge_port: EdgePort, + process_id: UUIDstr, + tt_number: TTNumber, + partner_name: str, ) -> LSOState: """Perform a dry run of deploying the destination BGP session.""" return { - "playbook_name": "gap_ansible/playbooks/manage_bgp_peers.yaml", + "playbook_name": "gap_ansible/playbooks/l3_core_service.yaml", "inventory": {"all": {"hosts": {destination_edge_port.edge_port.node.router_fqdn: None}}}, "extra_vars": { "dry_run": True, "verb": "deploy", + "object": "bgp", "subscription": scoped_subscription, + "partner_name": partner_name, "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploying BGP session for SBP.", }, } @@ -295,7 +346,11 @@ def deploy_bgp_session_dry( @step("[FOR REAL] Deploy BGP session") def deploy_bgp_session_real( - scoped_subscription: dict[str, Any], destination_edge_port: EdgePort, process_id: UUIDstr, tt_number: TTNumber + scoped_subscription: dict[str, Any], + destination_edge_port: EdgePort, + process_id: UUIDstr, + tt_number: TTNumber, + partner_name: str, ) -> LSOState: """Deploy the destination BGP session.""" return { @@ -304,13 +359,22 @@ def deploy_bgp_session_real( "extra_vars": { "dry_run": False, "verb": "deploy", + "object": "bgp", "subscription": scoped_subscription, + "partner_name": partner_name, "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploying BGP session for SBP.", }, "__remove_keys": ["scoped_subscription"], } +@step("Update Infoblox") +def update_dns_records(subscription: L3CoreService) -> State: + """Update DNS records in Infoblox.""" + # TODO: implement + return {"subscription": subscription} + + @step("Update subscription model") def update_subscription_model( subscription: L3CoreService, destination_edge_port: EdgePort, replaced_ap_index: int @@ -333,6 +397,7 @@ def migrate_l3_core_service() -> StepList: return ( begin >> store_process_subscription(Target.MODIFY) + >> inject_partner_name >> is_human_initiated_wf(lso_interaction(show_bgp_neighbors)) # TODO: send OTRS email with pre-check results >> is_human_initiated_wf(lso_interaction(deactivate_bgp_dry)) >> is_human_initiated_wf(lso_interaction(deactivate_bgp_real)) @@ -346,6 +411,7 @@ def migrate_l3_core_service() -> StepList: >> lso_interaction(deploy_destination_ep_real) >> lso_interaction(deploy_bgp_session_dry) >> lso_interaction(deploy_bgp_session_real) + >> update_dns_records >> update_subscription_model >> resync >> stop_moodi() diff --git a/setup.py b/setup.py index bb5d35a7a8a130ee8caebf15f4380b2003c3fce0..d28914755a4d884892f49f42ac4d885e852147ac 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ from setuptools import find_packages, setup setup( name="geant-service-orchestrator", - version="2.38", + version="2.39", author="GÉANT Orchestration and Automation Team", author_email="goat@geant.org", description="GÉANT Service Orchestrator", diff --git a/test/cli/test_imports.py b/test/cli/test_imports.py index b9cd651104da08f51dac3470bedc5499b8f5a348..39c9f31efbdf205afba48cc908096d172a67c4c9 100644 --- a/test/cli/test_imports.py +++ b/test/cli/test_imports.py @@ -418,7 +418,7 @@ def layer_2_circuit_data(temp_file, faker, partner_factory, edge_port_subscripti "edge_port": str(edge_port_subscription_factory().subscription_id), "vlan_id": faker.vlan_id(), }, - "layer_2_circuit_type": Layer2CircuitType.TAGGED, + "layer_2_circuit_type": Layer2CircuitType.VLAN, "vlan_range_lower_bound": faker.vlan_id(), "vlan_range_upper_bound": faker.vlan_id(), "policer_enabled": False, diff --git a/test/fixtures/iptrunk_fixtures.py b/test/fixtures/iptrunk_fixtures.py index 2bfa5ffafdac1ae8408ef84e3bb7c611977cf349..89a61feb25425aaf9ed1a94823423c25d326f411 100644 --- a/test/fixtures/iptrunk_fixtures.py +++ b/test/fixtures/iptrunk_fixtures.py @@ -23,12 +23,14 @@ def iptrunk_side_subscription_factory(router_subscription_factory, faker): ga_id=None, iptrunk_side_ae_members=None, iptrunk_side_ae_members_description=None, + *, + side_node_access_via_ts: bool | None = False, ) -> IptrunkSideBlock: return IptrunkSideBlock.new( faker.uuid4(), iptrunk_side_node=iptrunk_side_node.router if iptrunk_side_node - else router_subscription_factory(vendor=Vendor.NOKIA).router, + else router_subscription_factory(vendor=Vendor.NOKIA, router_access_via_ts=side_node_access_via_ts).router, iptrunk_side_ae_iface=iptrunk_side_ae_iface or faker.pystr(), ga_id=ga_id or faker.ga_id(), iptrunk_side_ae_members=iptrunk_side_ae_members @@ -66,6 +68,7 @@ def iptrunk_subscription_factory(iptrunk_side_subscription_factory, faker, geant status: SubscriptionLifecycle | None = None, partner: dict | None = None, *, + side_node_access_via_ts: bool | None = False, is_imported: bool | None = False, ) -> SubscriptionModel: if partner is None: @@ -89,8 +92,8 @@ def iptrunk_subscription_factory(iptrunk_side_subscription_factory, faker, geant iptrunk_ipv4_network = iptrunk_ipv4_network or faker.ipv4_network(max_subnet=31) iptrunk_ipv6_network = iptrunk_ipv6_network or faker.ipv6_network(max_subnet=126) iptrunk_minimum_links = 1 - iptrunk_side_a = iptrunk_side_subscription_factory() - iptrunk_side_b = iptrunk_side_subscription_factory() + iptrunk_side_a = iptrunk_side_subscription_factory(side_node_access_via_ts=side_node_access_via_ts) + iptrunk_side_b = iptrunk_side_subscription_factory(side_node_access_via_ts=side_node_access_via_ts) iptrunk_sides = iptrunk_sides or [iptrunk_side_a, iptrunk_side_b] iptrunk_subscription.iptrunk.gs_id = gs_id diff --git a/test/fixtures/layer_2_circuit_fixtures.py b/test/fixtures/layer_2_circuit_fixtures.py index 381cb31195f665d704b52bb14987ac8023a4da38..f00b3375ce9da225e07e0259991019aa137066fc 100644 --- a/test/fixtures/layer_2_circuit_fixtures.py +++ b/test/fixtures/layer_2_circuit_fixtures.py @@ -29,7 +29,7 @@ def layer_2_circuit_subscription_factory(faker, geant_partner, edge_port_subscri status: SubscriptionLifecycle | None = None, start_date: str | None = "2024-01-01T10:20:30+01:02", layer_2_circuit_service_type: Layer2CircuitServiceType | None = None, - layer_2_circuit_type: Layer2CircuitType = Layer2CircuitType.TAGGED, + layer_2_circuit_type: Layer2CircuitType = Layer2CircuitType.VLAN, vlan_range_lower_bound: VLAN_ID | None = None, vlan_range_upper_bound: VLAN_ID | None = None, policer_bandwidth: BandwidthString | None = None, @@ -89,7 +89,7 @@ def layer_2_circuit_subscription_factory(faker, geant_partner, edge_port_subscri sbp_type=SBPType.L2, vlan_id=vlan_id, gs_id=gs_id or faker.gs_id(), - is_tagged=layer_2_circuit_type == Layer2CircuitType.TAGGED, + is_tagged=layer_2_circuit_type == Layer2CircuitType.VLAN, custom_firewall_filters=False, ) layer_2_circuit_side = Layer2CircuitSideBlockInactive.new(uuid4(), sbp=sbp) @@ -98,7 +98,7 @@ def layer_2_circuit_subscription_factory(faker, geant_partner, edge_port_subscri subscription.layer_2_circuit.layer_2_circuit_sides = layer_2_circuit_sides subscription.layer_2_circuit.virtual_circuit_id = generate_unique_vc_id() subscription.layer_2_circuit.layer_2_circuit_type = layer_2_circuit_type - if layer_2_circuit_type == Layer2CircuitType.TAGGED: + if layer_2_circuit_type == Layer2CircuitType.VLAN: subscription.layer_2_circuit.vlan_range_lower_bound = vlan_range_lower_bound or faker.vlan_id() subscription.layer_2_circuit.vlan_range_upper_bound = vlan_range_upper_bound or faker.vlan_id() else: diff --git a/test/fixtures/router_fixtures.py b/test/fixtures/router_fixtures.py index f74f49544aa699167d44250f156cd472ac2e4a1b..52ef9072663121964b9f19a26a78426d82c8985f 100644 --- a/test/fixtures/router_fixtures.py +++ b/test/fixtures/router_fixtures.py @@ -30,7 +30,7 @@ def router_subscription_factory(site_subscription_factory, faker, geant_partner) partner: dict | None = None, vendor: Vendor | None = Vendor.NOKIA, *, - router_access_via_ts: bool | None = None, + router_access_via_ts: bool | None = False, is_imported: bool | None = False, ) -> SubscriptionModel: if partner is None: @@ -48,7 +48,7 @@ def router_subscription_factory(site_subscription_factory, faker, geant_partner) router_subscription.router.router_fqdn = router_fqdn or faker.domain_name(levels=4) router_subscription.router.router_ts_port = router_ts_port or faker.port_number(is_user=True) - router_subscription.router.router_access_via_ts = router_access_via_ts or faker.boolean() + router_subscription.router.router_access_via_ts = router_access_via_ts router_subscription.router.router_lo_ipv4_address = router_lo_ipv4_address or ipaddress.IPv4Address( faker.ipv4() ) diff --git a/test/workflows/iptrunk/test_migrate_iptrunk.py b/test/workflows/iptrunk/test_migrate_iptrunk.py index e3c3ba5f6b2c61e9836222e5bb1e8581783afc61..8054dfba1b9341dd41dad2ec47dae8c7661a79e8 100644 --- a/test/workflows/iptrunk/test_migrate_iptrunk.py +++ b/test/workflows/iptrunk/test_migrate_iptrunk.py @@ -34,9 +34,9 @@ def migrate_form_input( if use_juniper == UseJuniperSide.SIDE_A: # Nokia -> Juniper - old_subscription = iptrunk_subscription_factory() - new_router = str(router_subscription_factory(vendor=Vendor.JUNIPER).subscription_id) - replace_side = str(old_subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.subscription.subscription_id) + old_subscription = iptrunk_subscription_factory(side_node_access_via_ts=False) + new_router = str(router_subscription_factory(vendor=Vendor.JUNIPER, router_access_via_ts=False).subscription_id) + replace_side = str(old_subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.owner_subscription_id) new_side_ae_members = new_side_ae_members_juniper lag_name = "ae1" elif use_juniper == UseJuniperSide.SIDE_B: @@ -47,7 +47,7 @@ def migrate_form_input( old_side_b_node = iptrunk_side_subscription_factory(iptrunk_side_node=old_side_b_node) old_subscription = iptrunk_subscription_factory(iptrunk_sides=[old_side_a_node, old_side_b_node]) new_router = str(router_subscription_factory().subscription_id) - replace_side = str(old_subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.subscription.subscription_id) + replace_side = str(old_subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.owner_subscription_id) new_side_ae_members = new_side_ae_members_nokia lag_name = "lag-1" elif use_juniper == UseJuniperSide.SIDE_BOTH: @@ -56,7 +56,9 @@ def migrate_form_input( old_side_a_node = iptrunk_side_subscription_factory(iptrunk_side_node=old_side_a_node) old_side_b_node = router_subscription_factory(vendor=Vendor.JUNIPER) old_side_b_node = iptrunk_side_subscription_factory(iptrunk_side_node=old_side_b_node) - old_subscription = iptrunk_subscription_factory(iptrunk_sides=[old_side_a_node, old_side_b_node]) + old_subscription = iptrunk_subscription_factory( + iptrunk_sides=[old_side_a_node, old_side_b_node], side_node_access_via_ts=False + ) new_router = str(router_subscription_factory(vendor=Vendor.JUNIPER).subscription_id) replace_side = str(old_subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.subscription.subscription_id) new_side_ae_members = new_side_ae_members_juniper @@ -119,7 +121,7 @@ def interface_lists_are_equal(list1, list2): @patch("gso.services.netbox_client.NetboxClient.free_interface") @patch("gso.services.netbox_client.NetboxClient.delete_interface") @patch("gso.workflows.iptrunk.migrate_iptrunk.SharePointClient") -def test_migrate_iptrunk_success( +def test_migrate_iptrunk_success( # noqa: PLR0915 mock_sharepoint_client, mocked_delete_interface, mocked_free_interface, @@ -147,6 +149,10 @@ def test_migrate_iptrunk_success( mocked_delete_interface.return_value = mocked_netbox.delete_interface() mock_sharepoint_client.return_value = MockedSharePointClient + replace_side = migrate_form_input[1]["replace_side"] + replacing_router = Router.from_subscription(replace_side) + assert replacing_router.router.router_access_via_ts is False + migrate_form_input[1]["restore_isis_metric"] = restore_isis_metric result, process_stat, step_log = run_workflow("migrate_iptrunk", migrate_form_input) @@ -174,7 +180,7 @@ def test_migrate_iptrunk_success( subscription = Iptrunk.from_subscription(subscription_id) assert subscription.status == "active" - assert mock_execute_playbook.call_count == 17 if restore_isis_metric else 16 + assert mock_execute_playbook.call_count == (17 if restore_isis_metric else 16) assert mock_create_host_by_ip.call_count == 1 assert mock_delete_host_by_ip.call_count == 1 @@ -185,7 +191,8 @@ def test_migrate_iptrunk_success( new_lag_member_interfaces = migrate_form_input[3]["new_lag_member_interfaces"] # Get vendor for the new and old migrated node - vendor_old = Router.from_subscription(replace_side).router.vendor + replaced_router = Router.from_subscription(replace_side) + vendor_old = replaced_router.router.vendor vendor_new = Router.from_subscription(new_router).router.vendor # Only Nokia will be checked on netbox @@ -213,3 +220,9 @@ def test_migrate_iptrunk_success( existing_members = subscription.iptrunk.iptrunk_sides[0].iptrunk_side_ae_members assert interface_lists_are_equal(new_lag_member_interfaces, existing_members) assert (subscription.iptrunk.iptrunk_isis_metric == 999999) != restore_isis_metric + + # Assert the old side is replaced correctly + assert replaced_router.router.router_access_via_ts is True + assert replaced_router.router.router_fqdn == replacing_router.router.router_fqdn + assert subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_access_via_ts is False + assert subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_access_via_ts is False diff --git a/test/workflows/l2_circuit/test_create_imported_layer_2_circuit.py b/test/workflows/l2_circuit/test_create_imported_layer_2_circuit.py index dad5974d22ba81b0a1913f521d5319f0b982c495..49e19bc7c693e3a1f9d5be14cbab6898a439f18b 100644 --- a/test/workflows/l2_circuit/test_create_imported_layer_2_circuit.py +++ b/test/workflows/l2_circuit/test_create_imported_layer_2_circuit.py @@ -21,7 +21,7 @@ def test_create_imported_layer_2_circuit_success( { "service_type": layer_2_service_type, "partner": partner["name"], - "layer_2_circuit_type": Layer2CircuitType.TAGGED, + "layer_2_circuit_type": Layer2CircuitType.VLAN, "policer_enabled": policer_enabled, "vlan_range_lower_bound": faker.vlan_id(), "vlan_range_upper_bound": faker.vlan_id(), @@ -39,7 +39,7 @@ def test_create_imported_layer_2_circuit_success( assert_complete(result) subscription = Layer2Circuit.from_subscription(state["subscription_id"]) assert subscription.status == SubscriptionLifecycle.ACTIVE - assert subscription.layer_2_circuit.layer_2_circuit_type == Layer2CircuitType.TAGGED + assert subscription.layer_2_circuit.layer_2_circuit_type == Layer2CircuitType.VLAN assert subscription.layer_2_circuit.virtual_circuit_id == creation_form_input_data[0]["vc_id"] assert len(subscription.layer_2_circuit.layer_2_circuit_sides) == 2 assert subscription.layer_2_circuit.layer_2_circuit_sides[0].sbp.is_tagged is True diff --git a/test/workflows/l2_circuit/test_create_layer_2_circuit.py b/test/workflows/l2_circuit/test_create_layer_2_circuit.py index df6c1347a411d4d41da728dc285819b4a05fefe9..47e0806c0f67629bf1fdf6a7b3a9f85f3440a3f5 100644 --- a/test/workflows/l2_circuit/test_create_layer_2_circuit.py +++ b/test/workflows/l2_circuit/test_create_layer_2_circuit.py @@ -19,7 +19,7 @@ def layer_2_circuit_input(faker, partner_factory, edge_port_subscription_factory { "tt_number": faker.tt_number(), "partner": partner["partner_id"], - "layer_2_circuit_type": Layer2CircuitType.TAGGED, + "layer_2_circuit_type": Layer2CircuitType.VLAN, "policer_enabled": policer_enabled, "custom_service_name": faker.sentence(), }, @@ -63,7 +63,7 @@ def test_create_layer_2_circuit_success( == layer_2_circuit_input[2]["layer_2_circuit_side_b"]["edge_port"] ) assert subscription.layer_2_circuit.layer_2_circuit_sides[1].sbp.is_tagged is True - assert subscription.layer_2_circuit.layer_2_circuit_type == Layer2CircuitType.TAGGED + assert subscription.layer_2_circuit.layer_2_circuit_type == Layer2CircuitType.VLAN assert subscription.layer_2_circuit.vlan_range_lower_bound == layer_2_circuit_input[2]["vlan_range_lower_bound"] assert subscription.layer_2_circuit.vlan_range_upper_bound == layer_2_circuit_input[2]["vlan_range_upper_bound"] assert subscription.layer_2_circuit.bandwidth == layer_2_circuit_input[2]["policer_bandwidth"] diff --git a/test/workflows/l2_circuit/test_modify_layer_2_circuit.py b/test/workflows/l2_circuit/test_modify_layer_2_circuit.py index 5a0f12db5cb9b61c0f55d0576a606d8516e20721..c163d33e1375bcd353571280abb73b59005d6b79 100644 --- a/test/workflows/l2_circuit/test_modify_layer_2_circuit.py +++ b/test/workflows/l2_circuit/test_modify_layer_2_circuit.py @@ -19,7 +19,7 @@ def test_modify_layer_2_circuit_change_policer_bandwidth( {"subscription_id": str(subscription.subscription_id)}, { "tt_number": faker.tt_number(), - "layer_2_circuit_type": Layer2CircuitType.TAGGED, + "layer_2_circuit_type": Layer2CircuitType.VLAN, "policer_enabled": False, "custom_service_name": faker.sentence(), }, @@ -53,7 +53,7 @@ def test_modify_layer_2_circuit_change_circuit_type( {"subscription_id": str(subscription.subscription_id)}, { "tt_number": faker.tt_number(), - "layer_2_circuit_type": Layer2CircuitType.UNTAGGED, + "layer_2_circuit_type": Layer2CircuitType.ETHERNET, }, { "vlan_range_lower_bound": None, @@ -68,6 +68,6 @@ def test_modify_layer_2_circuit_change_circuit_type( assert subscription.status == SubscriptionLifecycle.ACTIVE assert subscription.layer_2_circuit.vlan_range_lower_bound is None assert subscription.layer_2_circuit.vlan_range_upper_bound is None - assert subscription.layer_2_circuit.layer_2_circuit_type == Layer2CircuitType.UNTAGGED + assert subscription.layer_2_circuit.layer_2_circuit_type == Layer2CircuitType.ETHERNET for layer_2_circuit_side in subscription.layer_2_circuit.layer_2_circuit_sides: assert layer_2_circuit_side.sbp.is_tagged is False