Is there any method to map multiple attributes in ansible - ansible

I have an output from yum list module. The thing is I want to display the output without those number (epoch attribute). The problem is I couldn't find any solutions of mapping 2 attributes (name and version). All the solutions I found are connected to only 1 attribute ( envra) in my case.
- name: check packages
become: true
yum:
list: installed
register: output
- name: add lines to files
lineinfile:
dest: "./file.txt"
line: "{{ inventory_hostname }} {{ item }}"
with_items:
- "{{ output.results | map(attribute='envra') |list }}"
delegate_to: localhost
This is the output without any mapping. As you can see there are multiple attributes. I would like to display only name and version of the package.
10.112.65.15 {u'envra': u'0:GeoIP-1.5.0-14.el7.x86_64', u'name': u'GeoIP', u'repo': u'installed', u'epoch': u'0', u'version': u'1.5.0', u'release': u'14.el7', u'yumstate': u'installed', u'arch': u'x86_64'}
The closest to expected values is envra attribute, but still has those epoch number inside...
10.112.65.15 0:GeoIP-1.5.0-14.el7.x86_64
As I mentioned at the begging I would like to get output of something like that
10.112.65.15 GeoIP 1.5.0
or at least without epoch attribute.
I've also change approach and tried this method
- name: add lines to files
lineinfile:
dest: "./file.txt"
line: "{{ inventory_hostname }} {{ item }} "
with_items:
- "{{ output | json_query(my_query) | list }}"
delegate_to: localhost
vars:
my_query: "results[].[name, version]"
but received result was with '[]' and u' which I'd like to delete but don't exactly know how.
10.112.65.15 [u'GeoIP', u'1.5.0']

Why do you extract the attribute or use json query ? Simply use the hash and print out the needed fields. The following should work out of the box.
- name: add lines to files
lineinfile:
dest: "./file.txt"
line: "{{ inventory_hostname }} {{ item.name }} {{ item.version }}"
with_items:
- "{{ output.results }}"
delegate_to: localhost

Related

Can we have 2 with_items in ansible in a single task

Below is the condition
- name: Find the image
slurp:
src: "{{ IMAGE }}"
register: slurp_results
- name: Upload image
shell: |
skopeo copy -docker-archive:{{ item }}.tar docker://{{ URL }}/TESTIMAGE
with_items: "{{ (slurp_results.content|b64decode).splitlines() }}"
The above code works.
But I would need "TESTIMAGE" also to be replaced as {{ item }} like below.
skopeo copy -docker-archive:{{ item }}.tar docker://{{ URL }}/{{ item }}
How to define 2 with_items in a single shell task with 2 different slurp results
I believe you can by using the subelements module. Here is a link. Try going by this example:
- name: Setup MySQL users, given the mysql hosts and privs subkey lists
mysql_user:
name: "{{ item.0.name }}"
password: "{{ item.0.mysql.password }}"
host: "{{ item.1 }}"
priv: "{{ item.0.mysql.privs | join('/') }}"
with_subelements:
- "{{ users }}"
- mysql.hosts
Users is referred to as item.0 and hosts as item.1 and so on.

printing the host name from group in inventory

I have below inventory file:
[web]
10.0.1.0
[db]
10.0.3.0
[frontend]
10.0.5.0
[X_all:children]
web
db
frontend
Now in my playbook: I'm trying to print the hostname under X_all group in the name line.
- name: "Copying the output of Registry run to local machine from {{ hostvars[item]['inventory_hostname'] }}"
become: true
become_user: "{{ login }}"
fetch:
src: /tmp/DeploymentRegistry.txt
dest: /tmp/DeploymentRegistry-{{ inventory_hostname }}.txt
flat: yes
with_items:
- "{{ groups['X_all'] }}"
Is it even possible to add the hostname in the name line?
I tried replacing {{ hostvars[item]['inventory_hostname'] }} with inventory_hostname but it's also not working.
Regarding your requirement
I'm trying to print the hostname under test group in the name line.
take note that the group has more than one member and therefore more than one hostname, but a list of hostnames.
- name: Show hosts {{ groups['test'] }}
delegate_to: localhost
debug:
msg: "{{ item }}" # will show the group member hostname(s)
with_items:
- "{{ groups['test'] }}"
Is it even possible to add the hostname in the name line? I tried ... with inventory_hostname but it's also not working.
Yes, it is.
- name: Show host {{ inventory_hostname }}
debug:
msg: "{{ item }}"
with_items:
- "{{ groups['test'] }}"
But it will print the first hostname from list only.

Ansible Registers - Dynamic naming

