# -*- coding: utf8 -*-
'''
A simple client test
@author: Luis Barrios Hernández
@version: 1.0
'''

from network.manager.networkManager import NetworkManager
from network.tests.dummyCallback import DummyCallback
from time import sleep

if __name__ == "__main__" :
    port = 8080
    networkManager = NetworkManager("/home/luis/Certificates")
    networkManager.startNetworkService()
    networkManager.connectTo('127.0.0.1', port, 5, DummyCallback(), True)
    sleep(2)
    p = networkManager.createPacket(0)
    p.writeString("Greetings from a client")    
    networkManager.sendPacket('127.0.0.1', port, p)
    p = networkManager.createPacket(0)
    p.writeString("Greetings from a client 1")    
    networkManager.sendPacket('127.0.0.1', port, p)
    sleep(60)
    networkManager.stopNetworkService()
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
class ClusterServerReactor(WebPacketReactor, VMServerPacketReactor):
    '''
    These objects react to packages received from the website or from
    a virtual machine server.
    '''
    def __init__(self, timeout):
        """
        Initializes the reactor's state.
        Args:
            None
        """
        self.__webCallback = WebCallback(self)
        self.__finished = False
        self.__timeout = timeout
        
    def connectToDatabase(self, mysqlRootsPassword, dbName, dbUser, dbPassword, scriptPath):
        """
        Establishes a connection with the cluster server database.
        Args:
            mysqlRootsPassword: MySQL root's password
            dbName: the cluster server database's name
            dbUser: the cluster server database's user name
            dbPassword: the cluster server database's user password
            scriptPath: the cluster server database's initialization script path
        """
        configurator = DBConfigurator(mysqlRootsPassword)
        configurator.runSQLScript(dbName, scriptPath)
        configurator.addUser(dbUser, dbPassword, dbName, True)
        self.__dbConnector = ClusterServerDatabaseConnector(dbUser, dbPassword, dbName)
        self.__dbConnector.connect()
        self.__dbConnector.resetVMServersStatus()
        
    def startListenning(self, certificatePath, port):
        """
        Starts the network service and creates a server connection.
        Args:
            certificatePath: the server.crt and server.key files path
            port: the listenning port
        Returns:
            Nothing
        """
        self.__loadBalancer = SimpleLoadBalancer(self.__dbConnector)
        self.__networkManager = NetworkManager(certificatePath)
        self.__webPort = port
        self.__networkManager.startNetworkService()        
        self.__webPacketHandler = ClusterServerPacketHandler(self.__networkManager)
        self.__vmServerPacketHandler = VMServerPacketHandler(self.__networkManager)
        self.__networkManager.listenIn(port, self.__webCallback, True)
        self.__vmServerCallback = VMServerCallback(self)
        
    def processWebIncomingPacket(self, packet):
        """
        Processes a packet received from the web server.
        Args:
            packet: the packet to process
        Returns:
            Nothing
        """
        data = self.__webPacketHandler.readPacket(packet)
        if (data["packet_type"] == WEB_PACKET_T.REGISTER_VM_SERVER) :
            self.__registerVMServer(data)
        elif (data["packet_type"] == WEB_PACKET_T.QUERY_VM_SERVERS_STATUS) :
            self.__sendVMServerStatusData()
        elif (data["packet_type"] == WEB_PACKET_T.UNREGISTER_OR_SHUTDOWN_VM_SERVER) :
            self.__unregisterOrShutdownVMServer(data)
        elif (data["packet_type"] == WEB_PACKET_T.BOOTUP_VM_SERVER) :
            self.__bootUpVMServer(data)
        elif (data["packet_type"] == WEB_PACKET_T.VM_BOOT_REQUEST):
            self.__bootUpVM(data)
        elif (data["packet_type"] == WEB_PACKET_T.HALT) :
            self.__halt(data)
        elif (data["packet_type"] == WEB_PACKET_T.QUERY_VM_DISTRIBUTION) :
            self.__sendVMDistributionData()
        elif (data["packet_type"] == WEB_PACKET_T.QUERY_ACTIVE_VM_DATA) :
            self.__requestVNCConnectionData()
            
    def __requestVNCConnectionData(self):
        """
        Sends a VNC connection data request packet to all the active virtual machine servers
        Args:
            None
        Returns:
            Nothing
        """
        # Create a VNC connection data packet
        p = self.__vmServerPacketHandler.createVMServerDataRequestPacket(VMSRVR_PACKET_T.QUERY_ACTIVE_VM_DATA)
        # Fetch the active virtual machine server's IP addresses and ports
        connectionData = self.__dbConnector.getActiveVMServersConnectionData()
        for cd in connectionData :
            self.__networkManager.sendPacket(cd["ServerIP"], cd["ServerPort"], p)
            
    def __halt(self, data):
        """
        Shuts down the cluster (including the virtual machine servers).
        Args:
            haltVMServers: if True, all the active virtual machines will be destroyed and the virtual machine
            servers will be shut down. If false, the virtual machine servers will wait until there are no
            active virtual machines, and then they'll be shut down.
        """    
        vmServersConnectionData = self.__dbConnector.getActiveVMServersConnectionData()
        if (vmServersConnectionData != None) :
            args = dict()
            args["Halt"] = data["HaltVMServers"]
            args["Unregister"] = False            
            for connectionData in vmServersConnectionData :
                args["ServerNameOrIPAddress"] = connectionData["ServerIP"]
                self.__unregisterOrShutdownVMServer(args)  
        self.__finished = True   
             
    def __registerVMServer(self, data):
        """
        Processes a virtual machine server registration packet.
        Args:
            data: the received virtual machine server registration packet.
        Returns:
            Nothing
        """
        try :
            # Check if the IP address is assigned to another virtual machine server
            server_id = self.__dbConnector.getVMServerID(data["VMServerIP"])
            if (server_id != None) :
                raise Exception("The IP address " + data["VMServerIP"] + " is assigned to another VM server")
            # Check if the name is assigned to another virtual machine server
            server_id = self.__dbConnector.getVMServerID(data["VMServerName"])
            if (server_id != None) :
                raise Exception("The name " + data["VMServerName"] + " is assigned to another VM server")
            # Establish a connection
            self.__networkManager.connectTo(data["VMServerIP"], data["VMServerPort"], 
                                                20, self.__vmServerCallback, True, True)
            while not self.__networkManager.isConnectionReady(data["VMServerIP"], data["VMServerPort"]) :
                sleep(0.1)
            # Register the server on the database
            self.__dbConnector.registerVMServer(data["VMServerName"], data["VMServerIP"], 
                                                    data["VMServerPort"])
            # Command the virtual machine server to tell us its state
            p = self.__vmServerPacketHandler.createVMServerDataRequestPacket(VMSRVR_PACKET_T.SERVER_STATUS_REQUEST)
            self.__networkManager.sendPacket(data["VMServerIP"], data["VMServerPort"], p)
            # Everything went fine
            p = self.__webPacketHandler.createCommandExecutedPacket(data["CommandID"])
        except Exception as e:                
            p = self.__webPacketHandler.createVMServerRegistrationErrorPacket(data["VMServerIP"], 
                                                                            data["VMServerPort"], 
                                                                            data["VMServerName"], str(e), data["CommandID"])        
        self.__networkManager.sendPacket('', self.__webPort, p)
            
    def __unregisterOrShutdownVMServer(self, data):
        """
        Processes a virtual machine server unregistration or shutdown packet.
        Args:
            key: the virtual machine server's IPv4 address or its name.
            halt: if True, the virtual machine server will be shut down immediately. If false, it'll
            wait until all the virtual machines are shut down, and then it will finally be shut down.
            unregister: if True, the virtual machine server will be deleted from the cluster server's database. 
        """
        key = data["ServerNameOrIPAddress"] 
        halt = data["Halt"]
        unregister = data["Unregister"]
        if data.has_key("CommandID") :
            useCommandID = True 
            commandID = data["CommandID"]
        else :
            useCommandID = False
        # Shut down the server (if necessary)
        serverId = self.__dbConnector.getVMServerID(key)
        if (serverId != None) :
            serverData = self.__dbConnector.getVMServerBasicData(serverId)
            status = serverData["ServerStatus"]
            if (status == SERVER_STATE_T.READY or status == SERVER_STATE_T.BOOTING) :
                if not halt :
                    p = self.__vmServerPacketHandler.createVMServerShutdownPacket()
                else :
                    p = self.__vmServerPacketHandler.createVMServerHaltPacket()
                self.__networkManager.sendPacket(serverData["ServerIP"], serverData["ServerPort"], p)
                # Close the network connection
                self.__networkManager.closeConnection(serverData["ServerIP"], serverData["ServerPort"])
            if (not unregister) :
                self.__dbConnector.updateVMServerStatus(serverId, SERVER_STATE_T.SHUT_DOWN)
                self.__dbConnector.deleteVMServerStatistics(serverId)
            else :
                # Update the virtual machine server's state
                self.__dbConnector.deleteVMServer(key)
            if (useCommandID) :
                # Create a command executed packet and send it to the website
                p = self.__webPacketHandler.createCommandExecutedPacket(commandID)
                self.__networkManager.sendPacket('', self.__webPort, p)
        else :
            # Error
            if (unregister) :
                packet_type = WEB_PACKET_T.VM_SERVER_UNREGISTRATION_ERROR
            else :
                packet_type = WEB_PACKET_T.VM_SERVER_SHUTDOWN_ERROR
            errorMessage = "The virtual machine server with name or IP address <<{0}>> is not registered".format(key)
            p = self.__webPacketHandler.createVMServerGenericErrorPacket(packet_type, key, errorMessage, commandID)
            self.__networkManager.sendPacket('', self.__webPort, p)   
            
    def __updateVMServerStatus(self, data):
        """
        Processes a virtual machine server's status packet.
        Args:
            data: the received packet
        Returns:
            Nothing
        """
        # Fetch the virtual machine server's ID
        serverID = None
        while (serverID == None) :
            serverID = self.__dbConnector.getVMServerID(data["VMServerIP"])
            if (serverID == None) :
                sleep(0.1)
        # Change its status
        self.__dbConnector.updateVMServerStatus(serverID, SERVER_STATE_T.READY)
        self.__dbConnector.setVMServerStatistics(serverID, data["ActiveDomains"])
        
    def __bootUpVMServer(self, data):
        """
        Processes a virtual machine server boot packet.
        Args:
            serverNameOrIPAddress: the virtual machine server's name or IPv4 address.
        Returns:
            Nothing
        """
        try :
            serverNameOrIPAddress = data["ServerNameOrIPAddress"]
            serverId = self.__dbConnector.getVMServerID(serverNameOrIPAddress)
            if (serverId == None) :
                raise Exception("The virtual machine server is not registered")
            serverData = self.__dbConnector.getVMServerBasicData(serverId)
            # Connect to the virtual machine server
            self.__networkManager.connectTo(serverData["ServerIP"], serverData["ServerPort"], 
                                                20, self.__vmServerCallback, True, True)
            while not self.__networkManager.isConnectionReady(serverData["ServerIP"], serverData["ServerPort"]) :
                sleep(0.1)
            # Change its status
            self.__dbConnector.updateVMServerStatus(serverId, SERVER_STATE_T.BOOTING)
            # Send the status request
            p = self.__vmServerPacketHandler.createVMServerDataRequestPacket(VMSRVR_PACKET_T.SERVER_STATUS_REQUEST)
            self.__networkManager.sendPacket(serverData["ServerIP"], serverData["ServerPort"], p)
            # Everything went fine
            p = self.__webPacketHandler.createCommandExecutedPacket(data["CommandID"])
        except Exception as e:
            p = self.__webPacketHandler.createVMServerGenericErrorPacket(WEB_PACKET_T.VM_SERVER_BOOTUP_ERROR, 
                                                                         serverNameOrIPAddress, str(e), data["CommandID"])
        self.__networkManager.sendPacket('', self.__webPort, p)
            
    def __sendVMServerStatusData(self):
        """
        Processes a virtual machine server data request packet.
        Args:
            None
        Returns:
            Nothing
        """
        self.__sendStatusData(self.__dbConnector.getVMServerBasicData, self.__webPacketHandler.createVMServerStatusPacket)
        
    def __sendVMDistributionData(self):
        """
        Processes a virtual machine distribution data packet
        Args:
            None
        Returns:
            Nothing
        """    
        self.__sendStatusData(self.__dbConnector.getHostedImages, self.__webPacketHandler.createVMDistributionPacket)
        
    def __sendStatusData(self, queryMethod, packetCreationMethod):
        """
        Processes a data request packet.
        Args:
            queryMethod: the method that extracts the data from the database
            packetCreationMethod: the method that creates the packet
        Returns:
            Nothing
        """        
        segmentSize = 5
        outgoingData = []
        serverIDs = self.__dbConnector.getVMServerIDs()
        if (len(serverIDs) == 0) :
            segmentCounter = 0
            segmentNumber = 0
            sendLastSegment = True
        else :
            segmentCounter = 1        
            segmentNumber = (len(serverIDs) / segmentSize)
            if (len(serverIDs) % segmentSize != 0) :
                segmentNumber += 1
                sendLastSegment = True
            else :
                sendLastSegment = False
        for serverID in serverIDs :
            row = queryMethod(serverID)
            if (isinstance(row, dict)) :
                outgoingData.append(row)
            else :
                outgoingData += row
            if (len(outgoingData) >= segmentSize) :
                # Flush
                packet = packetCreationMethod(segmentCounter, segmentNumber, outgoingData)
                self.__networkManager.sendPacket('', self.__webPort, packet)
                outgoingData = []
                segmentCounter += 1
        # Send the last segment
        if (sendLastSegment) :
            packet = packetCreationMethod(segmentCounter, segmentNumber, outgoingData)
            self.__networkManager.sendPacket('', self.__webPort, packet)             
                   
    def __bootUpVM(self, data):
        """
        Processes a virtual machine boot request packet.
        Args:
            vmName: the virtual machine's ID
            userID: the user's unique identifier
        Returns:
            Nothing
        """
        vmID = data["VMID"]
        userID = data["UserID"]
        # Choose the virtual machine server that will host the image
        (serverID, errorMessage) = self.__loadBalancer.assignVMServer(vmID)
        if (errorMessage != None) :
            # Something went wrong => warn the user
            p = self.__webPacketHandler.createVMBootFailurePacket(vmID, errorMessage, data["CommandID"])
            self.__networkManager.sendPacket('', self.__webPort, p)
        else :
            
            # Ask the virtual machine server to boot up the VM
            p = self.__vmServerPacketHandler.createVMBootPacket(vmID, userID, data["CommandID"])
            serverData = self.__dbConnector.getVMServerBasicData(serverID)
            self.__networkManager.sendPacket(serverData["ServerIP"], serverData["ServerPort"], p)    
            # Register the boot command
            self.__dbConnector.registerVMBootCommand(data["CommandID"], data["VMID"])
            # Everything went fine
            #p = self.__webPacketHandler.createCommandExecutedPacket(data["CommandID"])
            #self.__networkManager.sendPacket('', self.__webPort, p)
    
    def processVMServerIncomingPacket(self, packet):
        """
        Processes a packet sent from a virtual machine server.
        Args:
            packet: the packet to process
        Returns:
            Nothing
        """
        data = self.__vmServerPacketHandler.readPacket(packet)
        if (data["packet_type"] == VMSRVR_PACKET_T.SERVER_STATUS) :
            self.__updateVMServerStatus(data)
        elif (data["packet_type"] == VMSRVR_PACKET_T.DOMAIN_CONNECTION_DATA) :
            self.__sendVMConnectionData(data)
        elif (data["packet_type"] == VMSRVR_PACKET_T.ACTIVE_VM_DATA) :
            self.__sendVNCConnectionData(packet)
            
    def processServerReconnectionData(self, ipAddress, reconnection_status) :
        """
        Processes a reconnection status event
        Args:
            ipAddress: the connection's IPv4 address
            port: the connection's port
            reconnection_status: the reconnection process' status
        Returns:
            Nothing
        """
        if (reconnection_status == RECONNECTION_T.RECONNECTING) : 
            status = SERVER_STATE_T.RECONNECTING
        elif (reconnection_status == RECONNECTION_T.REESTABLISHED) :
            status = SERVER_STATE_T.READY
        else :
            status = SERVER_STATE_T.CONNECTION_TIMED_OUT
        
        serverID = self.__dbConnector.getVMServerID(ipAddress)
        self.__dbConnector.updateVMServerStatus(serverID, status)
            
    def __sendVNCConnectionData(self, packet):
        """
        Processes a VNC connection data packet
        Args:
            packet: the packet to process
        Returns:
            Nothing
        """
        p = self.__webPacketHandler.createActiveVMsDataPacket(packet)
        self.__networkManager.sendPacket('', self.__webPort, p)
            
    def __sendVMConnectionData(self, data):
        """
        Processes a virtual machine connection data packet.
        Args:
            data: the packet to process' data.
        Returns:
            Nothing
        """
        # Delete the boot command from the database
        self.__dbConnector.removeVMBootCommand(data["CommandID"])
        p = self.__webPacketHandler.createVMConnectionDataPacket(data["VNCServerIP"], 
                                                                 data["VNCServerPort"], data["VNCServerPassword"], data["CommandID"])
        self.__networkManager.sendPacket('', self.__webPort, p)        
    
    def monitorVMBootCommands(self):
        errorMessage = "Could not boot up virtual machine (timeout error)"
        while not self.__finished :
            data = self.__dbConnector.getOldVMBootCommandID(self.__timeout)
            if (not self.__finished and data == None) :
                # Be careful with this: if MySQL server receives too many queries per second,
                # the reactor's database connection will be closed.
                sleep(1) 
            else :
                # Create a virtual machine boot error packet
                p = self.__webPacketHandler.createVMBootFailurePacket(data[1], errorMessage, data[0])
                self.__networkManager.sendPacket('', self.__webPort, p)
    
    def hasFinished(self):
        """
        Indicates if the cluster server has finished or not.
        """
        return self.__finished
    
    def shutdown(self):
        """
        Shuts down the cluster server.
        Args:
            None
        Returns:
            Nothing
        @attention: this method MUST NOT be called from a network thread. If you do so, the application will hang!
        """
        self.__networkManager.stopNetworkService()
