# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
from .cloudException import CloudRuntimeException, formatExceptionInfo
import logging
from subprocess import PIPE, Popen
from signal import alarm, signal, SIGALRM, SIGKILL
import sys
import os
class bash:
    def __init__(self, args, timeout=600):
        self.args = args
        logging.debug("execute:%s"%args)
        self.timeout = timeout
        self.process = None
        self.success = False
        self.run()

    def run(self):
        class Alarm(Exception):
            pass
        def alarm_handler(signum, frame):
            raise Alarm

        try:
            self.process = Popen(self.args, shell=True, stdout=PIPE, stderr=PIPE)
            if self.timeout != -1:
                signal(SIGALRM, alarm_handler)
                alarm(self.timeout)

            try:
                self.stdout, self.stderr = self.process.communicate()
                if self.timeout != -1:
                    alarm(0)
            except Alarm:
                os.kill(self.process.pid, SIGKILL)
                raise CloudRuntimeException("Timeout during command execution")

            self.success = self.process.returncode == 0
        except:
            raise CloudRuntimeException(formatExceptionInfo())

        if not self.success:
            logging.debug("Failed to execute:" + self.getErrMsg())

    def isSuccess(self):
        return self.success

    def getStdout(self):
        return self.stdout.decode('utf-8').strip('\n')

    def getLines(self):
        return self.stdout.decode('utf-8').strip('\n').split('\n')

    def getStderr(self):
        return self.stderr.decode('utf-8').strip('\n')

    def getErrMsg(self):
        if self.isSuccess():
            return ""

        if self.getStderr() is None or self.getStderr() == "":
            return self.getStdout()
        else:
            return self.getStderr()

def initLoging(logFile=None):
    try:
        if logFile is None:
            logging.basicConfig(level=logging.DEBUG)
        else:
            logging.basicConfig(filename=logFile, level=logging.DEBUG)
    except:
        logging.basicConfig(level=logging.DEBUG)

def writeProgressBar(msg, result):
    output = "[%-6s]\n"%"Failed"
    if msg is not None:
        output = "%-30s"%msg
    elif result is True:
        output = "[%-2s]\n"%"OK"
    elif result is False:
        output = "[%-6s]\n"%"Failed"
    sys.stdout.write(output)
    sys.stdout.flush()

class UnknownSystemException(Exception):
    "This Exception is raised if the current operating environment is unknown"
    pass

class Distribution:
    def __init__(self):
        self.distro = "Unknown"
        self.release = "Unknown"

        if os.path.exists("/etc/fedora-release"):
            self.distro = "Fedora"
        elif os.path.exists("/etc/redhat-release"):
            version = open("/etc/redhat-release").readline()
            if (version.find("Red Hat Enterprise Linux Server release 6") != -1 or version.find("Scientific Linux release 6") != -1 or
                version.find("CentOS Linux release 6") != -1 or version.find("CentOS release 6.") != -1):
                self.distro = "RHEL6"
            elif (version.find("Red Hat Enterprise Linux Server release 7") != -1 or version.find("Scientific Linux release 7") != -1 or
                version.find("CentOS Linux release 7") != -1 or version.find("CentOS release 7.") != -1):
                self.distro = "RHEL7"
            elif (version.find("Red Hat Enterprise Linux Server release 8") != -1 or version.find("Scientific Linux release 8") != -1 or
                version.find("CentOS Linux release 8") != -1 or version.find("CentOS release 8.") != -1 or
                version.find("Linux release 8") != -1):
                self.distro = "RHEL8"
            elif (version.find("Red Hat Enterprise Linux Server release 9") != -1 or version.find("Scientific Linux release 9") != -1 or
                version.find("Red Hat Enterprise Linux release 9") != -1 or version.find("Linux release 9.") != -1 or
                version.find("Linux release 9") != -1):
                self.distro = "RHEL9"
            elif (version.find("Red Hat Enterprise Linux Server release 10") != -1 or version.find("Scientific Linux release 10") != -1 or
                version.find("Red Hat Enterprise Linux release 10") != -1 or version.find("Linux release 10.") != -1 or
                version.find("Linux release 10") != -1):
                self.distro = "RHEL10"
            elif version.find("CentOS") != -1:
                self.distro = "CentOS"
            else:
                self.distro = "RHEL5"
        elif os.path.exists("/etc/legal") and "Ubuntu" in open("/etc/legal").read(-1):
            self.distro = "Ubuntu"
            kernel = bash("uname -r").getStdout()
            if kernel.find("2.6.32") != -1:
                self.release = "10.04"
            self.arch = bash("uname -m").getStdout()
        elif os.path.exists("/usr/bin/lsb_release"):
            o = bash("/usr/bin/lsb_release -i")
            distributor = o.getStdout().split(":\t")[1]
            if "Debian" in distributor:
                # This obviously needs a rewrite at some point
                self.distro = "Ubuntu"
            else:
                raise UnknownSystemException(distributor)
        elif os.path.exists("/etc/os-release"):
            version = open("/etc/os-release").readline()
            distributor = version.split("=")[1].replace('"', '').strip()
            if "SUSE" in distributor or "SLES" in distributor:
                self.distro = "SUSE"
            else:
                raise UnknownSystemException(distributor)
        else:
            raise UnknownSystemException

    def getVersion(self):
        return self.distro
    def getRelease(self):
        return self.release
    def getArch(self):
        return self.arch


