Skip to content

Commit a7de491

Browse files
melwittmriedem
authored andcommitted
Populate InstanceMapping.user_id during migrations and schedules
The InstanceMapping user_id field is a new, non-nullable field representing the user_id for the instance. When new instance create requests come in, we create the instance mapping. We will set user_id here before creating the record. Some virtual interface online data migration and map_instances routine create InstanceMapping records and since the user_id field did not previously exist, they were not setting it. We will populate user_id in these cases. Finally, whenever an API does a compute_api.get(), we can opportunistically set and save user_id on the instance mapping if it is not set. Part of blueprint count-quota-usage-from-placement Change-Id: Ic4bb7b49b90a3d6d7ce6c6c62d87836f96309f06
1 parent 7475e85 commit a7de491

File tree

6 files changed

+81
-12
lines changed

6 files changed

+81
-12
lines changed

nova/cmd/manage.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,7 @@ def _get_and_map_instances(self, ctxt, cell_mapping, limit, marker):
11991199
mapping.instance_uuid = instance.uuid
12001200
mapping.cell_mapping = cell_mapping
12011201
mapping.project_id = instance.project_id
1202+
mapping.user_id = instance.user_id
12021203
mapping.create()
12031204
except db_exc.DBDuplicateEntry:
12041205
continue
@@ -1293,8 +1294,11 @@ def map_instances(self, cell_uuid, max_count=None, reset_marker=None):
12931294
# Don't judge me. There's already an InstanceMapping with this UUID
12941295
# so the marker needs to be non destructively modified.
12951296
next_marker = next_marker.replace('-', ' ')
1297+
# This is just the marker record, so set user_id to the special
1298+
# marker name as well.
12961299
objects.InstanceMapping(ctxt, instance_uuid=next_marker,
1297-
project_id=marker_project_id).create()
1300+
project_id=marker_project_id,
1301+
user_id=marker_project_id).create()
12981302
return 1
12991303
return 0
13001304