Beispiel #4
0
    Para realizarse la prueba debe ejecutarse este módulo el número de clientes 
     que se desee
    Testeado con server 03
'''
import time
from network.manager.networkManager import NetworkManager
from network.tests.dummyCallback import DummyCallback
from time import sleep

if __name__ == "__main__" :
    
    # Fase 1 :Creamos y conectamos a los clientes
    #Cliente 1
    client1 = NetworkManager()
    client1.startNetworkService()
    client1.connectTo('127.0.0.1', 8080, 20, DummyCallback())
  
    while(not client1.isConnectionReady('127.0.0.1', 8080)):
        sleep(0.1)
    #Fase 2: Cada cliente envia su paquete
    #Cliente 1
    p1 = client1.createPacket(0)
    p1.writeString("I am Client " + str(time.localtime()[3]) + ":" 
                    + str(time.localtime()[4]) + ":" + str(time.localtime()[5]))        
    client1.sendPacket('127.0.0.1', 8080, p1)
   
    #Fase 3: Esperamos y cerramos las conexiones
    sleep(200)
    #Cliente 1
    client1.stopNetworkService()
class ImageRepositoryReactor(object):
    """
    Image repository packet reactor
    """
    def __init__(self, diskImagesDirectory):
        """
        Initializes the reactor's state
        Args:
            diskImagesDirectory: the FTP server's root directory
        """
        self.__diskImagesDirectory = diskImagesDirectory
        self.__slotCounter = MultithreadingCounter()
        self.__retrieveQueue = GenericThreadSafeList()
        self.__storeQueue = GenericThreadSafeList()
        self.__finish = False
        self.__networkManager = None
        self.__ftpServer = None

    def connectToDatabase(self, repositoryDBName, repositoryDBUser,
                          repositoryDBPassword):
        """
        Establishes the connection with the image repository's database.
        Args:
            repositoryDBName: a database name
            repositoryDBUser: an user name
            repositoryDBPassword: a password
        Returns:
            Nothing
        """
        self.__dbConnector = ImageRepositoryDBConnector(
            repositoryDBUser, repositoryDBPassword, repositoryDBName)

    def startListenning(self, networkInterface, useSSL, certificatesDirectory,
                        commandsListenningPort, ftpListenningPort,
                        maxConnections, maxConnectionsPerIP,
                        uploadBandwidthRatio, downloadBandwidthRatio,
                        ftpUsername, ftpPasswordLength):
        """
        Boots up the FTP server and creates the control connection.
        Args:
            networkInterface: the network interface that will be used by the FTP server
            useSSL: indicates if SSL encryption must be used in the control connection or not
            certificatesDirectory: the directory where the files server.crt and server.key are
            commandsListenningPort: the control connection's port
            ftpListenningPort: the FTP server listenning port
            maxConnections: maximum FTP connections
            maxConnectionsPerIP: maximum FTP connections per IP address
            uploadBandwidthRatio: maximum download bandwidth fraction
            downloadBandwidthRatio: maximum upload bandwidth fraction
            ftpUsername: the FTP user that the virtual machine servers will use
            ftpPasswordLength: the random FTP password length
        Returns:
            Nothing
        @attention: The FTP password will be randomly generated at every boot.
        """
        try:
            self.__maxConnections = maxConnections
            self.__commandsListenningPort = commandsListenningPort
            self.__FTPListenningPort = ftpListenningPort
            self.__networkManager = NetworkManager(certificatesDirectory)
            self.__repositoryPacketHandler = ImageRepositoryPacketHandler(
                self.__networkManager)

            self.__commandsCallback = CommandsCallback(
                self.__networkManager, self.__repositoryPacketHandler,
                commandsListenningPort, self.__dbConnector,
                self.__retrieveQueue, self.__storeQueue,
                self.__diskImagesDirectory)

            self.__networkManager.startNetworkService()
            self.__networkManager.listenIn(commandsListenningPort,
                                           self.__commandsCallback, useSSL)

            dataCallback = FTPServerCallback(self.__slotCounter,
                                             self.__dbConnector)

            self.__ftpUsername = ftpUsername
            self.__ftpPassword = ChildProcessManager.runCommandInForeground(
                "openssl rand -base64 {0}".format(ftpPasswordLength),
                Exception)

            self.__ftpServer = ConfigurableFTPServer(
                "Image repository FTP Server")

            self.__ftpServer.startListenning(networkInterface,
                                             ftpListenningPort, maxConnections,
                                             maxConnectionsPerIP, dataCallback,
                                             downloadBandwidthRatio,
                                             uploadBandwidthRatio)
            self.__ftpServer.addUser(self.__ftpUsername, self.__ftpPassword,
                                     self.__diskImagesDirectory, "eramw")
        except Exception as e:
            print "Error: " + e.message
            self.__finish = True

    def stopListenning(self):
        """
        Stops the FTP server and closes the control connection.
        Args:
            None
        Returns:
            Nothing
        """
        if (self.__ftpServer != None):
            try:
                self.__ftpServer.stopListenning()
            except Exception:
                pass
        if (self.__networkManager != None):
            self.__networkManager.stopNetworkService()

    def initTransfers(self):
        """
        Initializes the upload and download transfers
        Args:
            None
        Returns:
            Nothing
        """
        store = False
        while not (self.__finish or self.__commandsCallback.haltReceived()):
            if (self.__slotCounter.read() == self.__maxConnections):
                # No free slots => sleep
                sleep(0.1)
            else:
                # There are slots => enable uploads and downloads in parallel
                self.__slotCounter.decrement()

                if (self.__retrieveQueue.isEmpty()
                        and self.__storeQueue.isEmpty()):
                    sleep(0.1)
                    continue
                if (not self.__retrieveQueue.isEmpty()
                        and self.__storeQueue.isEmpty()):
                    queue = self.__retrieveQueue
                    store = False
                elif (self.__retrieveQueue.isEmpty()
                      and not self.__storeQueue.isEmpty()):
                    queue = self.__storeQueue
                    store = True
                else:
                    if (store):
                        queue = self.__retrieveQueue
                        store = False
                    else:
                        queue = self.__storeQueue
                        store = True

                (imageID, clientIP, clientPort) = queue.pop(0)

                imageData = self.__dbConnector.getImageData(imageID)
                if (imageData == None):
                    if (store):
                        packet_type = PACKET_T.STOR_ERROR
                    else:
                        packet_type = PACKET_T.RETR_ERROR
                    p = self.__repositoryPacketHandler.createErrorPacket(
                        packet_type, ERROR_DESC_T.IR_IMAGE_DELETED)
                    self.__networkManager.sendPacket(
                        '', self.__commandsListenningPort, p, clientIP,
                        clientPort)
                else:
                    compressedFilePath = imageData["compressedFilePath"]

                    if (not "undefined" in compressedFilePath):
                        serverDirectory = path.relpath(
                            path.dirname(compressedFilePath),
                            self.__diskImagesDirectory)
                        compressedFileName = path.basename(compressedFilePath)
                    else:
                        serverDirectory = str(imageID)
                        compressedFileName = ""
                        serverDirectoryPath = path.join(
                            self.__diskImagesDirectory, serverDirectory)
                        if (path.exists(serverDirectoryPath)):
                            # The directory exists, and can store shit => clean it up!
                            ChildProcessManager.runCommandInForeground(
                                "rm -rf " + serverDirectoryPath, Exception)
                        mkdir(serverDirectoryPath)

                    if (store):
                        packet_type = PACKET_T.STOR_START
                    else:
                        packet_type = PACKET_T.RETR_START

                    p = self.__repositoryPacketHandler.createTransferEnabledPacket(
                        packet_type, imageID, self.__FTPListenningPort,
                        self.__ftpUsername, self.__ftpPassword,
                        serverDirectory, compressedFileName)

                    self.__networkManager.sendPacket(
                        '', self.__commandsListenningPort, p, clientIP,
                        clientPort)
class ImageRepositoryReactor(object):
    """
    Image repository packet reactor
    """    
    
    def __init__(self, diskImagesDirectory):
        """
        Initializes the reactor's state
        Args:
            diskImagesDirectory: the FTP server's root directory
        """
        self.__diskImagesDirectory = diskImagesDirectory        
        self.__slotCounter = MultithreadingCounter()
        self.__retrieveQueue = GenericThreadSafeList()
        self.__storeQueue = GenericThreadSafeList() 
        self.__finish = False   
        self.__networkManager = None
        self.__ftpServer = None    
    
    def connectToDatabase(self, repositoryDBName, repositoryDBUser, repositoryDBPassword):
        """
        Establishes the connection with the image repository's database.
        Args:
            repositoryDBName: a database name
            repositoryDBUser: an user name
            repositoryDBPassword: a password
        Returns:
            Nothing
        """
        self.__dbConnector = ImageRepositoryDBConnector(repositoryDBUser, repositoryDBPassword, repositoryDBName)
    
    def startListenning(self, networkInterface, useSSL, certificatesDirectory, commandsListenningPort, ftpListenningPort, maxConnections,
                        maxConnectionsPerIP, uploadBandwidthRatio, downloadBandwidthRatio, ftpUsername, ftpPasswordLength): 
        """
        Boots up the FTP server and creates the control connection.
        Args:
            networkInterface: the network interface that will be used by the FTP server
            useSSL: indicates if SSL encryption must be used in the control connection or not
            certificatesDirectory: the directory where the files server.crt and server.key are
            commandsListenningPort: the control connection's port
            ftpListenningPort: the FTP server listenning port
            maxConnections: maximum FTP connections
            maxConnectionsPerIP: maximum FTP connections per IP address
            uploadBandwidthRatio: maximum download bandwidth fraction
            downloadBandwidthRatio: maximum upload bandwidth fraction
            ftpUsername: the FTP user that the virtual machine servers will use
            ftpPasswordLength: the random FTP password length
        Returns:
            Nothing
        @attention: The FTP password will be randomly generated at every boot.
        """
        try :
            self.__maxConnections = maxConnections      
            self.__commandsListenningPort = commandsListenningPort
            self.__FTPListenningPort = ftpListenningPort
            self.__networkManager = NetworkManager(certificatesDirectory)
            self.__repositoryPacketHandler = ImageRepositoryPacketHandler(self.__networkManager)        
            
            self.__commandsCallback = CommandsCallback(self.__networkManager, self.__repositoryPacketHandler, commandsListenningPort, self.__dbConnector,
                                                       self.__retrieveQueue, self.__storeQueue, self.__diskImagesDirectory)        
            
            self.__networkManager.startNetworkService()
            self.__networkManager.listenIn(commandsListenningPort, self.__commandsCallback, useSSL)
            
            dataCallback = FTPServerCallback(self.__slotCounter, self.__dbConnector)
            
            self.__ftpUsername = ftpUsername
            self.__ftpPassword = ChildProcessManager.runCommandInForeground("openssl rand -base64 {0}".format(ftpPasswordLength), Exception)        
            
            self.__ftpServer = ConfigurableFTPServer("Image repository FTP Server")
       
            self.__ftpServer.startListenning(networkInterface, ftpListenningPort, maxConnections, maxConnectionsPerIP, 
                                             dataCallback, downloadBandwidthRatio, uploadBandwidthRatio)
            self.__ftpServer.addUser(self.__ftpUsername, self.__ftpPassword, self.__diskImagesDirectory, "eramw")      
        except Exception as e:
            print "Error: " + e.message
            self.__finish = True
        
    def stopListenning(self):
        """
        Stops the FTP server and closes the control connection.
        Args:
            None
        Returns:
            Nothing
        """
        if (self.__ftpServer != None) :
            try :
                self.__ftpServer.stopListenning()
            except Exception :
                pass
        if (self.__networkManager != None) :
            self.__networkManager.stopNetworkService()
    
    def initTransfers(self):
        """
        Initializes the upload and download transfers
        Args:
            None
        Returns:
            Nothing
        """
        store = False
        while not (self.__finish or self.__commandsCallback.haltReceived()):
            if (self.__slotCounter.read() == self.__maxConnections) :
                # No free slots => sleep
                sleep(0.1)
            else :
                # There are slots => enable uploads and downloads in parallel
                self.__slotCounter.decrement()
                
                if (self.__retrieveQueue.isEmpty() and self.__storeQueue.isEmpty()) :
                    sleep(0.1)
                    continue                
                if (not self.__retrieveQueue.isEmpty() and self.__storeQueue.isEmpty()) :
                    queue = self.__retrieveQueue
                    store = False
                elif (self.__retrieveQueue.isEmpty() and not self.__storeQueue.isEmpty()) :
                    queue = self.__storeQueue
                    store = True
                else :
                    if (store) :
                        queue = self.__retrieveQueue
                        store = False
                    else :
                        queue = self.__storeQueue
                        store = True
                
                                   
                (imageID, clientIP, clientPort) = queue.pop(0)
                    
                imageData = self.__dbConnector.getImageData(imageID)
                if (imageData == None) :
                    if (store) :
                        packet_type = PACKET_T.STOR_ERROR
                    else :
                        packet_type = PACKET_T.RETR_ERROR
                    p = self.__repositoryPacketHandler.createErrorPacket(packet_type, ERROR_DESC_T.IR_IMAGE_DELETED)
                    self.__networkManager.sendPacket('', self.__commandsListenningPort, p, clientIP, clientPort)
                else :
                    compressedFilePath = imageData["compressedFilePath"]    
                    
                    if (not "undefined" in compressedFilePath) :                                
                        serverDirectory = path.relpath(path.dirname(compressedFilePath), self.__diskImagesDirectory)
                        compressedFileName = path.basename(compressedFilePath)
                    else :
                        serverDirectory = str(imageID)
                        compressedFileName = ""
                        serverDirectoryPath = path.join(self.__diskImagesDirectory, serverDirectory)
                        if (path.exists(serverDirectoryPath)) :
                            # The directory exists, and can store shit => clean it up!
                            ChildProcessManager.runCommandInForeground("rm -rf " + serverDirectoryPath, Exception)
                        mkdir(serverDirectoryPath)                        
                    
                    if (store) :
                        packet_type = PACKET_T.STOR_START
                    else :
                        packet_type = PACKET_T.RETR_START
                                    
                    p = self.__repositoryPacketHandler.createTransferEnabledPacket(packet_type, imageID, self.__FTPListenningPort, 
                                    self.__ftpUsername, self.__ftpPassword, serverDirectory, compressedFileName)
                        
                    self.__networkManager.sendPacket('', self.__commandsListenningPort, p, clientIP, clientPort)
Beispiel #7
0
class ClusterServerReactor(WebPacketReactor, VMServerPacketReactor):
    '''
    These objects react to packages received from the website or from
    a virtual machine server.
    '''
    def __init__(self, timeout):
        """
        Initializes the reactor's state.
        Args:
            None
        """
        self.__webCallback = WebCallback(self)
        self.__finished = False
        self.__timeout = timeout

    def connectToDatabase(self, mysqlRootsPassword, dbName, dbUser, dbPassword,
                          scriptPath):
        """
        Establishes a connection with the cluster server database.
        Args:
            mysqlRootsPassword: MySQL root's password
            dbName: the cluster server database's name
            dbUser: the cluster server database's user name
            dbPassword: the cluster server database's user password
            scriptPath: the cluster server database's initialization script path
        """
        configurator = DBConfigurator(mysqlRootsPassword)
        configurator.runSQLScript(dbName, scriptPath)
        configurator.addUser(dbUser, dbPassword, dbName, True)
        self.__dbConnector = ClusterServerDatabaseConnector(
            dbUser, dbPassword, dbName)
        self.__dbConnector.connect()
        self.__dbConnector.resetVMServersStatus()

    def startListenning(self, certificatePath, port):
        """
        Starts the network service and creates a server connection.
        Args:
            certificatePath: the server.crt and server.key files path
            port: the listenning port
        Returns:
            Nothing
        """
        self.__loadBalancer = SimpleLoadBalancer(self.__dbConnector)
        self.__networkManager = NetworkManager(certificatePath)
        self.__webPort = port
        self.__networkManager.startNetworkService()
        self.__webPacketHandler = ClusterServerPacketHandler(
            self.__networkManager)
        self.__vmServerPacketHandler = VMServerPacketHandler(
            self.__networkManager)
        self.__networkManager.listenIn(port, self.__webCallback, True)
        self.__vmServerCallback = VMServerCallback(self)

    def processWebIncomingPacket(self, packet):
        """
        Processes a packet received from the web server.
        Args:
            packet: the packet to process
        Returns:
            Nothing
        """
        data = self.__webPacketHandler.readPacket(packet)
        if (data["packet_type"] == WEB_PACKET_T.REGISTER_VM_SERVER):
            self.__registerVMServer(data)
        elif (data["packet_type"] == WEB_PACKET_T.QUERY_VM_SERVERS_STATUS):
            self.__sendVMServerStatusData()
        elif (data["packet_type"] ==
              WEB_PACKET_T.UNREGISTER_OR_SHUTDOWN_VM_SERVER):
            self.__unregisterOrShutdownVMServer(data)
        elif (data["packet_type"] == WEB_PACKET_T.BOOTUP_VM_SERVER):
            self.__bootUpVMServer(data)
        elif (data["packet_type"] == WEB_PACKET_T.VM_BOOT_REQUEST):
            self.__bootUpVM(data)
        elif (data["packet_type"] == WEB_PACKET_T.HALT):
            self.__halt(data)
        elif (data["packet_type"] == WEB_PACKET_T.QUERY_VM_DISTRIBUTION):
            self.__sendVMDistributionData()
        elif (data["packet_type"] == WEB_PACKET_T.QUERY_ACTIVE_VM_DATA):
            self.__requestVNCConnectionData()

    def __requestVNCConnectionData(self):
        """
        Sends a VNC connection data request packet to all the active virtual machine servers
        Args:
            None
        Returns:
            Nothing
        """
        # Create a VNC connection data packet
        p = self.__vmServerPacketHandler.createVMServerDataRequestPacket(
            VMSRVR_PACKET_T.QUERY_ACTIVE_VM_DATA)
        # Fetch the active virtual machine server's IP addresses and ports
        connectionData = self.__dbConnector.getActiveVMServersConnectionData()
        for cd in connectionData:
            self.__networkManager.sendPacket(cd["ServerIP"], cd["ServerPort"],
                                             p)

    def __halt(self, data):
        """
        Shuts down the cluster (including the virtual machine servers).
        Args:
            haltVMServers: if True, all the active virtual machines will be destroyed and the virtual machine
            servers will be shut down. If false, the virtual machine servers will wait until there are no
            active virtual machines, and then they'll be shut down.
        """
        vmServersConnectionData = self.__dbConnector.getActiveVMServersConnectionData(
        )
        if (vmServersConnectionData != None):
            args = dict()
            args["Halt"] = data["HaltVMServers"]
            args["Unregister"] = False
            for connectionData in vmServersConnectionData:
                args["ServerNameOrIPAddress"] = connectionData["ServerIP"]
                self.__unregisterOrShutdownVMServer(args)
        self.__finished = True

    def __registerVMServer(self, data):
        """
        Processes a virtual machine server registration packet.
        Args:
            data: the received virtual machine server registration packet.
        Returns:
            Nothing
        """
        try:
            # Check if the IP address is assigned to another virtual machine server
            server_id = self.__dbConnector.getVMServerID(data["VMServerIP"])
            if (server_id != None):
                raise Exception("The IP address " + data["VMServerIP"] +
                                " is assigned to another VM server")
            # Check if the name is assigned to another virtual machine server
            server_id = self.__dbConnector.getVMServerID(data["VMServerName"])
            if (server_id != None):
                raise Exception("The name " + data["VMServerName"] +
                                " is assigned to another VM server")
            # Establish a connection
            self.__networkManager.connectTo(data["VMServerIP"],
                                            data["VMServerPort"], 20,
                                            self.__vmServerCallback, True,
                                            True)
            while not self.__networkManager.isConnectionReady(
                    data["VMServerIP"], data["VMServerPort"]):
                sleep(0.1)
            # Register the server on the database
            self.__dbConnector.registerVMServer(data["VMServerName"],
                                                data["VMServerIP"],
                                                data["VMServerPort"])
            # Command the virtual machine server to tell us its state
            p = self.__vmServerPacketHandler.createVMServerDataRequestPacket(
                VMSRVR_PACKET_T.SERVER_STATUS_REQUEST)
            self.__networkManager.sendPacket(data["VMServerIP"],
                                             data["VMServerPort"], p)
            # Everything went fine
            p = self.__webPacketHandler.createCommandExecutedPacket(
                data["CommandID"])
        except Exception as e:
            p = self.__webPacketHandler.createVMServerRegistrationErrorPacket(
                data["VMServerIP"], data["VMServerPort"], data["VMServerName"],
                str(e), data["CommandID"])
        self.__networkManager.sendPacket('', self.__webPort, p)

    def __unregisterOrShutdownVMServer(self, data):
        """
        Processes a virtual machine server unregistration or shutdown packet.
        Args:
            key: the virtual machine server's IPv4 address or its name.
            halt: if True, the virtual machine server will be shut down immediately. If false, it'll
            wait until all the virtual machines are shut down, and then it will finally be shut down.
            unregister: if True, the virtual machine server will be deleted from the cluster server's database. 
        """
        key = data["ServerNameOrIPAddress"]
        halt = data["Halt"]
        unregister = data["Unregister"]
        if data.has_key("CommandID"):
            useCommandID = True
            commandID = data["CommandID"]
        else:
            useCommandID = False
        # Shut down the server (if necessary)
        serverId = self.__dbConnector.getVMServerID(key)
        if (serverId != None):
            serverData = self.__dbConnector.getVMServerBasicData(serverId)
            status = serverData["ServerStatus"]
            if (status == SERVER_STATE_T.READY
                    or status == SERVER_STATE_T.BOOTING):
                if not halt:
                    p = self.__vmServerPacketHandler.createVMServerShutdownPacket(
                    )
                else:
                    p = self.__vmServerPacketHandler.createVMServerHaltPacket()
                self.__networkManager.sendPacket(serverData["ServerIP"],
                                                 serverData["ServerPort"], p)
                # Close the network connection
                self.__networkManager.closeConnection(serverData["ServerIP"],
                                                      serverData["ServerPort"])
            if (not unregister):
                self.__dbConnector.updateVMServerStatus(
                    serverId, SERVER_STATE_T.SHUT_DOWN)
                self.__dbConnector.deleteVMServerStatistics(serverId)
            else:
                # Update the virtual machine server's state
                self.__dbConnector.deleteVMServer(key)
            if (useCommandID):
                # Create a command executed packet and send it to the website
                p = self.__webPacketHandler.createCommandExecutedPacket(
                    commandID)
                self.__networkManager.sendPacket('', self.__webPort, p)
        else:
            # Error
            if (unregister):
                packet_type = WEB_PACKET_T.VM_SERVER_UNREGISTRATION_ERROR
            else:
                packet_type = WEB_PACKET_T.VM_SERVER_SHUTDOWN_ERROR
            errorMessage = "The virtual machine server with name or IP address <<{0}>> is not registered".format(
                key)
            p = self.__webPacketHandler.createVMServerGenericErrorPacket(
                packet_type, key, errorMessage, commandID)
            self.__networkManager.sendPacket('', self.__webPort, p)

    def __updateVMServerStatus(self, data):
        """
        Processes a virtual machine server's status packet.
        Args:
            data: the received packet
        Returns:
            Nothing
        """
        # Fetch the virtual machine server's ID
        serverID = None
        while (serverID == None):
            serverID = self.__dbConnector.getVMServerID(data["VMServerIP"])
            if (serverID == None):
                sleep(0.1)
        # Change its status
        self.__dbConnector.updateVMServerStatus(serverID, SERVER_STATE_T.READY)
        self.__dbConnector.setVMServerStatistics(serverID,
                                                 data["ActiveDomains"])

    def __bootUpVMServer(self, data):
        """
        Processes a virtual machine server boot packet.
        Args:
            serverNameOrIPAddress: the virtual machine server's name or IPv4 address.
        Returns:
            Nothing
        """
        try:
            serverNameOrIPAddress = data["ServerNameOrIPAddress"]
            serverId = self.__dbConnector.getVMServerID(serverNameOrIPAddress)
            if (serverId == None):
                raise Exception("The virtual machine server is not registered")
            serverData = self.__dbConnector.getVMServerBasicData(serverId)
            # Connect to the virtual machine server
            self.__networkManager.connectTo(serverData["ServerIP"],
                                            serverData["ServerPort"], 20,
                                            self.__vmServerCallback, True,
                                            True)
            while not self.__networkManager.isConnectionReady(
                    serverData["ServerIP"], serverData["ServerPort"]):
                sleep(0.1)
            # Change its status
            self.__dbConnector.updateVMServerStatus(serverId,
                                                    SERVER_STATE_T.BOOTING)
            # Send the status request
            p = self.__vmServerPacketHandler.createVMServerDataRequestPacket(
                VMSRVR_PACKET_T.SERVER_STATUS_REQUEST)
            self.__networkManager.sendPacket(serverData["ServerIP"],
                                             serverData["ServerPort"], p)
            # Everything went fine
            p = self.__webPacketHandler.createCommandExecutedPacket(
                data["CommandID"])
        except Exception as e:
            p = self.__webPacketHandler.createVMServerGenericErrorPacket(
                WEB_PACKET_T.VM_SERVER_BOOTUP_ERROR, serverNameOrIPAddress,
                str(e), data["CommandID"])
        self.__networkManager.sendPacket('', self.__webPort, p)

    def __sendVMServerStatusData(self):
        """
        Processes a virtual machine server data request packet.
        Args:
            None
        Returns:
            Nothing
        """
        self.__sendStatusData(
            self.__dbConnector.getVMServerBasicData,
            self.__webPacketHandler.createVMServerStatusPacket)

    def __sendVMDistributionData(self):
        """
        Processes a virtual machine distribution data packet
        Args:
            None
        Returns:
            Nothing
        """
        self.__sendStatusData(
            self.__dbConnector.getHostedImages,
            self.__webPacketHandler.createVMDistributionPacket)

    def __sendStatusData(self, queryMethod, packetCreationMethod):
        """
        Processes a data request packet.
        Args:
            queryMethod: the method that extracts the data from the database
            packetCreationMethod: the method that creates the packet
        Returns:
            Nothing
        """
        segmentSize = 5
        outgoingData = []
        serverIDs = self.__dbConnector.getVMServerIDs()
        if (len(serverIDs) == 0):
            segmentCounter = 0
            segmentNumber = 0
            sendLastSegment = True
        else:
            segmentCounter = 1
            segmentNumber = (len(serverIDs) / segmentSize)
            if (len(serverIDs) % segmentSize != 0):
                segmentNumber += 1
                sendLastSegment = True
            else:
                sendLastSegment = False
        for serverID in serverIDs:
            row = queryMethod(serverID)
            if (isinstance(row, dict)):
                outgoingData.append(row)
            else:
                outgoingData += row
            if (len(outgoingData) >= segmentSize):
                # Flush
                packet = packetCreationMethod(segmentCounter, segmentNumber,
                                              outgoingData)
                self.__networkManager.sendPacket('', self.__webPort, packet)
                outgoingData = []
                segmentCounter += 1
        # Send the last segment
        if (sendLastSegment):
            packet = packetCreationMethod(segmentCounter, segmentNumber,
                                          outgoingData)
            self.__networkManager.sendPacket('', self.__webPort, packet)

    def __bootUpVM(self, data):
        """
        Processes a virtual machine boot request packet.
        Args:
            vmName: the virtual machine's ID
            userID: the user's unique identifier
        Returns:
            Nothing
        """
        vmID = data["VMID"]
        userID = data["UserID"]
        # Choose the virtual machine server that will host the image
        (serverID, errorMessage) = self.__loadBalancer.assignVMServer(vmID)
        if (errorMessage != None):
            # Something went wrong => warn the user
            p = self.__webPacketHandler.createVMBootFailurePacket(
                vmID, errorMessage, data["CommandID"])
            self.__networkManager.sendPacket('', self.__webPort, p)
        else:

            # Ask the virtual machine server to boot up the VM
            p = self.__vmServerPacketHandler.createVMBootPacket(
                vmID, userID, data["CommandID"])
            serverData = self.__dbConnector.getVMServerBasicData(serverID)
            self.__networkManager.sendPacket(serverData["ServerIP"],
                                             serverData["ServerPort"], p)
            # Register the boot command
            self.__dbConnector.registerVMBootCommand(data["CommandID"],
                                                     data["VMID"])
            # Everything went fine
            #p = self.__webPacketHandler.createCommandExecutedPacket(data["CommandID"])
            #self.__networkManager.sendPacket('', self.__webPort, p)

    def processVMServerIncomingPacket(self, packet):
        """
        Processes a packet sent from a virtual machine server.
        Args:
            packet: the packet to process
        Returns:
            Nothing
        """
        data = self.__vmServerPacketHandler.readPacket(packet)
        if (data["packet_type"] == VMSRVR_PACKET_T.SERVER_STATUS):
            self.__updateVMServerStatus(data)
        elif (data["packet_type"] == VMSRVR_PACKET_T.DOMAIN_CONNECTION_DATA):
            self.__sendVMConnectionData(data)
        elif (data["packet_type"] == VMSRVR_PACKET_T.ACTIVE_VM_DATA):
            self.__sendVNCConnectionData(packet)

    def processServerReconnectionData(self, ipAddress, reconnection_status):
        """
        Processes a reconnection status event
        Args:
            ipAddress: the connection's IPv4 address
            port: the connection's port
            reconnection_status: the reconnection process' status
        Returns:
            Nothing
        """
        if (reconnection_status == RECONNECTION_T.RECONNECTING):
            status = SERVER_STATE_T.RECONNECTING
        elif (reconnection_status == RECONNECTION_T.REESTABLISHED):
            status = SERVER_STATE_T.READY
        else:
            status = SERVER_STATE_T.CONNECTION_TIMED_OUT

        serverID = self.__dbConnector.getVMServerID(ipAddress)
        self.__dbConnector.updateVMServerStatus(serverID, status)

    def __sendVNCConnectionData(self, packet):
        """
        Processes a VNC connection data packet
        Args:
            packet: the packet to process
        Returns:
            Nothing
        """
        p = self.__webPacketHandler.createActiveVMsDataPacket(packet)
        self.__networkManager.sendPacket('', self.__webPort, p)

    def __sendVMConnectionData(self, data):
        """
        Processes a virtual machine connection data packet.
        Args:
            data: the packet to process' data.
        Returns:
            Nothing
        """
        # Delete the boot command from the database
        self.__dbConnector.removeVMBootCommand(data["CommandID"])
        p = self.__webPacketHandler.createVMConnectionDataPacket(
            data["VNCServerIP"], data["VNCServerPort"],
            data["VNCServerPassword"], data["CommandID"])
        self.__networkManager.sendPacket('', self.__webPort, p)

    def monitorVMBootCommands(self):
        errorMessage = "Could not boot up virtual machine (timeout error)"
        while not self.__finished:
            data = self.__dbConnector.getOldVMBootCommandID(self.__timeout)
            if (not self.__finished and data == None):
                # Be careful with this: if MySQL server receives too many queries per second,
                # the reactor's database connection will be closed.
                sleep(1)
            else:
                # Create a virtual machine boot error packet
                p = self.__webPacketHandler.createVMBootFailurePacket(
                    data[1], errorMessage, data[0])
                self.__networkManager.sendPacket('', self.__webPort, p)

    def hasFinished(self):
        """
        Indicates if the cluster server has finished or not.
        """
        return self.__finished

    def shutdown(self):
        """
        Shuts down the cluster server.
        Args:
            None
        Returns:
            Nothing
        @attention: this method MUST NOT be called from a network thread. If you do so, the application will hang!
        """
        self.__networkManager.stopNetworkService()
Beispiel #8
0
    Para realizarse la prueba debe ejecutarse este módulo el número de clientes 
     que se desee
    Testeado con server 03
'''
import time
from network.manager.networkManager import NetworkManager
from network.tests.dummyCallback import DummyCallback
from time import sleep

