From 3a5263a92f01d486996e35f7ce05ddb1f1b79c9d Mon Sep 17 00:00:00 2001
From: Martin Krizek <martin.krizek@gmail.com>
Date: Fri, 16 Feb 2018 16:52:51 +0100
Subject: [PATCH] facts: fix device uuid's on el6 (#36128)

* facts: fix device uuid's on el6

Fixes #36077
---
 .../module_utils/facts/hardware/linux.py      | 32 ++++++++++++++++++-
 .../module_utils/facts/hardware/linux_data.py | 22 +++++++++++++
 .../module_utils/facts/hardware/test_linux.py | 14 ++++++--
 test/units/module_utils/facts/test_facts.py   |  6 +++-
 4 files changed, 70 insertions(+), 4 deletions(-)

diff --git a/lib/ansible/module_utils/facts/hardware/linux.py b/lib/ansible/module_utils/facts/hardware/linux.py
index e781419d98f..b758d11bf72 100644
--- a/lib/ansible/module_utils/facts/hardware/linux.py
+++ b/lib/ansible/module_utils/facts/hardware/linux.py
@@ -372,6 +372,31 @@ class LinuxHardware(Hardware):
 
         return uuids
 
+    def _udevadm_uuid(self, device):
+        # fallback for versions of lsblk <= 2.23 that don't have --paths, see _run_lsblk() above
+        uuid = 'N/A'
+
+        udevadm_path = self.module.get_bin_path('udevadm')
+        if not udevadm_path:
+            return uuid
+
+        cmd = [udevadm_path, 'info', '--query', 'property', '--name', device]
+        rc, out, err = self.module.run_command(cmd)
+        if rc != 0:
+            return uuid
+
+        # a snippet of the output of the udevadm command below will be:
+        # ...
+        # ID_FS_TYPE=ext4
+        # ID_FS_USAGE=filesystem
+        # ID_FS_UUID=57b1a3e7-9019-4747-9809-7ec52bba9179
+        # ...
+        m = re.search('ID_FS_UUID=(.*)\n', out)
+        if m:
+            uuid = m.group(1)
+
+        return uuid
+
     def _run_findmnt(self, findmnt_path):
         args = ['--list', '--noheadings', '--notruncate']
         cmd = [findmnt_path] + args
@@ -442,11 +467,16 @@ class LinuxHardware(Hardware):
                 if not self.MTAB_BIND_MOUNT_RE.match(options):
                     options += ",bind"
 
+            # _udevadm_uuid is a fallback for versions of lsblk <= 2.23 that don't have --paths
+            # see _run_lsblk() above
+            # https://github.com/ansible/ansible/issues/36077
+            uuid = uuids.get(device, self._udevadm_uuid(device))
+
             mount_info = {'mount': mount,
                           'device': device,
                           'fstype': fstype,
                           'options': options,
-                          'uuid': uuids.get(device, 'N/A')}
+                          'uuid': uuid}
 
             mount_info.update(mount_statvfs_info)
 
diff --git a/test/units/module_utils/facts/hardware/linux_data.py b/test/units/module_utils/facts/hardware/linux_data.py
index 61f0d0a5113..f41c4d6f70b 100644
--- a/test/units/module_utils/facts/hardware/linux_data.py
+++ b/test/units/module_utils/facts/hardware/linux_data.py
@@ -47,6 +47,28 @@ LSBLK_OUTPUT_2 = b"""
 
 LSBLK_UUIDS = {'/dev/sda1': '66Ojcd-ULtu-1cZa-Tywo-mx0d-RF4O-ysA9jK'}
 
+UDEVADM_UUID = 'N/A'
+
+UDEVADM_OUTPUT = """
+UDEV_LOG=3
+DEVPATH=/devices/pci0000:00/0000:00:07.0/virtio2/block/vda/vda1
+MAJOR=252
+MINOR=1
+DEVNAME=/dev/vda1
+DEVTYPE=partition
+SUBSYSTEM=block
+MPATH_SBIN_PATH=/sbin
+ID_PATH=pci-0000:00:07.0-virtio-pci-virtio2
+ID_PART_TABLE_TYPE=dos
+ID_FS_UUID=57b1a3e7-9019-4747-9809-7ec52bba9179
+ID_FS_UUID_ENC=57b1a3e7-9019-4747-9809-7ec52bba9179
+ID_FS_VERSION=1.0
+ID_FS_TYPE=ext4
+ID_FS_USAGE=filesystem
+LVM_SBIN_PATH=/sbin
+DEVLINKS=/dev/block/252:1 /dev/disk/by-path/pci-0000:00:07.0-virtio-pci-virtio2-part1 /dev/disk/by-uuid/57b1a3e7-9019-4747-9809-7ec52bba9179
+"""
+
 MTAB = """
 sysfs /sys sysfs rw,seclabel,nosuid,nodev,noexec,relatime 0 0
 proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
