#!/usr/bin/env python
# -*-coding:utf-8-*-
import traceback
import os
import sys
if sys.version_info[0] > 2:
    import subprocess as cmd_exec
else:
    import commands as cmd_exec

from upgrade.upgrade_base import UpgradeBase
from upgrade.upgrade_base import UpgBaseErrCode
from upgrade.upgrade_base import ResponseBody


class FscCliUpgrade(UpgradeBase):
    def __init__(self):
        super(FscCliUpgrade, self).__init__()
        self.base_backup_dir = '%(basedir)s/fsc-cli/%(component)s' % {
            "basedir": self.UPG_TMP_ROOT_PATH,
            "component": self.str_template_name}
        self.src_backup_dir = '%(base_backup_dir)s/%(version)s' % {
            "base_backup_dir": self.base_backup_dir,
            "version": self.str_sourceversion.replace(' ', '_')}
        self.dst_backup_dir = '%(base_backup_dir)s/%(version)s' % {
            "base_backup_dir": self.base_backup_dir,
            "version": self.str_targetversion.replace(' ', '_')}
        self.back_file = ['client_self.keystore', 'client_trust.keystore',
                          'configure.properties', 'dsware-api.properties',
                          'fsa_server.key', 'manager-ssl.properties', 'cert']

    def check(self):
        self.log_upgrade_info(
            "start to check the component:%s, "
            "the source version is:%s, "
            "the target version is :%s, "
            "the operation type is:%s" % (
                self.str_template_name, self.str_sourceversion,
                self.str_targetversion, self.str_optype))
        if super(FscCliUpgrade, self).check().code != UpgBaseErrCode.UPG_COMM_OK:
            self.log_upgrade_error("call the super check method failed.")
            return ResponseBody(UpgBaseErrCode.UPG_COMM_PROC_ABNORMAL_BEF)
        self.log_upgrade_info(
            "success to check the component:%s" % self.str_template_name)
        return ResponseBody(UpgBaseErrCode.UPG_COMM_OK)

    def prepare(self, cannot_rebuild_list=[], cannot_download_by_zypper={}):
        self.log_upgrade_info(
            "start to prepare the component:%s, the operation type is:%s" %
            (self.str_template_name, self.str_optype))
        if super(FscCliUpgrade, self).prepare(cannot_rebuild_list,
                                              cannot_download_by_zypper).code != UpgBaseErrCode.UPG_COMM_OK:
            self.log_upgrade_error("call the super prepare method failed.")
            return ResponseBody(UpgBaseErrCode.UPG_COMM_RPM_DOWNLOAD)
        if self.str_optype == "upgrade":
            # 升级操作时进行本地文件备份
            if not self.create_backup_dir():
                self.log_upgrade_error("create backup dir failed.")
                return ResponseBody(UpgBaseErrCode.UPG_COMM_DATA_BACKUP)
            if not self.backup_config_files():
                self.log_upgrade_error("backup cert file failed.")
                return ResponseBody(UpgBaseErrCode.UPG_COMM_DATA_BACKUP)
        self.log_upgrade_info(
            "success to prepare the component:%s" % self.str_template_name)
        return ResponseBody(UpgBaseErrCode.UPG_COMM_OK)

    def pre_execute(self):
        self.log_upgrade_info(
            "start to pre_execute the component:%s, the operation type is:%s" %
            (self.str_template_name, self.str_optype))
        if super(FscCliUpgrade,
                 self).pre_execute().code != UpgBaseErrCode.UPG_COMM_OK:
            self.log_upgrade_error("call the super pre_execute failed.")
            return ResponseBody(UpgBaseErrCode.UPG_COMM_PROC_STOP)
        self.log_upgrade_info(
            "success to pre_execute the component:%s" % self.str_template_name)
        return ResponseBody(UpgBaseErrCode.UPG_COMM_OK)

    def execute(self):
        self.log_upgrade_info(
            "start to execute the component:%s, the operation type is:%s" %
            (self.str_template_name, self.str_optype))
        if self.upgrade_process_pkg().code != UpgBaseErrCode.UPG_COMM_OK:
            self.log_upgrade_error(
                "call the super method install the package failed.")
            return ResponseBody(UpgBaseErrCode.UPG_COMM_RPM_INSTALL)
        self.log_upgrade_info(
            "success to execute the component:%s" % self.str_template_name)
        # 升级操作时进行本地文件恢复
        if not self.undo_config_files():
            self.log_upgrade_error("restore cert failed.")
            return ResponseBody(UpgBaseErrCode.UPG_COMM_DATA_BACKUP)
        if self.str_optype == "upgrade":
            # spc20以下版本升级22.1，fsm证书加解密方式不一致，需要更新加密密码
            self.update_dsware_file()
        if not self.restart_fsc_process():
            self.log_upgrade_error("restart FSC failed.")
            return ResponseBody(UpgBaseErrCode.UPG_COMM_DATA_BACKUP)
        return ResponseBody(UpgBaseErrCode.UPG_COMM_OK)

    def post_execute(self):
        self.log_upgrade_info(
            "start to post_execute the component:%s, the operation type is:%s" %
            (self.str_template_name, self.str_optype))
        if super(FscCliUpgrade,
                 self).post_execute().code != UpgBaseErrCode.UPG_COMM_OK:
            self.log_upgrade_error("call the super post_execute method failed.")
            return ResponseBody(UpgBaseErrCode.UPG_COMM_PROC_START)
        self.log_upgrade_info(
            "success to post_execute the component:%s" % self.str_template_name)
        return ResponseBody(UpgBaseErrCode.UPG_COMM_OK)

    def effect(self):
        self.log_upgrade_info(
            "start to effect the component:%s, the operation type is:%s" %
            (self.str_template_name, self.str_optype))
        if super(FscCliUpgrade,
                 self).effect().code != UpgBaseErrCode.UPG_COMM_OK:
            self.log_upgrade_error("call the super effect method failed.")
            return ResponseBody(UpgBaseErrCode.UPG_COMM_INTERNAL)
        self.log_upgrade_info(
            "success to effect the component:%s" % self.str_template_name)
        return ResponseBody(UpgBaseErrCode.UPG_COMM_OK)

    def finish(self):
        self.log_upgrade_info(
            "start to finish the component:%s, the operation type is:%s" %
            (self.str_template_name, self.str_optype))
        if super(FscCliUpgrade,
                 self).finish().code != UpgBaseErrCode.UPG_COMM_OK:
            self.log_upgrade_error("call the super finish method failed.")
            return ResponseBody(UpgBaseErrCode.UPG_COMM_DATA_BACKUP)
        if not self.clean_backup_file():
            self.log_upgrade_error("clean the backup file failed.")
            return ResponseBody(UpgBaseErrCode.UPG_COMM_DATA_BACKUP)
        self.log_upgrade_info(
            "success to finish the component:%s" % self.str_template_name)
        return ResponseBody(UpgBaseErrCode.UPG_COMM_OK)

    def create_backup_dir(self):
        if os.path.exists(self.src_backup_dir):
            return True
        try:
            os.makedirs(self.src_backup_dir)
        except Exception as e:
            self.log_upgrade_error(
                "create backup dir:%s occured exception:%s" % (self.src_backup_dir, e))
            return False
        return True

    def backup_config_files(self):
        """
        备份证书及配置文件/usr/bin/conf或者/usr/bin/conf/cert目录下
        :return:
        """
        for file in self.back_file:
            src_file = os.path.join("/usr/bin/conf/", file)
            if os.path.exists(src_file):
                try:
                    cmd = "cp -rf --preserve %s %s" % (src_file, self.src_backup_dir)
                    ret, out = cmd_exec.getstatusoutput(cmd)
                    if ret != 0:
                        self.log_upgrade_error(
                            'backup %s fail,the result is :%s, the reason is:%s' % (src_file, ret, out))
                        return False
                    else:
                        self.log_upgrade_debug("backup %s successful" % src_file)
                except Exception as e:
                    self.log_upgrade_error("backup %s fail %s,exception:%s"
                                           % (src_file,
                                              str(traceback.format_exc()), e))
                    return False
        return True

    def undo_config_files(self):
        """
        恢复证书及配置文件/usr/bin/conf或者/usr/bin/conf/cert目录下
        :return:
        """
        backup_dir = self.src_backup_dir
        if self.str_optype == "rollback":
            backup_dir = self.dst_backup_dir
        if not os.path.exists("/usr/bin/conf/"):
            # 老版本回退后没有/usr/bin/conf目录
            os.mkdir("/usr/bin/conf/")
            os.chmod("/usr/bin/conf/", 0o750)
        for file in self.back_file:
            src_file = os.path.join("/usr/bin/conf/", file)
            back_file = os.path.join(backup_dir, file)
            if os.path.exists(back_file):
                try:
                    cmd = "cp -rf --preserve %s %s" % (back_file, "/usr/bin/conf/")
                    ret, out = cmd_exec.getstatusoutput(cmd)
                    if ret != 0:
                        self.log_upgrade_error(
                            'Restore %s fail,the result is :%s, the reason is:%s' % (src_file, ret, out))
                        return False
                    else:
                        self.log_upgrade_debug("Restore %s successful" % src_file)
                except Exception as e:
                    self.log_upgrade_error("Restore %s fail %s,exception:%s"
                                           % (src_file,
                                              str(traceback.format_exc()), e))
                    return False
        return True

    def update_dsware_file(self):
        self.log_upgrade_info("Update dsware api file start.")
        dsware_file_path = "/usr/bin/conf/dsware-api.properties"
        manager_file_path = "/usr/bin/conf/manager-ssl.properties"
        with open(dsware_file_path, 'r') as f:
            dsware_file = f.read()
        with open(manager_file_path, 'r') as f:
            manager_ssl_file = f.read()
        dsware_items = dsware_file.split("\n")
        manager_items = manager_ssl_file.split("\n")
        key_list = ("ssl.self.ks.passwd=", "ssl.self.cert.passwd=", 'ssl.trust.ks.passwd=')
        need_modify_content = [item for item in manager_items if item.startswith(key_list)]
        for index, item in enumerate(dsware_items):
            if item.startswith(key_list):
                _start = item.split('=')[0]
                dsware_items[index] = [new_content for new_content in need_modify_content
                                       if new_content.startswith(_start)][0]
        new_dsware_file_content = "\n".join(dsware_items)
        with open(dsware_file_path, 'w') as f:
            f.write(new_dsware_file_content)
        self.log_upgrade_info("Update dsware api file success.")

    def restart_fsc_process(self):
        """
        重启FSCTools进程
        """
        try:
            cmd = "ps -ef | grep -v grep | grep FSCTools | awk '{print $2}'"
            ret, out = cmd_exec.getstatusoutput(cmd)
            if ret != 0:
                self.log_upgrade_error(
                    'Restart FSCTools fail,the result is :%s, the reason is:%s' % (ret, out))
                return False
            if out == '':
                return True
            pod_ids = out.strip("\n|' '").split("\n")
            for pod_id in pod_ids:
                cmd = "kill -9 %s" % pod_id
                ret, out = cmd_exec.getstatusoutput(cmd)
                if ret != 0:
                    self.log_upgrade_error(
                        'Restart FSCTools fail,the result is :%s, the reason is:%s' % (ret, out))
                    return False
        except Exception as e:
            self.log_upgrade_error("Restart FSCTools fail,the result is : %s" % e)
        self.log_upgrade_info("Restart FSCTools successful")
        return True

    def clean_backup_file(self):
        cmd = "rm -rf %s" % self.base_backup_dir
        ret, out = cmd_exec.getstatusoutput(cmd)
        self.log_upgrade_info(
            "the clean info is :command:[%s],result:[%s],output:[%s]" % (cmd, ret, out))
        if ret != 0:
            self.log_upgrade_error("run command [%s] failed, "
                                   "the reason is [%s],so do not finish"
                                   % (cmd, out))
            return False
        return True


def main():
    upgrade = FscCliUpgrade()
    upgrade.process_upgrade()


if __name__ == "__main__":
    main()
