#!/usr/bin/env python3
# -*- coding:utf-8 -*-
try:
    import os
    import sys
    import subprocess
    import platform
    import socket
    import time

    sys.path.append(sys.path[0] + "/../../../")
    localDirPath = os.path.dirname(os.path.realpath(__file__))
    sys.path.insert(0, localDirPath + "/../../../../lib/netifaces/")
    sys.path.append(localDirPath + "/../../inspection/lib/netifaces/")
    from gspylib.common.ErrorCode import ErrorCode
    from gspylib.os.platform.generic_platform import GenericPlatform, findCmdInPath
    from gspylib.os.platform.get_platform import GetPlatform
    from gspylib.os.platform import support_platform

    try:
        from netifaces import interfaces, ifaddresses, AF_INET, AF_INET6
    except ImportError:
        # get python unicode value. The current environment python is compiled with UCS2 or UCS4.
        # 1114111 is UCS4
        # 65535 is UCS2
        flagNum = 4 if sys.maxunicode == 1114111 else 2
        omToolsNetifacesPath = os.path.join(localDirPath, "./../../../../lib/netifaces/netifaces.so")
        inspectToolsNetifacesPath = os.path.join(localDirPath,
                                                 "./../../../../script/gspylib/inspection/lib/netifaces/netifaces.so")
        newPythonDependNetifacesPath = "%s_UCS%d" % (omToolsNetifacesPath, flagNum)
        glo_cmd = "rm -f '%s' && cp -f -p '%s' '%s' " % (omToolsNetifacesPath,
                                                         newPythonDependNetifacesPath,
                                                         omToolsNetifacesPath)
        glo_cmd += " && rm -f '%s' && cp -f -p '%s' '%s' " % (inspectToolsNetifacesPath,
                                                              newPythonDependNetifacesPath,
                                                              inspectToolsNetifacesPath)
        flagExce = True
        for retryNum in range(3):
            (statusExec, outputExec) = subprocess.getstatusoutput(glo_cmd)
            if statusExec != 0:
                flagExce = False
                time.sleep(1)
            else:
                flagExce = True
                break
        if not flagExce:
            raise Exception("Failed to execute cmd: %s. Error:\n%s" % (glo_cmd, outputExec))
        from netifaces import interfaces, ifaddresses, AF_INET, AF_INET6
except Exception as ie:
    sys.exit("[GAUSS-52200] : Unable to import module: %s." % str(ie))


class platformInfo:
    """
    """

    def __init__(self):
        """
        function : Init class platforminfo
        input  : NA
        output : NA
        """
        self.distname = ""
        self.version = ""
        self.id = ""
        self.bits = ""
        self.linkage = ""
        self.patchlevel = ""


