Touching a file on each mount point declared in `ansible_mounts` - ansible

I want to get info about mount points so that I can touch a file on them.
I can get mount points through item.mount but I want to store those mount points inside a variable so I can use it to form a path like {{mountpoint}}/test.txt.
But I don't know how to store that info to variable from the result of my debug task. And even if I get those mount points, I don't know how to create a file inside each mount point directories.
Should I use a loop again for that?
This is the playbook I have so far:
---
- hosts: all
gather_facts: false
tasks:
- setup:
filter: ansible_mounts
- debug:
var: item.mount
loop: "{{ ansible_mounts }}"
register: result
Which gives
TASK [setup] ***********************************************************************************************************
ok: [ambertest]
TASK [debug] ***********************************************************************************************************
ok: [ambertest] => (item={u'block_used': 258025, u'uuid': u'55555-c55c-55-555-0555a', u'size_total': 55584, u'block_total': 13104379, u'mount': u'/', u'block_available': 12846354, u'size_available': 5554, u'fstype': u'xfs', u'inode_total': 25524, u'inode_available': 558, u'device': u'/dev/vda1', u'inode_used': 30796, u'block_size': 4096, u'options': u'rw,seclabel,relatime,attr2,inode64,noquota'}) => {
"item": {
"block_available": 5554,
"block_size": 4096,
"block_total": 13559,
"block_used": 2555,
"device": "/dev/vda1",
"fstype": "xfs",
"inode_available": 2558,
"inode_total": 255824,
"inode_used": 355,
"mount": "/",
"options": "rw,seclabel,relatime,attr2,inode64,noquota",
"size_available": 5554,
"size_total": 5554,
"uuid": "55555"
},
"item.mount": "/"
}
ok: [ambertest] => (item={u'block_used': 672774, u'uuid': u'55dc9f5589e', u'size_total': 55, u'block_total': 155558, u'mount': u'/amber', u'block_available': 554, u'size_available': 49954627584, u'fstype': u'ext4', u'inode_total': 355, u'inode_available': 559, u'device': u'/dev/vdb', u'inode_used': 11, u'block_size': 4096, u'options': u'rw,seclabel,relatime,data=ordered'}) => {
"item": {
"block_available": 1554,
"block_size": 4556,
"block_total": 558,
"block_used": 6554,
"device": "/dev/vdb",
"fstype": "ext4",
"inode_available": 559,
"inode_total": 550,
"inode_used": 11,
"mount": "/amber",
"options": "rw,seclabel,relatime,data=ordered",
"size_available": 45584,
"size_total": 55,
"uuid": "5555"
},
"item.mount": "/amber"
}
PLAY RECAP *************************************************************************************************************
ambertest : ok=2 changed=0 unreachable=0 failed=0

The following is really far from optimized and bullet proofed but will at least give you an idea of how to start. Basically, you don't need to store your mount points inside a variable since the ansible_mounts variable already has them (as brilliantly demonstrated in your question)
---
- hosts: all
tasks:
- name: touch a test.txt file on each existing mountpoint
file:
path: "{{ item.mount }}/test.txt"
state: touch
loop: "{{ ansible_mounts }}"

Related

Suppress Assert output in Ansible

