Merge "NVMe-oF: Improve hostnqn creation"
This commit is contained in:
commit
136f76efd4
|
@ -771,7 +771,7 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
||||||
uuid = nvmf._get_host_uuid()
|
uuid = nvmf._get_host_uuid()
|
||||||
suuid = priv_nvmeof.get_system_uuid()
|
suuid = priv_nvmeof.get_system_uuid()
|
||||||
if cls.nvme_present():
|
if cls.nvme_present():
|
||||||
nqn = utils.get_host_nqn()
|
nqn = utils.get_host_nqn(suuid)
|
||||||
# Ensure /etc/nvme/hostid exists and defaults to the system uuid,
|
# Ensure /etc/nvme/hostid exists and defaults to the system uuid,
|
||||||
# or a random value.
|
# or a random value.
|
||||||
hostid = utils.get_nvme_host_id(suuid)
|
hostid = utils.get_nvme_host_id(suuid)
|
||||||
|
|
|
@ -30,44 +30,54 @@ LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@os_brick.privileged.default.entrypoint
|
@os_brick.privileged.default.entrypoint
|
||||||
def create_hostnqn() -> str:
|
def create_hostnqn(system_uuid: Optional[str] = None) -> str:
|
||||||
"""Create the hostnqn file to speed up finding out the nqn.
|
"""Create the hostnqn file to speed up finding out the nqn.
|
||||||
|
|
||||||
By having the /etc/nvme/hostnqn not only do we make sure that that value is
|
By having the /etc/nvme/hostnqn not only do we make sure that that value is
|
||||||
always used on this system, but we are also able to just open the file to
|
always used on this system, but we are also able to just open the file to
|
||||||
get the nqn on each get_connector_properties call instead of having to make
|
get the nqn on each get_connector_properties call instead of having to make
|
||||||
a call to nvme show-hostnqn command.
|
a call to nvme show-hostnqn command.
|
||||||
|
|
||||||
|
In newer nvme-cli versions calling show-hostnqn will not only try to
|
||||||
|
locate the file (which we know doesn't exist or this method wouldn't have
|
||||||
|
been called), but it will also generate one. In older nvme-cli versions
|
||||||
|
that is not the case.
|
||||||
"""
|
"""
|
||||||
host_nqn = ''
|
host_nqn = ''
|
||||||
try:
|
try:
|
||||||
os.makedirs('/etc/nvme', mode=0o755, exist_ok=True)
|
os.makedirs('/etc/nvme', mode=0o755, exist_ok=True)
|
||||||
|
|
||||||
# Try to get existing nqn generated from dmi or systemd
|
# If we have the system's unique uuid we can just write the file
|
||||||
try:
|
if system_uuid:
|
||||||
host_nqn, err = rootwrap.custom_execute('nvme', 'show-hostnqn')
|
host_nqn = 'nqn.2014-08.org.nvmexpress:uuid:' + system_uuid
|
||||||
host_nqn = host_nqn.strip()
|
else:
|
||||||
|
# Try to get existing nqn generated from dmi or systemd
|
||||||
|
try:
|
||||||
|
host_nqn, err = rootwrap.custom_execute('nvme', 'show-hostnqn')
|
||||||
|
host_nqn = host_nqn.strip()
|
||||||
|
|
||||||
# This is different from OSError's ENOENT, which is missing nvme
|
# This is different from OSError's ENOENT, which is missing nvme
|
||||||
# command. This ENOENT is when nvme says there isn't an nqn.
|
# command. This ENOENT is when nvme says there isn't an nqn.
|
||||||
except putils.ProcessExecutionError as e:
|
except putils.ProcessExecutionError as e:
|
||||||
# nvme-cli's error are all over the place, so merge the output
|
# nvme-cli's error are all over the place, so merge the output
|
||||||
err_msg = e.stdout + '\n' + e.stderr
|
err_msg = e.stdout + '\n' + e.stderr
|
||||||
msg = err_msg.casefold()
|
msg = err_msg.casefold()
|
||||||
if 'error: invalid sub-command' in msg:
|
if 'error: invalid sub-command' in msg:
|
||||||
LOG.debug('Version too old cannot check current hostnqn.')
|
LOG.debug('Version too old cannot check current hostnqn.')
|
||||||
elif 'hostnqn is not available' in msg:
|
elif 'hostnqn is not available' in msg:
|
||||||
LOG.debug('Version too old to return hostnqn from non file '
|
LOG.debug('Version too old to return hostnqn from non '
|
||||||
'sources')
|
'file sources')
|
||||||
elif e.exit_code == errno.ENOENT:
|
elif e.exit_code == errno.ENOENT:
|
||||||
LOG.debug('No nqn could be formed from dmi or systemd.')
|
LOG.debug('No nqn could be formed from dmi or systemd.')
|
||||||
else:
|
else:
|
||||||
LOG.debug('Unknown error from nvme show-hostnqn: %s', err_msg)
|
LOG.debug('Unknown error from nvme show-hostnqn: %s',
|
||||||
raise
|
err_msg)
|
||||||
|
raise
|
||||||
|
|
||||||
if not host_nqn:
|
if not host_nqn:
|
||||||
LOG.debug('Generating nqn')
|
LOG.debug('Generating nqn')
|
||||||
host_nqn, err = rootwrap.custom_execute('nvme', 'gen-hostnqn')
|
host_nqn, err = rootwrap.custom_execute('nvme', 'gen-hostnqn')
|
||||||
host_nqn = host_nqn.strip()
|
host_nqn = host_nqn.strip()
|
||||||
|
|
||||||
with open('/etc/nvme/hostnqn', 'w') as f:
|
with open('/etc/nvme/hostnqn', 'w') as f:
|
||||||
LOG.debug('Writing hostnqn file')
|
LOG.debug('Writing hostnqn file')
|
||||||
|
|
|
@ -945,6 +945,7 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
||||||
'nvme_hostid': SYS_UUID}
|
'nvme_hostid': SYS_UUID}
|
||||||
self.assertEqual(expected_props, props)
|
self.assertEqual(expected_props, props)
|
||||||
mock_get_host_id.assert_called_once_with(None)
|
mock_get_host_id.assert_called_once_with(None)
|
||||||
|
mock_nqn.assert_called_once_with(None)
|
||||||
|
|
||||||
@mock.patch.object(utils, 'get_nvme_host_id',
|
@mock.patch.object(utils, 'get_nvme_host_id',
|
||||||
return_value=SYS_UUID)
|
return_value=SYS_UUID)
|
||||||
|
@ -971,6 +972,7 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
||||||
'nvme_hostid': SYS_UUID}
|
'nvme_hostid': SYS_UUID}
|
||||||
self.assertEqual(expected_props, props)
|
self.assertEqual(expected_props, props)
|
||||||
mock_get_host_id.assert_called_once_with(SYS_UUID)
|
mock_get_host_id.assert_called_once_with(SYS_UUID)
|
||||||
|
mock_nqn.assert_called_once_with(SYS_UUID)
|
||||||
|
|
||||||
def test_get_volume_paths_device_info(self):
|
def test_get_volume_paths_device_info(self):
|
||||||
"""Device info path has highest priority."""
|
"""Device info path has highest priority."""
|
||||||
|
@ -1794,7 +1796,7 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
||||||
def test_get_host_nqn_file_available(self, mock_open):
|
def test_get_host_nqn_file_available(self, mock_open):
|
||||||
mock_open.return_value.__enter__.return_value.read = (
|
mock_open.return_value.__enter__.return_value.read = (
|
||||||
lambda: HOST_NQN + "\n")
|
lambda: HOST_NQN + "\n")
|
||||||
host_nqn = self._get_host_nqn()
|
host_nqn = utils.get_host_nqn()
|
||||||
mock_open.assert_called_once_with('/etc/nvme/hostnqn', 'r')
|
mock_open.assert_called_once_with('/etc/nvme/hostnqn', 'r')
|
||||||
self.assertEqual(HOST_NQN, host_nqn)
|
self.assertEqual(HOST_NQN, host_nqn)
|
||||||
|
|
||||||
|
@ -1805,7 +1807,17 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
||||||
mock_open.side_effect = IOError()
|
mock_open.side_effect = IOError()
|
||||||
result = utils.get_host_nqn()
|
result = utils.get_host_nqn()
|
||||||
mock_open.assert_called_once_with('/etc/nvme/hostnqn', 'r')
|
mock_open.assert_called_once_with('/etc/nvme/hostnqn', 'r')
|
||||||
mock_create.assert_called_once_with()
|
mock_create.assert_called_once_with(None)
|
||||||
|
self.assertEqual(mock.sentinel.nqn, result)
|
||||||
|
|
||||||
|
@mock.patch.object(utils.priv_nvme, 'create_hostnqn')
|
||||||
|
@mock.patch.object(builtins, 'open')
|
||||||
|
def test_get_host_nqn_io_err_sys_uuid(self, mock_open, mock_create):
|
||||||
|
mock_create.return_value = mock.sentinel.nqn
|
||||||
|
mock_open.side_effect = IOError()
|
||||||
|
result = utils.get_host_nqn(mock.sentinel.system_uuid)
|
||||||
|
mock_open.assert_called_once_with('/etc/nvme/hostnqn', 'r')
|
||||||
|
mock_create.assert_called_once_with(mock.sentinel.system_uuid)
|
||||||
self.assertEqual(mock.sentinel.nqn, result)
|
self.assertEqual(mock.sentinel.nqn, result)
|
||||||
|
|
||||||
@mock.patch.object(utils.priv_nvme, 'create_hostnqn')
|
@mock.patch.object(utils.priv_nvme, 'create_hostnqn')
|
||||||
|
@ -1863,16 +1875,6 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
||||||
self.assertFalse(result)
|
self.assertFalse(result)
|
||||||
mock_get_fs_type.assert_called_once_with(NVME_DEVICE_PATH)
|
mock_get_fs_type.assert_called_once_with(NVME_DEVICE_PATH)
|
||||||
|
|
||||||
def _get_host_nqn(self):
|
|
||||||
host_nqn = None
|
|
||||||
try:
|
|
||||||
with open('/etc/nvme/hostnqn', 'r') as f:
|
|
||||||
host_nqn = f.read().strip()
|
|
||||||
f.close()
|
|
||||||
except IOError:
|
|
||||||
host_nqn = HOST_NQN
|
|
||||||
return host_nqn
|
|
||||||
|
|
||||||
@ddt.data(True, False)
|
@ddt.data(True, False)
|
||||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'native_multipath_supported',
|
@mock.patch.object(nvmeof.NVMeOFConnector, 'native_multipath_supported',
|
||||||
None)
|
None)
|
||||||
|
|
|
@ -55,6 +55,27 @@ class PrivNVMeTestCase(base.TestCase):
|
||||||
mock_chmod.assert_called_once_with('/etc/nvme/hostnqn', 0o644)
|
mock_chmod.assert_called_once_with('/etc/nvme/hostnqn', 0o644)
|
||||||
self.assertEqual(stripped_hostnqn, res)
|
self.assertEqual(stripped_hostnqn, res)
|
||||||
|
|
||||||
|
@mock.patch('os.chmod')
|
||||||
|
@mock.patch.object(builtins, 'open', new_callable=mock.mock_open)
|
||||||
|
@mock.patch('os.makedirs')
|
||||||
|
@mock.patch.object(rootwrap, 'custom_execute')
|
||||||
|
def test_create_hostnqn_from_system_uuid(self, mock_exec, mock_mkdirs,
|
||||||
|
mock_open, mock_chmod):
|
||||||
|
|
||||||
|
system_uuid = 'ea841a98-444c-4abb-bd99-092b20518542'
|
||||||
|
hostnqn = 'nqn.2014-08.org.nvmexpress:uuid:' + system_uuid
|
||||||
|
|
||||||
|
res = privsep_nvme.create_hostnqn(system_uuid)
|
||||||
|
|
||||||
|
mock_mkdirs.assert_called_once_with('/etc/nvme',
|
||||||
|
mode=0o755,
|
||||||
|
exist_ok=True)
|
||||||
|
mock_exec.assert_not_called()
|
||||||
|
mock_open.assert_called_once_with('/etc/nvme/hostnqn', 'w')
|
||||||
|
mock_open().write.assert_called_once_with(hostnqn)
|
||||||
|
mock_chmod.assert_called_once_with('/etc/nvme/hostnqn', 0o644)
|
||||||
|
self.assertEqual(hostnqn, res)
|
||||||
|
|
||||||
@mock.patch('os.chmod')
|
@mock.patch('os.chmod')
|
||||||
@mock.patch.object(builtins, 'open', new_callable=mock.mock_open)
|
@mock.patch.object(builtins, 'open', new_callable=mock.mock_open)
|
||||||
@mock.patch('os.makedirs')
|
@mock.patch('os.makedirs')
|
||||||
|
|
|
@ -221,12 +221,21 @@ def convert_str(text: Union[bytes, str]) -> str:
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
def get_host_nqn() -> Optional[str]:
|
def get_host_nqn(system_uuid: Optional[str] = None) -> Optional[str]:
|
||||||
|
"""Ensure that hostnqn exists, creating if necessary.
|
||||||
|
|
||||||
|
This method tries to return contents from /etc/nvme/hostnqn and if not
|
||||||
|
possible then creates the file calling create_hostnqn and passing provided
|
||||||
|
system_uuid and returns the contents of the newly created file.
|
||||||
|
|
||||||
|
Method create_hostnqn gives priority to the provided system_uuid parameter
|
||||||
|
for the contents of the file over other alternatives it has.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
with open('/etc/nvme/hostnqn', 'r') as f:
|
with open('/etc/nvme/hostnqn', 'r') as f:
|
||||||
host_nqn = f.read().strip()
|
host_nqn = f.read().strip()
|
||||||
except IOError:
|
except IOError:
|
||||||
host_nqn = priv_nvme.create_hostnqn()
|
host_nqn = priv_nvme.create_hostnqn(system_uuid)
|
||||||
except Exception:
|
except Exception:
|
||||||
host_nqn = None
|
host_nqn = None
|
||||||
return host_nqn
|
return host_nqn
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
NVMe-oF connector: Improved speed of creation of the ``/etc/nvme/hostnqn``
|
||||||
|
file.
|
||||||
|
- |
|
||||||
|
NVMe-oF connector: Always write the same value in the same system for the
|
||||||
|
``/etc/nvme/hostnqn`` file in older nvme-cli versions when system UUID can
|
||||||
|
be read from DMI.
|
Loading…
Reference in New Issue