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
cinder
api
consistencygroup
objects
tests/unit
volume
tools
@ -187,9 +187,7 @@ class CgsnapshotsController(wsgi.Controller):
|
|||||||
except exception.CgSnapshotNotFound as error:
|
except exception.CgSnapshotNotFound as error:
|
||||||
raise exc.HTTPNotFound(explanation=error.msg)
|
raise exc.HTTPNotFound(explanation=error.msg)
|
||||||
|
|
||||||
retval = self._view_builder.summary(
|
retval = self._view_builder.summary(req, new_cgsnapshot)
|
||||||
req,
|
|
||||||
dict(new_cgsnapshot))
|
|
||||||
|
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
|
@ -42,8 +42,8 @@ class ViewBuilder(common.ViewBuilder):
|
|||||||
"""Generic, non-detailed view of a cgsnapshot."""
|
"""Generic, non-detailed view of a cgsnapshot."""
|
||||||
return {
|
return {
|
||||||
'cgsnapshot': {
|
'cgsnapshot': {
|
||||||
'id': cgsnapshot['id'],
|
'id': cgsnapshot.id,
|
||||||
'name': cgsnapshot['name']
|
'name': cgsnapshot.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,12 +51,12 @@ class ViewBuilder(common.ViewBuilder):
|
|||||||
"""Detailed view of a single cgsnapshot."""
|
"""Detailed view of a single cgsnapshot."""
|
||||||
return {
|
return {
|
||||||
'cgsnapshot': {
|
'cgsnapshot': {
|
||||||
'id': cgsnapshot.get('id'),
|
'id': cgsnapshot.id,
|
||||||
'consistencygroup_id': cgsnapshot.get('consistencygroup_id'),
|
'consistencygroup_id': cgsnapshot.consistencygroup_id,
|
||||||
'status': cgsnapshot.get('status'),
|
'status': cgsnapshot.status,
|
||||||
'created_at': cgsnapshot.get('created_at'),
|
'created_at': cgsnapshot.created_at,
|
||||||
'name': cgsnapshot.get('name'),
|
'name': cgsnapshot.name,
|
||||||
'description': cgsnapshot.get('description')
|
'description': cgsnapshot.description
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,15 +168,16 @@ class API(base.Base):
|
|||||||
orig_cg = None
|
orig_cg = None
|
||||||
if cgsnapshot_id:
|
if cgsnapshot_id:
|
||||||
try:
|
try:
|
||||||
cgsnapshot = self.db.cgsnapshot_get(context, cgsnapshot_id)
|
cgsnapshot = objects.CGSnapshot.get_by_id(context,
|
||||||
|
cgsnapshot_id)
|
||||||
except exception.CgSnapshotNotFound:
|
except exception.CgSnapshotNotFound:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
LOG.error(_LE("CG snapshot %(cgsnap)s not found when "
|
LOG.error(_LE("CG snapshot %(cgsnap)s not found when "
|
||||||
"creating consistency group %(cg)s from "
|
"creating consistency group %(cg)s from "
|
||||||
"source."),
|
"source."),
|
||||||
{'cg': name, 'cgsnap': cgsnapshot_id})
|
{'cg': name, 'cgsnap': cgsnapshot_id})
|
||||||
orig_cg = objects.ConsistencyGroup.get_by_id(
|
else:
|
||||||
context, cgsnapshot['consistencygroup_id'])
|
orig_cg = cgsnapshot.consistencygroup
|
||||||
|
|
||||||
source_cg = None
|
source_cg = None
|
||||||
if source_cgid:
|
if source_cgid:
|
||||||
@ -238,7 +239,7 @@ class API(base.Base):
|
|||||||
def _create_cg_from_cgsnapshot(self, context, group, cgsnapshot):
|
def _create_cg_from_cgsnapshot(self, context, group, cgsnapshot):
|
||||||
try:
|
try:
|
||||||
snapshots = objects.SnapshotList.get_all_for_cgsnapshot(
|
snapshots = objects.SnapshotList.get_all_for_cgsnapshot(
|
||||||
context, cgsnapshot['id'])
|
context, cgsnapshot.id)
|
||||||
|
|
||||||
if not snapshots:
|
if not snapshots:
|
||||||
msg = _("Cgsnahost is empty. No consistency group "
|
msg = _("Cgsnahost is empty. No consistency group "
|
||||||
@ -274,7 +275,7 @@ class API(base.Base):
|
|||||||
"creating consistency group %(group)s "
|
"creating consistency group %(group)s "
|
||||||
"from cgsnapshot %(cgsnap)s."),
|
"from cgsnapshot %(cgsnap)s."),
|
||||||
{'group': group.id,
|
{'group': group.id,
|
||||||
'cgsnap': cgsnapshot['id']})
|
'cgsnap': cgsnapshot.id})
|
||||||
except Exception:
|
except Exception:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
try:
|
try:
|
||||||
@ -284,7 +285,7 @@ class API(base.Base):
|
|||||||
"group %(group)s from cgsnapshot "
|
"group %(group)s from cgsnapshot "
|
||||||
"%(cgsnap)s."),
|
"%(cgsnap)s."),
|
||||||
{'group': group.id,
|
{'group': group.id,
|
||||||
'cgsnap': cgsnapshot['id']})
|
'cgsnap': cgsnapshot.id})
|
||||||
|
|
||||||
volumes = self.db.volume_get_all_by_group(context,
|
volumes = self.db.volume_get_all_by_group(context,
|
||||||
group.id)
|
group.id)
|
||||||
@ -444,10 +445,9 @@ class API(base.Base):
|
|||||||
"but current status is: %s") % group.status
|
"but current status is: %s") % group.status
|
||||||
raise exception.InvalidConsistencyGroup(reason=msg)
|
raise exception.InvalidConsistencyGroup(reason=msg)
|
||||||
|
|
||||||
cgsnaps = self.db.cgsnapshot_get_all_by_group(
|
cgsnapshots = objects.CGSnapshotList.get_all_by_group(
|
||||||
context.elevated(),
|
context.elevated(), group.id)
|
||||||
group.id)
|
if cgsnapshots:
|
||||||
if cgsnaps:
|
|
||||||
msg = _("Consistency group %s still has dependent "
|
msg = _("Consistency group %s still has dependent "
|
||||||
"cgsnapshots.") % group.id
|
"cgsnapshots.") % group.id
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
@ -709,14 +709,12 @@ class API(base.Base):
|
|||||||
context, context.project_id)
|
context, context.project_id)
|
||||||
return groups
|
return groups
|
||||||
|
|
||||||
def create_cgsnapshot(self, context,
|
def create_cgsnapshot(self, context, group, name, description):
|
||||||
group, name,
|
|
||||||
description):
|
|
||||||
return self._create_cgsnapshot(context, group, name, description)
|
return self._create_cgsnapshot(context, group, name, description)
|
||||||
|
|
||||||
def _create_cgsnapshot(self, context,
|
def _create_cgsnapshot(self, context,
|
||||||
group, name, description):
|
group, name, description):
|
||||||
options = {'consistencygroup_id': group['id'],
|
options = {'consistencygroup_id': group.id,
|
||||||
'user_id': context.user_id,
|
'user_id': context.user_id,
|
||||||
'project_id': context.project_id,
|
'project_id': context.project_id,
|
||||||
'status': "creating",
|
'status': "creating",
|
||||||
@ -724,65 +722,63 @@ class API(base.Base):
|
|||||||
'description': description}
|
'description': description}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cgsnapshot = self.db.cgsnapshot_create(context, options)
|
cgsnapshot = objects.CGSnapshot(context, **options)
|
||||||
cgsnapshot_id = cgsnapshot['id']
|
cgsnapshot.create()
|
||||||
|
cgsnapshot_id = cgsnapshot.id
|
||||||
|
|
||||||
volumes = self.db.volume_get_all_by_group(
|
volumes = self.db.volume_get_all_by_group(
|
||||||
context.elevated(),
|
context.elevated(),
|
||||||
cgsnapshot['consistencygroup_id'])
|
cgsnapshot.consistencygroup_id)
|
||||||
|
|
||||||
if not volumes:
|
if not volumes:
|
||||||
msg = _("Consistency group is empty. No cgsnapshot "
|
msg = _("Consistency group is empty. No cgsnapshot "
|
||||||
"will be created.")
|
"will be created.")
|
||||||
raise exception.InvalidConsistencyGroup(reason=msg)
|
raise exception.InvalidConsistencyGroup(reason=msg)
|
||||||
|
|
||||||
snap_name = cgsnapshot['name']
|
snap_name = cgsnapshot.name
|
||||||
snap_desc = cgsnapshot['description']
|
snap_desc = cgsnapshot.description
|
||||||
self.volume_api.create_snapshots_in_db(
|
self.volume_api.create_snapshots_in_db(
|
||||||
context, volumes, snap_name, snap_desc, True, cgsnapshot_id)
|
context, volumes, snap_name, snap_desc, True, cgsnapshot_id)
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
try:
|
try:
|
||||||
self.db.cgsnapshot_destroy(context, cgsnapshot_id)
|
cgsnapshot.destroy()
|
||||||
finally:
|
finally:
|
||||||
LOG.error(_LE("Error occurred when creating cgsnapshot"
|
LOG.error(_LE("Error occurred when creating cgsnapshot"
|
||||||
" %s."), cgsnapshot_id)
|
" %s."), cgsnapshot_id)
|
||||||
|
|
||||||
self.volume_rpcapi.create_cgsnapshot(context, group, cgsnapshot)
|
self.volume_rpcapi.create_cgsnapshot(context, cgsnapshot)
|
||||||
|
|
||||||
return cgsnapshot
|
return cgsnapshot
|
||||||
|
|
||||||
def delete_cgsnapshot(self, context, cgsnapshot, force=False):
|
def delete_cgsnapshot(self, context, cgsnapshot, force=False):
|
||||||
if cgsnapshot['status'] not in ["available", "error"]:
|
if cgsnapshot.status not in ["available", "error"]:
|
||||||
msg = _("Cgsnapshot status must be available or error")
|
msg = _("Cgsnapshot status must be available or error")
|
||||||
raise exception.InvalidCgSnapshot(reason=msg)
|
raise exception.InvalidCgSnapshot(reason=msg)
|
||||||
self.db.cgsnapshot_update(context, cgsnapshot['id'],
|
cgsnapshot.update({'status': 'deleting'})
|
||||||
{'status': 'deleting'})
|
cgsnapshot.save()
|
||||||
group = objects.ConsistencyGroup.get_by_id(context, cgsnapshot[
|
self.volume_rpcapi.delete_cgsnapshot(context.elevated(), cgsnapshot)
|
||||||
'consistencygroup_id'])
|
|
||||||
self.volume_rpcapi.delete_cgsnapshot(context.elevated(), cgsnapshot,
|
|
||||||
group.host)
|
|
||||||
|
|
||||||
def update_cgsnapshot(self, context, cgsnapshot, fields):
|
def update_cgsnapshot(self, context, cgsnapshot, fields):
|
||||||
self.db.cgsnapshot_update(context, cgsnapshot['id'], fields)
|
cgsnapshot.update(fields)
|
||||||
|
cgsnapshot.save()
|
||||||
|
|
||||||
def get_cgsnapshot(self, context, cgsnapshot_id):
|
def get_cgsnapshot(self, context, cgsnapshot_id):
|
||||||
check_policy(context, 'get_cgsnapshot')
|
check_policy(context, 'get_cgsnapshot')
|
||||||
rv = self.db.cgsnapshot_get(context, cgsnapshot_id)
|
cgsnapshots = objects.CGSnapshot.get_by_id(context, cgsnapshot_id)
|
||||||
return dict(rv)
|
return cgsnapshots
|
||||||
|
|
||||||
def get_all_cgsnapshots(self, context, search_opts=None):
|
def get_all_cgsnapshots(self, context, search_opts=None):
|
||||||
check_policy(context, 'get_all_cgsnapshots')
|
check_policy(context, 'get_all_cgsnapshots')
|
||||||
|
|
||||||
search_opts = search_opts or {}
|
search_opts = search_opts or {}
|
||||||
|
|
||||||
if (context.is_admin and 'all_tenants' in search_opts):
|
if context.is_admin and 'all_tenants' in search_opts:
|
||||||
# Need to remove all_tenants to pass the filtering below.
|
# Need to remove all_tenants to pass the filtering below.
|
||||||
del search_opts['all_tenants']
|
del search_opts['all_tenants']
|
||||||
cgsnapshots = self.db.cgsnapshot_get_all(context, search_opts)
|
cgsnapshots = objects.CGSnapshotList.get_all(context, search_opts)
|
||||||
else:
|
else:
|
||||||
cgsnapshots = self.db.cgsnapshot_get_all_by_project(
|
cgsnapshots = objects.CGSnapshotList.get_all_by_project(
|
||||||
context.elevated(), context.project_id, search_opts)
|
context.elevated(), context.project_id, search_opts)
|
||||||
|
|
||||||
return cgsnapshots
|
return cgsnapshots
|
||||||
|
@ -26,6 +26,7 @@ def register_all():
|
|||||||
# need to receive it via RPC.
|
# need to receive it via RPC.
|
||||||
__import__('cinder.objects.backup')
|
__import__('cinder.objects.backup')
|
||||||
__import__('cinder.objects.consistencygroup')
|
__import__('cinder.objects.consistencygroup')
|
||||||
|
__import__('cinder.objects.cgsnapshot')
|
||||||
__import__('cinder.objects.service')
|
__import__('cinder.objects.service')
|
||||||
__import__('cinder.objects.snapshot')
|
__import__('cinder.objects.snapshot')
|
||||||
__import__('cinder.objects.volume')
|
__import__('cinder.objects.volume')
|
||||||
|
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)
|
size=1)
|
||||||
volume_id = volume['id']
|
volume_id = volume['id']
|
||||||
cgsnapshot_returns = self._create_cgsnapshot(group.id, volume_id)
|
cgsnapshot_returns = self._create_cgsnapshot(group.id, volume_id)
|
||||||
cgsnapshot_id = cgsnapshot_returns[0]['id']
|
cgsnapshot = cgsnapshot_returns[0]
|
||||||
snapshot_id = cgsnapshot_returns[1]['id']
|
snapshot_id = cgsnapshot_returns[1]['id']
|
||||||
|
|
||||||
# Create CG from source CG snapshot.
|
# Create CG from source CG snapshot.
|
||||||
@ -5091,7 +5091,8 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
|
|||||||
self.context,
|
self.context,
|
||||||
availability_zone=CONF.storage_availability_zone,
|
availability_zone=CONF.storage_availability_zone,
|
||||||
volume_type='type1,type2',
|
volume_type='type1,type2',
|
||||||
cgsnapshot_id=cgsnapshot_id)
|
cgsnapshot_id=cgsnapshot.id)
|
||||||
|
group2 = objects.ConsistencyGroup.get_by_id(self.context, group2.id)
|
||||||
volume2 = tests_utils.create_volume(
|
volume2 = tests_utils.create_volume(
|
||||||
self.context,
|
self.context,
|
||||||
consistencygroup_id=group2.id,
|
consistencygroup_id=group2.id,
|
||||||
@ -5100,7 +5101,7 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
|
|||||||
volume2_id = volume2['id']
|
volume2_id = volume2['id']
|
||||||
self.volume.create_volume(self.context, volume2_id)
|
self.volume.create_volume(self.context, volume2_id)
|
||||||
self.volume.create_consistencygroup_from_src(
|
self.volume.create_consistencygroup_from_src(
|
||||||
self.context, group2, cgsnapshot_id=cgsnapshot_id)
|
self.context, group2, cgsnapshot=cgsnapshot)
|
||||||
cg2 = objects.ConsistencyGroup.get_by_id(self.context, group2.id)
|
cg2 = objects.ConsistencyGroup.get_by_id(self.context, group2.id)
|
||||||
expected = {
|
expected = {
|
||||||
'status': 'available',
|
'status': 'available',
|
||||||
@ -5113,7 +5114,7 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
|
|||||||
}
|
}
|
||||||
self.assertEqual('available', cg2.status)
|
self.assertEqual('available', cg2.status)
|
||||||
self.assertEqual(group2.id, cg2['id'])
|
self.assertEqual(group2.id, cg2['id'])
|
||||||
self.assertEqual(cgsnapshot_id, cg2['cgsnapshot_id'])
|
self.assertEqual(cgsnapshot.id, cg2['cgsnapshot_id'])
|
||||||
self.assertIsNone(cg2['source_cgid'])
|
self.assertIsNone(cg2['source_cgid'])
|
||||||
|
|
||||||
msg = self.notifier.notifications[2]
|
msg = self.notifier.notifications[2]
|
||||||
@ -5177,9 +5178,9 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
|
|||||||
self.assertEqual(group.id, cg3.source_cgid)
|
self.assertEqual(group.id, cg3.source_cgid)
|
||||||
self.assertIsNone(cg3.cgsnapshot_id)
|
self.assertIsNone(cg3.cgsnapshot_id)
|
||||||
|
|
||||||
self.volume.delete_cgsnapshot(self.context, cgsnapshot_id)
|
self.volume.delete_cgsnapshot(self.context, cgsnapshot)
|
||||||
|
|
||||||
self.volume.delete_consistencygroup(self.context, group)
|
self.volume.delete_consistencygroup(self.context, group)
|
||||||
self.volume.delete_consistencygroup(self.context, group3)
|
|
||||||
|
|
||||||
def test_sort_snapshots(self):
|
def test_sort_snapshots(self):
|
||||||
vol1 = {'id': '1', 'name': 'volume 1',
|
vol1 = {'id': '1', 'name': 'volume 1',
|
||||||
@ -5274,15 +5275,14 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
|
|||||||
self.volume._sort_source_vols,
|
self.volume._sort_source_vols,
|
||||||
volumes, [])
|
volumes, [])
|
||||||
|
|
||||||
@staticmethod
|
def _create_cgsnapshot(self, group_id, volume_id, size='0'):
|
||||||
def _create_cgsnapshot(group_id, volume_id, size='0'):
|
|
||||||
"""Create a cgsnapshot object."""
|
"""Create a cgsnapshot object."""
|
||||||
cgsnap = {}
|
cgsnap = objects.CGSnapshot(self.context)
|
||||||
cgsnap['user_id'] = 'fake'
|
cgsnap.user_id = 'fake'
|
||||||
cgsnap['project_id'] = 'fake'
|
cgsnap.project_id = 'fake'
|
||||||
cgsnap['consistencygroup_id'] = group_id
|
cgsnap.consistencygroup_id = group_id
|
||||||
cgsnap['status'] = "creating"
|
cgsnap.status = "creating"
|
||||||
cgsnapshot = db.cgsnapshot_create(context.get_admin_context(), cgsnap)
|
cgsnap.create()
|
||||||
|
|
||||||
# Create a snapshot object
|
# Create a snapshot object
|
||||||
snap = objects.Snapshot(context.get_admin_context())
|
snap = objects.Snapshot(context.get_admin_context())
|
||||||
@ -5291,10 +5291,10 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
|
|||||||
snap.project_id = 'fake'
|
snap.project_id = 'fake'
|
||||||
snap.volume_id = volume_id
|
snap.volume_id = volume_id
|
||||||
snap.status = "available"
|
snap.status = "available"
|
||||||
snap.cgsnapshot_id = cgsnapshot['id']
|
snap.cgsnapshot_id = cgsnap.id
|
||||||
snap.create()
|
snap.create()
|
||||||
|
|
||||||
return cgsnapshot, snap
|
return cgsnap, snap
|
||||||
|
|
||||||
@mock.patch('cinder.volume.driver.VolumeDriver.create_consistencygroup',
|
@mock.patch('cinder.volume.driver.VolumeDriver.create_consistencygroup',
|
||||||
autospec=True,
|
autospec=True,
|
||||||
@ -5331,11 +5331,12 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
|
|||||||
self.notifier.notifications)
|
self.notifier.notifications)
|
||||||
|
|
||||||
cgsnapshot_returns = self._create_cgsnapshot(group.id, volume_id)
|
cgsnapshot_returns = self._create_cgsnapshot(group.id, volume_id)
|
||||||
cgsnapshot_id = cgsnapshot_returns[0]['id']
|
cgsnapshot = cgsnapshot_returns[0]
|
||||||
self.volume.create_cgsnapshot(self.context, group.id, cgsnapshot_id)
|
self.volume.create_cgsnapshot(self.context, cgsnapshot)
|
||||||
self.assertEqual(cgsnapshot_id,
|
self.assertEqual(cgsnapshot.id,
|
||||||
db.cgsnapshot_get(context.get_admin_context(),
|
objects.CGSnapshot.get_by_id(
|
||||||
cgsnapshot_id).id)
|
context.get_admin_context(),
|
||||||
|
cgsnapshot.id).id)
|
||||||
|
|
||||||
if len(self.notifier.notifications) > 6:
|
if len(self.notifier.notifications) > 6:
|
||||||
self.assertFalse(self.notifier.notifications[6],
|
self.assertFalse(self.notifier.notifications[6],
|
||||||
@ -5346,7 +5347,7 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
|
|||||||
expected = {
|
expected = {
|
||||||
'created_at': 'DONTCARE',
|
'created_at': 'DONTCARE',
|
||||||
'name': None,
|
'name': None,
|
||||||
'cgsnapshot_id': cgsnapshot_id,
|
'cgsnapshot_id': cgsnapshot.id,
|
||||||
'status': 'creating',
|
'status': 'creating',
|
||||||
'tenant_id': 'fake',
|
'tenant_id': 'fake',
|
||||||
'user_id': 'fake',
|
'user_id': 'fake',
|
||||||
@ -5356,6 +5357,7 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
|
|||||||
msg = self.notifier.notifications[3]
|
msg = self.notifier.notifications[3]
|
||||||
self.assertEqual('snapshot.create.start', msg['event_type'])
|
self.assertEqual('snapshot.create.start', msg['event_type'])
|
||||||
msg = self.notifier.notifications[4]
|
msg = self.notifier.notifications[4]
|
||||||
|
expected['status'] = 'available'
|
||||||
self.assertEqual('cgsnapshot.create.end', msg['event_type'])
|
self.assertEqual('cgsnapshot.create.end', msg['event_type'])
|
||||||
self.assertDictMatch(expected, msg['payload'])
|
self.assertDictMatch(expected, msg['payload'])
|
||||||
msg = self.notifier.notifications[5]
|
msg = self.notifier.notifications[5]
|
||||||
@ -5364,7 +5366,7 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
|
|||||||
self.assertEqual(6, len(self.notifier.notifications),
|
self.assertEqual(6, len(self.notifier.notifications),
|
||||||
self.notifier.notifications)
|
self.notifier.notifications)
|
||||||
|
|
||||||
self.volume.delete_cgsnapshot(self.context, cgsnapshot_id)
|
self.volume.delete_cgsnapshot(self.context, cgsnapshot)
|
||||||
|
|
||||||
if len(self.notifier.notifications) > 10:
|
if len(self.notifier.notifications) > 10:
|
||||||
self.assertFalse(self.notifier.notifications[10],
|
self.assertFalse(self.notifier.notifications[10],
|
||||||
@ -5376,19 +5378,20 @@ class ConsistencyGroupTestCase(BaseVolumeTestCase):
|
|||||||
self.assertDictMatch(expected, msg['payload'])
|
self.assertDictMatch(expected, msg['payload'])
|
||||||
msg = self.notifier.notifications[8]
|
msg = self.notifier.notifications[8]
|
||||||
self.assertEqual('cgsnapshot.delete.end', msg['event_type'])
|
self.assertEqual('cgsnapshot.delete.end', msg['event_type'])
|
||||||
|
expected['status'] = 'deleted'
|
||||||
self.assertDictMatch(expected, msg['payload'])
|
self.assertDictMatch(expected, msg['payload'])
|
||||||
|
|
||||||
self.assertEqual(10, len(self.notifier.notifications),
|
self.assertEqual(10, len(self.notifier.notifications),
|
||||||
self.notifier.notifications)
|
self.notifier.notifications)
|
||||||
|
|
||||||
cgsnap = db.cgsnapshot_get(
|
cgsnap = objects.CGSnapshot.get_by_id(
|
||||||
context.get_admin_context(read_deleted='yes'),
|
context.get_admin_context(read_deleted='yes'),
|
||||||
cgsnapshot_id)
|
cgsnapshot.id)
|
||||||
self.assertEqual('deleted', cgsnap['status'])
|
self.assertEqual('deleted', cgsnap.status)
|
||||||
self.assertRaises(exception.NotFound,
|
self.assertRaises(exception.NotFound,
|
||||||
db.cgsnapshot_get,
|
objects.CGSnapshot.get_by_id,
|
||||||
self.context,
|
self.context,
|
||||||
cgsnapshot_id)
|
cgsnapshot.id)
|
||||||
|
|
||||||
self.volume.delete_consistencygroup(self.context, group)
|
self.volume.delete_consistencygroup(self.context, group)
|
||||||
|
|
||||||
|
@ -64,24 +64,25 @@ class VolumeRpcAPITestCase(test.TestCase):
|
|||||||
|
|
||||||
cgsnapshot = tests_utils.create_cgsnapshot(
|
cgsnapshot = tests_utils.create_cgsnapshot(
|
||||||
self.context,
|
self.context,
|
||||||
consistencygroup_id=source_group['id'])
|
consistencygroup_id=source_group.id)
|
||||||
|
|
||||||
group = tests_utils.create_consistencygroup(
|
group = tests_utils.create_consistencygroup(
|
||||||
self.context,
|
self.context,
|
||||||
availability_zone=CONF.storage_availability_zone,
|
availability_zone=CONF.storage_availability_zone,
|
||||||
volume_type='type1,type2',
|
volume_type='type1,type2',
|
||||||
host='fakehost@fakedrv#fakepool',
|
host='fakehost@fakedrv#fakepool',
|
||||||
cgsnapshot_id=cgsnapshot['id'])
|
cgsnapshot_id=cgsnapshot.id)
|
||||||
|
|
||||||
group2 = tests_utils.create_consistencygroup(
|
group2 = tests_utils.create_consistencygroup(
|
||||||
self.context,
|
self.context,
|
||||||
availability_zone=CONF.storage_availability_zone,
|
availability_zone=CONF.storage_availability_zone,
|
||||||
volume_type='type1,type2',
|
volume_type='type1,type2',
|
||||||
host='fakehost@fakedrv#fakepool',
|
host='fakehost@fakedrv#fakepool',
|
||||||
source_cgid=source_group['id'])
|
source_cgid=source_group.id)
|
||||||
|
|
||||||
group = objects.ConsistencyGroup.get_by_id(self.context, group.id)
|
group = objects.ConsistencyGroup.get_by_id(self.context, group.id)
|
||||||
group2 = objects.ConsistencyGroup.get_by_id(self.context, group2.id)
|
group2 = objects.ConsistencyGroup.get_by_id(self.context, group2.id)
|
||||||
|
cgsnapshot = objects.CGSnapshot.get_by_id(self.context, cgsnapshot.id)
|
||||||
self.fake_volume = jsonutils.to_primitive(volume)
|
self.fake_volume = jsonutils.to_primitive(volume)
|
||||||
self.fake_volume_metadata = volume["volume_metadata"]
|
self.fake_volume_metadata = volume["volume_metadata"]
|
||||||
self.fake_snapshot = snapshot
|
self.fake_snapshot = snapshot
|
||||||
@ -89,7 +90,7 @@ class VolumeRpcAPITestCase(test.TestCase):
|
|||||||
self.fake_cg = group
|
self.fake_cg = group
|
||||||
self.fake_cg2 = group2
|
self.fake_cg2 = group2
|
||||||
self.fake_src_cg = jsonutils.to_primitive(source_group)
|
self.fake_src_cg = jsonutils.to_primitive(source_group)
|
||||||
self.fake_cgsnap = jsonutils.to_primitive(cgsnapshot)
|
self.fake_cgsnap = cgsnapshot
|
||||||
|
|
||||||
def test_serialized_volume_has_id(self):
|
def test_serialized_volume_has_id(self):
|
||||||
self.assertIn('id', self.fake_volume)
|
self.assertIn('id', self.fake_volume)
|
||||||
@ -123,6 +124,11 @@ class VolumeRpcAPITestCase(test.TestCase):
|
|||||||
del expected_msg['snapshot']
|
del expected_msg['snapshot']
|
||||||
expected_msg['snapshot_id'] = snapshot.id
|
expected_msg['snapshot_id'] = snapshot.id
|
||||||
expected_msg['snapshot'] = snapshot
|
expected_msg['snapshot'] = snapshot
|
||||||
|
if 'cgsnapshot' in expected_msg:
|
||||||
|
cgsnapshot = expected_msg['cgsnapshot']
|
||||||
|
if cgsnapshot:
|
||||||
|
cgsnapshot.consistencygroup
|
||||||
|
kwargs['cgsnapshot'].consistencygroup
|
||||||
if 'host' in expected_msg:
|
if 'host' in expected_msg:
|
||||||
del expected_msg['host']
|
del expected_msg['host']
|
||||||
if 'dest_host' in expected_msg:
|
if 'dest_host' in expected_msg:
|
||||||
@ -136,22 +142,16 @@ class VolumeRpcAPITestCase(test.TestCase):
|
|||||||
del expected_msg['new_volume']
|
del expected_msg['new_volume']
|
||||||
expected_msg['new_volume_id'] = volume['id']
|
expected_msg['new_volume_id'] = volume['id']
|
||||||
|
|
||||||
if 'cgsnapshot' in expected_msg:
|
|
||||||
cgsnapshot = expected_msg['cgsnapshot']
|
|
||||||
if cgsnapshot:
|
|
||||||
del expected_msg['cgsnapshot']
|
|
||||||
expected_msg['cgsnapshot_id'] = cgsnapshot['id']
|
|
||||||
else:
|
|
||||||
expected_msg['cgsnapshot_id'] = None
|
|
||||||
|
|
||||||
if 'host' in kwargs:
|
if 'host' in kwargs:
|
||||||
host = kwargs['host']
|
host = kwargs['host']
|
||||||
elif 'group' in kwargs:
|
elif 'group' in kwargs:
|
||||||
host = kwargs['group']['host']
|
host = kwargs['group']['host']
|
||||||
elif 'volume' not in kwargs and 'snapshot' in kwargs:
|
elif 'volume' in kwargs:
|
||||||
host = 'fake_host'
|
|
||||||
else:
|
|
||||||
host = kwargs['volume']['host']
|
host = kwargs['volume']['host']
|
||||||
|
elif 'snapshot' in kwargs:
|
||||||
|
host = 'fake_host'
|
||||||
|
elif 'cgsnapshot' in kwargs:
|
||||||
|
host = kwargs['cgsnapshot'].consistencygroup.host
|
||||||
|
|
||||||
target['server'] = utils.extract_host(host)
|
target['server'] = utils.extract_host(host)
|
||||||
target['topic'] = '%s.%s' % (CONF.volume_topic, host)
|
target['topic'] = '%s.%s' % (CONF.volume_topic, host)
|
||||||
@ -190,6 +190,10 @@ class VolumeRpcAPITestCase(test.TestCase):
|
|||||||
expected_cg = expected_msg[kwarg].obj_to_primitive()
|
expected_cg = expected_msg[kwarg].obj_to_primitive()
|
||||||
cg = value.obj_to_primitive()
|
cg = value.obj_to_primitive()
|
||||||
self.assertEqual(expected_cg, cg)
|
self.assertEqual(expected_cg, cg)
|
||||||
|
elif isinstance(value, objects.CGSnapshot):
|
||||||
|
expected_cgsnapshot = expected_msg[kwarg].obj_to_primitive()
|
||||||
|
cgsnapshot = value.obj_to_primitive()
|
||||||
|
self.assertEqual(expected_cgsnapshot, cgsnapshot)
|
||||||
else:
|
else:
|
||||||
self.assertEqual(expected_msg[kwarg], value)
|
self.assertEqual(expected_msg[kwarg], value)
|
||||||
|
|
||||||
@ -209,13 +213,11 @@ class VolumeRpcAPITestCase(test.TestCase):
|
|||||||
|
|
||||||
def test_create_cgsnapshot(self):
|
def test_create_cgsnapshot(self):
|
||||||
self._test_volume_api('create_cgsnapshot', rpc_method='cast',
|
self._test_volume_api('create_cgsnapshot', rpc_method='cast',
|
||||||
group=self.fake_cg,
|
cgsnapshot=self.fake_cgsnap, version='1.31')
|
||||||
cgsnapshot=self.fake_cgsnap, version='1.26')
|
|
||||||
|
|
||||||
def test_delete_cgsnapshot(self):
|
def test_delete_cgsnapshot(self):
|
||||||
self._test_volume_api('delete_cgsnapshot', rpc_method='cast',
|
self._test_volume_api('delete_cgsnapshot', rpc_method='cast',
|
||||||
cgsnapshot=self.fake_cgsnap, host='fake_host1',
|
cgsnapshot=self.fake_cgsnap, version='1.31')
|
||||||
version='1.18')
|
|
||||||
|
|
||||||
def test_create_volume(self):
|
def test_create_volume(self):
|
||||||
self._test_volume_api('create_volume',
|
self._test_volume_api('create_volume',
|
||||||
@ -414,7 +416,7 @@ class VolumeRpcAPITestCase(test.TestCase):
|
|||||||
group=self.fake_cg,
|
group=self.fake_cg,
|
||||||
cgsnapshot=self.fake_cgsnap,
|
cgsnapshot=self.fake_cgsnap,
|
||||||
source_cg=None,
|
source_cg=None,
|
||||||
version='1.26')
|
version='1.31')
|
||||||
|
|
||||||
def test_create_consistencygroup_from_src_cg(self):
|
def test_create_consistencygroup_from_src_cg(self):
|
||||||
self._test_volume_api('create_consistencygroup_from_src',
|
self._test_volume_api('create_consistencygroup_from_src',
|
||||||
@ -422,7 +424,7 @@ class VolumeRpcAPITestCase(test.TestCase):
|
|||||||
group=self.fake_cg2,
|
group=self.fake_cg2,
|
||||||
cgsnapshot=None,
|
cgsnapshot=None,
|
||||||
source_cg=self.fake_src_cg,
|
source_cg=self.fake_src_cg,
|
||||||
version='1.26')
|
version='1.31')
|
||||||
|
|
||||||
def test_get_capabilities(self):
|
def test_get_capabilities(self):
|
||||||
self._test_volume_api('get_capabilities',
|
self._test_volume_api('get_capabilities',
|
||||||
|
@ -139,22 +139,23 @@ def create_consistencygroup(ctxt,
|
|||||||
|
|
||||||
|
|
||||||
def create_cgsnapshot(ctxt,
|
def create_cgsnapshot(ctxt,
|
||||||
name='test_cgsnap',
|
consistencygroup_id,
|
||||||
description='this is a test cgsnap',
|
name='test_cgsnapshot',
|
||||||
status='available',
|
description='this is a test cgsnapshot',
|
||||||
consistencygroup_id=None,
|
status='creating',
|
||||||
**kwargs):
|
**kwargs):
|
||||||
"""Create a cgsnapshot object in the DB."""
|
"""Create a cgsnapshot object in the DB."""
|
||||||
cgsnap = {}
|
cgsnap = objects.CGSnapshot(ctxt)
|
||||||
cgsnap['user_id'] = ctxt.user_id
|
cgsnap.user_id = ctxt.user_id or 'fake_user_id'
|
||||||
cgsnap['project_id'] = ctxt.project_id
|
cgsnap.project_id = ctxt.project_id or 'fake_project_id'
|
||||||
cgsnap['status'] = status
|
cgsnap.status = status
|
||||||
cgsnap['name'] = name
|
cgsnap.name = name
|
||||||
cgsnap['description'] = description
|
cgsnap.description = description
|
||||||
cgsnap['consistencygroup_id'] = consistencygroup_id
|
cgsnap.consistencygroup_id = consistencygroup_id
|
||||||
for key in kwargs:
|
for key in kwargs:
|
||||||
cgsnap[key] = kwargs[key]
|
setattr(cgsnap, key, kwargs[key])
|
||||||
return db.cgsnapshot_create(ctxt, cgsnap)
|
cgsnap.create()
|
||||||
|
return cgsnap
|
||||||
|
|
||||||
|
|
||||||
def create_backup(ctxt,
|
def create_backup(ctxt,
|
||||||
|
@ -191,7 +191,7 @@ def locked_snapshot_operation(f):
|
|||||||
class VolumeManager(manager.SchedulerDependentManager):
|
class VolumeManager(manager.SchedulerDependentManager):
|
||||||
"""Manages attachable block storage devices."""
|
"""Manages attachable block storage devices."""
|
||||||
|
|
||||||
RPC_API_VERSION = '1.30'
|
RPC_API_VERSION = '1.31'
|
||||||
|
|
||||||
target = messaging.Target(version=RPC_API_VERSION)
|
target = messaging.Target(version=RPC_API_VERSION)
|
||||||
|
|
||||||
@ -1966,7 +1966,7 @@ class VolumeManager(manager.SchedulerDependentManager):
|
|||||||
|
|
||||||
if not snapshots:
|
if not snapshots:
|
||||||
snapshots = objects.SnapshotList.get_all_for_cgsnapshot(
|
snapshots = objects.SnapshotList.get_all_for_cgsnapshot(
|
||||||
context, cgsnapshot['id'])
|
context, cgsnapshot.id)
|
||||||
if snapshots:
|
if snapshots:
|
||||||
for snapshot in snapshots:
|
for snapshot in snapshots:
|
||||||
vol_utils.notify_about_snapshot_usage(
|
vol_utils.notify_about_snapshot_usage(
|
||||||
@ -2335,41 +2335,45 @@ class VolumeManager(manager.SchedulerDependentManager):
|
|||||||
return group
|
return group
|
||||||
|
|
||||||
def create_consistencygroup_from_src(self, context, group,
|
def create_consistencygroup_from_src(self, context, group,
|
||||||
cgsnapshot_id=None, source_cg=None):
|
cgsnapshot=None, source_cg=None):
|
||||||
"""Creates the consistency group from source.
|
"""Creates the consistency group from source.
|
||||||
|
|
||||||
The source can be a CG snapshot or a source CG.
|
The source can be a CG snapshot or a source CG.
|
||||||
"""
|
"""
|
||||||
|
source_name = None
|
||||||
|
snapshots = None
|
||||||
|
source_vols = None
|
||||||
try:
|
try:
|
||||||
volumes = self.db.volume_get_all_by_group(context, group.id)
|
volumes = self.db.volume_get_all_by_group(context, group.id)
|
||||||
|
|
||||||
cgsnapshot = None
|
if cgsnapshot:
|
||||||
snapshots = None
|
|
||||||
if cgsnapshot_id:
|
|
||||||
try:
|
try:
|
||||||
cgsnapshot = self.db.cgsnapshot_get(context, cgsnapshot_id)
|
# Check if cgsnapshot still exists
|
||||||
|
cgsnapshot = objects.CGSnapshot.get_by_id(
|
||||||
|
context, cgsnapshot.id)
|
||||||
except exception.CgSnapshotNotFound:
|
except exception.CgSnapshotNotFound:
|
||||||
LOG.error(_LE("Create consistency group "
|
LOG.error(_LE("Create consistency group "
|
||||||
"from snapshot-%(snap)s failed: "
|
"from snapshot-%(snap)s failed: "
|
||||||
"SnapshotNotFound."),
|
"SnapshotNotFound."),
|
||||||
{'snap': cgsnapshot_id},
|
{'snap': cgsnapshot.id},
|
||||||
resource={'type': 'consistency_group',
|
resource={'type': 'consistency_group',
|
||||||
'id': group.id})
|
'id': group.id})
|
||||||
raise
|
raise
|
||||||
if cgsnapshot:
|
|
||||||
snapshots = objects.SnapshotList.get_all_for_cgsnapshot(
|
source_name = _("snapshot-%s") % cgsnapshot.id
|
||||||
context, cgsnapshot_id)
|
snapshots = objects.SnapshotList.get_all_for_cgsnapshot(
|
||||||
for snap in snapshots:
|
context, cgsnapshot.id)
|
||||||
if (snap.status not in
|
for snap in snapshots:
|
||||||
VALID_CREATE_CG_SRC_SNAP_STATUS):
|
if (snap.status not in
|
||||||
msg = (_("Cannot create consistency group "
|
VALID_CREATE_CG_SRC_SNAP_STATUS):
|
||||||
"%(group)s because snapshot %(snap)s is "
|
msg = (_("Cannot create consistency group "
|
||||||
"not in a valid state. Valid states are: "
|
"%(group)s because snapshot %(snap)s is "
|
||||||
"%(valid)s.") %
|
"not in a valid state. Valid states are: "
|
||||||
{'group': group.id,
|
"%(valid)s.") %
|
||||||
'snap': snap['id'],
|
{'group': group.id,
|
||||||
'valid': VALID_CREATE_CG_SRC_SNAP_STATUS})
|
'snap': snap['id'],
|
||||||
raise exception.InvalidConsistencyGroup(reason=msg)
|
'valid': VALID_CREATE_CG_SRC_SNAP_STATUS})
|
||||||
|
raise exception.InvalidConsistencyGroup(reason=msg)
|
||||||
|
|
||||||
if source_cg:
|
if source_cg:
|
||||||
try:
|
try:
|
||||||
@ -2383,21 +2387,22 @@ class VolumeManager(manager.SchedulerDependentManager):
|
|||||||
resource={'type': 'consistency_group',
|
resource={'type': 'consistency_group',
|
||||||
'id': group.id})
|
'id': group.id})
|
||||||
raise
|
raise
|
||||||
if source_cg:
|
|
||||||
source_vols = self.db.volume_get_all_by_group(
|
source_name = _("cg-%s") % source_cg.id
|
||||||
context, source_cg.id)
|
source_vols = self.db.volume_get_all_by_group(
|
||||||
for source_vol in source_vols:
|
context, source_cg.id)
|
||||||
if (source_vol['status'] not in
|
for source_vol in source_vols:
|
||||||
VALID_CREATE_CG_SRC_CG_STATUS):
|
if (source_vol['status'] not in
|
||||||
msg = (_("Cannot create consistency group "
|
VALID_CREATE_CG_SRC_CG_STATUS):
|
||||||
"%(group)s because source volume "
|
msg = (_("Cannot create consistency group "
|
||||||
"%(source_vol)s is not in a valid "
|
"%(group)s because source volume "
|
||||||
"state. Valid states are: "
|
"%(source_vol)s is not in a valid "
|
||||||
"%(valid)s.") %
|
"state. Valid states are: "
|
||||||
{'group': group.id,
|
"%(valid)s.") %
|
||||||
'source_vol': source_vol['id'],
|
{'group': group.id,
|
||||||
'valid': VALID_CREATE_CG_SRC_CG_STATUS})
|
'source_vol': source_vol['id'],
|
||||||
raise exception.InvalidConsistencyGroup(reason=msg)
|
'valid': VALID_CREATE_CG_SRC_CG_STATUS})
|
||||||
|
raise exception.InvalidConsistencyGroup(reason=msg)
|
||||||
|
|
||||||
# Sort source snapshots so that they are in the same order as their
|
# Sort source snapshots so that they are in the same order as their
|
||||||
# corresponding target volumes.
|
# corresponding target volumes.
|
||||||
@ -2434,15 +2439,9 @@ class VolumeManager(manager.SchedulerDependentManager):
|
|||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
group.status = 'error'
|
group.status = 'error'
|
||||||
group.save()
|
group.save()
|
||||||
if cgsnapshot_id:
|
|
||||||
source = _("snapshot-%s") % cgsnapshot_id
|
|
||||||
elif source_cg:
|
|
||||||
source = _("cg-%s") % source_cg.id
|
|
||||||
else:
|
|
||||||
source = None
|
|
||||||
LOG.error(_LE("Create consistency group "
|
LOG.error(_LE("Create consistency group "
|
||||||
"from source %(source)s failed."),
|
"from source %(source)s failed."),
|
||||||
{'source': source},
|
{'source': source_name},
|
||||||
resource={'type': 'consistency_group',
|
resource={'type': 'consistency_group',
|
||||||
'id': group.id})
|
'id': group.id})
|
||||||
# Update volume status to 'error' as well.
|
# Update volume status to 'error' as well.
|
||||||
@ -2463,10 +2462,9 @@ class VolumeManager(manager.SchedulerDependentManager):
|
|||||||
|
|
||||||
self._notify_about_consistencygroup_usage(
|
self._notify_about_consistencygroup_usage(
|
||||||
context, group, "create.end")
|
context, group, "create.end")
|
||||||
|
|
||||||
LOG.info(_LI("Create consistency group "
|
LOG.info(_LI("Create consistency group "
|
||||||
"from snapshot-%(snap)s completed successfully."),
|
"from source-%(source)s completed successfully."),
|
||||||
{'snap': cgsnapshot_id},
|
{'source': source_name},
|
||||||
resource={'type': 'consistency_group',
|
resource={'type': 'consistency_group',
|
||||||
'id': group.id})
|
'id': group.id})
|
||||||
return group
|
return group
|
||||||
@ -2833,33 +2831,33 @@ class VolumeManager(manager.SchedulerDependentManager):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def create_cgsnapshot(self, context, group, cgsnapshot_id):
|
def create_cgsnapshot(self, context, cgsnapshot):
|
||||||
"""Creates the cgsnapshot."""
|
"""Creates the cgsnapshot."""
|
||||||
caller_context = context
|
caller_context = context
|
||||||
context = context.elevated()
|
context = context.elevated()
|
||||||
cgsnapshot_ref = self.db.cgsnapshot_get(context, cgsnapshot_id)
|
|
||||||
LOG.info(_LI("Cgsnapshot %s: creating."), cgsnapshot_ref['id'])
|
LOG.info(_LI("Cgsnapshot %s: creating."), cgsnapshot.id)
|
||||||
|
|
||||||
snapshots = objects.SnapshotList.get_all_for_cgsnapshot(
|
snapshots = objects.SnapshotList.get_all_for_cgsnapshot(
|
||||||
context, cgsnapshot_id)
|
context, cgsnapshot.id)
|
||||||
|
|
||||||
self._notify_about_cgsnapshot_usage(
|
self._notify_about_cgsnapshot_usage(
|
||||||
context, cgsnapshot_ref, "create.start")
|
context, cgsnapshot, "create.start")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
utils.require_driver_initialized(self.driver)
|
utils.require_driver_initialized(self.driver)
|
||||||
|
|
||||||
LOG.debug("Cgsnapshot %(cgsnap_id)s: creating.",
|
LOG.debug("Cgsnapshot %(cgsnap_id)s: creating.",
|
||||||
{'cgsnap_id': cgsnapshot_id})
|
{'cgsnap_id': cgsnapshot.id})
|
||||||
|
|
||||||
# Pass context so that drivers that want to use it, can,
|
# Pass context so that drivers that want to use it, can,
|
||||||
# but it is not a requirement for all drivers.
|
# but it is not a requirement for all drivers.
|
||||||
cgsnapshot_ref['context'] = caller_context
|
cgsnapshot.context = caller_context
|
||||||
for snapshot in snapshots:
|
for snapshot in snapshots:
|
||||||
snapshot.context = caller_context
|
snapshot.context = caller_context
|
||||||
|
|
||||||
model_update, snapshots = \
|
model_update, snapshots = \
|
||||||
self.driver.create_cgsnapshot(context, cgsnapshot_ref)
|
self.driver.create_cgsnapshot(context, cgsnapshot)
|
||||||
|
|
||||||
if snapshots:
|
if snapshots:
|
||||||
for snapshot in snapshots:
|
for snapshot in snapshots:
|
||||||
@ -2868,7 +2866,7 @@ class VolumeManager(manager.SchedulerDependentManager):
|
|||||||
update = {'status': snapshot['status']}
|
update = {'status': snapshot['status']}
|
||||||
|
|
||||||
# TODO(thangp): Switch over to use snapshot.update()
|
# TODO(thangp): Switch over to use snapshot.update()
|
||||||
# after cgsnapshot has been switched over to objects
|
# after cgsnapshot-objects bugs are fixed
|
||||||
self.db.snapshot_update(context, snapshot['id'],
|
self.db.snapshot_update(context, snapshot['id'],
|
||||||
update)
|
update)
|
||||||
# If status for one snapshot is error, make sure
|
# If status for one snapshot is error, make sure
|
||||||
@ -2879,15 +2877,14 @@ class VolumeManager(manager.SchedulerDependentManager):
|
|||||||
if model_update:
|
if model_update:
|
||||||
if model_update['status'] == 'error':
|
if model_update['status'] == 'error':
|
||||||
msg = (_('Error occurred when creating cgsnapshot '
|
msg = (_('Error occurred when creating cgsnapshot '
|
||||||
'%s.') % cgsnapshot_ref['id'])
|
'%s.') % cgsnapshot.id)
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.VolumeDriverException(message=msg)
|
raise exception.VolumeDriverException(message=msg)
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
self.db.cgsnapshot_update(context,
|
cgsnapshot.status = 'error'
|
||||||
cgsnapshot_ref['id'],
|
cgsnapshot.save()
|
||||||
{'status': 'error'})
|
|
||||||
|
|
||||||
for snapshot in snapshots:
|
for snapshot in snapshots:
|
||||||
volume_id = snapshot['volume_id']
|
volume_id = snapshot['volume_id']
|
||||||
@ -2905,9 +2902,8 @@ class VolumeManager(manager.SchedulerDependentManager):
|
|||||||
'snapshot_id': snapshot_id})
|
'snapshot_id': snapshot_id})
|
||||||
|
|
||||||
# TODO(thangp): Switch over to use snapshot.update()
|
# TODO(thangp): Switch over to use snapshot.update()
|
||||||
# after cgsnapshot has been switched over to objects
|
# after cgsnapshot-objects bugs are fixed
|
||||||
self.db.snapshot_update(context,
|
self.db.snapshot_update(context, snapshot_id,
|
||||||
snapshot_id,
|
|
||||||
{'status': 'error'})
|
{'status': 'error'})
|
||||||
raise exception.MetadataCopyFailure(
|
raise exception.MetadataCopyFailure(
|
||||||
reason=six.text_type(ex))
|
reason=six.text_type(ex))
|
||||||
@ -2916,52 +2912,50 @@ class VolumeManager(manager.SchedulerDependentManager):
|
|||||||
snapshot['id'], {'status': 'available',
|
snapshot['id'], {'status': 'available',
|
||||||
'progress': '100%'})
|
'progress': '100%'})
|
||||||
|
|
||||||
self.db.cgsnapshot_update(context,
|
cgsnapshot.status = 'available'
|
||||||
cgsnapshot_ref['id'],
|
cgsnapshot.save()
|
||||||
{'status': 'available'})
|
|
||||||
|
|
||||||
LOG.info(_LI("cgsnapshot %s: created successfully"),
|
LOG.info(_LI("cgsnapshot %s: created successfully"),
|
||||||
cgsnapshot_ref['id'])
|
cgsnapshot.id)
|
||||||
self._notify_about_cgsnapshot_usage(
|
self._notify_about_cgsnapshot_usage(
|
||||||
context, cgsnapshot_ref, "create.end")
|
context, cgsnapshot, "create.end")
|
||||||
return cgsnapshot_id
|
return cgsnapshot
|
||||||
|
|
||||||
def delete_cgsnapshot(self, context, cgsnapshot_id):
|
def delete_cgsnapshot(self, context, cgsnapshot):
|
||||||
"""Deletes cgsnapshot."""
|
"""Deletes cgsnapshot."""
|
||||||
caller_context = context
|
caller_context = context
|
||||||
context = context.elevated()
|
context = context.elevated()
|
||||||
cgsnapshot_ref = self.db.cgsnapshot_get(context, cgsnapshot_id)
|
project_id = cgsnapshot.project_id
|
||||||
project_id = cgsnapshot_ref['project_id']
|
|
||||||
|
|
||||||
LOG.info(_LI("cgsnapshot %s: deleting"), cgsnapshot_ref['id'])
|
LOG.info(_LI("cgsnapshot %s: deleting"), cgsnapshot.id)
|
||||||
|
|
||||||
snapshots = objects.SnapshotList.get_all_for_cgsnapshot(
|
snapshots = objects.SnapshotList.get_all_for_cgsnapshot(
|
||||||
context, cgsnapshot_id)
|
context, cgsnapshot.id)
|
||||||
|
|
||||||
self._notify_about_cgsnapshot_usage(
|
self._notify_about_cgsnapshot_usage(
|
||||||
context, cgsnapshot_ref, "delete.start")
|
context, cgsnapshot, "delete.start")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
utils.require_driver_initialized(self.driver)
|
utils.require_driver_initialized(self.driver)
|
||||||
|
|
||||||
LOG.debug("cgsnapshot %(cgsnap_id)s: deleting",
|
LOG.debug("cgsnapshot %(cgsnap_id)s: deleting",
|
||||||
{'cgsnap_id': cgsnapshot_id})
|
{'cgsnap_id': cgsnapshot.id})
|
||||||
|
|
||||||
# Pass context so that drivers that want to use it, can,
|
# Pass context so that drivers that want to use it, can,
|
||||||
# but it is not a requirement for all drivers.
|
# but it is not a requirement for all drivers.
|
||||||
cgsnapshot_ref['context'] = caller_context
|
cgsnapshot.context = caller_context
|
||||||
for snapshot in snapshots:
|
for snapshot in snapshots:
|
||||||
snapshot['context'] = caller_context
|
snapshot['context'] = caller_context
|
||||||
|
|
||||||
model_update, snapshots = \
|
model_update, snapshots = \
|
||||||
self.driver.delete_cgsnapshot(context, cgsnapshot_ref)
|
self.driver.delete_cgsnapshot(context, cgsnapshot)
|
||||||
|
|
||||||
if snapshots:
|
if snapshots:
|
||||||
for snapshot in snapshots:
|
for snapshot in snapshots:
|
||||||
update = {'status': snapshot['status']}
|
update = {'status': snapshot['status']}
|
||||||
|
|
||||||
# TODO(thangp): Switch over to use snapshot.update()
|
# TODO(thangp): Switch over to use snapshot.update()
|
||||||
# after cgsnapshot has been switched over to objects
|
# after cgsnapshot-objects bugs are fixed
|
||||||
self.db.snapshot_update(context, snapshot['id'],
|
self.db.snapshot_update(context, snapshot['id'],
|
||||||
update)
|
update)
|
||||||
if snapshot['status'] in ['error_deleting', 'error'] and \
|
if snapshot['status'] in ['error_deleting', 'error'] and \
|
||||||
@ -2972,18 +2966,17 @@ class VolumeManager(manager.SchedulerDependentManager):
|
|||||||
if model_update:
|
if model_update:
|
||||||
if model_update['status'] in ['error_deleting', 'error']:
|
if model_update['status'] in ['error_deleting', 'error']:
|
||||||
msg = (_('Error occurred when deleting cgsnapshot '
|
msg = (_('Error occurred when deleting cgsnapshot '
|
||||||
'%s.') % cgsnapshot_ref['id'])
|
'%s.') % cgsnapshot.id)
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.VolumeDriverException(message=msg)
|
raise exception.VolumeDriverException(message=msg)
|
||||||
else:
|
else:
|
||||||
self.db.cgsnapshot_update(context, cgsnapshot_ref['id'],
|
cgsnapshot.update(model_update)
|
||||||
model_update)
|
cgsnapshot.save()
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
self.db.cgsnapshot_update(context,
|
cgsnapshot.status = 'error_deleting'
|
||||||
cgsnapshot_ref['id'],
|
cgsnapshot.save()
|
||||||
{'status': 'error_deleting'})
|
|
||||||
|
|
||||||
for snapshot in snapshots:
|
for snapshot in snapshots:
|
||||||
# Get reservations
|
# Get reservations
|
||||||
@ -3010,19 +3003,18 @@ class VolumeManager(manager.SchedulerDependentManager):
|
|||||||
self.db.volume_glance_metadata_delete_by_snapshot(context,
|
self.db.volume_glance_metadata_delete_by_snapshot(context,
|
||||||
snapshot['id'])
|
snapshot['id'])
|
||||||
|
|
||||||
# TODO(thangp): Switch over to use snapshot.destroy()
|
# TODO(thangp): Switch over to use snapshot.delete()
|
||||||
# after cgsnapshot has been switched over to objects
|
# after cgsnapshot-objects bugs are fixed
|
||||||
self.db.snapshot_destroy(context, snapshot['id'])
|
self.db.snapshot_destroy(context, snapshot['id'])
|
||||||
|
|
||||||
# Commit the reservations
|
# Commit the reservations
|
||||||
if reservations:
|
if reservations:
|
||||||
QUOTAS.commit(context, reservations, project_id=project_id)
|
QUOTAS.commit(context, reservations, project_id=project_id)
|
||||||
|
|
||||||
self.db.cgsnapshot_destroy(context, cgsnapshot_id)
|
cgsnapshot.destroy()
|
||||||
LOG.info(_LI("cgsnapshot %s: deleted successfully"),
|
LOG.info(_LI("cgsnapshot %s: deleted successfully"), cgsnapshot.id)
|
||||||
cgsnapshot_ref['id'])
|
self._notify_about_cgsnapshot_usage(context, cgsnapshot, "delete.end",
|
||||||
self._notify_about_cgsnapshot_usage(
|
snapshots)
|
||||||
context, cgsnapshot_ref, "delete.end", snapshots)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -76,6 +76,9 @@ class VolumeAPI(object):
|
|||||||
1.28 - Adds manage_existing_snapshot
|
1.28 - Adds manage_existing_snapshot
|
||||||
1.29 - Adds get_capabilities.
|
1.29 - Adds get_capabilities.
|
||||||
1.30 - Adds remove_export
|
1.30 - Adds remove_export
|
||||||
|
1.31 - Updated: create_consistencygroup_from_src(), create_cgsnapshot()
|
||||||
|
and delete_cgsnapshot() to cast method only with necessary
|
||||||
|
args. Forwarding CGSnapshot object instead of CGSnapshot_id.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
BASE_RPC_API_VERSION = '1.0'
|
BASE_RPC_API_VERSION = '1.0'
|
||||||
@ -85,7 +88,7 @@ class VolumeAPI(object):
|
|||||||
target = messaging.Target(topic=CONF.volume_topic,
|
target = messaging.Target(topic=CONF.volume_topic,
|
||||||
version=self.BASE_RPC_API_VERSION)
|
version=self.BASE_RPC_API_VERSION)
|
||||||
serializer = objects_base.CinderObjectSerializer()
|
serializer = objects_base.CinderObjectSerializer()
|
||||||
self.client = rpc.get_client(target, '1.30', serializer=serializer)
|
self.client = rpc.get_client(target, '1.31', serializer=serializer)
|
||||||
|
|
||||||
def create_consistencygroup(self, ctxt, group, host):
|
def create_consistencygroup(self, ctxt, group, host):
|
||||||
new_host = utils.extract_host(host)
|
new_host = utils.extract_host(host)
|
||||||
@ -111,25 +114,21 @@ class VolumeAPI(object):
|
|||||||
def create_consistencygroup_from_src(self, ctxt, group, cgsnapshot=None,
|
def create_consistencygroup_from_src(self, ctxt, group, cgsnapshot=None,
|
||||||
source_cg=None):
|
source_cg=None):
|
||||||
new_host = utils.extract_host(group.host)
|
new_host = utils.extract_host(group.host)
|
||||||
cctxt = self.client.prepare(server=new_host, version='1.26')
|
cctxt = self.client.prepare(server=new_host, version='1.31')
|
||||||
cctxt.cast(ctxt, 'create_consistencygroup_from_src',
|
cctxt.cast(ctxt, 'create_consistencygroup_from_src',
|
||||||
group=group,
|
group=group,
|
||||||
cgsnapshot_id=cgsnapshot['id'] if cgsnapshot else None,
|
cgsnapshot=cgsnapshot,
|
||||||
source_cg=source_cg)
|
source_cg=source_cg)
|
||||||
|
|
||||||
def create_cgsnapshot(self, ctxt, group, cgsnapshot):
|
def create_cgsnapshot(self, ctxt, cgsnapshot):
|
||||||
|
host = utils.extract_host(cgsnapshot.consistencygroup.host)
|
||||||
|
cctxt = self.client.prepare(server=host, version='1.31')
|
||||||
|
cctxt.cast(ctxt, 'create_cgsnapshot', cgsnapshot=cgsnapshot)
|
||||||
|
|
||||||
host = utils.extract_host(group['host'])
|
def delete_cgsnapshot(self, ctxt, cgsnapshot):
|
||||||
cctxt = self.client.prepare(server=host, version='1.26')
|
new_host = utils.extract_host(cgsnapshot.consistencygroup.host)
|
||||||
cctxt.cast(ctxt, 'create_cgsnapshot',
|
cctxt = self.client.prepare(server=new_host, version='1.31')
|
||||||
group=group,
|
cctxt.cast(ctxt, 'delete_cgsnapshot', cgsnapshot=cgsnapshot)
|
||||||
cgsnapshot_id=cgsnapshot['id'])
|
|
||||||
|
|
||||||
def delete_cgsnapshot(self, ctxt, cgsnapshot, host):
|
|
||||||
new_host = utils.extract_host(host)
|
|
||||||
cctxt = self.client.prepare(server=new_host, version='1.18')
|
|
||||||
cctxt.cast(ctxt, 'delete_cgsnapshot',
|
|
||||||
cgsnapshot_id=cgsnapshot['id'])
|
|
||||||
|
|
||||||
def create_volume(self, ctxt, volume, host, request_spec,
|
def create_volume(self, ctxt, volume, host, request_spec,
|
||||||
filter_properties, allow_reschedule=True):
|
filter_properties, allow_reschedule=True):
|
||||||
|
@ -239,15 +239,15 @@ def notify_about_consistencygroup_usage(context, group, event_suffix,
|
|||||||
usage_info)
|
usage_info)
|
||||||
|
|
||||||
|
|
||||||
def _usage_from_cgsnapshot(cgsnapshot_ref, **kw):
|
def _usage_from_cgsnapshot(cgsnapshot, **kw):
|
||||||
usage_info = dict(
|
usage_info = dict(
|
||||||
tenant_id=cgsnapshot_ref['project_id'],
|
tenant_id=cgsnapshot.project_id,
|
||||||
user_id=cgsnapshot_ref['user_id'],
|
user_id=cgsnapshot.user_id,
|
||||||
cgsnapshot_id=cgsnapshot_ref['id'],
|
cgsnapshot_id=cgsnapshot.id,
|
||||||
name=cgsnapshot_ref['name'],
|
name=cgsnapshot.name,
|
||||||
consistencygroup_id=cgsnapshot_ref['consistencygroup_id'],
|
consistencygroup_id=cgsnapshot.consistencygroup_id,
|
||||||
created_at=cgsnapshot_ref['created_at'].isoformat(),
|
created_at=cgsnapshot.created_at.isoformat(),
|
||||||
status=cgsnapshot_ref['status'])
|
status=cgsnapshot.status)
|
||||||
|
|
||||||
usage_info.update(kw)
|
usage_info.update(kw)
|
||||||
return usage_info
|
return usage_info
|
||||||
|
@ -65,15 +65,17 @@ objects_ignore_codes = ["E0213", "E1101", "E1102"]
|
|||||||
# member is created dynamically.
|
# member is created dynamically.
|
||||||
objects_ignore_messages = [
|
objects_ignore_messages = [
|
||||||
"No value passed for parameter 'id' in function call",
|
"No value passed for parameter 'id' in function call",
|
||||||
"Module 'cinder.objects' has no 'Snapshot' member",
|
|
||||||
"Module 'cinder.objects' has no 'SnapshotList' member",
|
|
||||||
"Module 'cinder.objects' has no 'Backup' member",
|
"Module 'cinder.objects' has no 'Backup' member",
|
||||||
"Module 'cinder.objects' has no 'BackupList' member",
|
|
||||||
"Module 'cinder.objects' has no 'Service' member",
|
|
||||||
"Module 'cinder.objects' has no 'ServiceList' member",
|
|
||||||
"Module 'cinder.objects' has no 'BackupImport' member",
|
"Module 'cinder.objects' has no 'BackupImport' member",
|
||||||
|
"Module 'cinder.objects' has no 'BackupList' member",
|
||||||
|
"Module 'cinder.objects' has no 'CGSnapshot' member",
|
||||||
|
"Module 'cinder.objects' has no 'CGSnapshotList' member",
|
||||||
"Module 'cinder.objects' has no 'ConsistencyGroup' member",
|
"Module 'cinder.objects' has no 'ConsistencyGroup' member",
|
||||||
"Module 'cinder.objects' has no 'ConsistencyGroupList' member",
|
"Module 'cinder.objects' has no 'ConsistencyGroupList' member",
|
||||||
|
"Module 'cinder.objects' has no 'Service' member",
|
||||||
|
"Module 'cinder.objects' has no 'ServiceList' member",
|
||||||
|
"Module 'cinder.objects' has no 'Snapshot' member",
|
||||||
|
"Module 'cinder.objects' has no 'SnapshotList' member",
|
||||||
]
|
]
|
||||||
objects_ignore_modules = ["cinder/objects/"]
|
objects_ignore_modules = ["cinder/objects/"]
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user