167 lines
6.3 KiB
Python
167 lines
6.3 KiB
Python
# Copyright 2021 Red Hat, Inc.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from tempest.common import utils
|
|
from tempest.common import waiters
|
|
from tempest import config
|
|
from tempest.lib import decorators
|
|
from tempest.lib import exceptions as lib_exc
|
|
from tempest.scenario import manager
|
|
|
|
CONF = config.CONF
|
|
|
|
|
|
class ComputeProjectQuotaTest(manager.ScenarioTest):
|
|
"""The test base class for compute unified limits tests.
|
|
|
|
Dynamic credentials (unique tenants) are created on a per-class basis, so
|
|
we test different quota limits in separate test classes to prevent a quota
|
|
limit update in one test class from affecting a test running in another
|
|
test class in parallel.
|
|
|
|
https://docs.openstack.org/tempest/latest/configuration.html#dynamic-credentials
|
|
"""
|
|
credentials = ['primary', 'system_admin']
|
|
force_tenant_isolation = True
|
|
|
|
@classmethod
|
|
def skip_checks(cls):
|
|
super(ComputeProjectQuotaTest, cls).skip_checks()
|
|
if not CONF.compute_feature_enabled.unified_limits:
|
|
raise cls.skipException('Compute unified limits are not enabled.')
|
|
|
|
@classmethod
|
|
def resource_setup(cls):
|
|
super(ComputeProjectQuotaTest, cls).resource_setup()
|
|
|
|
# Figure out and record the nova service id
|
|
services = cls.os_system_admin.identity_services_v3_client.\
|
|
list_services()
|
|
nova_services = [x for x in services['services']
|
|
if x['name'] == 'nova']
|
|
cls.nova_service_id = nova_services[0]['id']
|
|
|
|
# Pre-create quota limits in subclasses and record their IDs so we can
|
|
# update them in-place without needing to know which ones have been
|
|
# created and in which order.
|
|
cls.limit_ids = {}
|
|
|
|
@classmethod
|
|
def _create_limit(cls, name, value):
|
|
return cls.os_system_admin.identity_limits_client.create_limit(
|
|
CONF.identity.region, cls.nova_service_id,
|
|
cls.servers_client.tenant_id, name, value)['limits'][0]['id']
|
|
|
|
def _update_limit(self, name, value):
|
|
self.os_system_admin.identity_limits_client.update_limit(
|
|
self.limit_ids[name], value)
|
|
|
|
|
|
class ServersQuotaTest(ComputeProjectQuotaTest):
|
|
|
|
@classmethod
|
|
def resource_setup(cls):
|
|
super(ServersQuotaTest, cls).resource_setup()
|
|
|
|
try:
|
|
cls.limit_ids['servers'] = cls._create_limit(
|
|
'servers', 5)
|
|
cls.limit_ids['class:VCPU'] = cls._create_limit(
|
|
'class:VCPU', 10)
|
|
cls.limit_ids['class:MEMORY_MB'] = cls._create_limit(
|
|
'class:MEMORY_MB', 25 * 1024)
|
|
cls.limit_ids['class:DISK_GB'] = cls._create_limit(
|
|
'class:DISK_GB', 10)
|
|
except lib_exc.Forbidden:
|
|
raise cls.skipException('Target system is not configured with '
|
|
'compute unified limits')
|
|
|
|
@decorators.idempotent_id('555d8bbf-d2ed-4e39-858c-4235899402d9')
|
|
@utils.services('compute')
|
|
def test_server_count_vcpu_memory_disk_quota(self):
|
|
# Set a quota on the number of servers for our tenant to one.
|
|
self._update_limit('servers', 1)
|
|
|
|
# Create one server.
|
|
first = self.create_server(name='first')
|
|
|
|
# Second server would put us over quota, so expect failure.
|
|
# NOTE: In nova, quota exceeded raises 403 Forbidden.
|
|
self.assertRaises(lib_exc.Forbidden,
|
|
self.create_server,
|
|
name='second')
|
|
|
|
# Update our limit to two.
|
|
self._update_limit('servers', 2)
|
|
|
|
# Now the same create should succeed.
|
|
second = self.create_server(name='second')
|
|
|
|
# Third server would put us over quota, so expect failure.
|
|
self.assertRaises(lib_exc.Forbidden,
|
|
self.create_server,
|
|
name='third')
|
|
|
|
# Delete the first server to put us under quota.
|
|
self.servers_client.delete_server(first['id'])
|
|
waiters.wait_for_server_termination(self.servers_client, first['id'])
|
|
|
|
# Now the same create should succeed.
|
|
third = self.create_server(name='third')
|
|
|
|
# Set the servers limit back to 10 to test other resources.
|
|
self._update_limit('servers', 10)
|
|
|
|
# Default flavor has: VCPU=1, MEMORY_MB=512, DISK_GB=1
|
|
# We are currently using 2 VCPU, set the limit to 2.
|
|
self._update_limit('class:VCPU', 2)
|
|
|
|
# Server create should fail as it would go over quota.
|
|
self.assertRaises(lib_exc.Forbidden,
|
|
self.create_server,
|
|
name='fourth')
|
|
|
|
# Delete the second server to put us under quota.
|
|
self.servers_client.delete_server(second['id'])
|
|
waiters.wait_for_server_termination(self.servers_client, second['id'])
|
|
|
|
# Same create should now succeed.
|
|
fourth = self.create_server(name='fourth')
|
|
|
|
# We are currently using 2 DISK_GB. Set limit to 1.
|
|
self._update_limit('class:DISK_GB', 1)
|
|
|
|
# Server create should fail because we're already over (new) quota.
|
|
self.assertRaises(lib_exc.Forbidden,
|
|
self.create_server,
|
|
name='fifth')
|
|
|
|
# Delete the third server.
|
|
self.servers_client.delete_server(third['id'])
|
|
waiters.wait_for_server_termination(self.servers_client, third['id'])
|
|
|
|
# Server create should fail again because it would still put us over
|
|
# quota.
|
|
self.assertRaises(lib_exc.Forbidden,
|
|
self.create_server,
|
|
name='fifth')
|
|
|
|
# Delete the fourth server.
|
|
self.servers_client.delete_server(fourth['id'])
|
|
waiters.wait_for_server_termination(self.servers_client, fourth['id'])
|
|
|
|
# Server create should succeed now.
|
|
self.create_server(name='fifth')
|