PK œqhYî¶J‚ßFßF)nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/ $#$#$#

Dir : /opt/sharedrads/
Server: Linux ngx353.inmotionhosting.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64
IP: 209.182.202.254
Choose File :

Url:
Dir : //opt/sharedrads/megaclisas-status

#!/opt/imh-python/bin/python3
# $Id: megaclisas-status,v 1.68 2016/10/21 14:38:56 root Exp root $
#
# Written by Adam Cecile <gandalf@NOSPAM.le-vert.net>
# Modified by Vincent S. Cojot <vincent@NOSPAM.cojot.name>
#

import os
import re
import sys

if sys.platform == 'win32':
    import ctypes

def_megaclipath = "/opt/MegaRAID/MegaCli/MegaCli64"

# Non-Nagios Mode defaults
nagiosmode = False
nagiosoutput = ''
nagiosgoodarray = 0
nagiosbadarray = 0
nagiosgooddisk = 0
nagiosbaddisk = 0

# Sane defaults
printarray = True
printcontroller = True
debugmode = False
notempmode = False
totaldrivenumber = 0

# Hardcode a max of 16 HBA and 128 LDs for now. LDTable must be initialized to accept populating list of LD's into each ctlr's list.
MaxNumHBA = 64
MaxNumLD = 128
LDTable = [[] * MaxNumHBA for i in range(MaxNumLD)]
NestedLDTable = [[False for i in range(MaxNumHBA)] for j in range(MaxNumLD)]

# Outputs is a 'dict' of all MegaCLI outputs so we can re-use them during loops..
Outputs = {}

# Startup
def print_usage():
    print('Usage: megaraid-status [--nagios|--debug|--notemp]')


# We need root access to query
if __name__ == '__main__':
    try:
        root_or_admin = os.geteuid() == 0
    except AttributeError:
        root_or_admin = ctypes.windll.shell32.IsUserAnAdmin() != 0
    if not root_or_admin:
        print('# This script requires Administrator privileges')
        sys.exit(5)

# Check command line arguments to enable nagios or not
if len(sys.argv) > 2:
    print_usage()
    sys.exit(1)

if len(sys.argv) > 1:
    if sys.argv[1] == '--nagios':
        nagiosmode = True
    elif sys.argv[1] == '--debug':
        debugmode = True
    elif sys.argv[1] == '--notemp':
        notempmode = True
    else:
        print_usage()
        sys.exit(1)
# Functions
def dbgprint(msg):
    if debugmode:
        sys.stderr.write(str('# DEBUG : ' + msg + '\n'))


def is_exe(fpath):
    return os.path.isfile(fpath) and os.access(fpath, os.X_OK)


def which(program):
    import os

    fpath, fname = os.path.split(program)
    if fpath:
        if is_exe(program):
            return program
    else:
        # Add some defaults
        os.environ["PATH"] += os.pathsep + '/opt/MegaRAID/MegaCli'
        os.environ["PATH"] += os.pathsep + '/ms/dist/hwmgmt/bin'
        os.environ["PATH"] += os.pathsep + os.path.dirname(
            os.path.realpath(sys.argv[0])
        )
        for path in os.environ["PATH"].split(os.pathsep):
            dbgprint('Looking in PATH ' + str(path))
            path = path.strip('"')
            exe_file = os.path.join(path, program)
            if is_exe(exe_file):
                dbgprint('Found "' + program + '" at ' + exe_file)
                return exe_file
    return None


# Find MegaCli
for megabin in "MegaCli64", "MegaCli", "megacli", "MegaCli.exe":
    dbgprint('Looking for ' + str(megabin) + ' in PATH next..')
    megaclipath = which(megabin)
    if megaclipath != None:
        dbgprint('Will use MegaCLI from here: ' + str(megaclipath))
        break

# Check binary exists (and +x), if not print an error message
if megaclipath != None:
    if os.path.exists(megaclipath) and os.access(megaclipath, os.X_OK):
        pass
    else:
        if nagiosmode:
            print('UNKNOWN - Cannot find ' + megaclipath)
        else:
            print(
                'Cannot find '
                + megaclipath
                + 'in your PATH. Please install it.'
            )
        sys.exit(3)
else:
    print(
        'Cannot find "MegaCli64","MegaCli" or "megacli" or "MegaCli.exe" in your PATH. Please install it.'
    )
    sys.exit(3)


#### pdb.set_trace()


def returnWdthFromArrayCol(glarray, idx):
    maxwdth = 0
    for glrow in glarray:
        if len(glrow[idx]) > maxwdth:
            maxwdth = len(glrow[idx])
    return maxwdth


