Skip to content

Commit

Permalink
udev: Rework/simplify _cid_matches_tid()
Browse files Browse the repository at this point in the history
Signed-off-by: Martin Belanger <martin.belanger@dell.com>
  • Loading branch information
Martin Belanger committed Jun 12, 2023
1 parent b4fb2b4 commit a5f9dc2
Show file tree
Hide file tree
Showing 6 changed files with 685 additions and 149 deletions.
10 changes: 5 additions & 5 deletions coverage.sh.in
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ sd_start() {
sudo systemctl reset-failed "${unit}" >/dev/null 2>&1

log "Start ${app}"
sudo systemd-run --unit="${unit}" --working-directory=. --property=Type=dbus --property=BusName="${dbus}" --property="SyslogIdentifier=${app}" --property="ExecReload=/bin/kill -HUP \$MAINPID" --setenv=PYTHONPATH=${PYTHON_PATH} --setenv=RUNTIME_DIRECTORY=${RUNTIME_DIRECTORY} coverage run --rcfile=.coveragerc ${cmd} >/tmp/output.txt 2>&1
sudo systemd-run --unit="${unit}" --working-directory=. --property=Type=dbus --property=BusName="${dbus}" --property="SyslogIdentifier=${app}" --setenv=PYTHONPATH=${PYTHON_PATH} --setenv=RUNTIME_DIRECTORY=${RUNTIME_DIRECTORY} coverage run --rcfile=.coveragerc ${cmd} >/tmp/output.txt 2>&1
log_file_contents $? /tmp/output.txt
printf "\n"
sleep 1
Expand All @@ -112,10 +112,10 @@ sd_restart() {
reload_cfg() {
app="$1"
unit="${app}"-cov.service
log "Reload config ${app}"
sudo systemctl reload "${unit}" && printf "systemctl reload %s\n" "${unit}" >/tmp/output.txt 2>&1
#pid=$( systemctl show --property MainPID --value "${unit}" )
#sudo kill -HUP "${pid}" >/tmp/output.txt 2>&1
pid=$( systemctl show --property MainPID --value "${unit}" )
log "Reload config ${app} - SIGHUP ${pid}"
#sudo systemctl reload "${unit}" && printf "systemctl reload %s\n" "${unit}" >/tmp/output.txt 2>&1
sudo kill -HUP "${pid}" >/tmp/output.txt 2>&1
log_file_contents $? /tmp/output.txt
printf "\n"
sleep 1
Expand Down
4 changes: 2 additions & 2 deletions etc/stas/stacd.conf
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@

# kato: Keep Alive Timeout (KATO): This field specifies the timeout value
# for the Keep Alive feature in seconds. The default value for this
# field is 30 seconds (2 minutes).
# field is 120 seconds (2 minutes).
# Type: Unsigned integer
# Range: 0..N
# Unit: Seconds
#kato=30
#kato=120

# nr-io-queues: Overrides the default number of I/O queues create by the
# driver.
Expand Down
70 changes: 0 additions & 70 deletions staslib/iputil.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,76 +214,6 @@ def get_ipaddress_obj(ipaddr, ipv4_mapped_convert=False):
return ip


# ******************************************************************************
def get_primary_src_addrs(iface: str): # pylint: disable=too-many-locals, too-many-branches
'''@brief Return the two primary IP addresses associated with interface @iface.
@param iface: The interface name to match
@return: tuple(primary-ipv4-addr-or-None, primary-ipv6-addr-or-None)
'''
iface_indx = None
interfaces = {}
with socket.socket(socket.AF_NETLINK, socket.SOCK_RAW) as sock:
sock.sendall(GETADDRCMD)
nlmsg = sock.recv(8192)
nlmsg_idx = 0
while True: # pylint: disable=too-many-nested-blocks
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)

interfaces.setdefault(ifa_index, {})

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_IFNAME:
data = nlmsg[rtattr_indx + RTATTR_SZ : rtattr_indx + rta_len]
ifname = data.rstrip(b'\0').decode()
interfaces[ifa_index]['name'] = ifname
if ifname == iface:
iface_indx = ifa_index
ipv4_lst = interfaces[ifa_index].get(socket.AF_INET, [])
ipv6_lst = interfaces[ifa_index].get(socket.AF_INET6, [])
if len(ipv4_lst) and len(ipv6_lst):
return (ipv4_lst[0], ipv6_lst[0])

elif rta_type == IFLA_ADDRESS:
data = nlmsg[rtattr_indx + RTATTR_SZ : rtattr_indx + rta_len]
ip = get_ipaddress_obj(data)
if ip:
interfaces[ifa_index].setdefault(ifa_family, []).append(ip)
ifname = interfaces[ifa_index].get('name')
if ifname == iface:
ipv4_lst = interfaces[ifa_index].get(socket.AF_INET, [])
ipv6_lst = interfaces[ifa_index].get(socket.AF_INET6, [])
if len(ipv4_lst) and len(ipv6_lst):
return (ipv4_lst[0], ipv6_lst[0])

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

if iface_indx is not None:
ipv4_lst = interfaces[iface_indx].get(socket.AF_INET, [None])
ipv6_lst = interfaces[iface_indx].get(socket.AF_INET6, [None])
return (ipv4_lst[0], ipv6_lst[0])

return None, None


# ******************************************************************************
def net_if_addrs(): # pylint: disable=too-many-locals
'''@brief Return a dictionary listing every IP addresses for each interface.
Expand Down
4 changes: 2 additions & 2 deletions staslib/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -856,7 +856,7 @@ def _nvme_cli_interop(self, udev_obj):
return

# Did we receive a Change of DLP AEN or an NVME Event indicating 'connect' or 'rediscover'?
if not _is_dlp_changed_aen(udev_obj) and not _event_matches(udev_obj, ('connected', 'rediscover')):
if not _is_dlp_changed_aen(udev_obj) and not _event_matches(udev_obj, ('rediscover',)):
return

# We need to invoke "nvme connect-all" using nvme-cli's nvmf-connect@.service
Expand All @@ -873,6 +873,6 @@ def _nvme_cli_interop(self, udev_obj):
options = r'\x09'.join(
[fr'{option}\x3d{value}' for option, value in cnf if value not in (None, 'none', 'None', '')]
)
logging.info('Invoking: systemctl restart nvmf-connect@%s.service', options)
logging.debug('Invoking: systemctl restart nvmf-connect@%s.service', options)
cmd = [defs.SYSTEMCTL, '--quiet', '--no-block', 'restart', fr'nvmf-connect@{options}.service']
subprocess.run(cmd, check=False)
137 changes: 68 additions & 69 deletions staslib/udev.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,32 +154,36 @@ def is_ioc_device(device):
return False

@staticmethod
def _cid_matches_tcp_tid_legacy(cid, tid): # pylint: disable=too-many-return-statements
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).
'''
host_iface = cid['host-iface']
host_traddr = iputil.get_ipaddress_obj(cid['host-traddr'], ipv4_mapped_convert=True)
cid_host_iface = cid['host-iface']
cid_host_traddr = iputil.get_ipaddress_obj(cid['host-traddr'], ipv4_mapped_convert=True)

if not host_iface: # cid.host_iface is undefined
if not host_traddr: # cid.host_traddr is undefined
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.'
'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 defined,
# then it must match the existing cid.host_traddr.
if tid.host_traddr and iputil.get_ipaddress_obj(tid.host_traddr) != host_traddr:
return False
# 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
Expand All @@ -188,54 +192,48 @@ def _cid_matches_tcp_tid_legacy(cid, tid): # pylint: disable=too-many-return-st
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(host_traddr))
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 host_traddr: # cid.host_traddr is undefined
if tid.host_iface and tid.host_iface != host_iface:
if not cid_host_traddr: # cid.host_traddr is undefined
if tid.host_iface and tid.host_iface != cid_host_iface:
return False

if not tid.host_traddr:
return True
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

# 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(host_iface, {4: [], 6: []})
tid_traddr = iputil.get_ipaddress_obj(tid.traddr)
source_addrs = if_addrs[tid_traddr.version]
if len(source_addrs) == 1 and source_addrs[0] == tid.host_traddr:
return True
src_addr0 = iputil.get_ipaddress_obj(source_addrs[0], ipv4_mapped_convert=True)
if src_addr0 != tid_host_traddr:
return False

return False
return True

# cid.host_traddr is defined
if tid.host_iface and tid.host_iface != host_iface:
if tid.host_iface and tid.host_iface != cid_host_iface:
return False

if not tid.host_traddr:
# If candidate's tid.host_traddr is undefined, then
# the primary (default) source address will be used
# for the candidate controller connection. We need to
# make sure that the primary source address matches with
# the existing connection's source address.

# With the interface we can find the primary source
# address. If existing cid.host_traddr is one of the
# two primary addresses (i.e. primary IPv4 and primary
# IPv6), then we have a match.
return host_traddr in iputil.get_primary_src_addrs(host_iface)
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 iputil.get_ipaddress_obj(tid.host_traddr) == host_traddr
return True

@staticmethod
def _cid_matches_tid(cid, tid): # pylint: disable=too-many-return-statements
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.
Expand All @@ -261,46 +259,47 @@ def _cid_matches_tid(cid, tid): # pylint: disable=too-many-return-statements
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.
cid_traddr = iputil.get_ipaddress_obj(cid['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)
else:
cid_traddr = cid['traddr']
tid_traddr = tid.traddr

# 'transport', 'traddr', 'trsvcid', and 'subsysnqn' must exactly match.
if (
cid['transport'] != tid.transport
or cid_traddr != tid_traddr
or cid['trsvcid'] != tid.trsvcid
or cid['subsysnqn'] != tid.subsysnqn
):
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':
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(cid, tid)

# 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 and src_addr != iputil.get_ipaddress_obj(tid.host_traddr):
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
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.
Expand Down Expand Up @@ -329,7 +328,7 @@ def find_nvme_dc_device(self, tid):
continue

cid = self.get_cid(device)
if not self._cid_matches_tid(cid, tid):
if not self._cid_matches_tid(tid, cid):
continue

return device
Expand All @@ -348,7 +347,7 @@ def find_nvme_ioc_device(self, tid):
continue

cid = self.get_cid(device)
if not self._cid_matches_tid(cid, tid):
if not self._cid_matches_tid(tid, cid):
continue

return device
Expand Down
Loading

0 comments on commit a5f9dc2

Please sign in to comment.