Exemple #1
0
    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)), {}
Exemple #2
0
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))
Exemple #3
0
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)
Exemple #4
0
    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
Exemple #5
0
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)
Exemple #6
0
    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)