Ansible: no filter named 'selectattr' - ansible

I'm taking a foray into the world of Ansible. Right now, I have a task where I am trying to say:
If any of a list of files don't exist, then run this task
To accomplish this, I have this piece of code right here.
- name: Check if certificate already exists.
stat:
path: /etc/letsencrypt/live/{{ item }}/cert.pem
with_items: "{{ cert_domains }}"
register: letsencrypt_cert
- name: Create the certs
raw: certbot --nginx --noninteractive -d "{{ cert_domains.0 }}" -d "{{ cert_domains.1 }}"
register: certbot_create
changed_when: certbot_create.stdout
when:
- "{{ letsencrypt_cert.results|selectattr('stat','false') | length > 0 }}"
However, when I run my process it greets me with the following error:
"msg": "The conditional check '{{
letsencrypt_cert.results|selectattr('stat','false') | length > 1 }}' failed.
The error was: template error while templating string: no filter named
'selectattr'. String: {{ letsencrypt_cert.results|selectattr('stat','false')
| length > 0 }}\
In something like LINQ, i would do something like this:
list.where( n => n.equals(false)).Count() > 0
What could be going wrong?
For the sake of thoroughness, here are my versions:
ansible 2.4.2.0
python version = 2.6.9
Name: Jinja2 Version: 2.7.2
Any help on this is greatly appreciated.

I had installed Jinja2 using 'pip install jinja2'. What I needed to do was
'sudo yum install python-jinja2'
to get things working.

Try this:
- name: Create the certs
raw: certbot --nginx --noninteractive -d "{{ cert_domains.0 }}" -d "{{ cert_domains.1 }}"
register: certbot_create
changed_when: certbot_create.stdout
when:
- letsencrypt_cert.results | selectattr('stat', 'defined') | map(attribute='stat') | selectattr('exists', 'equalto', false) | list | length > 0

Related

How to Print the Version of an updated Package?

