Test value of all members in hash variable in Ansible playbook - ansible

I need to run a task only if one or more files from a pre-defined list of files are missing. I tried the following (and some variants):
touch a
cat test.yml
- hosts: localhost
vars:
filelist:
- a
- b
tasks:
- stat:
path: "{{ item }}"
with_items: "{{ filelist }}"
register: all_files
- debug:
var: all_files
- debug:
msg: "Some file(s) missing"
when: "false in all_files['results'][*]['stat']['exists']"
ansible-playbook test.yml
...
TASK [debug] ********************************************************************
ok: [localhost] => {
"all_files": {
...
"item": "a",
"stat": {
...
"exists": true,
...
"item": "b",
"stat": {
"exists": false
...
TASK [debug] ********************************************************************
fatal: [localhost]: FAILED! => {"failed": true, "msg": "The conditional check 'false in all_files['results'][*]['stat']['exists']' failed. The error was: template error while templating string: unexpected '*'. String: {% if false in all_files['results'][*]['stat']['exists'] %} True {% else %} False {% endif %}\n\nThe error appears to have been in 'test.yml': line 16, 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 - debug:\n ^ here\n"}
...
What is the correct syntax to use in the 'when:' clause? Or is this the wrong way altogether?

- hosts: localhost
gather_facts: false
vars:
file_vars:
- {name: file1}
- {name: file2}
tasks:
- name: Checking existing file name
stat:
path: ./{{ item.name }}
with_items: "{{ file_vars }}"
register: check_file_name
- debug:
msg: 'file name {{item.item.name}} not exists'
with_items: "{{ check_file_name.results }}"
when: item.stat.exists == False
- name: Create file
file:
path: "./{{item.item.name}}"
state: touch
with_items: "{{ check_file_name.results }}"
when: item.stat.exists == False

Related

Ansible Executing Nested Variable Loops on Multiple Files

I want to perform the following command using an Ansible playbook:
sed -i.bak 's/^mirror/#mirror/;s/#baseurl/baseurl/;s/mirror.centos/vault.centos/' /etc/yum.repos.d/CentOS-*.repo
A basic working Ansible playbook looks like this:
---
- name: Resolve any CentOS issues
hosts: "{{ nodes }}"
tasks:
- shell: sed -i.bak 's/^mirror/#mirror/;s/#baseurl/baseurl/;s/mirror.centos/vault.centos/' /opt/yum.repos.d/CentOS-*.repo
However, I would like to create a more advanced playbook, which *might *look something like this:
---
- name: Modify CentOS Reps
hosts: localhost
vars:
filelist: []
reg:
- ^mirrorlist
- ^#baseurl
- mirror.centos
rep:
- #mirror
- baseurl
- vault.centos
tasks:
- name: Find matching repo files and register to a variable
find:
paths: /etc/yum.repos.d
file_type: file
recurse: no
patterns: CentOS-*.repo
register: output
- name: Adding filelist to the LIST
no_log: true
set_fact:
filelist: "{{ filelist + [item.path]}}"
with_items: "{{ output.files }}"
- debug: var=filelist
- name: Change the lines
replace:
path: "{{ item.0 }}"
regexp: "{{ item.1.regexp }}"
replace: "{{ item.1.replace }}"
backup: true
with_nested:
- "{{ filelist }}"
- '{ regexp: "{{ reg }}", replace: "{{ rep }}" }'
The above gives the following attribute error:
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'regexp'\n\nThe error appears to be in '/etc/ansible/playbooks/centos-correct-repo301.yml': line 33, column 6, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Change the lines\n ^ here\n"}
Any thoughts?
Thanks
There are more options. For example,
Put the regex/replace logic into a dictionary
reg_rep:
- {reg: '^mirror', rep: '#mirror'}
- {reg: '^#baseurl', rep: 'baseurl'}
- {reg: 'mirror.centos', rep: 'vault.centos'}
Declare the list of the files
filelist: "{{ output.files|map(attribute='path')|list }}"
and find the files (no changes to your code in this task)
- name: Find matching repo files and register to a variable
find:
paths: /etc/yum.repos.d
file_type: file
recurse: no
patterns: CentOS-*.repo
register: output
Update the configuration
- name: Change the lines
replace:
path: "{{ item.0 }}"
regexp: "{{ item.1.reg }}"
replace: "{{ item.1.rep }}"
backup: true
with_nested:
- "{{ filelist }}"
- "{{ reg_rep }}"
loop_control:
label: "{{ item.0|basename }}"
Example of a complete playbook for testing
shell> cat pb.yml
- hosts: test_24
vars:
reg_rep:
- {reg: '^mirror', rep: '#mirror'}
- {reg: '^#baseurl', rep: 'baseurl'}
- {reg: 'mirror.centos', rep: 'vault.centos'}
filelist: "{{ output.files|map(attribute='path')|list }}"
tasks:
- name: Find matching repo files and register to a variable
find:
paths: /etc/yum.repos.d
file_type: file
recurse: no
patterns: CentOS-*.repo
register: output
- debug:
var: filelist
- debug:
msg: |
path: "{{ item.0 }}"
regexp: "{{ item.1.reg }}"
replace: "{{ item.1.rep }}"
backup: true
with_nested:
- "{{ filelist }}"
- "{{ reg_rep }}"
loop_control:
label: "{{ item.0|basename }}"
when: debug|d(false)|bool
- name: Change the lines
replace:
path: "{{ item.0 }}"
regexp: "{{ item.1.reg }}"
replace: "{{ item.1.rep }}"
backup: true
with_nested:
- "{{ filelist }}"
- "{{ reg_rep }}"
loop_control:
label: "{{ item.0|basename }}"
gives abridged, running in --check --diff mode
shell> ansible-playbook pb.yml -CD
PLAY [test_24] *******************************************************************************
TASK [Find matching repo files and register to a variable] ***********************************
ok: [test_24]
TASK [debug] *********************************************************************************
ok: [test_24] =>
filelist:
- /etc/yum.repos.d/CentOS-Linux-ContinuousRelease.repo
- /etc/yum.repos.d/CentOS-Linux-Debuginfo.repo
- /etc/yum.repos.d/CentOS-Linux-Devel.repo
- /etc/yum.repos.d/CentOS-Linux-Extras.repo
- /etc/yum.repos.d/CentOS-Linux-FastTrack.repo
- /etc/yum.repos.d/CentOS-Linux-HighAvailability.repo
- /etc/yum.repos.d/CentOS-Linux-Media.repo
- /etc/yum.repos.d/CentOS-Linux-Plus.repo
- /etc/yum.repos.d/CentOS-Linux-PowerTools.repo
- /etc/yum.repos.d/CentOS-Linux-Sources.repo
- /etc/yum.repos.d/CentOS-Linux-BaseOS.repo
- /etc/yum.repos.d/CentOS-Linux-AppStream.repo
TASK [debug] *********************************************************************************
skipping: [test_24] => (item=CentOS-Linux-ContinuousRelease.repo)
skipping: [test_24] => (item=CentOS-Linux-ContinuousRelease.repo)
...
skipping: [test_24] => (item=CentOS-Linux-AppStream.repo)
skipping: [test_24]
TASK [Change the lines] **********************************************************************
--- before: /etc/yum.repos.d/CentOS-Linux-ContinuousRelease.repo
+++ after: /etc/yum.repos.d/CentOS-Linux-ContinuousRelease.repo
## -17,7 +17,7 ##
[cr]
name=CentOS Linux $releasever - ContinuousRelease
-mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=cr&infra=$infra
+#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=cr&infra=$infra
#baseurl=http://mirror.centos.org/$contentdir/$releasever/cr/$basearch/os/
gpgcheck=1
enabled=0
changed: [test_24] => (item=CentOS-Linux-ContinuousRelease.repo)
--- before: /etc/yum.repos.d/CentOS-Linux-ContinuousRelease.repo
+++ after: /etc/yum.repos.d/CentOS-Linux-ContinuousRelease.repo
## -18,7 +18,7 ##
[cr]
name=CentOS Linux $releasever - ContinuousRelease
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=cr&infra=$infra
-#baseurl=http://mirror.centos.org/$contentdir/$releasever/cr/$basearch/os/
+baseurl=http://mirror.centos.org/$contentdir/$releasever/cr/$basearch/os/
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
changed: [test_24] => (item=CentOS-Linux-ContinuousRelease.repo)
...
--- before: /etc/yum.repos.d/CentOS-Linux-AppStream.repo
+++ after: /etc/yum.repos.d/CentOS-Linux-AppStream.repo
## -11,7 +11,7 ##
[appstream]
name=CentOS Linux $releasever - AppStream
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=AppStream&infra=$infra
-#baseurl=http://mirror.centos.org/$contentdir/$releasever/AppStream/$basearch/os/
+#baseurl=http://vault.centos.org/$contentdir/$releasever/AppStream/$basearch/os/
baseurl=http://vault.centos.org/$contentdir/$releasever/AppStream/$basearch/os/
gpgcheck=1
enabled=0
changed: [test_24] => (item=CentOS-Linux-AppStream.repo)
PLAY RECAP ***********************************************************************************
test_24: ok=3 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0

Conditional set_fact doesn't evaluate

I'm looking to set a variable based on the outcome of the following evaluation: ({{ found_files.files|length > 0 }}), however, it doesn't appear to be evaluated fully. What could be the issue?
Desired outcome:found_files2: /dev/null
Outcome:
TASK [debug] ********************************************************************************************************************************************************************************
ok: [localhost] => {
"found_files2": "{'files': [], 'changed': False, 'msg': '', 'matched': 0, 'examined': 0, 'failed': False} if (False) else '/dev/null'"
}
Playbook:
---
- hosts: localhost
gather_facts: no
vars:
backup_dir: /home/user1/projects/ansible/modules/backups/router2
tasks:
- name: get files in backups/<router name>/
delegate_to: localhost
find:
paths: "{{ backup_dir }}"
register: found_files
- set_fact:
found_files2: "{{ found_files }} if ({{ found_files.files|length > 0 }}) else '/dev/null'"
- debug: var=found_files2
The correct syntax is below
- set_fact:
found_files2: "{{ found_files if (found_files.files|length > 0) else '/dev/null' }}"
The filter ternary gives the same result
- set_fact:
found_files2: "{{ (found_files.files|length > 0)|ternary(found_files, '/dev/null') }}"

Setting become_user conditionally

I am trying to shutdown databases in a loop, the catch is some databases run as a different user and others just run as oracle. I login as oracle user and run the playbook and if the database is run as oracle user it goes through fine. If it is running as a different user I would like to become that user (oracle user has permissions to do that).
Here is my main playbook:
[oracle#ansctrlsrv.localdomain epd3]$ cat test.yml
---
- hosts: testdrive
tasks:
- set_fact:
db_list: "{{ lookup('file', 'vars/' ~ inventory_hostname ~ '.dblist')|from_yaml }}"
- name: Shutdown running databases
include_tasks: shutdowndb.yml
loop: "{{ db_list }}"
DB list is as follows:
[oracle#ansctrlsrv.localdomain epd3]$ cat vars/dbsrv.localdomain.dblist
- ebs1
- ebs2
- ndb1
[oracle#ansctrlsrv.localdomain epd3]$ cat shutdowndb.yml
---
- debug: msg='Shutting down {{ item }}'
- name: Execute shutdown
shell: id "{{ item }}"
register: shutdown_output
become: "{{ item is search('ebs') | ternary('yes','no') }}"
become_user: "{{ item }}"
- debug: msg="{{ shutdown_output.stdout }}"
[oracle#ansctrlsrv.localdomain epd3]$ cat inventory
[testdrive]
dbsrv.localdomain
[oracle#ansctrlsrv.localdomain epd3]$ ansible-playbook -i inventory test.yml
TASK [Execute shutdown] ***
fatal: [dbsrv1.localdomain]: FAILED! => {"changed": false, "module_stderr": "Shared connection to dbsrv1.localdomain closed.\r\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1} ...ignoring
I tried this question on another thread but got closed, so trying another hand after realizing a few issues myself such as being unable to run blocks of code with a loop, etc.
Appreciate any help.
Set become user conditionally
Fix the line
shell: id "{{ item }}"
correct syntax (use shell only when necessary)
command: "id {{ item }}"
The playbook below
- hosts: testdrive
tasks:
- set_fact:
db_list: "{{ lookup('file', 'vars/' ~ inventory_hostname ~ '.dblist')|from_yaml }}"
- name: Shutdown running databases
include_tasks: shutdowndb.yml
loop: "{{ db_list }}"
with the included tasks
$ cat shutdowndb.yml
- debug:
msg:
- "shell: {{ 'shutdown.sh ' ~ item }}"
- "become: {{ item is search('ebs')|ternary('yes', 'no') }}"
- "become_user: {{ item }}"
give
"msg": [
"shell: shutdown.sh ebs1",
"become: yes",
"become_user: ebs1"
]
"msg": [
"shell: shutdown.sh ebs2",
"become: yes",
"become_user: ebs2"
]
"msg": [
"shell: shutdown.sh ndb1",
"become: no",
"become_user: ndb1"
]
Q: "Why the command whoami is still giving oracle rather than ebs1?"
A: Short answer: Because become is not set to True.
Debugging
1) Is it possible to become all of the users in db_list? Yes.
- hosts: test_01
become: no
remote_user: admin
vars:
db_list: ['ebs1', 'ebs2', 'ndb1']
tasks:
- command: whoami
become_user: "{{ item }}"
become: true
register: result
loop: "{{ db_list }}"
- debug:
msg: "{{ result.results|json_query('[].stdout') }}"
give
"msg": [
"ebs1",
"ebs2",
"ndb1"
]
2) Does search and ternary work properly? Yes.
- debug:
msg: "{{ item is search('ebs')|ternary(true, false) }}"
loop: "{{ db_list }}"
gives
"msg": true
"msg": true
"msg": false
3) Does become and become_user work properly?. Yes.
- command: whoami
become_user: "{{ item }}"
become: "{{ item is search('ebs')|ternary(true, false) }}"
register: result
loop: "{{ db_list }}"
- debug:
msg: "{{ result.results|json_query('[].stdout') }}"
give
"msg": [
"ebs1",
"ebs2",
"admin"
]

How do I test in a var matches against a list of substrings in ansible?

I am fairly new to ansible and I am trying to determine how to test is a variable passed to my playbook matches against a list of substrings.
I have tried something like the following. Looping through my list of badcmds and then testing whether it is in the variable passed.
vars:
badcmds:
- clear
- no
tasks:
- name: validate input
debug:
msg: " {{ item }}"
when: item in my_command
with_items: "{{ badcmds }}"
I am getting the following error:
"msg": "The conditional check 'item in my_command' failed.
The error was: Unexpected templating type error occurred on
({% if item in my_command %} True {% else %} False {% endif %}):
coercing to Unicode: need string or buffer, bool found
Many thanks.
one problem with your playbook is that - no is automatically translated to boolean false. you should use "no" to make Ansible consider the variable as a string. without quotes:
---
- hosts: localhost
connection: local
gather_facts: false
vars:
badcmds:
- clear
- no
my_command: clear
tasks:
- name: print variable
debug:
msg: "{{ item }}"
with_items:
- "{{ badcmds }}"
output:
TASK [print variable] ***********************************************************************************************************************************************************************************************
ok: [localhost] => (item=None) => {
"msg": "clear"
}
ok: [localhost] => (item=None) => {
"msg": false
}
I guess you should enclose no in quotes, because this behavior was not your intention.
to make a loop and check if the variable matches any item from the badcmds list, you can use:
---
- hosts: localhost
connection: local
gather_facts: false
vars:
badcmds:
- "clear"
- "no"
tasks:
- name: validate input
debug:
msg: "{{ item }}"
when: item == my_command
with_items:
- "{{ badcmds }}"
hope it helps

Ansible v2.2 error when using with_subelements which worked fine in 1.7

I am getting the following error while running my ansible playbook.
TASK [base : Rsyslog yapilandiriliyor.]
**************************************** fatal: [gkts.ahtapot]: FAILED! => {"failed": true, "msg": "The conditional check
'ansible_fqdn == {{item.1}}' failed. The error was: error while
evaluating conditional (ansible_fqdn == {{item.1}}): 'item' is
undefined\n\nThe error appears to have been in
'/etc/ansible/roles/base/tasks/rsyslog.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- name: Rsyslog
yapilandiriliyor.\n ^ here\n"}
This worked fine in ansible 1.7 but doesn't work in ansible 2.2.1
- name: Rsyslog yapilandiriliyor.
template:
src: "{{ rsyslog['conf']['source'] }}"
dest: "{{ rsyslog['conf']['destination'] }}"
owner: "{{ rsyslog['conf']['owner'] }}"
group: "{{ rsyslog['conf']['group'] }}"
mode: "{{ rsyslog['conf']['mode'] }}"
# when: "ansible_fqdn == item.1"
when: "ansible_fqdn == {{item.1}}"
with_subelements:
- "{{ossimciks}}"
- "{{clients}}"
notify:
- rsyslog servisini yeniden baslat
sudo: yes
tags: rsyslog
ossimciks and clients are defined in my vars file:
ossimciks:
server01:
fqdn: "OSSIMCIK_FQDN"
port: "20514"
clients:
- "LOG_KAYNAGI_FQDN"
- "LOG_KAYNAGI_FQDN"
What am I missing?
with_subelements:
- "{{ossimciks}}"
- "{{clients}}"
Syntax
I don't know how where/how/when this might have changed in Ansible, but I think that
the first element in with_subelements is a variable, and
the second element in with_subelements is a key.
This works in playbooks I've written, and matches the relevant docs (since 2.5, there are no with_* loops [1] in the docs since they were always lookups anyhow [2]):
with_subelements:
- "{{ ossimciks }}"
- clients
Data
It's not clear to me which item you're attempting to target with {{ item.1 }} in your playbook, but if making a minor change to your data is permissible, you can structure it so that any of the information shown is accessible within a with_subelements loop (I've also expanded and restructured the data a bit to clarify what the loop is doing):
---
- hosts: localhost
gather_facts: false
vars:
fqdn_var: "OSSIMCIK_FQDN_2"
ossimciks:
- server: "server01"
fqdn: "OSSIMCIK_FQDN_1"
port: "20514"
clients:
- "LOG_KAYNAGI_FQDN_1"
- "LOG_KAYNAGI_FQDN_2"
- server: "server02"
fqdn: "OSSIMCIK_FQDN_2"
port: "20514"
clients:
- "LOG_KAYNAGI_FQDN_1"
- "LOG_KAYNAGI_FQDN_2"
tasks:
- name: Output ossimciks contents.
debug:
msg: "{{ item.0.server }} client: {{ item.1 }}"
with_subelements:
- "{{ ossimciks }}"
- clients
when: item.0.fqdn == fqdn_var
This outputs:
skipping: [localhost] => (item=None) => {"skip_reason": "Conditional result was False"}
skipping: [localhost] => (item=None) => {"skip_reason": "Conditional result was False"}
ok: [localhost] => (item=None) => {
"msg": "server02 client: LOG_KAYNAGI_FQDN_1"
}
ok: [localhost] => (item=None) => {
"msg": "server02 client: LOG_KAYNAGI_FQDN_2"
}
[1] http://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html
[2] Loops are actually a combination of things with_ + lookup(), so any lookup plugin can be used as a source for a loop, ‘items’ is lookup.
Edited to add: the playbooks where I've used the syntax above were definitely in the 2.x release series--I think ~2.1.

Resources