Merging upstream version 2.3~rc4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
0bb8b81ada
commit
3cb8664662
16 changed files with 708 additions and 276 deletions
22
.github/workflows/meson-test.yml
vendored
22
.github/workflows/meson-test.yml
vendored
|
@ -38,13 +38,15 @@ jobs:
|
||||||
python3 -m pip install --upgrade dasbus pylint pyflakes PyGObject
|
python3 -m pip install --upgrade dasbus pylint pyflakes PyGObject
|
||||||
python3 -m pip install --upgrade vermin pyfakefs importlib-resources
|
python3 -m pip install --upgrade vermin pyfakefs importlib-resources
|
||||||
|
|
||||||
- name: "INSTALL: libnvme"
|
- name: "INSTALL: libnvme dependencies"
|
||||||
|
run: |
|
||||||
|
sudo apt-get install --yes --quiet swig
|
||||||
|
sudo apt-get install --yes --quiet libjson-c-dev
|
||||||
|
|
||||||
|
- name: "SETUP: [nvme-stas, libnvme]"
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get install --yes --quiet swig libjson-c-dev
|
|
||||||
meson subprojects download
|
meson subprojects download
|
||||||
meson setup .build subprojects/libnvme -Dlibdbus=disabled -Dopenssl=disabled -Dbuildtype=release -Dprefix=/usr -Dpython=enabled
|
meson setup --buildtype=release --sysconfdir=/etc --prefix=/usr -Dman=true -Dhtml=true -Dlibnvme:python=enabled -Dlibnvme:libdbus=disabled -Dlibnvme:openssl=disabled -Dlibnvme:json-c=disabled -Dlibnvme:keyutils=disabled .build
|
||||||
ninja -C .build
|
|
||||||
sudo meson install -C .build
|
|
||||||
|
|
||||||
- name: "CONFIG: PYTHONPATH"
|
- name: "CONFIG: PYTHONPATH"
|
||||||
run: |
|
run: |
|
||||||
|
@ -55,7 +57,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
action: test
|
action: test
|
||||||
directory: .build
|
directory: .build
|
||||||
setup-options: -Dman=true -Dhtml=true
|
setup-options: --buildtype=release --sysconfdir=/etc --prefix=/usr -Dman=true -Dhtml=true -Dlibnvme:python=enabled -Dlibnvme:libdbus=disabled -Dlibnvme:openssl=disabled -Dlibnvme:json-c=disabled -Dlibnvme:keyutils=disabled
|
||||||
options: --print-errorlogs --suite nvme-stas
|
options: --print-errorlogs --suite nvme-stas
|
||||||
|
|
||||||
# Preserve meson's log file on failure
|
# Preserve meson's log file on failure
|
||||||
|
@ -67,9 +69,11 @@ jobs:
|
||||||
|
|
||||||
- name: "Generate coverage report"
|
- name: "Generate coverage report"
|
||||||
run: |
|
run: |
|
||||||
python3 -m pip install pytest
|
python3 -m pip install --upgrade pytest
|
||||||
python3 -m pip install pytest-cov
|
python3 -m pip install --upgrade pytest-cov
|
||||||
PYTHONPATH=.build:.build/subprojects/libnvme:/usr/lib/python3/dist-packages/ pytest --cov=./staslib --cov-report=xml test/test-*.py
|
echo $( pwd )
|
||||||
|
cp -r .build/staslib/* ./staslib/.
|
||||||
|
pytest --cov=./staslib --cov-report=xml test/test-*.py
|
||||||
|
|
||||||
- uses: codecov/codecov-action@v3
|
- uses: codecov/codecov-action@v3
|
||||||
with:
|
with:
|
||||||
|
|
1
NEWS.md
1
NEWS.md
|
@ -7,6 +7,7 @@ New features:
|
||||||
- Support for nBFT (NVMe-oF Boot Table).
|
- Support for nBFT (NVMe-oF Boot Table).
|
||||||
- The Avahi driver will now verify reachability of services discovered through mDNS to make sure all discovered IP addresses can be connected to. This avoids invoking the NVMe kernel driver with invalid IP addresses and getting error messages in the syslog.
|
- The Avahi driver will now verify reachability of services discovered through mDNS to make sure all discovered IP addresses can be connected to. This avoids invoking the NVMe kernel driver with invalid IP addresses and getting error messages in the syslog.
|
||||||
- The Avahi driver will now print an error message if the same IP address is found on multiple interfaces. This indicates a misconfiguration of the network.
|
- The Avahi driver will now print an error message if the same IP address is found on multiple interfaces. This indicates a misconfiguration of the network.
|
||||||
|
- Simplify algorithm that determines if an existing connection (is sysfs) can be reused by stafd/stacd instead of creating a duplicate connection.
|
||||||
|
|
||||||
Bug fixes:
|
Bug fixes:
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,10 @@ components = [
|
||||||
'environment.txt',
|
'environment.txt',
|
||||||
'installation.rst',
|
'installation.rst',
|
||||||
'nvme-stas.rst',
|
'nvme-stas.rst',
|
||||||
'org.nvmexpress.stac.debug.rst',
|
conf.get('STACD_DBUS_NAME') + '.debug.rst',
|
||||||
'org.nvmexpress.stac.rst',
|
conf.get('STACD_DBUS_NAME') + '.rst',
|
||||||
'org.nvmexpress.staf.debug.rst',
|
conf.get('STAFD_DBUS_NAME') + '.debug.rst',
|
||||||
'org.nvmexpress.staf.rst',
|
conf.get('STAFD_DBUS_NAME') + '.rst',
|
||||||
'stacctl.rst',
|
'stacctl.rst',
|
||||||
'stacd-index.rst',
|
'stacd-index.rst',
|
||||||
'stacd.conf.rst',
|
'stacd.conf.rst',
|
||||||
|
|
|
@ -9,15 +9,15 @@
|
||||||
dbus_conf_dir = datadir / 'dbus-1' / 'system.d'
|
dbus_conf_dir = datadir / 'dbus-1' / 'system.d'
|
||||||
|
|
||||||
configure_file(
|
configure_file(
|
||||||
input: 'org.nvmexpress.staf.in.conf',
|
input: conf.get('STAFD_DBUS_NAME') + '.in.conf',
|
||||||
output: 'org.nvmexpress.staf.conf',
|
output: conf.get('STAFD_DBUS_NAME') + '.conf',
|
||||||
configuration: conf,
|
configuration: conf,
|
||||||
install_dir: dbus_conf_dir,
|
install_dir: dbus_conf_dir,
|
||||||
)
|
)
|
||||||
|
|
||||||
configure_file(
|
configure_file(
|
||||||
input: 'org.nvmexpress.stac.in.conf',
|
input: conf.get('STACD_DBUS_NAME') + '.in.conf',
|
||||||
output: 'org.nvmexpress.stac.conf',
|
output: conf.get('STACD_DBUS_NAME') + '.conf',
|
||||||
configuration: conf,
|
configuration: conf,
|
||||||
install_dir: dbus_conf_dir,
|
install_dir: dbus_conf_dir,
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
project(
|
project(
|
||||||
'nvme-stas',
|
'nvme-stas',
|
||||||
meson_version: '>= 0.53.0',
|
meson_version: '>= 0.53.0',
|
||||||
version: '2.3-rc3',
|
version: '2.3-rc4',
|
||||||
license: 'Apache-2.0',
|
license: 'Apache-2.0',
|
||||||
default_options: [
|
default_options: [
|
||||||
'buildtype=release',
|
'buildtype=release',
|
||||||
|
@ -78,6 +78,7 @@ conf.set('STAFD_DBUS_NAME', 'org.nvmexpress.staf')
|
||||||
conf.set('STAFD_DBUS_PATH', '/org/nvmexpress/staf')
|
conf.set('STAFD_DBUS_PATH', '/org/nvmexpress/staf')
|
||||||
conf.set('STACD_DBUS_NAME', 'org.nvmexpress.stac')
|
conf.set('STACD_DBUS_NAME', 'org.nvmexpress.stac')
|
||||||
conf.set('STACD_DBUS_PATH', '/org/nvmexpress/stac')
|
conf.set('STACD_DBUS_PATH', '/org/nvmexpress/stac')
|
||||||
|
conf.set('ETC', etcdir)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
stafd = configure_file(
|
stafd = configure_file(
|
||||||
|
|
|
@ -36,18 +36,25 @@ KERNEL_IFACE_MIN_VERSION = KernelVersion('5.14')
|
||||||
KERNEL_TP8013_MIN_VERSION = KernelVersion('5.16')
|
KERNEL_TP8013_MIN_VERSION = KernelVersion('5.16')
|
||||||
KERNEL_HOSTKEY_MIN_VERSION = KernelVersion('5.20')
|
KERNEL_HOSTKEY_MIN_VERSION = KernelVersion('5.20')
|
||||||
KERNEL_CTRLKEY_MIN_VERSION = KernelVersion('5.20')
|
KERNEL_CTRLKEY_MIN_VERSION = KernelVersion('5.20')
|
||||||
|
KERNEL_ALL_MIN_VERSION = max(
|
||||||
|
# Minimum version required to have support for all
|
||||||
|
KERNEL_IFACE_MIN_VERSION,
|
||||||
|
KERNEL_TP8013_MIN_VERSION,
|
||||||
|
KERNEL_HOSTKEY_MIN_VERSION,
|
||||||
|
KERNEL_CTRLKEY_MIN_VERSION,
|
||||||
|
)
|
||||||
|
|
||||||
WELL_KNOWN_DISC_NQN = 'nqn.2014-08.org.nvmexpress.discovery'
|
WELL_KNOWN_DISC_NQN = 'nqn.2014-08.org.nvmexpress.discovery'
|
||||||
|
|
||||||
PROG_NAME = os.path.basename(sys.argv[0])
|
PROG_NAME = os.path.basename(sys.argv[0])
|
||||||
|
|
||||||
NVME_HOSTID = '/etc/nvme/hostid'
|
NVME_HOSTID = '@ETC@/nvme/hostid'
|
||||||
NVME_HOSTNQN = '/etc/nvme/hostnqn'
|
NVME_HOSTNQN = '@ETC@/nvme/hostnqn'
|
||||||
NVME_HOSTKEY = '/etc/nvme/hostkey'
|
NVME_HOSTKEY = '@ETC@/nvme/hostkey'
|
||||||
|
|
||||||
SYS_CONF_FILE = '/etc/stas/sys.conf'
|
SYS_CONF_FILE = '@ETC@/stas/sys.conf'
|
||||||
STAFD_CONF_FILE = '/etc/stas/stafd.conf'
|
STAFD_CONF_FILE = '@ETC@/stas/stafd.conf'
|
||||||
STACD_CONF_FILE = '/etc/stas/stacd.conf'
|
STACD_CONF_FILE = '@ETC@/stas/stacd.conf'
|
||||||
|
|
||||||
HAS_NBFT_SUPPORT = hasattr(nvme, 'nbft_get')
|
HAS_NBFT_SUPPORT = hasattr(nvme, 'nbft_get')
|
||||||
NBFT_SYSFS_PATH = "/sys/firmware/acpi/tables"
|
NBFT_SYSFS_PATH = "/sys/firmware/acpi/tables"
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
#
|
#
|
||||||
# Authors: Martin Belanger <Martin.Belanger@dell.com>
|
# Authors: Martin Belanger <Martin.Belanger@dell.com>
|
||||||
#
|
#
|
||||||
'''This module provides utility functions/classes to provide easier to use
|
'''This module provides utility functions (or classes) that simplify
|
||||||
access to GLib/Gio/Gobject resources.
|
the use of certain GLib/Gio/Gobject functions/resources.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
@ -443,7 +443,7 @@ class TcpChecker: # pylint: disable=too-many-instance-attributes
|
||||||
# the GLib context.
|
# the GLib context.
|
||||||
family = socket.AF_INET if self._traddr.version == 4 else socket.AF_INET6
|
family = socket.AF_INET if self._traddr.version == 4 else socket.AF_INET6
|
||||||
self._native_sock = socket.socket(family, socket.SOCK_STREAM | socket.SOCK_NONBLOCK, socket.IPPROTO_TCP)
|
self._native_sock = socket.socket(family, socket.SOCK_STREAM | socket.SOCK_NONBLOCK, socket.IPPROTO_TCP)
|
||||||
if isinstance(self._host_iface, str):
|
if self._host_iface and isinstance(self._host_iface, str):
|
||||||
self._native_sock.setsockopt(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, self._host_iface.encode('utf-8'))
|
self._native_sock.setsockopt(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, self._host_iface.encode('utf-8'))
|
||||||
|
|
||||||
# Convert socket.socket() to a Gio.Socket() object
|
# Convert socket.socket() to a Gio.Socket() object
|
||||||
|
|
|
@ -131,66 +131,22 @@ def mac2iface(mac: str): # pylint: disable=too-many-locals
|
||||||
|
|
||||||
|
|
||||||
# ******************************************************************************
|
# ******************************************************************************
|
||||||
def _data_matches_ip(data_family, data, ip):
|
def ip_equal(ip1, ip2):
|
||||||
if data_family == socket.AF_INET:
|
'''Check whther two IP addresses are equal.
|
||||||
try:
|
@param ip1: IPv4Address or IPv6Address object
|
||||||
other_ip = ipaddress.IPv4Address(data)
|
@param ip2: IPv4Address or IPv6Address object
|
||||||
except ValueError:
|
'''
|
||||||
return False
|
if not isinstance(ip1, ipaddress._BaseAddress): # pylint: disable=protected-access
|
||||||
if ip.version == 6:
|
return False
|
||||||
ip = ip.ipv4_mapped
|
if not isinstance(ip2, ipaddress._BaseAddress): # pylint: disable=protected-access
|
||||||
elif data_family == socket.AF_INET6:
|
|
||||||
try:
|
|
||||||
other_ip = ipaddress.IPv6Address(data)
|
|
||||||
except ValueError:
|
|
||||||
return False
|
|
||||||
if ip.version == 4:
|
|
||||||
other_ip = other_ip.ipv4_mapped
|
|
||||||
else:
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return other_ip == ip
|
if ip1.version == 4 and ip2.version == 6:
|
||||||
|
ip2 = ip2.ipv4_mapped
|
||||||
|
elif ip1.version == 6 and ip2.version == 4:
|
||||||
|
ip1 = ip1.ipv4_mapped
|
||||||
|
|
||||||
|
return ip1 == ip2
|
||||||
def _iface_of(src_addr): # pylint: disable=too-many-locals
|
|
||||||
'''@brief Find the interface that has src_addr as one of its assigned IP addresses.
|
|
||||||
@param src_addr: The IP address to match
|
|
||||||
@type src_addr: Instance of ipaddress.IPv4Address or ipaddress.IPv6Address
|
|
||||||
'''
|
|
||||||
with socket.socket(socket.AF_NETLINK, socket.SOCK_RAW) as sock:
|
|
||||||
sock.sendall(GETADDRCMD)
|
|
||||||
nlmsg = sock.recv(8192)
|
|
||||||
nlmsg_idx = 0
|
|
||||||
while True:
|
|
||||||
if nlmsg_idx >= len(nlmsg):
|
|
||||||
nlmsg += sock.recv(8192)
|
|
||||||
|
|
||||||
nlmsghdr = nlmsg[nlmsg_idx : nlmsg_idx + NLMSG_HDRLEN]
|
|
||||||
nlmsg_len, nlmsg_type, _, _, _ = struct.unpack('<LHHLL', nlmsghdr)
|
|
||||||
|
|
||||||
if nlmsg_type == NLMSG_DONE:
|
|
||||||
break
|
|
||||||
|
|
||||||
if nlmsg_type == RTM_NEWADDR:
|
|
||||||
msg_indx = nlmsg_idx + NLMSG_HDRLEN
|
|
||||||
msg = nlmsg[msg_indx : msg_indx + IFADDRMSG_SZ] # ifaddrmsg
|
|
||||||
ifa_family, _, _, _, ifa_index = struct.unpack('<BBBBL', msg)
|
|
||||||
|
|
||||||
rtattr_indx = msg_indx + IFADDRMSG_SZ
|
|
||||||
while rtattr_indx < (nlmsg_idx + nlmsg_len):
|
|
||||||
rtattr = nlmsg[rtattr_indx : rtattr_indx + RTATTR_SZ]
|
|
||||||
rta_len, rta_type = struct.unpack('<HH', rtattr)
|
|
||||||
if rta_type == IFLA_ADDRESS:
|
|
||||||
data = nlmsg[rtattr_indx + RTATTR_SZ : rtattr_indx + rta_len]
|
|
||||||
if _data_matches_ip(ifa_family, data, src_addr):
|
|
||||||
return socket.if_indextoname(ifa_index)
|
|
||||||
|
|
||||||
rta_len = RTA_ALIGN(rta_len) # Round up to multiple of 4
|
|
||||||
rtattr_indx += rta_len # Move to next rtattr
|
|
||||||
|
|
||||||
nlmsg_idx += nlmsg_len # Move to next Netlink message
|
|
||||||
|
|
||||||
return ''
|
|
||||||
|
|
||||||
|
|
||||||
# ******************************************************************************
|
# ******************************************************************************
|
||||||
|
@ -221,21 +177,21 @@ def net_if_addrs(): # pylint: disable=too-many-locals
|
||||||
source address.
|
source address.
|
||||||
@example: {
|
@example: {
|
||||||
'wlp0s20f3': {
|
'wlp0s20f3': {
|
||||||
4: ['10.0.0.28'],
|
4: [IPv4Address('10.0.0.28')],
|
||||||
6: [
|
6: [
|
||||||
'fd5e:9a9e:c5bd:0:5509:890c:1848:3843',
|
IPv6Address('fd5e:9a9e:c5bd:0:5509:890c:1848:3843'),
|
||||||
'fd5e:9a9e:c5bd:0:1fd5:e527:8df7:7912',
|
IPv6Address('fd5e:9a9e:c5bd:0:1fd5:e527:8df7:7912'),
|
||||||
'2605:59c8:6128:fb00:c083:1b8:c467:81d2',
|
IPv6Address('2605:59c8:6128:fb00:c083:1b8:c467:81d2'),
|
||||||
'2605:59c8:6128:fb00:e99d:1a02:38e0:ad52',
|
IPv6Address('2605:59c8:6128:fb00:e99d:1a02:38e0:ad52'),
|
||||||
'fe80::d71b:e807:d5ee:7614'
|
IPv6Address('fe80::d71b:e807:d5ee:7614'),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
'lo': {
|
'lo': {
|
||||||
4: ['127.0.0.1'],
|
4: [IPv4Address('127.0.0.1')],
|
||||||
6: ['::1'],
|
6: [IPv6Address('::1')],
|
||||||
},
|
},
|
||||||
'docker0': {
|
'docker0': {
|
||||||
4: ['172.17.0.1'],
|
4: [IPv4Address('172.17.0.1')],
|
||||||
6: []
|
6: []
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -295,14 +251,18 @@ def net_if_addrs(): # pylint: disable=too-many-locals
|
||||||
|
|
||||||
|
|
||||||
# ******************************************************************************
|
# ******************************************************************************
|
||||||
def get_interface(src_addr):
|
def get_interface(ifaces: dict, src_addr):
|
||||||
'''Get interface for given source address
|
'''Get interface for given source address
|
||||||
@param src_addr: The source address
|
@param ifaces: Interface info previously returned by @net_if_addrs()
|
||||||
@type src_addr: str
|
@param src_addr: IPv4Address or IPv6Address object
|
||||||
'''
|
'''
|
||||||
if not src_addr:
|
if not isinstance(src_addr, ipaddress._BaseAddress): # pylint: disable=protected-access
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
src_addr = src_addr.split('%')[0] # remove scope-id (if any)
|
for iface, addr_map in ifaces.items():
|
||||||
src_addr = get_ipaddress_obj(src_addr)
|
for addrs in addr_map.values():
|
||||||
return '' if src_addr is None else _iface_of(src_addr)
|
for addr in addrs:
|
||||||
|
if ip_equal(src_addr, addr):
|
||||||
|
return iface
|
||||||
|
|
||||||
|
return ''
|
||||||
|
|
257
staslib/udev.py
257
staslib/udev.py
|
@ -11,6 +11,7 @@
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
|
from functools import partial
|
||||||
import pyudev
|
import pyudev
|
||||||
from gi.repository import GLib
|
from gi.repository import GLib
|
||||||
from staslib import defs, iputil, trid
|
from staslib import defs, iputil, trid
|
||||||
|
@ -154,7 +155,7 @@ class Udev:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _cid_matches_tcp_tid_legacy(tid, cid): # pylint: disable=too-many-return-statements,too-many-branches
|
def _cid_matches_tcp_tid_legacy(tid, cid, ifaces): # pylint: disable=too-many-branches
|
||||||
'''On kernels older than 6.1, the src_addr parameter is not available
|
'''On kernels older than 6.1, the src_addr parameter is not available
|
||||||
from the sysfs. Therefore, we need to infer a match based on other
|
from the sysfs. Therefore, we need to infer a match based on other
|
||||||
parameters. And there are a few cases where we're simply not sure
|
parameters. And there are a few cases where we're simply not sure
|
||||||
|
@ -164,76 +165,66 @@ class Udev:
|
||||||
cid_host_iface = cid['host-iface']
|
cid_host_iface = cid['host-iface']
|
||||||
cid_host_traddr = iputil.get_ipaddress_obj(cid['host-traddr'], ipv4_mapped_convert=True)
|
cid_host_traddr = iputil.get_ipaddress_obj(cid['host-traddr'], ipv4_mapped_convert=True)
|
||||||
|
|
||||||
if not cid_host_iface: # cid.host_iface is undefined
|
# Only check host_traddr if candidate cares about it
|
||||||
if not cid_host_traddr: # cid.host_traddr is undefined
|
if tid.host_traddr:
|
||||||
# When the existing cid.src_addr, cid.host_traddr, and cid.host_iface
|
tid_host_traddr = iputil.get_ipaddress_obj(tid.host_traddr, ipv4_mapped_convert=True)
|
||||||
# are all undefined (which can only happen on kernels prior to 6.1),
|
|
||||||
# we can't know for sure on which interface an existing connection
|
|
||||||
# was made. In this case, we can only declare a match if both
|
|
||||||
# tid.host_iface and tid.host_traddr are undefined as well.
|
|
||||||
logging.debug(
|
|
||||||
'Udev._cid_matches_tcp_tid_legacy() - cid=%s, tid=%s - Not enough info. Assume "match" but this could be wrong.',
|
|
||||||
cid,
|
|
||||||
tid,
|
|
||||||
)
|
|
||||||
return True
|
|
||||||
|
|
||||||
# cid.host_traddr is defined. If tid.host_traddr is also
|
if cid_host_traddr:
|
||||||
# defined, then it must match the existing cid.host_traddr.
|
|
||||||
if tid.host_traddr:
|
|
||||||
tid_host_traddr = iputil.get_ipaddress_obj(tid.host_traddr, ipv4_mapped_convert=True)
|
|
||||||
if tid_host_traddr != cid_host_traddr:
|
if tid_host_traddr != cid_host_traddr:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# If tid.host_iface is defined, then the interface where
|
else:
|
||||||
# the connection is located must match. If tid.host_iface
|
# If c->cfg.host_traddr is unknown, then the controller (c)
|
||||||
# is not defined, then we don't really care on which
|
# uses the interface's primary address as the source
|
||||||
# interface the connection was made and we can skip this test.
|
# address. If c->cfg.host_iface is defined we can
|
||||||
if tid.host_iface:
|
# determine the primary address associated with that
|
||||||
# With the existing cid.host_traddr, we can find the
|
# interface and compare that to the candidate->host_traddr.
|
||||||
# interface of the exisiting connection.
|
if cid_host_iface:
|
||||||
connection_iface = iputil.get_interface(str(cid_host_traddr))
|
if_addrs = ifaces.get(cid_host_iface, {4: [], 6: []})
|
||||||
if tid.host_iface != connection_iface:
|
source_addrs = if_addrs[tid_host_traddr.version]
|
||||||
|
if len(source_addrs): # Make sure it's not empty
|
||||||
|
primary_addr = iputil.get_ipaddress_obj(source_addrs[0], ipv4_mapped_convert=True)
|
||||||
|
if primary_addr != tid_host_traddr:
|
||||||
|
return False
|
||||||
|
|
||||||
|
else:
|
||||||
|
# If both c->cfg.host_traddr and c->cfg.host_iface are
|
||||||
|
# unknown, we don't have enough information to make a
|
||||||
|
# 100% positive match. Regardless, let's be optimistic
|
||||||
|
# and assume that we have a match.
|
||||||
|
logging.debug(
|
||||||
|
'Udev._cid_matches_tcp_tid_legacy() - [1] cid=%s, tid=%s - Not enough info. Assume "match" but this could be wrong.',
|
||||||
|
cid,
|
||||||
|
tid,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Only check host_iface if candidate cares about it
|
||||||
|
if tid.host_iface:
|
||||||
|
if cid_host_iface:
|
||||||
|
if tid.host_iface != cid_host_iface:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
else:
|
||||||
|
if cid_host_traddr:
|
||||||
|
connection_iface = iputil.get_interface(ifaces, cid_host_traddr)
|
||||||
|
if tid.host_iface != connection_iface:
|
||||||
|
return False
|
||||||
|
|
||||||
# cid.host_iface is defined
|
else:
|
||||||
if not cid_host_traddr: # cid.host_traddr is undefined
|
# If both c->cfg.host_traddr and c->cfg.host_iface are
|
||||||
if tid.host_iface and tid.host_iface != cid_host_iface:
|
# unknown, we don't have enough information to make a
|
||||||
return False
|
# 100% positive match. Regardless, let's be optimistic
|
||||||
|
# and assume that we have a match.
|
||||||
if tid.host_traddr:
|
logging.debug(
|
||||||
# It's impossible to tell the existing connection source
|
'Udev._cid_matches_tcp_tid_legacy() - [2] cid=%s, tid=%s - Not enough info. Assume "match" but this could be wrong.',
|
||||||
# address. So, we can't tell if it matches tid.host_traddr.
|
cid,
|
||||||
# However, if the existing host_iface has only one source
|
tid,
|
||||||
# address assigned to it, we can assume that the source
|
)
|
||||||
# address used for the existing connection is that address.
|
|
||||||
if_addrs = iputil.net_if_addrs().get(cid_host_iface, {4: [], 6: []})
|
|
||||||
tid_host_traddr = iputil.get_ipaddress_obj(tid.host_traddr, ipv4_mapped_convert=True)
|
|
||||||
source_addrs = if_addrs[tid_host_traddr.version]
|
|
||||||
if len(source_addrs) != 1:
|
|
||||||
return False
|
|
||||||
|
|
||||||
src_addr0 = iputil.get_ipaddress_obj(source_addrs[0], ipv4_mapped_convert=True)
|
|
||||||
if src_addr0 != tid_host_traddr:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
# cid.host_traddr is defined
|
|
||||||
if tid.host_iface and tid.host_iface != cid_host_iface:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if tid.host_traddr:
|
|
||||||
tid_host_traddr = iputil.get_ipaddress_obj(tid.host_traddr, ipv4_mapped_convert=True)
|
|
||||||
if tid_host_traddr != cid_host_traddr:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _cid_matches_tid(tid, cid): # pylint: disable=too-many-return-statements,too-many-branches
|
def _cid_matches_tid(tid, cid, ifaces): # pylint: disable=too-many-return-statements,too-many-branches
|
||||||
'''Check if existing controller's cid matches candidate controller's tid.
|
'''Check if existing controller's cid matches candidate controller's tid.
|
||||||
@param cid: The Connection ID of an existing controller (from the sysfs).
|
@param cid: The Connection ID of an existing controller (from the sysfs).
|
||||||
@param tid: The Transport ID of a candidate controller.
|
@param tid: The Transport ID of a candidate controller.
|
||||||
|
@ -242,8 +233,8 @@ class Udev:
|
||||||
be re-used for the candidate controller (specified by tid).
|
be re-used for the candidate controller (specified by tid).
|
||||||
|
|
||||||
We do not have a match if the candidate's tid.transport, tid.traddr,
|
We do not have a match if the candidate's tid.transport, tid.traddr,
|
||||||
tid.trsvcid, and tid.subsysnqn are not identical to those of the cid.
|
tid.trsvcid, tid.subsysnqn, and tid.host_nqn are not identical to those
|
||||||
These 4 parameters are mandatory for a match.
|
of the cid. These 5 parameters are mandatory for a match.
|
||||||
|
|
||||||
The tid.host_traddr and tid.host_iface depend on the transport type.
|
The tid.host_traddr and tid.host_iface depend on the transport type.
|
||||||
These parameters may not apply or have a different syntax/meaning
|
These parameters may not apply or have a different syntax/meaning
|
||||||
|
@ -274,8 +265,9 @@ class Udev:
|
||||||
tid_traddr = iputil.get_ipaddress_obj(tid.traddr, ipv4_mapped_convert=True)
|
tid_traddr = iputil.get_ipaddress_obj(tid.traddr, ipv4_mapped_convert=True)
|
||||||
cid_traddr = iputil.get_ipaddress_obj(cid['traddr'], ipv4_mapped_convert=True)
|
cid_traddr = iputil.get_ipaddress_obj(cid['traddr'], ipv4_mapped_convert=True)
|
||||||
else:
|
else:
|
||||||
cid_traddr = cid['traddr']
|
# For FC and loop we can do a case-insensitive comparison
|
||||||
tid_traddr = tid.traddr
|
tid_traddr = tid.traddr.lower()
|
||||||
|
cid_traddr = cid['traddr'].lower()
|
||||||
|
|
||||||
if cid_traddr != tid_traddr:
|
if cid_traddr != tid_traddr:
|
||||||
return False
|
return False
|
||||||
|
@ -290,7 +282,7 @@ class Udev:
|
||||||
# For legacy kernels (i.e. older than 6.1), the existing cid.src_addr
|
# For legacy kernels (i.e. older than 6.1), the existing cid.src_addr
|
||||||
# is always undefined. We need to use advanced logic to determine
|
# is always undefined. We need to use advanced logic to determine
|
||||||
# whether cid and tid match.
|
# whether cid and tid match.
|
||||||
return Udev._cid_matches_tcp_tid_legacy(tid, cid)
|
return Udev._cid_matches_tcp_tid_legacy(tid, cid, ifaces)
|
||||||
|
|
||||||
# The existing controller's cid.src_addr is always defined for kernel
|
# The existing controller's cid.src_addr is always defined for kernel
|
||||||
# 6.1 and later. We can use the existing controller's cid.src_addr to
|
# 6.1 and later. We can use the existing controller's cid.src_addr to
|
||||||
|
@ -303,12 +295,12 @@ class Udev:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# host-iface is an optional tcp-only parameter.
|
# host-iface is an optional tcp-only parameter.
|
||||||
if tid.host_iface and tid.host_iface != iputil.get_interface(str(src_addr)):
|
if tid.host_iface and tid.host_iface != iputil.get_interface(ifaces, src_addr):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
elif tid.transport == 'fc':
|
elif tid.transport == 'fc':
|
||||||
# host-traddr is mandatory for FC.
|
# host-traddr is mandatory for FC.
|
||||||
if tid.host_traddr != cid['host-traddr']:
|
if tid.host_traddr.lower() != cid['host-traddr'].lower():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
elif tid.transport == 'rdma':
|
elif tid.transport == 'rdma':
|
||||||
|
@ -326,17 +318,20 @@ class Udev:
|
||||||
Discovery Controller.
|
Discovery Controller.
|
||||||
@return The device if a match is found, None otherwise.
|
@return The device if a match is found, None otherwise.
|
||||||
'''
|
'''
|
||||||
for device in self._context.list_devices(
|
devices = self._context.list_devices(
|
||||||
subsystem='nvme', NVME_TRADDR=tid.traddr, NVME_TRSVCID=tid.trsvcid, NVME_TRTYPE=tid.transport
|
subsystem='nvme', NVME_TRADDR=tid.traddr, NVME_TRSVCID=tid.trsvcid, NVME_TRTYPE=tid.transport
|
||||||
):
|
)
|
||||||
if not self.is_dc_device(device):
|
if devices:
|
||||||
continue
|
ifaces = iputil.net_if_addrs()
|
||||||
|
for device in devices:
|
||||||
|
if not self.is_dc_device(device):
|
||||||
|
continue
|
||||||
|
|
||||||
cid = self.get_cid(device)
|
cid = self.get_cid(device)
|
||||||
if not self._cid_matches_tid(tid, cid):
|
if not self._cid_matches_tid(tid, cid, ifaces):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
return device
|
return device
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -345,17 +340,20 @@ class Udev:
|
||||||
I/O Controller.
|
I/O Controller.
|
||||||
@return The device if a match is found, None otherwise.
|
@return The device if a match is found, None otherwise.
|
||||||
'''
|
'''
|
||||||
for device in self._context.list_devices(
|
devices = self._context.list_devices(
|
||||||
subsystem='nvme', NVME_TRADDR=tid.traddr, NVME_TRSVCID=tid.trsvcid, NVME_TRTYPE=tid.transport
|
subsystem='nvme', NVME_TRADDR=tid.traddr, NVME_TRSVCID=tid.trsvcid, NVME_TRTYPE=tid.transport
|
||||||
):
|
)
|
||||||
if not self.is_ioc_device(device):
|
if devices:
|
||||||
continue
|
ifaces = iputil.net_if_addrs()
|
||||||
|
for device in devices:
|
||||||
|
if not self.is_ioc_device(device):
|
||||||
|
continue
|
||||||
|
|
||||||
cid = self.get_cid(device)
|
cid = self.get_cid(device)
|
||||||
if not self._cid_matches_tid(tid, cid):
|
if not self._cid_matches_tid(tid, cid, ifaces):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
return device
|
return device
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -364,68 +362,63 @@ class Udev:
|
||||||
@return A list of pyudev.device._device.Device objects
|
@return A list of pyudev.device._device.Device objects
|
||||||
'''
|
'''
|
||||||
tids = []
|
tids = []
|
||||||
for device in self._context.list_devices(subsystem='nvme'):
|
devices = self._context.list_devices(subsystem='nvme')
|
||||||
if device.properties.get('NVME_TRTYPE', '') not in transports:
|
if devices:
|
||||||
continue
|
ifaces = iputil.net_if_addrs()
|
||||||
|
for device in devices:
|
||||||
|
if device.properties.get('NVME_TRTYPE', '') not in transports:
|
||||||
|
continue
|
||||||
|
|
||||||
if not self.is_ioc_device(device):
|
if not self.is_ioc_device(device):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
tids.append(self.get_tid(device))
|
tids.append(self.get_tid(device, ifaces))
|
||||||
|
|
||||||
return tids
|
return tids
|
||||||
|
|
||||||
def _process_udev_event(self, event_source, condition): # pylint: disable=unused-argument
|
def _process_udev_event(self, event_source, condition): # pylint: disable=unused-argument
|
||||||
if condition == GLib.IO_IN:
|
if condition == GLib.IO_IN:
|
||||||
event_count = 0
|
try:
|
||||||
while True:
|
self.__handle_events()
|
||||||
try:
|
|
||||||
device = self._monitor.poll(timeout=0)
|
|
||||||
except EnvironmentError as ex:
|
|
||||||
device = None
|
|
||||||
# This event seems to happen in bursts. So, let's suppress
|
|
||||||
# logging for 2 seconds to avoid filling the syslog.
|
|
||||||
self._log_event_count += 1
|
|
||||||
now = time.time()
|
|
||||||
if now > self._log_event_soak_time:
|
|
||||||
logging.debug('Udev._process_udev_event() - %s [%s]', ex, self._log_event_count)
|
|
||||||
self._log_event_soak_time = now + 2
|
|
||||||
self._log_event_count = 0
|
|
||||||
|
|
||||||
if device is None:
|
except EnvironmentError as ex:
|
||||||
break
|
# Exceptions seem to happen in bursts. So, let's suppress
|
||||||
|
# logging for 2 seconds to avoid filling the syslog.
|
||||||
event_count += 1
|
self._log_event_count += 1
|
||||||
self._device_event(device, event_count)
|
now = time.time()
|
||||||
|
if now > self._log_event_soak_time:
|
||||||
|
logging.debug('Udev._process_udev_event() - %s [%s]', ex, self._log_event_count)
|
||||||
|
self._log_event_soak_time = now + 2
|
||||||
|
self._log_event_count = 0
|
||||||
|
|
||||||
return GLib.SOURCE_CONTINUE
|
return GLib.SOURCE_CONTINUE
|
||||||
|
|
||||||
@staticmethod
|
def __handle_events(self):
|
||||||
def __cback_names(action_cbacks, device_cback):
|
event_count = 0
|
||||||
names = []
|
read_device = partial(self._monitor.poll, timeout=0)
|
||||||
for cback in action_cbacks:
|
for device in iter(read_device, None):
|
||||||
names.append(cback.__name__ + '()')
|
if device is None: # This should never happen,...
|
||||||
if device_cback:
|
break # ...but better safe than sorry.
|
||||||
names.append(device_cback.__name__ + '()')
|
|
||||||
return names
|
|
||||||
|
|
||||||
def _device_event(self, device, event_count):
|
event_count += 1
|
||||||
action_cbacks = self._action_event_registry.get(device.action, set())
|
|
||||||
device_cback = self._device_event_registry.get(device.sys_name, None)
|
|
||||||
|
|
||||||
logging.debug(
|
action_cbacks = self._action_event_registry.get(device.action, None)
|
||||||
'Udev._device_event() - %-8s %-6s %-8s %s',
|
device_cback = self._device_event_registry.get(device.sys_name, None)
|
||||||
f'{device.sys_name}:',
|
if action_cbacks or device_cback:
|
||||||
device.action,
|
logging.debug(
|
||||||
f'{event_count:2}:{device.sequence_number}',
|
'Udev.__handle_events() - %-7s %-6s %2s:%s',
|
||||||
self.__cback_names(action_cbacks, device_cback),
|
device.sys_name,
|
||||||
)
|
device.action,
|
||||||
|
event_count,
|
||||||
|
device.sequence_number,
|
||||||
|
)
|
||||||
|
|
||||||
for action_cback in action_cbacks:
|
if action_cbacks:
|
||||||
GLib.idle_add(action_cback, device)
|
for action_cback in action_cbacks:
|
||||||
|
GLib.idle_add(action_cback, device)
|
||||||
|
|
||||||
if device_cback is not None:
|
if device_cback is not None:
|
||||||
GLib.idle_add(device_cback, device)
|
GLib.idle_add(device_cback, device)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_property(device, prop, default=''):
|
def _get_property(device, prop, default=''):
|
||||||
|
@ -470,7 +463,7 @@ class Udev:
|
||||||
return attr_str[start:end]
|
return attr_str[start:end]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_tid(device):
|
def get_tid(device, ifaces):
|
||||||
'''@brief return the Transport ID associated with a udev device'''
|
'''@brief return the Transport ID associated with a udev device'''
|
||||||
cid = Udev.get_cid(device)
|
cid = Udev.get_cid(device)
|
||||||
if cid['transport'] == 'tcp':
|
if cid['transport'] == 'tcp':
|
||||||
|
@ -479,7 +472,7 @@ class Udev:
|
||||||
# We'll try to find the interface from the source address on
|
# We'll try to find the interface from the source address on
|
||||||
# the connection. Only available if kernel exposes the source
|
# the connection. Only available if kernel exposes the source
|
||||||
# address (src_addr) in the "address" attribute.
|
# address (src_addr) in the "address" attribute.
|
||||||
cid['host-iface'] = iputil.get_interface(src_addr)
|
cid['host-iface'] = iputil.get_interface(ifaces, iputil.get_ipaddress_obj(src_addr))
|
||||||
|
|
||||||
return trid.TID(cid)
|
return trid.TID(cid)
|
||||||
|
|
||||||
|
|
|
@ -31,14 +31,17 @@ class Test(unittest.TestCase):
|
||||||
|
|
||||||
def test_get_interface(self):
|
def test_get_interface(self):
|
||||||
'''Check that get_interface() returns the right info'''
|
'''Check that get_interface() returns the right info'''
|
||||||
|
ifaces = iputil.net_if_addrs()
|
||||||
for iface in self.ifaces:
|
for iface in self.ifaces:
|
||||||
for addr_entry in iface['addr_info']:
|
for addr_entry in iface['addr_info']:
|
||||||
addr = ipaddress.ip_address(addr_entry['local'])
|
addr = ipaddress.ip_address(addr_entry['local'])
|
||||||
# Link local addresses may appear on more than one interface and therefore cannot be used.
|
# Link local addresses may appear on more than one interface and therefore cannot be used.
|
||||||
if not addr.is_link_local:
|
if not addr.is_link_local:
|
||||||
self.assertEqual(iface['ifname'], iputil.get_interface(str(addr)))
|
self.assertEqual(iface['ifname'], iputil.get_interface(ifaces, addr))
|
||||||
|
|
||||||
self.assertEqual('', iputil.get_interface('255.255.255.255'))
|
self.assertEqual('', iputil.get_interface(ifaces, iputil.get_ipaddress_obj('255.255.255.255')))
|
||||||
|
self.assertEqual('', iputil.get_interface(ifaces, ''))
|
||||||
|
self.assertEqual('', iputil.get_interface(ifaces, None))
|
||||||
|
|
||||||
def test_mac2iface(self):
|
def test_mac2iface(self):
|
||||||
for iface in self.ifaces:
|
for iface in self.ifaces:
|
||||||
|
@ -69,9 +72,7 @@ class Test(unittest.TestCase):
|
||||||
self.assertNotIn(bad_trtype, l2)
|
self.assertNotIn(bad_trtype, l2)
|
||||||
|
|
||||||
def test__data_matches_ip(self):
|
def test__data_matches_ip(self):
|
||||||
self.assertFalse(iputil._data_matches_ip(None, None, None))
|
self.assertFalse(iputil.ip_equal(None, None))
|
||||||
self.assertFalse(iputil._data_matches_ip(socket.AF_INET, None, None))
|
|
||||||
self.assertFalse(iputil._data_matches_ip(socket.AF_INET6, None, None))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -48,11 +48,12 @@ class Test(unittest.TestCase):
|
||||||
self.assertEqual(nbft_conf.iocs, EXPECTED_IOCS)
|
self.assertEqual(nbft_conf.iocs, EXPECTED_IOCS)
|
||||||
|
|
||||||
def test_dir_without_nbft_files(self):
|
def test_dir_without_nbft_files(self):
|
||||||
conf.NbftConf.destroy() # Make sure singleton does not exist
|
if hasattr(self, 'assertNoLogs'): # assertNoLogs only in Python 3.10 or later
|
||||||
with self.assertNoLogs(logger=logging.getLogger(), level='DEBUG'):
|
conf.NbftConf.destroy() # Make sure singleton does not exist
|
||||||
nbft_conf = conf.NbftConf('/tmp')
|
with self.assertNoLogs(logger=logging.getLogger(), level='DEBUG'):
|
||||||
self.assertEqual(nbft_conf.dcs, [])
|
nbft_conf = conf.NbftConf('/tmp')
|
||||||
self.assertEqual(nbft_conf.iocs, [])
|
self.assertEqual(nbft_conf.dcs, [])
|
||||||
|
self.assertEqual(nbft_conf.iocs, [])
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import unittest
|
import unittest
|
||||||
from staslib import conf, log
|
from staslib import defs, conf, log
|
||||||
from pyfakefs.fake_filesystem_unittest import TestCase
|
from pyfakefs.fake_filesystem_unittest import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ class TestStandardNvmeFabricsFile(unittest.TestCase):
|
||||||
def test_regular_user(self):
|
def test_regular_user(self):
|
||||||
conf.NvmeOptions.destroy() # Make sure singleton does not exist
|
conf.NvmeOptions.destroy() # Make sure singleton does not exist
|
||||||
if os.path.exists('/dev/nvme-fabrics'):
|
if os.path.exists('/dev/nvme-fabrics'):
|
||||||
if os.geteuid() != 0:
|
if os.geteuid() != 0 and defs.KERNEL_VERSION < defs.KERNEL_ALL_MIN_VERSION:
|
||||||
with self.assertRaises(PermissionError):
|
with self.assertRaises(PermissionError):
|
||||||
nvme_options = conf.NvmeOptions()
|
nvme_options = conf.NvmeOptions()
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -3,7 +3,6 @@ import json
|
||||||
import shutil
|
import shutil
|
||||||
import logging
|
import logging
|
||||||
import unittest
|
import unittest
|
||||||
import ipaddress
|
|
||||||
import subprocess
|
import subprocess
|
||||||
from staslib import defs, iputil, log, trid, udev
|
from staslib import defs, iputil, log, trid, udev
|
||||||
|
|
||||||
|
@ -294,6 +293,7 @@ class Test(unittest.TestCase):
|
||||||
self.assertTrue(udev.UDEV.is_ioc_device(device))
|
self.assertTrue(udev.UDEV.is_ioc_device(device))
|
||||||
|
|
||||||
def test__cid_matches_tid(self):
|
def test__cid_matches_tid(self):
|
||||||
|
ifaces = iputil.net_if_addrs()
|
||||||
for ifname, addrs in self.ifaces.items():
|
for ifname, addrs in self.ifaces.items():
|
||||||
##############################################
|
##############################################
|
||||||
# IPV4
|
# IPV4
|
||||||
|
@ -321,10 +321,14 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
for case_id, tid, match in get_tids_to_test(4, src_ipv4, ifname):
|
for case_id, tid, match in get_tids_to_test(4, src_ipv4, ifname):
|
||||||
self.assertEqual(match, udev.UDEV._cid_matches_tid(tid, cid), msg=f'Test Case {case_id} failed')
|
self.assertEqual(
|
||||||
|
match, udev.UDEV._cid_matches_tid(tid, cid, ifaces), msg=f'Test Case {case_id} failed'
|
||||||
|
)
|
||||||
if case_id != 8:
|
if case_id != 8:
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
match, udev.UDEV._cid_matches_tid(tid, cid_legacy), msg=f'Legacy Test Case {case_id} failed'
|
match,
|
||||||
|
udev.UDEV._cid_matches_tid(tid, cid_legacy, ifaces),
|
||||||
|
msg=f'Legacy Test Case {case_id} failed',
|
||||||
)
|
)
|
||||||
|
|
||||||
cid_legacy = {
|
cid_legacy = {
|
||||||
|
@ -347,7 +351,9 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(True, udev.UDEV._cid_matches_tid(tid, cid_legacy), msg=f'Legacy Test Case A4.1 failed')
|
self.assertEqual(
|
||||||
|
True, udev.UDEV._cid_matches_tid(tid, cid_legacy, ifaces), msg=f'Legacy Test Case A4.1 failed'
|
||||||
|
)
|
||||||
|
|
||||||
cid_legacy = {
|
cid_legacy = {
|
||||||
'transport': 'tcp',
|
'transport': 'tcp',
|
||||||
|
@ -368,9 +374,13 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(True, udev.UDEV._cid_matches_tid(tid, cid_legacy), msg=f'Legacy Test Case A4.2 failed')
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
True, udev.UDEV._cid_matches_tcp_tid_legacy(tid, cid_legacy), msg=f'Legacy Test Case A4.3 failed'
|
True, udev.UDEV._cid_matches_tid(tid, cid_legacy, ifaces), msg=f'Legacy Test Case A4.2 failed'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
True,
|
||||||
|
udev.UDEV._cid_matches_tcp_tid_legacy(tid, cid_legacy, ifaces),
|
||||||
|
msg=f'Legacy Test Case A4.3 failed',
|
||||||
)
|
)
|
||||||
|
|
||||||
cid_legacy = {
|
cid_legacy = {
|
||||||
|
@ -393,7 +403,9 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(False, udev.UDEV._cid_matches_tid(tid, cid_legacy), msg=f'Legacy Test Case B4 failed')
|
self.assertEqual(
|
||||||
|
False, udev.UDEV._cid_matches_tid(tid, cid_legacy, ifaces), msg=f'Legacy Test Case B4 failed'
|
||||||
|
)
|
||||||
|
|
||||||
tid = trid.TID(
|
tid = trid.TID(
|
||||||
{
|
{
|
||||||
|
@ -405,7 +417,9 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(False, udev.UDEV._cid_matches_tid(tid, cid_legacy), msg=f'Legacy Test Case C4 failed')
|
self.assertEqual(
|
||||||
|
False, udev.UDEV._cid_matches_tid(tid, cid_legacy, ifaces), msg=f'Legacy Test Case C4 failed'
|
||||||
|
)
|
||||||
|
|
||||||
tid = trid.TID(
|
tid = trid.TID(
|
||||||
{
|
{
|
||||||
|
@ -417,7 +431,9 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(True, udev.UDEV._cid_matches_tid(tid, cid_legacy), msg=f'Legacy Test Case D4 failed')
|
self.assertEqual(
|
||||||
|
True, udev.UDEV._cid_matches_tid(tid, cid_legacy, ifaces), msg=f'Legacy Test Case D4 failed'
|
||||||
|
)
|
||||||
|
|
||||||
cid_legacy = {
|
cid_legacy = {
|
||||||
'transport': 'tcp',
|
'transport': 'tcp',
|
||||||
|
@ -440,7 +456,9 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(False, udev.UDEV._cid_matches_tid(tid, cid_legacy), msg=f'Legacy Test Case E4 failed')
|
self.assertEqual(
|
||||||
|
False, udev.UDEV._cid_matches_tid(tid, cid_legacy, ifaces), msg=f'Legacy Test Case E4 failed'
|
||||||
|
)
|
||||||
|
|
||||||
tid = trid.TID(
|
tid = trid.TID(
|
||||||
{
|
{
|
||||||
|
@ -452,7 +470,9 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(False, udev.UDEV._cid_matches_tid(tid, cid_legacy), msg=f'Legacy Test Case F4 failed')
|
self.assertEqual(
|
||||||
|
False, udev.UDEV._cid_matches_tid(tid, cid_legacy, ifaces), msg=f'Legacy Test Case F4 failed'
|
||||||
|
)
|
||||||
|
|
||||||
tid = trid.TID(
|
tid = trid.TID(
|
||||||
{
|
{
|
||||||
|
@ -467,7 +487,9 @@ class Test(unittest.TestCase):
|
||||||
match = len(ipv4_addrs) == 1 and iputil.get_ipaddress_obj(
|
match = len(ipv4_addrs) == 1 and iputil.get_ipaddress_obj(
|
||||||
ipv4_addrs[0], ipv4_mapped_convert=True
|
ipv4_addrs[0], ipv4_mapped_convert=True
|
||||||
) == iputil.get_ipaddress_obj(tid.host_traddr, ipv4_mapped_convert=True)
|
) == iputil.get_ipaddress_obj(tid.host_traddr, ipv4_mapped_convert=True)
|
||||||
self.assertEqual(match, udev.UDEV._cid_matches_tid(tid, cid_legacy), msg=f'Legacy Test Case G4 failed')
|
self.assertEqual(
|
||||||
|
match, udev.UDEV._cid_matches_tid(tid, cid_legacy, ifaces), msg=f'Legacy Test Case G4 failed'
|
||||||
|
)
|
||||||
|
|
||||||
##############################################
|
##############################################
|
||||||
# IPV6
|
# IPV6
|
||||||
|
@ -495,9 +517,13 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
for case_id, tid, match in get_tids_to_test(6, src_ipv6, ifname):
|
for case_id, tid, match in get_tids_to_test(6, src_ipv6, ifname):
|
||||||
self.assertEqual(match, udev.UDEV._cid_matches_tid(tid, cid), msg=f'Test Case {case_id} failed')
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
match, udev.UDEV._cid_matches_tid(tid, cid_legacy), msg=f'Legacy Test Case {case_id} failed'
|
match, udev.UDEV._cid_matches_tid(tid, cid, ifaces), msg=f'Test Case {case_id} failed'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
match,
|
||||||
|
udev.UDEV._cid_matches_tid(tid, cid_legacy, ifaces),
|
||||||
|
msg=f'Legacy Test Case {case_id} failed',
|
||||||
)
|
)
|
||||||
|
|
||||||
cid_legacy = {
|
cid_legacy = {
|
||||||
|
@ -520,7 +546,9 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(True, udev.UDEV._cid_matches_tid(tid, cid_legacy), msg=f'Legacy Test Case A6.1 failed')
|
self.assertEqual(
|
||||||
|
True, udev.UDEV._cid_matches_tid(tid, cid_legacy, ifaces), msg=f'Legacy Test Case A6.1 failed'
|
||||||
|
)
|
||||||
|
|
||||||
cid_legacy = {
|
cid_legacy = {
|
||||||
'transport': 'tcp',
|
'transport': 'tcp',
|
||||||
|
@ -541,9 +569,13 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(True, udev.UDEV._cid_matches_tid(tid, cid_legacy), msg=f'Legacy Test Case A6.2 failed')
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
True, udev.UDEV._cid_matches_tcp_tid_legacy(tid, cid_legacy), msg=f'Legacy Test Case A6.3 failed'
|
True, udev.UDEV._cid_matches_tid(tid, cid_legacy, ifaces), msg=f'Legacy Test Case A6.2 failed'
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
True,
|
||||||
|
udev.UDEV._cid_matches_tcp_tid_legacy(tid, cid_legacy, ifaces),
|
||||||
|
msg=f'Legacy Test Case A6.3 failed',
|
||||||
)
|
)
|
||||||
|
|
||||||
cid_legacy = {
|
cid_legacy = {
|
||||||
|
@ -566,7 +598,9 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(False, udev.UDEV._cid_matches_tid(tid, cid_legacy), msg=f'Legacy Test Case B6 failed')
|
self.assertEqual(
|
||||||
|
False, udev.UDEV._cid_matches_tid(tid, cid_legacy, ifaces), msg=f'Legacy Test Case B6 failed'
|
||||||
|
)
|
||||||
|
|
||||||
tid = trid.TID(
|
tid = trid.TID(
|
||||||
{
|
{
|
||||||
|
@ -578,7 +612,9 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(False, udev.UDEV._cid_matches_tid(tid, cid_legacy), msg=f'Legacy Test Case C6 failed')
|
self.assertEqual(
|
||||||
|
False, udev.UDEV._cid_matches_tid(tid, cid_legacy, ifaces), msg=f'Legacy Test Case C6 failed'
|
||||||
|
)
|
||||||
|
|
||||||
tid = trid.TID(
|
tid = trid.TID(
|
||||||
{
|
{
|
||||||
|
@ -590,7 +626,9 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(True, udev.UDEV._cid_matches_tid(tid, cid_legacy), msg=f'Legacy Test Case D6 failed')
|
self.assertEqual(
|
||||||
|
True, udev.UDEV._cid_matches_tid(tid, cid_legacy, ifaces), msg=f'Legacy Test Case D6 failed'
|
||||||
|
)
|
||||||
|
|
||||||
cid_legacy = {
|
cid_legacy = {
|
||||||
'transport': 'tcp',
|
'transport': 'tcp',
|
||||||
|
@ -613,7 +651,9 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(False, udev.UDEV._cid_matches_tid(tid, cid_legacy), msg=f'Legacy Test Case E6 failed')
|
self.assertEqual(
|
||||||
|
False, udev.UDEV._cid_matches_tid(tid, cid_legacy, ifaces), msg=f'Legacy Test Case E6 failed'
|
||||||
|
)
|
||||||
|
|
||||||
tid = trid.TID(
|
tid = trid.TID(
|
||||||
{
|
{
|
||||||
|
@ -625,7 +665,9 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(False, udev.UDEV._cid_matches_tid(tid, cid_legacy), msg=f'Legacy Test Case F6 failed')
|
self.assertEqual(
|
||||||
|
False, udev.UDEV._cid_matches_tid(tid, cid_legacy, ifaces), msg=f'Legacy Test Case F6 failed'
|
||||||
|
)
|
||||||
|
|
||||||
tid = trid.TID(
|
tid = trid.TID(
|
||||||
{
|
{
|
||||||
|
@ -640,7 +682,9 @@ class Test(unittest.TestCase):
|
||||||
match = len(ipv6_addrs) == 1 and iputil.get_ipaddress_obj(
|
match = len(ipv6_addrs) == 1 and iputil.get_ipaddress_obj(
|
||||||
ipv6_addrs[0], ipv4_mapped_convert=True
|
ipv6_addrs[0], ipv4_mapped_convert=True
|
||||||
) == iputil.get_ipaddress_obj(tid.host_traddr, ipv4_mapped_convert=True)
|
) == iputil.get_ipaddress_obj(tid.host_traddr, ipv4_mapped_convert=True)
|
||||||
self.assertEqual(match, udev.UDEV._cid_matches_tid(tid, cid_legacy), msg=f'Legacy Test Case G6 failed')
|
self.assertEqual(
|
||||||
|
match, udev.UDEV._cid_matches_tid(tid, cid_legacy, ifaces), msg=f'Legacy Test Case G6 failed'
|
||||||
|
)
|
||||||
|
|
||||||
##############################################
|
##############################################
|
||||||
# FC
|
# FC
|
||||||
|
@ -664,7 +708,7 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(True, udev.UDEV._cid_matches_tid(tid, cid), msg=f'Test Case FC-1 failed')
|
self.assertEqual(True, udev.UDEV._cid_matches_tid(tid, cid, ifaces), msg=f'Test Case FC-1 failed')
|
||||||
|
|
||||||
tid = trid.TID(
|
tid = trid.TID(
|
||||||
{
|
{
|
||||||
|
@ -676,7 +720,7 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(False, udev.UDEV._cid_matches_tid(tid, cid), msg=f'Test Case FC-2 failed')
|
self.assertEqual(False, udev.UDEV._cid_matches_tid(tid, cid, ifaces), msg=f'Test Case FC-2 failed')
|
||||||
|
|
||||||
##############################################
|
##############################################
|
||||||
# RDMA
|
# RDMA
|
||||||
|
@ -700,7 +744,7 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(True, udev.UDEV._cid_matches_tid(tid, cid), msg=f'Test Case RDMA-1 failed')
|
self.assertEqual(True, udev.UDEV._cid_matches_tid(tid, cid, ifaces), msg=f'Test Case RDMA-1 failed')
|
||||||
|
|
||||||
tid = trid.TID(
|
tid = trid.TID(
|
||||||
{
|
{
|
||||||
|
@ -712,7 +756,7 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(False, udev.UDEV._cid_matches_tid(tid, cid), msg=f'Test Case RDMA-2 failed')
|
self.assertEqual(False, udev.UDEV._cid_matches_tid(tid, cid, ifaces), msg=f'Test Case RDMA-2 failed')
|
||||||
|
|
||||||
tid = trid.TID(
|
tid = trid.TID(
|
||||||
{
|
{
|
||||||
|
@ -723,7 +767,7 @@ class Test(unittest.TestCase):
|
||||||
'host-nqn': '',
|
'host-nqn': '',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(True, udev.UDEV._cid_matches_tid(tid, cid), msg=f'Test Case RDMA-3 failed')
|
self.assertEqual(True, udev.UDEV._cid_matches_tid(tid, cid, ifaces), msg=f'Test Case RDMA-3 failed')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -26,7 +26,7 @@ configure_file(
|
||||||
input: 'stas-config@.service',
|
input: 'stas-config@.service',
|
||||||
output: 'stas-config@.service',
|
output: 'stas-config@.service',
|
||||||
install_dir: sd_unit_dir,
|
install_dir: sd_unit_dir,
|
||||||
copy: true,
|
configuration: conf,
|
||||||
)
|
)
|
||||||
|
|
||||||
configure_file(
|
configure_file(
|
||||||
|
|
|
@ -5,13 +5,13 @@
|
||||||
# This file is part of NVMe STorage Appliance Services (nvme-stas).
|
# This file is part of NVMe STorage Appliance Services (nvme-stas).
|
||||||
#
|
#
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=nvme-stas /etc/nvme/%i auto-generation
|
Description=nvme-stas @ETC@/nvme/%i auto-generation
|
||||||
Documentation=man:stas-config@.service(8)
|
Documentation=man:stas-config@.service(8)
|
||||||
ConditionFileNotEmpty=|!/etc/nvme/%i
|
ConditionFileNotEmpty=|!@ETC@/nvme/%i
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
ExecStart=/usr/bin/stasadm %i -f /etc/nvme/%i
|
ExecStart=/usr/bin/stasadm %i -f @ETC@/nvme/%i
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=stas-config.target
|
WantedBy=stas-config.target
|
||||||
|
|
420
utils/nvmet/nvmet-large.conf
Normal file
420
utils/nvmet/nvmet-large.conf
Normal file
|
@ -0,0 +1,420 @@
|
||||||
|
# Config file format: Python, i.e. dict(), list(), int, str, etc...
|
||||||
|
# port ids (id) are integers 0...N
|
||||||
|
# namespaces are integers 0..N
|
||||||
|
# subsysnqn can be integers or strings
|
||||||
|
{
|
||||||
|
'ports': [
|
||||||
|
{
|
||||||
|
'id': 1,
|
||||||
|
'adrfam': 'ipv6',
|
||||||
|
'traddr': '::',
|
||||||
|
#'adrfam': 'ipv4',
|
||||||
|
#'traddr': '0.0.0.0',
|
||||||
|
'trsvcid': 8009,
|
||||||
|
'trtype': 'tcp',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
'subsystems': [
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem1',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem2',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem3',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem4',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem5',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem6',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem7',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem8',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem9',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem10',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem11',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem12',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem13',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem14',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem15',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem16',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem17',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem18',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem19',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem20',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem21',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem22',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem23',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem24',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem25',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem26',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem27',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem28',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem29',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem30',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem31',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem32',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem33',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem34',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem35',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem36',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem37',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem38',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem39',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem40',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem41',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem42',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem43',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem44',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem45',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem46',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem47',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem48',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem49',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem50',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem51',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem52',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem53',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem54',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem55',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem56',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem57',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem58',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem59',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem60',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem61',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem62',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem63',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem64',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem65',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem66',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem67',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem68',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem69',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem70',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem71',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem72',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem73',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem74',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem75',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem76',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem77',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem78',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem79',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'subsysnqn': 'subsystem80',
|
||||||
|
'port': 1,
|
||||||
|
'namespaces': [1, 2, 3, 4, 5, 6],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue