nova/nova/tests/functional/regressions/test_bug_1780373.py

103 lines
4.9 KiB
Python

# 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 nova import test
from nova.tests import fixtures as nova_fixtures
from nova.tests.functional import fixtures as func_fixtures
from nova.tests.functional import integrated_helpers
from nova.tests.unit import policy_fixture
class TestMultiCreateServerGroupMemberOverQuota(
test.TestCase, integrated_helpers.InstanceHelperMixin):
"""This tests a regression introduced in the Pike release.
Starting in the Pike release, quotas are no longer tracked using usages
and reservations tables but instead perform a resource counting operation
at the point of resource creation.
When creating multiple servers in the same request that belong in the same
server group, the [quota]/server_group_members config option is checked
to determine if those servers can belong in the same group based on quota.
However, the quota check for server_group_members only counts existing
group members based on live instances in the cell database(s). But the
actual instance record isn't created in the cell database until *after* the
server_group_members quota check happens. Because of this, it is possible
to bypass the server_group_members quota check when creating multiple
servers in the same request.
"""
def setUp(self):
super(TestMultiCreateServerGroupMemberOverQuota, self).setUp()
self.flags(server_group_members=2, group='quota')
self.useFixture(policy_fixture.RealPolicyFixture())
self.useFixture(nova_fixtures.NeutronFixture(self))
self.glance = self.useFixture(nova_fixtures.GlanceFixture(self))
self.useFixture(func_fixtures.PlacementFixture())
api_fixture = self.useFixture(nova_fixtures.OSAPIFixture(
api_version='v2.1'))
self.api = api_fixture.api
self.api.microversion = '2.37' # so we can specify networks='none'
group = {'name': 'test group', 'policies': ['soft-anti-affinity']}
self.created_group = self.api.post_server_groups(group)
def test_multi_create_server_group_members_over_quota(self):
"""Recreate scenario for the bug where we create an anti-affinity
server group and then create 3 servers in the group using a
multi-create POST /servers request.
"""
server_req = self._build_server(networks='none')
server_req['min_count'] = 3
server_req['return_reservation_id'] = True
hints = {'group': self.created_group['id']}
# We should get a 403 response due to going over quota on server
# group members in a single request.
self.api.api_post(
'/servers', {'server': server_req, 'os:scheduler_hints': hints},
check_response_status=[403])
group = self.api.api_get(
'/os-server-groups/%s' %
self.created_group['id']).body['server_group']
self.assertEqual(0, len(group['members']))
def test_concurrent_request_server_group_members_over_quota(self):
"""Recreate scenario for the bug where we create 3 servers in the
same group but in separate requests. The NoopConductorFixture is used
to ensure the instances are not created in the nova cell database which
means the quota check will have to rely on counting group members using
build requests from the API DB.
"""
# These aren't really concurrent requests, but we can simulate that
# by using NoopConductorFixture.
self.useFixture(nova_fixtures.NoopConductorFixture())
for x in range(3):
server_req = self._build_server(networks='none')
hints = {'group': self.created_group['id']}
# This should result in a 403 response on the 3rd server.
if x == 2:
self.api.api_post(
'/servers',
{'server': server_req, 'os:scheduler_hints': hints},
check_response_status=[403])
else:
self.api.post_server(
{'server': server_req, 'os:scheduler_hints': hints})
# There should only be two servers created which are both members of
# the same group.
servers = self.api.get_servers(detail=False)
self.assertEqual(2, len(servers))
group = self.api.api_get(
'/os-server-groups/%s' %
self.created_group['id']).body['server_group']
self.assertEqual(2, len(group['members']))