Esempio n. 1
0
 def __startListenning(self):
     """
     Creates the control connection
     Args:
         None
     Returns:
         Nothing
     """    
     networkInterface = self.__parser.getConfigurationParameter("vncNetworkInterface")
     listenningPort = self.__parser.getConfigurationParameter("listenningPort")
     try :
         self.__vncServerIP = get_ip_address(networkInterface)
     except Exception :
         raise Exception("Error: the network interface '{0}' is not ready. Exiting now".format(networkInterface))    
     self.__ftpTimeout = self.__parser.getConfigurationParameter("FTPTimeout")
     self.__listenningPort = listenningPort
     self.__networkManager = NetworkManager(self.__parser.getConfigurationParameter("certificatePath"))
     self.__networkManager.startNetworkService()
     self.__useSSL = self.__parser.getConfigurationParameter("useSSL")
     self.__packetManager = VMServerPacketHandler(self.__networkManager)            
     self.__connectToDatabases("VMServerDB", self.__parser.getConfigurationParameter("databaseUserName"), self.__parser.getConfigurationParameter("databasePassword"))
         
     self.__domainHandler = DomainHandler(self.__commandsDBConnector, self.__vncServerIP, self.__networkManager, self.__packetManager, self.__listenningPort, 
                                              self.__parser.getConfigurationParameter("useQEMUWebsockets"),
                                              self.__parser.getConfigurationParameter("websockifyPath"),
                                              self.__parser.getConfigurationParameter("configFilePath"),
                                              self.__parser.getConfigurationParameter("sourceImagePath"), self.__parser.getConfigurationParameter("executionImagePath"),
                                              self.__parser.getConfigurationParameter("passwordLength"))
     self.__domainHandler.connectToLibvirt(self.__parser.getConfigurationParameter("vncNetworkInterface"), 
                                               self.__parser.getConfigurationParameter("vnName"), self.__parser.getConfigurationParameter("gatewayIP"), 
                                               self.__parser.getConfigurationParameter("netMask"), self.__parser.getConfigurationParameter("dhcpStartIP"), 
                                               self.__parser.getConfigurationParameter("dhcpEndIP"), self.__parser.getConfigurationParameter("createVirtualNetworkAsRoot"))
         
     self.__domainHandler.doInitialCleanup()
     self.__deleteTemporaryZipFiles()
     self.__fileTransferThread = FileTransferThread(self.__networkManager, self.__listenningPort, self.__packetManager,
                                                    self.__parser.getConfigurationParameter("TransferDirectory"),
                                                    self.__parser.getConfigurationParameter("FTPTimeout"), 
                                                    self.__parser.getConfigurationParameter("MaxTransferAttempts"), self.__commandsDBConnector, self.__useSSL)
     self.__compressionThread = CompressionThread(self.__parser.getConfigurationParameter("TransferDirectory"), self.__parser.getConfigurationParameter("sourceImagePath"),
                                                  self.__parser.getConfigurationParameter("configFilePath"),
                                                  self.__commandsDBConnector, self.__domainHandler, self.__networkManager, self.__listenningPort, self.__packetManager)
     self.__fileTransferThread.start()
     self.__compressionThread.start()
     self.__networkManager.listenIn(self.__listenningPort, self, self.__useSSL)
