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:
YanLin Ren 2016-08-11 01:31:17 -05:00
parent e89b0179e4
commit 5242d1f09f
6 changed files with 256 additions and 5 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -0,0 +1,4 @@
---
features:
- Volume manage/unmanage support for IBM FlashSystem FC and iSCSI drivers.