os-brick/os_brick/privileged/nvmeof.py
Gorka Eguileor 088c676bf5 NVMe-oF: Improve hostnqn creation
This patch improves the creation of the /etc/nvme/hostnqn file by using
the system UUID value we usually already know.

This saves us one or two calls to the nvme-cli command and it also
allows older nvme-cli versions that don't have the `show-hostnqn`
command or have it but can only read from file to generate the same
value every time, which may be useful when running inside a container
under some circumstances.

Change-Id: Ib250d213295695390dbdbb3506cb297a86e95218
2023-11-15 13:40:25 +01:00

130 lines
4.8 KiB
Python

# Copyright (c) 2021, Red Hat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import annotations
import errno
import os
from typing import Optional # noqa: H301
from oslo_concurrency import processutils as putils
from oslo_log import log as logging
import os_brick.privileged
from os_brick.privileged import rootwrap
LOG = logging.getLogger(__name__)
@os_brick.privileged.default.entrypoint
def create_hostnqn(system_uuid: Optional[str] = None) -> str:
"""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
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
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 = ''
try:
os.makedirs('/etc/nvme', mode=0o755, exist_ok=True)
# If we have the system's unique uuid we can just write the file
if system_uuid:
host_nqn = 'nqn.2014-08.org.nvmexpress:uuid:' + system_uuid
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
# command. This ENOENT is when nvme says there isn't an nqn.
except putils.ProcessExecutionError as e:
# nvme-cli's error are all over the place, so merge the output
err_msg = e.stdout + '\n' + e.stderr
msg = err_msg.casefold()
if 'error: invalid sub-command' in msg:
LOG.debug('Version too old cannot check current hostnqn.')
elif 'hostnqn is not available' in msg:
LOG.debug('Version too old to return hostnqn from non '
'file sources')
elif e.exit_code == errno.ENOENT:
LOG.debug('No nqn could be formed from dmi or systemd.')
else:
LOG.debug('Unknown error from nvme show-hostnqn: %s',
err_msg)
raise
if not host_nqn:
LOG.debug('Generating nqn')
host_nqn, err = rootwrap.custom_execute('nvme', 'gen-hostnqn')
host_nqn = host_nqn.strip()
with open('/etc/nvme/hostnqn', 'w') as f:
LOG.debug('Writing hostnqn file')
f.write(host_nqn)
os.chmod('/etc/nvme/hostnqn', 0o644)
except Exception as e:
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, FileNotFoundError) as e:
LOG.debug("Unable to locate dmidecode. For Cinder RSD Backend,"
" please make sure it is installed: %s", e)
out = ""
return out.strip()
@os_brick.privileged.default.entrypoint
def create_hostid(uuid: str) -> Optional[str]:
"""Create the hostid to ensure it's always the same."""
try:
os.makedirs('/etc/nvme', mode=0o755, exist_ok=True)
with open('/etc/nvme/hostid', 'w') as f:
LOG.debug('Writing nvme hostid %s', uuid)
f.write(f'{uuid}\n')
os.chmod('/etc/nvme/hostid', 0o644)
except Exception as e:
LOG.warning("Could not generate nvme host id: %s", e)
return None
return uuid