# -*- coding:utf-8 -*-
# Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
import os
import re
import traceback
import paramiko
import IPy

import utils.common.log as logger
from utils.common.ssh_util import Ssh
from utils.common.fic_base import TestCase
from utils.common.message import Message
from utils.DBAdapter.DBConnector import BaseOps
from utils.common.exception import FCDException
from utils.client.pecker_client import SSHClient
from plugins.DistributedStorageReplication.scripts.common_utils.config_params import Params
from plugins.DistributedStorage.scripts.utils.common.DeployConstant import DeployConstant
from plugins.DistributedStorageReplication.scripts.common_utils.rest_operate import RestOperate


class InstallQuorumSoftware(TestCase):
    def __init__(self, project_id, pod_id):
        super(InstallQuorumSoftware, self).__init__(project_id, pod_id, step_id=None, metadata=None)
        self.service_name = "FusionStorageBlockReplication"
        self.source_path = "/home/pkg/"
        self.target_path = "/OceanStor/QuorumServer/"
        parameters = Params(self.project_id, self.pod_id, self.service_name)
        lld_dict = parameters.get_params_dict()
        # 本端浮动IP与密码
        self.fsm_float_ip = lld_dict.get("local_storage_fsm_ip")
        self.cmd_password = lld_dict.get('local_admin_password')

        # 远端的浮动ip与密码
        self.az_fsm_float_ip = lld_dict.get("remote_storage_fsm_ip")
        self.remote_cmd_password = lld_dict.get("produce_admin_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")

        self.ssh_client_pecker = SSHClient(self.quorumserverip, self.quorumserveruser, self.quorumserveruserpassword)
        self.ssh_client = Ssh.ssh_create_client(self.quorumserverip, self.quorumserveruser,
                                                self.quorumserveruserpassword, port=22, timeout=20)

        self.remote_opr = RestOperate(self.az_fsm_float_ip)
        self.opr = RestOperate(self.fsm_float_ip)
        self.drop_rule_id = 0

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

    @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 procedure(self):
        try:
            self.main()
        except FCDException as err:
            logger.error(traceback.format_exc())
            return Message(500, err)
        except Exception as err:
            logger.error(traceback.format_exc())
            return Message(500, FCDException(627131, str(err)))
        return Message(200)

    def main(self):
        # 本端
        logger.info("[FSBR] Start to login fsm. ")
        self.opr.login(DeployConstant.DM_LOGIN_USER, self.cmd_password)
        # 远端
        logger.info("[FSBR] Start to login remote fsm. ")
        self.remote_opr.login(DeployConstant.DM_LOGIN_USER, self.remote_cmd_password)

        # 从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]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()
        self.configurate_quorumserver_whitelist()

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

    def install_quorumserver(self, absolute_path):
        self.config_firewall_rule()
        cmd = "cd %s;rm -rf package;rm -rf /opt/quorum_server;userdel quorumsvr 2>/dev/null;" % absolute_path
        logger.info("execute %s delete Original files" % cmd)
        Ssh.ssh_send_command(self.ssh_client, cmd, '#', 30)
        logger.info("execute cmd unzip quoorumsvr softpkg")
        Ssh.ssh_send_command(self.ssh_client, 'tar -xzf {}'.format(list(DeployConstant.QUORUM_SERVER_PKG.keys())[0]),
                             '#', 30)

        logger.info("begin to execute install cmd")
        cmd = "cd %s/package;sh ./quorum_server.sh -install" % absolute_path
        ret = Ssh.ssh_send_command(self.ssh_client, cmd, "<Y|N>:", 30)
        logger.info("result: {}".format(ret))
        if not re.search('Continue to install', str(ret)):
            if re.search('Do you want to uninstall it now', str(ret)):
                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 : {}".format(ret)
                raise FCDException(627131, msg)
        try:
            self.config_quorum_server_account()
        except Exception as err:
            logger.error(traceback.format_exc())
            self.roll_back()
            raise err
        try:
            self.check_installed_quorum_server()
        except FCDException as err:
            logger.error("[FSBR]Check Install quorumServer fail...")
            msg = "Install quorumServer check install result fail : %s" % err
            raise FCDException(627131, msg) from err

    def config_quorum_server_account(self):
        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)

    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)

    def delete_quorumsvr_user(self):
        self.ssh_client_pecker.exec_cmd("killall -u quorumsvr")
        self.ssh_client_pecker.exec_cmd("userdel quorumsvr")
        self.ssh_client_pecker.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:
            self.config_quorum_server()
        except Exception as err:
            logger.error("[FSBR]config quorum error:%s" % str(err))
            msg = "Configurate Quorumserver fail :%s" % err
            self.roll_back()
            raise FCDException(627132, msg) from err

    def config_quorum_server(self):
        Ssh.ssh_send_command(self.ssh_client, "qsadmin", 'admin:/', 30)
        cmd = "add server_ip ip={} port=30002".format(self.quorumserverip)
        ret = Ssh.ssh_send_command(self.ssh_client, cmd, "admin:/", 20)
        if not re.search("Command executed successfully", str(ret)):
            if re.search("already exists", str(ret)):
                logger.info("[FSBR]Server_ip already exists.{}".format(ret))
                Ssh.ssh_send_command(self.ssh_client, "exit", '#', 10)
            else:
                msg = "[FSBR]config quorum error:%s" % str(ret)
                logger.error(msg)
                raise FCDException(627132, msg)
        else:
            result_list = Ssh.ssh_send_command(self.ssh_client, "show server_ip", 'admin:/', 20)
            add_server_ip_success = False
            for item in result_list:
                if item.__contains__(self.quorumserverip):
                    add_server_ip_success = True
                    logger.info("[FSBR]add server_ip  return successfully")
                    break
            if not add_server_ip_success:
                err_msg = "add server_ip {} failed, details:{}".format(self.quorumserverip, result_list)
                logger.error(err_msg)
                raise FCDException(627132, err_msg)
        Ssh.ssh_send_command(self.ssh_client, "exit", '#', 10)
        logger.info("[FSBR]config quorum successfully")

    def roll_back(self):
        try:
            # 第一步 安装仲裁服务器软件
            # 第二步 配置仲裁服务器软件
            # 第三步 配置仲裁白名单
            # 第四部 本端/远端创建仲裁服务器
            self.uninstallquorm_config()
        except FCDException as err:
            logger.error(err)

    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)

    def configurate_quorumserver_whitelist(self):
        try:
            self.add_white_list_command()
        except FCDException as err:
            logger.error("[FSBR]Install  quorumServer:add white_list, errMsg=%s" % str(err))
            raise FCDException(627133, str(err)) from err
        except Exception as err:
            logger.error("[FSBR]add white_list:%s" % str(err))
            msg = "add white_list fail...%s" % err
            self.roll_back()
            raise FCDException(627133, msg) from err
        finally:
            if self.ssh_client is not None:
                try:
                    Ssh.ssh_close(self.ssh_client)
                except Exception as err:
                    logger.info("[FSBR]close ssh client failed. errMsg=%s" % str(err))

    def add_white_list_command(self):
        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")
