#!/usr/bin/env python3
# -*- coding:utf-8 -*-
#############################################################################
# Copyright (c): 2012-2017, Huawei Tech. Co., Ltd.
# Description  : GaussStat.py is utility for statistics
#############################################################################
from __future__ import print_function

try:
    import subprocess
    import os
    import sys
    import socket
    import glob
    from multiprocessing.dummy import Pool as ThreadPool

    sys.path.append(sys.path[0] + "/../../")
    from gspylib.common.DbClusterInfo import dbClusterInfo
    from gspylib.common.Common import DefaultValue, ClusterCommand
    from gspylib.common.ErrorCode import ErrorCode
    from gspylib.threads.SshTool import SshTool
    import gspylib.common.Sql as Sql
    from gspylib.os.gsnetwork import g_network
    from gspylib.os.gsfile import g_file
    from gspylib.os.gsplatform import g_Platform
except ImportError as ie:
    sys.exit("[GAUSS-52200] : Unable to import module: %s." % str(ie))

########################################################################
# Global variables define
########################################################################
INSTANCE_TYPE_UNDEFINED = -1
MASTER_INSTANCE = 0
STANDBY_INSTANCE = 1
DUMMY_STANDBY_INSTANCE = 2
# Limit multithreading to a maximum of 4
DEFAULT_PARALLEL_NUM = 4

ACTION_INSTALL_PMK = "install_pmk"
ACTION_COLLECT_STAT = "collect_stat"
ACTION_DISPLAY_STAT = "display_stat"
ACTION_ASYN_COLLECT = "asyn_collect"

SQL_FILE_PATH = os.path.realpath(os.path.join(os.path.dirname(__file__), "../etc/sql"))

g_recordList = {}
g_clusterInfo = None
g_DWS_mode = False
functionName = "GaussStat"
key = "checkperf"


def isNumber(num):
    """
    function: Judge if the variable is a number
    input : NA
    output: NA
    """
    try:
        # try to convert num to float. if catch error, it means num is not a number
        float(num)
    except Exception:
        return False
    return True


def isIp(ip):
    """
    function: Judge if the variable is an ip address
    input : NA
    output: NA
    """
    try:
        # only support ipv4...
        socket.inet_aton(ip)
    except ImportError:
        return False
    return True


class statItem():
    """
    Class for stating item
    """

    def __init__(self, item_value, unit=None):
        """
        function: initialize the parameters
        input : NA
        output: NA
        """
        # remove space
        item_value = item_value.strip()
        # judge if item is number
        if isNumber(item_value):
            self.value = item_value
        else:
            self.value = None
        self.unit = unit

    def __str__(self):
        """
        function: create a string
        input : NA
        output: value
        """
        if self.value is None:
            return ""
        elif self.unit is not None:
            return "%-10s %s" % (self.value, self.unit)
        else:
            return "%s" % self.value


class clusterStatistics():
    """
    Class for stating cluster message
    """

    def __init__(self):
        """
        function: Constructor
        input : NA
        output: NA
        """
        self.cluster_stat_generate_time = None

        # Host cpu time
        self.cluster_host_total_cpu_time = None
        self.cluster_host_cpu_busy_time = None
        self.cluster_host_cpu_iowait_time = None
        self.cluster_host_cpu_busy_time_perc = None
        self.cluster_host_cpu_iowait_time_perc = None

        # MPP cpu time
        self.cluster_mppdb_cpu_time_in_busy_time = None
        self.cluster_mppdb_cpu_time_in_total_time = None

        # Shared buffer
        self.cluster_share_buffer_read = None
        self.cluster_share_buffer_hit = None
        self.cluster_share_buffer_hit_ratio = None
        self.cluster_share_buffer_read_hit_speed = None

        # In memory sort ratio
        self.cluster_in_memory_sort_count = None
        self.cluster_disk_sort_count = None
        self.cluster_in_memory_sort_ratio = None

        # IO statistics
        self.cluster_io_stat_number_of_files = None
        self.cluster_io_stat_physical_reads = None
        self.cluster_io_stat_physical_writes = None
        self.cluster_io_stat_read_time = None
        self.cluster_io_stat_write_time = None

        # Disk usage
        self.cluster_disk_usage_db_size = None
        self.cluster_disk_usage_tot_physical_writes = None
        self.cluster_disk_usage_avg_physical_write = None
        self.cluster_disk_usage_max_physical_write = None

        # Db memory usage
        self.cluster_db_memory_usage = None

        # Activity statistics
        self.cluster_activity_active_sql_count = None
        self.cluster_activity_session_count = None


class nodeStatistics():
    """
    Class for stating node message
    """

    def __init__(self, nodename):
        """
        function: Constructor
        input : nodename
        output: NA
        """
        self.nodename = nodename
        self.node_mppdb_cpu_busy_time = None
        self.node_host_cpu_busy_time = None
        self.node_host_cpu_total_time = None
        self.node_mppdb_cpu_time_in_busy_time = None
        self.node_mppdb_cpu_time_in_total_time = None
        self.node_physical_memory = None
        self.node_db_memory_usage = None
        self.node_shared_buffer_size = None
        self.node_shared_buffer_hit_ratio = None
        self.node_in_memory_sorts = None
        self.node_in_disk_sorts = None
        self.node_in_memory_sort_ratio = None
        self.node_number_of_files = None
        self.node_physical_reads = None
        self.node_physical_writes = None
        self.node_read_time = None
        self.node_write_time = None


