summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cloudinit/util.py35
-rw-r--r--tests/unittests/test_util.py30
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