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

@ -187,9 +187,7 @@ class CgsnapshotsController(wsgi.Controller):
except exception.CgSnapshotNotFound as error: except exception.CgSnapshotNotFound as error:
raise exc.HTTPNotFound(explanation=error.msg) raise exc.HTTPNotFound(explanation=error.msg)
retval = self._view_builder.summary( retval = self._view_builder.summary(req, new_cgsnapshot)
req,
dict(new_cgsnapshot))
return retval return retval

@ -42,8 +42,8 @@ class ViewBuilder(common.ViewBuilder):
"""Generic, non-detailed view of a cgsnapshot.""" """Generic, non-detailed view of a cgsnapshot."""
return { return {
'cgsnapshot': { 'cgsnapshot': {
'id': cgsnapshot['id'], 'id': cgsnapshot.id,
'name': cgsnapshot['name'] 'name': cgsnapshot.name
} }
} }
@ -51,12 +51,12 @@ class ViewBuilder(common.ViewBuilder):
"""Detailed view of a single cgsnapshot.""" """Detailed view of a single cgsnapshot."""
return { return {
'cgsnapshot': { 'cgsnapshot': {
'id': cgsnapshot.get('id'), 'id': cgsnapshot.id,
'consistencygroup_id': cgsnapshot.get('consistencygroup_id'), 'consistencygroup_id': cgsnapshot.consistencygroup_id,
'status': cgsnapshot.get('status'), 'status': cgsnapshot.status,
'created_at': cgsnapshot.get('created_at'), 'created_at': cgsnapshot.created_at,
'name': cgsnapshot.get('name'), 'name': cgsnapshot.name,
'description': cgsnapshot.get('description') 'description': cgsnapshot.description
} }
} }

