# -*- coding:utf-8 -*-
import os
import requests
import time
from utils.common.fic_base import StepBaseInterface
import utils.common.log as logger
from utils.common.exception import FCUException
from utils.common.message import Message
from utils.common.ssh_util import Ssh
from plugins.eBackup.common.util import CommonConfig
from plugins.eBackup.common.util import ManageOneOcApi
from plugins.eBackup.common.util import Utils
from utils.business.manageone_cmdb_util import ManageOneCmdbUtil


class ConfigeBackup(StepBaseInterface):
    def __init__(self, project_id, pod_id, regionid_list=None):
        super(ConfigeBackup, self).__init__(project_id, pod_id, regionid_list)
        self.project_id = project_id
        self.pod_id = pod_id
        self.regionid_list = regionid_list
        self.param_dict = Utils.init_system_params(project_id,
                                                   regionid_list[0])

    def execute(self, project_id, pod_id, regionid_list=None):
        try:
            self.clear_history_cmd = ">~/.bash_history&&history -c"
            self.sshObj = Ssh()
            logger.info("Begin to report system info to manageone.")
            self.hcp_password = self.param_dict["eBackup_hcp_pwd"]
            self.root_password = self.param_dict["eBackup_root_pwd"]
            datamover_hosts = self.param_dict["eBackup_Datamover_nodes"]
            self.ebackup_workflow_ips = self.param_dict[
                "eBackup_Workflow_nodes"].split(';')
            self.workflow_float_ip = Utils.find_float_ip(
                self.ebackup_workflow_ips, self.hcp_password,
                self.root_password)
            self.modify_alarm_region_conf(project_id)
            ntp_config_result = self.modify_ntp_conf()
            if not ntp_config_result:
                raise Exception("failed to configure the NTP service.")
            self.workflow__url = \
                'https://' + self.workflow_float_ip + ':8088/rest/dev'
            service_system_info = []
            workflow_system_info = {
                "name": self.param_dict[
                    "current_region_id"] + '_' + "eBackup-Manager",
                "alias": self.param_dict[
                    "current_region_id"] + '_' + "eBackup-Manager",
                "product_name": "eBackup",
                "product_version": self.param_dict["eBackup_Version"],
                "product_patch_version": [],
                "ipaddress": self.workflow_float_ip
            }
            logger.info("eBackup-Manager info:" + str(workflow_system_info))
            service_system_info.append(workflow_system_info)
            for index, group in enumerate(datamover_hosts.split('|')):
                ebackup_datamover_ips = group.split(';')
                datamover_float_ip = Utils.find_float_ip(ebackup_datamover_ips,
                                                         self.hcp_password,
                                                         self.root_password)
                datamover_system_info = {
                    "name": self.param_dict[
                        "current_region_id"] + '_' + "eBackup-Server",
                    "alias": self.param_dict[
                        "current_region_id"] + '_' + "eBackup-Server",
                    "product_name": "eBackup",
                    "product_version": self.param_dict["eBackup_Version"],
                    "product_patch_version": [],
                    "ipaddress": datamover_float_ip
                }
                logger.info("eBackup-Server %s  info:%s " %
                            (index + 1, str(datamover_system_info)))
                service_system_info.append(datamover_system_info)
            self.mo_api = ManageOneOcApi(project_id,
                                         self.param_dict["current_region_id"])
            if not self.mo_api.login():
                logger.error("Login to OC failed.")
                return Message(500, "Login to OC failed.")
            if not self.mo_api.report_system_info(service_system_info):
                logger.error("Report system info to OC failed.")
                self.mo_api.logout()
                return Message(500, "Report system info to OC failed.")
            self.mo_api.logout()
            logger.info("Report system info to manageone successfully.")

            return Message(200)
        except FCUException as ex:
            logger.error(str(ex))
            return Message(500, ex)
        except Exception as ex:
            logger.error(str(ex))
            return Message(500, error_msg_cn="升级后配置出现异常，请联系技术支持",
                           error_msg_en="Exception occurs when config eBackup,"
                                        "please contact support engineers")

    def ping(self, host_ip):
        import subprocess
        if ":" in host_ip:
            ping_act = "ping6"
        else:
            ping_act = "ping"
        cmd = "%s %s -n 2" % (ping_act, host_ip)
        (err_output, std_output) = subprocess.getstatusoutput(cmd)
        std_output = std_output.decode() if isinstance(std_output,
                                                       bytes) else std_output
        logger.info("std_output:%s" % str(std_output))
        if std_output.find('TTL=') >= 0:
            return True
        return False

    def reboot_for_pvscan(self):
        logger.info("start to reboot for pvscan")
        host_info = self.param_dict["eBackup_Datamover_nodes"]
        host_info = host_info.replace(" ", "").split("|")
        ip_list = []
        for group in host_info:
            host_ips = group.split(";")
            ip_list += host_ips

        for ebk_ip in ip_list:
            try:
                self.sshObj.ssh_cmds(
                    ebk_ip, "reboot", "hcp", self.hcp_password,
                    self.root_password, "", "")
            except Exception:
                logger.info("reboot %s succ." % ebk_ip)
        time.sleep(30)
        failed_ip_list = []
        num = 0
        while num < 60:
            failed_ip_list = []
            for server_ip in ip_list:
                logger.info("begin to ping:" + server_ip)
                if not self.ping(server_ip):
                    logger.error(server_ip + " can not be connected.")
                    failed_ip_list.append(server_ip)
            if len(failed_ip_list) == 0:
                break
            time.sleep(10)
            num = num + 1

        if len(failed_ip_list) != 0:
            ip_str = ','.join(failed_ip_list)
            logger.info("%s can not be connect." % ip_str)
            return False
        return True

    def modify_alarm_region_conf(self, project_id):
        logger.info("start to get om_ip")
        self.cmdb_util = ManageOneCmdbUtil(project_id)
        region_infos = self.cmdb_util.get_region_info(
            self.param_dict['current_region_id'])[0]
        region_name = region_infos.get('name', '')

        ip_list = []
        host_info = self.param_dict["eBackup_Datamover_nodes"]
        logger.info("eBackup_Datamover_nodes:%s." % str(host_info))
        host_info = host_info.replace(" ", "").split("|")
        for group in host_info:
            host_ips = group.split(";")
            ip_list += host_ips
        ip_list += self.ebackup_workflow_ips
        logger.info("all nodes:%s." % str(ip_list))
        for ebk_ip in ip_list:
            cmd = "sed -i 's/CurrentRegion=.*/CurrentRegion=%s/g'" \
                  " /opt/huawei-data-protection/ebackup/microservice" \
                  "/ebk_alarm/conf/hcpconf.ini" % region_name

            self.sshObj.ssh_cmds(
                ebk_ip, cmd, "hcp", self.hcp_password,
                self.root_password, "", "")
        return True

    def modifyconfigforremotecopy(self):
        for ebk_ip in self.ebackup_workflow_ips:
            cmd = "sed -i 's/FspScene=0/FspScene=1/g' /opt" \
                  "/huawei-data-protection/ebackup/microservice" \
                  "/ebk_openstack_vm/conf/hcpconf.ini"

            self.sshObj.ssh_cmds(
                ebk_ip, cmd, "hcp", self.hcp_password,
                self.root_password, "", "")
        return True

    def modify_iam_config(self):
        logger.info("begin to config openstack(%s)." % self.iam_address)

        token, session = Utils.login(self.workflow_float_ip,
                                     self.param_dict['eBackup_admin_pwd'])
        self.header = {
            'Accept': 'application/json;version=2.2;charset=UTF-8',
            'iBaseToken': token,
            'Accept-Language': 'en',
            'Cookie': 'language=en; %s;DEVICE_ID=dev;sessionIdleTime=60000;'
                      'MACHINEROLE=2;CSRF_IBASE_TOKEN=%s' % (session, token)}
        self.openstack_url = self.workflow__url + '/openstack_settings/default'

        self.auth_user_name, nova_url, cinder_url, neutron_url = \
            self.get_openstack_config()
        data = '{"AUTHTYPE": "IAM", "AUTHURL": "' + self.iam_address + \
               '", "AUTHUSERNAME": "' + self.auth_user_name + \
               '", "AUTHPASSWORD": "' + \
               self.param_dict['eBackup_iam_password'] + \
               '", "CINDERURL": "' + cinder_url + \
               '", "NEUTRONURL": "' + neutron_url + \
               '", "NOVAURL": "' + nova_url + '"}'
        rsp = requests.put(self.openstack_url, headers=self.header,
                           data=data, verify=False)
        if rsp.status_code != 200:
            logger.error("rest req return not 200.")
            return False
        errcode = rsp.json()["error"]["code"]
        description = rsp.json()["error"]["description"]
        logger.info("config openstack result:%s." % str(rsp.json()))
        if errcode is None:
            return False
        if errcode != 0:
            logger.error("modify openstack cofnig failed, "
                         "description:" + description)
            return False
        logger.info("config openstack succ.")
        return True

    def get_openstack_config(self):
        rsp = requests.get(self.openstack_url, headers=self.header,
                           verify=False)
        if rsp.status_code != 200:
            logger.error("rest req return not 200.")
            return
        errcode = rsp.json()["error"]["code"]
        description = rsp.json()["error"]["description"]
        if errcode is None or description is None:
            logger.error("errcode or description is empty.")
            return
        if errcode != 0:
            logger.error(
                "get openstack config failed,description:" + description)
            return
        else:
            data_info = rsp.json()["data"]
            logger.info("get openstack config succ(%s)." % data_info)
            auth_user_name = data_info.get('AUTHUSERNAME', '')
            nova_url = data_info.get('NOVAURL', '')
            cinder_url = data_info.get('CINDERURL', '')
            neutron_url = data_info.get('NEUTRONURL', '')
            return auth_user_name, nova_url, cinder_url, neutron_url

    def modify_cert_and_pwd_config(self):
        self.tmp_cert_endpoint = "https://" + self.cert_endpoint
        self.tmp_oc_endpoint = "https://" + self.oc_endpoint
        logger.info("begin to config cert and pwd(%s)." % self.oc_endpoint)
        file_path = os.path.realpath(
            __file__ + '/../bin/RegisterCertToManageone.sh')
        ip_list = []
        ip_list.append(self.ebackup_workflow_ips[0])

        host_info = self.param_dict["eBackup_Datamover_nodes"]
        host_info = host_info.replace(" ", "").split("|")
        for group in host_info:
            host_ips = group.split(";")
            ip_list.append(host_ips[0])

        for ebk_ip in ip_list:
            self.sshObj.put_file(
                ebk_ip, "hcp",
                self.hcp_password, file_path, ".", 22)
            f_cmd = "; echo execute ebk_cmd result: $?"
            a = "'" + self.tmp_cert_endpoint + "'"
            b = "'" + self.regionid_list[0] + "'"
            c = "'" + self.auth_user_name + "'"
            d = "'" + self.param_dict['eBackup_iam_password'] + "'"
            e = "'" + self.param_dict['eBackup_admin_pwd'] + "'",
            f = "'" + self.tmp_oc_endpoint + "'"
            cmd = ' chmod 755 RegisterCertToManageone.sh;' \
                  'dos2unix RegisterCertToManageone.sh;' \
                  '/bin/bash RegisterCertToManageone.sh %s %s %s %s %s ' \
                  '%s' % (a, b, c, d, e, f) + "%s" % f_cmd
            result = self.sshObj.ssh_cmds(
                ebk_ip, cmd, "hcp", self.hcp_password,
                self.root_password, "", "")
            if "execute ebk_cmd result: 0" not in result:
                logger.error(
                    "config cert and pwd on node(%s) failed,description:%s" %
                    (ebk_ip, result))
                return False

            cmd = self.clear_history_cmd + "&&rm -rf /home/hcp" \
                                           "/RegisterCertToManageone.sh"
            self.sshObj.ssh_cmds(ebk_ip, cmd, "hcp",
                                 self.hcp_password, self.root_password, "", "")
            logger.info("config cert and pwd succ.")
            return True

    def modify_alarm_config(self):
        data = '{"ISALARMON":1,"ALARMSERVICETYPE":"1","ALARMSERVERURL":"' + \
               self.oc_endpoint + '","ALARMUSERNAME":"' + \
               self.mo_api.oc_username + '","ALARMPASSWORD":"' + \
               self.mo_api.oc_password + '"}'
        logger.info("begin to config alarm(%s)." % self.oc_endpoint)

        url = self.workflow__url + '/alarm_settings/default'
        rsp = requests.put(url, headers=self.header, data=data, verify=False)
        if rsp.status_code != 200:
            logger.error("rest req return not 200.")
            return False
        errcode = rsp.json()["error"]["code"]
        description = rsp.json()["error"]["description"]
        if errcode is None or description is None:
            logger.error("errcode or description is empty.")
            return False
        if errcode != 0:
            logger.error(
                "config alarm report failed,description:" + description)
            return False
        else:
            logger.info("config alarm report succ.")
            return True

    def modify_syslog_config(self):
        data = '{"SERVERURL":"' + self.syslog_endpoint + '","ISON":1}'
        logger.info("begin to config syslog(%s)." % data)

        url = self.workflow__url + '/syslog_settings/default'
        rsp = requests.put(url, headers=self.header, data=data, verify=False)
        if rsp.status_code != 200:
            logger.error("rest req return not 200.")
            return False
        errcode = rsp.json()["error"]["code"]
        description = rsp.json()["error"]["description"]
        if errcode is None or description is None:
            logger.error("errcode or description is empty.")
            return False
        if errcode != 0:
            logger.error(
                "config syslog failed,description:" + description)
            return False
        else:
            logger.info("config syslog succ.")
            return True

    def modify_ntp_conf(self):
        ip_list = []
        host_info = self.param_dict["eBackup_Datamover_nodes"]
        logger.info("eBackup_Datamover_nodes:%s." % str(host_info))
        host_info = host_info.replace(" ", "").split("|")
        for group in host_info:
            host_ips = group.split(";")
            ip_list += host_ips
        ip_list += self.ebackup_workflow_ips
        logger.info("all nodes:%s." % str(ip_list))
        for ebk_ip in ip_list:
            logger.info("The NTP node to be configured is %s ." % ebk_ip)
            f_cmd = "; echo execute ntp config result: $?"
            ntp_cmd = """sed -i '/maxdist/d' /etc/ntp.conf && sed -i """ \
                      """'1 i\tos maxdist 30' /etc/ntp.conf && """ \
                      """cat /etc/crontab|grep "/sbin/hwclock -w" || """ \
                      """sed -i '$a */10 * * * * root /sbin/hwclock""" \
                      """ -w' /etc/crontab && systemctl restart ntpd """ \
                      """ && service cron restart""" + "%s" % f_cmd

            result = self.sshObj.ssh_cmds(
                ebk_ip, ntp_cmd, "hcp", self.hcp_password,
                self.root_password, "", "")
            if "execute ntp config result: 0" not in result:
                logger.error(
                    "config ntp on node(%s) failed,description:%s" %
                    (ebk_ip, result))
                return False
        logger.info("config ntp succ.")
        return True

    def logout(self):
        url = self.workflow__url + '/sessions'
        try:
            requests.delete(url, self.header)
        except Exception as e:
            logger.error("log out failed, because:%s" % str(e))
        logger.info("log out succ.")

    def rollback(self, project_id, pod_id, regionid_list=None):
        return Message(200)

    def retry(self, project_id, pod_id, regionid_list=None):
        return self.execute(project_id, pod_id, regionid_list)


