diff --git a/gso/utils/helpers.py b/gso/utils/helpers.py index 1fde7d65c92d32229d03b3c9dd2860a5ebf8804a..48343817879a9373b01ba0f0490c6e97cc63c281 100644 --- a/gso/utils/helpers.py +++ b/gso/utils/helpers.py @@ -6,8 +6,7 @@ from ipaddress import IPv4Address from uuid import UUID import pycountry -from orchestrator import step -from orchestrator.types import State, UUIDstr +from orchestrator.types import UUIDstr from pydantic import BaseModel, validator from pydantic.fields import ModelField from pydantic_forms.validators import Choice @@ -15,9 +14,7 @@ from pydantic_forms.validators import Choice from gso.products.product_blocks.iptrunk import IptrunkInterfaceBlock from gso.products.product_blocks.router import RouterVendor from gso.products.product_blocks.site import SiteTier -from gso.products.product_types.iptrunk import Iptrunk from gso.products.product_types.router import Router -from gso.services import provisioning_proxy from gso.services.netbox_client import NetboxClient from gso.services.subscriptions import get_active_subscriptions_by_field_and_value @@ -39,26 +36,6 @@ class LAGMember(BaseModel): return hash((self.interface_name, self.interface_description)) -@step("[COMMIT] Set ISIS metric to 90.000") -def set_isis_to_90000(subscription: Iptrunk, process_id: UUIDstr, callback_route: str, tt_number: str) -> State: - """Workflow step for setting the :term:`ISIS` metric to 90k as an arbitrarily high value to drain a link.""" - old_isis_metric = subscription.iptrunk.iptrunk_isis_metric - subscription.iptrunk.iptrunk_isis_metric = 90000 - provisioning_proxy.provision_ip_trunk( - subscription, - process_id, - callback_route, - tt_number, - "isis_interface", - dry_run=False, - ) - - return { - "subscription": subscription, - "old_isis_metric": old_isis_metric, - } - - def available_interfaces_choices(router_id: UUID, speed: str) -> Choice | None: """Return a list of available interfaces for a given router and speed. diff --git a/gso/utils/workflow_steps.py b/gso/utils/workflow_steps.py index f4308838adfa4a144c7ec882c26c14cdd11dcfeb..1f87a2fa72dea2501a8d23de8a36940639a4300c 100644 --- a/gso/utils/workflow_steps.py +++ b/gso/utils/workflow_steps.py @@ -1,10 +1,13 @@ """Workflow steps that are shared across multiple workflows.""" +import json from typing import Any from orchestrator import step from orchestrator.types import State, UUIDstr +from orchestrator.utils.json import json_dumps +from gso.products.product_types.iptrunk import Iptrunk from gso.services.provisioning_proxy import execute_playbook @@ -60,3 +63,31 @@ def deploy_base_config_real( ) return {"subscription": subscription} + + +@step("[COMMIT] Set ISIS metric to 90.000") +def set_isis_to_90000(subscription: Iptrunk, process_id: UUIDstr, callback_route: str, tt_number: str) -> State: + """Workflow step for setting the :term:`ISIS` metric to 90k as an arbitrarily high value to drain a link.""" + old_isis_metric = subscription.iptrunk.iptrunk_isis_metric + subscription.iptrunk.iptrunk_isis_metric = 90000 + extra_vars = { + "wfo_trunk_json": json.loads(json_dumps(subscription)), + "dry_run": False, + "verb": "deploy", + "config_object": "isis_interface", + "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploy config for " + f"{subscription.iptrunk.geant_s_sid} ", + } + + execute_playbook( + playbook_name="playbooks.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, + "old_isis_metric": old_isis_metric, + } diff --git a/gso/workflows/iptrunk/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py index 0c0ee530fdf8933325f0a0dab3b2479f37065bf6..bae7f18a57176d8261bd2581f12794f0344b28d4 100644 --- a/gso/workflows/iptrunk/create_iptrunk.py +++ b/gso/workflows/iptrunk/create_iptrunk.py @@ -1,11 +1,13 @@ """A creation workflow that deploys a new IP trunk service.""" +import json from uuid import uuid4 from orchestrator.forms import FormPage from orchestrator.forms.validators import Choice, UniqueConstrainedList from orchestrator.targets import Target from orchestrator.types import FormGenerator, State, SubscriptionLifecycle, UUIDstr +from orchestrator.utils.json import json_dumps from orchestrator.workflow import StepList, done, init, step, workflow from orchestrator.workflows.steps import resync, set_status, store_process_subscription from orchestrator.workflows.utils import wrap_create_initial_input_form @@ -20,10 +22,10 @@ from gso.products.product_blocks.iptrunk import ( from gso.products.product_blocks.router import RouterVendor from gso.products.product_types.iptrunk import IptrunkInactive, IptrunkProvisioning from gso.products.product_types.router import Router -from gso.services import infoblox, provisioning_proxy, subscriptions +from gso.services import infoblox, subscriptions from gso.services.crm import customer_selector from gso.services.netbox_client import NetboxClient -from gso.services.provisioning_proxy import pp_interaction +from gso.services.provisioning_proxy import execute_playbook, pp_interaction from gso.utils.helpers import ( LAGMember, available_interfaces_choices, @@ -253,13 +255,21 @@ def provision_ip_trunk_iface_dry( tt_number: str, ) -> State: """Perform a dry run of deploying configuration on both sides of the trunk.""" - provisioning_proxy.provision_ip_trunk( - subscription, - process_id, - callback_route, - tt_number, - "trunk_interface", - dry_run=True, + extra_vars = { + "wfo_trunk_json": json.loads(json_dumps(subscription)), + "dry_run": True, + "verb": "deploy", + "config_object": "trunk_interface", + "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploy config for " + 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} @@ -273,13 +283,21 @@ def provision_ip_trunk_iface_real( tt_number: str, ) -> State: """Deploy IP trunk configuration on both sides.""" - provisioning_proxy.provision_ip_trunk( - subscription, - process_id, - callback_route, - tt_number, - "trunk_interface", - dry_run=False, + extra_vars = { + "wfo_trunk_json": json.loads(json_dumps(subscription)), + "dry_run": False, + "verb": "deploy", + "config_object": "trunk_interface", + "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploy config for " + 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} @@ -289,11 +307,16 @@ def provision_ip_trunk_iface_real( def check_ip_trunk_connectivity( subscription: IptrunkProvisioning, callback_route: str, - process_id: UUIDstr, - tt_number: str, ) -> State: """Check successful connectivity across the new trunk.""" - provisioning_proxy.check_ip_trunk(subscription, process_id, callback_route, tt_number, "ping") + 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} @@ -306,7 +329,22 @@ def provision_ip_trunk_isis_iface_dry( tt_number: str, ) -> State: """Perform a dry run of deploying :term:`ISIS` configuration.""" - provisioning_proxy.provision_ip_trunk(subscription, process_id, callback_route, tt_number, "isis_interface") + extra_vars = { + "wfo_trunk_json": json.loads(json_dumps(subscription)), + "dry_run": True, + "verb": "deploy", + "config_object": "isis_interface", + "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploy config for " + 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} @@ -319,13 +357,21 @@ def provision_ip_trunk_isis_iface_real( tt_number: str, ) -> State: """Deploy :term:`ISIS` configuration on both sides.""" - provisioning_proxy.provision_ip_trunk( - subscription, - process_id, - callback_route, - tt_number, - "isis_interface", - dry_run=False, + extra_vars = { + "wfo_trunk_json": json.loads(json_dumps(subscription)), + "dry_run": False, + "verb": "deploy", + "config_object": "isis_interface", + "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploy config for " + 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} @@ -335,11 +381,16 @@ def provision_ip_trunk_isis_iface_real( def check_ip_trunk_isis( subscription: IptrunkProvisioning, callback_route: str, - process_id: UUIDstr, - tt_number: str, ) -> State: """Run an Ansible playbook to confirm :term:`ISIS` adjacency.""" - provisioning_proxy.check_ip_trunk(subscription, process_id, callback_route, tt_number, "isis") + 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, + extra_vars=extra_vars, + ) return {"subscription": subscription} diff --git a/gso/workflows/iptrunk/migrate_iptrunk.py b/gso/workflows/iptrunk/migrate_iptrunk.py index 03a3dc08c8b948d11aba0b148ee49478cbee4e84..2968ff8645edaca51b3af299d0d776f9b6171388 100644 --- a/gso/workflows/iptrunk/migrate_iptrunk.py +++ b/gso/workflows/iptrunk/migrate_iptrunk.py @@ -5,6 +5,7 @@ configured to run from A to C. B is then no longer associated with this IP trunk """ import copy +import json import re from logging import getLogger from typing import NoReturn @@ -16,6 +17,7 @@ from orchestrator.forms import FormPage from orchestrator.forms.validators import Choice, Label, UniqueConstrainedList from orchestrator.targets import Target from orchestrator.types import FormGenerator, State, UUIDstr +from orchestrator.utils.json import json_dumps from orchestrator.workflow import StepList, conditional, done, init, inputstep from orchestrator.workflows.steps import resync, store_process_subscription, unsync from orchestrator.workflows.utils import wrap_modify_initial_input_form @@ -27,18 +29,17 @@ from gso.products.product_blocks.iptrunk import IptrunkInterfaceBlock from gso.products.product_blocks.router import RouterVendor from gso.products.product_types.iptrunk import Iptrunk from gso.products.product_types.router import Router -from gso.services import provisioning_proxy from gso.services.netbox_client import NetboxClient -from gso.services.provisioning_proxy import pp_interaction +from gso.services.provisioning_proxy import execute_playbook, pp_interaction from gso.services.subscriptions import get_active_router_subscriptions from gso.utils.helpers import ( LAGMember, available_interfaces_choices, available_lags_choices, get_router_vendor, - set_isis_to_90000, validate_interface_name_list, ) +from gso.utils.workflow_steps import set_isis_to_90000 logger = getLogger(__name__) @@ -182,22 +183,29 @@ def disable_old_config_dry( tt_number: str, ) -> State: """Perform a dry run of disabling the old configuration on the routers.""" - provisioning_proxy.migrate_ip_trunk( - subscription, - new_node, - new_lag_interface, - new_lag_member_interfaces, - replace_index, - process_id, - callback_route, - tt_number, - "deactivate", - "deactivate", + extra_vars = { + "wfo_trunk_json": json.loads(json_dumps(subscription)), + "new_node": json.loads(json_dumps(new_node)), + "new_lag_interface": new_lag_interface, + "new_lag_member_interfaces": new_lag_member_interfaces, + "replace_index": replace_index, + "verb": "deactivate", + "config_object": "deactivate", + "dry_run": True, + "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} " + 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 {"subscription": subscription} @step("[REAL] Disable configuration on old router") @@ -212,18 +220,26 @@ def disable_old_config_real( tt_number: str, ) -> State: """Disable old configuration on the routers.""" - provisioning_proxy.migrate_ip_trunk( - subscription, - new_node, - new_lag_interface, - new_lag_member_interfaces, - replace_index, - process_id, - callback_route, - tt_number, - "deactivate", - "deactivate", - dry_run=False, + extra_vars = { + "wfo_trunk_json": json.loads(json_dumps(subscription)), + "new_node": json.loads(json_dumps(new_node)), + "new_lag_interface": new_lag_interface, + "new_lag_member_interfaces": new_lag_member_interfaces, + "replace_index": replace_index, + "verb": "deactivate", + "config_object": "deactivate", + "dry_run": False, + "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} " + 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 { @@ -242,28 +258,30 @@ def deploy_new_config_dry( process_id: UUIDstr, tt_number: str, ) -> State: - """Perform a dry run of deploying configuration on the new router. + """Perform a dry run of deploying configuration on the new router.""" + extra_vars = { + "wfo_trunk_json": json.loads(json_dumps(subscription)), + "new_node": json.loads(json_dumps(new_node)), + "new_lag_interface": new_lag_interface, + "new_lag_member_interfaces": new_lag_member_interfaces, + "replace_index": replace_index, + "verb": "deploy", + "config_object": "trunk_interface", + "dry_run": True, + "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} " + f"- Deploy config for {subscription.iptrunk.geant_s_sid}", + } - TODO: set the proper playbook verb - """ - provisioning_proxy.migrate_ip_trunk( - subscription, - new_node, - new_lag_interface, - new_lag_member_interfaces, - replace_index, - process_id, - callback_route, - tt_number, - "deploy", - "trunk_interface", + 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, ) - logger.warning(PLAYBOOK_VERB_NOT_YET_PROPERLY_SET) - - return { - "subscription": subscription, - } + return {"subscription": subscription} @step("Deploy configuration on new router") @@ -277,29 +295,30 @@ def deploy_new_config_real( process_id: UUIDstr, tt_number: str, ) -> State: - """Deploy configuration on the new router. + """Deploy configuration on the new router.""" + extra_vars = { + "wfo_trunk_json": json.loads(json_dumps(subscription)), + "new_node": json.loads(json_dumps(new_node)), + "new_lag_interface": new_lag_interface, + "new_lag_member_interfaces": new_lag_member_interfaces, + "replace_index": replace_index, + "verb": "deploy", + "config_object": "trunk_interface", + "dry_run": False, + "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} " + f"- Deploy config for {subscription.iptrunk.geant_s_sid}", + } - TODO: set the proper playbook verb - """ - provisioning_proxy.migrate_ip_trunk( - subscription, - new_node, - new_lag_interface, - new_lag_member_interfaces, - replace_index, - process_id, - callback_route, - tt_number, - "deploy", - "trunk_interface", - dry_run=False, + 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, ) - logger.warning(PLAYBOOK_VERB_NOT_YET_PROPERLY_SET) - - return { - "subscription": subscription, - } + return {"subscription": subscription} @inputstep("Wait for confirmation", assignee=Assignee.SYSTEM) @@ -328,29 +347,30 @@ def deploy_new_isis( process_id: UUIDstr, tt_number: str, ) -> State: - """Deploy :term:`ISIS` configuration. + """Deploy :term:`ISIS` configuration.""" + extra_vars = { + "wfo_trunk_json": json.loads(json_dumps(subscription)), + "new_node": json.loads(json_dumps(new_node)), + "new_lag_interface": new_lag_interface, + "new_lag_member_interfaces": new_lag_member_interfaces, + "replace_index": replace_index, + "verb": "deploy", + "config_object": "isis_interface", + "dry_run": False, + "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} " + f"- Deploy config for {subscription.iptrunk.geant_s_sid}", + } - TODO: set the proper playbook verb. - """ - provisioning_proxy.migrate_ip_trunk( - subscription, - new_node, - new_lag_interface, - new_lag_member_interfaces, - replace_index, - process_id, - callback_route, - tt_number, - "deploy", - "isis_interface", - dry_run=False, + 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, ) - logger.warning(PLAYBOOK_VERB_NOT_YET_PROPERLY_SET) - - return { - "subscription": subscription, - } + return {"subscription": subscription} @inputstep("Wait for confirmation", assignee=Assignee.SYSTEM) @@ -378,13 +398,21 @@ def restore_isis_metric( ) -> State: """Restore the :term:`ISIS` metric to its original value.""" subscription.iptrunk.iptrunk_isis_metric = old_isis_metric - provisioning_proxy.provision_ip_trunk( - subscription, - process_id, - callback_route, - tt_number, - "isis_interface", - dry_run=False, + extra_vars = { + "wfo_trunk_json": json.loads(json_dumps(subscription)), + "dry_run": False, + "verb": "deploy", + "config_object": "isis_interface", + "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploy config for " + 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} @@ -401,25 +429,29 @@ def delete_old_config_dry( process_id: UUIDstr, tt_number: str, ) -> State: - """Perform a dry run of deleting the old configuration. + """Perform a dry run of deleting the old configuration.""" + extra_vars = { + "wfo_trunk_json": json.loads(json_dumps(subscription)), + "new_node": json.loads(json_dumps(new_node)), + "new_lag_interface": new_lag_interface, + "new_lag_member_interfaces": new_lag_member_interfaces, + "replace_index": replace_index, + "verb": "delete", + "config_object": "delete", + "dry_run": True, + "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} " + f"- Deploy config for {subscription.iptrunk.geant_s_sid}", + } - TODO: set the proper playbook verb - """ - provisioning_proxy.migrate_ip_trunk( - subscription, - new_node, - new_lag_interface, - new_lag_member_interfaces, - replace_index, - process_id, - callback_route, - tt_number, - "delete", - "delete", + 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, ) - logger.warning(PLAYBOOK_VERB_NOT_YET_PROPERLY_SET) - return {"subscription": subscription} @@ -434,26 +466,29 @@ def delete_old_config_real( process_id: UUIDstr, tt_number: str, ) -> State: - """Delete old configuration from the routers. + """Delete old configuration from the routers.""" + extra_vars = { + "wfo_trunk_json": json.loads(json_dumps(subscription)), + "new_node": json.loads(json_dumps(new_node)), + "new_lag_interface": new_lag_interface, + "new_lag_member_interfaces": new_lag_member_interfaces, + "replace_index": replace_index, + "verb": "delete", + "config_object": "delete", + "dry_run": False, + "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} " + f"- Deploy config for {subscription.iptrunk.geant_s_sid}", + } - TODO: set the proper playbook verb - """ - provisioning_proxy.migrate_ip_trunk( - subscription, - new_node, - new_lag_interface, - new_lag_member_interfaces, - replace_index, - process_id, - callback_route, - tt_number, - "delete", - "delete", - dry_run=False, + 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, ) - logger.warning(PLAYBOOK_VERB_NOT_YET_PROPERLY_SET) - return {"subscription": subscription} diff --git a/gso/workflows/iptrunk/modify_isis_metric.py b/gso/workflows/iptrunk/modify_isis_metric.py index 3ae91edf1b94b5705560947616a8812afe548dc2..c5b701a2ebcee1d1083e3b694b756351bd4bcd1e 100644 --- a/gso/workflows/iptrunk/modify_isis_metric.py +++ b/gso/workflows/iptrunk/modify_isis_metric.py @@ -1,15 +1,17 @@ """A modification workflow for setting a new :term:`ISIS` metric for an IP trunk.""" +import json + from orchestrator.forms import FormPage from orchestrator.targets import Target from orchestrator.types import FormGenerator, State, UUIDstr +from orchestrator.utils.json import json_dumps from orchestrator.workflow import StepList, done, init, step, workflow from orchestrator.workflows.steps import resync, store_process_subscription, unsync from orchestrator.workflows.utils import wrap_modify_initial_input_form from gso.products.product_types.iptrunk import Iptrunk -from gso.services import provisioning_proxy -from gso.services.provisioning_proxy import pp_interaction +from gso.services.provisioning_proxy import execute_playbook, pp_interaction def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: @@ -41,7 +43,22 @@ def provision_ip_trunk_isis_iface_dry( tt_number: str, ) -> State: """Perform a dry run of deploying the new :term:`ISIS` metric on both sides of the trunk.""" - provisioning_proxy.provision_ip_trunk(subscription, process_id, callback_route, tt_number, "isis_interface") + extra_vars = { + "wfo_trunk_json": json.loads(json_dumps(subscription)), + "dry_run": True, + "verb": "deploy", + "config_object": "isis_interface", + "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploy config for " + 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} @@ -54,13 +71,21 @@ def provision_ip_trunk_isis_iface_real( tt_number: str, ) -> State: """Deploy the new :term:`ISIS` metric on both sides of the trunk.""" - provisioning_proxy.provision_ip_trunk( - subscription, - process_id, - callback_route, - tt_number, - "isis_interface", - dry_run=False, + extra_vars = { + "wfo_trunk_json": json.loads(json_dumps(subscription)), + "dry_run": False, + "verb": "deploy", + "config_object": "isis_interface", + "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploy config for " + 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} diff --git a/gso/workflows/iptrunk/modify_trunk_interface.py b/gso/workflows/iptrunk/modify_trunk_interface.py index fca789dbf097cc6bbe476ce3a374299513fea6cd..570769887b74c852c9b4604ecce7f55c8798bc67 100644 --- a/gso/workflows/iptrunk/modify_trunk_interface.py +++ b/gso/workflows/iptrunk/modify_trunk_interface.py @@ -1,12 +1,14 @@ """A modification workflow that updates the :term:`LAG` interfaces that are part of an existing IP trunk.""" import ipaddress +import json from uuid import uuid4 from orchestrator.forms import FormPage, ReadOnlyField from orchestrator.forms.validators import UniqueConstrainedList from orchestrator.targets import Target from orchestrator.types import FormGenerator, State, UUIDstr +from orchestrator.utils.json import json_dumps from orchestrator.workflow import StepList, done, init, step, workflow from orchestrator.workflows.steps import resync, store_process_subscription, unsync from orchestrator.workflows.utils import wrap_modify_initial_input_form @@ -20,9 +22,8 @@ from gso.products.product_blocks.iptrunk import ( ) from gso.products.product_blocks.router import RouterVendor from gso.products.product_types.iptrunk import Iptrunk -from gso.services import provisioning_proxy from gso.services.netbox_client import NetboxClient -from gso.services.provisioning_proxy import pp_interaction +from gso.services.provisioning_proxy import execute_playbook, pp_interaction from gso.utils.helpers import ( LAGMember, available_interfaces_choices, @@ -215,14 +216,22 @@ def provision_ip_trunk_iface_dry( removed_ae_members: list[str], ) -> State: """Perform a dry run of deploying the updated IP trunk.""" - provisioning_proxy.provision_ip_trunk( - subscription, - process_id, - callback_route, - tt_number, - "trunk_interface", - dry_run=True, - removed_ae_members=removed_ae_members, + extra_vars = { + "wfo_trunk_json": json.loads(json_dumps(subscription)), + "dry_run": True, + "verb": "deploy", + "config_object": "trunk_interface", + "removed_ae_members": removed_ae_members, + "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploy config for " + 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} @@ -237,14 +246,22 @@ def provision_ip_trunk_iface_real( removed_ae_members: list[str], ) -> State: """Provision the new IP trunk with updated interfaces.""" - provisioning_proxy.provision_ip_trunk( - subscription, - process_id, - callback_route, - tt_number, - "trunk_interface", - dry_run=False, - removed_ae_members=removed_ae_members, + extra_vars = { + "wfo_trunk_json": json.loads(json_dumps(subscription)), + "dry_run": False, + "verb": "deploy", + "config_object": "trunk_interface", + "removed_ae_members": removed_ae_members, + "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Deploy config for " + 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} diff --git a/gso/workflows/iptrunk/terminate_iptrunk.py b/gso/workflows/iptrunk/terminate_iptrunk.py index 8f8d8d4140670230a2b0094f961b10c7d7127b95..855c6cabe4b857785a9c5107ee72081b40e277b1 100644 --- a/gso/workflows/iptrunk/terminate_iptrunk.py +++ b/gso/workflows/iptrunk/terminate_iptrunk.py @@ -1,11 +1,13 @@ """A termination workflow for an active IP trunk.""" import ipaddress +import json from orchestrator.forms import FormPage from orchestrator.forms.validators import Label from orchestrator.targets import Target from orchestrator.types import FormGenerator, State, SubscriptionLifecycle, UUIDstr +from orchestrator.utils.json import json_dumps from orchestrator.workflow import StepList, conditional, done, init, step, workflow from orchestrator.workflows.steps import ( resync, @@ -17,10 +19,11 @@ from orchestrator.workflows.utils import wrap_modify_initial_input_form from gso.products.product_blocks.router import RouterVendor from gso.products.product_types.iptrunk import Iptrunk -from gso.services import infoblox, provisioning_proxy +from gso.services import infoblox from gso.services.netbox_client import NetboxClient -from gso.services.provisioning_proxy import pp_interaction -from gso.utils.helpers import get_router_vendor, set_isis_to_90000 +from gso.services.provisioning_proxy import execute_playbook, pp_interaction +from gso.utils.helpers import get_router_vendor +from gso.utils.workflow_steps import set_isis_to_90000 def initial_input_form_generator() -> FormGenerator: @@ -39,33 +42,25 @@ def initial_input_form_generator() -> FormGenerator: return user_input.dict() -@step("Drain traffic from trunk") -def drain_traffic_from_ip_trunk( - subscription: Iptrunk, - process_id: UUIDstr, - callback_route: str, - tt_number: str, -) -> State: - """Drain all traffic from the trunk. - - XXX: Should this not be done with the isis-90k-step? - """ - provisioning_proxy.provision_ip_trunk( - subscription, - process_id, - callback_route, - tt_number, - "isis_interface", - dry_run=False, - ) - - return {"subscription": subscription} - - @step("Deprovision IP trunk [DRY RUN]") def deprovision_ip_trunk_dry(subscription: Iptrunk, process_id: UUIDstr, callback_route: str, tt_number: str) -> State: """Perform a dry run of deleting configuration from the routers.""" - provisioning_proxy.deprovision_ip_trunk(subscription, process_id, callback_route, tt_number, dry_run=True) + extra_vars = { + "wfo_trunk_json": json.loads(json_dumps(subscription)), + "dry_run": True, + "verb": "terminate", + "config_object": "trunk_deprovision", + "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} " + 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} @@ -73,7 +68,22 @@ def deprovision_ip_trunk_dry(subscription: Iptrunk, process_id: UUIDstr, callbac @step("Deprovision IP trunk [FOR REAL]") def deprovision_ip_trunk_real(subscription: Iptrunk, process_id: UUIDstr, callback_route: str, tt_number: str) -> State: """Delete configuration from the routers.""" - provisioning_proxy.deprovision_ip_trunk(subscription, process_id, callback_route, tt_number, dry_run=False) + extra_vars = { + "wfo_trunk_json": json.loads(json_dumps(subscription)), + "dry_run": False, + "verb": "terminate", + "config_object": "trunk_deprovision", + "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} " + 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} diff --git a/test/workflows/iptrunk/test_create_iptrunk.py b/test/workflows/iptrunk/test_create_iptrunk.py index fd5da66668de2307b0289304922bd86730885819..ea01fd4621a4839010f4ca45b50cd8e89f08971e 100644 --- a/test/workflows/iptrunk/test_create_iptrunk.py +++ b/test/workflows/iptrunk/test_create_iptrunk.py @@ -96,15 +96,13 @@ def input_form_wizard_data(request, juniper_router_subscription_factory, nokia_r @pytest.mark.workflow() -@patch("gso.workflows.iptrunk.create_iptrunk.provisioning_proxy.check_ip_trunk") -@patch("gso.workflows.iptrunk.create_iptrunk.provisioning_proxy.provision_ip_trunk") +@patch("gso.workflows.iptrunk.create_iptrunk.execute_playbook") @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.allocate_v6_network") @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.allocate_v4_network") def test_successful_iptrunk_creation_with_standard_lso_result( mock_allocate_v4_network, mock_allocate_v6_network, - mock_provision_ip_trunk, - mock_check_ip_trunk, + mock_execute_playbook, responses, input_form_wizard_data, faker, @@ -130,20 +128,17 @@ def test_successful_iptrunk_creation_with_standard_lso_result( assert subscription.status == "active" assert subscription.description == f"IP trunk, geant_s_sid:{input_form_wizard_data[0]['geant_s_sid']}" - assert mock_provision_ip_trunk.call_count == 4 - assert mock_check_ip_trunk.call_count == 2 + assert mock_execute_playbook.call_count == 6 @pytest.mark.workflow() -@patch("gso.workflows.iptrunk.create_iptrunk.provisioning_proxy.check_ip_trunk") -@patch("gso.workflows.iptrunk.create_iptrunk.provisioning_proxy.provision_ip_trunk") +@patch("gso.workflows.iptrunk.create_iptrunk.execute_playbook") @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.allocate_v6_network") @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.allocate_v4_network") def test_iptrunk_creation_fails_when_lso_return_code_is_one( mock_allocate_v4_network, mock_allocate_v6_network, - mock_provision_ip_trunk, - mock_check_ip_trunk, + mock_execute_playbook, responses, input_form_wizard_data, faker, @@ -161,21 +156,18 @@ def test_iptrunk_creation_fails_when_lso_return_code_is_one( assert_pp_interaction_failure(result, process_stat, step_log) - assert mock_check_ip_trunk.call_count == 0 - assert mock_provision_ip_trunk.call_count == 2 + assert mock_execute_playbook.call_count == 2 @pytest.mark.parametrize("input_form_wizard_data", [RouterVendor.JUNIPER], indirect=True) @pytest.mark.workflow() -@patch("gso.workflows.iptrunk.create_iptrunk.provisioning_proxy.check_ip_trunk") -@patch("gso.workflows.iptrunk.create_iptrunk.provisioning_proxy.provision_ip_trunk") +@patch("gso.workflows.iptrunk.create_iptrunk.execute_playbook") @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.allocate_v6_network") @patch("gso.workflows.iptrunk.create_iptrunk.infoblox.allocate_v4_network") def test_successful_iptrunk_creation_with_juniper_interface_names( mock_allocate_v4_network, mock_allocate_v6_network, - mock_provision_ip_trunk, - mock_check_ip_trunk, + mock_execute_playbook, responses, input_form_wizard_data, faker, diff --git a/test/workflows/iptrunk/test_migrate_iptrunk.py b/test/workflows/iptrunk/test_migrate_iptrunk.py index dc4be9f3271a47d0f05eec5538ea049f91597b2f..524624ff7bce7a8a3ea035990f4d89124348069e 100644 --- a/test/workflows/iptrunk/test_migrate_iptrunk.py +++ b/test/workflows/iptrunk/test_migrate_iptrunk.py @@ -107,8 +107,7 @@ def interface_lists_are_equal(list1, list2): indirect=True, ) @pytest.mark.workflow() -@patch("gso.workflows.iptrunk.migrate_iptrunk.provisioning_proxy.migrate_ip_trunk") -@patch("gso.workflows.iptrunk.migrate_iptrunk.provisioning_proxy.provision_ip_trunk") +@patch("gso.services.provisioning_proxy.execute_playbook") @patch("gso.services.netbox_client.NetboxClient.get_available_interfaces") @patch("gso.services.netbox_client.NetboxClient.get_available_lags") @patch("gso.services.netbox_client.NetboxClient.create_interface") @@ -126,8 +125,7 @@ def test_migrate_iptrunk_success( mocked_create_interface, mocked_get_available_lags, mocked_get_available_interfaces, - mock_provision_ip_trunk, - mock_migrate_ip_trunk, + mock_execute_playbook, migrate_form_input, data_config_filename: PathLike, ): @@ -165,8 +163,7 @@ def test_migrate_iptrunk_success( subscription = Iptrunk.from_subscription(subscription_id) assert subscription.status == "active" - assert mock_provision_ip_trunk.call_count == 2 - assert mock_migrate_ip_trunk.call_count == 7 + assert mock_execute_playbook.call_count == 9 # get some values from form new_router = migrate_form_input[2]["new_node"] diff --git a/test/workflows/iptrunk/test_modify_isis_metric.py b/test/workflows/iptrunk/test_modify_isis_metric.py index d26eded3abcdf104e7dc64a20cb36384f58fe398..914998552a4dfe3b88c884138cb281b88712f01c 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.workflows.iptrunk.modify_isis_metric.provisioning_proxy.provision_ip_trunk") +@patch("gso.services.provisioning_proxy.execute_playbook") def test_iptrunk_modify_isis_metric_success( mock_provision_ip_trunk, iptrunk_subscription_factory, diff --git a/test/workflows/iptrunk/test_modify_trunk_interface.py b/test/workflows/iptrunk/test_modify_trunk_interface.py index 713bf5c67aeb0eb1d9b407b3bc7d42d97a7ddca8..0a64e136c2d7a1b65073fa1e83ca39bef08afda8 100644 --- a/test/workflows/iptrunk/test_modify_trunk_interface.py +++ b/test/workflows/iptrunk/test_modify_trunk_interface.py @@ -89,7 +89,7 @@ def input_form_iptrunk_data( indirect=True, ) @pytest.mark.workflow() -@patch("gso.workflows.iptrunk.modify_trunk_interface.provisioning_proxy.provision_ip_trunk") +@patch("gso.workflows.iptrunk.modify_trunk_interface.execute_playbook") @patch("gso.services.netbox_client.NetboxClient.get_available_interfaces") @patch("gso.services.netbox_client.NetboxClient.attach_interface_to_lag") @patch("gso.services.netbox_client.NetboxClient.reserve_interface") @@ -138,7 +138,7 @@ def test_iptrunk_modify_trunk_interface_success( new_side_b_sid = input_form_iptrunk_data[3]["side_b_ae_geant_a_sid"] new_side_b_ae_members = input_form_iptrunk_data[3]["side_b_ae_members"] - # Only Nokia interfaces will checked + # Only Nokia interfaces are checked vendor_side_a = subscription.iptrunk.iptrunk_sides[0].iptrunk_side_node.vendor vendor_side_b = subscription.iptrunk.iptrunk_sides[1].iptrunk_side_node.vendor num_ifaces = (len(new_side_a_ae_members) if vendor_side_a == RouterVendor.NOKIA else 0) + ( diff --git a/test/workflows/iptrunk/test_terminate_iptrunk.py b/test/workflows/iptrunk/test_terminate_iptrunk.py index 1e17b34a631fc0a5f50b4d87945ef594c36b4af0..68b5f4edd155fa7f6e2760c2c36fec02e939f74f 100644 --- a/test/workflows/iptrunk/test_terminate_iptrunk.py +++ b/test/workflows/iptrunk/test_terminate_iptrunk.py @@ -13,8 +13,8 @@ from test.workflows import ( @pytest.mark.workflow() -@patch("gso.workflows.iptrunk.terminate_iptrunk.provisioning_proxy.provision_ip_trunk") -@patch("gso.workflows.iptrunk.terminate_iptrunk.provisioning_proxy.deprovision_ip_trunk") +@patch("gso.workflows.iptrunk.terminate_iptrunk.execute_playbook") +@patch("gso.utils.workflow_steps.execute_playbook") @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") @@ -22,8 +22,8 @@ def test_successful_iptrunk_termination( mocked_free_interface, mocked_delete_interface, mock_infoblox_delete_network, - mock_deprovision_ip_trunk, - mock_provision_ip_trunk, + mock_set_isis_to_90k, + mock_execute_playbook, iptrunk_subscription_factory, faker, data_config_filename, @@ -59,7 +59,7 @@ def test_successful_iptrunk_termination( subscription = Iptrunk.from_subscription(subscription_id) assert subscription.status == "terminated" - assert mock_provision_ip_trunk.call_count == 1 - assert mock_deprovision_ip_trunk.call_count == 2 + assert mock_execute_playbook.call_count == 2 + assert mock_set_isis_to_90k.call_count == 1 assert mock_infoblox_delete_network.call_count == 2 assert subscription.iptrunk.iptrunk_isis_metric == 90000