Merge "Add Cinder tests for quota sets"
This commit is contained in:
commit
9f7e7e0596
99
tempest/api/volume/admin/test_volume_quotas.py
Normal file
99
tempest/api/volume/admin/test_volume_quotas.py
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# Copyright (C) 2014 eNovance SAS <licensing@enovance.com>
|
||||||
|
#
|
||||||
|
# Author: Sylvain Baubeau <sylvain.baubeau@enovance.com>
|
||||||
|
#
|
||||||
|
# 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.api.volume import base
|
||||||
|
from tempest import test
|
||||||
|
|
||||||
|
QUOTA_KEYS = ['gigabytes', 'snapshots', 'volumes']
|
||||||
|
QUOTA_USAGE_KEYS = ['reserved', 'limit', 'in_use']
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeQuotasAdminTestJSON(base.BaseVolumeV1AdminTest):
|
||||||
|
_interface = "json"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(VolumeQuotasAdminTestJSON, cls).setUpClass()
|
||||||
|
cls.admin_volume_client = cls.os_adm.volumes_client
|
||||||
|
cls.demo_tenant_id = cls.isolated_creds.get_primary_user().get(
|
||||||
|
'tenantId')
|
||||||
|
|
||||||
|
@test.attr(type='gate')
|
||||||
|
def test_list_quotas(self):
|
||||||
|
resp, quotas = self.quotas_client.get_quota_set(self.demo_tenant_id)
|
||||||
|
self.assertEqual(200, resp.status)
|
||||||
|
for key in QUOTA_KEYS:
|
||||||
|
self.assertIn(key, quotas)
|
||||||
|
|
||||||
|
@test.attr(type='gate')
|
||||||
|
def test_list_default_quotas(self):
|
||||||
|
resp, quotas = self.quotas_client.get_default_quota_set(
|
||||||
|
self.demo_tenant_id)
|
||||||
|
self.assertEqual(200, resp.status)
|
||||||
|
for key in QUOTA_KEYS:
|
||||||
|
self.assertIn(key, quotas)
|
||||||
|
|
||||||
|
@test.attr(type='gate')
|
||||||
|
def test_update_all_quota_resources_for_tenant(self):
|
||||||
|
# Admin can update all the resource quota limits for a tenant
|
||||||
|
resp, default_quota_set = self.quotas_client.get_default_quota_set(
|
||||||
|
self.demo_tenant_id)
|
||||||
|
new_quota_set = {'gigabytes': 1009,
|
||||||
|
'volumes': 11,
|
||||||
|
'snapshots': 11}
|
||||||
|
|
||||||
|
# Update limits for all quota resources
|
||||||
|
resp, quota_set = self.quotas_client.update_quota_set(
|
||||||
|
self.demo_tenant_id,
|
||||||
|
**new_quota_set)
|
||||||
|
|
||||||
|
default_quota_set.pop('id')
|
||||||
|
self.addCleanup(self.quotas_client.update_quota_set,
|
||||||
|
self.demo_tenant_id, **default_quota_set)
|
||||||
|
self.assertEqual(200, resp.status)
|
||||||
|
self.assertEqual(new_quota_set, quota_set)
|
||||||
|
|
||||||
|
@test.attr(type='gate')
|
||||||
|
def test_show_quota_usage(self):
|
||||||
|
resp, quota_usage = self.quotas_client.get_quota_usage(self.adm_tenant)
|
||||||
|
self.assertEqual(200, resp.status)
|
||||||
|
for key in QUOTA_KEYS:
|
||||||
|
self.assertIn(key, quota_usage)
|
||||||
|
for usage_key in QUOTA_USAGE_KEYS:
|
||||||
|
self.assertIn(usage_key, quota_usage[key])
|
||||||
|
|
||||||
|
@test.attr(type='gate')
|
||||||
|
def test_quota_usage(self):
|
||||||
|
resp, quota_usage = self.quotas_client.get_quota_usage(
|
||||||
|
self.demo_tenant_id)
|
||||||
|
|
||||||
|
volume = self.create_volume(size=1)
|
||||||
|
self.addCleanup(self.admin_volume_client.delete_volume,
|
||||||
|
volume['id'])
|
||||||
|
|
||||||
|
resp, new_quota_usage = self.quotas_client.get_quota_usage(
|
||||||
|
self.demo_tenant_id)
|
||||||
|
|
||||||
|
self.assertEqual(200, resp.status)
|
||||||
|
self.assertEqual(quota_usage['volumes']['in_use'] + 1,
|
||||||
|
new_quota_usage['volumes']['in_use'])
|
||||||
|
|
||||||
|
self.assertEqual(quota_usage['gigabytes']['in_use'] + 1,
|
||||||
|
new_quota_usage['gigabytes']['in_use'])
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeQuotasAdminTestXML(VolumeQuotasAdminTestJSON):
|
||||||
|
_interface = "xml"
|
@ -145,6 +145,7 @@ class BaseVolumeV1AdminTest(BaseVolumeV1Test):
|
|||||||
cls.os_adm = clients.AdminManager(interface=cls._interface)
|
cls.os_adm = clients.AdminManager(interface=cls._interface)
|
||||||
cls.client = cls.os_adm.volume_types_client
|
cls.client = cls.os_adm.volume_types_client
|
||||||
cls.hosts_client = cls.os_adm.volume_hosts_client
|
cls.hosts_client = cls.os_adm.volume_hosts_client
|
||||||
|
cls.quotas_client = cls.os_adm.volume_quotas_client
|
||||||
|
|
||||||
|
|
||||||
class BaseVolumeV2Test(BaseVolumeTest):
|
class BaseVolumeV2Test(BaseVolumeTest):
|
||||||
|
@ -158,6 +158,8 @@ from tempest.services.telemetry.xml.telemetry_client import \
|
|||||||
TelemetryClientXML
|
TelemetryClientXML
|
||||||
from tempest.services.volume.json.admin.volume_hosts_client import \
|
from tempest.services.volume.json.admin.volume_hosts_client import \
|
||||||
VolumeHostsClientJSON
|
VolumeHostsClientJSON
|
||||||
|
from tempest.services.volume.json.admin.volume_quotas_client import \
|
||||||
|
VolumeQuotasClientJSON
|
||||||
from tempest.services.volume.json.admin.volume_types_client import \
|
from tempest.services.volume.json.admin.volume_types_client import \
|
||||||
VolumeTypesClientJSON
|
VolumeTypesClientJSON
|
||||||
from tempest.services.volume.json.backups_client import BackupsClientJSON
|
from tempest.services.volume.json.backups_client import BackupsClientJSON
|
||||||
@ -169,6 +171,8 @@ from tempest.services.volume.v2.json.volumes_client import VolumesV2ClientJSON
|
|||||||
from tempest.services.volume.v2.xml.volumes_client import VolumesV2ClientXML
|
from tempest.services.volume.v2.xml.volumes_client import VolumesV2ClientXML
|
||||||
from tempest.services.volume.xml.admin.volume_hosts_client import \
|
from tempest.services.volume.xml.admin.volume_hosts_client import \
|
||||||
VolumeHostsClientXML
|
VolumeHostsClientXML
|
||||||
|
from tempest.services.volume.xml.admin.volume_quotas_client import \
|
||||||
|
VolumeQuotasClientXML
|
||||||
from tempest.services.volume.xml.admin.volume_types_client import \
|
from tempest.services.volume.xml.admin.volume_types_client import \
|
||||||
VolumeTypesClientXML
|
VolumeTypesClientXML
|
||||||
from tempest.services.volume.xml.backups_client import BackupsClientXML
|
from tempest.services.volume.xml.backups_client import BackupsClientXML
|
||||||
@ -249,6 +253,8 @@ class Manager(manager.Manager):
|
|||||||
InstanceUsagesAuditLogClientXML(self.auth_provider)
|
InstanceUsagesAuditLogClientXML(self.auth_provider)
|
||||||
self.volume_hosts_client = VolumeHostsClientXML(
|
self.volume_hosts_client = VolumeHostsClientXML(
|
||||||
self.auth_provider)
|
self.auth_provider)
|
||||||
|
self.volume_quotas_client = VolumeQuotasClientXML(
|
||||||
|
self.auth_provider)
|
||||||
self.volumes_extension_client = VolumeExtensionClientXML(
|
self.volumes_extension_client = VolumeExtensionClientXML(
|
||||||
self.auth_provider)
|
self.auth_provider)
|
||||||
if CONF.service_available.ceilometer:
|
if CONF.service_available.ceilometer:
|
||||||
@ -329,6 +335,8 @@ class Manager(manager.Manager):
|
|||||||
InstanceUsagesAuditLogClientJSON(self.auth_provider)
|
InstanceUsagesAuditLogClientJSON(self.auth_provider)
|
||||||
self.volume_hosts_client = VolumeHostsClientJSON(
|
self.volume_hosts_client = VolumeHostsClientJSON(
|
||||||
self.auth_provider)
|
self.auth_provider)
|
||||||
|
self.volume_quotas_client = VolumeQuotasClientJSON(
|
||||||
|
self.auth_provider)
|
||||||
self.volumes_extension_client = VolumeExtensionClientJSON(
|
self.volumes_extension_client = VolumeExtensionClientJSON(
|
||||||
self.auth_provider)
|
self.auth_provider)
|
||||||
self.hosts_v3_client = HostsV3ClientJSON(self.auth_provider)
|
self.hosts_v3_client = HostsV3ClientJSON(self.auth_provider)
|
||||||
|
79
tempest/services/volume/json/admin/volume_quotas_client.py
Normal file
79
tempest/services/volume/json/admin/volume_quotas_client.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
# Copyright (C) 2014 eNovance SAS <licensing@enovance.com>
|
||||||
|
#
|
||||||
|
# Author: Sylvain Baubeau <sylvain.baubeau@enovance.com>
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
from tempest.openstack.common import jsonutils
|
||||||
|
|
||||||
|
from tempest.common import rest_client
|
||||||
|
from tempest import config
|
||||||
|
|
||||||
|
CONF = config.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeQuotasClientJSON(rest_client.RestClient):
|
||||||
|
"""
|
||||||
|
Client class to send CRUD Volume Quotas API requests to a Cinder endpoint
|
||||||
|
"""
|
||||||
|
|
||||||
|
TYPE = "json"
|
||||||
|
|
||||||
|
def __init__(self, auth_provider):
|
||||||
|
super(VolumeQuotasClientJSON, self).__init__(auth_provider)
|
||||||
|
|
||||||
|
self.service = CONF.volume.catalog_type
|
||||||
|
self.build_interval = CONF.volume.build_interval
|
||||||
|
self.build_timeout = CONF.volume.build_timeout
|
||||||
|
|
||||||
|
def get_default_quota_set(self, tenant_id):
|
||||||
|
"""List the default volume quota set for a tenant."""
|
||||||
|
|
||||||
|
url = 'os-quota-sets/%s/defaults' % tenant_id
|
||||||
|
resp, body = self.get(url)
|
||||||
|
return resp, self._parse_resp(body)
|
||||||
|
|
||||||
|
def get_quota_set(self, tenant_id, params=None):
|
||||||
|
"""List the quota set for a tenant."""
|
||||||
|
|
||||||
|
url = 'os-quota-sets/%s' % tenant_id
|
||||||
|
if params:
|
||||||
|
url += '?%s' % urllib.urlencode(params)
|
||||||
|
|
||||||
|
resp, body = self.get(url)
|
||||||
|
return resp, self._parse_resp(body)
|
||||||
|
|
||||||
|
def get_quota_usage(self, tenant_id):
|
||||||
|
"""List the quota set for a tenant."""
|
||||||
|
|
||||||
|
resp, body = self.get_quota_set(tenant_id, params={'usage': True})
|
||||||
|
return resp, body
|
||||||
|
|
||||||
|
def update_quota_set(self, tenant_id, gigabytes=None, volumes=None,
|
||||||
|
snapshots=None):
|
||||||
|
post_body = {}
|
||||||
|
|
||||||
|
if gigabytes is not None:
|
||||||
|
post_body['gigabytes'] = gigabytes
|
||||||
|
|
||||||
|
if volumes is not None:
|
||||||
|
post_body['volumes'] = volumes
|
||||||
|
|
||||||
|
if snapshots is not None:
|
||||||
|
post_body['snapshots'] = snapshots
|
||||||
|
|
||||||
|
post_body = jsonutils.dumps({'quota_set': post_body})
|
||||||
|
resp, body = self.put('os-quota-sets/%s' % tenant_id, post_body)
|
||||||
|
return resp, self._parse_resp(body)
|
70
tempest/services/volume/xml/admin/volume_quotas_client.py
Normal file
70
tempest/services/volume/xml/admin/volume_quotas_client.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# Copyright (C) 2014 eNovance SAS <licensing@enovance.com>
|
||||||
|
#
|
||||||
|
# Author: Sylvain Baubeau <sylvain.baubeau@enovance.com>
|
||||||
|
#
|
||||||
|
# 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 ast import literal_eval
|
||||||
|
from lxml import etree
|
||||||
|
|
||||||
|
from tempest import config
|
||||||
|
from tempest.services.compute.xml import common as xml
|
||||||
|
from tempest.services.volume.json.admin import volume_quotas_client
|
||||||
|
|
||||||
|
CONF = config.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeQuotasClientXML(volume_quotas_client.VolumeQuotasClientJSON):
|
||||||
|
"""
|
||||||
|
Client class to send CRUD Volume Quotas API requests to a Cinder endpoint
|
||||||
|
"""
|
||||||
|
|
||||||
|
TYPE = "xml"
|
||||||
|
|
||||||
|
def _format_quota(self, q):
|
||||||
|
quota = {}
|
||||||
|
for k, v in q.items():
|
||||||
|
try:
|
||||||
|
v = literal_eval(v)
|
||||||
|
except (ValueError, SyntaxError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
quota[k] = v
|
||||||
|
|
||||||
|
return quota
|
||||||
|
|
||||||
|
def get_quota_usage(self, tenant_id):
|
||||||
|
"""List the quota set for a tenant."""
|
||||||
|
|
||||||
|
resp, body = self.get_quota_set(tenant_id, params={'usage': True})
|
||||||
|
return resp, self._format_quota(body)
|
||||||
|
|
||||||
|
def update_quota_set(self, tenant_id, gigabytes=None, volumes=None,
|
||||||
|
snapshots=None):
|
||||||
|
post_body = {}
|
||||||
|
element = xml.Element("quota_set")
|
||||||
|
|
||||||
|
if gigabytes is not None:
|
||||||
|
post_body['gigabytes'] = gigabytes
|
||||||
|
|
||||||
|
if volumes is not None:
|
||||||
|
post_body['volumes'] = volumes
|
||||||
|
|
||||||
|
if snapshots is not None:
|
||||||
|
post_body['snapshots'] = snapshots
|
||||||
|
|
||||||
|
xml.deep_dict_to_xml(element, post_body)
|
||||||
|
resp, body = self.put('os-quota-sets/%s' % tenant_id,
|
||||||
|
str(xml.Document(element)))
|
||||||
|
body = xml.xml_to_json(etree.fromstring(body))
|
||||||
|
return resp, self._format_quota(body)
|
Loading…
Reference in New Issue
Block a user