class UpdateVM(StepBaseInterface):
    def __init__(self, project_id, pod_id, regionid_list=None):
        super(UpdateVM, self).__init__(project_id, pod_id, regionid_list)
        self.project_id = project_id
        self.pod_id = pod_id
        self.regionid_list = regionid_list
        self.__db_param_dict = Utils.init_system_params(project_id,
                                                        regionid_list[0])

    def execute(self, project_id, pod_id, regionid_list=None):
        try:
            config = CommonConfig(self.__db_param_dict)
            is_true = config.update_server_info()
            if not is_true:
                logger.error("Update VMs name failed.")
                return Message(500, error_msg_cn="更新Manager&Workflow虚"
                                                 "拟机名称失败",
                               error_msg_en="Failed to update Manager& "
                                            "Workflow VMs name")
            logger.info("Update VMs name success.")
            return Message(200)
        except Exception as e:
            logger.error(str(e))
            return Message(500, error_msg_cn="更新Manager&Workflow虚拟机名称出现"
                                             "异常：%s " % (str(e)),
                           error_msg_en="Exception occurs when update Manager"
                                        "&Workflow VMs name:%s " % (str(e)))

    def rollback(self, project_id, pod_id, regionid_list=None):
        return Message(200)

    def retry(self, project_id, pod_id, regionid_list=None):
        return self.execute(project_id, pod_id, regionid_list)


