Merge "Implement retype in HP LeftHand driver"

This commit is contained in:
Jenkins
2014-02-19 16:58:21 +00:00
committed by Gerrit Code Review
4 changed files with 228 additions and 14 deletions

View File

@@ -18,6 +18,7 @@ import mock
from hplefthandclient import exceptions as hpexceptions
from cinder import context
from cinder import exception
from cinder.openstack.common import log as logging
from cinder import test
@@ -974,8 +975,10 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
volume_with_vt['volume_type_id'] = self.volume_type_id
# get the extra specs of interest from this volume's volume type
extra_specs = self.driver.proxy._get_extra_specs(
volume_with_vt,
volume_extra_specs = self.driver.proxy._get_volume_extra_specs(
volume_with_vt)
extra_specs = self.driver.proxy._get_lh_extra_specs(
volume_extra_specs,
hp_lefthand_rest_proxy.extra_specs_key_map.keys())
# map the extra specs key/value pairs to key/value pairs
@@ -1000,8 +1003,10 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
'hplh:ao': 'true'}}
# get the extra specs of interest from this volume's volume type
extra_specs = self.driver.proxy._get_extra_specs(
volume_with_vt,
volume_extra_specs = self.driver.proxy._get_volume_extra_specs(
volume_with_vt)
extra_specs = self.driver.proxy._get_lh_extra_specs(
volume_extra_specs,
hp_lefthand_rest_proxy.extra_specs_key_map.keys())
# map the extra specs key/value pairs to key/value pairs
@@ -1012,3 +1017,134 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase):
# {'isAdaptiveOptimizationEnabled': True}
# without hplh:data_pl since r-07 is an invalid value
self.assertDictMatch({'isAdaptiveOptimizationEnabled': True}, optional)
def test_retype_with_no_LH_extra_specs(self):
# setup drive with default configuration
# and return the mock HTTP LeftHand client
mock_client = self.setup_driver()
ctxt = context.get_admin_context()
host = {'host': self.serverName}
key_specs_old = {'foo': False, 'bar': 2, 'error': True}
key_specs_new = {'foo': True, 'bar': 5, 'error': False}
old_type_ref = volume_types.create(ctxt, 'old', key_specs_old)
new_type_ref = volume_types.create(ctxt, 'new', key_specs_new)
diff, equal = volume_types.volume_types_diff(ctxt, old_type_ref['id'],
new_type_ref['id'])
volume = dict.copy(self.volume)
old_type = volume_types.get_volume_type(ctxt, old_type_ref['id'])
volume['volume_type'] = old_type
volume['host'] = host
new_type = volume_types.get_volume_type(ctxt, new_type_ref['id'])
self.driver.retype(ctxt, volume, new_type, diff, host)
expected = self.driver_startup_call_stack + [
mock.call.getVolumeByName('fakevolume')]
# validate call chain
mock_client.assert_has_calls(expected)
def test_retype_with_only_LH_extra_specs(self):
# setup drive with default configuration
# and return the mock HTTP LeftHand client
mock_client = self.setup_driver()
mock_client.getVolumeByName.return_value = {'id': self.volume_id}
ctxt = context.get_admin_context()
host = {'host': self.serverName}
key_specs_old = {'hplh:provisioning': 'thin'}
key_specs_new = {'hplh:provisioning': 'full', 'hplh:ao': 'true'}
old_type_ref = volume_types.create(ctxt, 'old', key_specs_old)
new_type_ref = volume_types.create(ctxt, 'new', key_specs_new)
diff, equal = volume_types.volume_types_diff(ctxt, old_type_ref['id'],
new_type_ref['id'])
volume = dict.copy(self.volume)
old_type = volume_types.get_volume_type(ctxt, old_type_ref['id'])
volume['volume_type'] = old_type
volume['host'] = host
new_type = volume_types.get_volume_type(ctxt, new_type_ref['id'])
self.driver.retype(ctxt, volume, new_type, diff, host)
expected = self.driver_startup_call_stack + [
mock.call.getVolumeByName('fakevolume'),
mock.call.modifyVolume(
1, {
'isThinProvisioned': False,
'isAdaptiveOptimizationEnabled': True})]
# validate call chain
mock_client.assert_has_calls(expected)
def test_retype_with_both_extra_specs(self):
# setup drive with default configuration
# and return the mock HTTP LeftHand client
mock_client = self.setup_driver()
mock_client.getVolumeByName.return_value = {'id': self.volume_id}
ctxt = context.get_admin_context()
host = {'host': self.serverName}
key_specs_old = {'hplh:provisioning': 'full', 'foo': 'bar'}
key_specs_new = {'hplh:provisioning': 'thin', 'foo': 'foobar'}
old_type_ref = volume_types.create(ctxt, 'old', key_specs_old)
new_type_ref = volume_types.create(ctxt, 'new', key_specs_new)
diff, equal = volume_types.volume_types_diff(ctxt, old_type_ref['id'],
new_type_ref['id'])
volume = dict.copy(self.volume)
old_type = volume_types.get_volume_type(ctxt, old_type_ref['id'])
volume['volume_type'] = old_type
volume['host'] = host
new_type = volume_types.get_volume_type(ctxt, new_type_ref['id'])
self.driver.retype(ctxt, volume, new_type, diff, host)
expected = self.driver_startup_call_stack + [
mock.call.getVolumeByName('fakevolume'),
mock.call.modifyVolume(1, {'isThinProvisioned': True})]
# validate call chain
mock_client.assert_has_calls(expected)
def test_retype_same_extra_specs(self):
# setup drive with default configuration
# and return the mock HTTP LeftHand client
mock_client = self.setup_driver()
mock_client.getVolumeByName.return_value = {'id': self.volume_id}
ctxt = context.get_admin_context()
host = {'host': self.serverName}
key_specs_old = {'hplh:provisioning': 'full', 'hplh:ao': 'true'}
key_specs_new = {'hplh:provisioning': 'full', 'hplh:ao': 'false'}
old_type_ref = volume_types.create(ctxt, 'old', key_specs_old)
new_type_ref = volume_types.create(ctxt, 'new', key_specs_new)
diff, equal = volume_types.volume_types_diff(ctxt, old_type_ref['id'],
new_type_ref['id'])
volume = dict.copy(self.volume)
old_type = volume_types.get_volume_type(ctxt, old_type_ref['id'])
volume['volume_type'] = old_type
volume['host'] = host
new_type = volume_types.get_volume_type(ctxt, new_type_ref['id'])
self.driver.retype(ctxt, volume, new_type, diff, host)
expected = self.driver_startup_call_stack + [
mock.call.getVolumeByName('fakevolume'),
mock.call.modifyVolume(
1,
{'isAdaptiveOptimizationEnabled': False})]
# validate call chain
mock_client.assert_has_calls(expected)

