Fujitsu Driver: Support for update migrated volume
After migrated volume is completed, cinder invokes function 'update_migrated_volume'. It updates provider_location, which possesses the new volume ID, to match the provider_location of original volume. Change-Id: I1fe29d077a657963016a581b6c87642a62979653
This commit is contained in:
@@ -19,6 +19,7 @@ from unittest import mock
|
|||||||
|
|
||||||
from oslo_utils import units
|
from oslo_utils import units
|
||||||
|
|
||||||
|
from cinder import context
|
||||||
from cinder import exception
|
from cinder import exception
|
||||||
from cinder import ssh_utils
|
from cinder import ssh_utils
|
||||||
from cinder.tests.unit import test
|
from cinder.tests.unit import test
|
||||||
@@ -178,7 +179,7 @@ FAKE_POOLS = [{
|
|||||||
}]
|
}]
|
||||||
|
|
||||||
FAKE_STATS = {
|
FAKE_STATS = {
|
||||||
'driver_version': '1.4.3',
|
'driver_version': '1.4.4',
|
||||||
'storage_protocol': 'iSCSI',
|
'storage_protocol': 'iSCSI',
|
||||||
'vendor_name': 'FUJITSU',
|
'vendor_name': 'FUJITSU',
|
||||||
'QoS_support': True,
|
'QoS_support': True,
|
||||||
@@ -188,7 +189,7 @@ FAKE_STATS = {
|
|||||||
'pools': FAKE_POOLS,
|
'pools': FAKE_POOLS,
|
||||||
}
|
}
|
||||||
FAKE_STATS2 = {
|
FAKE_STATS2 = {
|
||||||
'driver_version': '1.4.3',
|
'driver_version': '1.4.4',
|
||||||
'storage_protocol': 'FC',
|
'storage_protocol': 'FC',
|
||||||
'vendor_name': 'FUJITSU',
|
'vendor_name': 'FUJITSU',
|
||||||
'QoS_support': True,
|
'QoS_support': True,
|
||||||
@@ -198,7 +199,6 @@ FAKE_STATS2 = {
|
|||||||
'pools': FAKE_POOLS,
|
'pools': FAKE_POOLS,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Volume1 in pool abcd1234_TPP
|
# Volume1 in pool abcd1234_TPP
|
||||||
FAKE_KEYBIND1 = {
|
FAKE_KEYBIND1 = {
|
||||||
'SystemName': STORAGE_SYSTEM,
|
'SystemName': STORAGE_SYSTEM,
|
||||||
@@ -1007,6 +1007,8 @@ class FJFCDriverTestCase(test.TestCase):
|
|||||||
driver = dx_fc.FJDXFCDriver(configuration=self.configuration)
|
driver = dx_fc.FJDXFCDriver(configuration=self.configuration)
|
||||||
self.driver = driver
|
self.driver = driver
|
||||||
|
|
||||||
|
self.context = context.get_admin_context()
|
||||||
|
|
||||||
def fake_exec_cli_with_eternus(self, exec_cmdline):
|
def fake_exec_cli_with_eternus(self, exec_cmdline):
|
||||||
if exec_cmdline == "show users":
|
if exec_cmdline == "show users":
|
||||||
ret = ('\r\nCLI> %s\r\n00\r\n'
|
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.assertEqual(FAKE_MODEL_INFO_QOS, model_info)
|
||||||
self.driver.common._set_qos.assert_called()
|
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):
|
class FJISCSIDriverTestCase(test.TestCase):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@@ -1260,6 +1298,8 @@ class FJISCSIDriverTestCase(test.TestCase):
|
|||||||
driver = dx_iscsi.FJDXISCSIDriver(configuration=self.configuration)
|
driver = dx_iscsi.FJDXISCSIDriver(configuration=self.configuration)
|
||||||
self.driver = driver
|
self.driver = driver
|
||||||
|
|
||||||
|
self.context = context.get_admin_context()
|
||||||
|
|
||||||
def fake_exec_cli_with_eternus(self, exec_cmdline):
|
def fake_exec_cli_with_eternus(self, exec_cmdline):
|
||||||
if exec_cmdline == "show users":
|
if exec_cmdline == "show users":
|
||||||
ret = ('\r\nCLI> %s\r\n00\r\n'
|
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.assertEqual(FAKE_MODEL_INFO_QOS, model_info)
|
||||||
self.driver.common._set_qos.assert_called()
|
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):
|
class FJCLITestCase(test.TestCase):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@@ -1765,6 +1841,8 @@ class FJCommonTestCase(test.TestCase):
|
|||||||
driver = dx_iscsi.FJDXISCSIDriver(configuration=self.configuration)
|
driver = dx_iscsi.FJDXISCSIDriver(configuration=self.configuration)
|
||||||
self.driver = driver
|
self.driver = driver
|
||||||
|
|
||||||
|
self.context = context.get_admin_context()
|
||||||
|
|
||||||
def fake_exec_cli_with_eternus(self, exec_cmdline):
|
def fake_exec_cli_with_eternus(self, exec_cmdline):
|
||||||
if exec_cmdline == "show users":
|
if exec_cmdline == "show users":
|
||||||
ret = ('\r\nCLI> %s\r\n00\r\n'
|
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()
|
copy_session_list = self.driver.common._get_copy_sessions_list()
|
||||||
self.assertEqual(FAKE_COPY_SESSION, copy_session_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)
|
||||||
|
|||||||
@@ -69,10 +69,11 @@ class FJDXCommon(object):
|
|||||||
1.4.1 - Add the method for expanding RAID volumes by CLI.
|
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.2 - Add the secondary check for copy-sessions when deleting volumes.
|
||||||
1.4.3 - Add fragment capacity information of RAID Group.
|
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 = {
|
stats = {
|
||||||
'driver_version': VERSION,
|
'driver_version': VERSION,
|
||||||
'storage_protocol': None,
|
'storage_protocol': None,
|
||||||
@@ -2595,6 +2596,34 @@ class FJDXCommon(object):
|
|||||||
{'poolname': poolname, 'target_pool': target_pool})
|
{'poolname': poolname, 'target_pool': target_pool})
|
||||||
return poolname, 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):
|
def _get_eternus_model(self):
|
||||||
"""Get ENTERNUS model."""
|
"""Get ENTERNUS model."""
|
||||||
self.conn = self._get_eternus_connection()
|
self.conn = self._get_eternus_connection()
|
||||||
|
|||||||
@@ -173,6 +173,22 @@ class FJDXFCDriver(driver.FibreChannelDriver):
|
|||||||
LOG.debug('extend_volume, '
|
LOG.debug('extend_volume, '
|
||||||
'used pool name: %s, Exit method.', used_pool_name)
|
'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):
|
def _get_metadata(self, volume):
|
||||||
v_metadata = volume.get('volume_metadata')
|
v_metadata = volume.get('volume_metadata')
|
||||||
if v_metadata:
|
if v_metadata:
|
||||||
|
|||||||
@@ -159,3 +159,19 @@ class FJDXISCSIDriver(driver.ISCSIDriver):
|
|||||||
|
|
||||||
LOG.debug('extend_volume, '
|
LOG.debug('extend_volume, '
|
||||||
'used pool name: %s, Exit method.', used_pool_name)
|
'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
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ Supported operations
|
|||||||
* Clone a volume.
|
* Clone a volume.
|
||||||
* Extend a volume.
|
* Extend a volume.
|
||||||
* Get volume statistics.
|
* Get volume statistics.
|
||||||
|
* Migrate Volume.
|
||||||
|
|
||||||
Preparation
|
Preparation
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
@@ -241,6 +242,65 @@ Configuration example
|
|||||||
and the type ``DX_ISCSI`` is associated with the ``ISCSI``.
|
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
|
Supplementary Information for the Supported Functions
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
<https://docs.openstack.org/cinder/latest/configuration/block-storage/drivers/fujitsu-eternus-dx-driver.html>`_
|
||||||
|
for details.
|
||||||
Reference in New Issue
Block a user