I'm just trying to get a notification from my weekly Update Ansible Task.
The Idea is to get a notification when a specific repository got an update, e.g. "Jellyfin got the update 10.8.7-1".
So far the Playbook look like this:
- name: Manages Packages
hosts: ubuntu
become: true
tasks:
- name: Cache Update and Upgrade
apt:
update_cache: yes
cache_valid_time: 86400
upgrade: full
- name: Create Register
shell: grep -E "^$(date +%Y-%m-%d).+ (install|upgrade) " /var/log/dpkg.log |cut -d " " -f 3-5
register: result
- name: Show Output Register
debug: msg="{{ result.stdout_lines }}"
when: result.stdout_lines is defined
- name: Send notify to Telegram
telegram:
token: '###'
chat_id: '###'
msg: '{{ansible_hostname}} updated {{ result.stdout_lines | length }} Packages : {{ result.stdout_lines }}'
when: result.stdout_lines | length > 0
As workaroud I Print all upgraded Packages. But I hope on a elegant solution.
the interessting result.stdout_lines could be:
"upgrade jellyfin-server:amd64 10.8.7-1",
"upgrade jellyfin-web:all 10.8.7-1",
"upgrade jellyfin:all 10.8.7-1",
I tried:
- name: Check Jellyfin Upgrade
telegram:
token: '###'
chat_id: '###'
msg: 'Jellyfin got an New Update !'
when: '"upgrade jellyfin:all 10.8.7-1" in result.stdout_lines'
but this need the whole String to work, that's not the point and I have no idea how to extract just the Version into a notification.
Have someone an idea?
Thanks in advance!
Create a dictionary of installed and upgraded packages.
Use module community.general.read_csv to parse the log
- name: Parse dpkg.log
community.general.read_csv:
path: /var/log/dpkg.log
delimiter: ' '
fieldnames: [date, time, p1, p2, p3, p4]
register: dpkg_log
See the registered variable. The list of the dictionaries will be in the attribute dpkg_log.list
- debug:
var: dpkg_log
when: debug1|d(false)|bool
Group the items by date, select today's items, select install/upgrade items, and create the dictionary of packages and their versions. Declare the variables
dpkg_log_dict: "{{ dict(dpkg_log.list|groupby('date')) }}"
today: "{{ '%Y-%m-%d'|strftime }}"
pkg_arch_ver: "{{ dpkg_log_dict[today]|
selectattr('p1', 'in', ['install', 'upgrade'])|
items2dict(key_name='p2', value_name='p3') }}"
gives
pkg_arch_ver:
ansible-core:all: 2.12.9-1ppa~focal
containerd.io:amd64: 1.6.8-1
docker-ce-cli:amd64: 5:20.10.18~3-0~ubuntu-focal
docker-ce-rootless-extras:amd64: 5:20.10.18~3-0~ubuntu-focal
docker-ce:amd64: 5:20.10.18~3-0~ubuntu-focal
docker-scan-plugin:amd64: 0.17.0~ubuntu-focal
Remove the architecture from the keys
pkg_ver: "{{ dict(pkg_arch_ver.keys()|map('split', ':')|map('first')|list|
zip(pkg_arch_ver.values())) }}"
gives
pkg_ver:
ansible-core: 2.12.9-1ppa~focal
containerd.io: 1.6.8-1
docker-ce: 5:20.10.18~3-0~ubuntu-focal
docker-ce-cli: 5:20.10.18~3-0~ubuntu-focal
docker-ce-rootless-extras: 5:20.10.18~3-0~ubuntu-focal
docker-scan-plugin: 0.17.0~ubuntu-focal
Reporting should be trivial now
- community.general.mail:
to: admin
subject: Ansible Upgrade
body: |
ansible-core updated to version: {{ pkg_ver['ansible-core'] }}
when: pkg_ver.keys() is contains 'ansible-core'
gives
From: root#test.example.org
To: admin#test.example.org
Cc:
Subject: Ansible Upgrade
Date: Sat, 31 Dec 2022 04:15:21 +0100
X-Mailer: Ansible mail module
ansible-core updated to version: 2.12.9-1ppa~focal
Example of a complete playbook for testing
- hosts: localhost
become: true
vars:
dpkg_log_dict: "{{ dict(dpkg_log.list|groupby('date')) }}"
today: "{{ '%Y-%m-%d'|strftime }}"
pkg_arch_ver: "{{ dpkg_log_dict[today]|
selectattr('p1', 'in', ['install', 'upgrade'])|
items2dict(key_name='p2', value_name='p3') }}"
pkg_ver: "{{ dict(pkg_arch_ver.keys()|map('split', ':')|map('first')|list|
zip(pkg_arch_ver.values())) }}"
tasks:
- name: Cache Update and Upgrade
apt:
update_cache: yes
cache_valid_time: 86400
upgrade: full
when: upgrade|d(false)|bool
- name: Parse dpkg.log
community.general.read_csv:
path: /var/log/dpkg.log
delimiter: ' '
fieldnames: [date, time, p1, p2, p3, p4]
register: dpkg_log
- debug:
var: dpkg_log
when: debug1|d(false)|bool
- debug:
var: pkg_arch_ver
- debug:
var: pkg_ver
- community.general.mail:
to: admin
subject: Ansible Upgrade
body: |
ansible-core updated to version: {{ pkg_ver['ansible-core'] }}
when: pkg_ver.keys() is contains 'ansible-core'

How can i capture the failed items only from list of items and display output

I am trying to write Ansible to list only the failed commands from the list of commands
here is the code
---
- name: Verify sudo access
become: true
become_user: "{{ install_user }}"
shell: sudo -l | grep "{{ item }}"
register: systemctl_result
loop:
- /usr/bin/mv
- /usr/bin/olcenctl
- /usr/bin/yum
when: ansible_hostname in groups['operator']
ignore_errors: true
- name: Print result
debug:
var: systemctl_result['results']
/usr/bin/yum is not in sudo list and it fails and I want to capture only the failed command in the output.
Your problem can be simplified. The become, sudo, ... stuff is not relevant. What you want is to list failed items in the iteration. Use ignore_errors: true to take care of rc: 1 returned by failed grep. For example,
- shell: "echo ABC | grep {{ item }}"
register: out
ignore_errors: true
loop:
- A
- B
- X
Then, put the below declaration into the vars
failed_items: "{{ out.results|
selectattr('failed')|
map(attribute='item')|
list }}"
gives what you want
failed_items:
- X
Example of a complete playbook for testing
- hosts: localhost
vars:
failed_items: "{{ out.results|
selectattr('failed')|
map(attribute='item')|
list }}"
tasks:
- shell: "echo ABC | grep {{ item }}"
register: out
ignore_errors: true
loop:
- A
- B
- X
- debug:
var: failed_items

