mirror of
				https://github.com/eLvErDe/hwraid.git
				synced 2025-10-31 03:14:26 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			326 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			326 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/python
 | |
| 
 | |
| import os
 | |
| import re
 | |
| import sys
 | |
| from argparse import ArgumentParser
 | |
| 
 | |
| # Argument parser
 | |
| # My own ArgumentParser with single-line stdout output and unknown state Nagios retcode
 | |
| class NagiosArgumentParser(ArgumentParser):
 | |
|     def error(self, message):
 | |
|         sys.stdout.write('UNKNOWN: Bad arguments (see --help): %s\n' % message)
 | |
|         sys.exit(3)
 | |
| 
 | |
| def parse_args():
 | |
|     parser = NagiosArgumentParser(description='Adaptec AACRAID status script')
 | |
|     parser.add_argument('-d', '--disks-only', action="store_true", help='Only disply disk statuses')
 | |
|     parser.add_argument('-n', '--nagios',     action="store_true", help='Use Nagios-like output and return code')
 | |
|     return parser.parse_args()
 | |
| 
 | |
| def which(program):
 | |
|     fpath, fname = os.path.split(program)
 | |
|     if fpath:
 | |
|         if is_exe(program):
 | |
|             return program
 | |
|     else:
 | |
|         # Add some defaults
 | |
|         os.environ["PATH"] += os.pathsep + '/usr/StorMan/arcconf'
 | |
|         os.environ["PATH"] += os.pathsep + os.path.dirname(os.path.realpath(sys.argv[0]))
 | |
|         for path in os.environ["PATH"].split(os.pathsep):
 | |
|             path = path.strip('"')
 | |
|             exe_file = os.path.join(path, program)
 | |
|             if is_exe(exe_file):
 | |
|                 return exe_file
 | |
|     return None
 | |
| 
 | |
| def is_exe(fpath):
 | |
|     return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
 | |
| 
 | |
| # Get command output
 | |
| def getOutput(cmd):
 | |
|     output = os.popen('%s 2>%s' % (cmd, os.devnull))
 | |
|     lines = []
 | |
|     for line in output:
 | |
|         if not re.match(r'^$',line.strip()):
 | |
|             lines.append(line.strip())
 | |
|     return lines
 | |
| 
 | |
| def returnControllerNumber(output):
 | |
|     for line in output:
 | |
|         if re.match(r'^Controllers found: [0-9]+$',line.strip()):
 | |
|             return int(line.split(':')[1].strip().strip('.'))
 | |
| 
 | |
| def returnControllerModel(output):
 | |
|     for line in output:
 | |
|         if re.match(r'^Controller Model.*$',line.strip()):
 | |
|             return line.split(':')[1].strip()
 | |
| 
 | |
| def returnControllerStatus(output):
 | |
|     for line in output:
 | |
|         if re.match(r'^Controller Status.*$',line.strip()):
 | |
|             return line.split(':')[1].strip()
 | |
| 
 | |
| def returnArrayIds(output):
 | |
|     ids = []
 | |
|     for line in output:
 | |
|         if re.match(r'^Logical device number [0-9]+$',line.strip()):
 | |
|             ids.append(line.strip('Logical device number').strip())
 | |
|     return ids
 | |
| 
 | |
| def returnArrayInfo(output):
 | |
|     members = []
 | |
|     for line in output:
 | |
|         # RAID level may be either N or Simple_Volume
 | |
|         # (a disk connected to the card, not hotspare, not part of any array)
 | |
|         if re.match(r'^RAID level\s+: .+$',line.strip()):
 | |
|             type = line.split(':')[1].strip()
 | |
|         if re.match(r'^Status of logical device\s+: .*$',line.strip()):
 | |
|             status = line.split(':')[1].strip()
 | |
|         if re.match(r'^Size\s+: [0-9]+ MB$',line.strip()):
 | |
|             size = str(int(line.strip('MB').split(':')[1].strip()) / 1000)
 | |
|         if re.match(r'^(Group\s[0-9]+,\s)?Segment [0-9]+\s+: .*$',line.strip()):
 | |
|             splitter = re.compile('(\(.*\))')
 | |
|             # The line can be either
 | |
