diff options
-rw-r--r-- | cloudinit/util.py | 35 | ||||
-rw-r--r-- | tests/unittests/test_util.py | 30 |
2 files changed, 63 insertions, 2 deletions
diff --git a/cloudinit/util.py b/cloudinit/util.py index f7498b01..26456aa6 100644 --- a/cloudinit/util.py +++ b/cloudinit/util.py @@ -2016,7 +2016,7 @@ def human2bytes(size): return int(num * mpliers[mplier]) -def read_dmi_data(key): +def _read_dmi_syspath(key): """ Reads dmi data with from /sys/class/dmi/id """ @@ -2039,3 +2039,36 @@ def read_dmi_data(key): except Exception as e: logexc(LOG, "failed read of {}".format(dmi_key), e) return None + + +def _call_dmidecode(key, dmidecode_path): + """ + Calls out to dmidecode to get the data out. This is mostly for supporting + OS's without /sys/class/dmi/id support. + """ + try: + cmd = [dmidecode_path, "--string", key] + (result, _err) = subp(cmd) + LOG.debug("dmidecode returned '{}' for '{}'".format(result, key)) + return result + except OSError, _err: + LOG.debug('failed dmidecode cmd: {}\n{}'.format(cmd, _err.message)) + return None + + +def read_dmi_data(key): + """ + Wrapper for reading DMI data. This tries to determine whether the DMI + Data can be read directly, otherwise it will fallback to using dmidecode. + """ + if os.path.exists(DMI_SYS_PATH): + return _read_dmi_syspath(key) + + dmidecode_path = which('dmidecode') + if dmidecode_path: + return _call_dmidecode(key, dmidecode_path) + + LOG.warn("did not find either path {} or dmidecode command".format( + DMI_SYS_PATH)) + + return None diff --git a/tests/unittests/test_util.py b/tests/unittests/test_util.py index 6ae41bd6..3e079131 100644 --- a/tests/unittests/test_util.py +++ b/tests/unittests/test_util.py @@ -319,6 +319,7 @@ class TestReadDMIData(helpers.FilesystemMockingTestCase): self.patchUtils(root) def _write_key(self, key, content): + """Mocks the sys path found on Linux systems.""" new_root = self.makeDir() self._patchIn(new_root) util.ensure_dir(os.path.join('sys', 'class', 'dmi', 'id')) @@ -326,6 +327,24 @@ class TestReadDMIData(helpers.FilesystemMockingTestCase): dmi_key = "/sys/class/dmi/id/{}".format(key) util.write_file(dmi_key, content) + def _no_syspath(self, key, content): + """ + In order to test a missing sys path and call outs to dmidecode, this + function fakes the results of dmidecode to test the results. + """ + new_root = self.makeDir() + self._patchIn(new_root) + self.real_which = util.which + self.real_subp = util.subp + + def _which(key): + return True + util.which = _which + + def _cdd(_key, error=None): + return (content, error) + util.subp = _cdd + def test_key(self): key_content = "TEST-KEY-DATA" self._write_key("key", key_content) @@ -333,9 +352,18 @@ class TestReadDMIData(helpers.FilesystemMockingTestCase): def test_key_mismatch(self): self._write_key("test", "ABC") - self.assertNotEqual("123", util.read_dmi_data("test")) + self.assertNotEqual("123", util.read_dmi_data("test")) def test_no_key(self): + self._no_syspath(None, None) self.assertFalse(util.read_dmi_data("key")) + def test_callout_dmidecode(self): + """test to make sure that dmidecode is used when no syspath""" + self._no_syspath("key", "stuff") + self.assertEquals("stuff", util.read_dmi_data("key")) + self._no_syspath("key", None) + self.assertFalse(None, util.read_dmi_data("key")) + + # vi: ts=4 expandtab |