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
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
"""Generic, non-detailed view of a cgsnapshot."""
return {
'cgsnapshot': {
- 'id': cgsnapshot['id'],
- 'name': cgsnapshot['name']
+ 'id': cgsnapshot.id,
+ 'name': cgsnapshot.name
}
}
"""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
}
}
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:
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 "
"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:
"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)
"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)
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",
'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
# 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')
--- /dev/null
+# 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)
--- /dev/null
+# 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])
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.
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,
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',
}
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]
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',
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())
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,
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],
expected = {
'created_at': 'DONTCARE',
'name': None,
- 'cgsnapshot_id': cgsnapshot_id,
+ 'cgsnapshot_id': cgsnapshot.id,
'status': 'creating',
'tenant_id': 'fake',
'user_id': 'fake',
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]
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],
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)
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
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)
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:
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)
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)
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',
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',
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',
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,
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)
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(
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:
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.
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.
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
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:
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
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']
'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))
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 \
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
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
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'
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)
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):
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
# 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/"]