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:
parent
fb0b785169
commit
2c51688558
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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={
|
||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user