#!/usr/bin/env python

import re
import sys
import isi.hw.hal as hal
import isi.hw.bay as dbay
import isi.hw.drive_config as drive_config
import isi.hw.drivemon as drivemon
import isi.app.lib.procs as procs

# Command and regex to read drive model
MODEL_CMD = '/usr/bin/isi_radish -q bay%s'
MODEL_REGEX = '^.*is\s+(?P<model_string>[a-zA-Z0-9- ]*)\s+FW:.*$'

ACCEPTABLE_WEAR_PERCENT = 1

# Read drive model off the drive in the specified bay
def read_drive_model(bay):
    model = None
    cmd = MODEL_CMD % bay
    (error, output) = procs.get_cmd_output(cmd)
    if not error:
        m = re.match(MODEL_REGEX, output[0])
        if m:
            model = m.group('model_string')
    return model

def error(msg):
    print >> sys.stderr, "ERROR: %s" % msg

def info(msg):
    print >> sys.stdout, "INFO: %s" % msg

def main(argv):
    failures = 0
    num_bays = dbay.maxBays()
    num_ssds = hal.getNumSsds()

    print 'Checking SSD drives for wear'
    if num_ssds == 0:
        print "No SSD drives found"
        sys.exit(failures)

    print 'Found %s SSD drive(s)' % num_ssds

    # Load all of the known driveconfigs only once
    all_configs = {}
    drive_config.load_drive_configs_all(all_configs)

    bays = range(1, num_bays + 1)
    for bay in bays:
        worn = False
        if dbay.isSsd(bay):
            # Read the model of drive
            model = read_drive_model(bay)
            if model is None:
                error("Unable to read model for bay %d" % bay)
                failures += 1
                continue
            # Lookup the driveconfig info for this model of drive
            config = all_configs.get(model)
            if config is None:
                error("No config for bay %s model %s" % (bay, model))
                failures += 1
                continue
            # Verify that this config has all the needed fields to check wear
            if not drive_config.valid_drivemon_config(config):
                error("Invalid config for bay %s model %s" % (bay, model))
                failures += 1
                continue

            # No need for a get() because gconfig always fills in defaults, and
            # drive_config validity check has confirmed all fields are valid
            disk_type = config['type']
            wear_life = config['monitor']['wear-life']
            page = wear_life['page']
            offset = wear_life['offset']
            length = wear_life['length']
            direction = wear_life['direction']
            endian = wear_life['endian']
            length = wear_life['length']

            # Read the current wear value from the drive
            value = drivemon.getdiskdata(disk_type, endian, length, offset,
                page, bay, 0)

            if value is None:
                error('Unable to read wear value from bay %s' % bay)
                failures += 1
                continue

            # Determine if it's too far worn
            if direction == 'up':
                thresh = wear_life['min'] + ACCEPTABLE_WEAR_PERCENT
                if value > thresh:
                    worn = True
            else:
                thresh = wear_life['max'] - ACCEPTABLE_WEAR_PERCENT
                if value < thresh:
                    worn = True

            if not worn:
                info('Bay %s is ok' % bay)
            else:
                error('Bay %s is worn - DO NOT SHIP '
                      '[value="%s", thresh="%s", direction="%s"]' % 
                      (bay, value, thresh, direction))
                failures += 1

    sys.exit(failures)

if __name__ == "__main__":
    main(sys.argv[:])
