Reputation: 815
I'm working with lists, dictionary of lists, etc. and I'm kinda lost with it.
Functionnally speaking, I'm trying to know if mounts from an input list are all mounted on a different device.
So in input I have a list of mounts defined in a variable, ie:
mounts:
- '/'
- '/home'
- '/foo'
And facts like that:
"ansible_mounts": [
{
"device": "/device1",
"mount": "/"
},
{
"device": "/device1",
"mount": "/home"
},
{
"device": "/device2",
"mount": "/boot"
},
{
"device": "/device2",
"mount": "/bar"
},
{
"device": "/device3",
"mount": "/foo"
}
]
Thus I need to build a task that in this example would fail because both '/'
and '/home'
are mounted on '/device1'
(and we don't care about '/bar'
mounted on '/device2'
like '/boot'
because '/bar'
it's not in the input list).
I've suceeded in having the following elements: a dictionary with the device as key and all the related mounts as values:
"filesystems_by_device": {
"/device1": [
"/",
"/home"
],
"/device2": [
"/boot",
"/bar"
],
"/device3": [
"/foo"
]
}
And a dictionary with the mount as key and the device(s) as value (it could indeed be a single value instead of a list):
"filesystems_by_mount": {
"/": [
"/device1"
],
"/home": [
"/device1"
],
"/boot": [
"/device2"
],
"/bar": [
"/device2"
],
"/foo": [
"/device3"
]
}
I don't know how to mix these data to build the appropriate task. I've tried several solutions, made a lot of tests but right now I need a fresh eye on this problem. I'm lost with that syntax.
Any help appreciated
Note: this could be easily done in a static way or with a bash script called by the appropriate module, but I really want to avoid this and keep this "dynamic" way of doing it
Upvotes: 1
Views: 1675
Reputation: 68394
Q: "Mounts from an input list are all mounted on a different device ... Fail because both '/' and '/home' are mounted on '/device1'."
A: The task below
- assert:
that: my_mounts|intersect(mounts)|length <= 1
fail_msg: "[ERR] Too many mounts {{ my_mounts }} on {{ item.0 }}"
loop: "{{ ansible_mounts|groupby('device') }}"
vars:
my_mounts: "{{ item.1|map(attribute='mount')|list }}"
gives (abridged)
failed: ...
ansible_loop_var: item
assertion: my_mounts|intersect(mounts)|length <= 1
evaluated_to: false
item:
- /device1
- - device: /device1
mount: /
- device: /device1
mount: /home
msg: '[ERR] Too many mounts [''/'', ''/home''] on /device1'
ansible_loop_var: item
item:
- /device2
- - device: /device2
mount: /boot
- device: /device2
mount: /bar
msg: All assertions passed
ansible_loop_var: item
item:
- /device3
- - device: /device3
mount: /foo
msg: All assertions passed
Upvotes: 2
Reputation: 44808
You have your list of mounts. This one has a length
of X mounts you want to check.
You have a dictionary with mount points as keys and corresponding device as values.
From there you extract the values of list of devices (would be easier if it was a single string...) for each mount point, put them in a flattened list, make that list unique and get its length. You fail the task if the length of this list is different from the length of mount points.
An example always being better that a long speach:
- name: Fail if my mounts are not on separate devices
vars:
mount_num: "{{ mounts | length }}"
mount_devices: "{{ mounts | map('extract', filesystems_by_mount) | list | flatten | unique }}"
device_num: "{{ mount_devices | length }}"
fail:
msg: "Two or more mount are on the same device"
when: mount_num != device_num
Upvotes: 1