Esempio n. 2
0
class VMServerReactor(NetworkCallback):
    """
    Virtual machine server packet reactor
    """
    def __init__(self, configurationFileParser):
        """
        Initializes the reactor's state, establishes the connection with the database
        and creates the control connection
        Args:
            configurationFileParser: the virtual machine server's configuration file parser
        """        
        self.__finished = False
        self.__emergencyStop = False
        self.__fileTransferThread = None
        self.__compressionThread = None
        self.__networkManager = None
        self.__parser = configurationFileParser
        self.__domainHandler = None
        self.__domainTimeout = 0
        try :
            self.__connectToDatabases("VMServerDB", self.__parser.getConfigurationParameter("databaseUserName"), 
                                      self.__parser.getConfigurationParameter("databasePassword"))
            self.__startListenning()
        except Exception as e:
            print e.message
            self.__emergencyStop = True
            self.__finished = True
        
    def __connectToDatabases(self, databaseName, user, password) :
        """
        Establishes the connection with the database
        Args:
            databaseName: a database name
            user: a SQL username
            password: the user's password
        Returns:
            Nothing
        """
        self.__commandsDBConnector = VMServerDBConnector(user, password, databaseName)  
            
    def __startListenning(self):
        """
        Creates the control connection
        Args:
            None
        Returns:
            Nothing
        """    
        networkInterface = self.__parser.getConfigurationParameter("vncNetworkInterface")
        listenningPort = self.__parser.getConfigurationParameter("listenningPort")
        try :
            self.__vncServerIP = get_ip_address(networkInterface)
        except Exception :
            raise Exception("Error: the network interface '{0}' is not ready. Exiting now".format(networkInterface))    
        self.__ftpTimeout = self.__parser.getConfigurationParameter("FTPTimeout")
        self.__listenningPort = listenningPort
        self.__networkManager = NetworkManager(self.__parser.getConfigurationParameter("certificatePath"))
        self.__networkManager.startNetworkService()
        self.__useSSL = self.__parser.getConfigurationParameter("useSSL")
        self.__packetManager = VMServerPacketHandler(self.__networkManager)            
        self.__connectToDatabases("VMServerDB", self.__parser.getConfigurationParameter("databaseUserName"), self.__parser.getConfigurationParameter("databasePassword"))
            
        self.__domainHandler = DomainHandler(self.__commandsDBConnector, self.__vncServerIP, self.__networkManager, self.__packetManager, self.__listenningPort, 
                                                 self.__parser.getConfigurationParameter("useQEMUWebsockets"),
                                                 self.__parser.getConfigurationParameter("websockifyPath"),
                                                 self.__parser.getConfigurationParameter("configFilePath"),
                                                 self.__parser.getConfigurationParameter("sourceImagePath"), self.__parser.getConfigurationParameter("executionImagePath"),
                                                 self.__parser.getConfigurationParameter("passwordLength"))
        self.__domainHandler.connectToLibvirt(self.__parser.getConfigurationParameter("vncNetworkInterface"), 
                                                  self.__parser.getConfigurationParameter("vnName"), self.__parser.getConfigurationParameter("gatewayIP"), 
                                                  self.__parser.getConfigurationParameter("netMask"), self.__parser.getConfigurationParameter("dhcpStartIP"), 
                                                  self.__parser.getConfigurationParameter("dhcpEndIP"), self.__parser.getConfigurationParameter("createVirtualNetworkAsRoot"))
            
        self.__domainHandler.doInitialCleanup()
        self.__deleteTemporaryZipFiles()
        self.__fileTransferThread = FileTransferThread(self.__networkManager, self.__listenningPort, self.__packetManager,
                                                       self.__parser.getConfigurationParameter("TransferDirectory"),
                                                       self.__parser.getConfigurationParameter("FTPTimeout"), 
                                                       self.__parser.getConfigurationParameter("MaxTransferAttempts"), self.__commandsDBConnector, self.__useSSL)
        self.__compressionThread = CompressionThread(self.__parser.getConfigurationParameter("TransferDirectory"), self.__parser.getConfigurationParameter("sourceImagePath"),
                                                     self.__parser.getConfigurationParameter("configFilePath"),
                                                     self.__commandsDBConnector, self.__domainHandler, self.__networkManager, self.__listenningPort, self.__packetManager)
        self.__fileTransferThread.start()
        self.__compressionThread.start()
        self.__networkManager.listenIn(self.__listenningPort, self, self.__useSSL)
        
    def __deleteTemporaryZipFiles(self):
        """
        Deletes the temporary zip files
        Args:
            None
        Returns:
            Nothing
        """
        transfer_dir_path = self.__parser.getConfigurationParameter("TransferDirectory")
        for filePath in os.listdir(transfer_dir_path) :
            fileName = os.path.splitext(filePath)[0]
            if re.match("[^0-9]", fileName):
                # The non-temporary zip files only have numbers on their names
                os.remove(os.path.join(transfer_dir_path, filePath))
    
    def shutdown(self):
        """
        Shuts down the virtual machine server
        Args:
            None
        Returns:
            Nothing
        """                
            
        if (self.__emergencyStop) :            
            self.__domainTimeout = 0
            
        self.__domainHandler.shutdown(self.__domainTimeout)                   
        
        if (self.__fileTransferThread != None) :
            self.__fileTransferThread.stop()
            self.__fileTransferThread.join()
        if (self.__compressionThread != None) :
            self.__compressionThread.stop()
            self.__compressionThread.join()
            
        if (self.__networkManager != None) :
            self.__networkManager.stopNetworkService() # Dejar de atender peticiones inmediatamente
        sys.exit()              
        
    def processPacket(self, packet):
        """
        Processes a packet sent from the cluster server
        Args:
            packet: the packet to process
        Returns:
            Nothing
        """
        data = self.__packetManager.readPacket(packet)
        if (data["packet_type"] == VM_SERVER_PACKET_T.CREATE_DOMAIN) :
            self.__domainHandler.createDomain(data["MachineID"], data["UserID"], data["CommandID"])
        elif (data["packet_type"] == VM_SERVER_PACKET_T.SERVER_STATUS_REQUEST) :
            self.__sendStatusData()
        elif (data["packet_type"] == VM_SERVER_PACKET_T.USER_FRIENDLY_SHUTDOWN) :
            self.__domainTimeout = data["Timeout"]
            self.__finished = True
        elif (data["packet_type"] == VM_SERVER_PACKET_T.HALT) :
            self.__domainTimeout = 0
            self.__finished = True
        elif (data['packet_type'] == VM_SERVER_PACKET_T.QUERY_ACTIVE_VM_DATA) :
            self.__sendDomainsVNCConnectionData()
        elif (data['packet_type'] == VM_SERVER_PACKET_T.DESTROY_DOMAIN) :
            self.__domainHandler.destroyDomain(data["DomainID"])
        elif (data['packet_type'] == VM_SERVER_PACKET_T.REBOOT_DOMAIN) :
            self.__domainHandler.rebootDomain(data["DomainID"])
        elif (data['packet_type'] == VM_SERVER_PACKET_T.QUERY_ACTIVE_DOMAIN_UIDS) :
            self.__sendActiveDomainUIDs()
        elif (data['packet_type'] == VM_SERVER_PACKET_T.IMAGE_EDITION) :
            self.__processImageEditionPacket(data)
        elif (data['packet_type'] == VM_SERVER_PACKET_T.DEPLOY_IMAGE) :
            self.__processDeployImagePacket(data)
        elif (data['packet_type'] == VM_SERVER_PACKET_T.DELETE_IMAGE) :
            self.__processDeleteImagePacket(data)
            
    def __processDeployImagePacket(self, data):
        """
        Processes an image deployment packet
        Args:
            data: a dictionary containing the packet to process' data
        Returns:
            Nothing
        """
        data.pop("packet_type")
        data["Transfer_Type"] = TRANSFER_T.DEPLOY_IMAGE
        self.__commandsDBConnector.addToTransferQueue(data)
        
    def __processDeleteImagePacket(self, data):
        
        isBootable = self.__commandsDBConnector.getBootableFlag(data["ImageID"])
        
        if(isBootable):            
            osImagePath = os.path.join(self.__parser.getConfigurationParameter("sourceImagePath") 
                                   ,self.__commandsDBConnector.getOSImagePath(data["ImageID"]))
            definitionFilePath = os.path.join(self.__parser.getConfigurationParameter("configFilePath") 
                                   ,self.__commandsDBConnector.getDefinitionFilePath(data["ImageID"]))
            
            try :
                
                self.__commandsDBConnector.deleteImage(data["ImageID"])                
                ChildProcessManager.runCommandInForeground("rm -rf " + os.path.dirname(osImagePath), VMServerException)                
                ChildProcessManager.runCommandInForeground("rm -rf " + os.path.dirname(definitionFilePath), VMServerException)
                p = self.__packetManager.createConfirmationPacket(VM_SERVER_PACKET_T.IMAGE_DELETED, data["ImageID"], data["CommandID"])
            except Exception:
                p = self.__packetManager.createErrorPacket(VM_SERVER_PACKET_T.IMAGE_DELETION_ERROR, ERROR_DESC_T.VM_SRVR_INTERNAL_ERROR, 
                                                            data["CommandID"])                
            
        else:
            if (isBootable != None) :
                errorCode = ERROR_DESC_T.VMSRVR_LOCKED_IMAGE
            else :
                errorCode = ERROR_DESC_T.VMSRVR_UNKNOWN_IMAGE
            
            p = self.__packetManager.createErrorPacket(VM_SERVER_PACKET_T.IMAGE_DELETION_ERROR, errorCode, 
                                                       data["CommandID"])
        
        self.__networkManager.sendPacket('', self.__listenningPort, p)
            
        
        
    def __processImageEditionPacket(self, data):
        """
        Processes an image edition packet
        Args:
            data: a dictionary containing the packet to process' data
        Returns:
            Nothing
        """
        data.pop("packet_type")
        if (data["Modify"]) :
            data["Transfer_Type"] = TRANSFER_T.EDIT_IMAGE
        else :
            data["Transfer_Type"] = TRANSFER_T.CREATE_IMAGE
        data.pop("Modify")
        self.__commandsDBConnector.addToTransferQueue(data)

    def __sendDomainsVNCConnectionData(self):
        '''
        Sends the domains' VNC connection data to the cluster server
        Args:
            None
        Returns:
            Nothing
        '''
        vncConnectionData = self.__commandsDBConnector.getDomainsConnectionData()
        segmentSize = 150
        segmentCounter = 1
        outgoingData = []
        if (len(vncConnectionData) == 0):
            segmentCounter = 0
        segmentNumber = (len(vncConnectionData) / segmentSize)
        if (len(vncConnectionData) % segmentSize != 0) :
            segmentNumber += 1
            sendLastSegment = True
        else :
            sendLastSegment = segmentNumber == 0 
        for connectionParameters in vncConnectionData :
            outgoingData.append(connectionParameters)
            if (len(outgoingData) >= segmentSize) :
                # Flush
                packet = self.__packetManager.createActiveVMsVNCDataPacket(self.__vncServerIP, segmentCounter, segmentNumber, outgoingData)
                self.__networkManager.sendPacket('', self.__listenningPort, packet)
                outgoingData = []
                segmentCounter += 1
        # Send the last segment
        if (sendLastSegment) :
            packet = self.__packetManager.createActiveVMsVNCDataPacket(self.__vncServerIP, segmentCounter, segmentNumber, outgoingData)
            self.__networkManager.sendPacket('', self.__listenningPort, packet)         
    
    def __sendStatusData(self):
        """
        Sends the server's status data to the cluster server
        Args:
            None
        Returns:
            Nothing
        """
        info = self.__domainHandler.getLibvirtStatusData()
        realCPUNumber = multiprocessing.cpu_count()
        freeOutput = ChildProcessManager.runCommandInForeground("free -k", VMServerException)
        
        # free's output contains the following lines and collumns:
        #              total       used       free     shared    buffers     cached
        # Mem:    4146708480 3939934208  206774272          0  224706560 1117671424
        # -/+ buffers/cache: 2597556224 1549152256
        # Swap:   2046816256   42455040 2004361216
        #
        # We must get the third line
           
        availableMemory = int(freeOutput.split("\n")[1].split()[1])
        freeMemory = int(freeOutput.split("\n")[2].split()[2])
        
        freeStorageSpace, availableStorageSpace, freeTemporaryStorage, availableTemporaryStorage = self.__generateDiskStats()
        packet = self.__packetManager.createVMServerStatusPacket(self.__vncServerIP, info["#domains"], freeMemory, availableMemory, 
                                                                 freeStorageSpace, availableStorageSpace, 
                                                                 freeTemporaryStorage, availableTemporaryStorage,
                                                                 info["#vcpus"], realCPUNumber)
        self.__networkManager.sendPacket('', self.__listenningPort, packet) 
        
    def __generateDiskStats(self):
        """
        Generates the hard disk usage statistics
        Args:
            None
        Returns:
            the free and available storage and temporary space
        """
        diskStats_storage = os.statvfs(self.__parser.getConfigurationParameter("sourceImagePath"))
        diskStats_temporaryData = os.statvfs(self.__parser.getConfigurationParameter("executionImagePath"))
        allocatedStorageSpace, allocatedTemporaryStorageSpace = self.__checkDiskImagesSpace()
        freeStorageSpace = diskStats_storage.f_bfree * diskStats_storage.f_frsize / 1024 - allocatedStorageSpace
        availableStorageSpace = diskStats_storage.f_blocks * diskStats_storage.f_frsize / 1024
        freeTemporaryStorageSpace = diskStats_temporaryData.f_bfree * diskStats_temporaryData.f_frsize / 1024 - allocatedTemporaryStorageSpace
        availableTemporaryStorageSpace = diskStats_temporaryData.f_blocks * diskStats_temporaryData.f_frsize / 1024
        return freeStorageSpace, availableStorageSpace, freeTemporaryStorageSpace, availableTemporaryStorageSpace
    
    def __checkDiskImagesSpace(self):
        """
        Checks how much disk space must be allocated for the active virtual machines' disk images
        Args:
            None
        Returns:
            the storage and temporary storage space that must be allocated.
        """
        activeDomainNames = self.__commandsDBConnector.getRegisteredDomainNames()
        allocatedStorageSpace = 0
        allocatedTemporaryStorageSpace = 0
        for domainName in activeDomainNames:            
            imageID = self.__commandsDBConnector.getDomainImageID(domainName)
            if (imageID == None) :
                return 0, 0
            dataImagePath = self.__commandsDBConnector.getDomainDataImagePath(domainName)
            if (dataImagePath == None) :
                return 0, 0
            osImagePath = self.__commandsDBConnector.getDomainOSImagePath(domainName)          
            if (osImagePath == None):
                return 0, 0
            isEditedImage = self.__commandsDBConnector.getBootableFlag(imageID)      
            if (isEditedImage == None):
                return 0, 0
            dataSpace = self.__getAllocatedSpace(dataImagePath)
            if (isEditedImage) :
                osSpace = self.__getAllocatedSpace(osImagePath)
                allocatedStorageSpace += osSpace + dataSpace
            else :
                allocatedTemporaryStorageSpace += dataSpace
                
        return allocatedStorageSpace, allocatedTemporaryStorageSpace
            
    def __getAllocatedSpace(self, imagePath):
        """
        Returns the disk space that must be allocated for a disk image
        Args:
            imagePath: the disk image's path
        Returns:
            Nothing
        """
        try :
            output = ChildProcessManager.runCommandInForeground("qemu-img info " + imagePath, Exception)
            lines = output.split("\n")
            virtualSize = VMServerReactor.__extractImageSize(lines[2].split(":")[1].split("(")[0])
            usedSpace = VMServerReactor.__extractImageSize(lines[3].split(":")[1])            
            return virtualSize - usedSpace
        except Exception as e:
            print e
            return 0
            
    @staticmethod
    def __extractImageSize(string):
        """
        Extracts an image size from a quemu-img command output.
        Args:
            string: the qemu-img command's image size
        Returns:
            the disk image size (in kilobytes)
        """
        if ("G" in string) :
            power = 2
        elif ("M" in string):
            power = 1
        else:
            power = 0                                
        string = string.replace("G", "")
        string = string.replace("M", "")
        string = string.replace("K", "")
        return int(ceil(float(string))) * 1024 ** power        
    
    def __sendActiveDomainUIDs(self):
        """
        Sends domains' UUIDs to the cluster server
        Args:
            None
        Returns:
            Nothing
        """
        activeDomainUIDs = self.__commandsDBConnector.getActiveDomainUIDs()
        packet = self.__packetManager.createActiveDomainUIDsPacket(self.__vncServerIP, activeDomainUIDs)
        self.__networkManager.sendPacket('', self.__listenningPort, packet)
    
    def has_finished(self):
        """
        Checks if the virtual machine server has been shut down or not
        Args:
            None
        Returns:
            Nothing
        """
        return self.__finished