I have an Ansible script that checks Linux file systems for capacity and issues a message if it is over a certain threshold.
- hosts: all
gather_facts: yes
tasks:
- name: Collect only facts about hardware
setup:
gather_subset:
- 'hardware'
- name: Test for available disk space
setup: filter=ansible_mounts
- name: Ensure that free space on the tested volume is greater than 15%
assert:
that:
- mount.size_available > mount.size_total|float * 0.05
fail_msg: Disk space on {{ item.mount }} has reached 95% threshold. Total size is {{ mount.size_total }} bytes and the available size is {{ mount.size_available }}
quiet: yes
vars:
mount: "{{ ansible_mounts | selectattr('mount','equalto',item.mount) | list | first }}"
with_items:
- "{{ ansible_mounts }}"
The problem is that even though I added quiet: yes I still get this as output on drives that are OK:
{
"changed": false,
"msg": "All assertions passed",
"_ansible_no_log": false,
"item": {
"mount": "/boot",
"device": "/dev/sda2",
"fstype": "ext4",
"options": "rw,nodev,relatime,data=ordered",
"size_total": 1023303680,
"size_available": 729440256,
"block_size": 4096,
"block_total": 249830,
"block_available": 178086,
"block_used": 71744,
"inode_total": 65536,
"inode_available": 65223,
"inode_used": 313,
"uuid": "75c887d9-e8a9-45a6-9bb8-8e7ffe4b9e68"
},
"ansible_loop_var": "item",
"_ansible_item_label": {
"mount": "/boot",
"device": "/dev/sda2",
"fstype": "ext4",
"options": "rw,nodev,relatime,data=ordered",
"size_total": 1023303680,
"size_available": 729440256,
"block_size": 4096,
"block_total": 249830,
"block_available": 178086,
"block_used": 71744,
"inode_total": 65536,
"inode_available": 65223,
"inode_used": 313,
"uuid": "75c887d9-e8a9-45a6-9bb8-8e7ffe4b9e68"
}
}
And this on the failed:
{
"evaluated_to": false,
"assertion": "mount.size_available > mount.size_total|float * 0.05",
"msg": "Disk space on /snap/core/8268 has reached 95% threshold. Size 0 Total size 93454336",
"_ansible_no_log": false,
"changed": false,
"item": {
"mount": "/snap/core/8268",
"device": "/dev/loop0",
"fstype": "squashfs",
"options": "ro,nodev,relatime",
"size_total": 93454336,
"size_available": 0,
"block_size": 131072,
"block_total": 713,
"block_available": 0,
"block_used": 713,
"inode_total": 12842,
"inode_available": 0,
"inode_used": 12842,
"uuid": "N/A"
},
"ansible_loop_var": "item",
"_ansible_item_label": {
"mount": "/snap/core/8268",
"device": "/dev/loop0",
"fstype": "squashfs",
"options": "ro,nodev,relatime",
"size_total": 93454336,
"size_available": 0,
"block_size": 131072,
"block_total": 713,
"block_available": 0,
"block_used": 713,
"inode_total": 12842,
"inode_available": 0,
"inode_used": 12842,
"uuid": "N/A"
}
}
I would like to suppress everything but the assert failed message.
The short answer is to set an explicit label in a loop_control directive, which will suppress the default behavior of outputting the entire loop variable for each loop iteration.
But you're also doing something odd in the vars section of your task.
If we fix both of those, we get:
- hosts: all
gather_facts: false
tasks:
- name: Collect only facts about hardware
setup:
gather_subset:
- hardware
- name: Test for available disk space
setup:
filter: ansible_mounts
- name: Ensure that free space on the tested volume is greater than 15%
assert:
that:
- item.size_available|float > item.size_total|float * 0.05
fail_msg: Disk space on {{ item.mount }} has reached 95% threshold. Total size is {{ item.size_total }} bytes and the available size is {{ item.size_available }}
quiet: yes
loop: "{{ ansible_mounts }}"
loop_control:
label: "{{ item.mount }}"
Which will output something like this for healthy filesystems:
ok: [host0] => (item=/)
And something like this for filesystems that fail your assert:
failed: [host1] (item=/vol/failed) => {"ansible_loop_var": "item", "assertion": "item.size_available|float > item.size_total|float * 0.05", "changed": false, "evaluated_to": false, "item": {"block_available": 0, "block_size": 131072, "block_total": 444, "block_used": 444, "device": "/dev/loop4", "fstype": "squashfs", "inode_available": 0, "inode_total": 10809, "inode_used": 10809, "mount": "/vol/failed", "options": "ro,nodev,relatime", "size_available": 0, "size_total": 58195968, "uuid": "N/A"}, "msg": "Disk space on /vol/failed has reached 95% threshold. Total size is 58195968 bytes and the available size is 0"}
Note that the contents of ansible_mounts may contain filesystems that legitimately have no free space (e.g., read-only squashfs or isofs mounts), or that don't have size_total and size_available keys. You might want to add a filter onto your task, for example:
- name: Ensure that free space on the tested volume is greater than 15%
assert:
that:
- item.size_available|float > item.size_total|float * 0.05
fail_msg: Disk space on {{ item.mount }} has reached 95% threshold. Total size is {{ item.size_total }} bytes and the available size is {{ item.size_available }}
quiet: yes
when: >
"size_total" in item and item.fstype in ['xfs', 'ext4']
loop: "{{ ansible_mounts }}"
loop_control:
label: "{{ item.mount }}"
With that filter in place, the output on my system is:
TASK [Ensure that free space on the tested volume is greater than 15%] **************************************
ok: [localhost] => (item=/)
skipping: [localhost] => (item=/var/lib/snapd/snap/mame/2287)
ok: [localhost] => (item=/boot)
skipping: [localhost] => (item=/var/lib/snapd/snap/gtk-common-themes/1514)
ok: [localhost] => (item=/home)
skipping: [localhost] => (item=/var/lib/snapd/snap/kde-frameworks-5-core18/32)
skipping: [localhost] => (item=/var/lib/snapd/snap/snapd/10707)
skipping: [localhost] => (item=/var/lib/snapd/snap/core18/1944)
skipping: [localhost] => (item=/var/lib/snapd/snap/mame/2235)
ok: [localhost] => (item=/var/lib/libvirt)
ok: [localhost] => (item=/var/lib/containers)
ok: [localhost] => (item=/var/lib/packages)
ok: [localhost] => (item=/var/lib/mock)
ok: [localhost] => (item=/vol/log)
ok: [localhost] => (item=/scratch)
ok: [localhost] => (item=/var/cache)
skipping: [localhost] => (item=/var/lib/containers/storage/overlay)

