Merge "Add transform_image_metadata request filter"
This commit is contained in:
commit
9d212738be
@ -198,7 +198,17 @@ flavor/image. If an aggregate is configured with a property with key
|
|||||||
and/or image metadata must also contain ``trait:$TRAIT_NAME=required`` to be
|
and/or image metadata must also contain ``trait:$TRAIT_NAME=required`` to be
|
||||||
eligible to be scheduled to hosts in that aggregate. More technical details
|
eligible to be scheduled to hosts in that aggregate. More technical details
|
||||||
at https://docs.openstack.org/nova/latest/reference/isolate-aggregates.html
|
at https://docs.openstack.org/nova/latest/reference/isolate-aggregates.html
|
||||||
""")
|
"""),
|
||||||
|
cfg.BoolOpt("image_metadata_prefilter",
|
||||||
|
default=False,
|
||||||
|
help="""
|
||||||
|
This setting causes the scheduler to transform well known image metadata
|
||||||
|
properties into placement required traits to filter host based on image
|
||||||
|
metadata. This feature requires host support and is currently supported by the
|
||||||
|
following compute drivers:
|
||||||
|
|
||||||
|
- ``libvirt.LibvirtDriver`` (since Ussuri (21.0.0))
|
||||||
|
"""),
|
||||||
]
|
]
|
||||||
|
|
||||||
filter_scheduler_group = cfg.OptGroup(name="filter_scheduler",
|
filter_scheduler_group = cfg.OptGroup(name="filter_scheduler",
|
||||||
|
@ -193,6 +193,47 @@ def require_image_type_support(ctxt, request_spec):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@trace_request_filter
|
||||||
|
def transform_image_metadata(ctxt, request_spec):
|
||||||
|
"""Transform image metadata to required traits.
|
||||||
|
|
||||||
|
This will modify the request_spec to request hosts that support
|
||||||
|
virtualisation capabilities based on the image metadata properties.
|
||||||
|
"""
|
||||||
|
if not CONF.scheduler.image_metadata_prefilter:
|
||||||
|
return False
|
||||||
|
|
||||||
|
prefix_map = {
|
||||||
|
'hw_cdrom_bus': 'COMPUTE_STORAGE_BUS',
|
||||||
|
'hw_disk_bus': 'COMPUTE_STORAGE_BUS',
|
||||||
|
'hw_video_model': 'COMPUTE_GRAPHICS_MODEL',
|
||||||
|
'hw_vif_model': 'COMPUTE_NET_VIF_MODEL',
|
||||||
|
}
|
||||||
|
|
||||||
|
trait_names = []
|
||||||
|
|
||||||
|
for key, prefix in prefix_map.items():
|
||||||
|
if key in request_spec.image.properties:
|
||||||
|
value = request_spec.image.properties.get(key).replace(
|
||||||
|
'-', '_').upper()
|
||||||
|
trait_name = f'{prefix}_{value}'
|
||||||
|
if not hasattr(os_traits, trait_name):
|
||||||
|
LOG.error(('Computed trait name %r is not valid; '
|
||||||
|
'is os-traits up to date?'), trait_name)
|
||||||
|
return False
|
||||||
|
|
||||||
|
trait_names.append(trait_name)
|
||||||
|
|
||||||
|
for trait_name in trait_names:
|
||||||
|
LOG.debug(
|
||||||
|
'transform_image_metadata request filter added required '
|
||||||
|
'trait %s', trait_name
|
||||||
|
)
|
||||||
|
request_spec.root_required.add(trait_name)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
@trace_request_filter
|
@trace_request_filter
|
||||||
def compute_status_filter(ctxt, request_spec):
|
def compute_status_filter(ctxt, request_spec):
|
||||||
"""Pre-filter compute node resource providers using COMPUTE_STATUS_DISABLED
|
"""Pre-filter compute node resource providers using COMPUTE_STATUS_DISABLED
|
||||||
@ -215,6 +256,7 @@ ALL_REQUEST_FILTERS = [
|
|||||||
require_image_type_support,
|
require_image_type_support,
|
||||||
compute_status_filter,
|
compute_status_filter,
|
||||||
isolate_aggregates,
|
isolate_aggregates,
|
||||||
|
transform_image_metadata,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,11 +12,13 @@
|
|||||||
|
|
||||||
import mock
|
import mock
|
||||||
import os_traits as ot
|
import os_traits as ot
|
||||||
|
|
||||||
from oslo_utils.fixture import uuidsentinel as uuids
|
from oslo_utils.fixture import uuidsentinel as uuids
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
|
|
||||||
from nova import context as nova_context
|
from nova import context as nova_context
|
||||||
from nova import exception
|
from nova import exception
|
||||||
|
from nova.network import model as network_model
|
||||||
from nova import objects
|
from nova import objects
|
||||||
from nova.scheduler import request_filter
|
from nova.scheduler import request_filter
|
||||||
from nova import test
|
from nova import test
|
||||||
@ -400,3 +402,36 @@ class TestRequestFilter(test.NoDBTestCase):
|
|||||||
log_lines = [c[0][0] for c in mock_log.debug.call_args_list]
|
log_lines = [c[0][0] for c in mock_log.debug.call_args_list]
|
||||||
self.assertIn('added forbidden trait', log_lines[0])
|
self.assertIn('added forbidden trait', log_lines[0])
|
||||||
self.assertIn('took %.1f seconds', log_lines[1])
|
self.assertIn('took %.1f seconds', log_lines[1])
|
||||||
|
|
||||||
|
@mock.patch.object(request_filter, 'LOG', new=mock.Mock())
|
||||||
|
def test_transform_image_metadata(self):
|
||||||
|
self.flags(image_metadata_prefilter=True, group='scheduler')
|
||||||
|
properties = objects.ImageMetaProps(
|
||||||
|
hw_disk_bus=objects.fields.DiskBus.SATA,
|
||||||
|
hw_cdrom_bus=objects.fields.DiskBus.IDE,
|
||||||
|
hw_video_model=objects.fields.VideoModel.QXL,
|
||||||
|
hw_vif_model=network_model.VIF_MODEL_VIRTIO
|
||||||
|
)
|
||||||
|
reqspec = objects.RequestSpec(
|
||||||
|
image=objects.ImageMeta(properties=properties),
|
||||||
|
flavor=objects.Flavor(extra_specs={}),
|
||||||
|
)
|
||||||
|
self.assertTrue(
|
||||||
|
request_filter.transform_image_metadata(None, reqspec)
|
||||||
|
)
|
||||||
|
expected = {
|
||||||
|
'COMPUTE_GRAPHICS_MODEL_QXL',
|
||||||
|
'COMPUTE_NET_VIF_MODEL_VIRTIO',
|
||||||
|
'COMPUTE_STORAGE_BUS_IDE',
|
||||||
|
'COMPUTE_STORAGE_BUS_SATA',
|
||||||
|
}
|
||||||
|
self.assertEqual(expected, reqspec.root_required)
|
||||||
|
|
||||||
|
def test_transform_image_metadata__disabled(self):
|
||||||
|
self.flags(image_metadata_prefilter=False, group='scheduler')
|
||||||
|
reqspec = objects.RequestSpec(flavor=objects.Flavor(extra_specs={}))
|
||||||
|
# Assert that we completely skip the filter if disabled
|
||||||
|
self.assertFalse(
|
||||||
|
request_filter.transform_image_metadata(self.context, reqspec)
|
||||||
|
)
|
||||||
|
self.assertEqual(set(), reqspec.root_required)
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
A new image metadata prefilter has been added to allow translation of
|
||||||
|
hypervisor-specific device model requests to standard traits. When this
|
||||||
|
feature is enabled, nova is able to utilize placement to select hosts that
|
||||||
|
are capable of emulating the requested devices, avoiding hosts that
|
||||||
|
could not support the request. This feature is currently supported by the
|
||||||
|
libvirt driver and can be enabled by configuring the
|
||||||
|
``[scheduler]/image_metadata_prefilter`` to ``True`` in the controller
|
||||||
|
``nova.conf``.
|
Loading…
Reference in New Issue
Block a user