diff --git a/doc/source/admin/scheduling.rst b/doc/source/admin/scheduling.rst index 11fc8e0232..ea68cc1b72 100644 --- a/doc/source/admin/scheduling.rst +++ b/doc/source/admin/scheduling.rst @@ -1086,6 +1086,24 @@ A positive value will favor hosts with the same image properties (packing strategy) while a negative value will follow a spread strategy that will favor hosts not already having instances with those image properties. +You can also use +:oslo.config:option:`filter_scheduler.image_props_weight_setting` config option +for defining the exact properties you would like to weigh. For example, if you +configure ``os_distro`` to 2 and ``hw_machine_type`` to 0, then the latter +property won't be weighed while the former will count twice. Say you configure +``os_distro=10,os_secure_boot=1,os_require_quiesce=0``, then when requesting +an instance with an image using those properties, then, for each of the +instances already running on the host having an image using at least one of +those properties, a match of the same ``os_distro`` value (eg. ``windows`` or +``linux``) would count 10 times more than a match of the same +``os_secure_boot`` value (eg. ``true`` or ``false``), while any matches about +the same ``os_require_quiesce`` value wouldn't count. +If you define :oslo.config:option:`filter_scheduler.image_props_weight_setting` +then any property from the image used for booting wouldn't be counted if not +provided in the option value. +The resulted host weight would then be multiplied by the value of +:oslo.config:option:`filter_scheduler.image_props_weight_multiplier`. + Utilization-aware scheduling ---------------------------- diff --git a/nova/conf/scheduler.py b/nova/conf/scheduler.py index c0a3ec2118..c47170e400 100644 --- a/nova/conf/scheduler.py +++ b/nova/conf/scheduler.py @@ -589,6 +589,53 @@ Example: Related options: * ``[filter_scheduler] weight_classes`` +"""), + cfg.ListOpt("image_props_weight_setting", + default=[], + help=""" +Mapping of image properties to weight modifier. + +This setting specifies the properties to be weighed and the relative ratios for +each property. This should be a list of key/value pairs, consisting of a series +of one or more 'name=ratio' pairs, separated by commas, where ``name`` is the +name of the property to be weighed, and ``ratio`` is the relative weight for +that metric. + +Note that if the ratio is set to 0, the property value is ignored, and instead +the weight will be set to the value of the +``[filter_scheduler] image_props_weight_multiplier`` option. + +As an example, let's consider the case where this option is set to: + + ``os_distro=1, hw_machine_type=-1`` + +If an instance would boot with an image having ``os_distro=windows`` and +``hw_machine_type=q35``, the final host weight will be: + + ``(nb_inst(``os_distro=windows``) * 1.0) + + (nb_inst(``hw_machine_type=q35``) * -1)`` + +where nb_inst(``prop=value``) would give me the number of instances having +an image where ``prop`` is set to ``value`` (eg. the number of instances +running with ``os_distro=windows``) + +Possible values: + +* A list of zero or more key/value pairs separated by commas, where the key is + a string representing the name of a property and the value is a numeric + weight for that property. If any value is set to 0, the number of instances + match is ignored for that specific property key. + If no key/value pairs are provided, then the weigher will compare all the + instance's images with the requested image properties, all of them weighed + evenly. + + +The overall host weight will be multiplied by the value of the + ``[filter_scheduler] image_props_weight_multiplier`` option. + +Related options: + +* ``[filter_scheduler] image_props_weight_multiplier`` """), cfg.FloatOpt("pci_weight_multiplier", default=1.0, diff --git a/nova/scheduler/weights/image_props.py b/nova/scheduler/weights/image_props.py index 856b681af9..1a3835b841 100644 --- a/nova/scheduler/weights/image_props.py +++ b/nova/scheduler/weights/image_props.py @@ -30,6 +30,14 @@ CONF = nova.conf.CONF class ImagePropertiesWeigher(weights.BaseHostWeigher): + def __init__(self): + self._parse_setting() + + def _parse_setting(self): + self.setting = dict(utils.parse_options( + CONF.filter_scheduler.image_props_weight_setting, + sep='=', converter=float, + name="filter_scheduler.image_props_weight_setting")) def weight_multiplier(self, host_state): """Override the weight multiplier.""" @@ -81,6 +89,13 @@ class ImagePropertiesWeigher(weights.BaseHostWeigher): common_props = requested_props & set(existing_props) - for prop in common_props: - weight += existing_props.count(prop) + for (prop, value) in common_props: + if self.setting: + # Calculate the weigh for each property by what was set + # If it wasn't defined, then don't weigh this property. + weight += self.setting.get( + prop, 0.0) * existing_props.count((prop, value)) + else: + # By default, all properties are weighed evenly. + weight += existing_props.count((prop, value)) return weight diff --git a/nova/tests/unit/scheduler/weights/test_weights_image_props.py b/nova/tests/unit/scheduler/weights/test_weights_image_props.py index 9cbdd1adaa..c32ef96daf 100644 --- a/nova/tests/unit/scheduler/weights/test_weights_image_props.py +++ b/nova/tests/unit/scheduler/weights/test_weights_image_props.py @@ -152,3 +152,45 @@ class ImagePropertiesWeigherTestCase(test.NoDBTestCase): self.assertEqual('host3', weights[0].obj.host) mock_fm.assert_has_calls([mock.call(), mock.call(), mock.call(), mock.call()]) + + @mock.patch('nova.objects.InstanceList.fill_metadata') + def test_multiplier_per_property(self, mock_fm): + self.flags(image_props_weight_multiplier=1.0, group='filter_scheduler') + hostinfo_list = self._get_all_hosts() + + # For now, don't exclude any property check and boot with an image + # using both hw_machine_type and os_distro properties. + weights = self.weight_handler.get_weighed_objects( + self.weighers, hostinfo_list, + weighing_properties=objects.RequestSpec(image=PROP_LIN_PC)) + # host3 is preferred as it has both of the correct properties with + # the right values. + # host2 and host4 only support one of each property. + expected_weights = [{'weight': 1.0, 'host': 'host3'}, + {'weight': (1 / 3), 'host': 'host2'}, + {'weight': (1 / 3), 'host': 'host4'}, + {'weight': 0.0, 'host': 'host1'}] + self.assertEqual(expected_weights, [weigh.to_dict() + for weigh in weights]) + self.assertEqual('host3', weights[0].obj.host) + + # Now, let's exclude hw_machine_type property to be weighed. + self.flags(image_props_weight_setting=['os_distro=1', + 'hw_machine_type=0'], + group='filter_scheduler') + # Force a refresh of the settings since we updated them + self.weighers[0]._parse_setting() + weights = self.weight_handler.get_weighed_objects( + self.weighers, hostinfo_list, + weighing_properties=objects.RequestSpec(image=PROP_LIN_PC)) + # host3 and host4 have instances with linux distro but we favor + # host3 given he has more instances having the same requested property + expected_weights = [{'weight': 1.0, 'host': 'host3'}, + {'weight': 0.5, 'host': 'host4'}, + {'weight': 0.0, 'host': 'host1'}, + {'weight': 0.0, 'host': 'host2'}] + self.assertEqual(expected_weights, [weigh.to_dict() + for weigh in weights]) + self.assertEqual('host3', weights[0].obj.host) + mock_fm.assert_has_calls([mock.call(), mock.call(), + mock.call(), mock.call()]) diff --git a/releasenotes/notes/bp-image-metadata-props-weigher-b09125e1837428f5.yaml b/releasenotes/notes/bp-image-metadata-props-weigher-b09125e1837428f5.yaml index 6f6aa4de41..388fd988c1 100644 --- a/releasenotes/notes/bp-image-metadata-props-weigher-b09125e1837428f5.yaml +++ b/releasenotes/notes/bp-image-metadata-props-weigher-b09125e1837428f5.yaml @@ -11,4 +11,8 @@ features: hosts, modify `image_props_weight_multiplier` to a positive value. If you want to spread instances with the same properties around all hosts, then please modify `image_props_weight_multiplier` to a negative value. - + Another configuration option + `[filter_scheduler]/image_props_weight_setting` allows you to define + fine-grained weights for each of the properties you actually would like to + weigh (eg. `os_distro`). Please refer to the documentation for more details + about how to use this configuration option.