class LinuxPlatform(GenericPlatform):
    """
    manage Linux command,config or service for muti-platform
    """

    def __init__(self, kernel_flag=""):
        self.kernel_flag = kernel_flag

    def getCpuInfoFile(self):
        return "/proc/cpuinfo"

    def getMemInfoFile(self):
        return "/proc/meminfo"

    def getBondInfoPath(self):
        return "/proc/net/bonding/"

    def getSysctlFile(self):
        return "/etc/sysctl.conf"

    def getMtablFile(self):
        return "/etc/mtab"

    def getInterruptFile(self):
        return "/proc/interrupts"

    def getHostsFile(self):
        return "/etc/hosts"

    def getName(self):
        return "linux"

    def getDefaultLocale(self):
        return 'en_US.utf8'

    def getDiskFreeCmd(self, Mounted="", inode=False):
        # -P is for POSIX formatting.  Prevents error
        # on lines that would wrap
        return findCmdInPath('df') + " -Pk " + (" -i " if inode else " -h ") + Mounted

    def getDirSizeCmd(self, path, unit=""):
        # -s only shows the total size
        # unit specify the output size unit
        return findCmdInPath('du') + " -s " + (" -B %s " % unit if unit else " -h ") + path

    def getSadcCmd(self, interval, outFileName):
        cmd = "/usr/lib64/sa/sadc -F -d " + str(interval) + " " + outFileName
        return cmd

    def getCompressFilesCmd(self, tarName, fileSrc):
        cmd = "%s -zvcf '%s' %s" % (self.getTarCmd(), tarName, fileSrc)
        return cmd

    def getDecompressFilesCmd(self, srcPackage, dest):
        cmd = "%s -zxvf '%s' -C '%s'" % (self.getTarCmd(), srcPackage, dest)
        return cmd

    def getCompressZipFilesCmd(self, zipName, fileSrc):
        cmd = "cd %s && %s -r '%s.zip' ./*" % (fileSrc, self.getZipCmd(), zipName)
        return cmd

    def getDecompressZipFilesCmd(self, srcPackage, dest):
        cmd = "%s -o '%s' -d '%s'" % (self.getUnzipCmd(), srcPackage, dest)
        return cmd

    def getReplaceFileLineContentCmd(self, oldLine, newLine, path):
        cmd = "%s -i \"s/%s/%s/g\" '%s'" % (self.getSedCmd(), oldLine, newLine, path)
        return cmd

    def getDirPermissionCmd(self, dirPath):
        cmd = "%s -ld '%s' | %s -F\" \" '{print $1}' " % (self.getListCmd(), dirPath, self.getAwkCmd())
        return cmd

    def getFileSHA256Cmd(self, fileName):
        cmd = "%s '%s' | %s -F\" \" '{print $1}' " % (self.getSHA256Cmd(), fileName, self.getAwkCmd())
        return cmd

    def getExecuteCmdWithUserProfile(self, user, userProfile, executeCmd, ignoreError=True):
        """
        """
        if (user != "") and (os.getuid() == 0):
            cmd = "su - %s -c '%s %s; %s'" % (user, self.getSourceCmd(), userProfile, executeCmd)
        else:
            cmd = "%s %s; %s" % (self.getSourceCmd(), userProfile, executeCmd)
        if ignoreError:
            cmd += " 2>/dev/null"
        return cmd

    def getUserHomePath(self):
        """
        """
        # converts the relative path to an absolute path
        cmd = "echo ~ 2>/dev/null"
        (status, output) = subprocess.getstatusoutput(cmd)
        if status != 0:
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50219"] % "user home")
        return output

    def checkProcExists(self, procName):
        """
        """
        import psutil
        for pid in psutil.pids():
            p = psutil.Process(pid)
            if procName == p.name():
                return True
        return False

    def checkProcAlive(self, procPid):
        """
        """
        try:
            os.kill(procPid, 0)
        except OSError:
            return False
        else:
            return True

    def getIpAddressAndNICv4(self):
        """
        function:
            Get all IPv4 addresses, and net interface names as a pair list.
        input:
            None
        output:
            A list of (interfaceName, address).
        """
        key = AF_INET
        rl = []
        for iface in interfaces():
            if key in ifaddresses(iface):
                ipAddress = ifaddresses(iface)[key][0]['addr']
                rl.append((iface, ipAddress))
        return rl

    def getIpAddressAndNICv6(self):
        """
        function:
            Get all IPv6 addresses, and net interface names as a pair list.
        input:
            None
        output:
            A list of (interfaceName, address).
        """
        key = AF_INET6
        rl = []

        for iface in interfaces():
            if key in ifaddresses(iface):
                iplist = ifaddresses(iface)[key]
                for ip in iplist:
                    ipAddress = ip['addr']
                    rl.append((iface, ipAddress))
        return rl

    def getIpAddressAndNICList(self, ipType="all"):
        """
        function:
            Get all IP addresses, and net interface names as a pair list.
        input:
            ipType: ipv4, ipv6, all
        output:
            A list of (interfaceName, address).
        """
        if ipType == "ipv4":
            return self.getIpAddressAndNICv4()
        elif ipType == "ipv6":
            return self.getIpAddressAndNICv6()
        else:
            lr = []
            lr.extend(self.getIpAddressAndNICv4())
            lr.extend(self.getIpAddressAndNICv6())
            return lr

    def getNetworkNumByIPAddr(self, ipAddress):
        """
        function:
            Get the network interface number by IP.
        input:
            ipAddress: The IP string.
        output:
            (NICnumber, address)
        """
        try:
            from gspylib.os.gsnetwork import g_network

            ipType = "ipv4"

            if 6 == g_network.getIPType(ipAddress):
                ipAddress = g_network.formatIP(ipAddress)
                ipType = "ipv6"

            mappingList = self.getIpAddressAndNICList(ipType)
            for mapInfo in mappingList:
                if g_network.isSameIP(mapInfo[1], ipAddress):
                    return mapInfo[0]
            raise Exception(ErrorCode.GAUSS_506["GAUSS-50612"] % ipAddress)
        except Exception as e:
            raise Exception(str(e))

    def getHostNameByIPAddr(self, ipAddress):
        """
        """
        try:
            return socket.gethostbyaddr(ipAddress)[0]
        except Exception as e:
            raise Exception(str(e))

    def getLinuxNetworkConfigFile(self, networkConfPath, networkCardNum):
        """
        """
        try:
            networkConfFile = "%sifcfg-%s" % (networkConfPath, networkCardNum)
            # Network configuration file does not exist
            if not os.path.exists(networkConfFile):
                cmd = "%s %s -iname 'ifcfg-*-%s' -print" % (self.getFindCmd(), networkConfPath, networkCardNum)
                (status, output) = subprocess.getstatusoutput(cmd)
                if status != 0 or output.strip() == "":
                    raise Exception(ErrorCode.GAUSS_502["GAUSS-50201"] % networkConfFile)
                if len(output.split('\n')) != 1:
                    raise Exception(ErrorCode.GAUSS_502["GAUSS-50201"] % networkConfFile)
                networkConfFile = output.strip()
            return networkConfFile
        except Exception as e:
            raise Exception(str(e))

    def getNetworkBondModeByBondConfigFile(self, bondingConfFile):
        """
        """
        try:
            # Check the bond mode
            cmd = "%s -w '\<Bonding Mode\>' %s | %s  -F ':' '{print $NF}'" % \
                  (self.getGrepCmd(), bondingConfFile, self.getAwkCmd())
            (status, output) = subprocess.getstatusoutput(cmd)
            if status != 0:
                raise Exception(ErrorCode.GAUSS_514["GAUSS_51400"] % cmd + " Error: \n%s " % output)
            return "BondMode %s" % output.strip()
        except Exception as e:
            raise Exception(str(e))

    def getNetworkBondModeInfo(self, networkConfFile, networkCardNum):
        """
        """
        # Get the bond profile
        if not os.path.isfile(networkConfFile):
            return "BondMode Null"

        bondingConfFile = "%s%s" % (self.getBondInfoPath(), networkCardNum)
        cmd = "%s -i 'BONDING_OPTS\|BONDING_MODULE_OPTS' %s" % (self.getGrepCmd(), networkConfFile)
        (_, output) = subprocess.getstatusoutput(cmd)
        # Analysis results
        if output.strip() != "":
            if (output.find("mode") > 0) and os.path.exists(bondingConfFile):
                bondInfo = self.getNetworkBondModeByBondConfigFile(bondingConfFile)
            else:
                raise Exception(ErrorCode.GAUSS_514["GAUSS_51400"] % cmd + " Error: \n%s " % output)
        elif os.path.exists(bondingConfFile):
            bondInfo = self.getNetworkBondModeByBondConfigFile(bondingConfFile)
            bondInfo += "\nNo 'BONDING_OPTS' or 'BONDING_MODULE_OPTS' in bond config file[%s]." % networkConfFile
        else:
            bondInfo = "BondMode Null"
        return bondInfo

    def getNetworkMaskByNICNum(self, networkCardNum, ipType="ipv4"):
        """
        """
        if ipType == "ipv4":
            return ifaddresses(networkCardNum)[AF_INET][0]["netmask"]
        else:
            return ifaddresses(networkCardNum)[AF_INET6][0]["netmask"]

    def getNetworkMTUValueByNICNum(self, networkCardNum):
        """
        """
        import psutil
        return psutil.net_if_stats()[networkCardNum].mtu

    def getNetworkRXTXValueByNICNum(self, networkCardNum, valueType):
        """
        """
        try:
            cmd = "%s -g %s | %s '%s:' | %s -n 1" % \
                  (self.getEthtoolCmd(), networkCardNum, self.getGrepCmd(),
                   valueType.upper(), self.getTailCmd())
            (status, output) = subprocess.getstatusoutput(cmd)
            if status != 0:
                raise Exception(ErrorCode.GAUSS_514["GAUSS_51400"] % cmd + " Error: \n%s " % output)
            value = output.split(':')[-1].split(' ')[0].strip()
            if not str(value).isdigit():
                raise Exception(ErrorCode.GAUSS_514["GAUSS_51400"] % cmd + " Error: \n%s " % output)
            return int(value)
        except Exception as e:
            raise Exception(str(e))

    def setNetworkRXTXValue(self, networkCardNum, rxValue=8192, txValue=8192):
        """
        """
        try:
            cmd = "%s -G %s rx %s tx %s" % (self.getEthtoolCmd(), networkCardNum, rxValue, txValue)
            (status, output) = subprocess.getstatusoutput(cmd)
            if status != 0:
                if output.find("no ring parameters changed, aborting") < 0:
                    raise Exception(ErrorCode.GAUSS_514["GAUSS_51400"] % cmd + " Error: \n%s " % output)
        except Exception as e:
            raise Exception(str(e))

    def getNetworkSpeedByNICNum(self, networkCardNum):
        """
        """
        keyWord = "Speed: "
        speedUnit = "Mb/s"
        try:
            cmd = "%s %s | grep '%s'" % (self.getEthtoolCmd(), networkCardNum, keyWord)
            (status, output) = subprocess.getstatusoutput(cmd)
            if status != 0 or output == "":
                raise Exception(ErrorCode.GAUSS_514["GAUSS_51400"] % cmd + " Error: \n%s " % output)
            if len(output.split('\n')) >= 1:
                for line in output.split('\n'):
                    if line.find(keyWord) >= 0 and line.find(speedUnit) >= 0:
                        return int(line.split(':')[-1].strip()[:-4])
            return 0
        except Exception as e:
            raise Exception(str(e))

    def checkNetworkInterruptByNIC(self, networkCardNum):
        """
        """
        try:
            interruptConfFile = self.getInterruptFile()
            numberedListCmd = "%s %s | %s '%s-' | %s -F ' ' '{print $1}' | %s -F ':' '{print $1}'" % \
                              (self.getCatCmd(), interruptConfFile,
                               self.getGrepCmd(), networkCardNum,
                               self.getAwkCmd(), self.getAwkCmd())
            irqCmd = "%s /proc/irq/$i/smp_affinity" % self.getCatCmd()
            cmd = "for i in `%s`; do %s ; done" % (numberedListCmd, irqCmd)
            (status, output) = subprocess.getstatusoutput(cmd)
            if status != 0:
                raise Exception(ErrorCode.GAUSS_514["GAUSS_51400"] % cmd + " Error: \n%s " % output)
        except Exception as e:
            raise Exception(str(e))

        # cpu core number followed by 1 2 4 8,every 4 left shift one
        Mapping = {0: "1", 1: "2", 2: "4", 3: "8"}
        flag = True
        for index, eachLine in enumerate(output.split()):
            # Remove the ','
            eachLine = eachLine.replace(",", "")
            # Replace 0000,00001000 to 1,Remove invalid content
            validValue = eachLine.replace("0", "")
            # Convert the row index to the expected value
            expandNum = Mapping[index % 4]
            # Convert line index to expected position
            expandBit = index // 4 * -1 - 1
            # value and position is correct
            if eachLine[expandBit] == expandNum and validValue == expandNum:
                continue
            else:
                print("Network card [%s] multi-queue support is not enabled.\n" % networkCardNum)
                flag = False
                break
        return flag

    def getInterruptCountNum(self, networkCardNum):
        """
        function : We can makesure that all dev names is startwith 'ethX-' and endwith '-X'
        input  : String
        output : Int
        """
        try:
            interruptConfFile = self.getInterruptFile()
            cmd = "%s %s | %s '%s-' | %s -l" % \
                  (self.getCatCmd(), interruptConfFile, self.getGrepCmd(),
                   networkCardNum, self.getWcCmd())
            (status, output) = subprocess.getstatusoutput(cmd)
            if status != 0:
                raise Exception("Failed to obtain network card interrupt count numbers. \
Commands for getting interrupt count numbers: %s." % cmd)
            if not str(output.strip()).isdigit():
                return 0
            return int(output.strip())
        except Exception as e:
            raise Exception(str(e))

    def setNetworkInterruptByNIC(self, networkCardNum):
        """
        """
        # check process irqbalance
        procName = "irqbalance"
        if self.checkProcExists(procName):
            print("        Warning: irqbalance is running and will likely override this script's affinitization. \
Please stop the irqbalance service and/or execute 'killall irqbalance'.")
            killCmd = "%s %s" % (self.getKillallCmd(), procName)
            # we don't care about the return value
            os.system(killCmd)

        count = int(self.getInterruptCountNum(networkCardNum))
        i = 0
        while i < count:
            # the dev name type like this: eth1-1, eth1-rx-1, eth1-tx-1, eth1-TxRx-1
            interruptConfFile = self.getInterruptFile()
            numberedListCmd = "%s %s | %s '%s.*-%s$' | %s -F ' ' '{print $1}' | %s -F ':' '{print $1}'" % \
                              (self.getCatCmd(), interruptConfFile, self.getGrepCmd(),
                               networkCardNum, str(i), self.getAwkCmd(),
                               self.getAwkCmd())
            (status, output) = subprocess.getstatusoutput(numberedListCmd)
            if status != 0 or output.strip() == "":
                print("Failed to obtain network card interrupt value. \
Commands for getting interrupt value: %s." % numberedListCmd)
            else:
                IRQ = output.strip()
                print("The network '%s' interrupt configuration path: /proc/irq/%s/smp_affinity." %
                      (networkCardNum, IRQ))

                cmdTrans = "MASK=$((1<<%s)); echo $MASK" % i
                (status, output) = subprocess.getstatusoutput(cmdTrans)
                if status != 0 or not str(output.strip()).isdigit():
                    print("Failed to change decit to binary. Commands for translating: %s." % cmdTrans)
                else:
                    # Under SuSE platform, when the length is greater than 8, the ',' must be used.
                    value = str(hex(int(output.strip())))[2:]
                    if len(value) <= 8:
                        resultValue = value
                    else:
                        resultValue = value[:-8] + ',' + value[len(value) - 8:]

                    cmdSet = "%s > /proc/irq/%s/smp_affinity" % (self.getEchoCmd(resultValue), IRQ)
                    (status, output) = subprocess.getstatusoutput(cmdSet)
                    if status != 0:
                        print("Failed to set network '%s' IRQ. Commands for setting: %s." % (networkCardNum, cmdSet))
                    else:
                        print("Set network card '%s' IRQ to \"%s\"." % (networkCardNum, resultValue))
            i += 1

    def get_package_post_fix_string(self, file_type):
        """
        """
        if file_type == "tarFile":
            post_fix_string = "tar.gz"
        elif file_type == "binFile":
            post_fix_string = "tar.gz"
        elif file_type == "sha256File":
            post_fix_string = "sha256"
        else:
            raise Exception("The value of fileType is incorrect.")
        return post_fix_string

    def getPackageFile(self, package_version, product_version, file_type="tarFile",
                       jre_package=False):
        """
        function : Get the path of binary file version.
        input : NA
        output : String
        """
        gs_platform = GetPlatform()
        dist_name, _, _ = gs_platform.dist()
        lower_dist_name = dist_name.lower()
        machine_type = platform.machine()
        dir_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "./../../../../")
        pre_fix_str = product_version
        post_fix_string = self.get_package_post_fix_string(file_type)

        if self.kernel_flag:
            if file_type == "tarFile":
                file_name_suffix = "%sbit-%s.%s" % \
                                   (machine_type, self.kernel_flag, post_fix_string)
            else:
                file_name_suffix = "%s-%s.%s" % \
                                   (self.kernel_flag, support_platform.BIT_VERSION, post_fix_string)
        else:
            if file_type == "tarFile":
                file_name_suffix = "%sbit.%s" % (machine_type, post_fix_string)
            else:
                file_name_suffix = "%s.%s" % (support_platform.BIT_VERSION, post_fix_string)
            if jre_package:
                file_name_suffix = "%s-%s" % (support_platform.JRE, file_name_suffix)

        """
        aarch64:
            Euler/ZhongBiao Kylin/YinHe Kylin/UOS               -> EULER
            Redhat/CentOS/Oracle/Other                          -> REDHAT
        x86_64:
            SuSE                                               -> SUSE11
            Euler/ZhongBiao Kylin/YinHe Kylin/UOS              -> REDHAT
            Redhat/CentOS/Oracle/Other                          -> REDHAT
        """
        if machine_type == "aarch64":
            if lower_dist_name in support_platform.RHEL_DERIVATION_PLATFORM_LIST + support_platform.OTHER_PLATFORM_LIST:
                file_name = os.path.join(dir_path, "%s-%s-%s-%s" %
                                         (pre_fix_str, package_version, "EULER", file_name_suffix))
            else:
                file_name = os.path.join(dir_path, "%s-%s-%s-%s" %
                                         (pre_fix_str, package_version, "REDHAT", file_name_suffix))
        elif lower_dist_name == support_platform.SUSE:
            file_name = os.path.join(dir_path, "%s-%s-%s-%s" %
                                     (pre_fix_str, package_version, "SUSE11", file_name_suffix))
        elif lower_dist_name in support_platform.SUPPORT_WHOLE_PLATFORM_LIST:
            file_name = os.path.join(dir_path, "%s-%s-%s-%s" %
                                     (pre_fix_str, package_version, "REDHAT", file_name_suffix))
        else:
            raise Exception(ErrorCode.GAUSS_519["GAUSS_51900"] +
                            "Supported platforms are: %s." % str(support_platform.SUPPORT_WHOLE_PLATFORM_LIST))

        file_name = os.path.normpath(file_name)
        return file_name

    def setKeyValueInSshd(self, key, value):
        """
        function: Set a (key, value) pair into /etc/ssh/sshd_config, before "Match" section.
                "Match" section in sshd_config should always places in the end.
                Attention: you need to remove the old (key, value) from sshd_config manually.
        input:
            key: the configuration name of sshd_config
            value: the configuration value(Only single line string permitted here).
        output:
            void
        """
        sshd_config = '/etc/ssh/sshd_config'
        cmd = "grep -En '^\<Match\>' %s" % sshd_config
        (status, output) = subprocess.getstatusoutput(cmd)

        if status == 0 and output is not None and output != "":
            if len(output.strip()) <= 1:
                cmd = "sed -i '/^\<Match\>.*/i %s %s' %s" % (key, value, sshd_config)
            else:
                # Find the first match, and then insert a new line before it.
                first_line = output.split('\n')[0]
                line_num = first_line.split(':')[0]
                if line_num.isdigit():
                    # Insert line at before specific line.
                    cmd = "sed -i '%si %s %s' %s" % (line_num, key, value, sshd_config)
                else:
                    # Failed to get the line number, something is wrong.
                    # Then we go back to the old method, just insert new line before "Match"
                    cmd = "sed -i '/^\<Match\>.*/i %s %s' %s" % (key, value, sshd_config)
        else:
            # there is no content about Match information
            if output is not None and len(output.strip()) != 0:
                raise Exception(ErrorCode.GAUSS_503["GAUSS_50321"] % "Match section" +
                                "Command: %s, Error: %s" % (cmd, output))
            cmd = "echo '' >> %s ; echo '%s %s' >> %s" % (sshd_config, key, value, sshd_config)

        (status, output) = subprocess.getstatusoutput(cmd)
        if status != 0:
            raise Exception((ErrorCode.GAUSS_503["GAUSS_50320"] % (key, value)) +
                            ("Command: %s, Error: %s" % (cmd, output)))

    def collect_platform_info(self):
        """
        function : Collector platforminfo
        input  : NA
        output : Instantion
        """
        data = platformInfo()
        gs_platform = GetPlatform()
        dist_name, version, id_num = gs_platform.dist()
        bits, link_age = platform.architecture()
        data.distname = dist_name.lower()
        data.version = version
        data.id = id_num
        data.bits = bits
        data.linkage = link_age
        data.patchlevel = ""
        return data

    def get_suse_patch_level(self):
        """
        """
        suse_release_file = "/etc/SuSE-release"
        os_release_file = "/etc/os-release"
        # os-release is added since SLE 12; SuSE-release will be removed in a future service pack or release
        if os.path.exists(suse_release_file):
            cmd = "%s -Eiw 'PATCHLEVEL' %s | %s -F '=' '{print $2}'" % \
                  (self.getGrepCmd(), suse_release_file, self.getAwkCmd())
        else:
            cmd = "%s -Eiw 'VERSION_ID' %s | %s -F '.' '{print $2}' | %s 's/\"//'" % \
                  (self.getGrepCmd(), os_release_file, self.getAwkCmd(), self.getSedCmd())
        status, output = subprocess.getstatusoutput(cmd)
        if status == 0 and output != "":
            patch_level = output.strip()
        else:
            raise Exception(ErrorCode.GAUSS_514["GAUSS_51400"] % cmd + " Error: \n%s " % output)
        return patch_level

    def is_suse11_sp1(self):
        """
        """
        gs_platform = GetPlatform()
        dist_name, version, _ = gs_platform.dist()
        lower_dist_name = dist_name.lower()
        patchlevel = ""
        if lower_dist_name == support_platform.SUSE and version == support_platform.SUSE11:
            patchlevel = self.get_suse_patch_level()
        if patchlevel == "1":
            return True
        else:
            return False

    def special_checking_for_sysctl_para(self, key):
        """
        """
        machine_type = platform.machine()
        # The SuSE 11 SP1 operating system does not have vm.extfrag_threshold parameter, skip check
        if self.is_suse11_sp1() and key == "vm.extfrag_threshold":
            return True
        # Skip check net.ipv4.tcp_tw_recycle with ARM
        if machine_type == "aarch64" and key == "net.ipv4.tcp_tw_recycle":
            return True
        return False

    def special_setting_for_sysctl_para(self, param_list, logger):
        """
        """
        # The SuSE 11 SP1 operating system does not have vm.extfrag_threshold parameter, skip set
        if self.is_suse11_sp1() and "vm.extfrag_threshold" in param_list:
            param_list.pop("vm.extfrag_threshold")

        # The parameter sctpchecksumerrors set method is independent
        if "sctpchecksumerrors" in param_list:
            cmd = "echo 1 > /sys/module/sctp/parameters/no_checksums"
            status, output = subprocess.getstatusoutput(cmd)
            if status != 0:
                logger.log("        Failed to enforce sysctl kernel variable 'sctpchecksumerrors'. Error: %s" % output)
            param_list.pop("sctpchecksumerrors")

        # Union Tech should delete the context on /etc/profile.d/performance.sh
        gs_platform = GetPlatform()
        dist_name, _, _ = gs_platform.dist()
        if dist_name.lower() in [support_platform.UNIONTECH, support_platform.OPENEULER, support_platform.FUSIONOS]:
            performance_file = "/etc/profile.d/performance.sh"
            if os.path.isfile(performance_file):
                cmd = """sed -i '/^sysctl.*-w.*vm.min_free_kbytes.*=.*$/d' %s """ % performance_file
                status, output = subprocess.getstatusoutput(cmd)
                if status != 0:
                    raise Exception(ErrorCode.GAUSS_502["GAUSS_50205"] % performance_file + " Error:\n%s" % output)
        return param_list

    def fix_jemalloc_so_file(self, jemalloc4, jemalloc64, is_64KB=True):
        """
        """
        if os.path.isfile(jemalloc64):
            if is_64KB:
                cmd = "rm -f '%s' && mv '%s' '%s' " % (jemalloc4, jemalloc64, jemalloc4)
            else:
                cmd = "rm -f '%s' " % jemalloc64
            status, output = subprocess.getstatusoutput(cmd)
            if status != 0:
                raise Exception(ErrorCode.GAUSS_514["GAUSS_51400"] % cmd + " Error: \n%s" % str(output))

    def special_processing_for_install(self, install_path):
        """
        For Kylin V10/UnionTech V20-1050e
        1. Due to the mapping relationship with FIM,
           Kylin V10/UnionTech V20-1050e must use GaussDB compiled by ARM EulerOS.
        2.The default page size of ARM EulerOS is 4 KB, and that of Kylin V10/UnionTech V20-1050e is 64 KB.
           "Unsupported system page size" is reported by jemalloc.
        Therefore, when compiling jemalloc on ARM EulerOS, you need to compile the version whose page size is 64 KB
        for Kylin V10/UnionTech V20-1050e.

        The current installation package contains two versions of the .so file.
        The correct .so file has been selected for installation.
        """
        # get pagesize
        cmd = self.getGetConfValueCmd()
        status, output = subprocess.getstatusoutput(cmd)
        if status != 0:
            raise Exception(ErrorCode.GAUSS_502["GAUSS_50219"] % "system config pagesize")

        # 4096 is libjemalloc.so.2
        # 65536 is libjemalloc.so.2_64
        lib_path = os.path.join(install_path, "lib")
        jeprof_path = os.path.join(lib_path, "jeprof")
        so_name4 = "libjemalloc.so.2"
        so_name64 = "libjemalloc.so.2_64"
        lib_jemalloc64 = os.path.join(lib_path, so_name64)
        lib_jemalloc4 = os.path.join(lib_path, so_name4)
        jeprof_jemalloc64 = os.path.join(jeprof_path, so_name64)
        jeprof_jemalloc4 = os.path.join(jeprof_path, so_name4)
        is_64KB = True if output == "65536" else False
        self.fix_jemalloc_so_file(lib_jemalloc4, lib_jemalloc64, is_64KB)
        self.fix_jemalloc_so_file(jeprof_jemalloc4, jeprof_jemalloc64, is_64KB)

    def special_processing_for_cgroup(self):
        # The cgroup subsystem cpuset is reset when the orphan process using the cgroup is killed.
        # The solution provided by EulerOS engineers is
        # to set systemd<92>s CPUAccounting, MemoryAccounting, CPUSetAccounting to "yes" if the OS exists.
        # Special for EulerOS 2.0SP2, 2.0SP8, the CPUSetAccounting parameter does not exist.
        parameter_list = ['CPUAccounting', 'CPUSetAccounting', 'MemoryAccounting']
        special_versions = ['SP2', 'SP8']
        gs_platform = GetPlatform()
        dist_name, _, id_num = gs_platform.dist()
        lower_dist_name = dist_name.lower()
        if lower_dist_name == support_platform.EULEROS:
            if id_num in special_versions:
                parameter_list.remove('CPUSetAccounting')
            for parameter in parameter_list:
                cmd = "systemctl show -- -.slice | grep -wi '%s=yes' > /dev/null; " \
                      "if [ $? != 0 ]; then systemctl set-property -- -.slice %s=yes; fi" % (parameter, parameter)
                status, output = subprocess.getstatusoutput(cmd)
                if status != 0:
                    raise Exception(ErrorCode.GAUSS_514["GAUSS_51400"] % cmd + " Error: \n%s" % str(output))

    def get_lvs_module_file_type(self):
        """
        """
        gs_platform = GetPlatform()
        dist_name, version, _ = gs_platform.dist()
        lower_dist_name = dist_name.lower()
        if lower_dist_name == support_platform.SUSE:
            return "SUSE"
        elif lower_dist_name in support_platform.RHEL_SERIES_VERSION_LIST:
            return "REDHAT"
        elif lower_dist_name in support_platform.OTHER_PLATFORM_LIST:
            return "REDHAT"
        else:
            raise Exception(ErrorCode.GAUSS_519["GAUSS_51900"] +
                            "The OS version %s%s of host %s is not supported." %
                            (lower_dist_name, version, socket.gethostname()))