Ansible edit array of json objects in place or sort for different values when undefined

I have a list of json objects, that looks something like this
[
{
"apiVersion": "v1",
"count": 11,
"eventTime": null,
"firstTimestamp": "2020-10-20T16:17:08Z",
"lastTimestamp": "2020-10-20T16:30:38Z",
"involvedObject": {
"apiVersion": "v1"
},
"kind": "Event"
},
{
"apiVersion": "v1",
"count": 11,
"eventTime": "2020-10-20T16:17:10.182317Z"
"firstTimestamp": null,
"lastTimestamp": null,
"involvedObject": {
"apiVersion": "v1"
},
"kind": "Event"
}
]
I would like to be able to sort this array by lastTimestamp and if lastTimestamp was not defined by eventTime. Since this does not work with sort - to my knowledge at least, I was thinking about manipulating the array, setting lastTimestamp to eventTime when lastTimestamp is null.
Since I am new to Ansible, I am not sure how to manipulate a list in place in order to archive my goal. Or is there even a way to sort by two attributes?
To sumarize briefly, you don't edit in place in ansible. You manipulate the data to create a new data structure adapted to your need
In your specific case, I don't think there is any other way than using set_fact and looping over your original data to examine each item (i.e. I don't see a solution by simply applying a series of filters on the original data).
The new items in your new list will be obtained by combining the original dict with a dict containing the correct date or an empty one to keep the current date.
Here is a quick example playbook:
---
- name: Process date and sort
hosts: localhost
gather_facts: false
vars:
# Your original data as json on a single line to shorten display
api_events: [{"apiVersion": "v1", "count": 11, "eventTime": null, "firstTimestamp": "2020-10-20T16:17:08Z", "lastTimestamp": "2020-10-20T16:30:38Z", "involvedObject": {"apiVersion": "v1"}, "kind": "Event"}, {"apiVersion": "v1", "count": 11, "eventTime": "2020-10-20T16:17:10.182317Z", "firstTimestamp": null, "lastTimestamp": null, "involvedObject": {"apiVersion": "v1"}, "kind": "Event"}]
tasks:
- name: Process API events to determine time we will use
vars:
new_timestamp: "{{ item.firstTimestamp | ternary({}, {'firstTimestamp': item.eventTime}) }}"
current_event: "{{ item | combine(new_timestamp) }}"
set_fact:
processed_api_events: "{{ processed_api_events | default([]) + [current_event] }}"
loop: "{{ api_events }}"
- name: Show result sorted
debug:
msg: "{{ processed_api_events | sort(attribute='firstTimestamp') }}"
which gives:
PLAY [Process date and sort] ***********************************************************************************************************************************************************************************************************
TASK [Process API events to determine time we will use] ********************************************************************************************************************************************************************************
ok: [localhost] => (item={'apiVersion': 'v1', 'count': 11, 'eventTime': None, 'firstTimestamp': '2020-10-20T16:17:08Z', 'lastTimestamp': '2020-10-20T16:30:38Z', 'involvedObject': {'apiVersion': 'v1'}, 'kind': 'Event'})
ok: [localhost] => (item={'apiVersion': 'v1', 'count': 11, 'eventTime': '2020-10-20T16:17:10.182317Z', 'firstTimestamp': None, 'lastTimestamp': None, 'involvedObject': {'apiVersion': 'v1'}, 'kind': 'Event'})
TASK [Show result sorted] **************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
{
"apiVersion": "v1",
"count": 11,
"eventTime": null,
"firstTimestamp": "2020-10-20T16:17:08Z",
"involvedObject": {
"apiVersion": "v1"
},
"kind": "Event",
"lastTimestamp": "2020-10-20T16:30:38Z"
},
{
"apiVersion": "v1",
"count": 11,
"eventTime": "2020-10-20T16:17:10.182317Z",
"firstTimestamp": "2020-10-20T16:17:10.182317Z",
"involvedObject": {
"apiVersion": "v1"
},
"kind": "Event",
"lastTimestamp": null
}
]
}
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Using an attribute from parted output

