"""A workflow that terminates a router.""" import ipaddress import json import logging from orchestrator.forms import FormPage from orchestrator.forms.validators import Label from orchestrator.targets import Target from orchestrator.types import FormGenerator, SubscriptionLifecycle, UUIDstr from orchestrator.utils.json import json_dumps from orchestrator.workflow import StepList, conditional, done, init, step, workflow from orchestrator.workflows.steps import ( resync, set_status, store_process_subscription, unsync, ) from orchestrator.workflows.utils import wrap_modify_initial_input_form from gso.products.product_blocks.router import RouterVendor from gso.products.product_types.router import Router from gso.services import infoblox from gso.services.netbox_client import NetboxClient from gso.services.provisioning_proxy import execute_playbook, pp_interaction logger = logging.getLogger(__name__) def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: """Let the operator decide whether to delete configuration on the router, and clear up :term:`IPAM` resources.""" router = Router.from_subscription(subscription_id) class TerminateForm(FormPage): termination_label: Label = ( "Please confirm whether configuration should get removed from the router, and whether IPAM resources should" " be released." # type: ignore[assignment] ) tt_number: str remove_configuration: bool = True clean_up_ipam: bool = True user_input = yield TerminateForm return user_input.dict() | {"router_is_nokia": router.router.vendor == RouterVendor.NOKIA} @step("Deprovision loopback IPs from IPAM") def deprovision_loopback_ips(subscription: Router) -> dict: """Clear up the loopback addresses from :term:`IPAM`.""" infoblox.delete_host_by_ip(ipaddress.IPv4Address(subscription.router.router_lo_ipv4_address)) return {"subscription": subscription} @step("[DRY RUN] Remove configuration from router") def remove_config_from_router_dry( subscription: Router, callback_route: str, process_id: UUIDstr, tt_number: str ) -> None: """Remove configuration from the router, first as a dry run.""" extra_vars = { "wfo_router_json": json.loads(json_dumps(subscription)), "dry_run": True, "verb": "terminate", "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Terminating " f"{subscription.router.router_fqdn}", } execute_playbook( playbook_name="base_config.yaml", callback_route=callback_route, inventory=subscription.router.router_fqdn, extra_vars=extra_vars, ) @step("[FOR REAL] Remove configuration from router") def remove_config_from_router_real( subscription: Router, callback_route: str, process_id: UUIDstr, tt_number: str ) -> None: """Remove configuration from the router.""" extra_vars = { "wfo_router_json": json.loads(json_dumps(subscription)), "dry_run": False, "verb": "terminate", "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Terminating " f"{subscription.router.router_fqdn}", } execute_playbook( playbook_name="base_config.yaml", callback_route=callback_route, inventory=subscription.router.router_fqdn, extra_vars=extra_vars, ) @step("Remove Device from Netbox") def remove_device_from_netbox(subscription: Router) -> dict[str, Router]: """Remove the device from Netbox.""" NetboxClient().delete_device(subscription.router.router_fqdn) return {"subscription": subscription} @workflow( "Terminate router", initial_input_form=wrap_modify_initial_input_form(initial_input_form_generator), target=Target.TERMINATE, ) def terminate_router() -> StepList: """Terminate a router subscription. * Let the operator decide whether to delete :term:`IPAM` resources, and remove configuration from the router * Clear up :term:`IPAM` resources, if selected by the operator * Disable and delete configuration on the router, if selected by the operator * Mark the subscription as terminated in the service database """ run_ipam_steps = conditional(lambda state: state["clean_up_ipam"]) run_config_steps = conditional(lambda state: state["remove_configuration"]) router_is_nokia = conditional(lambda state: state["router_is_nokia"]) return ( init >> store_process_subscription(Target.TERMINATE) >> unsync >> run_ipam_steps(deprovision_loopback_ips) >> run_config_steps(pp_interaction(remove_config_from_router_dry)) >> run_config_steps(pp_interaction(remove_config_from_router_real)) >> router_is_nokia(remove_device_from_netbox) >> set_status(SubscriptionLifecycle.TERMINATED) >> resync >> done )