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