I need Ansible to install a server and I must partition for LVM. How can I use the attribute "size" from the second partition of "sda_info.partitions"?
hosts: node2
become: yes
become_method: sudo
become_flags: -H -S
tasks:
- name: Some name
parted:
device: /dev/sda
unit: s
register: sda_info
OUTPUT
TASK [debug] ***************************************************************
ok: [node2] => {
"sda_info.partitions": [
{
"begin": 4096.0,
"end": 51202047.0,
"flags": [
"boot"
],
"fstype": "ext4",
"name": "",
"num": 1,
"size": 51197952.0,
"unit": "s"
},
{
"begin": 51202048.0,
"end": 52248575.0,
"flags": [],
"fstype": "linux-swap(v1)",
"name": "",
"num": 2,
"size": 1046528.0,
"unit": "s"
}
]
}
sda_info.partitions is a list with 2 dictionaries. The 2nd dictionary can be referenced
sda_info.partitions[1]
and the attribute size can be assigned to a variable
- set_fact:
size_2nd_part: "{{ sda_info.partitions[1].size }}"
Very probably the list will be sorted by the number of the partition. If not the variable above won't be the size of the 2nd partition. Use json_query to be sure
- set_fact:
size_2nd_part: "{{ sda_info.partitions|json_query('[?num==`2`].size') }}"

How to access duplicate values in a JSON array and set each to a unique variable in ansible?

