#!/usr/bin/python

# Imports
import sys
import os
import shutil
import logging
import traceback
import types
import time
import pwd
import yum
from optparse import OptionParser, SUPPRESS_HELP
import basedefs
import output_messages
import common_utils as utils
import basedefs30
import upgrade_utils30 as utils30
import jasper30_utils as jsutils30
import output_messages30

# Consts

# The following constants are used in maintenance mode for
# running things in a loop
MAINTENANCE_TASKS_WAIT_PERIOD  = 180
MAINTENANCE_TASKS_WAIT_PERIOD_MINUTES = MAINTENANCE_TASKS_WAIT_PERIOD / 60
MAINTENANCE_TASKS_CYCLES = 20

#TODO: Work with a real list here
RPM_LIST = """
rhevm
rhevm-backend
rhevm-config
rhevm-dbscripts
rhevm-genericapi
rhevm-notification-service
rhevm-restapi
rhevm-setup
rhevm-tools-common
rhevm-userportal
rhevm-webadmin-portal
rhevm-image-uploader
rhevm-iso-uploader
rhevm-log-collector
vdsm-bootstrap
"""

RPM_BACKEND = "rhevm-backend"
RPM_DBSCRIPTS = "rhevm-dbscripts"
RPM_SETUP = "rhevm-setup"
RPM_UPGRADE = "rhevm-upgrade"

# DB default configuration
SERVER_NAME = basedefs.DB_HOST
SERVER_PORT = basedefs.DB_PORT
SERVER_ADMIN = basedefs.DB_ADMIN

# CONST
BACKUP_FILE = "ovirt-engine_db_backup"
LOG_PATH = "/var/log/ovirt-engine"
LOG_FILE = "ovirt-engine-upgrade.log"

# ASYNC TASKS AND COMPLETIONS QUERIES
ASYNC_TASKS_QUERY = "select action_type from async_tasks;"
ASYNC_TASKS_COUNT_QUERY = "select count(action_type) from async_tasks;"
COMPENSATIONS_QUERY = "select command_type, entity_type from business_entity_snapshot;"
COMPENSATIONS_COUNT_QUERY = "select count(*) from business_entity_snapshot;"

ETL_SERVICE="/etc/init.d/ovirt-engine-etl"

TASKS_CLI_BIN = "/usr/share/ovirt-engine/bin/engine-async-tasks-cli.sh"

# Versions
UNSUPPORTED_VERSION = "2.2"

#MSGS
MSG_CLEAN_ASYNC = "Cleaning async tasks"
MSG_TASKS_COMPENSATIONS = "\n\nSystem Tasks:\n%s\n%s\n\n"
MSG_STOP_RUNNING_TASKS = "\nInfo: The following tasks have been found running \
in the system: %s%sWould you like to proceed and try to stop tasks automatically?\
\n(Answering 'no' will stop the upgrade)"
MSG_RUNNING_TASKS = "Checking active system tasks"
MSG_TASKS_STILL_RUNNING = "\nThere are still running tasks: %sPlease make sure \
that there are no running system tasks before you continue. Please contact GSS \
for assistance. Stopping upgrade."
MSG_WAITING_RUNNING_TASKS = "Still waiting for system tasks to be cleared."
MSG_ERROR_PARSING_TASKS = "Error: failed to parse system tasks."
MSG_ERROR_USER_NOT_ROOT = "Error: insufficient permissions for user %s, you must run with user root."
MSG_NO_ROLLBACK = "Error: Current installation "
MSG_RC_ERROR = "Return Code is not zero"
MSG_INFO_NO_UPGRADE_AVAIL = "No updates available"
MSG_INFO_UPGRADE_AVAIL = "%d Updates available:"
MSG_ERROR_INCOMPATIBLE_UPGRADE = "\nError: a data center or cluster version %s were found on this system.\n\
Such upgrade flow is not supported. Upgrade all %s data centers and clusters and rerun the upgrade utility.\n" \
% (UNSUPPORTED_VERSION, UNSUPPORTED_VERSION)
MSG_ERROR_NO_ROLLBACK_AVAIL = "Error: Installed packages are missing from the yum repositories\n\
Please check your yum repositories or use --no-yum-rollback"
MSG_ERROR_NEW_SETUP_AVAIL="\nError: New %s rpm available via yum.\n\
Please execute `yum update %s`, then re-execute '%s'.\n\
To use the current %s rpm, execute '%s --force-current-setup-rpm'." % (RPM_SETUP, RPM_SETUP, RPM_UPGRADE, RPM_SETUP, RPM_UPGRADE)
MSG_ERROR_BACKUP_DB = "Error: Database backup failed"
MSG_ERROR_RESTORE_DB = "Error: Database restore failed"
MSG_ERROR_DROP_DB = "Error: Database drop failed"
MSG_ERROR_UPDATE_DB = "Error: Database update failed"
MSG_ERROR_RENAME_DB = "Error: Database rename failed. Check that there are no active connections to the DB and try again."
MSG_ERROR_YUM_HISTORY_LIST = "Error: Can't get history from yum"
MSG_ERROR_YUM_HISTORY_GETLAST = "Error: Can't find last install transaction in yum"
MSG_ERROR_YUM_HISTORY_UNDO = "Error: Can't rollback yum"
MSG_ERROR_YUM_LOCK = "Error: Can't edit yum lock file"
MSG_ERROR_EAP6_AVAILABILITY = "Error: jboss-standalone package from EAP6 version is unavailable. \
Can not continue upgrade."
MSG_ERROR_YUM_UPDATE = "Error: Yum update failed"
MSG_ERROR_CHECK_LOG = "Error: Upgrade failed.\nplease check log at %s"
MSG_ERROR_CONNECT_DB = "Error: Failed to connect to database"
MSG_ERR_FAILED_START_ENGINE_SERVICE = "Error: Can't start ovirt-engine"
MSG_ERR_FAILED_ENGINE_SERVICE_STILL_RUN = "Error: Can't stop ovirt-engine service. Please shut it down manually."
MSG_ERR_FAILED_STOP_ENGINE_SERVICE = "Error: Can't stop ovirt-engine"
MSG_ERR_FAILED_STATUS_ENGINE_SERVICE = "Error: Can't get ovirt-engine service status"
MSG_ERR_FAILED_START_SERVICE = "Error: Can't start the %s service"
MSG_ERR_FAILED_STOP_SERVICE = "Error: Can't stop the %s service"
MSG_ERR_SQL_CODE = "Failed running sql query"
MSG_ERR_EXP_UPD_DC_TYPE="Failed updating default Data Center Storage Type in %s db"
MSG_ERROR_ENGINE_PID = "Error: ovirt-engine service is dead, but pid file exists"
MSG_ERROR_YUM_TID = "Error: Yum transaction mismatch"
MSG_ERROR_PGPASS = "Error: DB password file was not found on this system. Verify \
that this system was previously installed and that there's a password file at %s or %s" % \
(basedefs.DB_PASS_FILE, basedefs.ORIG_PASS_FILE)
MSG_ERROR_UUID_VALIDATION_FAILED = (
    "Pre-upgade host UUID validation failed\n"
    "\n"
    "Please move the following hosts to maintenance mode before upgrade:\n"
    "%s"
    "\n"
)
MSG_ERROR_BLOCK_EXPORT_DOMAIN_EXIST_VALIDATION_FAILED = (
    "Error: The current system contains a block (iSCSI/Fibre Channel) Export Storage Domain which is no longer supported.\n"
    "Please remove all such Export Storage Domains before proceeding with the upgrade process.\n\n"
)
MSG_ERROR_UUID_FAILED_HOST = "   - Host %s(%s), reason: %s\n"
MSG_ERROR_UUID_FAILED_REASON_EMPTYUUID = "empty UUID"
MSG_ERROR_UUID_FAILED_REASON_NOUUID = "no BIOS UUID"
MSG_ERROR_UUID_FAILED_REASON_DUPUUID = "duplicate BIOS UUID"

