Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task names do not match task output when running with "strategy: free" #260

Closed
jhampson-dbre opened this issue Apr 15, 2021 · 11 comments
Closed
Labels
bug help wanted plugins Related to the Ansible plugins

Comments

@jhampson-dbre
Copy link
Contributor

jhampson-dbre commented Apr 15, 2021

What is the issue ?

When running a playbook with strategy: free and multiple hosts, the task names in ARA web UI do not match with the task output and task result that is recorded.

Here is an example.

Playbook

---
- hosts: all
  strategy: free
  gather_facts: yes
  vars:
    simulate_failure: true

  tasks:
    - name: debug 1
      debug:
        msg: "debug 1"

    - block:
        - name: debug 2
          debug:
            msg: "debug 2"

        - name: Simulate a failure
          fail:
            msg: "Did not compute"
          when: simulate_failure|bool
      rescue:
        - name: Import a role
          import_role:
            name: auto_oncall_demo_role

        - name: debug 3
          debug:
            msg: "debug 3"

        - name: Simulate successful completion
          debug:
            msg: "Everything is awesome"

ARA problematic record example

image

image

command line output from the 2 tasks that are mashed up

TASK [Simulate a failure] *********************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:19
fatal: [serverq01b]: FAILED! => {
    "changed": false,
    "msg": "Did not compute"
}

TASK [debug 3] ********************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:28
ok: [serverq01b] => {
    "msg": "debug 3"
}

The ARA screenshots show Task: debug 3 with Status: failed and Result: msg Did not compute

The Ansible output shows the correct task to match the status and result is Task: Simulate a failure

The ARA screenshots do not show a Task: Simulate a failure for this server.

You can also see in the screenshots that it recorded two debug 3 tasks (there second one is accurately recorded), and two Simulate successful completion tasks (the first has the wrong result recorded, second is correct)

relevant playbook command line debug output

