1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
|
from cloudinit.tests import helpers
from cloudinit import dmi
from cloudinit import util
from cloudinit import subp
import os
import tempfile
import shutil
from unittest import mock
class TestReadDMIData(helpers.FilesystemMockingTestCase):
def setUp(self):
super(TestReadDMIData, self).setUp()
self.new_root = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, self.new_root)
self.reRoot(self.new_root)
p = mock.patch("cloudinit.dmi.is_container", return_value=False)
self.addCleanup(p.stop)
self._m_is_container = p.start()
p = mock.patch("cloudinit.dmi.is_FreeBSD", return_value=False)
self.addCleanup(p.stop)
self._m_is_FreeBSD = p.start()
def _create_sysfs_parent_directory(self):
util.ensure_dir(os.path.join('sys', 'class', 'dmi', 'id'))
def _create_sysfs_file(self, key, content):
"""Mocks the sys path found on Linux systems."""
self._create_sysfs_parent_directory()
dmi_key = "/sys/class/dmi/id/{0}".format(key)
util.write_file(dmi_key, content)
def _configure_dmidecode_return(self, key, content, error=None):
"""
In order to test a missing sys path and call outs to dmidecode, this
function fakes the results of dmidecode to test the results.
"""
def _dmidecode_subp(cmd):
if cmd[-1] != key:
raise subp.ProcessExecutionError()
return (content, error)
self.patched_funcs.enter_context(
mock.patch("cloudinit.dmi.subp.which", side_effect=lambda _: True))
self.patched_funcs.enter_context(
mock.patch("cloudinit.dmi.subp.subp", side_effect=_dmidecode_subp))
def _configure_kenv_return(self, key, content, error=None):
"""
In order to test a FreeBSD system call outs to kenv, this
function fakes the results of kenv to test the results.
"""
def _kenv_subp(cmd):
if cmd[-1] != dmi.DMIDECODE_TO_KERNEL[key].freebsd:
raise subp.ProcessExecutionError()
return (content, error)
self.patched_funcs.enter_context(
mock.patch("cloudinit.dmi.subp.subp", side_effect=_kenv_subp))
def patch_mapping(self, new_mapping):
self.patched_funcs.enter_context(
mock.patch('cloudinit.dmi.DMIDECODE_TO_KERNEL',
new_mapping))
def test_sysfs_used_with_key_in_mapping_and_file_on_disk(self):
self.patch_mapping({'mapped-key': dmi.kdmi('mapped-value', None)})
expected_dmi_value = 'sys-used-correctly'
self._create_sysfs_file('mapped-value', expected_dmi_value)
self._configure_dmidecode_return('mapped-key', 'wrong-wrong-wrong')
self.assertEqual(expected_dmi_value, dmi.read_dmi_data('mapped-key'))
def test_dmidecode_used_if_no_sysfs_file_on_disk(self):
self.patch_mapping({})
self._create_sysfs_parent_directory()
expected_dmi_value = 'dmidecode-used'
self._configure_dmidecode_return('use-dmidecode', expected_dmi_value)
with mock.patch("cloudinit.util.os.uname") as m_uname:
m_uname.return_value = ('x-sysname', 'x-nodename',
'x-release', 'x-version', 'x86_64')
self.assertEqual(expected_dmi_value,
dmi.read_dmi_data('use-dmidecode'))
def test_dmidecode_not_used_on_arm(self):
self.patch_mapping({})
print("current =%s", subp)
self._create_sysfs_parent_directory()
dmi_val = 'from-dmidecode'
dmi_name = 'use-dmidecode'
self._configure_dmidecode_return(dmi_name, dmi_val)
print("now =%s", subp)
expected = {'armel': None, 'aarch64': dmi_val, 'x86_64': dmi_val}
found = {}
# we do not run the 'dmi-decode' binary on some arches
# verify that anything requested that is not in the sysfs dir
# will return None on those arches.
with mock.patch("cloudinit.util.os.uname") as m_uname:
for arch in expected:
m_uname.return_value = ('x-sysname', 'x-nodename',
'x-release', 'x-version', arch)
print("now2 =%s", subp)
found[arch] = dmi.read_dmi_data(dmi_name)
self.assertEqual(expected, found)
def test_none_returned_if_neither_source_has_data(self):
self.patch_mapping({})
self._configure_dmidecode_return('key', 'value')
self.assertIsNone(dmi.read_dmi_data('expect-fail'))
def test_none_returned_if_dmidecode_not_in_path(self):
self.patched_funcs.enter_context(
mock.patch.object(subp, 'which', lambda _: False))
self.patch_mapping({})
self.assertIsNone(dmi.read_dmi_data('expect-fail'))
def test_empty_string_returned_instead_of_foxfox(self):
# uninitialized dmi values show as \xff, return empty string
my_len = 32
dmi_value = b'\xff' * my_len + b'\n'
expected = ""
dmi_key = 'system-product-name'
sysfs_key = 'product_name'
self._create_sysfs_file(sysfs_key, dmi_value)
self.assertEqual(expected, dmi.read_dmi_data(dmi_key))
def test_container_returns_none(self):
"""In a container read_dmi_data should always return None."""
# first verify we get the value if not in container
self._m_is_container.return_value = False
key, val = ("system-product-name", "my_product")
self._create_sysfs_file('product_name', val)
self.assertEqual(val, dmi.read_dmi_data(key))
# then verify in container returns None
self._m_is_container.return_value = True
self.assertIsNone(dmi.read_dmi_data(key))
def test_container_returns_none_on_unknown(self):
"""In a container even bogus keys return None."""
self._m_is_container.return_value = True
self._create_sysfs_file('product_name', "should-be-ignored")
self.assertIsNone(dmi.read_dmi_data("bogus"))
self.assertIsNone(dmi.read_dmi_data("system-product-name"))
def test_freebsd_uses_kenv(self):
"""On a FreeBSD system, kenv is called."""
self._m_is_FreeBSD.return_value = True
key, val = ("system-product-name", "my_product")
self._configure_kenv_return(key, val)
self.assertEqual(dmi.read_dmi_data(key), val)
|