class GaussStat():
    """
    Class for stating Gauss message
    """

    def __init__(self, install_path="", user_name="", local_port="", curr_time="",
                 last_time="", snapshot_id="", flag_num=0, master_host="", logger_fp=None,
                 show_detail=False, database_name="postgres"):
        """
        function: Constructor
        input : install_path, user_name, local_port, curr_time, last_time, snapshot_id,
        flag_num, master_host, logger_fp, show_detail, database_name
        output: NA
        """
        # gsql paramter, must be set
        if install_path == "" or user_name == "" or local_port == "" or logger_fp is None:
            raise Exception(ErrorCode.GAUSS_500["GAUSS_50001"] % "p or -c or -u or -d " + ".")
        else:
            self.installPath = install_path
            self.user = user_name
            self.localport = local_port
            self.logger = logger_fp

        if curr_time == "":
            self.currTime = "NULL"
        else:
            self.currTime = "'%s'" % curr_time

        if last_time == "":
            self.lastTime = "NULL"
        else:
            self.lastTime = "'%s'" % last_time

        if snapshot_id == "":
            self.snapshotId = "NULL"
        else:
            self.snapshotId = snapshot_id

        self.flagNum = flag_num
        self.masterHost = master_host

        # show detail or not
        self.showDetail = show_detail

        # which database we should connect.
        self.database = database_name

        # initialize statistics
        self.cluster_stat = clusterStatistics()
        self.node_stat = []
        self.session_cpu_stat = []
        self.session_mem_stat = []
        self.session_io_stat = []

        # internal parameter
        self.__baselineFlag = "gauss_stat_output_time"  # default baseline check flag.
        self.__TopNSessions = 10

    def writeOutput(self, outstr):
        """
        function: write output message
        input : outstr
        output: NA
        """
        sys.stderr.write(outstr + "\n")
        sys.stderr.flush()

    def loadSingleNodeStatForDWSmode(self, nodePort, querySql, nodeName):
        """
        """
        (status, result, err_output) = ClusterCommand.excuteSqlOnLocalhost(nodePort, querySql)
        self.logger.debug("Get pgxc_node info from cluster. \ncommand: %s \nresult: %s." % (querySql, result))

        if status != 2:
            raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % querySql + " Error:\n%s" % err_output)

        if len(result) == 0:
            raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % querySql + " Return record is null")

        recordList = result[0]
        if len(recordList) != 3:
            raise Exception(ErrorCode.GAUSS_517["GAUSS_51700"] + "The record in recordList is:%s." % recordList)
        if recordList[0] != '':
            recordList[0] = (recordList[0]).strip()
        if recordList[1] != '':
            recordList[1] = (recordList[1]).strip()
        if recordList[2] != '':
            recordList[2] = (recordList[2]).strip()

        # when I query from pgxc_node on a dn node,
        # the node type is 'C'. it's wrong, so I modify here.
        # when it is dn node, I specify the node type as 'D' selfly.
        # load single node stat
        if dwsFlag:
            skipSupperRoles = 'TRUE'
        else:
            skipSupperRoles = 'FALSE'
        if nodeName != '':
            # this node is CN
            instType = 'C'
        else:
            instType = 'D'

        querySql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                   "SELECT * FROM pmk.load_node_stat(%s, %s, %s, '%s', '%s', %s, %s)" % \
                   (self.currTime, self.lastTime, self.snapshotId, recordList[0], instType, recordList[2],
                    skipSupperRoles)
        (status, result, err_output) = ClusterCommand.excuteSqlOnLocalhost(nodePort, querySql)
        self.logger.debug("Load single node stat. \ncommand: %s \nresult: %s." % (querySql, result))

        if status != 2:
            raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % querySql + " Error:\n%s" % err_output)

        if len(result) == 0:
            raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % querySql + " Return record is null")

        output = ""
        output_list = []
        if len(result) != 0:
            for i in range(len(result)):
                line = "|".join(result[i])
                output_list.append(line)
                output_list.append("\n")
            output = "".join(output_list)
        g_recordList[recordList[0]] = output

    def loadSingleNodeStat(self, connInfo):
        '''
        function: load single node(cn or dn) stat
        input : NA
        output: NA
        '''
        self.logger.debug("Loading single node stat on the node [%s]." % connInfo[0])
        global g_recordList
        try:
            nodeName = connInfo[0]
            nodePort = connInfo[1]
            # when I query from pgxc_node, if I query from a cn node,
            # it will return all the logical nodes of the cluster.
            if nodeName != '':
                # this node is CN
                querySql = "SELECT node_name, node_type, " \
                           "node_id FROM pg_catalog.pgxc_node WHERE node_name = '%s';" % nodeName
            else:
                # this node is DN
                querySql = "SELECT node_name, node_type, node_id FROM pg_catalog.pgxc_node;"
            if g_DWS_mode:
                self.loadSingleNodeStatForDWSmode(nodePort, querySql, nodeName)
            else:
                (status, output) = ClusterCommand.execSQLCommand(querySql, self.user, '', nodePort, "postgres")
                self.logger.debug("Get pgxc_node info from cluster. \ncommand: %s \nresult: %s." % (querySql, output))
                if status != 0:
                    raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % querySql + " Error:\n%s" % output)
                if output == "":
                    raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % querySql + " Return record is null")
                recordList = output.split('|')
                if len(recordList) != 3:
                    raise Exception(ErrorCode.GAUSS_517["GAUSS_51700"] + "The record in recordList is:%s." % recordList)
                if recordList[0] != '':
                    recordList[0] = (recordList[0]).strip()
                if recordList[1] != '':
                    recordList[1] = (recordList[1]).strip()
                if recordList[2] != '':
                    recordList[2] = (recordList[2]).strip()

                checkSql = "SELECT * FROM pg_catalog.pg_stat_activity WHERE state <> 'idle' " \
                           "AND query LIKE '%pmk.get_%' AND query NOT LIKE '%pg_stat_activity%';"
                (status, output) = ClusterCommand.execSQLCommand(checkSql, self.user, '', nodePort, "postgres")
                self.logger.debug("Get active pmk session result. \ncommand: %s \nresult: %s." % (checkSql, output))
                if status != 0:
                    raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % checkSql + " Error:\n%s" % output)
                if str(output).strip() != "":
                    raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % checkSql)

                # when I query from pgxc_node on a dn node, the node type is 'C'. it's wrong, so I modify here.
                # when it is dn node, I specify the node type as 'D' selfly.
                # load single node stat
                if dwsFlag:
                    skipSupperRoles = 'TRUE'
                else:
                    skipSupperRoles = 'FALSE'
                if nodeName != '':
                    # this node is CN
                    instType = 'C'
                else:
                    instType = 'D'

                querySql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                           "SELECT * FROM pmk.load_node_stat(%s, %s, %s, '%s', '%s', %s, %s)" % \
                           (self.currTime,
                            self.lastTime,
                            self.snapshotId,
                            recordList[0],
                            instType,
                            recordList[2],
                            skipSupperRoles)
                (status, output) = ClusterCommand.execSQLCommand(querySql, self.user, '', nodePort, "postgres")
                self.logger.debug("Load single node stat. \ncommand: %s \nresult: %s." % (querySql, output))

                if status != 0:
                    raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % querySql + " Error:\n%s" % output)

                if len(output.split('\n')) < 3:
                    raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % querySql + " Return record is null")

                g_recordList[recordList[0]] = output.split('\n')[2]
        except Exception as e:
            raise Exception(str(e))
        self.logger.debug("Successfully loaded single node stat on the node [%s]." % connInfo[0])

    def checkRoleOfDnInst(self, dnDataDir):
        '''
        function: check role of Dn Inst
        input : dnDataDir
        output: NA
        '''
        self.logger.debug("Checking role of dn instance.")
        try:
            if (not os.path.exists(os.path.join(dnDataDir, "postmaster.pid"))):
                return False

            checkCmd = "gs_ctl query -D %s | grep 'HA state' -A 1 | grep 'local_role'" % dnDataDir
            (status, output) = DefaultValue.retryGetstatusoutput(checkCmd)
            if (status != 0):
                raise Exception(ErrorCode.GAUSS_514["GAUSS_51400"] % checkCmd + " Error:\n%s" % output)

            roleStatus = ((output.split(':'))[1]).strip()
            if (roleStatus == "Primary"):
                return True
            else:
                return False
        except Exception as e:
            raise Exception(str(e))

    def getPGXCNode(self):
        '''
        function: get pgxc node of the cluster,contains CN and master DNs
        input : NA
        output: pgxcNodeList
        '''
        self.logger.debug("Getting pgxc node of the cluster.")
        pgxcNodeList = []
        nodeItem = []
        try:
            # get node info
            nodeInfo = g_clusterInfo.getDbNodeByName(socket.gethostname())
            if (len(nodeInfo.coordinators) > 0 and str(nodeInfo.coordinators[0].instanceId) not in abnormalCN):
                cooInst = nodeInfo.coordinators[0]
                nodeName = "cn_%d" % cooInst.instanceId
                nodePort = "%s" % cooInst.port
                nodeItem.append(nodeName)
                nodeItem.append(nodePort)
                pgxcNodeList.append(nodeItem)

            for dnInst in nodeInfo.datanodes:
                if (g_clusterInfo.isMasterStandbyCluster() or
                        g_clusterInfo.isMasterStandbyMultiAZCluster() or
                        g_clusterInfo.isSinglePrimaryMultiStandbyCluster() or
                        g_clusterInfo.isSingleInstCluster()):
                    if dnInst.instanceType != DUMMY_STANDBY_INSTANCE:
                        if self.checkRoleOfDnInst(dnInst.datadir):
                            nodeName = ""
                            nodePort = "%s" % dnInst.port
                            nodeItem = []
                            nodeItem.append(nodeName)
                            nodeItem.append(nodePort)
                            pgxcNodeList.append(nodeItem)
                elif g_clusterInfo.isSingleCluster():
                    nodeName = ""
                    nodePort = "%s" % dnInst.port
                    nodeItem = []
                    nodeItem.append(nodeName)
                    nodeItem.append(nodePort)
                    pgxcNodeList.append(nodeItem)

            return pgxcNodeList
        except Exception as e:
            raise Exception(str(e))

    def getPerfCheckPsqlCommand(self, dbname):
        """
        """
        cmd = ClusterCommand.getSQLCommand(self.localport, dbname, os.path.join(self.installPath, "bin/gsql"))
        return cmd

    # NOTICE: itemCounts must be more than two. so that we can distinguish records and (%d rows)
    def execQueryCommand(self, sql, itemCounts, collectNum, baselineflag=""):
        '''
        function: execute the query command
        input : sql, itemCounts, collectNum, baselineflag
        output: NA
        '''
        if (baselineflag == ""):
            baselineflag = self.__baselineFlag

        # save sql statement to file to reduce quot nesting
        sqlFile = os.path.join(DefaultValue.getTmpDirFromEnv(), "checkperf_query_%s_%s.sql" % (os.getpid(), collectNum))
        if (dwsFlag):
            sql = "set cgroup_name='Rush';" + sql

        cmd = "echo \"%s\" > '%s' && chown %s '%s'" % (sql, sqlFile, self.user, sqlFile)
        (status, output) = subprocess.getstatusoutput(cmd)
        if (status != 0):
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50205"] % "SQL statement to file" +
                            "\nCommand:\n  %s\nError:\n  %s" % (cmd, output))

        try:
            sql_cmd = self.getPerfCheckPsqlCommand(self.database)
            if (os.getuid() == 0):
                cmd = "su - %s -c \'%s -f %s -X --variable=ON_ERROR_STOP=on\' 2>/dev/null" % \
                      (self.user, sql_cmd, sqlFile)
            else:
                cmd = "%s -f %s -X --variable=ON_ERROR_STOP=on 2>/dev/null" % (sql_cmd, sqlFile)
            self.logger.debug("Execute command: %s" % (cmd))

            (status, output) = subprocess.getstatusoutput(cmd)
            if status != 0 or ClusterCommand.findErrorInSqlFile(sqlFile, output):
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % cmd + " Error: \n%s" % output)

            DefaultValue.cleanTmpFile(sqlFile)

            baseline = GaussStat.checkExpectedOutput(output, baselineflag, False)

            if (baseline == -1):
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] %
                                ("Cannot fetch query baseline. Error: \n%s" % (output)))

            if (GaussStat.checkExpectedOutput(output, "(0 rows)", True, baseline) != -1):
                # can not support now
                return []

            lines = output.split("\n")
            linesCount = len(lines)

            # result must more than 4 lines
            if (linesCount <= baseline + 4):
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % "Unexpected lines" + "  Error: \n%s" % (output))

            records = []
            for ino in range(baseline + 2, linesCount):
                line = lines[ino]
                record = line.split("|")
                if (len(record) != itemCounts):
                    break
                records.append(record)

            self.logger.debug("Query command succeeded.")
            self.logger.debug("Query results: \n%s." % str(records))
            return records
        except Exception as e:
            # execute query command failed. log and raise
            self.logger.debug("Failed to execute the command of query [%s] on local host." % sql)
            DefaultValue.cleanTmpFile(sqlFile)
            raise Exception(str(e))

    # check if the expected line existed in output.
    @staticmethod
    def checkExpectedOutput(output, expect, strict=True, starter=0):
        '''
        function: check expected output
        input : output, expect, strict, starter
        output: NA
        '''
        lines = output.split("\n")
        expect = expect.strip()
        if (starter < len(lines)):
            for i in range(starter, len(lines)):
                line = lines[i]
                if (strict):
                    if (expect == line.strip()):
                        return i - starter
                else:
                    if (line.strip().find(expect) != -1):
                        return i - starter
        return -1

    def CheckInstanceMode(self):
        '''
        function: test coordinator and datanode mode
        input : NA
        output: NA
        '''
        try:
            self.logger.debug("Checking the coordinator and datanode mode .")

            # test coordinator and coordinator mode...
            cmd = "ps ux|awk '{if($11 == \"%s\")print $0}'" % os.path.join(self.installPath, "bin/gaussdb")
            (status, output) = subprocess.getstatusoutput(cmd)
            if (status != 0 or output.strip() == ""):
                self.logger.debug("Failed to test the CN and CN's mode with the user name. Error: \n%s" % (output))
                raise Exception(ErrorCode.GAUSS_516["GAUSS_51605"] % "CN" + " Please check the cluster status.")

            self.logger.debug("Test CN output:\n%s" % output)
            if (GaussStat.checkExpectedOutput(output, "--restoremode", False) != -1):
                self.logger.debug("CN is running in restore mode.")
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51212"] % "running CN instance in normal mode")
            elif (GaussStat.checkExpectedOutput(output, "--coordinator", False) != -1):
                self.logger.debug("CN is running in normal mode.")
            else:
                self.logger.debug("There is no running CN instance on this node.")
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51212"] % "running CN instance on this node")

            self.logger.debug("Successfully checked the coordinator and datanode mode .")
        except Exception as e:
            # execute query command failed. log and raise
            self.logger.debug("Failed to check coordinator and datanode mode.")
            raise Exception(str(e))

    def rebuild_pmk_schema(self):
        """
        check pmk schema exist or not.
        :return:True/False
        """
        pmk_cmd = "select oid from pg_catalog.pg_namespace where nspname='pmk'"
        class_cmd = "select count(1) from pg_catalog.pg_class where relnamespace=" \
                    "(select oid from pg_catalog.pg_namespace where nspname='pmk')"
        proc_cmd = "select count(1) from pg_catalog.pg_proc where pronamespace=" \
                   "(select oid from pg_catalog.pg_namespace where nspname='pmk')"
        (pmk_status, pmkResult, pmk_error) = ClusterCommand.excuteSqlOnLocalhost(self.localport, pmk_cmd)
        (class_status, classResult, class_error) = ClusterCommand.excuteSqlOnLocalhost(self.localport, class_cmd)
        (proc_status, procResult, proc_error) = ClusterCommand.excuteSqlOnLocalhost(self.localport, proc_cmd)
        self.logger.debug("Test PMK schema. Output: \n%s %s %s." % (pmkResult, classResult, procResult))

        if pmk_status != 2:
            raise Exception("Test PMK schema failed. Output: \n%s;%s;%s" % pmk_error)
        if class_status != 2:
            raise Exception("Test PMK schema failed. Output: \n%s;%s;%s" % class_error)
        if proc_status != 2:
            raise Exception("Test PMK schema failed. Output: \n%s;%s;%s" % proc_error)

        if len(pmkResult) == 0:
            # schema not exist, so we create it.
            self.logger.debug("PMK schema does not exist. Install it for the first time.")
            need_install_pmk = True
        elif str(classResult[0][0]).strip() == "13" and str(procResult[0][0]).strip() == "29":
            # schema already created.
            self.logger.debug("PMK schema is already exist.")
            need_install_pmk = False
        elif str(classResult[0][0]).strip() == "5" and str(procResult[0][0]).strip() == "29":
            # schema already created.
            self.logger.debug("PMK schema is already exist.")
            need_install_pmk = False
        else:
            # maybe class count or proc count not the same.
            self.logger.debug("PMK schema is incomplete. Try to execute \"drop schema pmk cascade;\".")
            drop_cmd = "drop schema if exists pmk cascade"
            (_, _, err_output) = ClusterCommand.excuteSqlOnLocalhost(self.localport, drop_cmd)
            if err_output != "":
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % drop_cmd + " Error: \n%s" % err_output)
            else:
                self.logger.debug("Successfully dropped schema PMK.")
            need_install_pmk = True

        return need_install_pmk

    def installPMKInDWSMode(self):
        '''
        function: install PMK schema in DWS mode
        input : NA
        output: NA
        '''
        try:
            sql_cmd = "select * from pg_catalog.pgxc_node where node_type = 'D';"
            (status, result, err_output) = ClusterCommand.excuteSqlOnLocalhost(self.localport, sql_cmd)
            if (status != 2):
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql_cmd)
            # parse the result
            if (len(result) < 3):
                self.logger.debug("No DN is configured in cluster.")
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51212"] % "configured DN in cluster")

            if not self.rebuild_pmk_schema():
                return

            # add pmk schema to database.
            err_output = ""
            tablespace = DefaultValue.getEnv("ELK_SYSTEM_TABLESPACE")
            if (tablespace is not None and tablespace != ""):
                for i in range(len(Sql.PMK_NEW)):
                    (status, result, err_output) = ClusterCommand.excuteSqlOnLocalhost(self.localport, Sql.PMK_NEW[i])
                    if (err_output != ""):
                        self.logger.debug("Failed to install pmk schema,Error: \n%s" % err_output)
                        break
            else:
                for i in range(len(Sql.PMK_ORIGINAL)):
                    (status, result, err_output) = ClusterCommand.excuteSqlOnLocalhost(self.localport,
                                                                                       Sql.PMK_ORIGINAL[i])
                    if (err_output != ""):
                        self.logger.debug("Failed to install pmk schema,Error: \n%s" % err_output)
                        break

            # Determine the execution result of the pmk installation
            if (err_output != ""):
                dropSchemaCmd = "drop schema if exists pmk cascade"
                (status, result, err1_output) = ClusterCommand.excuteSqlOnLocalhost(self.localport, dropSchemaCmd)
                if (err1_output != ""):
                    self.logger.debug("Failed to drop schema PMK. Error: \n%s" % err1_output)
                raise Exception("Failed to install pmk schema,Error: \n%s" % err_output)
        except Exception as e:
            raise Exception(str(e))

    def checkIsExistDn(self, gsql_path, test_data_node_file):
        """
        function: Check whether DN configuration information exists.
        input : NA
        output: NA
        """
        if not g_clusterInfo.isSingleInstCluster():
            if (os.getuid() == 0):
                cmd = "su - %s -c \'%s -U %s -p %s -d %s -X --variable=ON_ERROR_STOP=on -f " % \
                      (self.user, gsql_path, self.user, str(self.localport), self.database)
                cmd += "%s" % test_data_node_file
                cmd += "\'"
            else:
                cmd = "%s -U %s -p %s -d %s -X --variable=ON_ERROR_STOP=on -f " % \
                      (gsql_path, self.user, str(self.localport), self.database)
                cmd += "%s" % test_data_node_file

            (status, output) = subprocess.getstatusoutput(cmd)
            self.logger.debug("Command for testing node: %s" % cmd)
            self.logger.debug("Output for testing node: %s" % output)
            if (status != 0 or ClusterCommand.findErrorInSqlFile(test_data_node_file, output)):
                self.logger.debug("Failed to query PGXC node. Error: \n%s" % output)
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % cmd + " Error: \n%s" % output)

            lines = output.split("\n")
            if (len(lines) < 4 or GaussStat.checkExpectedOutput(output, "(0 rows)") >= 2):
                self.logger.debug("No DN is configured in cluster.")
                raise Exception(ErrorCode.GAUSS_512["GAUSS_51212"] % "configured DN in cluster")

    def checkIsExistPMKshcema(self, gsql_path, tablespace):
        """
        """
        test_pmk_file = os.path.join(SQL_FILE_PATH, "test_pmk.sql")
        pmk_schema_num1 = "pmk schema exist. class count is 13, proc count is 29"
        pmk_schema_num2 = "pmk schema exist. class count is 5, proc count is 29"
        # test pmk schema exist or not.
        if (os.getuid() == 0):
            cmd = "su - %s -c \'%s -U %s -p %s -d %s -X -f " % \
                  (self.user, gsql_path, self.user, str(self.localport), self.database)
            cmd += "%s" % (test_pmk_file)
            cmd += "\'"
        else:
            cmd = "%s -U %s -p %s -d %s -X -f " % \
                  (gsql_path, self.user, str(self.localport), self.database)
            cmd += "%s" % (test_pmk_file)

        (status, output) = subprocess.getstatusoutput(cmd)
        self.logger.debug("Test PMK schema. Output: \n%s." % (output))
        if (status != 0):
            raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % cmd + " Error: \n%s" % output)

        if (GaussStat.checkExpectedOutput(output, "ERROR:  query returned no rows", False) != -1):
            # schema not exist, so we create it.
            self.logger.debug("PMK schema does not exist. Install it for the first time.")

        elif (GaussStat.checkExpectedOutput(output, pmk_schema_num1, False) != -1 and tablespace is None):
            # schema already created.
            self.logger.debug("PMK schema is already exist.")
            return True
        elif (GaussStat.checkExpectedOutput(output, pmk_schema_num2, False) != -1 and tablespace is not None):
            # schema already created.
            self.logger.debug("PMK schema is already exist.")
            return True
        else:
            # maybe class count or proc count not the same.
            self.logger.debug("PMK schema is incomplete. Try to execute \"drop schema pmk cascade;\".")
            if (os.getuid() == 0):
                cmd = "su - %s -c 'gsql -d %s -p %s -X -c \"drop schema pmk cascade;\"'" % \
                      (self.user, self.database, str(self.localport))
            else:
                cmd = "gsql -d %s -p %s -X -c \"drop schema pmk cascade;\"" % \
                      (self.database, str(self.localport))
            (status, output) = subprocess.getstatusoutput(cmd)
            if (status != 0 or ClusterCommand.findErrorInSql(output)):
                self.logger.debug("Failed to drop schema PMK. Error: \n%s" % output)
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % cmd + " Error: \n%s" % output)
            else:
                self.logger.debug("Successfully dropped schema PMK: %s." % output)
        return False

    def add_pmk_to_database(self, gsql_path, pmk_sql_file_back):
        """
        function: add pmk schema to database.
        input: NA
        output: NA
        """
        if os.getuid() == 0:
            cmd = "su - %s -c \'%s -U %s -p %s -d %s -X --variable=ON_ERROR_STOP=on -f \"" % \
                  (self.user, gsql_path, self.user, str(self.localport), self.database)
            cmd += "%s" % pmk_sql_file_back
            cmd += "\"\'"
        else:
            cmd = "%s -U %s -p %s -d %s -X --variable=ON_ERROR_STOP=on -f \"" % \
                  (gsql_path, self.user, str(self.localport), self.database)
            cmd += "%s" % pmk_sql_file_back
            cmd += "\""

        (status, output) = subprocess.getstatusoutput(cmd)
        self.logger.debug("Create pmk output:%s" % output)
        # Determine the execution result of the pmk installation
        if status != 0 or ClusterCommand.findErrorInSqlFile(pmk_sql_file_back, output):
            # Determine whether the current user is the root user
            if os.getuid() == 0:
                # Link under the root user command
                dropSchemaCmd = "su - %s -c '%s -d %s -p %s -X -c \"drop schema if exists pmk cascade;\"' " % \
                                (self.user, gsql_path, self.database, str(self.localport))
            else:
                # Link under the cluster user command
                dropSchemaCmd = "%s -d %s -p %s -X -c \"drop schema if exists pmk cascade;\"" % \
                                (gsql_path, self.database, str(self.localport))
            (status, output1) = subprocess.getstatusoutput(dropSchemaCmd)
            # Judge the results of the fallback installation pmk
            if status != 0 or ClusterCommand.findErrorInSql(output1):
                self.logger.debug("Failed to drop schema PMK. Error: \n%s" % output1)
            raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % cmd + " Error: \n%s" % output)

    def set_default_table_space(self, pmk_sql_file_back):
        """
        function: set default table space for ELK or MPPDB
        input: NA
        output: NA
        """
        tablespace = DefaultValue.getEnv("ELK_SYSTEM_TABLESPACE")
        if tablespace is not None and tablespace != "":
            cmd = "sed -i \"s/START TRANSACTION;//g\" %s && " % pmk_sql_file_back
            cmd += "sed -i \"s/COMMIT;//g\" %s && " % pmk_sql_file_back
            cmd += "sed -i \"/PRIMARY KEY/d\" %s && " % pmk_sql_file_back
            cmd += "sed -i \"/CREATE INDEX/d\" %s && " % pmk_sql_file_back
            cmd += "sed -i \"1i\\SET default_tablespace = %s;\" %s" % (tablespace, pmk_sql_file_back)
            (status, output) = subprocess.getstatusoutput(cmd)
            if status != 0:
                DefaultValue.cleanTmpFile(pmk_sql_file_back)
                raise Exception(ErrorCode.GAUSS_514["GAUSS_51400"] % cmd + " Error: \n%s" % output)
        else:
            if not g_clusterInfo.isSingleInstCluster():
                self.logger.debug("Set installation groupt to default_storage_nodegroup in this session.")
                sql_nodegroup = "SELECT group_name FROM pg_catalog.pgxc_group WHERE is_installation='t';"
                if os.getuid() == 0:
                    cmd = "su - %s -c 'gsql -d %s -p %s -x -A -c \"%s\"'" % \
                          (self.user, self.database, str(self.localport), sql_nodegroup)
                else:
                    cmd = "gsql -d %s -p %s -x -A -c \"%s\"" % (self.database, str(self.localport), sql_nodegroup)
                (status, output) = subprocess.getstatusoutput(cmd)
                if status != 0 or ClusterCommand.findErrorInSql(output):
                    self.logger.debug("Failed to get installation groupt. Error: \n%s" % output)
                    raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % cmd + " Error: \n%s" % output)

                installation_group = output.split('|')[-1]
                cmd = "sed -i \"1i\\SET default_storage_nodegroup = %s;\" %s" % \
                      (installation_group, pmk_sql_file_back)
                (status, output) = subprocess.getstatusoutput(cmd)
                if status != 0:
                    DefaultValue.cleanTmpFile(pmk_sql_file_back)
                    raise Exception(ErrorCode.GAUSS_514["GAUSS_51400"] % cmd + " Error: \n%s" % output)
                self.logger.debug("Successfully set default_storage_nodegroup is %s in this session." %
                                  installation_group)
        return pmk_sql_file_back

    def installPMKInNonDWSMode(self):
        '''
        function: install PMK schema in non DWS mode
        input : NA
        output: NA
        '''
        try:
            test_data_node_file = os.path.join(SQL_FILE_PATH, "test_data_node.sql")

            gsql_path = os.path.join(self.installPath, "bin/gsql")
            tablespace = DefaultValue.getEnv("ELK_SYSTEM_TABLESPACE")

            self.checkIsExistDn(gsql_path, test_data_node_file)
            if self.checkIsExistPMKshcema(gsql_path, tablespace):
                return

            if g_clusterInfo.isSingleInstCluster():
                pmkSqlFile = os.path.join(self.installPath, "share/postgresql/pmk_schema_single_inst.sql")
            else:
                pmkSqlFile = os.path.join(self.installPath, "share/postgresql/pmk_schema.sql")
            pmkSqlFile_back = os.path.join(self.installPath, "share/postgresql/pmk_schema_bak.sql")
            cmd = "cp '%s' '%s'" % (pmkSqlFile, pmkSqlFile_back)
            (status, output) = subprocess.getstatusoutput(cmd)
            if status != 0:
                raise Exception(ErrorCode.GAUSS_514["GAUSS_51400"] % cmd + " Error: \n%s" % output)
            # set default table space
            pmkSqlFile_back = self.set_default_table_space(pmkSqlFile_back)
            # add pmk schema to database.
            self.add_pmk_to_database(gsql_path, pmkSqlFile_back)
        except Exception as e:
            # execute query command failed. log and raise
            self.logger.debug("Failed to check coordinator and datanode mode.")
            raise Exception(str(e))

    def installPMKSchema(self):
        '''
        function: install PMK schema
        input : NA
        output: NA
        '''
        try:
            self.logger.debug("Installing PMK schema.")

            if not g_clusterInfo.isSingleInstCluster():
                # test cn and dn mode
                self.CheckInstanceMode()

            if g_DWS_mode:
                self.installPMKInDWSMode()
            else:
                self.installPMKInNonDWSMode()
            # create schema success.
            self.logger.debug("Successfully installed PMK schema.")
        except Exception as e:
            raise Exception("%s" % str(e))

    def collectStat(self, act):
        '''
        function: collect performance statistics
        input : act
        output: NA
        '''
        try:
            self.logger.debug("Collecting each performance statistics [%s]." % act)
            act_dict = {
                # Cluster statics
                "ClusterHostCpuStat": self.collectClusterHostCpuStat,
                "ClusterMPPDBCpuStat": self.collectClusterMPPDBCpuStat,
                "ShareBufferStat": self.collectShareBufferStat,
                "ClusterSortStat": self.collectClusterSortStat,
                "ClusterIOStat": self.collectClusterIOStat,
                "ClusterDiskStat": self.collectClusterDiskStat,
                "ClusterActiveSqlCount": self.collectClusterActiveSqlCount,
                "ClusterSessionCount": self.collectClusterSessionCount,

                # Node statics
                "NodeCpuStat": self.collectNodeCpuStat,
                "NodeMemoryStat": self.collectNodeMemoryStat,
                "NodeIOStat": self.collectNodeIOStat,
            }
            if act in act_dict.keys():
                act_dict[act]()
            self.logger.debug("Successfully collected each performance statistics [%s]." % act)
        except Exception as e:
            raise Exception(str(e))

    def collectPGXCNodeStat(self, pgxcNodeList):
        '''
        function: collect PGXC node performance statistics
        input : pgxcNodeList
        output: NA
        '''
        recordTempFile = ""
        try:
            self.logger.debug("Collecting PGXC node performance statistics.")
            if (len(pgxcNodeList) != 0):
                # load pgxc node statistics parallel
                pool = ThreadPool(DefaultValue.getCpuSet())
                pool.map(self.loadSingleNodeStat, pgxcNodeList)
                pool.close()
                pool.join()
                for record in g_recordList.keys():
                    self.logger.debug("%s:  %s\n" % (record, g_recordList[record]))
            else:
                return

            # create a temp file for records write
            recordTempFile = os.path.join(DefaultValue.getTmpDirFromEnv(), "recordTempFile_%d_%s" %
                                          (self.flagNum, socket.gethostname()))

            # clean the temp file first
            DefaultValue.cleanTmpFile(recordTempFile)
            # write records into the temp file
            cmd_list = []
            for record in g_recordList.keys():
                cmd_list.append("%s::::%s\n" % (record, g_recordList[record]))
            strCmd = "".join(cmd_list)

            g_file.write_file_with_default_permission(recordTempFile, strCmd)

            if self.masterHost != socket.gethostname():
                # scp record Temp File to tmpDir
                scp_cmd = g_Platform.getRemoteCopyCmd(recordTempFile,
                                                      recordTempFile,
                                                      g_network.makeSCPHost(self.masterHost))

                self.logger.debug("Execute command: %s" % scp_cmd)
                (status, output) = subprocess.getstatusoutput(scp_cmd)
                if status != 0:
                    raise Exception(ErrorCode.GAUSS_502["GAUSS_50205"] % "record temp file" + " Error: \n%s" % output)
            self.logger.debug("Successfully collected PGXC node performance statistics.")
        except Exception as e:
            # remove temporary file
            DefaultValue.cleanTmpFile(recordTempFile)
            raise Exception(str(e))

    def cleanTempFiles(self):
        """
        """
        # clean all the temp files before start collect the performance data
        recordTempFilePattern = os.path.join(DefaultValue.getTmpDirFromEnv(), 'recordTempFile_*_*')
        recordTempFileList = glob.iglob(r'%s' % recordTempFilePattern)
        for tempFile in recordTempFileList:
            os.remove(tempFile)

    def collect(self):
        '''
        function: collect performance statistics
        input : NA
        output: NA
        '''
        try:
            self.logger.debug("Collecting performance statistics.")
            self.cleanTempFiles()

            # get pgxc node of the cluster
            pgxcNodeList = self.getPGXCNode()

            self.logger.debug("pgxcNodeList: %s" % pgxcNodeList)

            # collect PGXC node performance statistics
            self.collectPGXCNodeStat(pgxcNodeList)

            self.logger.debug("Successfully collected performance statistics.")

        except Exception as e:
            raise Exception(str(e))

    def display(self):
        '''
        function: display performance statistics
        input : NA
        output: NA
        '''
        try:
            self.logger.debug("Displaying performance statistics.")

            # clean all the temp files before display the performance data
            queryTempFilePattern = os.path.join(DefaultValue.getTmpDirFromEnv(), 'checkperf_query_*_*')
            queryTempFileList = glob.iglob(r'%s' % queryTempFilePattern)
            for tempFile in queryTempFileList:
                os.remove(tempFile)

            actionList = ["ClusterHostCpuStat",
                          "ClusterMPPDBCpuStat",
                          "ShareBufferStat",
                          "ClusterSortStat",
                          "ClusterIOStat",
                          "ClusterDiskStat",
                          "ClusterActiveSqlCount",
                          "ClusterSessionCount",
                          "NodeCpuStat",
                          "NodeMemoryStat",
                          "NodeIOStat"]

            # Concurrent execute collectStat function
            pool = ThreadPool(DEFAULT_PARALLEL_NUM)
            pool.map(self.collectStat, actionList)
            pool.close()
            pool.join()
            self.outPut()
            self.logger.debug("Successfully displayed performance statistics.")
        except Exception as e:
            raise Exception(str(e))

    def asynCollectDatabaseSize(self, nodePort):
        """
        function: asyn collect database size
        input : NA
        output: NA
        """
        self.logger.debug("Asyn collecting database size on current node.")

        try:
            querySql = "SELECT 'total_database_size:' || " \
                       "SUM(pg_database_size(oid))::bigint FROM pg_catalog.pg_database;"
            if g_DWS_mode:
                (status, result, err_output) = ClusterCommand.excuteSqlOnLocalhost(nodePort, querySql)
                self.logger.debug("Asyn collecting database size. \ncommand: %s \nresult: %s." % (querySql, result))

                if status != 2 or err_output.strip() != "":
                    raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % querySql + " Error:\n%s" % err_output)

                binPath = os.path.join(g_clusterInfo.appPath, "bin")
                databaseSizeFile = os.path.join(binPath, DefaultValue.DB_SIZE_FILE)
                output = result[0][0]
                g_file.write_file_with_default_permission(databaseSizeFile, output)
                tmp_sshTool = SshTool(g_clusterInfo.getClusterNodeNames(), key, self.logger.logFile)
                tmp_sshTool.scpFiles(databaseSizeFile, binPath)
            else:
                (status, output) = ClusterCommand.execSQLCommand(querySql, self.user, '', nodePort, "postgres")
                self.logger.debug("Asyn collecting database size. \ncommand: %s \nresult: %s." % (querySql, output))

                if status != 0 or output.strip() == "":
                    raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % querySql + " Error:\n%s" % output)

                binPath = os.path.join(g_clusterInfo.appPath, "bin")
                databaseSizeFile = os.path.join(binPath, DefaultValue.DB_SIZE_FILE)
                g_file.write_file_with_default_permission(databaseSizeFile, output)
                tmp_sshTool = SshTool(g_clusterInfo.getClusterNodeNames(), key, self.logger.logFile)
                tmp_sshTool.scpFiles(databaseSizeFile, binPath)
        except Exception as e:
            raise Exception(str(e))

        self.logger.debug("Successfully asyn collected database size on current node.")

    def outPut(self):
        '''
        function: output statistics
        input : NA
        output: NA
        '''
        try:
            # judge if enter parameter '--detail'
            if self.showDetail:
                # detail display result
                self.detailDisplay()
            else:
                # summary display result
                self.summaryDisplay()
        except Exception as e:
            raise Exception(str(e))

    def collectClusterHostCpuStat(self):
        '''
        function: collect cluster host CPU statistics
        input : NA
        output: NA
        '''
        self.logger.debug("Collecting cluster host CPU statistics.")
        if g_DWS_mode:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_avg_cpu_total_time, " \
                  "o_avg_cpu_busy_time, " \
                  "o_avg_cpu_iowait_time, " \
                  "o_cpu_busy_perc, " \
                  "o_cpu_io_wait_perc "
            sql += "FROM pmk.get_cluster_host_cpu_stat(null, null);"
            try:
                # execute the sql command
                (_, result, _) = ClusterCommand.excuteSqlOnLocalhost(self.localport, sql)
            except Exception as e:
                raise Exception(str(e))
            # failed to execute the sql command
            if len(result) != 1:
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql + " Error number: %d." % len(result))
            items = result[0]
            if items is not None:
                self.cluster_stat.cluster_host_total_cpu_time = statItem(items[0], "Jiffies")
                self.cluster_stat.cluster_host_cpu_busy_time = statItem(items[1], "Jiffies")
                self.cluster_stat.cluster_host_cpu_iowait_time = statItem(items[2], "Jiffies")
                self.cluster_stat.cluster_host_cpu_busy_time_perc = statItem(items[3], "%")
                self.cluster_stat.cluster_host_cpu_iowait_time_perc = statItem(items[4], "%")
        else:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_stat_collect_time as %s, " % self.__baselineFlag
            sql += "o_avg_cpu_total_time, o_avg_cpu_busy_time, o_avg_cpu_iowait_time, " \
                   "o_cpu_busy_perc, o_cpu_io_wait_perc "
            sql += "FROM pmk.get_cluster_host_cpu_stat(null, null);"
            try:
                # execute the sql command
                records = self.execQueryCommand(sql, 6, 1)
            except Exception as e:
                raise Exception(str(e))
            # failed to execute the sql command
            if len(records) != 1:
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql + " Error number: %d." % len(records))
            items = records[0]
            if items is not None:
                self.cluster_stat.cluster_host_total_cpu_time = statItem(items[1], "Jiffies")
                self.cluster_stat.cluster_host_cpu_busy_time = statItem(items[2], "Jiffies")
                self.cluster_stat.cluster_host_cpu_iowait_time = statItem(items[3], "Jiffies")
                self.cluster_stat.cluster_host_cpu_busy_time_perc = statItem(items[4], "%")
                self.cluster_stat.cluster_host_cpu_iowait_time_perc = statItem(items[5], "%")

        self.logger.debug("Successfully collected cluster host CPU state.")

    def collectClusterMPPDBCpuStat(self):
        '''
        function: collect MPPDB CPU statistics
        input : NA
        output: NA
        '''
        self.logger.debug("Collecting MPPDB CPU statistics.")
        if g_DWS_mode:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_mppdb_cpu_time_perc_wrt_busy_time, o_mppdb_cpu_time_perc_wrt_total_time FROM "
            sql += "pmk.get_cluster_mppdb_cpu_stat(null, null);"
            try:
                (_, result, _) = ClusterCommand.excuteSqlOnLocalhost(self.localport, sql)
            except Exception as e:
                raise Exception(str(e))
            # failed to execute the sql command
            if len(result) != 1:
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql + " Error number: %d." % len(result))
            items = result[0]
            if items is not None:
                self.cluster_stat.cluster_mppdb_cpu_time_in_busy_time = statItem(items[0], "%")
                self.cluster_stat.cluster_mppdb_cpu_time_in_total_time = statItem(items[1], "%")
        else:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_stat_collect_time as %s, o_mppdb_cpu_time_perc_wrt_busy_time, " \
                  "o_mppdb_cpu_time_perc_wrt_total_time FROM " % self.__baselineFlag
            sql += "pmk.get_cluster_mppdb_cpu_stat(null, null);"
            try:
                # execute the sql command
                records = self.execQueryCommand(sql, 3, 2)
            except Exception as e:
                raise Exception(str(e))
            # failed to execute the sql command
            if len(records) != 1:
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql + " Error number: %d." % len(records))
            items = records[0]
            if items is not None:
                self.cluster_stat.cluster_mppdb_cpu_time_in_busy_time = statItem(items[1], "%")
                self.cluster_stat.cluster_mppdb_cpu_time_in_total_time = statItem(items[2], "%")
        self.logger.debug("Successfully collected cluster MPPDB CPU statistics.")

    def collectShareBufferStat(self):
        '''
        function: collect share buffer statistics
        input : NA
        output: NA
        '''
        self.logger.debug("Collecting shared buffer statistics.")
        if g_DWS_mode:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_total_blocks_read, o_total_blocks_hit, " \
                  "o_shared_buffer_hit_ratio, o_total_blocks_read_hit_speed "
            sql += "FROM pmk.get_cluster_shared_buffer_stat(null, null);"
            try:
                (_, result, _) = ClusterCommand.excuteSqlOnLocalhost(self.localport, sql)
            except Exception as e:
                raise Exception(str(e))
            # failed to execute the sql command
            if len(result) != 1:
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql + " Error number: %d." % len(result))
            items = result[0]
            if items is not None:
                self.cluster_stat.cluster_share_buffer_read = statItem(items[0])
                self.cluster_stat.cluster_share_buffer_hit = statItem(items[1])
                self.cluster_stat.cluster_share_buffer_hit_ratio = statItem(items[2], "%")
                self.cluster_stat.cluster_share_buffer_read_hit_speed = statItem(items[3], "MB/s")
        else:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_stat_collect_time as %s, o_total_blocks_read, o_total_blocks_hit, " \
                  "o_shared_buffer_hit_ratio, o_total_blocks_read_hit_speed " % self.__baselineFlag
            sql += "FROM pmk.get_cluster_shared_buffer_stat(null, null);"
            try:
                # execute the sql command
                records = self.execQueryCommand(sql, 5, 3)
            except Exception as e:
                raise Exception(str(e))
            # failed to execute the sql command
            if len(records) != 1:
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql + " Error number: %d." % len(records))
            items = records[0]
            if items is not None:
                self.cluster_stat.cluster_share_buffer_read = statItem(items[1])
                self.cluster_stat.cluster_share_buffer_hit = statItem(items[2])
                self.cluster_stat.cluster_share_buffer_hit_ratio = statItem(items[3], "%")
                self.cluster_stat.cluster_share_buffer_read_hit_speed = statItem(items[4], "MB/s")

        self.logger.debug("Successfully collected shared buffer statistics.")

    def collectClusterSortStat(self):
        '''
        function: collect sort statistics
        input : NA
        output: NA
        '''
        self.logger.debug("Collecting sort statistics.")
        if g_DWS_mode:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_total_memory_sorts, o_total_disk_sorts, o_memory_sort_ratio "
            sql += "FROM pmk.get_cluster_memory_sort_stat(null, null);"
            try:
                # execute the sql command
                (_, result, _) = ClusterCommand.excuteSqlOnLocalhost(self.localport, sql)
            except Exception as e:
                raise Exception(str(e))
            # failed to execute the sql command
            if len(result) != 1:
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql + " Error number: %d." % len(result))
            items = result[0]
            if items is not None:
                self.cluster_stat.cluster_in_memory_sort_count = statItem(items[0])
                self.cluster_stat.cluster_disk_sort_count = statItem(items[1])
                self.cluster_stat.cluster_in_memory_sort_ratio = statItem(items[2], "%")
        else:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_stat_collect_time as %s, o_total_memory_sorts, " \
                  "o_total_disk_sorts, o_memory_sort_ratio " % self.__baselineFlag
            sql += "FROM pmk.get_cluster_memory_sort_stat(null, null);"
            try:
                # execute the sql command
                records = self.execQueryCommand(sql, 4, 4)
            except Exception as e:
                raise Exception(str(e))
            # failed to execute the sql command
            if len(records) != 1:
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql + " Error number: %d." % len(records))
            items = records[0]
            if items is not None:
                self.cluster_stat.cluster_in_memory_sort_count = statItem(items[1])
                self.cluster_stat.cluster_disk_sort_count = statItem(items[2])
                self.cluster_stat.cluster_in_memory_sort_ratio = statItem(items[3], "%")
        self.logger.debug("Successfully collected cluster sort statistics.")

    def collectClusterIOStat(self):
        '''
        function: collect IO statistics
        input : NA
        output: NA
        '''
        self.logger.debug("Collecting IO statistics.")
        if g_DWS_mode:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_number_of_files, o_physical_reads, " \
                  "o_physical_writes, o_read_time, o_write_time "
            sql += "FROM pmk.get_cluster_io_stat(null, null);"
            try:
                # execute the sql command
                (_, result, _) = ClusterCommand.excuteSqlOnLocalhost(self.localport, sql)
            except Exception as e:
                raise Exception(str(e))
            # failed to execute the sql command
            if len(result) != 1:
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql + " Error number: %d." % len(result))
            items = result[0]
            if items is not None:
                self.cluster_stat.cluster_io_stat_number_of_files = statItem(items[0])
                self.cluster_stat.cluster_io_stat_physical_reads = statItem(items[1])
                self.cluster_stat.cluster_io_stat_physical_writes = statItem(items[2])
                self.cluster_stat.cluster_io_stat_read_time = statItem(items[3], "ms")
                self.cluster_stat.cluster_io_stat_write_time = statItem(items[4], "ms")
        else:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_stat_collect_time as %s, o_number_of_files, o_physical_reads, " \
                  "o_physical_writes, o_read_time, o_write_time " % self.__baselineFlag
            sql += "FROM pmk.get_cluster_io_stat(null, null);"
            try:
                # execute the sql command
                records = self.execQueryCommand(sql, 6, 5)
            except Exception as e:
                raise Exception(str(e))
            # failed to execute the sql command
            if len(records) != 1:
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql + " Error number: %d." % len(records))
            items = records[0]
            if items is not None:
                self.cluster_stat.cluster_io_stat_number_of_files = statItem(items[1])
                self.cluster_stat.cluster_io_stat_physical_reads = statItem(items[2])
                self.cluster_stat.cluster_io_stat_physical_writes = statItem(items[3])
                self.cluster_stat.cluster_io_stat_read_time = statItem(items[4], "ms")
                self.cluster_stat.cluster_io_stat_write_time = statItem(items[5], "ms")
        self.logger.debug("Successfully collected cluster IO statistics.")

    def collectClusterDiskStat(self):
        '''
        function: collect disk usage statistics
        input : NA
        output: NA
        '''
        self.logger.debug("Collecting disk usage statistics.")
        if g_DWS_mode:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_tot_datanode_db_size, o_tot_physical_writes, " \
                  "o_avg_write_per_sec, o_max_node_physical_writes "
            sql += "FROM pmk.get_cluster_disk_usage_stat(null, null, '%s');" % str(database_size)
            try:
                # execute the sql command
                (_, result, _) = ClusterCommand.excuteSqlOnLocalhost(self.localport, sql)
            except Exception as e:
                raise Exception(str(e))
            # failed to execute the sql command
            if len(result) != 1:
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql + " Error number: %d." % len(result))
            items = result[0]
            if items is not None:
                self.cluster_stat.cluster_disk_usage_db_size = statItem(items[0].split()[0], items[0].split()[1])
                self.cluster_stat.cluster_disk_usage_tot_physical_writes = statItem(items[1])
                self.cluster_stat.cluster_disk_usage_avg_physical_write = statItem(items[2])
                self.cluster_stat.cluster_disk_usage_max_physical_write = statItem(items[3])
        else:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_stat_collect_time as %s, o_tot_datanode_db_size, o_tot_physical_writes, " \
                  "o_avg_write_per_sec, o_max_node_physical_writes " % self.__baselineFlag
            sql += "FROM pmk.get_cluster_disk_usage_stat(null, null, '%s');" % str(database_size)
            try:
                # execute the sql command
                records = self.execQueryCommand(sql, 5, 6)
            except Exception as e:
                raise Exception(str(e))
            if len(records) != 1:
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql + " Error number: %d." % len(records))
            items = records[0]
            if items is not None:
                self.cluster_stat.cluster_disk_usage_db_size = statItem(items[1].split()[0], items[1].split()[1])
                self.cluster_stat.cluster_disk_usage_tot_physical_writes = statItem(items[2])
                self.cluster_stat.cluster_disk_usage_avg_physical_write = statItem(items[3])
                self.cluster_stat.cluster_disk_usage_max_physical_write = statItem(items[4])
        self.logger.debug("Successfully collected cluster disk statistics.")

    def collectClusterActiveSqlCount(self):
        '''
        function: collect active SQL statistics
        input : NA
        output: NA
        '''
        self.logger.debug("Collecting active SQL statistics.")
        if g_DWS_mode:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_tot_active_sql_count "
            sql += "FROM pmk.get_cluster_active_sql_count(null, null);"
            try:
                # execute the sql command
                (_, result, _) = ClusterCommand.excuteSqlOnLocalhost(self.localport, sql)
            except Exception as e:
                raise Exception(str(e))
            # failed to execute the sql command
            if len(result) != 1:
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql + " Error number: %d." % len(result))
            items = result[0]
            if items is not None:
                self.cluster_stat.cluster_activity_active_sql_count = statItem(items[0])
        else:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_stat_collect_time as %s, o_tot_active_sql_count " % self.__baselineFlag
            sql += "FROM pmk.get_cluster_active_sql_count(null, null);"
            try:
                # execute the sql command
                records = self.execQueryCommand(sql, 2, 7)
            except Exception as e:
                raise Exception(str(e))
            if len(records) != 1:
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql + " Error number: %d." % len(records))
            items = records[0]
            if items is not None:
                self.cluster_stat.cluster_activity_active_sql_count = statItem(items[1])
        self.logger.debug("Successfully collected cluster active SQL count.")

    def collectClusterSessionCount(self):
        '''
        function: collect session count statistics
        input : NA
        output: NA
        '''
        self.logger.debug("Collecting session count statistics.")
        if g_DWS_mode:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_tot_session_count "
            sql += "FROM pmk.get_cluster_session_count(null, null);"
            try:
                # execute the sql command
                (_, result, _) = ClusterCommand.excuteSqlOnLocalhost(self.localport, sql)
            except Exception as e:
                raise Exception(str(e))
            # failed to execute the sql command
            if len(result) != 1:
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql + " Error number: %d." % len(result))
            items = result[0]
            if items is not None:
                self.cluster_stat.cluster_activity_session_count = statItem(items[0])
        else:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_stat_collect_time as %s, o_tot_session_count " % self.__baselineFlag
            sql += "FROM pmk.get_cluster_session_count(null, null);"
            try:
                # execute the sql command
                records = self.execQueryCommand(sql, 2, 8)
            except Exception as e:
                raise Exception(str(e))
            if len(records) != 1:
                raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql + " Error number: %d." % len(records))
            items = records[0]
            if items is not None:
                self.cluster_stat.cluster_activity_session_count = statItem(items[1])
        self.logger.debug("Successfully collected cluster session count.")

    def handle_node_cpu_records(self, result, sql, n):
        """
        function: handle node cpu records
        input: result, sql, n, n=1 is dws mode, n=0 is normal mode
        output: NA
        """
        recordsCount = len(result)
        # failed to execute the sql command
        if recordsCount == 0:
            raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql + " Error: No records returned.")
        for i in range(0, recordsCount):
            cpu_records = result[i]
            found = False
            node = None
            for tmp_stat in self.node_stat:
                if tmp_stat.nodename == cpu_records[n].strip():
                    found = True
                    node = tmp_stat
                    break
            if not found:
                node = nodeStatistics(cpu_records[n].strip())
                self.node_stat.append(node)
            node.node_mppdb_cpu_busy_time = statItem(cpu_records[n + 1], "Jiffies")
            node.node_host_cpu_busy_time = statItem(cpu_records[n + 2], "Jiffies")
            node.node_host_cpu_total_time = statItem(cpu_records[n + 3], "Jiffies")
            node.node_mppdb_cpu_time_in_busy_time = statItem(cpu_records[n + 4], "%")
            node.node_mppdb_cpu_time_in_total_time = statItem(cpu_records[n + 5], "%")

    def collectNodeCpuStat(self):
        '''
        function: collect node CPU statistics
        input : NA
        output: NA
        '''
        self.logger.debug("Collecting node CPU statistics.")
        if g_DWS_mode:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_node_name, "
            sql += "o_mppdb_cpu_time, o_host_cpu_busy_time, o_host_cpu_total_time, " \
                   "o_mppdb_cpu_time_perc_wrt_busy_time, "
            sql += "o_mppdb_cpu_time_perc_wrt_total_time " \
                   "FROM pmk.get_node_cpu_stat('all', null, null);"
            try:
                # execute the sql command
                (_, result, _) = ClusterCommand.excuteSqlOnLocalhost(self.localport, sql)
            except Exception as e:
                raise Exception(str(e))
            self.handle_node_cpu_records(result, sql, 0)
        else:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_stat_collect_time as %s, o_node_name, " % self.__baselineFlag
            sql += "o_mppdb_cpu_time, o_host_cpu_busy_time, " \
                   "o_host_cpu_total_time, o_mppdb_cpu_time_perc_wrt_busy_time, "
            sql += "o_mppdb_cpu_time_perc_wrt_total_time " \
                   "FROM pmk.get_node_cpu_stat('all', null, null);"
            try:
                # execute the sql command
                records = self.execQueryCommand(sql, 7, 9)
            except Exception as e:
                raise Exception(str(e))
            self.handle_node_cpu_records(records, sql, 1)
        self.logger.debug("Successfully collected node CPU statistics.")

    def handle_node_memory_records(self, result, sql, n):
        """
        function: handle node cpu records
        input: result, sql, n, n=1 is dws mode, n=0 is normal mode
        output: NA
        """
        recordsCount = len(result)
        # failed to execute the sql command
        if recordsCount == 0:
            raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql + " Error: No records returned.")
        for i in range(0, recordsCount):
            memory_records = result[i]
            found = False
            node = None
            for tmp_stat in self.node_stat:
                if tmp_stat.nodename == memory_records[n].strip():
                    found = True
                    node = tmp_stat
                    break
            if not found:
                node = nodeStatistics(memory_records[n].strip())
                self.node_stat.append(node)
            node.node_physical_memory = statItem(memory_records[n + 1], "Bytes")
            node.node_shared_buffer_size = statItem(memory_records[n + 2], "Bytes")
            node.node_shared_buffer_hit_ratio = statItem(memory_records[n + 3], "%")
            node.node_in_memory_sorts = statItem(memory_records[n + 4], )
            node.node_in_disk_sorts = statItem(memory_records[n + 5], )
            node.node_in_memory_sort_ratio = statItem(memory_records[n + 6], "%")
            node.node_db_memory_usage = statItem(memory_records[n + 7], "Bytes")

    def collectNodeMemoryStat(self):
        '''
        function: collect node memory statistics
        input : NA
        output: NA
        '''
        self.logger.debug("Collecting node memory statistics.")
        if g_DWS_mode:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_node_name, "
            sql += "o_physical_memory, o_shared_buffer_size, " \
                   "o_shared_buffer_hit_ratio, o_sorts_in_memory, "
            sql += "o_sorts_in_disk, o_in_memory_sort_ratio, o_db_memory_usage " \
                   "FROM pmk.get_node_memory_stat('all', null, null);"
            try:
                # execute the sql command
                (_, result, _) = ClusterCommand.excuteSqlOnLocalhost(self.localport, sql)
            except Exception as e:
                raise Exception(str(e))
            self.handle_node_memory_records(result, sql, 0)
        else:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_stat_collect_time as %s, o_node_name, " % self.__baselineFlag
            sql += "o_physical_memory, o_shared_buffer_size, " \
                   "o_shared_buffer_hit_ratio, o_sorts_in_memory, "
            sql += "o_sorts_in_disk, o_in_memory_sort_ratio, o_db_memory_usage " \
                   "FROM pmk.get_node_memory_stat('all', null, null);"
            try:
                # execute the sql command
                records = self.execQueryCommand(sql, 9, 10)
            except Exception as e:
                raise Exception(str(e))
            self.handle_node_memory_records(records, sql, 1)
        self.logger.debug("Successfully collected node memory statistics.")

    def handle_node_io_records(self, result, sql, n):
        """
        function: handle node io records
        input: result, sql, n, n=1 is dws mode, n=0 is normal mode
        output: NA
        """
        recordsCount = len(result)
        # failed to execute the sql command
        if recordsCount == 0:
            raise Exception(ErrorCode.GAUSS_513["GAUSS_51300"] % sql + " Error: No records returned.")

        for i in range(0, recordsCount):
            io_records = result[i]
            found = False
            node = None
            for tmp_stat in self.node_stat:
                if tmp_stat.nodename == io_records[n].strip():
                    found = True
                    node = tmp_stat
                    break
            if not found:
                node = nodeStatistics(io_records[n].strip())
                self.node_stat.append(node)
            node.node_number_of_files = statItem(io_records[n + 1])
            node.node_physical_reads = statItem(io_records[n + 2])
            node.node_physical_writes = statItem(io_records[n + 3])
            node.node_read_time = statItem(io_records[n + 4])
            node.node_write_time = statItem(io_records[n + 5])

    def collectNodeIOStat(self):
        '''
        function: collect node IO statistics
        input : NA
        output: NA
        '''
        self.logger.debug("Collecting node IO statistics.")
        if g_DWS_mode:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_node_name, "
            sql += "o_number_of_files, o_physical_reads, o_physical_writes, o_read_time, "
            sql += "o_write_time FROM pmk.get_node_io_stat('all', null, null);"
            try:
                # execute the sql command
                (_, result, _) = ClusterCommand.excuteSqlOnLocalhost(self.localport, sql)
            except Exception as e:
                raise Exception(str(e))
            self.handle_node_io_records(result, sql, 0)
        else:
            sql = "SET autoanalyze=off; SET datestyle='ISO, MDY'; " \
                  "SELECT o_stat_collect_time as %s, o_node_name, " % self.__baselineFlag
            sql += "o_number_of_files, o_physical_reads, o_physical_writes, o_read_time, "
            sql += "o_write_time FROM pmk.get_node_io_stat('all', null, null);"
            try:
                # execute the sql command
                records = self.execQueryCommand(sql, 7, 11)
            except Exception as e:
                raise Exception(str(e))
            self.handle_node_io_records(records, sql, 1)
        self.logger.debug("Successfully collected node IO statistics.")

    def displayOneStatItem(self, desc, disvalue):
        '''
        function: display one statistic item
        input : desc, disvalue
        output: NA
        '''
        # judge if disvalue is none
        if str(disvalue) != "":
            self.writeOutput("    %-45s:    %s" % (desc, str(disvalue)))
        else:
            self.writeOutput("    %-45s:    0" % (desc))

    def summaryDisplay(self):
        '''
        function: summary display
        input : NA
        output: NA
        '''
        # show cluster statistics summary information
        self.writeOutput("Cluster statistics information:")
        # show host CPU busy time ratio
        self.displayOneStatItem("Host CPU busy time ratio", self.cluster_stat.cluster_host_cpu_busy_time_perc)
        # show MPPDB CPU time
        self.displayOneStatItem("MPPDB CPU time % in busy time", self.cluster_stat.cluster_mppdb_cpu_time_in_busy_time)
        # show shared buffer hit ratio
        self.displayOneStatItem("Shared Buffer Hit ratio", self.cluster_stat.cluster_share_buffer_hit_ratio)
        # show In-memory sort ratio
        self.displayOneStatItem("In-memory sort ratio", self.cluster_stat.cluster_in_memory_sort_ratio)
        # show physical reads
        self.displayOneStatItem("Physical Reads", self.cluster_stat.cluster_io_stat_physical_reads)
        # show physical writes
        self.displayOneStatItem("Physical Writes", self.cluster_stat.cluster_io_stat_physical_writes)
        # show DB size
        self.displayOneStatItem("DB size", self.cluster_stat.cluster_disk_usage_db_size)
        # show Total Physical writes
        self.displayOneStatItem("Total Physical writes", self.cluster_stat.cluster_disk_usage_tot_physical_writes)
        # show Active SQL count
        self.displayOneStatItem("Active SQL count", self.cluster_stat.cluster_activity_active_sql_count)
        # show Session count
        self.displayOneStatItem("Session count", self.cluster_stat.cluster_activity_session_count)

    def show_cluster_detail_statics(self):
        """
        function: cluster detail display
        input : NA
        output: NA
        """
        self.writeOutput("Cluster statistics information:")

        # show host CPU ratio in detail
        self.writeOutput("Host CPU usage rate:")
        self.displayOneStatItem("Host total CPU time", self.cluster_stat.cluster_host_total_cpu_time)
        self.displayOneStatItem("Host CPU busy time", self.cluster_stat.cluster_host_cpu_busy_time)
        self.displayOneStatItem("Host CPU iowait time", self.cluster_stat.cluster_host_cpu_iowait_time)
        self.displayOneStatItem("Host CPU busy time ratio", self.cluster_stat.cluster_host_cpu_busy_time_perc)
        self.displayOneStatItem("Host CPU iowait time ratio", self.cluster_stat.cluster_host_cpu_iowait_time_perc)

        # show MPPDB CPU time in detail
        self.writeOutput("MPPDB CPU usage rate:")
        self.displayOneStatItem("MPPDB CPU time % in busy time", self.cluster_stat.cluster_mppdb_cpu_time_in_busy_time)
        self.displayOneStatItem("MPPDB CPU time % in total time",
                                self.cluster_stat.cluster_mppdb_cpu_time_in_total_time)

        # show Shared Buffer Hit ratio in detail
        self.writeOutput("Shared buffer hit rate:")
        self.displayOneStatItem("Shared Buffer Reads", self.cluster_stat.cluster_share_buffer_read)
        self.displayOneStatItem("Shared Buffer Hits", self.cluster_stat.cluster_share_buffer_hit)
        self.displayOneStatItem("Shared Buffer Hit ratio", self.cluster_stat.cluster_share_buffer_hit_ratio)
        self.displayOneStatItem("Shared Buffer Read Hit Speed", self.cluster_stat.cluster_share_buffer_read_hit_speed)

        # show In-memory sort ratio in detail
        self.writeOutput("In memory sort rate:")
        self.displayOneStatItem("In-memory sort count", self.cluster_stat.cluster_in_memory_sort_count)
        self.displayOneStatItem("In-disk sort count", self.cluster_stat.cluster_disk_sort_count)
        self.displayOneStatItem("In-memory sort ratio", self.cluster_stat.cluster_in_memory_sort_ratio)

        # show I/O usage in detail
        self.writeOutput("I/O usage:")
        self.displayOneStatItem("Number of files", self.cluster_stat.cluster_io_stat_number_of_files)
        self.displayOneStatItem("Physical Reads", self.cluster_stat.cluster_io_stat_physical_reads)
        self.displayOneStatItem("Physical Writes", self.cluster_stat.cluster_io_stat_physical_writes)
        self.displayOneStatItem("Read Time", self.cluster_stat.cluster_io_stat_read_time)
        self.displayOneStatItem("Write Time", self.cluster_stat.cluster_io_stat_write_time)

        # show Disk usage in detail
        self.writeOutput("Disk usage:")
        self.displayOneStatItem("DB size", self.cluster_stat.cluster_disk_usage_db_size)
        self.displayOneStatItem("Total Physical writes", self.cluster_stat.cluster_disk_usage_tot_physical_writes)
        self.displayOneStatItem("Average Physical write", self.cluster_stat.cluster_disk_usage_avg_physical_write)
        self.displayOneStatItem("Maximum Physical write", self.cluster_stat.cluster_disk_usage_max_physical_write)

        # show SQL count and session count in detail
        self.writeOutput("Activity statistics:")
        self.displayOneStatItem("Active SQL count", self.cluster_stat.cluster_activity_active_sql_count)
        self.displayOneStatItem("Session count", self.cluster_stat.cluster_activity_session_count)

    def show_node_detail_statics(self):
        """
        function: node detail display
        input : NA
        output: NA
        """
        self.writeOutput("Node statistics information:")
        for node in self.node_stat:
            # show node name
            self.writeOutput("%s:" % node.nodename)
            # show cpu usage in host
            self.displayOneStatItem("MPPDB CPU Time", node.node_mppdb_cpu_busy_time)
            self.displayOneStatItem("Host CPU Busy Time", node.node_host_cpu_busy_time)
            self.displayOneStatItem("Host CPU Total Time", node.node_host_cpu_total_time)
            self.displayOneStatItem("MPPDB CPU Time % in Busy Time", node.node_mppdb_cpu_time_in_busy_time)
            self.displayOneStatItem("MPPDB CPU Time % in Total Time", node.node_mppdb_cpu_time_in_total_time)

            # show memory usage in host
            self.displayOneStatItem("Physical memory", node.node_physical_memory)
            self.displayOneStatItem("DB Memory usage", node.node_db_memory_usage)
            self.displayOneStatItem("Shared buffer size", node.node_shared_buffer_size)
            self.displayOneStatItem("Shared buffer hit ratio", node.node_shared_buffer_hit_ratio)
            self.displayOneStatItem("Sorts in memory", node.node_in_memory_sorts)
            self.displayOneStatItem("Sorts in disk", node.node_in_disk_sorts)
            self.displayOneStatItem("In-memory sort ratio", node.node_in_memory_sort_ratio)

            # show IO usage in host
            self.displayOneStatItem("Number of files", node.node_number_of_files)
            self.displayOneStatItem("Physical Reads", node.node_physical_reads)
            self.displayOneStatItem("Physical Writes", node.node_physical_writes)
            self.displayOneStatItem("Read Time", node.node_read_time)
            self.displayOneStatItem("Write Time", node.node_write_time)

    def detailDisplay(self):
        '''
        function: detail display
        input : NA
        output: NA
        '''
        # show cluster statistics information
        self.show_cluster_detail_statics()
        # show node statistics information
        self.show_node_detail_statics()


