Max Xapi
Max Xapi

Reputation: 815

Ansible - Check if elements from a list don't have the same value in another dictionary

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

Answers (2)

Vladimir Botka
Vladimir Botka

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

Zeitounator
Zeitounator

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

Related Questions