Format output to list

I'm working on an ansible playbook. I'm checking for new package patch versions of software, these versions are part of a list.
My code looks like this atm, credits for building the list goes to #Zeitounator
- name: get list of all supported version packages
shell: |
set -o pipefail
repoquery --cache --showduplicates --qf "%{VERSION}" --enablerepo xyz abc \
| grep -E -- "13." \
| sort --unique --version-sort
changed_when: false
register: versions
- name: get the major versions
set_fact:
major_versions: >-
{{
versions.stdout_lines
| map('regex_replace', '^(\d*\.\d*)\.\d*$', '\g<1>')
| unique
| sort
}}
- name: Create a consolidated list per major version
set_fact:
consolidated_versions: >-
{{
consolidated_versions | default([])
+
[{'major_version': item, 'patch_versions': versions.stdout_lines | select('contains', item) | list }]
}}
loop: "{{ major_versions }}"
My first task was to check the patch levels only through their corresponding major versions.
For example:
Check version 13.0.[1-10] only in folder 13.0, 13.1.[1-5] only in folder 13.1 etc.
This works as expected.
What I want to do now is to download only the latest package version of a major version, so I only need a list with the patch version numbers, like this:
13.0.10
13.1.5
13.2.2
I tried it with another set_fact
Like this:
- name: get latest patchversion of supported version
set_fact:
patch_version: "{{ consolidated_versions | json_query('[*].patch_versions[-1]') }}"
- name: output the last versions
debug:
var: patch_version
This gives me an output like this:
ok: [localhost] => {
"patch_version": [
"13.0.10",
"13.1.5",
"13.2.2"
]
}
This is indeed the data I need.
I need to use these 3 elements in a loop to download the packages like this:
- name: Download the files
get_url:
url: https://packages.xyz.com/abc/def/packages/el/{{ ansible_distribution_major_version }}/abc-def-{{ item }}-ee.0.el{{ ansible_distribution_major_version }}.x86_64.rpm/download.rpm
dest: /var/www/html/abc/{{ date }}/abc-def-{{ item }}-ee.0.el{{ ansible_distribution_major_version }}.x86_64.rpm
loop: "{{ patch_version }}"
Problem is now that ansible does not handle the fact as a list, so it replaces {{ patch_version }}with this: ['13.0.10', '13.1.5', '13.2.2']
Of course, this won't work.
How do I transform this output into a loopable list?
I already tried to make it a list, but then I got the whole output as a string as one element. How do I split this into a list like this:
- 13.0.10
- 13.1.5
- 13.2.2
?
Thanks in advance, I'm so confused.
I couldn't reproduce your problem because fact is handled as list in my ansible version (ansible 2.9.6, python 3.7.3).
Nevertheless, if fact is a string try to pass it through from_json filter:
loop: "{{ patch_version | from_json }}"
FWIW, you can check variable type piping it into type_debug:
- debug:
msg: "{{ patch_version | type_debug }}"
Fixed it by myself, not sure what the bug was, maybe just my mess ;).
For testing purposes I tried some things with with_nested and with loop.
So my code looked like this for a while:
- name: Download the files
get_url:
url: https://packages.xyz.com/abc/def/packages/el/{{ ansible_distribution_major_version }}/abc-def-{{ item }}-ee.0.el{{ ansible_distribution_major_version }}.x86_64.rpm/download.rpm
dest: /var/www/html/abc/{{ date }}/abc-def-{{ item }}-ee.0.el{{ ansible_distribution_major_version }}.x86_64.rpm
loop:
- "{{ patch_version }}"
# - commented out loop
# - another commented out loop
At this case the list seems to be read as a string.
So ansible replaces the variable with the all values as a string into my code.
After I changed the code to:
- name: Download the files
get_url:
url: https://packages.xyz.com/abc/def/packages/el/{{ ansible_distribution_major_version }}/abc-def-{{ item }}-ee.0.el{{ ansible_distribution_major_version }}.x86_64.rpm/download.rpm
dest: /var/www/html/abc/{{ date }}/abc-def-{{ item }}-ee.0.el{{ ansible_distribution_major_version }}.x86_64.rpm
loop: "{{ patch_version }}"
It worked as expected. Thanks for the hints, maybe my brain's a little bit confused after the whole day working on that playbook ;)