if __name__ == "__main__":

    # Fase 1 :Creamos y conectamos a los clientes
    #Cliente 1
    client1 = NetworkManager()
    client1.startNetworkService()
    client1.connectTo('127.0.0.1', 8080, 20, DummyCallback())

    while (not client1.isConnectionReady('127.0.0.1', 8080)):
        sleep(0.1)
    #Fase 2: Cada cliente envia su paquete
    #Cliente 1
    p1 = client1.createPacket(0)
    p1.writeString("I am Client " + str(time.localtime()[3]) + ":" +
                   str(time.localtime()[4]) + ":" + str(time.localtime()[5]))
    client1.sendPacket('127.0.0.1', 8080, p1)

    #Fase 3: Esperamos y cerramos las conexiones
    sleep(200)
    #Cliente 1
    client1.stopNetworkService()
class WebServerEndpoint(object):  
    """
    These objects communicate a cluster server and the web connectors.
    """    
    def __init__(self):
        """
        Initializes the endpoint's state
        Args:
            None
        """
        self.__stopped = False
    
    def connectToDatabases(self, mysqlRootsPassword, statusDBName, commandsDBName, statusdbSQLFilePath, commandsDBSQLFilePath,
                           websiteUser, websiteUserPassword, endpointUser, endpointUserPassword):
        """
        Establishes a connection with the system status database.
        Args:
            mysqlRootsPassword: MySQL root's password
            statusDBName: the status database name
            statusdbSQLFilePath: the database schema definition SQL file path
            websiteUser: the website user's name. 
            websiteUserPassword: the website user's password
            endpointUser: the update user's name. This user will have ALL privileges on the status database.
            endpointUserPassword: the update user's password.
        """        
        # Create the status database
        self.__rootsPassword = mysqlRootsPassword
        self.__statusDatabaseName = statusDBName
        self.__commandsDatabaseName = commandsDBName
        configurator = DBConfigurator(mysqlRootsPassword)
        configurator.runSQLScript(statusDBName, statusdbSQLFilePath)
        configurator.runSQLScript(commandsDBName, commandsDBSQLFilePath)
        # Register the website and the endpoint users
        configurator.addUser(websiteUser, websiteUserPassword, statusDBName, False)
        configurator.addUser(endpointUser, endpointUserPassword, statusDBName, True)
        configurator.addUser(websiteUser, websiteUserPassword, commandsDBName, True)
        configurator.addUser(endpointUser, endpointUserPassword, commandsDBName, True)
        # Create the database connectors
        self.__commandsDBConnector = CommandsDatabaseConnector(endpointUser, endpointUserPassword, 
                                                               commandsDBName, 1) 
        self.__writer = SystemStatusDatabaseWriter(endpointUser, endpointUserPassword, statusDBName)
        # Connect to the database
        self.__writer.connect()
        self.__commandsDBConnector.connect()
        
    def connectToClusterServer(self, certificatePath, clusterServerIP, clusterServerListenningPort, statusDBUpdateInterval):
        """
        Establishes a connection with the cluster server.
        Args:
            certificatePath: the server.crt and server.key directory path.
            clusterServerIP: the cluster server's IPv4 address
            clusterServerListenningPort: the cluster server's listenning port.
            statusDBUpdateInterval: the status database update interval (in seconds)
        Returns:
            Nothing
        """
        self.__manager = NetworkManager(certificatePath)
        self.__manager.startNetworkService()
        callback = _ClusterServerEndpointCallback(self)
        # Connect to the main server
        self.__clusterServerIP = clusterServerIP
        self.__clusterServerPort = clusterServerListenningPort
        self.__manager.connectTo(clusterServerIP, clusterServerListenningPort, 5, callback, True)
        while (not self.__manager.isConnectionReady(clusterServerIP, clusterServerListenningPort)) :
            sleep(0.1)
        # Create the packet handler
        self.__pHandler = ClusterServerPacketHandler(self.__manager)
        # Create the update thread
        self.__updateRequestThread = StatusDatabaseUpdateThread(_ClusterServerEndpointUpdateHandler(self), statusDBUpdateInterval)
        # Start it
        self.__updateRequestThread.start()
        
    def disconnectFromClusterServer(self):
        """
        Closes the connection with the cluster server and drops the databases.
        Args:
            haltVMServers: if True, the virtual machine servers will be halted immediately. If false, they will be
            halted until all their virtual machines have been shut down.
        Returns:
            Nothing
        @attention: DO NOT call this method inside a network thread (i.e. inside the web callback). If you do so,
        your application will hang.
        """
        # Send a halt packet to the cluster server
        p = self.__pHandler.createHaltPacket(self.__haltVMServers)
        self.__manager.sendPacket(self.__clusterServerIP, self.__clusterServerPort, p)
        # Stop the update request thread
        self.__updateRequestThread.stop()
        # Close the database connections
        self.__commandsDBConnector.disconnect()
        self.__writer.disconnect()
        # Stop the network service
        self.__manager.stopNetworkService()
        # Delete the status database
        dbConfigurator = DBConfigurator(self.__rootsPassword)
        dbConfigurator.dropDatabase(self.__statusDatabaseName)
        dbConfigurator.dropDatabase(self.__commandsDatabaseName)    
        
    def _processIncomingPacket(self, packet):
        """
        Processes an incoming package (sent from the cluster server).
        Args:
            packet: the packet to process
        Returns:
            Nothing
        """
        if (self.__stopped) :
            return
        data = self.__pHandler.readPacket(packet)
        if (data["packet_type"] == PACKET_T.VM_SERVERS_STATUS_DATA) :
            self.__writer.processVMServerSegment(data["Segment"], data["SequenceSize"], data["Data"])
        elif (data["packet_type"] == PACKET_T.VM_DISTRIBUTION_DATA) :
            self.__writer.processVMDistributionSegment(data["Segment"], data["SequenceSize"], data["Data"])
        elif (data["packet_type"] == PACKET_T.ACTIVE_VM_DATA) :
            self.__writer.processActiveVMSegment(data["Segment"], data["SequenceSize"], data["VMServerIP"], data["Data"])
        else :
            l = data["CommandID"].split("|")
            commandID = (int(l[0]), float(l[1]))
            if (data["packet_type"] == PACKET_T.COMMAND_EXECUTED) :
                self.__commandsDBConnector.removeExecutedCommand(commandID)
            else :           
                # Command outputs => serialize and add them to the commands database 
                if (data["packet_type"] == PACKET_T.VM_SERVER_BOOTUP_ERROR or 
                    data["packet_type"] == PACKET_T.VM_SERVER_UNREGISTRATION_ERROR or 
                    data["packet_type"] == PACKET_T.VM_SERVER_SHUTDOWN_ERROR) :
                    (outputType, outputContent) = CommandsHandler.createVMServerGenericErrorOutput(
                        data["packet_type"], data["ServerNameOrIPAddress"], data["ErrorMessage"])
                elif (data["packet_type"] == PACKET_T.VM_SERVER_REGISTRATION_ERROR) :
                    (outputType, outputContent) = CommandsHandler.createVMServerRegistrationErrorOutput(
                        data["VMServerIP"], data["VMServerPort"], data["VMServerName"], data["ErrorMessage"])
                elif (data["packet_type"] == PACKET_T.VM_BOOT_FAILURE) :
                    (outputType, outputContent) = CommandsHandler.createVMBootFailureErrorOutput(
                        data["VMID"], data["ErrorMessage"])  
                elif (data["packet_type"] == PACKET_T.VM_CONNECTION_DATA) :
                    (outputType, outputContent) = CommandsHandler.createVMConnectionDataOutput(
                        data["VNCServerIPAddress"], data["VNCServerPort"], data["VNCServerPassword"])
                self.__commandsDBConnector.addCommandOutput(commandID, outputType, outputContent)
                
    def processCommands(self):
        """
        Processes the commands sent from the web connectors.
        Args:
            None
        Returns:
            Nothing
        @attention: This method will only finish after processing a halt command.
        """
        while not self.__stopped :
            commandData = self.__commandsDBConnector.popCommand()
            if (commandData == None) :
                sleep(0.1)
            else :
                (commandID, commandType, commandArgs) = commandData
                parsedArgs = CommandsHandler.deserializeCommandArgs(commandType, commandArgs)
                if (commandType != COMMAND_TYPE.HALT) :
                    serializedCommandID = "{0}|{1}".format(commandID[0], commandID[1])                    
                    if (commandType == COMMAND_TYPE.BOOTUP_VM_SERVER) :                    
                        packet = self.__pHandler.createVMServerBootUpPacket(parsedArgs["VMServerNameOrIP"], serializedCommandID)
                    elif (commandType == COMMAND_TYPE.REGISTER_VM_SERVER) :
                        packet = self.__pHandler.createVMServerRegistrationPacket(parsedArgs["VMServerIP"], 
                            parsedArgs["VMServerPort"], parsedArgs["VMServerName"], serializedCommandID)
                    elif (commandType == COMMAND_TYPE.UNREGISTER_OR_SHUTDOWN_VM_SERVER) :
                        packet = self.__pHandler.createVMServerUnregistrationOrShutdownPacket(parsedArgs["VMServerNameOrIP"], 
                            parsedArgs["Halt"], parsedArgs["Unregister"], serializedCommandID)
                    elif (commandType == COMMAND_TYPE.VM_BOOT_REQUEST) :
                        packet = self.__pHandler.createVMBootRequestPacket(parsedArgs["VMID"], parsedArgs["UserID"], serializedCommandID)
                    self.__manager.sendPacket(self.__clusterServerIP, self.__clusterServerPort, packet)
                else :
                    self.__stopped = True
                    self.__haltVMServers = parsedArgs["HaltVMServers"]
    
    def _sendUpdateRequestPackets(self):
        """
        Sends the update request packets to the cluster server.
        Args:
            None
        Returns:
            Nothing
        """
        if (self.__stopped) :
            return
        # Send some update request packets to the cluster server
        p = self.__pHandler.createDataRequestPacket(PACKET_T.QUERY_VM_SERVERS_STATUS)
        self.__manager.sendPacket(self.__clusterServerIP, self.__clusterServerPort, p)        
        p = self.__pHandler.createDataRequestPacket(PACKET_T.QUERY_VM_DISTRIBUTION)
        self.__manager.sendPacket(self.__clusterServerIP, self.__clusterServerPort, p)
        p = self.__pHandler.createDataRequestPacket(PACKET_T.QUERY_ACTIVE_VM_DATA)
        self.__manager.sendPacket(self.__clusterServerIP, self.__clusterServerPort, p)
