#!/usr/bin/python
# vim:expandtab:autoindent:tabstop=4:shiftwidth=4:filetype=python:tw=0

  #############################################################################
  #
  # Copyright (c) 2005 Dell Computer Corporation
  # Dual Licenced under GNU GPL and OSL
  #
  #############################################################################
"""update_firmware:

usage:
    -h | --help         print this message
    -c | --config       Location of an additional config file to import
    -o | --over         Override config file option
"""

from __future__ import generators

# import arranged alphabetically
import getopt
import glob
import os
import sys
import ConfigParser
import traceback

import firmwaretools.repository
import firmwaretools.package
import firmwaretools.pycompat
import firmwaretools.clifuncs
import firmwaretools.trace_decorator as trace_decorator

class CmdlineError(Exception):pass

def main():
    global firmwaretools # so we can import more submodules below...
    ini = ConfigParser.ConfigParser()
    altConfig = 0
    overrides = []
    interactive = 1
    rpmMode = 0
    verbose=0

    try:
        opts, args = getopt.getopt(sys.argv[1:], "fhc:o:rytv", ["help", "config=", "overrides=", "rpm", "yes", "test", "verbose", "fake-mode"])
        for option, argument in opts:
            if option in ("-h", "--help"):
                print __doc__
                sys.exit(0)
            if option in ("-c", "--config"):
                firmwaretools.clifuncs.getConfig(ini, [argument,])
                altConfig = 1
            if option in ("-o", "--override"):
                overrides.append(argument)
            if option in ("--rpm",):
                rpmMode = 1
            if option in ("-y", "--yes"):
                interactive = 0
            if option in ("-t", "--test",):
                interactive = 2
            if option in ("-v", "--verbose",):
                verbose = verbose + 1
                trace_decorator.debug["__main__"] = verbose
            if option in ("-f", "--fake-mode"):
                os.environ['DEBUG_REPOSITORY'] = "1"
                os.environ['DEBUG_INVENTORY'] = "1"
                import firmwaretools.mockrepository
                import firmwaretools.mockpackage

        # load standard configuration
        if not altConfig:
            firmwaretools.clifuncs.getConfig(ini, firmwaretools.clifuncs.configLocations)

        for over in overrides:
            section, key, value = over.split(",", 2)
            if not ini.has_section(section):
                ini.add_section(section)
            ini.set(section, key, value)

        if rpmMode:
            v = 'manual'
            if ini.has_option("main", "rpm_mode"):
                v = ini.get("main", "rpm_mode")
            if v != 'auto':
                print "Config does not specify automatic install during package install."
                print "Please run update_firmware manually to install updates."
                return 0

        print
        print "Searching storage directory for available BIOS updates..."
        r = firmwaretools.repository.Repository( ini.get("main", "storage_topdir") )
        firmwaretools.package.RepositoryPackage.mainIni = ini

        depFailures = {}
        def show_work(*args, **kargs):
            #print "Got callback: %s  %s" % (args, kargs)
            if kargs.get("what") == "found_package_ini":
                p = kargs.get("path")
                if len(p) > 50:
                    p = p[-50:]
                firmwaretools.pycompat.spinPrint("Checking: %s" % p)

            if kargs.get("what") == "fail_dependency_check":
                pkg = kargs.get("package")
                pkgName = "%s-%s" % (pkg.name, pkg.version)
                if pkg.conf.has_option("package", "limit_system_support"):
                    pkgName = pkgName + "-" + pkg.conf.get("package", "limit_system_support")
                kargs.get("cb")[1][pkgName] = (kargs.get("package"), kargs.get("reason"))

        updateSet = firmwaretools.repository.generateUpdateSet(r, firmwaretools.clifuncs.runInventory(ini), cb=(show_work, depFailures) )
        print "\033[2K\033[0G"  # clear line
        needUpdate = 0
        for device in updateSet.iterDevices():
            print "Checking %s - %s" % (str(device), device.version)
            for availPkg in updateSet.iterAvailableUpdates(device):
                print "\tAvailable: %s - %s" % (availPkg.name, availPkg.version)

            pkg = updateSet.getUpdatePackageForDevice(device)
            if pkg is None:
                print "\tDid not find a newer package to install that meets all installation checks."
            else:
                print "\tFound Update: %s - %s" % (pkg.name, pkg.version)
                needUpdate = 1

        if depFailures:
            print
            print "Following packages could apply, but have dependency failures:"

        for pkg, reason in depFailures.values():
            print "\t%s - %s" % (pkg.name, pkg.version)
            print "\t\t REASON: %s" % reason

        if not needUpdate:
            print
            print "This system does not appear to have any updates available."
            print "No action necessary."
            print
            return 1
        else:
            print
            print "Found firmware which needs to be updated."
            print

        # if we get to this point, that means update is necessary.
        # any exit before this point means that there was an error, or no update
        # was necessary and should return non-zero
        if interactive == 2:
            print
            print "Test mode complete."
            print
            return 0

        if interactive == 1:
            print
            print "Please run the program with the '--yes' switch to enable BIOS update."
            print "   UPDATE NOT COMPLETED!"
            print
            return 0


        print "Running updates..."
        for pkg in updateSet.generateInstallationOrder():
            print "Installing %s - %s" % (pkg.name, pkg.version)
            try:
                ret = pkg.install()
            except (firmwaretools.package.NoInstaller,), e:
                print "package %s - %s does not have an installer available." % (pkg.name, pkg.version)
                print "skipping this package for now."
                continue
            except (firmwaretools.package.InstallError,), e:
                print "Installation failed for package: %s - %s" % (pkg.name, pkg.version)
                print "aborting update..."
                print
                print "The error message from the low-level command was:"
                print
                print e
                break

    except CmdlineError, e:
        print
        print e
        print
        print __doc__
        sys.exit(2)

    except (getopt.GetoptError):
        # print help information and exit:
        print __doc__
        sys.exit(2)

    return 0 #shell logic


if __name__ == "__main__":
    sys.exit( main() )

