CGSnapshot Object

This patch adds VersionedObjects abstraction layer to CGSnapshots.

Co-Authored-By: Szymon Wroblewski <szymon.wroblewski@intel.com>
Co-Authored-By: Michal Dulko <michal.dulko@intel.com>

Partial-Implements: blueprint cinder-objects
Change-Id: Ie4cdd1ffae15a93bff756ad278ca680f9f420748
This commit is contained in:
root 2015-06-24 12:32:29 +02:00 committed by Szymon Wroblewski
parent d6d9b4833f
commit 01ed01db34
13 changed files with 528 additions and 228 deletions

View File

@ -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

View File

@ -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
}
}

View File

@ -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

View File

@ -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')

View File

@ -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)

View File

@ -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])

View File

@ -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)

View File

@ -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',

View File

@ -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,

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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/"]