41370d92创建于 2025年10月14日历史提交
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
#############################################################################
# Copyright (c) 2023 Huawei Technologies Co.,Ltd.
#
# openGauss is licensed under Mulan PSL v2.
# You can use this software according to the terms
# and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#
#          http://license.coscl.org.cn/MulanPSL2
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
# ----------------------------------------------------------------------------
# Description  : xml_template is generate XML template file
#############################################################################

import os
import sys
import subprocess
import json
import xml.etree.ElementTree as ET

from gspylib.common.GaussLog import GaussLog
from base_utils.template.xml_status import XmlStatus
from base_utils.template.xml_constant import XmlConstant
from base_utils.os.cmd_util import CmdUtil


def load_json_file():
    if XmlConstant.IS_CHINESE:
        resource = "resource_zh.json"
    else:
        resource = "resource_en.json"
    with open(os.path.join(XmlConstant.get_current_dir(), resource), 'r') as f:
        XmlConstant.RESOURCE_DATA = json.load(f)


def get_locale():
    cmd_list = ['echo', '$LANG']
    output, error, status = CmdUtil.execCmdList(cmd_list)
    if status == 0:
        if output:
            if "CN" in output:
                XmlConstant.IS_CHINESE = True
            else:
                XmlConstant.IS_CHINESE = False
        else:
            GaussLog.exitWithError("Executing %s failed. output is empty." % ' '.join(cmd_list))
    else:
        GaussLog.exitWithError("Executing %s failed. Error: %s" % (' '.join(cmd_list), output))


def check_input_chinese():
    user_input = input(XmlConstant.RESOURCE_DATA.get('input_chinese')).strip()
    if not user_input:
        XmlConstant.IS_CHINESE = True
        return True
    if user_input == "1":
        XmlConstant.IS_CHINESE = True
        return True
    elif user_input == "2":
        XmlConstant.IS_CHINESE = False
        return True
    else:
        GaussLog.printMessage(XmlConstant.RESOURCE_DATA.get('invalid_character'))
        return False


def input_user_info():
    current_state = XmlStatus()
    while True:
        current_state = current_state.work()
        if current_state is None:
            break


def check_common(check):
    for i in range(XmlConstant.TRIES):
        if i == 3:
            sys.exit(0)
        if not check():
            continue
        else:
            break


def with_chinese():
    GaussLog.printMessage(XmlConstant.RESOURCE_DATA.get('navigation'))
    XmlConstant.select_option(XmlConstant.RESOURCE_DATA.get('chinese'), XmlConstant.RESOURCE_DATA.get('english'))
    check_common(check_input_chinese)

def confirm_xml():
    for i in range(XmlConstant.TRIES):
        if i == 3:
            sys.exit(0)
        user_input = input(XmlConstant.RESOURCE_DATA.get('confirm_xml')).strip()

        if user_input.lower() in ('y', 'yes'):
            return
        else:
            GaussLog.printMessage(XmlConstant.RESOURCE_DATA.get('invalid_confirm_xml'))


