From 247c178deb6dd05809b83b5b5df157300c7845f9 Mon Sep 17 00:00:00 2001 From: Karel van Klink <karel.vanklink@geant.org> Date: Tue, 1 Oct 2024 10:26:41 +0200 Subject: [PATCH] Rework LSO interaction Automatically skip a playbook execution if the inventory is empty to prevent hangs --- gso/services/lso_client.py | 75 +++- gso/utils/workflow_steps.py | 72 ++-- gso/workflows/iptrunk/create_iptrunk.py | 150 +++---- gso/workflows/iptrunk/deploy_twamp.py | 68 +-- gso/workflows/iptrunk/migrate_iptrunk.py | 395 +++++++++--------- gso/workflows/iptrunk/modify_isis_metric.py | 58 ++- .../iptrunk/modify_trunk_interface.py | 123 +++--- gso/workflows/iptrunk/terminate_iptrunk.py | 48 ++- gso/workflows/iptrunk/validate_iptrunk.py | 87 ++-- gso/workflows/router/promote_p_to_pe.py | 319 ++++++-------- gso/workflows/router/terminate_router.py | 113 +++-- gso/workflows/router/update_ibgp_mesh.py | 69 ++- gso/workflows/router/validate_router.py | 34 +- test/workflows/iptrunk/test_create_iptrunk.py | 6 +- test/workflows/iptrunk/test_deploy_twamp.py | 2 +- .../iptrunk/test_modify_isis_metric.py | 2 +- .../iptrunk/test_terminate_iptrunk.py | 12 +- .../iptrunk/test_validate_iptrunk.py | 4 +- test/workflows/router/test_create_router.py | 4 +- test/workflows/router/test_promote_p_to_pe.py | 7 +- .../workflows/router/test_terminate_router.py | 6 + .../workflows/router/test_update_ibgp_mesh.py | 2 +- test/workflows/router/test_validate_router.py | 2 +- 23 files changed, 824 insertions(+), 834 deletions(-) diff --git a/gso/services/lso_client.py b/gso/services/lso_client.py index 77c22647..2215a7f5 100644 --- a/gso/services/lso_client.py +++ b/gso/services/lso_client.py @@ -12,7 +12,7 @@ from orchestrator import step from orchestrator.config.assignee import Assignee from orchestrator.types import State from orchestrator.utils.errors import ProcessFailureError -from orchestrator.workflow import Step, StepList, begin, callback_step, inputstep +from orchestrator.workflow import Step, StepList, begin, callback_step, conditional, inputstep from pydantic import ConfigDict from pydantic_forms.core import FormPage from pydantic_forms.types import FormGenerator @@ -48,11 +48,9 @@ def _send_request(parameters: dict, callback_route: str) -> None: request.raise_for_status() -def execute_playbook( - playbook_name: str, - callback_route: str, - inventory: dict[str, Any] | str, - extra_vars: dict[str, Any], +@step("Execute Ansible playbook") +def _execute_playbook( + playbook_name: str, callback_route: str, inventory: dict[str, Any], extra_vars: dict[str, Any] ) -> None: """Execute a playbook remotely through the provisioning proxy. @@ -71,7 +69,8 @@ def execute_playbook( }, "host2.local": { "key": "value" - } + }, + "host3.local": None } } } @@ -141,7 +140,38 @@ def _show_results(state: State) -> FormGenerator: @step("Clean up keys from state") def _clean_state() -> State: - return {"__remove_keys": ["run_results", "lso_result_title", "lso_result_extra_label", "callback_result"]} + return { + "__remove_keys": [ + "run_results", + "lso_result_title", + "lso_result_extra_label", + "callback_result", + "playbook_name", + "callback_route", + "inventory", + "extra_vars", + ] + } + + +def _inventory_is_set(state: State) -> bool: + """Validate whether the passed Ansible inventory is empty. + + If the inventory is empty, which can happen in select cases, there should be no playbook run. This conditional will + prevent from calling out to :term:`LSO` with an empty playbook, which would cause the Ansible runner process to + hang. This in turn will result in a workflow step that is never called back to. + """ + if "inventory" not in state: + msg = "Missing Ansible inventory for playbook." + raise ProcessFailureError(msg, details="Key 'inventory' not found in state.") + if "all" not in state["inventory"] or "hosts" not in state["inventory"]["all"]: + msg = "Malformed Ansible inventory found in state." + raise ProcessFailureError(msg, details="Ansible inventory must be in YAML form, not string.") + + return state["inventory"]["all"]["hosts"] + + +_inventory_is_not_empty = conditional(_inventory_is_set) def lso_interaction(provisioning_step: Step) -> StepList: @@ -162,9 +192,15 @@ def lso_interaction(provisioning_step: Step) -> StepList: """ return ( begin - >> callback_step(name=provisioning_step.name, action_step=provisioning_step, validate_step=_evaluate_results) - >> step("Inject result title")(lambda: {"lso_result_title": provisioning_step.name}) - >> _show_results + >> provisioning_step + >> _inventory_is_not_empty( + begin + >> callback_step( + name="Running Ansible playbook", action_step=_execute_playbook, validate_step=_evaluate_results + ) + >> step("Inject result title")(lambda: {"lso_result_title": provisioning_step.name}) + >> _show_results + ) >> _clean_state ) @@ -187,9 +223,15 @@ def indifferent_lso_interaction(provisioning_step: Step) -> StepList: """ return ( begin - >> callback_step(name=provisioning_step.name, action_step=provisioning_step, validate_step=_ignore_results) - >> step("Inject result title")(lambda: {"lso_result_title": provisioning_step.name}) - >> _show_results + >> provisioning_step + >> _inventory_is_not_empty( + begin + >> callback_step( + name="Running Ansible playbook", action_step=_execute_playbook, validate_step=_ignore_results + ) + >> step("Inject result title")(lambda: {"lso_result_title": provisioning_step.name}) + >> _show_results + ) >> _clean_state ) @@ -207,6 +249,9 @@ def anonymous_lso_interaction(provisioning_step: Step, validation_step: Step = _ """ return ( begin - >> callback_step(name=provisioning_step.name, action_step=provisioning_step, validate_step=validation_step) + >> provisioning_step + >> _inventory_is_not_empty( + callback_step(name="Running Ansible playbook", action_step=_execute_playbook, validate_step=validation_step) + ) >> _clean_state ) diff --git a/gso/utils/workflow_steps.py b/gso/utils/workflow_steps.py index 93891456..5a714e1b 100644 --- a/gso/utils/workflow_steps.py +++ b/gso/utils/workflow_steps.py @@ -13,20 +13,16 @@ from pydantic_forms.types import FormGenerator from pydantic_forms.validators import Label from gso.products.product_types.iptrunk import Iptrunk -from gso.services import lso_client from gso.settings import load_oss_params def _deploy_base_config( subscription: dict[str, Any], tt_number: str, - callback_route: str, process_id: UUIDstr, *, dry_run: bool, -) -> None: - inventory = subscription["router"]["router_fqdn"] - +) -> State: extra_vars = { "wfo_router_json": subscription, "dry_run": dry_run, @@ -34,42 +30,27 @@ def _deploy_base_config( "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploy base config", } - lso_client.execute_playbook( - playbook_name="base_config.yaml", - callback_route=callback_route, - inventory=inventory, - extra_vars=extra_vars, - ) + return { + "playbook_name": "base_config.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}}, + "extra_vars": extra_vars, + } @step("[DRY RUN] Deploy base config") -def deploy_base_config_dry( - subscription: dict[str, Any], - tt_number: str, - callback_route: str, - process_id: UUIDstr, -) -> State: +def deploy_base_config_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Perform a dry run of provisioning base config on a router.""" - _deploy_base_config(subscription, tt_number, callback_route, process_id, dry_run=True) - - return {"subscription": subscription} + return _deploy_base_config(subscription, tt_number, process_id, dry_run=True) @step("[FOR REAL] Deploy base config") -def deploy_base_config_real( - subscription: dict[str, Any], - tt_number: str, - callback_route: str, - process_id: UUIDstr, -) -> State: +def deploy_base_config_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Deploy base config on a router using the provisioning proxy.""" - _deploy_base_config(subscription, tt_number, callback_route, process_id, dry_run=False) - - return {"subscription": subscription} + return _deploy_base_config(subscription, tt_number, process_id, dry_run=False) @step("[FOR REAL] Set ISIS metric to very high value") -def set_isis_to_max(subscription: Iptrunk, process_id: UUIDstr, callback_route: str, tt_number: str) -> State: +def set_isis_to_max(subscription: Iptrunk, process_id: UUIDstr, tt_number: str) -> State: """Workflow step for setting the :term:`ISIS` metric to an arbitrarily high value to drain a link.""" old_isis_metric = subscription.iptrunk.iptrunk_isis_metric params = load_oss_params() @@ -83,29 +64,30 @@ def set_isis_to_max(subscription: Iptrunk, process_id: UUIDstr, callback_route: f"{subscription.iptrunk.geant_s_sid}", } - lso_client.execute_playbook( - playbook_name="iptrunks.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", - extra_vars=extra_vars, - ) - return { "subscription": subscription, + "playbook_name": "iptrunks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, "old_isis_metric": old_isis_metric, } @step("Run show commands after base config install") -def run_checks_after_base_config(subscription: dict[str, Any], callback_route: str) -> None: +def run_checks_after_base_config(subscription: dict[str, Any]) -> State: """Workflow step for running show commands after installing base config.""" - lso_client.execute_playbook( - playbook_name="base_config_checks.yaml", - callback_route=callback_route, - inventory=subscription["router"]["router_fqdn"], - extra_vars={"wfo_router_json": subscription}, - ) + return { + "playbook_name": "base_config_checks.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]}}}, + "extra_vars": {"wfo_router_json": subscription}, + } @inputstep("Prompt for new SharePoint checklist", assignee=Assignee.SYSTEM) diff --git a/gso/workflows/iptrunk/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py index 56907a6c..8bb7c993 100644 --- a/gso/workflows/iptrunk/create_iptrunk.py +++ b/gso/workflows/iptrunk/create_iptrunk.py @@ -28,7 +28,7 @@ from gso.products.product_blocks.iptrunk import ( from gso.products.product_types.iptrunk import Iptrunk, IptrunkInactive, IptrunkProvisioning from gso.products.product_types.router import Router from gso.services import infoblox, subscriptions -from gso.services.lso_client import execute_playbook, lso_interaction +from gso.services.lso_client import lso_interaction from gso.services.netbox_client import NetboxClient from gso.services.partners import get_partner_by_name from gso.services.sharepoint import SharePointClient @@ -325,12 +325,7 @@ def initialize_subscription( @step("[DRY RUN] Provision IP trunk interface") -def provision_ip_trunk_iface_dry( - subscription: IptrunkInactive, - callback_route: str, - process_id: UUIDstr, - tt_number: str, -) -> State: +def provision_ip_trunk_iface_dry(subscription: IptrunkInactive, process_id: UUIDstr, tt_number: str) -> State: """Perform a dry run of deploying configuration on both sides of the trunk.""" extra_vars = { "wfo_trunk_json": json.loads(json_dumps(subscription)), @@ -341,24 +336,22 @@ def provision_ip_trunk_iface_dry( f"{subscription.iptrunk.geant_s_sid}", } - execute_playbook( - playbook_name="iptrunks.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("[FOR REAL] Provision IP trunk interface") -def provision_ip_trunk_iface_real( - subscription: IptrunkInactive, - callback_route: str, - process_id: UUIDstr, - tt_number: str, -) -> State: +def provision_ip_trunk_iface_real(subscription: IptrunkInactive, process_id: UUIDstr, tt_number: str) -> State: """Deploy IP trunk configuration on both sides.""" extra_vars = { "wfo_trunk_json": json.loads(json_dumps(subscription)), @@ -369,42 +362,34 @@ def provision_ip_trunk_iface_real( f"{subscription.iptrunk.geant_s_sid}", } - execute_playbook( - playbook_name="iptrunks.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("Check IP connectivity of the trunk") -def check_ip_trunk_connectivity( - subscription: IptrunkInactive, - callback_route: str, -) -> State: +def check_ip_trunk_connectivity(subscription: IptrunkInactive) -> State: """Check successful connectivity across the new trunk.""" extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "ping"} - execute_playbook( - playbook_name="iptrunks_checks.yaml", - callback_route=callback_route, - inventory=subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn, # type: ignore[arg-type] - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks_checks.yaml", + "inventory": {"all": {"hosts": {subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None}}}, + "extra_vars": extra_vars, + } @step("[DRY RUN] Provision IP trunk ISIS interface") -def provision_ip_trunk_isis_iface_dry( - subscription: IptrunkInactive, - callback_route: str, - process_id: UUIDstr, - tt_number: str, -) -> State: +def provision_ip_trunk_isis_iface_dry(subscription: IptrunkInactive, process_id: UUIDstr, tt_number: str) -> State: """Perform a dry run of deploying :term:`ISIS` configuration.""" extra_vars = { "wfo_trunk_json": json.loads(json_dumps(subscription)), @@ -415,24 +400,22 @@ def provision_ip_trunk_isis_iface_dry( f"{subscription.iptrunk.geant_s_sid}", } - execute_playbook( - playbook_name="iptrunks.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("[FOR REAL] Provision IP trunk ISIS interface") -def provision_ip_trunk_isis_iface_real( - subscription: IptrunkInactive, - callback_route: str, - process_id: UUIDstr, - tt_number: str, -) -> State: +def provision_ip_trunk_isis_iface_real(subscription: IptrunkInactive, process_id: UUIDstr, tt_number: str) -> State: """Deploy :term:`ISIS` configuration on both sides.""" extra_vars = { "wfo_trunk_json": json.loads(json_dumps(subscription)), @@ -443,33 +426,30 @@ def provision_ip_trunk_isis_iface_real( f"{subscription.iptrunk.geant_s_sid}", } - execute_playbook( - playbook_name="iptrunks.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("Check ISIS adjacency") -def check_ip_trunk_isis( - subscription: IptrunkInactive, - callback_route: str, -) -> State: +def check_ip_trunk_isis(subscription: IptrunkInactive) -> State: """Run an Ansible playbook to confirm :term:`ISIS` adjacency.""" extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "isis"} - execute_playbook( - playbook_name="iptrunks_checks.yaml", - callback_route=callback_route, - inventory=subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn, # type: ignore[arg-type] - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks_checks.yaml", + "inventory": {"all": {"hosts": {subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None}}}, + "extra_vars": extra_vars, + } @step("Register DNS records for both sides of the trunk") diff --git a/gso/workflows/iptrunk/deploy_twamp.py b/gso/workflows/iptrunk/deploy_twamp.py index 11dce53b..36d1644b 100644 --- a/gso/workflows/iptrunk/deploy_twamp.py +++ b/gso/workflows/iptrunk/deploy_twamp.py @@ -12,7 +12,7 @@ from orchestrator.workflows.steps import resync, store_process_subscription, uns from orchestrator.workflows.utils import wrap_modify_initial_input_form from gso.products.product_types.iptrunk import Iptrunk -from gso.services.lso_client import execute_playbook, lso_interaction +from gso.services.lso_client import lso_interaction from gso.utils.types.tt_number import TTNumber @@ -33,7 +33,7 @@ def _initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: @step("[DRY RUN] Deploy TWAMP on both sides") -def deploy_twamp_dry(subscription: Iptrunk, process_id: UUIDstr, callback_route: str, tt_number: str) -> State: +def deploy_twamp_dry(subscription: Iptrunk, process_id: UUIDstr, tt_number: str) -> State: """Perform a dry run of deploying the :term:`TWAMP` session.""" extra_vars = { "subscription": json.loads(json_dumps(subscription)), @@ -43,18 +43,22 @@ def deploy_twamp_dry(subscription: Iptrunk, process_id: UUIDstr, callback_route: "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploy TWAMP", } - inventory = ( - f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}" - f"\n{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}" - ) - - execute_playbook("deploy_twamp.yaml", callback_route, inventory, extra_vars) - - return {"subscription": subscription} + return { + "playbook_name": "deploy_twamp.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("[FOR REAL] Deploy TWAMP on both sides") -def deploy_twamp_real(subscription: Iptrunk, process_id: UUIDstr, callback_route: str, tt_number: str) -> State: +def deploy_twamp_real(subscription: Iptrunk, process_id: UUIDstr, tt_number: str) -> State: """Deploy the :term:`TWAMP` session.""" extra_vars = { "subscription": json.loads(json_dumps(subscription)), @@ -64,32 +68,40 @@ def deploy_twamp_real(subscription: Iptrunk, process_id: UUIDstr, callback_route "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploy TWAMP", } - inventory = ( - f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}" - f"\n{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}" - ) - - execute_playbook("deploy_twamp.yaml", callback_route, inventory, extra_vars) - - return {"subscription": subscription} + return { + "playbook_name": "deploy_twamp.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("Check TWAMP status on both sides") -def check_twamp_status(subscription: Iptrunk, callback_route: str) -> State: +def check_twamp_status(subscription: Iptrunk) -> State: """Check TWAMP session.""" extra_vars = { "subscription": json.loads(json_dumps(subscription)), "verb": "check_twamp", } - inventory = ( - f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}" - f"\n{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}" - ) - - execute_playbook("deploy_twamp.yaml", callback_route, inventory, extra_vars) - - return {"subscription": subscription} + return { + "playbook_name": "deploy_twamp.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @workflow( diff --git a/gso/workflows/iptrunk/migrate_iptrunk.py b/gso/workflows/iptrunk/migrate_iptrunk.py index c7021875..0077db4e 100644 --- a/gso/workflows/iptrunk/migrate_iptrunk.py +++ b/gso/workflows/iptrunk/migrate_iptrunk.py @@ -28,7 +28,7 @@ from gso.products.product_blocks.iptrunk import IptrunkInterfaceBlock, IptrunkTy from gso.products.product_types.iptrunk import Iptrunk from gso.products.product_types.router import Router from gso.services import infoblox -from gso.services.lso_client import execute_playbook, lso_interaction +from gso.services.lso_client import lso_interaction from gso.services.netbox_client import NetboxClient from gso.services.sharepoint import SharePointClient from gso.services.subscriptions import get_active_router_subscriptions @@ -203,28 +203,27 @@ def calculate_old_side_data(subscription: Iptrunk, replace_index: int) -> State: @step("Check Optical PRE levels on the trunk endpoint") -def check_ip_trunk_optical_levels_pre(subscription: Iptrunk, callback_route: str) -> State: +def check_ip_trunk_optical_levels_pre(subscription: Iptrunk) -> State: """Check Optical PRE levels on the trunk.""" extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "optical_pre"} - execute_playbook( - playbook_name="iptrunks_checks.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks_checks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("Check Optical POST levels on the trunk endpoint") def check_ip_trunk_optical_levels_post( - subscription: Iptrunk, - callback_route: str, - new_node: Router, - new_lag_member_interfaces: list[dict], - replace_index: int, + subscription: Iptrunk, new_node: Router, new_lag_member_interfaces: list[dict], replace_index: int ) -> State: """Check Optical POST levels on the trunk.""" extra_vars = { @@ -235,24 +234,23 @@ def check_ip_trunk_optical_levels_post( "check": "optical_post", } - execute_playbook( - playbook_name="iptrunks_checks.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn}\n" - f"{new_node.router.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks_checks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn: None, + new_node.router.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("Check LLDP on the trunk endpoints") def check_ip_trunk_lldp( - subscription: Iptrunk, - callback_route: str, - new_node: Router, - new_lag_member_interfaces: list[dict], - replace_index: int, + subscription: Iptrunk, new_node: Router, new_lag_member_interfaces: list[dict], replace_index: int ) -> State: """Check LLDP on the new trunk endpoints.""" extra_vars = { @@ -263,21 +261,23 @@ def check_ip_trunk_lldp( "check": "lldp", } - execute_playbook( - playbook_name="iptrunks_checks.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn}\n" - f"{new_node.router.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks_checks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn: None, + new_node.router.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("[DRY RUN] Disable configuration on old router") def disable_old_config_dry( subscription: Iptrunk, - callback_route: str, new_node: Router, new_lag_interface: str, new_lag_member_interfaces: list[dict], @@ -299,22 +299,24 @@ def disable_old_config_dry( f"- Deploy config for {subscription.iptrunk.geant_s_sid}", } - execute_playbook( - playbook_name="iptrunks_migration.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n" - f"{new_node.router.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks_migration.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + new_node.router.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("[FOR REAL] Disable configuration on old router") def disable_old_config_real( subscription: Iptrunk, - callback_route: str, new_node: Router, new_lag_interface: str, new_lag_member_interfaces: list[dict], @@ -336,24 +338,24 @@ def disable_old_config_real( f"- Deploy config for {subscription.iptrunk.geant_s_sid}", } - execute_playbook( - playbook_name="iptrunks_migration.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n" - f"{new_node.router.router_fqdn}\n", - extra_vars=extra_vars, - ) - return { - "subscription": subscription, + "playbook_name": "iptrunks_migration.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + new_node.router.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, } @step("[DRY RUN] Deploy configuration on new router") def deploy_new_config_dry( subscription: Iptrunk, - callback_route: str, new_node: Router, new_lag_interface: str, new_lag_member_interfaces: list[dict], @@ -375,22 +377,24 @@ def deploy_new_config_dry( f"- Deploy config for {subscription.iptrunk.geant_s_sid}", } - execute_playbook( - playbook_name="iptrunks_migration.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n" - f"{new_node.router.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks_migration.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + new_node.router.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("[FOR REAL] Deploy configuration on new router") def deploy_new_config_real( subscription: Iptrunk, - callback_route: str, new_node: Router, new_lag_interface: str, new_lag_member_interfaces: list[dict], @@ -412,26 +416,24 @@ def deploy_new_config_real( f"- Deploy config for {subscription.iptrunk.geant_s_sid}", } - execute_playbook( - playbook_name="iptrunks_migration.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n" - f"{new_node.router.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks_migration.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + new_node.router.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("[DRY RUN] Update BFD on the remaining side") def update_remaining_side_bfd_dry( - subscription: Iptrunk, - callback_route: str, - new_node: Router, - replace_index: int, - process_id: UUIDstr, - tt_number: str, + subscription: Iptrunk, new_node: Router, replace_index: int, process_id: UUIDstr, tt_number: str ) -> State: """Perform a dry run of updating configuration on the remaining router.""" extra_vars = { @@ -444,24 +446,22 @@ def update_remaining_side_bfd_dry( "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} " f"- Update BFD config.", } - execute_playbook( - playbook_name="iptrunks_migration.yaml", - callback_route=callback_route, - inventory=subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn, - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks_migration.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("[FOR REAL] Update BFD on the remaining side") def update_remaining_side_bfd_real( - subscription: Iptrunk, - callback_route: str, - new_node: Router, - replace_index: int, - process_id: UUIDstr, - tt_number: str, + subscription: Iptrunk, new_node: Router, replace_index: int, process_id: UUIDstr, tt_number: str ) -> State: """Update configuration on the remaining router.""" extra_vars = { @@ -474,23 +474,21 @@ def update_remaining_side_bfd_real( "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} " f"- Update BFD config.", } - execute_playbook( - playbook_name="iptrunks_migration.yaml", - callback_route=callback_route, - inventory=subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn, - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks_migration.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("Check BFD session over trunk") -def check_ip_trunk_bfd( - subscription: Iptrunk, - callback_route: str, - new_node: Router, - replace_index: int, -) -> State: +def check_ip_trunk_bfd(subscription: Iptrunk, new_node: Router, replace_index: int) -> State: """Check BFD session across the new trunk.""" extra_vars = { "wfo_ip_trunk_json": json.loads(json_dumps(subscription)), @@ -498,14 +496,17 @@ def check_ip_trunk_bfd( "check": "bfd", } - execute_playbook( - playbook_name="iptrunks_checks.yaml", - callback_route=callback_route, - inventory=subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn, - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks_checks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @inputstep("Wait for confirmation", assignee=Assignee.SYSTEM) @@ -523,28 +524,26 @@ def confirm_continue_move_fiber() -> FormGenerator: @step("Check IP connectivity of the trunk") -def check_ip_trunk_connectivity( - subscription: Iptrunk, - callback_route: str, - replace_index: int, -) -> State: +def check_ip_trunk_connectivity(subscription: Iptrunk, replace_index: int) -> State: """Check successful connectivity across the new trunk.""" extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "ping"} - execute_playbook( - playbook_name="iptrunks_checks.yaml", - callback_route=callback_route, - inventory=subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn, - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks_checks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("[FOR REAL] Deploy ISIS configuration on new router") def deploy_new_isis( subscription: Iptrunk, - callback_route: str, new_node: Router, new_lag_interface: str, new_lag_member_interfaces: list[dict], @@ -566,35 +565,37 @@ def deploy_new_isis( f"- Deploy config for {subscription.iptrunk.geant_s_sid}", } - execute_playbook( - playbook_name="iptrunks_migration.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n" - f"{new_node.router.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks_migration.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + new_node.router.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("Check ISIS adjacency") -def check_ip_trunk_isis( - subscription: Iptrunk, - callback_route: str, - replace_index: int, -) -> State: +def check_ip_trunk_isis(subscription: Iptrunk, replace_index: int) -> State: """Run an Ansible playbook to confirm :term:`ISIS` adjacency.""" extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "isis"} - execute_playbook( - playbook_name="iptrunks_checks.yaml", - callback_route=callback_route, - inventory=subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn, - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks_checks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[1 - replace_index].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @inputstep("Wait for confirmation", assignee=Assignee.SYSTEM) @@ -615,7 +616,6 @@ def confirm_continue_restore_isis() -> FormGenerator: def restore_isis_metric( subscription: Iptrunk, process_id: UUIDstr, - callback_route: str, tt_number: str, old_isis_metric: int, ) -> State: @@ -630,21 +630,23 @@ def restore_isis_metric( f"{subscription.iptrunk.geant_s_sid}", } - execute_playbook( - playbook_name="iptrunks.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("[DRY RUN] Delete configuration on old router") def delete_old_config_dry( subscription: Iptrunk, - callback_route: str, new_node: Router, new_lag_interface: str, new_lag_member_interfaces: list[dict], @@ -666,22 +668,24 @@ def delete_old_config_dry( f"- Deploy config for {subscription.iptrunk.geant_s_sid}", } - execute_playbook( - playbook_name="iptrunks_migration.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n" - f"{new_node.router.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks_migration.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + new_node.router.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("[FOR REAL] Delete configuration on old router") def delete_old_config_real( subscription: Iptrunk, - callback_route: str, new_node: Router, new_lag_interface: str, new_lag_member_interfaces: list[dict], @@ -703,16 +707,19 @@ def delete_old_config_real( f"- Deploy config for {subscription.iptrunk.geant_s_sid}", } - execute_playbook( - playbook_name="iptrunks_migration.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n" - f"{new_node.router.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks_migration.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + new_node.router.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("Update IP records in IPAM") diff --git a/gso/workflows/iptrunk/modify_isis_metric.py b/gso/workflows/iptrunk/modify_isis_metric.py index da14d078..daf24ec7 100644 --- a/gso/workflows/iptrunk/modify_isis_metric.py +++ b/gso/workflows/iptrunk/modify_isis_metric.py @@ -11,7 +11,7 @@ from orchestrator.workflows.steps import resync, store_process_subscription, uns from orchestrator.workflows.utils import wrap_modify_initial_input_form from gso.products.product_types.iptrunk import Iptrunk -from gso.services.lso_client import execute_playbook, lso_interaction +from gso.services.lso_client import lso_interaction from gso.utils.types.tt_number import TTNumber @@ -37,12 +37,7 @@ def modify_iptrunk_subscription(subscription: Iptrunk, isis_metric: int) -> Stat @step("[DRY RUN] Provision IP trunk ISIS interface") -def provision_ip_trunk_isis_iface_dry( - subscription: Iptrunk, - process_id: UUIDstr, - callback_route: str, - tt_number: str, -) -> State: +def provision_ip_trunk_isis_iface_dry(subscription: Iptrunk, process_id: UUIDstr, tt_number: str) -> State: """Perform a dry run of deploying the new :term:`ISIS` metric on both sides of the trunk.""" extra_vars = { "wfo_trunk_json": json.loads(json_dumps(subscription)), @@ -53,24 +48,22 @@ def provision_ip_trunk_isis_iface_dry( f"{subscription.iptrunk.geant_s_sid}", } - execute_playbook( - playbook_name="iptrunks.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("[FOR REAL] Provision IP trunk ISIS interface") -def provision_ip_trunk_isis_iface_real( - subscription: Iptrunk, - process_id: UUIDstr, - callback_route: str, - tt_number: str, -) -> State: +def provision_ip_trunk_isis_iface_real(subscription: Iptrunk, process_id: UUIDstr, tt_number: str) -> State: """Deploy the new :term:`ISIS` metric on both sides of the trunk.""" extra_vars = { "wfo_trunk_json": json.loads(json_dumps(subscription)), @@ -81,15 +74,18 @@ def provision_ip_trunk_isis_iface_real( f"{subscription.iptrunk.geant_s_sid}", } - execute_playbook( - playbook_name="iptrunks.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @workflow( diff --git a/gso/workflows/iptrunk/modify_trunk_interface.py b/gso/workflows/iptrunk/modify_trunk_interface.py index e947fd52..350ea64a 100644 --- a/gso/workflows/iptrunk/modify_trunk_interface.py +++ b/gso/workflows/iptrunk/modify_trunk_interface.py @@ -21,7 +21,7 @@ from gso.products.product_blocks.iptrunk import ( IptrunkType, ) from gso.products.product_types.iptrunk import Iptrunk -from gso.services.lso_client import execute_playbook, lso_interaction +from gso.services.lso_client import lso_interaction from gso.services.netbox_client import NetboxClient from gso.utils.helpers import ( available_interfaces_choices, @@ -186,34 +186,40 @@ def determine_change_in_capacity( @step("Check IP connectivity of the trunk") -def check_ip_trunk_connectivity(subscription: Iptrunk, callback_route: str) -> State: +def check_ip_trunk_connectivity(subscription: Iptrunk) -> State: """Check successful connectivity across a trunk.""" extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "ping"} - execute_playbook( - playbook_name="iptrunks_checks.yaml", - callback_route=callback_route, - inventory=subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn, - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks_checks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("Check LLDP on the trunk endpoints") -def check_ip_trunk_lldp(subscription: Iptrunk, callback_route: str) -> State: +def check_ip_trunk_lldp(subscription: Iptrunk) -> State: """Check LLDP on trunk endpoints.""" extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "lldp"} - execute_playbook( - playbook_name="iptrunks_checks.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks_checks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } def update_side_members(subscription: Iptrunk, side_index: int, new_members: list[dict]) -> None: @@ -301,11 +307,7 @@ def modify_iptrunk_subscription( @step("[DRY RUN] Provision IP trunk interface") def provision_ip_trunk_iface_dry( - subscription: Iptrunk, - process_id: UUIDstr, - callback_route: str, - tt_number: str, - removed_ae_members: list[str], + subscription: Iptrunk, process_id: UUIDstr, tt_number: str, removed_ae_members: list[str] ) -> State: """Perform a dry run of deploying the updated IP trunk.""" extra_vars = { @@ -318,24 +320,23 @@ def provision_ip_trunk_iface_dry( f"{subscription.iptrunk.geant_s_sid}", } - execute_playbook( - playbook_name="iptrunks.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("[FOR REAL] Provision IP trunk interface") def provision_ip_trunk_iface_real( - subscription: Iptrunk, - process_id: UUIDstr, - callback_route: str, - tt_number: str, - removed_ae_members: list[str], + subscription: Iptrunk, process_id: UUIDstr, tt_number: str, removed_ae_members: list[str] ) -> State: """Provision the new IP trunk with updated interfaces.""" extra_vars = { @@ -348,15 +349,18 @@ def provision_ip_trunk_iface_real( f"{subscription.iptrunk.geant_s_sid}", } - execute_playbook( - playbook_name="iptrunks.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } def _netbox_update_interfaces( @@ -455,19 +459,22 @@ def allocate_interfaces_in_netbox_side_b(subscription: Iptrunk, previous_ae_memb @step("Check Optical POST levels on the trunk endpoint") -def check_ip_trunk_optical_levels_post(subscription: Iptrunk, callback_route: str) -> State: +def check_ip_trunk_optical_levels_post(subscription: Iptrunk) -> State: """Check Optical POST levels on the trunk.""" extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "optical_post"} - execute_playbook( - playbook_name="iptrunks_checks.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", - extra_vars=extra_vars, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks_checks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @workflow( diff --git a/gso/workflows/iptrunk/terminate_iptrunk.py b/gso/workflows/iptrunk/terminate_iptrunk.py index ac628d39..9596c6b9 100644 --- a/gso/workflows/iptrunk/terminate_iptrunk.py +++ b/gso/workflows/iptrunk/terminate_iptrunk.py @@ -20,7 +20,7 @@ from orchestrator.workflows.utils import wrap_modify_initial_input_form from gso.products.product_blocks.iptrunk import IptrunkSideBlock from gso.products.product_types.iptrunk import Iptrunk from gso.services import infoblox -from gso.services.lso_client import execute_playbook, lso_interaction +from gso.services.lso_client import lso_interaction from gso.services.netbox_client import NetboxClient from gso.utils.helpers import get_router_vendor from gso.utils.shared_enums import Vendor @@ -51,7 +51,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: @step("[DRY RUN] Deprovision IP trunk") -def deprovision_ip_trunk_dry(subscription: Iptrunk, process_id: UUIDstr, callback_route: str, tt_number: str) -> State: +def deprovision_ip_trunk_dry(subscription: Iptrunk, process_id: UUIDstr, tt_number: str) -> State: """Perform a dry run of deleting configuration from the routers.""" extra_vars = { "wfo_trunk_json": json.loads(json_dumps(subscription)), @@ -62,19 +62,22 @@ def deprovision_ip_trunk_dry(subscription: Iptrunk, process_id: UUIDstr, callbac f"- Remove config for {subscription.iptrunk.geant_s_sid}", } - execute_playbook( - playbook_name="iptrunks.yaml", - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", - extra_vars=extra_vars, - callback_route=callback_route, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("[FOR REAL] Deprovision IP trunk") -def deprovision_ip_trunk_real(subscription: Iptrunk, process_id: UUIDstr, callback_route: str, tt_number: str) -> State: +def deprovision_ip_trunk_real(subscription: Iptrunk, process_id: UUIDstr, tt_number: str) -> State: """Delete configuration from the routers.""" extra_vars = { "wfo_trunk_json": json.loads(json_dumps(subscription)), @@ -85,15 +88,18 @@ def deprovision_ip_trunk_real(subscription: Iptrunk, process_id: UUIDstr, callba f"- Remove config for {subscription.iptrunk.geant_s_sid}", } - execute_playbook( - playbook_name="iptrunks.yaml", - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", - extra_vars=extra_vars, - callback_route=callback_route, - ) - - return {"subscription": subscription} + return { + "playbook_name": "iptrunks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } def _free_up_interfaces_from_netbox(side_block: IptrunkSideBlock) -> None: diff --git a/gso/workflows/iptrunk/validate_iptrunk.py b/gso/workflows/iptrunk/validate_iptrunk.py index bdfc4d3b..96372192 100644 --- a/gso/workflows/iptrunk/validate_iptrunk.py +++ b/gso/workflows/iptrunk/validate_iptrunk.py @@ -8,27 +8,33 @@ from orchestrator.utils.json import json_dumps 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 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.services.lso_client import anonymous_lso_interaction from gso.services.netbox_client import NetboxClient from gso.utils.helpers import get_router_vendor from gso.utils.shared_enums import Vendor @step("Validate IP trunk configuration") -def validate_router_config(subscription: Iptrunk, callback_route: str) -> None: +def validate_router_config(subscription: Iptrunk) -> State: """Run an Ansible playbook that validates the configuration that is present on an active IP trunk.""" extra_vars = {"wfo_trunk_json": json.loads(json_dumps(subscription)), "verb": "validate"} - execute_playbook( - playbook_name="base_config.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", - extra_vars=extra_vars, - ) + return { + "playbook_name": "base_config.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": extra_vars, + } @step("Verify IPAM resources for LAG interfaces") @@ -129,56 +135,71 @@ def verify_netbox_entries(subscription: Iptrunk) -> None: @step("Verify configuration of IPtrunk") -def verify_iptrunk_config(subscription: Iptrunk, callback_route: str) -> None: +def verify_iptrunk_config(subscription: Iptrunk) -> State: """Check for configuration drift on the relevant routers.""" - execute_playbook( - playbook_name="iptrunks.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", - extra_vars={ + return { + "playbook_name": "iptrunks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": { "wfo_trunk_json": json.loads(json_dumps(subscription)), "verb": "deploy", "dry_run": "true", "config_object": "trunk_interface", "is_verification_workflow": "true", }, - ) + } @step("Check ISIS configuration") -def check_ip_trunk_isis(subscription: Iptrunk, callback_route: str) -> None: +def check_ip_trunk_isis(subscription: Iptrunk) -> State: """Run an Ansible playbook to check for any :term:`ISIS` configuration drift.""" - execute_playbook( - playbook_name="iptrunks.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", - extra_vars={ + return { + "playbook_name": "iptrunks.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": { "wfo_trunk_json": json.loads(json_dumps(subscription)), "verb": "deploy", "dry_run": "true", "config_object": "isis_interface", "is_verification_workflow": "true", }, - ) + } @step("Verify TWAMP configuration") -def verify_twamp_config(subscription: Iptrunk, callback_route: str) -> None: +def verify_twamp_config(subscription: Iptrunk) -> State: """Check for configuration drift of TWAMP.""" - execute_playbook( - playbook_name="deploy_twamp.yaml", - callback_route=callback_route, - inventory=f"{subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn}\n" - f"{subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}\n", - extra_vars={ + return { + "playbook_name": "deploy_twamp.yaml", + "inventory": { + "all": { + "hosts": { + subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn: None, + subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn: None, + } + } + }, + "extra_vars": { "subscription": json.loads(json_dumps(subscription)), "verb": "deploy", "dry_run": "true", "is_verification_workflow": "true", }, - ) + } @workflow( diff --git a/gso/workflows/router/promote_p_to_pe.py b/gso/workflows/router/promote_p_to_pe.py index 0f0b31da..66be5635 100644 --- a/gso/workflows/router/promote_p_to_pe.py +++ b/gso/workflows/router/promote_p_to_pe.py @@ -17,7 +17,6 @@ from pydantic import ConfigDict, model_validator from gso.products.product_blocks.router import RouterRole from gso.products.product_types.router import Router -from gso.services import lso_client from gso.services.kentik_client import KentikClient, NewKentikDevice from gso.services.lso_client import lso_interaction from gso.services.subscriptions import get_all_active_sites @@ -49,7 +48,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: @step("Evacuate the router by setting isis_overload") -def set_isis_overload(subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr) -> None: +def set_isis_overload(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Evacuate the router by setting isis overload.""" extra_vars = { "dry_run": False, @@ -58,18 +57,15 @@ def set_isis_overload(subscription: dict[str, Any], callback_route: str, tt_numb "verb": "set_isis_overload", } - lso_client.execute_playbook( - playbook_name="promote_p_to_pe.yaml", - callback_route=callback_route, - inventory=subscription["router"]["router_fqdn"], - extra_vars=extra_vars, - ) + return { + "playbook_name": "promote_p_to_pe.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}}, + "extra_vars": extra_vars, + } @step("[DRY RUN] Deploy PE base config") -def deploy_pe_base_config_dry( - subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr -) -> None: +def deploy_pe_base_config_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Perform a dry run of adding the base config to the router.""" extra_vars = { "dry_run": True, @@ -80,18 +76,15 @@ def deploy_pe_base_config_dry( "geant_sites": json.loads(json_dumps(get_all_active_sites())), } - lso_client.execute_playbook( - playbook_name="promote_p_to_pe.yaml", - callback_route=callback_route, - inventory=subscription["router"]["router_fqdn"], - extra_vars=extra_vars, - ) + return { + "playbook_name": "promote_p_to_pe.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}}, + "extra_vars": extra_vars, + } @step("[FOR REAL] Deploy PE base config") -def deploy_pe_base_config_real( - subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr -) -> None: +def deploy_pe_base_config_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Perform a real run of adding the base config to the router.""" extra_vars = { "dry_run": False, @@ -102,12 +95,11 @@ def deploy_pe_base_config_real( "geant_sites": json.loads(json_dumps(get_all_active_sites())), } - lso_client.execute_playbook( - playbook_name="promote_p_to_pe.yaml", - callback_route=callback_route, - inventory=subscription["router"]["router_fqdn"], - extra_vars=extra_vars, - ) + return { + "playbook_name": "promote_p_to_pe.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}}, + "extra_vars": extra_vars, + } @inputstep("Prompt EARL insertion", assignee=Assignee.SYSTEM) @@ -159,7 +151,7 @@ def create_kentik_device(subscription: Router) -> State: @step("[DRY RUN] Include new PE into SDP mesh on other Nokia PEs") -def update_sdp_mesh_dry(subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr) -> None: +def update_sdp_mesh_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Perform a dry run for updating the SDP mesh with the new router.""" extra_vars = { "dry_run": True, @@ -175,18 +167,15 @@ def update_sdp_mesh_dry(subscription: dict[str, Any], callback_route: str, tt_nu }, } - lso_client.execute_playbook( - playbook_name="update_pe_sdp_mesh.yaml", - callback_route=callback_route, - inventory=generate_inventory_for_active_routers(router_role=RouterRole.PE, router_vendor=Vendor.NOKIA), - extra_vars=extra_vars, - ) + return { + "playbook_name": "update_pe_sdp_mesh.yaml", + "inventory": generate_inventory_for_active_routers(router_role=RouterRole.PE, router_vendor=Vendor.NOKIA), + "extra_vars": extra_vars, + } @step("[FOR REAL] Include new PE into SDP mesh on other Nokia PEs") -def update_sdp_mesh_real( - subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr -) -> None: +def update_sdp_mesh_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Update the SDP mesh for L2 circuits(epipes) config on PE NOKIA routers.""" extra_vars = { "dry_run": False, @@ -202,18 +191,15 @@ def update_sdp_mesh_real( }, } - lso_client.execute_playbook( - playbook_name="update_pe_sdp_mesh.yaml", - callback_route=callback_route, - inventory=generate_inventory_for_active_routers(router_role=RouterRole.PE, router_vendor=Vendor.NOKIA), - extra_vars=extra_vars, - ) + return { + "playbook_name": "update_pe_sdp_mesh.yaml", + "inventory": generate_inventory_for_active_routers(router_role=RouterRole.PE, router_vendor=Vendor.NOKIA), + "extra_vars": extra_vars, + } @step("[DRY RUN] Remove P from all PEs") -def remove_p_from_pe_dry( - subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr -) -> None: +def remove_p_from_pe_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Perform a dry run of removing the P router from all the PE routers.""" extra_vars = { "dry_run": True, @@ -223,18 +209,15 @@ def remove_p_from_pe_dry( "verb": "remove_p_from_pe", } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=generate_inventory_for_active_routers(RouterRole.PE), - extra_vars=extra_vars, - ) + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": generate_inventory_for_active_routers(RouterRole.PE), + "extra_vars": extra_vars, + } @step("[FOR REAL] Remove P from all PEs") -def remove_p_from_pe_real( - subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr -) -> None: +def remove_p_from_pe_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Remove the P router from all the PE routers.""" extra_vars = { "dry_run": False, @@ -244,18 +227,15 @@ def remove_p_from_pe_real( "verb": "remove_p_from_pe", } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=generate_inventory_for_active_routers(RouterRole.PE), - extra_vars=extra_vars, - ) + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": generate_inventory_for_active_routers(RouterRole.PE), + "extra_vars": extra_vars, + } @step("[DRY RUN] Add PE mesh to PE") -def add_pe_mesh_to_pe_dry( - subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr -) -> None: +def add_pe_mesh_to_pe_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Perform a dry run of adding list of PE routers into iGEANT/iGEANT6 of promoted router.""" extra_vars = { "dry_run": True, @@ -266,18 +246,15 @@ def add_pe_mesh_to_pe_dry( "pe_router_list": generate_inventory_for_active_routers(RouterRole.PE)["all"]["hosts"], } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=subscription["router"]["router_fqdn"], - extra_vars=extra_vars, - ) + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}}, + "extra_vars": extra_vars, + } @step("[FOR REAL] Add PE mesh to PE") -def add_pe_mesh_to_pe_real( - subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr -) -> None: +def add_pe_mesh_to_pe_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Perform a real run of adding list of PE routers into iGEANT/iGEANT6 of promoted router.""" extra_vars = { "dry_run": False, @@ -288,18 +265,15 @@ def add_pe_mesh_to_pe_real( "pe_router_list": generate_inventory_for_active_routers(RouterRole.PE)["all"]["hosts"], } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=subscription["router"]["router_fqdn"], - extra_vars=extra_vars, - ) + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}}, + "extra_vars": extra_vars, + } @step("[DRY RUN] Add PE to PE mesh") -def add_pe_to_pe_mesh_dry( - subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr -) -> None: +def add_pe_to_pe_mesh_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Perform a dry run of adding the promoted router to all PE routers in iGEANT/iGEANT6.""" extra_vars = { "dry_run": True, @@ -309,18 +283,15 @@ def add_pe_to_pe_mesh_dry( "verb": "add_pe_to_pe_mesh", } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=generate_inventory_for_active_routers(RouterRole.PE), - extra_vars=extra_vars, - ) + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": generate_inventory_for_active_routers(RouterRole.PE), + "extra_vars": extra_vars, + } @step("[FOR REAL] Add PE to PE mesh") -def add_pe_to_pe_mesh_real( - subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr -) -> None: +def add_pe_to_pe_mesh_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Perform a real run of adding the promoted router to all PE routers in iGEANT/iGEANT6.""" extra_vars = { "dry_run": False, @@ -330,16 +301,15 @@ def add_pe_to_pe_mesh_real( "verb": "add_pe_to_pe_mesh", } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=generate_inventory_for_active_routers(RouterRole.PE), - extra_vars=extra_vars, - ) + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": generate_inventory_for_active_routers(RouterRole.PE), + "extra_vars": extra_vars, + } @step("Check iBGP session") -def check_pe_ibgp(subscription: dict[str, Any], callback_route: str) -> None: +def check_pe_ibgp(subscription: dict[str, Any]) -> State: """Check the iBGP session.""" extra_vars = { "dry_run": False, @@ -347,18 +317,15 @@ def check_pe_ibgp(subscription: dict[str, Any], callback_route: str) -> None: "verb": "check_pe_ibgp", } - lso_client.execute_playbook( - playbook_name="check_ibgp.yaml", - callback_route=callback_route, - inventory=subscription["router"]["router_fqdn"], - extra_vars=extra_vars, - ) + return { + "playbook_name": "check_ibgp.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}}, + "extra_vars": extra_vars, + } @step("[DRY RUN] Deploy routing instances") -def deploy_routing_instances_dry( - subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr -) -> None: +def deploy_routing_instances_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Perform a dry run of deploying routing instances.""" extra_vars = { "dry_run": True, @@ -367,18 +334,15 @@ def deploy_routing_instances_dry( "verb": "deploy_routing_instances", } - lso_client.execute_playbook( - playbook_name="promote_p_to_pe.yaml", - callback_route=callback_route, - inventory=subscription["router"]["router_fqdn"], - extra_vars=extra_vars, - ) + return { + "playbook_name": "promote_p_to_pe.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}}, + "extra_vars": extra_vars, + } @step("[FOR REAL] Deploy routing instances") -def deploy_routing_instances_real( - subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr -) -> None: +def deploy_routing_instances_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Perform a real run of deploying routing instances.""" extra_vars = { "dry_run": False, @@ -387,16 +351,15 @@ def deploy_routing_instances_real( "verb": "deploy_routing_instances", } - lso_client.execute_playbook( - playbook_name="promote_p_to_pe.yaml", - callback_route=callback_route, - inventory=subscription["router"]["router_fqdn"], - extra_vars=extra_vars, - ) + return { + "playbook_name": "promote_p_to_pe.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}}, + "extra_vars": extra_vars, + } @step("Check L3 services") -def check_l3_services(subscription: dict[str, Any], callback_route: str) -> None: +def check_l3_services(subscription: dict[str, Any]) -> State: """Check L3 services.""" extra_vars = { "dry_run": False, @@ -404,18 +367,15 @@ def check_l3_services(subscription: dict[str, Any], callback_route: str) -> None "verb": "check_base_ris", } - lso_client.execute_playbook( - playbook_name="check_l3_services.yaml", - callback_route=callback_route, - inventory=subscription["router"]["router_fqdn"], - extra_vars=extra_vars, - ) + return { + "playbook_name": "check_l3_services.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}}, + "extra_vars": extra_vars, + } @step("Remove ISIS overload") -def remove_isis_overload( - subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr -) -> None: +def remove_isis_overload(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Remove ISIS overload.""" extra_vars = { "dry_run": False, @@ -424,12 +384,11 @@ def remove_isis_overload( "verb": "remove_isis_overload", } - lso_client.execute_playbook( - playbook_name="promote_p_to_pe.yaml", - callback_route=callback_route, - inventory=subscription["router"]["router_fqdn"], - extra_vars=extra_vars, - ) + return { + "playbook_name": "promote_p_to_pe.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}}, + "extra_vars": extra_vars, + } @step("Set router role to PE (Update subscription model)") @@ -441,7 +400,7 @@ def update_subscription_model(subscription: Router) -> State: @step("[DRY RUN] Add all P to this new PE") -def add_all_p_to_pe_dry(subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr) -> None: +def add_all_p_to_pe_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Perform a dry run of adding all P routers to the PE router.""" extra_vars = { "dry_run": True, @@ -453,18 +412,15 @@ def add_all_p_to_pe_dry(subscription: dict[str, Any], callback_route: str, tt_nu )["all"]["hosts"], } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=subscription["router"]["router_fqdn"], - extra_vars=extra_vars, - ) + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}}, + "extra_vars": extra_vars, + } @step("[FOR REAL] Add all P to this new PE") -def add_all_p_to_pe_real( - subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr -) -> None: +def add_all_p_to_pe_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Perform a real run of adding all P routers to the PE router.""" extra_vars = { "dry_run": False, @@ -476,16 +432,15 @@ def add_all_p_to_pe_real( )["all"]["hosts"], } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=subscription["router"]["router_fqdn"], - extra_vars=extra_vars, - ) + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}}, + "extra_vars": extra_vars, + } @step("[DRY RUN] Add this new PE to all P") -def add_pe_to_all_p_dry(subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr) -> None: +def add_pe_to_all_p_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Perform a dry run of adding promoted router to all PE routers in iGEANT/iGEANT6.""" extra_vars = { "dry_run": True, @@ -495,20 +450,17 @@ def add_pe_to_all_p_dry(subscription: dict[str, Any], callback_route: str, tt_nu "verb": "add_pe_to_all_p", } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=generate_inventory_for_active_routers( + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": generate_inventory_for_active_routers( RouterRole.P, exclude_routers=[subscription["router"]["router_fqdn"]] ), - extra_vars=extra_vars, - ) + "extra_vars": extra_vars, + } @step("[FOR REAL] Add this new PE to all P") -def add_pe_to_all_p_real( - subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr -) -> None: +def add_pe_to_all_p_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Perform a real run of adding promoted router to all PE routers in iGEANT/iGEANT6.""" extra_vars = { "dry_run": False, @@ -518,20 +470,17 @@ def add_pe_to_all_p_real( "verb": "add_pe_to_all_p", } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=generate_inventory_for_active_routers( + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": generate_inventory_for_active_routers( RouterRole.P, exclude_routers=[subscription["router"]["router_fqdn"]] ), - extra_vars=extra_vars, - ) + "extra_vars": extra_vars, + } @step("[DRY RUN] Delete default routes") -def delete_default_routes_dry( - subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr -) -> None: +def delete_default_routes_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Perform a dry run of deleting the default routes.""" extra_vars = { "dry_run": True, @@ -541,18 +490,15 @@ def delete_default_routes_dry( "verb": "delete_default_routes", } - lso_client.execute_playbook( - playbook_name="promote_p_to_pe.yaml", - callback_route=callback_route, - inventory=subscription["router"]["router_fqdn"], - extra_vars=extra_vars, - ) + return { + "playbook_name": "promote_p_to_pe.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}}, + "extra_vars": extra_vars, + } @step("[FOR REAL] Delete default routes") -def delete_default_routes_real( - subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr -) -> None: +def delete_default_routes_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Perform a real run of deleting the default routes.""" extra_vars = { "dry_run": False, @@ -562,12 +508,11 @@ def delete_default_routes_real( "verb": "delete_default_routes", } - lso_client.execute_playbook( - playbook_name="promote_p_to_pe.yaml", - callback_route=callback_route, - inventory=subscription["router"]["router_fqdn"], - extra_vars=extra_vars, - ) + return { + "playbook_name": "promote_p_to_pe.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}}, + "extra_vars": extra_vars, + } @workflow( diff --git a/gso/workflows/router/terminate_router.py b/gso/workflows/router/terminate_router.py index f7caa6c9..421a55c0 100644 --- a/gso/workflows/router/terminate_router.py +++ b/gso/workflows/router/terminate_router.py @@ -19,13 +19,14 @@ from orchestrator.workflows.steps import ( unsync, ) from orchestrator.workflows.utils import wrap_modify_initial_input_form +from pydantic_forms.types import State from gso.products.product_blocks.router import RouterRole from gso.products.product_types.router import Router -from gso.services import infoblox, lso_client +from gso.services import infoblox from gso.services.kentik_client import KentikClient from gso.services.librenms_client import LibreNMSClient -from gso.services.lso_client import execute_playbook, lso_interaction +from gso.services.lso_client import lso_interaction from gso.services.netbox_client import NetboxClient from gso.settings import load_oss_params from gso.utils.helpers import generate_inventory_for_active_routers @@ -69,9 +70,7 @@ def deprovision_loopback_ips(subscription: Router) -> dict: @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: +def remove_config_from_router_dry(subscription: Router, process_id: UUIDstr, tt_number: str) -> State: """Remove configuration from the router, first as a dry run.""" extra_vars = { "wfo_router_json": json.loads(json_dumps(subscription)), @@ -81,18 +80,15 @@ def remove_config_from_router_dry( 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, - ) + return { + "playbook_name": "base_config.yaml", + "inventory": {"all": {"hosts": {subscription.router.router_fqdn: None}}}, + "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: +def remove_config_from_router_real(subscription: Router, process_id: UUIDstr, tt_number: str) -> State: """Remove configuration from the router.""" extra_vars = { "wfo_router_json": json.loads(json_dumps(subscription)), @@ -102,12 +98,11 @@ def remove_config_from_router_real( f"{subscription.router.router_fqdn}", } - execute_playbook( - playbook_name="base_config.yaml", # FIX: need to use correct playbook. - callback_route=callback_route, - inventory=subscription.router.router_fqdn, - extra_vars=extra_vars, - ) + return { + "playbook_name": "base_config.yaml", + "inventory": {"all": {"hosts": {subscription.router.router_fqdn: None}}}, + "extra_vars": extra_vars, + } @step("Remove Device from Netbox") @@ -118,7 +113,7 @@ def remove_device_from_netbox(subscription: Router) -> dict[str, Router]: @step("[DRY RUN] Remove P router from all the PE routers") -def remove_p_from_all_pe_dry(subscription: Router, callback_route: str, tt_number: str, process_id: UUIDstr) -> None: +def remove_p_from_all_pe_dry(subscription: Router, tt_number: str, process_id: UUIDstr) -> State: """Perform a dry run of removing the terminated router from all the PE routers.""" extra_vars = { "dry_run": True, @@ -128,16 +123,15 @@ def remove_p_from_all_pe_dry(subscription: Router, callback_route: str, tt_numbe "verb": "remove_p_from_pe", } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=generate_inventory_for_active_routers(RouterRole.PE), - extra_vars=extra_vars, - ) + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": generate_inventory_for_active_routers(RouterRole.PE), + "extra_vars": extra_vars, + } @step("[REAL RUN] Remove P router from all the PE routers") -def remove_p_from_all_pe_real(subscription: Router, callback_route: str, tt_number: str, process_id: UUIDstr) -> None: +def remove_p_from_all_pe_real(subscription: Router, tt_number: str, process_id: UUIDstr) -> State: """Perform a real run of removing the terminated router from all the PE routers.""" extra_vars = { "dry_run": False, @@ -147,16 +141,15 @@ def remove_p_from_all_pe_real(subscription: Router, callback_route: str, tt_numb "verb": "remove_p_from_pe", } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=generate_inventory_for_active_routers(RouterRole.PE), - extra_vars=extra_vars, - ) + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": generate_inventory_for_active_routers(RouterRole.PE), + "extra_vars": extra_vars, + } @step("[DRY RUN] Remove PE router from all the PE routers") -def remove_pe_from_all_pe_dry(subscription: Router, callback_route: str, tt_number: str, process_id: UUIDstr) -> None: +def remove_pe_from_all_pe_dry(subscription: Router, tt_number: str, process_id: UUIDstr) -> State: """Perform a dry run of removing the terminated PE router from the PE router mesh.""" extra_vars = { "dry_run": True, @@ -166,18 +159,17 @@ def remove_pe_from_all_pe_dry(subscription: Router, callback_route: str, tt_numb "verb": "remove_pe_from_pe", } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=generate_inventory_for_active_routers( + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": generate_inventory_for_active_routers( RouterRole.PE, exclude_routers=[subscription.router.router_fqdn] ), - extra_vars=extra_vars, - ) + "extra_vars": extra_vars, + } @step("[REAL RUN] Remove all PE routers from all the PE routers") -def remove_pe_from_all_pe_real(subscription: Router, callback_route: str, tt_number: str, process_id: UUIDstr) -> None: +def remove_pe_from_all_pe_real(subscription: Router, tt_number: str, process_id: UUIDstr) -> State: """Perform a real run of removing terminated PE router from PE the router mesh.""" extra_vars = { "dry_run": False, @@ -187,18 +179,17 @@ def remove_pe_from_all_pe_real(subscription: Router, callback_route: str, tt_num "verb": "remove_pe_from_pe", } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=generate_inventory_for_active_routers( + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": generate_inventory_for_active_routers( RouterRole.PE, exclude_routers=[subscription.router.router_fqdn] ), - extra_vars=extra_vars, - ) + "extra_vars": extra_vars, + } @step("[DRY RUN] Remove PE router from all the P routers") -def remove_pe_from_all_p_dry(subscription: Router, callback_route: str, tt_number: str, process_id: UUIDstr) -> None: +def remove_pe_from_all_p_dry(subscription: Router, tt_number: str, process_id: UUIDstr) -> State: """Perform a dry run of removing PE router from all P routers.""" extra_vars = { "dry_run": True, @@ -208,16 +199,15 @@ def remove_pe_from_all_p_dry(subscription: Router, callback_route: str, tt_numbe "verb": "remove_pe_from_p", } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=generate_inventory_for_active_routers(RouterRole.P), - extra_vars=extra_vars, - ) + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": generate_inventory_for_active_routers(RouterRole.P), + "extra_vars": extra_vars, + } @step("[REAL RUN] Remove PE router from all P routers") -def remove_pe_from_all_p_real(subscription: Router, callback_route: str, tt_number: str, process_id: UUIDstr) -> None: +def remove_pe_from_all_p_real(subscription: Router, tt_number: str, process_id: UUIDstr) -> State: """Perform a real run of removing PE router from all P routers.""" extra_vars = { "dry_run": False, @@ -227,12 +217,11 @@ def remove_pe_from_all_p_real(subscription: Router, callback_route: str, tt_numb "verb": "remove_pe_from_p", } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=generate_inventory_for_active_routers(RouterRole.P), - extra_vars=extra_vars, - ) + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": generate_inventory_for_active_routers(RouterRole.P), + "extra_vars": extra_vars, + } @step("Remove Device from Librenms") diff --git a/gso/workflows/router/update_ibgp_mesh.py b/gso/workflows/router/update_ibgp_mesh.py index a506e625..8f99bb86 100644 --- a/gso/workflows/router/update_ibgp_mesh.py +++ b/gso/workflows/router/update_ibgp_mesh.py @@ -14,7 +14,7 @@ from pydantic import ConfigDict, model_validator from gso.products.product_blocks.router import RouterRole from gso.products.product_types.router import Router -from gso.services import librenms_client, lso_client +from gso.services import librenms_client from gso.services.lso_client import lso_interaction from gso.services.subscriptions import get_trunks_that_terminate_on_router from gso.utils.helpers import generate_inventory_for_active_routers @@ -52,7 +52,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: @step("[DRY RUN] Add P router to iBGP mesh") -def add_p_to_mesh_dry(subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr) -> None: +def add_p_to_mesh_dry(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Perform a dry run of adding the new P router to the PE router mesh.""" extra_vars = { "dry_run": True, @@ -61,16 +61,15 @@ def add_p_to_mesh_dry(subscription: dict[str, Any], callback_route: str, tt_numb "verb": "add_p_to_pe", } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=generate_inventory_for_active_routers(RouterRole.PE), - extra_vars=extra_vars, - ) + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": generate_inventory_for_active_routers(RouterRole.PE), + "extra_vars": extra_vars, + } @step("[FOR REAL] Add P router to iBGP mesh") -def add_p_to_mesh_real(subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr) -> None: +def add_p_to_mesh_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Add the P router to the mesh of PE routers.""" extra_vars = { "dry_run": False, @@ -79,16 +78,15 @@ def add_p_to_mesh_real(subscription: dict[str, Any], callback_route: str, tt_num "verb": "add_p_to_pe", } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=generate_inventory_for_active_routers(RouterRole.PE), - extra_vars=extra_vars, - ) + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": generate_inventory_for_active_routers(RouterRole.PE), + "extra_vars": extra_vars, + } @step("[DRY RUN] Add all PE routers to P router iBGP group") -def add_all_pe_to_p_dry(subscription: dict[str, Any], callback_route: str) -> None: +def add_all_pe_to_p_dry(subscription: dict[str, Any]) -> State: """Perform a dry run of adding the list of all PE routers to the new P router.""" extra_vars = { "dry_run": True, @@ -97,18 +95,15 @@ def add_all_pe_to_p_dry(subscription: dict[str, Any], callback_route: str) -> No "verb": "add_pe_to_p", } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=subscription["router"]["router_fqdn"], - extra_vars=extra_vars, - ) + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}}, + "extra_vars": extra_vars, + } @step("[FOR REAL] Add all PE routers to P router iBGP group") -def add_all_pe_to_p_real( - subscription: dict[str, Any], callback_route: str, tt_number: str, process_id: UUIDstr -) -> None: +def add_all_pe_to_p_real(subscription: dict[str, Any], tt_number: str, process_id: UUIDstr) -> State: """Add the list of all PE routers to the new P router.""" extra_vars = { "dry_run": False, @@ -118,23 +113,21 @@ def add_all_pe_to_p_real( "verb": "add_pe_to_p", } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=subscription["router"]["router_fqdn"], - extra_vars=extra_vars, - ) + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}}, + "extra_vars": extra_vars, + } @step("Verify iBGP session health") -def check_ibgp_session(subscription: Router, callback_route: str) -> None: +def check_ibgp_session(subscription: Router) -> State: """Run a playbook using the provisioning proxy, to check the health of the new iBGP session.""" - lso_client.execute_playbook( - playbook_name="check_ibgp.yaml", - callback_route=callback_route, - inventory=subscription.router.router_fqdn, - extra_vars={}, - ) + return { + "playbook_name": "check_ibgp.yaml", + "inventory": {"all": {"hosts": {subscription.router.router_fqdn: None}}}, + "extra_vars": {}, + } @step("Add the router to LibreNMS") diff --git a/gso/workflows/router/validate_router.py b/gso/workflows/router/validate_router.py index aa22b773..82c46fc6 100644 --- a/gso/workflows/router/validate_router.py +++ b/gso/workflows/router/validate_router.py @@ -1,11 +1,9 @@ """Router validation workflow. Used in a nightly schedule.""" -import json from typing import Any from orchestrator.targets import Target from orchestrator.utils.errors import ProcessFailureError -from orchestrator.utils.json import json_dumps 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 @@ -13,10 +11,10 @@ from pydantic_forms.types import State, UUIDstr from gso.products.product_blocks.router import RouterRole from gso.products.product_types.router import Router -from gso.services import infoblox, lso_client +from gso.services import infoblox from gso.services.kentik_client import KentikClient from gso.services.librenms_client import LibreNMSClient -from gso.services.lso_client import anonymous_lso_interaction, execute_playbook +from gso.services.lso_client import anonymous_lso_interaction from gso.services.netbox_client import NetboxClient from gso.utils.helpers import generate_inventory_for_active_routers from gso.utils.shared_enums import Vendor @@ -55,7 +53,7 @@ def check_netbox_entry_exists(subscription: Router) -> None: @step("Verify BGP configuration on P router") -def verify_p_ibgp(subscription: dict[str, Any], callback_route: str) -> None: +def verify_p_ibgp(subscription: dict[str, Any]) -> State: """Perform a dry run of adding the list of all PE routers to the new P router.""" extra_vars = { "dry_run": True, @@ -65,12 +63,11 @@ def verify_p_ibgp(subscription: dict[str, Any], callback_route: str) -> None: "is_verification_workflow": "true", } - lso_client.execute_playbook( - playbook_name="update_ibgp_mesh.yaml", - callback_route=callback_route, - inventory=subscription["router"]["router_fqdn"], - extra_vars=extra_vars, - ) + return { + "playbook_name": "update_ibgp_mesh.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}}, + "extra_vars": extra_vars, + } @step("Verify correct LibreNMS entry") @@ -100,19 +97,18 @@ def check_kentik_entry_exists(subscription: Router) -> None: @step("Check base config for drift") -def verify_base_config(subscription: Router, callback_route: str) -> None: +def verify_base_config(subscription: dict[str, Any]) -> State: """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": json.loads(json_dumps(subscription)), + return { + "playbook_name": "base_config.yaml", + "inventory": {"all": {"hosts": {subscription["router"]["router_fqdn"]: None}}}, + "extra_vars": { + "wfo_router_json": subscription, "verb": "deploy", "dry_run": "true", "is_verification_workflow": "true", }, - ) + } @workflow( diff --git a/test/workflows/iptrunk/test_create_iptrunk.py b/test/workflows/iptrunk/test_create_iptrunk.py index 0b7e00e6..267cd382 100644 --- a/test/workflows/iptrunk/test_create_iptrunk.py +++ b/test/workflows/iptrunk/test_create_iptrunk.py @@ -101,7 +101,7 @@ def input_form_wizard_data(request, juniper_router_subscription_factory, nokia_r @pytest.mark.workflow() -@patch("gso.workflows.iptrunk.create_iptrunk.execute_playbook") +@patch("gso.services.lso_client._send_request") @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.allocate_v6_network") @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.allocate_v4_network") @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.create_host_by_ip") @@ -162,7 +162,7 @@ def test_successful_iptrunk_creation_with_standard_lso_result( @pytest.mark.workflow() -@patch("gso.workflows.iptrunk.create_iptrunk.execute_playbook") +@patch("gso.services.lso_client._send_request") @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.allocate_v6_network") @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.allocate_v4_network") @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.find_host_by_ip") @@ -199,7 +199,7 @@ def test_iptrunk_creation_fails_when_lso_return_code_is_one( @pytest.mark.parametrize("input_form_wizard_data", [Vendor.JUNIPER], indirect=True) @pytest.mark.workflow() -@patch("gso.workflows.iptrunk.create_iptrunk.execute_playbook") +@patch("gso.services.lso_client._send_request") @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.allocate_v6_network") @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.allocate_v4_network") @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.create_host_by_ip") diff --git a/test/workflows/iptrunk/test_deploy_twamp.py b/test/workflows/iptrunk/test_deploy_twamp.py index 8584cd99..e65feac7 100644 --- a/test/workflows/iptrunk/test_deploy_twamp.py +++ b/test/workflows/iptrunk/test_deploy_twamp.py @@ -12,7 +12,7 @@ from test.workflows import ( @pytest.mark.workflow() -@patch("gso.workflows.iptrunk.deploy_twamp.execute_playbook") +@patch("gso.services.lso_client._send_request") def test_iptrunk_deploy_twamp_success( mock_execute_playbook, iptrunk_subscription_factory, diff --git a/test/workflows/iptrunk/test_modify_isis_metric.py b/test/workflows/iptrunk/test_modify_isis_metric.py index 38f4b4e8..26a9bbd4 100644 --- a/test/workflows/iptrunk/test_modify_isis_metric.py +++ b/test/workflows/iptrunk/test_modify_isis_metric.py @@ -12,7 +12,7 @@ from test.workflows import ( @pytest.mark.workflow() -@patch("gso.services.lso_client.execute_playbook") +@patch("gso.services.lso_client._send_request") def test_iptrunk_modify_isis_metric_success( mock_provision_ip_trunk, iptrunk_subscription_factory, diff --git a/test/workflows/iptrunk/test_terminate_iptrunk.py b/test/workflows/iptrunk/test_terminate_iptrunk.py index 7fcca946..7319596f 100644 --- a/test/workflows/iptrunk/test_terminate_iptrunk.py +++ b/test/workflows/iptrunk/test_terminate_iptrunk.py @@ -3,6 +3,7 @@ from unittest.mock import patch import pytest from gso.products import Iptrunk +from gso.products.product_blocks.router import RouterRole from gso.settings import load_oss_params from test.services.conftest import MockedNetboxClient from test.workflows import ( @@ -14,8 +15,7 @@ from test.workflows import ( @pytest.mark.workflow() -@patch("gso.workflows.iptrunk.terminate_iptrunk.execute_playbook") -@patch("gso.utils.workflow_steps.lso_client.execute_playbook") +@patch("gso.services.lso_client._send_request") @patch("gso.workflows.iptrunk.terminate_iptrunk.infoblox.delete_network") @patch("gso.services.netbox_client.NetboxClient.delete_interface") @patch("gso.services.netbox_client.NetboxClient.free_interface") @@ -23,17 +23,20 @@ def test_successful_iptrunk_termination( mocked_free_interface, mocked_delete_interface, mock_infoblox_delete_network, - mock_set_isis_to_90k, mock_execute_playbook, iptrunk_subscription_factory, faker, data_config_filename, + nokia_router_subscription_factory, ): # Set up mock return values product_id = iptrunk_subscription_factory() mocked_netbox = MockedNetboxClient() mocked_delete_interface.return_value = mocked_netbox.delete_interface() mocked_free_interface.return_value = mocked_netbox.free_interface() + # Add two more routers to our fake network + nokia_router_subscription_factory(router_role=RouterRole.P) + nokia_router_subscription_factory(router_role=RouterRole.PE) # Run workflow oss_params = load_oss_params() @@ -60,7 +63,6 @@ def test_successful_iptrunk_termination( subscription = Iptrunk.from_subscription(subscription_id) assert subscription.status == "terminated" - assert mock_execute_playbook.call_count == 2 - assert mock_set_isis_to_90k.call_count == 1 + assert mock_execute_playbook.call_count == 3 assert mock_infoblox_delete_network.call_count == 2 assert subscription.iptrunk.iptrunk_isis_metric == oss_params.GENERAL.isis_high_metric diff --git a/test/workflows/iptrunk/test_validate_iptrunk.py b/test/workflows/iptrunk/test_validate_iptrunk.py index 13bcaed9..6b132abc 100644 --- a/test/workflows/iptrunk/test_validate_iptrunk.py +++ b/test/workflows/iptrunk/test_validate_iptrunk.py @@ -69,7 +69,7 @@ def _mocked_netbox_client(): @patch("gso.services.infoblox.find_network_by_cidr") @patch("gso.services.infoblox.find_v6_host_by_fqdn") @patch("gso.services.infoblox.find_host_by_fqdn") -@patch("gso.workflows.iptrunk.validate_iptrunk.execute_playbook") +@patch("gso.services.lso_client._send_request") @patch("gso.services.netbox_client.NetboxClient.get_interface_by_name_and_device") def test_validate_iptrunk_success( mock_get_interface_by_name, @@ -219,7 +219,7 @@ def test_validate_iptrunk_success( @patch("gso.services.infoblox.find_network_by_cidr") @patch("gso.services.infoblox.find_v6_host_by_fqdn") @patch("gso.services.infoblox.find_host_by_fqdn") -@patch("gso.workflows.iptrunk.validate_iptrunk.execute_playbook") +@patch("gso.services.lso_client._send_request") @patch("gso.services.netbox_client.NetboxClient.get_interface_by_name_and_device") def test_validate_iptrunk_skip_legacy_trunks( mock_get_interface_by_name, diff --git a/test/workflows/router/test_create_router.py b/test/workflows/router/test_create_router.py index 83a2e533..a6bf456f 100644 --- a/test/workflows/router/test_create_router.py +++ b/test/workflows/router/test_create_router.py @@ -36,7 +36,7 @@ def router_creation_input_form_data(site_subscription_factory, faker): @pytest.mark.workflow() -@patch("gso.utils.workflow_steps.lso_client.execute_playbook") +@patch("gso.services.lso_client._send_request") @patch("gso.workflows.router.create_router.NetboxClient.create_device") @patch("gso.workflows.router.create_router.infoblox.hostname_available") @patch("gso.workflows.router.create_router.infoblox.find_host_by_fqdn") @@ -117,7 +117,7 @@ def test_create_nokia_router_success( @pytest.mark.workflow() -@patch("gso.utils.workflow_steps.lso_client.execute_playbook") +@patch("gso.services.lso_client._send_request") @patch("gso.workflows.router.create_router.NetboxClient.create_device") @patch("gso.workflows.router.create_router.infoblox.hostname_available") @patch("gso.workflows.router.create_router.infoblox.find_network_by_cidr") diff --git a/test/workflows/router/test_promote_p_to_pe.py b/test/workflows/router/test_promote_p_to_pe.py index 3bd7fdca..5e14dcaf 100644 --- a/test/workflows/router/test_promote_p_to_pe.py +++ b/test/workflows/router/test_promote_p_to_pe.py @@ -17,7 +17,7 @@ from test.workflows import ( @pytest.mark.workflow() -@patch("gso.workflows.router.promote_p_to_pe.lso_client.execute_playbook") +@patch("gso.services.lso_client._send_request") @patch("gso.workflows.router.promote_p_to_pe.KentikClient") def test_promote_p_to_pe_success( mock_kentik_client, @@ -29,6 +29,9 @@ def test_promote_p_to_pe_success( """Test the successful promotion of a Nokia P router to a PE router.""" mock_kentik_client.return_value = MockedKentikClient router_id = nokia_router_subscription_factory(router_role=RouterRole.P, status=SubscriptionLifecycle.ACTIVE) + # Add two more routers to our fake network + nokia_router_subscription_factory(router_role=RouterRole.P) + nokia_router_subscription_factory(router_role=RouterRole.PE) input_data = [{"subscription_id": router_id}, {"tt_number": faker.tt_number()}] result, process_stat, step_log = run_workflow("promote_p_to_pe", input_data) for _ in range(3): @@ -55,7 +58,7 @@ def test_promote_p_to_pe_juniper_router(juniper_router_subscription_factory, dat @pytest.mark.workflow() -@patch("gso.workflows.router.promote_p_to_pe.lso_client.execute_playbook") +@patch("gso.services.lso_client._send_request") def test_promote_p_to_pe_nokia_pe_router( mock_execute_playbook, nokia_router_subscription_factory, data_config_filename, faker ): diff --git a/test/workflows/router/test_terminate_router.py b/test/workflows/router/test_terminate_router.py index f2cb638a..a5f4fd3d 100644 --- a/test/workflows/router/test_terminate_router.py +++ b/test/workflows/router/test_terminate_router.py @@ -30,6 +30,9 @@ def test_terminate_pe_router_full_success( ): # Prepare mock values and expected results product_id = nokia_router_subscription_factory() + # Add two more routers to our fake network + nokia_router_subscription_factory(router_role=RouterRole.P) + nokia_router_subscription_factory(router_role=RouterRole.PE) router_termination_input_form_data = { "tt_number": faker.tt_number(), "remove_configuration": remove_configuration, @@ -81,6 +84,9 @@ def test_terminate_p_router_full_success( ): # Prepare mock values and expected results product_id = nokia_router_subscription_factory(router_role=RouterRole.P) + # Add two more routers to our fake network + nokia_router_subscription_factory(router_role=RouterRole.P) + nokia_router_subscription_factory(router_role=RouterRole.PE) router_termination_input_form_data = { "tt_number": faker.tt_number(), "remove_configuration": remove_configuration, diff --git a/test/workflows/router/test_update_ibgp_mesh.py b/test/workflows/router/test_update_ibgp_mesh.py index 2aa0a7b7..b473f2ea 100644 --- a/test/workflows/router/test_update_ibgp_mesh.py +++ b/test/workflows/router/test_update_ibgp_mesh.py @@ -19,7 +19,7 @@ from test.workflows import ( @pytest.mark.parametrize("trunk_status", [SubscriptionLifecycle.PROVISIONING, SubscriptionLifecycle.ACTIVE]) @pytest.mark.workflow() -@patch("gso.workflows.router.update_ibgp_mesh.lso_client.execute_playbook") +@patch("gso.services.lso_client._send_request") @patch("gso.workflows.router.update_ibgp_mesh.librenms_client.LibreNMSClient.add_device") @patch("gso.workflows.router.update_ibgp_mesh.librenms_client.LibreNMSClient.device_exists") def test_update_ibgp_mesh_success( diff --git a/test/workflows/router/test_validate_router.py b/test/workflows/router/test_validate_router.py index 924577e2..07fc78d7 100644 --- a/test/workflows/router/test_validate_router.py +++ b/test/workflows/router/test_validate_router.py @@ -15,7 +15,7 @@ from test.workflows import ( @pytest.mark.workflow() @patch("gso.services.infoblox.find_host_by_fqdn") -@patch("gso.services.lso_client.execute_playbook") +@patch("gso.services.lso_client._send_request") @patch("gso.services.netbox_client.NetboxClient.get_device_by_name") @patch("gso.services.librenms_client.LibreNMSClient.validate_device") @patch("gso.services.kentik_client.KentikClient") -- GitLab