MSG_INFO_DONE = "DONE"
MSG_INFO_ERROR = "ERROR"
MSG_INFO_REASON = " **Reason: %s**\n"
MSG_INFO_STOP_ENGINE = "Stopping ovirt-engine service"
MSG_INFO_STOP_DB = "Stopping DB related services"
MSG_INFO_START_DB = "Starting DB related services"
MSG_INFO_PREUPGRADE = "Pre-upgrade validations"
MSG_INFO_STOP_DB = "Stopping DB related services"
MSG_INFO_BACKUP_DB = "Backing Up Database"
MSG_INFO_RENAME_DB = "Rename Database"
MSG_INFO_RESTORE_DB = "Restore Database name"
MSG_INFO_YUM_UPDATE = "Updating rpms (This may take a while\n\
depending on your connection speed to the repository.\n\
To track the installation process run tail -f /var/log/yum.log in another session).\n\
Note that the transition from package removal to package installation is expected\n\
to take a significant amount of time. No output will appear in the log file during\n\
this transition."
MSG_INFO_DB_UPDATE = "Updating Database"
MSG_INFO_RUN_POST = "Running post install configuration"
MSG_ERROR_UPGRADE = "\n **Error: Upgrade failed, rolling back**"
MSG_INFO_DB_RESTORE = "Restoring Database"
MSG_INFO_YUM_ROLLBACK = "Rolling back rpms"
MSG_INFO_NO_YUM_ROLLBACK = "Skipping yum rollback"
MSG_INFO_START_ENGINE = "Starting ovirt-engine"
MSG_INFO_DB_BACKUP_FILE = "DB Backup available at "
MSG_INFO_LOG_FILE = "Upgrade log available at"
MSG_INFO_CHECK_UPDATE = "\nChecking for updates... (This may take several minutes)"
MSG_INFO_UPGRADE_OK = "%s upgrade completed successfully!" % basedefs.APP_NAME
MSG_INFO_CHECK_UPGRADE = "Checking pre-upgrade conditions...(This may take several minutes)"
MSG_INFO_STOP_INSTALL_EXIT="Upgrade stopped, Goodbye."
MSG_INFO_UPDATE_ENGINE_PROFILE="Updating ovirt-engine Profile"
MSG_INFO_REPORTS_BACKUP="Backing up reports installation"
MSG_INFO_PRE_30_UPGRADE="Preparing system for 3.1 upgrade"
MSG_INFO_RESTORE_30_CONFIGS = "Restoring previous configurations"
MSG_INFO_UUID_VALIDATION_INFO = (
    "NOTICE:\n"
    "\n"
    "  The following hosts must be reinstalled. If reason is BIOS\n"
    "  duplicate UUID execute the following command on each host before\n"
    "  reinstallation:\n"
    "  # uuidgen > /etc/vdsm/vdsm.id\n"
    "  If you are running rhev-h or ovirt-node, restart vdsm-reg service:\n"
    "  # service vdsm-reg restart\n"
    "\n"
    "%s"
    "."
)

MSG_ALERT_STOP_ENGINE="\nDuring the upgrade process, %s  will not be accessible.\n\
All existing running virtual machines will continue but you will not be able to\n\
start or stop any new virtual machines during the process.\n" % basedefs.APP_NAME
INFO_Q_PROCEED="Would you like to proceed"
MSG_INFO_REPORTS="Perform the following steps to upgrade the history service:\n\
1. Execute: yum install rhevm-dwh\n\
2. Execute: rhevm-dwh-setup\n\
\
To also upgrade the reports package, perform the following steps:\n\
3. Execute: yum install rhevm-reports\n\
4. Execute: rhevm-reports-setup"

messages = []
hostids = {}

# Code
def getOptions():
    parser = OptionParser()
    parser.add_option("-r", "--no-yum-rollback",
                      action="store_false", dest="yum_rollback", default=True,
                      help="don't rollback yum transaction")

    parser.add_option("-u", "--unattended",
                      action="store_true", dest="unattended_upgrade", default=False,
                      help="unattended upgrade (this option will stop ovirt-engine service before upgrading)")

    parser.add_option("-s", "--force-current-setup-rpm",
                      action="store_true", dest="force_current_setup_rpm", default=False,
                      help="Ignore new %s rpm"%(RPM_SETUP))

    parser.add_option("-c", "--check-update",
                      action="store_true", dest="check_update", default=False,
                      help="Check for available package updates")

    # Add an option for downloading RPMS for EAP6
    #parser.add_option("-d", "--download-only",
    #                  action="store_false", dest="download_only", default=True,
    #                  help="perform download only for all packages requred for
    #                  installation of RHEV 3.1")

    parser.add_option("-f", "--upgrade-ignore-tasks",
                      action="store_true", dest="ignore_tasks", default=False,
                      help=SUPPRESS_HELP)

    (options, args) = parser.parse_args()
    return (options, args)

def checkEngine(service=basedefs.ENGINE_SERVICE_NAME):
    """
    Ask user to stop ovirt-engine service before
    upgrading ovirt-engine

    returns: true if user choose to stop ovirt-engine
    false otherwise
    """
    logging.debug("checking the status of %s service", basedefs.ENGINE_SERVICE_NAME)
    cmd = [
        basedefs.EXEC_SERVICE, service , "status",
    ]
    output, rc = utils.execCmd(cmdList=cmd, failOnError=False)

    if rc == 0:
        logging.debug("%s service is up and running", basedefs.ENGINE_SERVICE_NAME)

        print MSG_ALERT_STOP_ENGINE
        answer = utils.askYesNo(INFO_Q_PROCEED)

        # If user choose yes -> return true (stop ovirt-engine)
        if answer:
            return True
        else:
            logging.debug("User chose not to stop ovirt-engine")
            return False

    elif rc == 1:
        # Proc is dead, pid exists
        raise Exception(MSG_ERROR_ENGINE_PID)

    elif rc == 3:
        # If ovirt-engine is not running, we don't need to stop it
        return True

    else:
        raise Exception(MSG_ERR_FAILED_STATUS_ENGINE_SERVICE)

