#!/usr/bin/python

"""
    This module contains the deployment script to flash an Intel system in WinPE and Linux
"""

import string, os, os.path, shutil, sys, types, systemMgr
from logMgr import addLogRecord,CRITICAL,ERROR,WARNING,INFO,DEBUG
from config import BootDevice, DeploymentStore, DeployPath
from deploymentData import chassisSpecs, chassisRestoreSpecs, UPDATEBIOS, UPDATEMGMT, UPDATEZIPS, UPDATECONFIG, UPDATEMASTER
from deploymentData import ATSTATE,FAILURE,UPDATE,REFRESH,REBOOT,UNSUPPORTED
try:
    __file__
except:
    __file__ = __name__

def getChassisSpec(restore):
    ret, system = systemMgr.queryProperty(0,'IntelBIOSID','BoardFamilyID','',{})
    if not system or ret:
        print 'Intel board family unavailable: ret = %d'%ret
        addLogRecord(0,__file__, ERROR,'Intel board family unavailable: ret = %d'%ret)
        return '', 0
    if system == 'S3000':
        chassis = system
    else:
        ret, chassis = systemMgr.queryProperty(0,'FRU','Chassis OEM Field','',{'info':'Mainboard FRU'})
        if not chassis or ret:
            print 'chassis unavailable: ret = %d'%ret
            addLogRecord(0,__file__, ERROR,'chassis unavailable: ret = %d'%ret)
            return '', 0
    if restore:
        if not chassisRestoreSpecs.has_key(chassis):
            print 'chassis %s is unsupported'%chassis
            addLogRecord(0,__file__, ERROR,'chassis %s is unsupported'%chassis)
            return chassis, 0
        chassisSpec = chassisRestoreSpecs[chassis]
    else:
        if not chassisSpecs.has_key(chassis):
            print 'chassis %s is unsupported'%chassis
            addLogRecord(0,__file__, ERROR,'chassis %s is unsupported'%chassis)
            return chassis, 0
        chassisSpec = chassisSpecs[chassis]
    return chassis, chassisSpec

def extractZip(zip,dst):
    """ Note: paths are removed """
    if not os.path.exists(dst):
        os.mkdir(dst, 0777)
    import zipfile
    myZipFile = zipfile.ZipFile( zip, "r" )
    for fileName in myZipFile.namelist():
        if fileName.endswith('/'):
            #os.mkdir(os.path.join(dst, fileName))
            continue
        else:
            outfile = open(os.path.join(dst, os.path.basename(fileName)), 'wb')
            outfile.write(myZipFile.read(fileName))
            outfile.close()

def extractUpdate(restore=0):
    ret, system = systemMgr.queryProperty(0,'IntelBIOSID','BoardFamilyID','',{})
    if not system or ret:
        print 'Intel board family unavailable: ret = %d'%ret
        addLogRecord(0,__file__, ERROR,'Intel board family unavailable: ret = %d'%ret)
        return FAILURE
    chassis, chassisSpec = getChassisSpec(restore)
    if not chassisSpec:
        return ''
    zipFiles = chassisSpec[UPDATEZIPS]
    cfgFile = chassisSpec[UPDATECONFIG]
    mstFile = chassisSpec[UPDATEMASTER]

    dataPath = os.path.join(BootDevice,'updates',system)
    filePath = os.path.join(DeployPath,'update')
    if os.path.exists(filePath):
        shutil.rmtree(filePath)
    for zipFile in zipFiles:
        extractZip(os.path.join(dataPath,zipFile),filePath)
    if os.path.exists(os.path.join(filePath,'flashupdt.cfg')):
        if not os.path.exists(os.path.join(filePath,'flashupdt.orig.cfg')):
            os.rename(os.path.join(filePath,'flashupdt.cfg'),os.path.join(filePath,'flashupdt.orig.cfg'))
        else:
            os.remove(os.path.join(filePath,'flashupdt.cfg'))
    shutil.copyfile(os.path.join(dataPath,cfgFile),os.path.join(filePath,'flashupdt.cfg'))
    if type(mstFile) is types.StringType: 
        if os.path.exists(os.path.join(filePath,'MASTER.CFG')):
            if not os.path.exists(os.path.join(filePath,'MASTER.orig.CFG')):
                os.rename(os.path.join(filePath,'MASTER.CFG'),os.path.join(filePath,'MASTER.orig.CFG'))
            else:
                os.remove(os.path.join(filePath,'MASTER.CFG'))
        shutil.copyfile(os.path.join(dataPath,mstFile),os.path.join(filePath,'MASTER.CFG'))
    else:
        if os.path.exists(os.path.join(filePath,'MASTER.CFG')):
            fd = open(os.path.join(filePath,'MASTER.CFG'),'r')
            mst = fd.read()
            fd.close()
            fd = open(os.path.join(filePath,'MASTER.CFG'),'w')
            fd.write('SET "%s"\n'%chassis)
            for mset in mstFile:
                fd.write('SET "%s"\n'%mset)
            fd.write(mst)
            fd.close()
        else:
            print 'MASTER.CFG not found in %s'%zipFiles
            addLogRecord(0,__file__, ERROR,'MASTER.CFG not found in %s'%zipFiles)
            return FAILURE
    return filePath

