Skip to content
Snippets Groups Projects

Rework LSO interaction

Merged Karel van Klink requested to merge feature/skip-empty-inventory into develop
All threads resolved!
23 files
+ 824
834
Compare changes
  • Side-by-side
  • Inline
Files
23
+ 60
15
@@ -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
)
Loading