def initLogging():
    global LOG_FILE
    try:
        if not os.path.isdir(LOG_PATH):
            os.makedirs(LOG_PATH)
        LOG_FILE = "%s/ovirt-engine-upgrade_%s.log"%(LOG_PATH, utils.getCurrentDateTime())
        level = logging.DEBUG
        # TODO: Move to mode="a"?
        hdlr = logging.FileHandler(filename = LOG_FILE, mode='w')
        fmts='%(asctime)s::%(levelname)s::%(module)s::%(lineno)d::%(name)s:: %(message)s'
        dfmt='%Y-%m-%d %H:%M:%S'
        fmt = logging.Formatter(fmts, dfmt)
        hdlr.setFormatter(fmt)
        logging.root.addHandler(hdlr)
        logging.root.setLevel(level)
    except:
        logging.error(traceback.format_exc())
        raise Exception("Failed to initiate logger")

def _verifyUserPermissions():
    if (os.geteuid() != 0):
        username = pwd.getpwuid(os.getuid())[0]
        print MSG_ERROR_USER_NOT_ROOT%(username)
        sys.exit(1)

def isDBUp():
    """
    check if ovirt-engine db is up
    """
    logging.debug("checking if %s db is already installed and running.."%basedefs.DB_NAME)
    (out, rc) = utils.execSqlCommand(basedefs.DB_ADMIN, basedefs.DB_NAME, "select 1", True)

class MYum():
    def __init__(self):
        self.updated = False
        self.yumbase = None
        self.upackages = []
        self.ipackages = []
        self.__initbase()
        self.tid = None

    def __initbase(self):
        self.yumbase = yum.YumBase()
        self.yumbase.preconf.disabled_plugins = ['versionlock']
        self.yumbase.conf.cache = False # Do not relay on existing cache
        self.yumbase.cleanMetadata()
        self.yumbase.cleanSqlite()

    def _validateRpmLockList(self):
        rpmLockList = []
        for rpmName in basedefs.RPM_LOCK_LIST.split():
            cmd = [
                basedefs.EXEC_RPM, "-q", rpmName,
            ]
            output, rc = utils.execCmd(cmdList=cmd)
            if rc == 0:
                rpmLockList.append(rpmName)

        return rpmLockList

    def _lock(self):
        logging.debug("Yum lock started")

        # Create RPM lock list
        cmd = [
            basedefs.EXEC_RPM, "-q",
        ] + self._validateRpmLockList()
        output, rc = utils.execCmd(cmdList=cmd, failOnError=True, msg=MSG_ERROR_YUM_LOCK)
        with open(basedefs.FILE_YUM_VERSION_LOCK, 'a') as yumlock:
            yumlock.write(output)
        logging.debug("Yum lock completed successfully")

    def _unlock(self):
        logging.debug("Yum unlock started")
        # Read file content
        fd = file(basedefs.FILE_YUM_VERSION_LOCK)
        fileText = fd.readlines()
        fd.close()

        # Change content:
        fd = file(basedefs.FILE_YUM_VERSION_LOCK, 'w')
        for line in fileText:
            if not basedefs.ENGINE_RPM_NAME in line:
                fd.write(line)
        fd.close()
        logging.debug("Yum unlock completed successfully")

    def update(self):
        self.tid = self.getLatestTid(False)
        self._unlock()
        try:
            # yum update ovirt-engine
            # TODO: Run test transaction
            logging.debug("Yum update started")
            cmd = [
                basedefs.EXEC_YUM, "update", "-q", "-y",
            ] + RPM_LIST.split()
            output, rc = utils.execCmd(cmdList=cmd, failOnError=True, msg=MSG_ERROR_YUM_UPDATE)
            logging.debug("Yum update completed successfully")
        finally:
            self._lock()

    def updateAvailable(self):
        logging.debug("Yum list updates started")

        # Get packages info from yum
        rpms = RPM_LIST.split()
        logging.debug("Getting list of packages to upgrade")
        pkgs = self.yumbase.doPackageLists(patterns=rpms)
        upkgs = self.yumbase.doPackageLists(pkgnarrow="updates", patterns=rpms)

        # Save update candidates
        if upkgs.updates:
            self.upackages = [str(i) for i in sorted(upkgs.updates)] # list of rpm names to update
            logging.debug("%s Packages marked for update:"%(len(self.upackages)))
            logging.debug(self.upackages)
        else:
            logging.debug("No packages marked for update")

        # Save installed packages
        self.ipackages = [str(i) for i in sorted(pkgs.installed)] # list of rpm names already installed
        logging.debug("Installed packages:")
        logging.debug(self.ipackages)

        logging.debug("Yum list updated completed successfully")


        # Return
        if upkgs.updates:
            return True
        else:
            return False

    def packageAvailable(self, pkg):
        pkglist = self.yumbase.doPackageLists(patterns=[pkg]).available
        return len(pkglist) > 0

    def packageInstalled(self, pkg):
        pkglist = self.yumbase.doPackageLists(patterns=[pkg]).installed
        return len(pkglist) > 0

    def depListForRemoval(self, pkgs):

        deplist = []

        # Create list of all packages to remove
        pkgs = self.yumbase.doPackageLists(patterns=pkgs).installed
        for pkg in pkgs:
            self.yumbase.remove(name=pkg.name)

        # Resolve dependencies for removing packages
        self.yumbase.resolveDeps()

        # Create a list of deps packages
        for pkg in self.yumbase.tsInfo.getMembers():
            if pkg.isDep:
                deplist.append(pkg.name)

        # Clear transactions from the 'self' object
        self.yumbase.closeRpmDB()

        # Return the list of dependencies
        return deplist

    def rollbackAvailable(self):
        logging.debug("Yum rollback-avail started")

        # Get All available packages in yum
        rpms = RPM_LIST.split()
        pkgs = self.yumbase.pkgSack.returnPackages(patterns=rpms)
        available = [str(i) for i in sorted(pkgs)] # list of available rpm names
        logging.debug("%s Packages available in yum:"%(len(available)))
        logging.debug(available)

        # Verify all installed packages available in yum
        # self.ipackages is populated in updateAvailable
        for installed in self.ipackages:
            if installed not in available:
                logging.debug("%s not available in yum"%(installed))
                return False

        logging.debug("Yum rollback-avail completed successfully")
        return True

    def rollback(self):
        upgradeTid = self.getLatestTid(True)
        if int(upgradeTid) <= int(self.tid):
            logging.error("Mismatch in yum TID, target TID (%s) is not higher than %s" %(upgradeTid, self.tid))
            raise Exception(MSG_ERROR_YUM_TID)

        if self.updated:
            self._unlock()
            try:
                # yum history undo 17
                # Do rollback only if update went well
                logging.debug("Yum rollback started")
                cmd = [
                    basedefs.EXEC_YUM, "history", "-y", "undo", upgradeTid,
                ]
                output, rc = utils.execCmd(cmdList=cmd, failOnError=True, msg=MSG_ERROR_YUM_HISTORY_UNDO)
                logging.debug("Yum rollback completed successfully")
            finally:
                self._lock()
        else:
            logging.debug("No rollback needed")

    def getLatestTid(self, updateOnly=False):
        logging.debug("Yum getLatestTid started")
        tid = None

        # Get the list
        cmd = [
            basedefs.EXEC_YUM, "history", "list", basedefs.ENGINE_RPM_NAME,
        ]
        output, rc = utils.execCmd(cmdList=cmd, failOnError=True, msg=MSG_ERROR_YUM_HISTORY_LIST)

        # Parse last tid
        for line in output.splitlines():
            lsplit = line.split("|")
            if len(lsplit) > 3:
                if updateOnly:
                    if 'Update' in lsplit[3].split() or "U" in lsplit[3].split():
                        tid = lsplit[0].strip()
                        break
                else:
                    if "Action" not in lsplit[3]: # Don't get header of output
                        tid = lsplit[0].strip()
                        break
        if tid is None:
            raise ValueError(MSG_ERROR_YUM_HISTORY_GETLAST)

        logging.debug("Found TID: %s" %(tid))
        logging.debug("Yum getLatestTid completed successfully")
        return tid

    def isCandidateForUpdate(self, rpm):
        candidate = False
        for package in self.upackages:
            if rpm in package:
                candidate = True
        return candidate

    def getUpdateCandidates(self):
        return self.upackages

