Merging upstream version 2.3~rc1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
972d2d9aa2
commit
ca2ec6771a
37 changed files with 1946 additions and 407 deletions
199
staslib/udev.py
199
staslib/udev.py
|
@ -153,6 +153,169 @@ class Udev:
|
|||
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _cid_matches_tcp_tid_legacy(tid, cid): # pylint: disable=too-many-return-statements,too-many-branches
|
||||
'''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
|
||||
parameters. And there are a few cases where we're simply not sure
|
||||
whether an existing connection (cid) matches the candidate
|
||||
connection (tid).
|
||||
'''
|
||||
cid_host_iface = cid['host-iface']
|
||||
cid_host_traddr = iputil.get_ipaddress_obj(cid['host-traddr'], ipv4_mapped_convert=True)
|
||||
|
||||
if not cid_host_iface: # cid.host_iface is undefined
|
||||
if not cid_host_traddr: # cid.host_traddr is undefined
|
||||
# When the existing cid.src_addr, cid.host_traddr, and cid.host_iface
|
||||
# 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
|
||||
# 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:
|
||||
return False
|
||||
|
||||
# If tid.host_iface is defined, then the interface where
|
||||
# the connection is located must match. If tid.host_iface
|
||||
# is not defined, then we don't really care on which
|
||||
# interface the connection was made and we can skip this test.
|
||||
if tid.host_iface:
|
||||
# With the existing cid.host_traddr, we can find the
|
||||
# interface of the exisiting connection.
|
||||
connection_iface = iputil.get_interface(str(cid_host_traddr))
|
||||
if tid.host_iface != connection_iface:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
# cid.host_iface is defined
|
||||
if not cid_host_traddr: # cid.host_traddr is undefined
|
||||
if tid.host_iface and tid.host_iface != cid_host_iface:
|
||||
return False
|
||||
|
||||
if tid.host_traddr:
|
||||
# It's impossible to tell the existing connection source
|
||||
# address. So, we can't tell if it matches tid.host_traddr.
|
||||
# However, if the existing host_iface has only one source
|
||||
# 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
|
||||
|
||||
@staticmethod
|
||||
def _cid_matches_tid(tid, cid): # pylint: disable=too-many-return-statements,too-many-branches
|
||||
'''Check if existing controller's cid matches candidate controller's tid.
|
||||
@param cid: The Connection ID of an existing controller (from the sysfs).
|
||||
@param tid: The Transport ID of a candidate controller.
|
||||
|
||||
We're trying to find if an existing connection (specified by cid) can
|
||||
be re-used for the candidate controller (specified by tid).
|
||||
|
||||
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.
|
||||
These 4 parameters are mandatory for a match.
|
||||
|
||||
The tid.host_traddr and tid.host_iface depend on the transport type.
|
||||
These parameters may not apply or have a different syntax/meaning
|
||||
depending on the transport type.
|
||||
|
||||
For TCP only:
|
||||
With regards to the candidate's tid.host_traddr and tid.host_iface,
|
||||
if those are defined but do not match the existing cid.host_traddr
|
||||
and cid.host_iface, we may still be able to find a match by taking
|
||||
the existing cid.src_addr into consideration since that parameter
|
||||
identifies the actual source address of the connection and therefore
|
||||
can be used to infer the interface of the connection. However, the
|
||||
cid.src_addr can only be read from the sysfs starting with kernel
|
||||
6.1.
|
||||
'''
|
||||
# 'transport', 'traddr', 'trsvcid', and 'subsysnqn' must exactly match.
|
||||
if cid['transport'] != tid.transport or cid['trsvcid'] != tid.trsvcid or cid['subsysnqn'] != tid.subsysnqn:
|
||||
return False
|
||||
|
||||
if tid.transport in ('tcp', 'rdma'):
|
||||
# Need to convert to ipaddress objects to properly
|
||||
# handle all variations of IPv6 addresses.
|
||||
tid_traddr = iputil.get_ipaddress_obj(tid.traddr, ipv4_mapped_convert=True)
|
||||
cid_traddr = iputil.get_ipaddress_obj(cid['traddr'], ipv4_mapped_convert=True)
|
||||
else:
|
||||
cid_traddr = cid['traddr']
|
||||
tid_traddr = tid.traddr
|
||||
|
||||
if cid_traddr != tid_traddr:
|
||||
return False
|
||||
|
||||
# We need to know the type of transport to compare 'host-traddr' and
|
||||
# 'host-iface'. These parameters don't apply to all transport types
|
||||
# and may have a different meaning/syntax.
|
||||
if tid.transport == 'tcp':
|
||||
if tid.host_traddr or tid.host_iface:
|
||||
src_addr = iputil.get_ipaddress_obj(cid['src-addr'], ipv4_mapped_convert=True)
|
||||
if not 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
|
||||
# whether cid and tid match.
|
||||
return Udev._cid_matches_tcp_tid_legacy(tid, cid)
|
||||
|
||||
# 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
|
||||
# find the interface on which the connection was made and therefore
|
||||
# match it to the candidate's tid.host_iface. And the cid.src_addr
|
||||
# can also be used to match the candidate's tid.host_traddr.
|
||||
if tid.host_traddr:
|
||||
tid_host_traddr = iputil.get_ipaddress_obj(tid.host_traddr, ipv4_mapped_convert=True)
|
||||
if tid_host_traddr != src_addr:
|
||||
return False
|
||||
|
||||
# host-iface is an optional tcp-only parameter.
|
||||
if tid.host_iface and tid.host_iface != iputil.get_interface(str(src_addr)):
|
||||
return False
|
||||
|
||||
elif tid.transport == 'fc':
|
||||
# host-traddr is mandatory for FC.
|
||||
if tid.host_traddr != cid['host-traddr']:
|
||||
return False
|
||||
|
||||
elif tid.transport == 'rdma':
|
||||
# host-traddr is optional for RDMA and is expressed as an IP address.
|
||||
if tid.host_traddr:
|
||||
tid_host_traddr = iputil.get_ipaddress_obj(tid.host_traddr, ipv4_mapped_convert=True)
|
||||
cid_host_traddr = iputil.get_ipaddress_obj(cid['host-traddr'], ipv4_mapped_convert=True)
|
||||
if tid_host_traddr != cid_host_traddr:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def find_nvme_dc_device(self, tid):
|
||||
'''@brief Find the nvme device associated with the specified
|
||||
Discovery Controller.
|
||||
|
@ -164,7 +327,8 @@ class Udev:
|
|||
if not self.is_dc_device(device):
|
||||
continue
|
||||
|
||||
if self.get_tid(device) != tid:
|
||||
cid = self.get_cid(device)
|
||||
if not self._cid_matches_tid(tid, cid):
|
||||
continue
|
||||
|
||||
return device
|
||||
|
@ -182,7 +346,8 @@ class Udev:
|
|||
if not self.is_ioc_device(device):
|
||||
continue
|
||||
|
||||
if self.get_tid(device) != tid:
|
||||
cid = self.get_cid(device)
|
||||
if not self._cid_matches_tid(tid, cid):
|
||||
continue
|
||||
|
||||
return device
|
||||
|
@ -299,29 +464,33 @@ class Udev:
|
|||
|
||||
return attr_str[start:end]
|
||||
|
||||
@staticmethod
|
||||
def _get_host_iface(device):
|
||||
host_iface = Udev._get_property(device, 'NVME_HOST_IFACE')
|
||||
if not host_iface:
|
||||
# We'll try to find the interface from the source address on
|
||||
# the connection. Only available if kernel exposes the source
|
||||
# address (src_addr) in the "address" attribute.
|
||||
src_addr = Udev.get_key_from_attr(device, 'address', 'src_addr=')
|
||||
host_iface = iputil.get_interface(src_addr)
|
||||
return host_iface
|
||||
|
||||
@staticmethod
|
||||
def get_tid(device):
|
||||
'''@brief return the Transport ID associated with a udev device'''
|
||||
cid = Udev.get_cid(device)
|
||||
if cid['transport'] == 'tcp':
|
||||
src_addr = cid['src-addr']
|
||||
if not cid['host-iface'] and src_addr:
|
||||
# We'll try to find the interface from the source address on
|
||||
# the connection. Only available if kernel exposes the source
|
||||
# address (src_addr) in the "address" attribute.
|
||||
cid['host-iface'] = iputil.get_interface(src_addr)
|
||||
|
||||
return trid.TID(cid)
|
||||
|
||||
@staticmethod
|
||||
def get_cid(device):
|
||||
'''@brief return the Connection ID associated with a udev device'''
|
||||
cid = {
|
||||
'transport': Udev._get_property(device, 'NVME_TRTYPE'),
|
||||
'traddr': Udev._get_property(device, 'NVME_TRADDR'),
|
||||
'trsvcid': Udev._get_property(device, 'NVME_TRSVCID'),
|
||||
'host-traddr': Udev._get_property(device, 'NVME_HOST_TRADDR'),
|
||||
'host-iface': Udev._get_host_iface(device),
|
||||
'host-iface': Udev._get_property(device, 'NVME_HOST_IFACE'),
|
||||
'subsysnqn': Udev._get_attribute(device, 'subsysnqn'),
|
||||
'src-addr': Udev.get_key_from_attr(device, 'address', 'src_addr='),
|
||||
}
|
||||
return trid.TID(cid)
|
||||
return cid
|
||||
|
||||
|
||||
UDEV = Udev() # Singleton
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue