Ansible - create a list of unused disks - ansible

I am trying to create a list of unused disks from gather facts
-
key | search ("sd")
with_dict: "{{ ansible_devices }}"
But its only displaying one disk, like if the server have two unused disks sdb and sdc , its only displaying sdb. How can I modify my code to include all unused disks.

the way you have it, set_fact gets equal to the "current" item from the iteration, probably this is why you see only 1 disk in the final result. You need to set the disks as a list of elements and append to that list the current item.key. you can use this syntax:
set_fact:
disks: "{{ disks|default([]) + ['/dev/{{item.key}}'] }}"
to understand how many results you have in the loops of the with_dict clause, you can try a debug task:
- name: Print disk result
debug:
msg: "/dev/{{item.key}}"
when:
- not item.value.partitions
- not item.value.holders
- not item.value.links.ids
- item.key | search ("sd")
with_dict: "{{ ansible_devices }}"
(indentation may need fixes, i just copied from your code)
hope it helps

thanks for the reply, I used the mentioned code for printing the disk result, it was displaying multiple disks, but when I used set_fact, and the msg to display the variable, its showing like this:-
"disks": [
"/dev/sdc",
"/dev/{{item.key}}"
]
- name: Print disk result
set_fact:
disks: "{{ disks|default([]) + ['/dev/{{item.key}}'] }}"
when:
- not item.value.partitions
- not item.value.holders
- not item.value.links.ids
- item.key | search ("sd")
with_dict: "{{ ansible_devices }}"
- debug: var=disks

If anyone is ever looking for the answer, this works for me:
- name: Print disk result
set_fact:
disks: "{{ disks|default([]) + ['/dev/' + item.key] }}"
when:
- not item.value.partitions
- not item.value.holders
- not item.value.links.ids
- item.key | regex_search ("sd")
with_dict: "{{ ansible_devices }}"
- name: debug
debug:
msg: "{{disks}}"
It returns:
ok: [localhost] => {
"msg": [
"/dev/sdb"
]
}

Related

Ansible - loop over multiple items in stdout_lines

I am performing a grep with multiple items.
---
- hosts: my_host
gather_facts: false
vars:
my_list:
- whatever
- something
tasks:
- name: grep for item in search path
shell: "grep -rIL {{ item }} /tmp"
register: the_grep
loop: "{{ my_list }}"
- debug:
msg: "{{ item.stdout_lines }}"
loop: "{{ the_grep.results }}"
Depending on the result, multiple files could match.
msg:
- /tmp/something.conf
- /tmp/folder/file.txt
Q: How would I configure Ansible to loop over the items in stdout_lines?
The use case I'm solving is to delete .ini sections based on the item, but in this case, Ansible doesn't loop over the stdout_lines.
- name: remove stanza from ini file
ini_file:
path: "{{ item.stdout_lines }}"
section: "{{ item.item }}"
mode: '0600'
state: absent
loop: "{{ the_grep.results }}"
when: item.stdout_lines | length > 0
It seems that this doesn't work, but configuring item.stdout_lines[0] gives the partially expected result, since Ansible will use only the first item in that list. But ofc, not the 2nd and so on.
Perhaps there's a prettier answer, but solved it by using with_nested and creating a json_query:
- name: remove stanza from ini file
ini_file:
path: "{{ item.0 }}"
section: "{{ item.1.item }}"
mode: '0600'
state: absent
with_nested:
- "{{ the_grep | json_query('results[].stdout_lines[]') }}"
- "{{ the_grep.results }}"

Ansible How do i sort an array in descending order upon the element substring

Below is my array:
- set_fact:
diskout:
- 85_20.198.65.132
- 86_52.140.118.141
- 84_20.198.75.31
- 82_20.204.75.114
- 83_20.204.24.160
I wish to sort this in descending order upon just the first substring separated by _ while ignoring whatever is after the underscore.
Thus, my expected output is:
- 86_52.140.118.141
- 85_20.198.65.132
- 84_20.198.75.31
- 83_20.204.24.160
- 82_20.204.75.114
I tried the below but it did not give me the desired output:
- debug:
msg: "The automation will run on {{ item }}"
with_items: "{{ diskout | reverse | list }}"
Can you please suggest?
Create index, e.g.
- debug:
msg: "{{ _dict|dict2items|
sort(attribute='key', reverse=true)|
map(attribute='value')|
list }}"
vars:
_index: "{{ diskout|map('regex_replace', '^(.*)_(.*)$', '\\1')|list }}"
_dict: "{{ dict(_index|zip(diskout)) }}"
gives
msg:
- 86_52.140.118.141
- 85_20.198.65.132
- 84_20.198.75.31
- 83_20.204.24.160
- 82_20.204.75.114
The next option might be faster
- debug:
msg: "{{ _dict|sort(reverse=true)|map('extract', _dict)|list }}"
vars:
_index: "{{ diskout|map('regex_replace', '^(.*)_(.*)$', '\\1')|list }}"
_dict: "{{ dict(_index|zip(diskout)) }}"

Ansible: group values on same line with common key

I'm struggling to group some common values on a same line, with ansible.
I have the vg name and disks on each line, as follows:
ok: [localhost] => {
"msg": [
"vg01 /dev/xvdk",
"vg01 /dev/xvdj",
"vg02 /dev/xvdi",
"vg02 /dev/xvdh",
"vg03 /dev/xvdg",
"vg03 /dev/xvdf"
]
}
Now, I want the vg with all it's disks, on one same line, one per vg, as below:
"vg01 /dev/xvdk, /dev/xvdj",
"vg02 /dev/xvdi, /dev/xvdh",
"vg03 /dev/xvdg, /dev/xvdf",
Still not able to achieve this; Can someone help, please?
Thank you.
Create a list of dictionaries. For example, given the data is stored in the variable disks
- set_fact:
l1: "{{ l1|default([]) + [{'dsk': item.split(' ')|first,
'dev': item.split(' ')|last}] }}"
loop: "{{ disks }}"
gives
l1:
- dev: /dev/xvdk
dsk: vg01
- dev: /dev/xvdj
dsk: vg01
- dev: /dev/xvdi
dsk: vg02
- dev: /dev/xvdh
dsk: vg02
- dev: /dev/xvdg
dsk: vg03
- dev: /dev/xvdf
dsk: vg03
Use groupby filter and format the lines in the loop, for example
- debug:
msg: "{{ item.0 }} {{ item.1|flatten|map(attribute='dev')|join(', ') }}"
loop: "{{ l1|groupby('dsk') }}"
gives
msg: vg01 /dev/xvdk, /dev/xvdj
msg: vg02 /dev/xvdi, /dev/xvdh
msg: vg03 /dev/xvdg, /dev/xvdf
A more robust option, using regex_replace/from_yaml, might be able to handle some malformed data where simple split/first/last would fail e.g.
- set_fact:
l1: "{{ l1|default([]) + [item.0|combine(item.1)] }}"
with_together:
- "{{ disks|map('regex_replace', '^(.*?) (.*)$', 'dsk: \\1')|
map('from_yaml')|list }}"
- "{{ disks|map('regex_replace', '^(.*?) (.*)$', 'dev: \\2')|
map('from_yaml')|list }}"

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 }}"

Adding field to dict items

Consider the following play. What I am trying to do is add a field, tmp_path which is basically the key and revision appended together to each element in the scripts dict.
---
- hosts: localhost
connection: local
gather_facts: no
vars:
scripts:
a.pl:
revision: 123
b.pl:
revision: 456
tasks:
- with_dict: "{{ scripts }}"
debug:
msg: "{{ item.key }}_{{ item.value.revision }}"
# - with_items: "{{ scripts }}"
# set_fact: {{item.value.tmp_path}}="{{item.key}}_{{item.value.revision}}"
# - with_items: "{{ scripts }}"
# debug:
# msg: "{{ item.value.tmp_path }}"
...
Obviously the commented code doesn't work, any idea how I can get this working? Is it possible to alter the scripts dict directly, or should I somehow be creating a new dict to reference instead?
By the way welcome to correct the terminology for what I am trying to do.
OK, I think I got a solution (below), at least to let me move forwards with this. Disadvantages are it has removed the structure of my dict and also seems a bit redundant having to redefine all the fields and use a new variable, If anyone can provide a better solution I will accept that instead.
---
- hosts: localhost
connection: local
gather_facts: no
vars:
scripts:
a.pl:
revision: 123
b.pl:
revision: 456
tasks:
- with_dict: "{{ scripts }}"
debug:
msg: "{{ item.key }}_{{ item.value.revision }}"
- with_dict: "{{ scripts }}"
set_fact:
new_scripts: "{{ (new_scripts | default([])) + [ {'name': item.key, 'revision': item.value.revision, 'tmp_path': item.key ~ '_' ~ item.value.revision}] }}"
# - debug:
# var: x
# - with_dict: "{{ scripts }}"
- with_items: "{{ new_scripts }}"
debug:
msg: "{{ item.tmp_path }}"
...
BTW credit to the following question which pointed me in the right direction:
Using Ansible set_fact to create a dictionary from register results

Resources