nova/compute/api.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,7 @@ def _provision_instances(self, context, instance_type, min_count,
10121012
inst_mapping = objects.InstanceMapping(context=context)
10131013
inst_mapping.instance_uuid = instance_uuid
10141014
inst_mapping.project_id = context.project_id
1015+
inst_mapping.user_id = context.user_id
10151016
inst_mapping.cell_mapping = None
10161017
inst_mapping.create()
10171018

@@ -2458,6 +2459,18 @@ def _get_instance_map_or_none(self, context, instance_uuid):
24582459
inst_map = None
24592460
return inst_map
24602461

2462+
@staticmethod
2463+
def _save_user_id_in_instance_mapping(mapping, instance):
2464+
# TODO(melwitt): We take the opportunity to migrate user_id on the
2465+
# instance mapping if it's not yet been migrated. This can be removed
2466+
# in a future release, when all migrations are complete.
2467+
# If the instance came from a RequestSpec because of a down cell, its
2468+
# user_id could be None and the InstanceMapping.user_id field is
2469+
# non-nullable. Avoid trying to set/save the user_id in that case.
2470+
if 'user_id' not in mapping and instance.user_id is not None:
2471+
mapping.user_id = instance.user_id
2472+
mapping.save()
2473+
24612474
def _get_instance_from_cell(self, context, im, expected_attrs,
24622475
cell_down_support):
24632476
# NOTE(danms): Even though we're going to scatter/gather to the
@@ -2471,7 +2484,9 @@ def _get_instance_from_cell(self, context, im, expected_attrs,
24712484
expected_attrs=expected_attrs)
24722485
cell_uuid = im.cell_mapping.uuid
24732486
if not nova_context.is_cell_failure_sentinel(result[cell_uuid]):
2474-
return result[cell_uuid]
2487+
inst = result[cell_uuid]
2488+
self._save_user_id_in_instance_mapping(im, inst)
2489+
return inst
24752490
elif isinstance(result[cell_uuid], exception.InstanceNotFound):
24762491
raise exception.InstanceNotFound(instance_id=uuid)
24772492
elif cell_down_support:
@@ -2491,14 +2506,16 @@ def _get_instance_from_cell(self, context, im, expected_attrs,
24912506
# and its id.
24922507
image_ref = (rs.image.id if rs.image and
24932508
'id' in rs.image else None)
2494-
return objects.Instance(context=context, power_state=0,
2509+
inst = objects.Instance(context=context, power_state=0,
24952510
uuid=uuid,
24962511
project_id=im.project_id,
24972512
created_at=im.created_at,
24982513
user_id=rs.user_id,
24992514
flavor=rs.flavor,
25002515
image_ref=image_ref,
25012516
availability_zone=rs.availability_zone)
2517+
self._save_user_id_in_instance_mapping(im, inst)
2518+
return inst
25022519
except exception.RequestSpecNotFound:
25032520
# could be that a deleted instance whose request
25042521
# spec has been archived is being queried.

nova/objects/virtual_interface.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ def _set_or_delete_marker_for_migrate_instances(context, marker=None):
301301
instance = objects.Instance(context)
302302
instance.uuid = FAKE_UUID
303303
instance.project_id = FAKE_UUID
304+
instance.user_id = FAKE_UUID
304305
instance.create()
305306
# Thats fake instance, lets destroy it.
306307
# We need only its row to solve constraint issue.

nova/tests/functional/db/test_virtual_interface.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,16 @@ def test_migration_multiple_cells(self):
323323
self.assertEqual(4, match)
324324
self.assertEqual(3, done)
325325

326+
# Verify that the marker instance has project_id/user_id set properly.
327+
with context.target_cell(self.context, self.cells[1]) as cctxt:
328+
# The marker record is destroyed right after it's created, since
329+
# only the presence of the row is needed to satisfy the fkey
330+
# constraint.
331+
cctxt = cctxt.elevated(read_deleted='yes')
332+
marker_instance = objects.Instance.get_by_uuid(cctxt, FAKE_UUID)
333+
self.assertEqual(FAKE_UUID, marker_instance.project_id)
334+
self.assertEqual(FAKE_UUID, marker_instance.user_id)
335+
326336
# Try again - should fill 3 left instances from cell1
327337
match, done = virtual_interface.fill_virtual_interface_list(
328338
self.context, 4)

nova/tests/unit/compute/test_compute_api.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4878,6 +4878,8 @@ def do_test(mock_inst_mapping, mock_check_num_inst_quota):
48784878
inst_mapping_mock.instance_uuid)
48794879
self.assertIsNone(inst_mapping_mock.cell_mapping)
48804880
self.assertEqual(ctxt.project_id, inst_mapping_mock.project_id)
4881+
# Verify that the instance mapping created has user_id populated.
4882+
self.assertEqual(ctxt.user_id, inst_mapping_mock.user_id)
48814883
do_test()
48824884

48834885
@mock.patch.object(objects.service, 'get_minimum_version_all_cells',
@@ -5759,6 +5761,8 @@ def test_get_all_with_cell_down_support_all_tenants(self, mock_get_ims,
57595761
None,
57605762
limit=3)
57615763

5764+
@mock.patch('nova.compute.api.API._save_user_id_in_instance_mapping',
5765+
new=mock.MagicMock())
57625766
@mock.patch.object(objects.Instance, 'get_by_uuid')
57635767
def test_get_instance_from_cell_success(self, mock_get_inst):
57645768
cell_mapping = objects.CellMapping(uuid=uuids.cell1,
@@ -5786,9 +5790,11 @@ def test_get_instance_from_cell_failure(self, mock_get_inst):
57865790
im, [], False)
57875791
self.assertIn('could not be found', six.text_type(exp))
57885792

5793+
@mock.patch('nova.compute.api.API._save_user_id_in_instance_mapping')
57895794
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
57905795
@mock.patch('nova.context.scatter_gather_cells')
5791-
def test_get_instance_with_cell_down_support(self, mock_sg, mock_rs):
5796+
def test_get_instance_with_cell_down_support(self, mock_sg, mock_rs,
5797+
mock_save_uid):
57925798
cell_mapping = objects.CellMapping(uuid=uuids.cell1,
57935799
name='1', id=1)
57945800
im1 = objects.InstanceMapping(instance_uuid=uuids.inst1,
@@ -5840,6 +5846,8 @@ def test_get_instance_with_cell_down_support(self, mock_sg, mock_rs):
58405846
self.assertEqual(uuids.inst2, result.uuid)
58415847
self.assertEqual('nova', result.availability_zone)
58425848
self.assertEqual(uuids.image, result.image_ref)
5849+
# Verify that user_id is populated during a compute_api.get().
5850+
mock_save_uid.assert_called_once_with(im2, result)
58435851

58445852
# Same as above, but boot-from-volume where image is not None but the
58455853
# id of the image is not set.
@@ -5903,6 +5911,8 @@ def test_get_instance_not_in_cell(self, mock_get_inst, mock_get_build_req,
59035911
'security_groups', 'info_cache'])
59045912
self.assertEqual(instance, inst_from_build_req)
59055913

5914+
@mock.patch('nova.compute.api.API._save_user_id_in_instance_mapping',
5915+
new=mock.MagicMock())
59065916
@mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
59075917
@mock.patch.object(objects.BuildRequest, 'get_by_instance_uuid')
59085918
@mock.patch.object(objects.Instance, 'get_by_uuid')
@@ -5988,11 +5998,34 @@ def test_get_instance_not_in_cell_buildreq_deleted_inst_still_not_in_cell(
59885998
'info_cache'])
59895999
self.assertEqual(instance, inst_from_get)
59906000

6001+
@mock.patch('nova.objects.InstanceMapping.save')
6002+
def test_save_user_id_in_instance_mapping(self, im_save):
6003+
# Verify user_id is populated if it not set
6004+
im = objects.InstanceMapping()
6005+
i = objects.Instance(user_id='fake')
6006+
self.compute_api._save_user_id_in_instance_mapping(im, i)
6007+
self.assertEqual(im.user_id, i.user_id)
6008+
im_save.assert_called_once_with()
6009+
# Verify user_id is not saved if it is already set
6010+
im_save.reset_mock()
6011+
im.user_id = 'fake-other'
6012+
self.compute_api._save_user_id_in_instance_mapping(im, i)
6013+
self.assertNotEqual(im.user_id, i.user_id)
6014+
im_save.assert_not_called()
6015+
# Verify user_id is not saved if it is None
6016+
im_save.reset_mock()
6017+
im = objects.InstanceMapping()
6018+
i = objects.Instance(user_id=None)
6019+
self.compute_api._save_user_id_in_instance_mapping(im, i)
6020+
self.assertNotIn('user_id', im)
6021+
im_save.assert_not_called()
6022+
6023+
@mock.patch('nova.compute.api.API._save_user_id_in_instance_mapping')
59916024
@mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
59926025
@mock.patch.object(objects.BuildRequest, 'get_by_instance_uuid')
59936026
@mock.patch.object(objects.Instance, 'get_by_uuid')
59946027
def test_get_instance_in_cell(self, mock_get_inst, mock_get_build_req,
5995-
mock_get_inst_map):
6028+
mock_get_inst_map, mock_save_uid):
59966029
self.useFixture(nova_fixtures.AllServicesCurrent())
59976030
# This just checks that the instance is looked up normally and not
59986031
# synthesized from a BuildRequest object. Verification of pulling the
@@ -6010,6 +6043,8 @@ def test_get_instance_in_cell(self, mock_get_inst, mock_get_build_req,
60106043
if self.cell_type is None:
60116044
mock_get_inst_map.assert_called_once_with(self.context,
60126045
instance.uuid)
6046+
# Verify that user_id is populated during a compute_api.get().
6047+
mock_save_uid.assert_called_once_with(inst_map, instance)
60136048
else:
60146049
self.assertFalse(mock_get_inst_map.called)
60156050
self.assertEqual(instance, returned_inst)

nova/tests/unit/test_nova_manage.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,14 +1198,16 @@ def test_map_instances(self, mock_target_cell):
11981198
uuid = uuidutils.generate_uuid()
11991199
instance_uuids.append(uuid)
12001200
objects.Instance(ctxt, project_id=ctxt.project_id,
1201-
uuid=uuid).create()
1201+
user_id=ctxt.user_id, uuid=uuid).create()
12021202

12031203
self.commands.map_instances(cell_uuid)
12041204

12051205
for uuid in instance_uuids:
12061206
inst_mapping = objects.InstanceMapping.get_by_instance_uuid(ctxt,
12071207
uuid)
12081208
self.assertEqual(ctxt.project_id, inst_mapping.project_id)
1209+
# Verify that map_instances populates user_id.
1210+
self.assertEqual(ctxt.user_id, inst_mapping.user_id)
12091211
self.assertEqual(cell_mapping.uuid, inst_mapping.cell_mapping.uuid)
12101212
mock_target_cell.assert_called_once_with(
12111213
test.MatchType(context.RequestContext),
@@ -1225,10 +1227,10 @@ def test_map_instances_duplicates(self, mock_target_cell):
12251227
uuid = uuidutils.generate_uuid()
12261228
instance_uuids.append(uuid)
12271229
objects.Instance(ctxt, project_id=ctxt.project_id,
1228-
uuid=uuid).create()
1230+
user_id=ctxt.user_id, uuid=uuid).create()
12291231

12301232
objects.InstanceMapping(ctxt, project_id=ctxt.project_id,
1231-
instance_uuid=instance_uuids[0],
1233+
user_id=ctxt.user_id, instance_uuid=instance_uuids[0],
12321234
cell_mapping=cell_mapping).create()
12331235

12341236
self.commands.map_instances(cell_uuid)
@@ -1260,7 +1262,7 @@ def test_map_instances_two_batches(self, mock_target_cell):
12601262
uuid = uuidutils.generate_uuid()
12611263
instance_uuids.append(uuid)
12621264
objects.Instance(ctxt, project_id=ctxt.project_id,
1263-
uuid=uuid).create()
1265+
user_id=ctxt.user_id, uuid=uuid).create()
12641266

12651267
ret = self.commands.map_instances(cell_uuid)
12661268
self.assertEqual(0, ret)
@@ -1292,7 +1294,7 @@ def test_map_instances_max_count(self, mock_target_cell):
12921294
uuid = uuidutils.generate_uuid()
12931295
instance_uuids.append(uuid)
12941296
objects.Instance(ctxt, project_id=ctxt.project_id,
1295-
uuid=uuid).create()
1297+
user_id=ctxt.user_id, uuid=uuid).create()
12961298

12971299
ret = self.commands.map_instances(cell_uuid, max_count=3)
12981300
self.assertEqual(1, ret)
@@ -1329,7 +1331,7 @@ def test_map_instances_marker_deleted(self, mock_target_cell):
13291331
uuid = uuidutils.generate_uuid()
13301332
instance_uuids.append(uuid)
13311333
objects.Instance(ctxt, project_id=ctxt.project_id,
1332-
uuid=uuid).create()
1334+
user_id=ctxt.user_id, uuid=uuid).create()
13331335

13341336
ret = self.commands.map_instances(cell_uuid, max_count=3)
13351337
self.assertEqual(1, ret)
@@ -1371,7 +1373,7 @@ def test_map_instances_marker_reset(self, mock_target_cell):
13711373
uuid = uuidutils.generate_uuid()
13721374
instance_uuids.append(uuid)
13731375
objects.Instance(ctxt, project_id=ctxt.project_id,
1374-
uuid=uuid).create()
1376+
user_id=ctxt.user_id, uuid=uuid).create()
13751377

13761378
# Maps first three instances.
13771379
ret = self.commands.map_instances(cell_uuid, max_count=3)

0 commit comments

Comments
 (0)