How should an include playbook be different from a standalone one? - ansible

I have two playbooks which are launched against a remote host (10.233.84.58). When launched standalone (ansible-playbook -i inventory.txt playbook.yml) they work fine.
The first playbook includes the second one, which is identical, except obviously for the include:
---
- hosts: all
tasks:
- debug: msg="hello form playbook1"
# up to now the content is identical between playbook1.yaml and playbook2.yaml
# without the next line the playbook runs fine
- include: playbook2.yml
When I run playbook1.yml:
# ansible-playbook -i inventory.txt playbook1.yml (master✱)
PLAY [all] *********************************************************************
TASK [setup] *******************************************************************
ok: [10.233.84.58]
TASK [debug] *******************************************************************
ok: [10.233.84.58] => {
"msg": "hello form playbook1"
}
TASK [include] *****************************************************************
fatal: [10.233.84.58]: FAILED! => {"failed": true, "reason": "no action detected in task. This often indicates a misspelled module name, or incorrect module path.\n\nThe error appears to have been in '/root/tests/playbook2.yml': line 2, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n---\n- hosts: all\n ^ here\n\n\nThe error appears to have been in '/root/tests/playbook2.yml': line 2, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n---\n- hosts: all\n ^ here\n"}
NO MORE HOSTS LEFT *************************************************************
to retry, use: --limit #playbook1.retry
PLAY RECAP *********************************************************************
10.233.84.58 : ok=2 changed=0 unreachable=0 failed=1
I extracted the "reason" from the error message above and made it more readable:
no action detected in task. This often indicates a misspelled module name, or incorrect module path.
The error appears to have been in '/root/tests/playbook2.yml': line 2, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
---
- hosts: all
^ here
How should an include playbook be different from a standalone one?

There are two types of include. You can include playbook or list of tasks.
---
- hosts: all
tasks:
- include: list_of_tasks.yml
- include: complete_playbook.yml
In your example you try to include playbook2.yml as a list of tasks.
Move include on the same indent as - hosts and you'll be good.

Related

How to loop multiple tasks in Ansible? [duplicate]

