Drop reliance on dmidecode executable.
This commit is contained in:
parent
49c849fdf0
commit
e24371042e
@ -40,7 +40,6 @@ LOG = logging.getLogger(__name__)
|
|||||||
CLOUD_INFO_FILE = '/etc/sysconfig/cloud-info'
|
CLOUD_INFO_FILE = '/etc/sysconfig/cloud-info'
|
||||||
|
|
||||||
# Shell command lists
|
# Shell command lists
|
||||||
CMD_DMI_SYSTEM = ['/usr/sbin/dmidecode', '--string', 'system-product-name']
|
|
||||||
CMD_PROBE_FLOPPY = ['/sbin/modprobe', 'floppy']
|
CMD_PROBE_FLOPPY = ['/sbin/modprobe', 'floppy']
|
||||||
CMD_UDEVADM_SETTLE = ['/sbin/udevadm', 'settle', '--quiet', '--timeout=5']
|
CMD_UDEVADM_SETTLE = ['/sbin/udevadm', 'settle', '--quiet', '--timeout=5']
|
||||||
|
|
||||||
@ -100,11 +99,7 @@ class DataSourceAltCloud(sources.DataSource):
|
|||||||
'''
|
'''
|
||||||
Description:
|
Description:
|
||||||
Get the type for the cloud back end this instance is running on
|
Get the type for the cloud back end this instance is running on
|
||||||
by examining the string returned by:
|
by examining the string returned by reading the dmi data.
|
||||||
dmidecode --string system-product-name
|
|
||||||
|
|
||||||
On VMWare/vSphere dmidecode returns: RHEV Hypervisor
|
|
||||||
On VMWare/vSphere dmidecode returns: VMware Virtual Platform
|
|
||||||
|
|
||||||
Input:
|
Input:
|
||||||
None
|
None
|
||||||
@ -117,26 +112,20 @@ class DataSourceAltCloud(sources.DataSource):
|
|||||||
|
|
||||||
uname_arch = os.uname()[4]
|
uname_arch = os.uname()[4]
|
||||||
if uname_arch.startswith("arm") or uname_arch == "aarch64":
|
if uname_arch.startswith("arm") or uname_arch == "aarch64":
|
||||||
# Disabling because dmidecode in CMD_DMI_SYSTEM crashes kvm process
|
# Disabling because dmi data is not available on ARM processors
|
||||||
LOG.debug("Disabling AltCloud datasource on arm (LP: #1243287)")
|
LOG.debug("Disabling AltCloud datasource on arm (LP: #1243287)")
|
||||||
return 'UNKNOWN'
|
return 'UNKNOWN'
|
||||||
|
|
||||||
cmd = CMD_DMI_SYSTEM
|
system_name = util.read_dmi_data("system-product-name")
|
||||||
try:
|
if not system_name:
|
||||||
(cmd_out, _err) = util.subp(cmd)
|
|
||||||
except ProcessExecutionError, _err:
|
|
||||||
LOG.debug(('Failed command: %s\n%s') % \
|
|
||||||
(' '.join(cmd), _err.message))
|
|
||||||
return 'UNKNOWN'
|
|
||||||
except OSError, _err:
|
|
||||||
LOG.debug(('Failed command: %s\n%s') % \
|
|
||||||
(' '.join(cmd), _err.message))
|
|
||||||
return 'UNKNOWN'
|
return 'UNKNOWN'
|
||||||
|
|
||||||
if cmd_out.upper().startswith('RHEV'):
|
sys_name = system_name.upper()
|
||||||
|
|
||||||
|
if sys_name.startswith('RHEV'):
|
||||||
return 'RHEV'
|
return 'RHEV'
|
||||||
|
|
||||||
if cmd_out.upper().startswith('VMWARE'):
|
if sys_name.startswith('VMWARE'):
|
||||||
return 'VSPHERE'
|
return 'VSPHERE'
|
||||||
|
|
||||||
return 'UNKNOWN'
|
return 'UNKNOWN'
|
||||||
|
@ -44,27 +44,25 @@ class DataSourceCloudSigma(sources.DataSource):
|
|||||||
|
|
||||||
def is_running_in_cloudsigma(self):
|
def is_running_in_cloudsigma(self):
|
||||||
"""
|
"""
|
||||||
Uses dmidecode to detect if this instance of cloud-init is running
|
Uses dmi data to detect if this instance of cloud-init is running
|
||||||
in the CloudSigma's infrastructure.
|
in the CloudSigma's infrastructure.
|
||||||
"""
|
"""
|
||||||
uname_arch = os.uname()[4]
|
uname_arch = os.uname()[4]
|
||||||
if uname_arch.startswith("arm") or uname_arch == "aarch64":
|
if uname_arch.startswith("arm") or uname_arch == "aarch64":
|
||||||
# Disabling because dmidecode in CMD_DMI_SYSTEM crashes kvm process
|
# Disabling because dmi data on ARM processors
|
||||||
LOG.debug("Disabling CloudSigma datasource on arm (LP: #1243287)")
|
LOG.debug("Disabling CloudSigma datasource on arm (LP: #1243287)")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
dmidecode_path = util.which('dmidecode')
|
LOG.debug("determining hypervisor product name via dmi data")
|
||||||
if not dmidecode_path:
|
sys_product_name = util.read_dmi_data("system-product-name")
|
||||||
|
if not sys_product_name:
|
||||||
|
LOG.warn("failed to get hypervisor product name via dmi data")
|
||||||
return False
|
return False
|
||||||
|
else:
|
||||||
|
LOG.debug("detected hypervisor as {}".format(sys_product_name))
|
||||||
|
return 'cloudsigma' in sys_product_name.lower()
|
||||||
|
|
||||||
LOG.debug("Determining hypervisor product name via dmidecode")
|
LOG.warn("failed to query dmi data for system product name")
|
||||||
try:
|
|
||||||
cmd = [dmidecode_path, "--string", "system-product-name"]
|
|
||||||
system_product_name, _ = util.subp(cmd)
|
|
||||||
return 'cloudsigma' in system_product_name.lower()
|
|
||||||
except:
|
|
||||||
LOG.warn("Failed to get hypervisor product name via dmidecode")
|
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
|
@ -358,26 +358,13 @@ def query_data(noun, seed_device, seed_timeout, strip=False, default=None,
|
|||||||
|
|
||||||
|
|
||||||
def dmi_data():
|
def dmi_data():
|
||||||
sys_uuid, sys_type = None, None
|
sys_uuid = util.read_dmi_data("system-uuid")
|
||||||
dmidecode_path = util.which('dmidecode')
|
sys_type = util.read_dmi_data("system-product-name")
|
||||||
if not dmidecode_path:
|
|
||||||
return False
|
|
||||||
|
|
||||||
sys_uuid_cmd = [dmidecode_path, "-s", "system-uuid"]
|
if not sys_uuid or not sys_type:
|
||||||
try:
|
return None
|
||||||
LOG.debug("Getting hostname from dmidecode")
|
|
||||||
(sys_uuid, _err) = util.subp(sys_uuid_cmd)
|
|
||||||
except Exception as e:
|
|
||||||
util.logexc(LOG, "Failed to get system UUID", e)
|
|
||||||
|
|
||||||
sys_type_cmd = [dmidecode_path, "-s", "system-product-name"]
|
return (sys_uuid.lower(), sys_type)
|
||||||
try:
|
|
||||||
LOG.debug("Determining hypervisor product name via dmidecode")
|
|
||||||
(sys_type, _err) = util.subp(sys_type_cmd)
|
|
||||||
except Exception as e:
|
|
||||||
util.logexc(LOG, "Failed to get system UUID", e)
|
|
||||||
|
|
||||||
return (sys_uuid.lower().strip(), sys_type.strip())
|
|
||||||
|
|
||||||
|
|
||||||
def write_boot_content(content, content_f, link=None, shebang=False,
|
def write_boot_content(content, content_f, link=None, shebang=False,
|
||||||
|
@ -72,6 +72,9 @@ FN_ALLOWED = ('_-.()' + string.digits + string.ascii_letters)
|
|||||||
# Helper utils to see if running in a container
|
# Helper utils to see if running in a container
|
||||||
CONTAINER_TESTS = ['running-in-container', 'lxc-is-container']
|
CONTAINER_TESTS = ['running-in-container', 'lxc-is-container']
|
||||||
|
|
||||||
|
# Path for DMI Data
|
||||||
|
DMI_SYS_PATH = "/sys/class/dmi/id"
|
||||||
|
|
||||||
|
|
||||||
class ProcessExecutionError(IOError):
|
class ProcessExecutionError(IOError):
|
||||||
|
|
||||||
@ -2011,3 +2014,28 @@ def human2bytes(size):
|
|||||||
raise ValueError("'%s': cannot be negative" % size_in)
|
raise ValueError("'%s': cannot be negative" % size_in)
|
||||||
|
|
||||||
return int(num * mpliers[mplier])
|
return int(num * mpliers[mplier])
|
||||||
|
|
||||||
|
|
||||||
|
def read_dmi_data(key):
|
||||||
|
"""
|
||||||
|
Reads dmi data with from /sys/class/dmi/id
|
||||||
|
"""
|
||||||
|
|
||||||
|
dmi_key = "{}/{}".format(DMI_SYS_PATH, key)
|
||||||
|
LOG.debug("querying dmi data {}".format(dmi_key))
|
||||||
|
try:
|
||||||
|
if not os.path.exists(dmi_key):
|
||||||
|
LOG.debug("did not find {}".format(dmi_key))
|
||||||
|
return None
|
||||||
|
|
||||||
|
key_data = load_file(dmi_key)
|
||||||
|
if not key_data:
|
||||||
|
LOG.debug("{} did not return any data".format(key))
|
||||||
|
return None
|
||||||
|
|
||||||
|
LOG.debug("dmi data {} returned {}".format(dmi_key, key_data))
|
||||||
|
return key_data.strip()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logexc(LOG, "failed read of {}".format(dmi_key), e)
|
||||||
|
return None
|
||||||
|
@ -26,6 +26,7 @@ import shutil
|
|||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from cloudinit import helpers
|
from cloudinit import helpers
|
||||||
|
from cloudinit import util
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
# Get the cloudinit.sources.DataSourceAltCloud import items needed.
|
# Get the cloudinit.sources.DataSourceAltCloud import items needed.
|
||||||
@ -98,6 +99,16 @@ def _remove_user_data_files(mount_dir,
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _dmi_data(expected):
|
||||||
|
'''
|
||||||
|
Spoof the data received over DMI
|
||||||
|
'''
|
||||||
|
def _data(key):
|
||||||
|
return expected
|
||||||
|
|
||||||
|
return _data
|
||||||
|
|
||||||
|
|
||||||
class TestGetCloudType(TestCase):
|
class TestGetCloudType(TestCase):
|
||||||
'''
|
'''
|
||||||
Test to exercise method: DataSourceAltCloud.get_cloud_type()
|
Test to exercise method: DataSourceAltCloud.get_cloud_type()
|
||||||
@ -106,24 +117,22 @@ class TestGetCloudType(TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
'''Set up.'''
|
'''Set up.'''
|
||||||
self.paths = helpers.Paths({'cloud_dir': '/tmp'})
|
self.paths = helpers.Paths({'cloud_dir': '/tmp'})
|
||||||
|
self.dmi_data = util.read_dmi_data
|
||||||
# We have a different code path for arm to deal with LP1243287
|
# We have a different code path for arm to deal with LP1243287
|
||||||
# We have to switch arch to x86_64 to avoid test failure
|
# We have to switch arch to x86_64 to avoid test failure
|
||||||
force_arch('x86_64')
|
force_arch('x86_64')
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
# Reset
|
# Reset
|
||||||
cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
|
util.read_dmi_data = self.dmi_data
|
||||||
['dmidecode', '--string', 'system-product-name']
|
|
||||||
# Return back to original arch
|
|
||||||
force_arch()
|
force_arch()
|
||||||
|
|
||||||
def test_rhev(self):
|
def test_rhev(self):
|
||||||
'''
|
'''
|
||||||
Test method get_cloud_type() for RHEVm systems.
|
Test method get_cloud_type() for RHEVm systems.
|
||||||
Forcing dmidecode return to match a RHEVm system: RHEV Hypervisor
|
Forcing read_dmi_data return to match a RHEVm system: RHEV Hypervisor
|
||||||
'''
|
'''
|
||||||
cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
|
util.read_dmi_data = _dmi_data('RHEV')
|
||||||
['echo', 'RHEV Hypervisor']
|
|
||||||
dsrc = DataSourceAltCloud({}, None, self.paths)
|
dsrc = DataSourceAltCloud({}, None, self.paths)
|
||||||
self.assertEquals('RHEV', \
|
self.assertEquals('RHEV', \
|
||||||
dsrc.get_cloud_type())
|
dsrc.get_cloud_type())
|
||||||
@ -131,10 +140,9 @@ class TestGetCloudType(TestCase):
|
|||||||
def test_vsphere(self):
|
def test_vsphere(self):
|
||||||
'''
|
'''
|
||||||
Test method get_cloud_type() for vSphere systems.
|
Test method get_cloud_type() for vSphere systems.
|
||||||
Forcing dmidecode return to match a vSphere system: RHEV Hypervisor
|
Forcing read_dmi_data return to match a vSphere system: RHEV Hypervisor
|
||||||
'''
|
'''
|
||||||
cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
|
util.read_dmi_data = _dmi_data('VMware Virtual Platform')
|
||||||
['echo', 'VMware Virtual Platform']
|
|
||||||
dsrc = DataSourceAltCloud({}, None, self.paths)
|
dsrc = DataSourceAltCloud({}, None, self.paths)
|
||||||
self.assertEquals('VSPHERE', \
|
self.assertEquals('VSPHERE', \
|
||||||
dsrc.get_cloud_type())
|
dsrc.get_cloud_type())
|
||||||
@ -142,30 +150,9 @@ class TestGetCloudType(TestCase):
|
|||||||
def test_unknown(self):
|
def test_unknown(self):
|
||||||
'''
|
'''
|
||||||
Test method get_cloud_type() for unknown systems.
|
Test method get_cloud_type() for unknown systems.
|
||||||
Forcing dmidecode return to match an unrecognized return.
|
Forcing read_dmi_data return to match an unrecognized return.
|
||||||
'''
|
'''
|
||||||
cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
|
util.read_dmi_data = _dmi_data('Unrecognized Platform')
|
||||||
['echo', 'Unrecognized Platform']
|
|
||||||
dsrc = DataSourceAltCloud({}, None, self.paths)
|
|
||||||
self.assertEquals('UNKNOWN', \
|
|
||||||
dsrc.get_cloud_type())
|
|
||||||
|
|
||||||
def test_exception1(self):
|
|
||||||
'''
|
|
||||||
Test method get_cloud_type() where command dmidecode fails.
|
|
||||||
'''
|
|
||||||
cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
|
|
||||||
['ls', 'bad command']
|
|
||||||
dsrc = DataSourceAltCloud({}, None, self.paths)
|
|
||||||
self.assertEquals('UNKNOWN', \
|
|
||||||
dsrc.get_cloud_type())
|
|
||||||
|
|
||||||
def test_exception2(self):
|
|
||||||
'''
|
|
||||||
Test method get_cloud_type() where command dmidecode is not available.
|
|
||||||
'''
|
|
||||||
cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
|
|
||||||
['bad command']
|
|
||||||
dsrc = DataSourceAltCloud({}, None, self.paths)
|
dsrc = DataSourceAltCloud({}, None, self.paths)
|
||||||
self.assertEquals('UNKNOWN', \
|
self.assertEquals('UNKNOWN', \
|
||||||
dsrc.get_cloud_type())
|
dsrc.get_cloud_type())
|
||||||
@ -180,6 +167,7 @@ class TestGetDataCloudInfoFile(TestCase):
|
|||||||
'''Set up.'''
|
'''Set up.'''
|
||||||
self.paths = helpers.Paths({'cloud_dir': '/tmp'})
|
self.paths = helpers.Paths({'cloud_dir': '/tmp'})
|
||||||
self.cloud_info_file = tempfile.mkstemp()[1]
|
self.cloud_info_file = tempfile.mkstemp()[1]
|
||||||
|
self.dmi_data = util.read_dmi_data
|
||||||
cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \
|
cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \
|
||||||
self.cloud_info_file
|
self.cloud_info_file
|
||||||
|
|
||||||
@ -192,6 +180,7 @@ class TestGetDataCloudInfoFile(TestCase):
|
|||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
util.read_dmi_data = self.dmi_data
|
||||||
cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \
|
cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \
|
||||||
'/etc/sysconfig/cloud-info'
|
'/etc/sysconfig/cloud-info'
|
||||||
|
|
||||||
@ -243,6 +232,7 @@ class TestGetDataNoCloudInfoFile(TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
'''Set up.'''
|
'''Set up.'''
|
||||||
self.paths = helpers.Paths({'cloud_dir': '/tmp'})
|
self.paths = helpers.Paths({'cloud_dir': '/tmp'})
|
||||||
|
self.dmi_data = util.read_dmi_data
|
||||||
cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \
|
cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \
|
||||||
'no such file'
|
'no such file'
|
||||||
# We have a different code path for arm to deal with LP1243287
|
# We have a different code path for arm to deal with LP1243287
|
||||||
@ -253,16 +243,14 @@ class TestGetDataNoCloudInfoFile(TestCase):
|
|||||||
# Reset
|
# Reset
|
||||||
cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \
|
cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \
|
||||||
'/etc/sysconfig/cloud-info'
|
'/etc/sysconfig/cloud-info'
|
||||||
cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
|
util.read_dmi_data = self.dmi_data
|
||||||
['dmidecode', '--string', 'system-product-name']
|
|
||||||
# Return back to original arch
|
# Return back to original arch
|
||||||
force_arch()
|
force_arch()
|
||||||
|
|
||||||
def test_rhev_no_cloud_file(self):
|
def test_rhev_no_cloud_file(self):
|
||||||
'''Test No cloud info file module get_data() forcing RHEV.'''
|
'''Test No cloud info file module get_data() forcing RHEV.'''
|
||||||
|
|
||||||
cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
|
util.read_dmi_data = _dmi_data('RHEV Hypervisor')
|
||||||
['echo', 'RHEV Hypervisor']
|
|
||||||
dsrc = DataSourceAltCloud({}, None, self.paths)
|
dsrc = DataSourceAltCloud({}, None, self.paths)
|
||||||
dsrc.user_data_rhevm = lambda: True
|
dsrc.user_data_rhevm = lambda: True
|
||||||
self.assertEquals(True, dsrc.get_data())
|
self.assertEquals(True, dsrc.get_data())
|
||||||
@ -270,8 +258,7 @@ class TestGetDataNoCloudInfoFile(TestCase):
|
|||||||
def test_vsphere_no_cloud_file(self):
|
def test_vsphere_no_cloud_file(self):
|
||||||
'''Test No cloud info file module get_data() forcing VSPHERE.'''
|
'''Test No cloud info file module get_data() forcing VSPHERE.'''
|
||||||
|
|
||||||
cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
|
util.read_dmi_data = _dmi_data('VMware Virtual Platform')
|
||||||
['echo', 'VMware Virtual Platform']
|
|
||||||
dsrc = DataSourceAltCloud({}, None, self.paths)
|
dsrc = DataSourceAltCloud({}, None, self.paths)
|
||||||
dsrc.user_data_vsphere = lambda: True
|
dsrc.user_data_vsphere = lambda: True
|
||||||
self.assertEquals(True, dsrc.get_data())
|
self.assertEquals(True, dsrc.get_data())
|
||||||
@ -279,8 +266,7 @@ class TestGetDataNoCloudInfoFile(TestCase):
|
|||||||
def test_failure_no_cloud_file(self):
|
def test_failure_no_cloud_file(self):
|
||||||
'''Test No cloud info file module get_data() forcing unrecognized.'''
|
'''Test No cloud info file module get_data() forcing unrecognized.'''
|
||||||
|
|
||||||
cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
|
util.read_dmi_data = _dmi_data('Unrecognized Platform')
|
||||||
['echo', 'Unrecognized Platform']
|
|
||||||
dsrc = DataSourceAltCloud({}, None, self.paths)
|
dsrc = DataSourceAltCloud({}, None, self.paths)
|
||||||
self.assertEquals(False, dsrc.get_data())
|
self.assertEquals(False, dsrc.get_data())
|
||||||
|
|
||||||
|
@ -310,4 +310,32 @@ class TestMountinfoParsing(helpers.ResourceUsingTestCase):
|
|||||||
expected = ('none', 'tmpfs', '/run/lock')
|
expected = ('none', 'tmpfs', '/run/lock')
|
||||||
self.assertEqual(expected, util.parse_mount_info('/run/lock', lines))
|
self.assertEqual(expected, util.parse_mount_info('/run/lock', lines))
|
||||||
|
|
||||||
|
|
||||||
|
class TestReadDMIData(helpers.FilesystemMockingTestCase):
|
||||||
|
|
||||||
|
def _patchIn(self, root):
|
||||||
|
self.restore()
|
||||||
|
self.patchOS(root)
|
||||||
|
self.patchUtils(root)
|
||||||
|
|
||||||
|
def _write_key(self, key, content):
|
||||||
|
new_root = self.makeDir()
|
||||||
|
self._patchIn(new_root)
|
||||||
|
util.ensure_dir(os.path.join('sys', 'class', 'dmi', 'id'))
|
||||||
|
|
||||||
|
dmi_key = "/sys/class/dmi/id/{}".format(key)
|
||||||
|
util.write_file(dmi_key, content)
|
||||||
|
|
||||||
|
def test_key(self):
|
||||||
|
key_content = "TEST-KEY-DATA"
|
||||||
|
self._write_key("key", key_content)
|
||||||
|
self.assertEquals(key_content, util.read_dmi_data("key"))
|
||||||
|
|
||||||
|
def test_key_mismatch(self):
|
||||||
|
self._write_key("test", "ABC")
|
||||||
|
self.assertNotEqual("123", util.read_dmi_data("test"))
|
||||||
|
|
||||||
|
def test_no_key(self):
|
||||||
|
self.assertFalse(util.read_dmi_data("key"))
|
||||||
|
|
||||||
# vi: ts=4 expandtab
|
# vi: ts=4 expandtab
|
||||||
|
Loading…
x
Reference in New Issue
Block a user