|             # Segment 0 : Present (Controller:1,Enclosure:0,Slot:0) JPW9J0N00RWMUV
 | |
|             # Or
 | |
|             # Segment 0 : Present (Controller:1,Channel:0,Device:0) S13PJ1CQ719255
 | |
|             # Or
 | |
|             # Segment 0 : Present (Controller:1,Connector:1,Device:2) 9QJ7D0MJ
 | |
|             line = re.sub('Controller:','',line)
 | |
|             line = re.sub('(Channel|Enclosure|Connector):','',line)
 | |
|             line = re.sub('(Device|Slot):','',line)
 | |
|             line = line.split(':')[1]
 | |
|             if re.match(r'^ Missing',line):
 | |
|                 members.append('?,?')
 | |
|             else:
 | |
|                 members.append(splitter.split(line)[1].strip('(').strip(')'))
 | |
|         if re.match(r'^Group [0-9], Segment [0-9]+\s+: .*$',line.strip()):
 | |
|             splitter = re.compile('(\(.*\))')
 | |
|             line = line.split(':')[1]
 | |
|             if re.match(r'^ Missing',line):
 | |
|                 members.append('?,?')
 | |
|             else:
 | |
|                 members.append(splitter.split(line)[1].strip('(').strip(')'))
 | |
|     return [type,status,size,members]
 | |
| 
 | |
| def returnControllerTasks(output):
 | |
|     arrayid = False
 | |
|     type = False
 | |
|     state = False
 | |
|     tasks = []
 | |
|     for line in output:
 | |
|         if re.match(r'^Logical device\s+: [0-9]+$',line.strip()):
 | |
|             arrayid = line.split(':')[1].strip()
 | |
|         if re.match(r'^Current operation\s+: .*$',line.strip()):
 | |
|             type = line.split(':')[1].strip()
 | |
|         if re.match(r'^Percentage complete\s+: [0-9]+$',line.strip()):
 | |
|             state = line.split(':')[1].strip()
 | |
|         if arrayid != False and type != False and state != False:
 | |
|             tasks.append([arrayid,type,state])
 | |
|             arrayid = False
 | |
|             type = False
 | |
|             state = False
 | |
|     return tasks
 | |
| 
 | |
| def returnDisksInfo(output,controllerid):
 | |
|     diskid = False
 | |
|     vendor = False
 | |
|     model = False
 | |
|     state = False
 | |
|     serial = False
 | |
|     disks = []
 | |
|     for line in output:
 | |
|         if re.match(r'^Reported Channel,Device(\(T:L\))?\s+: [0-9]+,[0-9]+(\([0-9]+:[0-9]+\))?$',line.strip()):
 | |
|             diskid = re.split('\s:\s',line)[1].strip()
 | |
|             diskid = re.sub('\(.*\)','',diskid)
 | |
|             diskid = str(controllerid)+','+diskid
 | |
|         if re.match(r'^State\s+:.*$',line.strip()):
 | |
|             state = line.split(':')[1].strip()
 | |
|         if re.match(r'^Vendor\s+:.*$',line.strip()):
 | |
|             vendor = line.split(':')[1].strip()
 | |
|         if re.match(r'^Model\s+:.*$',line.strip()):
 | |
|             model = line.split(':')[1].strip()
 | |
|         if re.match(r'^Serial number\s+:.*$',line.strip()):
 | |
|             serial = line.split(':')[1].strip()
 | |
|         if diskid != False and vendor != False and model != False and state != False and serial != False:
 | |
|             disks.append([diskid, state, vendor, model, serial])
 | |
|             diskid = False
 | |
|             vendor = False
 | |
|             model = False
 | |
|             state = False
 | |
|             serial = False
 | |
|     return disks
 | |
| 
 | |
| config = parse_args()
 | |
| if config.disks_only:
 | |
|     printarray = False
 | |
|     printcontroller = False
 | |
| else:
 | |
|     printarray = True
 | |
|     printcontroller = True
 | |
| 
 | |
| nagiosoutput=''
 | |
| nagiosgoodctrl = 0
 | |
| nagiosbadctrl = 0
 | |
| nagiosctrlbadarray = 0
 | |
| nagiosgoodarray = 0
 | |
| nagiosbadarray = 0
 | |
