Repository: ansible/ansible
Pull request: facts: fix device uuid's on el6
##### SUMMARY
<!--- Describe the change, including rationale and design decisions -->
Fixes https://github.com/ansible/ansible/issues/36077
<!---
If you are fixing an existing issue, please include "Fixes #nnn" in your
commit message and your description; but you should still explain what
the change does.
-->

##### ISSUE TYPE
 - Bugfix Pull Request

##### COMPONENT NAME
facts

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
devel
```


##### ADDITIONAL INFORMATION

Problem statement:
RHEL 6 Mount Facts Display "N/A" for UUID
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and devel branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
 - Bug Report

##### COMPONENT NAME
setup

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible 2.6.0 (devel 30adc2f124) last updated 2018/02/12 16:26:41 (GMT -400)
  config file = /Users/user/.ansible.cfg
  configured module search path = [u'/Users/user/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /Users/user/ansible-venv/devel/git-ansible/lib/ansible
  executable location = /Users/user/ansible-venv/devel/git-ansible/bin/ansible
  python version = 2.7.10 (default, Jul 15 2017, 17:16:57) [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)]
```

##### CONFIGURATION
<!---
If using Ansible 2.4 or above, paste the results of "ansible-config dump --only-changed"
Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->
HOST_KEY_CHECKING(/Users/user/.ansible.cfg) = False

##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.
-->
N/A

##### SUMMARY
Facts returned for ansible_mounts against RHEL 6 hosts result in "N/A" rather than printing the UUID

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->

<!--- Paste example playbooks or commands between quotes below -->
```
ansible rhel-68-box -m setup -a "filter=ansible_mounts"
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
Same UUID as displayed from
```
$ ls -l /dev/disk/by-uuid
total 0
lrwxrwxrwx. 1 root root 11 Feb 12 16:15 05d6faee-1d92-462b-be37-aaa0097875e8 -> ../../xvda1
```

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
```
rhel-68-box | SUCCESS => {
    "ansible_facts": {
        "ansible_mounts": [
            {
                "block_available": 1981017,
                "block_size": 4096,
                "block_total": 2547273,
                "block_used": 566256,
                "device": "/dev/xvda1",
                "fstype": "ext4",
                "inode_available": 598737,
                "inode_total": 655360,
                "inode_used": 56623,
                "mount": "/",
                "options": "rw",
                "size_available": 8114245632,
                "size_total": 10433630208,
                "uuid": "N/A"
            }
        ]
    },
    "changed": false,
    "failed": false
}
```

```
$ cat /etc/redhat-release && lsblk --list --noheadings --paths --output NAME,UUID --exclude 2
Red Hat Enterprise Linux Server release 6.8 (Santiago)
lsblk: unrecognized option '--paths'
```

Hints from issue discussion:
Files identified in the description:
* [lib/ansible/modules/system/setup.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/system/setup.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->

Commit message: Fix unit tests

Patch to review:
diff --git a/lib/ansible/module_utils/facts/hardware/linux.py b/lib/ansible/module_utils/facts/hardware/linux.py
index e781419d98fdfd..274d3f623ae6a3 100644
--- a/lib/ansible/module_utils/facts/hardware/linux.py
+++ b/lib/ansible/module_utils/facts/hardware/linux.py
@@ -372,6 +372,25 @@ def _lsblk_uuid(self):
 
         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=%s' % device]
+        rc, out, err = self.module.run_command(cmd)
+        if rc != 0:
+            return uuid
+
+        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 +461,16 @@ def get_mount_facts(self):
                 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 61f0d0a5113170..befcb3f197d9ad 100644
--- a/test/units/module_utils/facts/hardware/linux_data.py
+++ b/test/units/module_utils/facts/hardware/linux_data.py
@@ -47,6 +47,8 @@
 
 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
diff --git a/test/units/module_utils/facts/hardware/test_linux.py b/test/units/module_utils/facts/hardware/test_linux.py
index b5dd81892c906b..6624e8b51c9d38 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.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
 
 with open(os.path.join(os.path.dirname(__file__), '../fixtures/findmount_output.txt')) as f:
     FINDMNT_OUTPUT = f.read()
@@ -50,11 +50,13 @@ def tearDown(self):
     @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)
diff --git a/test/units/module_utils/facts/test_facts.py b/test/units/module_utils/facts/test_facts.py
index 7ee36ae2100981..d83058bb926a84 100644
--- a/test/units/module_utils/facts/test_facts.py
+++ b/test/units/module_utils/facts/test_facts.py
@@ -265,6 +265,8 @@ class TestSunOSVirtual(BaseTestFactsPlatform):
 
 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 @@ def tearDown(self):
     @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)


Code review comments:
diff_hunk: @@ -372,6 +372,25 @@ def _lsblk_uuid(self):
 
         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=%s' % device]
+        rc, out, err = self.module.run_command(cmd)
+        if rc != 0:
+            return uuid
+
+        m = re.search('ID_FS_UUID=(.*)\n', out)
path: lib/ansible/module_utils/facts/hardware/linux.py
text: @user1:
I would like to see an example output of 'udevadm info --query property --name  $DEVICE' and a test case that uses it to verify the regex here. It may be easier to test if the parsing bit is split into a separate method.

(granted, that is a very simple regex at the moment...)

@author:
:+1: unit test added