class WebServerEndpoint(object):  
    """
    Estos objetos comunican un servidor de cluster con la web
    """    
    def __init__(self):
        """
        Inicializa el estado del endpoint
        Argumentos:
            Ninguno
        """
        self.__stopped = False
    
    def connectToDatabases(self, mysqlRootsPassword, statusDBName, commandsDBName, statusdbSQLFilePath, commandsDBSQLFilePath,
                           websiteUser, websiteUserPassword, endpointUser, endpointUserPassword, minCommandInterval):
        """
        Establece la conexión con la base de datos de estado y con la base de datos de comandos.
        Argumentos:
            mysqlRootsPassword: la contraseña de root de MySQL
            statusDBName: el nombre de la base de datos de estado
            statusdbSQLFilePath: la ruta del script que crea la base de datos de estado
            websiteUser: nombre de usuario que usará la web para manipular las bases de datos
            websiteUserPassword: contraseña del usuario de la web
            endpointUser: usuario que utilizará en eldpoint para manipular las bases de datos de estado. Será el único
            que puede escribir en la base de datos de estado.
            endpointUserPassword: contraseña del usuario del endpoint
        """        
        # Crear las bases de datos
        self.__rootsPassword = mysqlRootsPassword
        self.__statusDatabaseName = statusDBName
        self.__commandsDatabaseName = commandsDBName
        configurator = DBConfigurator(mysqlRootsPassword)
        configurator.runSQLScript(statusDBName, statusdbSQLFilePath)
        configurator.runSQLScript(commandsDBName, commandsDBSQLFilePath)
        # Registrar en ellas los usuarios
        configurator.addUser(websiteUser, websiteUserPassword, statusDBName, False)
        configurator.addUser(endpointUser, endpointUserPassword, statusDBName, True)
        configurator.addUser(websiteUser, websiteUserPassword, commandsDBName, True)
        configurator.addUser(endpointUser, endpointUserPassword, commandsDBName, True)
        # Crear los conectores
        self.__commandsDBConnector = CommandsDatabaseConnector(endpointUser, endpointUserPassword, 
                                                               commandsDBName, minCommandInterval) 
        self.__endpointDBConnector = ClusterEndpointDBConnector(endpointUser, endpointUserPassword, statusDBName)
        
    def connectToClusterServer(self, certificatePath, clusterServerIP, clusterServerListenningPort, statusDBUpdateInterval,
                               commandTimeout, commandTimeoutCheckInterval):
        """
        Establece la conexión con el servidor de cluster
        Argumentos:
            certificatePath: la ruta del directorio con los ficheros server.crt y server.key.
            clusterServerIP: la IP del servidor de cluster
            clusterServerListenningPort: el puerto en el que escucha el servidor de cluster
            statusDBUpdateInterval: el periodo de actualización de la base de datos (en segundos)
        Devuelve:
            Nada
        Lanza:
            EnpointException: se lanza cuando no se puede establecer una conexión con el servidor web
        """
        self.__manager = NetworkManager(certificatePath)
        self.__manager.startNetworkService()
        callback = _ClusterServerEndpointCallback(self)
        # Establecer la conexión
        self.__clusterServerIP = clusterServerIP
        self.__clusterServerPort = clusterServerListenningPort
        try :
            self.__manager.connectTo(clusterServerIP, clusterServerListenningPort, 5, callback, True)
            while (not self.__manager.isConnectionReady(clusterServerIP, clusterServerListenningPort)) :
                sleep(0.1)
                    
            # TODO: si esto falla, terminar.
            # Preparar la recepción de paquetes y la actualización automática de la base de datos de estado
            self.__repositoryPacketHandler = ClusterServerPacketHandler(self.__manager)
            
            self.__updateRequestThread = VMServerMonitoringThread(_ClusterServerEndpointUpdateHandler(self), statusDBUpdateInterval)
            self.__updateRequestThread.start()            
            self.__commandMonitoringThread = CommandMonitoringThread(self.__commandsDBConnector, commandTimeout, commandTimeoutCheckInterval)
            self.__commandMonitoringThread.start()
        except NetworkManagerException as e :
            raise EndpointException(e.message)
        
    def disconnectFromClusterServer(self):
        """
        Cierra la conexión con el servidor de cluster y borra las bases de datos de estado
        Argumentos:
            Ninguno
        Devuelve:
            Nada
        @attention: Este método debe llamarse desde el hilo principal para evitar cuelgues
        """
        # Apagar el servidor de cluster
        p = self.__repositoryPacketHandler.createHaltPacket(self.__haltVMServers)
        errorMessage = self.__manager.sendPacket(self.__clusterServerIP, self.__clusterServerPort, p)
        NetworkManager.printConnectionWarningIfNecessary(self.__clusterServerIP, self.__clusterServerPort, "Cluster server halt", 
                                                         errorMessage)
        # Dejar de actualizar las bases de datos
        self.__updateRequestThread.stop()
        
        # Dejar de monitorizar los comandos
        self.__commandMonitoringThread.stop()
        
        # Cerrar las conexiones con las bases de datos
        self.closeNetworkAndDBConnections()
        
    def closeNetworkAndDBConnections(self):
        """
        Cierra las conexiones con las bases de datos
        Argumentos:
            Ninguno
        Devuelve:
            Nada
        """
        # Detener el servicio de red
        self.__manager.stopNetworkService()
        # Borrar las bases de datos de comandos y de estado
        dbConfigurator = DBConfigurator(self.__rootsPassword)
        dbConfigurator.dropDatabase(self.__commandsDatabaseName)    
        
    def _processIncomingPacket(self, packet):
        """
        Procesa un paquete enviado desde el servidor de cluster
        Argumentos:
            packet: el paquete a procesar
        Devuelve:
            Nada
        """
        if (self.__stopped) :
            return
        data = self.__repositoryPacketHandler.readPacket(packet)
        if (data["packet_type"] == PACKET_T.VM_SERVERS_STATUS_DATA) :
            self.__endpointDBConnector.processVMServerSegment(data["Segment"], data["SequenceSize"], data["Data"])
        elif (data["packet_type"] == PACKET_T.VM_DISTRIBUTION_DATA) :
            self.__endpointDBConnector.processVMDistributionSegment(data["Segment"], data["SequenceSize"], data["Data"])
        elif (data["packet_type"] == PACKET_T.ACTIVE_VM_DATA) :
            self.__endpointDBConnector.processActiveVMSegment(data["Segment"], data["SequenceSize"], data["VMServerIP"], data["Data"])
        else :
            l = data["CommandID"].split("|")
            commandID = (int(l[0]), float(l[1]))
            if (data["packet_type"] == PACKET_T.COMMAND_EXECUTED) :
                self.__commandsDBConnector.removeExecutedCommand(commandID)
            else :           
                # El resto de paquetes contienen el resultado de ejecutar comandos => los serializamos y los añadimos
                # a la base de datos de comandos para que los conectores se enteren
                if (data["packet_type"] == PACKET_T.VM_SERVER_BOOTUP_ERROR or 
                    data["packet_type"] == PACKET_T.VM_SERVER_UNREGISTRATION_ERROR or 
                    data["packet_type"] == PACKET_T.VM_SERVER_SHUTDOWN_ERROR) :
                    (outputType, outputContent) = CommandsHandler.createVMServerGenericErrorOutput(
                        data["packet_type"], data["ServerNameOrIPAddress"], data["ErrorMessage"])
                elif (data["packet_type"] == PACKET_T.VM_SERVER_REGISTRATION_ERROR) :
                    (outputType, outputContent) = CommandsHandler.createVMServerRegistrationErrorOutput(
                        data["VMServerIP"], data["VMServerPort"], data["VMServerName"], data["ErrorMessage"])
                elif (data["packet_type"] == PACKET_T.VM_BOOT_FAILURE) :
                    (outputType, outputContent) = CommandsHandler.createVMBootFailureErrorOutput(
                        data["VMID"], data["ErrorMessage"])  
                elif (data["packet_type"] == PACKET_T.VM_CONNECTION_DATA) :
                    (outputType, outputContent) = CommandsHandler.createVMConnectionDataOutput(
                        data["VNCServerIPAddress"], data["VNCServerPort"], data["VNCServerPassword"])
                elif (data["packet_type"] == PACKET_T.DOMAIN_DESTRUCTION_ERROR):
                    (outputType, outputContent) = CommandsHandler.createDomainDestructionErrorOutput(data["ErrorMessage"])
                elif (data["packet_type"] == PACKET_T.VM_SERVER_CONFIGURATION_CHANGE_ERROR) :
                    (outputType, outputContent) = CommandsHandler.createVMServerConfigurationChangeErrorOutput(data["ErrorMessage"])
                self.__commandsDBConnector.addCommandOutput(commandID, outputType, outputContent)
                
    def processCommands(self):
        """
        Procesa los comandos enviados desde los conectores
        Argumentos:
            Ninguno
        Devuelve:
            Nada
        """
        while not self.__stopped :
            commandData = self.__commandsDBConnector.popCommand()
            if (commandData == None) :
                sleep(0.1)
            else :
                (commandID, commandType, commandArgs) = commandData
                parsedArgs = CommandsHandler.deserializeCommandArgs(commandType, commandArgs)
                if (commandType != COMMAND_TYPE.HALT) :
                    serializedCommandID = "{0}|{1}".format(commandID[0], commandID[1])                    
                    if (commandType == COMMAND_TYPE.BOOTUP_VM_SERVER) :                    
                        packet = self.__repositoryPacketHandler.createVMServerBootUpPacket(parsedArgs["VMServerNameOrIP"], serializedCommandID)
                    elif (commandType == COMMAND_TYPE.REGISTER_VM_SERVER) :
                        packet = self.__repositoryPacketHandler.createVMServerRegistrationPacket(parsedArgs["VMServerIP"], 
                            parsedArgs["VMServerPort"], parsedArgs["VMServerName"], parsedArgs["IsVanillaServer"], serializedCommandID)
                    elif (commandType == COMMAND_TYPE.UNREGISTER_OR_SHUTDOWN_VM_SERVER) :
                        packet = self.__repositoryPacketHandler.createVMServerUnregistrationOrShutdownPacket(parsedArgs["VMServerNameOrIP"], 
                            parsedArgs["Halt"], parsedArgs["Unregister"], serializedCommandID)
                    elif (commandType == COMMAND_TYPE.VM_BOOT_REQUEST) :
                        packet = self.__repositoryPacketHandler.createVMBootRequestPacket(parsedArgs["VMID"], parsedArgs["UserID"], serializedCommandID)
                    elif (commandType == COMMAND_TYPE.DESTROY_DOMAIN):
                        packet = self.__repositoryPacketHandler.createDomainDestructionPacket(parsedArgs["DomainID"], serializedCommandID)
                    elif (commandType == COMMAND_TYPE.VM_SERVER_CONFIGURATION_CHANGE) :
                        packet = self.__repositoryPacketHandler.createVMServerConfigurationChangePacket(parsedArgs["VMServerNameOrIPAddress"],  parsedArgs["NewServerName"],
                                                                                         parsedArgs["NewServerIPAddress"], parsedArgs["NewServerPort"],
                                                                                         parsedArgs["NewVanillaImageEditionBehavior"], serializedCommandID)
                    errorMessage = self.__manager.sendPacket(self.__clusterServerIP, self.__clusterServerPort, packet)
                    if (errorMessage != None) :
                        # Error al enviar el paquete => el comando no se podrá ejecutar => avisar a la web
                        (outputType, outputContent) = CommandsHandler.createConnectionErrorOutput()
                        self.__commandsDBConnector.addCommandOutput(commandID, outputType, outputContent)
                        
                else :
                    self.__stopped = True
                    self.__haltVMServers = parsedArgs["HaltVMServers"]
    
    def _sendUpdateRequestPackets(self):
        """
        Solicita información de estado al serividor de cluster
        Argumentos:
            Ninguno
        Devuelve:
            Nada
        """
        if (self.__stopped) :
            return
        # Enviamos paquetes para obtener los tres tipos de información que necesitamos para actualizar la base de datos de estado
        p = self.__repositoryPacketHandler.createDataRequestPacket(PACKET_T.QUERY_VM_SERVERS_STATUS)
        errorMessage = self.__manager.sendPacket(self.__clusterServerIP, self.__clusterServerPort, p)          
        NetworkManager.printConnectionWarningIfNecessary(self.__clusterServerIP, self.__clusterServerPort, "Virtual machine servers status", errorMessage)     
        
        p = self.__repositoryPacketHandler.createDataRequestPacket(PACKET_T.QUERY_VM_DISTRIBUTION)
        errorMessage = self.__manager.sendPacket(self.__clusterServerIP, self.__clusterServerPort, p)        
        NetworkManager.printConnectionWarningIfNecessary(self.__clusterServerIP, self.__clusterServerPort, "Virtual machine distribution", errorMessage)
        
        p = self.__repositoryPacketHandler.createDataRequestPacket(PACKET_T.QUERY_ACTIVE_VM_DATA)
        errorMessage = self.__manager.sendPacket(self.__clusterServerIP, self.__clusterServerPort, p)
        NetworkManager.printConnectionWarningIfNecessary(self.__clusterServerIP, self.__clusterServerPort, "Active virtual machines data", errorMessage)