I need to check if a file named deploy.db exists. If it does not exist, I need to perform a set of tasks for which I am using a block.
Below is how I run the playbook
ansible-playbook test.yml \
-e Layer=APP \
-e BASEPATH="/logs" \
-e Filenames="file1,file2,file3"
Here is the playbook test.yml:
---
- name: "Play 1"
hosts: localhost
gather_facts: false
tasks:
- name: Construct
debug:
msg: "Run"
- block:
- stat: path="{{ BASEPATH }}/deploy.db"
register: currdb
- file: path="{{ BASEPATH }}/deploy.db" state=touch recurse=no
when: currdb.stat.exists == False
- shell: "echo done>>{{ BASEPATH }}/deploy.db"
when: currdb.stat.exists == False
when: Layer == 'APP'
with_items:
- "{{ Filenames.split(',') }}"
I am getting the below error running the playbook:
ERROR! 'with_items' is not a valid attribute for a Block
The error appears to be in '/app/test.yml': line 9, column 6, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- block:
^ here
After researching a bit, I understand that neither with_items nor loop is supported by a block and the solution is to include a tasks file.
I am, however, not sure how to get that to work. Can you suggest what tweaks I need in order to make my playbook work?
Considering I am on the latest version of Ansible, are there other solutions?
TL;DR
'with_items' is not a valid attribute for a Block
The error message says it all: you cannot loop over a block.
If you need to loop over a set of tasks, put them in a separate file and use include_tasks
Implementation (and some good practice...)
Below is an implementation based on your example illustrating the solution.
Since your question and code lacks some precision and since I pointed out some bad practices, please note that:
I fixed the looped code to effectively use the filenames you loop on (I inferred it was supposed to the deploy.db file). Note the use of loop_control to disambiguate the variable name in the included file (i.e. db_filename).
I made the code idempotent as much as possible by using the ansible module copy in place of shell and dropped the touch phase.
I transformed the var names to all lowercase and underscore separator.
To make sure the copy task works on all occasion, I replaced the removed tasks with a single making sure the basepath dir exists.
I added a unique filter after filenames.split(',') as well as a trim filter on each value to remove possible duplicates and eventual spaces added by error in the coma separated list.
I used not keyword and bool filter (for extra security) rather than a bare compare to a boolean False value.
Here is the included file create_db_each.yml
---
- name: Check if file exists
stat:
path: "{{ basepath }}/{{ db_filename }}"
register: currdb
- name: Create the file with "done" line if not present
copy:
content: "done"
dest: "{{ basepath }}/{{ db_filename }}"
when: not currdb.stat.exists | bool
used in the following create_db.yml playbook
---
- name: "Create my dbs"
hosts: localhost
gather_facts: false
tasks:
- name: Make sure the base directory exists
file:
path: "{{ basepath }}"
state: directory
- name: load each db
include_tasks: "create_db_each.yml"
when: layer == 'APP'
loop: "{{ filenames.split(',') | unique | map('trim') }}"
loop_control:
loop_var: db_filename
which gives
notes:
first run only, run it again on your side to witness it reports OK everywhere
see the filenames parameter value to illustrate the use of unique and trim
$ ansible-playbook -e basepath=/tmp/my/base/path -e "filenames='a.bla, b.toto, c , z.txt,a.bla'" -e layer=APP create_db.yml
PLAY [Create my dbs] ************************************************
TASK [Make sure the base directory exists] **************************
changed: [localhost]
TASK [load each db] *************************************************
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=a.bla)
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=b.toto)
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=c)
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=z.txt)
TASK [Check if file exists] *****************************************
ok: [localhost]
TASK [Create the file with "done" line if not present] **************
changed: [localhost]
TASK [Check if file exists] *****************************************
ok: [localhost]
TASK [Create the file with "done" line if not present] **************
changed: [localhost]
TASK [Check if file exists] *****************************************
ok: [localhost]
TASK [Create the file with "done" line if not present] **************
changed: [localhost]
TASK [Check if file exists] *****************************************
ok: [localhost]
TASK [Create the file with "done" line if not present] **************
changed: [localhost]
PLAY RECAP **********************************************************
localhost: ok=13 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$ tree /tmp/my/base/path/
/tmp/my/base/path/
├── a.bla
├── b.toto
├── c
└── z.txt
$ for f in /tmp/my/base/path/*; do cat $f; echo; done
done
done
done
done

Search for a regular expression in a file in Ansible

I am trying to convert a small bash code snippet to Ansible but I am finding it hard to implement. Basically, it is first checking if /etc/redhat-release exists. If yes, it is looking for a regex pattern *release 6.8*. If the pattern is found, then it is checking for another file /bin/login.ori. If it exists, then it performs a couple of operations.
#fixed RHEL6.8/CentOS6.8 rlogin issue
if [ -f /etc/redhat-release ]; then
case `cat /etc/redhat-release` in
*'release 6.8'*)
if [ ! -e /bin/login.ori ]; then
cp -f
/bin/login /bin/login.ori
cp -f $MDIR/login.bin.68 /bin/login
restorecon /bin/login
fi
;;
esac
fi
Here is what I have tried so far:
- name: Fix RHEL6.8/CentOS6.8 rlogin issue
stat:
path: /etc/redhat-release
register: redhat_file
- debug:
msg: "File exists: {{ redhat_file }}"
when: redhat_file.stat.exists
- name: Check whether /etc/redhat-release contains "*release 6.8*"
lineinfile:
path: /etc/redhat-release
line: '*release 7.3*'
# insertafter: [main]
register: checkmyconf
when: redhat_file.stat.exists
- name: Greet the world if /etc/redhat-release contains "*release 6.8*"
debug:
msg: "{{ checkmyconf }}"
when: checkmyconf.stdout | match('*release 7.3.1611*')
But I am getting below error. Kindly help.
TASK [qsc/hack/v1 : Check whether /etc/redhat-release contains "*release 6.8*"] *******************************************************
ok: [ansible-poc-cos6]
ok: [ansible-poc-rhel6]
ok: [ansible-poc-centos7]
[DEPRECATION WARNING]: Using tests as filters is deprecated. Instead of using `result|match` use `result is match`. This feature will
be removed in version 2.9. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
TASK [qsc/hack/v1 : Greet the world if /etc/redhat-release contains "*release 6.8*"] **************************************************
fatal: [ansible-poc-cos6]: FAILED! => {"msg": "The conditional check 'checkmyconf.stdout | match('*release 7.3.1611*')' failed. The error was: nothing to repeat\n\nThe error appears to have been in '/remote/us01home53/subburat/snps-ansible/roles/qsc/hack/v1/tasks/main.yml': line 31, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Greet the world if /etc/redhat-release contains \"*release 6.8*\"\n ^ here\n"}
[DEPRECATION WARNING]: Using tests as filters is deprecated. Instead of using `result|match` use `result is match`. This feature will
be removed in version 2.9. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
fatal: [ansible-poc-rhel6]: FAILED! => {"msg": "The conditional check 'checkmyconf.stdout | match('*release 7.3.1611*')' failed. The error was: nothing to repeat\n\nThe error appears to have been in '/remote/us01home53/subburat/snps-ansible/roles/qsc/hack/v1/tasks/main.yml': line 31, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Greet the world if /etc/redhat-release contains \"*release 6.8*\"\n ^ here\n"}
[DEPRECATION WARNING]: Using tests as filters is deprecated. Instead of using `result|match` use `result is match`. This feature will
be removed in version 2.9. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
fatal: [ansible-poc-centos7]: FAILED! => {"msg": "The conditional check 'checkmyconf.stdout | match('*release 7.3.1611*')' failed. The error was: nothing to repeat\n\nThe error appears to have been in '/remote/us01home53/subburat/snps-ansible/roles/qsc/hack/v1/tasks/main.yml': line 31, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Greet the world if /etc/redhat-release contains \"*release 6.8*\"\n ^ here\n"}
to retry, use: --limit #/remote/us01home53/subburat/snps-ansible/push-full.retry
PLAY RECAP ****************************************************************************************************************************
ansible-poc-centos7 : ok=3 changed=0 unreachable=0 failed=1
ansible-poc-cos6 : ok=3 changed=0 unreachable=0 failed=1
ansible-poc-rhel6 : ok=3 changed=0 unreachable=0 failed=1
Note: I have tried all the suggestions in this link but it does not work for this use-case as the line attribute in my use-case is dynamic.
This is the equivalent
- stat:
path: /bin/login.ori
register: result
- block:
- copy:
src: /bin/login
dest: /bin/login.ori
remote_src: true
- copy:
src: "{{ ansible_env.MDIR }}/login.bin.68"
dest: /bin/login
remote_src: true
force: true
- command: restorecon /bin/login
when:
- ansible_distribution == 'Red Hat Enterprise Linux'
- ansible_distribution_version == '6.8'
- not result.stat.exists|bool
(not tested)
Notes
gather_facts must be enabled to collect ansible_* variables.
It's not necessary to "force" the first copy because the "dest" does not exist.
I'm not sure if the requirement to fix both "RHEL6.8/CentOS6.8" and testing the existence of "/etc/redhat-release" only is consistent (I have no access to 6.8 atm). Fit the conditions in the block to your needs.

Is a correct YAML file enough for a correct ansible playbook, syntax while executing a role?

ERROR!
"unexpected parameter type in action: class 'ansible.parsing.yaml.objects.AnsibleSequence'"
The error appears to be in '/home/ansible/march/roles/apache/tasks/apt.yml': line 2, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
---
- hosts: all
^ here
The playbook(apt.yml) which contains:
---
- hosts: all
become: yes
tasks:
- name: uninstall git
apt:
name: git
state: absent
- name: update
apt:
update-cache: yes
Q: "The error appears to be in /home/ansible/march/roles/apache/tasks/apt.yml': line 2, ..."
A: The directives hosts and tasks are valid keywords in a playbook but not in a role.
- hosts: all
become: yes
tasks:
Q: "Is a correct YAML file enough for a correct Ansible playbook ...?"
A: It is not, obviously. The playbook presented in the question is syntactically correct. ansible-lint apt.yml, yamllint apt.yml, and ansible-playbook apt.yml --syntax-check report no errors.
The playbook was tested with the inventory
shell> cat hosts
linux:
hosts:
test_01:
ansible_host: 10.1.0.11
The playbook works as expected
shell> ansible-playbook apt.yml -C
PLAY [all] ***
TASK [Gathering Facts] ***
ok: [test_01]
TASK [uninstall git] ***
changed: [test_01]
TASK [update] ***
changed: [test_01]
PLAY RECAP ***
test_01: ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Issue looping on block containing a set of tasks in Ansible

I need to check if a file named deploy.db exists. If it does not exist, I need to perform a set of tasks for which I am using a block.
Below is how I run the playbook
ansible-playbook test.yml \
-e Layer=APP \
-e BASEPATH="/logs" \
-e Filenames="file1,file2,file3"
Here is the playbook test.yml:
---
- name: "Play 1"
hosts: localhost
gather_facts: false
tasks:
- name: Construct
debug:
msg: "Run"
- block:
- stat: path="{{ BASEPATH }}/deploy.db"
register: currdb
- file: path="{{ BASEPATH }}/deploy.db" state=touch recurse=no
when: currdb.stat.exists == False
- shell: "echo done>>{{ BASEPATH }}/deploy.db"
when: currdb.stat.exists == False
when: Layer == 'APP'
with_items:
- "{{ Filenames.split(',') }}"
I am getting the below error running the playbook:
ERROR! 'with_items' is not a valid attribute for a Block
The error appears to be in '/app/test.yml': line 9, column 6, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- block:
^ here
After researching a bit, I understand that neither with_items nor loop is supported by a block and the solution is to include a tasks file.
I am, however, not sure how to get that to work. Can you suggest what tweaks I need in order to make my playbook work?
Considering I am on the latest version of Ansible, are there other solutions?
TL;DR
'with_items' is not a valid attribute for a Block
The error message says it all: you cannot loop over a block.
If you need to loop over a set of tasks, put them in a separate file and use include_tasks
Implementation (and some good practice...)
Below is an implementation based on your example illustrating the solution.
Since your question and code lacks some precision and since I pointed out some bad practices, please note that:
I fixed the looped code to effectively use the filenames you loop on (I inferred it was supposed to the deploy.db file). Note the use of loop_control to disambiguate the variable name in the included file (i.e. db_filename).
I made the code idempotent as much as possible by using the ansible module copy in place of shell and dropped the touch phase.
I transformed the var names to all lowercase and underscore separator.
To make sure the copy task works on all occasion, I replaced the removed tasks with a single making sure the basepath dir exists.
I added a unique filter after filenames.split(',') as well as a trim filter on each value to remove possible duplicates and eventual spaces added by error in the coma separated list.
I used not keyword and bool filter (for extra security) rather than a bare compare to a boolean False value.
Here is the included file create_db_each.yml
---
- name: Check if file exists
stat:
path: "{{ basepath }}/{{ db_filename }}"
register: currdb
- name: Create the file with "done" line if not present
copy:
content: "done"
dest: "{{ basepath }}/{{ db_filename }}"
when: not currdb.stat.exists | bool
used in the following create_db.yml playbook
---
- name: "Create my dbs"
hosts: localhost
gather_facts: false
tasks:
- name: Make sure the base directory exists
file:
path: "{{ basepath }}"
state: directory
- name: load each db
include_tasks: "create_db_each.yml"
when: layer == 'APP'
loop: "{{ filenames.split(',') | unique | map('trim') }}"
loop_control:
loop_var: db_filename
which gives
notes:
first run only, run it again on your side to witness it reports OK everywhere
see the filenames parameter value to illustrate the use of unique and trim
$ ansible-playbook -e basepath=/tmp/my/base/path -e "filenames='a.bla, b.toto, c , z.txt,a.bla'" -e layer=APP create_db.yml
PLAY [Create my dbs] ************************************************
TASK [Make sure the base directory exists] **************************
changed: [localhost]
TASK [load each db] *************************************************
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=a.bla)
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=b.toto)
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=c)
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=z.txt)
TASK [Check if file exists] *****************************************
ok: [localhost]
TASK [Create the file with "done" line if not present] **************
changed: [localhost]
TASK [Check if file exists] *****************************************
ok: [localhost]
TASK [Create the file with "done" line if not present] **************
changed: [localhost]
TASK [Check if file exists] *****************************************
ok: [localhost]
TASK [Create the file with "done" line if not present] **************
changed: [localhost]
TASK [Check if file exists] *****************************************
ok: [localhost]
TASK [Create the file with "done" line if not present] **************
changed: [localhost]
PLAY RECAP **********************************************************
localhost: ok=13 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$ tree /tmp/my/base/path/
/tmp/my/base/path/
├── a.bla
├── b.toto
├── c
└── z.txt
$ for f in /tmp/my/base/path/*; do cat $f; echo; done
done
done
done
done

Unable to search output of Cisco show command using Ansible

Running Ansible 2.7.4
I have the following code:
- hosts: switches
tasks:
- name: show run on remote devices
ios_command:
commands: show run
register: output
- name: Display The Results
debug:
msg: "Enable Secret Found"
when: ('secret' in output.stdout)
I want to read in the output of a show run command on a Cisco switch and then search that output for specific phrases in the output.
If a match is found I want a message to be displayed to the screen, however no match is ever found.
PLAY [switches] *****************************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************************************************************************************************************************
ok: [10.10.2.68]
TASK [show run on remote devices] ***********************************************************************************************************************************************************************************************************
ok: [10.10.2.68]
TASK [Display The Results] ******************************************************************************************************************************************************************************************************************
skipping: [10.10.2.68]
PLAY RECAP **********************************************************************************************************************************************************************************************************************************
10.10.2.68 : ok=2 changed=0 unreachable=0 failed=0
I have also tried altering the when statement to
when: output.stdout.find('enable') != -1
but this gives me the following error:
fatal: [10.10.2.68]: FAILED! => {"msg": "The conditional check 'output.stdout.find('enable') != -1' failed. The error was: error while evaluating conditional (output.stdout.find('enable') != -1): 'list object' has no attribute 'find'\n\nThe error appears to have been in '/etc/ansible/playbooks/showrun2.yml': line 8, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Display The Results\n ^ here\n"}
Any idea what I'm doing wrong?
'list object' has no attribute 'find'
I'm not sure how much clearer you would expect the error message to be, but in that circumstance output.stdout is a list, not a str
So you'll want
"enable" in (output.stdout | join("\n"))
or the strictly pythonic way
"enable" in "\n".join(output.stdout)

Resources