'SubString in Ansible and/or Jinja2
I'm trying to unmout all mountpoints, excepted if they are part of the current list:
excluded: ['home', 'cdrom', 'tmpfs', 'sys', 'run', 'dev', 'root']
Sample fstab only devices:
- /dev/mapper/vgroot-local_home
- devtmpfs
- tmpfs
/dev/mapper/vgroot-local_home should be excluded from unmounting because the substring home is present on the array and the same for devtmpfs substring tmpfs. For tmpfs we have a perfect match. The goal is to check against devices.
After checking all Ansible filters and the Jinja2 documentation, I didn't find a solution to this problem. All Ansible facts are collected.
- name: Ensure the mountpoint is on the excluded list
ansible.posix.mount:
path: '{{ mount.device }}'
state: unmounted
when: {{ ??? }}
with_items: '{{ ??? }}'
become: true
tags: mountpoints
Solution 1:[1]
To test if a string contains a substring in Jinja, we use the in test, much like Python:
"somestring" in somevariable
In your case, you want to check if a given string contains any substring from the excluded list. Conceptually, what we want is something like the Python expression
if any(x in mount.device for x in excluded)
Using Jinja filters, we need to reverse our logic a little bit. We
can use the select filter to get a list of strings from the
excluded list that are contained in a given target string (such as
mount.device) like this:
excluded|select('in', item)
If item matches anything in the excluded list, the above
expression will result in a non-empty list (which evaluates to true
when used in a boolean context).
Used in a playbook, it would look like this:
- hosts: localhost
gather_facts: false
vars:
excluded: ['home', 'cdrom', 'tmpfs', 'sys', 'run', 'dev', 'root']
mounts:
- /dev/mapper/vgroot-local_home
- devtmpfs
- tmpfs
- something/else
tasks:
- debug:
msg: "unmount {{ item }}"
when: not excluded|select('in', item)
loop: "{{ mounts }}"
The above playbook produces as output:
TASK [debug] *******************************************************************
skipping: [localhost] => (item=/dev/mapper/vgroot-local_home)
skipping: [localhost] => (item=devtmpfs)
skipping: [localhost] => (item=tmpfs)
ok: [localhost] => (item=something/else) => {
"msg": "unmount something/else"
}
That is, it skips the task when the current loop item contains a
substring from the excluded list.
Assuming that your goal is "unmount all filesystems except those for
which the device name contains a substring from the excluded list",
you might write:
- name: Unmount filesystems that aren't excluded
ansible.posix.mount:
path: '{{ mount.device }}'
state: unmounted
when: not excluded|select('in', item.device)
loop: "{{ ansible_mounts }}"
become: true
tags: mountpoints
Solution 2:[2]
Iterate basename if you don't want to exclude the items of mounts because of matching the path, e.g. if you don't want to exclude /dev/mapper/vgroot-local_home because of dev in the excluded list
- debug:
msg: "Unmount {{ item }}"
loop: "{{ mounts|map('basename') }}"
when: not excluded|select('in', item)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | larsks |
| Solution 2 | Vladimir Botka |