# Get command output
def getOutput(cmd):
    lines = []
    if cmd in Outputs:
        dbgprint("Got Cached value: " + str(cmd))
        lines = Outputs[cmd]
    else:
        dbgprint("Not a Cached value: " + str(cmd))
        output = os.popen(cmd)
        for line in output:
            if not re.match(r'^$', line.strip()):
                lines.append(line.strip())
        Outputs[cmd] = lines
    return lines


def returnControllerNumber(output):
    for line in output:
        if re.match(r'^Controller Count.*$', line.strip()):
            return int(line.split(':')[1].strip().strip('.'))


def returnTotalDriveNumber(output):
    for line in output:
        if re.match(r'Number of Physical Drives on Adapter.*$', line.strip()):
            return int(line.split(':')[1].strip())


def returnRebuildProgress(output):
    percent = 0
    tmpstr = ''
    for line in output:
        if re.match(
            r'^Rebuild Progress on Device at Enclosure.*, Slot .* Completed ',
            line.strip(),
        ):
            tmpstr = line.split('Completed')[1].strip()
            percent = int(tmpstr.split('%')[0].strip())
    return percent


def returnConfDriveNumber(output):
    # Count the configured drives
    confdrives = 0
    for line in output:
        if re.match(r'.*Number of PDs:.*$', line.strip()):
            confdrives += int(line.split(':')[2].strip())
    return int(confdrives)


def returnUnConfDriveNumber(output):
    # Count the configured drives
    confdrives = 0
    for line in output:
        if re.match(r'^Firmware state: Unconfigured.*$', line.strip()):
            confdrives += 1
    return int(confdrives)


def returnControllerModel(output):
    for line in output:
        if re.match(r'^Product Name.*$', line.strip()):
            return line.split(':')[1].strip()


def returnMemorySize(output):
    for line in output:
        if re.match(r'^Memory Size.*$', line.strip()):
            return line.split(':')[1].strip()


def returnFirmwareVersion(output):
    for line in output:
        if re.match(r'^FW Package Build.*$', line.strip()):
            return line.split(':')[1].strip()


def returnROCTemp(output):
    ROCtemp = ''
    tmpstr = ''
    if notempmode:
        return 'N/A'
    else:
        for line in output:
            if re.match(r'^ROC temperature :.*$', line.strip()):
                tmpstr = line.split(':')[1].strip()
                ROCtemp = re.sub(' +.*$', '', tmpstr)
        if ROCtemp != '':
            return str(str(ROCtemp) + 'C')
        else:
            return 'N/A'


def returnBBUPresence(output):
    BBU = ''
    tmpstr = ''
    for line in output:
        if re.match(r'^BBU +:.*$', line.strip()):
            tmpstr = line.split(':')[1].strip()
            BBU = re.sub(' +.*$', '', tmpstr)
            break
    if BBU != '':
        return str(BBU)
    else:
        return 'N/A'


def returnBBUStatus(output):
    BBUStatus = ''
    tmpstr = ''
    for line in output:
        if re.match(r'^ *Battery Replacement required +:.*$', line.strip()):
            tmpstr = line.split(':')[1].strip()
            BBUStatus = re.sub(' +.*$', '', tmpstr)
            break
    if BBUStatus == 'Yes':
        return 'REPL'
    else:
        return 'Good'


def returnArrayNumber(output):
    i = 0
    for line in output:
        if re.match(r'^(CacheCade )?Virtual Drive:.*$', line.strip()):
            i += 1
    return i


def returnHBAPCIInfo(output):
    busprefix = '0000'
    busid = ''
    devid = ''
    functionid = ''
    pcipath = ''
    for line in output:
        if re.match(r'^Bus Number.*:.*$', line.strip()):
            busid = str(line.strip().split(':')[1].strip()).zfill(2)
        if re.match(r'^Device Number.*:.*$', line.strip()):
            devid = str(line.strip().split(':')[1].strip()).zfill(2)
        if re.match(r'^Function Number.*:.*$', line.strip()):
            functionid = str(line.strip().split(':')[1].strip()).zfill(1)
    if busid:
        pcipath = str(busprefix + ':' + busid + ':' + devid + '.' + functionid)
        dbgprint("Array PCI path : " + pcipath)
        return str(pcipath)
    else:
        return None


def returnHBAInfo(table, output, controllerid):
    controllermodel = 'Unknown'
    controllerram = 'Unknown'
    controllerrev = 'Unknown'
    controllertemp = ''
    controllermodel = returnControllerModel(output)
    controllerram = returnMemorySize(output)
    controllerrev = returnFirmwareVersion(output)
    controllertemp = returnROCTemp(output)
    controllerbbu = returnBBUPresence(output)
    if controllerbbu == 'Present':
        cmd = '%s -AdpBbuCmd -GetBbuStatus -a%d -NoLog' % (
            megaclipath,
            controllerid,
        )
        output = getOutput(cmd)
        controllerbbu = returnBBUStatus(output)

    if controllermodel != 'Unknown':
        table.append(
            [
                'c' + str(controllerid),
                controllermodel,
                controllerram,
                str(controllertemp),
                str(controllerbbu),
                str('FW: ' + controllerrev),
            ]
        )


