Add requested_destination field to RequestSpec

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
This commit is contained in:
Sylvain Bauza 2016-05-04 23:16:13 +02:00
parent 784f88b060
commit 2358ebacd8
4 changed files with 64 additions and 2 deletions

View File

@ -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
@ -25,6 +26,8 @@ from nova.objects import instance as obj_instance
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):
@ -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(),
@ -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),
@ -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
@ -222,6 +247,11 @@ class RequestSpec(base.NovaObject):
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):
@ -365,6 +395,10 @@ class RequestSpec(base.NovaObject):
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
@ -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

View File

@ -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

View File

@ -1112,6 +1112,7 @@ object_data = {
'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',
@ -1178,7 +1179,7 @@ object_data = {
'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',

View File

@ -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
@ -546,6 +547,17 @@ class _TestRequestSpecObject(object):
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):