# -*- coding:utf-8 -*-
import utils.common.log as logger
from utils.common.ssh_util import Ssh
from plugins.eBackup.common.dmkTask import DMKTask
from plugins.eBackup.common.util import EbackupConfig
import os
import time
import re
from plugins.eBackup.common.util import Utils
from utils.common.exception import FCUException


class EbackupUpgrader(object):
    def __init__(self, host_ips, db_param_dict, ebackup_type):
        self.__db_param_dict = db_param_dict
        self.__version = self.__db_param_dict['eBackup_Version']
        self.__eBackup_Type = ebackup_type
        self.__host_ips = host_ips
        self.dmk_task = DMKTask(db_param_dict['dmk_floatIp'],
                                db_param_dict['eBackup_dmk_user'],
                                db_param_dict['eBackup_dmk_password'])

    def precheck(self):
        is_true = self._is_all_upgraded()
        if is_true:
            logger.info("All node is upgraded.")
            return True
        return self._deploy_with_dmk("precheck")

    def upgrade(self):
        is_true = self._is_all_upgraded()
        if is_true:
            logger.info("All nodes are upgraded.")
            return True
        self.adjust_ha_relation()
        return self._deploy_with_dmk("upgrade")

    def rollback(self):
        is_true = self._is_all_rollback()
        if is_true:
            logger.info("All nodes are old version,no need to rollback.")
            return True
        return self._deploy_with_dmk("rollback")

    def rollback_node(self, ip):
        ssh_client = None
        try:
            logger.info("Begin to excute rollback force cmd on node " + ip)
            ssh = Ssh()
            rollback_force_cmd = 'cd /home/hcp/;dos2unix rollback_force.sh;' \
                                 'chmod +x rollback_force.sh;nohup sh ' \
                                 'rollback_force.sh %s &' \
                                 % self.__db_param_dict['eBackup_Version']
            file_path = os.path.realpath(__file__ + '/../bin/'
                                                    'rollback_force.sh')
            ssh_client = ssh.ssh_create_client(
                ip, 'hcp', self.__db_param_dict['eBackup_hcp_pwd'])
            ssh.ssh_send_command(ssh_client, 'su - root', 'Password:', 100)
            ssh.ssh_send_command(
                ssh_client, self.__db_param_dict['eBackup_root_pwd'], '#', 100)
            ssh.ssh_send_command(
                ssh_client, 'rm -rf /home/hcp/rollback_force.sh', '#', 100)
            ret = ssh.put_file(
                ip, "hcp", self.__db_param_dict["eBackup_hcp_pwd"],
                file_path, "/home/hcp/")
            if not ret:
                logger.error("Upload %s to host(%s) failed." % (file_path, ip))
                return False
            logger.info("Upload %s to host %s successfully." % (file_path, ip))
            ssh.ssh_send_command(
                ssh_client, rollback_force_cmd, 'nohup.out', 100)

            logger.info("Excute rollback cmd on %s successfully." % ip)
            return True
        except Exception as e:
            logger.error("rollback force failed,The reason is:" + str(e))
            return False
        finally:
            Utils.close_ssh_clinet(ssh_client)

    def check_service_status(self, ip):
        ssh_client = None
        check_rollback_status_cmd = 'ps -ef |grep "rollback_force" | ' \
                                    'grep -v grep >/dev/null 2>&1 || ' \
                                    'echo "successfully"'
        start_service_cmd = 'nohup service hcp start force &'
        check_status_cmd = 'service hcp status >/dev/null 2>&1 && ' \
                           'echo "successfully"'
        try:
            logger.info("Begin to check service status at " + ip)
            ssh = Ssh()
            ssh_client = ssh.ssh_create_client(
                ip, "hcp", self.__db_param_dict['eBackup_hcp_pwd'])
            ssh.ssh_send_command(
                ssh_client, 'su - root', 'Password:', 100)
            ssh.ssh_send_command(
                ssh_client, self.__db_param_dict['eBackup_root_pwd'], '#', 100)
            # check rollback completed
            count = 1
            # 50 * 15 = 750s
            flag = False
            while count < 50:
                ret = ssh.ssh_exec_command_return_list(
                    ssh_client, check_rollback_status_cmd)
                if -1 != str(ret).find("successfully"):
                    flag = True
                    break
                logger.info(
                    "rollback is still going at the %sst time on %s." %
                    (count, ip))
                count += 1
                time.sleep(15)
            if not flag:
                logger.error("maybe rollback failed at %s." % ip)

            ssh.ssh_send_command(ssh_client, start_service_cmd, '#', 100)
            count = 1
            # 50 * 15 = 750s
            flag = False
            while count < 50:
                ret = ssh.ssh_exec_command_return_list(
                    ssh_client, check_status_cmd)
                if -1 != str(ret).find("successfully"):
                    flag = True
                    break
                logger.info("Service is still starting at the %sst time on %s."
                            % (count, ip))
                count += 1
                time.sleep(15)

            if not flag:
                logger.error("Start Service failed at %s." % ip)
            logger.info("Service status is normal on now." + str(ip))
            return True
        except Exception as e:
            logger.error("Check service status failed.The reason is:" + str(e))
            return False
        finally:
            Utils.close_ssh_clinet(ssh_client)

    def is_need_rollback(self):
        logger.info("Begin to check whether need to rollback force.")
        check_version_cmd = '''grep "System Version" /opt/huawei-data-''' \
                            '''protection/ebackup/conf/versions.conf | ''' \
                            '''awk -F "=" '{print $2}' '''
        check_status_cmd = 'service hcp status >/dev/null 2>&1 && ' \
                           'echo "successfully"'
        ssh_client = None
        ssh = Ssh()
        need_rollback = False
        all_version = []
        for ip in self.__host_ips:
            try:
                ssh_client = ssh.ssh_create_client(
                    ip, "hcp", self.__db_param_dict['eBackup_hcp_pwd'])
                ssh.ssh_send_command(ssh_client, 'su - root', 'Password:', 100)
                ssh.ssh_send_command(
                    ssh_client, self.__db_param_dict['eBackup_root_pwd'],
                    '#', 100)
                ret = ssh.ssh_exec_command_return_list(
                    ssh_client, check_version_cmd)
                version = ret[0].strip().replace("\n", "")
                logger.info("The version is %s of node[%s]." % (version, ip))
                if version not in all_version:
                    all_version.append(version)
                if len(all_version) >= 2:
                    need_rollback = True
                    logger.info("There are different versions %s in cluster "
                                "%s,need to rollback." %
                                (str(all_version), str(self.__host_ips)))
                    break

                ret = ssh.ssh_exec_command_return_list(ssh_client,
                                                       check_status_cmd)
                if -1 == str(ret).find("successfully"):
                    need_rollback = True
                    logger.info("The service status is abnormal on node[%s],"
                                "need to rollback force." % ip)
                    break
                logger.info("The service is running on %s" % ip)
            except Exception as e:
                logger.error("Check service status and version failed."
                             "The reason is:" + str(e))
                raise e
            finally:
                Utils.close_ssh_clinet(ssh_client)

        logger.info("Check whether need to rollback "
                    "force,the result: " + str(need_rollback))
        return need_rollback

    def stop_all_nodes(self, stop_nodes_sequence):
        ssh_client = None
        stop_service_cmd = 'service hcp stop force >/dev/null 2>&1'
        remove_script_cmd = 'rm -f /home/hcp/rollback_force.sh'
        ssh = Ssh()
        logger.info("Begin to stop all node.")
        for ip in stop_nodes_sequence:
            try:
                ssh_client = ssh.ssh_create_client(
                    ip, "hcp", self.__db_param_dict['eBackup_hcp_pwd'])
                ssh.ssh_send_command(ssh_client, 'su - root', 'Password:', 100)
                ssh.ssh_send_command(
                    ssh_client, self.__db_param_dict['eBackup_root_pwd'],
                    '#', 100)
                ssh.ssh_send_command(ssh_client, remove_script_cmd, '#', 180)
                ssh.ssh_send_command(ssh_client, stop_service_cmd, '#', 180)
            except Exception as e:
                logger.debug("Exception occurs when stop service:" + str(e))
            finally:
                Utils.close_ssh_clinet(ssh_client)
        logger.info("All node stoped.")
        return True

    def rollback_force(self):
        logger.info("Begin to rollback:" + str(self.__host_ips))
        # step1:check whether to rollback force
        is_true = self.is_need_rollback()
        if not is_true:
            logger.info("All node[%s] have been rollbacked and "
                        "status is normal,No need to rollback force." %
                        str(self.__host_ips))
            return True
        # step2:stop service
        primary_ip = self.find_primary_node()
        if 0 == len(primary_ip):
            logger.error("Find primary node failed.")
            return False
        stop_service_sequence = self.__host_ips
        stop_service_sequence.remove(primary_ip)
        stop_service_sequence.append(primary_ip)
        self.stop_all_nodes(stop_service_sequence)
        # step2:rollback and start primary node
        is_true = self.rollback_node(primary_ip)
        if not is_true:
            logger.error("Excute rollback operation "
                         "failed on " + str(primary_ip))
            return False
        logger.info("Begin to sleep 200s to wait the primary starting.")
        time.sleep(200)
        is_true = self.check_service_status(primary_ip)
        if not is_true:
            logger.error("Start service failed on " + str(primary_ip))
            return False

        # step3:rollback and start standby and proxy node
        standby_and_proxy = self.__host_ips
        standby_and_proxy.remove(primary_ip)
        for ip in standby_and_proxy:
            is_true = self.rollback_node(ip)
            if not is_true:
                logger.error("Excute rollback operation failed on " + str(ip))
                return False
        logger.info("Begin to sleep 30s to wait the standby and "
                    "proxy starting.")
        time.sleep(30)
        for ip in standby_and_proxy:
            is_true = self.check_service_status(ip)
            if not is_true:
                logger.error("Start service failed on " + str(ip))
                return False
        logger.info("Rollback %s force successfully" % str(self.__host_ips))
        return True

    def find_primary_node(self):
        try:
            logger.info("Begin to find the primary node.")
            ssh = Ssh()
            primary_node_ip = ""
            find_primary_node = 'cat /var/ebackup_bak/ebackup/ha_current_role'
            for ip in self.__host_ips:
                ssh_client = ssh.ssh_create_client(ip, 'hcp',
                                                   self.__db_param_dict[
                                                       'eBackup_hcp_pwd'])
                ssh.ssh_send_command(ssh_client, 'su - root', 'Password:', 100)
                ssh.ssh_send_command(ssh_client,
                                     self.__db_param_dict['eBackup_root_pwd'],
                                     '#', 100)
                result = ssh.ssh_exec_command_return_list(ssh_client,
                                                          find_primary_node)
                ha_role = result[0].strip().replace('\n', '')
                if "0" == ha_role:
                    primary_node_ip = ip
                    logger.info("The primary ip is:" + ip)
                    break
            if 0 == len(primary_node_ip):
                logger.error("Find primary node failed.")
            logger.info(
                "Find primary node successfully,the primary "
                "ip is:" + primary_node_ip)
            return primary_node_ip
        except Exception as e:
            logger.error("Find primary node failed.The reason is:" + str(e))
            return ""

    def _is_current_version(self, server_ip):
        ssh_client = None
        try:
            account_hcp = "hcp"
            account_hcp_passwd = self.__db_param_dict['eBackup_hcp_pwd']
            account_root_passwd = self.__db_param_dict['eBackup_root_pwd']
            version_check_cmds = '''grep "System Version" /opt/huawei-data''' \
                                 '''-protection/ebackup/conf/versions.conf '''\
                                 '''| awk -F "=" '{print $2}' '''
            ssh = Ssh()
            ssh_client = ssh.ssh_create_client(server_ip, account_hcp,
                                               account_hcp_passwd)
            ssh.ssh_send_command(ssh_client, 'su - root', 'Password:', 100)
            ssh.ssh_send_command(ssh_client, account_root_passwd, '#', 100)
            result = ssh.ssh_exec_command_return_list(ssh_client,
                                                      version_check_cmds)

            logger.info(
                "eBackup node " + server_ip + " version is " + result[0])
            version = result[0].strip().replace('\n', '')
            if version == self.__db_param_dict['eBackup_Version']:
                return True
            else:
                return False
        except Exception as e:
            raise e
        finally:
            Utils.close_ssh_clinet(ssh_client)

    def _is_all_upgraded(self):
        try:
            for server_ip in self.__host_ips:
                is_true = self._is_current_version(server_ip)
                if not is_true:
                    return False
            return True
        except Exception as e:
            raise e

    def _is_all_rollback(self):
        try:
            for server_ip in self.__host_ips:
                is_true = self._is_current_version(server_ip)
                if is_true:
                    return False
            return True
        except Exception as e:
            raise e

    def _deploy_with_dmk(self, action):

        ebackup_config, ebackup_host = self.get_all_params()

        # install eBackup
        ebackup_action = ""
        if action == "precheck":
            ebackup_action = "[upgrade]1.Precheck eBackup"
        elif action == "upgrade":
            ebackup_action = "[upgrade]2.Upgrade eBackup"
        elif action == "rollback":
            ebackup_action = "[rollback]1.Rollback eBackup"

        result = self.dmk_task.do_task(self.__version, ebackup_action,
                                       ebackup_host, ebackup_config, "hcp")
        if not result:
            logger.error("Do DMK task %s failed." % ebackup_action)

        return result

    def get_all_params(self):
        config = EbackupConfig(self.__host_ips, self.__db_param_dict,
                               self.__eBackup_Type)
        return config.get_config()

    def adjust_ha_relation(self):
        try:
            primary_ip = ''
            standby_ip = ''
            change_ha_cmd = 'export LD_LIBRARY_PATH=' \
                            '/opt/huawei-data-protection/ebackup/libs/ && ' \
                            '/opt/huawei-data-protection/ebackup/ha/module' \
                            '/hacom/tools/ha_client_tool --switchover ' \
                            '--name=product'
            query_ha_role = 'sh /opt/huawei-data-protection/ebackup' \
                            '/bin/config_omm_ha.sh query|' \
                            r'grep "HaLocalName"|grep -E -o "\(.*\)";' \
                            'cat /tmp/upgrade/ha_current_role'

            hcp_pwd = self.__db_param_dict['eBackup_hcp_pwd']
            root_pwd = self.__db_param_dict['eBackup_root_pwd']
            is_ha_changed = False
            logger.info("Begin to check if ha has been changed "
                        "after prechecking.")

            for ip in self.__host_ips:
                ssh_client = Utils.get_ssh_client(ip, 'hcp', hcp_pwd, root_pwd)
                result = Ssh.ssh_exec_command_return_list(ssh_client,
                                                          query_ha_role)
                rule = re.compile(r'[(](.*?)[)]', re.S)
                ha_role = re.findall(rule, result[0])[0]
                ha_current_role = result[1].strip().replace('\n', '')
                if ha_role == 'active':
                    primary_ip = ip
                    if ha_current_role == '0':
                        logger.info("Ha has not been changed after "
                                    "prechecking.")
                        Utils.close_ssh_clinet(ssh_client)
                        return True
                    else:
                        logger.info("Ha has been changed after prechecking.")
                        is_ha_changed = True

                elif ha_role == 'standby':
                    standby_ip = ip
                    if ha_current_role == '1':
                        logger.info(
                            "Ha has not been changed after prechecking.")
                        Utils.close_ssh_clinet(ssh_client)
                        return True
                    else:
                        logger.info("Ha has been changed after prechecking.")
                        is_ha_changed = True
                Utils.close_ssh_clinet(ssh_client)
            logger.info("Ha has beend changed,Now we need "
                        "to restore the ha relationship.")
            if is_ha_changed and primary_ip != '' and standby_ip != '':
                ssh_client = Utils.get_ssh_client(primary_ip, 'hcp',
                                                  hcp_pwd, root_pwd)
                Ssh.ssh_exec_command_return_list(ssh_client, change_ha_cmd)
                Utils.close_ssh_clinet(ssh_client)
                logger.info("Excute the command to change ha successfully."
                            "Now sleep 2min to wait for it completed.")
                time.sleep(120)
                for i in range(0, 12):
                    ssh_client = Utils.get_ssh_client(
                        standby_ip, 'hcp', hcp_pwd, root_pwd)
                    result = Ssh.ssh_exec_command_return_list(ssh_client,
                                                              query_ha_role)
                    Utils.close_ssh_clinet(ssh_client)
                    rule = re.compile(r'[(](.*?)[)]', re.S)
                    ha_role = re.findall(rule, result[0])[0]
                    if ha_role == 'active':
                        logger.info("Ha has been changed completed.")
                        return True
                    time.sleep(10)
                logger.error("Changed the HA timeout.")
                raise FCUException(650039, primary_ip + ',' + standby_ip)

        except FCUException as e:
            logger.error("Restore Ha failed.")
            raise e
        except Exception as e:
            logger.error("Adjust Ha failed, the reason is:" + str(e))
            raise e
