Esempio n. 1
0
    def _check_array_list(self):
        p = subprocess.Popen([self.executable, '--detail', '--scan'],
                             stdout=subprocess.PIPE)
        out, _ = p.communicate()

        if p.returncode != 0:
            raise RaidReportException("mdadm could not get list of arrays")

        if not out:
            raise RaidReportException("mdadm is not managing any arrays")

        for line in out.decode().splitlines():
            self.arrays.append(line.split()[1])  # second item is device
Esempio n. 2
0
    def parse_physical_drives(self):
        if not self.adapters:
            raise RaidReportException(
                "omreport can't get physical drive info w/o controller info")

        drives = []

        for adapter in self.adapters:
            drives.extend(self.__parse_drives('pdisk', adapter.data['ID']))

        for drive in drives:
            drive['logical_drive_id'] = drive['ID'].split(':')[1]

            status = PhysicalDrive.STATUS_GOOD
            if drive['Status'] == 'Critical':
                status = PhysicalDrive.STATUS_FAILED

            pdrive = PhysicalDrive(
                drive['ID'],
                drive['Status'],
                drive['Capacity'].split('(')[0].strip(),
                drive['Bus Protocol'],
                drive['Product ID'],
                '',  # fru
                '',  # temperature
                status,
                drive['adapter_id'],
                drive['ID'],
                drive['Hot Spare'] != 'No',
                drive)

            self.phy_drives[drive['adapter_id'] + pdrive.drive_id] = pdrive

        return self.phy_drives
Esempio n. 3
0
    def parse_adapters(self):
        p = subprocess.Popen([self.executable, 'storage', 'controller'],
                             stdout=subprocess.PIPE)
        out, _ = p.communicate()

        if p.returncode != 0:
            raise RaidReportException("omreport could not get adapter info")

        adapters = []
        adapter = {}

        for line in out.decode().splitlines():
            line = line.rstrip()

            if line.startswith('Controller'):  # Begin parsing new adapter
                adapter = {}
                adapters.append(adapter)
            else:  # We are in adapter block, parse the line
                m = PROP_RE.match(line)
                if m:
                    # noinspection PyTypeChecker
                    adapter.update([
                        m.groups()
                    ])  # our regex has exactly 2 groups, ignore warning

        # Convert parsed data to a standard model
        for adapter in sorted(adapters, key=lambda a: a['ID']):
            self.adapters.append(
                Adapter(adapter['ID'], adapter['Name'], '', '', adapter))

        return self.adapters
Esempio n. 4
0
    def parse_adapters(self):
        p = subprocess.Popen([self.executable, 'adpallinfo', 'aall', 'nolog'],
                             stdout=subprocess.PIPE)
        out, _ = p.communicate()

        if p.returncode != 0:
            raise RaidReportException("MegaCli could not get adapter info")

        adapters = []
        adapter = {}

        for line in out.decode().splitlines():
            line = line.rstrip()

            if line.startswith('Adapter #'):  # Begin parsing new adapter
                adapter_id = line.replace('Adapter #', '')
                adapter = {'id': adapter_id}
                adapters.append(adapter)
            elif adapter:  # We are in adapter block, parse the line
                m = PROP_RE.match(line)
                if m:
                    # noinspection PyTypeChecker
                    adapter.update([
                        m.groups()
                    ])  # our regex has exactly 2 groups, ignore warning

        # Convert parsed data to a standard model
        for adapter in sorted(adapters, key=lambda a: a['id']):
            self.adapters.append(
                Adapter(
                    adapter['id'], adapter['Product Name'],
                    adapter['Serial No'], 'ROC temperature' in adapter
                    and adapter['ROC temperature'].split()[0]))

        return self.adapters