(app-root) sh-4.2$ ansible-playbook --check --user xxxxxxxxxxx --become-user xxxxxxxx --ask-pass --ask-become-pass -i /opt/app-root/src/jobs_local/inventory/hosts --limit "serverd01a:serverd01b:serverd01c:serverd02:serverd02b:serverd02c:serverq01a:serverq01b:serverq01c:serverq02:serverq02b:serverq02c" /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml
ansible-playbook 2.9.19
  config file = None
  configured module search path = ['/opt/app-root/src/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /opt/app-root/lib/python3.6/site-packages/ansible
  executable location = /opt/app-root/bin/ansible-playbook
  python version = 3.6.9 (default, Sep 11 2019, 16:40:19) [GCC 4.8.5 20150623 (Red Hat 4.8.5-16)]
No config file found; using defaults
SSH password: 
BECOME password[defaults to SSH password]: 
host_list declined parsing /opt/app-root/src/jobs_local/inventory/hosts as it did not pass its verify_file() method
script declined parsing /opt/app-root/src/jobs_local/inventory/hosts as it did not pass its verify_file() method
auto declined parsing /opt/app-root/src/jobs_local/inventory/hosts as it did not pass its verify_file() method
Parsed /opt/app-root/src/jobs_local/inventory/hosts inventory source with ini plugin
Skipping callback 'actionable', as we already have a stdout callback.
Skipping callback 'counter_enabled', as we already have a stdout callback.
Skipping callback 'debug', as we already have a stdout callback.
Skipping callback 'dense', as we already have a stdout callback.
Skipping callback 'dense', as we already have a stdout callback.
Skipping callback 'full_skip', as we already have a stdout callback.
Skipping callback 'json', as we already have a stdout callback.
Skipping callback 'minimal', as we already have a stdout callback.
Skipping callback 'null', as we already have a stdout callback.
Skipping callback 'oneline', as we already have a stdout callback.
Skipping callback 'selective', as we already have a stdout callback.
Skipping callback 'skippy', as we already have a stdout callback.
Skipping callback 'stderr', as we already have a stdout callback.
Skipping callback 'unixy', as we already have a stdout callback.
Skipping callback 'yaml', as we already have a stdout callback.

PLAYBOOK: self_service_prototype.yml **********************************************************************************************************************************************************
1 plays in /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml

PLAY [all] ************************************************************************************************************************************************************************************
Using module file /opt/app-root/lib/python3.6/site-packages/ansible/modules/windows/setup.ps1
Pipelining is enabled.
<serverd01a> ESTABLISH WINRM CONNECTION FOR USER: ansibleusr on PORT 5985 TO serverd01a
Using module file /opt/app-root/lib/python3.6/site-packages/ansible/modules/windows/setup.ps1
Pipelining is enabled.
<serverd01b> ESTABLISH WINRM CONNECTION FOR USER: ansibleusr on PORT 5985 TO serverd01b
Using module file /opt/app-root/lib/python3.6/site-packages/ansible/modules/windows/setup.ps1
Pipelining is enabled.
<serverd01c> ESTABLISH WINRM CONNECTION FOR USER: ansibleusr on PORT 5985 TO serverd01c
Using module file /opt/app-root/lib/python3.6/site-packages/ansible/modules/windows/setup.ps1
Pipelining is enabled.
<serverd02> ESTABLISH WINRM CONNECTION FOR USER: ansibleusr on PORT 5985 TO serverd02
Using module file /opt/app-root/lib/python3.6/site-packages/ansible/modules/windows/setup.ps1
Pipelining is enabled.
<serverd02b> ESTABLISH WINRM CONNECTION FOR USER: ansibleusr on PORT 5985 TO serverd02b
Using module file /opt/app-root/lib/python3.6/site-packages/ansible/modules/windows/setup.ps1
Pipelining is enabled.
<serverq01a> ESTABLISH WINRM CONNECTION FOR USER: ansibleusr on PORT 5985 TO serverq01a
Using module file /opt/app-root/lib/python3.6/site-packages/ansible/modules/windows/setup.ps1
Pipelining is enabled.
<serverd02c> ESTABLISH WINRM CONNECTION FOR USER: ansibleusr on PORT 5985 TO serverd02c
Using module file /opt/app-root/lib/python3.6/site-packages/ansible/modules/windows/setup.ps1
Pipelining is enabled.
<serverq01b> ESTABLISH WINRM CONNECTION FOR USER: ansibleusr on PORT 5985 TO serverq01b
Using module file /opt/app-root/lib/python3.6/site-packages/ansible/modules/windows/setup.ps1
Pipelining is enabled.
<serverq01c> ESTABLISH WINRM CONNECTION FOR USER: ansibleusr on PORT 5985 TO serverq01c
Using module file /opt/app-root/lib/python3.6/site-packages/ansible/modules/windows/setup.ps1
Pipelining is enabled.
<serverq02> ESTABLISH WINRM CONNECTION FOR USER: ansibleusr on PORT 5985 TO serverq02
EXEC (via pipeline wrapper)
EXEC (via pipeline wrapper)
EXEC (via pipeline wrapper)
EXEC (via pipeline wrapper)
EXEC (via pipeline wrapper)
EXEC (via pipeline wrapper)
EXEC (via pipeline wrapper)
EXEC (via pipeline wrapper)
EXEC (via pipeline wrapper)
EXEC (via pipeline wrapper)
Using module file /opt/app-root/lib/python3.6/site-packages/ansible/modules/windows/setup.ps1
Pipelining is enabled.
<serverq02b> ESTABLISH WINRM CONNECTION FOR USER: ansibleusr on PORT 5985 TO serverq02b

TASK [Gathering Facts] ************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:2
ok: [serverd01a]
ok: [serverd02b]
META: ran handlers
META: ran handlers
Using module file /opt/app-root/lib/python3.6/site-packages/ansible/modules/windows/setup.ps1
Pipelining is enabled.
<serverq02c> ESTABLISH WINRM CONNECTION FOR USER: ansibleusr on PORT 5985 TO serverq02c
ok: [serverd02c]

TASK [debug 1] ********************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:10
ok: [serverd01a] => {
    "msg": "debug 1"
}
ok: [serverd02b] => {
    "msg": "debug 1"
}
META: ran handlers

TASK [debug 2] ********************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:15
ok: [serverd01a] => {
    "msg": "debug 2"
}
ok: [serverd02b] => {
    "msg": "debug 2"
}

TASK [Simulate a failure] *********************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:19
fatal: [serverd01a]: FAILED! => {
    "changed": false,
    "msg": "Did not compute"
}
fatal: [serverd02b]: FAILED! => {
    "changed": false,
    "msg": "Did not compute"
}

TASK [debug 1] ********************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:10
ok: [serverd02c] => {
    "msg": "debug 1"
}
EXEC (via pipeline wrapper)

TASK [auto_oncall_demo_role : Auto Oncall Demo role] ******************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/roles/auto_oncall_demo_role/tasks/main.yml:2
ok: [serverd01a] => {
    "msg": "Hello from auto_oncall_demo_role! Have a great day!"
}
ok: [serverd02b] => {
    "msg": "Hello from auto_oncall_demo_role! Have a great day!"
}

TASK [debug 2] ********************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:15
ok: [serverd02c] => {
    "msg": "debug 2"
}

TASK [debug 3] ********************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:28
ok: [serverd01a] => {
    "msg": "debug 3"
}

TASK [Gathering Facts] ************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:2
ok: [serverq01a]

TASK [debug 3] ********************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:28
ok: [serverd02b] => {
    "msg": "debug 3"
}

TASK [Simulate a failure] *********************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:19
fatal: [serverd02c]: FAILED! => {
    "changed": false,
    "msg": "Did not compute"
}
EXEC (via pipeline wrapper)
META: ran handlers

TASK [Simulate successful completion] *********************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:32
ok: [serverd01a] => {
    "msg": "Everything is awesome"
}

TASK [Gathering Facts] ************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:2
ok: [serverq01c]

TASK [Simulate successful completion] *********************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:32
ok: [serverd02b] => {
    "msg": "Everything is awesome"
}

TASK [auto_oncall_demo_role : Auto Oncall Demo role] ******************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/roles/auto_oncall_demo_role/tasks/main.yml:2
ok: [serverd02c] => {
    "msg": "Hello from auto_oncall_demo_role! Have a great day!"
}
META: ran handlers
META: ran handlers
META: ran handlers

TASK [debug 3] ********************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:28
ok: [serverd02c] => {
    "msg": "debug 3"
}

TASK [debug 1] ********************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:10
ok: [serverq01a] => {
    "msg": "debug 1"
}
META: ran handlers
META: ran handlers

TASK [Simulate successful completion] *********************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:32
ok: [serverd02c] => {
    "msg": "Everything is awesome"
}

TASK [debug 2] ********************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:15
ok: [serverq01a] => {
    "msg": "debug 2"
}

TASK [debug 1] ********************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:10
ok: [serverq01c] => {
    "msg": "debug 1"
}
META: ran handlers

TASK [Gathering Facts] ************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:2
ok: [serverq01b]

TASK [Simulate a failure] *********************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:19
fatal: [serverq01a]: FAILED! => {
    "changed": false,
    "msg": "Did not compute"
}

TASK [debug 2] ********************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:15
ok: [serverq01c] => {
    "msg": "debug 2"
}
META: ran handlers
META: ran handlers

TASK [auto_oncall_demo_role : Auto Oncall Demo role] ******************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/roles/auto_oncall_demo_role/tasks/main.yml:2
ok: [serverq01a] => {
    "msg": "Hello from auto_oncall_demo_role! Have a great day!"
}

TASK [Simulate a failure] *********************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:19
fatal: [serverq01c]: FAILED! => {
    "changed": false,
    "msg": "Did not compute"
}

TASK [debug 3] ********************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:28
ok: [serverq01a] => {
    "msg": "debug 3"
}

TASK [debug 1] ********************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:10
ok: [serverq01b] => {
    "msg": "debug 1"
}

TASK [Simulate successful completion] *********************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:32
ok: [serverq01a] => {
    "msg": "Everything is awesome"
}

TASK [debug 2] ********************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:15
ok: [serverq01b] => {
    "msg": "debug 2"
}

TASK [auto_oncall_demo_role : Auto Oncall Demo role] ******************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/roles/auto_oncall_demo_role/tasks/main.yml:2
ok: [serverq01c] => {
    "msg": "Hello from auto_oncall_demo_role! Have a great day!"
}
META: ran handlers
META: ran handlers


TASK [Simulate a failure] *********************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:19
fatal: [serverq01b]: FAILED! => {
    "changed": false,
    "msg": "Did not compute"
}

TASK [debug 3] ********************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:28
ok: [serverq01c] => {
    "msg": "debug 3"
}

TASK [auto_oncall_demo_role : Auto Oncall Demo role] ******************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/roles/auto_oncall_demo_role/tasks/main.yml:2
ok: [serverq01b] => {
    "msg": "Hello from auto_oncall_demo_role! Have a great day!"
}

TASK [Simulate successful completion] *********************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:32
ok: [serverq01c] => {
    "msg": "Everything is awesome"
}
META: ran handlers
META: ran handlers

TASK [debug 3] ********************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:28
ok: [serverq01b] => {
    "msg": "debug 3"
}

TASK [Simulate successful completion] *********************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:32
ok: [serverq01b] => {
    "msg": "Everything is awesome"
}
META: ran handlers
META: ran handlers

What should be happening ?

All status/results in ARA should have the correct task names associated when the playbook runs.

@dmsimard
Copy link
Contributor

dmsimard commented Apr 15, 2021

Hi @jhampson-dbre and thanks for the issue !

I know what the free strategy is but honestly I've never had the use case for it and therefore I don't have a lot of experience running ara with it .. there could be dragons.

I don't have time to look into this right now but I will leave the issue opened so we can look at it when we have a chance.

@dmsimard dmsimard added the plugins Related to the Ansible plugins label Apr 15, 2021
@dmsimard dmsimard changed the title Task names do not match task output Task names do not match task output when running with "strategy: free" Apr 15, 2021
@jhampson-dbre
Copy link
Contributor Author

jhampson-dbre commented Apr 15, 2021

I'll try and do some digging if see if I can turn anything up. From poking around in the db (sqlite), I have found this:

task

{
  "model": "api.task",
  "pk": 212,
  "fields": {
    "created": "2021-04-15T15:25:59.950Z",
    "updated": "2021-04-15T15:26:00.026Z",
    "started": "2021-04-15T15:25:59.940Z",
    "ended": "2021-04-15T15:26:00.014Z",
    "duration": "00:00:00.073905",
    "name": "debug 3",
    "action": "debug",
    "lineno": 28,
    "tags": "eJyLjgUAARUAuQ==",
    "handler": false,
    "status": "completed",
    "play": 3,
    "file": 11,
    "playbook": 3
  }
},

result

{
  "model": "api.result",
  "pk": 205,
  "fields": {
    "created": "2021-04-15T15:25:59.984Z",
    "updated": "2021-04-15T15:25:59.984Z",
    "started": "2021-04-15T15:25:59.911Z",
    "ended": "2021-04-15T15:25:59.969Z",
    "duration": "00:00:00.058667",
    "status": "failed",
    "changed": false,
    "ignore_errors": false,
    "content": "eJyrVkrOSMxLT01RslJIS8wpTtVRUMotTgfylFwyUxTy8ksUkvNzC0pLUpVqAUCMDoA=",
    "host": 30,
    "task": 212,
    "play": 3,
    "playbook": 3
  }
},
{
  "model": "api.result",
  "pk": 206,
  "fields": {
    "created": "2021-04-15T15:26:00.004Z",
    "updated": "2021-04-15T15:26:00.004Z",
    "started": "2021-04-15T15:25:59.955Z",
    "ended": "2021-04-15T15:25:59.990Z",
    "duration": "00:00:00.035031",
    "status": "ok",
    "changed": false,
    "ignore_errors": false,
    "content": "eJyrVkrOSMxLT01RslJIS8wpTtVRUMotTgfylFJSk0rTFYyVagHYFQs7",
    "host": 29,
    "task": 212,
    "play": 3,
    "playbook": 3
  }
},

The top result record (id 205) is the same as the screenshot above. It is associated with task id 212 (debug 3) and host id 30 (serverq01b).

However, result id 206 is also associated with task id 212 and host id 29 (serverq01c). From what I can tell, this result/task combination is the correct one. The result id 205 associated with task id 212 is incorrect.

If you can point me in the right direction of that code where this task/result mapping occurs, I'll see if i can turn up anything else. Thanks!

@jhampson-dbre
Copy link
Contributor Author

jhampson-dbre commented Apr 15, 2021

Added some extra logging to plugin. My initial thought is that it appears to be some sort of race condition with either how task id's are generated or retrieved:

TASK [Gathering Facts] ************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:2
ok: [serverd01c]
got task 587 for host 160

TASK [debug 2] ********************************************************************************************************************************************************************************
task path: /opt/app-root/src/jobs_local/playbooks/self_service_prototype.yml:15
ok: [serverd02b] => {
    "msg": "debug 2"
}
got task 587 for host 159
META: ran handlers

@dmsimard
Copy link
Contributor

dmsimard commented Apr 16, 2021

Thanks for looking into this @jhampson-dbre!

In the normal linear strategy, it goes a bit like this in chronological order:

  1. create a playbook:
    # Create the playbook
    self.playbook = self.client.post(
    "/api/v1/playbooks",
    ansible_version=ansible_version,
    arguments=cli_options,
    status="running",
    path=path,
    controller=socket.getfqdn(),
    started=datetime.datetime.now(datetime.timezone.utc).isoformat(),
    )
  2. create a play, associate it to the playbook:
    self.play = self.client.post(
    "/api/v1/plays",
    name=play.name,
    status="running",
    uuid=play._uuid[:36],
    playbook=self.playbook["id"],
    started=datetime.datetime.now(datetime.timezone.utc).isoformat(),
    )
  3. create a task, associate it to the play and the playbook:
    self.task = self.client.post(
    "/api/v1/tasks",
    name=task.get_name(),
    status="running",
    action=task.action,
    play=self.play["id"],
    playbook=self.playbook["id"],
    file=task_file["id"],
    tags=task.tags,
    lineno=lineno,
    handler=handler,
    started=datetime.datetime.now(datetime.timezone.utc).isoformat(),
    )
  4. when saving a result, create a host if need be:
    # Retrieve the host so we can associate the result to the host id
    host = self._get_or_create_host(hostname)
  5. once the we have the host, create a result and associate it to the playbook, play and task:
    self.result = self.client.post(
    "/api/v1/results",
    playbook=self.playbook["id"],
    task=self.task["id"],
    host=host["id"],
    play=self.task["play"],
    content=results,
    status=status,
    started=self.result_started[hostname] if hostname in self.result_started else self.task["started"],
    ended=self.result_ended[hostname],
    changed=result._result.get("changed", False),
    # Note: ignore_errors might be None instead of a boolean
    ignore_errors=kwargs.get("ignore_errors", False) or False,
    )

At the beginning of each play and each task, we "end" the previous objects:

def v2_playbook_on_play_start(self, play):
self.log.debug("v2_playbook_on_play_start")
self._end_task()
self._end_play()

def v2_playbook_on_task_start(self, task, is_conditional, handler=False):
self.log.debug("v2_playbook_on_task_start")
self._end_task()

I suspect that with the free strategy we can get into a situation where the callback ends a task (because it's starting a new one) and then another host later returns a result for a task that was already ended so it ends up being associated to another one.

Enabling multi-threading (with ex: ARA_CALLBACK_THREADS=4) would probably add fuel to the fire.

Maybe instead of storing/caching the tasks and plays in self.play and self.task, we could store the data in something like self.task[something] and then when we end the task, we end self.task[something] so that others results can be associated with self.task[somethingelse].

@dmsimard
Copy link
Contributor

dmsimard commented Apr 16, 2021

By the way, poking at the DB doesn't provide the best user experience since some fields are serialized by the API or compressed. You might want to browse around using the API browser instead like https://demo.recordsansible.org/api/v1/ for example -- it's available when you run ara-manage runserver under /api/v1.

@jhampson-dbre
Copy link
Contributor Author

jhampson-dbre commented Apr 16, 2021

A couple of thoughts:

  1. When we were looking at Exception with ara running in AWX  #211 , we saw there is a task uuid. Maybe this could be used to associate the same task that is started at a different time for a particular host. I image this would be similar to how play uuid is used.
  2. I think there would need to be an alternate way of ending a task. Ending a task when another task starts will be problematic, since different tasks could be started on different hosts in overlapping timeframes, causing one to be ended prematurely.
  3. I think the result object here:
    def _load_result(self, result, status, **kwargs):

    Contains a _task property. Maybe that could be used in combination to recording the task uuid to ensure the correct association is made.

@dmsimard
Copy link
Contributor

dmsimard commented Apr 16, 2021

When we were looking at #211 , we saw there is a task uuid. Maybe this could be used to associate the same task that is started at a different time for a particular host. I image this would be similar to how play uuid is used.

Yeah, maybe we could use the UUID as the identifier key in place of the [something] I suggested above for the local cache, like here it would be something like self.task[task._uuid]:

self.task = self.client.post(
"/api/v1/tasks",
name=task.get_name(),
status="running",
action=task.action,
play=self.play["id"],
playbook=self.playbook["id"],
file=task_file["id"],
tags=task.tags,
lineno=lineno,
handler=handler,
started=datetime.datetime.now(datetime.timezone.utc).isoformat(),
)

I'm not sure if we should store the UUID in the database like for plays but in any case I don't see us doing an API request for every task (or result) to retrieve the task belonging to a UUID, the performance overhead would be significant. We don't do a lookup for plays, it's really just for ara's action plugins so it's fine.

I think there would need to be an alternate way of ending a task. Ending a task when another task starts will be problematic, since different tasks could be started on different hosts in overlapping timeframes, causing one to be ended prematurely.

Agreed. The current approach works well for linear, probably need to revisit this to work well with free.

I think the result object here contains a _task property. Maybe that could be used in combination to recording the task uuid to ensure the correct association is made.

If the UUID is available there, that would be a way, yes.

@jhampson-dbre
Copy link
Contributor Author

jhampson-dbre commented Apr 16, 2021

I have a lightly-tested-but-seems-to-work fix for this. Surely it needs some work, but it might at least generate some better ideas :) Basically, I've added a _ara_tasks_by_uuid class attribute to CallbackModule class. This serves as a "global cache" of sorts.

When a new task is started

  1. check _ara_tasks_by_uuid to see if the uuid is exists in cache
  2. If it does, we return the task from cache
  3. Otherwise, send the post request to create the new task and add it to the _ara_tasks_by_uuid

This prevents multiple task id's from being generated for the same task uuid

When we go to store the result for a particular task/host combination in _load_result, we use task_uuid = result._task._uuid to look up the task id from _ara_tasks_by_uuid[task_uuid]["id"] and use that task id to post the result entry.

Now we have the correct task name associated with the result.

Are you taking PR's on GitHub yet? If so, I can open one with what I've got so far.

@dmsimard
Copy link
Contributor

dmsimard commented Apr 17, 2021

We've transitioned to GitHub pull requests a while ago but I haven't had the chance to update the contributors documentation yet. Feel free to open something and we can discuss it there.

@jhampson-dbre
Copy link
Contributor Author

jhampson-dbre commented Apr 20, 2021

PR #268 is open. Since my last post, I saw the existing host_cache and file_cache and changed the implementation to be similar to that.

@dmsimard
Copy link
Contributor

dmsimard commented Jul 23, 2021

PR 268 merged with a fix that will be released as part of 1.5.7.

Thanks for the issue and the contribution, much appreciated !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug help wanted plugins Related to the Ansible plugins
Projects
None yet
Development

No branches or pull requests

2 participants