class serviceOps:
    pass
class serviceOpsRedhat(serviceOps):
    def isServiceRunning(self, servicename):
        try:
            o = bash("service " + servicename + " status")
            if "running" in o.getStdout() or "start" in o.getStdout() or "Running" in o.getStdout():
                return True
            else:
                return False
        except:
            return False

    def stopService(self, servicename,force=False):
        if self.isServiceRunning(servicename) or force:
            return bash("service " + servicename +" stop").isSuccess()

        return True
    def disableService(self, servicename):
        result = self.stopService(servicename)
        bash("chkconfig --del " + servicename)
        return result

    def startService(self, servicename,force=False):
        if not self.isServiceRunning(servicename) or force:
            return bash("service " + servicename + " start").isSuccess()
        return True

    def enableService(self, servicename,forcestart=False):
        bash("chkconfig --level 2345 " + servicename + " on")
        return self.startService(servicename,force=forcestart)

    def isKVMEnabled(self):
        if os.path.exists("/dev/kvm"):
            return True
        else:
            return False

class serviceOpsUbuntu(serviceOps):
    def isServiceRunning(self, servicename):
        try:
            o = bash("sudo /usr/sbin/service " + servicename + " status")
            if "not running" in o.getStdout():
                return False
            else:
                return True
        except:
            return False

    def stopService(self, servicename,force=True):
        if self.isServiceRunning(servicename) or force:
            return bash("sudo /usr/sbin/service " + servicename +" stop").isSuccess()

    def disableService(self, servicename):
        result = self.stopService(servicename)
        bash("sudo update-rc.d -f " + servicename + " remove")
        return result

    def startService(self, servicename,force=True):
        if not self.isServiceRunning(servicename) or force:
            return bash("sudo /usr/sbin/service " + servicename + " start").isSuccess()

    def enableService(self, servicename,forcestart=True):
        bash("sudo update-rc.d -f " + servicename + " remove")
        bash("sudo update-rc.d -f " + servicename + " defaults")
        return self.startService(servicename,force=forcestart)

    def isKVMEnabled(self):
        return bash("kvm-ok").isSuccess()

class serviceOpsRedhat7Later(serviceOps):
    def isServiceRunning(self, servicename):
        try:
            o = bash("systemctl is-active " + servicename)
            textout = o.getStdout()
            return "inactive" not in textout and "failed" not in textout
        except:
            return False

    def stopService(self, servicename,force=False):
        if self.isServiceRunning(servicename) or force:
            return bash("systemctl stop " + servicename).isSuccess()

        return True
    def disableService(self, servicename):
        result = self.stopService(servicename)
        bash("systemctl disable " + servicename)
        return result

    def startService(self, servicename,force=False):
        if not self.isServiceRunning(servicename) or force:
            return bash("systemctl start " + servicename).isSuccess()
        return True

    def enableService(self, servicename,forcestart=False):
        bash("systemctl enable " + servicename)
        return self.startService(servicename,force=forcestart)

    def isKVMEnabled(self):
        if os.path.exists("/dev/kvm"):
            return True
        else:
            return False

class serviceOpsSUSE(serviceOpsRedhat7Later):
    pass
