Merge "Dell SC: Add support for ManageableVD"

This commit is contained in:
Jenkins 2015-07-15 21:08:35 +00:00 committed by Gerrit Code Review
commit c84976fd3d
6 changed files with 751 additions and 36 deletions

View File

@ -1445,3 +1445,119 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
cgsnap['consistencygroup_id'])
mock_delete_cg_replay.assert_called_once_with(self.SCRPLAYPROFILE,
cgsnap['id'])
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'manage_existing')
def test_manage_existing(self,
mock_manage_existing,
mock_close_connection,
mock_open_connection,
mock_init):
# Very little to do in this one. The call is sent
# straight down.
volume = {'id': 'guid'}
existing_ref = {'source-name': 'imavolumename'}
self.driver.manage_existing(volume, existing_ref)
mock_manage_existing.assert_called_once_with(volume['id'],
existing_ref)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'manage_existing')
def test_manage_existing_id(self,
mock_manage_existing,
mock_close_connection,
mock_open_connection,
mock_init):
# Very little to do in this one. The call is sent
# straight down.
volume = {'id': 'guid'}
existing_ref = {'source-id': 'imadeviceid'}
self.driver.manage_existing(volume, existing_ref)
mock_manage_existing.assert_called_once_with(volume['id'],
existing_ref)
def test_manage_existing_bad_ref(self,
mock_close_connection,
mock_open_connection,
mock_init):
volume = {'id': 'guid'}
existing_ref = {'banana-name': 'imavolumename'}
self.assertRaises(exception.ManageExistingInvalidReference,
self.driver.manage_existing,
volume,
existing_ref)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'get_unmanaged_volume_size',
return_value=4)
def test_manage_existing_get_size(self,
mock_get_unmanaged_volume_size,
mock_close_connection,
mock_open_connection,
mock_init):
# Almost nothing to test here. Just that we call our function.
volume = {'id': 'guid'}
existing_ref = {'source-name': 'imavolumename'}
res = self.driver.manage_existing_get_size(volume, existing_ref)
mock_get_unmanaged_volume_size.assert_called_once_with(existing_ref)
# The above is 4GB and change.
self.assertEqual(4, res)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'get_unmanaged_volume_size',
return_value=4)
def test_manage_existing_get_size_id(self,
mock_get_unmanaged_volume_size,
mock_close_connection,
mock_open_connection,
mock_init):
# Almost nothing to test here. Just that we call our function.
volume = {'id': 'guid'}
existing_ref = {'source-id': 'imadeviceid'}
res = self.driver.manage_existing_get_size(volume, existing_ref)
mock_get_unmanaged_volume_size.assert_called_once_with(existing_ref)
# The above is 4GB and change.
self.assertEqual(4, res)
def test_manage_existing_get_size_bad_ref(self,
mock_close_connection,
mock_open_connection,
mock_init):
volume = {'id': 'guid'}
existing_ref = {'banana-name': 'imavolumename'}
self.assertRaises(exception.ManageExistingInvalidReference,
self.driver.manage_existing_get_size,
volume,
existing_ref)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_volume',
return_value=VOLUME)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'unmanage')
def test_unmanage(self,
mock_unmanage,
mock_find_volume,
mock_close_connection,
mock_open_connection,
mock_init):
volume = {'id': 'guid'}
self.driver.unmanage(volume)
mock_find_volume.assert_called_once_with(volume['id'])
mock_unmanage.assert_called_once_with(self.VOLUME)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_volume',
return_value=None)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'unmanage')
def test_unmanage_volume_not_found(self,
mock_unmanage,
mock_find_volume,
mock_close_connection,
mock_open_connection,
mock_init):
volume = {'id': 'guid'}
self.driver.unmanage(volume)
mock_find_volume.assert_called_once_with(volume['id'])
self.assertFalse(mock_unmanage.called)

View File