In Ansible ,can we loop through list of files and match content in file using lineinfile module

I have to find all config.xml on a server and produce the list on a given server.
Once we registered the files list, i have to check the content in each file on the list using Ansible
I tried to derive the paths for all config.xml
register them and print the list
Added the registered variable into lineinfile path
##Derive Config.xml path
- name: Find the location of xml file
shell: find {{ wlx_mount_point }} -maxdepth 1 -name {{xml_file}} | rev | cut -d '/' -f3- | rev
register: wlx_domain_home
ignore_errors: yes
- debug:
msg: "{{ wlx_domain_home.stdout_lines|list }}"
- name: check domain home directory exists
stat:
path: "{{ wlx_domain_home |list }}"
ignore_errors: true
- debug:
msg: "{{ wlx_domain_home.stdout_lines|list }}"
- name: "Ensure Logging Settings in config.xml"
lineinfile:
line: "{{ item.line }}"
regexp: "{{ item.regexp }}"
path: "{{ wlx_domain_home.stdout_lines|list }}/config/config.xml"
state: present
backrefs: yes
register: config.xml.Logging
with_fileglob: "{{ wlx_domain_home.stdout_lines|list }}/config/config.xml"
with_items:
- line: "<logger-severity>Info</logger-severity>"
regexp: "^logger-severity.*"
Expected results are , it has to look for lines in each file and loop through the list. ` Its printing the list and not able to find the content
"_ansible_ignore_errors": true, "msg": "Destination
[u'/appl/cmpas9/user_projects/pte-ipscasws',
u'/appl/bbb/user_projects/qa-ucxservices_bkp',
u'/appl/app/user_projects/weiss_apps12',
u'appl/view/user_projects/weiss_apps12_oldbkp',
u'appl/voc/user_projects/qa-voc']/config/config.xml does not exist !"
}
This is how i fixed the issue. Now it gives output
- name: find all weblogic domain paths
shell: find /tech/appl -type f -name config.xml
register: wlx_domain_config
- debug:
var: wlx_domain_config.stdout_lines
name: "Ensure allow-unencrypted-null-cipher Encryption Settings in config.xml"
shell: grep -i "" {{ item }}
with_items: "{{ wlx_domain_config.stdout_lines }}"
register: allowunencrypted
debug:
var: allowunencrypted.stdout_lines

Ansible conditional handlers for a task?

FYI, I'm running my Ansible playbook in "pull-mode" not push mode. Therefore, my nodes will publish the results of their task via Hipchat.
With that said, I have a task that installs RPMs. When the installs are successful, the nodes notify via Hipchat that the task was successfully run. Now, in the event that a task fails, i force it to notify hipchat w/ the "--force-handlers" paramter. My question, is there a way to call a particular handler depending on the outcome of the task?
Task
- name: Install Perl modules
command: sudo rpm -Uvh {{ rpm_repository }}/{{ item.key }}-{{ item.value.svn_tag }}.rpm --force
with_dict: deploy_modules_perl
notify: announce_hipchat
Handler
- name: announce_hipchat
local_action: hipchat
from="deployment"
token={{ hipchat_auth_token }}
room={{ hipchat_room }}
msg="[{{ ansible_hostname }}] Successfully installed RPMs!"
validate_certs="no"
In this case, I use multiple handlers. See following example :
file site.yml
- hosts: all
gather_facts: false
force_handlers: true
vars:
- cmds:
echo: hello
eccho: hello
tasks:
- name: echo
command: "{{ item.key }} {{ item.value }}"
register: command_result
with_dict: "{{ cmds }}"
changed_when: true
failed_when: false
notify:
- "hipchat"
handlers:
- name: "hipchat"
command: "/bin/true"
notify:
- "hipchat_succeeded"
- "hipchat_failed"
- name: "hipchat_succeeded"
debug:
var: "{{ item }}"
with_items: "{{ command_result.results | selectattr('rc', 'equalto', 0) | list }}"
- name: "hipchat_failed"
debug:
var: "{{ item }}"
with_items: "{{ command_result.results | rejectattr('rc', 'equalto', 0) | list }}"
Use command
ansible-playbook -c local -i "localhost," site.yml
CAUTION: use test filter 'equalsto' added in jinja2 2.8

Resources