@ -168,15 +168,16 @@ class API(base.Base):
orig_cg = None orig_cg = None
if cgsnapshot_id: if cgsnapshot_id:
try: try:
cgsnapshot = self.db.cgsnapshot_get(context, cgsnapshot_id) cgsnapshot = objects.CGSnapshot.get_by_id(context,
cgsnapshot_id)
except exception.CgSnapshotNotFound: except exception.CgSnapshotNotFound:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
LOG.error(_LE("CG snapshot %(cgsnap)s not found when " LOG.error(_LE("CG snapshot %(cgsnap)s not found when "
"creating consistency group %(cg)s from " "creating consistency group %(cg)s from "
"source."), "source."),
{'cg': name, 'cgsnap': cgsnapshot_id}) {'cg': name, 'cgsnap': cgsnapshot_id})
orig_cg = objects.ConsistencyGroup.get_by_id( else:
context, cgsnapshot['consistencygroup_id']) orig_cg = cgsnapshot.consistencygroup
source_cg = None source_cg = None
if source_cgid: if source_cgid:
@ -238,7 +239,7 @@ class API(base.Base):
def _create_cg_from_cgsnapshot(self, context, group, cgsnapshot): def _create_cg_from_cgsnapshot(self, context, group, cgsnapshot):
try: try:
snapshots = objects.SnapshotList.get_all_for_cgsnapshot( snapshots = objects.SnapshotList.get_all_for_cgsnapshot(
context, cgsnapshot['id']) context, cgsnapshot.id)
if not snapshots: if not snapshots:
msg = _("Cgsnahost is empty. No consistency group " msg = _("Cgsnahost is empty. No consistency group "
@ -274,7 +275,7 @@ class API(base.Base):
"creating consistency group %(group)s " "creating consistency group %(group)s "
"from cgsnapshot %(cgsnap)s."), "from cgsnapshot %(cgsnap)s."),
{'group': group.id, {'group': group.id,
'cgsnap': cgsnapshot['id']}) 'cgsnap': cgsnapshot.id})
except Exception: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
try: try:
@ -284,7 +285,7 @@ class API(base.Base):
"group %(group)s from cgsnapshot " "group %(group)s from cgsnapshot "
"%(cgsnap)s."), "%(cgsnap)s."),
{'group': group.id, {'group': group.id,
'cgsnap': cgsnapshot['id']}) 'cgsnap': cgsnapshot.id})
volumes = self.db.volume_get_all_by_group(context, volumes = self.db.volume_get_all_by_group(context,
group.id) group.id)
@ -444,10 +445,9 @@ class API(base.Base):
"but current status is: %s") % group.status "but current status is: %s") % group.status
raise exception.InvalidConsistencyGroup(reason=msg) raise exception.InvalidConsistencyGroup(reason=msg)
cgsnaps = self.db.cgsnapshot_get_all_by_group( cgsnapshots = objects.CGSnapshotList.get_all_by_group(
context.elevated(), context.elevated(), group.id)
group.id) if cgsnapshots:
if cgsnaps:
msg = _("Consistency group %s still has dependent " msg = _("Consistency group %s still has dependent "
"cgsnapshots.") % group.id "cgsnapshots.") % group.id
LOG.error(msg) LOG.error(msg)
@ -709,14 +709,12 @@ class API(base.Base):
context, context.project_id) context, context.project_id)
return groups return groups
def create_cgsnapshot(self, context, def create_cgsnapshot(self, context, group, name, description):
group, name,
description):
return self._create_cgsnapshot(context, group, name, description) return self._create_cgsnapshot(context, group, name, description)
def _create_cgsnapshot(self, context, def _create_cgsnapshot(self, context,
group, name, description): group, name, description):
options = {'consistencygroup_id': group['id'], options = {'consistencygroup_id': group.id,
'user_id': context.user_id, 'user_id': context.user_id,
'project_id': context.project_id, 'project_id': context.project_id,
'status': "creating", 'status': "creating",
@ -724,65 +722,63 @@ class API(base.Base):
'description': description} 'description': description}
try: try:
cgsnapshot = self.db.cgsnapshot_create(context, options) cgsnapshot = objects.CGSnapshot(context, **options)
cgsnapshot_id = cgsnapshot['id'] cgsnapshot.create()
cgsnapshot_id = cgsnapshot.id
volumes = self.db.volume_get_all_by_group( volumes = self.db.volume_get_all_by_group(
context.elevated(), context.elevated(),
cgsnapshot['consistencygroup_id']) cgsnapshot.consistencygroup_id)
if not volumes: if not volumes:
msg = _("Consistency group is empty. No cgsnapshot " msg = _("Consistency group is empty. No cgsnapshot "
"will be created.") "will be created.")
raise exception.InvalidConsistencyGroup(reason=msg) raise exception.InvalidConsistencyGroup(reason=msg)
snap_name = cgsnapshot['name'] snap_name = cgsnapshot.name
snap_desc = cgsnapshot['description'] snap_desc = cgsnapshot.description
self.volume_api.create_snapshots_in_db( self.volume_api.create_snapshots_in_db(
context, volumes, snap_name, snap_desc, True, cgsnapshot_id) context, volumes, snap_name, snap_desc, True, cgsnapshot_id)
except Exception: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
try: try:
self.db.cgsnapshot_destroy(context, cgsnapshot_id) cgsnapshot.destroy()
finally: finally:
LOG.error(_LE("Error occurred when creating cgsnapshot" LOG.error(_LE("Error occurred when creating cgsnapshot"
" %s."), cgsnapshot_id) " %s."), cgsnapshot_id)
self.volume_rpcapi.create_cgsnapshot(context, group, cgsnapshot) self.volume_rpcapi.create_cgsnapshot(context, cgsnapshot)
return cgsnapshot return cgsnapshot
def delete_cgsnapshot(self, context, cgsnapshot, force=False): 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") msg = _("Cgsnapshot status must be available or error")
raise exception.InvalidCgSnapshot(reason=msg) raise exception.InvalidCgSnapshot(reason=msg)
self.db.cgsnapshot_update(context, cgsnapshot['id'], cgsnapshot.update({'status': 'deleting'})
{'status': 'deleting'}) cgsnapshot.save()
group = objects.ConsistencyGroup.get_by_id(context, cgsnapshot[ self.volume_rpcapi.delete_cgsnapshot(context.elevated(), cgsnapshot)
'consistencygroup_id'])
self.volume_rpcapi.delete_cgsnapshot(context.elevated(), cgsnapshot,
group.host)
def update_cgsnapshot(self, context, cgsnapshot, fields): 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): def get_cgsnapshot(self, context, cgsnapshot_id):
check_policy(context, 'get_cgsnapshot') check_policy(context, 'get_cgsnapshot')
rv = self.db.cgsnapshot_get(context, cgsnapshot_id) cgsnapshots = objects.CGSnapshot.get_by_id(context, cgsnapshot_id)
return dict(rv) return cgsnapshots
def get_all_cgsnapshots(self, context, search_opts=None): def get_all_cgsnapshots(self, context, search_opts=None):
check_policy(context, 'get_all_cgsnapshots') check_policy(context, 'get_all_cgsnapshots')
search_opts = search_opts or {} 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. # Need to remove all_tenants to pass the filtering below.
del search_opts['all_tenants'] del search_opts['all_tenants']
cgsnapshots = self.db.cgsnapshot_get_all(context, search_opts) cgsnapshots = objects.CGSnapshotList.get_all(context, search_opts)
else: else:
cgsnapshots = self.db.cgsnapshot_get_all_by_project( cgsnapshots = objects.CGSnapshotList.get_all_by_project(
context.elevated(), context.project_id, search_opts) context.elevated(), context.project_id, search_opts)
return cgsnapshots return cgsnapshots

@ -26,6 +26,7 @@ def register_all():
# need to receive it via RPC. # need to receive it via RPC.
__import__('cinder.objects.backup') __import__('cinder.objects.backup')
__import__('cinder.objects.consistencygroup') __import__('cinder.objects.consistencygroup')
__import__('cinder.objects.cgsnapshot')
__import__('cinder.objects.service') __import__('cinder.objects.service')
__import__('cinder.objects.snapshot') __import__('cinder.objects.snapshot')
__import__('cinder.objects.volume') __import__('cinder.objects.volume')

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

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

