Parse forbidden in extra_specs

Accept forbidden traits in the processing of extra_specs, with the
format of:

      trait:CUSTOM_MAGIC=forbidden

This will be transformed into required=!CUSTOM_MAGIC when the traits
are assembled into a request to GET /allocation_candidates.

Implements blueprint forbidden-traits-in-nova

Change-Id: I31e609aef47d2fea03f279e4bfdd30f072d062b4
This commit is contained in:
Chris Dent 2018-04-16 17:55:46 +01:00
parent fb0b785169
commit 2c51688558
6 changed files with 76 additions and 14 deletions

View File

@ -632,9 +632,26 @@ Required traits
The scheduler will pass required traits to the
``GET /allocation_candidates`` endpoint in the Placement API to include
only resource providers that can satisfy the required traits. Currently
the only valid value is ``required``. Any other value will be considered
only resource providers that can satisfy the required traits. In 17.0.0
the only valid value is ``required``. In 18.0.0 ``forbidden`` is added (see
below). Any other value will be considered
invalid.
The FilterScheduler is currently the only scheduler driver that supports
this feature.
Forbidden traits
Added in the 18.0.0 Rocky release.
Forbidden traits are similar to required traits, described above, but
instead of specifying the set of traits that must be satisfied by a compute
node, forbidden traits must **not** be present.
The syntax of the extra spec is ``trait:<trait_name>=forbidden``, for
example:
- trait:HW_CPU_X86_AVX2=forbidden
- trait:STORAGE_DISK_SSD=forbidden
The FilterScheduler is currently the only scheduler driver that supports
this feature.

View File

@ -339,6 +339,7 @@ class SchedulerReportClient(object):
# and traits in the query string (via a new method on ResourceRequest).
res = resources.get_request_group(None).resources
required_traits = resources.get_request_group(None).required_traits
forbidden_traits = resources.get_request_group(None).forbidden_traits
aggregates = resources.get_request_group(None).member_of
resource_query = ",".join(
@ -350,6 +351,14 @@ class SchedulerReportClient(object):
}
if required_traits:
qs_params['required'] = ",".join(required_traits)
if forbidden_traits:
# Sorted to make testing easier to manage and for
# predictability.
forbiddens = ',!'.join(sorted(forbidden_traits))
if qs_params['required']:
qs_params['required'] += ',!' + forbiddens
else:
qs_params['required'] = '!' + forbiddens
if aggregates:
# NOTE(danms): In 1.21, placement cannot take an AND'd
# set of aggregates, only an OR'd set. Thus, if we have

View File

@ -85,17 +85,20 @@ class ResourceRequest(object):
self.get_request_group(groupid).resources[rclass] = amount
def _add_trait(self, groupid, trait_name, trait_type):
# Currently the only valid value for a trait entry is 'required'.
trait_vals = ('required',)
# Ensure the value is supported.
if trait_type not in trait_vals:
# Currently the only valid values for a trait entry are 'required'
# and 'forbidden'
trait_vals = ('required', 'forbidden')
if trait_type == 'required':
self.get_request_group(groupid).required_traits.add(trait_name)
elif trait_type == 'forbidden':
self.get_request_group(groupid).forbidden_traits.add(trait_name)
else:
LOG.warning(
"Only (%(tvals)s) traits are supported. Received '%(val)s' "
"for key trait%(groupid)s.",
{"tvals": ', '.join(trait_vals), "groupid": groupid,
"val": trait_type})
return
self.get_request_group(groupid).required_traits.add(trait_name)
return
@classmethod
def from_extra_specs(cls, extra_specs):

View File

@ -1421,14 +1421,18 @@ class TestProviderOperations(SchedulerReportClientTestCase):
'resources1:DISK_GB': '30',
'trait:CUSTOM_TRAIT1': 'required',
'trait:CUSTOM_TRAIT2': 'preferred',
'trait:CUSTOM_TRAIT3': 'forbidden',
'trait:CUSTOM_TRAIT4': 'forbidden',
})
resources.get_request_group(None).member_of = [
('agg1', 'agg2', 'agg3'), ('agg1', 'agg2')]
expected_path = '/allocation_candidates'
expected_query = {'resources': ['MEMORY_MB:1024,VCPU:1'],
'required': ['CUSTOM_TRAIT1'],
'member_of': ['in:agg1,agg2'],
'limit': ['1000']}
expected_query = {
'resources': ['MEMORY_MB:1024,VCPU:1'],
'required': ['CUSTOM_TRAIT1,!CUSTOM_TRAIT3,!CUSTOM_TRAIT4'],
'member_of': ['in:agg1,agg2'],
'limit': ['1000']
}
resp_mock.json.return_value = json_data
self.ks_adap_mock.get.return_value = resp_mock

View File

@ -373,6 +373,7 @@ class TestUtils(test.NoDBTestCase):
# Key skipped because no colons
'nocolons': '42',
'trait:CUSTOM_MAGIC': 'required',
'trait:CUSTOM_BRONZE': 'forbidden',
# Resource skipped because invalid resource class name
'resources86:CUTSOM_MISSPELLED': '86',
'resources1:SRIOV_NET_VF': '1',
@ -384,6 +385,7 @@ class TestUtils(test.NoDBTestCase):
# Trait skipped because unsupported value
'trait86:CUSTOM_GOLD': 'preferred',
'trait1:CUSTOM_PHYSNET_NET1': 'required',
'trait1:CUSTOM_PHYSNET_NET2': 'forbidden',
'resources2:SRIOV_NET_VF': '1',
'resources2:IPV4_ADDRESS': '2',
'trait2:CUSTOM_PHYSNET_NET2': 'required',
@ -405,7 +407,10 @@ class TestUtils(test.NoDBTestCase):
required_traits={
'HW_CPU_X86_AVX',
'CUSTOM_MAGIC',
}
},
forbidden_traits={
'CUSTOM_BRONZE',
},
)
expected._rg_by_id['1'] = plib.RequestGroup(
resources={
@ -414,7 +419,10 @@ class TestUtils(test.NoDBTestCase):
},
required_traits={
'CUSTOM_PHYSNET_NET1',
}
},
forbidden_traits={
'CUSTOM_PHYSNET_NET2',
},
)
expected._rg_by_id['2'] = plib.RequestGroup(
resources={

View File

@ -0,0 +1,21 @@
---
features:
- |
Added support for forbidden traits to the scheduler. A flavor extra spec
is extended to support specifying the forbidden traits. The syntax of
extra spec is ``trait:<trait_name>=forbidden``, for example:
- trait:HW_CPU_X86_AVX2=forbidden
- trait:STORAGE_DISK_SSD=forbidden
The scheduler will pass the forbidden traits to the
``GET /allocation_candidates`` endpoint in the Placement API to include
only resource providers that do not include the forbidden traits. Currently
the only valid values are ``required`` and ``forbidden``. Any other values
will be considered invalid.
This requires that the Placement API version 1.22 is available before
the ``nova-scheduler`` service can use this feature.
The FilterScheduler is currently the only scheduler driver that supports
this feature.