From 2d04764756c25c47f20c6ea8f2c94a0c6673786b Mon Sep 17 00:00:00 2001 From: John Griffith Date: Wed, 5 Feb 2014 23:33:58 -0700 Subject: [PATCH] Implement retype in SolidFire driver This just adds the ability to modify the type of an existing SolidFire volume to another type suppported on the same backend. Main use case for this is changing QoS settings for a volume on-demand. Also updates the internal attributes to store the deltas between the old-type and new-type. Change-Id: I65a4c34cbec17d0f3987c1d964e6ad1bafea3601 Implements: blueprint retype-support-for-solidfire-driver --- cinder/tests/test_solidfire.py | 76 +++++++++++++++++++++++++++++- cinder/volume/drivers/solidfire.py | 38 +++++++++++++++ 2 files changed, 112 insertions(+), 2 deletions(-) diff --git a/cinder/tests/test_solidfire.py b/cinder/tests/test_solidfire.py index 1a46bc5f58b..1505538edf6 100644 --- a/cinder/tests/test_solidfire.py +++ b/cinder/tests/test_solidfire.py @@ -124,7 +124,7 @@ class SolidFireVolumeTestCase(test.TestCase): 'enable512e': True, 'access': "readWrite", 'status': "active", - 'attributes': None, + 'attributes': {}, 'qos': None, 'iqn': test_name}]}} return result @@ -221,7 +221,7 @@ class SolidFireVolumeTestCase(test.TestCase): 'created_at': timeutils.utcnow()} sfv = SolidFireDriver(configuration=self.configuration) - model_update = sfv.create_volume(testvol) + sfv.create_volume(testvol) sfv.create_snapshot(testsnap) def test_create_clone(self): @@ -479,3 +479,75 @@ class SolidFireVolumeTestCase(test.TestCase): self.assertEqual(qos, {'minIOPS': 100, 'maxIOPS': 200, 'burstIOPS': 300}) + + def test_retype(self): + sfv = SolidFireDriver(configuration=self.configuration) + self.stubs.Set(SolidFireDriver, '_issue_api_request', + self.fake_issue_api_request) + type_ref = volume_types.create(self.ctxt, + "type1", {"qos:minIOPS": "500", + "qos:burstIOPS": "2000", + "qos:maxIOPS": "1000"}) + diff = {'encryption': {}, 'qos_specs': {}, + 'extra_specs': {'qos:burstIOPS': ('10000', u'2000'), + 'qos:minIOPS': ('1000', u'500'), + 'qos:maxIOPS': ('10000', u'1000')}} + host = None + testvol = {'project_id': 'testprjid', + 'name': 'test_volume', + 'size': 1, + 'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66', + 'created_at': timeutils.utcnow()} + + sfv = SolidFireDriver(configuration=self.configuration) + self.assertTrue(sfv.retype(self.ctxt, + testvol, + type_ref, diff, host)) + + def test_retype_with_qos_spec(self): + test_type = {'name': 'sf-1', + 'qos_specs_id': 'fb0576d7-b4b5-4cad-85dc-ca92e6a497d1', + 'deleted': False, + 'created_at': '2014-02-06 04:58:11', + 'updated_at': None, + 'extra_specs': {}, + 'deleted_at': None, + 'id': 'e730e97b-bc7d-4af3-934a-32e59b218e81'} + + test_qos_spec = {'id': 'asdfafdasdf', + 'specs': {'minIOPS': '1000', + 'maxIOPS': '2000', + 'burstIOPS': '3000'}} + + qos_specs + + def _fake_get_volume_type(ctxt, type_id): + return test_type + + def _fake_get_qos_spec(ctxt, spec_id): + return test_qos_spec + + self.stubs.Set(SolidFireDriver, '_issue_api_request', + self.fake_issue_api_request) + self.stubs.Set(volume_types, 'get_volume_type', + _fake_get_volume_type) + self.stubs.Set(qos_specs, 'get_qos_specs', + _fake_get_qos_spec) + + sfv = SolidFireDriver(configuration=self.configuration) + + diff = {'encryption': {}, 'extra_specs': {}, + 'qos_specs': {'burstIOPS': ('10000', '2000'), + 'minIOPS': ('1000', '500'), + 'maxIOPS': ('10000', '1000')}} + host = None + testvol = {'project_id': 'testprjid', + 'name': 'test_volume', + 'size': 1, + 'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66', + 'created_at': timeutils.utcnow()} + + sfv = SolidFireDriver(configuration=self.configuration) + self.assertTrue(sfv.retype(self.ctxt, + testvol, + test_type, diff, host)) diff --git a/cinder/volume/drivers/solidfire.py b/cinder/volume/drivers/solidfire.py index ed79444d2a1..a50877eec29 100644 --- a/cinder/volume/drivers/solidfire.py +++ b/cinder/volume/drivers/solidfire.py @@ -769,3 +769,41 @@ class SolidFireDriver(SanISCSIDriver): raise exception.SolidFireAPIDataException(data=data) LOG.debug(_("Leaving SolidFire transfer volume")) + + 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 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 (Not Used). + + """ + qos = {} + attributes = {} + + sfaccount = self._get_sfaccount(volume['project_id']) + params = {'accountID': sfaccount['accountID']} + sf_vol = self._get_sf_volume(volume['id'], params) + + if sf_vol is None: + raise exception.VolumeNotFound(volume_id=volume['id']) + + attributes = sf_vol['attributes'] + attributes['retyped_at'] = timeutils.strtime() + params = {'volumeID': sf_vol['volumeID']} + qos = self._set_qos_by_volume_type(ctxt, new_type['id']) + + if qos: + params['qos'] = qos + for k, v in qos.items(): + attributes[k] = str(v) + params['attributes'] = attributes + + self._issue_api_request('ModifyVolume', params) + return True