Volume Manage/Unmanage Support for IBM FlashSystem
This patch introduce volume manage/unmanage feature for IBM FlashSystem. In this patch, * Add volume manage/unmanage feature for IBM FlashSystem driver. * Bump version to 1.0.12. DocImpact Change-Id: I97ad90756674851a4940015443394e7c99c3d9cb Implements: blueprint ibm-flashsystem-manage-unmanage
This commit is contained in:
parent
e89b0179e4
commit
5242d1f09f
@ -476,6 +476,24 @@ class FlashSystemManagementSimulator(object):
|
||||
return ('Virtual Disk, id [%s], successfully created' %
|
||||
(vdisk_info['id']), '')
|
||||
|
||||
def _cmd_chvdisk(self, **kwargs):
|
||||
"""chvdisk command
|
||||
|
||||
svcask chvdisk -name <new_name_arg> -udid <vdisk_udid>
|
||||
-open_access_scsi_id <vdisk_scsi_id> <vdisk_name> <vdisk_id>
|
||||
"""
|
||||
|
||||
if 'obj' not in kwargs:
|
||||
return self._errors['CMMVC50000']
|
||||
|
||||
source_name = kwargs['obj'].strip('\'\"')
|
||||
dest_name = kwargs['name'].strip('\'\"')
|
||||
vol = self._volumes_list[source_name]
|
||||
vol['name'] = dest_name
|
||||
del self._volumes_list[source_name]
|
||||
self._volumes_list[dest_name] = vol
|
||||
return ('', '')
|
||||
|
||||
def _cmd_rmvdisk(self, **kwargs):
|
||||
"""svctask rmvdisk -force <vdisk_name>"""
|
||||
|
||||
@ -1270,3 +1288,78 @@ class FlashSystemDriverTestCase(test.TestCase):
|
||||
|
||||
# case 4: If there is no vdisk mapped to host, host should be removed
|
||||
self.assertIsNone(self.driver._get_host_from_connector(self.connector))
|
||||
|
||||
def test_flashsystem_manage_existing(self):
|
||||
# case 1: manage a vdisk good path
|
||||
kwargs = {'name': u'unmanage-vol-01', 'size': u'1', 'unit': 'gb'}
|
||||
self.sim._cmd_mkvdisk(**kwargs)
|
||||
vol1 = self._generate_vol_info(None)
|
||||
existing_ref = {'source-name': u'unmanage-vol-01'}
|
||||
self.driver.manage_existing(vol1, existing_ref)
|
||||
self.driver.delete_volume(vol1)
|
||||
|
||||
# case 2: manage a vdisk not exist
|
||||
vol1 = self._generate_vol_info(None)
|
||||
existing_ref = {'source-name': u'unmanage-vol-01'}
|
||||
self.assertRaises(exception.ManageExistingInvalidReference,
|
||||
self.driver.manage_existing, vol1, existing_ref)
|
||||
|
||||
# case 3: manage a vdisk without name and uid
|
||||
kwargs = {'name': u'unmanage-vol-01', 'size': u'1', 'unit': 'gb'}
|
||||
self.sim._cmd_mkvdisk(**kwargs)
|
||||
vol1 = self._generate_vol_info(None)
|
||||
existing_ref = {}
|
||||
self.assertRaises(exception.ManageExistingInvalidReference,
|
||||
self.driver.manage_existing, vol1, existing_ref)
|
||||
vdisk1 = {'obj': u'unmanage-vol-01'}
|
||||
self.sim._cmd_rmvdisk(**vdisk1)
|
||||
|
||||
@mock.patch.object(flashsystem_fc.FlashSystemFCDriver,
|
||||
'_get_vdiskhost_mappings')
|
||||
def test_flashsystem_manage_existing_get_size_mapped(
|
||||
self,
|
||||
_get_vdiskhost_mappings_mock):
|
||||
# manage a vdisk with mappings
|
||||
_get_vdiskhost_mappings_mock.return_value = {'mapped': u'yes'}
|
||||
kwargs = {'name': u'unmanage-vol-01', 'size': u'1', 'unit': 'gb'}
|
||||
self.sim._cmd_mkvdisk(**kwargs)
|
||||
vol1 = self._generate_vol_info(None)
|
||||
existing_ref = {'source-name': u'unmanage-vol-01'}
|
||||
self.assertRaises(exception.ManageExistingInvalidReference,
|
||||
self.driver.manage_existing_get_size,
|
||||
vol1,
|
||||
existing_ref)
|
||||
|
||||
# clean environment
|
||||
vdisk1 = {'obj': u'unmanage-vol-01'}
|
||||
self.sim._cmd_rmvdisk(**vdisk1)
|
||||
|
||||
def test_flashsystem_manage_existing_get_size_bad_ref(self):
|
||||
# bad existing_ref
|
||||
vol1 = self._generate_vol_info(None, None)
|
||||
existing_ref = {}
|
||||
self.assertRaises(exception.ManageExistingInvalidReference,
|
||||
self.driver.manage_existing_get_size, vol1,
|
||||
existing_ref)
|
||||
|
||||
def test_flashsystem_manage_existing_get_size_vdisk_not_exist(self):
|
||||
# vdisk not exist
|
||||
vol1 = self._generate_vol_info(None)
|
||||
existing_ref = {'source-name': u'unmanage-vol-01'}
|
||||
self.assertRaises(exception.ManageExistingInvalidReference,
|
||||
self.driver.manage_existing_get_size,
|
||||
vol1,
|
||||
existing_ref)
|
||||
|
||||
def test_flashsystem_manage_existing_get_size(self):
|
||||
# good path
|
||||
kwargs = {'name': u'unmanage-vol-01', 'size': u'10001', 'unit': 'gb'}
|
||||
self.sim._cmd_mkvdisk(**kwargs)
|
||||
vol1 = self._generate_vol_info(None)
|
||||
existing_ref = {'source-name': u'unmanage-vol-01'}
|
||||
vdisk_size = self.driver.manage_existing_get_size(vol1, existing_ref)
|
||||
self.assertEqual(10001, vdisk_size)
|
||||
|
||||
# clean environment
|
||||
vdisk1 = {'obj': u'unmanage-vol-01'}
|
||||
self.sim._cmd_rmvdisk(**vdisk1)
|
||||
|
@ -344,3 +344,78 @@ class FlashSystemISCSIDriverTestCase(test.TestCase):
|
||||
self.driver._delete_host(host2)
|
||||
self.sim.set_protocol('iSCSI')
|
||||
self._reset_flags()
|
||||
|
||||
def test_flashsystem_manage_existing(self):
|
||||
# case 1: manage a vdisk good path
|
||||
kwargs = {'name': u'unmanage-vol-01', 'size': u'1', 'unit': 'gb'}
|
||||
self.sim._cmd_mkvdisk(**kwargs)
|
||||
vol1 = self._generate_vol_info(None)
|
||||
existing_ref = {'source-name': u'unmanage-vol-01'}
|
||||
self.driver.manage_existing(vol1, existing_ref)
|
||||
self.driver.delete_volume(vol1)
|
||||
|
||||
# case 2: manage a vdisk not exist
|
||||
vol1 = self._generate_vol_info(None)
|
||||
existing_ref = {'source-name': u'unmanage-vol-01'}
|
||||
self.assertRaises(exception.ManageExistingInvalidReference,
|
||||
self.driver.manage_existing, vol1, existing_ref)
|
||||
|
||||
# case 3: manage a vdisk without name and uid
|
||||
kwargs = {'name': u'unmanage-vol-01', 'size': u'1', 'unit': 'gb'}
|
||||
self.sim._cmd_mkvdisk(**kwargs)
|
||||
vol1 = self._generate_vol_info(None)
|
||||
existing_ref = {}
|
||||
self.assertRaises(exception.ManageExistingInvalidReference,
|
||||
self.driver.manage_existing, vol1, existing_ref)
|
||||
vdisk1 = {'obj': u'unmanage-vol-01'}
|
||||
self.sim._cmd_rmvdisk(**vdisk1)
|
||||
|
||||
@mock.patch.object(flashsystem_iscsi.FlashSystemISCSIDriver,
|
||||
'_get_vdiskhost_mappings')
|
||||
def test_flashsystem_manage_existing_get_size_mapped(
|
||||
self,
|
||||
_get_vdiskhost_mappings_mock):
|
||||
# case 2: manage a vdisk with mappings
|
||||
_get_vdiskhost_mappings_mock.return_value = {'mapped': u'yes'}
|
||||
kwargs = {'name': u'unmanage-vol-01', 'size': u'1', 'unit': 'gb'}
|
||||
self.sim._cmd_mkvdisk(**kwargs)
|
||||
vol1 = self._generate_vol_info(None)
|
||||
existing_ref = {'source-name': u'unmanage-vol-01'}
|
||||
self.assertRaises(exception.ManageExistingInvalidReference,
|
||||
self.driver.manage_existing_get_size,
|
||||
vol1,
|
||||
existing_ref)
|
||||
|
||||
# clean environment
|
||||
vdisk1 = {'obj': u'unmanage-vol-01'}
|
||||
self.sim._cmd_rmvdisk(**vdisk1)
|
||||
|
||||
def test_flashsystem_manage_existing_get_size_bad_ref(self):
|
||||
# bad existing_ref
|
||||
vol1 = self._generate_vol_info(None, None)
|
||||
existing_ref = {}
|
||||
self.assertRaises(exception.ManageExistingInvalidReference,
|
||||
self.driver.manage_existing_get_size, vol1,
|
||||
existing_ref)
|
||||
|
||||
def test_flashsystem_manage_existing_get_size_vdisk_not_exist(self):
|
||||
# vdisk not exist
|
||||
vol1 = self._generate_vol_info(None)
|
||||
existing_ref = {'source-name': u'unmanage-vol-01'}
|
||||
self.assertRaises(exception.ManageExistingInvalidReference,
|
||||
self.driver.manage_existing_get_size,
|
||||
vol1,
|
||||
existing_ref)
|
||||
|
||||
def test_flashsystem_manage_existing_get_size(self):
|
||||
# good path
|
||||
kwargs = {'name': u'unmanage-vol-01', 'size': u'10001', 'unit': 'gb'}
|
||||
self.sim._cmd_mkvdisk(**kwargs)
|
||||
vol1 = self._generate_vol_info(None)
|
||||
existing_ref = {'source-name': u'unmanage-vol-01'}
|
||||
vdisk_size = self.driver.manage_existing_get_size(vol1, existing_ref)
|
||||
self.assertEqual(10001, vdisk_size)
|
||||
|
||||
# clean environment
|
||||
vdisk1 = {'obj': u'unmanage-vol-01'}
|
||||
self.sim._cmd_rmvdisk(**vdisk1)
|
||||
|
@ -64,6 +64,7 @@ CONF.register_opts(flashsystem_opts)
|
||||
|
||||
|
||||
class FlashSystemDriver(san.SanDriver,
|
||||
driver.ManageableVD,
|
||||
driver.TransferVD,
|
||||
driver.ExtendVD,
|
||||
driver.SnapshotVD,
|
||||
@ -92,9 +93,11 @@ class FlashSystemDriver(san.SanDriver,
|
||||
1.0.10 - Fix bug #1585085, add host name check in
|
||||
_find_host_exhaustive for iSCSI
|
||||
1.0.11 - Update driver to use ABC metaclasses
|
||||
1.0.12 - Update driver to support Manage/Unmanage
|
||||
existing volume
|
||||
"""
|
||||
|
||||
VERSION = "1.0.11"
|
||||
VERSION = "1.0.12"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(FlashSystemDriver, self).__init__(*args, **kwargs)
|
||||
@ -542,15 +545,17 @@ class FlashSystemDriver(san.SanDriver,
|
||||
except KeyError:
|
||||
self._handle_keyerror('lsnode', header)
|
||||
|
||||
def _get_vdisk_attributes(self, vdisk_name):
|
||||
def _get_vdisk_attributes(self, vdisk_ref):
|
||||
"""Return vdisk attributes
|
||||
|
||||
Exception is raised if the information from system can not be
|
||||
parsed/matched to a single vdisk.
|
||||
|
||||
:param vdisk_ref: vdisk name or vdisk id
|
||||
"""
|
||||
|
||||
ssh_cmd = [
|
||||
'svcinfo', 'lsvdisk', '-bytes', '-delim', '!', vdisk_name]
|
||||
'svcinfo', 'lsvdisk', '-bytes', '-delim', '!', vdisk_ref]
|
||||
|
||||
return self._execute_command_and_parse_attributes(ssh_cmd)
|
||||
|
||||
@ -700,6 +705,27 @@ class FlashSystemDriver(san.SanDriver,
|
||||
'out': six.text_type(out),
|
||||
'err': six.text_type(err)})
|
||||
|
||||
def _manage_input_check(self, existing_ref):
|
||||
"""Verify the input of manage function."""
|
||||
# Check that the reference is valid
|
||||
if 'source-name' in existing_ref:
|
||||
manage_source = existing_ref['source-name']
|
||||
vdisk = self._get_vdisk_attributes(manage_source)
|
||||
elif 'source-id' in existing_ref:
|
||||
manage_source = existing_ref['source-id']
|
||||
vdisk = self._get_vdisk_attributes(manage_source)
|
||||
else:
|
||||
reason = _('Reference must contain source-id or '
|
||||
'source-name element.')
|
||||
raise exception.ManageExistingInvalidReference(
|
||||
existing_ref=existing_ref, reason=reason)
|
||||
if vdisk is None:
|
||||
reason = (_('No vdisk with the ID specified by ref %s.')
|
||||
% manage_source)
|
||||
raise exception.ManageExistingInvalidReference(
|
||||
existing_ref=existing_ref, reason=reason)
|
||||
return vdisk
|
||||
|
||||
@utils.synchronized('flashsystem-map', external=True)
|
||||
def _map_vdisk_to_host(self, vdisk_name, connector):
|
||||
"""Create a mapping between a vdisk to a host."""
|
||||
@ -801,6 +827,26 @@ class FlashSystemDriver(san.SanDriver,
|
||||
|
||||
LOG.debug('leave: _remove_device')
|
||||
|
||||
def _rename_vdisk(self, vdisk_name, new_name):
|
||||
"""Rename vdisk"""
|
||||
# Try to rename volume only if found on the storage
|
||||
vdisk_defined = self._is_vdisk_defined(vdisk_name)
|
||||
if not vdisk_defined:
|
||||
LOG.warning(_LW('warning: Tried to rename vdisk %s but '
|
||||
'it does not exist.'), vdisk_name)
|
||||
return
|
||||
ssh_cmd = [
|
||||
'svctask', 'chvdisk', '-name', new_name, vdisk_name]
|
||||
out, err = self._ssh(ssh_cmd)
|
||||
# No output should be returned from chvdisk
|
||||
self._assert_ssh_return(
|
||||
(not out.strip()),
|
||||
'_rename_vdisk %(name)s' % {'name': vdisk_name},
|
||||
ssh_cmd, out, err)
|
||||
|
||||
LOG.info(_LI('Renamed %(vdisk)s to %(newname)s .'),
|
||||
{'vdisk': vdisk_name, 'newname': new_name})
|
||||
|
||||
def _scan_device(self, properties):
|
||||
LOG.debug('enter: _scan_device')
|
||||
|
||||
@ -1110,3 +1156,32 @@ class FlashSystemDriver(san.SanDriver,
|
||||
self._update_volume_stats()
|
||||
|
||||
return self._stats
|
||||
|
||||
def manage_existing(self, volume, existing_ref):
|
||||
"""Manages an existing vdisk.
|
||||
|
||||
Renames the vdisk to match the expected name for the volume.
|
||||
"""
|
||||
LOG.debug('enter: manage_existing: volume %(vol)s ref %(ref)s.',
|
||||
{'vol': volume, 'ref': existing_ref})
|
||||
vdisk = self._manage_input_check(existing_ref)
|
||||
new_name = 'volume-' + volume['id']
|
||||
self._rename_vdisk(vdisk['name'], new_name)
|
||||
LOG.debug('leave: manage_existing: volume %(vol)s ref %(ref)s.',
|
||||
{'vol': volume, 'ref': existing_ref})
|
||||
return
|
||||
|
||||
def manage_existing_get_size(self, volume, existing_ref):
|
||||
"""Return size of volume to be managed by manage_existing."""
|
||||
vdisk = self._manage_input_check(existing_ref)
|
||||
if self._get_vdiskhost_mappings(vdisk['name']):
|
||||
reason = _('The specified vdisk is mapped to a host.')
|
||||
raise exception.ManageExistingInvalidReference(
|
||||
existing_ref=existing_ref, reason=reason)
|
||||
return int(vdisk['capacity']) / units.Gi
|
||||
|
||||
def unmanage(self, volume):
|
||||
"""Removes the specified volume from Cinder management."""
|
||||
LOG.debug('unmanage: volume %(vol)s is no longer managed by cinder.',
|
||||
{'vol': volume})
|
||||
pass
|
||||
|
@ -79,9 +79,11 @@ class FlashSystemFCDriver(fscommon.FlashSystemDriver):
|
||||
1.0.10 - Fix bug #1585085, add host name check in
|
||||
_find_host_exhaustive for iSCSI
|
||||
1.0.11 - Update driver to use ABC metaclasses
|
||||
1.0.12 - Update driver to support Manage/Unmanage
|
||||
existing volume
|
||||
"""
|
||||
|
||||
VERSION = "1.0.11"
|
||||
VERSION = "1.0.12"
|
||||
|
||||
# ThirdPartySystems wiki page
|
||||
CI_WIKI_NAME = "IBM_FlashSystem_CI"
|
||||
|
@ -77,9 +77,11 @@ class FlashSystemISCSIDriver(fscommon.FlashSystemDriver):
|
||||
1.0.10 - Fix bug #1585085, add host name check in
|
||||
_find_host_exhaustive for iSCSI
|
||||
1.0.11 - Update driver to use ABC metaclasses
|
||||
1.0.12 - Update driver to support Manage/Unmanage
|
||||
existing volume
|
||||
"""
|
||||
|
||||
VERSION = "1.0.11"
|
||||
VERSION = "1.0.12"
|
||||
|
||||
# ThirdPartySystems wiki page
|
||||
CI_WIKI_NAME = "IBM_FlashSystem_CI"
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- Volume manage/unmanage support for IBM FlashSystem FC and iSCSI drivers.
|
||||
|
Loading…
Reference in New Issue
Block a user