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
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
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
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
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
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
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
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
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