def returnArrayInfo(output, controllerid, arrayid, arrayindex):
    id = 'c' + str(controllerid) + 'u' + str(arrayid)
    operationlinennumber = False
    linenumber = 0
    targetid = ''
    raidtype = ''
    raidlvl = ''
    size = ''
    state = 'N/A'
    strpsz = ''
    dskcache = 'N/A'
    properties = ''
    spandepth = 0
    diskperspan = 0
    cachecade_info = 'None'

    for line in output:
        if re.match(
            r'^(CacheCade )?Virtual Drive:.*(Target Id: [0-9]+).*$',
            line.strip(),
        ):
            # Extract the SCSI Target ID
            targetid = line.strip().split(':')[2].split(')')[0].strip()
        elif re.match(r'^RAID Level.*?:.*$', line.strip()):
            # Extract the primary raid type, decide on X0 RAID level later when we hit Span Depth
            raidlvl = int(
                line.strip().split(':')[1].split(',')[0].split('-')[1].strip()
            )
        elif re.match(r'^Size.*?:.*$', line.strip()):
            # Size reported in MB
            if re.match(r'^.*MB$', line.strip().split(':')[1]):
                size = line.strip().split(':')[1].strip('MB').strip()
                if float(size) > 1000:
                    size = str(int(round(float(size) / 1000))) + 'G'
                else:
                    size = str(int(round(float(size)))) + 'M'
            # Size reported in TB
            elif re.match(r'^.*TB$', line.strip().split(':')[1]):
                size = line.strip().split(':')[1].strip('TB').strip()
                size = str(int(round(float(size) * 1000))) + 'G'
            # Size reported in GB (default)
            else:
                size = line.strip().split(':')[1].strip('GB').strip()
                size = str(int(round(float(size)))) + 'G'
        elif re.match(r'^Span Depth.*?:.*$', line.strip()):
            # If Span Depth is greater than 1 chances are we have a RAID 10, 50 or 60
            spandepth = line.strip().split(':')[1].strip()
        elif re.match(r'^State.*?:.*$', line.strip()):
            state = line.strip().split(':')[1].strip()
        elif re.match(r'^Strip Size.*?:.*$', line.strip()):
            strpsz = line.strip().split(':')[1].strip()
        elif re.match(r'^Number Of Drives per span.*:.*$', line.strip()):
            diskperspan = int(line.strip().split(':')[1].strip())
        elif re.match(r'^Current Cache Policy.*?:.*$', line.strip()):
            props = line.strip().split(':')[1].strip()
            if re.search('ReadAdaptive', props):
                properties += 'ADRA'
            if re.search('ReadAhead', props):
                properties += 'RA'
            if re.match('ReadAheadNone', props):
                properties += 'NORA'
            if re.search('WriteBack', props):
                properties += ',WB'
            if re.match('WriteThrough', props):
                properties += ',WT'
        elif re.match(r'^Disk Cache Policy.*?:.*$', line.strip()):
            props = line.strip().split(':')[1].strip()
            if re.search('Disabled', props):
                dskcache = 'Disabled'
            if re.search('Disk.s Default', props):
                dskcache = 'Default'
            if re.search('Enabled', props):
                dskcache = 'Enabled'
        elif re.match(r'^Ongoing Progresses.*?:.*$', line.strip()):
            operationlinennumber = linenumber
        elif re.match(r'Cache Cade Type\s*:.*$', line):
            cachecade_info = "Type : " + line.strip().split(':')[1].strip()
        elif re.match(r'^Target Id of the Associated LDs\s*:.*$', line):
            associated = []
            for array in line.split(':')[1].strip().split(','):
                if array.isdigit():
                    associated.append('c%du%d' % (controllerid, int(array)))
            if len(associated) >= 1:
                cachecade_info = "Associated : %s" % (', '.join(associated))
        linenumber += 1

    # If there was an ongoing operation, find the relevant line in the previous output
    if operationlinennumber:
        inprogress = output[operationlinennumber + 1]
    else:
        inprogress = 'None'

    # Compute the RAID level
    NestedLDTable[int(controllerid)][int(arrayindex)] = False
    if raidlvl == '':
        raidtype = 'N/A'
    else:
        if int(spandepth) >= 2:
            raidtype = str('RAID-' + str(raidlvl) + '0')
            NestedLDTable[controllerid][int(arrayindex)] = True
        else:
            if raidlvl == 1:
                if diskperspan > 2:
                    raidtype = 'RAID-10'
                    NestedLDTable[controllerid][int(arrayindex)] = True
                else:
                    raidtype = str('RAID-' + str(raidlvl))
            else:
                raidtype = str('RAID-' + str(raidlvl))

    dbgprint(
        'RAID Level: '
        + str(raidlvl)
        + ' Span Depth: '
        + str(spandepth)
        + ' Disk Per Span: '
        + str(diskperspan)
        + ' Raid Type: '
        + str(raidtype)
    )
    return [
        id,
        raidtype,
        size,
        strpsz,
        properties,
        dskcache,
        state,
        targetid,
        cachecade_info,
        inprogress,
    ]


