diff --git a/cinder/tests/unit/volume/drivers/test_fujitsu_dx.py b/cinder/tests/unit/volume/drivers/test_fujitsu_dx.py index 7b5807e46d0..d700fb087f3 100644 --- a/cinder/tests/unit/volume/drivers/test_fujitsu_dx.py +++ b/cinder/tests/unit/volume/drivers/test_fujitsu_dx.py @@ -19,6 +19,7 @@ from unittest import mock from oslo_utils import units +from cinder import context from cinder import exception from cinder import ssh_utils from cinder.tests.unit import test @@ -178,7 +179,7 @@ FAKE_POOLS = [{ }] FAKE_STATS = { - 'driver_version': '1.4.3', + 'driver_version': '1.4.4', 'storage_protocol': 'iSCSI', 'vendor_name': 'FUJITSU', 'QoS_support': True, @@ -188,7 +189,7 @@ FAKE_STATS = { 'pools': FAKE_POOLS, } FAKE_STATS2 = { - 'driver_version': '1.4.3', + 'driver_version': '1.4.4', 'storage_protocol': 'FC', 'vendor_name': 'FUJITSU', 'QoS_support': True, @@ -198,7 +199,6 @@ FAKE_STATS2 = { 'pools': FAKE_POOLS, } - # Volume1 in pool abcd1234_TPP FAKE_KEYBIND1 = { 'SystemName': STORAGE_SYSTEM, @@ -1007,6 +1007,8 @@ class FJFCDriverTestCase(test.TestCase): driver = dx_fc.FJDXFCDriver(configuration=self.configuration) self.driver = driver + self.context = context.get_admin_context() + def fake_exec_cli_with_eternus(self, exec_cmdline): if exec_cmdline == "show users": ret = ('\r\nCLI> %s\r\n00\r\n' @@ -1219,6 +1221,42 @@ class FJFCDriverTestCase(test.TestCase): self.assertEqual(FAKE_MODEL_INFO_QOS, model_info) self.driver.common._set_qos.assert_called() + def test_update_migrated_volume(self): + model_info = self.driver.create_volume(TEST_VOLUME) + self.assertEqual(FAKE_MODEL_INFO1, model_info) + + volume_info = {} + for key in TEST_VOLUME: + if key == 'provider_location': + volume_info[key] = model_info[key] + elif key == 'metadata': + volume_info[key] = model_info[key] + else: + volume_info[key] = TEST_VOLUME[key] + + model_info2 = self.driver.create_volume(TEST_VOLUME2) + self.assertEqual(FAKE_MODEL_INFO3, model_info2) + + volume_info2 = {} + for key in TEST_VOLUME: + if key == 'provider_location': + volume_info2[key] = model_info2[key] + elif key == 'metadata': + volume_info2[key] = model_info2[key] + else: + volume_info2[key] = TEST_VOLUME2[key] + + model_update = self.driver.update_migrated_volume(self.context, + volume_info, + volume_info2, + 'available') + + FAKE_MIGRATED_MODEL_UPDATE = { + '_name_id': TEST_VOLUME2['id'], + 'provider_location': model_info2['provider_location'] + } + self.assertEqual(FAKE_MIGRATED_MODEL_UPDATE, model_update) + class FJISCSIDriverTestCase(test.TestCase): def __init__(self, *args, **kwargs): @@ -1260,6 +1298,8 @@ class FJISCSIDriverTestCase(test.TestCase): driver = dx_iscsi.FJDXISCSIDriver(configuration=self.configuration) self.driver = driver + self.context = context.get_admin_context() + def fake_exec_cli_with_eternus(self, exec_cmdline): if exec_cmdline == "show users": ret = ('\r\nCLI> %s\r\n00\r\n' @@ -1471,6 +1511,42 @@ class FJISCSIDriverTestCase(test.TestCase): self.assertEqual(FAKE_MODEL_INFO_QOS, model_info) self.driver.common._set_qos.assert_called() + def test_update_migrated_volume(self): + model_info = self.driver.create_volume(TEST_VOLUME) + self.assertEqual(FAKE_MODEL_INFO1, model_info) + + volume_info = {} + for key in TEST_VOLUME: + if key == 'provider_location': + volume_info[key] = model_info[key] + elif key == 'metadata': + volume_info[key] = model_info[key] + else: + volume_info[key] = TEST_VOLUME[key] + + model_info2 = self.driver.create_volume(TEST_VOLUME2) + self.assertEqual(FAKE_MODEL_INFO3, model_info2) + + volume_info2 = {} + for key in TEST_VOLUME: + if key == 'provider_location': + volume_info2[key] = model_info2[key] + elif key == 'metadata': + volume_info2[key] = model_info2[key] + else: + volume_info2[key] = TEST_VOLUME2[key] + + model_update = self.driver.update_migrated_volume(self.context, + volume_info, + volume_info2, + 'available') + + FAKE_MIGRATED_MODEL_UPDATE = { + '_name_id': TEST_VOLUME2['id'], + 'provider_location': model_info2['provider_location'] + } + self.assertEqual(FAKE_MIGRATED_MODEL_UPDATE, model_update) + class FJCLITestCase(test.TestCase): def __init__(self, *args, **kwargs): @@ -1765,6 +1841,8 @@ class FJCommonTestCase(test.TestCase): driver = dx_iscsi.FJDXISCSIDriver(configuration=self.configuration) self.driver = driver + self.context = context.get_admin_context() + def fake_exec_cli_with_eternus(self, exec_cmdline): if exec_cmdline == "show users": ret = ('\r\nCLI> %s\r\n00\r\n' @@ -1935,3 +2013,38 @@ class FJCommonTestCase(test.TestCase): }] copy_session_list = self.driver.common._get_copy_sessions_list() self.assertEqual(FAKE_COPY_SESSION, copy_session_list) + + def test_update_migrated_volume(self): + model_info = self.driver.create_volume(TEST_VOLUME) + self.assertEqual(FAKE_MODEL_INFO1, model_info) + + volume_info = {} + for key in TEST_VOLUME: + if key == 'provider_location': + volume_info[key] = model_info[key] + elif key == 'metadata': + volume_info[key] = model_info[key] + else: + volume_info[key] = TEST_VOLUME[key] + + model_info2 = self.driver.create_volume(TEST_VOLUME2) + self.assertEqual(FAKE_MODEL_INFO3, model_info2) + + volume_info2 = {} + for key in TEST_VOLUME: + if key == 'provider_location': + volume_info2[key] = model_info2[key] + elif key == 'metadata': + volume_info2[key] = model_info2[key] + else: + volume_info2[key] = TEST_VOLUME2[key] + + model_update = self.driver.common.update_migrated_volume(self.context, + volume_info, + volume_info2) + + FAKE_MIGRATED_MODEL_UPDATE = { + '_name_id': TEST_VOLUME2['id'], + 'provider_location': model_info2['provider_location'] + } + self.assertEqual(FAKE_MIGRATED_MODEL_UPDATE, model_update) diff --git a/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_common.py b/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_common.py index 5dc53e63c43..b513268cd35 100644 --- a/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_common.py +++ b/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_common.py @@ -69,10 +69,11 @@ class FJDXCommon(object): 1.4.1 - Add the method for expanding RAID volumes by CLI. 1.4.2 - Add the secondary check for copy-sessions when deleting volumes. 1.4.3 - Add fragment capacity information of RAID Group. + 1.4.4 - Add support for update migrated volume. """ - VERSION = "1.4.3" + VERSION = "1.4.4" stats = { 'driver_version': VERSION, 'storage_protocol': None, @@ -2595,6 +2596,34 @@ class FJDXCommon(object): {'poolname': poolname, 'target_pool': target_pool}) return poolname, target_pool + def update_migrated_volume(self, ctxt, volume, new_volume): + """Update migrated volume.""" + LOG.debug('update_migrated_volume, ' + 'source volume id: %(s_id)s, ' + 'target volume id: %(t_id)s.', + {'s_id': volume['id'], 't_id': new_volume['id']}) + + model_update = None + + dst_metadata = self.get_metadata(new_volume) + src_metadata = self.get_metadata(volume) + + LOG.debug('source: (%(src_meta)s)(%(src_loc)s), ' + 'target: (%(dst_meta)s)(%(dst_loc)s).', + {'src_meta': src_metadata, + 'src_loc': volume['provider_location'], + 'dst_meta': dst_metadata, + 'dst_loc': new_volume['provider_location']}) + + if volume['provider_location']: + dst_location = new_volume['provider_location'] + model_update = {'_name_id': new_volume['id'], + 'provider_location': dst_location} + + LOG.debug('update_migrated_volume, model_update: %s.', + model_update) + return model_update + def _get_eternus_model(self): """Get ENTERNUS model.""" self.conn = self._get_eternus_connection() diff --git a/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_fc.py b/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_fc.py index 2f207c6c6c1..248d95bd614 100644 --- a/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_fc.py +++ b/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_fc.py @@ -173,6 +173,22 @@ class FJDXFCDriver(driver.FibreChannelDriver): LOG.debug('extend_volume, ' 'used pool name: %s, Exit method.', used_pool_name) + def update_migrated_volume(self, ctxt, volume, new_volume, + original_volume_status): + """Update migrated volume.""" + LOG.debug('update_migrated_volume, ' + 'source volume id: %(s_id)s, ' + 'target volume id: %(t_id)s, Enter method.', + {'s_id': volume['id'], 't_id': new_volume['id']}) + + model_update = self.common.update_migrated_volume( + ctxt, volume, new_volume) + + LOG.debug('update_migrated_volume, ' + 'target volume meta: %s, Exit method.', model_update) + + return model_update + def _get_metadata(self, volume): v_metadata = volume.get('volume_metadata') if v_metadata: diff --git a/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_iscsi.py b/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_iscsi.py index 82bd1a07c20..dd8d71f36bf 100644 --- a/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_iscsi.py +++ b/cinder/volume/drivers/fujitsu/eternus_dx/eternus_dx_iscsi.py @@ -159,3 +159,19 @@ class FJDXISCSIDriver(driver.ISCSIDriver): LOG.debug('extend_volume, ' 'used pool name: %s, Exit method.', used_pool_name) + + def update_migrated_volume(self, ctxt, volume, new_volume, + original_volume_status): + """Update migrated volume.""" + LOG.debug('update_migrated_volume, ' + 'source volume id: %(s_id)s, ' + 'target volume id: %(t_id)s, Enter method.', + {'s_id': volume['id'], 't_id': new_volume['id']}) + + model_update = self.common.update_migrated_volume( + ctxt, volume, new_volume) + + LOG.debug('update_migrated_volume, ' + 'target volume meta: %s, Exit method.', model_update) + + return model_update diff --git a/doc/source/configuration/block-storage/drivers/fujitsu-eternus-dx-driver.rst b/doc/source/configuration/block-storage/drivers/fujitsu-eternus-dx-driver.rst index a1cb4535e92..957ceb66313 100644 --- a/doc/source/configuration/block-storage/drivers/fujitsu-eternus-dx-driver.rst +++ b/doc/source/configuration/block-storage/drivers/fujitsu-eternus-dx-driver.rst @@ -46,6 +46,7 @@ Supported operations * Clone a volume. * Extend a volume. * Get volume statistics. +* Migrate Volume. Preparation ~~~~~~~~~~~ @@ -241,6 +242,65 @@ Configuration example and the type ``DX_ISCSI`` is associated with the ``ISCSI``. +Supported Functions of the ETERNUS OpenStack VolumeDriver +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Migrate Volume +-------------- + +Moves volumes to a different storage pool. + +#. ETERNUS AF/DX functions + + * Creates migration destination volumes / deletes migration + source volumes. + + * Sets access paths to migration volumes / deletes migration + access paths to migration source volumes. + + * Uses Create Volume, Delete Volume, Attach Volume and Detach + Volume. + +#. Cinder operation + + * Copies data in the migration source volume to the migration + destination volume. + +.. note:: + + Host information must be specified in Migrated Volume. + + The input format is as follows: + + ``Host-Name@Backend-Name#Pool-Name`` + + For the following environment or settings, specify + ``test.localhost@Backend1#PoolA`` for the host. + + * PoolA is a pool specified in ``/etc/cinder/cinder_fujitsu_eternus_dx.xml``. + + .. code-block:: console + + $ hostname + test.localhost + + $ cat /etc/cinder/cinder.conf + (snip) + [Backend1] + volume_driver=cinder.volume.drivers.fujitsu.eternus_dx.eternus_dx_fc.FJDXFCDriver + cinder_eternus_config_file = /etc/cinder/cinder_fujitsu_eternus_dx.xml + volume_backend_name=volume_backend_name1 + +.. warning:: + + There are some restrictions for volume migration: + + #. You cannot migrate a volume that has snapshots. + + #. You cannot use driver-assisted migration to move a volume to or from a + backend that does not use the ETERNUS OpenStack volume driver. + + Supplementary Information for the Supported Functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/releasenotes/notes/fujitsu-update-migrated-volume-1d205cdbd7e65a28.yaml b/releasenotes/notes/fujitsu-update-migrated-volume-1d205cdbd7e65a28.yaml new file mode 100644 index 00000000000..d84913e02b6 --- /dev/null +++ b/releasenotes/notes/fujitsu-update-migrated-volume-1d205cdbd7e65a28.yaml @@ -0,0 +1,10 @@ +--- +features: + - | + Fujitsu ETERNUS DX driver: Added support for update migrated volume + + Now we update the required values to successfully complete the migration. + + See the `Fujitsu ETERNUS DX driver documentation + `_ + for details.