class ClusterServerMainReactor(object):
    '''
    This class is associated with the cluster server main reactor.
    '''
    def __init__(self, loadBalancerSettings, averageCompressionRatio, timeout):
        """
        Initializes the reactor's state
        Args:
            loadBalancerSettings: a list containing the load balancing algorithm settings
            averageCompressionRation: the compression algorithm's average compression ratio
            timeout: the virtual machine boot commands timeout
        """
        self.__exit = False
        self.__loadBalancerSettings = loadBalancerSettings
        self.__averageCompressionRatio = averageCompressionRatio
        self.__timeout = timeout
        self.__statusMonitoringThread = None

    def connectToDatabase(self, mysqlRootsPassword, dbName, dbUser, dbPassword,
                          scriptPath):
        """
        Establishes a connection with the cluster server database
        Args:
            mysqlRootsPassword: the MySQL root user's password
            dbName: a database name
            dbUser: a MySQL user name
            dbPassword: the user's password
            scriptPath: the schema definition script
        Returns:
            Nothing
        """
        configurator = DBConfigurator(mysqlRootsPassword)
        configurator.runSQLScript(dbName, scriptPath, "root",
                                  mysqlRootsPassword)
        configurator.addUser(dbUser, dbPassword, dbName, True)
        self.__dbConnector = ClusterServerDatabaseConnector(
            dbUser, dbPassword, dbName)
        self.__dbConnector.initializeVMServersStatus()

    def startListenning(self, useSSL, certificatePath, listenningPort,
                        repositoryIP, repositoryPort,
                        vmServerStatusUpdateInterval):
        """
        Creates the control connection
        Args:
            useSSL: indicates if SSL encryption must be used in the control connection or not
            certificatePath: the directory where the server.crt and server.key files are
            listenningPort: the control connection's port
            repositoryIP: the image repository IP address
            repositoryPort: the image repository's port
            vmServerStatusUpdateInterval: the virtual machine server status database interval
        Returns:
            Nothing
        """

        self.__networkManager = NetworkManager(certificatePath)
        self.__networkManager.startNetworkService()
        self.__listenningPort = listenningPort
        self.__packetHandler = ClusterServerPacketHandler(
            self.__networkManager)
        self.__useSSL = useSSL
        imageRepositoryPacketHandler = ImageRepositoryPacketHandler(
            self.__networkManager)
        vmServerPacketHandler = VMServerPacketHandler(self.__networkManager)
        networkEventsReactor = NetworkEventsReactor(self.__dbConnector,
                                                    repositoryIP,
                                                    repositoryPort)

        imageRepositoryPacketReactor = ImageRepositoryPacketReactor(
            self.__dbConnector, self.__networkManager, listenningPort,
            repositoryIP, repositoryPort, self.__packetHandler,
            vmServerPacketHandler, imageRepositoryPacketHandler)
        try:
            imageRepositoryCallback = ImageRepositoryCallback(
                imageRepositoryPacketReactor, networkEventsReactor)
            self.__networkManager.connectTo(repositoryIP, repositoryPort, 10,
                                            imageRepositoryCallback,
                                            self.__useSSL, True)
            self.__dbConnector.addImageRepository(repositoryIP, repositoryPort,
                                                  SERVER_STATE_T.READY)
        except Exception as e:
            print "Can't connect to the image repository: " + e.message
            self.__exit = True
            return

        vmServerPacketReactor = VMServerPacketReactor(self.__dbConnector,
                                                      self.__networkManager,
                                                      listenningPort,
                                                      vmServerPacketHandler,
                                                      self.__packetHandler)

        self.__endpointPacketReactor = EndpointPacketReactor(
            self.__dbConnector, self.__networkManager, vmServerPacketHandler,
            self.__packetHandler, imageRepositoryPacketHandler,
            VMServerCallback(vmServerPacketReactor,
                             networkEventsReactor), listenningPort,
            repositoryIP, repositoryPort, self.__loadBalancerSettings,
            self.__averageCompressionRatio, self.__useSSL)
        clusterEndpointCallback = ClusterEndpointCallback(
            self.__endpointPacketReactor)
        self.__networkManager.listenIn(listenningPort, clusterEndpointCallback,
                                       self.__useSSL)

        self.__statusMonitoringThread = ClusterStatusMonitoringThread(
            vmServerStatusUpdateInterval, self.__dbConnector,
            self.__networkManager, repositoryIP, repositoryPort,
            vmServerPacketHandler, imageRepositoryPacketHandler)
        self.__statusMonitoringThread.start()

    def monitorVMBootCommands(self):
        """
        Deletes the timed out virtual machine boot commands
        Args:
            None
        Returns:
            Nothing
        """

        while not self.__exit and not self.__endpointPacketReactor.hasFinished(
        ):
            data = self.__dbConnector.getOldVMBootCommandID(self.__timeout)
            if (data == None):
                sleep(1)
            else:
                self.__dbConnector.deleteActiveVMLocation(data[0])
                p = self.__packetHandler.createErrorPacket(
                    CLUSTER_SERVER_PACKET_T.VM_BOOT_FAILURE,
                    ERROR_DESC_T.CLSRVR_VM_BOOT_TIMEOUT, data[0])
                self.__networkManager.sendPacket('', self.__listenningPort, p)

    def closeNetworkConnections(self):
        """
        Closes the control connection
        Args:
            None
        Returns:
            Nothing
        """
        if (self.__statusMonitoringThread != None):
            self.__statusMonitoringThread.stop()
        self.__networkManager.stopNetworkService()
