# Copyright (c) 2022, Dell Inc. or its subsidiaries. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # See the LICENSE file for details. # # This file is part of NVMe STorage Appliance Services (nvme-stas). # # Authors: Martin Belanger # '''This module defines the Transport Identifier Object, which is used throughout nvme-stas to uniquely identify a Controller''' import hashlib from staslib import conf class TID: # pylint: disable=too-many-instance-attributes '''Transport Identifier''' RDMA_IP_PORT = '4420' DISC_IP_PORT = '8009' def __init__(self, cid: dict): '''@param cid: Controller Identifier. A dictionary with the following contents. { # Transport parameters 'transport': str, # [mandatory] 'traddr': str, # [mandatory] 'subsysnqn': str, # [mandatory] 'trsvcid': str, # [optional] 'host-traddr': str, # [optional] 'host-iface': str, # [optional] 'host-nqn': str, # [optional] # Connection parameters 'dhchap-ctrl-secret': str, # [optional] 'hdr-digest': str, # [optional] 'data-digest': str, # [optional] 'nr-io-queues': str, # [optional] 'nr-write-queues': str, # [optional] 'nr-poll-queues': str, # [optional] 'queue-size': str, # [optional] 'kato': str, # [optional] 'reconnect-delay': str, # [optional] 'ctrl-loss-tmo': str, # [optional] 'disable-sqflow': str, # [optional] } ''' self._cfg = { k: v for k, v in cid.items() if k not in ('transport', 'traddr', 'subsysnqn', 'trsvcid', 'host-traddr', 'host-iface') } self._transport = cid.get('transport', '') self._traddr = cid.get('traddr', '') self._trsvcid = '' if self._transport in ('tcp', 'rdma'): trsvcid = cid.get('trsvcid', None) self._trsvcid = ( trsvcid if trsvcid else (TID.RDMA_IP_PORT if self._transport == 'rdma' else TID.DISC_IP_PORT) ) sysconf = conf.SysConf() self._host_traddr = cid.get('host-traddr', '') self._host_iface = '' if conf.SvcConf().ignore_iface else cid.get('host-iface', '') self._host_nqn = cid.get('host-nqn', sysconf.hostnqn) self._subsysnqn = cid.get('subsysnqn', '') self._key = ( self._transport, self._traddr, self._trsvcid, self._subsysnqn, self._host_traddr, self._host_iface, self._host_nqn, ) self._hash = int.from_bytes( hashlib.md5(''.join(self._key).encode('utf-8')).digest(), 'big' ) # We need a consistent hash between restarts self._id = f'({self._transport}, {self._traddr}, {self._trsvcid}{", " + self._subsysnqn if self._subsysnqn else ""}{", " + self._host_iface if self._host_iface else ""}{", " + self._host_traddr if self._host_traddr else ""})' # pylint: disable=line-too-long host_traddr = property(lambda self: self._host_traddr) host_iface = property(lambda self: self._host_iface) subsysnqn = property(lambda self: self._subsysnqn) transport = property(lambda self: self._transport) host_nqn = property(lambda self: self._host_nqn) trsvcid = property(lambda self: self._trsvcid) traddr = property(lambda self: self._traddr) cfg = property(lambda self: self._cfg) def as_dict(self): '''Return object members as a dictionary''' data = { 'traddr': self.traddr, 'trsvcid': self.trsvcid, 'transport': self.transport, 'subsysnqn': self.subsysnqn, 'host-iface': self.host_iface, 'host-traddr': self.host_traddr, } # When migrating an old last known config, some members may not # exist. Therefore retrieve them with getattr() to avoid a crash. cfg = getattr(self, '_cfg', None) if cfg: data.update(cfg) sysconf = conf.SysConf() data['host-nqn'] = getattr(self, '_host_nqn', sysconf.hostnqn) return data def __str__(self): return self._id def __repr__(self): return self._id def __eq__(self, other): return isinstance(other, self.__class__) and self._key == other._key def __ne__(self, other): return not isinstance(other, self.__class__) or self._key != other._key def __hash__(self): return self._hash