Merge "Infinidat: add storage assisted volume migration"

This commit is contained in:
Zuul 2023-01-28 20:49:40 +00:00 committed by Gerrit Code Review
commit 7a312ab1a8
4 changed files with 131 additions and 7 deletions

View File

@ -52,11 +52,16 @@ TEST_TARGET_PORTAL4 = '{}:{}'.format(TEST_IP_ADDRESS4, TEST_ISCSI_TCP_PORT2)
TEST_FC_PROTOCOL = 'fc'
TEST_ISCSI_PROTOCOL = 'iscsi'
TEST_VOLUME_SOURCE_NAME = 'test-volume'
TEST_VOLUME_TYPE = 'MASTER'
TEST_VOLUME_SOURCE_ID = 12345
TEST_VOLUME_METADATA = {'cinder_id': fake.VOLUME_ID}
TEST_SNAPSHOT_SOURCE_NAME = 'test-snapshot'
TEST_SNAPSHOT_SOURCE_ID = 67890
TEST_SNAPSHOT_METADATA = {'cinder_id': fake.SNAPSHOT_ID}
TEST_POOL_NAME = 'pool'
TEST_POOL_NAME2 = 'pool2'
TEST_SYSTEM_SERIAL = 123
TEST_SYSTEM_SERIAL2 = 456
test_volume = mock.Mock(id=fake.VOLUME_ID, name_id=fake.VOLUME_ID, size=1,
volume_type_id=fake.VOLUME_TYPE_ID, group_id=None,
@ -113,7 +118,7 @@ class InfiniboxDriverTestCaseBase(test.TestCase):
configuration.SHARED_CONF_GROUP)
self.override_config('san_password', 'password',
configuration.SHARED_CONF_GROUP)
self.override_config('infinidat_pool_name', 'pool')
self.override_config('infinidat_pool_name', TEST_POOL_NAME)
self.driver = infinidat.InfiniboxVolumeDriver(
configuration=self.configuration)
self._system = self._infinibox_mock()
@ -139,9 +144,8 @@ class InfiniboxDriverTestCaseBase(test.TestCase):
self._mock_new_volume = mock.Mock()
self._mock_volume.get_id.return_value = TEST_VOLUME_SOURCE_ID
self._mock_volume.get_name.return_value = TEST_VOLUME_SOURCE_NAME
self._mock_volume.get_type.return_value = 'MASTER'
self._mock_volume.get_pool_name.return_value = (
self.configuration.infinidat_pool_name)
self._mock_volume.get_type.return_value = TEST_VOLUME_TYPE
self._mock_volume.get_pool_name.return_value = TEST_POOL_NAME
self._mock_volume.get_size.return_value = 1 * units.Gi
self._mock_volume.has_children.return_value = False
self._mock_volume.get_qos_policy.return_value = None
@ -180,6 +184,7 @@ class InfiniboxDriverTestCaseBase(test.TestCase):
result.components.nodes.get_all.return_value = []
result.qos_policies.create.return_value = self._mock_qos_policy
result.qos_policies.safe_get.return_value = None
result.get_serial.return_value = TEST_SYSTEM_SERIAL
return result
def _raise_infinisdk(self, *args, **kwargs):
@ -797,7 +802,7 @@ class InfiniboxDriverTestCase(InfiniboxDriverTestCaseBase):
@mock.patch('cinder.volume.volume_types.get_volume_type_qos_specs')
def test_manage_existing_invalid_pool(self, *mocks):
existing_ref = {'source-name': TEST_VOLUME_SOURCE_NAME}
self._mock_volume.get_pool_name.return_value = 'invalid'
self._mock_volume.get_pool_name.return_value = TEST_POOL_NAME2
self.assertRaises(exception.InvalidConfigurationValue,
self.driver.manage_existing,
test_volume, existing_ref)
@ -913,7 +918,7 @@ class InfiniboxDriverTestCase(InfiniboxDriverTestCaseBase):
@mock.patch('cinder.volume.volume_types.get_volume_type_qos_specs')
def test_manage_existing_snapshot_invalid_pool(self, *mocks):
existing_ref = {'source-name': TEST_SNAPSHOT_SOURCE_NAME}
self._mock_volume.get_pool_name.return_value = 'invalid'
self._mock_volume.get_pool_name.return_value = TEST_POOL_NAME2
self.assertRaises(exception.InvalidConfigurationValue,
self.driver.manage_existing_snapshot,
test_snapshot, existing_ref)
@ -1049,6 +1054,72 @@ class InfiniboxDriverTestCase(InfiniboxDriverTestCaseBase):
test_volume)
self.assertEqual(0, self._log.error.call_count)
@ddt.data(None, {})
def test_migrate_volume_no_host(self, host):
expected = False, None
update = self.driver.migrate_volume(None, test_volume, host)
self.assertEqual(expected, update)
@ddt.data(None, {})
def test_migrate_volume_no_capabilities(self, capabilities):
expected = False, None
host = {'capabilities': capabilities}
update = self.driver.migrate_volume(None, test_volume, host)
self.assertEqual(expected, update)
@ddt.data(None, 123, 'location')
def test_migrate_volume_invalid_location_info(self, location_info):
expected = False, None
capabilities = {'location_info': location_info}
host = {'capabilities': capabilities}
update = self.driver.migrate_volume(None, test_volume, host)
self.assertEqual(expected, update)
def test_migrate_volume_invalid_driver(self):
expected = False, None
location_info = 'vendor:0:/path'
capabilities = {'location_info': location_info}
host = {'capabilities': capabilities}
update = self.driver.migrate_volume(None, test_volume, host)
self.assertEqual(expected, update)
def test_migrate_volume_invalid_serial(self):
expected = False, None
location_info = '%s:%s:%s' % (self.driver.__class__.__name__,
TEST_SYSTEM_SERIAL2, TEST_POOL_NAME2)
capabilities = {'location_info': location_info}
host = {'capabilities': capabilities}
update = self.driver.migrate_volume(None, test_volume, host)
self.assertEqual(expected, update)
def test_migrate_volume_same_pool(self):
expected = True, None
location_info = '%s:%s:%s' % (self.driver.__class__.__name__,
TEST_SYSTEM_SERIAL, TEST_POOL_NAME)
capabilities = {'location_info': location_info}
host = {'capabilities': capabilities}
update = self.driver.migrate_volume(None, test_volume, host)
self.assertEqual(expected, update)
def test_migrate_volume_no_pool(self):
expected = False, None
self._system.pools.safe_get.return_value = None
location_info = '%s:%s:%s' % (self.driver.__class__.__name__,
TEST_SYSTEM_SERIAL, TEST_POOL_NAME2)
capabilities = {'location_info': location_info}
host = {'capabilities': capabilities}
update = self.driver.migrate_volume(None, test_volume, host)
self.assertEqual(expected, update)
def test_migrate_volume(self):
expected = True, None
location_info = '%s:%s:%s' % (self.driver.__class__.__name__,
TEST_SYSTEM_SERIAL, TEST_POOL_NAME2)
capabilities = {'location_info': location_info}
host = {'capabilities': capabilities}
update = self.driver.migrate_volume(None, test_volume, host)
self.assertEqual(expected, update)
@ddt.ddt
class InfiniboxDriverTestCaseFC(InfiniboxDriverTestCaseBase):

