# -*- coding: utf-8 -*-
import time
import os
import copy
import traceback
from utils.common.fic_base import TestCase
from xml.etree.ElementTree import ElementTree
from utils.common.ssh_util import Ssh as ssh
from utils.common.message import Message
from utils.common.exception import FCDException
from plugins.DistributedStorage.scripts.logic.DeployOperate import DeployOperate
from plugins.DistributedStorage.scripts.utils.common.DeployConstant import DeployConstant
from utils.client.FSPAuthClient import FSPAuthClient
from plugins.DistributedStorage.scripts.logic.InstallOperate import InstallOperate
from utils.common.fic_common import logger, get_cur_path


class CreateCluster(TestCase):
    def __init__(self, project_id, pod_id, fs_args, condition=None, metadata=None, **kwargs):
        super(CreateCluster, self).__init__(project_id, pod_id)
        self.fs_args = fs_args
        self.opr = DeployOperate(self.fs_args)
        self.mode = self.fs_args.get("mode")
        self.scene = self.fs_args.get("scene")
        self.update_pwd = self.fs_args['dm_update_pwd']
        self.float_ip = self.fs_args.get('float_ip')
        self.dsware_passwd = self.fs_args.get('fsm_dsware_pwd')
        self.fsa_list = self.fs_args.get('fsa_list')
        self.first_metadata_node = None
        self.first_metadata_type = None
        self.first_metadata_size = None
        self.all_flash = False
        self.condition = condition
        self.metadata = metadata
        self.more_args = kwargs
        self.metadata_node_id_list = list()
        self.metadata_node_manager_ip_list = list()
        self.operate = InstallOperate(self.project_id, self.pod_id, self.fs_args)

    def procedure(self):
        try:
            logger.info("Start to create cluster.")
            status_code, error_code, error_des = self.opr.login(DeployConstant.DM_LOGIN_USER, self.update_pwd)
            if status_code != 200 or error_code != 0:
                err_msg = "Failed to login deploy manager, " \
                          "Detail:[status:%s,code:%s]%s" % (status_code, error_code, error_des)
                logger.error(err_msg)
                raise Exception(err_msg)
            partition_mode = self.partition_mode()
            if partition_mode:
                self.sys_disk_partition_deploy()
            else:
                self.independent_disk_mode()

        except FCDException as e:
            logger.error(traceback.format_exc())
            return Message(500, e)
        except Exception as e:
            return Message(500, FCDException(626002, str(e)))
        finally:
            self.opr.logout()

        return Message()

    def sys_disk_partition_deploy(self):
        logger.info("System disk partition deploy mode.")
        # 获取元数据节点ip对应id列表
        logger.info("Start to zk partition deploy in nodes[%s]" % str(self.metadata_node_manager_ip_list))
        webui_audit = FSPAuthClient.get_cps_web_host_and_tool(self.db, self.project_id, self.pod_id)
        roles = []
        cps_client = FSPAuthClient.get_cps_rest_client(self.db, self.project_id, self.pod_id)
        hosts_info = self.operate.query_hosts_info_from_webui(webui_audit)
        for host_ip in self.metadata_node_manager_ip_list:
            host_id = hosts_info.get(host_ip)
            if host_ip:
                self.metadata_node_id_list.append(host_id)
                role = InstallOperate.get_role_on_host(cps_client, host_id)
                if not role:
                    msg = "There is no fusionstorage-blockXXX role on host[%s]" % host_id
                    logger.error(msg)
                    raise Exception(msg)
                roles.append(role)
                logger.info("The fusionstorage-blockXXX role nodes%s is %s"
                            % (",".join(self.metadata_node_manager_ip_list), role))

        host_id_str = ','.join(self.metadata_node_id_list)
        logger.info("Start to config zk on nodes[%s]" % host_id_str)

        # 划分zk hostcfg组
        hostcfg_zk_map = self.get_host_hostcfg(cps_client)

        zk_dir = '/opt/fusionstorage/persistence_layer/agent/zk/data'
        ccdb_dir = '/opt/dsware/infrastructure/ccdb/ccdb_server/data'
        ccdb2_dir = '/opt/dsware/service/dr/ccdb_server/data'
        zk_mount_dict_info = {"zk_disk": zk_dir,
                              "ccdb_disk": ccdb_dir,
                              "ccdb_disk2": ccdb2_dir,
                              }
        # 获取元数据节点角色
        logger.info("Get fusionstorage-blockXXX role nodes[%s], nodes uuid[%s]"
                    % (",".join(self.metadata_node_manager_ip_list), ",".join(self.metadata_node_id_list)))
        # 执行存储规则
        logger.info("Start to mount dir %s to nodes %s" % (",".join([zk_dir, ccdb_dir, ccdb2_dir]), host_id_str))
        # 在新建组添加逻辑分区的同时在default组也添加分区信息，防止分区添加后，有节点移出分组，未加入新分组前，
        # 后台检测到default组中无分区信息，自动取消分区挂载，导致分区信息丢失
        hostcfg_zk_map['default'] = roles
        self.add_hostcfg_item_storage(cps_client, "zk_disk", "65G", hostcfg_zk_map, zk_dir)
        self.add_hostcfg_item_storage(cps_client, "ccdb_disk", "20G", hostcfg_zk_map, ccdb_dir)
        self.add_hostcfg_item_storage(cps_client, "ccdb_disk2", "20G", hostcfg_zk_map, ccdb2_dir)

        self.check_dir_exist(zk_mount_dict_info)
        # 创建集群
        self.create_cluster(zk_dir)

    def get_host_hostcfg(self, cps_client):
        try:
            host_hostcfg_map = self.get_host_cfg_map(cps_client)
            logger.info("The host name and hostcfg name map is %s" % str(host_hostcfg_map))
            hostcfg_name_zk_suffix = self.get_zk_suffix()
            hostcfg_zk_map = dict()
            # 如果host id已经被加入了一个zk分组，则进行回退
            self.rollback_hosts_from_hostcfg(hostcfg_name_zk_suffix, host_hostcfg_map, cps_client)
            for host_id, hostcfg_name in host_hostcfg_map.items():
                # 重新加入zk分组
                if hostcfg_name.find(hostcfg_name_zk_suffix) != -1:
                    hostcfg_name = hostcfg_name[:hostcfg_name.find(hostcfg_name_zk_suffix)]

                new_hostcfg_name = hostcfg_name + hostcfg_name_zk_suffix
                if not hostcfg_zk_map.get(new_hostcfg_name):
                    hostcfg_zk_map[new_hostcfg_name] = []
                logger.info("Query hostcfg name")
                rsp_hostcfg = cps_client.hostcfg_list('storage')
                if not isinstance(rsp_hostcfg, dict):
                    raise Exception("Failed to query hostcfg information")
                hostcfg_data_list = rsp_hostcfg.get('hostcfg')
                hostcfg_name_list = [hostcfg_data.get('name') for hostcfg_data in hostcfg_data_list]
                if new_hostcfg_name not in hostcfg_name_list:
                    logger.info("Copy hostcfg[%s] from hostcfg[%s]" % (new_hostcfg_name, hostcfg_name))
                    cps_client.do_hostcfg_add('storage', new_hostcfg_name, copy_from=hostcfg_name)

                logger.info("Delete host[%s] from hostcfg[%s]" % (host_id, hostcfg_name))
                cps_client.do_hostcfg_delete_hosts('storage', hostcfg_name, [host_id])

                logger.info("Add host[%s] to hostcfg[%s]" % (host_id, new_hostcfg_name))
                cps_client.do_hostcfg_add_hosts('storage', new_hostcfg_name, [host_id])
                cps_client.do_commit()

                if self.check_group_has_no_hosts(cps_client, hostcfg_name):
                    logger.info("Delete origin host group [%s]" % hostcfg_name)
                    cps_client.do_hostcfg_delete('storage', hostcfg_name)
                    cps_client.do_commit()
                time.sleep(len(host_hostcfg_map) * 5)
                role = InstallOperate.get_role_on_host(cps_client, host_id)
                if not role:
                    msg = "There is no fusionstorage-blockXXX role on host[%s]" % host_id
                    logger.error(msg)
                    raise Exception(msg)
                hostcfg_zk_map[new_hostcfg_name].append(role)
                logger.info("Add host_id %s to hostcfg %s successfully" % (host_id, new_hostcfg_name))

            return hostcfg_zk_map
        except Exception as e:
            logger.error(traceback.format_exc())
            raise e

    def get_zk_suffix(self):
        host_cfg_name_zk_suffix = '_zk'
        if self.scene == "RegionConHA":
            if self.mode == "ha_master":
                host_cfg_name_zk_suffix = '_zk_master'
            else:
                host_cfg_name_zk_suffix = '_zk_slaver'
        logger.info('host cfg name zk suffix:{}'.format(host_cfg_name_zk_suffix))
        return host_cfg_name_zk_suffix

    def get_host_cfg_map(self, cps_client):
        logger.info('Get host cfg map')
        response = cps_client.hostcfg_list('storage')
        hostcfg_list = response['hostcfg']
        host_cfg_map = dict()
        for hostcfg in hostcfg_list:
            hosts = hostcfg['hosts']
            hostcfg_name = hostcfg['name']
            self.get_host_name_and_id_map(host_cfg_map, hostcfg_name, hosts)
        # 有zk的节点必须都是有存储能力的节点，即有zk的节点必须都要在资源分组为storage中
        if len(host_cfg_map) != len(self.metadata_node_manager_ip_list):
            msg = "The number of storage type nodes and the number of metadata nodes are not equal."
            logger.error(msg)
            raise Exception(msg)
        return host_cfg_map

    def get_host_name_and_id_map(self, host_cfg_map, hostcfg_name, hosts):
        if 'hostid' in hosts:
            for host in hosts['hostid']:
                if host in self.metadata_node_id_list:
                    host_cfg_map[host] = hostcfg_name

    @staticmethod
    def check_group_has_no_hosts(cps_client, target_hostcfg_name):
        response = cps_client.hostcfg_list('storage')
        hostcfg_list = response['hostcfg']
        for hostcfg in hostcfg_list:
            hostcfg_name = hostcfg['name']
            if target_hostcfg_name == hostcfg_name:
                hosts = hostcfg['hosts']
                if len(hosts) > 0:
                    logger.info("hostcfg_name: %s has host, not need to delete." % hostcfg_name)
                    return False
                else:
                    logger.info("hostcfg_name: %s has no host, delete." % hostcfg_name)
                    return True
        return False

    @staticmethod
    def rollback_hosts_from_hostcfg(hostcfg_name_zk_suffix, host_hostcfg_map, cps_client):
        def check_group_exist(client, target_hostcfg_name):
            response = client.hostcfg_list('storage')
            hostcfg_list = response['hostcfg']
            for hostcfg in hostcfg_list:
                cur_hostcfg_name = hostcfg['name']
                if target_hostcfg_name == cur_hostcfg_name:
                    return True
            logger.info("hostcfg_name: %s not exist." % target_hostcfg_name)
            return False
        logger.info("Try to delete hosts %s on node %s" % (host_hostcfg_map.values(), host_hostcfg_map.keys()))
        for host_id, hostcfg_name in host_hostcfg_map.items():
            if hostcfg_name.find(hostcfg_name_zk_suffix) != -1:
                logger.info("Delete host[%s] from hostcfg[%s]" % (host_id, hostcfg_name))
                cps_client.do_hostcfg_delete_hosts('storage', hostcfg_name, [host_id])

                origin_hostcfg_name = hostcfg_name[:hostcfg_name.find(hostcfg_name_zk_suffix)]
                if not check_group_exist(cps_client, origin_hostcfg_name):
                    logger.info("Copy hostcfg[%s] from hostcfg[%s]" % (origin_hostcfg_name, hostcfg_name))
                    cps_client.do_hostcfg_add('storage', origin_hostcfg_name, copy_from=hostcfg_name)
                logger.info("Add host[%s] to hostcfg[%s]" % (host_id, origin_hostcfg_name))
                cps_client.do_hostcfg_add_hosts('storage', origin_hostcfg_name, [host_id])
                cps_client.do_commit()

                time.sleep(30)
                logger.info("Perform a delete operation on %s on node %s successfully" % (hostcfg_name, host_id))

    @staticmethod
    def _check_lv_existed(cps_client, hostcfg_zk_name, lvname, roles):
        resp = cps_client.do_hostcfg_show('storage', hostcfg_zk_name)
        logical_volume_infos = resp.get("logical-volume")
        for lv_info in logical_volume_infos:
            if lv_info.get("name") == lvname:
                logger.error("lvname %s is added" % lvname)
                if isinstance(roles, str):
                    roles = roles.split()
                existed_roles = lv_info.get("role")
                if isinstance(existed_roles, str):
                    existed_roles = existed_roles.split()
                if set(roles) <= set(existed_roles):
                    return True, None
                else:
                    return False, existed_roles
        return False, None

    def add_hostcfg_item_storage(self, cps_client, lvname, partition_size, hostcfg_zk_map, partition_dir):
        for hostcfg_zk_name, roles in hostcfg_zk_map.items():
            # 查询是否已经挂载了盘
            lv_existed, existed_roles = self._check_lv_existed(cps_client, hostcfg_zk_name, lvname, roles)
            if lv_existed:
                continue
            if existed_roles:
                # 组内如果已经添加了挂载点，需要先卸载，但不提交，在添加时一起进行提交操作。
                logger.warning("delete %s in hostcfg %s" % (lvname, hostcfg_zk_name))
                del_params = {"name": lvname}
                cps_client.do_hostcfg_item_delete("storage", hostcfg_zk_name, 'logical-volume', del_params)
                logger.error("add roles %s not in %s for lv %s in %s" % (roles, existed_roles, lvname, hostcfg_zk_name))
                roles.extend(existed_roles)
            roles = list(set(roles))
            logger.info("add %s to %s for %s" % (lvname, hostcfg_zk_name, roles))
            # 执行挂载
            add_params = {'lvname': lvname,
                          'size': partition_size,
                          'format': 'ext4',
                          'mount': partition_dir,
                          'role': roles}
            logger.info("Add logic volume %s to hostcfg[%s] in roles %s" % (lvname, hostcfg_zk_name, roles))
            cps_client.do_hostcfg_item_add('storage', hostcfg_zk_name, 'logical-volume', add_params)
            cps_client.do_commit()
            time.sleep(30)

    @staticmethod
    def hostcfg_item_add_zk(cps_client, role, hostcfg_zk_name_set, zk_dir):
        for hostcfg_zk_name in hostcfg_zk_name_set:
            # 先执行卸载，然后再执行挂载
            try:
                del_params = {"name": 'zk_disk'}
                logger.info("Delete existing zk_disk from hostcfg[%s]" % hostcfg_zk_name)
                cps_client.do_hostcfg_item_delete('storage', hostcfg_zk_name, 'logical-volume', del_params)
                cps_client.do_commit()
                time.sleep(30)
            except Exception as e:
                err_msg = "Failed to delete %s. Detail:%s" % (hostcfg_zk_name, str(e))
                logger.error(err_msg)
            # 然后再执行挂载
            add_params = {'lvname': 'zk_disk',
                          'size': '65G',
                          'format': 'ext4',
                          'mount': zk_dir,
                          'role': role}
            logger.info("Add zk_disk to hostcfg[%s]" % hostcfg_zk_name)
            cps_client.do_hostcfg_item_add('storage', hostcfg_zk_name, 'logical-volume', add_params)
            cps_client.do_commit()
            time.sleep(30)

    @staticmethod
    def hostcfg_item_add_ccdb(cps_client, role, hostcfg_zk_name_set, ccdb_dir):
        for hostcfg_zk_name in hostcfg_zk_name_set:
            # 先执行卸载
            try:
                del_params = {"name": 'ccdb_disk'}
                logger.info("Delete existing ccdb_disk from %s" % hostcfg_zk_name)
                cps_client.do_hostcfg_item_delete('storage', hostcfg_zk_name, 'logical-volume', del_params)
                cps_client.do_commit()
                time.sleep(30)
            except Exception as e:
                err_msg = "Failed to delete %s. Detail:%s" % (hostcfg_zk_name, str(e))
                logger.error(err_msg)
            # 然后再执行挂载
            add_params = {'lvname': 'ccdb_disk',
                          'size': '20G',
                          'format': 'ext4',
                          'mount': ccdb_dir,
                          'role': role}
            logger.info("Add ccdb_disk to %s" % hostcfg_zk_name)
            cps_client.do_hostcfg_item_add('storage', hostcfg_zk_name, 'logical-volume', add_params)
            cps_client.do_commit()
            time.sleep(30)

    @staticmethod
    def hostcfg_item_add_ccdb2(cps_client, role, hostcfg_zk_name_set, ccdb2_dir):
        for hostcfg_zk_name in hostcfg_zk_name_set:
            # 先执行卸载
            try:
                del_params = {"name": 'ccdb_disk2'}
                logger.info("Delete existing ccdb_disk2 from %s" % hostcfg_zk_name)
                cps_client.do_hostcfg_item_delete('storage', hostcfg_zk_name, 'logical-volume', del_params)
                cps_client.do_commit()
                time.sleep(30)
            except Exception as e:
                err_msg = "Failed to delete %s. Detail:%s" % (hostcfg_zk_name, str(e))
                logger.error(err_msg)

            # 然后再执行挂载
            add_params = {'lvname': 'ccdb_disk2',
                          'size': '20G',
                          'format': 'ext4',
                          'mount': ccdb2_dir,
                          'role': role}
            logger.info("Add ccdb_disk2 to %s" % hostcfg_zk_name)
            cps_client.do_hostcfg_item_add('storage', hostcfg_zk_name, 'logical-volume', add_params)
            cps_client.do_commit()
            time.sleep(30)

    def check_dir_exist(self, zk_mount_dict_info):
        root_pwd = self.fsa_list[0].get('root_pwd')
        fsp_pwd = self.fsa_list[0].get('passwd')
        user = self.fsa_list[0].get('user')

        for ip in self.metadata_node_manager_ip_list:
            sshclient = ssh.ssh_create_client(ip, user, fsp_pwd)
            ssh.ssh_send_command(sshclient, 'su root', 'assword:', 60)
            ssh.ssh_send_command(sshclient, root_pwd, '#', 60)
            self.check_dir_name(ip, sshclient, zk_mount_dict_info)

            logger.info('check zk mount point, host:{}'.format(ip))
            self.check_zk_mount_point(ip, zk_mount_dict_info, sshclient)
            logger.info('success, host:{}'.format(ip))
            ssh.ssh_close(sshclient)

    @staticmethod
    def check_dir_name(ip, sshclient, zk_mount_dict_info):
        for dir_name in zk_mount_dict_info.values():
            dir_check_cmd = 'test -d %s; echo $?' % dir_name
            rsp = ssh.ssh_send_command(sshclient, dir_check_cmd, "#", 90)
            for line in rsp:
                if (line.__contains__('0') or line.__contains__('1')) and line.split("\n")[0] == '1':
                    err_msg = "Failed to get mount dir %s to host %s" % (dir_name, ip)
                    logger.error(err_msg)
                    raise Exception(err_msg)
            logger.info("Run check dir cmd[%s] out:%s" % (dir_check_cmd, rsp))

    @staticmethod
    def check_zk_mount_point(ip, zk_mount_dict_info, client):
        for mount_key, mount_dir in zk_mount_dict_info.items():
            mount_check_cmd = 'df -h | grep --color=never %s' % mount_key
            res = ssh.ssh_exec_command_return(client, mount_check_cmd)
            if not res:
                ssh.ssh_close(client)
                logger.error("Failed to get mount dir %s, host %s" % (mount_dir, ip))
                raise FCDException(626331, mount_key, ip)

            flag = False
            for mount_dev_info in res:
                mount_info = mount_dev_info.strip().split()
                if mount_dir in mount_info:
                    logger.info('current dir:{}, check result: {}'.format(mount_dir, mount_info))
                    flag = True
                    break
            if not flag:
                ssh.ssh_close(client)
                logger.error("Failed to get mount dir %s, host %s" % (mount_dir, ip))
                raise FCDException(626331, mount_key, ip)

    def get_control_manager_ip(self):
        node_num = 3
        node_info = self.fs_args.get("all_openstack_nodes")
        metadata_node_bmc_ip = list()
        for node in node_info:
            if node.get("is_metadata_node") == "1":
                metadata_node_bmc_ip.append(node.get("bmc_ip"))

        control_manager_ip = list()
        for fsa in self.fsa_list:
            if fsa.get('bmc_ip') in metadata_node_bmc_ip:
                control_manager_ip.append(fsa.get('om_ip'))

        if len(control_manager_ip) < node_num:
            err_msg = "Number of control nodes less than 3"
            raise Exception(err_msg)
        return control_manager_ip

    def independent_disk_mode(self):
        logger.info("Independent_disk mode.")
        cluster_ip_list = self.get_control_manager_ip()
        zk_slot = self.get_cluster_list(scene=self.scene, mode=self.mode)
        logger.info('Creating cluster')
        cluster_name = 'cluster01'
        server_list = list()
        res = self.opr.scan_server_media(cluster_ip_list)
        for ip in cluster_ip_list:
            media_data = res.get_media_by_node_slot(ip, zk_slot, self.all_flash)
            if not media_data:
                err_msg = "Failed to get media by slot[%s] on node[%s]. The disk of slot is not exist" % (zk_slot, ip)
                logger.error(err_msg)
                raise Exception(err_msg)

            if not self.first_metadata_node:
                self.first_metadata_node = ip
            media_type = media_data.get('mediaType')
            self.compare_with_first_value(media_type, self.first_metadata_type, 'type', ip)
            media_size = media_data.get('mediaSize')
            self.compare_with_first_value(media_size, self.first_metadata_size, 'size', ip)
            media_status = media_data.get('mediaRole')
            if media_status != 'no_use':
                err_msg = "Failed to get media by slot[%s] on node[%s]. The disk of slot is not exist" % (zk_slot, ip)
                logger.error(err_msg)
                raise Exception(err_msg)

            if media_type == 'ssd_card':
                zk_disk_esn = media_data.get('phyDevEsn')
                server = {'nodeMgrIp': ip, 'zkType': media_type, 'zkDiskSlot': zk_slot, 'zkDiskEsn': zk_disk_esn}
            else:
                server = {'nodeMgrIp': ip, 'zkType': media_type, 'zkDiskSlot': zk_slot}
            server_list.append(server)
        res = self.opr.create_cluster(cluster_name, server_list)
        result, res_detail = res.get_query_detail()
        if result != 0:
            err_msg = "Failed to Create Cluster, Result:%s, Detail:%s" % (result, res_detail)
            logger.error(err_msg)
            raise Exception(err_msg)

        logger.info('Create cluster successful')
        self.opr.login_out(DeployConstant.DM_LOGIN_USER, self.update_pwd)

    def get_cluster_list(self, scene=None, mode=None):
        nodes = self.fs_args.get('all_openstack_nodes')
        t_zk_slot = 'zk_slot'
        # 获取主节点的数据库值
        primary_slot_list, role_first_node = \
            self._get_role_first_node_info(nodes, scene, mode)
        cache_type_list = self._compare_node_cache_and_zk_slot(
            nodes, role_first_node, primary_slot_list)
        zk_slot = role_first_node[t_zk_slot]
        data_len = 0  # 数据长度
        if zk_slot is None or len(zk_slot) == data_len:
            zk_slot = '2'
        logger.info("all cache type list:%s" % cache_type_list)
        if 'none' in cache_type_list:
            self.all_flash = True
        return zk_slot

    @staticmethod
    def _get_role_first_node_info(nodes, scene, mode):
        role_first_node = None
        t_bmc_role = 'bmc_role'
        t_first_node = 'first_node'
        t_controller = 'controller'
        t_primary_slot = 'primary_slot'
        primary_slot_list = []
        for node in nodes:
            if scene == "RegionConHA":
                if mode == "ha_master" and node[t_bmc_role] == t_first_node or\
                        mode == "ha_standby" and node[t_bmc_role] == t_controller:
                    role_first_node = node
                    primary_slot_list.append(node[t_primary_slot])
                    break
            else:
                if node[t_bmc_role] == t_first_node:
                    role_first_node = node
                    primary_slot_list.append(node[t_primary_slot])
                    break

        if role_first_node is None:
            raise Exception('first_node is not exist')
        return primary_slot_list, role_first_node

    @staticmethod
    def _compare_node_cache_and_zk_slot(nodes, role_first_node, primary_slot_list):
        # 要求first_node 和 controller节点的缓存和主存值要一样
        t_bmc_role = 'bmc_role'
        t_controller = 'controller'
        t_cache_type = 'cache_type'
        t_primary_slot = 'primary_slot'
        t_zk_slot = 'zk_slot'
        same_status = True
        cache_type_list = list()
        cache_type_list.append(role_first_node[t_cache_type])
        for node in nodes:
            if node[t_bmc_role] == t_controller:
                primary_slot_list.append(node[t_primary_slot])
                cache_type_list.append(node[t_cache_type])
                if node[t_cache_type] != role_first_node[t_cache_type]:
                    same_status = False
                    break
                if node[t_zk_slot] != role_first_node[t_zk_slot]:
                    same_status = False
                    break
        if not same_status:
            raise Exception('cache_type,zk_slot must be the same')
        return cache_type_list

    def partition_mode(self):
        logger.info("Start to determine if it is sys_disk_partition_deploy mode or independent_disk_mode")
        zk_slot = 'zk_slot'
        bmc_ip = 'bmc_ip'

        metadata_slot_count = 0
        metadata_node_count = 0

        fsa_om_ip_list = [fsa.get('om_ip') for fsa in self.fsa_list]
        fsa_bmc_ip_list = [fsa.get('bmc_ip') for fsa in self.fsa_list]

        logger.info("The bmc ips of manage metadata node are %s" % str(fsa_bmc_ip_list))
        logger.info("The om ips of manage metadata node are %s" % str(fsa_om_ip_list))

        for node in self.fs_args.get('all_openstack_nodes'):
            ip = node[bmc_ip]
            if node["is_metadata_node"] == "1" and ip in fsa_bmc_ip_list:
                index = fsa_bmc_ip_list.index(ip)
                self.metadata_node_manager_ip_list.append(fsa_om_ip_list[index])
                metadata_node_count += 1
                if node[zk_slot] != '':
                    metadata_slot_count += 1

        # 如果slot数量是0，则分区方式部署，否则磁盘方式部署
        logger.debug("The number of metadata node slot is %s" % metadata_slot_count)
        if metadata_slot_count == 0:
            return True

        if metadata_node_count != metadata_slot_count:
            raise Exception("The number of metadata node slots %d should be the same as the number of metadata nodes %d"
                            % (metadata_slot_count, metadata_node_count))

        return False

    def check_disk_type(self, metadata_node_manager_ip):
        root_pwd = self.fsa_list[0].get('root_pwd')
        fsp_pwd = self.fsa_list[0].get('passwd')
        user = self.fsa_list[0].get('user')

        for ip in metadata_node_manager_ip:
            sshclient = ssh.ssh_create_client(ip, user, fsp_pwd)
            ssh.ssh_send_command(sshclient, 'su root', 'assword:', 60)
            ssh.ssh_send_command(sshclient, root_pwd, '#', 60)

            cmd = 'mount | grep " / " | grep "/dev/" | awk \'{ print $1 }\''
            tmp = ssh.ssh_send_command(sshclient, cmd, "dev", 90)
            logger.info("Run cmd[%s] out:%s" % (cmd, tmp))
            disk_info = self.get_disk_info(sshclient, tmp)
            cmd = 'cat /sys/block/%s/queue/rotational' % disk_info[0:3]
            disk_type = ssh.ssh_send_command(sshclient, cmd, "\n", 90)
            logger.info("Run cmd[%s] out:%s" % (cmd, disk_type))
            for s in disk_type:
                if s.__contains__('0') or s.__contains__('1'):
                    disk_type = s.split("\n")[0]
                    break
            ssh.ssh_close(sshclient)
            # 如果返回1则表示磁盘可旋转，那么就是HDD了；反之，如果返回0，则表示磁盘不可以旋转，那么是SSD了
            if disk_type != '0':
                msg = "System disk type must be SSD for zk partition deployment, but now is HDD[%s]" % disk_type
                logger.error(msg)
                raise Exception(msg)
            logger.info("Check disk type in nodes [%s] success." % ",".join(metadata_node_manager_ip))

    def get_disk_info(self, sshclient, tmp):
        disk_info = ''
        for line in tmp:
            if not line.__contains__('/mapper/'):
                continue
            dm_device = self.get_dm_device(line, sshclient)
            cmd = 'cat /proc/partitions|awk \'{print $1":"$2" "$4}\'|grep -w %s|awk \'{print $2}\'' % dm_device
            disk_info = ssh.ssh_send_command(sshclient, cmd, "sd", 90)
            logger.info("Run cmd[%s] out:%s" % (cmd, disk_info))
            for s in disk_info:
                if s.__contains__("sd"):
                    disk_info = s.split("\n")[0]
                    break
        device_idx = 2
        if disk_info == '':
            for line in tmp:
                if line.__contains__('/dev/'):
                    disk_info = line.split("\n")[0].split("/")[device_idx]
                    break
        return disk_info

    @staticmethod
    def get_dm_device(line, sshclient):
        str_list = line.split("\n")[0].split("/")
        dm_rootfs = str_list[str_list.index('mapper') + 1]
        cmd = 'dmsetup table |grep %s |awk \'{print $5}\' |sed -n \'1p\'' % dm_rootfs
        dm_device = ssh.ssh_send_command(sshclient, cmd, ":", 90)
        logger.info("Run cmd[%s] out:%s" % (cmd, dm_device))
        for s in dm_device:
            if s.__contains__(':'):
                dm_device = s.split("\n")[0]
                break
        return dm_device

    def compare_with_first_value(self, src_value, cmp_value, value_type, node_ip):
        if not cmp_value:
            if value_type == 'type':
                self.first_metadata_type = src_value
            else:
                self.first_metadata_size = src_value
            return
        if src_value != cmp_value:
            err_msg = "The %s of metadata disk on the node[%s] is inconsistent with the first node[%s]." \
                      "Detail: current node:%s,first node:%s" % (
                          value_type, node_ip, self.first_metadata_node, cmp_value, src_value)
            logger.error(err_msg)
            raise Exception(err_msg)
        return

    def create_cluster(self, zk_dir):
        logger.info("Start to create control cluster.")
        host_ip = self.float_ip

        # 编辑控制集群xml配置文件参数
        cluster_host_info_lst = []
        for ip in self.metadata_node_manager_ip_list:
            cluster_host_info_lst.append(
                {'node_mgr_ip': ip, 'zk_type': 'dir', 'zk_disk_slot': "*",
                 'zk_partition': "*", 'zk_disk_esn': '*', 'zk_dir': zk_dir})

        # 初始化ssh client端
        sshclient = ssh.ssh_create_client(host_ip, "dsware", self.dsware_passwd)

        self.edit_and_upload_xml(cluster_host_info_lst)

        # 创建控制集群
        self.create(sshclient)
        ssh.ssh_close(sshclient)
        logger.info("Create control cluster successfully.")

    def create(self, sshclient):
        expect = 'This operation is high risk,please input y to continue:'
        create_control_cluster_cmd = "/opt/dsware/client/bin/dswareTool.sh --op createManageCluster " \
                                     "-f /opt/dsware/client/conf/manage_cluster.xml"
        ssh.ssh_send_command(sshclient, create_control_cluster_cmd, expect, 90)
        ssh.ssh_send_command(sshclient, "y", "Enter User Name:", 60)
        ssh.ssh_send_command(sshclient, "admin", "Enter Password :", 30)
        resp = ssh.ssh_send_command(sshclient, self.update_pwd, "DswareTool operation end", 600)
        time.sleep(60)
        logger.info("Create control cluster cmd[%s], result: %s" % (create_control_cluster_cmd, str(resp)))

        flag = False
        for line in resp:
            if line.__contains__("Operation finish successfully"):
                flag = True
                break
        if not flag:
            raise Exception(
                "create control cluster failed, the resp:%s, the float_ip:%s" % (resp, self.float_ip))

    def edit_and_upload_xml(self, cluster_host_info_lst):
        host_ip = self.float_ip

        cluster_name = "cluster_01"
        file_name = "manage_cluster.xml"
        remote_cluster_xml_dir = '/opt/dsware/client/conf/'
        remote_cluster_xml = os.path.join(remote_cluster_xml_dir, file_name)
        local_cluster_xml_dir = os.path.join(get_cur_path(), 'Tmp', 'FusionStorage')
        local_cluster_xml = os.path.join(local_cluster_xml_dir, file_name)

        if not os.path.exists(local_cluster_xml_dir):
            # 本地xml文件目录如果不存在则创建
            os.makedirs(local_cluster_xml_dir)

        # 下载控制集群xml配置文件模板
        if ssh.get_file(host=host_ip, user='dsware', passwd=self.dsware_passwd, source=remote_cluster_xml,
                        destination=local_cluster_xml):
            logger.info(
                "Download file from [%s:%s] to local %s success." % (host_ip, remote_cluster_xml, local_cluster_xml))
        # 编译xml文件
        tree = ElementTree()
        tree.parse(local_cluster_xml)

        tree.find('clusterName').text = cluster_name
        server_xml_lst = tree.find('serverList')
        server_template = server_xml_lst.find('server')
        server_xml_lst.clear()
        for cluster_server_info in cluster_host_info_lst:
            server_copy = copy.deepcopy(server_template)
            server_copy.find('nodeMgrIp').text = cluster_server_info.get('node_mgr_ip')
            server_copy.find('zkType').text = cluster_server_info.get('zk_type')
            server_copy.find('zkDiskSlot').text = cluster_server_info.get('zk_disk_slot')
            server_copy.find('zkPartition').text = cluster_server_info.get('zk_partition')
            server_copy.find('zkDiskEsn').text = cluster_server_info.get('zk_disk_esn')
            server_copy.find('zkDir').text = cluster_server_info.get('zk_dir')
            server_xml_lst.append(server_copy)
        # 写入xml文件
        tree.write(local_cluster_xml, 'utf-8', xml_declaration=True)
        # 上传xml文件到fsm节点对应目录
        if ssh.put_file(host=host_ip, user='dsware', passwd=self.dsware_passwd, source=local_cluster_xml,
                        destination=remote_cluster_xml_dir):
            logger.info("Upload file %s to host [%s:%s] success."
                        % (local_cluster_xml, host_ip, remote_cluster_xml_dir))

    def clean_master_cluster(self):
        status_code, error_code, error_des = self.opr.login(DeployConstant.DM_LOGIN_USER, self.update_pwd)
        if status_code != 200 or error_code != 0:
            err_msg = "Failed to login deploy manager, " \
                      "Detail:[status:%s,code:%s]%s" % (status_code, error_code, error_des)
            logger.error(err_msg)
            raise Exception(err_msg)

        logger.info('Check cluster...')
        response = self.opr.query_manage_cluster()
        cluster = response.get_query_data()
        if not cluster.get('clusterName'):
            logger.info('cluster not existed!')
        else:
            logger.info('Query cluster successfully...')
            # 删除控制集群
            response = self.opr.delete_manage_cluster()
            if response.get("result") != 0:
                err_msg = "Failed to delete manage cluster, ret %s" % response
                raise Exception(err_msg)

    def rollback(self):
        try:
            logger.info("Start to rollback create cluster.")
            self.clean_master_cluster()
        except FCDException as e:
            logger.error(traceback.format_exc())
            return Message(500, e)
        except Exception as e:
            return Message(500, FCDException(626002, str(e)))
        finally:
            self.opr.logout()
        return Message()
