import ipaddress from collections.abc import Generator from typing import Any from uuid import uuid4 import pytest from orchestrator import step, workflow from orchestrator.config.assignee import Assignee from orchestrator.db import ( ProductTable, SubscriptionInstanceTable, SubscriptionInstanceValueTable, SubscriptionTable, db, ) from orchestrator.domain import SubscriptionModel from orchestrator.types import SubscriptionLifecycle, UUIDstr from orchestrator.utils.datetime import nowtz from orchestrator.workflow import done, init, inputstep from pydantic_forms.core import FormPage from pydantic_forms.types import FormGenerator, SubscriptionMapping from pydantic_forms.validators import Choice from gso.products import ProductName from gso.products.product_blocks.iptrunk import ( IptrunkInterfaceBlock, IptrunkSideBlock, IptrunkType, ) from gso.products.product_blocks.router import RouterRole from gso.products.product_blocks.site import SiteTier from gso.products.product_types.iptrunk import ImportedIptrunkInactive, IptrunkInactive from gso.products.product_types.office_router import ImportedOfficeRouterInactive, OfficeRouterInactive from gso.products.product_types.opengear import ImportedOpengearInactive, OpengearInactive from gso.products.product_types.router import ImportedRouterInactive, Router, RouterInactive from gso.products.product_types.site import ImportedSiteInactive, Site, SiteInactive from gso.products.product_types.super_pop_switch import ImportedSuperPopSwitchInactive, SuperPopSwitchInactive from gso.services import subscriptions from gso.types.interfaces import PhysicalPortCapacity from gso.utils.shared_enums import Vendor from test.workflows import WorkflowInstanceForTests @pytest.fixture() def site_subscription_factory(faker, geant_partner): def subscription_create( description=None, start_date="2023-05-24T00:00:00+00:00", site_name=None, site_city=None, site_country=None, site_country_code=None, site_latitude=None, site_longitude=None, site_bgp_community_id=None, site_internal_id=None, site_tier=SiteTier.TIER1, site_ts_address=None, status: SubscriptionLifecycle | None = None, partner: dict | None = None, *, is_imported: bool | None = True, ) -> UUIDstr: if partner is None: partner = geant_partner description = description or "Site Subscription" site_name = site_name or faker.site_name() site_city = site_city or faker.city() site_country = site_country or faker.country() site_country_code = site_country_code or faker.country_code() site_latitude = site_latitude or str(faker.latitude()) site_longitude = site_longitude or str(faker.longitude()) site_bgp_community_id = site_bgp_community_id or faker.pyint() site_internal_id = site_internal_id or faker.pyint() site_ts_address = site_ts_address or faker.ipv4() if is_imported: product_id = subscriptions.get_product_id_by_name(ProductName.SITE) site_subscription = SiteInactive.from_product_id(product_id, customer_id=partner["partner_id"], insync=True) else: product_id = subscriptions.get_product_id_by_name(ProductName.IMPORTED_SITE) site_subscription = ImportedSiteInactive.from_product_id( product_id, customer_id=partner["partner_id"], insync=True ) site_subscription.site.site_city = site_city site_subscription.site.site_name = site_name site_subscription.site.site_country = site_country site_subscription.site.site_country_code = site_country_code site_subscription.site.site_latitude = site_latitude site_subscription.site.site_longitude = site_longitude site_subscription.site.site_bgp_community_id = site_bgp_community_id site_subscription.site.site_internal_id = site_internal_id site_subscription.site.site_tier = site_tier site_subscription.site.site_ts_address = site_ts_address site_subscription = SubscriptionModel.from_other_lifecycle(site_subscription, SubscriptionLifecycle.ACTIVE) site_subscription.description = description site_subscription.start_date = start_date if status: site_subscription.status = status site_subscription.save() db.session.commit() return str(site_subscription.subscription_id) return subscription_create @pytest.fixture() def nokia_router_subscription_factory(site_subscription_factory, faker, geant_partner): def subscription_create( description=None, start_date="2023-05-24T00:00:00+00:00", router_fqdn=None, router_ts_port=None, router_access_via_ts=None, router_lo_ipv4_address=None, router_lo_ipv6_address=None, router_lo_iso_address=None, router_role=RouterRole.PE, router_site=None, status: SubscriptionLifecycle | None = None, partner: dict | None = None, *, is_imported: bool | None = True, ) -> UUIDstr: if partner is None: partner = geant_partner description = description or faker.text(max_nb_chars=30) router_fqdn = router_fqdn or faker.domain_name(levels=4) router_ts_port = router_ts_port or faker.random_int(min=1, max=49151) router_access_via_ts = router_access_via_ts or faker.boolean() router_lo_ipv4_address = router_lo_ipv4_address or ipaddress.IPv4Address(faker.ipv4()) router_lo_ipv6_address = router_lo_ipv6_address or ipaddress.IPv6Address(faker.ipv6()) router_lo_iso_address = router_lo_iso_address or faker.word() router_site = router_site or site_subscription_factory() if is_imported: product_id = subscriptions.get_product_id_by_name(ProductName.ROUTER) router_subscription = RouterInactive.from_product_id( product_id, customer_id=partner["partner_id"], insync=True ) else: product_id = subscriptions.get_product_id_by_name(ProductName.IMPORTED_ROUTER) router_subscription = ImportedRouterInactive.from_product_id( product_id, customer_id=partner["partner_id"], insync=True ) router_subscription.router.router_fqdn = router_fqdn router_subscription.router.router_ts_port = router_ts_port router_subscription.router.router_access_via_ts = router_access_via_ts router_subscription.router.router_lo_ipv4_address = router_lo_ipv4_address router_subscription.router.router_lo_ipv6_address = router_lo_ipv6_address router_subscription.router.router_lo_iso_address = router_lo_iso_address router_subscription.router.router_role = router_role router_subscription.router.router_site = Site.from_subscription(router_site).site router_subscription.router.vendor = Vendor.NOKIA router_subscription = SubscriptionModel.from_other_lifecycle(router_subscription, SubscriptionLifecycle.ACTIVE) router_subscription.insync = True router_subscription.description = description router_subscription.start_date = start_date if status: router_subscription.status = status router_subscription.save() db.session.commit() return str(router_subscription.subscription_id) return subscription_create @pytest.fixture() def juniper_router_subscription_factory(site_subscription_factory, faker, geant_partner): def subscription_create( description=None, start_date="2023-05-24T00:00:00+00:00", router_fqdn=None, router_ts_port=None, router_access_via_ts=None, router_lo_ipv4_address=None, router_lo_ipv6_address=None, router_lo_iso_address=None, router_role=RouterRole.PE, router_site=None, status: SubscriptionLifecycle | None = None, partner: dict | None = None, *, is_imported: bool | None = True, ) -> UUIDstr: if partner is None: partner = geant_partner description = description or faker.text(max_nb_chars=30) router_fqdn = router_fqdn or faker.domain_name(levels=4) router_ts_port = router_ts_port or faker.random_int(min=1, max=49151) router_access_via_ts = router_access_via_ts or faker.boolean() router_lo_ipv4_address = router_lo_ipv4_address or ipaddress.IPv4Address(faker.ipv4()) router_lo_ipv6_address = router_lo_ipv6_address or ipaddress.IPv6Address(faker.ipv6()) router_lo_iso_address = router_lo_iso_address or faker.word() router_site = router_site or site_subscription_factory() if is_imported: product_id = subscriptions.get_product_id_by_name(ProductName.ROUTER) router_subscription = RouterInactive.from_product_id( product_id, customer_id=partner["partner_id"], insync=True ) else: product_id = subscriptions.get_product_id_by_name(ProductName.IMPORTED_ROUTER) router_subscription = ImportedRouterInactive.from_product_id( product_id, customer_id=partner["partner_id"], insync=True ) router_subscription.router.router_fqdn = router_fqdn router_subscription.router.router_ts_port = router_ts_port router_subscription.router.router_access_via_ts = router_access_via_ts router_subscription.router.router_lo_ipv4_address = router_lo_ipv4_address router_subscription.router.router_lo_ipv6_address = router_lo_ipv6_address router_subscription.router.router_lo_iso_address = router_lo_iso_address router_subscription.router.router_role = router_role router_subscription.router.router_site = Site.from_subscription(router_site).site router_subscription.router.vendor = Vendor.JUNIPER router_subscription = SubscriptionModel.from_other_lifecycle(router_subscription, SubscriptionLifecycle.ACTIVE) router_subscription.description = description router_subscription.start_date = start_date if status: router_subscription.status = status router_subscription.save() db.session.commit() return str(router_subscription.subscription_id) return subscription_create @pytest.fixture() def iptrunk_side_subscription_factory(nokia_router_subscription_factory, faker): def subscription_create( iptrunk_side_node=None, iptrunk_side_ae_iface=None, iptrunk_side_ae_geant_a_sid=None, iptrunk_side_ae_members=None, iptrunk_side_ae_members_description=None, ) -> IptrunkSideBlock: iptrunk_side_node_id = iptrunk_side_node or nokia_router_subscription_factory() iptrunk_side_node = Router.from_subscription(iptrunk_side_node_id).router iptrunk_side_ae_iface = iptrunk_side_ae_iface or faker.pystr() iptrunk_side_ae_geant_a_sid = iptrunk_side_ae_geant_a_sid or faker.geant_sid() iptrunk_side_ae_members = iptrunk_side_ae_members or [ IptrunkInterfaceBlock.new( faker.uuid4(), interface_name=faker.network_interface(), interface_description=faker.sentence(), ), IptrunkInterfaceBlock.new( faker.uuid4(), interface_name=faker.network_interface(), interface_description=faker.sentence(), ), ] return IptrunkSideBlock.new( faker.uuid4(), iptrunk_side_node=iptrunk_side_node, iptrunk_side_ae_iface=iptrunk_side_ae_iface, iptrunk_side_ae_geant_a_sid=iptrunk_side_ae_geant_a_sid, iptrunk_side_ae_members=iptrunk_side_ae_members, iptrunk_side_ae_members_description=iptrunk_side_ae_members_description, ) return subscription_create @pytest.fixture() def iptrunk_subscription_factory(iptrunk_side_subscription_factory, faker, geant_partner): def subscription_create( description=None, start_date="2023-05-24T00:00:00+00:00", geant_s_sid=None, iptrunk_description=None, iptrunk_type=IptrunkType.LEASED, iptrunk_speed=PhysicalPortCapacity.ONE_GIGABIT_PER_SECOND, iptrunk_isis_metric=None, iptrunk_ipv4_network=None, iptrunk_ipv6_network=None, iptrunk_sides=None, status: SubscriptionLifecycle | None = None, partner: dict | None = None, *, is_imported: bool | None = True, ) -> UUIDstr: if partner is None: partner = geant_partner if is_imported: product_id = subscriptions.get_product_id_by_name(ProductName.IP_TRUNK) iptrunk_subscription = IptrunkInactive.from_product_id( product_id, customer_id=partner["partner_id"], insync=True ) else: product_id = subscriptions.get_product_id_by_name(ProductName.IMPORTED_IP_TRUNK) iptrunk_subscription = ImportedIptrunkInactive.from_product_id( product_id, customer_id=partner["partner_id"], insync=True ) description = description or faker.sentence() geant_s_sid = geant_s_sid or faker.geant_sid() iptrunk_description = iptrunk_description or faker.sentence() iptrunk_isis_metric = iptrunk_isis_metric or faker.pyint() 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_sides = iptrunk_sides or [iptrunk_side_a, iptrunk_side_b] iptrunk_subscription.iptrunk.geant_s_sid = geant_s_sid iptrunk_subscription.iptrunk.iptrunk_description = iptrunk_description iptrunk_subscription.iptrunk.iptrunk_type = iptrunk_type iptrunk_subscription.iptrunk.iptrunk_speed = iptrunk_speed iptrunk_subscription.iptrunk.iptrunk_minimum_links = iptrunk_minimum_links iptrunk_subscription.iptrunk.iptrunk_isis_metric = iptrunk_isis_metric iptrunk_subscription.iptrunk.iptrunk_ipv4_network = iptrunk_ipv4_network iptrunk_subscription.iptrunk.iptrunk_ipv6_network = iptrunk_ipv6_network iptrunk_subscription.iptrunk.iptrunk_sides = iptrunk_sides iptrunk_subscription = SubscriptionModel.from_other_lifecycle( iptrunk_subscription, SubscriptionLifecycle.ACTIVE, ) if status: iptrunk_subscription.status = status iptrunk_subscription.description = description iptrunk_subscription.start_date = start_date iptrunk_subscription.save() db.session.commit() return str(iptrunk_subscription.subscription_id) return subscription_create @pytest.fixture() def office_router_subscription_factory(site_subscription_factory, faker, geant_partner): def subscription_create( description=None, start_date="2023-05-24T00:00:00+00:00", office_router_fqdn=None, office_router_ts_port=None, office_router_lo_ipv4_address=None, office_router_lo_ipv6_address=None, office_router_site=None, status: SubscriptionLifecycle | None = None, partner: dict | None = None, *, is_imported: bool | None = True, ) -> UUIDstr: if partner is None: partner = geant_partner description = description or faker.text(max_nb_chars=30) office_router_fqdn = office_router_fqdn or faker.domain_name(levels=4) office_router_ts_port = office_router_ts_port or faker.random_int(min=1, max=49151) office_router_lo_ipv4_address = office_router_lo_ipv4_address or ipaddress.IPv4Address(faker.ipv4()) office_router_lo_ipv6_address = office_router_lo_ipv6_address or ipaddress.IPv6Address(faker.ipv6()) office_router_site = office_router_site or site_subscription_factory() if is_imported: product_id = subscriptions.get_product_id_by_name(ProductName.OFFICE_ROUTER) office_router_subscription = OfficeRouterInactive.from_product_id( product_id, customer_id=partner["partner_id"], insync=True ) else: product_id = subscriptions.get_product_id_by_name(ProductName.IMPORTED_OFFICE_ROUTER) office_router_subscription = ImportedOfficeRouterInactive.from_product_id( product_id, customer_id=partner["partner_id"], insync=True ) office_router_subscription.office_router.office_router_fqdn = office_router_fqdn office_router_subscription.office_router.office_router_ts_port = office_router_ts_port office_router_subscription.office_router.office_router_lo_ipv4_address = office_router_lo_ipv4_address office_router_subscription.office_router.office_router_lo_ipv6_address = office_router_lo_ipv6_address office_router_subscription.office_router.office_router_site = Site.from_subscription(office_router_site).site office_router_subscription.office_router.vendor = Vendor.NOKIA office_router_subscription = SubscriptionModel.from_other_lifecycle( office_router_subscription, SubscriptionLifecycle.ACTIVE ) office_router_subscription.description = description office_router_subscription.start_date = start_date if status: office_router_subscription.status = status office_router_subscription.save() db.session.commit() return str(office_router_subscription.subscription_id) return subscription_create @pytest.fixture() def super_pop_switch_subscription_factory(site_subscription_factory, faker, geant_partner): def subscription_create( description=None, start_date="2023-05-24T00:00:00+00:00", super_pop_switch_fqdn=None, super_pop_switch_ts_port=None, super_pop_switch_mgmt_ipv4_address=None, super_pop_switch_site=None, status: SubscriptionLifecycle | None = None, partner: dict | None = None, *, is_imported: bool | None = True, ) -> UUIDstr: if partner is None: partner = geant_partner description = description or faker.text(max_nb_chars=30) super_pop_switch_fqdn = super_pop_switch_fqdn or faker.domain_name(levels=4) super_pop_switch_ts_port = super_pop_switch_ts_port or faker.random_int(min=1, max=49151) super_pop_switch_mgmt_ipv4_address = super_pop_switch_mgmt_ipv4_address or ipaddress.IPv4Address(faker.ipv4()) super_pop_switch_site = super_pop_switch_site or site_subscription_factory() if is_imported: product_id = subscriptions.get_product_id_by_name(ProductName.SUPER_POP_SWITCH) super_pop_switch_subscription = SuperPopSwitchInactive.from_product_id( product_id, customer_id=partner["partner_id"], insync=True ) else: product_id = subscriptions.get_product_id_by_name(ProductName.IMPORTED_SUPER_POP_SWITCH) super_pop_switch_subscription = ImportedSuperPopSwitchInactive.from_product_id( product_id, customer_id=partner["partner_id"], insync=True ) super_pop_switch_subscription.super_pop_switch.super_pop_switch_fqdn = super_pop_switch_fqdn super_pop_switch_subscription.super_pop_switch.super_pop_switch_ts_port = super_pop_switch_ts_port super_pop_switch_subscription.super_pop_switch.super_pop_switch_mgmt_ipv4_address = ( super_pop_switch_mgmt_ipv4_address ) super_pop_switch_subscription.super_pop_switch.super_pop_switch_site = Site.from_subscription( super_pop_switch_site ).site super_pop_switch_subscription.super_pop_switch.vendor = Vendor.NOKIA super_pop_switch_subscription = SubscriptionModel.from_other_lifecycle( super_pop_switch_subscription, SubscriptionLifecycle.ACTIVE ) super_pop_switch_subscription.description = description super_pop_switch_subscription.start_date = start_date if status: super_pop_switch_subscription.status = status super_pop_switch_subscription.save() db.session.commit() return str(super_pop_switch_subscription.subscription_id) return subscription_create @pytest.fixture() def opengear_subscription_factory(site_subscription_factory, faker, geant_partner): def subscription_create( description=None, start_date="2023-05-24T00:00:00+00:00", opengear_site=None, opengear_hostname=None, opengear_wan_address=None, opengear_wan_netmask=None, opengear_wan_gateway=None, status: SubscriptionLifecycle | None = None, partner: dict | None = None, *, is_imported: bool | None = True, ) -> UUIDstr: if partner is None: partner = geant_partner description = description or faker.text(max_nb_chars=30) opengear_site = opengear_site or site_subscription_factory() opengear_hostname = opengear_hostname or faker.domain_name(levels=4) opengear_wan_address = opengear_wan_address or faker.ipv4() opengear_wan_netmask = opengear_wan_netmask or faker.ipv4() opengear_wan_gateway = opengear_wan_gateway or faker.ipv4() if is_imported: product_id = subscriptions.get_product_id_by_name(ProductName.OPENGEAR) opengear_subscription = OpengearInactive.from_product_id( product_id, customer_id=partner["partner_id"], insync=True ) else: product_id = subscriptions.get_product_id_by_name(ProductName.IMPORTED_OPENGEAR) opengear_subscription = ImportedOpengearInactive.from_product_id( product_id, customer_id=partner["partner_id"], insync=True ) opengear_subscription.opengear.opengear_site = Site.from_subscription(opengear_site).site opengear_subscription.opengear.opengear_hostname = opengear_hostname opengear_subscription.opengear.opengear_wan_address = opengear_wan_address opengear_subscription.opengear.opengear_wan_netmask = opengear_wan_netmask opengear_subscription.opengear.opengear_wan_gateway = opengear_wan_gateway opengear_subscription = SubscriptionModel.from_other_lifecycle( opengear_subscription, SubscriptionLifecycle.ACTIVE ) opengear_subscription.description = description opengear_subscription.start_date = start_date if status: opengear_subscription.status = status opengear_subscription.save() db.session.commit() return str(opengear_subscription.subscription_id) return subscription_create @pytest.fixture() def test_workflow(generic_subscription_1: UUIDstr, generic_product_type_1) -> Generator: _, generic_product_one = generic_product_type_1 @step("Insert UUID in state") def insert_object(): return {"subscription_id": str(uuid4()), "model": generic_product_one.from_subscription(generic_subscription_1)} @step("Test that it is a string now") def check_object(subscription_id: Any, model: dict) -> None: # This is actually a test. It would be nicer to have this in a proper test but that takes to much setup that # already happens here. So we hijack this fixture and run this test for all tests that use this fixture # (which should not be an issue) assert isinstance(subscription_id, str) assert isinstance(model, dict) @inputstep("Modify", assignee=Assignee.CHANGES) def modify(subscription_id: UUIDstr) -> FormGenerator: class TestChoice(Choice): A = "A" B = "B" C = "C" class TestForm(FormPage): generic_select: TestChoice user_input = yield TestForm return user_input.model_dump() @workflow("Workflow") def workflow_for_testing_processes_py(): return init >> insert_object >> check_object >> modify >> done with WorkflowInstanceForTests(workflow_for_testing_processes_py, "workflow_for_testing_processes_py") as wf: yield wf def create_subscription_for_mapping( product: ProductTable, mapping: SubscriptionMapping, values: dict[str, Any], **kwargs: Any ) -> SubscriptionTable: """Create a subscription in the test coredb for the given subscription_mapping and values. This function handles optional resource types starting with a ? in the mapping not supplied in the values array. Args: product: the ProductTable to create a sub for mapping: the subscription_mapping belonging to that product values: a dictionary of keys from the sub_map and their corresponding test values kwargs: The rest of the arguments Returns: The conforming subscription. """ def build_instance(name, value_mapping): block = product.find_block_by_name(name) def build_value(rt, value): resource_type = block.find_resource_type_by_name(rt) return SubscriptionInstanceValueTable(resource_type_id=resource_type.resource_type_id, value=value) return SubscriptionInstanceTable( product_block_id=block.product_block_id, values=[ build_value(resource_type, values[value_key]) for (resource_type, value_key) in value_mapping.items() ], ) # recreate the mapping: leave out the ?keys if no value supplied for them mapping = { name: [ { **{k: value_map[k] for k in value_map if not value_map[k].startswith("?")}, **{ k: value_map[k][1:] for k in value_map if value_map[k].startswith("?") and value_map[k][1:] in values }, } for value_map in mapping[name] ] for name in mapping } instances = [ build_instance(name, value_mapping) for (name, value_mappings) in mapping.items() for value_mapping in value_mappings ] return create_subscription(instances=instances, product=product, **kwargs) def create_subscription(**kwargs): attrs = { "description": "A subscription.", "customer_id": kwargs.get("customer_id", "85938c4c-0a11-e511-80d0-005056956c1a"), "start_date": nowtz(), "status": "active", "insync": True, **kwargs, } o = SubscriptionTable(**attrs) db.session.add(o) db.session.commit() return o