import json
import re
import subprocess
from baseCheck import baseCheck

MINDISKSPACEREMAINING = 3.0

class diskLimits(baseCheck):

    virtualsizeregexp = re.compile("virtual size:\s+([\d\.]+[MGT]?)\s+")
    disksizeregexp = re.compile("disk size:\s+([\d\.]+[MGT]?)")
    virshregexp = re.compile("^\s*\d+\s+(\S+)\s+")

    def getHumanName(self):
        return "Confirm disk space on KVM hosts"

    def parseSize(self, s):
        if s[-1:] == 'G':
            return float(s[0:len(s)-1])
        if s[-1:] == 'T':
            return float(s[0:len(s)-1]) * 1024.0
        if s[-1:] == 'M':
            return float(s[0:len(s)-1]) / 1024.0
        if s[-1:] == 'K':
            return float(s[0:len(s)-1]) / 1048576.0
        return float(s)

    def getSizeDiff(self, server, image):
        try:
            returnstr = subprocess.Popen(['ssh',server,'-o','StrictHostKeyChecking=no','qemu-img','info',image], stdout=subprocess.PIPE).communicate()[0]
            virtualsize = 0.0
            disksize = 0.0
            for line in returnstr.split("\n"):
                m = self.virtualsizeregexp.match(line)
                if m != None:
                    virtualsize = self.parseSize(m.group(1))
                m = self.disksizeregexp.match(line)
                if m != None:
                    disksize = self.parseSize(m.group(1))
            return virtualsize - disksize
        except Exception as e:
            self.logMsg("Unable to determine size of image %s on server %s." % (image,server))
            self.logMsg("Exception: %s" % str(e))
            return 0.0

    def runCheck(self, config, debug):
        # do whatever is desired to check here.
        # return non-zero if an internal error is detected (equivilent to raising an exception)
        # return 0 for both check pass and check fail.
        #    call self.addWarning(text) or self.addWarning(rawText,humanText) for every warning to be logged
        #    call self.addFailure(text) or self.addFailure(rawText,humanText) for every failure to be logged

        # make sure the
        returnCode=0

        for server in config["environment"]["servers"]:

            # Get the image directory
            imagedir = server["imageStore1"] + '/'

            # First get a list of the VMs already running that may grow
            runningvms = {}
            try:
                returnstr = subprocess.Popen(['ssh',server["accessIP"],'-o','StrictHostKeyChecking=no','virsh','list'], stdout=subprocess.PIPE).communicate()[0]
                for line in returnstr.split("\n"):
                    m = self.virshregexp.match(line)
                    if m != None:
                        name = m.group(1)
                        if name.startswith('CMS_Installer_'):
                            name = 'cs1'
                        else:
                            index = name.rfind('_')
                            if index != -1:
                                name = name[index+1:]
                        runningvms[name] = self.getSizeDiff(server["accessIP"], imagedir + m.group(1) + '*')
            except Exception as e:
                self.addFailure("Unable to retrieve a list of VMs on server %s." % server["accessIP"])
                self.logMsg("Exception: %s" % str(e))
                return 0

            # Get the total size of the VMs to be deployed on the host server
            newVMSize = 0.0
            for vm in server["vms"]:
                if vm['name'] not in runningvms:
                    newVMSize += float(vm["addDisk"])
                    # TODO: should be using qemu-img to get the virtual size of the image, but it is not available on the CS node, so have to hard-code 50GB for each image
                    newVMSize += 50.0

            # Get the amount of disk free space
            diskFree = 0.0
            try:
                returnstr = subprocess.Popen(['ssh',server["accessIP"],'-o','StrictHostKeyChecking=no','df','-P','-h','/'], stdout=subprocess.PIPE).communicate()[0]
                for line in returnstr.split("\n"):
                    if line.startswith('/'):
                        values = line.split()
                        diskFree = self.parseSize(values[3])
            except Exception as e:
                self.addFailure("Unable to determine disk free space on server %s" % server["accessIP"])
                self.logMsg("Exception: %s" % str(e))
                return 0
  
            # Now take off the amount of disk that may be used by the VMs
            existingVMSize = 0.0
            for vm in runningvms:
                existingVMSize += runningvms[vm]

            # Calculate the amount of disk free space and make sure the host has at least 3GB free
            newDiskFree = diskFree - newVMSize - existingVMSize
            self.logMsg("Estimated remaining disk space on server %s is %.1fGB = %.1fGB - %.1fGB (new VMs) - %.1fGB (existing VMs)" % (server["accessIP"],newDiskFree,diskFree,newVMSize,existingVMSize))
            if newDiskFree < MINDISKSPACEREMAINING:
                required = (newDiskFree - MINDISKSPACEREMAINING) * -1
                self.addFailure("Insufficient disk space on KVM host server %s.  Please verify Additional Disk Space configuration for each VM and free up %.1f GB on this host." % (server["accessIP"],required))

        return 0
