Merge "Fix docs and add functional test for AggregateMultiTenancyIsolation"

This commit is contained in:
Zuul 2018-09-17 11:31:15 +00:00 committed by Gerrit Code Review
commit 82270cc261
2 changed files with 107 additions and 8 deletions

View File

@ -213,14 +213,20 @@ use this filter, see :ref:`host-aggregates`. See also :ref:`IoOpsFilter`.
AggregateMultiTenancyIsolation
------------------------------
Ensures that the tenant (or list of tenants) creates all instances only on
specific :ref:`host-aggregates`. If a host is in an aggregate that has the
``filter_tenant_id`` metadata key, the host creates instances from only that
tenant or list of tenants. A host can be in different aggregates. If a host
does not belong to an aggregate with the metadata key, the host can create
instances from all tenants. This setting does not isolate the aggregate from
other tenants. Any other tenant can continue to build instances on the
specified aggregate.
Ensures hosts in tenant-isolated :ref:`host-aggregates` will only be available
to a specified set of tenants. If a host is in an aggregate that has the
``filter_tenant_id`` metadata key, the host can build instances from only that
tenant or comma-separated list of tenants. A host can be in different
aggregates. If a host does not belong to an aggregate with the metadata key,
the host can build instances from all tenants. This does not restrict the
tenant from creating servers on hosts outside the tenant-isolated aggregate.
For example, consider there are two available hosts for scheduling, HostA and
HostB. HostB is in an aggregate isolated to tenant X. A server create request
from tenant X will result in either HostA *or* HostB 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.
AggregateNumInstancesFilter
---------------------------

View File

@ -12,6 +12,8 @@
import time
from oslo_utils.fixture import uuidsentinel as uuids
from nova.scheduler.client import report
import nova.conf
@ -22,6 +24,7 @@ from nova.tests import fixtures as nova_fixtures
from nova.tests.functional import integrated_helpers
import nova.tests.unit.image.fake
from nova.tests.unit import policy_fixture
from nova import utils
from nova.virt import fake
CONF = nova.conf.CONF
@ -363,3 +366,93 @@ class TestAggregateFiltersTogether(AggregateRequestFiltersTest):
self.assertIsNone(self._get_instance_host(server))
server = self.api.get_server(server['id'])
self.assertEqual('ERROR', server['status'])
class TestAggregateMultiTenancyIsolationFilter(
test.TestCase, integrated_helpers.InstanceHelperMixin):
def _start_compute(self, host):
fake.set_nodes([host])
self.addCleanup(fake.restore_nodes)
self.start_service('compute', host=host)
def setUp(self):
super(TestAggregateMultiTenancyIsolationFilter, self).setUp()
# Stub out glance, placement and neutron.
nova.tests.unit.image.fake.stub_out_image_service(self)
self.addCleanup(nova.tests.unit.image.fake.FakeImageService_reset)
self.useFixture(nova_fixtures.PlacementFixture())
self.useFixture(nova_fixtures.NeutronFixture(self))
# Start nova services.
self.start_service('conductor')
self.admin_api = self.useFixture(
nova_fixtures.OSAPIFixture(api_version='v2.1')).admin_api
# Add the AggregateMultiTenancyIsolation to the list of enabled
# filters since it is not enabled by default.
enabled_filters = CONF.filter_scheduler.enabled_filters
enabled_filters.append('AggregateMultiTenancyIsolation')
self.flags(enabled_filters=enabled_filters, group='filter_scheduler')
self.start_service('scheduler')
for host in ('host1', 'host2'):
self._start_compute(host)
def test_aggregate_multitenancy_isolation_filter(self):
"""Tests common scenarios with the AggregateMultiTenancyIsolation
filter:
* hosts in a tenant-isolated aggregate are only accepted for that
tenant
* hosts not in a tenant-isolated aggregate are acceptable for all
tenants, including tenants with access to the isolated-tenant
aggregate
"""
# Create a tenant-isolated aggregate for the non-admin user.
user_api = self.useFixture(
nova_fixtures.OSAPIFixture(api_version='v2.1',
project_id=uuids.non_admin)).api
agg_id = self.admin_api.post_aggregate(
{'aggregate': {'name': 'non_admin_agg'}})['id']
meta_req = {'set_metadata': {
'metadata': {'filter_tenant_id': uuids.non_admin}}}
self.admin_api.api_post('/os-aggregates/%s/action' % agg_id, meta_req)
# Add host2 to the aggregate; we'll restrict host2 to the non-admin
# tenant.
host_req = {'add_host': {'host': 'host2'}}
self.admin_api.api_post('/os-aggregates/%s/action' % agg_id, host_req)
# Stub out select_destinations to assert how many host candidates were
# available per tenant-specific request.
original_filtered_hosts = (
nova.scheduler.host_manager.HostManager.get_filtered_hosts)
def spy_get_filtered_hosts(*args, **kwargs):
self.filtered_hosts = original_filtered_hosts(*args, **kwargs)
return self.filtered_hosts
self.stub_out(
'nova.scheduler.host_manager.HostManager.get_filtered_hosts',
spy_get_filtered_hosts)
# Create a server for the admin - should only have one host candidate.
server_req = self._build_minimal_create_server_request(
self.admin_api,
'test_aggregate_multitenancy_isolation_filter-admin',
networks='none') # requires microversion 2.37
server_req = {'server': server_req}
with utils.temporary_mutation(self.admin_api, microversion='2.37'):
server = self.admin_api.post_server(server_req)
server = self._wait_for_state_change(self.admin_api, server, 'ACTIVE')
# Assert it's not on host2 which is isolated to the non-admin tenant.
self.assertNotEqual('host2', server['OS-EXT-SRV-ATTR:host'])
self.assertEqual(1, len(self.filtered_hosts))
# Now create a server for the non-admin tenant to which host2 is
# isolated via the aggregate, but the other compute host is a
# candidate. We don't assert that the non-admin tenant server shows
# up on host2 because the other host, which is not isolated to the
# aggregate, is still a candidate.
server_req = self._build_minimal_create_server_request(
user_api,
'test_aggregate_multitenancy_isolation_filter-user',
networks='none') # requires microversion 2.37
server_req = {'server': server_req}
with utils.temporary_mutation(user_api, microversion='2.37'):
server = user_api.post_server(server_req)
self._wait_for_state_change(user_api, server, 'ACTIVE')
self.assertEqual(2, len(self.filtered_hosts))