Skip to content
Snippets Groups Projects
Verified Commit 447f301d authored by Karel van Klink's avatar Karel van Klink :smiley_cat:
Browse files

remove JSON interpretation of ansible runner output

parent 0695ae1b
Branches
Tags
2 merge requests!71Publish LSO version 1.0,!68remove JSON interpretation of ansible runner output
Pipeline #85091 failed
{ {
"ansible_playbooks_root_dir": "/app/gap/ansible/ansible_collections/geant/gap_ansible/playbooks", "ansible_playbooks_root_dir": "/app/gap/ansible/ansible_collections/geant/gap_ansible/playbooks"
"filtered_ansible_keys": [
"_ansible_no_log",
"_ansible_delegated_vars",
"_ansible_verbose_always"
]
} }
...@@ -19,7 +19,7 @@ CONFIG_SCHEMA = { ...@@ -19,7 +19,7 @@ CONFIG_SCHEMA = {
"ansible_playbooks_root_dir": {"type": "string"}, "ansible_playbooks_root_dir": {"type": "string"},
"filtered_ansible_keys": {"type": "array", "items": {"type": "string"}}, "filtered_ansible_keys": {"type": "array", "items": {"type": "string"}},
}, },
"required": ["ansible_playbooks_root_dir", "filtered_ansible_keys"], "required": ["ansible_playbooks_root_dir"],
"additionalProperties": False, "additionalProperties": False,
} }
DEFAULT_REQUEST_TIMEOUT = 10 DEFAULT_REQUEST_TIMEOUT = 10
...@@ -33,7 +33,9 @@ class Config(BaseModel): ...@@ -33,7 +33,9 @@ class Config(BaseModel):
""" """
ansible_playbooks_root_dir: str ansible_playbooks_root_dir: str
filtered_ansible_keys: list[str] #: .. deprecated:: 0.21
#: Not used anymore, does not have to be present in config.
filtered_ansible_keys: list[str] | None = None
def load_from_file(file: Path) -> Config: def load_from_file(file: Path) -> Config:
......
"""Module that gathers common API responses and data models.""" """Module that gathers common API responses and data models."""
import json
import logging import logging
import threading import threading
import uuid import uuid
...@@ -44,56 +43,6 @@ def playbook_launch_error(reason: str, status_code: int = status.HTTP_400_BAD_RE ...@@ -44,56 +43,6 @@ def playbook_launch_error(reason: str, status_code: int = status.HTTP_400_BAD_RE
return JSONResponse(content={"error": reason}, status_code=status_code) return JSONResponse(content={"error": reason}, status_code=status_code)
def _process_json_output(runner: ansible_runner.Runner) -> list[dict[Any, Any]]: # ignore: C901
"""Handle Ansible runner output, and filter out redundant an overly verbose messages.
:param :class:`ansible_runner.Runner` runner: Ansible runner that has already executed a playbook.
:return: A filtered dictionary that contains only the relevant parts of the output from executing an Ansible
playbook.
:rtype: list[dict[Any, Any]]
"""
config_params = config.load()
json_content = runner.stdout.read()
parsed_output = []
for line in json_content.strip().splitlines():
try:
task_output = json.loads(line)
if "res" in task_output["event_data"] and (
int(runner.rc) != 0 or task_output["event_data"]["res"]["changed"] is True
):
# The line contains result data, and must either consist of a change, or the playbook failed, and all
# steps should be included, including those that didn't make changes.
task_result = task_output["event_data"]["res"]
# Remove redundant ansible-related keys.
for remove in config_params.filtered_ansible_keys:
task_result.pop(remove, None)
# Remove meta-steps that just copy some temporary files, and continue to the next event
if "state" in task_result and (task_result["state"] == "directory" or task_result["state"] == "file"):
continue
if "diff_lines" in task_result:
# Juniper-specific
# Prevent the diff from being displayed twice, and only keep the formatted version.
task_result.pop("diff", None)
if bool(task_result):
# Only add the event if there are any relevant keys left.
parsed_output.append({"host": task_output["event_data"]["host"]} | task_result)
elif "ok" in task_output["event_data"]:
# Always include the final message that contains the playbook execution overview.
parsed_output.append(task_output["event_data"])
except json.JSONDecodeError:
# If the line can't be decoded as JSON, include it in its entirety.
parsed_output.append({"invalid_json": line})
return parsed_output
def _run_playbook_proc( def _run_playbook_proc(
job_id: str, job_id: str,
playbook_path: str, playbook_path: str,
...@@ -115,17 +64,15 @@ def _run_playbook_proc( ...@@ -115,17 +64,15 @@ def _run_playbook_proc(
extravars=extra_vars, extravars=extra_vars,
) )
parsed_output = _process_json_output(ansible_playbook_run)
payload = { payload = {
"status": ansible_playbook_run.status, "status": ansible_playbook_run.status,
"job_id": job_id, "job_id": job_id,
"output": parsed_output, "output": ansible_playbook_run.stdout.readlines(),
"return_code": int(ansible_playbook_run.rc), "return_code": int(ansible_playbook_run.rc),
} }
request_result = requests.post(callback, json=payload, timeout=DEFAULT_REQUEST_TIMEOUT) request_result = requests.post(callback, json=payload, timeout=DEFAULT_REQUEST_TIMEOUT)
if request_result.status_code != status.HTTP_200_OK: if not status.HTTP_200_OK <= request_result.status_code < status.HTTP_300_MULTIPLE_CHOICES:
msg = f"Callback failed: {request_result.text}" msg = f"Callback failed: {request_result.text}"
logger.error(msg) logger.error(msg)
......
...@@ -39,10 +39,7 @@ def configuration_data() -> dict[str, str]: ...@@ -39,10 +39,7 @@ def configuration_data() -> dict[str, str]:
(Path(tempdir) / "iptrunks_checks.yaml").touch() (Path(tempdir) / "iptrunks_checks.yaml").touch()
(Path(tempdir) / "iptrunks_migration.yaml").touch() (Path(tempdir) / "iptrunks_migration.yaml").touch()
yield { yield {"ansible_playbooks_root_dir": tempdir}
"ansible_playbooks_root_dir": tempdir,
"filtered_ansible_keys": ["too_verbose_output_key"],
}
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment