From 1145a9899de4efeda3eb53c1be709584ccf5f39b Mon Sep 17 00:00:00 2001
From: =?utf8?q?Micha=C5=82=20Dulko?= <michal.dulko@intel.com>
Date: Thu, 19 Nov 2015 17:50:20 +0100
Subject: [PATCH] Recognize extra fields in CinderObjectDictCompat

We're still in the process of transitioning to the object notation in a
lot of places and that's why we've decided to keep specialized get in
CinderObjectDictCompat. This one wasn't looking at aliases (properties)
from obj_extra_fields. This commit fixes that and adds a unit test for
this method.

We should eventually stop using it, but for Mitaka this will make a lot
of things easier.

Change-Id: I1df610bedffdeae56444ce0a40f8fb48216e91b7
Related-Bug: 1516903
---
 cinder/objects/base.py                 |  7 +++++++
 cinder/tests/unit/objects/test_base.py | 28 ++++++++++++++++++++++++++
 2 files changed, 35 insertions(+)

diff --git a/cinder/objects/base.py b/cinder/objects/base.py
index efa1293f8..61498370d 100644
--- a/cinder/objects/base.py
+++ b/cinder/objects/base.py
@@ -240,6 +240,13 @@ class CinderObjectDictCompat(base.VersionedObjectDictCompat):
         else:
             return getattr(self, key)
 
+    def __contains__(self, name):
+        try:
+            # Overriding this to make extra fields pass "'foo' in obj" tests
+            return name in self.obj_extra_fields or self.obj_attr_is_set(name)
+        except AttributeError:
+            return False
+
 
 class CinderPersistentObject(object):
     """Mixin class for Persistent objects.
diff --git a/cinder/tests/unit/objects/test_base.py b/cinder/tests/unit/objects/test_base.py
index ec12ab6b8..6e95124c3 100644
--- a/cinder/tests/unit/objects/test_base.py
+++ b/cinder/tests/unit/objects/test_base.py
@@ -538,3 +538,31 @@ class TestCinderObjectConditionalUpdate(test.TestCase):
 
         # Check that the volume in the DB has also been updated
         self._check_volume(volume, 'deleting', expected_size, True)
+
+
+class TestCinderDictObject(test_objects.BaseObjectsTestCase):
+    @objects.base.CinderObjectRegistry.register_if(False)
+    class TestDictObject(objects.base.CinderObjectDictCompat,
+                         objects.base.CinderObject):
+        obj_extra_fields = ['foo']
+
+        fields = {
+            'abc': fields.StringField(nullable=True),
+            'def': fields.IntegerField(nullable=True),
+        }
+
+        @property
+        def foo(self):
+            return 42
+
+    def test_dict_objects(self):
+        obj = self.TestDictObject()
+        self.assertIsNone(obj.get('non_existing'))
+        self.assertEqual('val', obj.get('abc', 'val'))
+        obj.abc = 'val2'
+        self.assertEqual('val2', obj.get('abc', 'val'))
+        self.assertEqual(42, obj.get('foo'))
+
+        self.assertTrue('foo' in obj)
+        self.assertTrue('abc' in obj)
+        self.assertFalse('def' in obj)
-- 
2.45.2