1# This file is part of Ansible
2# -*- coding: utf-8 -*-
3#
4#
5# Ansible is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# Ansible is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
17#
18
19# Make coding more python3-ish
20from __future__ import (absolute_import, division)
21__metaclass__ = type
22
23import os
24
25import pytest
26
27# for testing
28from units.compat import unittest
29from units.compat.mock import Mock, patch
30
31from ansible.module_utils import facts
32from ansible.module_utils.facts import hardware
33from ansible.module_utils.facts import network
34from ansible.module_utils.facts import virtual
35
36
37class BaseTestFactsPlatform(unittest.TestCase):
38    platform_id = 'Generic'
39    fact_class = hardware.base.Hardware
40    collector_class = None
41
42    """Verify that the automagic in Hardware.__new__ selects the right subclass."""
43    @patch('platform.system')
44    def test_new(self, mock_platform):
45        if not self.fact_class:
46            pytest.skip('This platform (%s) does not have a fact_class.' % self.platform_id)
47        mock_platform.return_value = self.platform_id
48        inst = self.fact_class(module=Mock(), load_on_init=False)
49        self.assertIsInstance(inst, self.fact_class)
50        self.assertEqual(inst.platform, self.platform_id)
51
52    def test_subclass(self):
53        if not self.fact_class:
54            pytest.skip('This platform (%s) does not have a fact_class.' % self.platform_id)
55        # 'Generic' will try to map to platform.system() that we are not mocking here
56        if self.platform_id == 'Generic':
57            return
58        inst = self.fact_class(module=Mock(), load_on_init=False)
59        self.assertIsInstance(inst, self.fact_class)
60        self.assertEqual(inst.platform, self.platform_id)
61
62    def test_collector(self):
63        if not self.collector_class:
64            pytest.skip('This test class needs to be updated to specify collector_class')
65        inst = self.collector_class()
66        self.assertIsInstance(inst, self.collector_class)
67        self.assertEqual(inst._platform, self.platform_id)
68
69
70class TestLinuxFactsPlatform(BaseTestFactsPlatform):
71    platform_id = 'Linux'
72    fact_class = hardware.linux.LinuxHardware
73    collector_class = hardware.linux.LinuxHardwareCollector
74
75
76class TestHurdFactsPlatform(BaseTestFactsPlatform):
77    platform_id = 'GNU'
78    fact_class = hardware.hurd.HurdHardware
79    collector_class = hardware.hurd.HurdHardwareCollector
80
81
82class TestSunOSHardware(BaseTestFactsPlatform):
83    platform_id = 'SunOS'
84    fact_class = hardware.sunos.SunOSHardware
85    collector_class = hardware.sunos.SunOSHardwareCollector
86
87
88class TestOpenBSDHardware(BaseTestFactsPlatform):
89    platform_id = 'OpenBSD'
90    fact_class = hardware.openbsd.OpenBSDHardware
91    collector_class = hardware.openbsd.OpenBSDHardwareCollector
92
93
94class TestFreeBSDHardware(BaseTestFactsPlatform):
95    platform_id = 'FreeBSD'
96    fact_class = hardware.freebsd.FreeBSDHardware
97    collector_class = hardware.freebsd.FreeBSDHardwareCollector
98
99
100class TestDragonFlyHardware(BaseTestFactsPlatform):
101    platform_id = 'DragonFly'
102    fact_class = None
103    collector_class = hardware.dragonfly.DragonFlyHardwareCollector
104
105
106class TestNetBSDHardware(BaseTestFactsPlatform):
107    platform_id = 'NetBSD'
108    fact_class = hardware.netbsd.NetBSDHardware
109    collector_class = hardware.netbsd.NetBSDHardwareCollector
110
111
112class TestAIXHardware(BaseTestFactsPlatform):
113    platform_id = 'AIX'
114    fact_class = hardware.aix.AIXHardware
115    collector_class = hardware.aix.AIXHardwareCollector
116
117
118class TestHPUXHardware(BaseTestFactsPlatform):
119    platform_id = 'HP-UX'
120    fact_class = hardware.hpux.HPUXHardware
121    collector_class = hardware.hpux.HPUXHardwareCollector
122
123
124class TestDarwinHardware(BaseTestFactsPlatform):
125    platform_id = 'Darwin'
126    fact_class = hardware.darwin.DarwinHardware
127    collector_class = hardware.darwin.DarwinHardwareCollector
128
129
130class TestGenericNetwork(BaseTestFactsPlatform):
131    platform_id = 'Generic'
132    fact_class = network.base.Network
133
134
135class TestHurdPfinetNetwork(BaseTestFactsPlatform):
136    platform_id = 'GNU'
137    fact_class = network.hurd.HurdPfinetNetwork
138    collector_class = network.hurd.HurdNetworkCollector
139
140
141class TestLinuxNetwork(BaseTestFactsPlatform):
142    platform_id = 'Linux'
143    fact_class = network.linux.LinuxNetwork
144    collector_class = network.linux.LinuxNetworkCollector
145
146
147class TestGenericBsdIfconfigNetwork(BaseTestFactsPlatform):
148    platform_id = 'Generic_BSD_Ifconfig'
149    fact_class = network.generic_bsd.GenericBsdIfconfigNetwork
150    collector_class = None
151
152
153class TestHPUXNetwork(BaseTestFactsPlatform):
154    platform_id = 'HP-UX'
155    fact_class = network.hpux.HPUXNetwork
156    collector_class = network.hpux.HPUXNetworkCollector
157
158
159class TestDarwinNetwork(BaseTestFactsPlatform):
160    platform_id = 'Darwin'
161    fact_class = network.darwin.DarwinNetwork
162    collector_class = network.darwin.DarwinNetworkCollector
163
164
165class TestFreeBSDNetwork(BaseTestFactsPlatform):
166    platform_id = 'FreeBSD'
167    fact_class = network.freebsd.FreeBSDNetwork
168    collector_class = network.freebsd.FreeBSDNetworkCollector
169
170
171class TestDragonFlyNetwork(BaseTestFactsPlatform):
172    platform_id = 'DragonFly'
173    fact_class = network.dragonfly.DragonFlyNetwork
174    collector_class = network.dragonfly.DragonFlyNetworkCollector
175
176
177class TestAIXNetwork(BaseTestFactsPlatform):
178    platform_id = 'AIX'
179    fact_class = network.aix.AIXNetwork
180    collector_class = network.aix.AIXNetworkCollector
181
182
183class TestNetBSDNetwork(BaseTestFactsPlatform):
184    platform_id = 'NetBSD'
185    fact_class = network.netbsd.NetBSDNetwork
186    collector_class = network.netbsd.NetBSDNetworkCollector
187
188
189class TestOpenBSDNetwork(BaseTestFactsPlatform):
190    platform_id = 'OpenBSD'
191    fact_class = network.openbsd.OpenBSDNetwork
192    collector_class = network.openbsd.OpenBSDNetworkCollector
193
194
195class TestSunOSNetwork(BaseTestFactsPlatform):
196    platform_id = 'SunOS'
197    fact_class = network.sunos.SunOSNetwork
198    collector_class = network.sunos.SunOSNetworkCollector
199
200
201class TestLinuxVirtual(BaseTestFactsPlatform):
202    platform_id = 'Linux'
203    fact_class = virtual.linux.LinuxVirtual
204    collector_class = virtual.linux.LinuxVirtualCollector
205
206
207class TestFreeBSDVirtual(BaseTestFactsPlatform):
208    platform_id = 'FreeBSD'
209    fact_class = virtual.freebsd.FreeBSDVirtual
210    collector_class = virtual.freebsd.FreeBSDVirtualCollector
211
212
213class TestNetBSDVirtual(BaseTestFactsPlatform):
214    platform_id = 'NetBSD'
215    fact_class = virtual.netbsd.NetBSDVirtual
216    collector_class = virtual.netbsd.NetBSDVirtualCollector
217
218
219class TestOpenBSDVirtual(BaseTestFactsPlatform):
220    platform_id = 'OpenBSD'
221    fact_class = virtual.openbsd.OpenBSDVirtual
222    collector_class = virtual.openbsd.OpenBSDVirtualCollector
223
224
225class TestHPUXVirtual(BaseTestFactsPlatform):
226    platform_id = 'HP-UX'
227    fact_class = virtual.hpux.HPUXVirtual
228    collector_class = virtual.hpux.HPUXVirtualCollector
229
230
231class TestSunOSVirtual(BaseTestFactsPlatform):
232    platform_id = 'SunOS'
233    fact_class = virtual.sunos.SunOSVirtual
234    collector_class = virtual.sunos.SunOSVirtualCollector
235
236
237LSBLK_OUTPUT = b"""
238/dev/sda
239/dev/sda1                             32caaec3-ef40-4691-a3b6-438c3f9bc1c0
240/dev/sda2                             66Ojcd-ULtu-1cZa-Tywo-mx0d-RF4O-ysA9jK
241/dev/mapper/fedora_dhcp129--186-swap  eae6059d-2fbe-4d1c-920d-a80bbeb1ac6d
242/dev/mapper/fedora_dhcp129--186-root  d34cf5e3-3449-4a6c-8179-a1feb2bca6ce
243/dev/mapper/fedora_dhcp129--186-home  2d3e4853-fa69-4ccf-8a6a-77b05ab0a42d
244/dev/sr0
245/dev/loop0                            0f031512-ab15-497d-9abd-3a512b4a9390
246/dev/loop1                            7c1b0f30-cf34-459f-9a70-2612f82b870a
247/dev/loop9                            0f031512-ab15-497d-9abd-3a512b4a9390
248/dev/loop9                            7c1b4444-cf34-459f-9a70-2612f82b870a
249/dev/mapper/docker-253:1-1050967-pool
250/dev/loop2
251/dev/mapper/docker-253:1-1050967-pool
252"""
253
254LSBLK_OUTPUT_2 = b"""
255/dev/sda
256/dev/sda1                            32caaec3-ef40-4691-a3b6-438c3f9bc1c0
257/dev/sda2                            66Ojcd-ULtu-1cZa-Tywo-mx0d-RF4O-ysA9jK
258/dev/mapper/fedora_dhcp129--186-swap eae6059d-2fbe-4d1c-920d-a80bbeb1ac6d
259/dev/mapper/fedora_dhcp129--186-root d34cf5e3-3449-4a6c-8179-a1feb2bca6ce
260/dev/mapper/fedora_dhcp129--186-home 2d3e4853-fa69-4ccf-8a6a-77b05ab0a42d
261/dev/mapper/an-example-mapper with a space in the name 84639acb-013f-4d2f-9392-526a572b4373
262/dev/sr0
263/dev/loop0                           0f031512-ab15-497d-9abd-3a512b4a9390
264"""
265
266LSBLK_UUIDS = {'/dev/sda1': '66Ojcd-ULtu-1cZa-Tywo-mx0d-RF4O-ysA9jK'}
267
268UDEVADM_UUID = 'N/A'
269
270MTAB = """
271sysfs /sys sysfs rw,seclabel,nosuid,nodev,noexec,relatime 0 0
272proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
273devtmpfs /dev devtmpfs rw,seclabel,nosuid,size=8044400k,nr_inodes=2011100,mode=755 0 0
274securityfs /sys/kernel/security securityfs rw,nosuid,nodev,noexec,relatime 0 0
275tmpfs /dev/shm tmpfs rw,seclabel,nosuid,nodev 0 0
276devpts /dev/pts devpts rw,seclabel,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
277tmpfs /run tmpfs rw,seclabel,nosuid,nodev,mode=755 0 0
278tmpfs /sys/fs/cgroup tmpfs ro,seclabel,nosuid,nodev,noexec,mode=755 0 0
279cgroup /sys/fs/cgroup/systemd cgroup rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd 0 0
280pstore /sys/fs/pstore pstore rw,seclabel,nosuid,nodev,noexec,relatime 0 0
281cgroup /sys/fs/cgroup/devices cgroup rw,nosuid,nodev,noexec,relatime,devices 0 0
282cgroup /sys/fs/cgroup/freezer cgroup rw,nosuid,nodev,noexec,relatime,freezer 0 0
283cgroup /sys/fs/cgroup/memory cgroup rw,nosuid,nodev,noexec,relatime,memory 0 0
284cgroup /sys/fs/cgroup/pids cgroup rw,nosuid,nodev,noexec,relatime,pids 0 0
285cgroup /sys/fs/cgroup/blkio cgroup rw,nosuid,nodev,noexec,relatime,blkio 0 0
286cgroup /sys/fs/cgroup/cpuset cgroup rw,nosuid,nodev,noexec,relatime,cpuset 0 0
287cgroup /sys/fs/cgroup/cpu,cpuacct cgroup rw,nosuid,nodev,noexec,relatime,cpu,cpuacct 0 0
288cgroup /sys/fs/cgroup/hugetlb cgroup rw,nosuid,nodev,noexec,relatime,hugetlb 0 0
289cgroup /sys/fs/cgroup/perf_event cgroup rw,nosuid,nodev,noexec,relatime,perf_event 0 0
290cgroup /sys/fs/cgroup/net_cls,net_prio cgroup rw,nosuid,nodev,noexec,relatime,net_cls,net_prio 0 0
291configfs /sys/kernel/config configfs rw,relatime 0 0
292/dev/mapper/fedora_dhcp129--186-root / ext4 rw,seclabel,relatime,data=ordered 0 0
293selinuxfs /sys/fs/selinux selinuxfs rw,relatime 0 0
294systemd-1 /proc/sys/fs/binfmt_misc autofs rw,relatime,fd=24,pgrp=1,timeout=0,minproto=5,maxproto=5,direct 0 0
295debugfs /sys/kernel/debug debugfs rw,seclabel,relatime 0 0
296hugetlbfs /dev/hugepages hugetlbfs rw,seclabel,relatime 0 0
297tmpfs /tmp tmpfs rw,seclabel 0 0
298mqueue /dev/mqueue mqueue rw,seclabel,relatime 0 0
299/dev/loop0 /var/lib/machines btrfs rw,seclabel,relatime,space_cache,subvolid=5,subvol=/ 0 0
300/dev/sda1 /boot ext4 rw,seclabel,relatime,data=ordered 0 0
301/dev/mapper/fedora_dhcp129--186-home /home ext4 rw,seclabel,relatime,data=ordered 0 0
302tmpfs /run/user/1000 tmpfs rw,seclabel,nosuid,nodev,relatime,size=1611044k,mode=700,uid=1000,gid=1000 0 0
303gvfsd-fuse /run/user/1000/gvfs fuse.gvfsd-fuse rw,nosuid,nodev,relatime,user_id=1000,group_id=1000 0 0
304fusectl /sys/fs/fuse/connections fusectl rw,relatime 0 0
305grimlock.g.a: /home/adrian/sshfs-grimlock fuse.sshfs rw,nosuid,nodev,relatime,user_id=1000,group_id=1000 0 0
306grimlock.g.a:test_path/path_with'single_quotes /home/adrian/sshfs-grimlock-single-quote fuse.sshfs rw,nosuid,nodev,relatime,user_id=1000,group_id=1000 0 0
307grimlock.g.a:path_with'single_quotes /home/adrian/sshfs-grimlock-single-quote-2 fuse.sshfs rw,nosuid,nodev,relatime,user_id=1000,group_id=1000 0 0
308grimlock.g.a:/mnt/data/foto's /home/adrian/fotos fuse.sshfs rw,nosuid,nodev,relatime,user_id=1000,group_id=1000 0 0
309"""
310
311MTAB_ENTRIES = [
312    [
313        'sysfs',
314        '/sys',
315        'sysfs',
316        'rw,seclabel,nosuid,nodev,noexec,relatime',
317        '0',
318        '0'
319    ],
320    ['proc', '/proc', 'proc', 'rw,nosuid,nodev,noexec,relatime', '0', '0'],
321    [
322        'devtmpfs',
323        '/dev',
324        'devtmpfs',
325        'rw,seclabel,nosuid,size=8044400k,nr_inodes=2011100,mode=755',
326        '0',
327        '0'
328    ],
329    [
330        'securityfs',
331        '/sys/kernel/security',
332        'securityfs',
333        'rw,nosuid,nodev,noexec,relatime',
334        '0',
335        '0'
336    ],
337    ['tmpfs', '/dev/shm', 'tmpfs', 'rw,seclabel,nosuid,nodev', '0', '0'],
338    [
339        'devpts',
340        '/dev/pts',
341        'devpts',
342        'rw,seclabel,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000',
343        '0',
344        '0'
345    ],
346    ['tmpfs', '/run', 'tmpfs', 'rw,seclabel,nosuid,nodev,mode=755', '0', '0'],
347    [
348        'tmpfs',
349        '/sys/fs/cgroup',
350        'tmpfs',
351        'ro,seclabel,nosuid,nodev,noexec,mode=755',
352        '0',
353        '0'
354    ],
355    [
356        'cgroup',
357        '/sys/fs/cgroup/systemd',
358        'cgroup',
359        'rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd',
360        '0',
361        '0'
362    ],
363    [
364        'pstore',
365        '/sys/fs/pstore',
366        'pstore',
367        'rw,seclabel,nosuid,nodev,noexec,relatime',
368        '0',
369        '0'
370    ],
371    [
372        'cgroup',
373        '/sys/fs/cgroup/devices',
374        'cgroup',
375        'rw,nosuid,nodev,noexec,relatime,devices',
376        '0',
377        '0'
378    ],
379    [
380        'cgroup',
381        '/sys/fs/cgroup/freezer',
382        'cgroup',
383        'rw,nosuid,nodev,noexec,relatime,freezer',
384        '0',
385        '0'
386    ],
387    [
388        'cgroup',
389        '/sys/fs/cgroup/memory',
390        'cgroup',
391        'rw,nosuid,nodev,noexec,relatime,memory',
392        '0',
393        '0'
394    ],
395    [
396        'cgroup',
397        '/sys/fs/cgroup/pids',
398        'cgroup',
399        'rw,nosuid,nodev,noexec,relatime,pids',
400        '0',
401        '0'
402    ],
403    [
404        'cgroup',
405        '/sys/fs/cgroup/blkio',
406        'cgroup',
407        'rw,nosuid,nodev,noexec,relatime,blkio',
408        '0',
409        '0'
410    ],
411    [
412        'cgroup',
413        '/sys/fs/cgroup/cpuset',
414        'cgroup',
415        'rw,nosuid,nodev,noexec,relatime,cpuset',
416        '0',
417        '0'
418    ],
419    [
420        'cgroup',
421        '/sys/fs/cgroup/cpu,cpuacct',
422        'cgroup',
423        'rw,nosuid,nodev,noexec,relatime,cpu,cpuacct',
424        '0',
425        '0'
426    ],
427    [
428        'cgroup',
429        '/sys/fs/cgroup/hugetlb',
430        'cgroup',
431        'rw,nosuid,nodev,noexec,relatime,hugetlb',
432        '0',
433        '0'
434    ],
435    [
436        'cgroup',
437        '/sys/fs/cgroup/perf_event',
438        'cgroup',
439        'rw,nosuid,nodev,noexec,relatime,perf_event',
440        '0',
441        '0'
442    ],
443    [
444        'cgroup',
445        '/sys/fs/cgroup/net_cls,net_prio',
446        'cgroup',
447        'rw,nosuid,nodev,noexec,relatime,net_cls,net_prio',
448        '0',
449        '0'
450    ],
451    ['configfs', '/sys/kernel/config', 'configfs', 'rw,relatime', '0', '0'],
452    [
453        '/dev/mapper/fedora_dhcp129--186-root',
454        '/',
455        'ext4',
456        'rw,seclabel,relatime,data=ordered',
457        '0',
458        '0'
459    ],
460    ['selinuxfs', '/sys/fs/selinux', 'selinuxfs', 'rw,relatime', '0', '0'],
461    [
462        'systemd-1',
463        '/proc/sys/fs/binfmt_misc',
464        'autofs',
465        'rw,relatime,fd=24,pgrp=1,timeout=0,minproto=5,maxproto=5,direct',
466        '0',
467        '0'
468    ],
469    ['debugfs', '/sys/kernel/debug', 'debugfs', 'rw,seclabel,relatime', '0', '0'],
470    [
471        'hugetlbfs',
472        '/dev/hugepages',
473        'hugetlbfs',
474        'rw,seclabel,relatime',
475        '0',
476        '0'
477    ],
478    ['tmpfs', '/tmp', 'tmpfs', 'rw,seclabel', '0', '0'],
479    ['mqueue', '/dev/mqueue', 'mqueue', 'rw,seclabel,relatime', '0', '0'],
480    [
481        '/dev/loop0',
482        '/var/lib/machines',
483        'btrfs',
484        'rw,seclabel,relatime,space_cache,subvolid=5,subvol=/',
485        '0',
486        '0'
487    ],
488    ['/dev/sda1', '/boot', 'ext4', 'rw,seclabel,relatime,data=ordered', '0', '0'],
489    # A 'none' fstype
490    ['/dev/sdz3', '/not/a/real/device', 'none', 'rw,seclabel,relatime,data=ordered', '0', '0'],
491    # lets assume this is a bindmount
492    ['/dev/sdz4', '/not/a/real/bind_mount', 'ext4', 'rw,seclabel,relatime,data=ordered', '0', '0'],
493    [
494        '/dev/mapper/fedora_dhcp129--186-home',
495        '/home',
496        'ext4',
497        'rw,seclabel,relatime,data=ordered',
498        '0',
499        '0'
500    ],
501    [
502        'tmpfs',
503        '/run/user/1000',
504        'tmpfs',
505        'rw,seclabel,nosuid,nodev,relatime,size=1611044k,mode=700,uid=1000,gid=1000',
506        '0',
507        '0'
508    ],
509    [
510        'gvfsd-fuse',
511        '/run/user/1000/gvfs',
512        'fuse.gvfsd-fuse',
513        'rw,nosuid,nodev,relatime,user_id=1000,group_id=1000',
514        '0',
515        '0'
516    ],
517    ['fusectl', '/sys/fs/fuse/connections', 'fusectl', 'rw,relatime', '0', '0'],
518    # Mount path with space in the name
519    # The space is encoded as \040 since the fields in /etc/mtab are space-delimeted
520    ['/dev/sdz9', r'/mnt/foo\040bar', 'ext4', 'rw,relatime', '0', '0'],
521]
522
523BIND_MOUNTS = ['/not/a/real/bind_mount']
524
525with open(os.path.join(os.path.dirname(__file__), 'fixtures/findmount_output.txt')) as f:
526    FINDMNT_OUTPUT = f.read()
527
528
529class TestFactsLinuxHardwareGetMountFacts(unittest.TestCase):
530
531    # FIXME: mock.patch instead
532    def setUp(self):
533        # The @timeout tracebacks if there isn't a GATHER_TIMEOUT is None (the default until get_all_facts sets it via global)
534        facts.GATHER_TIMEOUT = 10
535
536    def tearDown(self):
537        facts.GATHER_TIMEOUT = None
538
539    # The Hardware subclasses freakout if instaniated directly, so
540    # mock platform.system and inst Hardware() so we get a LinuxHardware()
541    # we can test.
542    @patch('ansible.module_utils.facts.hardware.linux.LinuxHardware._mtab_entries', return_value=MTAB_ENTRIES)
543    @patch('ansible.module_utils.facts.hardware.linux.LinuxHardware._find_bind_mounts', return_value=BIND_MOUNTS)
544    @patch('ansible.module_utils.facts.hardware.linux.LinuxHardware._lsblk_uuid', return_value=LSBLK_UUIDS)
545    @patch('ansible.module_utils.facts.hardware.linux.LinuxHardware._udevadm_uuid', return_value=UDEVADM_UUID)
546    def test_get_mount_facts(self,
547                             mock_lsblk_uuid,
548                             mock_find_bind_mounts,
549                             mock_mtab_entries,
550                             mock_udevadm_uuid):
551        module = Mock()
552        # Returns a LinuxHardware-ish
553        lh = hardware.linux.LinuxHardware(module=module, load_on_init=False)
554
555        # Nothing returned, just self.facts modified as a side effect
556        mount_facts = lh.get_mount_facts()
557        self.assertIsInstance(mount_facts, dict)
558        self.assertIn('mounts', mount_facts)
559        self.assertIsInstance(mount_facts['mounts'], list)
560        self.assertIsInstance(mount_facts['mounts'][0], dict)
561
562        # Find mounts with space in the mountpoint path
563        mounts_with_space = [x for x in mount_facts['mounts'] if ' ' in x['mount']]
564        self.assertEqual(len(mounts_with_space), 1)
565        self.assertEqual(mounts_with_space[0]['mount'], '/mnt/foo bar')
566
567    @patch('ansible.module_utils.facts.hardware.linux.get_file_content', return_value=MTAB)
568    def test_get_mtab_entries(self, mock_get_file_content):
569
570        module = Mock()
571        lh = hardware.linux.LinuxHardware(module=module, load_on_init=False)
572        mtab_entries = lh._mtab_entries()
573        self.assertIsInstance(mtab_entries, list)
574        self.assertIsInstance(mtab_entries[0], list)
575        self.assertEqual(len(mtab_entries), 38)
576
577    @patch('ansible.module_utils.facts.hardware.linux.LinuxHardware._run_findmnt', return_value=(0, FINDMNT_OUTPUT, ''))
578    def test_find_bind_mounts(self, mock_run_findmnt):
579        module = Mock()
580        lh = hardware.linux.LinuxHardware(module=module, load_on_init=False)
581        bind_mounts = lh._find_bind_mounts()
582
583        # If bind_mounts becomes another seq type, feel free to change
584        self.assertIsInstance(bind_mounts, set)
585        self.assertEqual(len(bind_mounts), 1)
586        self.assertIn('/not/a/real/bind_mount', bind_mounts)
587
588    @patch('ansible.module_utils.facts.hardware.linux.LinuxHardware._run_findmnt', return_value=(37, '', ''))
589    def test_find_bind_mounts_non_zero(self, mock_run_findmnt):
590        module = Mock()
591        lh = hardware.linux.LinuxHardware(module=module, load_on_init=False)
592        bind_mounts = lh._find_bind_mounts()
593
594        self.assertIsInstance(bind_mounts, set)
595        self.assertEqual(len(bind_mounts), 0)
596
597    def test_find_bind_mounts_no_findmnts(self):
598        module = Mock()
599        module.get_bin_path = Mock(return_value=None)
600        lh = hardware.linux.LinuxHardware(module=module, load_on_init=False)
601        bind_mounts = lh._find_bind_mounts()
602
603        self.assertIsInstance(bind_mounts, set)
604        self.assertEqual(len(bind_mounts), 0)
605
606    @patch('ansible.module_utils.facts.hardware.linux.LinuxHardware._run_lsblk', return_value=(0, LSBLK_OUTPUT, ''))
607    def test_lsblk_uuid(self, mock_run_lsblk):
608        module = Mock()
609        lh = hardware.linux.LinuxHardware(module=module, load_on_init=False)
610        lsblk_uuids = lh._lsblk_uuid()
611
612        self.assertIsInstance(lsblk_uuids, dict)
613        self.assertIn(b'/dev/loop9', lsblk_uuids)
614        self.assertIn(b'/dev/sda1', lsblk_uuids)
615        self.assertEqual(lsblk_uuids[b'/dev/sda1'], b'32caaec3-ef40-4691-a3b6-438c3f9bc1c0')
616
617    @patch('ansible.module_utils.facts.hardware.linux.LinuxHardware._run_lsblk', return_value=(37, LSBLK_OUTPUT, ''))
618    def test_lsblk_uuid_non_zero(self, mock_run_lsblk):
619        module = Mock()
620        lh = hardware.linux.LinuxHardware(module=module, load_on_init=False)
621        lsblk_uuids = lh._lsblk_uuid()
622
623        self.assertIsInstance(lsblk_uuids, dict)
624        self.assertEqual(len(lsblk_uuids), 0)
625
626    def test_lsblk_uuid_no_lsblk(self):
627        module = Mock()
628        module.get_bin_path = Mock(return_value=None)
629        lh = hardware.linux.LinuxHardware(module=module, load_on_init=False)
630        lsblk_uuids = lh._lsblk_uuid()
631
632        self.assertIsInstance(lsblk_uuids, dict)
633        self.assertEqual(len(lsblk_uuids), 0)
634
635    @patch('ansible.module_utils.facts.hardware.linux.LinuxHardware._run_lsblk', return_value=(0, LSBLK_OUTPUT_2, ''))
636    def test_lsblk_uuid_dev_with_space_in_name(self, mock_run_lsblk):
637        module = Mock()
638        lh = hardware.linux.LinuxHardware(module=module, load_on_init=False)
639        lsblk_uuids = lh._lsblk_uuid()
640        self.assertIsInstance(lsblk_uuids, dict)
641        self.assertIn(b'/dev/loop0', lsblk_uuids)
642        self.assertIn(b'/dev/sda1', lsblk_uuids)
643        self.assertEqual(lsblk_uuids[b'/dev/mapper/an-example-mapper with a space in the name'], b'84639acb-013f-4d2f-9392-526a572b4373')
644        self.assertEqual(lsblk_uuids[b'/dev/sda1'], b'32caaec3-ef40-4691-a3b6-438c3f9bc1c0')
645