def returnDiskInfo(output, controllerid):
    arrayid = False
    arrayindex = -1
    sarrayid = 'Unknown'
    diskid = False
    oldenclid = False
    enclid = False
    spanid = False
    slotid = False
    lsidid = 'Unknown'
    table = []
    fstate = 'Offline'
    substate = 'Unknown'
    model = 'Unknown'
    speed = 'Unknown'
    dsize = 'Unknown'
    temp = 'Unk0C'
    percent = 0
    for line in output:
        if re.match(r'^Span: [0-9]+ - Number of PDs:', line.strip()):
            spanid = line.split(':')[1].strip()
            spanid = re.sub(' - Number of PDs.*', '', spanid)
        elif re.match(r'Enclosure Device ID: .*$', line.strip()):
            # We match here early in the analysis so reset the vars if this is a new disk we're reading..
            oldenclid = enclid
            enclid = line.split(':')[1].strip()
            if oldenclid != False:
                fstate = 'Offline'
                model = 'Unknown'
                speed = 'Unknown'
                temp = 'Unk0C'
                slotid = False
                lsidid = 'Unknown'
        elif re.match(r'^Coerced Size: ', line.strip()):
            dsize = line.split(':')[1].strip()
            dsize = re.sub(r' \[.*\.*$', '', dsize)
            dsize = re.sub('[0-9][0-9] GB', ' Gb', dsize)
        elif re.match(
            r'^(CacheCade )?Virtual (Disk|Drive): [0-9]+.*$', line.strip()
        ):
            arrayindex += 1
            arrayid = line.split('(')[0].split(':')[1].strip()
        elif re.match(r'PD: [0-9]+ Information.*$', line.strip()):
            diskid = line.split()[1].strip()
        elif re.match(r'^Device Id: .*$', line.strip()):
            lsidid = line.split(':')[1].strip()
        elif re.match(r'Slot Number: .*$', line.strip()):
            slotid = line.split(':')[1].strip()
        elif re.match(r'Firmware state: .*$', line.strip()):
            fstate = line.split(':')[1].strip()
            subfstate = re.sub(r'\(.*', '', fstate)
            dbgprint('Firmware State: ' + str(fstate) + ' ' + str(subfstate))
        elif re.match(r'Inquiry Data: .*$', line.strip()):
            model = line.split(':')[1].strip()
            model = re.sub(' +', ' ', model)
            # Sub code
            manuf = re.sub(' .*', '', model)
            dtype = re.sub(manuf + ' ', '', model)
            dtype = re.sub(' .*', '', dtype)
            hwserial = re.sub('.*' + dtype + ' *', '', model)
        elif re.match(r'^Media Type: .*$', line.strip()):
            mtype = line.split(':')[1].strip()
            if mtype == 'Hard Disk Device':
                mtype = 'HDD'
            else:
                if mtype == 'Solid State Device':
                    mtype = 'SSD'
                else:
                    mtype = 'N/A'
        elif re.match(r'Device Speed: .*$', line.strip()):
            speed = line.split(':')[1].strip()
        elif re.match(r'Drive Temperature :.*$', line.strip()):
            if notempmode:
                temp = 'N/A'
            else:
                # Drive temp is amongst the last few lines matched, decide here if we add information to the table..
                temp = line.split(':')[1].strip()
                temp = re.sub(r' \(.*\)', '', temp)
            if model != 'Unknown':
                dbgprint(
                    'Disk Info: '
                    + str(arrayid)
                    + ' '
                    + str(diskid)
                    + ' '
                    + str(oldenclid)
                )
                if subfstate == 'Rebuild':
                    cmd = (
                        r'%s pdrbld -showprog -physdrv\[%s:%s\] -a%d -NoLog'
                        % (megaclipath, enclid, slotid, controllerid)
                    )
                    output = getOutput(cmd)
                    percent = returnRebuildProgress(output)
                    fstate = str('Rebuilding (%d%%)' % (percent))

                if (NestedLDTable[controllerid][int(arrayindex)] == True) and (
                    spanid != False
                ):
                    sarrayid = str(arrayid) + "s" + spanid
                else:
                    sarrayid = str(arrayid)
                table.append(
                    [
                        sarrayid,
                        str(diskid),
                        mtype,
                        model,
                        dsize,
                        fstate,
                        speed,
                        temp,
                        enclid,
                        slotid,
                        lsidid,
                    ]
                )
    return table