I am trying to use a register in Ansible playbook to store my output. Below is the code which i am using.
I have tried below code
- name: Check if Service Exists
stat: path=/etc/init.d/{{ item }}
register: {{ item }}_service_status
with_items:
- XXX
- YYY
- ZZZ
I need different outputs to be stored in different register variables based on the items as mentioned in the code. It is failing and not able to proceed. Any help would be appreciated.
Updated answer
I think you need to put quotes around it:
register: "{{ item }}_service_status"
Or you can use set_fact (1, 2, 3, 4)
register all the output to a single static variable output and then use a loop to iteratively build a new variable service_status (a list) by looping over each item in the static variable output
- name: Check if Service Exists
stat: path=/etc/init.d/{{ item }}
register: output
with_items:
- XXX
- YYY
- ZZZ
- name: Setting fact using output of loop
set_fact:
service_status:
- rc: "{{ item.rc }}"
stdout: "{{ item.stdout }}"
id: "{{ item.id }}"
with_items:
- "{{ output }}"
- debug:
msg: "ID and stdout: {{ item.id }} - {{ item.stdout }}"
with_items:
- "{{ service_status }}"
Initial Answer
IIUC, this link from the Ansible docs shows how to use register inside a loop (see another example in this SO post).
A couple of points
it may be more convenient to assign the list (XXX, YYY, ZZZ) to a separate variable (eg. 1, 2)
I don't know if this is part of the problem, but with_items is no longer the recommended approach to loop over a variable: instead use loop - see here for an example
vars:
items:
- XXX
- YYY
- ZZZ
- name: Check if Service Exists
stat: path=/etc/init.d/{{ item }}
register: service_status
loop: "{{ items|flatten(levels=1) }}"
- name: Show the return code and stdout
debug:
msg: "Cmd {{ item.cmd }}, return code {{ item.rc }}, stdout {{ item.stdout }}"
when: item.rc != 0
with_items: "{{ service_status.results }}"

How to have a condition for with-items as a whole, not for the individual items

I have this:
- name: Add hosts to /etc/hosts
lineinfile:
dest=/etc/hosts
line='{{ item.dest }} {{ item.src }}'
regexp='.*{{ item.src }}.*'
state=present
with_items:
- "{{ hosts[service_name] }}"
when: (service_name in hosts)
What I get
'dict object' has no attribute u'blah'
What I need is to skip the whole task if the when condition is false.
There is no way to attach when condition to a whole task when using with_items.
But you can go another way – default iterator to empty list if there is no key, like this:
- name: Add hosts to /etc/hosts
lineinfile:
dest=/etc/hosts
line='{{ item.dest }} {{ item.src }}'
regexp='.*{{ item.src }}.*'
state=present
with_items: "{{ hosts[service_name] | default([]) }}"
In this case the task will have zero items to iterate over if you provide unknown service name.
Also note that I removed unnecessary list definition in your with_items construction.

Ansible with_items if item is defined

Ansible 1.9.4.
The script should execute some task only on hosts where some variable is defined. It works fine normally, but it doesn't work with the with_items statement.
- debug: var=symlinks
when: symlinks is defined
- name: Create other symlinks
file: src={{ item.src }} dest={{ item.dest }} state=link
with_items: "{{ symlinks }}"
when: symlinks is defined
But I get:
TASK: [app/symlinks | debug var=symlinks] *********************
skipping: [another-host-yet]
TASK: [app/symlinks | Create other symlinks] ******************
fatal: [another-host-yet] => with_items expects a list or a set
Maybe I am doing something wrong?
with_items: "{{ symlinks | default([]) }}"
The reason for this behavior is conditions work differently inside loops. If a loop was defined the condition is evaluated for every item while iterating over the items. But the loop itself requires a valid list.
This is also mentioned in the docs:
Note that when combining when with with_items (see Loops), be aware that the when statement is processed separately for each item. This is by design:
tasks:
- command: echo {{ item }}
with_items: [ 0, 2, 4, 6, 8, 10 ]
when: item > 5
I think this is a bad design choice and for this functionality they better should have introduced something like with_when.
As you have already figured out yourself, you can default to an empty list.
with_items: "{{ symlinks | default([]) }}"
Finally if the list is dynamically loaded from a var, say x, use:
with_items: "{{ symlinks[x|default('')] | default([])}}"
This will default to an empty list when 'x' is undefined
Accordingly, fall back to an empty dict with default({}):
# service_facts skips, then dict2items fails?
with_dict: "{{ ansible_facts.services|default({})|dict2items|selectattr('key', 'match', '[^#]+#.+\\.service')|list|items2dict }}"

Resources