if __name__ == '__main__':

    import getopt

    sys.path.append(sys.path[0] + "/../../")
    from gspylib.common.GaussLog import GaussLog


    def usage():
        """
Usage:
    python3 GaussStat.py -p installpath -u user -c ip:port [-f output] [-d] [-l log]

options:
    -p                                Install path.
    -u                                Database user name.
    -c                                Host information.
    -d --detail                       Show the detail info about performance check.
    -l --logpath=logfile              The log file of operation.
    -h --help                         Show this help, then exit.
        """
        print(usage.__doc__)


    # parse parameters from command line
    opts = None
    try:
        (opts, args) = getopt.getopt(sys.argv[1:], "t:p:u:c:l:dh", ["logpath=",
                                                                    "detail", "help", "dws-mode", "curr-time=",
                                                                    "last-time=", "snapshot-id=",
                                                                    "flag-num=", "master-host=", "database-size=",
                                                                    "abnormal-CN=", "log-action=", "log-uuid=",
                                                                    "log-step="])

        if len(args) > 0:
            GaussLog.exitWithError(ErrorCode.GAUSS_500["GAUSS_50000"] % str(args[0]))
    except Exception as pex:
        GaussLog.exitWithError(ErrorCode.GAUSS_500["GAUSS_50000"] % str(pex))

    # state variable
    installPath = ""
    user = ""
    logFile = ""
    localPort = ""
    detail = False
    currTime = ""
    lastTime = ""
    snapshotId = ""
    flagNum = 0
    masterHost = ""
    action = ""
    dwsFlag = False
    database_size = 0
    abnormalCN = []
    logAction = ""
    logUuid = ""
    logStep = 0

    # get parameter value
    key_map = {
        "-t": action,
        "-p": installPath,
        "-u": user,
        "-c": localPort,
        "--curr-time": currTime,
        "--last-time": lastTime,
        "--snapshot-id": snapshotId,
        "--flag-num": flagNum,
        "--master-host": masterHost,
        "--log-action": logAction,
        "--log-uuid": logUuid,
        "--log-step": logStep
    }
    for (key, value) in opts:
        if key == "-h" or key == "--help":
            usage()
            sys.exit(0)
        if key in key_map.keys():
            key_map[key] = value.strip()
        elif key == "-l" or key == "--logpath":
            logFile = value.strip()
        elif key == "-d" or key == "--detail":
            detail = True
        elif key == "--dws-mode":
            dwsFlag = True
        elif key == "--database-size":
            database_size = int(value)
        elif key == "--abnormal-CN":
            abnormalCN = str(value).split(",")

    action = key_map["-t"]
    installPath = key_map["-p"]
    user = key_map["-u"]
    localPort = key_map["-c"]
    currTime = key_map["--curr-time"]
    lastTime = key_map["--last-time"]
    snapshotId = key_map["--snapshot-id"]
    flagNum = key_map["--flag-num"]
    masterHost = key_map["--master-host"]
    logAction = key_map["--log-action"]
    logUuid = key_map["--log-uuid"]
    logStep = key_map["--log-step"]

    # judge if install path exists or user or local port is none
    if not os.path.exists(installPath) or user == "" or localPort == "":
        usage()
        GaussLog.exitWithError(ErrorCode.GAUSS_500["GAUSS_50001"] % "p or -u or -c" + ".")

    # get log file
    if (logFile == ""):
        logFile = "%s/om/%s" % (DefaultValue.getUserLogDirWithUser(user), DefaultValue.LOCAL_LOG_FILE)
    if logStep != 0:
        logStep = int(logStep)
    else:
        logStep = 0
    # initialize log
    LOG_DEBUG = 1
    logger = GaussLog(logFile, functionName, LOG_DEBUG, logAction, logUuid, logStep)

    try:
        g_clusterInfo = dbClusterInfo()
        # Init cluster from static configuration file
        if g_clusterInfo.isMasterStandbyCluster() or g_clusterInfo.isMasterStandbyMultiAZCluster():
            g_clusterInfo.initFromStaticConfig(user)
            localNodeInfo = g_clusterInfo.getDbNodeByName(socket.gethostname())
            datadir = localNodeInfo.cmagents[0].datadir
            security_mode_value = DefaultValue.getSecurityMode(datadir)
            if security_mode_value == "on":
                g_DWS_mode = True
            stat = GaussStat(installPath, user, localPort, currTime, lastTime, snapshotId, int(flagNum),
                             masterHost, logger, detail)
            if action == ACTION_INSTALL_PMK:
                # install PMK schema
                stat.installPMKSchema()
            elif action == ACTION_COLLECT_STAT:
                # collect performance statistics
                stat.collect()
            elif action == ACTION_DISPLAY_STAT:
                # display performance statistics
                stat.display()
            elif action == ACTION_ASYN_COLLECT:
                # asyn collect database size
                stat.asynCollectDatabaseSize(localPort)

        else:
            raise Exception("The clusterType which should be 'MasterStandbyCluster' couldn't be identified.")
    except Exception as ae:
        logger.logExit(str(ae))
    # close log file
    logger.closeLog()
