def __init__(self, networkManager, serverListeningPort, packetHandler, transferDirectory, ftpTimeout, maxTransferAttempts, dbConnector, useSSL, max_cancel_timeout=60): """ Initializes the transfer thread's state Args: networkManager: the network manager to use serverListeningPort: the control connection's port packetHandler: the virtual machine server packet handler to use transferDirectory: the directory where the .zip files will be stored ftpTimeout: the FTP timeout (in seconds) maxTransferAttempts: the maximum number of times that a transfer will be restarted after a failure. dbConnector: a database connector useSSL: indicates wheter SSL encryption must be used when establishing the connection with the image repository or not max_cancel_timeout: unlock transfers timeout (in seconds) """ BasicThread.__init__(self, "File transfer thread") self.__networkManager = networkManager self.__serverListeningPort = serverListeningPort self.__transferDirectory = transferDirectory self.__repositoryPacketHandler = ImageRepositoryPacketHandler( self.__networkManager) self.__vmServerPacketHandler = packetHandler self.__ftpTimeout = ftpTimeout self.__maxTransferAttempts = maxTransferAttempts self.__dbConnector = dbConnector self.__max_cancel_timeout = max_cancel_timeout self.__useSSL = useSSL
def startListenning(self, networkInterface, useSSL, certificatesDirectory, commandsListenningPort, ftpListenningPort, maxConnections, maxConnectionsPerIP, uploadBandwidthRatio, downloadBandwidthRatio, ftpUsername, ftpPasswordLength): """ Boots up the FTP server and creates the control connection. Args: networkInterface: the network interface that will be used by the FTP server useSSL: indicates if SSL encryption must be used in the control connection or not certificatesDirectory: the directory where the files server.crt and server.key are commandsListenningPort: the control connection's port ftpListenningPort: the FTP server listenning port maxConnections: maximum FTP connections maxConnectionsPerIP: maximum FTP connections per IP address uploadBandwidthRatio: maximum download bandwidth fraction downloadBandwidthRatio: maximum upload bandwidth fraction ftpUsername: the FTP user that the virtual machine servers will use ftpPasswordLength: the random FTP password length Returns: Nothing @attention: The FTP password will be randomly generated at every boot. """ try : self.__maxConnections = maxConnections self.__commandsListenningPort = commandsListenningPort self.__FTPListenningPort = ftpListenningPort self.__networkManager = NetworkManager(certificatesDirectory) self.__repositoryPacketHandler = ImageRepositoryPacketHandler(self.__networkManager) self.__commandsCallback = CommandsCallback(self.__networkManager, self.__repositoryPacketHandler, commandsListenningPort, self.__dbConnector, self.__retrieveQueue, self.__storeQueue, self.__diskImagesDirectory) self.__networkManager.startNetworkService() self.__networkManager.listenIn(commandsListenningPort, self.__commandsCallback, useSSL) dataCallback = FTPServerCallback(self.__slotCounter, self.__dbConnector) self.__ftpUsername = ftpUsername self.__ftpPassword = ChildProcessManager.runCommandInForeground("openssl rand -base64 {0}".format(ftpPasswordLength), Exception) self.__ftpServer = ConfigurableFTPServer("Image repository FTP Server") self.__ftpServer.startListenning(networkInterface, ftpListenningPort, maxConnections, maxConnectionsPerIP, dataCallback, downloadBandwidthRatio, uploadBandwidthRatio) self.__ftpServer.addUser(self.__ftpUsername, self.__ftpPassword, self.__diskImagesDirectory, "eramw") except Exception as e: print "Error: " + e.message self.__finish = True
def __init__( self, networkManager, serverListeningPort, packetHandler, transferDirectory, ftpTimeout, maxTransferAttempts, dbConnector, useSSL, max_cancel_timeout=60, ): """ Initializes the transfer thread's state Args: networkManager: the network manager to use serverListeningPort: the control connection's port packetHandler: the virtual machine server packet handler to use transferDirectory: the directory where the .zip files will be stored ftpTimeout: the FTP timeout (in seconds) maxTransferAttempts: the maximum number of times that a transfer will be restarted after a failure. dbConnector: a database connector useSSL: indicates wheter SSL encryption must be used when establishing the connection with the image repository or not max_cancel_timeout: unlock transfers timeout (in seconds) """ BasicThread.__init__(self, "File transfer thread") self.__networkManager = networkManager self.__serverListeningPort = serverListeningPort self.__transferDirectory = transferDirectory self.__repositoryPacketHandler = ImageRepositoryPacketHandler(self.__networkManager) self.__vmServerPacketHandler = packetHandler self.__ftpTimeout = ftpTimeout self.__maxTransferAttempts = maxTransferAttempts self.__dbConnector = dbConnector self.__max_cancel_timeout = max_cancel_timeout self.__useSSL = useSSL
def startListenning(self, networkInterface, useSSL, certificatesDirectory, commandsListenningPort, ftpListenningPort, maxConnections, maxConnectionsPerIP, uploadBandwidthRatio, downloadBandwidthRatio, ftpUsername, ftpPasswordLength): """ Boots up the FTP server and creates the control connection. Args: networkInterface: the network interface that will be used by the FTP server useSSL: indicates if SSL encryption must be used in the control connection or not certificatesDirectory: the directory where the files server.crt and server.key are commandsListenningPort: the control connection's port ftpListenningPort: the FTP server listenning port maxConnections: maximum FTP connections maxConnectionsPerIP: maximum FTP connections per IP address uploadBandwidthRatio: maximum download bandwidth fraction downloadBandwidthRatio: maximum upload bandwidth fraction ftpUsername: the FTP user that the virtual machine servers will use ftpPasswordLength: the random FTP password length Returns: Nothing @attention: The FTP password will be randomly generated at every boot. """ try: self.__maxConnections = maxConnections self.__commandsListenningPort = commandsListenningPort self.__FTPListenningPort = ftpListenningPort self.__networkManager = NetworkManager(certificatesDirectory) self.__repositoryPacketHandler = ImageRepositoryPacketHandler( self.__networkManager) self.__commandsCallback = CommandsCallback( self.__networkManager, self.__repositoryPacketHandler, commandsListenningPort, self.__dbConnector, self.__retrieveQueue, self.__storeQueue, self.__diskImagesDirectory) self.__networkManager.startNetworkService() self.__networkManager.listenIn(commandsListenningPort, self.__commandsCallback, useSSL) dataCallback = FTPServerCallback(self.__slotCounter, self.__dbConnector) self.__ftpUsername = ftpUsername self.__ftpPassword = ChildProcessManager.runCommandInForeground( "openssl rand -base64 {0}".format(ftpPasswordLength), Exception) self.__ftpServer = ConfigurableFTPServer( "Image repository FTP Server") self.__ftpServer.startListenning(networkInterface, ftpListenningPort, maxConnections, maxConnectionsPerIP, dataCallback, downloadBandwidthRatio, uploadBandwidthRatio) self.__ftpServer.addUser(self.__ftpUsername, self.__ftpPassword, self.__diskImagesDirectory, "eramw") except Exception as e: print "Error: " + e.message self.__finish = True
class ImageRepositoryReactor(object): """ Image repository packet reactor """ def __init__(self, diskImagesDirectory): """ Initializes the reactor's state Args: diskImagesDirectory: the FTP server's root directory """ self.__diskImagesDirectory = diskImagesDirectory self.__slotCounter = MultithreadingCounter() self.__retrieveQueue = GenericThreadSafeList() self.__storeQueue = GenericThreadSafeList() self.__finish = False self.__networkManager = None self.__ftpServer = None def connectToDatabase(self, repositoryDBName, repositoryDBUser, repositoryDBPassword): """ Establishes the connection with the image repository's database. Args: repositoryDBName: a database name repositoryDBUser: an user name repositoryDBPassword: a password Returns: Nothing """ self.__dbConnector = ImageRepositoryDBConnector( repositoryDBUser, repositoryDBPassword, repositoryDBName) def startListenning(self, networkInterface, useSSL, certificatesDirectory, commandsListenningPort, ftpListenningPort, maxConnections, maxConnectionsPerIP, uploadBandwidthRatio, downloadBandwidthRatio, ftpUsername, ftpPasswordLength): """ Boots up the FTP server and creates the control connection. Args: networkInterface: the network interface that will be used by the FTP server useSSL: indicates if SSL encryption must be used in the control connection or not certificatesDirectory: the directory where the files server.crt and server.key are commandsListenningPort: the control connection's port ftpListenningPort: the FTP server listenning port maxConnections: maximum FTP connections maxConnectionsPerIP: maximum FTP connections per IP address uploadBandwidthRatio: maximum download bandwidth fraction downloadBandwidthRatio: maximum upload bandwidth fraction ftpUsername: the FTP user that the virtual machine servers will use ftpPasswordLength: the random FTP password length Returns: Nothing @attention: The FTP password will be randomly generated at every boot. """ try: self.__maxConnections = maxConnections self.__commandsListenningPort = commandsListenningPort self.__FTPListenningPort = ftpListenningPort self.__networkManager = NetworkManager(certificatesDirectory) self.__repositoryPacketHandler = ImageRepositoryPacketHandler( self.__networkManager) self.__commandsCallback = CommandsCallback( self.__networkManager, self.__repositoryPacketHandler, commandsListenningPort, self.__dbConnector, self.__retrieveQueue, self.__storeQueue, self.__diskImagesDirectory) self.__networkManager.startNetworkService() self.__networkManager.listenIn(commandsListenningPort, self.__commandsCallback, useSSL) dataCallback = FTPServerCallback(self.__slotCounter, self.__dbConnector) self.__ftpUsername = ftpUsername self.__ftpPassword = ChildProcessManager.runCommandInForeground( "openssl rand -base64 {0}".format(ftpPasswordLength), Exception) self.__ftpServer = ConfigurableFTPServer( "Image repository FTP Server") self.__ftpServer.startListenning(networkInterface, ftpListenningPort, maxConnections, maxConnectionsPerIP, dataCallback, downloadBandwidthRatio, uploadBandwidthRatio) self.__ftpServer.addUser(self.__ftpUsername, self.__ftpPassword, self.__diskImagesDirectory, "eramw") except Exception as e: print "Error: " + e.message self.__finish = True def stopListenning(self): """ Stops the FTP server and closes the control connection. Args: None Returns: Nothing """ if (self.__ftpServer != None): try: self.__ftpServer.stopListenning() except Exception: pass if (self.__networkManager != None): self.__networkManager.stopNetworkService() def initTransfers(self): """ Initializes the upload and download transfers Args: None Returns: Nothing """ store = False while not (self.__finish or self.__commandsCallback.haltReceived()): if (self.__slotCounter.read() == self.__maxConnections): # No free slots => sleep sleep(0.1) else: # There are slots => enable uploads and downloads in parallel self.__slotCounter.decrement() if (self.__retrieveQueue.isEmpty() and self.__storeQueue.isEmpty()): sleep(0.1) continue if (not self.__retrieveQueue.isEmpty() and self.__storeQueue.isEmpty()): queue = self.__retrieveQueue store = False elif (self.__retrieveQueue.isEmpty() and not self.__storeQueue.isEmpty()): queue = self.__storeQueue store = True else: if (store): queue = self.__retrieveQueue store = False else: queue = self.__storeQueue store = True (imageID, clientIP, clientPort) = queue.pop(0) imageData = self.__dbConnector.getImageData(imageID) if (imageData == None): if (store): packet_type = PACKET_T.STOR_ERROR else: packet_type = PACKET_T.RETR_ERROR p = self.__repositoryPacketHandler.createErrorPacket( packet_type, ERROR_DESC_T.IR_IMAGE_DELETED) self.__networkManager.sendPacket( '', self.__commandsListenningPort, p, clientIP, clientPort) else: compressedFilePath = imageData["compressedFilePath"] if (not "undefined" in compressedFilePath): serverDirectory = path.relpath( path.dirname(compressedFilePath), self.__diskImagesDirectory) compressedFileName = path.basename(compressedFilePath) else: serverDirectory = str(imageID) compressedFileName = "" serverDirectoryPath = path.join( self.__diskImagesDirectory, serverDirectory) if (path.exists(serverDirectoryPath)): # The directory exists, and can store shit => clean it up! ChildProcessManager.runCommandInForeground( "rm -rf " + serverDirectoryPath, Exception) mkdir(serverDirectoryPath) if (store): packet_type = PACKET_T.STOR_START else: packet_type = PACKET_T.RETR_START p = self.__repositoryPacketHandler.createTransferEnabledPacket( packet_type, imageID, self.__FTPListenningPort, self.__ftpUsername, self.__ftpPassword, serverDirectory, compressedFileName) self.__networkManager.sendPacket( '', self.__commandsListenningPort, p, clientIP, clientPort)
class FileTransferThread(BasicThread): """ This thread class is associated with the transfer thread """ def __init__( self, networkManager, serverListeningPort, packetHandler, transferDirectory, ftpTimeout, maxTransferAttempts, dbConnector, useSSL, max_cancel_timeout=60, ): """ Initializes the transfer thread's state Args: networkManager: the network manager to use serverListeningPort: the control connection's port packetHandler: the virtual machine server packet handler to use transferDirectory: the directory where the .zip files will be stored ftpTimeout: the FTP timeout (in seconds) maxTransferAttempts: the maximum number of times that a transfer will be restarted after a failure. dbConnector: a database connector useSSL: indicates wheter SSL encryption must be used when establishing the connection with the image repository or not max_cancel_timeout: unlock transfers timeout (in seconds) """ BasicThread.__init__(self, "File transfer thread") self.__networkManager = networkManager self.__serverListeningPort = serverListeningPort self.__transferDirectory = transferDirectory self.__repositoryPacketHandler = ImageRepositoryPacketHandler(self.__networkManager) self.__vmServerPacketHandler = packetHandler self.__ftpTimeout = ftpTimeout self.__maxTransferAttempts = maxTransferAttempts self.__dbConnector = dbConnector self.__max_cancel_timeout = max_cancel_timeout self.__useSSL = useSSL def run(self): while not self.finish(): data = self.__dbConnector.peekFromTransferQueue() if data == None: sleep(0.5) else: self.__processElement(data) self.__dbConnector.removeFirstElementFromTransferQueue() def __processElement(self, data): """ Performs a transfer request Args: data: a dictionary containing the request's data Returns: Nothing """ attempts = 0 while attempts < self.__maxTransferAttempts: try: # Prepare for the new transfer if data["Transfer_Type"] == TRANSFER_T.CREATE_IMAGE or data["Transfer_Type"] == TRANSFER_T.EDIT_IMAGE: p = self.__repositoryPacketHandler.createRetrieveRequestPacket( data["SourceImageID"], data["Transfer_Type"] == TRANSFER_T.EDIT_IMAGE ) sourceFilePath = None elif data["Transfer_Type"] == TRANSFER_T.DEPLOY_IMAGE: p = self.__repositoryPacketHandler.createRetrieveRequestPacket(data["SourceImageID"], False) sourceFilePath = None elif data["Transfer_Type"] == TRANSFER_T.STORE_IMAGE: p = self.__repositoryPacketHandler.createStoreRequestPacket(int(data["TargetImageID"])) sourceFilePath = data["SourceFilePath"] else: p = self.__repositoryPacketHandler.createCancelEditionPacket(data["ImageID"]) # Establish the connection with the image repository callback = FileTransferCallback( self.__repositoryPacketHandler, self.__transferDirectory, data["RepositoryIP"], self.__ftpTimeout, sourceFilePath, ) callback.prepareForNewTransfer() self.__networkManager.connectTo( data["RepositoryIP"], data["RepositoryPort"], self.__ftpTimeout, callback, self.__useSSL ) while not self.__networkManager.isConnectionReady(data["RepositoryIP"], data["RepositoryPort"]): sleep(0.1) # Send the transfer request packet self.__networkManager.sendPacket(data["RepositoryIP"], data["RepositoryPort"], p) if data["Transfer_Type"] != TRANSFER_T.CANCEL_EDITION: # Wait until the transfer finishes while not callback.isTransferCompleted(): sleep(0.1) timeout = False else: elapsed_time = 0 while not callback.isTransferCompleted() and elapsed_time < self.__max_cancel_timeout: sleep(0.1) elapsed_time += 0.1 timeout = elapsed_time >= self.__max_cancel_timeout if callback.getErrorDescription() != None or timeout: if callback.getImageNotFound(): attempts = self.__maxTransferAttempts else: attempts += 1 if attempts == self.__maxTransferAttempts: if data["Transfer_Type"] != TRANSFER_T.CANCEL_EDITION: # Error => abort the transfer and warn the user p = self.__vmServerPacketHandler.createErrorPacket( VM_SERVER_PACKET_T.IMAGE_EDITION_ERROR, callback.getErrorDescription(), data["CommandID"], ) else: p = self.__vmServerPacketHandler.createInternalErrorPacket(data["CommandID"]) self.__networkManager.sendPacket("", self.__serverListeningPort, p) else: sleep(4 ** attempts) else: # The transfer has finished => do something with the .zip file if data["Transfer_Type"] == TRANSFER_T.CREATE_IMAGE: # Image creation => ask for a new image ID and add a request to the compression queue callback.prepareForNewTransfer() self.__networkManager.sendPacket( data["RepositoryIP"], data["RepositoryPort"], self.__repositoryPacketHandler.createAddImagePacket(), ) while not callback.isTransferCompleted(): sleep(0.1) data["TargetImageID"] = callback.getDomainImageID() self.__dbConnector.addToCompressionQueue(data) elif ( data["Transfer_Type"] == TRANSFER_T.EDIT_IMAGE or data["Transfer_Type"] == TRANSFER_T.DEPLOY_IMAGE ): # Add a request to the compression queue data["TargetImageID"] = data["SourceImageID"] self.__dbConnector.addToCompressionQueue(data) elif data["Transfer_Type"] == TRANSFER_T.STORE_IMAGE: # The image was successfully uploaded => delete the .zip file and send the confirmation packet ChildProcessManager.runCommandInForeground( "rm " + path.join(self.__transferDirectory, data["SourceFilePath"]), Exception ) p = self.__vmServerPacketHandler.createImageEditedPacket( data["TargetImageID"], data["CommandID"] ) self.__networkManager.sendPacket("", self.__serverListeningPort, p) # Disconnect from the image repository self.__networkManager.closeConnection(data["RepositoryIP"], data["RepositoryPort"]) attempts = self.__maxTransferAttempts except Exception: # Something went wrong => increment the attempts counter and, if necessary, abort the transfer and warn the user attempts += 1 if attempts == self.__maxTransferAttempts: errorCode = ERROR_DESC_T.VMSRVR_IR_CONNECTION_ERROR try: self.__networkManager.closeConnection(data["RepositoryIP"], data["RepositoryPort"]) except Exception: pass p = self.__vmServerPacketHandler.createErrorPacket( VM_SERVER_PACKET_T.IMAGE_EDITION_ERROR, errorCode, data["CommandID"] ) self.__networkManager.sendPacket("", self.__serverListeningPort, p) else: sleep(4 ** attempts)
class FileTransferThread(BasicThread): """ This thread class is associated with the transfer thread """ def __init__(self, networkManager, serverListeningPort, packetHandler, transferDirectory, ftpTimeout, maxTransferAttempts, dbConnector, useSSL, max_cancel_timeout=60): """ Initializes the transfer thread's state Args: networkManager: the network manager to use serverListeningPort: the control connection's port packetHandler: the virtual machine server packet handler to use transferDirectory: the directory where the .zip files will be stored ftpTimeout: the FTP timeout (in seconds) maxTransferAttempts: the maximum number of times that a transfer will be restarted after a failure. dbConnector: a database connector useSSL: indicates wheter SSL encryption must be used when establishing the connection with the image repository or not max_cancel_timeout: unlock transfers timeout (in seconds) """ BasicThread.__init__(self, "File transfer thread") self.__networkManager = networkManager self.__serverListeningPort = serverListeningPort self.__transferDirectory = transferDirectory self.__repositoryPacketHandler = ImageRepositoryPacketHandler( self.__networkManager) self.__vmServerPacketHandler = packetHandler self.__ftpTimeout = ftpTimeout self.__maxTransferAttempts = maxTransferAttempts self.__dbConnector = dbConnector self.__max_cancel_timeout = max_cancel_timeout self.__useSSL = useSSL def run(self): while not self.finish(): data = self.__dbConnector.peekFromTransferQueue() if data == None: sleep(0.5) else: self.__processElement(data) self.__dbConnector.removeFirstElementFromTransferQueue() def __processElement(self, data): """ Performs a transfer request Args: data: a dictionary containing the request's data Returns: Nothing """ attempts = 0 while attempts < self.__maxTransferAttempts: try: # Prepare for the new transfer if (data["Transfer_Type"] == TRANSFER_T.CREATE_IMAGE or data["Transfer_Type"] == TRANSFER_T.EDIT_IMAGE): p = self.__repositoryPacketHandler.createRetrieveRequestPacket( data["SourceImageID"], data["Transfer_Type"] == TRANSFER_T.EDIT_IMAGE) sourceFilePath = None elif (data["Transfer_Type"] == TRANSFER_T.DEPLOY_IMAGE): p = self.__repositoryPacketHandler.createRetrieveRequestPacket( data["SourceImageID"], False) sourceFilePath = None elif (data["Transfer_Type"] == TRANSFER_T.STORE_IMAGE): p = self.__repositoryPacketHandler.createStoreRequestPacket( int(data["TargetImageID"])) sourceFilePath = data["SourceFilePath"] else: p = self.__repositoryPacketHandler.createCancelEditionPacket( data["ImageID"]) # Establish the connection with the image repository callback = FileTransferCallback(self.__repositoryPacketHandler, self.__transferDirectory, data["RepositoryIP"], self.__ftpTimeout, sourceFilePath) callback.prepareForNewTransfer() self.__networkManager.connectTo(data["RepositoryIP"], data["RepositoryPort"], self.__ftpTimeout, callback, self.__useSSL) while not self.__networkManager.isConnectionReady( data["RepositoryIP"], data["RepositoryPort"]): sleep(0.1) # Send the transfer request packet self.__networkManager.sendPacket(data["RepositoryIP"], data["RepositoryPort"], p) if (data["Transfer_Type"] != TRANSFER_T.CANCEL_EDITION): # Wait until the transfer finishes while not callback.isTransferCompleted(): sleep(0.1) timeout = False else: elapsed_time = 0 while not callback.isTransferCompleted( ) and elapsed_time < self.__max_cancel_timeout: sleep(0.1) elapsed_time += 0.1 timeout = elapsed_time >= self.__max_cancel_timeout if (callback.getErrorDescription() != None or timeout): if callback.getImageNotFound(): attempts = self.__maxTransferAttempts else: attempts += 1 if (attempts == self.__maxTransferAttempts): if (data["Transfer_Type"] != TRANSFER_T.CANCEL_EDITION): # Error => abort the transfer and warn the user p = self.__vmServerPacketHandler.createErrorPacket( VM_SERVER_PACKET_T.IMAGE_EDITION_ERROR, callback.getErrorDescription(), data["CommandID"]) else: p = self.__vmServerPacketHandler.createInternalErrorPacket( data["CommandID"]) self.__networkManager.sendPacket( '', self.__serverListeningPort, p) else: sleep(4**attempts) else: # The transfer has finished => do something with the .zip file if (data["Transfer_Type"] == TRANSFER_T.CREATE_IMAGE): # Image creation => ask for a new image ID and add a request to the compression queue callback.prepareForNewTransfer() self.__networkManager.sendPacket( data["RepositoryIP"], data["RepositoryPort"], self.__repositoryPacketHandler. createAddImagePacket()) while not callback.isTransferCompleted(): sleep(0.1) data["TargetImageID"] = callback.getDomainImageID() self.__dbConnector.addToCompressionQueue(data) elif (data["Transfer_Type"] == TRANSFER_T.EDIT_IMAGE or data["Transfer_Type"] == TRANSFER_T.DEPLOY_IMAGE): # Add a request to the compression queue data["TargetImageID"] = data["SourceImageID"] self.__dbConnector.addToCompressionQueue(data) elif (data["Transfer_Type"] == TRANSFER_T.STORE_IMAGE): # The image was successfully uploaded => delete the .zip file and send the confirmation packet ChildProcessManager.runCommandInForeground( "rm " + path.join(self.__transferDirectory, data["SourceFilePath"]), Exception) p = self.__vmServerPacketHandler.createImageEditedPacket( data["TargetImageID"], data["CommandID"]) self.__networkManager.sendPacket( '', self.__serverListeningPort, p) # Disconnect from the image repository self.__networkManager.closeConnection(data["RepositoryIP"], data["RepositoryPort"]) attempts = self.__maxTransferAttempts except Exception: # Something went wrong => increment the attempts counter and, if necessary, abort the transfer and warn the user attempts += 1 if (attempts == self.__maxTransferAttempts): errorCode = ERROR_DESC_T.VMSRVR_IR_CONNECTION_ERROR try: self.__networkManager.closeConnection( data["RepositoryIP"], data["RepositoryPort"]) except Exception: pass p = self.__vmServerPacketHandler.createErrorPacket( VM_SERVER_PACKET_T.IMAGE_EDITION_ERROR, errorCode, data["CommandID"]) self.__networkManager.sendPacket( '', self.__serverListeningPort, p) else: sleep(4**attempts)
def startListenning(self, useSSL, certificatePath, listenningPort, repositoryIP, repositoryPort, vmServerStatusUpdateInterval): """ Creates the control connection Args: useSSL: indicates if SSL encryption must be used in the control connection or not certificatePath: the directory where the server.crt and server.key files are listenningPort: the control connection's port repositoryIP: the image repository IP address repositoryPort: the image repository's port vmServerStatusUpdateInterval: the virtual machine server status database interval Returns: Nothing """ self.__networkManager = NetworkManager(certificatePath) self.__networkManager.startNetworkService() self.__listenningPort = listenningPort self.__packetHandler = ClusterServerPacketHandler( self.__networkManager) self.__useSSL = useSSL imageRepositoryPacketHandler = ImageRepositoryPacketHandler( self.__networkManager) vmServerPacketHandler = VMServerPacketHandler(self.__networkManager) networkEventsReactor = NetworkEventsReactor(self.__dbConnector, repositoryIP, repositoryPort) imageRepositoryPacketReactor = ImageRepositoryPacketReactor( self.__dbConnector, self.__networkManager, listenningPort, repositoryIP, repositoryPort, self.__packetHandler, vmServerPacketHandler, imageRepositoryPacketHandler) try: imageRepositoryCallback = ImageRepositoryCallback( imageRepositoryPacketReactor, networkEventsReactor) self.__networkManager.connectTo(repositoryIP, repositoryPort, 10, imageRepositoryCallback, self.__useSSL, True) self.__dbConnector.addImageRepository(repositoryIP, repositoryPort, SERVER_STATE_T.READY) except Exception as e: print "Can't connect to the image repository: " + e.message self.__exit = True return vmServerPacketReactor = VMServerPacketReactor(self.__dbConnector, self.__networkManager, listenningPort, vmServerPacketHandler, self.__packetHandler) self.__endpointPacketReactor = EndpointPacketReactor( self.__dbConnector, self.__networkManager, vmServerPacketHandler, self.__packetHandler, imageRepositoryPacketHandler, VMServerCallback(vmServerPacketReactor, networkEventsReactor), listenningPort, repositoryIP, repositoryPort, self.__loadBalancerSettings, self.__averageCompressionRatio, self.__useSSL) clusterEndpointCallback = ClusterEndpointCallback( self.__endpointPacketReactor) self.__networkManager.listenIn(listenningPort, clusterEndpointCallback, self.__useSSL) self.__statusMonitoringThread = ClusterStatusMonitoringThread( vmServerStatusUpdateInterval, self.__dbConnector, self.__networkManager, repositoryIP, repositoryPort, vmServerPacketHandler, imageRepositoryPacketHandler) self.__statusMonitoringThread.start()
class ImageRepositoryReactor(object): """ Image repository packet reactor """ def __init__(self, diskImagesDirectory): """ Initializes the reactor's state Args: diskImagesDirectory: the FTP server's root directory """ self.__diskImagesDirectory = diskImagesDirectory self.__slotCounter = MultithreadingCounter() self.__retrieveQueue = GenericThreadSafeList() self.__storeQueue = GenericThreadSafeList() self.__finish = False self.__networkManager = None self.__ftpServer = None def connectToDatabase(self, repositoryDBName, repositoryDBUser, repositoryDBPassword): """ Establishes the connection with the image repository's database. Args: repositoryDBName: a database name repositoryDBUser: an user name repositoryDBPassword: a password Returns: Nothing """ self.__dbConnector = ImageRepositoryDBConnector(repositoryDBUser, repositoryDBPassword, repositoryDBName) def startListenning(self, networkInterface, useSSL, certificatesDirectory, commandsListenningPort, ftpListenningPort, maxConnections, maxConnectionsPerIP, uploadBandwidthRatio, downloadBandwidthRatio, ftpUsername, ftpPasswordLength): """ Boots up the FTP server and creates the control connection. Args: networkInterface: the network interface that will be used by the FTP server useSSL: indicates if SSL encryption must be used in the control connection or not certificatesDirectory: the directory where the files server.crt and server.key are commandsListenningPort: the control connection's port ftpListenningPort: the FTP server listenning port maxConnections: maximum FTP connections maxConnectionsPerIP: maximum FTP connections per IP address uploadBandwidthRatio: maximum download bandwidth fraction downloadBandwidthRatio: maximum upload bandwidth fraction ftpUsername: the FTP user that the virtual machine servers will use ftpPasswordLength: the random FTP password length Returns: Nothing @attention: The FTP password will be randomly generated at every boot. """ try : self.__maxConnections = maxConnections self.__commandsListenningPort = commandsListenningPort self.__FTPListenningPort = ftpListenningPort self.__networkManager = NetworkManager(certificatesDirectory) self.__repositoryPacketHandler = ImageRepositoryPacketHandler(self.__networkManager) self.__commandsCallback = CommandsCallback(self.__networkManager, self.__repositoryPacketHandler, commandsListenningPort, self.__dbConnector, self.__retrieveQueue, self.__storeQueue, self.__diskImagesDirectory) self.__networkManager.startNetworkService() self.__networkManager.listenIn(commandsListenningPort, self.__commandsCallback, useSSL) dataCallback = FTPServerCallback(self.__slotCounter, self.__dbConnector) self.__ftpUsername = ftpUsername self.__ftpPassword = ChildProcessManager.runCommandInForeground("openssl rand -base64 {0}".format(ftpPasswordLength), Exception) self.__ftpServer = ConfigurableFTPServer("Image repository FTP Server") self.__ftpServer.startListenning(networkInterface, ftpListenningPort, maxConnections, maxConnectionsPerIP, dataCallback, downloadBandwidthRatio, uploadBandwidthRatio) self.__ftpServer.addUser(self.__ftpUsername, self.__ftpPassword, self.__diskImagesDirectory, "eramw") except Exception as e: print "Error: " + e.message self.__finish = True def stopListenning(self): """ Stops the FTP server and closes the control connection. Args: None Returns: Nothing """ if (self.__ftpServer != None) : try : self.__ftpServer.stopListenning() except Exception : pass if (self.__networkManager != None) : self.__networkManager.stopNetworkService() def initTransfers(self): """ Initializes the upload and download transfers Args: None Returns: Nothing """ store = False while not (self.__finish or self.__commandsCallback.haltReceived()): if (self.__slotCounter.read() == self.__maxConnections) : # No free slots => sleep sleep(0.1) else : # There are slots => enable uploads and downloads in parallel self.__slotCounter.decrement() if (self.__retrieveQueue.isEmpty() and self.__storeQueue.isEmpty()) : sleep(0.1) continue if (not self.__retrieveQueue.isEmpty() and self.__storeQueue.isEmpty()) : queue = self.__retrieveQueue store = False elif (self.__retrieveQueue.isEmpty() and not self.__storeQueue.isEmpty()) : queue = self.__storeQueue store = True else : if (store) : queue = self.__retrieveQueue store = False else : queue = self.__storeQueue store = True (imageID, clientIP, clientPort) = queue.pop(0) imageData = self.__dbConnector.getImageData(imageID) if (imageData == None) : if (store) : packet_type = PACKET_T.STOR_ERROR else : packet_type = PACKET_T.RETR_ERROR p = self.__repositoryPacketHandler.createErrorPacket(packet_type, ERROR_DESC_T.IR_IMAGE_DELETED) self.__networkManager.sendPacket('', self.__commandsListenningPort, p, clientIP, clientPort) else : compressedFilePath = imageData["compressedFilePath"] if (not "undefined" in compressedFilePath) : serverDirectory = path.relpath(path.dirname(compressedFilePath), self.__diskImagesDirectory) compressedFileName = path.basename(compressedFilePath) else : serverDirectory = str(imageID) compressedFileName = "" serverDirectoryPath = path.join(self.__diskImagesDirectory, serverDirectory) if (path.exists(serverDirectoryPath)) : # The directory exists, and can store shit => clean it up! ChildProcessManager.runCommandInForeground("rm -rf " + serverDirectoryPath, Exception) mkdir(serverDirectoryPath) if (store) : packet_type = PACKET_T.STOR_START else : packet_type = PACKET_T.RETR_START p = self.__repositoryPacketHandler.createTransferEnabledPacket(packet_type, imageID, self.__FTPListenningPort, self.__ftpUsername, self.__ftpPassword, serverDirectory, compressedFileName) self.__networkManager.sendPacket('', self.__commandsListenningPort, p, clientIP, clientPort)