#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import os
import sys
import json
import optparse
import paramiko
import socket
import httplib2
import time
from datetime import datetime
import traceback
import autotest

class InvalidConfigError(Exception):
    def __init__(self, conf_file, message=''):
        self.conf_file = conf_file
        self.message = message
    
    def __str__(self):
        return 'Invalid config file: %s%s%s' % (self.conf_file, os.linesep, self.message)

class StepsExecutionFailure(Exception):
    def __init__(self, conf_file, message=''):
        self.conf_file = conf_file
        self.message = message
    
    def __str__(self):
        return 'Execute steps failure! config file: %s%s%s' % (self.conf_file, os.linesep, self.message)

class StepsExecutor(object):
    """class docs"""
    CMD_INSTALL_RPMS = 'cd {pkg_root}/pysetup; sh ./pysetup.sh true;sh ./pysetup.sh false'
    CMD_INIT_ENV = '[ -e {pkg_root}/utils/initAppServer.sh ] && {{ cd {pkg_root}/utils; sh ./initAppServer.sh; }}'
    
    CMD_INIT_CSNODE_INFO = 'sed -i \'/cluster_service_nodes/{{n;d;}}\' {pkg_root}/install_sysInfo.json;' + \
            'sed -i \'/cluster_service_nodes/a {{"ip":"{installer_ip}", "user":"cms", "password":"cms1234", "rootPasswd":"root1234"}}\' {pkg_root}/install_sysInfo.json'
    CMD_INIT_APP_INFO = 'sed -i \'/cluster_app_nodes/a {{"ip":"{app_ip}", "user":"cms", "password":"cms1234", "rootPasswd":"root1234"}}\' {pkg_root}/install_sysInfo.json'
    
    CONTENT_CLASS_FILE = '/tmp/contentClassSrc.xml'
    FILE_MONITOR_FILE = '/tmp/cms_file_monitor.json'
    DBITEM_MONITOR_FILE = '/tmp/cms_data_monitor.json'
    IMPORTED_TEMPLATE_INFO = '/tmp/cms_imported_templates_info.json'
    MONITOR_FILES = [CONTENT_CLASS_FILE, FILE_MONITOR_FILE, DBITEM_MONITOR_FILE, IMPORTED_TEMPLATE_INFO]
    
    ACTION_DEPLOY = 'deploy'
    ACTION_UPGRADE = 'upgrade'
    ACTION_ROLLBACK = 'rollback'
    ACTION_EXPORT = 'export'
    ACTION_COMPARE = 'compare'
    ACTION_REFRESH = 'refresh'
    ACTION_APPLYLICENSE = 'applyLicense'
    
    CONSTANT_TRUE = 'true'
    CONSTANT_FALSE = 'false'
    
    def __init__(self, **argsDict):
        self._step_json = argsDict['step_json'] if argsDict['step_json'] else ''
        
        self._pkg_folder = argsDict['pkg_folder'] if argsDict['pkg_folder'] else ''
        self._prepack_name = argsDict['prepack_name'] if argsDict['prepack_name'] else ''
        self._workspace = argsDict['workspace'] if argsDict['workspace'] else ''
        self._app_ip = argsDict['app_ip'] if argsDict['app_ip'] else ''
        self._snapshot_name = argsDict['snapshot_name'] if argsDict['snapshot_name'] else ''
        self._db_domain = argsDict['db_domain'] if argsDict['db_domain'] else ''
        self._app_domain = argsDict['app_domain'] if argsDict['app_domain'] else ''
        
        self._below4x = argsDict['below4x'] if argsDict['below4x'] is not None else False
        self._installer_ip = argsDict['installer_ip'] if argsDict['installer_ip'] else ''
        self._installer_domain = argsDict['installer_domain'] if argsDict['installer_domain'] else ''
        self._db_ip = argsDict['db_ip'] if argsDict['db_ip'] else ''
        self._es_domain = argsDict['es_domain'] if argsDict['es_domain'] else ''
        self._pt_domain = argsDict['pt_domain'] if argsDict['pt_domain'] else ''
        
        self._virt_serv_ip = argsDict['virt_serv_ip'] if argsDict['virt_serv_ip'] else ''
    
    def execute(self):
        with open(self._step_json) as jsonfile:
            try:
                jsonData = json.load(jsonfile)
            except:
                raise InvalidConfigError(os.path.abspath(self._step_json), 'Invalid json format.')
        
        if 'steps' not in jsonData or not jsonData['steps']:
            print('No steps to execute.')
            return True
        
        fs_tailing = datetime.now().strftime('%Y%m%d%H%M%S%f') + str(os.getpid())
        report_file_name = 'cms_compare_report.%s.txt' % fs_tailing
        rpfile_path = os.path.join('/tmp', report_file_name)
        local_rpfile_path = os.path.join(self._workspace, report_file_name)
        
        package_root = '/root/' + self._prepack_name
        
        for step in jsonData['steps']:
            if 'description' in step:
                StepsExecutor.printDescription(step['description'])
            
            if 'action' not in step:
                raise InvalidConfigError(os.path.abspath(self._step_json), 'No action value :%s' % step)
            
            instruction = step['instruction'] if 'instruction' in step else ''
            preconfig = step['preconfig'] if 'preconfig' in step else ''
            
            sshExecuteStatus = 0
            if StepsExecutor.ACTION_DEPLOY == step['action']:
                if not self._below4x:
                    cmd_str = StepsExecutor.CMD_INIT_CSNODE_INFO.format(pkg_root=package_root, installer_ip=self._installer_ip)
                    autotest.exe_generic_remote_cmd(self._app_ip[0], cmd_str)
                autotest.deploy(self._app_ip[0], package_root, instructions=instruction, pre_config=preconfig)
            
            elif StepsExecutor.ACTION_UPGRADE == step['action']:
                # TODO: FOR cms4.0
                autotest.upgrade(self._app_ip[0], package_root, instructions=instruction, pre_config=preconfig)
            
            elif StepsExecutor.ACTION_ROLLBACK == step['action']:
                # TODO: FOR cms4.0
                autotest.rollback(self._app_ip[0], package_root, instructions=instruction, pre_config=preconfig)
            
            elif StepsExecutor.ACTION_APPLYLICENSE == step['action']:
                autotest.apply_license(self._app_ip[0], autotest.CI_SCRIPTS_ROOT + 'autotest/conf/licenses/' + str(step['license']))
            
            elif StepsExecutor.ACTION_EXPORT == step['action']:
                autotest.exe_generic_remote_cmd(self._app_ip[0], StepsExecutor.CMD_INSTALL_RPMS.format(pkg_root=package_root))
                if step.has_key('withImportedTemplate') and step['withImportedTemplate']:
                    autotest.export(self._app_ip[0], package_root, True)
                else:
                    autotest.export(self._app_ip[0], package_root)
            
            elif StepsExecutor.ACTION_COMPARE == step['action']:
                autotest.exe_generic_remote_cmd(self._app_ip[0], StepsExecutor.CMD_INSTALL_RPMS.format(pkg_root=package_root))
                configObj = step["excludeDifferentSet"] if step and step.has_key("excludeDifferentSet") else {}
                configPathOnAppServer = autotest.dump_exclusion_json(configObj, self._app_ip[0])
                # compare with previous status
                if step.has_key('withImportedTemplate') and step['withImportedTemplate']:
                    autotest.compare(self._app_ip[0], package_root, configPathOnAppServer, rpfile_path, True)
                else:
                    autotest.compare(self._app_ip[0], package_root, configPathOnAppServer, rpfile_path)
                
                # save the report to local
                autotest.download_file_to_local(self._app_ip[0], rpfile_path, local_rpfile_path)
            
            elif StepsExecutor.ACTION_REFRESH == step['action']:
                if 'simple' not in step or StepsExecutor.CONSTANT_TRUE != step['simple']:
                    # backup something before refreshing
                    for mnt_file in StepsExecutor.MONITOR_FILES:
                        autotest.download_file_to_local(self._app_ip[0], mnt_file, self._workspace + mnt_file, True)
                
                # do refreshing
                if self._below4x:
                    autotest.refresh_cms3(self._virt_serv_ip, \
                                          self._db_ip, self._db_domain, \
                                          self._app_ip[0], self._app_domain[0], \
                                          self._snapshot_name)
                else:
                    autotest.refresh_cms4(self._virt_serv_ip, \
                                          self._db_ip, self._db_domain, \
                                          self._app_ip, self._app_domain, \
                                          self._snapshot_name, \
                                          self._installer_ip, self._installer_domain, \
                                          self._es_domain, self._pt_domain)
                
                if 'simple' not in step or StepsExecutor.CONSTANT_TRUE != step['simple']:
                    # restore the backup just done
                    autotest.upload_installation_package(self._app_ip[0], self._pkg_folder, self._prepack_name)
                    autotest.exe_generic_remote_cmd(self._app_ip[0], StepsExecutor.CMD_INIT_ENV.format(pkg_root=package_root))
                    for mnt_file in StepsExecutor.MONITOR_FILES:
                        autotest.upload_file_to_remote(self._app_ip[0], self._workspace + mnt_file, mnt_file, True)
            
            else:
                raise InvalidConfigError(os.path.abspath(self._step_json), 'Invalid action value :%s' % step['action'])
            if sshExecuteStatus != 0:
                print 'Encountering problem while executing step: ' + step['description']
                return False
        return True
    
    @staticmethod
    def printDescription(msg):
        print
        print('=' * 50)
        print(msg)
        print('=' * 50)
        print

