Skip to content

Commit 0c77580

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Add user_id field to InstanceMapping"
2 parents 151deeb + 7475e85 commit 0c77580

File tree

4 files changed

+66
-6
lines changed

4 files changed

+66
-6
lines changed

nova/objects/instance_mapping.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
# License for the specific language governing permissions and limitations
1111
# under the License.
1212

13+
from oslo_log import log as logging
1314
from oslo_utils import versionutils
1415
from sqlalchemy.orm import joinedload
1516
from sqlalchemy.sql import false
@@ -19,34 +20,51 @@
1920
from nova.db.sqlalchemy import api as db_api
2021
from nova.db.sqlalchemy import api_models
2122
from nova import exception
23+
from nova.i18n import _
2224
from nova import objects
2325
from nova.objects import base
2426
from nova.objects import cell_mapping
2527
from nova.objects import fields
2628

2729

30+
LOG = logging.getLogger(__name__)
31+
32+
2833
@base.NovaObjectRegistry.register
2934
class InstanceMapping(base.NovaTimestampObject, base.NovaObject):
3035
# Version 1.0: Initial version
3136
# Version 1.1: Add queued_for_delete
32-
VERSION = '1.1'
37+
# Version 1.2: Add user_id
38+
VERSION = '1.2'
3339

3440
fields = {
3541
'id': fields.IntegerField(read_only=True),
3642
'instance_uuid': fields.UUIDField(),
3743
'cell_mapping': fields.ObjectField('CellMapping', nullable=True),
3844
'project_id': fields.StringField(),
45+
'user_id': fields.StringField(),
3946
'queued_for_delete': fields.BooleanField(default=False),
4047
}
4148

4249
def obj_make_compatible(self, primitive, target_version):
4350
super(InstanceMapping, self).obj_make_compatible(primitive,
4451
target_version)
4552
target_version = versionutils.convert_version_to_tuple(target_version)
53+
if target_version < (1, 2) and 'user_id' in primitive:
54+
del primitive['user_id']
4655
if target_version < (1, 1):
4756
if 'queued_for_delete' in primitive:
4857
del primitive['queued_for_delete']
4958

59+
def obj_load_attr(self, attrname):
60+
if attrname == 'user_id':
61+
LOG.error('The unset user_id attribute of an unmigrated instance '
62+
'mapping should not be accessed.')
63+
raise exception.ObjectActionError(
64+
action='obj_load_attr',
65+
reason=_('attribute user_id is not lazy-loadable'))
66+
super(InstanceMapping, self).obj_load_attr(attrname)
67+
5068
def _update_with_cell_id(self, updates):
5169
cell_mapping_obj = updates.pop("cell_mapping", None)
5270
if cell_mapping_obj:
@@ -63,6 +81,11 @@ def _from_db_object(context, instance_mapping, db_instance_mapping):
6381
if db_value:
6482
db_value = cell_mapping.CellMapping._from_db_object(
6583
context, cell_mapping.CellMapping(), db_value)
84+
if key == 'user_id' and db_value is None:
85+
# NOTE(melwitt): If user_id is NULL, we can't set the field
86+
# because it's non-nullable. We don't plan for any code to read
87+
# the user_id field at this time, so skip setting it.
88+
continue
6689
setattr(instance_mapping, key, db_value)
6790
instance_mapping.obj_reset_changes()
6891
instance_mapping._context = context

nova/tests/functional/db/test_instance_mapping.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,16 @@ def test_populate_queued_for_delete(self):
206206
self.assertEqual(0, done)
207207
self.assertEqual(0, total)
208208

209+
def test_user_id_not_set_if_null_from_db(self):
210+
# Create an instance mapping with user_id=None.
211+
db_mapping = create_mapping()
212+
self.assertIsNone(db_mapping['user_id'])
213+
# Get the mapping to run convert from db object to versioned object.
214+
im = instance_mapping.InstanceMapping.get_by_instance_uuid(
215+
self.context, db_mapping['instance_uuid'])
216+
# Verify the user_id is not set.
217+
self.assertNotIn('user_id', im)
218+
209219

210220
class InstanceMappingListTestCase(test.NoDBTestCase):
211221
USES_DB_SELF = True

nova/tests/unit/objects/test_instance_mapping.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import mock
1414
from oslo_utils import uuidutils
1515

16+
from nova import exception
1617
from nova import objects
1718
from nova.objects import instance_mapping
1819
from nova.tests.unit.objects import test_cell_mapping
@@ -25,6 +26,7 @@ def get_db_mapping(**updates):
2526
'instance_uuid': uuidutils.generate_uuid(),
2627
'cell_id': None,
2728
'project_id': 'fake-project',
29+
'user_id': 'fake-user',
2830
'created_at': None,
2931
'updated_at': None,
3032
'queued_for_delete': False,
@@ -77,13 +79,15 @@ def test_create(self, create_in_db):
7779
mapping_obj.cell_mapping = objects.CellMapping(self.context,
7880
id=db_mapping['cell_mapping']['id'])
7981
mapping_obj.project_id = db_mapping['project_id']
82+
mapping_obj.user_id = db_mapping['user_id']
8083

