NVMe-oF: Get system uuid in privsep

Move the _get_system_uuid method from the NVMeOFConnector connector to
os_brick/privsep/nvmeof.py renaming it to get_system_uuid and running it
as privileged.

This allows us to read sys/class/dmi/id/product_uuid file in Python
instead of running a subprocess to execute cat.

It also allows the connector to have one less privsep calls if the file
doesn't exist, because it can execute the dmidecode command directly
without making another request to the privsep daemon.

Change-Id: I8f2edef6fda97af0ff3f92e39c8b24a85b6c3402
This commit is contained in:
Gorka Eguileor 2022-03-17 14:20:27 +01:00
parent b41b3c8814
commit da4561165d
5 changed files with 70 additions and 29 deletions

View File

@ -34,6 +34,7 @@ try:
from os_brick.initiator.connectors import nvmeof_agent
except ImportError:
nvmeof_agent = None
from os_brick.privileged import nvmeof as priv_nvmeof
from os_brick.privileged import rootwrap as priv_rootwrap
from os_brick import utils
@ -767,7 +768,7 @@ class NVMeOFConnector(base.BaseLinuxConnector):
nqn = None
uuid = nvmf._get_host_uuid()
suuid = nvmf._get_system_uuid()
suuid = priv_nvmeof.get_system_uuid()
if cls.nvme_present():
nqn = utils.get_host_nqn()
if uuid:
@ -795,31 +796,6 @@ class NVMeOFConnector(base.BaseLinuxConnector):
"Process execution error in _get_host_uuid: %s", e)
return None
def _get_system_uuid(self) -> str:
"""Get the system's UUID from DMI
First try reading it from sysfs and if not possible use the dmidecode
tool.
"""
# RSD requires system_uuid to let Cinder RSD Driver identify
# Nova node for later RSD volume attachment.
try:
out, err = self._execute('cat', '/sys/class/dmi/id/product_uuid',
root_helper=self._root_helper,
run_as_root=True)
except putils.ProcessExecutionError:
try:
out, err = self._execute('dmidecode', '-ssystem-uuid',
root_helper=self._root_helper,
run_as_root=True)
if not out:
LOG.warning('dmidecode returned empty system-uuid')
except putils.ProcessExecutionError as e:
LOG.debug("Unable to locate dmidecode. For Cinder RSD Backend,"
" please make sure it is installed: %s", e)
out = ""
return out.strip()
@classmethod
def _set_native_multipath_supported(cls):
if cls.native_multipath_supported is None:

View File

@ -71,3 +71,25 @@ def create_hostnqn():
LOG.warning("Could not generate host nqn: %s", e)
return host_nqn
@os_brick.privileged.default.entrypoint
def get_system_uuid() -> str:
# RSD requires system_uuid to let Cinder RSD Driver identify
# Nova node for later RSD volume attachment.
try:
with open('/sys/class/dmi/id/product_uuid', 'r') as f:
return f.read().strip()
except Exception:
LOG.debug("Could not read dmi's 'product_uuid' on sysfs")
try:
out, err = rootwrap.custom_execute('dmidecode', '-ssystem-uuid')
if not out:
LOG.warning('dmidecode returned empty system-uuid')
except putils.ProcessExecutionError as e:
LOG.debug("Unable to locate dmidecode. For Cinder RSD Backend,"
" please make sure it is installed: %s", e)
out = ""
return out.strip()

View File

