Ansible to run the tasks in sequence

Hi All,

I am writing to build a code with Ansible to run specify tasks on one hosts and another specify tasks on another host in a sequence manner. Here is the use-case:

Role1-- > needs to be executed on first host which I request
Role 2–> rest of other hosts

Here is the condition:
My first hosts and it’s role must be executed everytime before role2 and other hosts starts its execution

Example:
Role1(host1) → role2(host2)
Role1(host1) → role2( host3)

Except for host1 all other hosts must be run in sequence not in parallel

Note: cannot use commands like delegate_to or pre-tasks or hosts: localhost or serial:1

Could you elaborate on why you can’t use these options, so we can better understand the limitations you’re working with?

2 Likes

Hi lyle,
The reason we cannot use these commands is that these are been restricted for usage in our organisation so if we try to use them the play building jenkins gets failed for us.

It’s still a bit unclear to me. Does a Jenkins job vet your playbooks? Or does it assemble plays for you with these restrictions? Or something else? I’m trying to understand how Jenkins fits into your workflow.

In any case, disallowing serial: 1 and delegate_to seems like rather harsh restrictions.

Are you allowed to create your own Jenkins jobs? If so, you could move the host selection and serialization up into Jenkins which could fire off ansible-playbook invocations in a loop to satisfy your requirements. But I suspect if you can’t use serial: then you probably aren’t allowed to customize Jenkins jobs either.

How up-to-date is your résumé? (I’m not sure if that’s a joke. Sorry.)

1 Like

Hi,
Yes jenkins vet the playbooks,
Though for serial I won’t get a direct restriction but will get a warning to change. I can leave that as it is too. But if use serial : 1 then hosts are picked in serial manner like host1, host2 and host 3 and so on but not as host1, host2 then again host 1 then host3 to do the job

I know that it’s not strange that we cannot use the mentioned restricted commands but cannot help in my case. It’s strictly not allowed atleast for delegate to, hosts: localhost and pre-tasks - it fails my jenkins build
Serial – I get a warning in build

And to answer the creating own jenkins jobs? – it’s a no, we are not allowed these are already configured jenkins which we use from many years

Thanks everyone for trying to understand my use case.
But I cracked the logic using it

Here is the code:

  • name: Test the list of tasks
    hosts: all
    gather_facts: false
    vars:
    hostlist: “{{hostlist.split(‘,’) }}”
    tasks:
    - name: testing the use case
    include_role:
    name: test-usecase
    loop: “{{ range(1, hostlist | length | int) list}}”
    loop_control:
    loop_var: index
    when: inventory_hostname in [hostlist[0], hostlist[index]]

Role: role/test-usecase/tasks/main.yml

  • name: test the case for host1
    block:
    - name: testing on host1
    shell: hostname
    when: inventory_ hostname == hostlist[0]

  • name: test the case for host2
    block:
    - name: testing on host1
    shell: hostname
    when: inventory_ hostname != hostlist[0]

Your org is ran by insane people. Good luck

1 Like

May I suggest a few tweaks? First off is this variable initialization:

vars:
  hostlist: "{{ hostlist.split(',') }}"

If you’re passing in hostlist as an --extra-vars, this isn’t going to work.
It looks like you’re redefining the variable hostlist in terms of itself, but it doesn’t do that. Instead, it defines a new Play var (precedence 12) which has the same name as an Extra var (precedence 22). That doesn’t make the Extra var go away. Since Extra vars have the highest precedence, the identically named Play var’s value will never be seen. (It may “work” for you in older Ansibles, but it behaves as I’ve described above in my Ansible core 2.18.6.)

I recommend a different Extra var name, say hostlist_in, so your variable initialization becomes
hostlist: "{{ hostlist_in.split(',') }}"

This next one is personal preference, but I like to avoid creating index variables when filters can manipulate the data directly into the form I need. In this case, you want to create pairs of host names where the first or left host in each pair is always hostlist[0] and the second or right host is in turn each of the other hostlist hosts. The product filter will do exactly that.
loop: "{{ [hostlist[0]] | product(hostlist[1:]) }}"

Lastly, when posting code (or editing a prior post containing code - hint, hint) to the forum, precede and follow the code with lines containing only three back-ticks (“```”). You can include a syntax highlighting hint on the first line, like this:

```yaml
- name: Code never looked so good
  ansible.builtin.admire: |
    Hey, I can read this easily!
```

With the above changes, here’s my re-working of your test/example. Note that both hosts: and the Play var hostlist are derived from the same Extra var hostlist_in. It would be neat if you could use the ansible_play_hosts special variable which will contain the same hosts as your limit: and hostlist_in; then you wouldn’t have to pass in the hosts twice. Alas, ansible_play_hosts is a sorted list, and we must distinguish the first host from the others. (Which we could do, but that adds more code which kind of defeats the purpose.)

# test-playbook.yml
- name: Test the list of tasks
  hosts: "{{ hostlist_in | split(',') }}"
  gather_facts: false
  vars:
    hostlist: "{{ hostlist_in.split(',') }}"
  tasks:
    - name: Testing the use case
      ansible.builtin.include_role:
        name: test-usecase
      loop: "{{ [hostlist[0]] | product(hostlist[1:]) }}"
      loop_control:
        loop_var: hostpair
---
# role/test-usecase/tasks/main.yml
- name: Document this role inclusion
  ansible.builtin.debug:
    msg: "test-usecase -- primary: {{ hostpair[0] }}, secondary: {{ hostpair[1] }}"
  run_once: true

- name: Test block for primary host {{ hostpair[0] }}
  block:
    - name: Primary is {{ hostpair[0] }}
      ansible.builtin.shell: hostname
  when: inventory_hostname == hostpair[0]

- name: Test block for secondary host {{ hostpair[1] }}
  block:
    - name: Secondary is {{ hostpair[1] }}
      ansible.builtin.shell: hostname
  when: inventory_hostname == hostpair[1]

Hi,

I will definitely give a try and let you know on result. Thank you, for giving an alternative for my conditions specified.

FWIW, a simple script does the job

#!/usr/bin/bash

IFS=',' read -r -a hosts <<< "$1"

ansible-playbook pb1.yml -l "${hosts[0]}"

for host in "${hosts[@]:1}"; do
    ansible-playbook pb2.yml -l "$host"
done

For example,

shell> ./project.bash "test_03,test_01,test_02"

PLAY [all] **********************************************************************************************************

TASK [role1 : debug] ************************************************************************************************
ok: [test_03] => 
    msg: 'role1: test_03'

PLAY RECAP **********************************************************************************************************
test_03                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   


PLAY [all] **********************************************************************************************************

TASK [role2 : debug] ************************************************************************************************
ok: [test_01] => 
    msg: 'role2: test_01'

PLAY RECAP **********************************************************************************************************
test_01                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   


PLAY [all] **********************************************************************************************************

TASK [role2 : debug] ************************************************************************************************
ok: [test_02] => 
    msg: 'role2: test_02'

PLAY RECAP **********************************************************************************************************
test_02                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0