Add Selection objects

The Selection object incorporates the information returned from the
scheduler into an object that is cleaner to pass over RPC, and which
allows versioning of any changes to this data.

Blueprint: return-alternate-hosts

Change-Id: Ic135752bcee73f8b5fc73a8df785c698c16ac324
This commit is contained in:
Ed Leafe 2017-08-28 19:40:31 +00:00
parent 9f46043f2f
commit 7fac6a849a
4 changed files with 160 additions and 0 deletions

View File

@ -65,6 +65,7 @@ def register_all():
__import__('nova.objects.quotas')
__import__('nova.objects.security_group')
__import__('nova.objects.security_group_rule')
__import__('nova.objects.selection')
__import__('nova.objects.service')
__import__('nova.objects.task_log')
__import__('nova.objects.vcpu_model')

60
nova/objects/selection.py Normal file
View File

@ -0,0 +1,60 @@
# Copyright (c) 2017 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_serialization import jsonutils
from oslo_versionedobjects import base as ovo_base
from oslo_versionedobjects import fields
from nova import objects
from nova.objects import base
@base.NovaObjectRegistry.register
class Selection(base.NovaObject, ovo_base.ComparableVersionedObject):
"""Represents a destination that has been selected by the Scheduler. Note
that these objects are not persisted to the database.
"""
# Version 1.0: Initial version
VERSION = "1.0"
fields = {
"compute_node_uuid": fields.UUIDField(),
"service_host": fields.StringField(),
"nodename": fields.StringField(),
"cell_uuid": fields.UUIDField(),
"limits": fields.ObjectField("SchedulerLimits", nullable=True),
# An allocation_request is a non-trivial dict, and so it will be stored
# as an encoded string.
"allocation_request": fields.StringField(nullable=True),
"allocation_request_version": fields.StringField(nullable=True),
}
@classmethod
def from_host_state(cls, host_state, allocation_request=None,
allocation_request_version=None):
"""A convenience method for converting a HostState, an
allocation_request, and an allocation_request_version into a Selection
object. Note that allocation_request and allocation_request_version
must be passed separately, as they are not part of the HostState.
"""
allocation_request_json = jsonutils.dumps(allocation_request)
limits = objects.SchedulerLimits.from_dict(host_state.limits)
return cls(compute_node_uuid=host_state.uuid,
service_host=host_state.host,
nodename=host_state.nodename,
cell_uuid=host_state.cell_uuid,
limits=limits,
allocation_request=allocation_request_json,
allocation_request_version=allocation_request_version)

View File

@ -1154,6 +1154,7 @@ object_data = {
'SecurityGroupList': '1.1-c655ed13298e630f4d398152f7d08d71',
'SecurityGroupRule': '1.1-ae1da17b79970012e8536f88cb3c6b29',
'SecurityGroupRuleList': '1.2-0005c47fcd0fb78dd6d7fd32a1409f5b',
'Selection': '1.0-7f5c065097371fe527dd1245f1530653',
'Service': '1.22-8a740459ab9bf258a19c8fcb875c2d9a',
'ServiceList': '1.19-5325bce13eebcbf22edc9678285270cc',
'TaskLog': '1.0-78b0534366f29aa3eebb01860fbe18fe',

View File

@ -0,0 +1,98 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_serialization import jsonutils
from nova import objects
from nova.objects import numa
from nova.scheduler import host_manager
from nova.tests.unit.objects import test_objects
from nova.tests import uuidsentinel as uuids
fake_numa_limit1 = numa.NUMATopologyLimits(cpu_allocation_ratio=1.0,
ram_allocation_ratio=1.0)
fake_limit1 = {"memory_mb": 1024, "disk_gb": 100, "vcpus": 2,
"numa_topology": fake_numa_limit1}
fake_limit_obj1 = objects.SchedulerLimits.from_dict(fake_limit1)
fake_host1 = {
"uuid": uuids.host1,
"host": "host1",
"nodename": "node1",
"cell_uuid": uuids.cell,
"limits": fake_limit1,
}
fake_host_state1 = host_manager.HostState("host1", "node1", uuids.cell)
fake_host_state1.uuid = uuids.host1
fake_host_state1.limits = fake_limit1.copy()
fake_alloc1 = {"allocations": [
{"resource_provider": {"uuid": uuids.host1},
"resources": {"VCPU": 1,
"MEMORY_MB": 1024,
"DISK_GB": 100}
}]}
fake_alloc_version = "1.23"
class _TestSelectionObject(object):
def test_create_with_values(self):
json_alloc = jsonutils.dumps(fake_alloc1)
dest = objects.Selection(service_host="host", nodename="node",
compute_node_uuid=uuids.host1, cell_uuid=uuids.cell,
limits=fake_limit_obj1, allocation_request=json_alloc,
allocation_request_version=fake_alloc_version)
self.assertEqual("host", dest.service_host)
self.assertEqual(uuids.host1, dest.compute_node_uuid)
self.assertEqual("node", dest.nodename)
self.assertEqual(uuids.cell, dest.cell_uuid)
self.assertEqual(fake_limit_obj1, dest.limits)
self.assertEqual(json_alloc, dest.allocation_request)
self.assertEqual(fake_alloc_version, dest.allocation_request_version)
def test_passing_dict_allocation_fails(self):
self.assertRaises(ValueError, objects.Selection, service_host="host",
compute_node_uuid=uuids.host, nodename="node",
cell_uuid=uuids.cell, allocation_request=fake_alloc1,
allocation_request_version=fake_alloc_version)
def test_passing_numeric_allocation_version_converts(self):
json_alloc = jsonutils.dumps(fake_alloc1)
dest = objects.Selection(service_host="host",
compute_node_uuid=uuids.host, nodename="node",
cell_uuid=uuids.cell, allocation_request=json_alloc,
allocation_request_version=1.23)
self.assertEqual("1.23", dest.allocation_request_version)
def test_from_host_state(self):
dest = objects.Selection.from_host_state(fake_host_state1, fake_alloc1,
fake_alloc_version)
self.assertEqual(dest.service_host, fake_host_state1.host)
expected_alloc = jsonutils.dumps(fake_alloc1)
self.assertEqual(dest.allocation_request, expected_alloc)
self.assertEqual(dest.allocation_request_version, fake_alloc_version)
def test_from_host_state_no_alloc_info(self):
dest = objects.Selection.from_host_state(fake_host_state1)
self.assertEqual(dest.service_host, fake_host_state1.host)
expected_alloc = jsonutils.dumps(None)
self.assertEqual(expected_alloc, dest.allocation_request)
self.assertIsNone(dest.allocation_request_version)
class TestSelectionObject(test_objects._LocalTest,
_TestSelectionObject):
pass
class TestRemoteSelectionObject(test_objects._RemoteTest,
_TestSelectionObject):
pass