def send_async_file(self, file_content, file_info):
        """
        Sends a file asynchronously. Status of the request may be checked through
        check_async_command_status.

        @param file_content: Content of the file being sent
        @param file_info: File information of the file being sent
        @see check_async_command_status
        """

        lab_session_id = self._reservation_session.get('lab_session_id')
        lab_coordaddr = self._reservation_session.get('lab_coordaddr')

        if lab_session_id is None or lab_coordaddr is None:
            raise core_exc.NoCurrentReservationError(
                "send_async_file called but no current reservation")

        laboratory_server = self._locator.get_server_from_coordaddr(
            lab_coordaddr, ServerType.Laboratory)

        usage_file_sent = self._store_file(file_content, file_info)
        command_id_pack = self._append_file(usage_file_sent)
        try:
            response = laboratory_server.send_async_file(
                lab_session_id, file_content, file_info)

            # TODO: how do we store async files? whenever somebody ask for the status? what if they don't ask for it?

            return response
        except LaboratoryErrors.SessionNotFoundInLaboratoryServerError:
            self._update_command_or_file(
                command_id_pack,
                Command.Command("ERROR: SessionNotFound: None"))
            try:
                self.finish()
            except core_exc.FailedToFreeReservationError:
                pass
            raise core_exc.NoCurrentReservationError(
                'Experiment reservation expired')
        except LaboratoryErrors.FailedToInteractError as ftspe:
            self._update_command_or_file(
                command_id_pack, Command.Command("ERROR: " + str(ftspe)))
            try:
                self.finish()
            except core_exc.FailedToFreeReservationError:
                pass
            raise core_exc.FailedToInteractError("Failed to send file: %s" %
                                                 ftspe)
    def send_command(self, command):
        #
        # Check the that the experiment is enabled
        #
        lab_session_id = self._reservation_session.get('lab_session_id')
        lab_coordaddr = self._reservation_session.get('lab_coordaddr')

        if lab_session_id is None or lab_coordaddr is None:
            raise core_exc.NoCurrentReservationError(
                "send_command called but the reservation is not enabled")

        laboratory_server = self._locator.get_server_from_coordaddr(
            lab_coordaddr, ServerType.Laboratory)
        command_id_pack = self._append_command(command)
        try:

            # We call the laboratory server's send_command, which will finally
            # get the command to be handled by the experiment.
            response = laboratory_server.send_command(lab_session_id, command)

            # The previous call was executed synchronously and we have
            # received the response. Before returning it, we will store it
            # locally so that we can log it.
            self._update_command_or_file(command_id_pack, response)

            return response

        except LaboratoryErrors.SessionNotFoundInLaboratoryServerError:
            self._update_command_or_file(
                command_id_pack,
                Command.Command("ERROR: SessionNotFound: None"))
            try:
                self.finish()
            except core_exc.FailedToFreeReservationError:
                pass
            raise core_exc.NoCurrentReservationError(
                'Experiment reservation expired')
        except LaboratoryErrors.FailedToInteractError as ftspe:
            self._update_command_or_file(
                command_id_pack, Command.Command("ERROR: " + str(ftspe)))
            try:
                self.finish()
            except core_exc.FailedToFreeReservationError:
                pass

            raise core_exc.FailedToInteractError("Failed to send command: %s" %
                                                 ftspe)
    def send_file(self, file_content, file_info):
        #
        # Check that the reservation is enabled
        #
        lab_session_id = self._reservation_session.get('lab_session_id')
        lab_coordaddr = self._reservation_session.get('lab_coordaddr')
        if lab_session_id is None or lab_coordaddr is None:
            raise core_exc.NoCurrentReservationError(
                "send_file called but the reservation was not enabled")

        #
        # Retrieve the laboratory server
        #

        laboratory_server = self._locator.get_server_from_coordaddr(
            lab_coordaddr, ServerType.Laboratory)

        usage_file_sent = self._store_file(file_content, file_info)
        command_id_pack = self._append_file(usage_file_sent)
        try:
            response = laboratory_server.send_file(lab_session_id,
                                                   file_content, file_info)
            self._update_command_or_file(command_id_pack, response)
            return response
        except LaboratoryErrors.SessionNotFoundInLaboratoryServerError:
            self._update_command_or_file(
                command_id_pack, Command.Command("ERROR: SessionNotFound"))
            try:
                self.finish()
            except core_exc.FailedToFreeReservationError:
                pass
            raise core_exc.NoCurrentReservationError(
                'Experiment reservation expired')
        except LaboratoryErrors.FailedToInteractError as ftie:
            self._update_command_or_file(
                command_id_pack, Command.Command("ERROR: " + str(ftie)))
            try:
                self.finish()
            except core_exc.FailedToFreeReservationError:
                pass
            raise core_exc.FailedToInteractError("Failed to send: %s" % ftie)
    def send_async_command(self, command):
        """
        Runs a command asynchronously. Status of the request may be checked
        through the check_async_command_status method.

        @param command The command to run
        @see check_async_command_status
        """

        lab_session_id = self._reservation_session.get('lab_session_id')
        lab_coordaddr = self._reservation_session.get('lab_coordaddr')

        if lab_session_id is None or lab_coordaddr is None:
            raise core_exc.NoCurrentReservationError(
                "send_async_command called but no current reservation")

        command_id_pack = self._append_command(command)

        try:
            laboratory_server = self._locator[lab_coordaddr]

            # We forward the request to the laboratory server, which
            # will forward it to the actual experiment. Because this is
            # an asynchronous call, we will not receive the actual response
            # to the command, but simply an ID identifying our request. This also
            # means that by the time this call returns, the real response to the
            # command is most likely not available yet.
            request_id = laboratory_server.send_async_command(
                lab_session_id, command)

            # If this was a standard, synchronous send_command, we would now store the response
            # we received, so that later, when the experiment finishes, the log is properly
            # written. However, the real response is not available yet, so we can't do that here.
            # Instead, we will store a reference to our usage object, so that we can later update it
            # when the response to the asynchronous command is ready.
            self._reservation_session["async_commands_ids"][
                request_id] = command_id_pack

            # TODO: when do we store async commands? whenever user asks for status? what if they don't ever ask?

            return request_id

        except LaboratoryErrors.SessionNotFoundInLaboratoryServerError:
            self._update_command_or_file(
                command_id_pack,
                Command.Command("ERROR: SessionNotFound: None"))
            try:
                self.finish()
            except core_exc.FailedToFreeReservationError:
                pass
            raise core_exc.NoCurrentReservationError(
                'Experiment reservation expired')
        except LaboratoryErrors.FailedToInteractError as ftspe:
            self._update_command_or_file(
                command_id_pack, Command.Command("ERROR: " + str(ftspe)))
            try:
                self.finish()
            except core_exc.FailedToFreeReservationError:
                pass

            raise core_exc.FailedToInteractError("Failed to send command: %s" %
                                                 ftspe)
    def check_async_command_status(self, request_identifiers):
        """
        Checks the status of several asynchronous commands. The request will be
        internally forwarded to the lab server. Standard async commands
        and file_send commands are treated in the same way.
        Commands reported as finished (either successfully or not) will be
        removed, so check_async_command_status should not be called on them again.
        Before removing the commands, it will also register their response for
        logging purposes.

        @param request_identifiers: List of the identifiers to check
        @return: Dictionary by request-id of tuples: (status, content)
        """

        lab_session_id = self._reservation_session.get('lab_session_id')
        lab_coordaddr = self._reservation_session.get('lab_coordaddr')

        if lab_session_id is None or lab_coordaddr is None:
            raise core_exc.NoCurrentReservationError(
                "check_async_command called but no current reservation")

        try:
            laboratory_server = self._locator[lab_coordaddr]
            response = laboratory_server.check_async_command_status(
                lab_session_id, request_identifiers)

            # Within the response map, we might now have the real response to one
            # (or more) async commands. We will update the usage object of the
            # command with its response, so that once the experiment ends it appears
            # in the log as expected.
            for req_id, (cmd_status,
                         cmd_response) in response.items():  #@UnusedVariable
                if (req_id in self._reservation_session["async_commands_ids"]):
                    #usage_obj_id = self._reservation_session["async_commands_ids"][req_id]
                    # TODO: Bug here. async_commands_ids is empty.
                    # self._update_command_or_file(usage_obj_id, cmd_response)
                    pass

            return response

        except LaboratoryErrors.SessionNotFoundInLaboratoryServerError:
            # We did not find the specified session in the laboratory server.
            # We'll finish the experiment.
            #self._update_command(command_id_pack, Command.Command("ERROR: SessionNotFound: None"))
            try:
                self.finish()
            except core_exc.FailedToFreeReservationError:
                pass
            raise core_exc.NoCurrentReservationError(
                'Experiment reservation expired')
        except LaboratoryErrors.FailedToInteractError as ftspe:
            # There was an error while trying to send the command.
            # We'll finish the experiment.
            #self._update_command(command_id_pack, Command.Command("ERROR: " + str(ftspe)))
            try:
                self.finish()
            except core_exc.FailedToFreeReservationError:
                pass

            raise core_exc.FailedToInteractError("Failed to send command: %s" %
                                                 ftspe)