View File

@@ -448,3 +448,18 @@ class HPLeftHandCLIQProxy(SanISCSIDriver):
def remove_export(self, context, volume):
pass
def retype(self, context, volume, new_type, diff, host):
"""Convert the volume to be of the new type.
Returns a boolean indicating whether the retype occurred.
:param ctxt: Context
:param volume: A dictionary describing the volume to migrate
:param new_type: A dictionary describing the volume type to convert to
:param diff: A dictionary with the difference between the two types
:param host: A dictionary describing the host to migrate to, where
host['host'] is its name, and host['capabilities'] is a
dictionary of its reported capabilities.
"""
return False

View File

@@ -46,9 +46,10 @@ class HPLeftHandISCSIDriver(VolumeDriver):
Version history:
1.0.0 - Initial driver
1.0.1 - Added support for retype
"""
VERSION = "1.0.0"
VERSION = "1.0.1"
def __init__(self, *args, **kwargs):
super(HPLeftHandISCSIDriver, self).__init__(*args, **kwargs)
@@ -135,3 +136,8 @@ class HPLeftHandISCSIDriver(VolumeDriver):
@utils.synchronized('lefthand', external=True)
def remove_export(self, context, volume):
return self.proxy.remove_export(context, volume)
@utils.synchronized('lefthand', external=True)
def retype(self, context, volume, new_type, diff, host):
"""Convert the volume to be of the new type."""
return self.proxy.retype(context, volume, new_type, diff, host)

View File

@@ -83,9 +83,10 @@ class HPLeftHandRESTProxy(ISCSIDriver):
Version history:
1.0.0 - Initial REST iSCSI proxy
1.0.1 - Added support for retype
"""
VERSION = "1.0.0"
VERSION = "1.0.1"
device_stats = {}
@@ -131,8 +132,9 @@ class HPLeftHandRESTProxy(ISCSIDriver):
"""Creates a volume."""
try:
# get the extra specs of interest from this volume's volume type
extra_specs = self._get_extra_specs(
volume,
volume_extra_specs = self._get_volume_extra_specs(volume)
extra_specs = self._get_lh_extra_specs(
volume_extra_specs,
extra_specs_key_map.keys())
# map the extra specs key/value pairs to key/value pairs
@@ -285,19 +287,24 @@ class HPLeftHandRESTProxy(ISCSIDriver):
except Exception as ex:
raise exception.VolumeBackendAPIException(str(ex))
def _get_extra_specs(self, volume, valid_keys):
"""Get extra specs of interest (valid_keys) from volume type."""
def _get_volume_extra_specs(self, volume):
"""Get extra specs from a volume."""
extra_specs = {}
type_id = volume.get('volume_type_id', None)
if type_id is not None:
ctxt = context.get_admin_context()
volume_type = volume_types.get_volume_type(ctxt, type_id)
specs = volume_type.get('extra_specs')
for key, value in specs.iteritems():
if key in valid_keys:
extra_specs[key] = value
extra_specs = volume_type.get('extra_specs')
return extra_specs
def _get_lh_extra_specs(self, extra_specs, valid_keys):
"""Get LeftHand extra_specs (valid_keys only)."""
extra_specs_of_interest = {}
for key, value in extra_specs.iteritems():
if key in valid_keys:
extra_specs_of_interest[key] = value
return extra_specs_of_interest
def _map_extra_specs(self, extra_specs):
"""Map the extra spec key/values to LeftHand key/values."""
client_options = {}
@@ -361,3 +368,53 @@ class HPLeftHandRESTProxy(ISCSIDriver):
def remove_export(self, context, volume):
pass
def retype(self, ctxt, volume, new_type, diff, host):
"""Convert the volume to be of the new type.
Returns a boolean indicating whether the retype occurred.
:param ctxt: Context
:param volume: A dictionary describing the volume to retype
:param new_type: A dictionary describing the volume type to convert to
:param diff: A dictionary with the difference between the two types
:param host: A dictionary describing the host, where
host['host'] is its name, and host['capabilities'] is a
dictionary of its reported capabilities.
"""
LOG.debug(_('enter: retype: id=%(id)s, new_type=%(new_type)s,'
'diff=%(diff)s, host=%(host)s') % {'id': volume['id'],
'new_type': new_type,
'diff': diff,
'host': host})
try:
volume_info = self.client.getVolumeByName(volume['name'])
except hpexceptions.HTTPNotFound:
raise exception.VolumeNotFound(volume_id=volume['id'])
try:
# pick out the LH extra specs
new_extra_specs = dict(new_type).get('extra_specs')
lh_extra_specs = self._get_lh_extra_specs(
new_extra_specs,
extra_specs_key_map.keys())
LOG.debug(_('LH specs=%(specs)s') % {'specs': lh_extra_specs})
# only set the ones that have changed
changed_extra_specs = {}
for key, value in lh_extra_specs.iteritems():
(old, new) = diff['extra_specs'][key]
if old != new:
changed_extra_specs[key] = value
# map extra specs to LeftHand options
options = self._map_extra_specs(changed_extra_specs)
if len(options) > 0:
self.client.modifyVolume(volume_info['id'], options)
return True
except Exception as ex:
LOG.warning("%s" % str(ex))
return False