class DB():
    def __init__(self):
        date = utils.getCurrentDateTime()
        self.sqlfile = "%s/%s_%s.sql" % (basedefs.DIR_DB_BACKUPS, BACKUP_FILE, date)
        self.updated = False
        self.dbrenamed = False
        self.name = basedefs.DB_NAME

    def __del__(self):
        if self.updated:
            logging.debug(MSG_INFO_DB_BACKUP_FILE + self.sqlfile)
            print "* %s %s" % (MSG_INFO_DB_BACKUP_FILE, self.sqlfile)

    def backup(self):
        logging.debug("DB Backup started")
        #cmd = "%s -C -E UTF8 --column-inserts --disable-dollar-quoting  --disable-triggers -U %s -h %s -p %s --format=p -f %s %s"\
            #%(basedefs.EXEC_PGDUMP, SERVER_ADMIN, SERVER_HOST, SERVER_PORT, self.sqlfile, basedefs.DB_NAME)
        cmd = [
            basedefs.EXEC_PGDUMP,
            "-C", "-E", "UTF8",
            "--disable-dollar-quoting",
            "--disable-triggers",
            "-U", SERVER_ADMIN,
            "-h", SERVER_NAME,
            "-p", SERVER_PORT,
            "--format=p",
            "-f", self.sqlfile,
            basedefs.DB_NAME,
        ]
        output, rc = utils.execCmd(cmdList=cmd, failOnError=True, msg=MSG_ERROR_BACKUP_DB, envDict=utils.getPgPassEnv())
        logging.debug("DB Backup completed successfully")

    def restore(self):
        # run psql -U engine -h host -p port -d template1 -f <backup directory>/<backup_file>
        # If DB was renamed, restore it

        if self.updated or self.dbrenamed:
            logging.debug("DB Restore started")

            # If we're here, upgrade failed. Drop temp DB.
            cmd = [
                basedefs.EXEC_DROPDB,
                "-U", SERVER_ADMIN,
                "-h", SERVER_NAME,
                "-p", SERVER_PORT,
                self.name,
            ]
            output, rc = utils.execCmd(cmdList=cmd, failOnError=True, msg=MSG_ERROR_DROP_DB)

            # Restore
            cmd = [
                basedefs.EXEC_PSQL,
                "-U", SERVER_ADMIN,
                "-h", SERVER_NAME,
                "-p", SERVER_PORT,
                "-d", basedefs.DB_TEMPLATE,
                "-f", self.sqlfile,
            ]
            output, rc = utils.execCmd(cmdList=cmd, failOnError=True, msg=MSG_ERROR_RESTORE_DB, envDict=utils.getPgPassEnv())
            logging.debug("DB Restore completed successfully")
        else:
            logging.debug("No DB Restore needed")

    def update(self):
        cwd = os.getcwd()
        os.chdir(basedefs.DIR_DB_SCRIPTS)

        # Make sure we always returning to cwd
        try:
            self.updated = True
            logging.debug("DB Update started")

            # Perform the upgrade
            # ./upgrade.sh -s ${SERVERNAME} -p ${PORT} -u ${USERNAME} -d ${DATABASE};
            dbupgrade = os.path.join(basedefs.DIR_DB_SCRIPTS, basedefs.FILE_DB_UPGRADE_SCRIPT)
            cmd = [
                dbupgrade,
                "-s", SERVER_NAME,
                "-p", SERVER_PORT,
                "-u", SERVER_ADMIN,
                "-d", self.name,
            ]

            # If ignore_tasks supplied, use it:
            if options.ignore_tasks:
                cmd.extend(["-c"])

            output, rc = utils.execCmd(cmdList=cmd, failOnError=True, msg=MSG_ERROR_UPDATE_DB)
            logging.debug("DB Update completed successfully")

        finally:
            os.chdir(cwd)

    def rename(self, newname):
        """ Rename DB from current name to a newname"""

        # Check that newname is different from current
        if self.name == newname:
            return

        # run the rename query and raise Exception on error
        query = "ALTER DATABASE %s RENAME TO %s" % (self.name, newname)
        try:
            utils.execRemoteSqlCommand(SERVER_ADMIN, SERVER_NAME, SERVER_PORT, basedefs.DB_TEMPLATE, query, True, MSG_ERROR_RENAME_DB)
            # set name to the newname
            self.name = newname
            # toggle dbrenamed value to TRUE
            self.dbrenamed = True
        except:
            # if this happened before DB update, remove DB backup file.
            if not self.updated and os.path.exists(self.sqlfile):
                os.remove(self.sqlfile)
            raise

def stopEngine(service=basedefs.ENGINE_SERVICE_NAME):
    logging.debug("stopping %s service.", service)
    cmd = [
        basedefs.EXEC_SERVICE, service, "stop",
    ]
    output, rc = utils. execCmd(cmdList=cmd, failOnError=True, msg=MSG_ERR_FAILED_STOP_ENGINE_SERVICE)

def startEngine(service=basedefs.ENGINE_SERVICE_NAME):
    logging.debug("starting %s service.", service)
    cmd = [
        basedefs.EXEC_SERVICE, service, "start",
    ]
    output, rc = utils.execCmd(cmdList=cmd, failOnError=True, msg=MSG_ERR_FAILED_START_ENGINE_SERVICE)

def restoreConfigs30():
    logging.debug("Restoring configurations from previous version")
    import upgrade_configs30 as configs30
    configs30.run()
    logging.debug("Restoring completed successfully")

def runPost():
    logging.debug("Running post script")
    import post_upgrade as post
    post.run()
    logging.debug("Post script completed successfully")

def runPost30():
    logging.debug("Running post 3.0 configuration script")
    import post_upgrade30 as post
    post.run()
    logging.debug("Post 3.0 configuration script completed successfully")

