Skip to content

Commit

Permalink
Add requested_destination field to RequestSpec
Browse files Browse the repository at this point in the history
As agreed in the spec, we want to provide to the scheduler the possible
destination asked by the admin when calling evacuate or live-migrate.
Adding a possibility to default the field to None if an old RequestSpec
or a legacy dict wants to hydrate.

Change-Id: I6ddcaaca37fc5387c2d2e9f51c67ea9e85acb5c5
Partially-Implements: blueprint check-destination-on-migrations-newton
  • Loading branch information
sbauza committed May 27, 2016
1 parent 784f88b commit 2358eba
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 2 deletions.
50 changes: 49 additions & 1 deletion nova/objects/request_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# under the License.

from oslo_serialization import jsonutils
from oslo_utils import versionutils
import six

from nova.db.sqlalchemy import api as db
Expand All @@ -25,6 +26,8 @@
from nova.scheduler import utils as scheduler_utils
from nova.virt import hardware

REQUEST_SPEC_OPTIONAL_ATTRS = ['requested_destination']


@base.NovaObjectRegistry.register
class RequestSpec(base.NovaObject):
Expand All @@ -34,7 +37,8 @@ class RequestSpec(base.NovaObject):
# Version 1.3: InstanceGroup version 1.10
# Version 1.4: ImageMeta version 1.7
# Version 1.5: Added get_by_instance_uuid(), create(), save()
VERSION = '1.5'
# Version 1.6: Added requested_destination
VERSION = '1.6'

fields = {
'id': fields.IntegerField(),
Expand All @@ -50,6 +54,9 @@ class RequestSpec(base.NovaObject):
'ignore_hosts': fields.ListOfStringsField(nullable=True),
'force_hosts': fields.ListOfStringsField(nullable=True),
'force_nodes': fields.ListOfStringsField(nullable=True),
'requested_destination': fields.ObjectField('Destination',
nullable=True,
default=None),
'retry': fields.ObjectField('SchedulerRetries', nullable=True),
'limits': fields.ObjectField('SchedulerLimits', nullable=True),
'instance_group': fields.ObjectField('InstanceGroup', nullable=True),
Expand All @@ -60,6 +67,24 @@ class RequestSpec(base.NovaObject):
'instance_uuid': fields.UUIDField(),
}

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, 6):
if 'requested_destination' in primitive:
del primitive['requested_destination']

def obj_load_attr(self, attrname):
if attrname not in REQUEST_SPEC_OPTIONAL_ATTRS:
raise exception.ObjectActionError(
action='obj_load_attr',
reason='attribute %s not lazy-loadable' % attrname)

# NOTE(sbauza): In case the primitive was not providing that field
# because of a previous RequestSpec version, we want to default
# that field in order to have the same behaviour.
self.obj_set_defaults(attrname)

@property
def vcpus(self):
return self.flavor.vcpus
Expand Down Expand Up @@ -222,6 +247,11 @@ def from_primitives(cls, context, request_spec, filter_properties):
spec._populate_group_info(filter_properties)
scheduler_hints = filter_properties.get('scheduler_hints', {})
spec._from_hints(scheduler_hints)

# NOTE(sbauza): Default the other fields that are not part of the
# original contract
spec.obj_set_defaults()

return spec

def get_scheduler_hint(self, hint_name, default=None):
Expand Down Expand Up @@ -365,6 +395,10 @@ def from_components(cls, context, instance_uuid, image, flavor,
spec_obj._from_limits(filter_properties.get('limits', {}))
spec_obj._from_hints(filter_properties.get('scheduler_hints', {}))
spec_obj.availability_zone = availability_zone

# NOTE(sbauza): Default the other fields that are not part of the
# original contract
spec_obj.obj_set_defaults()
return spec_obj

@staticmethod
Expand Down Expand Up @@ -548,6 +582,20 @@ def migrate_instances_add_request_spec(context, max_count):
return count_all, count_hit


@base.NovaObjectRegistry.register
class Destination(base.NovaObject):
# Version 1.0: Initial version
VERSION = '1.0'

fields = {
'host': fields.StringField(),
# NOTE(sbauza): Given we want to split the host/node relationship later
# and also remove the possibility to have multiple nodes per service,
# let's provide a possible nullable node here.
'node': fields.StringField(nullable=True),
}


@base.NovaObjectRegistry.register
class SchedulerRetries(base.NovaObject):
# Version 1.0: Initial version
Expand Down
1 change: 1 addition & 0 deletions nova/tests/unit/fake_request_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ def fake_spec_obj(remove_id=False):
req_obj.force_hosts = ['host1', 'host3']
req_obj.force_nodes = ['node1', 'node2']
req_obj.scheduler_hints = {'hint': ['over-there']}
req_obj.requested_destination = None
# This should never be a changed field
req_obj.obj_reset_changes(['id'])
return req_obj
3 changes: 2 additions & 1 deletion nova/tests/unit/objects/test_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,7 @@ def obj_name(cls):
'ComputeNodeList': '1.14-3b6f4f5ade621c40e70cb116db237844',
'DNSDomain': '1.0-7b0b2dab778454b6a7b6c66afe163a1a',
'DNSDomainList': '1.0-4ee0d9efdfd681fed822da88376e04d2',
'Destination': '1.0-4c59dd1288b2e7adbda6051a2de59183',
'DeviceMetadata': '1.0-04eb8fd218a49cbc3b1e54b774d179f7',
'DeviceMetadataList': '1.0-15ecf022a68ddbb8c2a6739cfc9f8f5e',
'DiskMetadata': '1.0-e7a0f1ccccf10d26a76b28e7492f3788',
Expand Down Expand Up @@ -1178,7 +1179,7 @@ def obj_name(cls):
'PciDevicePoolList': '1.1-15ecf022a68ddbb8c2a6739cfc9f8f5e',
'Quotas': '1.2-1fe4cd50593aaf5d36a6dc5ab3f98fb3',
'QuotasNoOp': '1.2-e041ddeb7dc8188ca71706f78aad41c1',
'RequestSpec': '1.5-576a249869c161e17b7cd6d55f9d85f3',
'RequestSpec': '1.6-c1cb516acdf120d367a42d343ed695b5',
'ResourceProvider': '1.0-57a9a344b0faed9cf6d6811835b6deb6',
'S3ImageMapping': '1.0-7dd7366a890d82660ed121de9092276e',
'SchedulerLimits': '1.0-249c4bd8e62a9b327b7026b7f19cc641',
Expand Down
12 changes: 12 additions & 0 deletions nova/tests/unit/objects/test_request_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import mock
from oslo_serialization import jsonutils
from oslo_utils import uuidutils
from oslo_versionedobjects import base as ovo_base

from nova import context
from nova import exception
Expand Down Expand Up @@ -546,6 +547,17 @@ def test_reset_forced_destinations(self):
self.assertIsNone(req_obj.force_nodes)
mock_reset.assert_called_once_with(['force_hosts', 'force_nodes'])

def test_compat_requested_destination(self):
req_obj = objects.RequestSpec()
versions = ovo_base.obj_tree_get_versions('RequestSpec')
primitive = req_obj.obj_to_primitive(target_version='1.5',
version_manifest=versions)
self.assertNotIn('requested_destination', primitive)

def test_default_requested_destination(self):
req_obj = objects.RequestSpec()
self.assertIsNone(req_obj.requested_destination)


class TestRequestSpecObject(test_objects._LocalTest,
_TestRequestSpecObject):
Expand Down

0 comments on commit 2358eba

Please sign in to comment.