class ClusterServerMainReactor(object):
    '''
    This class is associated with the cluster server main reactor.
    '''
    def __init__(self, loadBalancerSettings, averageCompressionRatio, timeout):
        """
        Initializes the reactor's state
        Args:
            loadBalancerSettings: a list containing the load balancing algorithm settings
            averageCompressionRation: the compression algorithm's average compression ratio
            timeout: the virtual machine boot commands timeout
        """        
        self.__exit = False
        self.__loadBalancerSettings = loadBalancerSettings
        self.__averageCompressionRatio = averageCompressionRatio
        self.__timeout = timeout
        self.__statusMonitoringThread = None
        
    def connectToDatabase(self, mysqlRootsPassword, dbName, dbUser, dbPassword, scriptPath):
        """
        Establishes a connection with the cluster server database
        Args:
            mysqlRootsPassword: the MySQL root user's password
            dbName: a database name
            dbUser: a MySQL user name
            dbPassword: the user's password
            scriptPath: the schema definition script
        Returns:
            Nothing
        """
        configurator = DBConfigurator(mysqlRootsPassword)
        configurator.runSQLScript(dbName, scriptPath, "root", mysqlRootsPassword) 
        configurator.addUser(dbUser, dbPassword, dbName, True)
        self.__dbConnector = ClusterServerDatabaseConnector(dbUser, dbPassword, dbName)
        self.__dbConnector.initializeVMServersStatus()
        
    def startListenning(self, useSSL, certificatePath, listenningPort, repositoryIP, repositoryPort, vmServerStatusUpdateInterval):
        """
        Creates the control connection
        Args:
            useSSL: indicates if SSL encryption must be used in the control connection or not
            certificatePath: the directory where the server.crt and server.key files are
            listenningPort: the control connection's port
            repositoryIP: the image repository IP address
            repositoryPort: the image repository's port
            vmServerStatusUpdateInterval: the virtual machine server status database interval
        Returns:
            Nothing
        """            
        
        self.__networkManager = NetworkManager(certificatePath)        
        self.__networkManager.startNetworkService()    
        self.__listenningPort = listenningPort
        self.__packetHandler = ClusterServerPacketHandler(self.__networkManager)    
        self.__useSSL = useSSL
        imageRepositoryPacketHandler = ImageRepositoryPacketHandler(self.__networkManager)
        vmServerPacketHandler = VMServerPacketHandler(self.__networkManager)        
        networkEventsReactor = NetworkEventsReactor(self.__dbConnector, repositoryIP, repositoryPort)
        
        
        imageRepositoryPacketReactor = ImageRepositoryPacketReactor(self.__dbConnector, self.__networkManager,
                                                                    listenningPort, repositoryIP, repositoryPort,
                                                                    self.__packetHandler, vmServerPacketHandler, imageRepositoryPacketHandler)
        try :
            imageRepositoryCallback = ImageRepositoryCallback(imageRepositoryPacketReactor, networkEventsReactor)
            self.__networkManager.connectTo(repositoryIP, repositoryPort, 10, imageRepositoryCallback, self.__useSSL, True)
            self.__dbConnector.addImageRepository(repositoryIP, repositoryPort, SERVER_STATE_T.READY) 
        except Exception as e:
            print "Can't connect to the image repository: " + e.message
            self.__exit = True
            return        
        
        vmServerPacketReactor = VMServerPacketReactor(self.__dbConnector, self.__networkManager, listenningPort,
                                                      vmServerPacketHandler, self.__packetHandler)
        
        self.__endpointPacketReactor = EndpointPacketReactor(self.__dbConnector, self.__networkManager, 
                                                             vmServerPacketHandler, self.__packetHandler,
                                                             imageRepositoryPacketHandler,
                                                             VMServerCallback(vmServerPacketReactor, networkEventsReactor),
                                                             listenningPort, repositoryIP, repositoryPort, self.__loadBalancerSettings,
                                                             self.__averageCompressionRatio, self.__useSSL) 
        clusterEndpointCallback = ClusterEndpointCallback(self.__endpointPacketReactor)
        self.__networkManager.listenIn(listenningPort, clusterEndpointCallback, self.__useSSL)
       
        self.__statusMonitoringThread = ClusterStatusMonitoringThread(vmServerStatusUpdateInterval,
                                                                      self.__dbConnector, self.__networkManager,
                                                                      repositoryIP, repositoryPort,
                                                                      vmServerPacketHandler, imageRepositoryPacketHandler)
        self.__statusMonitoringThread.start()           
    
    def monitorVMBootCommands(self):
        """
        Deletes the timed out virtual machine boot commands
        Args:
            None
        Returns:
            Nothing
        """
        
        while not self.__exit and not self.__endpointPacketReactor.hasFinished():
            data = self.__dbConnector.getOldVMBootCommandID(self.__timeout)
            if (data == None) :               
                sleep(1) 
            else :                
                self.__dbConnector.deleteActiveVMLocation(data[0])
                p = self.__packetHandler.createErrorPacket(CLUSTER_SERVER_PACKET_T.VM_BOOT_FAILURE, ERROR_DESC_T.CLSRVR_VM_BOOT_TIMEOUT, data[0])
                self.__networkManager.sendPacket('', self.__listenningPort, p)
    
    def closeNetworkConnections(self):
        """
        Closes the control connection
        Args:
            None
        Returns:
            Nothing
        """
        if (self.__statusMonitoringThread != None) :
            self.__statusMonitoringThread.stop()
        self.__networkManager.stopNetworkService()    
Beispiel #13
0
class WebServerEndpoint(object):
    """
    These objects communicate a cluster server and the web connectors.
    """
    def __init__(self):
        """
        Initializes the endpoint's state
        Args:
            None
        """
        self.__stopped = False

    def connectToDatabases(self, mysqlRootsPassword, statusDBName,
                           commandsDBName, statusdbSQLFilePath,
                           commandsDBSQLFilePath, websiteUser,
                           websiteUserPassword, endpointUser,
                           endpointUserPassword):
        """
        Establishes a connection with the system status database.
        Args:
            mysqlRootsPassword: MySQL root's password
            statusDBName: the status database name
            statusdbSQLFilePath: the database schema definition SQL file path
            websiteUser: the website user's name. 
            websiteUserPassword: the website user's password
            endpointUser: the update user's name. This user will have ALL privileges on the status database.
            endpointUserPassword: the update user's password.
        """
        # Create the status database
        self.__rootsPassword = mysqlRootsPassword
        self.__statusDatabaseName = statusDBName
        self.__commandsDatabaseName = commandsDBName
        configurator = DBConfigurator(mysqlRootsPassword)
        configurator.runSQLScript(statusDBName, statusdbSQLFilePath)
        configurator.runSQLScript(commandsDBName, commandsDBSQLFilePath)
        # Register the website and the endpoint users
        configurator.addUser(websiteUser, websiteUserPassword, statusDBName,
                             False)
        configurator.addUser(endpointUser, endpointUserPassword, statusDBName,
                             True)
        configurator.addUser(websiteUser, websiteUserPassword, commandsDBName,
                             True)
        configurator.addUser(endpointUser, endpointUserPassword,
                             commandsDBName, True)
        # Create the database connectors
        self.__commandsDBConnector = CommandsDatabaseConnector(
            endpointUser, endpointUserPassword, commandsDBName, 1)
        self.__writer = SystemStatusDatabaseWriter(endpointUser,
                                                   endpointUserPassword,
                                                   statusDBName)
        # Connect to the database
        self.__writer.connect()
        self.__commandsDBConnector.connect()

    def connectToClusterServer(self, certificatePath, clusterServerIP,
                               clusterServerListenningPort,
                               statusDBUpdateInterval):
        """
        Establishes a connection with the cluster server.
        Args:
            certificatePath: the server.crt and server.key directory path.
            clusterServerIP: the cluster server's IPv4 address
            clusterServerListenningPort: the cluster server's listenning port.
            statusDBUpdateInterval: the status database update interval (in seconds)
        Returns:
            Nothing
        """
        self.__manager = NetworkManager(certificatePath)
        self.__manager.startNetworkService()
        callback = _ClusterServerEndpointCallback(self)
        # Connect to the main server
        self.__clusterServerIP = clusterServerIP
        self.__clusterServerPort = clusterServerListenningPort
        self.__manager.connectTo(clusterServerIP, clusterServerListenningPort,
                                 5, callback, True)
        while (not self.__manager.isConnectionReady(
                clusterServerIP, clusterServerListenningPort)):
            sleep(0.1)
        # Create the packet handler
        self.__pHandler = ClusterServerPacketHandler(self.__manager)
        # Create the update thread
        self.__updateRequestThread = StatusDatabaseUpdateThread(
            _ClusterServerEndpointUpdateHandler(self), statusDBUpdateInterval)
        # Start it
        self.__updateRequestThread.start()

    def disconnectFromClusterServer(self):
        """
        Closes the connection with the cluster server and drops the databases.
        Args:
            haltVMServers: if True, the virtual machine servers will be halted immediately. If false, they will be
            halted until all their virtual machines have been shut down.
        Returns:
            Nothing
        @attention: DO NOT call this method inside a network thread (i.e. inside the web callback). If you do so,
        your application will hang.
        """
        # Send a halt packet to the cluster server
        p = self.__pHandler.createHaltPacket(self.__haltVMServers)
        self.__manager.sendPacket(self.__clusterServerIP,
                                  self.__clusterServerPort, p)
        # Stop the update request thread
        self.__updateRequestThread.stop()
        # Close the database connections
        self.__commandsDBConnector.disconnect()
        self.__writer.disconnect()
        # Stop the network service
        self.__manager.stopNetworkService()
        # Delete the status database
        dbConfigurator = DBConfigurator(self.__rootsPassword)
        dbConfigurator.dropDatabase(self.__statusDatabaseName)
        dbConfigurator.dropDatabase(self.__commandsDatabaseName)

    def _processIncomingPacket(self, packet):
        """
        Processes an incoming package (sent from the cluster server).
        Args:
            packet: the packet to process
        Returns:
            Nothing
        """
        if (self.__stopped):
            return
        data = self.__pHandler.readPacket(packet)
        if (data["packet_type"] == PACKET_T.VM_SERVERS_STATUS_DATA):
            self.__writer.processVMServerSegment(data["Segment"],
                                                 data["SequenceSize"],
                                                 data["Data"])
        elif (data["packet_type"] == PACKET_T.VM_DISTRIBUTION_DATA):
            self.__writer.processVMDistributionSegment(data["Segment"],
                                                       data["SequenceSize"],
                                                       data["Data"])
        elif (data["packet_type"] == PACKET_T.ACTIVE_VM_DATA):
            self.__writer.processActiveVMSegment(data["Segment"],
                                                 data["SequenceSize"],
                                                 data["VMServerIP"],
                                                 data["Data"])
        else:
            l = data["CommandID"].split("|")
            commandID = (int(l[0]), float(l[1]))
            if (data["packet_type"] == PACKET_T.COMMAND_EXECUTED):
                self.__commandsDBConnector.removeExecutedCommand(commandID)
            else:
                # Command outputs => serialize and add them to the commands database
                if (data["packet_type"] == PACKET_T.VM_SERVER_BOOTUP_ERROR
                        or data["packet_type"]
                        == PACKET_T.VM_SERVER_UNREGISTRATION_ERROR
                        or data["packet_type"]
                        == PACKET_T.VM_SERVER_SHUTDOWN_ERROR):
                    (outputType, outputContent
                     ) = CommandsHandler.createVMServerGenericErrorOutput(
                         data["packet_type"], data["ServerNameOrIPAddress"],
                         data["ErrorMessage"])
                elif (data["packet_type"] ==
                      PACKET_T.VM_SERVER_REGISTRATION_ERROR):
                    (outputType, outputContent
                     ) = CommandsHandler.createVMServerRegistrationErrorOutput(
                         data["VMServerIP"], data["VMServerPort"],
                         data["VMServerName"], data["ErrorMessage"])
                elif (data["packet_type"] == PACKET_T.VM_BOOT_FAILURE):
                    (outputType, outputContent
                     ) = CommandsHandler.createVMBootFailureErrorOutput(
                         data["VMID"], data["ErrorMessage"])
                elif (data["packet_type"] == PACKET_T.VM_CONNECTION_DATA):
                    (outputType, outputContent
                     ) = CommandsHandler.createVMConnectionDataOutput(
                         data["VNCServerIPAddress"], data["VNCServerPort"],
                         data["VNCServerPassword"])
                self.__commandsDBConnector.addCommandOutput(
                    commandID, outputType, outputContent)

    def processCommands(self):
        """
        Processes the commands sent from the web connectors.
        Args:
            None
        Returns:
            Nothing
        @attention: This method will only finish after processing a halt command.
        """
        while not self.__stopped:
            commandData = self.__commandsDBConnector.popCommand()
            if (commandData == None):
                sleep(0.1)
            else:
                (commandID, commandType, commandArgs) = commandData
                parsedArgs = CommandsHandler.deserializeCommandArgs(
                    commandType, commandArgs)
                if (commandType != COMMAND_TYPE.HALT):
                    serializedCommandID = "{0}|{1}".format(
                        commandID[0], commandID[1])
                    if (commandType == COMMAND_TYPE.BOOTUP_VM_SERVER):
                        packet = self.__pHandler.createVMServerBootUpPacket(
                            parsedArgs["VMServerNameOrIP"],
                            serializedCommandID)
                    elif (commandType == COMMAND_TYPE.REGISTER_VM_SERVER):
                        packet = self.__pHandler.createVMServerRegistrationPacket(
                            parsedArgs["VMServerIP"],
                            parsedArgs["VMServerPort"],
                            parsedArgs["VMServerName"], serializedCommandID)
                    elif (commandType ==
                          COMMAND_TYPE.UNREGISTER_OR_SHUTDOWN_VM_SERVER):
                        packet = self.__pHandler.createVMServerUnregistrationOrShutdownPacket(
                            parsedArgs["VMServerNameOrIP"], parsedArgs["Halt"],
                            parsedArgs["Unregister"], serializedCommandID)
                    elif (commandType == COMMAND_TYPE.VM_BOOT_REQUEST):
                        packet = self.__pHandler.createVMBootRequestPacket(
                            parsedArgs["VMID"], parsedArgs["UserID"],
                            serializedCommandID)
                    self.__manager.sendPacket(self.__clusterServerIP,
                                              self.__clusterServerPort, packet)
                else:
                    self.__stopped = True
                    self.__haltVMServers = parsedArgs["HaltVMServers"]

    def _sendUpdateRequestPackets(self):
        """
        Sends the update request packets to the cluster server.
        Args:
            None
        Returns:
            Nothing
        """
        if (self.__stopped):
            return
        # Send some update request packets to the cluster server
        p = self.__pHandler.createDataRequestPacket(
            PACKET_T.QUERY_VM_SERVERS_STATUS)
        self.__manager.sendPacket(self.__clusterServerIP,
                                  self.__clusterServerPort, p)
        p = self.__pHandler.createDataRequestPacket(
            PACKET_T.QUERY_VM_DISTRIBUTION)
        self.__manager.sendPacket(self.__clusterServerIP,
                                  self.__clusterServerPort, p)
        p = self.__pHandler.createDataRequestPacket(
            PACKET_T.QUERY_ACTIVE_VM_DATA)
        self.__manager.sendPacket(self.__clusterServerIP,
                                  self.__clusterServerPort, p)
