]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
vmware: Force chunked transfer for upload-to-image
authorVipin Balachandran <vbala@vmware.com>
Thu, 3 Apr 2014 06:12:55 +0000 (11:42 +0530)
committerVipin Balachandran <vbala@vmware.com>
Fri, 20 Jun 2014 12:51:31 +0000 (18:21 +0530)
The upload-to-image operation downloads (using stream-optimized HTTP NFC
transfer) the virtual disk corresponding to the volume to a pipe from
where the glance image client reads the data for upload. Due to a recent
change in the glance image client, success in seeking the input file
object results in forgoing the chunked transfer in favor of regular
transfer. The glance image client expects an IOError if the input file
object is a pipe. Currently the pipe seek() is a NOP and this results in
upload-to-image failure. This changes fixes such failures by throwing
an IOError indicating an illegal seek.

Change-Id: I2e69fc4103559d49d2cee16ea04787a252dbf879
Closes-Bug: #1295239

cinder/tests/test_vmware_io_util.py [new file with mode: 0644]
cinder/volume/drivers/vmware/io_util.py
cinder/volume/drivers/vmware/read_write_util.py

diff --git a/cinder/tests/test_vmware_io_util.py b/cinder/tests/test_vmware_io_util.py
new file mode 100644 (file)
index 0000000..6c13291
--- /dev/null
@@ -0,0 +1,67 @@
+# Copyright (c) 2014 VMware, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+"""
+Unit tests for image transfer utility classes.
+"""
+
+import math
+
+import mock
+
+from cinder import test
+from cinder.volume.drivers.vmware import io_util
+
+
+class ThreadSafePipeTest(test.TestCase):
+    """Tests for ThreadSafePipe."""
+
+    def test_read(self):
+        max_size = 10
+        chunk_size = 10
+        max_transfer_size = 30
+        queue = io_util.ThreadSafePipe(max_size, max_transfer_size)
+
+        def get_side_effect():
+            return [1] * chunk_size
+
+        queue.get = mock.Mock(side_effect=get_side_effect)
+        while True:
+            data_item = queue.read(chunk_size)
+            if not data_item:
+                break
+
+        self.assertEqual(max_transfer_size, queue.transferred)
+        exp_calls = [mock.call()] * int(math.ceil(float(max_transfer_size) /
+                                                  chunk_size))
+        self.assertEqual(exp_calls, queue.get.call_args_list)
+
+    def test_write(self):
+        queue = io_util.ThreadSafePipe(10, 30)
+        queue.put = mock.Mock()
+        write_count = 10
+        for _ in range(0, write_count):
+            queue.write([1])
+        exp_calls = [mock.call([1])] * write_count
+        self.assertEqual(exp_calls, queue.put.call_args_list)
+
+    def test_seek(self):
+        queue = io_util.ThreadSafePipe(10, 30)
+        self.assertRaises(IOError, queue.seek, 0)
+
+    def test_tell(self):
+        max_transfer_size = 30
+        queue = io_util.ThreadSafePipe(10, 30)
+        self.assertEqual(max_transfer_size, queue.tell())
index 48b35a5333b40b320c15a6e9bf616d7462478f07..f1991064da56b3d3b1b9a79be00bef5033ef0777 100644 (file)
@@ -18,6 +18,8 @@ Utility classes for defining the time saving transfer of data from the reader
 to the write using a LightQueue as a Pipe between the reader and the writer.
 """
 
+import errno
+
 from eventlet import event
 from eventlet import greenthread
 from eventlet import queue
@@ -62,7 +64,8 @@ class ThreadSafePipe(queue.LightQueue):
 
     def seek(self, offset, whence=0):
         """Set the file's current position at the offset."""
-        pass
+        # Illegal seek; the file object is a pipe
+        raise IOError(errno.ESPIPE, "Illegal seek")
 
     def tell(self):
         """Get size of the file to be read."""
index e5deac1453b68d01d39eeef2e5fee7350cdad6fa..fd67037662739a667bebd2241ed3334d12ce575f 100644 (file)
@@ -304,9 +304,10 @@ class VMwareHTTPReadVmdk(VMwareHTTPFile):
 
     def read(self, chunk_size):
         """Read a chunk from file."""
-        self._progress += READ_CHUNKSIZE
+        data = self.file_handle.read(READ_CHUNKSIZE)
+        self._progress += len(data)
         LOG.debug("Read %s bytes from vmdk." % self._progress)
-        return self.file_handle.read(READ_CHUNKSIZE)
+        return data
 
     def update_progress(self):
         """Updates progress to lease.