@ -2099,7 +2099,7 @@ class DellSCSanAPITestCase(test.TestCase):
mock_open_connection,
mock_init):
# Test case to find volume in the configured volume folder
res = self.scapi._get_volume_list(self.volume_name, True)
res = self.scapi._get_volume_list(self.volume_name, None, True)
self.assertTrue(mock_post.called)
self.assertTrue(mock_get_json.called)
self.assertEqual(self.VOLUME_LIST, res, 'Unexpected volume list')
@ -2117,17 +2117,17 @@ class DellSCSanAPITestCase(test.TestCase):
mock_open_connection,
mock_init):
# Test case to find volume anywhere in the configured SC
res = self.scapi._get_volume_list(self.volume_name, False)
res = self.scapi._get_volume_list(self.volume_name, None, False)
self.assertTrue(mock_post.called)
self.assertTrue(mock_get_json.called)
self.assertEqual(self.VOLUME_LIST, res, 'Unexpected volume list')
def test__get_volume_list_no_name(self,
mock_close_connection,
mock_open_connection,
mock_init):
# Test case specified volume name is None
res = self.scapi._get_volume_list(None, True)
def test_get_volume_list_no_name_no_id(self,
mock_close_connection,
mock_open_connection,
mock_init):
# Test case specified volume name is None and device id is None.
res = self.scapi._get_volume_list(None, None, True)
self.assertIsNone(res, 'None expected')
@mock.patch.object(dell_storagecenter_api.HttpClient,
@ -2139,7 +2139,7 @@ class DellSCSanAPITestCase(test.TestCase):
mock_open_connection,
mock_init):
# Test case to find volume in the configured volume folder
res = self.scapi._get_volume_list(self.volume_name, True)
res = self.scapi._get_volume_list(self.volume_name, None, True)
self.assertTrue(mock_post.called)
self.assertIsNone(res, 'None expected')
@ -4683,6 +4683,380 @@ class DellSCSanAPITestCase(test.TestCase):
self.assertTrue(mock_find_replay.called)
self.assertTrue(res)
def test_size_to_gb(self,
mock_close_connection,
mock_open_connection,
mock_init):
gb, rem = self.scapi._size_to_gb('1.073741824E9 Byte')
self.assertEqual(1, gb)
self.assertEqual(0, rem)
self.assertRaises(exception.VolumeBackendAPIException,
self.scapi._size_to_gb,
'banana')
gb, rem = self.scapi._size_to_gb('1.073741924E9 Byte')
self.assertEqual(1, gb)
self.assertEqual(100, rem)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_volume_list',
return_value=[{'configuredSize':
'1.073741824E9 Bytes'}])
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_size_to_gb',
return_value=(1, 0))
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings',
return_value=[])
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_volume_folder',
return_value={'id': '1'})
@mock.patch.object(dell_storagecenter_api.HttpClient,
'put',
return_value=RESPONSE_200)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_id')
def test_manage_existing(self,
mock_get_id,
mock_put,
mock_find_volume_folder,
mock_find_mappings,
mock_size_to_gb,
mock_get_volume_list,
mock_close_connection,
mock_open_connection,
mock_init):
newname = 'guid'
existing = {'source-name': 'scvolname'}
# First call is foldername, second is vollist. This is reflected
# in the payload.
mock_get_id.side_effect = ['1', '100']
expected_url = 'StorageCenter/ScVolume/100'
expected_payload = {'Name': newname,
'VolumeFolder': '1'}
self.scapi.manage_existing(newname, existing)
mock_get_volume_list.asert_called_once_with(existing, False)
self.assertTrue(mock_get_id.called)
mock_put.assert_called_once_with(expected_url, expected_payload)
self.assertTrue(mock_find_volume_folder.called)
self.assertTrue(mock_find_mappings.called)
self.assertTrue(mock_size_to_gb.called)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_volume_list',
return_value=[{'configuredSize':
'1.073741824E9 Bytes'}])
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_size_to_gb',
return_value=(1, 0))
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings',
return_value=[])
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_volume_folder',
return_value=None)
@mock.patch.object(dell_storagecenter_api.HttpClient,
'put',
return_value=RESPONSE_200)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_id',
return_value='100')
def test_manage_existing_folder_not_found(self,
mock_get_id,
mock_put,
mock_find_volume_folder,
mock_find_mappings,
mock_size_to_gb,
mock_get_volume_list,
mock_close_connection,
mock_open_connection,
mock_init):
# Same as above only we don't have a volume folder.
newname = 'guid'
existing = {'source-name': 'scvolname'}
expected_url = 'StorageCenter/ScVolume/100'
expected_payload = {'Name': newname}
self.scapi.manage_existing(newname, existing)
mock_get_volume_list.asert_called_once_with(
existing.get('source-name'),
existing.get('source-id'),
False)
mock_put.assert_called_once_with(expected_url, expected_payload)
self.assertTrue(mock_get_id.called)
self.assertTrue(mock_find_volume_folder.called)
self.assertTrue(mock_find_mappings.called)
self.assertTrue(mock_size_to_gb.called)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_volume_list',
return_value=[])
def test_manage_existing_vol_not_found(self,
mock_get_volume_list,
mock_close_connection,
mock_open_connection,
mock_init):
# Same as above only we don't have a volume folder.
newname = 'guid'
existing = {'source-name': 'scvolname'}
self.assertRaises(exception.ManageExistingInvalidReference,
self.scapi.manage_existing,
newname,
existing)
mock_get_volume_list.asert_called_once_with(
existing.get('source-name'),
existing.get('source-id'),
False)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_volume_list',
return_value=[{}, {}, {}])
def test_manage_existing_vol_multiple_found(self,
mock_get_volume_list,
mock_close_connection,
mock_open_connection,
mock_init):
# Same as above only we don't have a volume folder.
newname = 'guid'
existing = {'source-name': 'scvolname'}
self.assertRaises(exception.ManageExistingInvalidReference,
self.scapi.manage_existing,
newname,
existing)
mock_get_volume_list.asert_called_once_with(
existing.get('source-name'),
existing.get('source-id'),
False)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_volume_list',
return_value=[{'configuredSize':
'1.073741924E9 Bytes'}])
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_size_to_gb',
return_value=(1, 100))
def test_manage_existing_bad_size(self,
mock_size_to_gb,
mock_get_volume_list,
mock_close_connection,
mock_open_connection,
mock_init):
# Same as above only we don't have a volume folder.
newname = 'guid'
existing = {'source-name': 'scvolname'}
self.assertRaises(exception.VolumeBackendAPIException,
self.scapi.manage_existing,
newname,
existing)
mock_get_volume_list.asert_called_once_with(
existing.get('source-name'),
existing.get('source-id'),
False)
self.assertTrue(mock_size_to_gb.called)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_volume_list',
return_value=[{'configuredSize':
'1.073741824E9 Bytes'}])
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_size_to_gb',
return_value=(1, 0))
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings',
return_value=[{}, {}])
def test_manage_existing_already_mapped(self,
mock_find_mappings,
mock_size_to_gb,
mock_get_volume_list,
mock_close_connection,
mock_open_connection,
mock_init):
newname = 'guid'
existing = {'source-name': 'scvolname'}
self.assertRaises(exception.VolumeBackendAPIException,
self.scapi.manage_existing,
newname,
existing)
mock_get_volume_list.asert_called_once_with(
existing.get('source-name'),
existing.get('source-id'),
False)
self.assertTrue(mock_find_mappings.called)
self.assertTrue(mock_size_to_gb.called)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_volume_list',
return_value=[{'configuredSize':
'1.073741824E9 Bytes'}])
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_size_to_gb',
return_value=(1, 0))
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings',
return_value=[])
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_volume_folder',
return_value=None)
@mock.patch.object(dell_storagecenter_api.HttpClient,
'put',
return_value=RESPONSE_400)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_id',
return_value='100')
def test_manage_existing_rename_fail(self,
mock_get_id,
mock_put,
mock_find_volume_folder,
mock_find_mappings,
mock_size_to_gb,
mock_get_volume_list,
mock_close_connection,
mock_open_connection,
mock_init):
# We fail on the _find_volume_folder to make this easier.
newname = 'guid'
existing = {'source-name': 'scvolname'}
expected_url = 'StorageCenter/ScVolume/100'
expected_payload = {'Name': newname}
self.assertRaises(exception.VolumeBackendAPIException,
self.scapi.manage_existing,
newname,
existing)
mock_get_volume_list.asert_called_once_with(
existing.get('source-name'),
existing.get('source-id'),
False)
self.assertTrue(mock_get_id.called)
mock_put.assert_called_once_with(expected_url, expected_payload)
self.assertTrue(mock_find_volume_folder.called)
self.assertTrue(mock_find_mappings.called)
self.assertTrue(mock_size_to_gb.called)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_volume_list',
return_value=[{'configuredSize':
'1.073741824E9 Bytes'}])
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_size_to_gb',
return_value=(1, 0))
def test_get_unmanaged_volume_size(self,
mock_size_to_gb,
mock_get_volume_list,
mock_close_connection,
mock_open_connection,
mock_init):
existing = {'source-name': 'scvolname'}
res = self.scapi.get_unmanaged_volume_size(existing)
mock_get_volume_list.asert_called_once_with(
existing.get('source-name'),
existing.get('source-id'),
False)
self.assertTrue(mock_size_to_gb.called)
self.assertEqual(1, res)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_volume_list',
return_value=[])
def test_get_unmanaged_volume_size_not_found(self,
mock_get_volume_list,
mock_close_connection,
mock_open_connection,
mock_init):
existing = {'source-name': 'scvolname'}
self.assertRaises(exception.ManageExistingInvalidReference,
self.scapi.get_unmanaged_volume_size,
existing)
mock_get_volume_list.asert_called_once_with(
existing.get('source-name'),
existing.get('source-id'),
False)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_volume_list',
return_value=[{}, {}, {}])
def test_get_unmanaged_volume_size_many_found(self,
mock_get_volume_list,
mock_close_connection,
mock_open_connection,
mock_init):
existing = {'source-name': 'scvolname'}
self.assertRaises(exception.ManageExistingInvalidReference,
self.scapi.get_unmanaged_volume_size,
existing)
mock_get_volume_list.asert_called_once_with(
existing.get('source-name'),
existing.get('source-id'),
False)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_volume_list',
return_value=[{'configuredSize':
'1.073741924E9 Bytes'}])
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_size_to_gb',
return_value=(1, 100))
def test_get_unmanaged_volume_size_bad_size(self,
mock_size_to_gb,
mock_get_volume_list,
mock_close_connection,
mock_open_connection,
mock_init):
existing = {'source-name': 'scvolname'}
self.assertRaises(exception.VolumeBackendAPIException,
self.scapi.get_unmanaged_volume_size,
existing)
self.assertTrue(mock_size_to_gb.called)
mock_get_volume_list.asert_called_once_with(
existing.get('source-name'),
existing.get('source-id'),
False)
@mock.patch.object(dell_storagecenter_api.HttpClient,
'put',
return_value=RESPONSE_200)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_id',
return_value='100')
def test_unmanage(self,
mock_get_id,
mock_put,
mock_close_connection,
mock_open_connection,
mock_init):
# Same as above only we don't have a volume folder.
scvolume = {'name': 'guid'}
expected_url = 'StorageCenter/ScVolume/100'
newname = 'Unmanaged_' + scvolume['name']
expected_payload = {'Name': newname}
self.scapi.unmanage(scvolume)
self.assertTrue(mock_get_id.called)
mock_put.assert_called_once_with(expected_url, expected_payload)
@mock.patch.object(dell_storagecenter_api.HttpClient,
'put',
return_value=RESPONSE_400)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_id',
return_value='100')
def test_unmanage_fail(self,
mock_get_id,
mock_put,
mock_close_connection,
mock_open_connection,
mock_init):
# Same as above only we don't have a volume folder.
scvolume = {'name': 'guid'}
expected_url = 'StorageCenter/ScVolume/100'
newname = 'Unmanaged_' + scvolume['name']
expected_payload = {'Name': newname}
self.assertRaises(exception.VolumeBackendAPIException,
self.scapi.unmanage,
scvolume)
self.assertTrue(mock_get_id.called)
mock_put.assert_called_once_with(expected_url, expected_payload)
class DellSCSanAPIConnectionTestCase(test.TestCase):