def returnUnconfDiskInfo(output, controllerid):
    arrayid = False
    diskid = False
    olddiskid = False
    enclid = False
    slotid = False
    lsidid = 'Unknown'
    table = []
    fstate = 'Offline'
    substate = 'Unknown'
    model = 'Unknown'
    speed = 'Unknown'
    mtype = 'Unknown'
    dsize = 'Unknown'
    temp = 'Unk0C'
    for line in output:
        if re.match(r'Enclosure Device ID: .*$', line.strip()):
            # We match here early in the analysis so reset the vars if this is a new disk we're reading..
            oldenclid = enclid
            enclid = line.split(':')[1].strip()
            if oldenclid != False:
                arrayid = False
                fstate = 'Offline'
                model = 'Unknown'
                speed = 'Unknown'
                temp = 'Unk0C'
                slotid = False
                lsidid = 'Unknown'

        elif re.match(r'^Coerced Size: ', line.strip()):
            dsize = line.split(':')[1].strip()
            dsize = re.sub(r' \[.*\.*$', '', dsize)
            dsize = re.sub('[0-9][0-9] GB', ' Gb', dsize)
        elif re.match(
            r'^Drive.s position: DiskGroup: [0-9]+,.*$', line.strip()
        ):
            arrayid = line.split(',')[1].split(':')[1].strip()
        elif re.match(r'^Device Id: [0-9]+.*$', line.strip()):
            diskid = line.split(':')[1].strip()
        elif re.match(r'^Device Id: .*$', line.strip()):
            lsidid = line.split(':')[1].strip()
        elif re.match(r'Slot Number: .*$', line.strip()):
            slotid = line.split(':')[1].strip()
        elif re.match(r'Firmware state: .*$', line.strip()):
            fstate = line.split(':')[1].strip()
            subfstate = re.sub(r'\(.*', '', fstate)
            dbgprint('Firmware State: ' + str(fstate) + ' ' + str(subfstate))
        elif re.match(r'Inquiry Data: .*$', line.strip()):
            model = line.split(':')[1].strip()
            model = re.sub(' +', ' ', model)
            manuf = re.sub(' .*', '', model)
            dtype = re.sub(manuf + ' ', '', model)
            dtype = re.sub(' .*', '', dtype)
            hwserial = re.sub('.*' + dtype + ' *', '', model)
        elif re.match(r'^Media Type: .*$', line.strip()):
            mtype = line.split(':')[1].strip()
            if mtype == 'Hard Disk Device':
                mtype = 'HDD'
            else:
                if mtype == 'Solid State Device':
                    mtype = 'SSD'
                else:
                    mtype = 'N/A'
        elif re.match(r'Device Speed: .*$', line.strip()):
            speed = line.split(':')[1].strip()
        elif re.match(r'Drive Temperature :.*$', line.strip()):
            temp = line.split(':')[1].strip()
            temp = re.sub(r'\(.*\)', '', temp)
            # Drive temp is amongst the last few lines matched, decide here if we add information to the table..
            if arrayid == False:
                if subfstate == 'Unconfigured':
                    dbgprint(
                        'Unconfigured Disk: Arrayid: '
                        + str(arrayid)
                        + ' DiskId: '
                        + str(diskid)
                        + ' '
                        + str(olddiskid)
                        + ' '
                        + str(fstate)
                    )
                elif subfstate == 'Online, Spun Up':
                    dbgprint(
                        'Online Disk: Arrayid: '
                        + str(arrayid)
                        + ' DiskId: '
                        + str(diskid)
                        + ' '
                        + str(olddiskid)
                        + ' '
                        + str(fstate)
                    )
                table.append(
                    [
                        mtype,
                        model,
                        dsize,
                        fstate,
                        speed,
                        temp,
                        enclid,
                        slotid,
                        lsidid,
                    ]
                )
    return table


def returnForeignDisks(controllerid):
    cmd = "%s -CfgForeign -Dsply -a%d" % (megaclipath, controllerid)
    foreignout = getOutput(cmd)
    foreigndisks = []
    for line in foreignout:
        result = re.search(r"Slot Number: (.*)", line)
        if result:
            matches = result.groups()
            foreigndisks.append(matches[0])

    return foreigndisks


cmd = '%s -adpCount -NoLog' % (megaclipath)
output = getOutput(cmd)
controllernumber = returnControllerNumber(output)

bad = False

