From 01ed01db34efcb19e16ad2322abfa9eac70b7704 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 24 Jun 2015 12:32:29 +0200 Subject: [PATCH] CGSnapshot Object This patch adds VersionedObjects abstraction layer to CGSnapshots. Co-Authored-By: Szymon Wroblewski Co-Authored-By: Michal Dulko Partial-Implements: blueprint cinder-objects Change-Id: Ie4cdd1ffae15a93bff756ad278ca680f9f420748 --- cinder/api/contrib/cgsnapshots.py | 4 +- cinder/api/views/cgsnapshots.py | 16 +- cinder/consistencygroup/api.py | 66 ++++--- cinder/objects/__init__.py | 1 + cinder/objects/cgsnapshot.py | 158 +++++++++++++++++ cinder/tests/unit/objects/test_cgsnapshot.py | 148 ++++++++++++++++ cinder/tests/unit/test_volume.py | 59 ++++--- cinder/tests/unit/test_volume_rpcapi.py | 44 ++--- cinder/tests/unit/utils.py | 27 +-- cinder/volume/manager.py | 176 +++++++++---------- cinder/volume/rpcapi.py | 29 ++- cinder/volume/utils.py | 16 +- tools/lintstack.py | 12 +- 13 files changed, 528 insertions(+), 228 deletions(-) create mode 100644 cinder/objects/cgsnapshot.py create mode 100644 cinder/tests/unit/objects/test_cgsnapshot.py diff --git a/cinder/api/contrib/cgsnapshots.py b/cinder/api/contrib/cgsnapshots.py index 16ebf9917..752f5e4b1 100644 --- a/cinder/api/contrib/cgsnapshots.py +++ b/cinder/api/contrib/cgsnapshots.py @@ -187,9 +187,7 @@ class CgsnapshotsController(wsgi.Controller): except exception.CgSnapshotNotFound as error: raise exc.HTTPNotFound(explanation=error.msg) - retval = self._view_builder.summary( - req, - dict(new_cgsnapshot)) + retval = self._view_builder.summary(req, new_cgsnapshot) return retval diff --git a/cinder/api/views/cgsnapshots.py b/cinder/api/views/cgsnapshots.py index b8b7eddce..d8f92fcb3 100644 --- a/cinder/api/views/cgsnapshots.py +++ b/cinder/api/views/cgsnapshots.py @@ -42,8 +42,8 @@ class ViewBuilder(common.ViewBuilder): """Generic, non-detailed view of a cgsnapshot.""" return { 'cgsnapshot': { - 'id': cgsnapshot['id'], - 'name': cgsnapshot['name'] + 'id': cgsnapshot.id, + 'name': cgsnapshot.name } } @@ -51,12 +51,12 @@ class ViewBuilder(common.ViewBuilder): """Detailed view of a single cgsnapshot.""" return { 'cgsnapshot': { - 'id': cgsnapshot.get('id'), - 'consistencygroup_id': cgsnapshot.get('consistencygroup_id'), - 'status': cgsnapshot.get('status'), - 'created_at': cgsnapshot.get('created_at'), - 'name': cgsnapshot.get('name'), - 'description': cgsnapshot.get('description') + 'id': cgsnapshot.id, + 'consistencygroup_id': cgsnapshot.consistencygroup_id, + 'status': cgsnapshot.status, + 'created_at': cgsnapshot.created_at, + 'name': cgsnapshot.name, + 'description': cgsnapshot.description } } diff --git a/cinder/consistencygroup/api.py b/cinder/consistencygroup/api.py index 5df29bf42..3539df8ad 100644 --- a/cinder/consistencygroup/api.py +++ b/cinder/consistencygroup/api.py @@ -168,15 +168,16 @@ class API(base.Base): orig_cg = None if cgsnapshot_id: try: - cgsnapshot = self.db.cgsnapshot_get(context, cgsnapshot_id) + cgsnapshot = objects.CGSnapshot.get_by_id(context, + cgsnapshot_id) except exception.CgSnapshotNotFound: with excutils.save_and_reraise_exception(): LOG.error(_LE("CG snapshot %(cgsnap)s not found when " "creating consistency group %(cg)s from " "source."), {'cg': name, 'cgsnap': cgsnapshot_id}) - orig_cg = objects.ConsistencyGroup.get_by_id( - context, cgsnapshot['consistencygroup_id']) + else: + orig_cg = cgsnapshot.consistencygroup source_cg = None if source_cgid: @@ -238,7 +239,7 @@ class API(base.Base): def _create_cg_from_cgsnapshot(self, context, group, cgsnapshot): try: snapshots = objects.SnapshotList.get_all_for_cgsnapshot( - context, cgsnapshot['id']) + context, cgsnapshot.id) if not snapshots: msg = _("Cgsnahost is empty. No consistency group " @@ -274,7 +275,7 @@ class API(base.Base): "creating consistency group %(group)s " "from cgsnapshot %(cgsnap)s."), {'group': group.id, - 'cgsnap': cgsnapshot['id']}) + 'cgsnap': cgsnapshot.id}) except Exception: with excutils.save_and_reraise_exception(): try: @@ -284,7 +285,7 @@ class API(base.Base): "group %(group)s from cgsnapshot " "%(cgsnap)s."), {'group': group.id, - 'cgsnap': cgsnapshot['id']}) + 'cgsnap': cgsnapshot.id}) volumes = self.db.volume_get_all_by_group(context, group.id) @@ -444,10 +445,9 @@ class API(base.Base): "but current status is: %s") % group.status raise exception.InvalidConsistencyGroup(reason=msg) - cgsnaps = self.db.cgsnapshot_get_all_by_group( - context.elevated(), - group.id) - if cgsnaps: + cgsnapshots = objects.CGSnapshotList.get_all_by_group( + context.elevated(), group.id) + if cgsnapshots: msg = _("Consistency group %s still has dependent " "cgsnapshots.") % group.id LOG.error(msg) @@ -709,14 +709,12 @@ class API(base.Base): context, context.project_id) return groups - def create_cgsnapshot(self, context, - group, name, - description): + def create_cgsnapshot(self, context, group, name, description): return self._create_cgsnapshot(context, group, name, description) def _create_cgsnapshot(self, context, group, name, description): - options = {'consistencygroup_id': group['id'], + options = {'consistencygroup_id': group.id, 'user_id': context.user_id, 'project_id': context.project_id, 'status': "creating", @@ -724,65 +722,63 @@ class API(base.Base): 'description': description} try: - cgsnapshot = self.db.cgsnapshot_create(context, options) - cgsnapshot_id = cgsnapshot['id'] + cgsnapshot = objects.CGSnapshot(context, **options) + cgsnapshot.create() + cgsnapshot_id = cgsnapshot.id volumes = self.db.volume_get_all_by_group( context.elevated(), - cgsnapshot['consistencygroup_id']) + cgsnapshot.consistencygroup_id) if not volumes: msg = _("Consistency group is empty. No cgsnapshot " "will be created.") raise exception.InvalidConsistencyGroup(reason=msg) - snap_name = cgsnapshot['name'] - snap_desc = cgsnapshot['description'] + snap_name = cgsnapshot.name + snap_desc = cgsnapshot.description self.volume_api.create_snapshots_in_db( context, volumes, snap_name, snap_desc, True, cgsnapshot_id) except Exception: with excutils.save_and_reraise_exception(): try: - self.db.cgsnapshot_destroy(context, cgsnapshot_id) + cgsnapshot.destroy() finally: LOG.error(_LE("Error occurred when creating cgsnapshot" " %s."), cgsnapshot_id) - self.volume_rpcapi.create_cgsnapshot(context, group, cgsnapshot) + self.volume_rpcapi.create_cgsnapshot(context, cgsnapshot) return cgsnapshot def delete_cgsnapshot(self, context, cgsnapshot, force=False): - if cgsnapshot['status'] not in ["available", "error"]: + if cgsnapshot.status not in ["available", "error"]: msg = _("Cgsnapshot status must be available or error") raise exception.InvalidCgSnapshot(reason=msg) - self.db.cgsnapshot_update(context, cgsnapshot['id'], - {'status': 'deleting'}) - group = objects.ConsistencyGroup.get_by_id(context, cgsnapshot[ - 'consistencygroup_id']) - self.volume_rpcapi.delete_cgsnapshot(context.elevated(), cgsnapshot, - group.host) + cgsnapshot.update({'status': 'deleting'}) + cgsnapshot.save() + self.volume_rpcapi.delete_cgsnapshot(context.elevated(), cgsnapshot) def update_cgsnapshot(self, context, cgsnapshot, fields): - self.db.cgsnapshot_update(context, cgsnapshot['id'], fields) + cgsnapshot.update(fields) + cgsnapshot.save() def get_cgsnapshot(self, context, cgsnapshot_id): check_policy(context, 'get_cgsnapshot') - rv = self.db.cgsnapshot_get(context, cgsnapshot_id) - return dict(rv) + cgsnapshots = objects.CGSnapshot.get_by_id(context, cgsnapshot_id) + return cgsnapshots def get_all_cgsnapshots(self, context, search_opts=None): check_policy(context, 'get_all_cgsnapshots') search_opts = search_opts or {} - if (context.is_admin and 'all_tenants' in search_opts): + if context.is_admin and 'all_tenants' in search_opts: # Need to remove all_tenants to pass the filtering below. del search_opts['all_tenants'] - cgsnapshots = self.db.cgsnapshot_get_all(context, search_opts) + cgsnapshots = objects.CGSnapshotList.get_all(context, search_opts) else: - cgsnapshots = self.db.cgsnapshot_get_all_by_project( + cgsnapshots = objects.CGSnapshotList.get_all_by_project( context.elevated(), context.project_id, search_opts) - return cgsnapshots diff --git a/cinder/objects/__init__.py b/cinder/objects/__init__.py index b0b82323b..f8c2ba489 100644 --- a/cinder/objects/__init__.py +++ b/cinder/objects/__init__.py @@ -26,6 +26,7 @@ def register_all(): # need to receive it via RPC. __import__('cinder.objects.backup') __import__('cinder.objects.consistencygroup') + __import__('cinder.objects.cgsnapshot') __import__('cinder.objects.service') __import__('cinder.objects.snapshot') __import__('cinder.objects.volume') diff --git a/cinder/objects/cgsnapshot.py b/cinder/objects/cgsnapshot.py new file mode 100644 index 000000000..1defc3146 --- /dev/null +++ b/cinder/objects/cgsnapshot.py @@ -0,0 +1,158 @@ +# Copyright 2015 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from cinder import db +from cinder import exception +from cinder.i18n import _ +from cinder import objects +from cinder.objects import base +from oslo_versionedobjects import fields + +OPTIONAL_FIELDS = ['consistencygroup', 'snapshots'] + + +@base.CinderObjectRegistry.register +class CGSnapshot(base.CinderPersistentObject, base.CinderObject, + base.CinderObjectDictCompat): + VERSION = '1.0' + + fields = { + 'id': fields.UUIDField(), + 'consistencygroup_id': fields.UUIDField(nullable=True), + 'project_id': fields.UUIDField(), + 'user_id': fields.UUIDField(), + 'name': fields.StringField(nullable=True), + 'description': fields.StringField(nullable=True), + 'status': fields.StringField(nullable=True), + 'consistencygroup': fields.ObjectField('ConsistencyGroup', + nullable=True), + 'snapshots': fields.ObjectField('SnapshotList', nullable=True), + } + + @staticmethod + def _from_db_object(context, cgsnapshot, db_cgsnapshots, + expected_attrs=None): + expected_attrs = expected_attrs or [] + for name, field in cgsnapshot.fields.items(): + if name in OPTIONAL_FIELDS: + continue + value = db_cgsnapshots.get(name) + setattr(cgsnapshot, name, value) + + if 'consistencygroup' in expected_attrs: + consistencygroup = objects.ConsistencyGroup(context) + consistencygroup._from_db_object(context, consistencygroup, + db_cgsnapshots[ + 'consistencygroup']) + cgsnapshot.consistencygroup = consistencygroup + + if 'snapshots' in expected_attrs: + snapshots = base.obj_make_list( + context, objects.SnapshotsList(context), + objects.Snapshots, + db_cgsnapshots['snapshots']) + cgsnapshot.snapshots = snapshots + + cgsnapshot._context = context + cgsnapshot.obj_reset_changes() + return cgsnapshot + + @base.remotable_classmethod + def get_by_id(cls, context, id): + db_cgsnapshots = db.cgsnapshot_get(context, id) + return cls._from_db_object(context, cls(context), db_cgsnapshots) + + @base.remotable + def create(self): + if self.obj_attr_is_set('id'): + raise exception.ObjectActionError(action='create', + reason=_('already_created')) + updates = self.cinder_obj_get_changes() + + if 'consistencygroup' in updates: + raise exception.ObjectActionError( + action='create', reason=_('consistencygroup assigned')) + + db_cgsnapshots = db.cgsnapshot_create(self._context, updates) + self._from_db_object(self._context, self, db_cgsnapshots) + + def obj_load_attr(self, attrname): + if attrname not in OPTIONAL_FIELDS: + raise exception.ObjectActionError( + action='obj_load_attr', + reason=_('attribute %s not lazy-loadable') % attrname) + if not self._context: + raise exception.OrphanedObjectError(method='obj_load_attr', + objtype=self.obj_name()) + + if attrname == 'consistencygroup': + self.consistencygroup = objects.ConsistencyGroup.get_by_id( + self._context, self.consistencygroup_id) + + if attrname == 'snapshots': + self.snapshots = objects.SnapshotList.get_all_for_cgsnapshot( + self._context, self.id) + + self.obj_reset_changes(fields=[attrname]) + + @base.remotable + def save(self): + updates = self.cinder_obj_get_changes() + if updates: + if 'consistencygroup' in updates: + raise exception.ObjectActionError( + action='save', reason=_('consistencygroup changed')) + if 'snapshots' in updates: + raise exception.ObjectActionError( + action='save', reason=_('snapshots changed')) + db.cgsnapshot_update(self._context, self.id, updates) + self.obj_reset_changes() + + @base.remotable + def destroy(self): + with self.obj_as_admin(): + db.cgsnapshot_destroy(self._context, self.id) + + +@base.CinderObjectRegistry.register +class CGSnapshotList(base.ObjectListBase, base.CinderObject): + VERSION = '1.0' + + fields = { + 'objects': fields.ListOfObjectsField('CGSnapshot') + } + child_version = { + '1.0': '1.0' + } + + @base.remotable_classmethod + def get_all(cls, context, filters=None): + cgsnapshots = db.cgsnapshot_get_all(context, filters) + return base.obj_make_list(context, cls(context), objects.CGSnapshot, + cgsnapshots) + + @base.remotable_classmethod + def get_all_by_project(cls, context, project_id, filters=None): + cgsnapshots = db.cgsnapshot_get_all_by_project(context, project_id, + filters) + return base.obj_make_list(context, cls(context), objects.CGSnapshot, + cgsnapshots) + + @base.remotable_classmethod + def get_all_by_group(cls, context, group_id, filters=None): + cgsnapshots = db.cgsnapshot_get_all_by_group(context, group_id, + filters) + return base.obj_make_list(context, cls(context), + objects.CGSnapshot, + cgsnapshots) diff --git a/cinder/tests/unit/objects/test_cgsnapshot.py b/cinder/tests/unit/objects/test_cgsnapshot.py new file mode 100644 index 000000000..d72225084 --- /dev/null +++ b/cinder/tests/unit/objects/test_cgsnapshot.py @@ -0,0 +1,148 @@ +# Copyright 2015 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock + +from cinder import context +from cinder import exception +from cinder import objects +from cinder.tests.unit import objects as test_objects +from cinder.tests.unit.objects.test_consistencygroup import \ + fake_consistencygroup + +fake_cgsnapshot = { + 'id': '1', + 'user_id': 'fake_user_id', + 'project_id': 'fake_project_id', + 'name': 'fake_name', + 'description': 'fake_description', + 'status': 'creating', + 'consistencygroup_id': 'fake_id', +} + + +class TestCGSnapshot(test_objects.BaseObjectsTestCase): + def setUp(self): + super(TestCGSnapshot, self).setUp() + # NOTE (e0ne): base tests contains original RequestContext from + # oslo_context. We change it to our RequestContext implementation + # to have 'elevated' method + self.user_id = 123 + self.project_id = 456 + self.context = context.RequestContext(self.user_id, self.project_id, + is_admin=False) + + @staticmethod + def _compare(test, db, obj): + for field, value in db.items(): + test.assertEqual(db[field], getattr(obj, field)) + + @mock.patch('cinder.db.cgsnapshot_get', + return_value=fake_cgsnapshot) + def test_get_by_id(self, cgsnapshot_get): + cgsnapshot = objects.CGSnapshot.get_by_id(self.context, 1) + self._compare(self, fake_cgsnapshot, cgsnapshot) + + @mock.patch('cinder.db.cgsnapshot_create', + return_value=fake_cgsnapshot) + def test_create(self, cgsnapshot_create): + fake_cgsnap = fake_cgsnapshot.copy() + del fake_cgsnap['id'] + cgsnapshot = objects.CGSnapshot(context=self.context, **fake_cgsnap) + cgsnapshot.create() + self._compare(self, fake_cgsnapshot, cgsnapshot) + + def test_create_with_id_except_exception(self): + cgsnapshot = objects.CGSnapshot(context=self.context, **{'id': 2}) + self.assertRaises(exception.ObjectActionError, cgsnapshot.create) + + @mock.patch('cinder.db.cgsnapshot_update') + def test_save(self, cgsnapshot_update): + cgsnapshot = objects.CGSnapshot._from_db_object( + self.context, objects.CGSnapshot(), fake_cgsnapshot) + cgsnapshot.status = 'active' + cgsnapshot.save() + cgsnapshot_update.assert_called_once_with(self.context, cgsnapshot.id, + {'status': 'active'}) + + @mock.patch('cinder.db.consistencygroup_update', + return_value=fake_consistencygroup) + @mock.patch('cinder.db.cgsnapshot_update') + def test_save_with_consistencygroup(self, cgsnapshot_update, + cgsnapshot_cg_update): + consistencygroup = objects.ConsistencyGroup._from_db_object( + self.context, objects.ConsistencyGroup(), fake_consistencygroup) + cgsnapshot = objects.CGSnapshot._from_db_object( + self.context, objects.CGSnapshot(), fake_cgsnapshot) + cgsnapshot.name = 'foobar' + cgsnapshot.consistencygroup = consistencygroup + self.assertEqual({'name': 'foobar', + 'consistencygroup': consistencygroup}, + cgsnapshot.obj_get_changes()) + self.assertRaises(exception.ObjectActionError, cgsnapshot.save) + + @mock.patch('cinder.db.cgsnapshot_destroy') + def test_destroy(self, cgsnapshot_destroy): + cgsnapshot = objects.CGSnapshot(context=self.context, id=1) + cgsnapshot.destroy() + self.assertTrue(cgsnapshot_destroy.called) + admin_context = cgsnapshot_destroy.call_args[0][0] + self.assertTrue(admin_context.is_admin) + + @mock.patch('cinder.objects.consistencygroup.ConsistencyGroup.get_by_id') + @mock.patch('cinder.objects.snapshot.SnapshotList.get_all_for_cgsnapshot') + def test_obj_load_attr(self, snapshotlist_get_for_cgs, + consistencygroup_get_by_id): + cgsnapshot = objects.CGSnapshot._from_db_object( + self.context, objects.CGSnapshot(), fake_cgsnapshot) + # Test consistencygroup lazy-loaded field + consistencygroup = objects.ConsistencyGroup(context=self.context, id=2) + consistencygroup_get_by_id.return_value = consistencygroup + self.assertEqual(consistencygroup, cgsnapshot.consistencygroup) + consistencygroup_get_by_id.assert_called_once_with( + self.context, cgsnapshot.consistencygroup_id) + # Test snapshots lazy-loaded field + snapshots_objs = [objects.Snapshot(context=self.context, id=i) + for i in [3, 4, 5]] + snapshots = objects.SnapshotList(context=self.context, + objects=snapshots_objs) + snapshotlist_get_for_cgs.return_value = snapshots + self.assertEqual(snapshots, cgsnapshot.snapshots) + snapshotlist_get_for_cgs.assert_called_once_with( + self.context, cgsnapshot.id) + + +class TestCGSnapshotList(test_objects.BaseObjectsTestCase): + @mock.patch('cinder.db.cgsnapshot_get_all', + return_value=[fake_cgsnapshot]) + def test_get_all(self, cgsnapshot_get_all): + cgsnapshots = objects.CGSnapshotList.get_all(self.context) + self.assertEqual(1, len(cgsnapshots)) + TestCGSnapshot._compare(self, fake_cgsnapshot, cgsnapshots[0]) + + @mock.patch('cinder.db.cgsnapshot_get_all_by_project', + return_value=[fake_cgsnapshot]) + def test_get_all_by_project(self, cgsnapshot_get_all_by_project): + cgsnapshots = objects.CGSnapshotList.get_all_by_project( + self.context, self.project_id) + self.assertEqual(1, len(cgsnapshots)) + TestCGSnapshot._compare(self, fake_cgsnapshot, cgsnapshots[0]) + + @mock.patch('cinder.db.cgsnapshot_get_all_by_group', + return_value=[fake_cgsnapshot]) + def test_get_all_by_group(self, cgsnapshot_get_all_by_group): + cgsnapshots = objects.CGSnapshotList.get_all_by_group( + self.context, self.project_id) + self.assertEqual(1, len(cgsnapshots)) + TestCGSnapshot._compare(self, fake_cgsnapshot, cgsnapshots[0]) diff --git a/cinder/tests/unit/test_volume.py b/cinder/tests/unit/test_volume.py index fa10f4928..bbc43abab 100644 --- a/cinder/tests/unit/test_volume.py +++ b/cinder/tests/unit/test_volume.py @@ -5083,7 +5083,7 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase): size=1) volume_id = volume['id'] cgsnapshot_returns = self._create_cgsnapshot(group.id, volume_id) - cgsnapshot_id = cgsnapshot_returns[0]['id'] + cgsnapshot = cgsnapshot_returns[0] snapshot_id = cgsnapshot_returns[1]['id'] # Create CG from source CG snapshot. @@ -5091,7 +5091,8 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase): self.context, availability_zone=CONF.storage_availability_zone, volume_type='type1,type2', - cgsnapshot_id=cgsnapshot_id) + cgsnapshot_id=cgsnapshot.id) + group2 = objects.ConsistencyGroup.get_by_id(self.context, group2.id) volume2 = tests_utils.create_volume( self.context, consistencygroup_id=group2.id, @@ -5100,7 +5101,7 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase): volume2_id = volume2['id'] self.volume.create_volume(self.context, volume2_id) self.volume.create_consistencygroup_from_src( - self.context, group2, cgsnapshot_id=cgsnapshot_id) + self.context, group2, cgsnapshot=cgsnapshot) cg2 = objects.ConsistencyGroup.get_by_id(self.context, group2.id) expected = { 'status': 'available', @@ -5113,7 +5114,7 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase): } self.assertEqual('available', cg2.status) self.assertEqual(group2.id, cg2['id']) - self.assertEqual(cgsnapshot_id, cg2['cgsnapshot_id']) + self.assertEqual(cgsnapshot.id, cg2['cgsnapshot_id']) self.assertIsNone(cg2['source_cgid']) msg = self.notifier.notifications[2] @@ -5177,9 +5178,9 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase): self.assertEqual(group.id, cg3.source_cgid) self.assertIsNone(cg3.cgsnapshot_id) - self.volume.delete_cgsnapshot(self.context, cgsnapshot_id) + self.volume.delete_cgsnapshot(self.context, cgsnapshot) + self.volume.delete_consistencygroup(self.context, group) - self.volume.delete_consistencygroup(self.context, group3) def test_sort_snapshots(self): vol1 = {'id': '1', 'name': 'volume 1', @@ -5274,15 +5275,14 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase): self.volume._sort_source_vols, volumes, []) - @staticmethod - def _create_cgsnapshot(group_id, volume_id, size='0'): + def _create_cgsnapshot(self, group_id, volume_id, size='0'): """Create a cgsnapshot object.""" - cgsnap = {} - cgsnap['user_id'] = 'fake' - cgsnap['project_id'] = 'fake' - cgsnap['consistencygroup_id'] = group_id - cgsnap['status'] = "creating" - cgsnapshot = db.cgsnapshot_create(context.get_admin_context(), cgsnap) + cgsnap = objects.CGSnapshot(self.context) + cgsnap.user_id = 'fake' + cgsnap.project_id = 'fake' + cgsnap.consistencygroup_id = group_id + cgsnap.status = "creating" + cgsnap.create() # Create a snapshot object snap = objects.Snapshot(context.get_admin_context()) @@ -5291,10 +5291,10 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase): snap.project_id = 'fake' snap.volume_id = volume_id snap.status = "available" - snap.cgsnapshot_id = cgsnapshot['id'] + snap.cgsnapshot_id = cgsnap.id snap.create() - return cgsnapshot, snap + return cgsnap, snap @mock.patch('cinder.volume.driver.VolumeDriver.create_consistencygroup', autospec=True, @@ -5331,11 +5331,12 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase): self.notifier.notifications) cgsnapshot_returns = self._create_cgsnapshot(group.id, volume_id) - cgsnapshot_id = cgsnapshot_returns[0]['id'] - self.volume.create_cgsnapshot(self.context, group.id, cgsnapshot_id) - self.assertEqual(cgsnapshot_id, - db.cgsnapshot_get(context.get_admin_context(), - cgsnapshot_id).id) + cgsnapshot = cgsnapshot_returns[0] + self.volume.create_cgsnapshot(self.context, cgsnapshot) + self.assertEqual(cgsnapshot.id, + objects.CGSnapshot.get_by_id( + context.get_admin_context(), + cgsnapshot.id).id) if len(self.notifier.notifications) > 6: self.assertFalse(self.notifier.notifications[6], @@ -5346,7 +5347,7 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase): expected = { 'created_at': 'DONTCARE', 'name': None, - 'cgsnapshot_id': cgsnapshot_id, + 'cgsnapshot_id': cgsnapshot.id, 'status': 'creating', 'tenant_id': 'fake', 'user_id': 'fake', @@ -5356,6 +5357,7 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase): msg = self.notifier.notifications[3] self.assertEqual('snapshot.create.start', msg['event_type']) msg = self.notifier.notifications[4] + expected['status'] = 'available' self.assertEqual('cgsnapshot.create.end', msg['event_type']) self.assertDictMatch(expected, msg['payload']) msg = self.notifier.notifications[5] @@ -5364,7 +5366,7 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase): self.assertEqual(6, len(self.notifier.notifications), self.notifier.notifications) - self.volume.delete_cgsnapshot(self.context, cgsnapshot_id) + self.volume.delete_cgsnapshot(self.context, cgsnapshot) if len(self.notifier.notifications) > 10: self.assertFalse(self.notifier.notifications[10], @@ -5376,19 +5378,20 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase): self.assertDictMatch(expected, msg['payload']) msg = self.notifier.notifications[8] self.assertEqual('cgsnapshot.delete.end', msg['event_type']) + expected['status'] = 'deleted' self.assertDictMatch(expected, msg['payload']) self.assertEqual(10, len(self.notifier.notifications), self.notifier.notifications) - cgsnap = db.cgsnapshot_get( + cgsnap = objects.CGSnapshot.get_by_id( context.get_admin_context(read_deleted='yes'), - cgsnapshot_id) - self.assertEqual('deleted', cgsnap['status']) + cgsnapshot.id) + self.assertEqual('deleted', cgsnap.status) self.assertRaises(exception.NotFound, - db.cgsnapshot_get, + objects.CGSnapshot.get_by_id, self.context, - cgsnapshot_id) + cgsnapshot.id) self.volume.delete_consistencygroup(self.context, group) diff --git a/cinder/tests/unit/test_volume_rpcapi.py b/cinder/tests/unit/test_volume_rpcapi.py index b9c57166b..60adbe8b3 100644 --- a/cinder/tests/unit/test_volume_rpcapi.py +++ b/cinder/tests/unit/test_volume_rpcapi.py @@ -64,24 +64,25 @@ class VolumeRpcAPITestCase(test.TestCase): cgsnapshot = tests_utils.create_cgsnapshot( self.context, - consistencygroup_id=source_group['id']) + consistencygroup_id=source_group.id) group = tests_utils.create_consistencygroup( self.context, availability_zone=CONF.storage_availability_zone, volume_type='type1,type2', host='fakehost@fakedrv#fakepool', - cgsnapshot_id=cgsnapshot['id']) + cgsnapshot_id=cgsnapshot.id) group2 = tests_utils.create_consistencygroup( self.context, availability_zone=CONF.storage_availability_zone, volume_type='type1,type2', host='fakehost@fakedrv#fakepool', - source_cgid=source_group['id']) + source_cgid=source_group.id) group = objects.ConsistencyGroup.get_by_id(self.context, group.id) group2 = objects.ConsistencyGroup.get_by_id(self.context, group2.id) + cgsnapshot = objects.CGSnapshot.get_by_id(self.context, cgsnapshot.id) self.fake_volume = jsonutils.to_primitive(volume) self.fake_volume_metadata = volume["volume_metadata"] self.fake_snapshot = snapshot @@ -89,7 +90,7 @@ class VolumeRpcAPITestCase(test.TestCase): self.fake_cg = group self.fake_cg2 = group2 self.fake_src_cg = jsonutils.to_primitive(source_group) - self.fake_cgsnap = jsonutils.to_primitive(cgsnapshot) + self.fake_cgsnap = cgsnapshot def test_serialized_volume_has_id(self): self.assertIn('id', self.fake_volume) @@ -123,6 +124,11 @@ class VolumeRpcAPITestCase(test.TestCase): del expected_msg['snapshot'] expected_msg['snapshot_id'] = snapshot.id expected_msg['snapshot'] = snapshot + if 'cgsnapshot' in expected_msg: + cgsnapshot = expected_msg['cgsnapshot'] + if cgsnapshot: + cgsnapshot.consistencygroup + kwargs['cgsnapshot'].consistencygroup if 'host' in expected_msg: del expected_msg['host'] if 'dest_host' in expected_msg: @@ -136,22 +142,16 @@ class VolumeRpcAPITestCase(test.TestCase): del expected_msg['new_volume'] expected_msg['new_volume_id'] = volume['id'] - if 'cgsnapshot' in expected_msg: - cgsnapshot = expected_msg['cgsnapshot'] - if cgsnapshot: - del expected_msg['cgsnapshot'] - expected_msg['cgsnapshot_id'] = cgsnapshot['id'] - else: - expected_msg['cgsnapshot_id'] = None - if 'host' in kwargs: host = kwargs['host'] elif 'group' in kwargs: host = kwargs['group']['host'] - elif 'volume' not in kwargs and 'snapshot' in kwargs: - host = 'fake_host' - else: + elif 'volume' in kwargs: host = kwargs['volume']['host'] + elif 'snapshot' in kwargs: + host = 'fake_host' + elif 'cgsnapshot' in kwargs: + host = kwargs['cgsnapshot'].consistencygroup.host target['server'] = utils.extract_host(host) target['topic'] = '%s.%s' % (CONF.volume_topic, host) @@ -190,6 +190,10 @@ class VolumeRpcAPITestCase(test.TestCase): expected_cg = expected_msg[kwarg].obj_to_primitive() cg = value.obj_to_primitive() self.assertEqual(expected_cg, cg) + elif isinstance(value, objects.CGSnapshot): + expected_cgsnapshot = expected_msg[kwarg].obj_to_primitive() + cgsnapshot = value.obj_to_primitive() + self.assertEqual(expected_cgsnapshot, cgsnapshot) else: self.assertEqual(expected_msg[kwarg], value) @@ -209,13 +213,11 @@ class VolumeRpcAPITestCase(test.TestCase): def test_create_cgsnapshot(self): self._test_volume_api('create_cgsnapshot', rpc_method='cast', - group=self.fake_cg, - cgsnapshot=self.fake_cgsnap, version='1.26') + cgsnapshot=self.fake_cgsnap, version='1.31') def test_delete_cgsnapshot(self): self._test_volume_api('delete_cgsnapshot', rpc_method='cast', - cgsnapshot=self.fake_cgsnap, host='fake_host1', - version='1.18') + cgsnapshot=self.fake_cgsnap, version='1.31') def test_create_volume(self): self._test_volume_api('create_volume', @@ -414,7 +416,7 @@ class VolumeRpcAPITestCase(test.TestCase): group=self.fake_cg, cgsnapshot=self.fake_cgsnap, source_cg=None, - version='1.26') + version='1.31') def test_create_consistencygroup_from_src_cg(self): self._test_volume_api('create_consistencygroup_from_src', @@ -422,7 +424,7 @@ class VolumeRpcAPITestCase(test.TestCase): group=self.fake_cg2, cgsnapshot=None, source_cg=self.fake_src_cg, - version='1.26') + version='1.31') def test_get_capabilities(self): self._test_volume_api('get_capabilities', diff --git a/cinder/tests/unit/utils.py b/cinder/tests/unit/utils.py index e30455fcd..6b008d9ae 100644 --- a/cinder/tests/unit/utils.py +++ b/cinder/tests/unit/utils.py @@ -139,22 +139,23 @@ def create_consistencygroup(ctxt, def create_cgsnapshot(ctxt, - name='test_cgsnap', - description='this is a test cgsnap', - status='available', - consistencygroup_id=None, + consistencygroup_id, + name='test_cgsnapshot', + description='this is a test cgsnapshot', + status='creating', **kwargs): """Create a cgsnapshot object in the DB.""" - cgsnap = {} - cgsnap['user_id'] = ctxt.user_id - cgsnap['project_id'] = ctxt.project_id - cgsnap['status'] = status - cgsnap['name'] = name - cgsnap['description'] = description - cgsnap['consistencygroup_id'] = consistencygroup_id + cgsnap = objects.CGSnapshot(ctxt) + cgsnap.user_id = ctxt.user_id or 'fake_user_id' + cgsnap.project_id = ctxt.project_id or 'fake_project_id' + cgsnap.status = status + cgsnap.name = name + cgsnap.description = description + cgsnap.consistencygroup_id = consistencygroup_id for key in kwargs: - cgsnap[key] = kwargs[key] - return db.cgsnapshot_create(ctxt, cgsnap) + setattr(cgsnap, key, kwargs[key]) + cgsnap.create() + return cgsnap def create_backup(ctxt, diff --git a/cinder/volume/manager.py b/cinder/volume/manager.py index 75369c8f7..31a2b29d6 100644 --- a/cinder/volume/manager.py +++ b/cinder/volume/manager.py @@ -191,7 +191,7 @@ def locked_snapshot_operation(f): class VolumeManager(manager.SchedulerDependentManager): """Manages attachable block storage devices.""" - RPC_API_VERSION = '1.30' + RPC_API_VERSION = '1.31' target = messaging.Target(version=RPC_API_VERSION) @@ -1966,7 +1966,7 @@ class VolumeManager(manager.SchedulerDependentManager): if not snapshots: snapshots = objects.SnapshotList.get_all_for_cgsnapshot( - context, cgsnapshot['id']) + context, cgsnapshot.id) if snapshots: for snapshot in snapshots: vol_utils.notify_about_snapshot_usage( @@ -2335,41 +2335,45 @@ class VolumeManager(manager.SchedulerDependentManager): return group def create_consistencygroup_from_src(self, context, group, - cgsnapshot_id=None, source_cg=None): + cgsnapshot=None, source_cg=None): """Creates the consistency group from source. The source can be a CG snapshot or a source CG. """ + source_name = None + snapshots = None + source_vols = None try: volumes = self.db.volume_get_all_by_group(context, group.id) - cgsnapshot = None - snapshots = None - if cgsnapshot_id: + if cgsnapshot: try: - cgsnapshot = self.db.cgsnapshot_get(context, cgsnapshot_id) + # Check if cgsnapshot still exists + cgsnapshot = objects.CGSnapshot.get_by_id( + context, cgsnapshot.id) except exception.CgSnapshotNotFound: LOG.error(_LE("Create consistency group " "from snapshot-%(snap)s failed: " "SnapshotNotFound."), - {'snap': cgsnapshot_id}, + {'snap': cgsnapshot.id}, resource={'type': 'consistency_group', 'id': group.id}) raise - if cgsnapshot: - snapshots = objects.SnapshotList.get_all_for_cgsnapshot( - context, cgsnapshot_id) - for snap in snapshots: - if (snap.status not in - VALID_CREATE_CG_SRC_SNAP_STATUS): - msg = (_("Cannot create consistency group " - "%(group)s because snapshot %(snap)s is " - "not in a valid state. Valid states are: " - "%(valid)s.") % - {'group': group.id, - 'snap': snap['id'], - 'valid': VALID_CREATE_CG_SRC_SNAP_STATUS}) - raise exception.InvalidConsistencyGroup(reason=msg) + + source_name = _("snapshot-%s") % cgsnapshot.id + snapshots = objects.SnapshotList.get_all_for_cgsnapshot( + context, cgsnapshot.id) + for snap in snapshots: + if (snap.status not in + VALID_CREATE_CG_SRC_SNAP_STATUS): + msg = (_("Cannot create consistency group " + "%(group)s because snapshot %(snap)s is " + "not in a valid state. Valid states are: " + "%(valid)s.") % + {'group': group.id, + 'snap': snap['id'], + 'valid': VALID_CREATE_CG_SRC_SNAP_STATUS}) + raise exception.InvalidConsistencyGroup(reason=msg) if source_cg: try: @@ -2383,21 +2387,22 @@ class VolumeManager(manager.SchedulerDependentManager): resource={'type': 'consistency_group', 'id': group.id}) raise - if source_cg: - source_vols = self.db.volume_get_all_by_group( - context, source_cg.id) - for source_vol in source_vols: - if (source_vol['status'] not in - VALID_CREATE_CG_SRC_CG_STATUS): - msg = (_("Cannot create consistency group " - "%(group)s because source volume " - "%(source_vol)s is not in a valid " - "state. Valid states are: " - "%(valid)s.") % - {'group': group.id, - 'source_vol': source_vol['id'], - 'valid': VALID_CREATE_CG_SRC_CG_STATUS}) - raise exception.InvalidConsistencyGroup(reason=msg) + + source_name = _("cg-%s") % source_cg.id + source_vols = self.db.volume_get_all_by_group( + context, source_cg.id) + for source_vol in source_vols: + if (source_vol['status'] not in + VALID_CREATE_CG_SRC_CG_STATUS): + msg = (_("Cannot create consistency group " + "%(group)s because source volume " + "%(source_vol)s is not in a valid " + "state. Valid states are: " + "%(valid)s.") % + {'group': group.id, + 'source_vol': source_vol['id'], + 'valid': VALID_CREATE_CG_SRC_CG_STATUS}) + raise exception.InvalidConsistencyGroup(reason=msg) # Sort source snapshots so that they are in the same order as their # corresponding target volumes. @@ -2434,15 +2439,9 @@ class VolumeManager(manager.SchedulerDependentManager): with excutils.save_and_reraise_exception(): group.status = 'error' group.save() - if cgsnapshot_id: - source = _("snapshot-%s") % cgsnapshot_id - elif source_cg: - source = _("cg-%s") % source_cg.id - else: - source = None LOG.error(_LE("Create consistency group " "from source %(source)s failed."), - {'source': source}, + {'source': source_name}, resource={'type': 'consistency_group', 'id': group.id}) # Update volume status to 'error' as well. @@ -2463,10 +2462,9 @@ class VolumeManager(manager.SchedulerDependentManager): self._notify_about_consistencygroup_usage( context, group, "create.end") - LOG.info(_LI("Create consistency group " - "from snapshot-%(snap)s completed successfully."), - {'snap': cgsnapshot_id}, + "from source-%(source)s completed successfully."), + {'source': source_name}, resource={'type': 'consistency_group', 'id': group.id}) return group @@ -2833,33 +2831,33 @@ class VolumeManager(manager.SchedulerDependentManager): return True - def create_cgsnapshot(self, context, group, cgsnapshot_id): + def create_cgsnapshot(self, context, cgsnapshot): """Creates the cgsnapshot.""" caller_context = context context = context.elevated() - cgsnapshot_ref = self.db.cgsnapshot_get(context, cgsnapshot_id) - LOG.info(_LI("Cgsnapshot %s: creating."), cgsnapshot_ref['id']) + + LOG.info(_LI("Cgsnapshot %s: creating."), cgsnapshot.id) snapshots = objects.SnapshotList.get_all_for_cgsnapshot( - context, cgsnapshot_id) + context, cgsnapshot.id) self._notify_about_cgsnapshot_usage( - context, cgsnapshot_ref, "create.start") + context, cgsnapshot, "create.start") try: utils.require_driver_initialized(self.driver) LOG.debug("Cgsnapshot %(cgsnap_id)s: creating.", - {'cgsnap_id': cgsnapshot_id}) + {'cgsnap_id': cgsnapshot.id}) # Pass context so that drivers that want to use it, can, # but it is not a requirement for all drivers. - cgsnapshot_ref['context'] = caller_context + cgsnapshot.context = caller_context for snapshot in snapshots: snapshot.context = caller_context model_update, snapshots = \ - self.driver.create_cgsnapshot(context, cgsnapshot_ref) + self.driver.create_cgsnapshot(context, cgsnapshot) if snapshots: for snapshot in snapshots: @@ -2868,7 +2866,7 @@ class VolumeManager(manager.SchedulerDependentManager): update = {'status': snapshot['status']} # TODO(thangp): Switch over to use snapshot.update() - # after cgsnapshot has been switched over to objects + # after cgsnapshot-objects bugs are fixed self.db.snapshot_update(context, snapshot['id'], update) # If status for one snapshot is error, make sure @@ -2879,15 +2877,14 @@ class VolumeManager(manager.SchedulerDependentManager): if model_update: if model_update['status'] == 'error': msg = (_('Error occurred when creating cgsnapshot ' - '%s.') % cgsnapshot_ref['id']) + '%s.') % cgsnapshot.id) LOG.error(msg) raise exception.VolumeDriverException(message=msg) except Exception: with excutils.save_and_reraise_exception(): - self.db.cgsnapshot_update(context, - cgsnapshot_ref['id'], - {'status': 'error'}) + cgsnapshot.status = 'error' + cgsnapshot.save() for snapshot in snapshots: volume_id = snapshot['volume_id'] @@ -2905,9 +2902,8 @@ class VolumeManager(manager.SchedulerDependentManager): 'snapshot_id': snapshot_id}) # TODO(thangp): Switch over to use snapshot.update() - # after cgsnapshot has been switched over to objects - self.db.snapshot_update(context, - snapshot_id, + # after cgsnapshot-objects bugs are fixed + self.db.snapshot_update(context, snapshot_id, {'status': 'error'}) raise exception.MetadataCopyFailure( reason=six.text_type(ex)) @@ -2916,52 +2912,50 @@ class VolumeManager(manager.SchedulerDependentManager): snapshot['id'], {'status': 'available', 'progress': '100%'}) - self.db.cgsnapshot_update(context, - cgsnapshot_ref['id'], - {'status': 'available'}) + cgsnapshot.status = 'available' + cgsnapshot.save() LOG.info(_LI("cgsnapshot %s: created successfully"), - cgsnapshot_ref['id']) + cgsnapshot.id) self._notify_about_cgsnapshot_usage( - context, cgsnapshot_ref, "create.end") - return cgsnapshot_id + context, cgsnapshot, "create.end") + return cgsnapshot - def delete_cgsnapshot(self, context, cgsnapshot_id): + def delete_cgsnapshot(self, context, cgsnapshot): """Deletes cgsnapshot.""" caller_context = context context = context.elevated() - cgsnapshot_ref = self.db.cgsnapshot_get(context, cgsnapshot_id) - project_id = cgsnapshot_ref['project_id'] + project_id = cgsnapshot.project_id - LOG.info(_LI("cgsnapshot %s: deleting"), cgsnapshot_ref['id']) + LOG.info(_LI("cgsnapshot %s: deleting"), cgsnapshot.id) snapshots = objects.SnapshotList.get_all_for_cgsnapshot( - context, cgsnapshot_id) + context, cgsnapshot.id) self._notify_about_cgsnapshot_usage( - context, cgsnapshot_ref, "delete.start") + context, cgsnapshot, "delete.start") try: utils.require_driver_initialized(self.driver) LOG.debug("cgsnapshot %(cgsnap_id)s: deleting", - {'cgsnap_id': cgsnapshot_id}) + {'cgsnap_id': cgsnapshot.id}) # Pass context so that drivers that want to use it, can, # but it is not a requirement for all drivers. - cgsnapshot_ref['context'] = caller_context + cgsnapshot.context = caller_context for snapshot in snapshots: snapshot['context'] = caller_context model_update, snapshots = \ - self.driver.delete_cgsnapshot(context, cgsnapshot_ref) + self.driver.delete_cgsnapshot(context, cgsnapshot) if snapshots: for snapshot in snapshots: update = {'status': snapshot['status']} # TODO(thangp): Switch over to use snapshot.update() - # after cgsnapshot has been switched over to objects + # after cgsnapshot-objects bugs are fixed self.db.snapshot_update(context, snapshot['id'], update) if snapshot['status'] in ['error_deleting', 'error'] and \ @@ -2972,18 +2966,17 @@ class VolumeManager(manager.SchedulerDependentManager): if model_update: if model_update['status'] in ['error_deleting', 'error']: msg = (_('Error occurred when deleting cgsnapshot ' - '%s.') % cgsnapshot_ref['id']) + '%s.') % cgsnapshot.id) LOG.error(msg) raise exception.VolumeDriverException(message=msg) else: - self.db.cgsnapshot_update(context, cgsnapshot_ref['id'], - model_update) + cgsnapshot.update(model_update) + cgsnapshot.save() except Exception: with excutils.save_and_reraise_exception(): - self.db.cgsnapshot_update(context, - cgsnapshot_ref['id'], - {'status': 'error_deleting'}) + cgsnapshot.status = 'error_deleting' + cgsnapshot.save() for snapshot in snapshots: # Get reservations @@ -3010,19 +3003,18 @@ class VolumeManager(manager.SchedulerDependentManager): self.db.volume_glance_metadata_delete_by_snapshot(context, snapshot['id']) - # TODO(thangp): Switch over to use snapshot.destroy() - # after cgsnapshot has been switched over to objects + # TODO(thangp): Switch over to use snapshot.delete() + # after cgsnapshot-objects bugs are fixed self.db.snapshot_destroy(context, snapshot['id']) # Commit the reservations if reservations: QUOTAS.commit(context, reservations, project_id=project_id) - self.db.cgsnapshot_destroy(context, cgsnapshot_id) - LOG.info(_LI("cgsnapshot %s: deleted successfully"), - cgsnapshot_ref['id']) - self._notify_about_cgsnapshot_usage( - context, cgsnapshot_ref, "delete.end", snapshots) + cgsnapshot.destroy() + LOG.info(_LI("cgsnapshot %s: deleted successfully"), cgsnapshot.id) + self._notify_about_cgsnapshot_usage(context, cgsnapshot, "delete.end", + snapshots) return True diff --git a/cinder/volume/rpcapi.py b/cinder/volume/rpcapi.py index 7a5c858af..c79ebb57c 100644 --- a/cinder/volume/rpcapi.py +++ b/cinder/volume/rpcapi.py @@ -76,6 +76,9 @@ class VolumeAPI(object): 1.28 - Adds manage_existing_snapshot 1.29 - Adds get_capabilities. 1.30 - Adds remove_export + 1.31 - Updated: create_consistencygroup_from_src(), create_cgsnapshot() + and delete_cgsnapshot() to cast method only with necessary + args. Forwarding CGSnapshot object instead of CGSnapshot_id. """ BASE_RPC_API_VERSION = '1.0' @@ -85,7 +88,7 @@ class VolumeAPI(object): target = messaging.Target(topic=CONF.volume_topic, version=self.BASE_RPC_API_VERSION) serializer = objects_base.CinderObjectSerializer() - self.client = rpc.get_client(target, '1.30', serializer=serializer) + self.client = rpc.get_client(target, '1.31', serializer=serializer) def create_consistencygroup(self, ctxt, group, host): new_host = utils.extract_host(host) @@ -111,25 +114,21 @@ class VolumeAPI(object): def create_consistencygroup_from_src(self, ctxt, group, cgsnapshot=None, source_cg=None): new_host = utils.extract_host(group.host) - cctxt = self.client.prepare(server=new_host, version='1.26') + cctxt = self.client.prepare(server=new_host, version='1.31') cctxt.cast(ctxt, 'create_consistencygroup_from_src', group=group, - cgsnapshot_id=cgsnapshot['id'] if cgsnapshot else None, + cgsnapshot=cgsnapshot, source_cg=source_cg) - def create_cgsnapshot(self, ctxt, group, cgsnapshot): + def create_cgsnapshot(self, ctxt, cgsnapshot): + host = utils.extract_host(cgsnapshot.consistencygroup.host) + cctxt = self.client.prepare(server=host, version='1.31') + cctxt.cast(ctxt, 'create_cgsnapshot', cgsnapshot=cgsnapshot) - host = utils.extract_host(group['host']) - cctxt = self.client.prepare(server=host, version='1.26') - cctxt.cast(ctxt, 'create_cgsnapshot', - group=group, - cgsnapshot_id=cgsnapshot['id']) - - def delete_cgsnapshot(self, ctxt, cgsnapshot, host): - new_host = utils.extract_host(host) - cctxt = self.client.prepare(server=new_host, version='1.18') - cctxt.cast(ctxt, 'delete_cgsnapshot', - cgsnapshot_id=cgsnapshot['id']) + def delete_cgsnapshot(self, ctxt, cgsnapshot): + new_host = utils.extract_host(cgsnapshot.consistencygroup.host) + cctxt = self.client.prepare(server=new_host, version='1.31') + cctxt.cast(ctxt, 'delete_cgsnapshot', cgsnapshot=cgsnapshot) def create_volume(self, ctxt, volume, host, request_spec, filter_properties, allow_reschedule=True): diff --git a/cinder/volume/utils.py b/cinder/volume/utils.py index 9e1e2c72f..e94ac20ec 100644 --- a/cinder/volume/utils.py +++ b/cinder/volume/utils.py @@ -239,15 +239,15 @@ def notify_about_consistencygroup_usage(context, group, event_suffix, usage_info) -def _usage_from_cgsnapshot(cgsnapshot_ref, **kw): +def _usage_from_cgsnapshot(cgsnapshot, **kw): usage_info = dict( - tenant_id=cgsnapshot_ref['project_id'], - user_id=cgsnapshot_ref['user_id'], - cgsnapshot_id=cgsnapshot_ref['id'], - name=cgsnapshot_ref['name'], - consistencygroup_id=cgsnapshot_ref['consistencygroup_id'], - created_at=cgsnapshot_ref['created_at'].isoformat(), - status=cgsnapshot_ref['status']) + tenant_id=cgsnapshot.project_id, + user_id=cgsnapshot.user_id, + cgsnapshot_id=cgsnapshot.id, + name=cgsnapshot.name, + consistencygroup_id=cgsnapshot.consistencygroup_id, + created_at=cgsnapshot.created_at.isoformat(), + status=cgsnapshot.status) usage_info.update(kw) return usage_info diff --git a/tools/lintstack.py b/tools/lintstack.py index b181b08fc..e9a90496c 100755 --- a/tools/lintstack.py +++ b/tools/lintstack.py @@ -65,15 +65,17 @@ objects_ignore_codes = ["E0213", "E1101", "E1102"] # member is created dynamically. objects_ignore_messages = [ "No value passed for parameter 'id' in function call", - "Module 'cinder.objects' has no 'Snapshot' member", - "Module 'cinder.objects' has no 'SnapshotList' member", "Module 'cinder.objects' has no 'Backup' member", - "Module 'cinder.objects' has no 'BackupList' member", - "Module 'cinder.objects' has no 'Service' member", - "Module 'cinder.objects' has no 'ServiceList' member", "Module 'cinder.objects' has no 'BackupImport' member", + "Module 'cinder.objects' has no 'BackupList' member", + "Module 'cinder.objects' has no 'CGSnapshot' member", + "Module 'cinder.objects' has no 'CGSnapshotList' member", "Module 'cinder.objects' has no 'ConsistencyGroup' member", "Module 'cinder.objects' has no 'ConsistencyGroupList' member", + "Module 'cinder.objects' has no 'Service' member", + "Module 'cinder.objects' has no 'ServiceList' member", + "Module 'cinder.objects' has no 'Snapshot' member", + "Module 'cinder.objects' has no 'SnapshotList' member", ] objects_ignore_modules = ["cinder/objects/"]