diff --git a/doc/source/admin/scheduling.rst b/doc/source/admin/scheduling.rst index 5fb811ee6e80..b44fa0ea358b 100644 --- a/doc/source/admin/scheduling.rst +++ b/doc/source/admin/scheduling.rst @@ -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 ``HostA`` is not part of the tenant-isolated aggregate. -.. note:: - - There is a `known limitation - `_ 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`` diff --git a/nova/scheduler/filters/aggregate_multitenancy_isolation.py b/nova/scheduler/filters/aggregate_multitenancy_isolation.py index e74294ad7f6c..533d8b3f4042 100644 --- a/nova/scheduler/filters/aggregate_multitenancy_isolation.py +++ b/nova/scheduler/filters/aggregate_multitenancy_isolation.py @@ -31,20 +31,24 @@ class AggregateMultiTenancyIsolation(filters.BaseHostFilter): RUN_ON_REBUILD = False def host_passes(self, host_state, spec_obj): - """If a host is in an aggregate that has the metadata key - "filter_tenant_id" it can only create instances from that tenant(s). - A host can be in different aggregates. + """If a host is in an aggregate that has the metadata key is prefixed + with "filter_tenant_id" it can only create instances from that + tenant(s). A host can be in different aggregates. 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 - metadata = utils.aggregate_metadata_get_by_host(host_state, - key="filter_tenant_id") + metadata = utils.aggregate_metadata_get_by_host(host_state) 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 tenant_id not in configured_tenant_ids: LOG.debug("%s fails tenant id on aggregate", host_state) diff --git a/nova/tests/unit/scheduler/filters/test_aggregate_multitenancy_isolation_filters.py b/nova/tests/unit/scheduler/filters/test_aggregate_multitenancy_isolation_filters.py index 7f2f75a5bd95..2e7eae8d97fb 100644 --- a/nova/tests/unit/scheduler/filters/test_aggregate_multitenancy_isolation_filters.py +++ b/nova/tests/unit/scheduler/filters/test_aggregate_multitenancy_isolation_filters.py @@ -63,3 +63,30 @@ class TestAggregateMultitenancyIsolationFilter(test.NoDBTestCase): context=mock.sentinel.ctx, project_id='my_tenantid') host = fakes.FakeHostState('host1', 'compute', {}) 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)) diff --git a/releasenotes/notes/remove-filter-tenant-id-limitation-7aae698b02ec3bc2.yaml b/releasenotes/notes/remove-filter-tenant-id-limitation-7aae698b02ec3bc2.yaml new file mode 100644 index 000000000000..fdeecda73182 --- /dev/null +++ b/releasenotes/notes/remove-filter-tenant-id-limitation-7aae698b02ec3bc2.yaml @@ -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.