#!/usr/bin/env python3
# -*- coding:utf-8 -*-


try:
    import sys
    import importlib

    importlib.reload(sys)
    import os
    import math
    import subprocess
    from gspylib.inspection.common import SharedFuncs
    from gspylib.inspection.common.CheckItem import BaseItem
    from gspylib.inspection.common.CheckResult import ResultStatus
    from gspylib.common.DbClusterStatus import DbClusterStatus
    from gspylib.common.Common import ClusterCommand
except ImportError as ie:
    raise Exception("[GAUSS-52200] : Unable to import module: %s." % str(ie))


class CheckDBParams(BaseItem):
    def __init__(self):
        super(CheckDBParams, self).__init__(self.__class__.__name__)

    def doCheck(self):
        local_master_dn = []
        #Gets the current node information
        nodeInfo = self.cluster.getDbNodeByName(self.host)
        # Get the number of instances
        InatorsList = nodeInfo.coordinators + nodeInfo.datanodes
        #Get local primary DN id
        for dn in nodeInfo.datanodes:
            if dn.instanceId in self.master_dn_list:
                local_master_dn.append(dn)
        self.result.raw = ""
        #Determine if there are DN and CN instances
        if (len(nodeInfo.coordinators) < 1 and len(local_master_dn) < 1):
            self.result.raw = "There is no CN instance and primary DN instance in the current node."
            self.result.rst = ResultStatus.OK
            return
        for inst in InatorsList:
            self.CheckGaussdbParameters(inst, nodeInfo, local_master_dn)
        if (self.result.rst != ResultStatus.NG):
            self.result.rst = ResultStatus.OK

    def CheckSingleGaussdbParameter(self, port, desc, INDENTATION_VALUE_INT=60):
        """
        function: check gaussdb instance parameters
        input: int, string, int
        output: bool
        """
        sqlResultFile = ""
        try:
            flag = True
            # Generate different temporary files when parallel
            # Identify by instance number
            # Remove parentheses from the instance number
            InstNum = desc.replace('(', '')
            InstNum = InstNum.replace(')', '')
            kB = 1 * 1024
            shared_buffers, shared_buffer_size = self.get_shared_buffers(port)
            try:
                # check shared_buffers
                strCmd = "cat /proc/sys/kernel/shmmax"
                shmmax = SharedFuncs.runShellCmd(strCmd)
                # check shmall parameters
                strCmd = "cat /proc/sys/kernel/shmall"
                shmall = SharedFuncs.runShellCmd(strCmd)
                # get PAGESIZE
                strCmd = "getconf PAGESIZE"
                PAGESIZE = SharedFuncs.runShellCmd(strCmd)
                if shared_buffers < 128 * kB:
                    self.result.raw += "Shared_buffers must be greater than or equal to 128KB.\n"
                    flag = False
                elif shared_buffers > int(shmmax):
                    self.result.raw += "Shared_buffers must be less than shmmax(%d).\n" % int(shmmax)
                    flag = False
                elif shared_buffers > int(shmall) * int(PAGESIZE):
                    self.result.raw += "Shared_buffers must be less than shmall*PAGESIZE(%d).\n" % \
                                       int(shmall) * int(PAGESIZE)
                    flag = False
                else:
                    self.result.raw += "%s Shared buffers size is %s.\n" % (desc, shared_buffer_size)
                # check sem
                flag = self.check_sem(port, desc, flag)
            except Exception as ex:
                flag = False
                self.result.raw += str(ex)
            if os.path.exists(sqlResultFile):
                os.remove(sqlResultFile)
            return flag
        except Exception as e:
            if os.path.exists(sqlResultFile):
                os.remove(sqlResultFile)
            raise Exception("%s: Abnormal reason:%s" %
                            (("The max number of %s connections.\n" % desc).ljust(INDENTATION_VALUE_INT), str(e)))

    def CheckGaussdbParameters(self, inst, nodeInfo, primaryDNidList):
        """
        function: Check gaussdb instance parameters
        input: instance
        output: NA
        """
        INDENTATION_VALUE_INT = 50
        resultList = []
        try:
            # Check all CN instances
            if inst in nodeInfo.coordinators:
                resultList.append(self.CheckSingleGaussdbParameter(inst.port, "CN(%s)" %
                                                                   str(inst.instanceId),
                                                                   INDENTATION_VALUE_INT))
            # Check all master DN instances
            if primaryDNidList:
                if inst in nodeInfo.datanodes:
                    if inst.instanceId in primaryDNidList:
                        resultList.append(self.CheckSingleGaussdbParameter(inst.port, "DN(%s)" %
                                                                           str(inst.instanceId),
                                                                           INDENTATION_VALUE_INT))
            if False in resultList:
                self.result.rst = ResultStatus.NG
                return
        except Exception as e:
            raise Exception(str(e))

    def doSet(self):
        resultStr = ""
        cmd = "gs_guc set -N all -I all -Z coordinator -c 'shared_buffers=1GB' -c 'max_connections=400'"
        (status, output) = subprocess.getstatusoutput(cmd)
        if status != 0:
            resultStr += "Falied to set cn shared_buffers.\nError : %s" % output
        cmd = "gs_guc set -N all -I all -Z datanode -c 'shared_buffers=1GB' -c 'max_connections=3000'"
        (status, output) = subprocess.getstatusoutput(cmd)
        if status != 0:
            resultStr += "Falied to set dn shared_buffers.\nError : %s" % output
        if len(resultStr) > 0:
            self.result.val = resultStr
        else:
            self.result.val = "Set shared_buffers successfully."

    def get_shared_buffers(self, port):
        GB = 1 * 1024 * 1024 * 1024
        MB = 1 * 1024 * 1024
        kB = 1 * 1024
        shared_buffers = 0
        # Execute the query command
        sqlcmd = "show shared_buffers;"
        output = SharedFuncs.runSqlCmd(sqlcmd, self.user, "", port, self.tmpPath, "postgres", self.mpprcFile)
        shared_buffer_size = str(output)
        # The result of the conversion query is a regular display
        if ((shared_buffer_size[0:-2].isdigit() is True)
                and ((shared_buffer_size[-2:] > "GB") - (shared_buffer_size[-2:] < "GB")) == 0):
            shared_buffers = int(shared_buffer_size[0:-2]) * GB
        if ((shared_buffer_size[0:-2].isdigit() is True)
                and ((shared_buffer_size[-2:] > "MB") - (shared_buffer_size[-2:] < "MB")) == 0):
            shared_buffers = int(shared_buffer_size[0:-2]) * MB
        if ((shared_buffer_size[0:-2].isdigit() is True)
                and ((shared_buffer_size[-2:] > "kB") - (shared_buffer_size[-2:] < "kB")) == 0):
            shared_buffers = int(shared_buffer_size[0:-2]) * kB
        if ((shared_buffer_size[0:-1].isdigit() is True)
                and ((shared_buffer_size[-1:] > "B") - (shared_buffer_size[-1:] < "B")) == 0):
            shared_buffers = int(shared_buffer_size[0:-1])
        return shared_buffers, shared_buffer_size

    def check_sem(self, port, desc, flag):
        # get max connection number
        sqlcmd = "show max_connections;"
        output = SharedFuncs.runSqlCmd(sqlcmd, self.user, "", port, self.tmpPath, "postgres", self.mpprcFile)
        maxConnections = int(output)
        if desc.find("CN(") < 0:
            self.result.raw += "The max number of %s connections is %s.\n" % (desc, maxConnections)
        if desc.find("CN(") >= 0:
            strCmd = "cat /proc/sys/kernel/sem"
            output = SharedFuncs.runShellCmd(strCmd)
            paramList = output.split("\t")
            if int(paramList[0]) < 17:
                self.result.raw += "The system limit for the maximum number of semaphores per set (SEMMSL)" \
                                   " must be greater than or equal to 17. The current SEMMSL value is: " + \
                                   str(paramList[0]) + ".\n"
                flag = False

            if int(paramList[3]) < math.ceil((maxConnections + 150) // 16):
                self.result.raw += "The system limit for the maximum number of semaphore sets (SEMMNI) must" \
                                   " be greater than or equal to the" \
                                   " value(math.ceil((maxConnections + 150) / 16)) " + \
                                   str(math.ceil((maxConnections + 150) // 16)) + \
                                   ", The current SEMMNI value is: " + str(paramList[3]) + ".\n"
                flag = False
            elif int(paramList[1]) < math.ceil((maxConnections + 150) // 16) * 17:
                self.result.raw += "The system limit for the maximum number of semaphores (SEMMNS)" \
                                   " must be greater than or equal to the" \
                                   " value(math.ceil((maxConnections + 150) / 16) * 17) " + \
                                   str(math.ceil((maxConnections + 150) // 16) * 17) + \
                                   ", The current SEMMNS value is: " + str(paramList[1]) + ".\n"
                flag = False
            else:
                self.result.raw += "The max number of %s connections is %s.\n" % (desc, maxConnections)
        return flag
