"""Used as mock for rbd.ImageBusy."""
+class MockImageExistsException(MockException):
+ """Used as mock for rbd.ImageExists."""
+
+
def common_mocks(f):
"""Decorator to set mocks common to all tests.
inst.mock_rados.Error = Exception
inst.mock_rbd.ImageBusy = MockImageBusyException
inst.mock_rbd.ImageNotFound = MockImageNotFoundException
+ inst.mock_rbd.ImageExists = MockImageExistsException
inst.driver.rbd = inst.mock_rbd
inst.driver.rados = inst.mock_rados
client.__exit__.assert_called_once()
mock_supports_layering.assert_called_once()
+ @common_mocks
+ def test_manage_existing_get_size(self):
+ with mock.patch.object(self.driver.rbd.Image, 'size') as \
+ mock_rbd_image_size:
+ with mock.patch.object(self.driver.rbd.Image, 'close') \
+ as mock_rbd_image_close:
+ mock_rbd_image_size.return_value = 2 * units.Gi
+ existing_ref = {'rbd_name': self.volume_name}
+ return_size = self.driver.manage_existing_get_size(
+ self.volume,
+ existing_ref)
+ self.assertEqual(2, return_size)
+ mock_rbd_image_size.assert_called_once_with()
+ mock_rbd_image_close.assert_called_once_with()
+
+ @common_mocks
+ def test_manage_existing_get_invalid_size(self):
+
+ with mock.patch.object(self.driver.rbd.Image, 'size') as \
+ mock_rbd_image_size:
+ with mock.patch.object(self.driver.rbd.Image, 'close') \
+ as mock_rbd_image_close:
+ mock_rbd_image_size.return_value = 'abcd'
+ existing_ref = {'rbd_name': self.volume_name}
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.manage_existing_get_size,
+ self.volume, existing_ref)
+
+ mock_rbd_image_size.assert_called_once_with()
+ mock_rbd_image_close.assert_called_once_with()
+
+ @common_mocks
+ def test_manage_existing(self):
+ client = self.mock_client.return_value
+ client.__enter__.return_value = client
+
+ with mock.patch.object(driver, 'RADOSClient') as mock_rados_client:
+ with mock.patch.object(self.driver.rbd.RBD(), 'rename') as \
+ mock_rbd_image_rename:
+ exist_volume = 'vol-exist'
+ existing_ref = {'rbd_name': exist_volume}
+ mock_rbd_image_rename.return_value = 0
+ mock_rbd_image_rename(mock_rados_client.ioctx,
+ exist_volume,
+ self.volume_name)
+ self.driver.manage_existing(self.volume, existing_ref)
+ mock_rbd_image_rename.assert_called_with(
+ mock_rados_client.ioctx,
+ exist_volume,
+ self.volume_name)
+
+ @common_mocks
+ def test_manage_existing_with_exist_rbd_image(self):
+ client = self.mock_client.return_value
+ client.__enter__.return_value = client
+
+ self.mock_rbd.Image.rename = mock.Mock()
+ self.mock_rbd.Image.rename.side_effect = \
+ MockImageExistsException
+
+ exist_volume = 'vol-exist'
+ existing_ref = {'rbd_name': exist_volume}
+ self.assertRaises(self.mock_rbd.ImageExists,
+ self.driver.manage_existing,
+ self.volume, existing_ref)
+
+ #make sure the exception was raised
+ self.assertEqual(RAISED_EXCEPTIONS,
+ [self.mock_rbd.ImageExists])
+
@common_mocks
def test_create_volume_no_layering(self):
client = self.mock_client.return_value
LOG.debug("Extend volume from %(old_size)s GB to %(new_size)s GB.",
{'old_size': old_size, 'new_size': new_size})
+
+ def manage_existing(self, volume, existing_ref):
+ """Manages an existing image.
+
+ Renames the image name to match the expected name for the volume.
+ Error checking done by manage_existing_get_size is not repeated.
+
+ :param volume:
+ volume ref info to be set
+ :param existing_ref:
+ existing_ref is a dictionary of the form:
+ {'rbd_name': <name of rbd image>}
+ """
+ # Raise an exception if we didn't find a suitable rbd image.
+ with RADOSClient(self) as client:
+ rbd_name = existing_ref['rbd_name']
+ self.rbd.RBD().rename(client.ioctx, strutils.safe_encode(rbd_name),
+ strutils.safe_encode(volume['name']))
+
+ def manage_existing_get_size(self, volume, existing_ref):
+ """Return size of an existing image for manage_existing.
+
+ :param volume:
+ volume ref info to be set
+ :param existing_ref:
+ existing_ref is a dictionary of the form:
+ {'rbd_name': <name of rbd image>}
+ """
+
+ # Check that the reference is valid
+ if 'rbd_name' not in existing_ref:
+ reason = _('Reference must contain rbd_name element.')
+ raise exception.ManageExistingInvalidReference(
+ existing_ref=existing_ref, reason=reason)
+
+ rbd_name = strutils.safe_encode(existing_ref['rbd_name'])
+
+ with RADOSClient(self) as client:
+ # Raise an exception if we didn't find a suitable rbd image.
+ try:
+ rbd_image = self.rbd.Image(client.ioctx, rbd_name)
+ image_size = rbd_image.size()
+ except self.rbd.ImageNotFound:
+ kwargs = {'existing_ref': rbd_name,
+ 'reason': 'Specified rbd image does not exist.'}
+ raise exception.ManageExistingInvalidReference(**kwargs)
+ finally:
+ rbd_image.close()
+
+ # RBD image size is returned in bytes. Attempt to parse
+ # size as a float and round up to the next integer.
+ try:
+ convert_size = int(math.ceil(int(image_size))) / units.Gi
+ return convert_size
+ except ValueError:
+ exception_message = (_("Failed to manage existing volume "
+ "%(name)s, because reported size "
+ "%(size)s was not a floating-point"
+ " number.")
+ % {'name': rbd_name,
+ 'size': image_size})
+ raise exception.VolumeBackendAPIException(
+ data=exception_message)