| Index: client/tests/kvm/rss_file_transfer.py
|
| diff --git a/client/tests/kvm/rss_file_transfer.py b/client/tests/kvm/rss_file_transfer.py
|
| index 3de8259cca17d8c29870ce754d96a451c1e36123..4d00d174e48753a3d7a55e1b17cb7a6d795bbac2 100755
|
| --- a/client/tests/kvm/rss_file_transfer.py
|
| +++ b/client/tests/kvm/rss_file_transfer.py
|
| @@ -27,7 +27,21 @@ RSS_DONE = 9
|
|
|
|
|
| class FileTransferError(Exception):
|
| - pass
|
| + def __init__(self, msg, e=None, filename=None):
|
| + Exception.__init__(self, msg, e, filename)
|
| + self.msg = msg
|
| + self.e = e
|
| + self.filename = filename
|
| +
|
| + def __str__(self):
|
| + s = self.msg
|
| + if self.e and self.filename:
|
| + s += " (error: %s, filename: %s)" % (self.e, self.filename)
|
| + elif self.e:
|
| + s += " (%s)" % self.e
|
| + elif self.filename:
|
| + s += " (filename: %s)" % self.filename
|
| + return s
|
|
|
|
|
| class FileTransferConnectError(FileTransferError):
|
| @@ -42,12 +56,19 @@ class FileTransferProtocolError(FileTransferError):
|
| pass
|
|
|
|
|
| -class FileTransferSendError(FileTransferError):
|
| +class FileTransferSocketError(FileTransferError):
|
| pass
|
|
|
|
|
| class FileTransferServerError(FileTransferError):
|
| - pass
|
| + def __init__(self, errmsg):
|
| + FileTransferError.__init__(self, None, errmsg)
|
| +
|
| + def __str__(self):
|
| + s = "Server said: %r" % self.e
|
| + if self.filename:
|
| + s += " (filename: %s)" % self.filename
|
| + return s
|
|
|
|
|
| class FileTransferNotFoundError(FileTransferError):
|
| @@ -59,23 +80,24 @@ class FileTransferClient(object):
|
| Connect to a RSS (remote shell server) and transfer files.
|
| """
|
|
|
| - def __init__(self, address, port, timeout=10):
|
| + def __init__(self, address, port, log_func=None, timeout=20):
|
| """
|
| Connect to a server.
|
|
|
| @param address: The server's address
|
| @param port: The server's port
|
| + @param log_func: If provided, transfer stats will be passed to this
|
| + function during the transfer
|
| @param timeout: Time duration to wait for connection to succeed
|
| @raise FileTransferConnectError: Raised if the connection fails
|
| - @raise FileTransferProtocolError: Raised if an incorrect magic number
|
| - is received
|
| """
|
| self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
| self._socket.settimeout(timeout)
|
| try:
|
| self._socket.connect((address, port))
|
| - except socket.error:
|
| - raise FileTransferConnectError("Could not connect to server")
|
| + except socket.error, e:
|
| + raise FileTransferConnectError("Cannot connect to server at "
|
| + "%s:%s" % (address, port), e)
|
| try:
|
| if self._receive_msg(timeout) != RSS_MAGIC:
|
| raise FileTransferConnectError("Received wrong magic number")
|
| @@ -83,6 +105,10 @@ class FileTransferClient(object):
|
| raise FileTransferConnectError("Timeout expired while waiting to "
|
| "receive magic number")
|
| self._send(struct.pack("=i", CHUNKSIZE))
|
| + self._log_func = log_func
|
| + self._last_time = time.time()
|
| + self._last_transferred = 0
|
| + self.transferred = 0
|
|
|
|
|
| def __del__(self):
|
| @@ -96,86 +122,116 @@ class FileTransferClient(object):
|
| self._socket.close()
|
|
|
|
|
| - def _send(self, str):
|
| + def _send(self, str, timeout=60):
|
| try:
|
| + if timeout <= 0:
|
| + raise socket.timeout
|
| + self._socket.settimeout(timeout)
|
| self._socket.sendall(str)
|
| - except socket.error:
|
| - raise FileTransferSendError("Could not send data to server")
|
| + except socket.timeout:
|
| + raise FileTransferTimeoutError("Timeout expired while sending "
|
| + "data to server")
|
| + except socket.error, e:
|
| + raise FileTransferSocketError("Could not send data to server", e)
|
|
|
|
|
| - def _receive(self, size, timeout=10):
|
| + def _receive(self, size, timeout=60):
|
| strs = []
|
| end_time = time.time() + timeout
|
| - while size > 0:
|
| - try:
|
| - self._socket.settimeout(max(0.0001, end_time - time.time()))
|
| + try:
|
| + while size > 0:
|
| + timeout = end_time - time.time()
|
| + if timeout <= 0:
|
| + raise socket.timeout
|
| + self._socket.settimeout(timeout)
|
| data = self._socket.recv(size)
|
| - except socket.timeout:
|
| - raise FileTransferTimeoutError("Timeout expired while "
|
| - "receiving data from server")
|
| - except socket.error:
|
| - raise FileTransferProtocolError("Error receiving data from "
|
| - "server")
|
| - if not data:
|
| - raise FileTransferProtocolError("Connection closed "
|
| - "unexpectedly")
|
| - strs.append(data)
|
| - size -= len(data)
|
| + if not data:
|
| + raise FileTransferProtocolError("Connection closed "
|
| + "unexpectedly while "
|
| + "receiving data from "
|
| + "server")
|
| + strs.append(data)
|
| + size -= len(data)
|
| + except socket.timeout:
|
| + raise FileTransferTimeoutError("Timeout expired while receiving "
|
| + "data from server")
|
| + except socket.error, e:
|
| + raise FileTransferSocketError("Error receiving data from server",
|
| + e)
|
| return "".join(strs)
|
|
|
|
|
| - def _send_packet(self, str):
|
| + def _report_stats(self, str):
|
| + if self._log_func:
|
| + dt = time.time() - self._last_time
|
| + if dt >= 1:
|
| + transferred = self.transferred / 1048576.
|
| + speed = (self.transferred - self._last_transferred) / dt
|
| + speed /= 1048576.
|
| + self._log_func("%s %.3f MB (%.3f MB/sec)" %
|
| + (str, transferred, speed))
|
| + self._last_time = time.time()
|
| + self._last_transferred = self.transferred
|
| +
|
| +
|
| + def _send_packet(self, str, timeout=60):
|
| self._send(struct.pack("=I", len(str)))
|
| - self._send(str)
|
| + self._send(str, timeout)
|
| + self.transferred += len(str) + 4
|
| + self._report_stats("Sent")
|
|
|
|
|
| - def _receive_packet(self, timeout=10):
|
| + def _receive_packet(self, timeout=60):
|
| size = struct.unpack("=I", self._receive(4))[0]
|
| - return self._receive(size, timeout)
|
| + str = self._receive(size, timeout)
|
| + self.transferred += len(str) + 4
|
| + self._report_stats("Received")
|
| + return str
|
|
|
|
|
| - def _send_file_chunks(self, filename, timeout=30):
|
| + def _send_file_chunks(self, filename, timeout=60):
|
| + if self._log_func:
|
| + self._log_func("Sending file %s" % filename)
|
| f = open(filename, "rb")
|
| try:
|
| - end_time = time.time() + timeout
|
| - while time.time() < end_time:
|
| - data = f.read(CHUNKSIZE)
|
| - self._send_packet(data)
|
| - if len(data) < CHUNKSIZE:
|
| - break
|
| - else:
|
| - raise FileTransferTimeoutError("Timeout expired while sending "
|
| - "file %s" % filename)
|
| + try:
|
| + end_time = time.time() + timeout
|
| + while True:
|
| + data = f.read(CHUNKSIZE)
|
| + self._send_packet(data, end_time - time.time())
|
| + if len(data) < CHUNKSIZE:
|
| + break
|
| + except FileTransferError, e:
|
| + e.filename = filename
|
| + raise
|
| finally:
|
| f.close()
|
|
|
|
|
| - def _receive_file_chunks(self, filename, timeout=30):
|
| + def _receive_file_chunks(self, filename, timeout=60):
|
| + if self._log_func:
|
| + self._log_func("Receiving file %s" % filename)
|
| f = open(filename, "wb")
|
| try:
|
| - end_time = time.time() + timeout
|
| - while True:
|
| - try:
|
| + try:
|
| + end_time = time.time() + timeout
|
| + while True:
|
| data = self._receive_packet(end_time - time.time())
|
| - except FileTransferTimeoutError:
|
| - raise FileTransferTimeoutError("Timeout expired while "
|
| - "receiving file %s" %
|
| - filename)
|
| - except FileTransferProtocolError:
|
| - raise FileTransferProtocolError("Error receiving file %s" %
|
| - filename)
|
| - f.write(data)
|
| - if len(data) < CHUNKSIZE:
|
| - break
|
| + f.write(data)
|
| + if len(data) < CHUNKSIZE:
|
| + break
|
| + except FileTransferError, e:
|
| + e.filename = filename
|
| + raise
|
| finally:
|
| f.close()
|
|
|
|
|
| - def _send_msg(self, msg, timeout=10):
|
| + def _send_msg(self, msg, timeout=60):
|
| self._send(struct.pack("=I", msg))
|
|
|
|
|
| - def _receive_msg(self, timeout=10):
|
| + def _receive_msg(self, timeout=60):
|
| s = self._receive(4, timeout)
|
| return struct.unpack("=I", s)[0]
|
|
|
| @@ -191,7 +247,7 @@ class FileTransferClient(object):
|
| raise e[0], e[1], e[2]
|
| if msg == RSS_ERROR:
|
| errmsg = self._receive_packet()
|
| - raise FileTransferServerError("Server said: %s" % errmsg)
|
| + raise FileTransferServerError(errmsg)
|
| raise e[0], e[1], e[2]
|
|
|
|
|
| @@ -200,20 +256,22 @@ class FileUploadClient(FileTransferClient):
|
| Connect to a RSS (remote shell server) and upload files or directory trees.
|
| """
|
|
|
| - def __init__(self, address, port, timeout=10):
|
| + def __init__(self, address, port, log_func=None, timeout=20):
|
| """
|
| Connect to a server.
|
|
|
| @param address: The server's address
|
| @param port: The server's port
|
| + @param log_func: If provided, transfer stats will be passed to this
|
| + function during the transfer
|
| @param timeout: Time duration to wait for connection to succeed
|
| @raise FileTransferConnectError: Raised if the connection fails
|
| @raise FileTransferProtocolError: Raised if an incorrect magic number
|
| is received
|
| - @raise FileTransferSendError: Raised if the RSS_UPLOAD message cannot
|
| + @raise FileTransferSocketError: Raised if the RSS_UPLOAD message cannot
|
| be sent to the server
|
| """
|
| - super(FileUploadClient, self).__init__(address, port, timeout)
|
| + super(FileUploadClient, self).__init__(address, port, log_func, timeout)
|
| self._send_msg(RSS_UPLOAD)
|
|
|
|
|
| @@ -221,7 +279,7 @@ class FileUploadClient(FileTransferClient):
|
| if os.path.isfile(path):
|
| self._send_msg(RSS_CREATE_FILE)
|
| self._send_packet(os.path.basename(path))
|
| - self._send_file_chunks(path, max(0, end_time - time.time()))
|
| + self._send_file_chunks(path, end_time - time.time())
|
| elif os.path.isdir(path):
|
| self._send_msg(RSS_CREATE_DIR)
|
| self._send_packet(os.path.basename(path))
|
| @@ -277,12 +335,12 @@ class FileUploadClient(FileTransferClient):
|
| "directories" %
|
| src_pattern)
|
| # Look for RSS_OK or RSS_ERROR
|
| - msg = self._receive_msg(max(0, end_time - time.time()))
|
| + msg = self._receive_msg(end_time - time.time())
|
| if msg == RSS_OK:
|
| return
|
| elif msg == RSS_ERROR:
|
| errmsg = self._receive_packet()
|
| - raise FileTransferServerError("Server said: %s" % errmsg)
|
| + raise FileTransferServerError(errmsg)
|
| else:
|
| # Neither RSS_OK nor RSS_ERROR found
|
| raise FileTransferProtocolError("Received unexpected msg")
|
| @@ -297,12 +355,14 @@ class FileDownloadClient(FileTransferClient):
|
| Connect to a RSS (remote shell server) and download files or directory trees.
|
| """
|
|
|
| - def __init__(self, address, port, timeout=10):
|
| + def __init__(self, address, port, log_func=None, timeout=20):
|
| """
|
| Connect to a server.
|
|
|
| @param address: The server's address
|
| @param port: The server's port
|
| + @param log_func: If provided, transfer stats will be passed to this
|
| + function during the transfer
|
| @param timeout: Time duration to wait for connection to succeed
|
| @raise FileTransferConnectError: Raised if the connection fails
|
| @raise FileTransferProtocolError: Raised if an incorrect magic number
|
| @@ -310,7 +370,7 @@ class FileDownloadClient(FileTransferClient):
|
| @raise FileTransferSendError: Raised if the RSS_UPLOAD message cannot
|
| be sent to the server
|
| """
|
| - super(FileDownloadClient, self).__init__(address, port, timeout)
|
| + super(FileDownloadClient, self).__init__(address, port, log_func, timeout)
|
| self._send_msg(RSS_DOWNLOAD)
|
|
|
|
|
| @@ -358,8 +418,7 @@ class FileDownloadClient(FileTransferClient):
|
| filename = self._receive_packet()
|
| if os.path.isdir(dst_path):
|
| dst_path = os.path.join(dst_path, filename)
|
| - self._receive_file_chunks(
|
| - dst_path, max(0, end_time - time.time()))
|
| + self._receive_file_chunks(dst_path, end_time - time.time())
|
| dst_path = os.path.dirname(dst_path)
|
| file_count += 1
|
| elif msg == RSS_CREATE_DIR:
|
| @@ -385,7 +444,7 @@ class FileDownloadClient(FileTransferClient):
|
| elif msg == RSS_ERROR:
|
| # Receive error message and abort
|
| errmsg = self._receive_packet()
|
| - raise FileTransferServerError("Server said: %s" % errmsg)
|
| + raise FileTransferServerError(errmsg)
|
| else:
|
| # Unexpected msg
|
| raise FileTransferProtocolError("Received unexpected msg")
|
| @@ -395,26 +454,26 @@ class FileDownloadClient(FileTransferClient):
|
| raise
|
|
|
|
|
| -def upload(address, port, src_pattern, dst_path, timeout=60,
|
| - connect_timeout=10):
|
| +def upload(address, port, src_pattern, dst_path, log_func=None, timeout=60,
|
| + connect_timeout=20):
|
| """
|
| Connect to server and upload files.
|
|
|
| @see: FileUploadClient
|
| """
|
| - client = FileUploadClient(address, port, connect_timeout)
|
| + client = FileUploadClient(address, port, log_func, connect_timeout)
|
| client.upload(src_pattern, dst_path, timeout)
|
| client.close()
|
|
|
|
|
| -def download(address, port, src_pattern, dst_path, timeout=60,
|
| - connect_timeout=10):
|
| +def download(address, port, src_pattern, dst_path, log_func=None, timeout=60,
|
| + connect_timeout=20):
|
| """
|
| Connect to server and upload files.
|
|
|
| @see: FileDownloadClient
|
| """
|
| - client = FileDownloadClient(address, port, connect_timeout)
|
| + client = FileDownloadClient(address, port, log_func, connect_timeout)
|
| client.download(src_pattern, dst_path, timeout)
|
| client.close()
|
|
|
| @@ -430,6 +489,9 @@ def main():
|
| parser.add_option("-u", "--upload",
|
| action="store_true", dest="upload",
|
| help="upload files to server")
|
| + parser.add_option("-v", "--verbose",
|
| + action="store_true", dest="verbose",
|
| + help="be verbose")
|
| parser.add_option("-t", "--timeout",
|
| type="int", dest="timeout", default=3600,
|
| help="transfer timeout")
|
| @@ -441,10 +503,16 @@ def main():
|
| address, port, src_pattern, dst_path = args
|
| port = int(port)
|
|
|
| + logger = None
|
| + if options.verbose:
|
| + def p(s):
|
| + print s
|
| + logger = p
|
| +
|
| if options.download:
|
| - download(address, port, src_pattern, dst_path, options.timeout)
|
| + download(address, port, src_pattern, dst_path, logger, options.timeout)
|
| elif options.upload:
|
| - upload(address, port, src_pattern, dst_path, options.timeout)
|
| + upload(address, port, src_pattern, dst_path, logger, options.timeout)
|
|
|
|
|
| if __name__ == "__main__":
|
|
|