I am running an ansible playbook which outputs my information in JSON. This array has a loop that has multiple identical keys with different values. Please see below, I am interested in the "intf_id":
"stdout": [
{
"TABLE_cdp_neighbor_brief_info": {
"ROW_cdp_neighbor_brief_info": [
{
"capability": [
"switch",
"IGMP_cnd_filtering"
],
"device_id": "osw1J15",
"ifindex": "83886080",
"intf_id": "mgmt0",
"platform_id": "cisco WS-C2960X-48TS-LL",
"port_id": "GigabitEthernet0/45",
"ttl": "160"
},
{
"capability": [
"router",
"switch",
"Supports-STP-Dispute"
],
"device_id": "spine01",
"ifindex": "436232192",
"intf_id": "Ethernet1/49",
"platform_id": "N9K-C9508",
"port_id": "Ethernet1/11",
"ttl": "159"
},
{
"capability": [
"router",
"switch",
"Supports-STP-Dispute"
],
"device_id": "spine02",
"ifindex": "436232704",
"intf_id": "Ethernet1/50",
"platform_id": "N9K-C9508",
"port_id": "Ethernet1/11",
"ttl": "127"
},
{
"capability": [
"router",
"switch",
"IGMP_cnd_filtering",
"Supports-STP-Dispute"
],
"device_id": "leaf1J1402",
"ifindex": "436234240",
"intf_id": "Ethernet1/53",
"platform_id": "N9K-C93180YC-EX",
"port_id": "Ethernet1/53",
"ttl": "175"
},
{
"capability": [
"router",
"switch",
"IGMP_cnd_filtering",
"Supports-STP-Dispute"
],
"device_id": "leaf1J1402",
"ifindex": "436234752",
"intf_id": "Ethernet1/54",
"platform_id": "N9K-C93180YC-EX",
"port_id": "Ethernet1/54",
"ttl": "175"
}
]
},
"neigh_count": "5"
}
]
Currently I can access the first iteration of "intf_id" with :
- debug: msg="{{ list.stdout[0].TABLE_cdp_neighbor_brief_info.ROW_cdp_neighbor_brief_info[0].intf_id }}"
I feel like I'm close, but cannot figure out how to get each iteration of "intf_id". I can get subsequent ones by changing ROW_cdp_neighbor_brief_info[0] to [1] or [2], etc. I need to be able to access each key, without knowing how many keys there will be. Each value to the keys also need to be individually callable by subsequent tasks.
1) How do I get ansible to debug each iteration?
2) Depending on the answer to 1, how can I assign a particualr variable to each value? (ie. interface1, interface2, interface3...)
ansible-playbook json_query.yml
tasks:
- name:
debug:
msg: "{{ item }}"
loop: "{{ stdout | json_query('TABLE_cdp_neighbor_brief_info.ROW_cdp_neighbor_brief_info[*].intf_id') }}"
(output abridged)
"msg": "mgmt0"
"msg": "Ethernet1/49"
"msg": "Ethernet1/50"
"msg": "Ethernet1/53"
"msg": "Ethernet1/54"
You can use the filter json_query with jmespath syntax to loop over the various elements intf_id this way
---
- hosts: localhost
gather_facts: no
tasks:
- name: populate router data from json
set_fact:
data: "{{ lookup('file','so-router-info.json') }}"
- name: looping over interfaces
debug:
var: item
loop: "{{ data | json_query('TABLE_cdp_neighbor_brief_info.ROW_cdp_neighbor_brief_info[*].intf_id') }}"
it gives this output
…
TASK [looping over interfaces] ********************************************************************************************************
ok: [localhost] => (item=mgmt0) => {
"item": "mgmt0"
}
ok: [localhost] => (item=Ethernet1/49) => {
"item": "Ethernet1/49"
}
ok: [localhost] => (item=Ethernet1/50) => {
"item": "Ethernet1/50"
…
You can use the site http://jmespath.org/ to tests filters

Ansible - get list of multiple attributes and do lineinfile

I've got multiple values under mounts, I want to have all 'mountpoints' of these attributes.
"mounts": {
"/dev/sdb": {
"fstype": "xfs",
"mountpoint": "/my/point1",
"opts": "defaults,_netdev",
"partition": "/dev/sdb1",
"state": "mounted"
},
"/dev/sdc": {
"fstype": "xfs",
"mountpoint": "/my/point2",
"opts": "defaults,_netdev",
"partition": "/dev/sdc1",
"state": "mounted"
},
"/dev/sdd": {
"fstype": "xfs",
"mountpoint": "/my/point3",
"opts": "defaults,_netdev",
"partition": "/dev/sdd1",
"state": "mounted"
How do I register the three mountpoints in memory for later use, so I get:
/my/point1, /my/point2, /my/point3
I want to place the values in /etc/updatedb.conf with lineinfile, so there should be no spacing.
My end result would look like; cat /etc/updatedb.conf
PRUNEPATHS = "/my/point1 /my/point2 /my/point3"
Currently, I use a template to copy pre-defined variables. But this is not dynamic enough.
I'm playing around how to get the right variables, but no success:
- debug: var=mount[all].mountpoints
To get a list:
- debug: msg="{{ mounts.values() | map(attribute='mountpoint') | list }}"
Or a string:
- debug: msg="{{ mounts.values() | map(attribute='mountpoint') | list | join(' ') }}"
EDIT:
You can get the join of the keys from mounts like this:
mounts: {{ mounts.keys()|join(', ') }}
(old answer that is not valid for this question)
as this describes, you can do
---
- hosts: all
tasks:
- set_fact: mounts={{ ansible_mounts | map(attribute='device')|join(',')}}
- debug: var=ansible_mounts
- debug: var=mounts
sample output
ap test.yml -i hosts -l server
PLAY [all] *********************************************************************
TASK [setup] *******************************************************************
ok: [server]
TASK [set_fact] ****************************************************************
ok: [server]
TASK [debug] *******************************************************************
ok: [server] => {
"ansible_mounts": [
{
"device": "/dev/sda1",
"fstype": "ext4",
"mount": "/",
"options": "rw,errors=remount-ro",
"size_available": 2890289152,
"size_total": 9376751616,
"uuid": "N/A"
},
{
"device": "/dev/sdb1",
"fstype": "ext4",
"mount": "/mnt/data1",
"options": "rw",
"size_available": 50684461056,
"size_total": 200674758656,
"uuid": "N/A"
}
]
}
TASK [debug] *******************************************************************
ok: [server] => {
"mounts": "/dev/sda1,/dev/sdb1"
}
PLAY RECAP *********************************************************************
server : ok=4 changed=0 unreachable=0 failed=0

Resources