def flashWin32System(system,restore):
    if system == 'S3000':
        import flashamt.flashamt
        state = flashamt.flashamt.checkState(restore)
        if state >= UPDATE:
            state = flashamt.flashamt.amtUpdate(restore)
        if state != ATSTATE:
            return state
    else:
        import fwpiaupd.fwpiaupd
        state = fwpiaupd.fwpiaupd.checkState(restore)
        if state >= UPDATE:
            state = fwpiaupd.fwpiaupd.bmcUpdate(restore)
            if state == REFRESH or state == REBOOT:
                import frusdr.frusdr
                frusdr.frusdr.frusdrUpdate(restore)
        if state != ATSTATE:
            return state
    import iFlash.iflash
    state = iFlash.iflash.checkState(restore)
    if state < UPDATE:
        return state
    state = iFlash.iflash.iFlashUpdate(restore)
    return state

def flashLinuxSystem(system,restore):
    if system == 'S3000':
        import flashamt.flashamt
        state = flashamt.flashamt.checkState(restore)
        if state >= UPDATE:
            state = flashamt.flashamt.amtUpdate(restore)
        if state != ATSTATE:
            return state
    
    import ofu.ofu
    state = ofu.ofu.checkState(restore)
    if state < UPDATE:
        return state
    state = ofu.ofu.flashSystem(restore)
    return state

def runState(restore=0):
    import time
    ret, system = systemMgr.queryProperty(0,'IntelBIOSID','BoardFamilyID','',{})
    if not system or ret:
        print 'Intel board family unavailable: ret = %d'%ret
        addLogRecord(0,__file__, ERROR,'Intel board family unavailable: ret = %d'%ret)
        return FAILURE
    for i in [1,2,3]:   # try refresh 3 time to get to state
        if sys.platform == 'linux2':
            state = flashLinuxSystem(system,restore)
        elif sys.platform == 'win32':
            state = flashWin32System(system,restore)
        else:
            return FAILURE
        if state == REFRESH:
            time.sleep(5)
            os.system('prepareDeployment refresh')
        else:
            break
    else:
        print 'system flash failed after 3 retries'
        addLogRecord(0,__file__, ERROR,'system flash failed after 3 retries')
        state = FAILURE
    return state

if __name__ == '__main__':
    """
    this script assumed these enviroment variables have been set:
    (see deploy.sh or deploy.cmd)
        DeployPath
        DeploymentStore
        PATH
        LD_LIBRARY_PATH (Linux)
        PYTHONPATH
    and this script has been run:
        prepareDeployment
    and flash data is located in:
        BootDevice/updates/S5000'
    """
    
    if len(sys.argv) == 1:
        restore = 0
    elif len(sys.argv) == 2 and sys.argv[1] == '0':
        restore = 0
    else:
        restore = 1
    state = runState(restore)
    if state == REBOOT:
        print "System must be reset to complete flash"
    sys.exit(state)