@ -5083,7 +5083,7 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
size=1) size=1)
volume_id = volume['id'] volume_id = volume['id']
cgsnapshot_returns = self._create_cgsnapshot(group.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'] snapshot_id = cgsnapshot_returns[1]['id']
# Create CG from source CG snapshot. # Create CG from source CG snapshot.
@ -5091,7 +5091,8 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
self.context, self.context,
availability_zone=CONF.storage_availability_zone, availability_zone=CONF.storage_availability_zone,
volume_type='type1,type2', 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( volume2 = tests_utils.create_volume(
self.context, self.context,
consistencygroup_id=group2.id, consistencygroup_id=group2.id,
@ -5100,7 +5101,7 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
volume2_id = volume2['id'] volume2_id = volume2['id']
self.volume.create_volume(self.context, volume2_id) self.volume.create_volume(self.context, volume2_id)
self.volume.create_consistencygroup_from_src( 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) cg2 = objects.ConsistencyGroup.get_by_id(self.context, group2.id)
expected = { expected = {
'status': 'available', 'status': 'available',
@ -5113,7 +5114,7 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
} }
self.assertEqual('available', cg2.status) self.assertEqual('available', cg2.status)
self.assertEqual(group2.id, cg2['id']) 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']) self.assertIsNone(cg2['source_cgid'])
msg = self.notifier.notifications[2] msg = self.notifier.notifications[2]
@ -5177,9 +5178,9 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
self.assertEqual(group.id, cg3.source_cgid) self.assertEqual(group.id, cg3.source_cgid)
self.assertIsNone(cg3.cgsnapshot_id) 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, group)
self.volume.delete_consistencygroup(self.context, group3)
def test_sort_snapshots(self): def test_sort_snapshots(self):
vol1 = {'id': '1', 'name': 'volume 1', vol1 = {'id': '1', 'name': 'volume 1',
@ -5274,15 +5275,14 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
self.volume._sort_source_vols, self.volume._sort_source_vols,
volumes, []) volumes, [])
@staticmethod def _create_cgsnapshot(self, group_id, volume_id, size='0'):
def _create_cgsnapshot(group_id, volume_id, size='0'):
"""Create a cgsnapshot object.""" """Create a cgsnapshot object."""
cgsnap = {} cgsnap = objects.CGSnapshot(self.context)
cgsnap['user_id'] = 'fake' cgsnap.user_id = 'fake'
cgsnap['project_id'] = 'fake' cgsnap.project_id = 'fake'
cgsnap['consistencygroup_id'] = group_id cgsnap.consistencygroup_id = group_id
cgsnap['status'] = "creating" cgsnap.status = "creating"
cgsnapshot = db.cgsnapshot_create(context.get_admin_context(), cgsnap) cgsnap.create()
# Create a snapshot object # Create a snapshot object
snap = objects.Snapshot(context.get_admin_context()) snap = objects.Snapshot(context.get_admin_context())
@ -5291,10 +5291,10 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
snap.project_id = 'fake' snap.project_id = 'fake'
snap.volume_id = volume_id snap.volume_id = volume_id
snap.status = "available" snap.status = "available"
snap.cgsnapshot_id = cgsnapshot['id'] snap.cgsnapshot_id = cgsnap.id
snap.create() snap.create()
return cgsnapshot, snap return cgsnap, snap
@mock.patch('cinder.volume.driver.VolumeDriver.create_consistencygroup', @mock.patch('cinder.volume.driver.VolumeDriver.create_consistencygroup',
autospec=True, autospec=True,
@ -5331,11 +5331,12 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
self.notifier.notifications) self.notifier.notifications)
cgsnapshot_returns = self._create_cgsnapshot(group.id, volume_id) cgsnapshot_returns = self._create_cgsnapshot(group.id, volume_id)
cgsnapshot_id = cgsnapshot_returns[0]['id'] cgsnapshot = cgsnapshot_returns[0]
self.volume.create_cgsnapshot(self.context, group.id, cgsnapshot_id) self.volume.create_cgsnapshot(self.context, cgsnapshot)
self.assertEqual(cgsnapshot_id, self.assertEqual(cgsnapshot.id,
db.cgsnapshot_get(context.get_admin_context(), objects.CGSnapshot.get_by_id(
cgsnapshot_id).id) context.get_admin_context(),
cgsnapshot.id).id)
if len(self.notifier.notifications) > 6: if len(self.notifier.notifications) > 6:
self.assertFalse(self.notifier.notifications[6], self.assertFalse(self.notifier.notifications[6],
@ -5346,7 +5347,7 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
expected = { expected = {
'created_at': 'DONTCARE', 'created_at': 'DONTCARE',
'name': None, 'name': None,
'cgsnapshot_id': cgsnapshot_id, 'cgsnapshot_id': cgsnapshot.id,
'status': 'creating', 'status': 'creating',
'tenant_id': 'fake', 'tenant_id': 'fake',
'user_id': 'fake', 'user_id': 'fake',
@ -5356,6 +5357,7 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
msg = self.notifier.notifications[3] msg = self.notifier.notifications[3]
self.assertEqual('snapshot.create.start', msg['event_type']) self.assertEqual('snapshot.create.start', msg['event_type'])
msg = self.notifier.notifications[4] msg = self.notifier.notifications[4]
expected['status'] = 'available'
self.assertEqual('cgsnapshot.create.end', msg['event_type']) self.assertEqual('cgsnapshot.create.end', msg['event_type'])
self.assertDictMatch(expected, msg['payload']) self.assertDictMatch(expected, msg['payload'])
msg = self.notifier.notifications[5] msg = self.notifier.notifications[5]
@ -5364,7 +5366,7 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
self.assertEqual(6, len(self.notifier.notifications), self.assertEqual(6, len(self.notifier.notifications),
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: if len(self.notifier.notifications) > 10:
self.assertFalse(self.notifier.notifications[10], self.assertFalse(self.notifier.notifications[10],
@ -5376,19 +5378,20 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
self.assertDictMatch(expected, msg['payload']) self.assertDictMatch(expected, msg['payload'])
msg = self.notifier.notifications[8] msg = self.notifier.notifications[8]
self.assertEqual('cgsnapshot.delete.end', msg['event_type']) self.assertEqual('cgsnapshot.delete.end', msg['event_type'])
expected['status'] = 'deleted'
self.assertDictMatch(expected, msg['payload']) self.assertDictMatch(expected, msg['payload'])
self.assertEqual(10, len(self.notifier.notifications), self.assertEqual(10, len(self.notifier.notifications),
self.notifier.notifications) self.notifier.notifications)
cgsnap = db.cgsnapshot_get( cgsnap = objects.CGSnapshot.get_by_id(
context.get_admin_context(read_deleted='yes'), context.get_admin_context(read_deleted='yes'),
cgsnapshot_id) cgsnapshot.id)
self.assertEqual('deleted', cgsnap['status']) self.assertEqual('deleted', cgsnap.status)
self.assertRaises(exception.NotFound, self.assertRaises(exception.NotFound,
db.cgsnapshot_get, objects.CGSnapshot.get_by_id,
self.context, self.context,
cgsnapshot_id) cgsnapshot.id)
self.volume.delete_consistencygroup(self.context, group) self.volume.delete_consistencygroup(self.context, group)

@ -64,24 +64,25 @@ class VolumeRpcAPITestCase(test.TestCase):
cgsnapshot = tests_utils.create_cgsnapshot( cgsnapshot = tests_utils.create_cgsnapshot(
self.context, self.context,
consistencygroup_id=source_group['id']) consistencygroup_id=source_group.id)
group = tests_utils.create_consistencygroup( group = tests_utils.create_consistencygroup(
self.context, self.context,
availability_zone=CONF.storage_availability_zone, availability_zone=CONF.storage_availability_zone,
volume_type='type1,type2', volume_type='type1,type2',
host='fakehost@fakedrv#fakepool', host='fakehost@fakedrv#fakepool',
cgsnapshot_id=cgsnapshot['id']) cgsnapshot_id=cgsnapshot.id)
group2 = tests_utils.create_consistencygroup( group2 = tests_utils.create_consistencygroup(
self.context, self.context,
availability_zone=CONF.storage_availability_zone, availability_zone=CONF.storage_availability_zone,
volume_type='type1,type2', volume_type='type1,type2',
host='fakehost@fakedrv#fakepool', host='fakehost@fakedrv#fakepool',
source_cgid=source_group['id']) source_cgid=source_group.id)
group = objects.ConsistencyGroup.get_by_id(self.context, group.id) group = objects.ConsistencyGroup.get_by_id(self.context, group.id)
group2 = objects.ConsistencyGroup.get_by_id(self.context, group2.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 = jsonutils.to_primitive(volume)
self.fake_volume_metadata = volume["volume_metadata"] self.fake_volume_metadata = volume["volume_metadata"]
self.fake_snapshot = snapshot self.fake_snapshot = snapshot
@ -89,7 +90,7 @@ class VolumeRpcAPITestCase(test.TestCase):
self.fake_cg = group self.fake_cg = group
self.fake_cg2 = group2 self.fake_cg2 = group2
self.fake_src_cg = jsonutils.to_primitive(source_group) 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): def test_serialized_volume_has_id(self):
self.assertIn('id', self.fake_volume) self.assertIn('id', self.fake_volume)
@ -123,6 +124,11 @@ class VolumeRpcAPITestCase(test.TestCase):
del expected_msg['snapshot'] del expected_msg['snapshot']
expected_msg['snapshot_id'] = snapshot.id expected_msg['snapshot_id'] = snapshot.id
expected_msg['snapshot'] = snapshot 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: if 'host' in expected_msg:
del expected_msg['host'] del expected_msg['host']
if 'dest_host' in expected_msg: if 'dest_host' in expected_msg:
@ -136,22 +142,16 @@ class VolumeRpcAPITestCase(test.TestCase):
del expected_msg['new_volume'] del expected_msg['new_volume']
expected_msg['new_volume_id'] = volume['id'] 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: if 'host' in kwargs:
host = kwargs['host'] host = kwargs['host']
elif 'group' in kwargs: elif 'group' in kwargs:
host = kwargs['group']['host'] host = kwargs['group']['host']
elif 'volume' not in kwargs and 'snapshot' in kwargs: elif 'volume' in kwargs:
host = 'fake_host'
else:
host = kwargs['volume']['host'] 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['server'] = utils.extract_host(host)
target['topic'] = '%s.%s' % (CONF.volume_topic, 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() expected_cg = expected_msg[kwarg].obj_to_primitive()
cg = value.obj_to_primitive() cg = value.obj_to_primitive()
self.assertEqual(expected_cg, cg) 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: else:
self.assertEqual(expected_msg[kwarg], value) self.assertEqual(expected_msg[kwarg], value)
@ -209,13 +213,11 @@ class VolumeRpcAPITestCase(test.TestCase):
def test_create_cgsnapshot(self): def test_create_cgsnapshot(self):
self._test_volume_api('create_cgsnapshot', rpc_method='cast', self._test_volume_api('create_cgsnapshot', rpc_method='cast',
group=self.fake_cg, cgsnapshot=self.fake_cgsnap, version='1.31')
cgsnapshot=self.fake_cgsnap, version='1.26')
def test_delete_cgsnapshot(self): def test_delete_cgsnapshot(self):
self._test_volume_api('delete_cgsnapshot', rpc_method='cast', self._test_volume_api('delete_cgsnapshot', rpc_method='cast',
cgsnapshot=self.fake_cgsnap, host='fake_host1', cgsnapshot=self.fake_cgsnap, version='1.31')
version='1.18')
def test_create_volume(self): def test_create_volume(self):
self._test_volume_api('create_volume', self._test_volume_api('create_volume',
@ -414,7 +416,7 @@ class VolumeRpcAPITestCase(test.TestCase):
group=self.fake_cg, group=self.fake_cg,
cgsnapshot=self.fake_cgsnap, cgsnapshot=self.fake_cgsnap,
source_cg=None, source_cg=None,
version='1.26') version='1.31')
def test_create_consistencygroup_from_src_cg(self): def test_create_consistencygroup_from_src_cg(self):
self._test_volume_api('create_consistencygroup_from_src', self._test_volume_api('create_consistencygroup_from_src',
@ -422,7 +424,7 @@ class VolumeRpcAPITestCase(test.TestCase):
group=self.fake_cg2, group=self.fake_cg2,
cgsnapshot=None, cgsnapshot=None,
source_cg=self.fake_src_cg, source_cg=self.fake_src_cg,
version='1.26') version='1.31')
def test_get_capabilities(self): def test_get_capabilities(self):
self._test_volume_api('get_capabilities', self._test_volume_api('get_capabilities',

@ -139,22 +139,23 @@ def create_consistencygroup(ctxt,
def create_cgsnapshot(ctxt, def create_cgsnapshot(ctxt,
name='test_cgsnap', consistencygroup_id,
description='this is a test cgsnap', name='test_cgsnapshot',
status='available', description='this is a test cgsnapshot',
consistencygroup_id=None, status='creating',
**kwargs): **kwargs):
"""Create a cgsnapshot object in the DB.""" """Create a cgsnapshot object in the DB."""
cgsnap = {} cgsnap = objects.CGSnapshot(ctxt)
cgsnap['user_id'] = ctxt.user_id cgsnap.user_id = ctxt.user_id or 'fake_user_id'
cgsnap['project_id'] = ctxt.project_id cgsnap.project_id = ctxt.project_id or 'fake_project_id'
cgsnap['status'] = status cgsnap.status = status
cgsnap['name'] = name cgsnap.name = name
cgsnap['description'] = description cgsnap.description = description
cgsnap['consistencygroup_id'] = consistencygroup_id cgsnap.consistencygroup_id = consistencygroup_id
for key in kwargs: for key in kwargs:
cgsnap[key] = kwargs[key] setattr(cgsnap, key, kwargs[key])
return db.cgsnapshot_create(ctxt, cgsnap) cgsnap.create()
return cgsnap
def create_backup(ctxt, def create_backup(ctxt,

@ -191,7 +191,7 @@ def locked_snapshot_operation(f):
class VolumeManager(manager.SchedulerDependentManager): class VolumeManager(manager.SchedulerDependentManager):
"""Manages attachable block storage devices.""" """Manages attachable block storage devices."""
RPC_API_VERSION = '1.30' RPC_API_VERSION = '1.31'
target = messaging.Target(version=RPC_API_VERSION) target = messaging.Target(version=RPC_API_VERSION)
@ -1966,7 +1966,7 @@ class VolumeManager(manager.SchedulerDependentManager):
if not snapshots: if not snapshots:
snapshots = objects.SnapshotList.get_all_for_cgsnapshot( snapshots = objects.SnapshotList.get_all_for_cgsnapshot(
context, cgsnapshot['id']) context, cgsnapshot.id)
if snapshots: if snapshots:
for snapshot in snapshots: for snapshot in snapshots:
vol_utils.notify_about_snapshot_usage( vol_utils.notify_about_snapshot_usage(
@ -2335,41 +2335,45 @@ class VolumeManager(manager.SchedulerDependentManager):
return group return group
def create_consistencygroup_from_src(self, context, 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. """Creates the consistency group from source.
The source can be a CG snapshot or a source CG. The source can be a CG snapshot or a source CG.
""" """
source_name = None
snapshots = None
source_vols = None
try: try:
volumes = self.db.volume_get_all_by_group(context, group.id) volumes = self.db.volume_get_all_by_group(context, group.id)
cgsnapshot = None if cgsnapshot:
snapshots = None
if cgsnapshot_id:
try: 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: except exception.CgSnapshotNotFound:
LOG.error(_LE("Create consistency group " LOG.error(_LE("Create consistency group "
"from snapshot-%(snap)s failed: " "from snapshot-%(snap)s failed: "
"SnapshotNotFound."), "SnapshotNotFound."),
{'snap': cgsnapshot_id}, {'snap': cgsnapshot.id},
resource={'type': 'consistency_group', resource={'type': 'consistency_group',
'id': group.id}) 'id': group.id})
raise raise
if cgsnapshot:
snapshots = objects.SnapshotList.get_all_for_cgsnapshot( source_name = _("snapshot-%s") % cgsnapshot.id
context, cgsnapshot_id) snapshots = objects.SnapshotList.get_all_for_cgsnapshot(
for snap in snapshots: context, cgsnapshot.id)
if (snap.status not in for snap in snapshots:
VALID_CREATE_CG_SRC_SNAP_STATUS): if (snap.status not in
msg = (_("Cannot create consistency group " VALID_CREATE_CG_SRC_SNAP_STATUS):
"%(group)s because snapshot %(snap)s is " msg = (_("Cannot create consistency group "
"not in a valid state. Valid states are: " "%(group)s because snapshot %(snap)s is "
"%(valid)s.") % "not in a valid state. Valid states are: "
{'group': group.id, "%(valid)s.") %
'snap': snap['id'], {'group': group.id,
'valid': VALID_CREATE_CG_SRC_SNAP_STATUS}) 'snap': snap['id'],
raise exception.InvalidConsistencyGroup(reason=msg) 'valid': VALID_CREATE_CG_SRC_SNAP_STATUS})
raise exception.InvalidConsistencyGroup(reason=msg)
if source_cg: if source_cg:
try: try:
@ -2383,21 +2387,22 @@ class VolumeManager(manager.SchedulerDependentManager):
resource={'type': 'consistency_group', resource={'type': 'consistency_group',
'id': group.id}) 'id': group.id})
raise raise
if source_cg:
source_vols = self.db.volume_get_all_by_group( source_name = _("cg-%s") % source_cg.id
context, source_cg.id) source_vols = self.db.volume_get_all_by_group(
for source_vol in source_vols: context, source_cg.id)
if (source_vol['status'] not in for source_vol in source_vols:
VALID_CREATE_CG_SRC_CG_STATUS): if (source_vol['status'] not in
msg = (_("Cannot create consistency group " VALID_CREATE_CG_SRC_CG_STATUS):
"%(group)s because source volume " msg = (_("Cannot create consistency group "
"%(source_vol)s is not in a valid " "%(group)s because source volume "
"state. Valid states are: " "%(source_vol)s is not in a valid "
"%(valid)s.") % "state. Valid states are: "
{'group': group.id, "%(valid)s.") %
'source_vol': source_vol['id'], {'group': group.id,
'valid': VALID_CREATE_CG_SRC_CG_STATUS}) 'source_vol': source_vol['id'],
raise exception.InvalidConsistencyGroup(reason=msg) '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 # Sort source snapshots so that they are in the same order as their
# corresponding target volumes. # corresponding target volumes.
@ -2434,15 +2439,9 @@ class VolumeManager(manager.SchedulerDependentManager):
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
group.status = 'error' group.status = 'error'
group.save() 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 " LOG.error(_LE("Create consistency group "
"from source %(source)s failed."), "from source %(source)s failed."),
{'source': source}, {'source': source_name},
resource={'type': 'consistency_group', resource={'type': 'consistency_group',
'id': group.id}) 'id': group.id})
# Update volume status to 'error' as well. # Update volume status to 'error' as well.
@ -2463,10 +2462,9 @@ class VolumeManager(manager.SchedulerDependentManager):
self._notify_about_consistencygroup_usage( self._notify_about_consistencygroup_usage(
context, group, "create.end") context, group, "create.end")
LOG.info(_LI("Create consistency group " LOG.info(_LI("Create consistency group "
"from snapshot-%(snap)s completed successfully."), "from source-%(source)s completed successfully."),
{'snap': cgsnapshot_id}, {'source': source_name},
resource={'type': 'consistency_group', resource={'type': 'consistency_group',
'id': group.id}) 'id': group.id})
return group return group
@ -2833,33 +2831,33 @@ class VolumeManager(manager.SchedulerDependentManager):
return True return True
def create_cgsnapshot(self, context, group, cgsnapshot_id): def create_cgsnapshot(self, context, cgsnapshot):
"""Creates the cgsnapshot.""" """Creates the cgsnapshot."""
caller_context = context caller_context = context
context = context.elevated() 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( snapshots = objects.SnapshotList.get_all_for_cgsnapshot(
context, cgsnapshot_id) context, cgsnapshot.id)
self._notify_about_cgsnapshot_usage( self._notify_about_cgsnapshot_usage(
context, cgsnapshot_ref, "create.start") context, cgsnapshot, "create.start")
try: try:
utils.require_driver_initialized(self.driver) utils.require_driver_initialized(self.driver)
LOG.debug("Cgsnapshot %(cgsnap_id)s: creating.", 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, # Pass context so that drivers that want to use it, can,
# but it is not a requirement for all drivers. # but it is not a requirement for all drivers.
cgsnapshot_ref['context'] = caller_context cgsnapshot.context = caller_context
for snapshot in snapshots: for snapshot in snapshots:
snapshot.context = caller_context snapshot.context = caller_context
model_update, snapshots = \ model_update, snapshots = \
self.driver.create_cgsnapshot(context, cgsnapshot_ref) self.driver.create_cgsnapshot(context, cgsnapshot)
if snapshots: if snapshots:
for snapshot in snapshots: for snapshot in snapshots:
@ -2868,7 +2866,7 @@ class VolumeManager(manager.SchedulerDependentManager):
update = {'status': snapshot['status']} update = {'status': snapshot['status']}
# TODO(thangp): Switch over to use snapshot.update() # 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'], self.db.snapshot_update(context, snapshot['id'],
update) update)
# If status for one snapshot is error, make sure # If status for one snapshot is error, make sure
@ -2879,15 +2877,14 @@ class VolumeManager(manager.SchedulerDependentManager):
if model_update: if model_update:
if model_update['status'] == 'error': if model_update['status'] == 'error':
msg = (_('Error occurred when creating cgsnapshot ' msg = (_('Error occurred when creating cgsnapshot '
'%s.') % cgsnapshot_ref['id']) '%s.') % cgsnapshot.id)
LOG.error(msg) LOG.error(msg)
raise exception.VolumeDriverException(message=msg) raise exception.VolumeDriverException(message=msg)
except Exception: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
self.db.cgsnapshot_update(context, cgsnapshot.status = 'error'
cgsnapshot_ref['id'], cgsnapshot.save()
{'status': 'error'})
for snapshot in snapshots: for snapshot in snapshots:
volume_id = snapshot['volume_id'] volume_id = snapshot['volume_id']
@ -2905,9 +2902,8 @@ class VolumeManager(manager.SchedulerDependentManager):
'snapshot_id': snapshot_id}) 'snapshot_id': snapshot_id})
# TODO(thangp): Switch over to use snapshot.update() # 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, self.db.snapshot_update(context, snapshot_id,
snapshot_id,
{'status': 'error'}) {'status': 'error'})
raise exception.MetadataCopyFailure( raise exception.MetadataCopyFailure(
reason=six.text_type(ex)) reason=six.text_type(ex))
@ -2916,52 +2912,50 @@ class VolumeManager(manager.SchedulerDependentManager):
snapshot['id'], {'status': 'available', snapshot['id'], {'status': 'available',
'progress': '100%'}) 'progress': '100%'})
self.db.cgsnapshot_update(context, cgsnapshot.status = 'available'
cgsnapshot_ref['id'], cgsnapshot.save()
{'status': 'available'})
LOG.info(_LI("cgsnapshot %s: created successfully"), LOG.info(_LI("cgsnapshot %s: created successfully"),
cgsnapshot_ref['id']) cgsnapshot.id)
self._notify_about_cgsnapshot_usage( self._notify_about_cgsnapshot_usage(
context, cgsnapshot_ref, "create.end") context, cgsnapshot, "create.end")
return cgsnapshot_id return cgsnapshot
def delete_cgsnapshot(self, context, cgsnapshot_id): def delete_cgsnapshot(self, context, cgsnapshot):
"""Deletes cgsnapshot.""" """Deletes cgsnapshot."""
caller_context = context caller_context = context
context = context.elevated() context = context.elevated()
cgsnapshot_ref = self.db.cgsnapshot_get(context, cgsnapshot_id) project_id = cgsnapshot.project_id
project_id = cgsnapshot_ref['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( snapshots = objects.SnapshotList.get_all_for_cgsnapshot(
context, cgsnapshot_id) context, cgsnapshot.id)
self._notify_about_cgsnapshot_usage( self._notify_about_cgsnapshot_usage(
context, cgsnapshot_ref, "delete.start") context, cgsnapshot, "delete.start")
try: try:
utils.require_driver_initialized(self.driver) utils.require_driver_initialized(self.driver)
LOG.debug("cgsnapshot %(cgsnap_id)s: deleting", 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, # Pass context so that drivers that want to use it, can,
# but it is not a requirement for all drivers. # but it is not a requirement for all drivers.
cgsnapshot_ref['context'] = caller_context cgsnapshot.context = caller_context
for snapshot in snapshots: for snapshot in snapshots:
snapshot['context'] = caller_context snapshot['context'] = caller_context
model_update, snapshots = \ model_update, snapshots = \
self.driver.delete_cgsnapshot(context, cgsnapshot_ref) self.driver.delete_cgsnapshot(context, cgsnapshot)
if snapshots: if snapshots:
for snapshot in snapshots: for snapshot in snapshots:
update = {'status': snapshot['status']} update = {'status': snapshot['status']}
# TODO(thangp): Switch over to use snapshot.update() # 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'], self.db.snapshot_update(context, snapshot['id'],
update) update)
if snapshot['status'] in ['error_deleting', 'error'] and \ if snapshot['status'] in ['error_deleting', 'error'] and \
@ -2972,18 +2966,17 @@ class VolumeManager(manager.SchedulerDependentManager):
if model_update: if model_update:
if model_update['status'] in ['error_deleting', 'error']: if model_update['status'] in ['error_deleting', 'error']:
msg = (_('Error occurred when deleting cgsnapshot ' msg = (_('Error occurred when deleting cgsnapshot '
'%s.') % cgsnapshot_ref['id']) '%s.') % cgsnapshot.id)
LOG.error(msg) LOG.error(msg)
raise exception.VolumeDriverException(message=msg) raise exception.VolumeDriverException(message=msg)
else: else:
self.db.cgsnapshot_update(context, cgsnapshot_ref['id'], cgsnapshot.update(model_update)
model_update) cgsnapshot.save()
except Exception: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
self.db.cgsnapshot_update(context, cgsnapshot.status = 'error_deleting'
cgsnapshot_ref['id'], cgsnapshot.save()
{'status': 'error_deleting'})
for snapshot in snapshots: for snapshot in snapshots:
# Get reservations # Get reservations
@ -3010,19 +3003,18 @@ class VolumeManager(manager.SchedulerDependentManager):
self.db.volume_glance_metadata_delete_by_snapshot(context, self.db.volume_glance_metadata_delete_by_snapshot(context,
snapshot['id']) snapshot['id'])
# TODO(thangp): Switch over to use snapshot.destroy() # TODO(thangp): Switch over to use snapshot.delete()
# after cgsnapshot has been switched over to objects # after cgsnapshot-objects bugs are fixed
self.db.snapshot_destroy(context, snapshot['id']) self.db.snapshot_destroy(context, snapshot['id'])
# Commit the reservations # Commit the reservations
if reservations: if reservations:
QUOTAS.commit(context, reservations, project_id=project_id) QUOTAS.commit(context, reservations, project_id=project_id)
self.db.cgsnapshot_destroy(context, cgsnapshot_id) cgsnapshot.destroy()
LOG.info(_LI("cgsnapshot %s: deleted successfully"), LOG.info(_LI("cgsnapshot %s: deleted successfully"), cgsnapshot.id)
cgsnapshot_ref['id']) self._notify_about_cgsnapshot_usage(context, cgsnapshot, "delete.end",
self._notify_about_cgsnapshot_usage( snapshots)
context, cgsnapshot_ref, "delete.end", snapshots)
return True return True

@ -76,6 +76,9 @@ class VolumeAPI(object):
1.28 - Adds manage_existing_snapshot 1.28 - Adds manage_existing_snapshot
1.29 - Adds get_capabilities. 1.29 - Adds get_capabilities.
1.30 - Adds remove_export 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' BASE_RPC_API_VERSION = '1.0'
@ -85,7 +88,7 @@ class VolumeAPI(object):
target = messaging.Target(topic=CONF.volume_topic, target = messaging.Target(topic=CONF.volume_topic,
version=self.BASE_RPC_API_VERSION) version=self.BASE_RPC_API_VERSION)
serializer = objects_base.CinderObjectSerializer() 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): def create_consistencygroup(self, ctxt, group, host):
new_host = utils.extract_host(host) new_host = utils.extract_host(host)
@ -111,25 +114,21 @@ class VolumeAPI(object):
def create_consistencygroup_from_src(self, ctxt, group, cgsnapshot=None, def create_consistencygroup_from_src(self, ctxt, group, cgsnapshot=None,
source_cg=None): source_cg=None):
new_host = utils.extract_host(group.host) 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', cctxt.cast(ctxt, 'create_consistencygroup_from_src',
group=group, group=group,
cgsnapshot_id=cgsnapshot['id'] if cgsnapshot else None, cgsnapshot=cgsnapshot,
source_cg=source_cg) 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']) def delete_cgsnapshot(self, ctxt, cgsnapshot):
cctxt = self.client.prepare(server=host, version='1.26') new_host = utils.extract_host(cgsnapshot.consistencygroup.host)
cctxt.cast(ctxt, 'create_cgsnapshot', cctxt = self.client.prepare(server=new_host, version='1.31')
group=group, cctxt.cast(ctxt, 'delete_cgsnapshot', cgsnapshot=cgsnapshot)
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 create_volume(self, ctxt, volume, host, request_spec, def create_volume(self, ctxt, volume, host, request_spec,
filter_properties, allow_reschedule=True): filter_properties, allow_reschedule=True):

@ -239,15 +239,15 @@ def notify_about_consistencygroup_usage(context, group, event_suffix,
usage_info) usage_info)
def _usage_from_cgsnapshot(cgsnapshot_ref, **kw): def _usage_from_cgsnapshot(cgsnapshot, **kw):
usage_info = dict( usage_info = dict(
tenant_id=cgsnapshot_ref['project_id'], tenant_id=cgsnapshot.project_id,
user_id=cgsnapshot_ref['user_id'], user_id=cgsnapshot.user_id,
cgsnapshot_id=cgsnapshot_ref['id'], cgsnapshot_id=cgsnapshot.id,
name=cgsnapshot_ref['name'], name=cgsnapshot.name,
consistencygroup_id=cgsnapshot_ref['consistencygroup_id'], consistencygroup_id=cgsnapshot.consistencygroup_id,
created_at=cgsnapshot_ref['created_at'].isoformat(), created_at=cgsnapshot.created_at.isoformat(),
status=cgsnapshot_ref['status']) status=cgsnapshot.status)
usage_info.update(kw) usage_info.update(kw)
return usage_info return usage_info

@ -65,15 +65,17 @@ objects_ignore_codes = ["E0213", "E1101", "E1102"]
# member is created dynamically. # member is created dynamically.
objects_ignore_messages = [ objects_ignore_messages = [
"No value passed for parameter 'id' in function call", "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 '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 '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 'ConsistencyGroup' member",
"Module 'cinder.objects' has no 'ConsistencyGroupList' 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/"] objects_ignore_modules = ["cinder/objects/"]