@ -21,6 +21,7 @@ from oslo_concurrency import processutils as putils
from os_brick import exception
from os_brick import executor
from os_brick.initiator.connectors import nvmeof
from os_brick.privileged import nvmeof as priv_nvmeof
from os_brick.privileged import rootwrap as priv_rootwrap
from os_brick.tests import base as test_base
from os_brick.tests.initiator import test_connector
@ -920,7 +921,7 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
return_value=True)
@mock.patch.object(utils, 'get_host_nqn',
return_value='fakenqn')
@mock.patch.object(nvmeof.NVMeOFConnector, '_get_system_uuid',
@mock.patch.object(priv_nvmeof, 'get_system_uuid',
return_value=None)
@mock.patch.object(nvmeof.NVMeOFConnector, '_get_host_uuid',
return_value=None)
@ -937,7 +938,7 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
return_value=True)
@mock.patch.object(nvmeof.NVMeOFConnector, 'nvme_present')
@mock.patch.object(utils, 'get_host_nqn', autospec=True)
@mock.patch.object(nvmeof.NVMeOFConnector, '_get_system_uuid',
@mock.patch.object(priv_nvmeof, 'get_system_uuid',
autospec=True)
@mock.patch.object(nvmeof.NVMeOFConnector, '_get_host_uuid', autospec=True)
def test_get_connector_properties_with_sysuuid(self, mock_host_uuid,

View File

@ -26,6 +26,7 @@ from os_brick.initiator.connectors import fake
from os_brick.initiator.connectors import iscsi
from os_brick.initiator.connectors import nvmeof
from os_brick.initiator import linuxfc
from os_brick.privileged import nvmeof as priv_nvmeof
from os_brick.privileged import rootwrap as priv_rootwrap
from os_brick.tests import base as test_base
from os_brick import utils
@ -45,7 +46,7 @@ class ConnectorUtilsTestCase(test_base.TestCase):
@mock.patch.object(nvmeof.NVMeOFConnector,
'_is_native_multipath_supported',
return_value=False)
@mock.patch.object(nvmeof.NVMeOFConnector, '_get_system_uuid',
@mock.patch.object(priv_nvmeof, 'get_system_uuid',
return_value=None)
@mock.patch.object(nvmeof.NVMeOFConnector, '_get_host_uuid',
return_value=None)

View File

@ -125,3 +125,44 @@ class PrivNVMeTestCase(base.TestCase):
exist_ok=True)
mock_exec.assert_called_once_with('nvme', 'show-hostnqn')
self.assertEqual('', res)
@mock.patch.object(builtins, 'open', new_callable=mock.mock_open)
def test_get_system_uuid_product_uuid(self, mock_open):
uuid = 'dbc6ba60-36ae-4b96-9310-628832bdfc3d'
mock_fd = mock_open.return_value.__enter__.return_value
mock_fd.read.return_value = uuid
res = privsep_nvme.get_system_uuid()
self.assertEqual(uuid, res)
mock_open.assert_called_once_with('/sys/class/dmi/id/product_uuid',
'r')
mock_fd.read.assert_called_once_with()
@mock.patch.object(builtins, 'open', side_effect=Exception)
@mock.patch.object(rootwrap, 'custom_execute')
def test_get_system_uuid_dmidecode(self, mock_exec, mock_open):
uuid = 'dbc6ba60-36ae-4b96-9310-628832bdfc3d'
mock_exec.return_value = (f' {uuid} ', '')
res = privsep_nvme.get_system_uuid()
self.assertEqual(uuid, res)
mock_open.assert_called_once_with('/sys/class/dmi/id/product_uuid',
'r')
mock_exec.assert_called_once_with('dmidecode', '-ssystem-uuid')
@mock.patch.object(builtins, 'open', side_effect=Exception)
@mock.patch.object(rootwrap, 'custom_execute', return_value=('', ''))
def test_get_system_uuid_dmidecode_empty(self, mock_exec, mock_open):
res = privsep_nvme.get_system_uuid()
self.assertEqual('', res)
mock_open.assert_called_once_with('/sys/class/dmi/id/product_uuid',
'r')
mock_exec.assert_called_once_with('dmidecode', '-ssystem-uuid')
@mock.patch.object(builtins, 'open', side_effect=Exception)
@mock.patch.object(rootwrap, 'custom_execute',
side_effect=putils.ProcessExecutionError)
def test_get_system_uuid_failure(self, mock_exec, mock_open):
res = privsep_nvme.get_system_uuid()
self.assertEqual('', res)
mock_open.assert_called_once_with('/sys/class/dmi/id/product_uuid',
'r')
mock_exec.assert_called_once_with('dmidecode', '-ssystem-uuid')