class UpdateCMDB(StepBaseInterface):
    def __init__(self, project_id, pod_id, regionid_list=None):
        super(UpdateCMDB, self).__init__(project_id, pod_id, regionid_list)
        self.project_id = project_id
        self.pod_id = pod_id
        self.regionid_list = regionid_list
        self.param_dict = Utils.init_system_params(project_id,
                                                   regionid_list[0])
        self.cmdb_util = ManageOneCmdbUtil(project_id)

    def execute(self, project_id, pod_id, regionid_list=None):
        try:
            logger.info("Begin to register eBackup info to CMDB.")
            self.hcp_password = self.param_dict["eBackup_hcp_pwd"]
            self.root_password = self.param_dict["eBackup_root_pwd"]
            datamover_hosts = self.param_dict["eBackup_Datamover_nodes"]
            index_name = 'eBackup'
            current_version = self.param_dict['eBackup_Version']
            node_info_list = self.cmdb_util.get_deploy_node_info_for_mo(
                self.param_dict['current_region_id'], index_name)

            ebk_manager_ip = ''
            ebk_workflow_ip = ''
            for sub_node_info in node_info_list:
                sub_node_name = sub_node_info.get('name', '')
                ipinfo_list = sub_node_info.get(
                    'ipAddresses', None)
                if sub_node_name == "eBackup-Manager":
                    ebk_manager_ip = ipinfo_list[0].get('ip', '')
                if sub_node_name == "eBackup-Workflow":
                    ebk_workflow_ip = ipinfo_list[0].get('ip', '')
            ebackup_workflow_info = {
                "indexName": index_name,
                "version": current_version,
                "name": "workflow_service" + '_' + self.param_dict[
                    'current_region_id'],
                "deployNodeInfos": [
                    {
                        "name": "eBackup-Manager",
                        "ipAddresses": [{"ip": ebk_manager_ip}]
                    },
                    {
                        "name": "eBackup-Workflow",
                        "ipAddresses": [{"ip": ebk_workflow_ip}]
                    }
                ]
            }
            logger.info("The workflow registration info is"
                        ":" + str(ebackup_workflow_info))
            self.cmdb_util.set_cloud_service_info(
                self.param_dict['current_region_id'], ebackup_workflow_info)
            for index, group in enumerate(datamover_hosts.split('|')):
                datamover_index = 1
                ebackup_datamover_ips = group.split(';')
                datamover_group_name = "eBackup_service" + str(index + 1) + \
                                       '_' + \
                                       self.param_dict['current_region_id']
                ebackup_datamover_info = {
                    "indexName": index_name,
                    "version": current_version,
                    "name": datamover_group_name,
                    "deployNodeInfos": []
                }
                datamover_float_ip = Utils.find_float_ip(ebackup_datamover_ips,
                                                         self.hcp_password,
                                                         self.root_password)
                for ip in ebackup_datamover_ips:
                    node_name = "eBackup_service" + str(index + 1) + \
                                "_node" + str(datamover_index)
                    is_find = False
                    sub_node_name = ""
                    for sub_node_info in node_info_list:
                        ipinfo_list = sub_node_info.get(
                            'ipAddresses', None)
                        for sub_ip_info in ipinfo_list:
                            node_ip = sub_ip_info.get('ip', '')
                            if node_ip == ip:
                                sub_node_name = sub_node_info.get('name', '')
                                is_find = True
                                break
                        if is_find:
                            break
                    if sub_node_name:
                        node_info = {
                            "name": sub_node_name,
                            "ipAddresses": [{"ip": ip}],
                            'floatIpAddresses':
                                [{'floatIp': datamover_float_ip,
                                  'fixedIp': ip}]
                        }
                    else:
                        node_info = {
                            "name": node_name,
                            "ipAddresses": [{"ip": ip}],
                            'floatIpAddresses': [
                                {'floatIp': datamover_float_ip,
                                 'fixedIp': ip}]
                        }
                    ebackup_datamover_info["deployNodeInfos"].append(node_info)
                    datamover_index += 1
                logger.info("The datamover group %s registration info is: %s"
                            % (str(index + 1), str(ebackup_datamover_info)))
                self.cmdb_util.set_cloud_service_info(
                    self.param_dict['current_region_id'],
                    ebackup_datamover_info)

            logger.info("Register deploy node info to CMDB completed.")
            return Message(200)
        except FCUException as ex:
            logger.error(str(ex))
            return Message(500, ex)
        except Exception as e:
            return Message(500,
                           error_msg_cn="更新eBackup CMDB"
                                        "信息出现异常：%s " % (str(e)),
                           error_msg_en="Exception occurs when update eBackup "
                                        "info in CMDB:%s " % (str(e)))

    def rollback(self, project_id, pod_id, regionid_list=None):
        return Message(200)

    def retry(self, project_id, pod_id, regionid_list=None):
        return self.execute(project_id, pod_id, regionid_list)


