Add service helper for trove quota management

Change-Id: Ib09692b1451f1493394af755cbcd14cc96a36b02
This commit is contained in:
Simon Merrick 2021-01-20 15:10:48 +13:00
parent bf27115ad2
commit 45160d3378
8 changed files with 150 additions and 11 deletions

View File

@ -29,6 +29,7 @@ from adjutant.common.tests.fake_clients import (
get_fake_neutron,
get_fake_novaclient,
get_fake_cinderclient,
get_fake_troveclient,
setup_neutron_cache,
neutron_cache,
cinder_cache,
@ -36,6 +37,7 @@ from adjutant.common.tests.fake_clients import (
setup_mock_caches,
get_fake_octaviaclient,
octavia_cache,
trove_cache,
)
from adjutant.common.tests.utils import AdjutantTestCase
from adjutant.config import CONF
@ -516,6 +518,7 @@ class ProjectSetupActionTests(AdjutantTestCase):
@mock.patch(
"adjutant.common.openstack_clients.get_octaviaclient", get_fake_octaviaclient
)
@mock.patch("adjutant.common.openstack_clients.get_troveclient", get_fake_troveclient)
class QuotaActionTests(AdjutantTestCase):
def test_update_quota(self):
"""
@ -686,13 +689,15 @@ class QuotaActionTests(AdjutantTestCase):
"adjutant.quota.services": [
{
"operation": "override",
"value": {"*": ["cinder", "neutron", "nova", "octavia"]},
"value": {"*": ["cinder", "neutron", "nova", "octavia", "trove"]},
}
]
},
)
def test_update_quota_octavia(self):
"""Tests the quota update of the octavia service"""
def test_update_quota_extra_services(self):
"""Tests the quota update of extra services over and above
core openstack services.
"""
project = mock.Mock()
project.id = "test_project_id"
project.name = "test_project"
@ -735,6 +740,8 @@ class QuotaActionTests(AdjutantTestCase):
self.assertEqual(neutronquota["network"], 10)
octaviaquota = octavia_cache["RegionOne"]["test_project_id"]["quota"]
self.assertEqual(octaviaquota["load_balancer"], 10)
trove_quota = trove_cache["RegionOne"]["test_project_id"]["quota"]
self.assertEqual(trove_quota["instances"], 20)
@conf_utils.modify_conf(
CONF,
@ -742,13 +749,15 @@ class QuotaActionTests(AdjutantTestCase):
"adjutant.quota.services": [
{
"operation": "override",
"value": {"*": ["cinder", "neutron", "nova", "octavia"]},
"value": {"*": ["cinder", "neutron", "nova", "octavia", "trove"]},
}
]
},
)
def test_update_quota_octavia_over_usage(self):
"""When octavia usage is higher than new quota it won't be changed"""
def test_quota_downgrade_fails_when_usage_exceeds_requested_quota(self):
"""Ensures that a quota change will fail validation when the
current usage exceeds the requested quota.
"""
project = mock.Mock()
project.id = "test_project_id"
project.name = "test_project"
@ -780,6 +789,13 @@ class QuotaActionTests(AdjutantTestCase):
{"id": "fake_id2"},
]
trove_cache["RegionOne"][project.id]["instances"] = [
{"id": "fake_id"},
{"id": "fake_id2"},
{"id": "fake_id3"},
{"id": "fake_id4"},
]
action = UpdateProjectQuotasAction(data, task=task, order=1)
action.prepare()
@ -790,5 +806,8 @@ class QuotaActionTests(AdjutantTestCase):
# check the quotas were updated
octaviaquota = octavia_cache["RegionOne"]["test_project_id"]["quota"]
trove_quota = trove_cache["RegionOne"]["test_project_id"]["quota"]
# Still set to default
self.assertEqual(octaviaquota["load_balancer"], 1)
self.assertEqual(trove_quota["instances"], 3)

View File