diff --git a/test/units/module_utils/facts/hardware/test_linux.py b/test/units/module_utils/facts/hardware/test_linux.py
index b5dd81892c9..f581c8068be 100644
--- a/test/units/module_utils/facts/hardware/test_linux.py
+++ b/test/units/module_utils/facts/hardware/test_linux.py
@@ -25,7 +25,7 @@ from ansible.module_utils.facts import timeout
 
 from ansible.module_utils.facts.hardware import linux
 
-from . linux_data import LSBLK_OUTPUT, LSBLK_OUTPUT_2, LSBLK_UUIDS, MTAB, MTAB_ENTRIES, BIND_MOUNTS, STATVFS_INFO
+from . linux_data import LSBLK_OUTPUT, LSBLK_OUTPUT_2, LSBLK_UUIDS, MTAB, MTAB_ENTRIES, BIND_MOUNTS, STATVFS_INFO, UDEVADM_UUID, UDEVADM_OUTPUT
 
 with open(os.path.join(os.path.dirname(__file__), '../fixtures/findmount_output.txt')) as f:
     FINDMNT_OUTPUT = f.read()
@@ -50,11 +50,13 @@ class TestFactsLinuxHardwareGetMountFacts(unittest.TestCase):
     @patch('ansible.module_utils.facts.hardware.linux.LinuxHardware._find_bind_mounts', return_value=BIND_MOUNTS)
     @patch('ansible.module_utils.facts.hardware.linux.LinuxHardware._lsblk_uuid', return_value=LSBLK_UUIDS)
     @patch('ansible.module_utils.facts.hardware.linux.get_mount_size', side_effect=mock_get_mount_size)
+    @patch('ansible.module_utils.facts.hardware.linux.LinuxHardware._udevadm_uuid', return_value=UDEVADM_UUID)
     def test_get_mount_facts(self,
                              mock_get_mount_size,
                              mock_lsblk_uuid,
                              mock_find_bind_mounts,
-                             mock_mtab_entries):
+                             mock_mtab_entries,
+                             mock_udevadm_uuid):
         module = Mock()
         # Returns a LinuxHardware-ish
         lh = linux.LinuxHardware(module=module, load_on_init=False)
@@ -162,3 +164,11 @@ class TestFactsLinuxHardwareGetMountFacts(unittest.TestCase):
         self.assertIn(b'/dev/sda1', lsblk_uuids)
         self.assertEqual(lsblk_uuids[b'/dev/mapper/an-example-mapper with a space in the name'], b'84639acb-013f-4d2f-9392-526a572b4373')
         self.assertEqual(lsblk_uuids[b'/dev/sda1'], b'32caaec3-ef40-4691-a3b6-438c3f9bc1c0')
+
+    def test_udevadm_uuid(self):
+        module = Mock()
+        module.run_command = Mock(return_value=(0, UDEVADM_OUTPUT, ''))  # (rc, out, err)
+        lh = linux.LinuxHardware(module=module, load_on_init=False)
+        udevadm_uuid = lh._udevadm_uuid('mock_device')
+
+        self.assertEqual(udevadm_uuid, '57b1a3e7-9019-4747-9809-7ec52bba9179')
diff --git a/test/units/module_utils/facts/test_facts.py b/test/units/module_utils/facts/test_facts.py
index 7ee36ae2100..d83058bb926 100644
--- a/test/units/module_utils/facts/test_facts.py
+++ b/test/units/module_utils/facts/test_facts.py
@@ -265,6 +265,8 @@ LSBLK_OUTPUT_2 = b"""
 
 LSBLK_UUIDS = {'/dev/sda1': '66Ojcd-ULtu-1cZa-Tywo-mx0d-RF4O-ysA9jK'}
 
+UDEVADM_UUID = 'N/A'
+
 MTAB = """
 sysfs /sys sysfs rw,seclabel,nosuid,nodev,noexec,relatime 0 0
 proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
@@ -536,10 +538,12 @@ class TestFactsLinuxHardwareGetMountFacts(unittest.TestCase):
     @patch('ansible.module_utils.facts.hardware.linux.LinuxHardware._mtab_entries', return_value=MTAB_ENTRIES)
     @patch('ansible.module_utils.facts.hardware.linux.LinuxHardware._find_bind_mounts', return_value=BIND_MOUNTS)
     @patch('ansible.module_utils.facts.hardware.linux.LinuxHardware._lsblk_uuid', return_value=LSBLK_UUIDS)
+    @patch('ansible.module_utils.facts.hardware.linux.LinuxHardware._udevadm_uuid', return_value=UDEVADM_UUID)
     def test_get_mount_facts(self,
                              mock_lsblk_uuid,
                              mock_find_bind_mounts,
-                             mock_mtab_entries):
+                             mock_mtab_entries,
+                             mock_udevadm_uuid):
         module = Mock()
         # Returns a LinuxHardware-ish
         lh = hardware.linux.LinuxHardware(module=module, load_on_init=False)