# -*- coding: utf-8 -*-
import math
import utils.common.log as logger


class DeviceResource(object):
    def __init__(self):
        pass

    # 筛选主存槽位
    def get_storage_pool_slot(self, slot_list):
        if 0 == len(slot_list):
            raise Exception("The list of primary pool slot is empty")
        storage_slot = slot_list[0]
        for slot in slot_list:
            if slot.find('-') < 0 or not slot.split('-')[0].isdigit() or not slot.split('-')[1].isdigit():
                raise Exception("The format of primary pool slot is not correct")
            if storage_slot != slot:
                slot_start = int(slot.split('-')[0])
                slot_end = int(slot.split('-')[1])
                old_slot_start = int(storage_slot.split('-')[0])
                old_slot_end = int(storage_slot.split('-')[1])
                old_slot_start = min(slot_start, old_slot_start)
                old_slot_end = max(slot_end, old_slot_end)
                storage_slot = str(old_slot_start) + '-' + str(old_slot_end)
        return storage_slot

    def get_fsa_memory_800(self, storage_slot, cache_capacity, primary_capacity,
                           storagepool_redundancy_policy='3Redundancy',
                           total_ec_data_and_verify=0, deploy_rep=False, is_none_cache=False, condition='businessFB'):
        """
        基础服务：5G
        VBS服务：4GB，不管VBS是分离部署(部署在计算节点)，还是融合部署(部署在存储节点)，所耗内存都要在存储节点上算进去，
                VBS的任何部署形态内存耗费都一样
        主存盘：包括：a、IO数据占用：12GB，b、MDC：2GB，c、盘消耗：192MB固定消耗+34MB/TB*盘容量(TB)；
               总内存消耗：12GB+2GB+(192MB+34MB/TB*盘容量)*盘数量
        缓存盘：SSD作为缓存时内存占用为512MB/TB，总的内存消耗=512MB/TB*缓存盘大小(TB)*(实插主存盘数量/节点最大盘规格) ，
               如12盘位型号节点，实插盘位4块，那么总内存消耗=512MB/TB*缓存盘大小(TB)*(4/12) ；
        EDS服务：dsc_cache：前端IO写缓存空间，rcache：前端IO读缓存空间，meta_cache：index+dedup元数据空间；
                基础数据结构内存占用：8GB；EDS总内存消耗=dsc_cache+rcache+meta_cache+8GB
        复制服务：8GB，复制独立部署：64GB内存
        :param storage_slot:主存槽位
        :param cache_capacity:缓存容量
        :param primary_capacity:主存容量
        :param storagepool_redundancy_policy:存储池资源策略
        :param total_ec_data_and_verify:ec分片+校验分片的和
        :param deploy_rep:是否是复制场景
        :param is_none_cache:是否是全闪场景
        :return: 所需内存容量
        """
        # [1:[max_memory, off_reduce_limit, on_reduce_limit, dsc_cache, rcache]]
        meta_cache_map = [[64, 16, 16, 4, 1.5],
                          [80, 18, 18, 5.3, 1.5],
                          [96, 24, 24, 6.6, 2],
                          [128, 28, 28, 8, 2],
                          [192, 48, 48, 12, 2],
                          [256, 68, 68, 16, 3],
                          [384, 96, 96, 20, 3],
                          [512, 96, 96, 24, 3.5],
                          ['-', 96, 96, 32, 3.5]]

        # 数据盘个数计算
        slot_start = int(storage_slot.split('-')[0])
        slot_end = int(storage_slot.split('-')[1])
        num = slot_end - slot_start + 1
        min_num = 4
        if num < min_num:
            logger.error("Main memory number is less than 4")
            raise Exception("Main memory number is less than 4")
        # 管理存储默认关闭重删压缩
        if condition == 'manageFB':
            reduce_switch = 'disable'
        else:
            reduce_switch = 'enable'

        meta_cache_map_index = 0
        while True:
            fsa_memory = self.get_total_memory(primary_capacity, is_none_cache, cache_capacity, num,
                                               meta_cache_map[meta_cache_map_index], reduce_switch, deploy_rep)
            if meta_cache_map[meta_cache_map_index][0] == '-':
                return fsa_memory
            if fsa_memory >= meta_cache_map[meta_cache_map_index][0]:
                meta_cache_map_index = self.search_meta_cache_map_idx(fsa_memory, meta_cache_map, meta_cache_map_index)
            else:
                return fsa_memory

    @staticmethod
    def search_meta_cache_map_idx(fsa_memory, meta_cache_map, meta_cache_map_index):
        for i in range(meta_cache_map_index, len(meta_cache_map)):
            if fsa_memory < meta_cache_map[i][0]:
                meta_cache_map_index = i
                logger.info("meta cache index %d " % meta_cache_map_index)
                break
        return meta_cache_map_index

    def get_total_memory(self, primary_capacity, is_none_cache,
                         cache_capacity, num, meta_cache_map_item, reduce_switch, deploy_rep):
        primary_capacity = float(math.ceil(primary_capacity))

        # 基础服务内存
        base_software_memory = 5

        # VBS进程占用内存容量(只支持TCP/IP网络)
        vbs_software_memory = 4

        # EDS软件占用的内存
        eds_software_memory = self.get_eds_soft_memory(meta_cache_map_item, reduce_switch)

        # OSD软件内存容量
        osd_software_memory = self.get_osd_soft_memory(primary_capacity, is_none_cache, cache_capacity, num)

        # 内存容量 = vbs + eds + osd + base
        fsa_memory = vbs_software_memory + eds_software_memory + osd_software_memory + base_software_memory
        logger.info("fsa memory(%f)= vbs(%f) + eds(%f) + osd(%f) + base(%f)"
                    % (fsa_memory, vbs_software_memory, eds_software_memory, osd_software_memory, base_software_memory))
        if fsa_memory < 35:
            # 计算得出的值小于最小值35GB时取最小值35
            fsa_memory = 35
        # 复制节点和存储节点采用同一服务器时：容量=vbs + eds + osd + rep(8GB) + base(5)
        if deploy_rep:
            fsa_memory += 8
        logger.info("The calculated memory of node for ConfigBlockStorage is %s GB" % fsa_memory)
        fsa_memory = int(math.ceil(fsa_memory))
        logger.info("The memory of node for ConfigBlockStorage is %s GB" % fsa_memory)
        return fsa_memory

    @staticmethod
    def get_eds_soft_memory(meta_cache_map_item, reduce_switch):
        eds_base_memory = 8
        eds_dsc_cache_memory = meta_cache_map_item[3]
        eds_rcache_memory = meta_cache_map_item[4]
        eds_cache_memory = eds_dsc_cache_memory + eds_rcache_memory
        if reduce_switch == 'disable':
            eds_meta_cache_memory = meta_cache_map_item[1]
        else:
            eds_meta_cache_memory = meta_cache_map_item[2]
        eds_software_memory = eds_base_memory + eds_meta_cache_memory + eds_cache_memory
        logger.info("eds memory(%f) = eds_base_memory(%f) + eds_meta_cache_memory(%f) + eds_cache_memory(%f)" %
                    (eds_software_memory, eds_base_memory, eds_meta_cache_memory, eds_cache_memory))
        return eds_software_memory

    @staticmethod
    def get_osd_soft_memory(primary_capacity, is_none_cache, cache_capacity, num):
        # 基础服务内存容量
        mdc_base_memory = 2
        io_base_memory = 12

        # osd进程内存容量
        osd_memory = 0.192 + 0.034 * primary_capacity

        # 缓存内存容量
        ssd_cache_memory = 0
        # 缓存类型不是none时
        if not is_none_cache:
            max_primary_num = [12, 24, 25, 36, 60]
            for index, item in enumerate(max_primary_num):
                if num <= item:
                    scale = item
                    break
            else:
                scale = 60
            ssd_cache_memory = float(cache_capacity) * num * 0.5 / 1000 / scale

        osd_software_memory = mdc_base_memory + io_base_memory + num * osd_memory + ssd_cache_memory
        logger.info("osd memory(%f) = mdc_base_memory(%d) + io_base_memory(%d) + osd_memory(%f) + ssd_cache_memory(%f)"
                    % (osd_software_memory, mdc_base_memory, io_base_memory, num * osd_memory, ssd_cache_memory))
        return osd_software_memory
