diff --git a/cinder/objects/__init__.py b/cinder/objects/__init__.py index f8c2ba4897c..10367843ae1 100644 --- a/cinder/objects/__init__.py +++ b/cinder/objects/__init__.py @@ -25,8 +25,8 @@ def register_all(): # function in order for it to be registered by services that may # need to receive it via RPC. __import__('cinder.objects.backup') - __import__('cinder.objects.consistencygroup') __import__('cinder.objects.cgsnapshot') + __import__('cinder.objects.consistencygroup') __import__('cinder.objects.service') __import__('cinder.objects.snapshot') __import__('cinder.objects.volume') diff --git a/cinder/objects/consistencygroup.py b/cinder/objects/consistencygroup.py index a252c7f880e..1c16fbc6e2f 100644 --- a/cinder/objects/consistencygroup.py +++ b/cinder/objects/consistencygroup.py @@ -19,11 +19,15 @@ from cinder import objects from cinder.objects import base from oslo_versionedobjects import fields +OPTIONAL_FIELDS = ['cgsnapshots', 'volumes'] + @base.CinderObjectRegistry.register class ConsistencyGroup(base.CinderPersistentObject, base.CinderObject, base.CinderObjectDictCompat): - VERSION = '1.0' + # Version 1.0: Initial version + # Version 1.1: Added cgsnapshots and volumes relationships + VERSION = '1.1' fields = { 'id': fields.UUIDField(), @@ -37,14 +41,35 @@ class ConsistencyGroup(base.CinderPersistentObject, base.CinderObject, 'status': fields.StringField(nullable=True), 'cgsnapshot_id': fields.UUIDField(nullable=True), 'source_cgid': fields.UUIDField(nullable=True), + 'cgsnapshots': fields.ObjectField('CGSnapshotList', nullable=True), + 'volumes': fields.ObjectField('VolumeList', nullable=True), } @staticmethod - def _from_db_object(context, consistencygroup, db_consistencygroup): + def _from_db_object(context, consistencygroup, db_consistencygroup, + expected_attrs=None): + if expected_attrs is None: + expected_attrs = [] for name, field in consistencygroup.fields.items(): + if name in OPTIONAL_FIELDS: + continue value = db_consistencygroup.get(name) setattr(consistencygroup, name, value) + if 'cgsnapshots' in expected_attrs: + cgsnapshots = base.obj_make_list( + context, objects.CGSnapshotsList(context), + objects.CGSnapshot, + db_consistencygroup['cgsnapshots']) + consistencygroup.cgsnapshots = cgsnapshots + + if 'volumes' in expected_attrs: + volumes = base.obj_make_list( + context, objects.VolumeList(context), + objects.Volume, + db_consistencygroup['volumes']) + consistencygroup.cgsnapshots = volumes + consistencygroup._context = context consistencygroup.obj_reset_changes() return consistencygroup @@ -55,14 +80,49 @@ class ConsistencyGroup(base.CinderPersistentObject, base.CinderObject, raise exception.ObjectActionError(action='create', reason=_('already_created')) updates = self.cinder_obj_get_changes() + + if 'cgsnapshots' in updates: + raise exception.ObjectActionError(action='create', + reason=_('cgsnapshots assigned')) + + if 'volumes' in updates: + raise exception.ObjectActionError(action='create', + reason=_('volumes assigned')) + db_consistencygroups = db.consistencygroup_create(self._context, updates) self._from_db_object(self._context, self, db_consistencygroups) + 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 == 'cgsnapshots': + self.cgsnapshots = objects.CGSnapshotList.get_all_by_group( + self._context, self.id) + + if attrname == 'volumes': + self.volumes = objects.VolumeList.get_all_by_group(self._context, + self.id) + + self.obj_reset_changes(fields=[attrname]) + @base.remotable def save(self): updates = self.cinder_obj_get_changes() if updates: + if 'cgsnapshots' in updates: + raise exception.ObjectActionError( + action='save', reason=_('cgsnapshots changed')) + if 'volumes' in updates: + raise exception.ObjectActionError( + action='save', reason=_('volumes changed')) + db.consistencygroup_update(self._context, self.id, updates) self.obj_reset_changes() diff --git a/cinder/tests/unit/fake_consistencygroup.py b/cinder/tests/unit/fake_consistencygroup.py index 042321e0a83..280959c308b 100644 --- a/cinder/tests/unit/fake_consistencygroup.py +++ b/cinder/tests/unit/fake_consistencygroup.py @@ -23,6 +23,7 @@ def fake_db_consistencygroup(**updates): 'user_id': '2', 'project_id': '3', 'host': 'FakeHost', + 'volumes': [], } for name, field in objects.ConsistencyGroup.fields.items(): if name in db_values: diff --git a/cinder/tests/unit/objects/test_consistencygroup.py b/cinder/tests/unit/objects/test_consistencygroup.py index 9c66bb9cfaf..d4a5e1ffa12 100644 --- a/cinder/tests/unit/objects/test_consistencygroup.py +++ b/cinder/tests/unit/objects/test_consistencygroup.py @@ -32,6 +32,16 @@ fake_consistencygroup = { 'source_cgid': None, } +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 TestConsistencyGroup(test_objects.BaseObjectsTestCase): @@ -74,6 +84,57 @@ class TestConsistencyGroup(test_objects.BaseObjectsTestCase): consistencygroup.id, {'status': 'active'}) + def test_save_with_cgsnapshots(self): + consistencygroup = objects.ConsistencyGroup._from_db_object( + self.context, objects.ConsistencyGroup(), fake_consistencygroup) + cgsnapshots_objs = [objects.CGSnapshot(context=self.context, id=i) + for i in [3, 4, 5]] + cgsnapshots = objects.CGSnapshotList(objects=cgsnapshots_objs) + consistencygroup.name = 'foobar' + consistencygroup.cgsnapshots = cgsnapshots + self.assertEqual({'name': 'foobar', + 'cgsnapshots': cgsnapshots}, + consistencygroup.obj_get_changes()) + self.assertRaises(exception.ObjectActionError, consistencygroup.save) + + def test_save_with_volumes(self): + consistencygroup = objects.ConsistencyGroup._from_db_object( + self.context, objects.ConsistencyGroup(), fake_consistencygroup) + volumes_objs = [objects.Volume(context=self.context, id=i) + for i in [3, 4, 5]] + volumes = objects.VolumeList(objects=volumes_objs) + consistencygroup.name = 'foobar' + consistencygroup.volumes = volumes + self.assertEqual({'name': 'foobar', + 'volumes': volumes}, + consistencygroup.obj_get_changes()) + self.assertRaises(exception.ObjectActionError, consistencygroup.save) + + @mock.patch('cinder.objects.cgsnapshot.CGSnapshotList.get_all_by_group') + @mock.patch('cinder.objects.volume.VolumeList.get_all_by_group') + def test_obj_load_attr(self, mock_vol_get_all_by_group, + mock_cgsnap_get_all_by_group): + consistencygroup = objects.ConsistencyGroup._from_db_object( + self.context, objects.ConsistencyGroup(), fake_consistencygroup) + # Test cgsnapshots lazy-loaded field + cgsnapshots_objs = [objects.CGSnapshot(context=self.context, id=i) + for i in [3, 4, 5]] + cgsnapshots = objects.CGSnapshotList(context=self.context, + objects=cgsnapshots_objs) + mock_cgsnap_get_all_by_group.return_value = cgsnapshots + self.assertEqual(cgsnapshots, consistencygroup.cgsnapshots) + mock_cgsnap_get_all_by_group.assert_called_once_with( + self.context, consistencygroup.id) + + # Test volumes lazy-loaded field + volume_objs = [objects.Volume(context=self.context, id=i) + for i in [3, 4, 5]] + volumes = objects.VolumeList(context=self.context, objects=volume_objs) + mock_vol_get_all_by_group.return_value = volumes + self.assertEqual(volumes, consistencygroup.volumes) + mock_vol_get_all_by_group.assert_called_once_with(self.context, + consistencygroup.id) + @mock.patch('cinder.db.consistencygroup_destroy') def test_destroy(self, consistencygroup_destroy): consistencygroup = objects.ConsistencyGroup(context=self.context, diff --git a/cinder/tests/unit/objects/test_objects.py b/cinder/tests/unit/objects/test_objects.py index 1348cb633d6..bf58eaf68f0 100644 --- a/cinder/tests/unit/objects/test_objects.py +++ b/cinder/tests/unit/objects/test_objects.py @@ -26,7 +26,7 @@ object_data = { 'BackupList': '1.0-24591dabe26d920ce0756fe64cd5f3aa', 'CGSnapshot': '1.0-190da2a2aa9457edc771d888f7d225c4', 'CGSnapshotList': '1.0-e8c3f4078cd0ee23487b34d173eec776', - 'ConsistencyGroup': '1.0-b9bad093daee0b259edddb3993c60c31', + 'ConsistencyGroup': '1.1-8d8b867a67c1bd6e9f840bcf5e375dbb', 'ConsistencyGroupList': '1.0-09d0aad5491e762ecfdf66bef02ceb8d', 'Service': '1.0-64baeb4911dbab1153064dd1c87edb9f', 'ServiceList': '1.0-d242d3384b68e5a5a534e090ff1d5161',