Merge "scheduler: AggregateMultitenancyIsolation to support unlimited tenant"
This commit is contained in:
@@ -296,15 +296,6 @@ as candidates during scheduling. A server create request from another tenant
|
|||||||
``Y`` will result in only ``HostA`` being a scheduling candidate since
|
``Y`` will result in only ``HostA`` being a scheduling candidate since
|
||||||
``HostA`` is not part of the tenant-isolated aggregate.
|
``HostA`` is not part of the tenant-isolated aggregate.
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
There is a `known limitation
|
|
||||||
<https://bugs.launchpad.net/nova/+bug/1802111>`_ with the number of tenants
|
|
||||||
that can be isolated per aggregate using this filter. This limitation does
|
|
||||||
not exist, however, for the :ref:`tenant-isolation-with-placement`
|
|
||||||
filtering capability added in the 18.0.0 Rocky release.
|
|
||||||
|
|
||||||
|
|
||||||
.. _AggregateNumInstancesFilter:
|
.. _AggregateNumInstancesFilter:
|
||||||
|
|
||||||
``AggregateNumInstancesFilter``
|
``AggregateNumInstancesFilter``
|
||||||
|
|||||||
@@ -31,20 +31,24 @@ class AggregateMultiTenancyIsolation(filters.BaseHostFilter):
|
|||||||
RUN_ON_REBUILD = False
|
RUN_ON_REBUILD = False
|
||||||
|
|
||||||
def host_passes(self, host_state, spec_obj):
|
def host_passes(self, host_state, spec_obj):
|
||||||
"""If a host is in an aggregate that has the metadata key
|
"""If a host is in an aggregate that has the metadata key is prefixed
|
||||||
"filter_tenant_id" it can only create instances from that tenant(s).
|
with "filter_tenant_id" it can only create instances from that
|
||||||
A host can be in different aggregates.
|
tenant(s). A host can be in different aggregates.
|
||||||
|
|
||||||
If a host doesn't belong to an aggregate with the metadata key
|
If a host doesn't belong to an aggregate with the metadata key
|
||||||
"filter_tenant_id" it can create instances from all tenants.
|
prefixed with "filter_tenant_id" The filter keeps non-specified
|
||||||
|
tenants out of an aggregate that has restrictions, but allows anyone
|
||||||
|
into unrestricted aggregates.
|
||||||
"""
|
"""
|
||||||
tenant_id = spec_obj.project_id
|
tenant_id = spec_obj.project_id
|
||||||
|
|
||||||
metadata = utils.aggregate_metadata_get_by_host(host_state,
|
metadata = utils.aggregate_metadata_get_by_host(host_state)
|
||||||
key="filter_tenant_id")
|
|
||||||
|
|
||||||
if metadata != {}:
|
if metadata != {}:
|
||||||
configured_tenant_ids = metadata.get("filter_tenant_id")
|
configured_tenant_ids = set()
|
||||||
|
for name, values in metadata.items():
|
||||||
|
if name.startswith("filter_tenant_id"):
|
||||||
|
configured_tenant_ids.update(set(values))
|
||||||
if configured_tenant_ids:
|
if configured_tenant_ids:
|
||||||
if tenant_id not in configured_tenant_ids:
|
if tenant_id not in configured_tenant_ids:
|
||||||
LOG.debug("%s fails tenant id on aggregate", host_state)
|
LOG.debug("%s fails tenant id on aggregate", host_state)
|
||||||
|
|||||||
@@ -63,3 +63,30 @@ class TestAggregateMultitenancyIsolationFilter(test.NoDBTestCase):
|
|||||||
context=mock.sentinel.ctx, project_id='my_tenantid')
|
context=mock.sentinel.ctx, project_id='my_tenantid')
|
||||||
host = fakes.FakeHostState('host1', 'compute', {})
|
host = fakes.FakeHostState('host1', 'compute', {})
|
||||||
self.assertTrue(self.filt_cls.host_passes(host, spec_obj))
|
self.assertTrue(self.filt_cls.host_passes(host, spec_obj))
|
||||||
|
|
||||||
|
def test_aggregate_multi_tenancy_isolation_with_prefix(self,
|
||||||
|
agg_mock):
|
||||||
|
agg_mock.return_value = {
|
||||||
|
'filter_tenant_id': set(['my_tenantid']),
|
||||||
|
'filter_tenant_id_gr1': set([
|
||||||
|
'my_tenantid1', 'my_tenantid2']),
|
||||||
|
'filter_tenant_id_gr2': set([
|
||||||
|
'my_tenantid5', 'my_tenantid4'])}
|
||||||
|
|
||||||
|
# my_tenantid should pass considering previous behavior.
|
||||||
|
spec_obj = objects.RequestSpec(
|
||||||
|
context=mock.sentinel.ctx, project_id='my_tenantid')
|
||||||
|
host = fakes.FakeHostState('host1', 'compute', {})
|
||||||
|
self.assertTrue(self.filt_cls.host_passes(host, spec_obj))
|
||||||
|
|
||||||
|
# my_tenantid2 should pass considering new behavior.
|
||||||
|
spec_obj = objects.RequestSpec(
|
||||||
|
context=mock.sentinel.ctx, project_id='my_tenantid2')
|
||||||
|
host = fakes.FakeHostState('host1', 'compute', {})
|
||||||
|
self.assertTrue(self.filt_cls.host_passes(host, spec_obj))
|
||||||
|
|
||||||
|
# my_tenantid6 still should not pass
|
||||||
|
spec_obj = objects.RequestSpec(
|
||||||
|
context=mock.sentinel.ctx, project_id='my_tenantid6')
|
||||||
|
host = fakes.FakeHostState('host1', 'compute', {})
|
||||||
|
self.assertFalse(self.filt_cls.host_passes(host, spec_obj))
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
This release removes the limit of the number of tenants that can
|
||||||
|
be specified for an aggregate and honored by the
|
||||||
|
`AggregateMultitenancyIsolation` filter. It now respects multiple
|
||||||
|
keys prefixed by `filter_tenant_id` like the request filter
|
||||||
|
implementation. You can use `filter_tenant_id` as a prefix to set
|
||||||
|
an infinite number of properties for tenant IDs on the aggregate.
|
||||||
|
This change has been implemented in a manner that preserves
|
||||||
|
backward compatibility. Existing configurations using
|
||||||
|
`filter_tenant_id` will continue to function as expected.
|
||||||
Reference in New Issue
Block a user