# coding=utf-8
# Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
import collections
import traceback

import utils.common.log as logger
from utils.common.exception import HCCIException
from utils.common.message import Message
from utils.common.fic_base import StepBaseInterface
from utils.DBAdapter.DBConnector import BaseOps
from utils.common.ssh_util2 import Ssh as ssh
from utils.common.error.hcci_error_code import get_code_msg
from utils.business.project_condition_utils import get_project_condition_boolean
from plugins.DistributedStorage.utils.common.deploy_constant import DeployConstant


class CheckStorageNodeNICIntf(StepBaseInterface):
    def __init__(self, project_id, pod_id):
        super(CheckStorageNodeNICIntf, self).__init__(project_id, pod_id)
        self.project_id = project_id
        self.pod_id = pod_id
        self.db = BaseOps()
        self.bmc_info = self.get_all_bmc_info()
        self.failed_nodes = collections.defaultdict(list)
        self.client_list = []
        self.ssh_error_code = 626265
        self.su_root_error_code = 626041
        self.storage_data0_error_code = 626264
        self.storage_inner_error_code = 626266
        self.rep_error_code = 626294
        self.arb_error_code = 626295
        self.backend_net_independent = get_project_condition_boolean(project_id, "TenantStorBackendNetSep")
        self.is_rep_net = get_project_condition_boolean(project_id, "CSDRStorage_TFB|CSHAStorage_TFB")
        self.is_arb_net = get_project_condition_boolean(project_id, "CSHAStorage_TFB")

    @staticmethod
    def check_su_root(root_passwd, ssh_client):
        ssh.ssh_send_command(ssh_client, 'su -', 'assword:', 20)
        ssh.ssh_send_command(ssh_client, root_passwd, '#', 20)
        ssh.ssh_send_command(ssh_client, 'TMOUT=0', '#', 20)

    def procedure(self):
        # 装机场景下不做检查
        if get_project_condition_boolean(
                self.project_id,
                '!TenantStorFB_Heterogeneous|'
                '(TenantStorFB80&(BMS|CascadedOpenStack_BMS|ExpansionPod_BMS|ExpansionAZ_BMS))'):
            return
        logger.info("start to check node connectivity and network port name")
        try:
            self.check_node_connectivity_and_net_port_name()
        finally:
            for cur_client in self.client_list:
                ssh.ssh_close(cur_client)

        ret_err_msg = []
        for error_code, failed_node_list in self.failed_nodes.items():
            ret_err_msg.append(str(get_code_msg(error_code) % failed_node_list).strip())
        if ret_err_msg:
            raise Exception(ret_err_msg)

    def get_all_bmc_info(self):
        ret = []
        all_bmc_info = self.db.get_install_os_list_info(self.pod_id) + \
            self.db.get_install_os_list_info(self.pod_id, component="rep")
        all_bmc_ip = set()
        for node_info in all_bmc_info:
            if node_info.get("bmc_ip") not in all_bmc_ip:
                ret.append(node_info)
                all_bmc_ip.add(node_info.get("bmc_ip"))
        return ret

    def check_nic(self, error_code, ssh_client, net_port_name, net_port_ip):
        cmd = "ip addr show %s 2>/dev/null | grep -w %s" % (net_port_name, net_port_ip)
        try:
            cmd_res = ssh.ssh_send_command(ssh_client, cmd, "inet", 20)
        except Exception as e:
            logger.error("Failed to check Network port:{}, Network port IP, err:{}, "
                         "details:{}".format(net_port_name, net_port_ip, e, traceback.format_exc()))
            self.failed_nodes[error_code].append(net_port_ip)
            return
        logger.info('Network port IP:{}, Network port name:{}, result:{}'.format(net_port_ip, net_port_name, cmd_res))

    def check_node_connectivity_and_net_port_name(self):
        for bmc_info in self.bmc_info:
            bmc_ip = bmc_info.get("bmc_ip")
            manage_ip = bmc_info.get("manageIp")
            _, root_passwd, user, user_passwd = bmc_info.get("creuser").split(",")
            try:
                ssh_client = ssh.ssh_create_client(manage_ip, user, user_passwd)
            except Exception as e:
                logger.error("node:{}, manager_ip:{}, Failed to connect host by user:{}. err:{}, "
                             "details:{}".format(bmc_ip, manage_ip, user, e, traceback.format_exc()))
                self.failed_nodes[self.ssh_error_code].append(manage_ip)
                continue
            self.client_list.append(ssh_client)
            try:
                self.check_su_root(root_passwd, ssh_client)
            except Exception as e:
                logger.error("node:{}, manager_ip:{}, Failed to su root, err:{},"
                             "details:{}".format(bmc_ip, manage_ip, e, traceback.format_exc()))
                self.failed_nodes[self.su_root_error_code].append(manage_ip)
                continue

            self.check_nic(self.storage_data0_error_code, ssh_client,
                           DeployConstant.MGR_STORAGE_NET_INTF, bmc_info.get("storageIp"))
            if self.backend_net_independent:
                logger.info("check storageInner port")
                self.check_nic(self.storage_inner_error_code, ssh_client,
                               DeployConstant.BACKEND_STORAGE_NET_INTF, bmc_info.get("storageInnerIp"))
            if self.is_rep_net:
                rep0_ip, rep1_ip = bmc_info.get("replication_plane_ip").split(",")
                logger.info("rep IP:{}, {}".format(rep0_ip, rep1_ip))
                logger.info("check rep0 port:{}".format(rep0_ip))
                self.check_nic(self.rep_error_code, ssh_client, DeployConstant.REP0_NIC_NAME, rep0_ip)
                logger.info("check rep1 port:{}".format(rep1_ip))
                self.check_nic(self.rep_error_code, ssh_client, DeployConstant.REP1_NIC_NAME, rep1_ip)
            if self.is_arb_net:
                logger.info("check rep_arb port:{}".format(bmc_info.get("arbitration_plane_ip")))
                self.check_nic(self.arb_error_code, ssh_client,
                               DeployConstant.ARB_NIC_NAME, bmc_info.get("arbitration_plane_ip"))

    def pre_check(self, project_id, pod_id):
        """
        插件内部接口：执行安装前的资源预检查，该接口由execute接口调用，工具框架不会直接调用此接口。
        :param project_id:
        :param pod_id:
        :return:Message类对象
        """
        return Message(200)

    def execute(self, project_id, pod_id):
        try:
            self.procedure()
        except HCCIException as e1:
            logger.error("Check Nic error:{}".format(traceback.format_exc()))
            return Message(500, e1)
        except Exception as e2:
            logger.error("Check Nic error:{}".format(traceback.format_exc()))
            return Message(500, e2)
        return Message(200)

    def rollback(self, project_id, pod_id):
        """
        标准调用接口：执行回滚
        :param project_id:
        :param pod_id:
        :return:Message类对象
        """
        return Message(200)

    def retry(self, project_id, pod_id):
        """
        标准调用接口：重试
        :return: Message类对象
        """
        return self.execute(project_id, pod_id)

    def check(self, project_id, pod_id):
        """
        标准调用接口：重试
        :param project_id:
        :param pod_id:
        :return:Message类对象
        """
        return Message(200)
