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?
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.