From f930d163f21cfcefbff830496ef5d15968016dbd Mon Sep 17 00:00:00 2001 From: Federico Ressi Date: Fri, 16 Jul 2021 10:32:28 +0200 Subject: [PATCH] Update networking quotas before creating network stacks Change-Id: If97e5e42e0bae0117f52c87c7ae8cba411aa546f --- tobiko/openstack/stacks/_neutron.py | 15 ++++-- tobiko/openstack/stacks/_nova.py | 23 +++------ .../tests/functional/openstack/test_heat.py | 49 +++++++++++++++++++ 3 files changed, 67 insertions(+), 20 deletions(-) diff --git a/tobiko/openstack/stacks/_neutron.py b/tobiko/openstack/stacks/_neutron.py index aff44fd80..cb6e3c892 100644 --- a/tobiko/openstack/stacks/_neutron.py +++ b/tobiko/openstack/stacks/_neutron.py @@ -31,9 +31,6 @@ from tobiko.shell import sh from tobiko.shell import ssh -LOG = log.getLogger(__name__) - - CONF = config.CONF LOG = log.getLogger(__name__) @@ -282,6 +279,18 @@ class NetworkStackFixture(heat.HeatStackFixture): def gateway_network_details(self): return neutron.get_network(self.gateway_network_id) + @property + def neutron_required_quota_set(self) -> typing.Dict[str, int]: + requirements = super().neutron_required_quota_set + requirements['network'] += 1 + if self.has_ipv4: + requirements['subnet'] += 1 + if self.has_ipv6: + requirements['subnet'] += 1 + if self.has_gateway: + requirements['router'] += 1 + return requirements + @neutron.skip_if_missing_networking_extensions('net-mtu-writable') class NetworkWithNetMtuWriteStackFixture(NetworkStackFixture): diff --git a/tobiko/openstack/stacks/_nova.py b/tobiko/openstack/stacks/_nova.py index abc762d02..8899f0d23 100644 --- a/tobiko/openstack/stacks/_nova.py +++ b/tobiko/openstack/stacks/_nova.py @@ -28,7 +28,6 @@ import tobiko from tobiko import config from tobiko.openstack import glance from tobiko.openstack import heat -from tobiko.openstack import keystone from tobiko.openstack import neutron from tobiko.openstack import nova from tobiko.openstack.stacks import _hot @@ -106,10 +105,6 @@ class ServerStackFixture(heat.HeatStackFixture, abc.ABC): #: whenever the server will use config-drive to get metadata config_drive = False - def create_stack(self, retry=None): - self.ensure_quota_limits() - super(ServerStackFixture, self).create_stack(retry=retry) - @property def image_fixture(self) -> glance.GlanceImageFixture: """Glance image used to create a Nova server instance""" @@ -349,18 +344,12 @@ class ServerStackFixture(heat.HeatStackFixture, abc.ABC): return server - def ensure_quota_limits(self): - """Ensures Nova quota limits before creating a new server - """ - project = keystone.get_project_id( - session=self.client.http_client.session) - user = keystone.get_user_id( - session=self.client.http_client.session) - nova.ensure_nova_quota_limits( - project=project, - user=user, - instances=1, - cores=self.flavor_stack.vcpus or 1) + @property + def nova_required_quota_set(self) -> typing.Dict[str, int]: + requirements = super().nova_required_quota_set + requirements['instances'] += 1 + requirements['cores'] += (self.flavor_stack.vcpus or 1) + return requirements user_data = None diff --git a/tobiko/tests/functional/openstack/test_heat.py b/tobiko/tests/functional/openstack/test_heat.py index 19c9b99f1..61c8ff2e0 100644 --- a/tobiko/tests/functional/openstack/test_heat.py +++ b/tobiko/tests/functional/openstack/test_heat.py @@ -18,11 +18,14 @@ from __future__ import absolute_import import os import random import string +import typing import testtools import tobiko from tobiko.openstack import heat +from tobiko.openstack import neutron +from tobiko.openstack import nova TEMPLATE_DIRS = [os.path.dirname(__file__)] @@ -66,3 +69,49 @@ class HeatStackFixtureTest(testtools.TestCase): self.assertEqual(tobiko.get_fixture_name(MyStack), stack_0.stack_name) self.assertEqual(tobiko.get_fixture_name(MyStack) + '-1', stack_1.stack_name) + + +class EnsureNeutronQuotaLimitsFixture(MyStack): + + requirements = {'network': 100, 'subnet': 100, 'router': 10} + + @property + def neutron_required_quota_set(self) -> typing.Dict[str, int]: + return self.requirements + + +class EnsureNovaQuotaLimitsFixture(MyStack): + + requirements = {'cores': 20, 'instances': 10} + + @property + def nova_required_quota_set(self) -> typing.Dict[str, int]: + return self.requirements + + +class EnsureQuotaLimitsTest(testtools.TestCase): + + def test_ensure_neutron_quota_limits(self): + stack = EnsureNeutronQuotaLimitsFixture(stack_name=self.id()) + self.useFixture(stack) + quota_set = neutron.get_neutron_quota_set(detail=True) + for name, requirement in stack.requirements.items(): + quota = quota_set[name] + self.assertGreaterEqual(quota['limit'], + requirement + + max(0, quota['used']) - + max(0, quota['reserved'])) + + def test_ensure_nova_quota_limits(self): + stack = EnsureNovaQuotaLimitsFixture(stack_name=self.id()) + self.useFixture(stack) + quota_set = nova.get_nova_quota_set(detail=True) + for name, requirement in stack.requirements.items(): + quota = getattr(quota_set, name) + if quota['limit'] > 0: + self.assertGreaterEqual(quota['limit'], + requirement + + max(0, quota['in_use']) - + max(0, quota['reserved'])) + else: + self.assertEqual(quota['limit'], -1)