3

I need to set up an application directory for a "hairy" app. Depending on the case, the directory may be local to each server participating, or shared among several servers via NFS.

So, I need to be able to detect, whether the given path is local or NFS-accessed and skip some of the tasks in the latter case.

What's the best way to detect this in an Ansible role?

I tried using the stat module, but device_type seems to be set to 0 in all cases, NFS or local (XFS).

On Linux I could invoke stat -f /path -- this would output details, including the type (the utility uses the statfs syscall). But this is a Linux-only method, and I'd rather avoid such petty OS-dependencies (same goes for the mountpoint utility).

I would write a custom library function, but there is no os.statfs in Python...

What's left?

Mikhail T.
  • 3,043
  • 3
  • 29
  • 46
  • Recent Ansible versions can execute binary modules as well. Make a tiny statically compiled module in C or Go. – Konstantin Suvorov Mar 31 '18 at 09:14
  • That's even more OS-dependent, though... I'll need an executable for every target OS/release/architecture :( Will Ansible even be smart enough to look in different library/ subdirectories for such executables? – Mikhail T. Mar 31 '18 at 19:25
  • The solutions that were ruled-out here as too platform-specific actually worked for me! In case anyone would like to try them, I posted my production Ansible code here: https://stackoverflow.com/a/49615669/1268949 – Isac Casapu Apr 02 '18 at 17:23

2 Answers2

2

Another approach would make use of Ansible facts. You could filter the ansible_mounts array for your mount=your mount point and extract the filesystem type field. For an example, please see the answer I got here: https://stackoverflow.com/a/49662135/1268949 .

Another example from my production code:

- name: Determine shared-dir mount point
command: "/usr/bin/env stat -c '%m' {{ shared_dir_real_path }}"
register: shared_dir_mount_point
changed_when: False

- name: Determine the mount point's filesystem type and mount options
set_fact:
    "shared_dir_mount_{{ item }}": "{{ ansible_mounts | selectattr('mount', 'equalto', shared_dir_mount_point.stdout) | map(attribute = item) | join(',') }}"
with_items:
    - fstype
    - options
Isac Casapu
  • 1,163
  • 13
  • 21
1

If the GNU stat utility is available on your target platforms, then you can invoke it in a way that doesn't make use of the statfs call to detect the mount-point and then search it in the output of mount, e.g. on Linux:

$ mount | grep -F `stat -c %m /boot/grub` | cut -d' ' -f5
ext2

I verified that this invocation of stat only makes use of standard system calls (see CONFORMING TO in the man page of stat(2)):

$ strace stat -c %m /boot/grub/ |& fgrep stat
execve("/usr/bin/stat", ["stat", "-c", "%m", "/boot/grub/"], [/* 65 vars */]) = 0
fstat(3, {st_mode=S_IFREG|0644, st_size=218501, ...}) = 0
fstat(3, {st_mode=S_IFREG|0644, st_size=130224, ...}) = 0
fstat(3, {st_mode=S_IFREG|0755, st_size=1868984, ...}) = 0
fstat(3, {st_mode=S_IFREG|0644, st_size=456632, ...}) = 0
fstat(3, {st_mode=S_IFREG|0644, st_size=14608, ...}) = 0
fstat(3, {st_mode=S_IFREG|0755, st_size=138696, ...}) = 0
statfs("/sys/fs/selinux", 0x7ffe62882ff0) = -1 ENOENT (No such file or directory)
statfs("/selinux", 0x7ffe62882ff0)      = -1 ENOENT (No such file or directory)
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
fstat(3, {st_mode=S_IFREG|0644, st_size=5152256, ...}) = 0
lstat("/boot/grub/", {st_mode=S_IFDIR|0755, st_size=1024, ...}) = 0
lstat("/boot", {st_mode=S_IFDIR|0755, st_size=3072, ...}) = 0
lstat("/boot/grub", {st_mode=S_IFDIR|0755, st_size=1024, ...}) = 0
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
stat("/boot/grub", {st_mode=S_IFDIR|0755, st_size=1024, ...}) = 0
stat("..", {st_mode=S_IFDIR|0755, st_size=3072, ...}) = 0
stat("..", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/boot", {st_mode=S_IFDIR|0755, st_size=3072, ...}) = 0
fstat(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
Isac Casapu
  • 1,163
  • 13
  • 21
  • Thanks, but that's still Linux-only. On BSD, for example, stat is rather different. And output of mount is different too :( – Mikhail T. Apr 03 '18 at 16:37
  • Are GNU coreutils available for BSD? Seems like many GNU utilities have ports to OS/X which is BSD-derived via *brew* etc. If worse comes to worst you can always have multiple version of the step that determines this, and run the right one according to appropriate *when* conditions. – Isac Casapu Apr 03 '18 at 17:50
  • 1
    On FreeBSD they are available, but the `stat` executable is installed as `gnustat`. It could work. Instead of parsing its output -- and relying on mount's output format -- one could just `stat -f --format=%T /path/of/interest`. I guess, that's how I'd do it... Please, update your answer so I can "accept" it. – Mikhail T. Apr 03 '18 at 17:58
  • %T means "minor device type in hex", so just be sure you're relying on a documented interface here... – Isac Casapu Apr 03 '18 at 18:02
  • 1
    No, when stat-ing filesystems (with the `-f` flag), `%T` means "file system type in human readable form". – Mikhail T. Apr 03 '18 at 18:06