diff --git a/nova/conductor/manager.py b/nova/conductor/manager.py index a409f16ac1c8..632d0325d99b 100644 --- a/nova/conductor/manager.py +++ b/nova/conductor/manager.py @@ -794,7 +794,7 @@ class ComputeTaskManager(base.Base): objects.Destination( cell=instance_mapping.cell_mapping)) - request_spec.ensure_project_id(instance) + request_spec.ensure_project_and_user_id(instance) host_lists = self._schedule_instances(context, request_spec, [instance.uuid], return_alternates=False) @@ -942,7 +942,7 @@ class ComputeTaskManager(base.Base): # is not forced to be the original host request_spec.reset_forced_destinations() try: - request_spec.ensure_project_id(instance) + request_spec.ensure_project_and_user_id(instance) host_lists = self._schedule_instances(context, request_spec, [instance.uuid], return_alternates=False) diff --git a/nova/conductor/tasks/live_migrate.py b/nova/conductor/tasks/live_migrate.py index 84624a272747..8e9ed5acfdc3 100644 --- a/nova/conductor/tasks/live_migrate.py +++ b/nova/conductor/tasks/live_migrate.py @@ -301,7 +301,7 @@ class LiveMigrationTask(base.TaskBase): request_spec.requested_destination = objects.Destination( cell=cell_mapping) - request_spec.ensure_project_id(self.instance) + request_spec.ensure_project_and_user_id(self.instance) return request_spec diff --git a/nova/conductor/tasks/migrate.py b/nova/conductor/tasks/migrate.py index fb8c2fc18285..7e675b2b2d26 100644 --- a/nova/conductor/tasks/migrate.py +++ b/nova/conductor/tasks/migrate.py @@ -219,7 +219,7 @@ class MigrationTask(base.TaskBase): # instance record. migration = self._preallocate_migration() - self.request_spec.ensure_project_id(self.instance) + self.request_spec.ensure_project_and_user_id(self.instance) # On an initial call to migrate, 'self.host_list' will be None, so we # have to call the scheduler to get a list of acceptable hosts to # migrate to. That list will consist of a selected host, along with diff --git a/nova/objects/request_spec.py b/nova/objects/request_spec.py index e1ae19a6fbca..5db3d26a89b7 100644 --- a/nova/objects/request_spec.py +++ b/nova/objects/request_spec.py @@ -41,7 +41,8 @@ class RequestSpec(base.NovaObject): # Version 1.6: Added requested_destination # Version 1.7: Added destroy() # Version 1.8: Added security_groups - VERSION = '1.8' + # Version 1.9: Added user_id + VERSION = '1.9' fields = { 'id': fields.IntegerField(), @@ -53,6 +54,7 @@ class RequestSpec(base.NovaObject): # TODO(mriedem): The project_id shouldn't be nullable since the # scheduler relies on it being set. 'project_id': fields.StringField(nullable=True), + 'user_id': fields.StringField(nullable=True), 'availability_zone': fields.StringField(nullable=True), 'flavor': fields.ObjectField('Flavor', nullable=False), 'num_instances': fields.IntegerField(default=1), @@ -82,6 +84,9 @@ class RequestSpec(base.NovaObject): def obj_make_compatible(self, primitive, target_version): super(RequestSpec, self).obj_make_compatible(primitive, target_version) target_version = versionutils.convert_version_to_tuple(target_version) + if target_version < (1, 9): + if 'user_id' in primitive: + del primitive['user_id'] if target_version < (1, 8): if 'security_groups' in primitive: del primitive['security_groups'] @@ -153,7 +158,7 @@ class RequestSpec(base.NovaObject): return instance_fields = ['numa_topology', 'pci_requests', 'uuid', - 'project_id', 'availability_zone'] + 'project_id', 'user_id', 'availability_zone'] for field in instance_fields: if field == 'uuid': setattr(self, 'instance_uuid', getter(instance, field)) @@ -307,7 +312,8 @@ class RequestSpec(base.NovaObject): # fields, we can only return a dict. instance = {} instance_fields = ['numa_topology', 'pci_requests', - 'project_id', 'availability_zone', 'instance_uuid'] + 'project_id', 'user_id', 'availability_zone', + 'instance_uuid'] for field in instance_fields: if not self.obj_attr_is_set(field): continue @@ -388,7 +394,8 @@ class RequestSpec(base.NovaObject): @classmethod def from_components(cls, context, instance_uuid, image, flavor, numa_topology, pci_requests, filter_properties, instance_group, - availability_zone, security_groups=None, project_id=None): + availability_zone, security_groups=None, project_id=None, + user_id=None): """Returns a new RequestSpec object hydrated by various components. This helper is useful in creating the RequestSpec from the various @@ -409,6 +416,8 @@ class RequestSpec(base.NovaObject): set security_groups on the resulting object. :param project_id: The project_id for the requestspec (should match the instance project_id). + :param user_id: The user_id for the requestspec (should match + the instance user_id). """ spec_obj = cls(context) spec_obj.num_instances = 1 @@ -417,6 +426,7 @@ class RequestSpec(base.NovaObject): if spec_obj.instance_group is None and filter_properties: spec_obj._populate_group_info(filter_properties) spec_obj.project_id = project_id or context.project_id + spec_obj.user_id = user_id or context.user_id spec_obj._image_meta_from_image(image) spec_obj._from_flavor(flavor) spec_obj._from_instance_pci_requests(pci_requests) @@ -438,9 +448,11 @@ class RequestSpec(base.NovaObject): spec_obj.obj_set_defaults() return spec_obj - def ensure_project_id(self, instance): + def ensure_project_and_user_id(self, instance): if 'project_id' not in self or self.project_id is None: self.project_id = instance.project_id + if 'user_id' not in self or self.user_id is None: + self.user_id = instance.user_id @staticmethod def _from_db_object(context, spec, db_spec): @@ -628,7 +640,8 @@ def _create_minimal_request_spec(context, instance): instance.flavor, instance.numa_topology, instance.pci_requests, {}, None, instance.availability_zone, - project_id=instance.project_id + project_id=instance.project_id, + user_id=instance.user_id ) scheduler_utils.setup_instance_group(context, request_spec) request_spec.create() diff --git a/nova/tests/unit/fake_request_spec.py b/nova/tests/unit/fake_request_spec.py index 3957d8f29074..3c7cf316d914 100644 --- a/nova/tests/unit/fake_request_spec.py +++ b/nova/tests/unit/fake_request_spec.py @@ -80,6 +80,7 @@ def fake_spec_obj(remove_id=False): req_obj.limits = objects.SchedulerLimits() req_obj.instance_group = objects.InstanceGroup(uuid=uuids.instgroup) req_obj.project_id = 'fake' + req_obj.user_id = 'fake-user' req_obj.num_instances = 1 req_obj.availability_zone = None req_obj.ignore_hosts = ['host2', 'host4'] diff --git a/nova/tests/unit/objects/test_objects.py b/nova/tests/unit/objects/test_objects.py index ce4c568ac9cb..07b5c2f4c3a0 100644 --- a/nova/tests/unit/objects/test_objects.py +++ b/nova/tests/unit/objects/test_objects.py @@ -1141,7 +1141,7 @@ object_data = { 'PowerVMLiveMigrateData': '1.2-b62cd242c5205a853545b1085b072340', 'Quotas': '1.3-40fcefe522111dddd3e5e6155702cf4e', 'QuotasNoOp': '1.3-347a039fc7cfee7b225b68b5181e0733', - 'RequestSpec': '1.8-35033ecef47a880f9a5e46e2269e2b97', + 'RequestSpec': '1.9-e506ccb22cd7807a1207c22a3f179387', 'S3ImageMapping': '1.0-7dd7366a890d82660ed121de9092276e', 'SchedulerLimits': '1.0-249c4bd8e62a9b327b7026b7f19cc641', 'SchedulerRetries': '1.1-3c9c8b16143ebbb6ad7030e999d14cc0', diff --git a/nova/tests/unit/objects/test_request_spec.py b/nova/tests/unit/objects/test_request_spec.py index 8b1e9f063fdd..2ae92adbe03a 100644 --- a/nova/tests/unit/objects/test_request_spec.py +++ b/nova/tests/unit/objects/test_request_spec.py @@ -78,12 +78,13 @@ class _TestRequestSpecObject(object): instance.numa_topology = None instance.pci_requests = None instance.project_id = fakes.FAKE_PROJECT_ID + instance.user_id = fakes.FAKE_USER_ID instance.availability_zone = 'nova' spec = objects.RequestSpec() spec._from_instance(instance) instance_fields = ['numa_topology', 'pci_requests', 'uuid', - 'project_id', 'availability_zone'] + 'project_id', 'user_id', 'availability_zone'] for field in instance_fields: if field == 'uuid': self.assertEqual(getattr(instance, field), @@ -97,12 +98,13 @@ class _TestRequestSpecObject(object): numa_topology=None, pci_requests=None, project_id=fakes.FAKE_PROJECT_ID, + user_id=fakes.FAKE_USER_ID, availability_zone='nova') spec = objects.RequestSpec() spec._from_instance(instance) instance_fields = ['numa_topology', 'pci_requests', 'uuid', - 'project_id', 'availability_zone'] + 'project_id', 'user_id', 'availability_zone'] for field in instance_fields: if field == 'uuid': self.assertEqual(instance.get(field), @@ -124,6 +126,7 @@ class _TestRequestSpecObject(object): vcpus=1, numa_topology=None, project_id=fakes.FAKE_PROJECT_ID, + user_id=fakes.FAKE_USER_ID, availability_zone='nova', pci_requests={ 'instance_uuid': 'fakeid', @@ -142,6 +145,7 @@ class _TestRequestSpecObject(object): memory_mb=10, vcpus=1, project_id=fakes.FAKE_PROJECT_ID, + user_id=fakes.FAKE_USER_ID, availability_zone='nova', pci_requests=None, numa_topology={'cells': [{'id': 1, 'cpuset': ['1'], 'memory': 8192, @@ -180,6 +184,7 @@ class _TestRequestSpecObject(object): spec.numa_topology = None spec.pci_requests = None spec.project_id = fakes.FAKE_PROJECT_ID + spec.user_id = fakes.FAKE_USER_ID spec.availability_zone = 'nova' instance = spec._to_legacy_instance() @@ -190,6 +195,7 @@ class _TestRequestSpecObject(object): 'numa_topology': None, 'pci_requests': None, 'project_id': fakes.FAKE_PROJECT_ID, + 'user_id': fakes.FAKE_USER_ID, 'availability_zone': 'nova'}, instance) def test_to_legacy_instance_with_unset_values(self): @@ -281,6 +287,7 @@ class _TestRequestSpecObject(object): numa_topology=None, pci_requests=None, project_id=1, + user_id=2, availability_zone='nova')} filt_props = {} @@ -624,6 +631,16 @@ class _TestRequestSpecObject(object): version_manifest=versions) self.assertNotIn('security_groups', primitive) + def test_compat_user_id(self): + req_obj = objects.RequestSpec(project_id=fakes.FAKE_PROJECT_ID, + user_id=fakes.FAKE_USER_ID) + versions = ovo_base.obj_tree_get_versions('RequestSpec') + primitive = req_obj.obj_to_primitive(target_version='1.8', + version_manifest=versions) + primitive = primitive['nova_object.data'] + self.assertNotIn('user_id', primitive) + self.assertIn('project_id', primitive) + def test_default_requested_destination(self): req_obj = objects.RequestSpec() self.assertIsNone(req_obj.requested_destination)