From cf18199571b7965a8fe77db3d910b0e798f12946 Mon Sep 17 00:00:00 2001 From: Vipin Balachandran Date: Thu, 3 Apr 2014 11:42:55 +0530 Subject: [PATCH] vmware: Force chunked transfer for upload-to-image 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 | 67 +++++++++++++++++++ cinder/volume/drivers/vmware/io_util.py | 5 +- .../volume/drivers/vmware/read_write_util.py | 5 +- 3 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 cinder/tests/test_vmware_io_util.py diff --git a/cinder/tests/test_vmware_io_util.py b/cinder/tests/test_vmware_io_util.py new file mode 100644 index 000000000..6c13291f2 --- /dev/null +++ b/cinder/tests/test_vmware_io_util.py @@ -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()) diff --git a/cinder/volume/drivers/vmware/io_util.py b/cinder/volume/drivers/vmware/io_util.py index 48b35a533..f1991064d 100644 --- a/cinder/volume/drivers/vmware/io_util.py +++ b/cinder/volume/drivers/vmware/io_util.py @@ -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.""" diff --git a/cinder/volume/drivers/vmware/read_write_util.py b/cinder/volume/drivers/vmware/read_write_util.py index e5deac145..fd6703766 100644 --- a/cinder/volume/drivers/vmware/read_write_util.py +++ b/cinder/volume/drivers/vmware/read_write_util.py @@ -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. -- 2.45.2