diff --git a/gso/workflows/iptrunk/validate_iptrunk.py b/gso/workflows/iptrunk/validate_iptrunk.py index a66cb41986f9117447e81939afb608507d925b8a..ea80bb9cff06fbbc5f7cabb1686e68e1530914ec 100644 --- a/gso/workflows/iptrunk/validate_iptrunk.py +++ b/gso/workflows/iptrunk/validate_iptrunk.py @@ -8,6 +8,7 @@ from orchestrator.utils.json import json_dumps from orchestrator.workflow import StepList, done, init, step, workflow from orchestrator.workflows.steps import resync, store_process_subscription, unsync from orchestrator.workflows.utils import wrap_modify_initial_input_form +from services.netbox_client import NetboxClient from workflows.iptrunk.deploy_twamp import deploy_twamp_dry from workflows.iptrunk.migrate_iptrunk import check_ip_trunk_isis from workflows.iptrunk.modify_trunk_interface import provision_ip_trunk_iface_dry @@ -15,6 +16,8 @@ from workflows.iptrunk.modify_trunk_interface import provision_ip_trunk_iface_dr from gso.products.product_types.iptrunk import Iptrunk from gso.services import infoblox from gso.services.lso_client import anonymous_lso_interaction, execute_playbook +from gso.utils.helpers import get_router_vendor +from gso.utils.shared_enums import Vendor from gso.utils.workflow_steps import detect_configuration_drift @@ -46,8 +49,8 @@ def verify_ipam_records(subscription: Iptrunk) -> None: ipam_errors += [ ( "Missing IP trunk IPAM records, found the following instead.\n" - f"IPv4 expected {subscription.iptrunk.iptrunk_ipv4_network}, actual: {ipam_v4_network.network}\n" - f"IPv6 expected {subscription.iptrunk.iptrunk_ipv6_network}, actual: {ipam_v6_network.network}" + f"IPv4 expected '{subscription.iptrunk.iptrunk_ipv4_network}', actual: '{ipam_v4_network.network}'\n" + f"IPv6 expected '{subscription.iptrunk.iptrunk_ipv6_network}', actual: '{ipam_v6_network.network}'" ) ] @@ -58,14 +61,14 @@ def verify_ipam_records(subscription: Iptrunk) -> None: # Validate IPv4 address allocation record = infoblox.find_host_by_fqdn(lag_fqdn) if not record: - ipam_errors += [f"No IPv4 host record found with FQDN {lag_fqdn}"] + ipam_errors += [f"No IPv4 host record found with FQDN '{lag_fqdn}'"] else: # Allocation inside IPv4 network must be correct if str(side_v4) != record.ipv4addr: ipam_errors += [ ( - f"Incorrectly allocated host record for FQDN {lag_fqdn}.\n" - f"Expected {side_v4}, actual: {record.ipv4addr}" + f"Incorrectly allocated host record for FQDN '{lag_fqdn}'.\n" + f"Expected '{side_v4}', actual: '{record.ipv4addr}'" ) ] @@ -73,22 +76,22 @@ def verify_ipam_records(subscription: Iptrunk) -> None: if record.comment != subscription.subscription_id: ipam_errors += [ ( - f"Incorrect host record found for {lag_fqdn} at {side_v4}. Comment should have been equal to" - f"subscription ID {subscription.subscription_id}." + f"Incorrect host record found for '{lag_fqdn}' at '{side_v4}'. Comment should have been equal " + f"to subscription ID '{subscription.subscription_id}'." ) ] # Validate IPv6 address allocation record = infoblox.find_v6_host_by_fqdn(lag_fqdn) if not record: - ipam_errors += [f"No IPv6 host record found with FQDN {lag_fqdn}"] + ipam_errors += [f"No IPv6 host record found with FQDN '{lag_fqdn}'"] else: # Allocation inside IPv6 network must be correct if str(side_v6) != record.ipv6addr: ipam_errors += [ ( - f"Incorrectly allocated host record for FQDN {lag_fqdn}.\n" - f"Expected {side_v6}, actual: {record.ipv6addr}" + f"Incorrectly allocated host record for FQDN '{lag_fqdn}'.\n" + f"Expected '{side_v6}', actual: '{record.ipv6addr}'" ) ] @@ -96,8 +99,8 @@ def verify_ipam_records(subscription: Iptrunk) -> None: if record.comment != subscription.subscription_id: ipam_errors += [ ( - f"Incorrect host record found for {lag_fqdn} at {side_v6}. Comment should have been equal to" - f"subscription ID {subscription.subscription_id}." + f"Incorrect host record found for '{lag_fqdn}' at '{side_v6}'. Comment should have been equal " + f"to subscription ID '{subscription.subscription_id}'." ) ] @@ -105,9 +108,38 @@ def verify_ipam_records(subscription: Iptrunk) -> None: raise ProcessFailureError(message="IPAM misconfiguration(s) found", details=str(ipam_errors)) -@step("Verify Netbox entries") -def verify_netbox_entries() -> None: - """Validate required entries for an IP trunk in Netbox.""" +@step("Verify NetBox entries") +def verify_netbox_entries(subscription: Iptrunk): + """Validate required entries for an IP trunk in NetBox.""" + nbclient = NetboxClient() + netbox_errors = [] + for side in subscription.iptrunk.iptrunk_sides: + if get_router_vendor(side.iptrunk_side_node.owner_subscription_id) == Vendor.NOKIA: + # Raises en exception when not found. + interface = nbclient.get_interface_by_name_and_device( + side.iptrunk_side_ae_iface, side.iptrunk_side_node.router_fqdn + ) + if interface.description != str(subscription.subscription_id): + netbox_errors += [ + ( + f"Incorrect description for '{side.iptrunk_side_ae_iface}', expected " + f"'{subscription.subscription_id}' but got '{interface.description}'" + ) + ] + for member in side.iptrunk_side_ae_members: + interface = nbclient.get_interface_by_name_and_device( + member.interface_name, side.iptrunk_side_node.router_fqdn + ) + if interface.description != str(subscription.subscription_id): + netbox_errors += [ + ( + f"Incorrect description for '{member.interface_name}', expected " + f"'{subscription.subscription_id}' but got '{interface.description}'" + ) + ] + + if netbox_errors: + raise ProcessFailureError(message="NetBox misconfiguration(s) found", details=str(netbox_errors)) @workflow( @@ -118,8 +150,11 @@ def verify_netbox_entries() -> None: def validate_iptrunk() -> StepList: """Validate an existing, active IP Trunk subscription. - * Run an Ansible playbook to verify the configuration is intact. * Verify that the :term:`LAG` interfaces are correctly configured in :term:`IPAM`. + * Check correct configuration of interfaces in NetBox. + * Verify the configuration on both sides of the trunk is intact. + * Check the ISIS metric of the trunk. + * Verify that TWAMP configuration is correct. """ return ( init