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:
parent
784f88b060
commit
2358ebacd8
@ -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
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
|
@ -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):
|
||||
|
Loading…
x
Reference in New Issue
Block a user