@ -28,10 +28,12 @@ from adjutant.common.tests.fake_clients import (
get_fake_novaclient,
get_fake_cinderclient,
get_fake_octaviaclient,
get_fake_troveclient,
cinder_cache,
nova_cache,
neutron_cache,
octavia_cache,
trove_cache,
setup_mock_caches,
setup_quota_cache,
FakeResource,
@ -416,6 +418,7 @@ class OpenstackAPITests(AdjutantAPITestCase):
@mock.patch(
"adjutant.common.openstack_clients.get_octaviaclient", get_fake_octaviaclient
)
@mock.patch("adjutant.common.openstack_clients.get_troveclient", get_fake_troveclient)
class QuotaAPITests(AdjutantAPITestCase):
def setUp(self):
super(QuotaAPITests, self).setUp()
@ -447,6 +450,11 @@ class QuotaAPITests(AdjutantAPITestCase):
load_balancer = CONF.quota.sizes.get(size)["octavia"]["load_balancer"]
self.assertEqual(octaviaquota["load_balancer"], load_balancer)
if "trove" in extra_services:
trove_quota = trove_cache[region_name][project_id]["quota"]
instance = CONF.quota.sizes.get(size)["trove"]["instances"]
self.assertEqual(trove_quota["instances"], instance)
def test_update_quota_no_history(self):
""" Update the quota size of a project with no history """
@ -1255,13 +1263,13 @@ class QuotaAPITests(AdjutantAPITestCase):
"adjutant.quota.services": [
{
"operation": "override",
"value": {"*": ["cinder", "neutron", "nova", "octavia"]},
"value": {"*": ["cinder", "neutron", "nova", "octavia", "trove"]},
},
],
},
)
def test_update_quota_no_history_with_octavia(self):
""" Update quota for octavia."""
def test_update_quota_extra_services(self):
""" Update quota for extra services """
project = fake_clients.FakeProject(name="test_project", id="test_project_id")
@ -1290,5 +1298,5 @@ class QuotaAPITests(AdjutantAPITestCase):
# Then check to see the quotas have changed
self.check_quota_cache(
"RegionOne", project.id, "medium", extra_services=["octavia"]
"RegionOne", project.id, "medium", extra_services=["octavia", "trove"]
)

View File

@ -21,6 +21,7 @@ from cinderclient import client as cinderclient
from neutronclient.v2_0 import client as neutronclient
from novaclient import client as novaclient
from octaviaclient.api.v2 import octavia
from troveclient.v1 import client as troveclient
from adjutant.config import CONF
@ -78,3 +79,7 @@ def get_octaviaclient(region):
service = ks.services.list(name="octavia")[0]
endpoint = ks.endpoints.list(service=service, region=region, interface="public")[0]
return octavia.OctaviaAPI(session=get_auth_session(), endpoint=endpoint.url)
def get_troveclient(region):
return troveclient.Client(session=get_auth_session(), region_name=region)

View File