class ClusterEndpointEntryPoint(object):  
    """
    These objects create, run and stop the cluster endpoint daemons.
    """    
    def __init__(self):
        """
        Initializes the daemon's state
        Args:
            None
        """
        self.__commandExecutionThread = None
        self.__updateRequestThread = None
        self.__codeTranslator = SpanishCodesTranslator()
        self.__commandsHandler = CommandsHandler(self.__codeTranslator)       
    
    def connectToDatabases(self, mysqlRootsPassword, endpointDBName, commandsDBName, endpointdbSQLFilePath, commandsDBSQLFilePath,
                           websiteUser, websiteUserPassword, endpointUser, endpointUserPassword, minCommandInterval):
        """
        Establishes the connection with the two databases
        Args:
            mysqlRootsPassword: the MySQL root's password
            endpointDBName: the endpoint database's name
            endpointdbSQLFilePath: the endpoint database's schema definition file path
            websiteUser: the web application's username
            websiteUserPassword: the web application's username
            endpointUser: the endpoint daemon user's name
            endpointUserPassword: the endpoint daemon user's password
        """        
        self.__rootsPassword = mysqlRootsPassword
        self.__statusDatabaseName = endpointDBName
        self.__commandsDatabaseName = commandsDBName
        configurator = DBConfigurator(mysqlRootsPassword)
        configurator.runSQLScript(endpointDBName, endpointdbSQLFilePath, "root", mysqlRootsPassword)
        configurator.runSQLScript(commandsDBName, commandsDBSQLFilePath, "root", mysqlRootsPassword)
        
        configurator.addUser(websiteUser, websiteUserPassword, endpointDBName, False)
        configurator.addUser(endpointUser, endpointUserPassword, endpointDBName, True)
        configurator.addUser(websiteUser, websiteUserPassword, commandsDBName, True)
        configurator.addUser(endpointUser, endpointUserPassword, commandsDBName, True)
        
        self.__commandsDBConnector = CommandsDatabaseConnector(endpointUser, endpointUserPassword, 
                                                               commandsDBName, minCommandInterval) 
        self.__endpointDBConnector = ClusterEndpointDBConnector(endpointUser, endpointUserPassword, endpointDBName)
        
    def connectToClusterServer(self, useSSL, certificatePath, clusterServerIP, clusterServerListenningPort, statusDBUpdateInterval,
                               commandTimeout, commandTimeoutCheckInterval):
        """
        Establishes a connection with the cluster server
        Args:
            certificatePath: the directory where the server.crt and server.key files are
            clusterServerIP: the cluster server's IP address
            clusterServerListenningPort: the cluster server commands connection's port
            statusDBUpdateInterval: the database update interval (in seconds)
        Returns:
            Nothing
        """
        self.__networkManager = NetworkManager(certificatePath)
        self.__networkManager.startNetworkService()

        self.__clusterServerIP = clusterServerIP
        self.__clusterServerPort = clusterServerListenningPort
        self.__packetHandler = ClusterServerPacketHandler(self.__networkManager)     
        self.__commandsProcessor = CommandsProcessor(self.__commandsHandler, self.__packetHandler, self.__networkManager, 
                 self.__clusterServerIP, self.__clusterServerPort, self.__commandsDBConnector, self.__endpointDBConnector)
        packetReactor = ClusterEndpointPacketReactor(self.__codeTranslator, self.__commandsHandler, self.__packetHandler, self.__commandsProcessor,
                                                     self.__endpointDBConnector, self.__commandsDBConnector)
        try :
            self.__networkManager.connectTo(clusterServerIP, clusterServerListenningPort, 5, packetReactor, useSSL)
            while (not self.__networkManager.isConnectionReady(clusterServerIP, clusterServerListenningPort)) :
                sleep(0.1)                   
            self.__updateRequestThread = VMServerMonitoringThread(self.__packetHandler, self.__networkManager, self.__commandsProcessor, 
                                                                  self.__clusterServerIP, self.__clusterServerPort, statusDBUpdateInterval)
            self.__updateRequestThread.start()            
            self.__commandExecutionThread = CommandsMonitoringThread(self.__commandsDBConnector, commandTimeout, self.__commandsHandler, commandTimeoutCheckInterval)
            self.__commandExecutionThread.start()
        except NetworkManagerException as e :
            raise Exception(e.message)
        
    def doEmergencyStop(self):
        """
        Stops the endpoint daemon immediately
        Args:
            None
        Returns:
            Nothing
        """
        self.__networkManager.stopNetworkService()
        if (self.__updateRequestThread != None):
            self.__updateRequestThread.stop()
        if (self.__commandExecutionThread != None):
            self.__commandExecutionThread.stop()        
        
    def disconnectFromClusterServer(self):
        """
        Closes the connection with the cluster server
        Args:
            None
        Devuelve:
            Nada
        """
        p = self.__packetHandler.createHaltPacket(self.__commandsProcessor.haltVMServers())
        errorMessage = self.__networkManager.sendPacket(self.__clusterServerIP, self.__clusterServerPort, p)
        NetworkManager.printConnectionWarningIfNecessary(self.__clusterServerIP, self.__clusterServerPort, "Cluster server halt", 
                                                         errorMessage)
        self.__updateRequestThread.stop()
        
        self.__commandExecutionThread.stop()
        
        self.closeNetworkConnections()
        
    def closeNetworkConnections(self):
        """
        Closes the network connections
        Args:
            None
        Returns:
            Nothing
        """
        self.__networkManager.stopNetworkService()
        
    def processCommands(self):
        """
        Processes the users' requests
        Args:
            None
        Returns:
            Nothing
        """
        self.__commandsProcessor.processCommands()
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   
Beispiel #16
0
class ClusterEndpointEntryPoint(object):
    """
    These objects create, run and stop the cluster endpoint daemons.
    """
    def __init__(self):
        """
        Initializes the daemon's state
        Args:
            None
        """
        self.__commandExecutionThread = None
        self.__updateRequestThread = None
        self.__codeTranslator = SpanishCodesTranslator()
        self.__commandsHandler = CommandsHandler(self.__codeTranslator)

    def connectToDatabases(self, mysqlRootsPassword, endpointDBName,
                           commandsDBName, endpointdbSQLFilePath,
                           commandsDBSQLFilePath, websiteUser,
                           websiteUserPassword, endpointUser,
                           endpointUserPassword, minCommandInterval):
        """
        Establishes the connection with the two databases
        Args:
            mysqlRootsPassword: the MySQL root's password
            endpointDBName: the endpoint database's name
            endpointdbSQLFilePath: the endpoint database's schema definition file path
            websiteUser: the web application's username
            websiteUserPassword: the web application's username
            endpointUser: the endpoint daemon user's name
            endpointUserPassword: the endpoint daemon user's password
        """
        self.__rootsPassword = mysqlRootsPassword
        self.__statusDatabaseName = endpointDBName
        self.__commandsDatabaseName = commandsDBName
        configurator = DBConfigurator(mysqlRootsPassword)
        configurator.runSQLScript(endpointDBName, endpointdbSQLFilePath,
                                  "root", mysqlRootsPassword)
        configurator.runSQLScript(commandsDBName, commandsDBSQLFilePath,
                                  "root", mysqlRootsPassword)

        configurator.addUser(websiteUser, websiteUserPassword, endpointDBName,
                             False)
        configurator.addUser(endpointUser, endpointUserPassword,
                             endpointDBName, True)
        configurator.addUser(websiteUser, websiteUserPassword, commandsDBName,
                             True)
        configurator.addUser(endpointUser, endpointUserPassword,
                             commandsDBName, True)

        self.__commandsDBConnector = CommandsDatabaseConnector(
            endpointUser, endpointUserPassword, commandsDBName,
            minCommandInterval)
        self.__endpointDBConnector = ClusterEndpointDBConnector(
            endpointUser, endpointUserPassword, endpointDBName)

    def connectToClusterServer(self, useSSL, certificatePath, clusterServerIP,
                               clusterServerListenningPort,
                               statusDBUpdateInterval, commandTimeout,
                               commandTimeoutCheckInterval):
        """
        Establishes a connection with the cluster server
        Args:
            certificatePath: the directory where the server.crt and server.key files are
            clusterServerIP: the cluster server's IP address
            clusterServerListenningPort: the cluster server commands connection's port
            statusDBUpdateInterval: the database update interval (in seconds)
        Returns:
            Nothing
        """
        self.__networkManager = NetworkManager(certificatePath)
        self.__networkManager.startNetworkService()

        self.__clusterServerIP = clusterServerIP
        self.__clusterServerPort = clusterServerListenningPort
        self.__packetHandler = ClusterServerPacketHandler(
            self.__networkManager)
        self.__commandsProcessor = CommandsProcessor(
            self.__commandsHandler, self.__packetHandler,
            self.__networkManager, self.__clusterServerIP,
            self.__clusterServerPort, self.__commandsDBConnector,
            self.__endpointDBConnector)
        packetReactor = ClusterEndpointPacketReactor(
            self.__codeTranslator, self.__commandsHandler,
            self.__packetHandler, self.__commandsProcessor,
            self.__endpointDBConnector, self.__commandsDBConnector)
        try:
            self.__networkManager.connectTo(clusterServerIP,
                                            clusterServerListenningPort, 5,
                                            packetReactor, useSSL)
            while (not self.__networkManager.isConnectionReady(
                    clusterServerIP, clusterServerListenningPort)):
                sleep(0.1)
            self.__updateRequestThread = VMServerMonitoringThread(
                self.__packetHandler, self.__networkManager,
                self.__commandsProcessor, self.__clusterServerIP,
                self.__clusterServerPort, statusDBUpdateInterval)
            self.__updateRequestThread.start()
            self.__commandExecutionThread = CommandsMonitoringThread(
                self.__commandsDBConnector, commandTimeout,
                self.__commandsHandler, commandTimeoutCheckInterval)
            self.__commandExecutionThread.start()
        except NetworkManagerException as e:
            raise Exception(e.message)

    def doEmergencyStop(self):
        """
        Stops the endpoint daemon immediately
        Args:
            None
        Returns:
            Nothing
        """
        self.__networkManager.stopNetworkService()
        if (self.__updateRequestThread != None):
            self.__updateRequestThread.stop()
        if (self.__commandExecutionThread != None):
            self.__commandExecutionThread.stop()

    def disconnectFromClusterServer(self):
        """
        Closes the connection with the cluster server
        Args:
            None
        Devuelve:
            Nada
        """
        p = self.__packetHandler.createHaltPacket(
            self.__commandsProcessor.haltVMServers())
        errorMessage = self.__networkManager.sendPacket(
            self.__clusterServerIP, self.__clusterServerPort, p)
        NetworkManager.printConnectionWarningIfNecessary(
            self.__clusterServerIP, self.__clusterServerPort,
            "Cluster server halt", errorMessage)
        self.__updateRequestThread.stop()

        self.__commandExecutionThread.stop()

        self.closeNetworkConnections()

    def closeNetworkConnections(self):
        """
        Closes the network connections
        Args:
            None
        Returns:
            Nothing
        """
        self.__networkManager.stopNetworkService()

    def processCommands(self):
        """
        Processes the users' requests
        Args:
            None
        Returns:
            Nothing
        """
        self.__commandsProcessor.processCommands()
