#!/usr/libexec/isilon/isi_patch_d
#
# Copyright C 2002-2003 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.
#
# $Id: isi_patch_d 115826 2009-04-21 01:37:53Z cjohnson $
import sys
import os
import isi.app.daemon as daemon
import isi.misc
import isi.ui.lib.pkg as pkg
import isi.sys.update.util as util
import isi.config.ifs as ifs
import isi.sys.version as sysver

logging = util.Logging('isi_pkg', "/var/log/isi_pkg")
logger = logging.get_logger()

def pkg_perform(action, playpan, pkg_name):
    
    if action == "install":
        func_tbl = { "check"    : isi.misc.pkg_install_check,
                     "prep"     : isi.misc.pkg_install,
                     "commit"   : isi.misc.pkg_commit,
                     "rollback" : isi.misc.pkg_rollback}

        type_str = { "present"  : "install",
                     "noun"     : "installation",
                     "past"     : "installed",
                     "future"   : "Installing"}
    else:
        func_tbl = { "check"    : isi.misc.pkg_delete_check,
                     "prep"     : isi.misc.pkg_delete,
                     "commit"   : isi.misc.pkg_dcommit,
                     "rollback" : isi.misc.pkg_drollback}

        type_str = { "present"  : "delete",
                     "noun"     : "deletion",
                     "past"     : "deleted",
                     "future"   : "deleting"}
    
    if os.environ["PKG_OVERRIDE"]:
        logger.info("Participant: Overriding checks.")

    err = isi.misc.pkg_setup(playpan, pkg_name)       
    if (err):
        
        sys.exit(1)

    if (not isi.misc.pkg_participating()):
        logger.info("Participant: dees not participate in '%s'." % pkg_name)
        sys.exit(1)
        
    journal = pkg.Journal()
    timeout = isi.misc.pkg_gettimeout()
    stime = isi.misc.clock_gettime(isi.misc.CLOCK_MONOTONIC)
    node_id = ifs.get()['current node id']

    # TO DO: running in separated process/thread
    if journal.g_state == isi.misc.PKG_GS_COMMIT:
        if (journal.reboot_state != isi.misc.PKG_REBOOT_ROLLING or
            node_id == journal.reboot_node_id or
            action != "install"):
            logger.info("Participant: committing '%s' %s."
                        % (pkg_name, type_str["noun"]))
            err = func_tbl["commit"](playpan, pkg_name)
            if not err:
                logger.info("Participant: '%s' committed." % pkg_name)
                return    
    else:
        logger.info("Participant: Checking '%s' for %s." % (pkg_name, 
                type_str["present"]))
        err = func_tbl["check"](playpan, pkg_name)
            
    if (err):
        logger.error("Participant: %s." % err)
        sys.exit(1)
        
    logger.info("Participant: %s of '%s' is valid." % (type_str["noun"], 
            pkg_name))
    last_node_id = journal.reboot_node_id
    last_g_state = -1
    while True:
        if not journal.read():
            logger.error("Participant: %s." % err)
            sys.exit(1)
        if (journal.g_state == last_g_state):
            err = isi.misc.pkg_monitorjournal()
            if (err):
                sys.exit(1)
            cur = isi.misc.clock_gettime(isi.misc.CLOCK_MONOTONIC)
            if ((cur[0] - stime[0]) > timeout):
                logger.error("Participant: timeout.")
                sys.exit(1)
        elif (journal.g_state == isi.misc.PKG_GS_COMMIT and 
            (journal.reboot_state != isi.misc.PKG_REBOOT_ROLLING or
             node_id == journal.reboot_node_id or
             action != "install")):
            logger.info("Participant: committing '%s' %s."
                        % (pkg_name, type_str["noun"]))
            err = func_tbl["commit"](playpan, pkg_name)
            
            if err:
                logger.error("Participant: %s." % err)
                sys.exit(1)
            logger.info("Participant: '%s' committed." % pkg_name)
            sysver.update_cache()
            break    
        elif journal.g_state == isi.misc.PKG_GS_ROLLBACK:
            logger.info("Participant: rolling back '%s' %s."
                        % (pkg_name, type_str["noun"]))
            err = func_tbl["rollback"](playpan, pkg_name)
            
            if err:
                logger.error("Participant: %s." % err)
                sys.exit(1)
            logger.info("Participant: '%s' is rolled back." % pkg_name)
            break
        elif journal.g_state == isi.misc.PKG_GS_PREP:
            logger.info("Participant: %s '%s'." % (type_str["future"], pkg_name))
            err = func_tbl["prep"](playpan, pkg_name)
            
            if (err):
                logger.error("Participant: %s." % err)
                
                logger.info("Participant: rolling back '%s' %s."
                        % (pkg_name, type_str["noun"]))
                err = func_tbl["rollback"](playpan, pkg_name)
            
                if err:
                    logger.error("Participant: %s." % err)
                    sys.exit(1)
                logger.info("Participant: '%s' is rolled back." % pkg_name)
                break   
            logger.info("Participant: '%s' is %s." % (pkg_name, type_str["past"]))
            last_node_id = journal.reboot_node_id
        else: 
            
            cur = isi.misc.clock_gettime(isi.misc.CLOCK_MONOTONIC)
            if (journal.reboot_state == isi.misc.PKG_REBOOT_ROLLING and
                last_node_id != journal.reboot_node_id):
                last_node_id = journal.reboot_node_id
                stime = cur
                
            
        last_g_state = journal.g_state

if __name__ == '__main__':
    if len(sys.argv) != 5 or not (sys.argv[1] == "install" or \
                                  sys.argv[1] == "uninstall"):
        print >> sys.stderr, "usage: isi_patch_d [install | uninstall] " \
              "[override | no_override] playpan pkg_name"
        sys.exit(1)
    daemon.daemonize()
    if sys.argv[2] == 'override':
        os.environ["PKG_OVERRIDE"] = 'true'
    else:
        os.environ["PKG_OVERRIDE"] = ""
    pkg_perform(sys.argv[1], sys.argv[3], sys.argv[4])
