diff --git a/gso/services/provisioning_proxy.py b/gso/services/provisioning_proxy.py
index 38d3037b33d59cc102d5e9ed02ffc4eb8d964074..0aa6d8fbeb4d95ad15b06e39ebcda97905a354f3 100644
--- a/gso/services/provisioning_proxy.py
+++ b/gso/services/provisioning_proxy.py
@@ -88,8 +88,8 @@ _send_delete = partial(_send_request, CUDOperation.DELETE)
def execute_playbook(
playbook_name: str,
callback_route: str,
- inventory: dict[str, Any],
- extra_vars: dict[str, Any]
+ inventory: dict[str, Any] | str,
+ extra_vars: dict[str, Any],
) -> None:
"""Execute a playbook remotely through the provisioning proxy.
@@ -144,7 +144,7 @@ def execute_playbook(
parameters = {
"playbook_name": playbook_name,
"inventory": inventory,
- "extra_vars": extra_vars
+ "extra_vars": extra_vars,
}
_send_post("playbook", parameters, callback_route)
diff --git a/gso/translations/en-GB.json b/gso/translations/en-GB.json
index 4fe3a15c1782c16e2ba1a889e724bb72b6d7a85b..b0af674f4e87d6d3653c97b231fe78fb6bdd7808 100644
--- a/gso/translations/en-GB.json
+++ b/gso/translations/en-GB.json
@@ -36,9 +36,10 @@
}
},
"workflow": {
- "modify_isis_metric": "Modify the ISIS metric",
- "modify_trunk_interface": "Modify IP Trunk interface",
- "migrate_iptrunk": "Migrate IP Trunk",
- "confirm_info": "Please verify this form looks correct."
- }
+ "deploy_twamp": "Deploy TWAMP",
+ "modify_isis_metric": "Modify the ISIS metric",
+ "modify_trunk_interface": "Modify IP Trunk interface",
+ "migrate_iptrunk": "Migrate IP Trunk",
+ "confirm_info": "Please verify this form looks correct."
+ }
}
diff --git a/gso/workflows/__init__.py b/gso/workflows/__init__.py
index 845f5c2babb89502a3f18022b46eaed13d3a8f46..b10a919ec0ac98cb65434ad187974637fae76c6c 100644
--- a/gso/workflows/__init__.py
+++ b/gso/workflows/__init__.py
@@ -3,6 +3,7 @@
from orchestrator.workflows import LazyWorkflowInstance
LazyWorkflowInstance("gso.workflows.iptrunk.create_iptrunk", "create_iptrunk")
+LazyWorkflowInstance("gso.workflows.iptrunk.deploy_twamp", "deploy_twamp")
LazyWorkflowInstance("gso.workflows.iptrunk.modify_isis_metric", "modify_isis_metric")
LazyWorkflowInstance("gso.workflows.iptrunk.modify_trunk_interface", "modify_trunk_interface")
LazyWorkflowInstance("gso.workflows.iptrunk.migrate_iptrunk", "migrate_iptrunk")
diff --git a/gso/workflows/iptrunk/deploy_twamp.py b/gso/workflows/iptrunk/deploy_twamp.py
new file mode 100644
index 0000000000000000000000000000000000000000..073aa35e6c7df075c624ef02c083100247dd2d83
--- /dev/null
+++ b/gso/workflows/iptrunk/deploy_twamp.py
@@ -0,0 +1,86 @@
+""""Workflow for adding TWAMP to an existing IP trunk."""
+
+from orchestrator.forms import FormPage
+from orchestrator.forms.validators import Label
+from orchestrator.targets import Target
+from orchestrator.types import FormGenerator, State, UUIDstr
+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.provisioning_proxy import execute_playbook, pp_interaction
+
+
+def _initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
+ trunk = Iptrunk.from_subscription(subscription_id)
+
+ class DeployTWAMPForm(FormPage):
+ info_label: Label = (
+ "Please confirm deployment of TWAMP on IP trunk from "
+ f"{trunk.iptrunk.iptrunk_sides[0].iptrunk_side_node.router_fqdn} to "
+ f"{trunk.iptrunk.iptrunk_sides[1].iptrunk_side_node.router_fqdn}" # type: ignore[assignment]
+ )
+
+ yield DeployTWAMPForm
+
+ return {"subscription": trunk}
+
+
+@step("[DRY RUN] Deploy TWAMP on both sides")
+def deploy_twamp_dry(subscription: Iptrunk, process_id: UUIDstr, callback_route: str) -> State:
+ """Perform a dry run of deploying the TWAMP session."""
+ extra_vars = {
+ "subscription": subscription,
+ "process_id": process_id,
+ "dry_run": True,
+ }
+
+ 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}
+
+
+@step("[FOR REAL] Deploy TWAMP on both sides")
+def deploy_twamp_real(subscription: Iptrunk, process_id: UUIDstr, callback_route: str) -> State:
+ """Deploy the TWAMP session."""
+ extra_vars = {
+ "subscription": subscription,
+ "process_id": process_id,
+ "dry_run": False,
+ }
+
+ 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}
+
+
+@workflow(
+ "Deploy TWAMP",
+ initial_input_form=wrap_modify_initial_input_form(_initial_input_form_generator),
+ target=Target.MODIFY,
+)
+def deploy_twamp() -> StepList:
+ """Deploy a TWAMP session on an IP trunk.
+
+ * Run the TWAMP playbook, including an initial dry run
+ """
+ return (
+ init
+ >> store_process_subscription(Target.MODIFY)
+ >> unsync
+ >> pp_interaction(deploy_twamp_dry)
+ >> pp_interaction(deploy_twamp_real)
+ >> resync
+ >> done
+ )