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)