def runFunc(funcList, dispString):
    print "%s..."%(dispString),
    sys.stdout.flush()
    spaceLen = basedefs.SPACE_LEN - len(dispString)
    try:
        for func in funcList:
            if type(func) is types.ListType:
                func[0](*func[1:])
            else:
                func()
        print ("[ " + utils.getColoredText(MSG_INFO_DONE, basedefs.GREEN) + " ]").rjust(spaceLen)
    except:
        print ("[ " + utils.getColoredText(MSG_INFO_ERROR, basedefs.RED) + " ]").rjust(spaceLen+3)
        raise

def isUpdateRelatedToDb(yumo):
    """
    Verifies current update needs DB manipulation (backup/update/rollback)
    """

    logging.debug("Verifing update is related to db")

    related = False
    for rpm in RPM_BACKEND, RPM_DBSCRIPTS:
        if yumo.isCandidateForUpdate(rpm):
            related = True

    if hostids:
        related = True

    logging.debug("isUpdateRelatedToDb value is %s"%(str(related)))
    return related

def printMessages():
    for msg in messages:
        logging.info(msg)
        print "* %s" % msg.strip()

def addAdditionalMessages(addReports=False):
    global messages
    messages.append(MSG_INFO_LOG_FILE + " " + LOG_FILE)

    if addReports:
        messages.append(MSG_INFO_REPORTS)


def stopDbRelatedServices(etlService, notificationService):
    """
    shut down etl and notifier services
    in order to disconnect any open sessions to the db
    """
    # If the ovirt-engine-etl service is installed, then try and stop it.
    if etlService.isServiceAvailable():
        try:
            etlService.stop(True)
        except:
            logging.warn("Failed to stop %s", etlService.name)
            logging.warn(traceback.format_exc())
            messages.append(MSG_ERR_FAILED_STOP_SERVICE % etlService.name)

    # If the ovirt-engine-notifierd service is up, then try and stop it.
    if notificationService.isServiceAvailable():
        try:
            (status, rc) = notificationService.status()
            if utils.verifyStringFormat(status, ".*running.*"):
                logging.debug("stopping %s service.", notificationService.name)
                notificationService.stop()
        except:
            logging.warn("Failed to stop %s service", notificationService.name)
            logging.warn(traceback.format_exc())
            messages.append(MSG_ERR_FAILED_STOP_SERVICE % notificationService.name)

def startDbRelatedServices(etlService, notificationService):
    """
    bring back any service we stopped
    we won't start services that are down
    but weren't stopped by us
    """
    if etlService.isServiceAvailable():
        (output, rc) = etlService.conditionalStart()
        if rc != 0:
            logging.warn("Failed to start %s", etlService.name)
            messages.append(MSG_ERR_FAILED_START_SERVICE % etlService.name)

    if notificationService.isServiceAvailable():
        (output, rc) = notificationService.conditionalStart()
        if rc != 0:
            logging.warn("Failed to start %s: exit code %d", notificationService.name, rc)
            messages.append(MSG_ERR_FAILED_START_SERVICE % notificationService.name)

def unsupportedVersionsPresent(oldversion=UNSUPPORTED_VERSION, dbName=basedefs.DB_NAME):
    """ Check whether there are UNSUPPORTED_VERSION
    objects present. If yes, throw an Exception
    """
    queryCheckDCVersions="SELECT compatibility_version FROM storage_pool;"
    dcVersions, rc = utils.execRemoteSqlCommand(
        SERVER_ADMIN,
        SERVER_NAME,
        SERVER_PORT,
        dbName,
        queryCheckDCVersions,
        True,
        MSG_ERROR_CONNECT_DB,
    )
    queryCheckClusterVersions="SELECT compatibility_version FROM vds_groups;"
    clusterVersions, rc = utils.execRemoteSqlCommand(
        SERVER_ADMIN,
        SERVER_NAME,
        SERVER_PORT,
        dbName,
        queryCheckClusterVersions,
        True,
        MSG_ERROR_CONNECT_DB
    )

    for versions in dcVersions, clusterVersions:
        if oldversion in versions:
            return True

    return False

def preupgradeExistingSANExportDomainCheck(upgrade_3):
    query="SELECT COUNT(*) FROM storage_domain_static WHERE storage_domain_type = 3 AND storage_type IN (2,3);"
    out, rc = utils.parseRemoteSqlCommand(
        SERVER_ADMIN,
        SERVER_NAME,
        SERVER_PORT,
        basedefs.DB_NAME if not upgrade_3 else basedefs.DB_OLD_NAME,
        query,
        True,
        MSG_ERROR_CONNECT_DB
    )

    count = out[0]['count']

    if count != "0":
        raise Exception(MSG_ERROR_BLOCK_EXPORT_DOMAIN_EXIST_VALIDATION_FAILED)


def preupgradeUUIDCheck(upgrade_3):
    VDSStatus_Maintenance = 2

    query="select vds_id, vds_name, host_name, vds_unique_id, status from vds;"
    out, rc = utils.parseRemoteSqlCommand(
        SERVER_ADMIN,
        SERVER_NAME,
        SERVER_PORT,
        basedefs.DB_NAME if not upgrade_3 else basedefs.DB_OLD_NAME,
        query,
        True,
        MSG_ERROR_CONNECT_DB
    )

    def get_dups(l):
        """python-2.6 has no collections.Counter"""

        _seen = set()
        return [x for x in l if x in _seen or _seen.add(x)]

    dups = get_dups([i['vds_unique_id'].split('_')[0] for i in out])
    ok = True
    msgs = ""
    global hostids
    for r in out:
        warn = False
        if r['vds_unique_id'] == "":
            warn = True
            r['message'] = MSG_ERROR_UUID_FAILED_REASON_EMPTYUUID
        elif r['vds_unique_id'].startswith('_') or r['vds_unique_id'].startswith('Not_'):
            warn = True
            r['message'] = MSG_ERROR_UUID_FAILED_REASON_NOUUID
        elif r['vds_unique_id'].split('_')[0] in dups:
            warn = True
            r['message'] = MSG_ERROR_UUID_FAILED_REASON_DUPUUID

        if warn:
            msgs += MSG_ERROR_UUID_FAILED_HOST % (
                r['vds_name'],
                r['host_name'],
                r['message']
            )
            if int(r['status']) != VDSStatus_Maintenance:
                ok = False
        else:
            if '_' in r['vds_unique_id']:
                hostids[r['vds_id']] = r['vds_unique_id'].split('_')[0]

    if not ok:
        raise Exception(MSG_ERROR_UUID_VALIDATION_FAILED % msgs)

    if msgs != "":
        global messages
        messages.append(MSG_INFO_UUID_VALIDATION_INFO % msgs)

def modifyUUIDs():
    for vds_id, vds_unique_id in hostids.items():
        query="UPDATE vds_static SET vds_unique_id='%s' where vds_id='%s';" % (
            vds_unique_id,
            vds_id
        )
        utils.execRemoteSqlCommand(
            SERVER_ADMIN,
            SERVER_NAME,
            SERVER_PORT,
            basedefs.DB_NAME,
            query,
            True,
            MSG_ERROR_CONNECT_DB
        )

