Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ansible systemd mount dependency

I use Ansible to deploy a systemd unit file to all the servers in an estate and enable it to start on boot. On some of those servers, the binary defined in the unit file is on a different filesystem. This requires the unit file to have a dependency on that filesystem being mounted. E.g.:

[Unit]
Description = Start the widget
After = network.target usr.mount

[Service]
Type = simple
ExecStart = /usr/bin/widget

[Install]
WantedBy = multi-user.target

Currently the unit file is deployed by Ansible as a static file, using the ansible.builtin.copy module. Is there a way I can convert this to a template and append the mountpoint to After = if /usr is a mountpoint?

like image 331
Steve Crook Avatar asked Jul 10 '26 21:07

Steve Crook


1 Answers

Ansible fact ansible_mounts keeps the list of the mount points. Let's use the following vars for testing

    widgets:
      - /usr/local/apps/widget
      - /usr/share/apps/widget
      - /scratch/apps/widget
    my_ansible_mounts:
      - mount: /
      - mount: /boot/efi
      - mount: /usr
      - mount: /usr/local
    root:
      - /

Create a dictionary of widgets and related mount points, e.g.

    - set_fact:
        _mlist: []
    - set_fact:
        _mlist: "{{ _mlist + [{'dict': item.0, 'mount': item.1}] }}"
      with_nested:
        - "{{ widgets }}"
        - "{{ my_ansible_mounts|map(attribute='mount')|difference(root) }}"
      when: item.0|regex_search('^' ~ item.1) != None
    - set_fact:
        _mdict: {}
    - set_fact:
        _mdict: "{{ _mdict|combine({item.0: item.1|
                                            map(attribute='mount')|
                                            map('regex_replace', '/', '-')|
                                            list}) }}"
      loop: "{{ _mlist|groupby('dict') }}"

gives

  _mdict:
    /usr/local/apps/widget:
    - -usr
    - -usr-local
    /usr/share/apps/widget:
    - -usr

Then the flow should be trivial, e.g.

    - debug:
        msg: |-
          {% if _mdict[item]|default([])|length > 0 %}
          [Unit]
          Description = Start the widget
          After = network.target{% for i in _mdict[item] %} {{ i[1:] }}.mount{% endfor %}


          {% endif %}
          [Service]
          Type = simple
          ExecStart = {{ item }}
      loop: "{{ widgets }}"

gives

ok: [localhost] => (item=/usr/local/apps/widget) => 
  msg: |-
    [Unit]
    Description = Start the widget
    After = network.target usr.mount usr-local.mount
  
    [Service]
    Type = simple
    ExecStart = /usr/local/apps/widget
ok: [localhost] => (item=/usr/share/apps/widget) => 
  msg: |-
    [Unit]
    Description = Start the widget
    After = network.target usr.mount
  
    [Service]
    Type = simple
    ExecStart = /usr/share/apps/widget
ok: [localhost] => (item=/scratch/apps/widget) => 
  msg: |-
    [Service]
    Type = simple
    ExecStart = /scratch/apps/widget

To test the real stuff, in the code, replace my_ansible_mounts with ansible_mounts and fit the widgets and hosts to your needs.

like image 146
Vladimir Botka Avatar answered Jul 13 '26 20:07

Vladimir Botka



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!