From 9082273305b0b9c117eb677a0e34c2ffde4e66f0 Mon Sep 17 00:00:00 2001 From: Xing Yang Date: Sat, 6 Sep 2014 13:32:02 -0400 Subject: [PATCH] Volume types need to be specified when creating CG When creating a consistency group, the scheduler will find a backend that supports all input volume types. If volume types are not provided, the default_volume_type in cinder.conf will be used, however, this could cause inconsistent behavior in a user environment where the default_volume_type is defined in some places but not in others. This fix removed the use of default_volume_type for CG creation, added a check to verify that volume types are provided when creating a CG. When creating a volume and adding it to a CG, we need to make sure a volume type is provided as well. Change-Id: I078d4fdd8d92529e853be16272ad74d1e130f712 Closes-Bug: #1366371 --- cinder/api/contrib/consistencygroups.py | 13 +- cinder/consistencygroup/api.py | 19 +- cinder/scheduler/manager.py | 10 +- .../api/contrib/test_consistencygroups.py | 257 ++++++++++-------- cinder/tests/api/v2/test_volumes.py | 30 ++ cinder/tests/scheduler/test_scheduler.py | 43 +++ cinder/volume/api.py | 8 +- 7 files changed, 242 insertions(+), 138 deletions(-) diff --git a/cinder/api/contrib/consistencygroups.py b/cinder/api/contrib/consistencygroups.py index 9ae8863c0..6b09f868b 100644 --- a/cinder/api/contrib/consistencygroups.py +++ b/cinder/api/contrib/consistencygroups.py @@ -125,11 +125,10 @@ class ConsistencyGroupsController(wsgi.Controller): group = self.consistencygroup_api.get(context, id) self.consistencygroup_api.delete(context, group, force) except exception.ConsistencyGroupNotFound: - msg = _("Consistency group could not be found") + msg = _("Consistency group %s could not be found.") % id raise exc.HTTPNotFound(explanation=msg) - except exception.InvalidConsistencyGroup: - msg = _("Invalid consistency group") - raise exc.HTTPBadRequest(explanation=msg) + except exception.InvalidConsistencyGroup as error: + raise exc.HTTPBadRequest(explanation=error.msg) return webob.Response(status_int=202) @@ -176,6 +175,10 @@ class ConsistencyGroupsController(wsgi.Controller): name = consistencygroup.get('name', None) description = consistencygroup.get('description', None) volume_types = consistencygroup.get('volume_types', None) + if not volume_types: + msg = _("volume_types must be provided to create " + "consistency group %(name)s.") % {'name': name} + raise exc.HTTPBadRequest(explanation=msg) availability_zone = consistencygroup.get('availability_zone', None) LOG.info(_("Creating consistency group %(name)s."), @@ -184,7 +187,7 @@ class ConsistencyGroupsController(wsgi.Controller): try: new_consistencygroup = self.consistencygroup_api.create( - context, name, description, cg_volume_types=volume_types, + context, name, description, volume_types, availability_zone=availability_zone) except exception.InvalidConsistencyGroup as error: raise exc.HTTPBadRequest(explanation=error.msg) diff --git a/cinder/consistencygroup/api.py b/cinder/consistencygroup/api.py index bda01e4fa..a4dfead0f 100644 --- a/cinder/consistencygroup/api.py +++ b/cinder/consistencygroup/api.py @@ -104,27 +104,20 @@ class API(base.Base): return availability_zone def create(self, context, name, description, - cg_volume_types=None, availability_zone=None): + cg_volume_types, availability_zone=None): check_policy(context, 'create') volume_type_list = None - if cg_volume_types: - volume_type_list = cg_volume_types.split(',') + volume_type_list = cg_volume_types.split(',') req_volume_types = [] - if volume_type_list: - req_volume_types = (self.db.volume_types_get_by_name_or_id( - context, volume_type_list)) - - if not req_volume_types: - volume_type = volume_types.get_default_volume_type() - req_volume_types.append(volume_type) + req_volume_types = (self.db.volume_types_get_by_name_or_id( + context, volume_type_list)) req_volume_type_ids = "" for voltype in req_volume_types: - if voltype: - req_volume_type_ids = ( - req_volume_type_ids + voltype.get('id') + ",") + req_volume_type_ids = ( + req_volume_type_ids + voltype.get('id') + ",") if len(req_volume_type_ids) == 0: req_volume_type_ids = None diff --git a/cinder/scheduler/manager.py b/cinder/scheduler/manager.py index 68a022e24..937671384 100644 --- a/cinder/scheduler/manager.py +++ b/cinder/scheduler/manager.py @@ -96,18 +96,18 @@ class SchedulerManager(manager.Manager): context, group_id, request_spec_list, filter_properties_list) - except exception.NoValidHost as ex: + except exception.NoValidHost: msg = (_("Could not find a host for consistency group " "%(group_id)s.") % {'group_id': group_id}) LOG.error(msg) db.consistencygroup_update(context, group_id, {'status': 'error'}) - except Exception as ex: + except Exception: with excutils.save_and_reraise_exception(): - LOG.error(_("Failed to create consistency group " - "%(group_id)s.")) - LOG.exception(ex) + LOG.exception(_("Failed to create consistency group " + "%(group_id)s."), + {'group_id': group_id}) db.consistencygroup_update(context, group_id, {'status': 'error'}) diff --git a/cinder/tests/api/contrib/test_consistencygroups.py b/cinder/tests/api/contrib/test_consistencygroups.py index 18a183c0b..11889e25d 100644 --- a/cinder/tests/api/contrib/test_consistencygroups.py +++ b/cinder/tests/api/contrib/test_consistencygroups.py @@ -26,6 +26,7 @@ import webob import cinder.consistencygroup from cinder import context from cinder import db +from cinder.i18n import _ from cinder import test from cinder.tests.api import fakes @@ -73,14 +74,15 @@ class ConsistencyGroupsAPITestCase(test.TestCase): res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) - self.assertEqual(res.status_int, 200) - self.assertEqual(res_dict['consistencygroup']['availability_zone'], - 'az1') - self.assertEqual(res_dict['consistencygroup']['description'], - 'this is a test consistency group') - self.assertEqual(res_dict['consistencygroup']['name'], - 'test_consistencygroup') - self.assertEqual(res_dict['consistencygroup']['status'], 'creating') + self.assertEqual(200, res.status_int) + self.assertEqual('az1', + res_dict['consistencygroup']['availability_zone']) + self.assertEqual('this is a test consistency group', + res_dict['consistencygroup']['description']) + self.assertEqual('test_consistencygroup', + res_dict['consistencygroup']['name']) + self.assertEqual('creating', + res_dict['consistencygroup']['status']) db.consistencygroup_destroy(context.get_admin_context(), consistencygroup_id) @@ -93,11 +95,11 @@ class ConsistencyGroupsAPITestCase(test.TestCase): req.headers['Content-Type'] = 'application/xml' req.headers['Accept'] = 'application/xml' res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) + self.assertEqual(200, res.status_int) dom = minidom.parseString(res.body) consistencygroup = dom.getElementsByTagName('consistencygroup') name = consistencygroup.item(0).getAttribute('name') - self.assertEqual(name.strip(), "test_consistencygroup") + self.assertEqual("test_consistencygroup", name.strip()) db.consistencygroup_destroy( context.get_admin_context(), consistencygroup_id) @@ -109,10 +111,10 @@ class ConsistencyGroupsAPITestCase(test.TestCase): res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) - self.assertEqual(res.status_int, 404) - self.assertEqual(res_dict['itemNotFound']['code'], 404) - self.assertEqual(res_dict['itemNotFound']['message'], - 'ConsistencyGroup 9999 could not be found.') + self.assertEqual(404, res.status_int) + self.assertEqual(404, res_dict['itemNotFound']['code']) + self.assertEqual('ConsistencyGroup 9999 could not be found.', + res_dict['itemNotFound']['message']) def test_list_consistencygroups_json(self): consistencygroup_id1 = self._create_consistencygroup() @@ -125,19 +127,19 @@ class ConsistencyGroupsAPITestCase(test.TestCase): res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) - self.assertEqual(res.status_int, 200) - self.assertEqual(res_dict['consistencygroups'][0]['id'], - consistencygroup_id1) - self.assertEqual(res_dict['consistencygroups'][0]['name'], - 'test_consistencygroup') - self.assertEqual(res_dict['consistencygroups'][1]['id'], - consistencygroup_id2) - self.assertEqual(res_dict['consistencygroups'][1]['name'], - 'test_consistencygroup') - self.assertEqual(res_dict['consistencygroups'][2]['id'], - consistencygroup_id3) - self.assertEqual(res_dict['consistencygroups'][2]['name'], - 'test_consistencygroup') + self.assertEqual(200, res.status_int) + self.assertEqual(consistencygroup_id1, + res_dict['consistencygroups'][0]['id']) + self.assertEqual('test_consistencygroup', + res_dict['consistencygroups'][0]['name']) + self.assertEqual(consistencygroup_id2, + res_dict['consistencygroups'][1]['id']) + self.assertEqual('test_consistencygroup', + res_dict['consistencygroups'][1]['name']) + self.assertEqual(consistencygroup_id3, + res_dict['consistencygroups'][2]['id']) + self.assertEqual('test_consistencygroup', + res_dict['consistencygroups'][2]['name']) db.consistencygroup_destroy(context.get_admin_context(), consistencygroup_id3) @@ -157,16 +159,16 @@ class ConsistencyGroupsAPITestCase(test.TestCase): req.headers['Accept'] = 'application/xml' res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) + self.assertEqual(200, res.status_int) dom = minidom.parseString(res.body) consistencygroup_list = dom.getElementsByTagName('consistencygroup') - self.assertEqual(consistencygroup_list.item(0).getAttribute('id'), - consistencygroup_id1) - self.assertEqual(consistencygroup_list.item(1).getAttribute('id'), - consistencygroup_id2) - self.assertEqual(consistencygroup_list.item(2).getAttribute('id'), - consistencygroup_id3) + self.assertEqual(consistencygroup_id1, + consistencygroup_list.item(0).getAttribute('id')) + self.assertEqual(consistencygroup_id2, + consistencygroup_list.item(1).getAttribute('id')) + self.assertEqual(consistencygroup_id3, + consistencygroup_list.item(2).getAttribute('id')) db.consistencygroup_destroy(context.get_admin_context(), consistencygroup_id3) @@ -187,39 +189,39 @@ class ConsistencyGroupsAPITestCase(test.TestCase): res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) - self.assertEqual(res.status_int, 200) - self.assertEqual(res_dict['consistencygroups'][0]['availability_zone'], - 'az1') - self.assertEqual(res_dict['consistencygroups'][0]['description'], - 'this is a test consistency group') - self.assertEqual(res_dict['consistencygroups'][0]['name'], - 'test_consistencygroup') - self.assertEqual(res_dict['consistencygroups'][0]['id'], - consistencygroup_id1) - self.assertEqual(res_dict['consistencygroups'][0]['status'], - 'creating') - - self.assertEqual(res_dict['consistencygroups'][1]['availability_zone'], - 'az1') - self.assertEqual(res_dict['consistencygroups'][1]['description'], - 'this is a test consistency group') - self.assertEqual(res_dict['consistencygroups'][1]['name'], - 'test_consistencygroup') - self.assertEqual(res_dict['consistencygroups'][1]['id'], - consistencygroup_id2) - self.assertEqual(res_dict['consistencygroups'][1]['status'], - 'creating') - - self.assertEqual(res_dict['consistencygroups'][2]['availability_zone'], - 'az1') - self.assertEqual(res_dict['consistencygroups'][2]['description'], - 'this is a test consistency group') - self.assertEqual(res_dict['consistencygroups'][2]['name'], - 'test_consistencygroup') - self.assertEqual(res_dict['consistencygroups'][2]['id'], - consistencygroup_id3) - self.assertEqual(res_dict['consistencygroups'][2]['status'], - 'creating') + self.assertEqual(200, res.status_int) + self.assertEqual('az1', + res_dict['consistencygroups'][0]['availability_zone']) + self.assertEqual('this is a test consistency group', + res_dict['consistencygroups'][0]['description']) + self.assertEqual('test_consistencygroup', + res_dict['consistencygroups'][0]['name']) + self.assertEqual(consistencygroup_id1, + res_dict['consistencygroups'][0]['id']) + self.assertEqual('creating', + res_dict['consistencygroups'][0]['status']) + + self.assertEqual('az1', + res_dict['consistencygroups'][1]['availability_zone']) + self.assertEqual('this is a test consistency group', + res_dict['consistencygroups'][1]['description']) + self.assertEqual('test_consistencygroup', + res_dict['consistencygroups'][1]['name']) + self.assertEqual(consistencygroup_id2, + res_dict['consistencygroups'][1]['id']) + self.assertEqual('creating', + res_dict['consistencygroups'][1]['status']) + + self.assertEqual('az1', + res_dict['consistencygroups'][2]['availability_zone']) + self.assertEqual('this is a test consistency group', + res_dict['consistencygroups'][2]['description']) + self.assertEqual('test_consistencygroup', + res_dict['consistencygroups'][2]['name']) + self.assertEqual(consistencygroup_id3, + res_dict['consistencygroups'][2]['id']) + self.assertEqual('creating', + res_dict['consistencygroups'][2]['status']) db.consistencygroup_destroy(context.get_admin_context(), consistencygroup_id3) @@ -239,54 +241,57 @@ class ConsistencyGroupsAPITestCase(test.TestCase): req.headers['Accept'] = 'application/xml' res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) + self.assertEqual(200, res.status_int) dom = minidom.parseString(res.body) consistencygroup_detail = dom.getElementsByTagName('consistencygroup') self.assertEqual( - consistencygroup_detail.item(0).getAttribute('availability_zone'), - 'az1') + 'az1', + consistencygroup_detail.item(0).getAttribute('availability_zone')) self.assertEqual( - consistencygroup_detail.item(0).getAttribute('description'), - 'this is a test consistency group') + 'this is a test consistency group', + consistencygroup_detail.item(0).getAttribute('description')) self.assertEqual( - consistencygroup_detail.item(0).getAttribute('name'), - 'test_consistencygroup') + 'test_consistencygroup', + consistencygroup_detail.item(0).getAttribute('name')) self.assertEqual( - consistencygroup_detail.item(0).getAttribute('id'), - consistencygroup_id1) + consistencygroup_id1, + consistencygroup_detail.item(0).getAttribute('id')) self.assertEqual( - consistencygroup_detail.item(0).getAttribute('status'), 'creating') + 'creating', + consistencygroup_detail.item(0).getAttribute('status')) self.assertEqual( - consistencygroup_detail.item(1).getAttribute('availability_zone'), - 'az1') + 'az1', + consistencygroup_detail.item(1).getAttribute('availability_zone')) self.assertEqual( - consistencygroup_detail.item(1).getAttribute('description'), - 'this is a test consistency group') + 'this is a test consistency group', + consistencygroup_detail.item(1).getAttribute('description')) self.assertEqual( - consistencygroup_detail.item(1).getAttribute('name'), - 'test_consistencygroup') + 'test_consistencygroup', + consistencygroup_detail.item(1).getAttribute('name')) self.assertEqual( - consistencygroup_detail.item(1).getAttribute('id'), - consistencygroup_id2) + consistencygroup_id2, + consistencygroup_detail.item(1).getAttribute('id')) self.assertEqual( - consistencygroup_detail.item(1).getAttribute('status'), 'creating') + 'creating', + consistencygroup_detail.item(1).getAttribute('status')) self.assertEqual( - consistencygroup_detail.item(2).getAttribute('availability_zone'), - 'az1') + 'az1', + consistencygroup_detail.item(2).getAttribute('availability_zone')) self.assertEqual( - consistencygroup_detail.item(2).getAttribute('description'), - 'this is a test consistency group') + 'this is a test consistency group', + consistencygroup_detail.item(2).getAttribute('description')) self.assertEqual( - consistencygroup_detail.item(2).getAttribute('name'), - 'test_consistencygroup') + 'test_consistencygroup', + consistencygroup_detail.item(2).getAttribute('name')) self.assertEqual( - consistencygroup_detail.item(2).getAttribute('id'), - consistencygroup_id3) + consistencygroup_id3, + consistencygroup_detail.item(2).getAttribute('id')) self.assertEqual( - consistencygroup_detail.item(2).getAttribute('status'), 'creating') + 'creating', + consistencygroup_detail.item(2).getAttribute('status')) db.consistencygroup_destroy(context.get_admin_context(), consistencygroup_id3) @@ -297,7 +302,14 @@ class ConsistencyGroupsAPITestCase(test.TestCase): def test_create_consistencygroup_json(self): group_id = "1" + + # Create volume type + vol_type = 'test' + db.volume_type_create(context.get_admin_context(), + {'name': vol_type, 'extra_specs': {}}) + body = {"consistencygroup": {"name": "cg1", + "volume_types": vol_type, "description": "Consistency Group 1", }} req = webob.Request.blank('/v2/fake/consistencygroups') @@ -307,7 +319,7 @@ class ConsistencyGroupsAPITestCase(test.TestCase): res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) - self.assertEqual(res.status_int, 202) + self.assertEqual(202, res.status_int) self.assertIn('id', res_dict['consistencygroup']) db.consistencygroup_destroy(context.get_admin_context(), group_id) @@ -322,11 +334,11 @@ class ConsistencyGroupsAPITestCase(test.TestCase): res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) - self.assertEqual(res.status_int, 400) - self.assertEqual(res_dict['badRequest']['code'], 400) - self.assertEqual(res_dict['badRequest']['message'], - 'The server could not comply with the request since' - ' it is either malformed or otherwise incorrect.') + self.assertEqual(400, res.status_int) + self.assertEqual(400, res_dict['badRequest']['code']) + self.assertEqual('The server could not comply with the request since' + ' it is either malformed or otherwise incorrect.', + res_dict['badRequest']['message']) def test_delete_consistencygroup_available(self): consistencygroup_id = self._create_consistencygroup(status='available') @@ -338,10 +350,10 @@ class ConsistencyGroupsAPITestCase(test.TestCase): req.body = json.dumps(body) res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 202) - self.assertEqual(self._get_consistencygroup_attrib(consistencygroup_id, - 'status'), - 'deleting') + self.assertEqual(202, res.status_int) + self.assertEqual('deleting', + self._get_consistencygroup_attrib(consistencygroup_id, + 'status')) db.consistencygroup_destroy(context.get_admin_context(), consistencygroup_id) @@ -354,10 +366,10 @@ class ConsistencyGroupsAPITestCase(test.TestCase): res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) - self.assertEqual(res.status_int, 404) - self.assertEqual(res_dict['itemNotFound']['code'], 404) - self.assertEqual(res_dict['itemNotFound']['message'], - 'Consistency group could not be found') + self.assertEqual(404, res.status_int) + self.assertEqual(404, res_dict['itemNotFound']['code']) + self.assertEqual('Consistency group 9999 could not be found.', + res_dict['itemNotFound']['message']) def test_delete_consistencygroup_with_Invalidconsistencygroup(self): consistencygroup_id = self._create_consistencygroup(status='invalid') @@ -370,10 +382,11 @@ class ConsistencyGroupsAPITestCase(test.TestCase): res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) - self.assertEqual(res.status_int, 400) - self.assertEqual(res_dict['badRequest']['code'], 400) - self.assertEqual(res_dict['badRequest']['message'], - 'Invalid consistency group') + self.assertEqual(400, res.status_int) + self.assertEqual(400, res_dict['badRequest']['code']) + msg = (_('Invalid ConsistencyGroup: Consistency group status must be ' + 'available or error, but current status is: invalid')) + self.assertEqual(msg, res_dict['badRequest']['message']) db.consistencygroup_destroy(context.get_admin_context(), consistencygroup_id) @@ -425,3 +438,21 @@ class ConsistencyGroupsAPITestCase(test.TestCase): context.get_admin_context(read_deleted='yes'), cg['id']) self.assertEqual(cg['status'], 'deleted') + + def test_create_consistencygroup_failed_no_volume_type(self): + name = 'cg1' + body = {"consistencygroup": {"name": name, + "description": + "Consistency Group 1", }} + req = webob.Request.blank('/v2/fake/consistencygroups') + req.method = 'POST' + req.headers['Content-Type'] = 'application/json' + req.body = json.dumps(body) + res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + + self.assertEqual(400, res.status_int) + self.assertEqual(400, res_dict['badRequest']['code']) + msg = (_('volume_types must be provided to create ' + 'consistency group %s.') % name) + self.assertEqual(msg, res_dict['badRequest']['message']) diff --git a/cinder/tests/api/v2/test_volumes.py b/cinder/tests/api/v2/test_volumes.py index ca94c4fea..1fded8937 100644 --- a/cinder/tests/api/v2/test_volumes.py +++ b/cinder/tests/api/v2/test_volumes.py @@ -115,6 +115,36 @@ class VolumeApiTest(test.TestCase): 'encrypted': False}} self.assertEqual(res_dict, ex) + def test_volume_create_with_consistencygroup_invalid_type(self): + ctxt = context.RequestContext('fake', 'fake', auth_token=True) + vol_type = db.volume_type_create( + context.get_admin_context(), + dict(name=CONF.default_volume_type, extra_specs={}) + ) + db_vol_type = db.volume_type_get(context.get_admin_context(), + vol_type.id) + cg = { + 'id': '1', + 'name': 'cg1', + 'volume_type_id': db_vol_type['id'], + } + fake_type = { + 'id': '9999', + 'name': 'fake', + } + vol_api = volume_api.API() + + self.assertRaises(exception.InvalidInput, + vol_api.create, + ctxt, 1, 'vol1', 'volume 1', + consistencygroup=cg) + + self.assertRaises(exception.InvalidInput, + vol_api.create, + ctxt, 1, 'vol1', 'volume 1', + volume_type=fake_type, + consistencygroup=cg) + def test_volume_create_with_type(self): vol_type = db.volume_type_create( context.get_admin_context(), diff --git a/cinder/tests/scheduler/test_scheduler.py b/cinder/tests/scheduler/test_scheduler.py index 42c7180ee..c7497c98c 100644 --- a/cinder/tests/scheduler/test_scheduler.py +++ b/cinder/tests/scheduler/test_scheduler.py @@ -22,7 +22,10 @@ import mock from oslo.config import cfg from cinder import context +from cinder import db from cinder import exception +from cinder.i18n import _ +from cinder.openstack.common import log as logging from cinder.scheduler import driver from cinder.scheduler import filter_scheduler from cinder.scheduler import manager @@ -188,6 +191,46 @@ class SchedulerManagerTestCase(test.TestCase): {'status': 'in-use'}) self.manager.driver.find_retype_host = orig_retype + def test_create_consistencygroup_exceptions(self): + with mock.patch.object(filter_scheduler.FilterScheduler, + 'schedule_create_consistencygroup') as mock_cg: + original_driver = self.manager.driver + self.manager.driver = filter_scheduler.FilterScheduler + LOG = logging.getLogger('cinder.scheduler.manager') + self.stubs.Set(LOG, 'error', mock.Mock()) + self.stubs.Set(LOG, 'exception', mock.Mock()) + self.stubs.Set(db, 'consistencygroup_update', mock.Mock()) + + ex = exception.CinderException('test') + mock_cg.side_effect = ex + group_id = '1' + self.assertRaises(exception.CinderException, + self.manager.create_consistencygroup, + self.context, + 'volume', + group_id) + LOG.exception.assert_called_once_with(_( + "Failed to create consistency group " + "%(group_id)s."), {'group_id': group_id}) + db.consistencygroup_update.assert_called_once_with( + self.context, group_id, {'status': 'error'}) + + mock_cg.reset_mock() + LOG.exception.reset_mock() + db.consistencygroup_update.reset_mock() + + mock_cg.side_effect = exception.NoValidHost( + reason="No weighed hosts available") + self.manager.create_consistencygroup( + self.context, 'volume', group_id) + LOG.error.assert_called_once_with(_( + "Could not find a host for consistency group " + "%(group_id)s.") % {'group_id': group_id}) + db.consistencygroup_update.assert_called_once_with( + self.context, group_id, {'status': 'error'}) + + self.manager.driver = original_driver + class SchedulerTestCase(test.TestCase): """Test case for base scheduler driver class.""" diff --git a/cinder/volume/api.py b/cinder/volume/api.py index 8bbe004ae..e06ee1d08 100644 --- a/cinder/volume/api.py +++ b/cinder/volume/api.py @@ -155,11 +155,15 @@ class API(base.Base): scheduler_hints=None, backup_source_volume=None, source_replica=None, consistencygroup=None): - if volume_type and consistencygroup: + if consistencygroup: + if not volume_type: + msg = _("volume_type must be provided when creating " + "a volume in a consistency group.") + raise exception.InvalidInput(reason=msg) cg_voltypeids = consistencygroup.get('volume_type_id') if volume_type.get('id') not in cg_voltypeids: msg = _("Invalid volume_type provided (requested type " - "must be supported by this consistency group.") + "must be supported by this consistency group).") raise exception.InvalidInput(reason=msg) if source_volume and volume_type: -- 2.45.2