Beispiel #17
0
# -*- coding: utf8 -*-
'''
A simple client test
@author: Luis Barrios Hernández
@version: 1.0
'''

from network.manager.networkManager import NetworkManager
from network.tests.dummyCallback import DummyCallback
from time import sleep

if __name__ == "__main__" :
    networkManager = NetworkManager("/home/luis/Certificates")
    networkManager.startNetworkService()
    networkManager.connectTo('127.0.0.1', 8080, 20, DummyCallback(), True, True)
    sleep(2)
    p = networkManager.createPacket(0)
    p.writeString("Greetings from a client")    
    networkManager.sendPacket('127.0.0.1', 8080, p)
    sleep(60)
    p = networkManager.createPacket(0)
    p.writeString("Greetings from a client 1")    
    networkManager.sendPacket('127.0.0.1', 8080, p)
    sleep(200)
    networkManager.stopNetworkService()
class WebServerEndpoint(object):
    """
    Estos objetos comunican un servidor de cluster con la web
    """
    def __init__(self):
        """
        Inicializa el estado del endpoint
        Argumentos:
            Ninguno
        """
        self.__stopped = False

    def connectToDatabases(self, mysqlRootsPassword, statusDBName,
                           commandsDBName, statusdbSQLFilePath,
                           commandsDBSQLFilePath, websiteUser,
                           websiteUserPassword, endpointUser,
                           endpointUserPassword, minCommandInterval):
        """
        Establece la conexión con la base de datos de estado y con la base de datos de comandos.
        Argumentos:
            mysqlRootsPassword: la contraseña de root de MySQL
            statusDBName: el nombre de la base de datos de estado
            statusdbSQLFilePath: la ruta del script que crea la base de datos de estado
            websiteUser: nombre de usuario que usará la web para manipular las bases de datos
            websiteUserPassword: contraseña del usuario de la web
            endpointUser: usuario que utilizará en eldpoint para manipular las bases de datos de estado. Será el único
            que puede escribir en la base de datos de estado.
            endpointUserPassword: contraseña del usuario del endpoint
        """
        # Crear las bases de datos
        self.__rootsPassword = mysqlRootsPassword
        self.__statusDatabaseName = statusDBName
        self.__commandsDatabaseName = commandsDBName
        configurator = DBConfigurator(mysqlRootsPassword)
        configurator.runSQLScript(statusDBName, statusdbSQLFilePath)
        configurator.runSQLScript(commandsDBName, commandsDBSQLFilePath)
        # Registrar en ellas los usuarios
        configurator.addUser(websiteUser, websiteUserPassword, statusDBName,
                             False)
        configurator.addUser(endpointUser, endpointUserPassword, statusDBName,
                             True)
        configurator.addUser(websiteUser, websiteUserPassword, commandsDBName,
                             True)
        configurator.addUser(endpointUser, endpointUserPassword,
                             commandsDBName, True)
        # Crear los conectores
        self.__commandsDBConnector = CommandsDatabaseConnector(
            endpointUser, endpointUserPassword, commandsDBName,
            minCommandInterval)
        self.__endpointDBConnector = ClusterEndpointDBConnector(
            endpointUser, endpointUserPassword, statusDBName)

    def connectToClusterServer(self, certificatePath, clusterServerIP,
                               clusterServerListenningPort,
                               statusDBUpdateInterval, commandTimeout,
                               commandTimeoutCheckInterval):
        """
        Establece la conexión con el servidor de cluster
        Argumentos:
            certificatePath: la ruta del directorio con los ficheros server.crt y server.key.
            clusterServerIP: la IP del servidor de cluster
            clusterServerListenningPort: el puerto en el que escucha el servidor de cluster
            statusDBUpdateInterval: el periodo de actualización de la base de datos (en segundos)
        Devuelve:
            Nada
        Lanza:
            EnpointException: se lanza cuando no se puede establecer una conexión con el servidor web
        """
        self.__manager = NetworkManager(certificatePath)
        self.__manager.startNetworkService()
        callback = _ClusterServerEndpointCallback(self)
        # Establecer la conexión
        self.__clusterServerIP = clusterServerIP
        self.__clusterServerPort = clusterServerListenningPort
        try:
            self.__manager.connectTo(clusterServerIP,
                                     clusterServerListenningPort, 5, callback,
                                     True)
            while (not self.__manager.isConnectionReady(
                    clusterServerIP, clusterServerListenningPort)):
                sleep(0.1)

            # TODO: si esto falla, terminar.
            # Preparar la recepción de paquetes y la actualización automática de la base de datos de estado
            self.__repositoryPacketHandler = ClusterServerPacketHandler(
                self.__manager)

            self.__updateRequestThread = VMServerMonitoringThread(
                _ClusterServerEndpointUpdateHandler(self),
                statusDBUpdateInterval)
            self.__updateRequestThread.start()
            self.__commandMonitoringThread = CommandMonitoringThread(
                self.__commandsDBConnector, commandTimeout,
                commandTimeoutCheckInterval)
            self.__commandMonitoringThread.start()
        except NetworkManagerException as e:
            raise EndpointException(e.message)

    def disconnectFromClusterServer(self):
        """
        Cierra la conexión con el servidor de cluster y borra las bases de datos de estado
        Argumentos:
            Ninguno
        Devuelve:
            Nada
        @attention: Este método debe llamarse desde el hilo principal para evitar cuelgues
        """
        # Apagar el servidor de cluster
        p = self.__repositoryPacketHandler.createHaltPacket(
            self.__haltVMServers)
        errorMessage = self.__manager.sendPacket(self.__clusterServerIP,
                                                 self.__clusterServerPort, p)
        NetworkManager.printConnectionWarningIfNecessary(
            self.__clusterServerIP, self.__clusterServerPort,
            "Cluster server halt", errorMessage)
        # Dejar de actualizar las bases de datos
        self.__updateRequestThread.stop()

        # Dejar de monitorizar los comandos
        self.__commandMonitoringThread.stop()

        # Cerrar las conexiones con las bases de datos
        self.closeNetworkAndDBConnections()

    def closeNetworkAndDBConnections(self):
        """
        Cierra las conexiones con las bases de datos
        Argumentos:
            Ninguno
        Devuelve:
            Nada
        """
        # Detener el servicio de red
        self.__manager.stopNetworkService()
        # Borrar las bases de datos de comandos y de estado
        dbConfigurator = DBConfigurator(self.__rootsPassword)
        dbConfigurator.dropDatabase(self.__commandsDatabaseName)

    def _processIncomingPacket(self, packet):
        """
        Procesa un paquete enviado desde el servidor de cluster
        Argumentos:
            packet: el paquete a procesar
        Devuelve:
            Nada
        """
        if (self.__stopped):
            return
        data = self.__repositoryPacketHandler.readPacket(packet)
        if (data["packet_type"] == PACKET_T.VM_SERVERS_STATUS_DATA):
            self.__endpointDBConnector.processVMServerSegment(
                data["Segment"], data["SequenceSize"], data["Data"])
        elif (data["packet_type"] == PACKET_T.VM_DISTRIBUTION_DATA):
            self.__endpointDBConnector.processVMDistributionSegment(
                data["Segment"], data["SequenceSize"], data["Data"])
        elif (data["packet_type"] == PACKET_T.ACTIVE_VM_DATA):
            self.__endpointDBConnector.processActiveVMSegment(
                data["Segment"], data["SequenceSize"], data["VMServerIP"],
                data["Data"])
        else:
            l = data["CommandID"].split("|")
            commandID = (int(l[0]), float(l[1]))
            if (data["packet_type"] == PACKET_T.COMMAND_EXECUTED):
                self.__commandsDBConnector.removeExecutedCommand(commandID)
            else:
                # El resto de paquetes contienen el resultado de ejecutar comandos => los serializamos y los añadimos
                # a la base de datos de comandos para que los conectores se enteren
                if (data["packet_type"] == PACKET_T.VM_SERVER_BOOTUP_ERROR
                        or data["packet_type"]
                        == PACKET_T.VM_SERVER_UNREGISTRATION_ERROR
                        or data["packet_type"]
                        == PACKET_T.VM_SERVER_SHUTDOWN_ERROR):
                    (outputType, outputContent
                     ) = CommandsHandler.createVMServerGenericErrorOutput(
                         data["packet_type"], data["ServerNameOrIPAddress"],
                         data["ErrorMessage"])
                elif (data["packet_type"] ==
                      PACKET_T.VM_SERVER_REGISTRATION_ERROR):
                    (outputType, outputContent
                     ) = CommandsHandler.createVMServerRegistrationErrorOutput(
                         data["VMServerIP"], data["VMServerPort"],
                         data["VMServerName"], data["ErrorMessage"])
                elif (data["packet_type"] == PACKET_T.VM_BOOT_FAILURE):
                    (outputType, outputContent
                     ) = CommandsHandler.createVMBootFailureErrorOutput(
                         data["VMID"], data["ErrorMessage"])
                elif (data["packet_type"] == PACKET_T.VM_CONNECTION_DATA):
                    (outputType, outputContent
                     ) = CommandsHandler.createVMConnectionDataOutput(
                         data["VNCServerIPAddress"], data["VNCServerPort"],
                         data["VNCServerPassword"])
                elif (data["packet_type"] == PACKET_T.DOMAIN_DESTRUCTION_ERROR
                      ):
                    (outputType, outputContent
                     ) = CommandsHandler.createDomainDestructionErrorOutput(
                         data["ErrorMessage"])
                elif (data["packet_type"] ==
                      PACKET_T.VM_SERVER_CONFIGURATION_CHANGE_ERROR):
                    (
                        outputType, outputContent
                    ) = CommandsHandler.createVMServerConfigurationChangeErrorOutput(
                        data["ErrorMessage"])
                self.__commandsDBConnector.addCommandOutput(
                    commandID, outputType, outputContent)

    def processCommands(self):
        """
        Procesa los comandos enviados desde los conectores
        Argumentos:
            Ninguno
        Devuelve:
            Nada
        """
        while not self.__stopped:
            commandData = self.__commandsDBConnector.popCommand()
            if (commandData == None):
                sleep(0.1)
            else:
                (commandID, commandType, commandArgs) = commandData
                parsedArgs = CommandsHandler.deserializeCommandArgs(
                    commandType, commandArgs)
                if (commandType != COMMAND_TYPE.HALT):
                    serializedCommandID = "{0}|{1}".format(
                        commandID[0], commandID[1])
                    if (commandType == COMMAND_TYPE.BOOTUP_VM_SERVER):
                        packet = self.__repositoryPacketHandler.createVMServerBootUpPacket(
                            parsedArgs["VMServerNameOrIP"],
                            serializedCommandID)
                    elif (commandType == COMMAND_TYPE.REGISTER_VM_SERVER):
                        packet = self.__repositoryPacketHandler.createVMServerRegistrationPacket(
                            parsedArgs["VMServerIP"],
                            parsedArgs["VMServerPort"],
                            parsedArgs["VMServerName"],
                            parsedArgs["IsVanillaServer"], serializedCommandID)
                    elif (commandType ==
                          COMMAND_TYPE.UNREGISTER_OR_SHUTDOWN_VM_SERVER):
                        packet = self.__repositoryPacketHandler.createVMServerUnregistrationOrShutdownPacket(
                            parsedArgs["VMServerNameOrIP"], parsedArgs["Halt"],
                            parsedArgs["Unregister"], serializedCommandID)
                    elif (commandType == COMMAND_TYPE.VM_BOOT_REQUEST):
                        packet = self.__repositoryPacketHandler.createVMBootRequestPacket(
                            parsedArgs["VMID"], parsedArgs["UserID"],
                            serializedCommandID)
                    elif (commandType == COMMAND_TYPE.DESTROY_DOMAIN):
                        packet = self.__repositoryPacketHandler.createDomainDestructionPacket(
                            parsedArgs["DomainID"], serializedCommandID)
                    elif (commandType ==
                          COMMAND_TYPE.VM_SERVER_CONFIGURATION_CHANGE):
                        packet = self.__repositoryPacketHandler.createVMServerConfigurationChangePacket(
                            parsedArgs["VMServerNameOrIPAddress"],
                            parsedArgs["NewServerName"],
                            parsedArgs["NewServerIPAddress"],
                            parsedArgs["NewServerPort"],
                            parsedArgs["NewVanillaImageEditionBehavior"],
                            serializedCommandID)
                    errorMessage = self.__manager.sendPacket(
                        self.__clusterServerIP, self.__clusterServerPort,
                        packet)
                    if (errorMessage != None):
                        # Error al enviar el paquete => el comando no se podrá ejecutar => avisar a la web
                        (outputType, outputContent
                         ) = CommandsHandler.createConnectionErrorOutput()
                        self.__commandsDBConnector.addCommandOutput(
                            commandID, outputType, outputContent)

                else:
                    self.__stopped = True
                    self.__haltVMServers = parsedArgs["HaltVMServers"]

    def _sendUpdateRequestPackets(self):
        """
        Solicita información de estado al serividor de cluster
        Argumentos:
            Ninguno
        Devuelve:
            Nada
        """
        if (self.__stopped):
            return
        # Enviamos paquetes para obtener los tres tipos de información que necesitamos para actualizar la base de datos de estado
        p = self.__repositoryPacketHandler.createDataRequestPacket(
            PACKET_T.QUERY_VM_SERVERS_STATUS)
        errorMessage = self.__manager.sendPacket(self.__clusterServerIP,
                                                 self.__clusterServerPort, p)
        NetworkManager.printConnectionWarningIfNecessary(
            self.__clusterServerIP, self.__clusterServerPort,
            "Virtual machine servers status", errorMessage)

        p = self.__repositoryPacketHandler.createDataRequestPacket(
            PACKET_T.QUERY_VM_DISTRIBUTION)
        errorMessage = self.__manager.sendPacket(self.__clusterServerIP,
                                                 self.__clusterServerPort, p)
        NetworkManager.printConnectionWarningIfNecessary(
            self.__clusterServerIP, self.__clusterServerPort,
            "Virtual machine distribution", errorMessage)

        p = self.__repositoryPacketHandler.createDataRequestPacket(
            PACKET_T.QUERY_ACTIVE_VM_DATA)
        errorMessage = self.__manager.sendPacket(self.__clusterServerIP,
                                                 self.__clusterServerPort, p)
        NetworkManager.printConnectionWarningIfNecessary(
            self.__clusterServerIP, self.__clusterServerPort,
            "Active virtual machines data", errorMessage)