# List available controller
if printcontroller:
    if controllernumber:
        if not nagiosmode:
            print('-- Controller information --')

        i = 0
        controllerid = 0
        mlen = 0
        hbainfo = []
        while controllerid < controllernumber:
            cmd = '%s -AdpAllInfo -a%d -NoLog' % (megaclipath, controllerid)
            output = getOutput(cmd)
            returnHBAInfo(hbainfo, output, controllerid)
            controllerid += 1
        mlen = returnWdthFromArrayCol(hbainfo, 1)

        controllerid = 0
        for hba in hbainfo:
            hbafmt = str(
                '%-5s | %-' + str(mlen) + 's | %-6s | %-4s | %-6s | %-12s '
            )
            # Header
            if i == 0:
                if not nagiosmode:
                    print(
                        hbafmt
                        % (
                            "-- ID",
                            "H/W Model",
                            "RAM",
                            "Temp",
                            "BBU",
                            "Firmware",
                        )
                    )
            if not nagiosmode:
                print(hbafmt % (hba[0], hba[1], hba[2], hba[3], hba[4], hba[5]))
            i += 1
        if not nagiosmode:
            print('')
    else:
        print("No MegaRAID or PERC adapter detected on your system!")
        exit(1)

if printarray:
    if not nagiosmode:
        print('-- Array information --')

    controllerid = 0
    pcipath = ''
    diskpath = ''
    i = 0
    j = 0
    mlen = 0
    rlen = 0
    clen = 0
    while controllerid < controllernumber:
        arrayindex = 0

        cmd = '%s -LDInfo -lall -a%d -NoLog' % (megaclipath, controllerid)
        output = getOutput(cmd)
        arraynumber = returnArrayNumber(output)
        # We need to explore each HBA to look for gaps in LD's
        ldid = 0
        ldcount = 0
        while ldcount < arraynumber:
            cmd = '%s -LDInfo -l%d -a%d -NoLog' % (
                megaclipath,
                ldid,
                controllerid,
            )
            output = getOutput(cmd)
            for line in output:
                if re.match(
                    r'^Adapter.*Virtual Drive .* Does not Exist', line.strip()
                ):
                    ldid += 1
                elif re.match(r'^(CacheCade )?Virtual Drive:', line.strip()):
                    LDTable[controllerid].append(ldid)
                    # NestedLDTable[controllerid][int(arrayindex)] = False
                    ldcount += 1
                    ldid += 1

        while arrayindex < arraynumber:
            ldid = LDTable[controllerid][arrayindex]
            cmd = '%s -LDInfo -l%d -a%d -NoLog' % (
                megaclipath,
                ldid,
                controllerid,
            )
            output = getOutput(cmd)
            arrayinfo = returnArrayInfo(output, controllerid, ldid, arrayindex)
            if len(arrayinfo[1]) > rlen:
                rlen = len(arrayinfo[1])
            if len(arrayinfo[4]) > mlen:
                mlen = len(arrayinfo[4])
            if len(arrayinfo[8]) > clen:
                clen = len(arrayinfo[8])
            arrayindex += 1
        controllerid += 1

    controllerid = 0
    while controllerid < controllernumber:
        arrayindex = 0

        cmd = '%s -AdpGetPciInfo -a%d -NoLog' % (megaclipath, controllerid)
        output = getOutput(cmd)
        pcipath = returnHBAPCIInfo(output)

        cmd = '%s -LDInfo -lall -a%d -NoLog' % (megaclipath, controllerid)
        output = getOutput(cmd)
        arraynumber = returnArrayNumber(output)
        while arrayindex < arraynumber:
            ldid = LDTable[controllerid][arrayindex]
            cmd = '%s -LDInfo -l%d -a%d -NoLog' % (
                megaclipath,
                ldid,
                controllerid,
            )
            output = getOutput(cmd)
            arrayinfo = returnArrayInfo(output, controllerid, ldid, arrayindex)

            if pcipath:
                diskprefix = str(
                    '/dev/disk/by-path/pci-' + pcipath + '-scsi-0:'
                )
                for j in range(8):
                    diskpath = (
                        diskprefix + str(j) + ':' + str(arrayinfo[7]) + ':0'
                    )
                    if os.path.exists(diskpath):
                        arrayinfo[7] = os.path.realpath(diskpath)
            else:
                arrayinfo[7] = 'N/A'

            # Pad the string length, just to make sure it's aligned with the headers...
            if rlen < len("Type"):
                rlen = len("Type")
            if mlen < len("Flags"):
                mlen = len("Flags")
            if clen < len("CacheCade"):
                clen = len("CacheCade")

            ldfmt = str(
                '%-5s | %-'
                + str(rlen)
                + 's | %7s | %7s | %'
                + str(mlen)
                + 's | %8s | %8s | %8s | %-'
                + str(clen)
                + 's |%-12s '
            )
            # Header
            if i == 0:
                if not nagiosmode:
                    print(
                        ldfmt
                        % (
                            "-- ID",
                            "Type",
                            "Size",
                            "Strpsz",
                            "Flags",
                            "DskCache",
                            "Status",
                            "OS Path",
                            "CacheCade",
                            "InProgress",
                        )
                    )
            if not nagiosmode:
                print(
                    ldfmt
                    % (
                        arrayinfo[0],
                        arrayinfo[1],
                        arrayinfo[2],
                        arrayinfo[3],
                        arrayinfo[4],
                        arrayinfo[5],
                        arrayinfo[6],
                        arrayinfo[7],
                        arrayinfo[8],
                        arrayinfo[9],
                    )
                )
            dbgprint("Array state : " + arrayinfo[6])
            if arrayinfo[6] not in ['Optimal', 'N/A']:
                bad = True
                nagiosbadarray = nagiosbadarray + 1
            else:
                nagiosgoodarray = nagiosgoodarray + 1
            arrayindex += 1
            i += 1
        controllerid += 1
    if not nagiosmode:
        print('')