Esempio n. 5
0
    def parse_logical_drives(self):
        p = subprocess.Popen([self.executable, 'ldpdinfo', 'aall', 'nolog'],
                             stdout=subprocess.PIPE)
        out, _ = p.communicate()

        if p.returncode != 0:
            raise RaidReportException(
                "MegaCli could not get logical drives info")

        re_adp = re.compile(r'Adapter #(\d+)')
        re_vdr = re.compile(r'Virtual Drive: (\d+)')

        adapter_id = None
        drive_id = None
        parse_opts = False

        drives = []
        drive = {}

        for line in out.decode().splitlines():
            line = line.rstrip()

            if line.startswith('Adapter #'):  # Begin parsing new adapter
                adapter_id = re_adp.match(line).group(1)
            elif line.startswith('Virtual Drive: '):
                drive_id = re_vdr.match(line).group(1)
                drive = {
                    'id': drive_id,
                    'adapter_id': adapter_id,
                    'physical_drives': []
                }
                drives.append(drive)
                parse_opts = True
            elif line.startswith('PD:'):
                parse_opts = False

            if adapter_id and drive_id:
                if parse_opts:  # get logical drive options
                    m = PROP_RE.match(line)
                    if m:
                        # noinspection PyTypeChecker
                        drive.update([
                            m.groups()
                        ])  # our regex has exactly 2 groups, ignore warning
                elif line.startswith('Device Id:'):  # get physical drive id
                    drive['physical_drives'].append(
                        '%s:%s' % (adapter_id, PROP_RE.match(line).group(2)))

        for drive in drives:
            self.log_drives.append(
                LogicalDrive(drive['id'],
                             RAID_LEVEL_MAP.get(drive['RAID Level'], '?'),
                             drive['Size'], drive['State'],
                             drive['adapter_id'], drive['physical_drives'],
                             drive['State'] != 'Optimal'))

        return self.log_drives
Esempio n. 6
0
    def parse_logical_drives(self):
        if not self.adapters:
            raise RaidReportException(
                "omreport can't get logical drive info w/o controller info")

        drives = []

        for adapter in self.adapters:
            drives.extend(self.__parse_drives('vdisk', adapter.data['ID']))

        for drive in drives:
            p = subprocess.Popen([
                self.executable, 'storage', 'pdisk',
                'controller=%s' % drive['adapter_id'],
                'vdisk=%s' % drive['ID']
            ],
                                 stdout=subprocess.PIPE)
            out, _ = p.communicate()

            if p.returncode != 0:
                raise RaidReportException(
                    "omreport could not get logical drive info")

            pdrives = []

            for line in out.decode().splitlines():
                line = line.rstrip()

                if line.startswith('ID'):
                    m = PROP_RE.match(line)
                    physical_drive_id = m.group(2)
                    pdrives.append(drive['adapter_id'] + physical_drive_id)

            self.log_drives.append(
                LogicalDrive(drive['ID'], drive['Layout'],
                             drive['Size'].split('(')[0].strip(),
                             '%s (%s)' % (drive['Status'], drive['State']),
                             drive['adapter_id'], pdrives,
                             drive['Status'] != 'Ok'))

        return self.log_drives
Esempio n. 7
0
    def parse_logical_drives(self):
        pdrives_by_array_uuid = defaultdict(list)
        for drive_id, drive in self.phy_drives.items():
            if not drive.hotspare and 'Array UUID' in drive.data:
                pdrives_by_array_uuid[drive.data['Array UUID']].append(
                    drive_id)

        for arr in self.arrays:
            cmd = [self.executable, '--detail', arr]
            log.debug("Logical drive %s details: '%s'" % (
                arr,
                ' '.join(cmd),
            ))

            p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
            out, _ = p.communicate()

            if p.returncode != 0:
                raise RaidReportException("mdadm could not get array details")

            drive = {}
            for line in out.decode().splitlines():
                m = PROP_RE.match(line.strip())
                if m:
                    # noinspection PyTypeChecker
                    drive.update([
                        m.groups()
                    ])  # our regex has exactly 2 groups, ignore warning

            size = 0
            if 'Array Size' in drive:  # Failed arrays don't report size
                size = int(drive['Array Size'].split('(')[0].strip())

            self.log_drives.append(
                LogicalDrive(arr, drive['Raid Level'].upper(),
                             '%.1fTB' % (size / 1024**3, ), drive['State'],
                             'Linux RAID',
                             pdrives_by_array_uuid.get(drive['UUID'], []),
                             'FAILED' in drive['State']))

        return self.log_drives
