nova/nova/tests/unit/objects/test_request_spec.py

1520 lines
62 KiB
Python

# Copyright 2015 Red Hat, Inc.
#
# 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.
import collections
import mock
from oslo_serialization import jsonutils
from oslo_utils.fixture import uuidsentinel as uuids
from oslo_utils import uuidutils
from oslo_versionedobjects import base as ovo_base
from nova import context
from nova import exception
from nova.network import model as network_model
from nova import objects
from nova.objects import base
from nova.objects import request_spec
from nova import test
from nova.tests.unit.api.openstack import fakes
from nova.tests.unit import fake_flavor
from nova.tests.unit import fake_instance
from nova.tests.unit import fake_network_cache_model
from nova.tests.unit import fake_request_spec
from nova.tests.unit.objects import test_objects
class _TestRequestSpecObject(object):
def test_image_meta_from_image_as_object(self):
# Just isolating the test for the from_dict() method
image_meta = objects.ImageMeta(name='foo')
spec = objects.RequestSpec()
spec._image_meta_from_image(image_meta)
self.assertEqual(image_meta, spec.image)
@mock.patch.object(objects.ImageMeta, 'from_dict')
def test_image_meta_from_image_as_dict(self, from_dict):
# Just isolating the test for the from_dict() method
image_meta = objects.ImageMeta(name='foo')
from_dict.return_value = image_meta
spec = objects.RequestSpec()
spec._image_meta_from_image({'name': 'foo'})
self.assertEqual(image_meta, spec.image)
def test_image_meta_from_image_as_none(self):
# just add a dumb check to have a full coverage
spec = objects.RequestSpec()
spec._image_meta_from_image(None)
self.assertIsNone(spec.image)
@mock.patch.object(base, 'obj_to_primitive')
def test_to_legacy_image(self, obj_to_primitive):
spec = objects.RequestSpec(image=objects.ImageMeta())
fake_dict = mock.Mock()
obj_to_primitive.return_value = fake_dict
self.assertEqual(fake_dict, spec._to_legacy_image())
obj_to_primitive.assert_called_once_with(spec.image)
@mock.patch.object(base, 'obj_to_primitive')
def test_to_legacy_image_with_none(self, obj_to_primitive):
spec = objects.RequestSpec(image=None)
self.assertEqual({}, spec._to_legacy_image())
self.assertFalse(obj_to_primitive.called)
def test_from_instance_as_object(self):
instance = objects.Instance()
instance.uuid = uuidutils.generate_uuid()
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', 'user_id', 'availability_zone']
for field in instance_fields:
if field == 'uuid':
self.assertEqual(getattr(instance, field),
getattr(spec, 'instance_uuid'))
else:
self.assertEqual(getattr(instance, field),
getattr(spec, field))
def test_from_instance_as_dict(self):
instance = dict(uuid=uuidutils.generate_uuid(),
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', 'user_id', 'availability_zone']
for field in instance_fields:
if field == 'uuid':
self.assertEqual(instance.get(field),
getattr(spec, 'instance_uuid'))
else:
self.assertEqual(instance.get(field), getattr(spec, field))
@mock.patch.object(objects.InstancePCIRequests,
'from_request_spec_instance_props')
def test_from_instance_with_pci_requests(self, pci_from_spec):
fake_pci_requests = objects.InstancePCIRequests()
pci_from_spec.return_value = fake_pci_requests
instance = dict(
uuid=uuidutils.generate_uuid(),
root_gb=10,
ephemeral_gb=0,
memory_mb=10,
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',
'requests': [{'count': 1, 'spec': [{'vendor_id': '8086'}]}]})
spec = objects.RequestSpec()
spec._from_instance(instance)
pci_from_spec.assert_called_once_with(instance['pci_requests'])
self.assertEqual(fake_pci_requests, spec.pci_requests)
def test_from_instance_with_numa_stuff(self):
instance = dict(
uuid=uuidutils.generate_uuid(),
root_gb=10,
ephemeral_gb=0,
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,
'pagesize': None, 'cpu_topology': None,
'cpu_pinning_raw': None}]})
spec = objects.RequestSpec()
spec._from_instance(instance)
self.assertIsInstance(spec.numa_topology, objects.InstanceNUMATopology)
cells = spec.numa_topology.cells
self.assertEqual(1, len(cells))
self.assertIsInstance(cells[0], objects.InstanceNUMACell)
def test_from_flavor_as_object(self):
flavor = objects.Flavor()
spec = objects.RequestSpec()
spec._from_flavor(flavor)
self.assertEqual(flavor, spec.flavor)
def test_from_flavor_as_dict(self):
flavor_dict = dict(id=1)
ctxt = context.RequestContext('fake', 'fake')
spec = objects.RequestSpec(ctxt)
spec._from_flavor(flavor_dict)
self.assertIsInstance(spec.flavor, objects.Flavor)
self.assertEqual({'id': 1}, spec.flavor.obj_get_changes())
def test_to_legacy_instance(self):
spec = objects.RequestSpec()
spec.flavor = objects.Flavor(root_gb=10,
ephemeral_gb=0,
memory_mb=10,
vcpus=1)
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()
self.assertEqual({'root_gb': 10,
'ephemeral_gb': 0,
'memory_mb': 10,
'vcpus': 1,
'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):
spec = objects.RequestSpec()
self.assertEqual({}, spec._to_legacy_instance())
def test_from_retry(self):
retry_dict = {'num_attempts': 1,
'hosts': [['fake1', 'node1']]}
ctxt = context.RequestContext('fake', 'fake')
spec = objects.RequestSpec(ctxt)
spec._from_retry(retry_dict)
self.assertIsInstance(spec.retry, objects.SchedulerRetries)
self.assertEqual(1, spec.retry.num_attempts)
self.assertIsInstance(spec.retry.hosts, objects.ComputeNodeList)
self.assertEqual(1, len(spec.retry.hosts))
self.assertEqual('fake1', spec.retry.hosts[0].host)
self.assertEqual('node1', spec.retry.hosts[0].hypervisor_hostname)
def test_from_retry_missing_values(self):
retry_dict = {}
ctxt = context.RequestContext('fake', 'fake')
spec = objects.RequestSpec(ctxt)
spec._from_retry(retry_dict)
self.assertIsNone(spec.retry)
def test_populate_group_info(self):
filt_props = {}
filt_props['group_updated'] = True
filt_props['group_policies'] = set(['affinity'])
filt_props['group_hosts'] = set(['fake1'])
filt_props['group_members'] = set(['fake-instance1'])
spec = objects.RequestSpec()
spec._populate_group_info(filt_props)
self.assertIsInstance(spec.instance_group, objects.InstanceGroup)
self.assertEqual('affinity', spec.instance_group.policy)
self.assertEqual(['fake1'], spec.instance_group.hosts)
self.assertEqual(['fake-instance1'], spec.instance_group.members)
def test_populate_group_info_missing_values(self):
filt_props = {}
spec = objects.RequestSpec()
spec._populate_group_info(filt_props)
self.assertIsNone(spec.instance_group)
def test_from_limits(self):
limits_dict = {'numa_topology': None,
'vcpu': 1.0,
'disk_gb': 1.0,
'memory_mb': 1.0}
spec = objects.RequestSpec()
spec._from_limits(limits_dict)
self.assertIsInstance(spec.limits, objects.SchedulerLimits)
self.assertIsNone(spec.limits.numa_topology)
self.assertEqual(1, spec.limits.vcpu)
self.assertEqual(1, spec.limits.disk_gb)
self.assertEqual(1, spec.limits.memory_mb)
def test_from_limits_missing_values(self):
limits_dict = {}
spec = objects.RequestSpec()
spec._from_limits(limits_dict)
self.assertIsInstance(spec.limits, objects.SchedulerLimits)
self.assertIsNone(spec.limits.numa_topology)
self.assertIsNone(spec.limits.vcpu)
self.assertIsNone(spec.limits.disk_gb)
self.assertIsNone(spec.limits.memory_mb)
def test_from_hints(self):
hints_dict = {'foo_str': '1',
'bar_list': ['2']}
spec = objects.RequestSpec()
spec._from_hints(hints_dict)
expected = {'foo_str': ['1'],
'bar_list': ['2']}
self.assertEqual(expected, spec.scheduler_hints)
def test_from_hints_with_no_hints(self):
spec = objects.RequestSpec()
spec._from_hints(None)
self.assertIsNone(spec.scheduler_hints)
@mock.patch.object(objects.SchedulerLimits, 'from_dict')
def test_from_primitives(self, mock_limits):
spec_dict = {'instance_type': objects.Flavor(),
'instance_properties': objects.Instance(
uuid=uuidutils.generate_uuid(),
numa_topology=None,
pci_requests=None,
project_id=1,
user_id=2,
availability_zone='nova')}
filt_props = {}
# We seriously don't care about the return values, we just want to make
# sure that all the fields are set
mock_limits.return_value = None
ctxt = context.RequestContext('fake', 'fake')
spec = objects.RequestSpec.from_primitives(ctxt, spec_dict, filt_props)
mock_limits.assert_called_once_with({})
# Make sure that all fields are set using that helper method
skip = ['id', 'security_groups', 'network_metadata', 'is_bfv']
for field in [f for f in spec.obj_fields if f not in skip]:
self.assertTrue(spec.obj_attr_is_set(field),
'Field: %s is not set' % field)
# just making sure that the context is set by the method
self.assertEqual(ctxt, spec._context)
def test_from_primitives_with_requested_destination(self):
destination = objects.Destination(host='foo')
spec_dict = {}
filt_props = {'requested_destination': destination}
ctxt = context.RequestContext('fake', 'fake')
spec = objects.RequestSpec.from_primitives(ctxt, spec_dict, filt_props)
self.assertEqual(destination, spec.requested_destination)
def test_from_components(self):
ctxt = context.RequestContext('fake-user', 'fake-project')
destination = objects.Destination(host='foo')
instance = fake_instance.fake_instance_obj(ctxt)
image = {'id': uuids.image_id, 'properties': {'mappings': []},
'status': 'fake-status', 'location': 'far-away'}
flavor = fake_flavor.fake_flavor_obj(ctxt)
filter_properties = {'requested_destination': destination}
instance_group = None
spec = objects.RequestSpec.from_components(ctxt, instance.uuid, image,
flavor, instance.numa_topology, instance.pci_requests,
filter_properties, instance_group, instance.availability_zone,
objects.SecurityGroupList())
# Make sure that all fields are set using that helper method
skip = ['id', 'network_metadata', 'is_bfv']
for field in [f for f in spec.obj_fields if f not in skip]:
self.assertTrue(spec.obj_attr_is_set(field),
'Field: %s is not set' % field)
# just making sure that the context is set by the method
self.assertEqual(ctxt, spec._context)
self.assertEqual(destination, spec.requested_destination)
@mock.patch('nova.objects.RequestSpec._populate_group_info')
def test_from_components_with_instance_group(self, mock_pgi):
# This test makes sure that we don't overwrite instance group passed
# to from_components
ctxt = context.RequestContext('fake-user', 'fake-project')
instance = fake_instance.fake_instance_obj(ctxt)
image = {'id': uuids.image_id, 'properties': {'mappings': []},
'status': 'fake-status', 'location': 'far-away'}
flavor = fake_flavor.fake_flavor_obj(ctxt)
filter_properties = {'fake': 'property'}
instance_group = objects.InstanceGroup()
objects.RequestSpec.from_components(ctxt, instance.uuid, image,
flavor, instance.numa_topology, instance.pci_requests,
filter_properties, instance_group, instance.availability_zone)
self.assertFalse(mock_pgi.called)
@mock.patch('nova.objects.RequestSpec._populate_group_info')
def test_from_components_without_instance_group(self, mock_pgi):
# This test makes sure that we populate instance group if not
# present
ctxt = context.RequestContext(fakes.FAKE_USER_ID,
fakes.FAKE_PROJECT_ID)
instance = fake_instance.fake_instance_obj(ctxt)
image = {'id': uuids.image_id, 'properties': {'mappings': []},
'status': 'fake-status', 'location': 'far-away'}
flavor = fake_flavor.fake_flavor_obj(ctxt)
filter_properties = {'fake': 'property'}
objects.RequestSpec.from_components(ctxt, instance.uuid, image,
flavor, instance.numa_topology, instance.pci_requests,
filter_properties, None, instance.availability_zone)
mock_pgi.assert_called_once_with(filter_properties)
@mock.patch('nova.objects.RequestSpec._populate_group_info')
def test_from_components_without_security_groups(self, mock_pgi):
# This test makes sure that we populate instance group if not
# present
ctxt = context.RequestContext(fakes.FAKE_USER_ID,
fakes.FAKE_PROJECT_ID)
instance = fake_instance.fake_instance_obj(ctxt)
image = {'id': uuids.image_id, 'properties': {'mappings': []},
'status': 'fake-status', 'location': 'far-away'}
flavor = fake_flavor.fake_flavor_obj(ctxt)
filter_properties = {'fake': 'property'}
spec = objects.RequestSpec.from_components(ctxt, instance.uuid, image,
flavor, instance.numa_topology, instance.pci_requests,
filter_properties, None, instance.availability_zone)
self.assertNotIn('security_groups', spec)
def test_from_components_with_port_resource_request(self, ):
ctxt = context.RequestContext(fakes.FAKE_USER_ID,
fakes.FAKE_PROJECT_ID)
instance = fake_instance.fake_instance_obj(ctxt)
image = {'id': uuids.image_id, 'properties': {'mappings': []},
'status': 'fake-status', 'location': 'far-away'}
flavor = fake_flavor.fake_flavor_obj(ctxt)
filter_properties = {'fake': 'property'}
rg = request_spec.RequestGroup()
spec = objects.RequestSpec.from_components(ctxt, instance.uuid, image,
flavor, instance.numa_topology, instance.pci_requests,
filter_properties, None, instance.availability_zone,
port_resource_requests=[rg])
self.assertListEqual([rg], spec.requested_resources)
def test_get_scheduler_hint(self):
spec_obj = objects.RequestSpec(scheduler_hints={'foo_single': ['1'],
'foo_mul': ['1', '2']})
self.assertEqual('1', spec_obj.get_scheduler_hint('foo_single'))
self.assertEqual(['1', '2'], spec_obj.get_scheduler_hint('foo_mul'))
self.assertIsNone(spec_obj.get_scheduler_hint('oops'))
self.assertEqual('bar', spec_obj.get_scheduler_hint('oops',
default='bar'))
def test_get_scheduler_hint_with_no_hints(self):
spec_obj = objects.RequestSpec()
self.assertEqual('bar', spec_obj.get_scheduler_hint('oops',
default='bar'))
@mock.patch.object(objects.RequestSpec, '_to_legacy_instance')
@mock.patch.object(base, 'obj_to_primitive')
def test_to_legacy_request_spec_dict(self, image_to_primitive,
spec_to_legacy_instance):
fake_image_dict = mock.Mock()
image_to_primitive.return_value = fake_image_dict
fake_instance = {'root_gb': 1.0,
'ephemeral_gb': 1.0,
'memory_mb': 1.0,
'vcpus': 1,
'numa_topology': None,
'pci_requests': None,
'project_id': fakes.FAKE_PROJECT_ID,
'availability_zone': 'nova',
'uuid': '1'}
spec_to_legacy_instance.return_value = fake_instance
fake_flavor = objects.Flavor(root_gb=10,
ephemeral_gb=0,
memory_mb=512,
vcpus=1)
spec = objects.RequestSpec(num_instances=1,
image=objects.ImageMeta(),
# instance properties
numa_topology=None,
pci_requests=None,
project_id=1,
availability_zone='nova',
instance_uuid=uuids.instance,
flavor=fake_flavor)
spec_dict = spec.to_legacy_request_spec_dict()
expected = {'num_instances': 1,
'image': fake_image_dict,
'instance_properties': fake_instance,
'instance_type': fake_flavor}
self.assertEqual(expected, spec_dict)
def test_to_legacy_request_spec_dict_with_unset_values(self):
spec = objects.RequestSpec()
self.assertEqual({'num_instances': 1,
'image': {},
'instance_properties': {},
'instance_type': {}},
spec.to_legacy_request_spec_dict())
def test_to_legacy_filter_properties_dict(self):
fake_numa_limits = objects.NUMATopologyLimits()
fake_computes_obj = objects.ComputeNodeList(
objects=[objects.ComputeNode(host='fake1',
hypervisor_hostname='node1')])
fake_dest = objects.Destination(host='fakehost')
spec = objects.RequestSpec(
ignore_hosts=['ignoredhost'],
force_hosts=['fakehost'],
force_nodes=['fakenode'],
retry=objects.SchedulerRetries(num_attempts=1,
hosts=fake_computes_obj),
limits=objects.SchedulerLimits(numa_topology=fake_numa_limits,
vcpu=1.0,
disk_gb=10.0,
memory_mb=8192.0),
instance_group=objects.InstanceGroup(hosts=['fake1'],
policy='affinity',
members=['inst1', 'inst2']),
scheduler_hints={'foo': ['bar']},
requested_destination=fake_dest)
expected = {'ignore_hosts': ['ignoredhost'],
'force_hosts': ['fakehost'],
'force_nodes': ['fakenode'],
'retry': {'num_attempts': 1,
'hosts': [['fake1', 'node1']]},
'limits': {'numa_topology': fake_numa_limits,
'vcpu': 1.0,
'disk_gb': 10.0,
'memory_mb': 8192.0},
'group_updated': True,
'group_hosts': set(['fake1']),
'group_policies': set(['affinity']),
'group_members': set(['inst1', 'inst2']),
'scheduler_hints': {'foo': 'bar'},
'requested_destination': fake_dest}
self.assertEqual(expected, spec.to_legacy_filter_properties_dict())
def test_to_legacy_filter_properties_dict_with_nullable_values(self):
spec = objects.RequestSpec(force_hosts=None,
force_nodes=None,
retry=None,
limits=None,
instance_group=None,
scheduler_hints=None)
self.assertEqual({}, spec.to_legacy_filter_properties_dict())
def test_to_legacy_filter_properties_dict_with_unset_values(self):
spec = objects.RequestSpec()
self.assertEqual({}, spec.to_legacy_filter_properties_dict())
def test_ensure_network_metadata(self):
network_a = fake_network_cache_model.new_network({
'physical_network': 'foo', 'tunneled': False})
vif_a = fake_network_cache_model.new_vif({'network': network_a})
network_b = fake_network_cache_model.new_network({
'physical_network': 'foo', 'tunneled': False})
vif_b = fake_network_cache_model.new_vif({'network': network_b})
network_c = fake_network_cache_model.new_network({
'physical_network': 'bar', 'tunneled': False})
vif_c = fake_network_cache_model.new_vif({'network': network_c})
network_d = fake_network_cache_model.new_network({
'physical_network': None, 'tunneled': True})
vif_d = fake_network_cache_model.new_vif({'network': network_d})
nw_info = network_model.NetworkInfo([vif_a, vif_b, vif_c, vif_d])
info_cache = objects.InstanceInfoCache(network_info=nw_info,
instance_uuid=uuids.instance)
instance = objects.Instance(id=3, uuid=uuids.instance,
info_cache=info_cache)
spec = objects.RequestSpec()
self.assertNotIn('network_metadata', spec)
spec.ensure_network_metadata(instance)
self.assertIn('network_metadata', spec)
self.assertIsInstance(spec.network_metadata, objects.NetworkMetadata)
self.assertEqual(spec.network_metadata.physnets, set(['foo', 'bar']))
self.assertTrue(spec.network_metadata.tunneled)
def test_ensure_network_metadata_missing(self):
nw_info = network_model.NetworkInfo([])
info_cache = objects.InstanceInfoCache(network_info=nw_info,
instance_uuid=uuids.instance)
instance = objects.Instance(id=3, uuid=uuids.instance,
info_cache=info_cache)
spec = objects.RequestSpec()
self.assertNotIn('network_metadata', spec)
spec.ensure_network_metadata(instance)
self.assertNotIn('network_metadata', spec)
@mock.patch.object(request_spec.RequestSpec,
'_get_by_instance_uuid_from_db')
@mock.patch('nova.objects.InstanceGroup.get_by_uuid')
def test_get_by_instance_uuid(self, mock_get_ig, get_by_uuid):
fake_spec = fake_request_spec.fake_db_spec()
get_by_uuid.return_value = fake_spec
mock_get_ig.return_value = objects.InstanceGroup(name='fresh')
req_obj = request_spec.RequestSpec.get_by_instance_uuid(self.context,
fake_spec['instance_uuid'])
self.assertEqual(1, req_obj.num_instances)
self.assertEqual(['host2', 'host4'], req_obj.ignore_hosts)
self.assertEqual('fake', req_obj.project_id)
self.assertEqual({'hint': ['over-there']}, req_obj.scheduler_hints)
self.assertEqual(['host1', 'host3'], req_obj.force_hosts)
self.assertIsNone(req_obj.availability_zone)
self.assertEqual(['node1', 'node2'], req_obj.force_nodes)
self.assertIsInstance(req_obj.image, objects.ImageMeta)
self.assertIsInstance(req_obj.numa_topology,
objects.InstanceNUMATopology)
self.assertIsInstance(req_obj.pci_requests,
objects.InstancePCIRequests)
self.assertIsInstance(req_obj.flavor, objects.Flavor)
# The 'retry' field is not persistent.
self.assertIsNone(req_obj.retry)
self.assertIsInstance(req_obj.limits, objects.SchedulerLimits)
self.assertIsInstance(req_obj.instance_group, objects.InstanceGroup)
self.assertEqual('fresh', req_obj.instance_group.name)
def _check_update_primitive(self, req_obj, changes):
self.assertEqual(req_obj.instance_uuid, changes['instance_uuid'])
serialized_obj = objects.RequestSpec.obj_from_primitive(
jsonutils.loads(changes['spec']))
# primitive fields
for field in ['instance_uuid', 'num_instances', 'ignore_hosts',
'project_id', 'scheduler_hints', 'force_hosts',
'availability_zone', 'force_nodes']:
self.assertEqual(getattr(req_obj, field),
getattr(serialized_obj, field))
# object fields
for field in ['image', 'numa_topology', 'pci_requests', 'flavor',
'limits', 'network_metadata']:
self.assertEqual(
getattr(req_obj, field).obj_to_primitive(),
getattr(serialized_obj, field).obj_to_primitive())
self.assertIsNone(serialized_obj.instance_group.members)
self.assertIsNone(serialized_obj.instance_group.hosts)
self.assertIsNone(serialized_obj.retry)
self.assertIsNone(serialized_obj.requested_destination)
def test_create(self):
req_obj = fake_request_spec.fake_spec_obj(remove_id=True)
def _test_create_args(self2, context, changes):
self._check_update_primitive(req_obj, changes)
# DB creation would have set an id
changes['id'] = 42
return changes
with mock.patch.object(request_spec.RequestSpec, '_create_in_db',
_test_create_args):
req_obj.create()
def test_create_id_set(self):
req_obj = request_spec.RequestSpec(self.context)
req_obj.id = 3
self.assertRaises(exception.ObjectActionError, req_obj.create)
def test_create_does_not_persist_requested_fields(self):
req_obj = fake_request_spec.fake_spec_obj(remove_id=True)
expected_network_metadata = objects.NetworkMetadata(
physnets=set(['foo', 'bar']), tunneled=True)
req_obj.network_metadata = expected_network_metadata
expected_destination = request_spec.Destination(host='sample-host')
req_obj.requested_destination = expected_destination
rg = request_spec.RequestGroup(resources={'fake-rc': 13})
req_obj.requested_resources = [rg]
expected_retry = objects.SchedulerRetries(
num_attempts=2,
hosts=objects.ComputeNodeList(objects=[
objects.ComputeNode(host='host1', hypervisor_hostname='node1'),
objects.ComputeNode(host='host2', hypervisor_hostname='node2'),
]))
req_obj.retry = expected_retry
orig_create_in_db = request_spec.RequestSpec._create_in_db
with mock.patch.object(request_spec.RequestSpec, '_create_in_db') \
as mock_create_in_db:
mock_create_in_db.side_effect = orig_create_in_db
req_obj.create()
mock_create_in_db.assert_called_once()
updates = mock_create_in_db.mock_calls[0][1][1]
# assert that the following fields are not stored in the db
# 1. network_metadata
# 2. requested_destination
# 3. requested_resources
# 4. retry
data = jsonutils.loads(updates['spec'])['nova_object.data']
self.assertNotIn('network_metadata', data)
self.assertIsNone(data['requested_destination'])
self.assertIsNone(data['requested_resources'])
self.assertIsNone(data['retry'])
self.assertIsNotNone(data['instance_uuid'])
# also we expect that the following fields are not reset after create
# 1. network_metadata
# 2. requested_destination
# 3. requested_resources
# 4. retry
self.assertIsNotNone(req_obj.network_metadata)
self.assertJsonEqual(expected_network_metadata.obj_to_primitive(),
req_obj.network_metadata.obj_to_primitive())
self.assertIsNotNone(req_obj.requested_destination)
self.assertJsonEqual(expected_destination.obj_to_primitive(),
req_obj.requested_destination.obj_to_primitive())
self.assertIsNotNone(req_obj.requested_resources)
self.assertEqual(
13, req_obj.requested_resources[0].resources['fake-rc'])
self.assertIsNotNone(req_obj.retry)
self.assertJsonEqual(expected_retry.obj_to_primitive(),
req_obj.retry.obj_to_primitive())
def test_save_does_not_persist_requested_fields(self):
req_obj = fake_request_spec.fake_spec_obj(remove_id=True)
req_obj.create()
# change something to make sure _save_in_db is called
expected_network_metadata = objects.NetworkMetadata(
physnets=set(['foo', 'bar']), tunneled=True)
req_obj.network_metadata = expected_network_metadata
expected_destination = request_spec.Destination(host='sample-host')
req_obj.requested_destination = expected_destination
rg = request_spec.RequestGroup(resources={'fake-rc': 13})
req_obj.requested_resources = [rg]
expected_retry = objects.SchedulerRetries(
num_attempts=2,
hosts=objects.ComputeNodeList(objects=[
objects.ComputeNode(host='host1', hypervisor_hostname='node1'),
objects.ComputeNode(host='host2', hypervisor_hostname='node2'),
]))
req_obj.retry = expected_retry
req_obj.num_instances = 2
orig_save_in_db = request_spec.RequestSpec._save_in_db
with mock.patch.object(request_spec.RequestSpec, '_save_in_db') \
as mock_save_in_db:
mock_save_in_db.side_effect = orig_save_in_db
req_obj.save()
mock_save_in_db.assert_called_once()
updates = mock_save_in_db.mock_calls[0][1][2]
# assert that the following fields are not stored in the db
# 1. network_metadata
# 2. requested_destination
# 3. requested_resources
# 4. retry
data = jsonutils.loads(updates['spec'])['nova_object.data']
self.assertNotIn('network_metadata', data)
self.assertIsNone(data['requested_destination'])
self.assertIsNone(data['requested_resources'])
self.assertIsNone(data['retry'])
self.assertIsNotNone(data['instance_uuid'])
# also we expect that the following fields are not reset after save
# 1. network_metadata
# 2. requested_destination
# 3. requested_resources
# 4. retry
self.assertIsNotNone(req_obj.network_metadata)
self.assertJsonEqual(expected_network_metadata.obj_to_primitive(),
req_obj.network_metadata.obj_to_primitive())
self.assertIsNotNone(req_obj.requested_destination)
self.assertJsonEqual(expected_destination.obj_to_primitive(),
req_obj.requested_destination.obj_to_primitive())
self.assertIsNotNone(req_obj.requested_resources)
self.assertEqual(13, req_obj.requested_resources[0].resources
['fake-rc'])
self.assertIsNotNone(req_obj.retry)
self.assertJsonEqual(expected_retry.obj_to_primitive(),
req_obj.retry.obj_to_primitive())
def test_save(self):
req_obj = fake_request_spec.fake_spec_obj()
# Make sure the requested_destination is not persisted since it is
# only valid per request/operation.
req_obj.requested_destination = objects.Destination(host='fake')
def _test_save_args(self2, context, instance_uuid, changes):
self._check_update_primitive(req_obj, changes)
# DB creation would have set an id
changes['id'] = 42
return changes
with mock.patch.object(request_spec.RequestSpec, '_save_in_db',
_test_save_args):
req_obj.save()
@mock.patch.object(request_spec.RequestSpec, '_destroy_in_db')
def test_destroy(self, destroy_in_db):
req_obj = fake_request_spec.fake_spec_obj()
req_obj.destroy()
destroy_in_db.assert_called_once_with(req_obj._context,
req_obj.instance_uuid)
@mock.patch.object(request_spec.RequestSpec, '_destroy_bulk_in_db')
def test_destroy_bulk(self, destroy_bulk_in_db):
uuids_to_be_deleted = []
for i in range(0, 5):
uuid = uuidutils.generate_uuid()
uuids_to_be_deleted.append(uuid)
destroy_bulk_in_db.return_value = 5
result = objects.RequestSpec.destroy_bulk(self.context,
uuids_to_be_deleted)
destroy_bulk_in_db.assert_called_once_with(self.context,
uuids_to_be_deleted)
self.assertEqual(5, result)
def test_reset_forced_destinations(self):
req_obj = fake_request_spec.fake_spec_obj()
# Making sure the fake object has forced hosts and nodes
self.assertIsNotNone(req_obj.force_hosts)
self.assertIsNotNone(req_obj.force_nodes)
with mock.patch.object(req_obj, 'obj_reset_changes') as mock_reset:
req_obj.reset_forced_destinations()
self.assertIsNone(req_obj.force_hosts)
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(
requested_destination=objects.Destination())
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['nova_object.data'])
primitive = req_obj.obj_to_primitive(target_version='1.6',
version_manifest=versions)
self.assertIn('requested_destination', primitive['nova_object.data'])
def test_compat_security_groups(self):
sgl = objects.SecurityGroupList(objects=[])
req_obj = objects.RequestSpec(security_groups=sgl)
versions = ovo_base.obj_tree_get_versions('RequestSpec')
primitive = req_obj.obj_to_primitive(target_version='1.7',
version_manifest=versions)
self.assertNotIn('security_groups', primitive['nova_object.data'])
primitive = req_obj.obj_to_primitive(target_version='1.8',
version_manifest=versions)
self.assertIn('security_groups', primitive['nova_object.data'])
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_compat_network_metadata(self):
network_metadata = objects.NetworkMetadata(physnets=set(),
tunneled=False)
req_obj = objects.RequestSpec(network_metadata=network_metadata,
user_id=fakes.FAKE_USER_ID)
versions = ovo_base.obj_tree_get_versions('RequestSpec')
primitive = req_obj.obj_to_primitive(target_version='1.9',
version_manifest=versions)
primitive = primitive['nova_object.data']
self.assertNotIn('network_metadata', primitive)
self.assertIn('user_id', primitive)
def test_compat_requested_resources(self):
req_obj = objects.RequestSpec(requested_resources=[],
instance_uuid=uuids.instance)
versions = ovo_base.obj_tree_get_versions('RequestSpec')
primitive = req_obj.obj_to_primitive(target_version='1.11',
version_manifest=versions)
primitive = primitive['nova_object.data']
self.assertNotIn('requested_resources', primitive)
self.assertIn('instance_uuid', primitive)
def test_default_requested_destination(self):
req_obj = objects.RequestSpec()
self.assertIsNone(req_obj.requested_destination)
def test_security_groups_load(self):
req_obj = objects.RequestSpec()
self.assertNotIn('security_groups', req_obj)
self.assertIsInstance(req_obj.security_groups,
objects.SecurityGroupList)
self.assertIn('security_groups', req_obj)
def test_network_requests_load(self):
req_obj = objects.RequestSpec()
self.assertNotIn('network_metadata', req_obj)
self.assertIsInstance(req_obj.network_metadata,
objects.NetworkMetadata)
self.assertIn('network_metadata', req_obj)
def test_destination_aggregates_default(self):
destination = objects.Destination()
self.assertIsNone(destination.aggregates)
def test_destination_require_aggregates(self):
destination = objects.Destination()
destination.require_aggregates(['foo', 'bar'])
destination.require_aggregates(['baz'])
self.assertEqual(['foo,bar', 'baz'], destination.aggregates)
def test_destination_1dotoh(self):
destination = objects.Destination(aggregates=['foo'])
primitive = destination.obj_to_primitive(target_version='1.0')
self.assertNotIn('aggregates', primitive['nova_object.data'])
def test_create_raises_on_unchanged_object(self):
ctxt = context.RequestContext(uuids.user_id, uuids.project_id)
req_obj = request_spec.RequestSpec(context=ctxt)
self.assertRaises(exception.ObjectActionError, req_obj.create)
def test_save_can_be_called_on_unchanged_object(self):
req_obj = fake_request_spec.fake_spec_obj(remove_id=True)
req_obj.create()
req_obj.save()
class TestRequestSpecObject(test_objects._LocalTest,
_TestRequestSpecObject):
pass
class TestRemoteRequestSpecObject(test_objects._RemoteTest,
_TestRequestSpecObject):
pass
class TestRequestGroupObject(test.TestCase):
def setUp(self):
super(TestRequestGroupObject, self).setUp()
self.user_id = uuids.user_id
self.project_id = uuids.project_id
self.context = context.RequestContext(uuids.user_id, uuids.project_id)
def test_fields_defaulted_at_create(self):
rg = request_spec.RequestGroup(self.context)
self.assertTrue(rg.use_same_provider)
self.assertEqual({}, rg.resources)
self.assertEqual(set(), rg.required_traits)
self.assertEqual(set(), rg.forbidden_traits)
self.assertEqual([], rg.aggregates)
self.assertIsNone(None, rg.requester_id)
self.assertEqual([], rg.provider_uuids)
def test_from_port_request(self):
port_resource_request = {
"resources": {
"NET_BW_IGR_KILOBIT_PER_SEC": 1000,
"NET_BW_EGR_KILOBIT_PER_SEC": 1000},
"required": ["CUSTOM_PHYSNET_2",
"CUSTOM_VNIC_TYPE_NORMAL"]
}
rg = request_spec.RequestGroup.from_port_request(
self.context, uuids.port_id, port_resource_request)
self.assertTrue(rg.use_same_provider)
self.assertEqual(
{"NET_BW_IGR_KILOBIT_PER_SEC": 1000,
"NET_BW_EGR_KILOBIT_PER_SEC": 1000},
rg.resources)
self.assertEqual({"CUSTOM_PHYSNET_2", "CUSTOM_VNIC_TYPE_NORMAL"},
rg.required_traits)
self.assertEqual(uuids.port_id, rg.requester_id)
# and the rest is defaulted
self.assertEqual(set(), rg.forbidden_traits)
self.assertEqual([], rg.aggregates)
self.assertEqual([], rg.provider_uuids)
def test_from_port_request_without_traits(self):
port_resource_request = {
"resources": {
"NET_BW_IGR_KILOBIT_PER_SEC": 1000,
"NET_BW_EGR_KILOBIT_PER_SEC": 1000}}
rg = request_spec.RequestGroup.from_port_request(
self.context, uuids.port_id, port_resource_request)
self.assertTrue(rg.use_same_provider)
self.assertEqual(
{"NET_BW_IGR_KILOBIT_PER_SEC": 1000,
"NET_BW_EGR_KILOBIT_PER_SEC": 1000},
rg.resources)
self.assertEqual(uuids.port_id, rg.requester_id)
# and the rest is defaulted
self.assertEqual(set(), rg.required_traits)
self.assertEqual(set(), rg.forbidden_traits)
self.assertEqual([], rg.aggregates)
self.assertEqual([], rg.provider_uuids)
def test_compat_requester_and_provider(self):
req_obj = objects.RequestGroup(
requester_id=uuids.requester, provider_uuids=[uuids.rp1],
required_traits=set(['CUSTOM_PHYSNET_2']))
versions = ovo_base.obj_tree_get_versions('RequestGroup')
primitive = req_obj.obj_to_primitive(
target_version='1.0',
version_manifest=versions)['nova_object.data']
self.assertNotIn('requester_id', primitive)
self.assertNotIn('provider_uuids', primitive)
self.assertIn('required_traits', primitive)
class TestMappingRequestGroupsToProviders(test.NoDBTestCase):
def setUp(self):
super(TestMappingRequestGroupsToProviders, self).setUp()
self.spec = request_spec.RequestSpec()
def test_no_groups(self):
allocations = None
provider_traits = {}
self.spec.map_requested_resources_to_providers(
allocations, provider_traits)
# we cannot assert much, at least we see that the above call doesn't
# blow
self.assertIsNone(self.spec.requested_resources)
def test_unnumbered_group_not_supported(self):
allocations = {}
provider_traits = {}
group1 = request_spec.RequestGroup(
use_same_provider=False)
self.spec.requested_resources = [group1]
self.assertRaises(
NotImplementedError,
self.spec.map_requested_resources_to_providers, allocations,
provider_traits)
def test_forbidden_traits_not_supported(self):
allocations = {}
provider_traits = {}
group1 = request_spec.RequestGroup(
forbidden_traits={'STORAGE_DISK_HDD'})
self.spec.requested_resources = [group1]
self.assertRaises(
NotImplementedError,
self.spec.map_requested_resources_to_providers, allocations,
provider_traits)
def test_aggregates_not_supported(self):
allocations = {}
provider_traits = {}
group1 = request_spec.RequestGroup(
aggregates=[[uuids.agg1]])
self.spec.requested_resources = [group1]
self.assertRaises(
NotImplementedError,
self.spec.map_requested_resources_to_providers, allocations,
provider_traits)
def test_one_group(self):
allocations = {
uuids.compute1_rp: {
"resources": {
'VCPU': 1
}
},
uuids.net_dev1_rp: {
"resources": {
'NET_BW_IGR_KILOBIT_PER_SEC': 1,
'NET_BW_EGR_KILOBIT_PER_SEC': 1,
}
}
}
provider_traits = {
uuids.compute1_rp: [],
uuids.net_dev1_rp: [
'CUSTOM_PHYSNET_PHYSNET0',
'CUSTOM_VNIC_TYPE_NORMAL'
],
}
group1 = request_spec.RequestGroup(
resources={
"NET_BW_IGR_KILOBIT_PER_SEC": 1,
"NET_BW_EGR_KILOBIT_PER_SEC": 1,
},
required_traits={
"CUSTOM_PHYSNET_PHYSNET0",
"CUSTOM_VNIC_TYPE_NORMAL",
})
self.spec.requested_resources = [group1]
self.spec.map_requested_resources_to_providers(
allocations, provider_traits)
self.assertEqual([uuids.net_dev1_rp], group1.provider_uuids)
def test_one_group_no_matching_allocation(self):
# NOTE(gibi): This negative test scenario should not happen in real
# end to end test as we assume that placement only returns candidates
# that are valid. But still we want to cover the error case in our
# implementation
allocations = {
uuids.compute1_rp: {
"resources": {
'VCPU': 1
}
},
uuids.net_dev1_rp: {
"resources": {
'NET_BW_IGR_KILOBIT_PER_SEC': 1,
}
}
}
provider_traits = {
uuids.compute1_rp: [],
uuids.net_dev1_rp: [
'CUSTOM_PHYSNET_PHYSNET0', 'CUSTOM_VNIC_TYPE_NORMAL'
],
}
group1 = request_spec.RequestGroup(
resources={
"NET_BW_IGR_KILOBIT_PER_SEC": 1,
"NET_BW_EGR_KILOBIT_PER_SEC": 1,
},
required_traits={
"CUSTOM_PHYSNET_PHYSNET0",
"CUSTOM_VNIC_TYPE_NORMAL",
})
self.spec.requested_resources = [group1]
self.assertRaises(
ValueError, self.spec.map_requested_resources_to_providers,
allocations, provider_traits)
def test_one_group_no_matching_trait(self):
# NOTE(gibi): This negative test scenario should not happen in real
# end to end test as we assume that placement only returns candidates
# that are valid. But still we want to cover the error case in our
# implementation
allocations = {
uuids.compute1_rp: {
"resources": {
'VCPU': 1
}
},
uuids.net_dev1_rp: {
"resources": {
'NET_BW_IGR_KILOBIT_PER_SEC': 1,
'NET_BW_EGR_KILOBIT_PER_SEC': 1,
}
}
}
provider_traits = {
uuids.compute1_rp: [],
uuids.net_dev1_rp: [
'CUSTOM_PHYSNET_PHYSNET1',
'CUSTOM_VNIC_TYPE_NORMAL'
],
}
group1 = request_spec.RequestGroup(
resources={
"NET_BW_IGR_KILOBIT_PER_SEC": 1,
"NET_BW_EGR_KILOBIT_PER_SEC": 1,
},
required_traits={
"CUSTOM_PHYSNET_PHYSNET0",
"CUSTOM_VNIC_TYPE_NORMAL",
})
self.spec.requested_resources = [group1]
self.assertRaises(
ValueError, self.spec.map_requested_resources_to_providers,
allocations, provider_traits)
def test_two_groups_same_provider(self):
allocations = {
uuids.compute1_rp: {
"resources": {
'VCPU': 1
}
},
uuids.net_dev1_rp: {
"resources": {
'NET_BW_IGR_KILOBIT_PER_SEC': 3,
'NET_BW_EGR_KILOBIT_PER_SEC': 3,
}
}
}
provider_traits = {
uuids.compute1_rp: [],
uuids.net_dev1_rp: [
'CUSTOM_PHYSNET_PHYSNET0',
'CUSTOM_VNIC_TYPE_NORMAL'
],
}
group1 = request_spec.RequestGroup(
resources={
"NET_BW_IGR_KILOBIT_PER_SEC": 1,
"NET_BW_EGR_KILOBIT_PER_SEC": 1,
},
required_traits={
"CUSTOM_PHYSNET_PHYSNET0",
"CUSTOM_VNIC_TYPE_NORMAL",
})
group2 = request_spec.RequestGroup(
resources={
"NET_BW_IGR_KILOBIT_PER_SEC": 2,
"NET_BW_EGR_KILOBIT_PER_SEC": 2,
},
required_traits={
"CUSTOM_PHYSNET_PHYSNET0",
"CUSTOM_VNIC_TYPE_NORMAL",
})
self.spec.requested_resources = [group1, group2]
self.spec.map_requested_resources_to_providers(
allocations, provider_traits)
self.assertEqual([uuids.net_dev1_rp], group1.provider_uuids)
self.assertEqual([uuids.net_dev1_rp], group2.provider_uuids)
def test_two_groups_different_providers(self):
# NOTE(gibi): we use OrderedDict here to make the test deterministic
allocations = collections.OrderedDict()
allocations[uuids.compute1_rp] = {
"resources": {
'VCPU': 1
}
}
allocations[uuids.net_dev1_rp] = {
"resources": {
'NET_BW_IGR_KILOBIT_PER_SEC': 2,
'NET_BW_EGR_KILOBIT_PER_SEC': 2,
}
}
allocations[uuids.net_dev2_rp] = {
"resources": {
'NET_BW_IGR_KILOBIT_PER_SEC': 1,
'NET_BW_EGR_KILOBIT_PER_SEC': 1,
}
}
provider_traits = {
uuids.compute1_rp: [],
uuids.net_dev1_rp: [
'CUSTOM_PHYSNET_PHYSNET0',
'CUSTOM_VNIC_TYPE_NORMAL'
],
uuids.net_dev2_rp: [
'CUSTOM_PHYSNET_PHYSNET0',
'CUSTOM_VNIC_TYPE_NORMAL'
],
}
group1 = request_spec.RequestGroup(
resources={
"NET_BW_IGR_KILOBIT_PER_SEC": 1,
"NET_BW_EGR_KILOBIT_PER_SEC": 1,
},
required_traits={
"CUSTOM_PHYSNET_PHYSNET0",
"CUSTOM_VNIC_TYPE_NORMAL",
})
group2 = request_spec.RequestGroup(
resources={
"NET_BW_IGR_KILOBIT_PER_SEC": 2,
"NET_BW_EGR_KILOBIT_PER_SEC": 2,
},
required_traits={
"CUSTOM_PHYSNET_PHYSNET0",
"CUSTOM_VNIC_TYPE_NORMAL",
})
self.spec.requested_resources = [group1, group2]
self.spec.map_requested_resources_to_providers(
allocations, provider_traits)
self.assertEqual([uuids.net_dev2_rp], group1.provider_uuids)
self.assertEqual([uuids.net_dev1_rp], group2.provider_uuids)
def test_two_groups_different_providers_reverse(self):
"""Similar as test_two_groups_different_providers but reorder the
groups to exercises another code path
"""
# NOTE(gibi): we use OrderedDict here to make the test deterministic
allocations = collections.OrderedDict()
allocations[uuids.compute1_rp] = {
"resources": {
'VCPU': 1
}
}
allocations[uuids.net_dev1_rp] = {
"resources": {
'NET_BW_IGR_KILOBIT_PER_SEC': 2,
'NET_BW_EGR_KILOBIT_PER_SEC': 2,
}
}
allocations[uuids.net_dev2_rp] = {
"resources": {
'NET_BW_IGR_KILOBIT_PER_SEC': 1,
'NET_BW_EGR_KILOBIT_PER_SEC': 1,
}
}
provider_traits = {
uuids.compute1_rp: [],
uuids.net_dev1_rp: [
'CUSTOM_PHYSNET_PHYSNET0', 'CUSTOM_VNIC_TYPE_NORMAL'
],
uuids.net_dev2_rp: [
'CUSTOM_PHYSNET_PHYSNET0', 'CUSTOM_VNIC_TYPE_NORMAL'
],
}
group1 = request_spec.RequestGroup(
resources={
"NET_BW_IGR_KILOBIT_PER_SEC": 2,
"NET_BW_EGR_KILOBIT_PER_SEC": 2,
},
required_traits={
"CUSTOM_PHYSNET_PHYSNET0",
"CUSTOM_VNIC_TYPE_NORMAL",
})
group2 = request_spec.RequestGroup(
resources={
"NET_BW_IGR_KILOBIT_PER_SEC": 1,
"NET_BW_EGR_KILOBIT_PER_SEC": 1,
},
required_traits={
"CUSTOM_PHYSNET_PHYSNET0",
"CUSTOM_VNIC_TYPE_NORMAL",
})
self.spec.requested_resources = [group1, group2]
self.spec.map_requested_resources_to_providers(
allocations, provider_traits)
self.assertEqual([uuids.net_dev1_rp], group1.provider_uuids)
self.assertEqual([uuids.net_dev2_rp], group2.provider_uuids)
def test_two_groups_different_providers_different_traits(self):
allocations = collections.OrderedDict()
allocations[uuids.compute1_rp] = {
"resources": {
'VCPU': 1
}
}
allocations[uuids.net_dev1_physnet1_rp] = {
"resources": {
'NET_BW_IGR_KILOBIT_PER_SEC': 1,
'NET_BW_EGR_KILOBIT_PER_SEC': 1,
}
}
allocations[uuids.net_dev2_physnet0_rp] = {
"resources": {
'NET_BW_IGR_KILOBIT_PER_SEC': 1,
'NET_BW_EGR_KILOBIT_PER_SEC': 1,
}
}
provider_traits = {
uuids.compute1_rp: [],
uuids.net_dev1_physnet1_rp: [
'CUSTOM_PHYSNET_PHYSNET1', 'CUSTOM_VNIC_TYPE_NORMAL'
],
uuids.net_dev2_physnet0_rp: [
'CUSTOM_PHYSNET_PHYSNET0', 'CUSTOM_VNIC_TYPE_NORMAL'
],
}
group1 = request_spec.RequestGroup(
resources={
"NET_BW_IGR_KILOBIT_PER_SEC": 1,
"NET_BW_EGR_KILOBIT_PER_SEC": 1,
},
required_traits={
"CUSTOM_PHYSNET_PHYSNET0",
"CUSTOM_VNIC_TYPE_NORMAL",
})
group2 = request_spec.RequestGroup(
resources={
"NET_BW_IGR_KILOBIT_PER_SEC": 1,
"NET_BW_EGR_KILOBIT_PER_SEC": 1,
},
required_traits={
"CUSTOM_PHYSNET_PHYSNET1",
"CUSTOM_VNIC_TYPE_NORMAL",
})
self.spec.requested_resources = [group1, group2]
self.spec.map_requested_resources_to_providers(
allocations, provider_traits)
self.assertEqual([uuids.net_dev2_physnet0_rp], group1.provider_uuids)
self.assertEqual([uuids.net_dev1_physnet1_rp], group2.provider_uuids)
def test_three_groups(self):
"""A complex example where a lot of mappings are tried before the
solution is found.
"""
allocations = collections.OrderedDict()
allocations[uuids.compute1_rp] = {
"resources": {
'VCPU': 1
}
}
allocations[uuids.net_dev1_rp] = {
"resources": {
'NET_BW_IGR_KILOBIT_PER_SEC': 3,
'NET_BW_EGR_KILOBIT_PER_SEC': 3,
}
}
allocations[uuids.net_dev2_rp] = {
"resources": {
'NET_BW_IGR_KILOBIT_PER_SEC': 2,
'NET_BW_EGR_KILOBIT_PER_SEC': 2,
}
}
allocations[uuids.net_dev3_rp] = {
"resources": {
'NET_BW_IGR_KILOBIT_PER_SEC': 1,
'NET_BW_EGR_KILOBIT_PER_SEC': 3,
}
}
provider_traits = {
uuids.compute1_rp: [],
uuids.net_dev1_rp: [
'CUSTOM_PHYSNET_PHYSNET0', 'CUSTOM_VNIC_TYPE_NORMAL'
],
uuids.net_dev2_rp: [
'CUSTOM_PHYSNET_PHYSNET0', 'CUSTOM_VNIC_TYPE_NORMAL'
],
uuids.net_dev3_rp: [
'CUSTOM_PHYSNET_PHYSNET0', 'CUSTOM_VNIC_TYPE_NORMAL'
],
}
# this fits to 2 RPs
group1 = request_spec.RequestGroup(
resources={
"NET_BW_IGR_KILOBIT_PER_SEC": 1,
"NET_BW_EGR_KILOBIT_PER_SEC": 3,
},
required_traits={
"CUSTOM_PHYSNET_PHYSNET0",
"CUSTOM_VNIC_TYPE_NORMAL",
})
# this fits to 2 RPs
group2 = request_spec.RequestGroup(
resources={
"NET_BW_IGR_KILOBIT_PER_SEC": 2,
"NET_BW_EGR_KILOBIT_PER_SEC": 2,
},
required_traits={
"CUSTOM_PHYSNET_PHYSNET0",
"CUSTOM_VNIC_TYPE_NORMAL",
})
# this fits to only one RPs
group3 = request_spec.RequestGroup(
resources={
"NET_BW_IGR_KILOBIT_PER_SEC": 3,
"NET_BW_EGR_KILOBIT_PER_SEC": 3,
},
required_traits={
"CUSTOM_PHYSNET_PHYSNET0",
"CUSTOM_VNIC_TYPE_NORMAL",
})
self.spec.requested_resources = [group1, group2, group3]
orig_validator = self.spec._is_valid_group_rp_mapping
with mock.patch.object(
self.spec, '_is_valid_group_rp_mapping',
side_effect=orig_validator
) as mock_validator:
self.spec.map_requested_resources_to_providers(
allocations, provider_traits)
self.assertEqual([uuids.net_dev3_rp], group1.provider_uuids)
self.assertEqual([uuids.net_dev2_rp], group2.provider_uuids)
self.assertEqual([uuids.net_dev1_rp], group3.provider_uuids)
# the algorithm tried out many possible mappings before found the
# the solution
self.assertEqual(58, mock_validator.call_count)
@mock.patch.object(request_spec.LOG, 'debug')
def test_two_groups_matches_but_allocation_leftover(self, mock_debug):
# NOTE(gibi): This negative test scenario should not happen in real
# end to end test as we assume that placement only returns candidates
# that are valid and this candidate is not valid as it provides more
# resources than the ports are requesting. Still we want to cover the
# error case in our implementation
allocations = collections.OrderedDict()
allocations[uuids.compute1_rp] = {
"resources": {
'VCPU': 1
}
}
allocations[uuids.net_dev1_physnet0_rp] = {
"resources": {
'NET_BW_IGR_KILOBIT_PER_SEC': 2,
'NET_BW_EGR_KILOBIT_PER_SEC': 2,
}
}
allocations[uuids.net_dev2_physnet0_rp] = {
"resources": {
'NET_BW_IGR_KILOBIT_PER_SEC': 1,
'NET_BW_EGR_KILOBIT_PER_SEC': 1,
}
}
provider_traits = {
uuids.compute1_rp: [],
uuids.net_dev1_physnet0_rp: [
'CUSTOM_PHYSNET_PHYSNET0', 'CUSTOM_VNIC_TYPE_NORMAL'
],
uuids.net_dev2_physnet0_rp: [
'CUSTOM_PHYSNET_PHYSNET0', 'CUSTOM_VNIC_TYPE_NORMAL'
],
}
group1 = request_spec.RequestGroup(
resources={
"NET_BW_IGR_KILOBIT_PER_SEC": 1,
"NET_BW_EGR_KILOBIT_PER_SEC": 1,
},
required_traits={
"CUSTOM_PHYSNET_PHYSNET0",
"CUSTOM_VNIC_TYPE_NORMAL",
})
group2 = request_spec.RequestGroup(
resources={
"NET_BW_IGR_KILOBIT_PER_SEC": 1,
"NET_BW_EGR_KILOBIT_PER_SEC": 1,
},
required_traits={
"CUSTOM_PHYSNET_PHYSNET0",
"CUSTOM_VNIC_TYPE_NORMAL",
})
self.spec.requested_resources = [group1, group2]
self.assertRaises(
ValueError, self.spec.map_requested_resources_to_providers,
allocations, provider_traits)
self.assertIn('allocations leftover', mock_debug.mock_calls[3][1][0])