Skip to content

Commit

Permalink
feat(play): Add play level trace span
Browse files Browse the repository at this point in the history
clean(plugin): Remove code from other work

fix(inventory): missing inventory for test one host

fix(inventory): missing new line
  • Loading branch information
louisquentinjoucla authored and mhansen committed Jun 23, 2022
1 parent b9d7992 commit 234ab54
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 0 deletions.
42 changes: 42 additions & 0 deletions plugins/callback/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ def __init__(self):
self._first: bool = True
self._start_date: str = datetime.now().isoformat()
self._output_file: str = 'trace-%s.json' % self._start_date
self._current_play: str = ''
self._play_id: int = 0

if not os.path.exists(self._output_dir):
os.makedirs(self._output_dir)
Expand All @@ -89,6 +91,12 @@ def _write_event(self, e: Dict):
json.dump(e, self._f, sort_keys=True, indent=2)
self._f.flush()

def v2_playbook_on_play_start(self, play):

self._end_play_span()
self._current_play = play
self._play_id += 1

def v2_runner_on_start(self, host, task):
uuid = task._uuid
name = task.get_name().strip()
Expand All @@ -113,6 +121,21 @@ def v2_runner_on_start(self, host, task):
},
})

# If it's the first task of the host for the play, start duration event for the current play
if not self._hosts[host_uuid].hasTaskInPlay:
self._write_event({
"name": self._current_play.get_name().strip(),
"cat": "play",
"ph": "B", # Begin
"ts": time.time_ns() / 1000 if "time_ns" in time.__dict__ else time.time() * 100000,
"pid": self._hosts[host_uuid].pid,
"id": self._play_id,
"args": {
"host": host.name,
},
})
self._hosts[host_uuid].hasTaskInPlay = True

# See "Duration Events" in:
# https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview#heading=h.nso4gcezn7n1
self._write_event({
Expand All @@ -130,6 +153,22 @@ def v2_runner_on_start(self, host, task):
},
})

def _end_play_span(self):
# Spawn ending play event for each play that are done and then reset flag
for host in self._hosts.values():
if host.hasTaskInPlay:
# Write end event
self._write_event({
"name": self._current_play.get_name().strip(),
"cat": "play",
"id": self._play_id,
"ph": "E", # End
"ts": time.time_ns() / 1000 if "time_ns" in time.__dict__ else time.time() * 100000,
"pid": host.pid,
})

host.hasTaskInPlay = False

def _end_span(self, result, status: str):
task = result._task
uuid = task._uuid
Expand Down Expand Up @@ -160,6 +199,8 @@ def v2_runner_on_skipped(self, result):
self._end_span(result, status='skipped')

def _end(self):

self._end_play_span()
self._f.write("\n]")
self._f.close()

Expand All @@ -168,3 +209,4 @@ def _end(self):
class Host:
name: str
pid: int
hasTaskInPlay: bool = False
13 changes: 13 additions & 0 deletions tests/integration/inventories/multiple_hosts.ini
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,16 @@
127.0.0.8 ansible_connection=local
127.0.0.9 ansible_connection=local
127.0.0.10 ansible_connection=local

[group_a]
127.0.0.1 ansible_connection=local
127.0.0.2 ansible_connection=local
127.0.0.8 ansible_connection=local
127.0.0.9 ansible_connection=local
127.0.0.10 ansible_connection=local

[group_b]
127.0.0.8 ansible_connection=local
127.0.0.9 ansible_connection=local
127.0.0.10 ansible_connection=local
127.0.0.11 ansible_connection=local
6 changes: 6 additions & 0 deletions tests/integration/inventories/one_host.ini
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
[all]
localhost ansible_connection=local

[group_a]
127.0.0.1 ansible_connection=local

[group_b]
127.0.0.1 ansible_connection=local
17 changes: 17 additions & 0 deletions tests/integration/plays/base.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---

- hosts: group_a
name: First play
environment:
CALLBACKS_ENABLED: trace
TRACE_OUTPUT_DIR: /ansible_collections/mhansen/ansible-trace
TRACE_HIDE_TASK_ARGUMENTS: True
tasks:
- name: One task before import
shell: "echo 'Play 1'"

- name: Second play
import_playbook: otherplays.yml

- name: Third play
import_playbook: nested.yml
15 changes: 15 additions & 0 deletions tests/integration/plays/nested.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---

- hosts: group_b
name: Third play
environment:
CALLBACKS_ENABLED: trace
TRACE_OUTPUT_DIR: /ansible_collections/mhansen/ansible-trace
TRACE_HIDE_TASK_ARGUMENTS: True
tasks:
- name: Random task
shell: "echo 'Hello world'"

- name: Nested play
import_playbook: otherplays.yml

12 changes: 12 additions & 0 deletions tests/integration/plays/otherplays.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---

- hosts: group_a
name: Random play
environment:
CALLBACKS_ENABLED: trace
TRACE_OUTPUT_DIR: /ansible_collections/mhansen/ansible-trace
TRACE_HIDE_TASK_ARGUMENTS: True
tasks:
- name: A fake task
shell: "echo 'hello there'"

37 changes: 37 additions & 0 deletions tests/integration/tests/plays.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Handle integration tests
from typing import Union, Dict, List, Any
from utils import get_last_trace, parse_and_validate_trace
from event import HostEvent
import pytest

JSONTYPE = Union[None, int, str, bool, List[Any], Dict[str, Any]]


@pytest.mark.ansible_playbook('plays/base.yml')
@pytest.mark.ansible_inventory('inventories/multiple_hosts.ini')
@pytest.mark.ansible_strategy('free')
def test_basic_multiple_free(ansible_play):
trace_hosts: Dict[int, HostEvent]
trace_events: Dict[int, Any]
trace_json: JSONTYPE = get_last_trace()
trace_hosts, trace_events = parse_and_validate_trace(trace_json)


@pytest.mark.ansible_playbook('plays/base.yml')
@pytest.mark.ansible_inventory('inventories/multiple_hosts.ini')
@pytest.mark.ansible_strategy('linear')
def test_basic_multiple_linear(ansible_play):
trace_hosts: Dict[int, HostEvent]
trace_events: Dict[int, Any]
trace_json: JSONTYPE = get_last_trace()
trace_hosts, trace_events = parse_and_validate_trace(trace_json)


@pytest.mark.ansible_playbook('plays/base.yml')
@pytest.mark.ansible_inventory('inventories/one_host.ini')
@pytest.mark.ansible_strategy('linear')
def test_basic_single_linear(ansible_play):
trace_hosts: Dict[int, HostEvent]
trace_events: Dict[int, Any]
trace_json: JSONTYPE = get_last_trace()
trace_hosts, trace_events = parse_and_validate_trace(trace_json)

0 comments on commit 234ab54

Please sign in to comment.