controllerid = 0
while controllerid < controllernumber:
    cmd = '%s -PDGetNum -a%d -NoLog' % (megaclipath, controllerid)
    output = getOutput(cmd)
    totaldrivenumber += returnTotalDriveNumber(output)
    controllerid += 1

if totaldrivenumber:
    if not nagiosmode:
        print('-- Disk information --')

    i = 0
    dlen = 0
    mlen = 0
    flen = 0
    controllerid = 0
    while controllerid < controllernumber:
        arrayid = 0
        cmd = '%s -LDInfo -lall -a%d -NoLog' % (megaclipath, controllerid)
        output = getOutput(cmd)
        arraynumber = returnArrayNumber(output)
        #### BUG: -LdPdInfo shows all PD on the adapter, not just for said LD..
        #### while arrayid <= arraynumber:
        cmd = '%s -LdPdInfo -a%d -NoLog' % (megaclipath, controllerid)
        output = getOutput(cmd)
        arraydisk = returnDiskInfo(output, controllerid)
        for array in arraydisk:
            dbgprint(
                'Disk c'
                + str(controllerid)
                + 'u'
                + array[0]
                + 'p'
                + array[1]
                + ' status : '
                + array[5]
            )
            if array[5] not in ['Online', 'Online, Spun Up']:
                bad = True
                nagiosbaddisk = nagiosbaddisk + 1
            else:
                nagiosgooddisk = nagiosgooddisk + 1

        if returnWdthFromArrayCol(arraydisk, 0) > dlen:
            dlen = returnWdthFromArrayCol(arraydisk, 0)
        if returnWdthFromArrayCol(arraydisk, 3) > mlen:
            mlen = returnWdthFromArrayCol(arraydisk, 3)
        if returnWdthFromArrayCol(arraydisk, 5) > flen:
            flen = returnWdthFromArrayCol(arraydisk, 5)
        controllerid += 1

    controllerid = 0
    while controllerid < controllernumber:
        arrayid = 0

        cmd = '%s -LDInfo -lall -a%d -NoLog' % (megaclipath, controllerid)
        output = getOutput(cmd)
        arraynumber = returnArrayNumber(output)
        #### BUG: -LdPdInfo shows all PD on the adapter, not just for said LD..
        #### while arrayid <= arraynumber:

        cmd = '%s -LdPdInfo -a%d -NoLog' % (megaclipath, controllerid)
        output = getOutput(cmd)
        arraydisk = returnDiskInfo(output, controllerid)

        # Adjust print format with width computed above
        drvfmt = (
            "%-"
            + str(dlen + 5)
            + "s | %-4s | %-"
            + str(mlen)
            + "s | %-8s | %-"
            + str(flen)
            + "s | %-8s | %-4s | %-8s | %-8s"
        )
        for array in arraydisk:
            # Header
            if i == 0:
                if not nagiosmode:
                    print(
                        drvfmt
                        % (
                            "-- ID",
                            "Type",
                            "Drive Model",
                            "Size",
                            "Status",
                            "Speed",
                            "Temp",
                            "Slot ID",
                            "LSI Device ID",
                        )
                    )
            # Drive information
            if not nagiosmode:
                print(
                    drvfmt
                    % (
                        str(
                            'c'
                            + str(controllerid)
                            + 'u'
                            + array[0]
                            + 'p'
                            + array[1]
                        ),  # c0p0
                        array[2],  # HDD/SDD
                        array[3],  # Model Information (Variable len)
                        array[4],  # Size
                        array[5],  # Status (Variable len)
                        array[6],  # Speed
                        array[7],  # Temp
                        str('[' + array[8] + ':' + array[9] + ']'),  # Slot ID
                        array[10],
                    )
                )  # LSI ID
            i = i + 1
        controllerid += 1
    if not nagiosmode:
        print('')