8184
mapping_obj.create()
8285
create_in_db.assert_called_once_with(self.context,
8386
{'instance_uuid': uuid,
8487
'queued_for_delete': False,
8588
'cell_id': db_mapping['cell_mapping']['id'],
86-
'project_id': db_mapping['project_id']})
89+
'project_id': db_mapping['project_id'],
90+
'user_id': db_mapping['user_id']})
8791
self.compare_obj(mapping_obj, db_mapping,
8892
subs={'cell_mapping': 'cell_id'},
8993
comparators={
@@ -98,12 +102,14 @@ def test_create_cell_mapping_none(self, create_in_db):
98102
mapping_obj.instance_uuid = uuid
99103
mapping_obj.cell_mapping = None
100104
mapping_obj.project_id = db_mapping['project_id']
105+
mapping_obj.user_id = db_mapping['user_id']
101106

102107
mapping_obj.create()
103108
create_in_db.assert_called_once_with(self.context,
104109
{'instance_uuid': uuid,
105110
'queued_for_delete': False,
106-
'project_id': db_mapping['project_id']})
111+
'project_id': db_mapping['project_id'],
112+
'user_id': db_mapping['user_id']})
107113
self.compare_obj(mapping_obj, db_mapping,
108114
subs={'cell_mapping': 'cell_id'})
109115
self.assertIsNone(mapping_obj.cell_mapping)
@@ -116,13 +122,15 @@ def test_create_cell_mapping_with_qfd_true(self, create_in_db):
116122
mapping_obj.instance_uuid = db_mapping['instance_uuid']
117123
mapping_obj.cell_mapping = None
118124
mapping_obj.project_id = db_mapping['project_id']
125+
mapping_obj.user_id = db_mapping['user_id']
119126
mapping_obj.queued_for_delete = True
120127

121128
mapping_obj.create()
122129
create_in_db.assert_called_once_with(self.context,
123130
{'instance_uuid': db_mapping['instance_uuid'],
124131
'queued_for_delete': True,
125-
'project_id': db_mapping['project_id']})
132+
'project_id': db_mapping['project_id'],
133+
'user_id': db_mapping['user_id']})
126134

127135
@mock.patch.object(instance_mapping.InstanceMapping, '_save_in_db')
128136
def test_save(self, save_in_db):
@@ -162,13 +170,32 @@ def test_obj_make_compatible(self):
162170
im_obj = instance_mapping.InstanceMapping(context=self.context)
163171
fake_im_obj = instance_mapping.InstanceMapping(context=self.context,
164172
instance_uuid=uuid,
165-
queued_for_delete=False)
173+
queued_for_delete=False,
174+
user_id='fake-user')
175+
obj_primitive = fake_im_obj.obj_to_primitive('1.1')
176+
obj = im_obj.obj_from_primitive(obj_primitive)
177+
self.assertIn('queued_for_delete', obj)
178+
self.assertNotIn('user_id', obj)
179+
166180
obj_primitive = fake_im_obj.obj_to_primitive('1.0')
167181
obj = im_obj.obj_from_primitive(obj_primitive)
168182
self.assertIn('instance_uuid', obj)
169183
self.assertEqual(uuid, obj.instance_uuid)
170184
self.assertNotIn('queued_for_delete', obj)
171185

186+
@mock.patch('nova.objects.instance_mapping.LOG.error')
187+
def test_obj_load_attr(self, mock_log):
188+
im_obj = instance_mapping.InstanceMapping()
189+
# Access of unset user_id should have special handling
190+
self.assertRaises(exception.ObjectActionError, im_obj.obj_load_attr,
191+
'user_id')
192+
msg = ('The unset user_id attribute of an unmigrated instance mapping '
193+
'should not be accessed.')
194+
mock_log.assert_called_once_with(msg)
195+
# Access of any other unset attribute should fall back to base class
196+
self.assertRaises(NotImplementedError, im_obj.obj_load_attr,
197+
'project_id')
198+
172199

173200
class TestInstanceMappingObject(test_objects._LocalTest,
174201
_TestInstanceMappingObject):

nova/tests/unit/objects/test_objects.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1107,7 +1107,7 @@ def obj_name(cls):
11071107
'InstanceGroupList': '1.8-90f8f1a445552bb3bbc9fa1ae7da27d4',
11081108
'InstanceInfoCache': '1.5-cd8b96fefe0fc8d4d337243ba0bf0e1e',
11091109
'InstanceList': '2.4-d2c5723da8c1d08e07cb00160edfd292',
1110-
'InstanceMapping': '1.1-808df83f25987578ed3b187e16b47405',
1110+
'InstanceMapping': '1.2-3bd375e65c8eb9c45498d2f87b882e03',
11111111
'InstanceMappingList': '1.2-ee638619aa3d8a82a59c0c83bfa64d78',
11121112
'InstanceNUMACell': '1.4-7c1eb9a198dee076b4de0840e45f4f55',
11131113
'InstanceNUMATopology': '1.3-ec0030cb0402a49c96da7051c037082a',

0 commit comments

Comments
 (0)