def buildUpOptions():
    optparser = optparse.OptionParser()
    optparser.add_option("-w", "--ws-this-time",
                         action="store", type="string", dest="workspace",
                         help="workspace for this time, result report is supposed to be saved here, OPTIONAL.")
    optparser.add_option("-s", "--steps",
                         action="store", type="string", dest="stepsJson",
                         help="config json for steps, relative path to current directory, REQUIRED.")
    optparser.add_option("-v", "--virt-server-ip",
                         action="store", type="string", dest="virtServIp",
                         help="IP address of VM server, OPTIONAL.")
    optparser.add_option("-n", "--prepack-name",
                         action="store", type="string", dest="prepackName",
                         help="which package to be installed, single file name, OPTIONAL.")
    optparser.add_option("-S", "--snapshot-name",
                         action="store", type="string", dest="snapshotName",
                         help="which snapshot to revert to, OPTIONAL.")
    optparser.add_option("-b", "--below-4x",
                         action="store_true", dest="below4x", default=False,
                         help="whether below 4x or not, OPTIONAL.")
    optparser.add_option("-i", "--installer-ip",
                         action="store", type="string", dest="installerIp",
                         help="IP address of the installer, OPTIONAL.")
    optparser.add_option("-I", "--installer-domain",
                         action="store", type="string", dest="installerDomain",
                         help="installer-domain to refresh server, OPTIONAL.")
    optparser.add_option("-d", "--db-ip",
                         action="store", type="string", dest="dbIp",
                         help="IP address of the database server, OPTIONAL.")
    optparser.add_option("-D", "--db-domain",
                         action="store", type="string", dest="dbDomain",
                         help="db-domain to refresh server, OPTIONAL.")
    optparser.add_option("-a", "--app-ip",
                         action="append", type="string", dest="appIp",
                         help="IP address of App server, REQUIRED.")
    optparser.add_option("-A", "--app-domain",
                         action="append", type="string", dest="appDomain",
                         help="app-domain to refresh server, OPTIONAL.")
    optparser.add_option("-E", "--es-domain",
                         action="store", type="string", dest="esDomain",
                         help="es-domain to refresh server, OPTIONAL.")
    optparser.add_option("-P", "--pt-domain",
                         action="store", type="string", dest="ptDomain",
                         help="pt-domain to refresh server, OPTIONAL.")
    return optparser