def parseRunningTasks(runningTasks):
    newTasks = ""
    tlist = runningTasks.split('\n')
    tasksList = tlist[2:len(tlist)-1]
    try:
        from async_tasks_map import async_tasks_map as tasksMap
        for taskid in tasksList:
            taskid = taskid.replace(" ", "")
            if taskid in tasksMap.keys():
                newTasks = newTasks + "-----\nTask ID: %s\n" % taskid \
                    + "Task name: %s\n" % tasksMap[taskid][0] \
                    + "Task description: %s\n" % tasksMap[taskid][1]

    except:
        raise(MSG_ERROR_PARSING_TASKS)

    return newTasks

def zombieTasksFound():
    """
    Fetching and cleaning async tasks on 3.0
    """
    cmd = [
        TASKS_CLI_BIN,
        "-z",
    ]

    tasks, rc = utils.execCmd(cmdList=cmd,
                              failOnError=False,
                              msg="Can't get async tasks",
                             )

    if rc > 1:
        raise Exception(output_messages.ERR_CANT_GET_ZOMBIE_TASKS)

    # If tasks has content and rc == 0, return True
    return (tasks and not rc)

def clearZombieTasks():
    """
    Clear zombie tasks on 3.0 system, raise Exception if it fails.
    """
    cmd = [
        TASKS_CLI_BIN,
        "-R",
    ]

    out, rc = utils.execCmd(cmdList=cmd,
                  failOnError=False,
                  msg="Can't clear async tasks",
                 )

    if rc > 1:
        raise Exception(output_messages.ERR_CANT_CLEAR_ZOMBIE_TASKS)

def getRunningTasks(dbName=basedefs.DB_NAME):
    # Check the number of running async tasks first
    cmd = [
        basedefs.EXEC_PSQL,
        "-q",
        "-P", "tuples_only=on",
        "-P", "format=unaligned",
        "-h", SERVER_NAME,
        "-p", SERVER_PORT,
        "-U", SERVER_ADMIN,
        "-d", dbName,
        "-c", ASYNC_TASKS_COUNT_QUERY,
    ]
    runningTasksCount, rc = utils.execCmd(
                                       cmdList=cmd,
                                       failOnError=True,
                                       msg="Can't get async tasks count",
                                       envDict=utils.getPgPassEnv(),
                                   )
    if runningTasksCount and int(runningTasksCount) > 0:
        # Get async tasks:
        runningTasks, rc = utils.execRemoteSqlCommand(
                                        userName=SERVER_ADMIN,
                                        dbHost=SERVER_NAME,
                                        dbPort=SERVER_PORT,
                                        dbName=dbName,
                                        sqlQuery=ASYNC_TASKS_QUERY,
                                        failOnError=True,
                                        errMsg="Can't get async tasks list",
                                    )
        return parseRunningTasks(runningTasks)

    # We only want to return anything if there are really async tasks records
    else:
        return ""

def getCompensations(dbName=basedefs.DB_NAME):
    # Get compensations count first
    cmd = [
        basedefs.EXEC_PSQL,
        "-q",
        "-P", "tuples_only=on",
        "-P", "format=unaligned",
        "-h", SERVER_NAME,
        "-p", SERVER_PORT,
        "-U", SERVER_ADMIN,
        "-d", dbName,
        "-c", COMPENSATIONS_COUNT_QUERY,
    ]
    compensationsCount, rc = utils.execCmd(
                                       cmdList=cmd,
                                       failOnError=True,
                                       msg="Can't get compensations tasks count",
                                       envDict=utils.getPgPassEnv(),
                                    )
    if compensationsCount and int(compensationsCount) > 0:
        # Get compensations
        compensations, rc = utils.execRemoteSqlCommand(
                                        userName=SERVER_ADMIN,
                                        dbHost=SERVER_NAME,
                                        dbPort=SERVER_PORT,
                                        dbName=dbName,
                                        sqlQuery=COMPENSATIONS_QUERY,
                                        failOnError=True,
                                        errMsg="Can't get compensations list",
                                        )
        return compensations

    # We only want to return anything if there are really compensations records
    else:
        return ""

def checkRunningTasks(dbName=basedefs.DB_NAME, service=basedefs.ENGINE_SERVICE_NAME):

    # Check the base version for upgrade
    if dbName == basedefs.DB_OLD_NAME and zombieTasksFound():
        # Now, clean zombie tasks. We assume that the tool works.
        runFunc([clearZombieTasks], output_messages.MSG_CLEAN_ASYNC)

    # Find running tasks first
    logging.debug(MSG_RUNNING_TASKS)
    runningTasks = getRunningTasks(dbName)
    compensations = getCompensations(dbName)
    engineConfigBinary = basedefs.FILE_ENGINE_CONFIG_BIN
    engineConfigExtended = basedefs.FILE_ENGINE_EXTENDED_CONF
    origTimeout = 0

    # Update some consts if running in 3.0 -> 3.1 flow
    if dbName == basedefs.DB_OLD_NAME:
        engineConfigBinary = basedefs30.FILE_RHEVM_CONFIG_BIN
        engineConfigExtended = basedefs30.FILE_RHEVM_EXTENDED_CONF

    # Define state
    in_maintenance = False

    if runningTasks or compensations:

        # TODO: update runningTasks names/presentation and compensations
        timestamp = "\n[ " + utils.getCurrentDateTimeHuman() + " ] "
        stopTasksQuestion = MSG_STOP_RUNNING_TASKS % (
                                MSG_TASKS_COMPENSATIONS % (runningTasks, compensations),
                                timestamp,
                            )
        answerYes = utils.askYesNo(stopTasksQuestion)
        if not answerYes:
            raise Exception(output_messages.INFO_STOP_WITH_RUNNING_TASKS)

        timestamp = "\n[ " + utils.getCurrentDateTimeHuman() + " ] "
        print timestamp + output_messages.INFO_STOPPING_TASKS % MAINTENANCE_TASKS_WAIT_PERIOD_MINUTES

        # Set AsyncZombieTasksTimout and store the original value
        origTimeout = utils.configureTasksTimeout(timeout=0,
                                                  engineConfigBin=engineConfigBinary,
                                                  engineConfigExtended=engineConfigExtended)

        try:

            # Enter maintenance mode
            # Detect 3.0 -> 3.1 upgrade (JBOSS 5)
            if dbName == basedefs.DB_OLD_NAME:
                utils30.configureEngineForMaintenance()
            else:
                utils.configureEngineForMaintenance()

            # Update engine state
            in_maintenance = True

            # Start the engine in maintenance mode
            startEngine(service)

            # Pull tasks in a loop for some time
            # MAINTENANCE_TASKS_WAIT_PERIOD  = 180 (seconds, between trials)
            # MAINTENANCE_TASKS_CYCLES = 20 (how many times to try)
            while runningTasks or compensations:
                time.sleep(MAINTENANCE_TASKS_WAIT_PERIOD)
                runningTasks = getRunningTasks(dbName)
                compensations = getCompensations(dbName)
                logging.debug(MSG_WAITING_RUNNING_TASKS)

                # Show the list of tasks to the user, ask what to do
                if runningTasks or compensations:
                    timestamp = "\n[ " + utils.getCurrentDateTimeHuman() + " ] "
                    stopTasksQuestion = MSG_STOP_RUNNING_TASKS % (
                                         MSG_TASKS_COMPENSATIONS % (runningTasks, compensations),
                                         timestamp,
                                        )
                    answerYes = utils.askYesNo(stopTasksQuestion)

                    # Should we continue? If yes, go for another iteration
                    # If not, break the loop
                    if not answerYes:
                        # There are still tasks running, so exit and tell to resolve
                        # before user continues.
                        RUNNING_TASKS_MSG = MSG_TASKS_COMPENSATIONS % (runningTasks, compensations)
                        raise Exception(MSG_TASKS_STILL_RUNNING % RUNNING_TASKS_MSG)

                    logging.debug(output_messages.INFO_RETRYING + output_messages.INFO_STOPPING_TASKS, MAINTENANCE_TASKS_WAIT_PERIOD_MINUTES)
                    timestamp = "\n[ " + utils.getCurrentDateTimeHuman() + " ] "
                    print timestamp + output_messages.INFO_RETRYING + output_messages.INFO_STOPPING_TASKS % MAINTENANCE_TASKS_WAIT_PERIOD_MINUTES

        except:
            logging.error(traceback.format_exc())
            raise

        finally:
            # Restore previous engine configuration
            # Stop the engine first
            logging.debug(output_messages.INFO_STOP_ENGINE)
            stopEngine(service)

            # Detect 3.0 -> 3.1 upgrade (JBOSS 5)
            logging.debug(output_messages.INFO_RESTORING_NORMAL_CONFIGURATION)
            if in_maintenance:
                if dbName == basedefs.DB_OLD_NAME:
                    utils30.restoreEngineFromMaintenance()
                else:
                    utils.restoreEngineFromMaintenance()

            utils.configureTasksTimeout(timeout=origTimeout,
                                        engineConfigBin=engineConfigBinary,
                                        engineConfigExtended=engineConfigExtended)