Esempio n. 8
0
    def parse_physical_drives(self):
        p = subprocess.Popen([self.executable, 'pdlist', 'aall', 'nolog'],
                             stdout=subprocess.PIPE)
        out, _ = p.communicate()

        if p.returncode != 0:
            raise RaidReportException(
                "MegaCli could not get physical drives info")

        adapter = None
        drives = []
        drive = {}
        blank_count = 0

        for line in out.decode().splitlines():
            line = line.rstrip()

            if not line:
                blank_count += 1
            else:
                blank_count = 0

            if blank_count == 3 and drive:
                drives.append(drive)
                # self.phy_drives[drive['Device Id']] = drive
                drive = {}

            if line.startswith('Adapter #'):  # Begin parsing new adapter
                adapter = line.replace('Adapter #', '')

            if adapter is not None:  # Ignore anything before adapter is defined
                drive[
                    'adapter_id'] = adapter  # can't be bothered to have an if or this line duplicated somewhere

                m = PROP_RE.match(line)
                if m:
                    # noinspection PyTypeChecker
                    drive.update([
                        m.groups()
                    ])  # our regex has exactly 2 groups, ignore warning
                elif line.startswith('Hotspare Information:'):
                    drive['hotspare'] = True

        for drive in drives:
            status = PhysicalDrive.STATUS_GOOD
            if drive['Predictive Failure Count'] != '0':
                status = PhysicalDrive.STATUS_FAILING
            if 'bad' in drive['Firmware state']:
                status = PhysicalDrive.STATUS_FAILED

            pdrive = PhysicalDrive(
                drive['Device Id'], drive['Firmware state'],
                drive['Raw Size'].split('[')[0].strip(), drive['PD Type'],
                ' '.join(drive['Inquiry Data'].split()),
                drive.get('IBM FRU/CRU',
                          ''), drive['Drive Temperature'].split()[0], status,
                drive['adapter_id'], drive['Slot Number'],
                drive.get('hotspare', False))
            self.phy_drives['%s:%s' %
                            (pdrive.adapter_id, pdrive.drive_id)] = pdrive

        return self.phy_drives
Esempio n. 9
0
    def parse_physical_drives(self):
        # Find OS drive first, so we can exclude it from the list
        os_drives = []
        with open('/etc/mtab') as mtab:
            for line in mtab:
                if line.startswith('/dev/sd'):
                    partition = line.split()[0]
                    os_drives.append(re.sub(r'[0-9]', '', partition))

        log.info("Ignoring drives with OS partitions on them: %s" %
                 (', '.join(os_drives), ))

        cmd = [
            'lsblk', '--ascii', '--nodeps', '--noheadings', '--raw',
            '--output', 'NAME,MAJ:MIN,MODEL,SIZE,STATE'
        ]
        log.debug("Listing physical drives: '%s'" % (' '.join(cmd), ))

        p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
        out, _ = p.communicate()

        if p.returncode != 0:
            raise RaidReportException("Error running lsblk")

        devices = {}

        for line in out.decode('ascii').splitlines():
            # noinspection PyTypeChecker
            line = re.sub(r'\\x[0-9]{2}', '_',
                          line)  # Replace all \\x## with underscore
            drive_id, device_number, model, size, state = line.split()

            device_path = '/dev/' + drive_id

            if device_path in os_drives:
                continue

            status = PhysicalDrive.STATUS_GOOD
            if state != 'running':
                status = PhysicalDrive.STATUS_FAILED

            if os.path.exists(device_path + '1'):
                # We're using partitions in mdadm
                device_path += '1'

            devices[device_path] = drive_id

            pdrive = PhysicalDrive(
                drive_id,
                state,
                size,
                device_number,
                model,
                '',  # fru
                '',  # temperature
                status,
                'Linux RAID',
                device_number,
                False  # hotspare
            )

            self.phy_drives[drive_id] = pdrive

        log.debug("Creating mdadm check thread pool of size %s",
                  self.concurrency)
        pool = get_pool()(processes=self.concurrency)
        res = pool.map(
            partial(examine_physical_drive,
                    mdadm=self.executable,
                    timeout=self.timeout), devices.keys())
        for device_path, device_out in res:
            pdrive = self.phy_drives[
                devices[device_path]]  # Map path back to PhysicalDrive

            if device_out:
                pdrive.hotspare = device_out.get('Device Role') == 'spare'
                pdrive.data = device_out
            else:  # mdadm timeout occurred
                pdrive.status = PhysicalDrive.STATUS_FAILING

        pool.close()

        return self.phy_drives