@ -166,11 +166,37 @@ class QuotaManager(object):
)
return usage
class ServiceQuotaTroveHelper(ServiceQuotaHelper):
def __init__(self, region_name, project_id):
self.client = openstack_clients.get_troveclient(region=region_name)
self.project_id = project_id
def get_quota(self):
project_quota = self.client.quota.show(self.project_id)
quotas = {}
for quota in project_quota:
quotas[quota.resource] = quota.limit
return quotas
def set_quota(self, values):
self.client.quota.update(self.project_id, values)
def get_usage(self):
project_quota = self.client.quota.show(self.project_id)
usage = {}
for quota in project_quota:
usage[quota.resource] = quota.in_use
return usage
_quota_updaters = {
"cinder": ServiceQuotaCinderHelper,
"nova": ServiceQuotaNovaHelper,
"neutron": ServiceQuotaNeutronHelper,
"octavia": ServiceQuotaOctaviaHelper,
"trove": ServiceQuotaTroveHelper,
}
def __init__(self, project_id, size_difference_threshold=None):

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from collections import namedtuple
from unittest import mock
from uuid import uuid4
@ -23,6 +24,7 @@ neutron_cache = {}
nova_cache = {}
cinder_cache = {}
octavia_cache = {}
trove_cache = {}
class FakeProject(object):
@ -731,6 +733,43 @@ class FakeOctaviaClient(object):
raise AttributeError
class FakeTroveClient(object):
class FakeTroveQuotaManager(object):
FakeTroveResource = namedtuple(
"FakeTroveResource", ["resource", "in_use", "reserved", "limit"]
)
def __init__(self, region):
global trove_cache
self.region = region
if region not in trove_cache:
trove_cache[region] = {}
self.cache = trove_cache[region]
def show(self, project_id):
quota = self.cache[project_id]["quota"]
quotas = []
resources = self.cache[project_id]["quota"].keys()
reserved = 0
for resource in resources:
in_use = len(self.cache[project_id][resource])
quotas.append(
self.FakeTroveResource(resource, in_use, reserved, quota[resource])
)
return quotas
def update(self, project_id, values):
if project_id not in self.cache:
self.cache[project_id] = {"quota": {}}
self.cache[project_id]["quota"] = values
def __init__(self, region):
self.quota = self.FakeTroveQuotaManager(region)
class FakeNovaClient(FakeOpenstackClient):
def __init__(self, region):
global nova_cache
@ -784,6 +823,22 @@ class FakeResource(object):
self.size = size
def setup_trove_cache(region, project_id):
global trove_cache
if region not in trove_cache:
trove_cache[region] = {}
if project_id not in trove_cache[region]:
trove_cache[region][project_id] = {}
trove_cache[region][project_id] = {
"instances": [],
"backups": [],
"volumes": [],
}
trove_cache[region][project_id]["quota"] = dict(CONF.quota.sizes["small"]["trove"])
def setup_neutron_cache(region, project_id):
global neutron_cache
if region not in neutron_cache:
@ -884,6 +939,7 @@ def setup_mock_caches(region, project_id):
setup_nova_cache(region, project_id)
setup_cinder_cache(region, project_id)
setup_neutron_cache(region, project_id)
setup_trove_cache(region, project_id)
client = FakeOctaviaClient(region)
if project_id in octavia_cache[region]:
del octavia_cache[region][project_id]
@ -905,3 +961,7 @@ def get_fake_cinderclient(region):
def get_fake_octaviaclient(region):
return FakeOctaviaClient(region)
def get_fake_troveclient(region):
return FakeTroveClient(region)

View File

@ -53,6 +53,11 @@ DEFAULT_QUOTA_SIZES = {
"member": 2,
"pool": 1,
},
"trove": {
"instances": 3,
"volumes": 3,
"backups": 15,
},
},
"medium": {
"cinder": {"gigabytes": 10000, "volumes": 100, "snapshots": 300},
@ -85,6 +90,11 @@ DEFAULT_QUOTA_SIZES = {
"member": 5,
"pool": 5,
},
"trove": {
"instances": 10,
"volumes": 10,
"backups": 50,
},
},
"large": {
"cinder": {"gigabytes": 50000, "volumes": 200, "snapshots": 600},
@ -117,6 +127,11 @@ DEFAULT_QUOTA_SIZES = {
"member": 10,
"pool": 10,
},
"trove": {
"instances": 20,
"volumes": 20,
"backups": 100,
},
},
}

View File

@ -0,0 +1,5 @@
---
features:
- |
Add a service quota helper for trove to facilitate the management of trove
quotas via adjutant.

View File

@ -13,6 +13,7 @@ python-keystoneclient>=3.19.0
python-neutronclient>=6.12.0
python-novaclient>=14.0.0
python-octaviaclient>=1.8.0
python-troveclient>=6.0.1
six>=1.12.0
confspirator>=0.2.2
mysqlclient>=1.4.6
mysqlclient>=1.4.6