def __init__(self): """ Initializes the manager's state Args: None """ self.__backgroundProcesses = GenericThreadSafeList() self.__thread = BackgroundProcessesPollingThread(self.__backgroundProcesses) self.__thread.start()
def __init__(self): """ Creates an empty queue Args: None Returns: Nothing """ self.__list = GenericThreadSafeList()
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
class GenericThreadSafeQueue(Queue): def __init__(self): """ Creates an empty queue Args: None Returns: Nothing """ self.__list = GenericThreadSafeList() def queue(self, element): """ Adds an element to the queue Args: element: the element to consider Returns: Nothing """ self.__list.append(element) def isEmpty(self): """ Checks if the queue is empty Args: None Returns: True if the queue is empty and False if its not. """ return self.__list.isEmpty() def dequeue(self): """ Removes an element from the queue Args: None Returns: the removed element """ return self.__list.pop(0)
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 ChildProcessManager(object): """ These objects create child processes and manage their return values. """ def __init__(self): """ Initializes the manager's state Args: None """ self.__backgroundProcesses = GenericThreadSafeList() self.__thread = BackgroundProcessesPollingThread(self.__backgroundProcesses) self.__thread.start() def waitForBackgroundChildrenToTerminate(self): """ Waits for all the child processes running in background to terminate. Args: None Returns: Nothing """ self.__thread.stop() while not self.__thread.finish() : sleep(0.1) @staticmethod def runCommandInForeground(cmd, ExceptionClass): """ Runs a command in foreground Args: cmd: a string with the command's name and its arguments ExceptionClass: the exception that will be raised if something goes wrong Returns: The run command's output Raises: ExceptionClass: this exception will be raised if something goes wrong when running the command. """ p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) code = p.wait() if (code != 0 and ExceptionClass != None) : raise ExceptionClass(p.stdout.read()) else : return p.stdout.read() def runCommandInBackground(self, cmd): """ Runs a command in background Args: cmd: a string with the command's name and its arguments Returns: the child process' PID """ p = Popen(cmd, shell=False, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) self.__backgroundProcesses.append(p) return p.pid @staticmethod def terminateProcess(pid, ExceptionClass): """ Sends a SIGTERM signal to a process Args: pid: a process' PID ExceptionClass: the exception that will be raised if something goes wrong Returns : Nothing Raises: ExceptionClass: this exception will be raised if something goes wrong while killing the process. """ ChildProcessManager.runCommandInForeground(["kill -s TERM " + str(pid)], ExceptionClass) @staticmethod def runCommandInForegroundAsRoot(cmd, ExceptionClass): """ Runs a command in foreground and as the super-user. Args: cmd: a string with the command's name and its arguments ExceptionClass: the exception that will be raised if something goes wrong Returns: The run command's output Raises: ExceptionClass: this exception will be raised if something goes wrong when running the command. """ password = RootPasswordHandler().getRootsPassword() header = "echo " + password + " | sudo -S " return ChildProcessManager.runCommandInForeground(header + cmd, ExceptionClass)
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)