def get_scsi_device_info(self, scsi_device, want_queue_attrs=False): verb = self.disp.get('verbose') dev_info_fmt = [] if verb == 1: dev_info_fmt.append('{vendor}') elif verb > 1: dev_info_fmt.append('vendor: {vendor}') if verb > 1: dev_info_fmt += ['model: {model}', 'rev: {rev}'] if self.disp.get('addr'): dev_info_fmt.append('addr: {sas_address}') if self.disp.get('counters'): dev_info_fmt.append('ioerr_cnt: {ioerr_cnt}') dev_info_fmt.append('iodone_cnt: {iodone_cnt}') ikeys = ( 'vendor', 'model', 'rev', 'sas_address', 'ioerr_cnt', 'iodone_cnt') iargs = dict((k, scsi_device.attrs.get(k, 'N/A')) for k in ikeys) dev_info = ' '.join(dev_info_fmt).format(**iargs) dev_sg = '' if self.disp.get('devices'): sg = scsi_device.scsi_generic if sg: dev_sg = '(%s)' % sg.name scsi_type = scsi_device.attrs.type unknown_type = 'unknown[%s]' % scsi_type try: dev_type = MAP_TYPES.get(int(scsi_type), unknown_type) except ValueError: dev_type = 'unknown scsi type' if scsi_device.block: block = scsi_device.block size = block.sizebytes() if size >= 1e12: blk_info = "size: %.1fTB" % (size / 1e12) else: blk_info = "size: %.1fGB" % (size / 1e9) queue_attr_info = {} if want_queue_attrs: # -vvv: print all queue attributes queue_attr_info = dict(scsi_device.block.queue.attrs) return ' '.join( (dev_type, dev_info, blk_info, dev_sg)), queue_attr_info elif dev_type == 'enclosure': if verb > 0: sg = scsi_device.scsi_generic snic = ses_get_snic_nickname(sg.name) if snic: dev_type += ' %s' % snic return ' '.join((dev_type, dev_info, dev_sg)), {}
def ses_report(): """ses_report command-line""" pargs = _init_argparser() if pargs.debug: # debugging on the same stream is recommended (stdout) logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) pfx = pargs.prefix.strip('.') if pfx: pfx += '.' json_encl_dict = {} # Iterate over sysfs SCSI enclosures for node in sysfs.node('class').node('enclosure'): # Get enclosure device enclosure = EnclosureDevice(node.node('device')) # Get enclosure SG device sg_dev = enclosure.scsi_generic # Resolve SES enclosure nickname snic = ses_get_snic_nickname(sg_dev.name) if snic: snic = snic.replace(' ', '_') else: # Use Vendor + SAS address if SES encl. nickname not defined snic = enclosure.attrs.vendor.replace(' ', '-') snic += '_' + enclosure.attrs.sas_address if pargs.carbon: if pargs.json: encl_json_list = [] for edinfo in ses_get_ed_metrics(sg_dev.name): encl_json_list.append(edinfo) json_encl_dict[snic] = encl_json_list else: time_now = time.time() for edinfo in ses_get_ed_metrics(sg_dev.name): # Print output using Carbon format fmt = '{element_type}.{descriptor}.{key}_{unit} {value}' path = fmt.format(**edinfo) print('%s%s.%s %d' % (pfx, snic, path, time_now)) else: if pargs.json: encl_json_list = [] for edstatus in ses_get_ed_status(sg_dev.name): encl_json_list.append(edstatus) json_encl_dict[snic] = encl_json_list else: for edstatus in ses_get_ed_status(sg_dev.name): fmt = '{element_type}.{descriptor} {status}' output = fmt.format(**edstatus) print('%s%s.%s' % (pfx, snic, output)) if pargs.json: print(json.dumps(json_encl_dict, sort_keys=True, indent=4))
def sas_sd_snic_alias(blkdev): """Use sasutils library to get the alias name from the block device.""" # NOTE: Unfortunately, we cannot always rely on sysfs block device # 'enclosure_device' symlink to the array device (at least not on # 3.10.0-327.36.3.el7). We have to do the enclosure lookup ourselves # as a workaround. # Preload enclosure dict (sas_address -> EnclosureDevice) enclosures = {} for encl in sysfs.node('class').node('enclosure'): encldev = EnclosureDevice(encl.node('device')) enclosures[encldev.attrs.sas_address] = encldev # Instantiate SASBlockDevice object from block device sysfs node # eg. /sys/block/sdx/device blkdev = SASBlockDevice(sysfs.node('block').node(blkdev).node('device')) sasdev = blkdev.end_device.sas_device if blkdev.array_device: # 'enclosure_device' symlink is present (preferred method) # Use array_device and enclosure to retrieve the ses sg name ses_sg = blkdev.array_device.enclosure.scsi_generic.sg_name else: # 'enclosure_device' symlink is absent: use workaround (see NOTE) try: encl = enclosures[sasdev.attrs.enclosure_identifier] ses_sg = encl.scsi_generic.sg_name except KeyError: # not an array device logging.warning('%s not an array device (%s)', blkdev.name, blkdev.sysfsnode.path) raise # Retrieve bay_identifier from matching sas_device bay = int(sasdev.attrs.bay_identifier) # Get subenclosure nickname snic = ses_get_snic_nickname(ses_sg) or '%s_no_snic' % blkdev.name return ALIAS_FORMAT.format(nickname=snic, bay_identifier=bay)
def get_scsi_device_info(self, scsi_device): # print(scsi_device.sysfsnode.path) dev_info = '.'.join((scsi_device.attrs.get('model', 'MODEL_UNKNOWN'), scsi_device.attrs['sas_address'])) scsi_type = scsi_device.attrs.type unknown_type = 'unknown[%s]' % scsi_type try: dev_type = MAP_TYPES.get(int(scsi_type), unknown_type) except ValueError: dev_type = 'unknown scsi type' if dev_type == 'enclosure': sg = scsi_device.scsi_generic snic = ses_get_snic_nickname(sg.name) # automatically resolve parent expander nickname self.parent.parent.nickname = snic if snic: return '.'.join((scsi_device.attrs.get('model', 'MODEL_UNKNOWN'), snic)) return dev_info
def sas_mpath_snic_alias(dmdev): """Use sasutils to get the alias name from the dm device.""" # Principle: # scan slaves block devices, for each: # get bay identifier # get enclosure device and its sg name # get snic (subenclosure nickname) # sanity: check if the bay identifers are the same # find common snic part and return result # NOTE: Unfortunately, we cannot always rely on sysfs block device # 'enclosure_device' symlink to the array device (at least not on # 3.10.0-327.36.3.el7). We have to do the enclosure lookup ourselves # as a workaround. # Preload enclosure dict (sas_address -> EnclosureDevice) enclosures = {} for encl in sysfs.node('class').node('enclosure'): encldev = EnclosureDevice(encl.node('device')) enclosures[encldev.attrs.sas_address] = encldev snics = [] bayids = [] # dm's underlying sd* devices can easily be found in 'slaves' for node in sysfs.node('block').node(dmdev).node('slaves'): # Instantiate a block device object with SAS attributes blkdev = SASBlockDevice(node.node('device')) sasdev = blkdev.end_device.sas_device if blkdev.array_device: # 'enclosure_device' symlink is present (preferred method) # Use array_device and enclosure to retrieve the ses sg name ses_sg = blkdev.array_device.enclosure.scsi_generic.sg_name else: # 'enclosure_device' symlink is absent: use workaround (see NOTE) try: encl = enclosures[sasdev.attrs.enclosure_identifier] ses_sg = encl.scsi_generic.sg_name except KeyError: # definitively not an array device logging.warning('%s not an array device (%s)', blkdev.name, blkdev.sysfsnode.path) continue # Retrieve bay_identifier from matching sas_device bayids.append(int(sasdev.attrs.bay_identifier)) # Get subenclosure nickname snic = ses_get_snic_nickname(ses_sg) or '%s_no_snic' % dmdev snics.append(snic) if not bayids or not snics: return # assert that bay ids are the same... bay = bayids[0] assert bayids.count(bay) == len(bayids) snic = os.path.commonprefix(snics) snic = snic.rstrip('-_ ').replace(' ', '_') return ALIAS_FORMAT.format(nickname=snic, bay_identifier=bay)
def print_end_devices(self, sysfsnode): # NOTE: Unfortunately, we cannot always rely on sysfs block device # 'enclosure_device' symlink to the array device (at least not on # 3.10.0-327.36.3.el7). We have to do the enclosure lookup ourselves # as a workaround. # Preload enclosure dict (sas_address -> EnclosureDevice) enclosures = {} for encl in sysfs.node('class').node('enclosure'): encldev = EnclosureDevice(encl.node('device')) enclosures[encldev.attrs.sas_address] = encldev # This code is ugly and should be rewritten... devmap = {} # LU -> list of (SASEndDevice, SCSIDevice) for node in sysfsnode: sas_end_device = SASEndDevice(node.node('device')) for scsi_device in sas_end_device.targets: if scsi_device.block: try: pg83 = bytes(scsi_device.attrs.vpd_pg83) lu = vpd_decode_pg83_lu(pg83) except AttributeError: lu = vpd_get_page83_lu(scsi_device.block.name) devmap.setdefault(lu, []).append( (sas_end_device, scsi_device)) # list of set of enclosure encgroups = [] orphans = [] for lu, dev_list in devmap.items(): encs = set() for sas_ed, scsi_device in dev_list: blk = scsi_device.block assert blk if blk.array_device: # 'enclosure_device' symlink is present (preferred method) encs.add(blk.array_device.enclosure) else: print("Warning: no enclosure symlink set for %s in %s" % (blk.name, blk.scsi_device.sysfsnode.path)) sasdev = sas_ed.sas_device try: encs.add(enclosures[sasdev.attrs.enclosure_identifier]) except (AttributeError, KeyError): # not an array device? print("Warning: %s not an array device (%s)" % (blk.name, sasdev.sysfsnode.path)) if not encs: orphans.append((lu, dev_list)) continue done = False for encset in encgroups: if not encset.isdisjoint(encs): encset.update(encs) done = True break if not done: encgroups.append(encs) print("Found %d enclosure groups" % len(encgroups)) if orphans: print("Found %d orphan devices" % len(orphans)) for encset in encgroups: encinfolist = [] def kfun(o): return int(re.sub("\D", "", o.scsi_generic.name)) for enc in sorted(encset, key=kfun): snic = ses_get_snic_nickname(enc.scsi_generic.name) if snic: if self.args.verbose: encinfolist.append('[%s:%s]' % (enc.scsi_generic.name, snic)) else: encinfolist.append('[%s]' % snic) else: if self.args.verbose: vals = (enc.scsi_generic.name, enc.attrs.vendor, enc.attrs.model, enc.attrs.sas_address) encinfolist.append('[%s:%s %s, addr: %s]' % vals) else: vals = (enc.attrs.vendor, enc.attrs.model, enc.attrs.sas_address) encinfolist.append('[%s %s, addr: %s]' % vals) print("Enclosure group: %s" % ''.join(encinfolist)) cnt = 0 def enclosure_finder(arg): _lu, _dev_list = arg for _sas_ed, _scsi_device in _dev_list: _blk = _scsi_device.block assert _blk if _blk.array_device: # 'enclosure_device' symlink is present # (preferred method) _encl = _blk.array_device.enclosure else: # 'enclosure_device' symlink is absent: use workaround try: _sasdev = _sas_ed.sas_device _encl = enclosures[ _sasdev.attrs.enclosure_identifier] except (AttributeError, KeyError): # not an array device continue if _encl in encset: return True return False encdevs = list(filter(enclosure_finder, devmap.items())) maxpaths = max(len(devs) for lu, devs in encdevs) if self.args.verbose: print(self.FMT_DEVLIST_VERB.format(**self.HDR_DEVLIST_VERB)) def kfun(o): return int(o[1][0][0].sas_device.attrs.bay_identifier) for lu, devlist in sorted(encdevs, key=kfun): self._print_lu_devlist(lu, devlist, maxpaths) cnt += 1 else: folded = {} for lu, devlist in encdevs: # try to regroup disks by getting common attributes devinfo = self._get_dev_attrs(*devlist[0], with_sn=False) devinfo['paths'] = len(devlist) folded_key = namedtuple('FoldedDict', devinfo.keys())(**devinfo) folded.setdefault(folded_key, []).append(devlist) cnt += 1 print("NUM %12s %12s %6s %6s" % ('VENDOR', 'MODEL', 'REV', 'PATHS')) for t, v in folded.items(): if maxpaths and t.paths < maxpaths: pathstr = '%s*' % t.paths else: pathstr = '%s ' % t.paths infofmt = '{vendor:>12} {model:>12} {rev:>6} {paths:>6}' infostr = infofmt.format(**t._asdict()) print('%3d x %s' % (len(v), infostr)) print("Total: %d block devices in enclosure group" % cnt) if orphans: print("Orphan devices:") for lu, blklist in orphans: self._print_lu_devlist(lu, blklist)