controllerid = 0
totalconfdrivenumber = 0
totalunconfdrivenumber = 0
totaldrivenumber = 0
while controllerid < controllernumber:
    cmd = '%s -LdPdInfo -a%d -NoLog' % (megaclipath, controllerid)
    output = getOutput(cmd)
    totalconfdrivenumber += returnConfDriveNumber(output)

    cmd = '%s -PDGetNum -a%d -NoLog' % (megaclipath, controllerid)
    output = getOutput(cmd)
    totaldrivenumber += returnTotalDriveNumber(output)

    cmd = '%s -PDList -a%d -NoLog' % (megaclipath, controllerid)
    output = getOutput(cmd)
    totalunconfdrivenumber += returnUnConfDriveNumber(output)

    controllerid += 1

dbgprint('Total Drives in system : ' + str(totaldrivenumber))
dbgprint('Total Configured Drives : ' + str(totalconfdrivenumber))
dbgprint('Total Unconfigured Drives : ' + str(totalunconfdrivenumber))

if totalunconfdrivenumber:
    if not nagiosmode:
        print('-- Unconfigured Disk information --')

    controllerid = 0
    while controllerid < controllernumber:
        foreigndisks = returnForeignDisks(controllerid)
        arrayid = 0

        cmd = '%s -LDInfo -lall -a%d -NoLog' % (megaclipath, controllerid)
        output = getOutput(cmd)
        arraynumber = returnArrayNumber(output)
        #### BUG: -LdPdInfo shows all PD on the adapter, not just for given LD..
        #### while arrayid <= arraynumber:

        cmd = '%s -PDList -a%d -NoLog' % (megaclipath, controllerid)
        output = getOutput(cmd)
        arraydisk = returnUnconfDiskInfo(output, controllerid)
        for array in arraydisk:
            dbgprint('Disk c' + str(controllerid) + 'uXpY status : ' + array[3])
            if array[3] not in [
                'Online',
                'Unconfigured(good), Spun Up',
                'Unconfigured(good), Spun down',
                'JBOD',
                'Hotspare, Spun Up',
                'Hotspare, Spun down',
            ]:
                bad = True
                nagiosbaddisk = nagiosbaddisk + 1
            else:
                nagiosgooddisk = nagiosgooddisk + 1
            if array[7] in foreigndisks:
                array[3] = array[3] + " (Foreign)"

        mlen = returnWdthFromArrayCol(arraydisk, 1)
        flen = returnWdthFromArrayCol(arraydisk, 3)

        # Adjust print format with widths computed above
        drvfmt = (
            "%-7s | %-4s | %-"
            + str(mlen)
            + "s | %-8s | %-"
            + str(flen + 2)
            + "s | %-8s | %-4s | %-8s | %-8s"
        )
        i = 0
        for array in arraydisk:
            # Header
            if i == 0:
                if not nagiosmode:
                    print(
                        drvfmt
                        % (
                            "-- ID",
                            "Type",
                            "Drive Model",
                            "Size",
                            "Status",
                            "Speed",
                            "Temp",
                            "Slot ID",
                            "LSI Device ID",
                        )
                    )
            # Drive information
            if not nagiosmode:
                print(
                    drvfmt
                    % (
                        str('c' + str(controllerid) + 'uXpY'),  # cXpY
                        array[0],  # HDD/SDD
                        array[1],  # Model Information (Variable len)
                        array[2],  # Size
                        array[3],  # Status (Variable len)
                        array[4],  # Speed
                        array[5],  # Temp
                        str('[' + array[6] + ':' + array[7] + ']'),  # Slot ID
                        array[8],
                    )
                )  # LSI ID
            i = i + 1
        controllerid += 1
    if not nagiosmode:
        print('')

if nagiosmode:
    if bad:
        print(
            'RAID ERROR - Arrays: OK:'
            + str(nagiosgoodarray)
            + ' Bad:'
            + str(nagiosbadarray)
            + ' - Disks: OK:'
            + str(nagiosgooddisk)
            + ' Bad:'
            + str(nagiosbaddisk)
        )
        sys.exit(2)
    else:
        print(
            'RAID OK - Arrays: OK:'
            + str(nagiosgoodarray)
            + ' Bad:'
            + str(nagiosbadarray)
            + ' - Disks: OK:'
            + str(nagiosgooddisk)
            + ' Bad:'
            + str(nagiosbaddisk)
        )
else:
    if bad:
        print('\nThere is at least one disk/array in a NOT OPTIMAL state.')
        sys.exit(1)