def main(options):
    rhyum = MYum()
    db = DB()
    DB_NAME_TEMP = "%s_%s" % (basedefs.DB_NAME, utils.getCurrentDateTime())
    UPGRADE_FROM_30 = utils30.isUpdateFrom30()

    # Handle pgpass
    if not os.path.exists(basedefs.DB_PASS_FILE):
        if not os.path.exists(basedefs.ORIG_PASS_FILE):
            logging.error(MSG_ERROR_PGPASS)
            print MSG_ERROR_PGPASS
            sys.exit(1)
        else:
            logging.info("Info: Found .pgpass file at old location. Moving it to a new location.")

            # Create directory if needed
            dbPassFileDirectory = os.path.dirname(basedefs.DB_PASS_FILE)
            if not os.path.exists(dbPassFileDirectory):
                os.makedirs(dbPassFileDirectory)
            shutil.copy(basedefs.ORIG_PASS_FILE, basedefs.DB_PASS_FILE)

            # Verify ownership and permissions as required by psql.
            os.chmod(basedefs.DB_PASS_FILE, 0600)
            os.chown(basedefs.DB_PASS_FILE, utils.getUsernameId("root"), utils.getGroupId("root"))
    else:
        logging.info("Info: %s file found. Continue.", basedefs.DB_PASS_FILE)

    # Functions/parameters definitions
    currentDbName = basedefs.DB_NAME
    stopEngineService = [stopEngine]
    preupgradeFunc = [[preupgradeUUIDCheck, UPGRADE_FROM_30], [preupgradeExistingSANExportDomainCheck, UPGRADE_FROM_30]]
    startEngineService = [startEngine]
    upgradeFunc = [rhyum.update]
    postFunc = [modifyUUIDs, runPost]
    engineService = basedefs.ENGINE_SERVICE_NAME
    # define db connections services
    etlService = utils.Service(basedefs.ETL_SERVICE_NAME)
    notificationService = utils.Service(basedefs.NOTIFIER_SERVICE_NAME)
    dwh_package = ""
    reports_package = ""

    if UPGRADE_FROM_30:
        # Alert user
        print output_messages30.MSG_INFO_30_DETECTED

        # Update Admin user
        global SERVER_ADMIN
        SERVER_ADMIN = utils.getDbAdminUser()

        # Notify about long time for checking
        print MSG_INFO_CHECK_UPGRADE

        # Check that other software doesn't interrupt with the upgrade
        if rhyum.packageInstalled("ipa-server"):
            print output_messages30.MSG_ERROR_UPGRADE_IPA
            sys.exit(1)

        if rhyum.packageInstalled("redhat-ds"):
            print output_messages30.MSG_ERROR_UPGRADE_RHDS
            sys.exit(1)

        dwhp = "rhevm-reports-dwh"
        if rhyum.packageInstalled(dwhp):
            dwh_package = "-".join([dwhp, utils.getRpmVersion(dwhp)])

        reportsp = "rhevm-reports"
        if rhyum.packageInstalled(reportsp):
            reports_package = "-".join([reportsp, utils.getRpmVersion(reportsp)])

        if not utils30.depsWhiteListed(rhyum):
            print MSG_ERROR_CHECK_LOG%(LOG_FILE)
            sys.exit(1)

        # Check that EAP6 packages are available
        if not rhyum.packageAvailable("jbossas-standalone"):
            print MSG_ERROR_EAP6_AVAILABILITY
            sys.exit(1)

        # Automatic rollback in 3.0 -> 3.1 failure doesn't work yet, so disable
        # the check for it.
        options.yum_rollback = False

        # Functions/parameters update for 3.0 -> 3.1 upgrade
        engineService = basedefs30.ENGINE_SERVICE_NAME_30
        etlService = utils.Service(basedefs30.ETL_SERVICE_NAME)
        notificationService = utils.Service(basedefs30.NOTIFIER_SERVICE_NAME)
        stopEngineService = [[stopEngine, basedefs30.ENGINE_SERVICE_NAME_30]]
        startEngineService = [[startEngine, basedefs30.ENGINE_SERVICE_NAME_30]]
        postFunc = [runPost30] + postFunc
        currentDbName = basedefs.DB_OLD_NAME

    if unsupportedVersionsPresent(dbName=currentDbName):
        print MSG_ERROR_INCOMPATIBLE_UPGRADE
        raise Exception(MSG_ERROR_INCOMPATIBLE_UPGRADE)

    # Check for upgrade, else exit
    print MSG_INFO_CHECK_UPDATE
    if not rhyum.updateAvailable():
        logging.debug(MSG_INFO_NO_UPGRADE_AVAIL)
        print MSG_INFO_NO_UPGRADE_AVAIL
        sys.exit(0)
    else:
        updates = rhyum.getUpdateCandidates()
        print MSG_INFO_UPGRADE_AVAIL % (len(updates))
        for package in updates:
            print " * %s" % package
        if options.check_update:
            sys.exit(100)

    # Check for setup package
    if rhyum.isCandidateForUpdate(RPM_SETUP) and not options.force_current_setup_rpm:
        logging.debug(MSG_ERROR_NEW_SETUP_AVAIL)
        print MSG_ERROR_NEW_SETUP_AVAIL
        sys.exit(3)

    # Make sure we will be able to rollback
    if not rhyum.rollbackAvailable() and options.yum_rollback:
        logging.debug(MSG_ERROR_NO_ROLLBACK_AVAIL)
        print MSG_ERROR_NO_ROLLBACK_AVAIL
        print MSG_ERROR_CHECK_LOG%(LOG_FILE)
        sys.exit(2)

    # If download only was selected, do that.
    #if options.download_only:
    #logging.debug(MSG_INFO_DOWNLOAD_ONLY)
    # do the listing with yum object
    # create cmd for running yumdownloader/yum object download
    # run the command
    # show the message about using the downloaded rpms

    # No rollback in this case
    try:

        # We ask the user before stoping ovirt-engine or take command line option
        if options.unattended_upgrade or checkEngine(engineService):
            # Stopping engine
            runFunc(stopEngineService, MSG_INFO_STOP_ENGINE)
            if isUpdateRelatedToDb(rhyum):
                runFunc([[stopDbRelatedServices, etlService, notificationService]], MSG_INFO_STOP_DB)

            # Check that there are no running tasks/compensations
            if not options.ignore_tasks:
                try:
                    if UPGRADE_FROM_30:
                        checkRunningTasks(dbName=basedefs.DB_OLD_NAME,
                                          service=basedefs30.ENGINE_SERVICE_NAME_30)
                    else:
                        checkRunningTasks()
                # If something went wrong, restart DB services and the engine
                except:
                    runFunc([[startDbRelatedServices, etlService, notificationService]], MSG_INFO_START_DB)
                    runFunc(startEngineService, MSG_INFO_START_ENGINE)
                    raise
        else:
            # This means that user chose not to stop ovirt-engine
            logging.debug("exiting gracefully")
            print MSG_INFO_STOP_INSTALL_EXIT
            sys.exit(0)

        # Preupgrade checks
        runFunc(preupgradeFunc, MSG_INFO_PREUPGRADE)

        # 3.0 Upgrade specific code
        if UPGRADE_FROM_30:
            # Handle Jasper server first
            if utils30.jasperInstalled(SERVER_ADMIN):
                logging.info("Jasper installation found. Backing up.")
                messages.append(MSG_INFO_REPORTS)
                runFunc([jsutils30.backupJsInstallation], MSG_INFO_REPORTS_BACKUP)

            # Now, rhevm
            runFunc([utils30.createDbUser,
                    utils30.backupOldInstallation,
                    [utils30.generateAnswerFile, utils.getDbPassword(SERVER_ADMIN)],
                    [utils30.modifyDb, SERVER_ADMIN, utils.getDbPassword(SERVER_ADMIN)],
                    utils30.renameConfigDirectories], # This should happen always after db rename in modifyDb
                    MSG_INFO_PRE_30_UPGRADE)

            # Update upgradeFunc with the required parameters
            upgradeFunc = [[utils30.upgrade, rhyum, RPM_LIST, rhyum.ipackages, db.sqlfile]]

        # Backup DB
        if isUpdateRelatedToDb(rhyum):
            runFunc([db.backup], MSG_INFO_BACKUP_DB)
            runFunc([[db.rename, DB_NAME_TEMP]], MSG_INFO_RENAME_DB)

    except Exception as e:
        print e
        raise

    # In case of failure, do rollback
    try:
        # yum update
        runFunc(upgradeFunc, MSG_INFO_YUM_UPDATE)

        # If we're here, update/upgrade went fine, so
        rhyum.updated = True

        # Restore configurations from 3.0
        if UPGRADE_FROM_30:
            runFunc([restoreConfigs30], MSG_INFO_RESTORE_30_CONFIGS)

        # check if update is relevant to db update
        if isUpdateRelatedToDb(rhyum):

            # Update the db and restore its name back
            runFunc([db.update], MSG_INFO_DB_UPDATE)
            runFunc([[db.rename, basedefs.DB_NAME]], MSG_INFO_RESTORE_DB)

            # Bring up any services we shut down before db upgrade
            startDbRelatedServices(etlService, notificationService)

        # post install conf
        runFunc(postFunc, MSG_INFO_RUN_POST)

        # start engine
        runFunc([startEngine], MSG_INFO_START_ENGINE)

    except:

        # Save the root cause of the failure, as we will need it later
        # to tell the user and it can be changed in the roll back
        # actions that we are going to try:
        failureReason = sys.exc_info()[1]

        logging.error(traceback.format_exc())
        logging.error("Rolling back update")

        # allow db restore
        if isUpdateRelatedToDb(rhyum):
            try:
                runFunc([db.restore], MSG_INFO_DB_RESTORE)
            except:
                # This Exception have already been logged, so just pass along
                pass

        # allow yum rollback even if db restore failed
        if options.yum_rollback:
            try:
                runFunc([rhyum.rollback], MSG_INFO_YUM_ROLLBACK)
            except:
                # This Exception have already been logged, so just pass along
                pass
        else:
            print MSG_INFO_NO_YUM_ROLLBACK
            logging.debug("Skipping yum rollback")

        # start engine
        try:
            runFunc([startEngine], MSG_INFO_START_ENGINE)
        except:
            # This exception has already been logged, so just pass along
            pass

        # Tell the user that the upgrade failed and what was the root
        # cause of the failure:
        print MSG_ERROR_UPGRADE
        print MSG_INFO_REASON % failureReason

        # If this was an upgrade from 3.0 to 3.1 then give the user the
        # instructions on how to perform the rollback manually, as we
        # don't do anything automatically:
        if UPGRADE_FROM_30:
            print(output_messages30.MSG_ROLLBACK_INSTRUCTIONS %
                  (" ".join(rhyum.ipackages), db.sqlfile))

            if dwh_package:
                print output_messages30.MSG_ERROR_UPGRADE_DWH % dwh_package

            if reports_package:
                print output_messages30.MSG_ERROR_UPGRADE_REPORTS % reports_package

        raise

    # Print log location on success
    addAdditionalMessages(etlService.isServiceAvailable())
    print "\n%s\n" % MSG_INFO_UPGRADE_OK
    printMessages()

if __name__ == '__main__':
    try:
        # Must run as root
        _verifyUserPermissions()

        # Init logging facility
        initLogging()

        # DB Configuration
        SERVER_NAME = utils.getDbHostName()
        SERVER_PORT = utils.getDbPort()
        SERVER_ADMIN = utils.getDbUser()

        # get iso and domain from user arguments
        (options, args) = getOptions()

        main(options)

    except SystemExit:
        raise

    except:
        print MSG_ERROR_CHECK_LOG%(LOG_FILE)
        logging.error(traceback.format_exc())
        sys.exit(1)