| nagiosgooddisk = 0
 | |
| nagiosbaddisk = 0
 | |
| 
 | |
| bad = False
 | |
| 
 | |
| # Find arcconf
 | |
| for arcconfbin in "arcconf","arcconf.exe":
 | |
|     arcconfpath = which(arcconfbin)
 | |
|     if (arcconfpath != None):
 | |
|         break
 | |
| 
 | |
| # Check binary exists (and +x), if not print an error message
 | |
| if (arcconfpath != None):
 | |
|     if is_exe(arcconfpath):
 | |
|         pass
 | |
|     else:
 | |
|         if config.nagios:
 | |
|             print 'UNKNOWN - Cannot find '+arcconfpath
 | |
|         else:
 | |
|             print 'Cannot find ' + arcconfpath + 'in your PATH. Please install it.'
 | |
|         sys.exit(3)
 | |
| else:
 | |
|     print 'Cannot find "arcconf, "arcconf.exe" in your PATH. Please install it.'
 | |
|     sys.exit(3)
 | |
| 
 | |
| 
 | |
| cmd = '"%s" GETVERSION' % arcconfpath
 | |
| output = getOutput(cmd)
 | |
| controllernumber = returnControllerNumber(output)
 | |
| 
 | |
| # List controllers
 | |
| if printcontroller:
 | |
|     if not config.nagios:
 | |
|         print '-- Controller informations --'
 | |
|         print '-- ID | Model | Status'
 | |
|     controllerid = 1
 | |
|     while controllerid <= controllernumber:
 | |
|         cmd = '"%s" GETCONFIG %d' % (arcconfpath, controllerid)
 | |
|         output = getOutput(cmd)
 | |
|         controllermodel = returnControllerModel(output)
 | |
|         controllerstatus = returnControllerStatus(output)
 | |
|         if controllerstatus != 'Optimal':
 | |
|             bad = True
 | |
|             nagiosbadctrl += 1
 | |
|         else:
 | |
|             nagiosgoodctrl += 1
 | |
|         if not config.nagios:
 | |
|             print 'c'+str(controllerid-1)+' | '+controllermodel+' | '+controllerstatus
 | |
|         controllerid += 1
 | |
|         if not config.nagios:
 | |
|             print ''
 | |
| 
 | |
| # List arrays
 | |
| if printarray:
 | |
|     controllerid = 1
 | |
|     if not config.nagios:
 | |
|         print '-- Arrays informations --'
 | |
|         print '-- ID | Type | Size | Status | Task | Progress'
 | |
|     while controllerid <= controllernumber:
 | |
|         arrayid = 0
 | |
|         cmd = '"%s" GETCONFIG %s' % (arcconfpath, controllerid)
 | |
|         output = getOutput(cmd)
 | |
|         arrayids = returnArrayIds(output)
 | |
|         for arrayid in arrayids:
 | |
|             cmd = '"%s" GETCONFIG %s LD %s' % (arcconfpath, controllerid, arrayid)
 | |
|             output = getOutput(cmd)
 | |
|             arrayinfo = returnArrayInfo(output)
 | |
|             if arrayinfo[1] != 'Optimal':
 | |
|                 nagiosbadarray += 1
 | |
|                 bad = True
 | |
|             else:
 | |
|                 nagiosgoodarray += 1
 | |
|             cmd = '"%s" GETSTATUS %s', (arcconfpath, controllerid)
 | |
|             output = getOutput(cmd)
 | |
|             tasksinfo = returnControllerTasks(output)
 | |
|             done = False
 | |
|             # Usually it should return either [0-9] or Simple_Volume but...
 | |
|             # It can also return "6 Reed-Solomon" so we need to handle this too...
 | |
|             # So let's match [0-9] followed by a space or EOL.
 | |
|             if re.match('^[0-9]+(\s|$)',arrayinfo[0]):
 | |
|                 raidtype = re.sub('^','RAID',arrayinfo[0])
 | |
|             else:
 | |
|                 raidtype = arrayinfo[0]
 | |
|             for tasks in tasksinfo:
 | |
|                 if int(tasks[0]) == int(arrayid):
 | |
|                     if not config.nagios:
 | |