class SpecialOperation(StepBaseInterface):
    def __init__(self, project_id, pod_id, regionid_list=None):
        super(SpecialOperation, self).__init__(project_id, pod_id,
                                               regionid_list)
        self.project_id = project_id
        self.pod_id = pod_id
        self.regionid_list = regionid_list
        self.param_dict = Utils.init_system_params(project_id,
                                                   regionid_list[0])

    def execute(self, project_id, pod_id, regionid_list=None):
        try:
            logger.info("Begin to config eBackup.")

            all_result = []
            workflow_nodes = self.param_dict['eBackup_Workflow_nodes'].\
                split(';')
            workflow_float_ip = Utils.find_float_ip(
                workflow_nodes,
                self.param_dict["eBackup_hcp_pwd"],
                self.param_dict["eBackup_root_pwd"])
            manageone_oc_base_url = Utils.query_alarm_info(
                workflow_float_ip,
                self.param_dict['eBackup_admin_pwd'])
            if len(manageone_oc_base_url) == 0:
                logger.error("Query alarm info failed.")
                raise Exception("Query alarm info failed.")
            logger.info("Begin to set unified backup on workflow nodes %s" %
                        workflow_nodes)
            configer = CommonConfig(self.param_dict)
            is_ture = configer.set_unified_backup(
                workflow_float_ip, manageone_oc_base_url + ':26335')
            if not is_ture:
                logger.error("set unified backup for workflow nodes %s  "
                             "failed." % workflow_nodes)
            else:
                logger.info("set unified backup for workflow nodes %s  "
                            "successfully." % workflow_nodes)
            all_result.append(is_ture)

            datamover_host_group = self.param_dict["eBackup_Datamover_nodes"].\
                replace(" ", "").split("|")
            for datamover_hosts in datamover_host_group:
                host_list = datamover_hosts.split(";")
                web_ip = Utils.find_float_ip(
                    host_list,
                    self.param_dict["eBackup_hcp_pwd"],
                    self.param_dict["eBackup_root_pwd"])
                logger.info("Begin to set unified backup for datamover nodes "
                            "%s ." % host_list)
                is_ture = configer.set_unified_backup(
                    web_ip, manageone_oc_base_url + ':26335')
                if not is_ture:
                    logger.error("set unified backup for datamover nodes "
                                 "%s  failed." % host_list)
                else:
                    logger.info("set unified backup for datamover nodes %s  "
                                "successfully." % host_list)
                all_result.append(is_ture)

            return Message(200)

        except FCUException as ex:
            logger.error(str(ex))
            return Message(500, ex)
        except Exception as ex:
            logger.error(str(ex))
            return Message(500, error_msg_cn="配置eBackup出现异常，"
                                             "请联系华为技术工程师",
                           error_msg_en="Exception occurs when config "
                                        "eBackup,please contact huawei "
                                        "technical engineers")

    def retry(self, project_id, pod_id, regionid_list=None):
        return self.execute(project_id, pod_id, regionid_list)
