# -*- coding:utf-8 -*-
import os
import re
import secrets
import paramiko
import IPy
from tenacity import retry, stop_after_attempt, wait_fixed

from utils.common import message
import utils.common.log as logger
from utils.common.ssh_util import Ssh
from utils.common.fic_base import TestCase
from utils.DBAdapter.DBConnector import BaseOps
from utils.common.exception import HCCIException
from utils.client.pecker_client import SSHClient
from utils.business.param_util import ParamUtil
from utils.business.project_condition_utils import get_project_condition_boolean
from plugins.DistributedStorage.Replication.scripts.common_utils.config_params import Params
from plugins.DistributedStorage.utils.common.DeployConstant import DeployConstant
from plugins.DistributedStorage.Replication.scripts.common_utils.RestOperate import RestOperate
from plugins.DistributedStorage.logic.InstallOperate import InstallOperate


class CreateQuorumServer_01(TestCase):
    def __init__(self, project_id, pod_id):
        super(CreateQuorumServer_01, self).__init__(project_id, pod_id, step_id=None, metadata=None)
        self.param_util = ParamUtil()
        self.service_name = "FusionStorageBlockReplication"
        self.source_path = "/home/pkg/"
        self.target_path = "/OceanStor/QuorumServer/"
        self.install_operate = InstallOperate(project_id, pod_id)
        self.config_license_switch_condition = get_project_condition_boolean(
            self.project_id, '!ExpansionAdCloudService')
        self.drop_rule_id = 0
        self.opr = None
        self.remote_opr = None

    def procedure(self):
        try:
            self.get_config_param_info()

            # 从HCC Turnkey后台传复制包到仲裁服务器，并解压
            file_name = self.findfilename(self.source_path)
            target_name = file_name.replace(self.source_path, self.target_path)

            # 上传复制包
            logger.info("[FSBR]begin to uploadpackage to HCC Turnkey ")
            self.uploadpackage(file_name, target_name)
            logger.info("[FSBR]uploadpackage success ")

            logger.info("[FSBR] Start to login fsm. ")
            self.opr = RestOperate(self.fsm_float_ip)
            self.opr.login(DeployConstant.DM_LOGIN_USER, self.cmd_password)

            logger.info("[FSBR] Start to login remote fsm. ")
            self.remote_opr = RestOperate(self.az_fsm_float_ip)
            self.remote_opr.login(DeployConstant.DM_LOGIN_USER, self.remote_cmd_password)

            # 解压安装包,安装仲裁软件
            logger.info("[FSBR]begin to install quorumserver on quorumserver ")
            self.install_quorumserver(self.target_path)
            logger.info("[FSBR]install quorumserver soft success ")

            #  配置仲裁软件
            logger.info("[FSBR]begin to  configurate_quorumserver  ")
            self.configurate_quorumserver()
            logger.info("[FSBR]configurate  quorumserver soft success ")
            #  在本端执行查询预共享秘钥命令获取本端复制集群的sn信息

            if self.config_license_switch_condition:
                self.install_operate.config_license_switch(self.fsm_float_ip, self.username, self.passwd,
                                                           self.root_passwd)

            logger.info("[FSBR] Start to query control cluster id, local_sn and remote_sn.")
            self.query_control_cluster_id()
            local_sn = self.query_local_sn()
            # 查询远端设备命令可获取对端复制集群sn信息
            remote_sn = self.query_remote_sn()

            # 根据查询的sn信息配置仲裁白名单
            self.configurate_quorumserver_whitelist(local_sn, remote_sn)
            logger.info("[FSBR]add quorunServer white_list success ")
            # 7、创建仲裁服务器
            # 本端
            quorum_server_ip_port = self.quorumserverip + ":30002"
            self.create_local_quorum_server(quorum_server_ip_port)

            # 远端
            if self.config_license_switch_condition:
                self.install_operate.config_license_switch(self.az_fsm_float_ip, self.username,
                                                           self.remote_dsware_password, self.root_passwd)

            self.create_remote_quorum_server(quorum_server_ip_port)
            logger.info("[FSBR]createQuorumServer successfully")

            self.opr.login_out(DeployConstant.DM_LOGIN_USER, self.cmd_password)
            self.remote_opr.login_out(DeployConstant.DM_LOGIN_USER, self.remote_cmd_password)
            return message.Message(200)
        except HCCIException as e:
            logger.error("[FSBR]createQuorumServer fail msg :%s " % str(e))
            return message.Message(500, str(e))
        except Exception as e:
            return message.Message(500, Exception(6280007, str(e)))
        finally:
            self.install_operate.config_license_switch(
                self.fsm_float_ip, self.username, self.passwd, self.root_passwd, delete=True)
            self.install_operate.config_license_switch(
                self.az_fsm_float_ip, self.username, self.remote_dsware_password, self.root_passwd, delete=True)

    def get_config_param_info(self):
        logger.info("[FSBR]Get info from config_param begin.")
        parameters = Params(self.project_id, self.pod_id, self.service_name)
        lld_dict = parameters.get_params_dict()
        self.address_ip_list = lld_dict.get("ip_pool")
        self.fsa_ip_list = lld_dict.get("replication_cluster")
        self.fsm_float_ip = lld_dict.get("local_storage_fsm_ip")
        self.control_ip_list = lld_dict.get("replication_controller_cluster")

        # 远端的浮动ip
        self.az_fsm_float_ip = lld_dict.get("remote_storage_fsm_ip")
        self.remote_dsware_password = lld_dict.get("remote_storage_fsm_dsware_password")
        self.remote_cmd_password = lld_dict.get("produce_admin_password")

        self.username = "dsware"
        self.password = lld_dict.get("local_storage_fsm_dsware_password")
        self.cmd_password = lld_dict.get('local_admin_password')
        self.passwd = lld_dict.get("local_storage_fsm_dsware_password")
        self.root_passwd = lld_dict.get("local_storage_fsm_root_password")

        # 仲裁服务器相关参数
        self.quorumserverip = lld_dict.get("storage_arbitration_ip")
        self.quorumserveruser = "root"
        self.quorumserveruserpassword = lld_dict.get("storage_arbitration_root_password")

        # 安装仲裁软件时新创建用户的密码,使用默认的用户名，密码给一个默认值
        self.quorumdefaultpwd = BaseOps().get_value_from_cloudparam(
            self.pod_id, self.service_name, "quorumsvr_password")
        # 远端控制集群id
        self.remote_cluster_id = '0'
        self.sshClient = SSHClient(self.quorumserverip, self.quorumserveruser, self.quorumserveruserpassword)
        self.ssh_client = Ssh.ssh_create_client(self.quorumserverip, self.quorumserveruser,
                                                self.quorumserveruserpassword, port=22, timeout=20)
        logger.info("[FSBR]Get info from config_param end.")

    def query_control_cluster_id(self):
        result = self.opr.query_control_cluster()
        self.control_cluster_id = result.get_control_cluster_id()
        if not self.control_cluster_id:
            msg = "[FSBR] Failed to create quorum server, not found control cluster id. " \
                  "detail: \n %s" % result.res.json()
            logger.error(msg)
            raise Exception(msg)
        logger.info("[FSBR] query control cluster id success ")

    def query_local_sn(self):
        result = self.opr.query_rep_cls_psk(self.control_cluster_id)
        local_sn = result.get_rep_cls_sn()
        if not local_sn:
            msg = "[FSBR] Failed to create quorum server, not found local sn. detail: \n %s" % result.res.json()
            logger.error(msg)
            raise Exception(msg)
        logger.info("[FSBR] query local sn success ")
        return local_sn

    def query_remote_sn(self):
        result = self.opr.query_remote_device(self.remote_cluster_id)
        remote_sn = result.get_remote_sn(self.address_ip_list)
        if not remote_sn:
            msg = "[FSBR] Failed to create quorum server, not found remote sn. detail: \n %s" % result.res.json()
            logger.error(msg)
            raise Exception(msg)
        logger.info("[FSBR]query  remote_sn  success ")
        return remote_sn

    @retry(stop=stop_after_attempt(3), wait=wait_fixed(20), reraise=True)
    def create_local_quorum_server(self, quorum_server_ip_port):
        local_quorm_name = self.generate_random_str()
        result = self.opr.create_quorum_server(local_quorm_name, quorum_server_ip_port, self.control_cluster_id)
        ret_value = result.query_dr_cmd_result()
        if 0 != ret_value:
            msg = "[FSBR] Failed to create local quorum server. detail: \n %s" % result.res.json()
            logger.error(msg)
            raise Exception(msg)
        logger.info("[FSBR]local  createQuorumServer success ")

    @retry(stop=stop_after_attempt(3), wait=wait_fixed(20), reraise=True)
    def create_remote_quorum_server(self, quorum_server_ip_port):
        remote_quorm_name = self.generate_random_str()
        result = self.remote_opr.create_quorum_server(remote_quorm_name, quorum_server_ip_port,
                                                      self.remote_cluster_id)
        ret_value = result.query_dr_cmd_result()
        if 0 != ret_value:
            msg = "[FSBR] Failed to create remote quorum server. detail: \n %s" % result.res.json()
            logger.error(msg)
            raise Exception(msg)

    @staticmethod
    def findfilename(file_dir_path):
        pkg_pattern = re.compile('^OceanStor-Pacific_8(.*)_QuorumServer.tar.gz$')
        file_path = ''
        for fileitem in os.listdir(file_dir_path):
            if pkg_pattern.findall(fileitem):
                file_path = os.path.join(file_dir_path, fileitem)
                return file_path
        if not file_path:
            msg = 'can not find OceanStor-Pacific_8(.*)_QuorumServer.tar.gz'
            logger.error(msg)
            raise HCCIException(627130, msg)
        logger.info('[FSBR]get Replication package:%s' % file_path)

    def uploadpackage(self, file_path, remote_path):
        try:
            if not remote_path or remote_path == "/":
                logger.error("remote_path is invalid.detail: %s" % remote_path)
                raise HCCIException("remote_path is invalid.detail: %s" % remote_path)
            # 执行删除命令，删除以前的压缩包
            del_cmd = "rm -rf %s" % self.target_path
            Ssh.ssh_send_command(self.ssh_client, del_cmd, "#", 30)
            # 执行创建路径的命令，若路径已存在，不起作用；若路径不存在，则创建
            cmd = "mkdir -p %s" % self.target_path
            Ssh.ssh_send_command(self.ssh_client, cmd, "#", 30)
            sftp = paramiko.SFTPClient.from_transport(self.ssh_client.get('client'))
            logger.info("upload %s to %s ," % (file_path, remote_path))
            sftp.put(file_path, remote_path)
        except Exception as exc:
            logger.error("uploadpkg fail: %s" % exc)
            msg = "uploadpkg to quorumServer fail: %s" % str(exc)
            raise HCCIException(627130, msg)
        finally:
            sftp.close()

    def install_quorumserver(self, absulate_path):
        try:
            self.config_firewall_rule()
            pkg_file_name = "OceanStor-Pacific_8.*_QuorumServer.tar.gz"
            cmds = "cd %s;rm -rf package;rm -rf /opt/quorum_server;userdel quorumsvr 2>/dev/null;" % absulate_path
            logger.info("execute %s delete Original files" % cmds)
            Ssh.ssh_send_command(self.ssh_client, cmds, '#', 30)
            logger.info("execute cmd unzip quoorumsvr softpkg")
            Ssh.ssh_send_command(self.ssh_client, 'tar -xzf {}'.format(pkg_file_name), '#', 30)
            logger.info("begin to execute install cmd")
            Ssh.ssh_send_command(self.ssh_client, "cd %s/package;sh ./quorum_server.sh -install" % absulate_path,
                                 'Continue to install', 60)
        except HCCIException as e:
            logger.error("result: %s" % str(e))
            if str(e).__contains__('Do you want to uninstall it now?'):
                Ssh.ssh_send_command(self.ssh_client, "Y", '<Y|N>:', 20)
                logger.info("[FSBR]the quorum server already exists, now is being uninstall and reinstall.")
                # 系统命令卸载的时候，不会删除安装过程中创建的用户，所以需要手动去进行删除，才能继续安装
                self.delete_quorumsvr_user()
            else:
                msg = "Install quorumServer execute install cmd fail : %s" % e
                raise HCCIException(627131, msg)

        try:
            Ssh.ssh_send_command(self.ssh_client, "Y", "[default: quorumsvr]:", 60)
            Ssh.ssh_send_command(self.ssh_client, "quorumsvr", "New password:", 20)
            Ssh.ssh_send_command(self.ssh_client, self.quorumdefaultpwd, "Retype new password:", 20)
            Ssh.ssh_send_command(self.ssh_client, self.quorumdefaultpwd, "#", 60)
        except HCCIException as e:
            logger.error("[FSBR]not get install sucesss: %s" % str(e))
            if not str(e).__contains__('install success'):
                msg = "Install quorumServer create new user fail : %s" % e
                self.roll_back("first")
                raise HCCIException(627131, msg)
        try:
            # 安装成功之后进行检查
            self.check_installed_quorum_server()
        except HCCIException as e:
            logger.error("[FSBR]Check Install quorumServer fail...")
            msg = "Install quorumServer check install result fail : %s" % e
            raise HCCIException(627131, msg)

    def config_firewall_rule(self):
        """
        1、仲裁服务器上存在firewall-cmd命令并且firewall-cmd状态为running状态时使用firewall-cmd命令进行配置，firewall-cmd配置默认都是drop，所以只需要开启复制节点仲裁平面网段
        2、使用iptables命令配置仲裁服务器防火墙时，对源地址为仲裁网络开放30001/30002端口，对原地址为管理网段关闭30002端口
        """
        logger.info("config the iptables.")
        firewall_cmd_state = "firewall-cmd --state;echo last_result=$?"
        cmd_res = Ssh.ssh_exec_command_return(self.ssh_client, firewall_cmd_state)
        cmd_res = str(cmd_res)
        firewall_cmd_flag = True
        if cmd_res.find('last_result=0') == -1:
            firewall_cmd_flag = False
        logger.info("Firewall-cmd flag is %s" % firewall_cmd_flag)
        local_arb_net = self.get_rep_node_net(self.opr, "rep_arb")
        remote_arb_net = self.get_rep_node_net(self.remote_opr, "rep_arb")
        local_mgmt_net = self.get_rep_node_net(self.opr, "mgmt")
        remote_mgmt_net = self.get_rep_node_net(self.remote_opr, "mgmt")
        self.config_firewall_rule_opt(firewall_cmd_flag, local_arb_net, ports=["30001", "30002"])
        if remote_arb_net != local_arb_net:
            self.config_firewall_rule_opt(firewall_cmd_flag, remote_arb_net, ports=["30001", "30002"])
        if not firewall_cmd_flag:
            self.config_firewall_rule_opt(firewall_cmd_flag, local_mgmt_net, opt_rule="DROP", ports=["30002"])
        if not firewall_cmd_flag and local_mgmt_net != remote_mgmt_net:
            self.config_firewall_rule_opt(firewall_cmd_flag, remote_mgmt_net, opt_rule="DROP", ports=["30002"])

    def config_firewall_rule_opt(self, firewall_cmd_flag, arb_nets, opt_rule="ACCEPT", ports=None):
        for arb_net in arb_nets:
            if not firewall_cmd_flag:
                # 使用iptables命令添加防火墙规则
                current_drop = ""
                if opt_rule == "DROP":
                    self.drop_rule_id += 1
                    current_drop = str(self.drop_rule_id)
                iptables_cmd = "iptables -nL | grep -w %s | grep -w %s | grep -w %s>/dev/null; if [ $? -ne 0 ]; " \
                               "then iptables -I INPUT %s -p tcp -s %s --dport %s -j %s; fi"
                iptables_sed_cmd = "cat /etc/sysconfig/iptables 2>/dev/null | grep -w %s | " \
                                   "grep -w %s | grep -w %s>/dev/null;if [ $? -ne 0 ]; " \
                                   "then sed -i \"/^COMMIT/i -A INPUT %s -p tcp -s %s --dport %s " \
                                   " -j %s\" /etc/sysconfig/iptables 2>/dev/null; fi"
                cmd_list = [iptables_cmd % (port, opt_rule, arb_net, current_drop, arb_net, port, opt_rule) for port in ports]
                cmd_sed_list = [iptables_sed_cmd % (port, opt_rule, arb_net, current_drop, arb_net, port, opt_rule) for port in ports]
                echo_cmd = "cat /etc/rc.local | grep %s | grep %s | grep -w %s; if [ $? -ne 0 ];" \
                           "then echo iptables -I INPUT %s -p tcp -s %s --dport %s -j %s >> /etc/rc.local;fi"
                echo_cmd_list = [echo_cmd % (port, opt_rule, arb_net, current_drop, arb_net, port, opt_rule) for port in ports]
                cmd = ";".join(cmd_list) + ";" + ";".join(cmd_sed_list) + ";" + ";".join(echo_cmd_list)
            else:
                # 使用firewall-cmd命令配置防火墙规则
                firewall_cmd_per = 'firewall-cmd --permanent --add-rich-rule' \
                                   '="rule family="ipv4" source address="%s" port protocol="tcp" port="%s" accept"'
                firewall_cmd = 'firewall-cmd --add-rich-rule' \
                               '="rule family="ipv4" source address="%s" port protocol="tcp" port="%s" accept"'
                firewall_cmd_per_list = [firewall_cmd_per % (arb_net, port) for port in ports]
                firewall_cmd_list = [firewall_cmd % (arb_net, port) for port in ports]
                cmd = ";".join(firewall_cmd_per_list) + ";" + ";".join(firewall_cmd_list)
            logger.info("Config firewall rule for net %s success" % arb_net)
            Ssh.ssh_send_command(self.ssh_client, cmd, '#', 30)

    def get_rep_node_net(self, opt, port_name):
        """
        获取节点制定网口地址和掩码，并生成网段信息
        :param opt: 生产和灾备存储复DM登录账号密码
        :param port_name: 网口名称包括管理网卡和复制网卡
        :return:
        """
        rsp_result, rsp_data = opt.query_network_servers()
        error_code = rsp_result.get('code')
        if error_code != 0:
            err_msg = "Failed to query rep node mgmt network info."
            logger.error(err_msg)
            raise Exception(err_msg)
        net_set = set()
        for net_info in rsp_data:
            ip_address_list = net_info.get("ip_address_list")
            self.get_one_rep_node_net(ip_address_list, net_set, port_name)
        if not net_set:
            err_msg = "Failed to query rep node mgmt network info. details: %s" % rsp_data
            logger.error(err_msg)
            raise Exception(err_msg)
        return list(net_set)

    @staticmethod
    def get_one_rep_node_net(ip_address_list, net_set, port_name):
        for ip_address in ip_address_list:
            if ip_address.get("port_name") == port_name:
                arb_ip = ip_address.get("ip_address")
                arb_subnet_prefix = ip_address.get("subnet_prefix")
                if arb_ip and arb_subnet_prefix:
                    net_set.add(IPy.IP(arb_ip).make_net(arb_subnet_prefix))

    def delete_quorumsvr_user(self):
        self.sshClient.exec_cmd("killall -u quorumsvr")
        self.sshClient.exec_cmd("userdel quorumsvr")
        self.sshClient.close()

    def check_installed_quorum_server(self):
        Ssh.ssh_send_command(self.ssh_client, "qsadmin", "admin:/", 60)
        Ssh.ssh_send_command(self.ssh_client, "exit", '#', 10)
        result_list = Ssh.ssh_send_command(self.ssh_client, "ps -elf |grep quorum_server", "#", 60)
        count = 0
        for item in result_list:
            if item.find("./bin/quorum_serverd") >= 0:
                count += 1
        if count > 0:
            logger.info("[FSBR]Check Install quorumServer successful")

    def configurate_quorumserver(self):
        try:
            Ssh.ssh_send_command(self.ssh_client, "qsadmin", 'admin:/', 30)
            Ssh.ssh_send_command(self.ssh_client, "add server_ip ip=%s port=30002" % self.quorumserverip,
                                 'Command executed successfully.', 20)
            result_list = Ssh.ssh_send_command(self.ssh_client, "show server_ip", 'admin:/', 20)
            for item in result_list:
                if item.__contains__(self.quorumserverip):
                    logger.info("[FSBR]add server_ip  return successfully")
        except HCCIException as e:
            if str(e).__contains__("already exists"):
                logger.info("[FSBR]add server_ip  return successfully, and  find the str 'already exists.'")
            Ssh.ssh_send_command(self.ssh_client, "exit", '#', 10)
            logger.info("[FSBR]config quorum successfully")
        except Exception as err:
            logger.error("[FSBR]config quorum error:%s" % str(err))
            msg = "Configurate Quorumserver fail :%s" % err
            self.roll_back("second")
            raise HCCIException(627132, msg)

    def configurate_quorumserver_whitelist(self, local_sn, remote_sn):
        try:
            Ssh.ssh_send_command(self.ssh_client, "qsadmin", "admin:/", 20)
            Ssh.ssh_send_command(self.ssh_client, "change white_list enable_switch=no", "admin:/", 20)
            logger.info("[FSBR]Install  quorumServer add white_list successfully")
        except HCCIException as e:
            logger.error("[FSBR]Install  quorumServer:add white_list, errMsg=%s" % str(e))
            raise HCCIException(627133, str(e))
        except Exception as err:
            logger.error("[FSBR]add white_list:%s" % str(err))
            msg = "add white_list fail...%s" % err
            self.roll_back("third")
            raise HCCIException(627133, msg)
        finally:
            if self.ssh_client is not None:
                try:
                    Ssh.ssh_close(self.ssh_client)
                except Exception as e:
                    logger.info("[FSBR]close ssh client failed. errMsg=%s" % str(e))

    # 仲裁服务器的名字
    @staticmethod
    def generate_random_str(randomlength=24):
        random_str = ''
        base_str = 'ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789'
        length = len(base_str) - 1
        for i in range(randomlength):
            random_str += base_str[secrets.choice(list(range(0, length)))]
        return random_str

    def roll_back(self, step):
        try:
            # 第一步 安装仲裁服务器软件
            # 第二步 配置仲裁服务器软件
            # 第三步 配置仲裁白名单
            # 第四部 本端/远端创建仲裁服务器
            if "fourth" == step:
                self.delquorumserver()
                self.uninstallquorm_config()
            else:
                self.uninstallquorm_config()
        except HCCIException as e:
            logger.error(e)

    def delquorumserver(self):
        result = self.opr.query_quorum_server(self.control_cluster_id)
        server_id, _ = result.get_quorum_server_id(self.quorumserverip)
        if server_id:
            result = self.opr.delete_quorum_server(self.control_cluster_id, server_id)
            ret_value = result.query_dr_cmd_result()
            if 0 != ret_value:
                err_msg = "[FSBR] Delete local QuorumServer failed. result:%s " % result.res.json()
                logger.error(err_msg)
                raise Exception(err_msg)

        result = self.remote_opr.query_quorum_server(self.remote_cluster_id)
        server_id, _ = result.get_quorum_server_id(self.quorumserverip)
        if server_id:
            result = self.opr.delete_quorum_server(self.remote_cluster_id, server_id)
            ret_value = result.query_dr_cmd_result()
            if 0 != ret_value:
                err_msg = "[FSBR] Delete remote QuorumServer failed. result:%s " % result.res.json()
                logger.error(err_msg)
                raise Exception(err_msg)

    def uninstallquorm_config(self):
        cmd = "cd %s/package" % self.target_path
        Ssh.ssh_exec_command_return(self.ssh_client, cmd)
        result_list = Ssh.ssh_send_command(self.ssh_client, "ps -elf |grep quorum_server", "#", 60)
        count = 0
        for item in result_list:
            if item.find("./bin/quorum_serverd") >= 0:
                count += 1
        if count > 0:
            Ssh.ssh_send_command(self.ssh_client, "sh ./quorum_server.sh -uninstall",
                                 "<Y|N>:", 60)
            Ssh.ssh_send_command(self.ssh_client, "N", "#", 20)
        cmds = "killall -u quorumsvr;userdel quorumsvr;cd %s;rm -rf ./*" % self.target_path
        Ssh.ssh_exec_command_return(self.ssh_client, cmds)
