validate_router.py 4.45 KiB
"""Router validation workflow. Used in a nightly schedule."""
from orchestrator.targets import Target
from orchestrator.utils.errors import ProcessFailureError
from orchestrator.workflow import StepList, begin, conditional, done, step, workflow
from orchestrator.workflows.steps import resync, store_process_subscription, unsync
from orchestrator.workflows.utils import wrap_modify_initial_input_form
from pydantic_forms.types import State, UUIDstr
from gso.products.product_types.router import Router
from gso.schedules import TT_NUMBER_ZERO
from gso.services import infoblox
from gso.services.librenms_client import LibreNMSClient
from gso.services.lso_client import anonymous_lso_interaction, execute_playbook
from gso.services.netbox_client import NetboxClient
from gso.utils.shared_enums import Vendor
@step("Prepare required keys in state")
def prepare_state(subscription_id: UUIDstr) -> State:
"""Add required keys to the state for the workflow to run successfully.
This includes the router product itself and a fake TT number for running playbooks.
"""
router = Router.from_subscription(subscription_id)
return {"subscription": router, "tt_number": TT_NUMBER_ZERO}
@step("Verify IPAM resources for loopback interface")
def verify_ipam_loopback(subscription: Router) -> None:
"""Validate the :term:`IPAM` resources for the loopback interface.
Raises an :class:`orchestrator.utils.errors.ProcessFailureError` if :term:`IPAM` is configured incorrectly.
"""
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:
msg = "Loopback record is incorrectly configured in IPAM, please investigate this manually!"
raise ProcessFailureError(msg)
@step("Verify correct Netbox entry")
def verify_netbox_entry(subscription: Router) -> None:
"""Validate the Netbox entry for a Router.
This will only ensure existence of the node itself in Netbox. Validation of separate interfaces takes places in
other subscriptions' validation workflows.
"""
client = NetboxClient()
# Try and fetch the host, which will raise an exception on failure.
client.get_device_by_name(subscription.router.router_fqdn)
@step("Verify correct LibreNMS entry")
def verify_librenms_entry(subscription: Router) -> None:
"""Validate the LibreNMS entry for a Router.
Raises an HTTP error 404 when the device is not present in LibreNMS.
"""
client = LibreNMSClient()
client.get_device(subscription.router.router_fqdn)
@step("Check base config for drift")
def verify_base_config(subscription: Router, callback_route: str) -> None:
"""Workflow step for running a playbook that checks whether base config has drifted."""
execute_playbook(
playbook_name="base_config.yaml",
callback_route=callback_route,
inventory=subscription.router.router_fqdn,
extra_vars={"wfo_router_json": subscription, "verb": "check-drift"},
)
@step("Validate iBGP mesh configuration")
def validate_ibgp_mesh_config(subscription: Router, callback_route: str) -> None:
"""Workflow step for running a playbook that check iBGP mesh configuration."""
execute_playbook(
playbook_name="ibgp_checks.yaml",
callback_route=callback_route,
inventory=subscription.router.router_fqdn,
extra_vars={"wfo_router_json": subscription},
)
@workflow(
"Validate router configuration", target=Target.SYSTEM, initial_input_form=wrap_modify_initial_input_form(None)
)
def validate_router() -> StepList:
"""Validate an existing, active Router subscription.
* Verify that the loopback interface is correctly configured in :term:`IPAM`.
* Verify that the router is correctly configured in Netbox.
* Verify that the router is correctly configured in LibreNMS.
* Redeploy base config to verify the configuration is intact.
* Validate configuration of the iBGP mesh
"""
is_juniper_router = conditional(lambda state: state["subscription"]["router"]["vendor"] == Vendor.JUNIPER)
return (
begin
>> store_process_subscription(Target.SYSTEM)
>> prepare_state
>> is_juniper_router(done)
>> unsync
>> verify_ipam_loopback
>> verify_netbox_entry
>> verify_librenms_entry
>> anonymous_lso_interaction(verify_base_config)
>> anonymous_lso_interaction(validate_ibgp_mesh_config)
>> resync
>> done
)