|                         print 'c'+str(controllerid-1)+'u'+str(arrayid)+' | '+raidtype+' | '+arrayinfo[2]+'G | '+arrayinfo[1]+' | '+tasks[1]+' | '+tasks[2]+'%'
 | |
|                     done = True
 | |
|                     break
 | |
|             if done == False:
 | |
|                 if not config.nagios:
 | |
|                     print 'c'+str(controllerid-1)+'u'+str(arrayid)+' | '+raidtype+' | '+arrayinfo[2]+'G | '+arrayinfo[1]
 | |
|         controllerid += 1
 | |
|     if not config.nagios:
 | |
|         print ''
 | |
| 
 | |
| # List disks
 | |
| controllerid = 1
 | |
| if not config.nagios:
 | |
|     print '-- Disks informations'
 | |
|     print '-- ID | Model | Status'
 | |
| while controllerid <= controllernumber:
 | |
|     arrayid = 0
 | |
|     cmd = '"%s" GETCONFIG %s' % (arcconfpath, controllerid)
 | |
|     output = getOutput(cmd)
 | |
|     arrayids = returnArrayIds(output)
 | |
| 
 | |
| 
 | |
|     cmd = '"%s" GETCONFIG %d PD' % (arcconfpath, controllerid)
 | |
|     output = getOutput(cmd)
 | |
|     diskinfo = returnDisksInfo(output,controllerid)
 | |
|     no_array_disk_id = 0
 | |
|     for disk in diskinfo:
 | |
| 
 | |
|         # Generic verification no matter is the disk is member of an array or not
 | |
|         if disk[1] != 'Online' and disk[1] != 'Hot Spare' and disk[1] != 'Ready' and disk[1] != 'Global Hot-Spare':
 | |
|             bad = True
 | |
|             nagiosbaddisk += 1
 | |
|         else:
 | |
|             nagiosgooddisk += 1
 | |
| 
 | |
|         array_member = False
 | |
|         for arrayid in arrayids:
 | |
|             cmd = '"%s" GETCONFIG %s LD %s' % (arcconfpath, controllerid, arrayid)
 | |
|             output = getOutput(cmd)
 | |
|             arrayinfo = returnArrayInfo(output)
 | |
| 
 | |
|             # Try to attach current disk to array (loop)
 | |
|             memberid = 0
 | |
|             for member in arrayinfo[3]:
 | |
| 
 | |
|                 # Matched in members of this array
 | |
|                 if disk[0] == member:
 | |
|                     if not config.nagios:
 | |
|                         print 'c'+str(controllerid-1)+'u'+str(arrayid)+'d'+str(memberid)+' | '+disk[2]+' '+disk[3]+' '+disk[4]+' | '+disk[1]
 | |
|                     array_member = True
 | |
|                     memberid += 1
 | |
| 
 | |
|         # Some disks may not be attached to any array (ie: global hot spare)
 | |
|         if not array_member:
 | |
|             if not config.nagios:
 | |
|                 print 'c'+str(controllerid-1)+'uX'+'d'+str(no_array_disk_id)+' | '+disk[2]+' '+disk[3]+' '+disk[4]+' | '+disk[1]
 | |
|             no_array_disk_id += 1
 | |
| 
 | |
|     controllerid += 1
 | |
| 
 | |
| if config.nagios:
 | |
|     if bad:
 | |
|         print('RAID ERROR - Controllers OK:%d Bad:%d - Arrays OK:%d Bad:%d - Disks OK:%d Bad:%d' % (nagiosgoodctrl, nagiosbadctrl, nagiosgoodarray, nagiosbadarray, nagiosgooddisk, nagiosbaddisk))
 | |
|         sys.exit(2)
 | |
|     else:
 | |
|         print('RAID OK - Controllers OK:%d Bad:%d - Arrays OK:%d Bad:%d - Disks OK:%d Bad:%d' % (nagiosgoodctrl, nagiosbadctrl, nagiosgoodarray, nagiosbadarray, nagiosgooddisk, nagiosbaddisk))
 | |
| else:
 | |
|     if bad:
 | |
|         print '\nThere is at least one disk/array in a NOT OPTIMAL state.'
 | |
|         print '\nUse "arcconf GETCONFIG [1-9]" to get details.'
 | |
|         sys.exit(1)
 |