View File

@ -128,10 +128,11 @@ class InfiniboxVolumeDriver(san.SanISCSIDriver):
1.11 - fixed generic volume migration
1.12 - fixed volume multi-attach
1.13 - fixed consistency groups feature
1.14 - added storage assisted volume migration
"""
VERSION = '1.13'
VERSION = '1.14'
# ThirdPartySystems wiki page
CI_WIKI_NAME = "INFINIDAT_CI"
@ -573,6 +574,10 @@ class InfiniboxVolumeDriver(san.SanISCSIDriver):
def get_volume_stats(self, refresh=False):
if self._volume_stats is None or refresh:
pool = self._get_infinidat_pool()
location_info = '%(driver)s:%(serial)s:%(pool)s' % {
'driver': self.__class__.__name__,
'serial': self._system.get_serial(),
'pool': self.configuration.infinidat_pool_name}
free_capacity_bytes = (pool.get_free_physical_capacity() /
capacity.byte)
physical_capacity_bytes = (pool.get_physical_capacity() /
@ -587,6 +592,7 @@ class InfiniboxVolumeDriver(san.SanISCSIDriver):
vendor_name=VENDOR_NAME,
driver_version=self.VERSION,
storage_protocol=self._protocol,
location_info=location_info,
consistencygroup_support=False,
total_capacity_gb=total_capacity_gb,
free_capacity_gb=free_capacity_gb,
@ -1325,3 +1331,44 @@ class InfiniboxVolumeDriver(san.SanISCSIDriver):
new_volume_name, volume_name, error)
return model_update
return {'_name_id': None, 'provider_location': None}
@infinisdk_to_cinder_exceptions
def migrate_volume(self, ctxt, volume, host):
"""Migrate a volume within the same InfiniBox system."""
LOG.debug('Starting volume migration for volume %s to host %s',
volume.name, host)
if not (host and 'capabilities' in host):
LOG.error('No capabilities found for host %s', host)
return False, None
capabilities = host['capabilities']
if not (capabilities and 'location_info' in capabilities):
LOG.error('No location info found for host %s', host)
return False, None
location = capabilities['location_info']
try:
driver, serial, pool = location.split(':')
serial = int(serial)
except (AttributeError, ValueError) as error:
LOG.error('Invalid location info %s found for host %s: %s',
location, host, error)
return False, None
if driver != self.__class__.__name__:
LOG.debug('Unsupported storage driver %s found for host %s',
driver, host)
return False, None
if serial != self._system.get_serial():
LOG.error('Unable to migrate volume %s to remote host %s',
volume.name, host)
return False, None
infinidat_volume = self._get_infinidat_volume(volume)
if pool == infinidat_volume.get_pool_name():
LOG.debug('Volume %s already migrated to pool %s',
volume.name, pool)
return True, None
infinidat_pool = self._system.pools.safe_get(name=pool)
if infinidat_pool is None:
LOG.error('Destination pool %s not found on host %s', pool, host)
return False, None
infinidat_volume.move_pool(infinidat_pool)
LOG.info('Migrated volume %s to pool %s', volume.name, pool)
return True, None

View File

@ -26,6 +26,7 @@ Supported operations
* Manage and unmanage volumes and snapshots.
* List manageable volumes and snapshots.
* Attach a volume to multiple instances at once (multi-attach).
* Host and storage assisted volume migration.
External package installation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,5 @@
---
features:
- |
Infinidat: Added support for storage assisted volume migration
within a same InfiniBox host (iSCSI and FC).