Beispiel #1
0
 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 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 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 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)
Beispiel #5
0
 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 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)
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
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()
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)
Beispiel #11
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 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)