View File

@ -169,7 +169,7 @@ class StorageCenterApi(object):
Handles calls to Dell Enterprise Manager (EM) via the REST API interface.
'''
APIVERSION = '1.2.0'
APIVERSION = '2.0.1'
def __init__(self, host, port, user, password, verify):
'''This creates a connection to Dell Enterprise Manager.
@ -605,36 +605,39 @@ class StorageCenterApi(object):
return scvolume
def _get_volume_list(self, name, filterbyvfname=True):
def _get_volume_list(self, name, deviceid, filterbyvfname=True):
'''Return the specified list of volumes.
:param name: Volume name.
:param deviceid: Volume device ID on the SC backend.
:param filterbyvfname: If set to true then this filters by the preset
folder name.
:return: Returns the scvolume or None.
:return: Returns the scvolume list or None.
'''
result = None
pf = PayloadFilter()
pf.append('scSerialNumber', self.ssn)
# We need a name to find a volume.
if name is not None:
pf.append('Name', name)
else:
return None
# set folderPath
if filterbyvfname:
vfname = (self.vfname if self.vfname.endswith('/')
else self.vfname + '/')
pf.append('volumeFolderPath', vfname)
r = self.client.post('StorageCenter/ScVolume/GetList',
pf.payload)
if r.status_code != 200:
LOG.debug('ScVolume GetList error %(name)s: %(code)d %(reason)s',
{'name': name,
'code': r.status_code,
'reason': r.reason})
else:
result = self._get_json(r)
# We need a name or a device ID to find a volume.
if name or deviceid:
pf = PayloadFilter()
pf.append('scSerialNumber', self.ssn)
if name is not None:
pf.append('Name', name)
if deviceid is not None:
pf.append('DeviceId', deviceid)
# set folderPath
if filterbyvfname:
vfname = (self.vfname if self.vfname.endswith('/')
else self.vfname + '/')
pf.append('volumeFolderPath', vfname)
r = self.client.post('StorageCenter/ScVolume/GetList',
pf.payload)
if r.status_code != 200:
LOG.debug('ScVolume GetList error '
'%(name)s: %(code)d %(reason)s',
{'name': name,
'code': r.status_code,
'reason': r.reason})
else:
result = self._get_json(r)
# We return None if there was an error and a list if the command
# succeeded. It might be an empty list.
return result
@ -661,6 +664,7 @@ class StorageCenterApi(object):
# Look for our volume in our folder.
vollist = self._get_volume_list(name,
None,
True)
# If an empty list was returned they probably moved the volumes or
# changed the folder name so try again without the folder.
@ -669,6 +673,7 @@ class StorageCenterApi(object):
{'n': name,
'v': self.vfname})
vollist = self._get_volume_list(name,
None,
False)
# If multiple volumes of the same name are found we need to error.
@ -1825,3 +1830,140 @@ class StorageCenterApi(object):
return False
# We either couldn't find it or expired it.
return True
def _size_to_gb(self, spacestring):
'''Splits a SC size string into GB and a remainder.
Space is returned in a string like ...
7.38197504E8 Bytes
Need to split that apart and convert to GB.
:param spacestring: SC size string.
:return: Size in GB and remainder in byte.
'''
try:
n = spacestring.split(' ', 1)
fgb = int(float(n[0]) // 1073741824)
frem = int(float(n[0]) % 1073741824)
return fgb, frem
except Exception:
# We received an invalid size string. Blow up.
raise exception.VolumeBackendAPIException(
_('Error retrieving volume size'))
def manage_existing(self, newname, existing):
'''Finds the volume named existing and renames it.
This checks a few things. The volume has to exist. There can
only be one volume by that name. Since cinder manages volumes
by the GB it has to be defined on a GB boundry.
This renames existing to newname. newname is the guid from
the cinder volume['id']. The volume is moved to the defined
cinder volume folder.
:param newname: Name to rename the volume to.
:param existing: The existing volume dict..
:return: Nothing.
:raises: VolumeBackendAPIException, ManageExistingInvalidReference
'''
vollist = self._get_volume_list(existing.get('source-name'),
existing.get('source-id'),
False)
count = len(vollist)
# If we found one volume with that name we can work with it.
if count == 1:
# First thing to check is if the size is something we can
# work with.
sz, rem = self._size_to_gb(vollist[0]['configuredSize'])
if rem > 0:
raise exception.VolumeBackendAPIException(
_('Volume size must multiple of 1 GB.'))
# We only want to grab detached volumes.
mappings = self._find_mappings(vollist[0])
if len(mappings) > 0:
raise exception.VolumeBackendAPIException(
_('Volume is attached to a server. (%s)') % existing)
# Find our folder
folder = self._find_volume_folder(True)
# If we actually have a place to put our volume create it
if folder is None:
LOG.warning(_LW('Unable to create folder %s'),
self.vfname)
# Rename and move our volume.
payload = {}
payload['Name'] = newname
if folder:
payload['VolumeFolder'] = self._get_id(folder)
r = self.client.put('StorageCenter/ScVolume/%s' %
self._get_id(vollist[0]),
payload)
if r.status_code != 200:
LOG.error(_LE('ScVolume error on rename: %(code)d %(reason)s'),
{'code': r.status_code,
'reason': r.reason})
raise exception.VolumeBackendAPIException(
_('Unable to manage volume %s') % existing)
elif count > 1:
raise exception.ManageExistingInvalidReference(
_('Volume not unique. (%s)') % existing)
else:
raise exception.ManageExistingInvalidReference(
_('Volume not found. (%s)') % existing)
def get_unmanaged_volume_size(self, existing):
'''Looks up the volume named existing and returns its size string.
:param existing: Existing volume dict.
:return: The SC configuredSize string.
:raises: ManageExistingInvalidReference
'''
vollist = self._get_volume_list(existing.get('source-name'),
existing.get('source-id'),
False)
count = len(vollist)
# If we found one volume with that name we can work with it.
if count == 1:
sz, rem = self._size_to_gb(vollist[0]['configuredSize'])
if rem > 0:
raise exception.VolumeBackendAPIException(
_('Volume size must multiple of 1 GB.'))
return sz
elif count > 1:
raise exception.ManageExistingInvalidReference(
_('Volume not unique. (%s)') % existing)
else:
raise exception.ManageExistingInvalidReference(
_('Volume not found. (%s)') % existing)
def unmanage(self, scvolume):
'''Unmanage our volume.
We simply rename with with a prefix of 'Unmanaged_'. That's it.
:param scvolume: The Dell SC volume object.
:return: Nothing.
:raises: VolumeBackendAPIException
'''
newname = 'Unmanaged_' + scvolume['name']
payload = {}
payload['Name'] = newname
r = self.client.put('StorageCenter/ScVolume/%s' %
self._get_id(scvolume),
payload)
if r.status_code == 200:
LOG.info(_LI('Volume %s unmanaged.'), scvolume['name'])
else:
LOG.error(_LE('ScVolume error on rename: %(code)d %(reason)s'),
{'code': r.status_code,
'reason': r.reason})
raise exception.VolumeBackendAPIException(
_('Unable to rename volume %(existing)s to %(newname)s') %
{'existing': scvolume['name'],
'newname': newname})

View File

@ -48,7 +48,9 @@ CONF = cfg.CONF
CONF.register_opts(common_opts)
class DellCommonDriver(driver.VolumeDriver):
class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
driver.ExtendVD, driver.CloneableVD, driver.SnapshotVD,
driver.BaseVD):
def __init__(self, *args, **kwargs):
super(DellCommonDriver, self).__init__(*args, **kwargs)
@ -374,6 +376,7 @@ class DellCommonDriver(driver.VolumeDriver):
# The world was horrible to us so we should error and leave.
LOG.error(_LE('Unable to rename the logical volume for volume: %s'),
original_volume_name)
return {'_name_id': new_volume['_name_id'] or new_volume['id']}
def create_consistencygroup(self, context, group):
@ -530,3 +533,77 @@ class DellCommonDriver(driver.VolumeDriver):
model_update = {'status': 'deleted'}
return model_update, snapshots
def manage_existing(self, volume, existing_ref):
"""Brings an existing backend storage object under Cinder management.
existing_ref is passed straight through from the API request's
manage_existing_ref value, and it is up to the driver how this should
be interpreted. It should be sufficient to identify a storage object
that the driver should somehow associate with the newly-created cinder
volume structure.
There are two ways to do this:
1. Rename the backend storage object so that it matches the,
volume['name'] which is how drivers traditionally map between a
cinder volume and the associated backend storage object.
2. Place some metadata on the volume, or somewhere in the backend, that
allows other driver requests (e.g. delete, clone, attach, detach...)
to locate the backend storage object when required.
If the existing_ref doesn't make sense, or doesn't refer to an existing
backend storage object, raise a ManageExistingInvalidReference
exception.
The volume may have a volume_type, and the driver can inspect that and
compare against the properties of the referenced backend storage
object. If they are incompatible, raise a
ManageExistingVolumeTypeMismatch, specifying a reason for the failure.
:param volume: Cinder volume to manage
:param existing_ref: Driver-specific information used to identify a
volume
"""
if existing_ref.get('source-name') or existing_ref.get('source-id'):
with self._client.open_connection() as api:
api.manage_existing(volume['id'], existing_ref)
else:
raise exception.ManageExistingInvalidReference(
_('Must specify source-name or source-id. (%s)') %
existing_ref)
def manage_existing_get_size(self, volume, existing_ref):
"""Return size of volume to be managed by manage_existing.
When calculating the size, round up to the next GB.
:param volume: Cinder volume to manage
:param existing_ref: Driver-specific information used to identify a
volume
"""
if existing_ref.get('source-name') or existing_ref.get('source-id'):
with self._client.open_connection() as api:
return api.get_unmanaged_volume_size(existing_ref)
else:
raise exception.ManageExistingInvalidReference(
_('Must specify source-name or source-id. (%s)') %
existing_ref)
def unmanage(self, volume):
"""Removes the specified volume from Cinder management.
Does not delete the underlying backend storage object.
For most drivers, this will not need to do anything. However, some
drivers might use this call as an opportunity to clean up any
Cinder-specific configuration that they have associated with the
backend storage object.
:param volume: Cinder volume to unmanage
"""
with self._client.open_connection() as api:
scvolume = api.find_volume(volume['id'])
if scvolume:
api.unmanage(scvolume)

View File

@ -38,9 +38,12 @@ class DellStorageCenterFCDriver(dell_storagecenter_common.DellCommonDriver,
1.0.0 - Initial driver
1.1.0 - Added extra spec support for Storage Profile selection
1.2.0 - Added consistency group support.
2.0.0 - Switched to inheriting functional objects rather than volume
driver.
2.1.0 - Added support for ManageableVD.
'''
VERSION = '1.2.0'
VERSION = '2.1.0'
def __init__(self, *args, **kwargs):
super(DellStorageCenterFCDriver, self).__init__(*args, **kwargs)

View File

@ -36,9 +36,12 @@ class DellStorageCenterISCSIDriver(dell_storagecenter_common.DellCommonDriver,
1.0.0 - Initial driver
1.1.0 - Added extra spec support for Storage Profile selection
1.2.0 - Added consistency group support.
2.0.0 - Switched to inheriting functional objects rather than volume
driver.
2.1.0 - Added support for ManageableVD.
'''
VERSION = '1.2.0'
VERSION = '2.1.0'
def __init__(self, *args, **kwargs):
super(DellStorageCenterISCSIDriver, self).__init__(*args, **kwargs)