Skip to content
Snippets Groups Projects
create_router.py 5.96 KiB
import ipaddress
import re

# noinspection PyProtectedMember
from orchestrator.forms import FormPage
from orchestrator.forms.validators import Choice
from orchestrator.targets import Target
from orchestrator.types import FormGenerator, State, SubscriptionLifecycle, UUIDstr
from orchestrator.workflow import StepList, 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 gso import repository
from gso.products.product_blocks import router as router_pb
from gso.products.product_types import router
from gso.products.product_types.router import RouterInactive, RouterProvisioning
from gso.products.product_types.site import Site
from gso.services import ipam, provisioning_proxy
from gso.services.provisioning_proxy import pp_interaction
from gso.workflows.utils import customer_selector


def site_selector() -> Choice:
    site_subscriptions = {}
    for site_id, site_description in repository.all_active_site_subscriptions(
        fields=["subscription_id", "description"]
    ):
        site_subscriptions[str(site_id)] = site_description

    # noinspection PyTypeChecker
    return Choice("Select a site", zip(site_subscriptions.keys(), site_subscriptions.items()))  # type: ignore


def initial_input_form_generator(product_name: str) -> FormGenerator:
    class CreateRouterForm(FormPage):
        class Config:
            title = product_name

        customer: customer_selector()  # type: ignore
        router_site: site_selector()  # type: ignore
        hostname: str
        ts_port: int
        router_vendor: router_pb.RouterVendor
        router_role: router_pb.RouterRole
        is_ias_connected: bool

    user_input = yield CreateRouterForm

    return user_input.dict()


@step("Create subscription")
def create_subscription(product: UUIDstr, customer: UUIDstr) -> State:
    subscription = RouterInactive.from_product_id(product, customer)

    return {
        "subscription": subscription,
        "subscription_id": subscription.subscription_id,
    }


def iso_from_ipv4(ipv4_address: ipaddress.IPv4Address) -> str:
    padded_octets = [f"{x:>03}" for x in str(ipv4_address).split(".")]
    joined_octets = "".join(padded_octets)
    re_split = ".".join(re.findall("....", joined_octets))
    return ".".join(["49.51e5.0001", re_split, "00"])


@step("Get information from IPAM")
def get_info_from_ipam(subscription: RouterProvisioning, is_ias_connected: bool) -> State:
    lo0_alias = re.sub(".geant.net", "", subscription.router.router_fqdn)
    lo0_name = f"lo0.{lo0_alias}"
    lo0_addr = ipam.allocate_host(hostname=lo0_name, service_type="LO", cname_aliases=[lo0_alias])
    subscription.router.router_lo_ipv4_address = lo0_addr.v4
    subscription.router.router_lo_ipv6_address = lo0_addr.v6
    subscription.router.router_lo_iso_address = iso_from_ipv4(subscription.router.router_lo_ipv4_address)
    subscription.router.router_is_ias_connected = is_ias_connected

    if is_ias_connected is True:
        subscription.router.router_si_ipv4_network = ipam.allocate_ipv4_network(
            service_type="SI", comment=f"SI for {lo0_name}"
        ).v4
        subscription.router.router_ias_lt_ipv4_network = ipam.allocate_ipv4_network(
            service_type="LT_IAS", comment=f"LT for {lo0_name}"
        ).v4
        subscription.router.router_ias_lt_ipv6_network = ipam.allocate_ipv6_network(
            service_type="LT_IAS", comment=f"LT for {lo0_name}"
        ).v6

    return {"subscription": subscription}


@step("Initialize subscription")
def initialize_subscription(
    subscription: router.RouterInactive,
    hostname: str,
    ts_port: int,
    router_vendor: router_pb.RouterVendor,
    router_site: str,
    router_role: router_pb.RouterRole,
) -> State:
    subscription.router.router_ts_port = ts_port
    subscription.router.router_vendor = router_vendor
    subscription.router.router_site = Site.from_subscription(router_site).site
    fqdn = (
        f"{hostname}.{subscription.router.router_site.site_name.lower()}."
        f"{subscription.router.router_site.site_country_code.lower()}"
        ".geant.net"
    )
    subscription.router.router_fqdn = fqdn
    subscription.router.router_role = router_role
    subscription.router.router_access_via_ts = True
    subscription.description = f"Router {fqdn}"

    subscription = router.RouterProvisioning.from_other_lifecycle(subscription, SubscriptionLifecycle.PROVISIONING)

    return {"subscription": subscription}


@step("Provision router [DRY RUN]")
def provision_router_dry(subscription: RouterProvisioning, process_id: UUIDstr) -> State:
    provisioning_proxy.provision_router(subscription, process_id)

    return {
        "subscription": subscription,
        "label_text": (
            "Dry run for the deployment of base config on a new router. Deployment is done by the"
            " provisioning proxy, please wait for the results to come back before continuing."
        ),
    }


@step("Provision router [FOR REAL]")
def provision_router_real(subscription: RouterProvisioning, process_id: UUIDstr) -> State:
    provisioning_proxy.provision_router(subscription, process_id, False)

    return {
        "subscription": subscription,
        "label_text": (
            "Deployment of base config for a new router. Deployment is being taken care of by the"
            " provisioning proxy, please wait for the results to come back before continuing."
        ),
    }


@workflow(
    "Create router",
    initial_input_form=wrap_create_initial_input_form(initial_input_form_generator),
    target=Target.CREATE,
)
def create_router() -> StepList:
    return (
        init
        >> create_subscription
        >> store_process_subscription(Target.CREATE)
        >> initialize_subscription
        >> get_info_from_ipam
        >> pp_interaction(provision_router_dry, 3)
        >> pp_interaction(provision_router_real, 3)
        >> set_status(SubscriptionLifecycle.ACTIVE)
        >> resync
        >> done
    )