class GenerateTemplate:

    def __init__(self):
        """
        function: constructor
        """
        self.tree = ET.ElementTree()
        self.root = None
        self.xml_file_path = ""
        self.target_xml = ""

    def load_xml(self):
        try:
            # xml default path is ./cluster_tmp.xml 
            tmp_xml_dir = "./cluster_tmp.xml"
            xml_dir = os.path.normpath(os.path.join(XmlConstant.get_current_dir(), tmp_xml_dir))
            self.tree = ET.parse(xml_dir)
            self.root = self.tree.getroot()
            self.xml_file_path = xml_dir
        except Exception as e:
            raise Exception("xml file parsing failed: ", e)

    def delete_xml_node(self):
        if 9 >= XmlConstant.PRI_STANDBY_COUNT > 0:
            new_node_list = XmlConstant.HOST_NODE_INFO[XmlConstant.PRI_STANDBY_COUNT:]
            for child in self.root[1].findall('DEVICE'):
                if child.get('sn') in new_node_list:
                    self.root[1].remove(child)

    def delete_xml_ddes(self):
        if XmlConstant.IS_DDES:
            return
        # remove dss info: {'enable_dss', 'dss_home', 'dss_vg_info', 'votingDiskPath', 'shareDiskDir', 'ss_dss_vg_name'}
        for child in self.root[0].findall('PARAM'):
            if child.attrib['name'] in XmlConstant.DSS_PARA_INFO:
                self.root[0].remove(child)

    def delete_xml_cm(self):
        if XmlConstant.IS_CM:
            return
        # remove cm info: ['cmDir', 'cmsNum', 'cmServerPortBase', 'cmServerPortStandby', 'cmServerListenIp1',
        #                     'cmServerHaIp1', 'cmServerlevel', 'cmServerRelation']
        for i in range(0, len(self.root[1])):
            for child in self.root[1][i].findall('PARAM'):
                if child.attrib['name'] in XmlConstant.CM_PARA_INFO:
                    self.root[1][i].remove(child)

    def update_cluster_label_nodename_info(self):
        for child in self.root[0]:
            if child.attrib['name'] == "nodeNames":
                child.attrib['value'] = ",".join(XmlConstant.HOSTNAME_LISTS)
            if child.attrib['name'] == "backIp1s":
                child.attrib["value"] = ",".join(XmlConstant.IP_LISTS)

    def update_database_install_dir(self):
        for child in self.root[0]:
            if child.get('name') == "gaussdbAppPath":
                child.attrib['value'] = os.path.normpath(os.path.join(XmlConstant.OPENGAUSS_INSTALL_DIR, 'app'))
            elif child.get('name') == "gaussdbLogPath":
                child.attrib['value'] = os.path.normpath(os.path.join(XmlConstant.OPENGAUSS_INSTALL_DIR, 'log'))
            elif child.get('name') == "tmpMppdbPath":
                child.attrib['value'] = os.path.normpath(os.path.join(XmlConstant.OPENGAUSS_INSTALL_DIR, 'tmp'))
            elif child.get('name') == "gaussdbToolPath":
                child.attrib['value'] = os.path.normpath(os.path.join(XmlConstant.OPENGAUSS_INSTALL_DIR, 'tool'))
            elif child.get('name') == "corePath":
                child.attrib['value'] = os.path.normpath(os.path.join(XmlConstant.OPENGAUSS_INSTALL_DIR, 'corefile'))

    def update_cluster_label_common_info(self):
        self.update_cluster_label_nodename_info()
        self.update_database_install_dir()

    def update_database_port(self):
        for i in range(len(self.root[1])):
            for child in self.root[1][i]:
                if child.attrib['name'] == 'dataPortBase':
                    child.attrib['value'] = XmlConstant.DATABASE_PORT

    def update_node_ip_hostname_info(self):
        datanode1 = os.path.normpath(os.path.join(XmlConstant.OPENGAUSS_INSTALL_DIR, "data/dn1"))
        if not XmlConstant.IS_PRI_STANDBY:
            datanode1_value = datanode1
        else:
            datanode1_list = []
            datanode1_list.append(datanode1)
            for hostname in XmlConstant.HOSTNAME_LISTS[1:]:
                datanode1_list.append(hostname)
                datanode1_list.append(datanode1)
            datanode1_value = ",".join(datanode1_list)

        for i in range(len(self.root[1])):
            for ele in self.root[1][i]:
                if ele.attrib['name'] == "name":
                    ele.attrib['value'] = XmlConstant.HOSTNAME_LISTS[i]
                if ele.attrib['name'] == "backIp1":
                    ele.attrib['value'] = XmlConstant.IP_LISTS[i]
                if ele.attrib['name'] == "sshIp1":
                    ele.attrib['value'] = XmlConstant.IP_LISTS[i]
                if ele.attrib['name'] == "sshPort":
                    ele.attrib['value'] = str(XmlConstant.SSH_PORTS[i % len(XmlConstant.SSH_PORTS)])
                if ele.attrib['name'] == "dataNode1":
                    ele.attrib['value'] = datanode1_value

    def update_device_label_info(self):
        # update database port
        self.update_database_port()
        # update node ip
        self.update_node_ip_hostname_info()

    def update_ddes_info(self):
        if not XmlConstant.IS_DDES:
            return
        # update ddes info
        for child in self.root[0].findall('PARAM'):
            if child.attrib['name'] in XmlConstant.UPDATE_DSS_PARA_INFO:
                child.attrib['value'] = XmlConstant.DDES_INFO.get(child.attrib['name'])

    def update_cm_info(self):
        if not XmlConstant.IS_CM:
            return
        # update cm: update cmServerPortBase cmServerListenIp1 cmServerHaIp1 cmServerRelation cmDir
        for child in self.root[1]:
            for ele in child:
                if ele.attrib['name'] == "cmServerPortBase":
                    ele.attrib['value'] = XmlConstant.CM_SERVER_PORT
                if ele.attrib['name'] == "cmServerPortStandby":
                    ele.attrib['value'] = XmlConstant.CM_SERVER_PORT
                if ele.attrib['name'] == "cmServerListenIp1":
                    ele.attrib['value'] = ",".join(XmlConstant.IP_LISTS)
                if ele.attrib['name'] == "cmServerHaIp1":
                    ele.attrib["value"] = ",".join(XmlConstant.IP_LISTS)
                if ele.attrib['name'] == "cmServerRelation":
                    ele.attrib['value'] = ",".join(XmlConstant.HOSTNAME_LISTS)
                if ele.attrib['name'] == "cmDir":
                    ele.attrib['value'] = os.path.normpath(
                        os.path.join(XmlConstant.OPENGAUSS_INSTALL_DIR, 'data', 'cmserver'))

    def update_xml_all_info(self):
        # update all info
        # 1.update cluster label common info
        self.update_cluster_label_common_info()
        # 2.update device label common info
        self.update_device_label_info()
        # 3.update ddes info
        self.update_ddes_info()
        # 4.update cm info
        self.update_cm_info()

    def generate_new_xml_file(self):
        self.target_xml = XmlConstant.TARGET_XML
        if os.path.exists(self.target_xml):
            os.remove(self.target_xml)
        ET.ElementTree(self.root).write(self.target_xml)

    def display_xml_info(self):
        if not os.path.exists(self.target_xml):
            raise Exception("new xml file not found!")
        GaussLog.printMessage("%s   %s" % (XmlConstant.RESOURCE_DATA.get('target_xml_dir'), self.target_xml))
        GaussLog.printMessage(XmlConstant.RESOURCE_DATA.get('target_xml_content'))
        # use cat
        output, error, status = CmdUtil.execCmdList(['cat', self.target_xml])
        if status == 0:
            GaussLog.printMessage(output)

    def run(self):
        # get locale
        get_locale()
        # load json file
        load_json_file()
        # navigation for english or chinese
        with_chinese()
        # load json file
        load_json_file()
        # input user info
        input_user_info()
        # load xml
        self.load_xml()
        # delete xml excess node count
        self.delete_xml_node()
        # delete xml ddes info
        self.delete_xml_ddes()
        # delete xml cm info
        self.delete_xml_cm()
        # update xml all info
        self.update_xml_all_info()
        # generate a new xml file
        self.generate_new_xml_file()
        # display xml info
        self.display_xml_info()
        # confirm xml content
        confirm_xml()