def main():
    """main function."""
    optparser = buildUpOptions()
    (options, args) = optparser.parse_args()
    print("options: {0}".format(options))
    if not options.stepsJson or not options.appIp:
        optparser.print_help()
        optparser.error("Sorry, require more options.")
        return
    
    username = "root"
    password = "root1234"
    pkgFolder = '/var/www/html/rpms/prepack-aio-release-3.5.000'
    
    try:
        if options.prepackName:
            package_root = '/root/' + options.prepackName
            #cmd_str = "cd %s; find . -type f -name '*.sh' | xargs chmod a+x" % package_root
            #autotest.exe_generic_remote_cmd(options.appIp[0], cmd_str)
            autotest.exe_generic_remote_cmd(options.appIp[0], StepsExecutor.CMD_INIT_ENV.format(pkg_root=package_root))
        
        steps_executor = StepsExecutor(step_json=options.stepsJson,
                  prepack_name=options.prepackName, workspace=options.workspace,
                  db_domain=options.dbDomain, app_domain=options.appDomain,
                  app_ip=options.appIp, snapshot_name=options.snapshotName,
                  below4x=options.below4x, installer_ip=options.installerIp,
                  installer_domain=options.installerDomain, db_ip=options.dbIp,
                  es_domain=options.esDomain, pt_domain=options.ptDomain,
                  virt_serv_ip=options.virtServIp, pkg_folder=pkgFolder)
        result = steps_executor.execute()
        if not result:
            print 'Encountering problem while executing Steps.'
            sys.exit(2)
    except Exception:
        print "Unexpected error:" + traceback.format_exc()
        sys.exit(2)
    sys.exit(0)
    

if __name__ == "__main__":
    main()
