#! /usr/bin/env python
'''A tool that performs a CTO FRU update'''

# Copyright C 2008-2011 by Isilon Systems, Inc. All rights not expressly
# granted herein are reserved.
# Created in the United States of America.
# 
# This digital media is protected by U.S. and international copyright and
# intellectual property laws. Unless otherwise specified, all information
# and screens appearing as part of this digital medium, including
# software, services, documents, text, images, icons, and logos design;
# the selection, assembly, arrangement, and design thereof; and the code
# that enables its presentation, are the sole property of Isilon Systems,
# Inc.
# 
# Except as otherwise required by applicable law, no part of any
# copyrighted material may be reproduced, modified, translated,
# distributed, published, or transmitted by any means, electronic or
# mechanical, for any purpose, without the prior written consent of Isilon
# Systems. 
#  
# Isilon and Isilon Systems are registered trademarks of Isilon Systems,
# Inc.  Legion is a trademark of Isilon Systems, Inc.  All other
# trademarks and registered trademarks are the property of their
# respective holders.

#
# A tool that performs a CTO FRU update
#
import os
import re
import sys
import time
import getopt
import syslog

import isi.error
import isi.app.lib.procs as procs
import isi.hw.hal as hal
import isi.misc as misc

import isi_cto_lib as cto

#----------------------------------------------------------------------
def doUpdate(packageLocation, noPrompt=False):
    '''Perform the update using the CTO files in the directory
        specified by 'packageLocation' and return error status (error).
        if 'noPrompt' is True, all user prompting will be skipped. This
        is intended solely for development and QA testing and should
        NEVER be used in the field.'''

    error = 0

    # Make sure the node is CTO capable
    error = cto.verifyCtoCapable()
    if error:
        return error

    print 'Node CTO capability verified'

    # Import the CTO files
    error = cto.importCtoInformation(packageLocation, None)
    if error:
        return error

    print 'CTO files imported'

    return error

#----------------------------------------------------------------------
def doABR(noPrompt=False, noSendABR=False):
    '''Perform a configuration check to make sure the node configuration
        is still valid after the FRU has been installed and then, based
        on the results, either ask the user if they want to send an ARB
        back to Isilon (in the case of configuration check errors) or just
        send it automatically without asking. Returns error status (error).
        if 'noPrompt' is True, all user prompting will be skipped. This
        is intended solely for development and QA testing and should
        NEVER be used in the field. If 'noSendABR' is True, rather than 
        sending the new ABR to Isilon, a copy of the new ABR will be left 
        in the user's current directory.'''

    error = 0

    # Make sure the node is CTO capable
    error = cto.verifyCtoCapable()
    if error:
        return error

    print 'Node CTO capability verified'

    # Run a configuration check on the node. If the configuration 
    # check fails, ask user if they want to ignore the failure and 
    # generate an ABR anyway
    error = cto.verifyConfigCheck()
    if error:
        if not noPrompt:
            print ''
            answer = cto.ask('Generate a new ABR anyway? (y/n) ')
            if answer.lower().startswith('y'):
                print 'Generating new ABR and sending to Isilon'
            else:
                print ('Fix the failures, and re-run ABR generation ')
                error = 1
                return error
        else:
            print 'Skipping user prompting due to noprompt option'
    else:
        print 'Node CTO configuration verified'

    (error, serialNo) = cto.readFile('/etc/isilon_serial_number')
    if error:
        print 'Unable to determine node serial number, ABR generation aborted'
        error = 1
        return error
        
    # Generate and send a new ABR to Isilon
    error = cto.generateAndSendABR(serialNo, noSendABR)
    if error:
        return error

    if not noSendABR:
        print 'ABR generated and sent to Isilon'

    return error

#----------------------------------------------------------------------
def usage():
    '''Print the usage message'''

    usage = (
'''Usage: %s [OPTION] [ARGS]

OPTIONS:
    -h, --help  Prints this text
    --update    Perform the update
    --abr       Generate a new ABR and send to Isilon
    --filepath <package location>  Designate a package location
''')

    print usage % sys.argv[0]

#----------------------------------------------------------------------
def main(argv):
    '''The main CTO FRU update script'''

    update = False
    abr = False
    noPrompt = False
    noSendABR = False
    filepath = None

    try:
        # NOTE: The 'noprompt' and 'nosendabr' options are for testing
        #       only and hidden from the usage message on purpose.
        opt, args = getopt.getopt(argv, 'h', ['update', 'abr', 'noprompt',
            'nosendabr', 'filepath=']) 

    except getopt.GetoptError, err:
        print str(err)
        usage()
        sys.exit(1)

    for o, a in opt:
        if o in ('-h', '--help'):
            usage()
            sys.exit(0)
        elif o in ('--update'):
            update = True
        elif o in ('--abr'):
            abr = True
        elif o in ('--noprompt'):
            noPrompt = True
        elif o in ('--nosendabr'):
            noSendABR = True
        elif o in ('--filepath'):
            filepath = a

    if not update and not abr:
        print 'Missing required option'
        usage()
        sys.exit(1)

    if update and abr:
        print 'Must be run separately'
        usage()
        sys.exit(1)

    # Filepath option must be provided in compliance mode
    if misc.sysctlbyname('isi.compliance') == 1 and not filepath:
        print '--filepath argument must be used in compliance mode'
        usage()
        sys.exit(1)

    syslog.openlog('isi_cto_udpate', 0, syslog.LOG_USER)
    
    # Get package location
    (packageLocation, scriptName) = cto.getPackageInfo(sys.argv[0])
    # If --filepath option is used, override packageLocation
    if filepath:
        packageLocation = filepath

    # Verify package integrity
    msg = 'Verifying package integrity'
    syslog.syslog(syslog.LOG_NOTICE, msg)
    print msg
    error = cto.verifyMd5(packageLocation)
    if error:
        msg = 'Package integrity is BAD'
        syslog.syslog(syslog.LOG_CRIT, msg)
        print msg
        sys.exit(1)
    msg = 'Package integrity is good'
    syslog.syslog(syslog.LOG_NOTICE, msg)
    print msg

    if update:
        msg = 'Beginning update'
        # Perform update
        syslog.syslog(syslog.LOG_NOTICE, msg)
        print msg
        error = doUpdate(packageLocation, noPrompt)
        if error:
            msg = 'Update failed'
            syslog.syslog(syslog.LOG_CRIT, msg)
            print msg
            sys.exit(1)
        msg = 'Update successful'
        syslog.syslog(syslog.LOG_NOTICE, msg)
        print msg

    elif abr:
        # Generate and send new ABR
        msg = 'Beginning ABR generation and send'
        syslog.syslog(syslog.LOG_NOTICE, msg)
        print msg
        error = doABR(noPrompt, noSendABR)
        if error:
            msg = 'ABR generation and/or send failed'
            syslog.syslog(syslog.LOG_CRIT, msg)
            print msg
            sys.exit(1)
        msg = 'ABR generation and send successful'
        syslog.syslog(syslog.LOG_NOTICE, msg)
        print msg

    print 'Done'
    sys.exit(0)

#----------------------------------------------------------------------
if __name__ == '__main__':
    main(sys.argv[1:])
