# -*- coding: utf-8 -*-
import time
import utils.common.log as logger
from utils.common.exception import FCDException as FCUException
from plugins.DistributedStorage.scripts.logic.UpgradeOperate import UpgradeOperate
from utils.common.fic_base import TestCase
import traceback


class UpgradePkg(TestCase):
    def __init__(self, project_id, pod_id, fs_args, condition=None,
                 metadata=None, **kwargs):
        super(UpgradePkg, self).__init__(project_id, pod_id)
        self.condition = condition
        self.metadata = metadata
        self.more_args = kwargs
        self.opr = UpgradeOperate(fs_args)
        self.user_name = fs_args.get("user_name", "admin")
        self.password = fs_args["dm_update_pwd"]

    def procedure(self):
        logger.info('Start rollback task.')
        try:
            status_code, error_code, error_des = self.opr.try_login(
                self.user_name, self.password)
            if status_code != 200 or error_code != 0:
                err_msg = "Failed to login, Detail:[status:%s,code:%s]%s" \
                          % (status_code, error_code, error_des)
                logger.error(err_msg)
                raise Exception(err_msg)
            rsp_result, rsp_data = self.opr.get_product()
            if rsp_result.get("code") != 0:
                err_msg = "Failed to get product, Detail:[result:%s]%s" % (
                    rsp_result, rsp_data)
                logger.error(err_msg)
                raise FCUException(626313, err_msg)
            version = rsp_data.get("version")
            if version != "8.0.1.SPC600":
                logger.error("version is %s, skip!" % rsp_data)
                return

            self._check_upgrade_status()

            osd_nodes_list, vbs_nodes_list = self._get_server_list()

            self._do_upgrade_task()

            logger.info('get rollback upgrade task info.')
            osd_timeout = 300*len(osd_nodes_list)
            vbs_timeout = 300 * (len(vbs_nodes_list)/20 + 1)
            check_timeout = 40*60 + osd_timeout + vbs_timeout
            self._wait_upgrade_result(check_timeout)

        except FCUException as e:
            logger.error(traceback.format_exc())
            raise e
        except Exception as e:
            logger.error(traceback.format_exc())
            raise FCUException(626316, str(e))
        finally:
            logger.info('procedure end.')

    def _wait_upgrade_result(self, check_timeout):
        max_retry_times = 30
        failed_nums = 0
        ret_result = None
        ret_data = None
        while check_timeout > 0:
            try:
                ret_result, ret_data = self.opr.get_upgrade_task()
            except Exception as ex:
                err_msg = "get rollback task failed, Exception: %s" \
                          % (str(ex))
                logger.error(err_msg)
                failed_nums += 1
                if failed_nums > max_retry_times:
                    logger.error("failed number ({}) too more"
                                 .format(failed_nums))
                    raise
            else:
                failed_nums = 0
                if 0 == self._check_upgrade_result(ret_result, ret_data):
                    break
            time.sleep(10)
            check_timeout -= 10
        if check_timeout <= 0:
            err_msg = "upgrade rollback timeout, status:{status}, " \
                      "progress:{progress}. result:{ret_result}, " \
                      "data: {ret_data}" \
                .format(status=ret_data["status"],
                        progress=ret_data["percent"],
                        ret_result=ret_result, ret_data=ret_data)
            logger.error(err_msg)
            raise FCUException(626300, err_msg)

    def _check_upgrade_status(self):
        logger.info("check upgrade status.")
        ret_result, ret_data = self.opr.get_upgrade_status()
        if ret_result["code"] != '0':
            err_msg = "get upgrade status failed, " \
                      "Detail:[result:%s, data:%s]"\
                      % (ret_result, ret_data)
            logger.error(err_msg)
            raise Exception(err_msg)
        elif ret_data["currentPhase"] == "upgrading":
            logger.info("current is upgrading")

    def _get_server_list(self):
        osd_nodes_list = []
        vbs_nodes_list = []
        ret_result, ret_data = self.opr.get_servers()
        if ret_result["code"] != 0:
            err_msg = "get servers failed, " \
                      "Detail:[result:%s, data:%s]" \
                      % (ret_result, ret_data)
            logger.error(err_msg)
            raise Exception(err_msg)
        for item in ret_data:
            if "storage" in item["role"]:
                osd_nodes_list.append(item["management_ip"])
            if "compute" in item["role"]:
                vbs_nodes_list.append(item["management_ip"])
        return osd_nodes_list, vbs_nodes_list

    def _do_upgrade_task(self):
        logger.info('start upgrade task.')
        ret_result, ret_data = self.opr.rollback_upgrade_task()
        if ret_result["code"] != '0':
            err_msg = "rollback upgrade task failed, " \
                      "Detail:[result:%s, data:%s]" \
                      % (ret_result, ret_data)
            logger.error(err_msg)
            raise Exception(err_msg)

    def _check_upgrade_result(self, ret_result, ret_data):
        if ret_result["code"] != '0':
            err_msg = "get rollback task failed, " \
                      "Detail:[result:%s, data:%s]" \
                      % (ret_result, ret_data)
            logger.error(err_msg)
            raise Exception(err_msg)
        if ret_data["status"] == "failed":
            failed_map = self.get_failed_component(
                ret_data["result"])
            err_msg = "rollback {} failed".format(failed_map)
            logger.error(err_msg)
            raise Exception(err_msg)
        elif ret_data["status"] == "success":
            logger.info("upgrade success!")
            return 0
        elif ret_data["status"] == "running":
            logger.info("rollback is running. Progress is {}%"
                        .format(ret_data["percent"]))
        else:
            logger.info("get upgrade task status {status}. "
                        "result: {ret_result}, data: {ret_data}"
                        .format(status=ret_data["status"],
                                ret_result=ret_result,
                                ret_data=ret_data))
        return 1

    @staticmethod
    def get_failed_component(data_result):
        failed_component_map = dict()

        def _get_failed_component():
            component_info = sequence.get("componentInfoList")
            for component in component_info:
                if component.get("componentStatus") == "failed":
                    failed_component_list.append(component.get("name"))

        for node in data_result:
            if node["status"] != "failed":
                continue
            node_ip = node["hostIp"]
            sequence_infos = node["sequenceInfos"]
            failed_component_list = []
            for sequence in sequence_infos:
                if sequence.get("status") == "failed":
                    _get_failed_component()
            failed_component_map[node_ip] = failed_component_list
        return failed_component_map
