diff --git a/gso/products/product_blocks/router.py b/gso/products/product_blocks/router.py index 580263185b9a7f05c537b231e3d4975422d5ddfa..547b73c462731c577d6ce768ab0f43e18b458ee6 100644 --- a/gso/products/product_blocks/router.py +++ b/gso/products/product_blocks/router.py @@ -44,6 +44,10 @@ class RouterBlockInactive( router_is_ias_connected: Optional[bool] = None +def generate_fqdn(hostname: str, site_name: str, country_code: str) -> str: + return f"{hostname}.{site_name.lower()}.{country_code.lower()}.geant.net" + + class RouterBlockProvisioning(RouterBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): """A router that's being provisioned. See {class}`RouterBlock`.""" diff --git a/gso/workflows/router/create_router.py b/gso/workflows/router/create_router.py index 952d6eaf9a05daf287eabf50d0c6f6274d98ddd1..38742c77cb3ef07897878fcec531eae1ceba36b8 100644 --- a/gso/workflows/router/create_router.py +++ b/gso/workflows/router/create_router.py @@ -1,6 +1,5 @@ -from typing import Optional - -import infoblox_client.objects +from ipaddress import IPv4Network, IPv6Network +from typing import Any # noinspection PyProtectedMember from orchestrator.forms import FormPage @@ -10,6 +9,7 @@ from orchestrator.types import FormGenerator, State, SubscriptionLifecycle, UUID from orchestrator.workflow import StepList, conditional, done, init, step, workflow from orchestrator.workflows.steps import resync, set_status, store_process_subscription from orchestrator.workflows.utils import wrap_create_initial_input_form +from products.product_blocks.router import RouterRole, RouterVendor, generate_fqdn from pydantic import validator from gso.products.product_types.router import RouterInactive, RouterProvisioning @@ -18,7 +18,6 @@ from gso.products.shared import PortNumber from gso.services import infoblox, provisioning_proxy, subscriptions from gso.services.provisioning_proxy import pp_interaction from gso.workflows.utils import customer_selector, iso_from_ipv4 -from products.product_blocks.router import RouterRole, RouterVendor, generate_fqdn def _site_selector() -> Choice: @@ -43,13 +42,17 @@ def initial_input_form_generator(product_name: str) -> FormGenerator: ts_port: PortNumber router_vendor: RouterVendor router_role: RouterRole - is_ias_connected: Optional[bool] + is_ias_connected: bool | None @validator("hostname", allow_reuse=True) - def hostname_must_be_available(cls, hostname: str, **kwargs) -> str: - selected_site = Site.from_subscription(kwargs["values"].get("router_site", "")).site + def hostname_must_be_available(cls, hostname: str, **kwargs: dict[str, Any]) -> str: + router_site = kwargs["values"].get("router_site") + if not router_site: + raise ValueError("Please select a site before setting the hostname.") + + selected_site = Site.from_subscription(router_site).site input_fqdn = generate_fqdn(hostname, selected_site.site_name, selected_site.site_country_code) - if not infoblox.hostname_available(input_fqdn): + if not infoblox.hostname_available(f"lo0.{input_fqdn}"): raise ValueError(f'FQDN "{input_fqdn}" is not available.') return hostname @@ -154,18 +157,16 @@ def provision_router_real(subscription: RouterProvisioning, process_id: UUIDstr) def verify_ipam_loopback(subscription: RouterProvisioning) -> State: host_record = infoblox.find_host_by_fqdn(f"lo0.{subscription.router.router_fqdn}") if not host_record or str(subscription.subscription_id) not in host_record.comment: - return { - "ipam_warning": "!!! Loopback record is incorrectly configured in IPAM, please investigate this manually. !!!" - } + return {"ipam_warning": "Loopback record is incorrectly configured in IPAM, please investigate this manually!"} return {"subscription": subscription} @step("Verify IPAM resources for IAS/LT networks") def verify_ipam_ias(subscription: RouterProvisioning) -> State: - si_ipv4_network = infoblox.find_network_by_cidr(subscription.router.router_si_ipv4_network) - ias_lt_ipv4_network = infoblox.find_network_by_cidr(subscription.router.router_ias_lt_ipv4_network) - ias_lt_ipv6_network = infoblox.find_network_by_cidr(subscription.router.router_ias_lt_ipv6_network) + si_ipv4_network = infoblox.find_network_by_cidr(IPv4Network(subscription.router.router_si_ipv4_network)) + ias_lt_ipv4_network = infoblox.find_network_by_cidr(IPv4Network(subscription.router.router_ias_lt_ipv4_network)) + ias_lt_ipv6_network = infoblox.find_network_by_cidr(IPv6Network(subscription.router.router_ias_lt_ipv6_network)) new_state = {} @@ -176,13 +177,15 @@ def verify_ipam_ias(subscription: RouterProvisioning) -> State: } if not ias_lt_ipv4_network or str(subscription.subscription_id) not in ias_lt_ipv4_network.comment: new_state = new_state | { - "ipam_ias_lt_ipv4_warning": f"IAS/LT IPv4 network expected at {subscription.router.router_ias_lt_ipv4_network}, " - f"but it was not found or misconfigured, please investigate and adjust if necessary." + "ipam_ias_lt_ipv4_warning": "IAS/LT IPv4 network expected at " + f"{subscription.router.router_ias_lt_ipv4_network}, but it was not found or misconfigured, please " + "investigate and adjust if necessary." } if not ias_lt_ipv6_network or str(subscription.subscription_id) not in ias_lt_ipv6_network.comment: new_state = new_state | { - "ipam_ias_lt_ipv6_warning": f"IAS/LT IPv6 network expected at {subscription.router.router_ias_lt_ipv6_network}, " - f"but it was not found or misconfigured, please investigate and adjust if necessary." + "ipam_ias_lt_ipv6_warning": f"IAS/LT IPv6 network expected at " + f"{subscription.router.router_ias_lt_ipv6_network}, but it was not found or misconfigured, please " + "investigate and adjust if necessary." } return new_state