Merge "VPNaas IKE policies tests"
diff --git a/tempest/api/compute/v3/admin/test_quotas.py b/tempest/api/compute/v3/admin/test_quotas.py
new file mode 100644
index 0000000..f49aae4
--- /dev/null
+++ b/tempest/api/compute/v3/admin/test_quotas.py
@@ -0,0 +1,220 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# 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.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import config
+from tempest import exceptions
+from tempest.test import attr
+from tempest.test import skip_because
+
+
+class QuotasAdminTestJSON(base.BaseV2ComputeAdminTest):
+ _interface = 'json'
+ force_tenant_isolation = True
+
+ @classmethod
+ def setUpClass(cls):
+ super(QuotasAdminTestJSON, cls).setUpClass()
+ cls.auth_url = cls.config.identity.uri
+ cls.client = cls.os.quotas_client
+ cls.adm_client = cls.os_adm.quotas_client
+ cls.identity_admin_client = cls._get_identity_admin_client()
+ cls.sg_client = cls.security_groups_client
+
+ # NOTE(afazekas): these test cases should always create and use a new
+ # tenant most of them should be skipped if we can't do that
+ cls.demo_tenant_id = cls.isolated_creds.get_primary_user().get(
+ 'tenantId')
+
+ cls.default_quota_set = set(('injected_file_content_bytes',
+ 'metadata_items', 'injected_files',
+ 'ram', 'floating_ips',
+ 'fixed_ips', 'key_pairs',
+ 'injected_file_path_bytes',
+ 'instances', 'security_group_rules',
+ 'cores', 'security_groups'))
+
+ @attr(type='smoke')
+ def test_get_default_quotas(self):
+ # Admin can get the default resource quota set for a tenant
+ expected_quota_set = self.default_quota_set | set(['id'])
+ resp, quota_set = self.client.get_default_quota_set(
+ self.demo_tenant_id)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(sorted(expected_quota_set),
+ sorted(quota_set.keys()))
+ self.assertEqual(quota_set['id'], self.demo_tenant_id)
+
+ @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.client.get_default_quota_set(
+ self.demo_tenant_id)
+ new_quota_set = {'injected_file_content_bytes': 20480,
+ 'metadata_items': 256, 'injected_files': 10,
+ 'ram': 10240, 'floating_ips': 20, 'fixed_ips': 10,
+ 'key_pairs': 200, 'injected_file_path_bytes': 512,
+ 'instances': 20, 'security_group_rules': 20,
+ 'cores': 2, 'security_groups': 20}
+ # Update limits for all quota resources
+ resp, quota_set = self.adm_client.update_quota_set(
+ self.demo_tenant_id,
+ force=True,
+ **new_quota_set)
+
+ default_quota_set.pop('id')
+ self.addCleanup(self.adm_client.update_quota_set,
+ self.demo_tenant_id, **default_quota_set)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(new_quota_set, quota_set)
+
+ # TODO(afazekas): merge these test cases
+ @attr(type='gate')
+ def test_get_updated_quotas(self):
+ # Verify that GET shows the updated quota set
+ tenant_name = data_utils.rand_name('cpu_quota_tenant_')
+ tenant_desc = tenant_name + '-desc'
+ identity_client = self.os_adm.identity_client
+ _, tenant = identity_client.create_tenant(name=tenant_name,
+ description=tenant_desc)
+ tenant_id = tenant['id']
+ self.addCleanup(identity_client.delete_tenant,
+ tenant_id)
+
+ self.adm_client.update_quota_set(tenant_id,
+ ram='5120')
+ resp, quota_set = self.adm_client.get_quota_set(tenant_id)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(quota_set['ram'], 5120)
+
+ # TODO(afazekas): Add dedicated tenant to the skiped quota tests
+ # it can be moved into the setUpClass as well
+ @attr(type='gate')
+ def test_create_server_when_cpu_quota_is_full(self):
+ # Disallow server creation when tenant's vcpu quota is full
+ resp, quota_set = self.client.get_quota_set(self.demo_tenant_id)
+ default_vcpu_quota = quota_set['cores']
+ vcpu_quota = 0 # Set the quota to zero to conserve resources
+
+ resp, quota_set = self.adm_client.update_quota_set(self.demo_tenant_id,
+ force=True,
+ cores=vcpu_quota)
+
+ self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
+ cores=default_vcpu_quota)
+ self.assertRaises(exceptions.OverLimit, self.create_test_server)
+
+ @attr(type='gate')
+ def test_create_server_when_memory_quota_is_full(self):
+ # Disallow server creation when tenant's memory quota is full
+ resp, quota_set = self.client.get_quota_set(self.demo_tenant_id)
+ default_mem_quota = quota_set['ram']
+ mem_quota = 0 # Set the quota to zero to conserve resources
+
+ self.adm_client.update_quota_set(self.demo_tenant_id,
+ force=True,
+ ram=mem_quota)
+
+ self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
+ ram=default_mem_quota)
+ self.assertRaises(exceptions.OverLimit, self.create_test_server)
+
+ @attr(type='gate')
+ def test_update_quota_normal_user(self):
+ self.assertRaises(exceptions.Unauthorized,
+ self.client.update_quota_set,
+ self.demo_tenant_id,
+ ram=0)
+
+ @attr(type=['negative', 'gate'])
+ def test_create_server_when_instances_quota_is_full(self):
+ # Once instances quota limit is reached, disallow server creation
+ resp, quota_set = self.client.get_quota_set(self.demo_tenant_id)
+ default_instances_quota = quota_set['instances']
+ instances_quota = 0 # Set quota to zero to disallow server creation
+
+ self.adm_client.update_quota_set(self.demo_tenant_id,
+ force=True,
+ instances=instances_quota)
+ self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
+ instances=default_instances_quota)
+ self.assertRaises(exceptions.OverLimit, self.create_test_server)
+
+ @skip_because(bug="1186354",
+ condition=config.TempestConfig().service_available.neutron)
+ @attr(type=['negative', 'gate'])
+ def test_security_groups_exceed_limit(self):
+ # Negative test: Creation Security Groups over limit should FAIL
+
+ resp, quota_set = self.client.get_quota_set(self.demo_tenant_id)
+ default_sg_quota = quota_set['security_groups']
+ sg_quota = 0 # Set the quota to zero to conserve resources
+
+ resp, quota_set =\
+ self.adm_client.update_quota_set(self.demo_tenant_id,
+ force=True,
+ security_groups=sg_quota)
+
+ self.addCleanup(self.adm_client.update_quota_set,
+ self.demo_tenant_id,
+ security_groups=default_sg_quota)
+
+ # Check we cannot create anymore
+ self.assertRaises(exceptions.OverLimit,
+ self.sg_client.create_security_group,
+ "sg-overlimit", "sg-desc")
+
+ @skip_because(bug="1186354",
+ condition=config.TempestConfig().service_available.neutron)
+ @attr(type=['negative', 'gate'])
+ def test_security_groups_rules_exceed_limit(self):
+ # Negative test: Creation of Security Group Rules should FAIL
+ # when we reach limit maxSecurityGroupRules
+
+ resp, quota_set = self.client.get_quota_set(self.demo_tenant_id)
+ default_sg_rules_quota = quota_set['security_group_rules']
+ sg_rules_quota = 0 # Set the quota to zero to conserve resources
+
+ resp, quota_set =\
+ self.adm_client.update_quota_set(
+ self.demo_tenant_id,
+ force=True,
+ security_group_rules=sg_rules_quota)
+
+ self.addCleanup(self.adm_client.update_quota_set,
+ self.demo_tenant_id,
+ security_group_rules=default_sg_rules_quota)
+
+ s_name = data_utils.rand_name('securitygroup-')
+ s_description = data_utils.rand_name('description-')
+ resp, securitygroup =\
+ self.sg_client.create_security_group(s_name, s_description)
+ self.addCleanup(self.sg_client.delete_security_group,
+ securitygroup['id'])
+
+ secgroup_id = securitygroup['id']
+ ip_protocol = 'tcp'
+
+ # Check we cannot create SG rule anymore
+ self.assertRaises(exceptions.OverLimit,
+ self.sg_client.create_security_group_rule,
+ secgroup_id, ip_protocol, 1025, 1025)
+
+
+class QuotasAdminTestXML(QuotasAdminTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/v3/servers/test_create_server.py b/tempest/api/compute/v3/servers/test_create_server.py
new file mode 100644
index 0000000..24ade96
--- /dev/null
+++ b/tempest/api/compute/v3/servers/test_create_server.py
@@ -0,0 +1,130 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# 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.
+
+import base64
+
+import netaddr
+import testtools
+
+from tempest.api import compute
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest.common.utils.linux.remote_client import RemoteClient
+import tempest.config
+from tempest.test import attr
+
+
+class ServersTestJSON(base.BaseV2ComputeTest):
+ _interface = 'json'
+ run_ssh = tempest.config.TempestConfig().compute.run_ssh
+ disk_config = 'AUTO'
+
+ @classmethod
+ def setUpClass(cls):
+ super(ServersTestJSON, cls).setUpClass()
+ cls.meta = {'hello': 'world'}
+ cls.accessIPv4 = '1.1.1.1'
+ cls.accessIPv6 = '0000:0000:0000:0000:0000:babe:220.12.22.2'
+ cls.name = data_utils.rand_name('server')
+ file_contents = 'This is a test file.'
+ personality = [{'path': '/test.txt',
+ 'contents': base64.b64encode(file_contents)}]
+ cls.client = cls.servers_client
+ cli_resp = cls.create_test_server(name=cls.name,
+ meta=cls.meta,
+ accessIPv4=cls.accessIPv4,
+ accessIPv6=cls.accessIPv6,
+ personality=personality,
+ disk_config=cls.disk_config)
+ cls.resp, cls.server_initial = cli_resp
+ cls.password = cls.server_initial['adminPass']
+ cls.client.wait_for_server_status(cls.server_initial['id'], 'ACTIVE')
+ resp, cls.server = cls.client.get_server(cls.server_initial['id'])
+
+ @attr(type='smoke')
+ def test_create_server_response(self):
+ # Check that the required fields are returned with values
+ self.assertEqual(202, self.resp.status)
+ self.assertTrue(self.server_initial['id'] is not None)
+ self.assertTrue(self.server_initial['adminPass'] is not None)
+
+ @attr(type='smoke')
+ def test_verify_server_details(self):
+ # Verify the specified server attributes are set correctly
+ self.assertEqual(self.accessIPv4, self.server['accessIPv4'])
+ # NOTE(maurosr): See http://tools.ietf.org/html/rfc5952 (section 4)
+ # Here we compare directly with the canonicalized format.
+ self.assertEqual(self.server['accessIPv6'],
+ str(netaddr.IPAddress(self.accessIPv6)))
+ self.assertEqual(self.name, self.server['name'])
+ self.assertEqual(self.image_ref, self.server['image']['id'])
+ self.assertEqual(self.flavor_ref, self.server['flavor']['id'])
+ self.assertEqual(self.meta, self.server['metadata'])
+
+ @attr(type='smoke')
+ def test_list_servers(self):
+ # The created server should be in the list of all servers
+ resp, body = self.client.list_servers()
+ servers = body['servers']
+ found = any([i for i in servers if i['id'] == self.server['id']])
+ self.assertTrue(found)
+
+ @attr(type='smoke')
+ def test_list_servers_with_detail(self):
+ # The created server should be in the detailed list of all servers
+ resp, body = self.client.list_servers_with_detail()
+ servers = body['servers']
+ found = any([i for i in servers if i['id'] == self.server['id']])
+ self.assertTrue(found)
+
+ @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
+ @attr(type='gate')
+ def test_can_log_into_created_server(self):
+ # Check that the user can authenticate with the generated password
+ linux_client = RemoteClient(self.server, self.ssh_user, self.password)
+ self.assertTrue(linux_client.can_authenticate())
+
+ @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
+ @attr(type='gate')
+ def test_verify_created_server_vcpus(self):
+ # Verify that the number of vcpus reported by the instance matches
+ # the amount stated by the flavor
+ resp, flavor = self.flavors_client.get_flavor_details(self.flavor_ref)
+ linux_client = RemoteClient(self.server, self.ssh_user, self.password)
+ self.assertEqual(flavor['vcpus'], linux_client.get_number_of_vcpus())
+
+ @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
+ @attr(type='gate')
+ def test_host_name_is_same_as_server_name(self):
+ # Verify the instance host name is the same as the server name
+ linux_client = RemoteClient(self.server, self.ssh_user, self.password)
+ self.assertTrue(linux_client.hostname_equals_servername(self.name))
+
+
+class ServersTestManualDisk(ServersTestJSON):
+ disk_config = 'MANUAL'
+
+ @classmethod
+ def setUpClass(cls):
+ if not compute.DISK_CONFIG_ENABLED:
+ msg = "DiskConfig extension not enabled."
+ raise cls.skipException(msg)
+ super(ServersTestManualDisk, cls).setUpClass()
+
+
+class ServersTestXML(ServersTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/v3/servers/test_multiple_create.py b/tempest/api/compute/v3/servers/test_multiple_create.py
new file mode 100644
index 0000000..080bd1a
--- /dev/null
+++ b/tempest/api/compute/v3/servers/test_multiple_create.py
@@ -0,0 +1,95 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corp
+# 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.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest.test import attr
+
+
+class MultipleCreateTestJSON(base.BaseV2ComputeTest):
+ _interface = 'json'
+ _name = 'multiple-create-test'
+
+ def _generate_name(self):
+ return data_utils.rand_name(self._name)
+
+ def _create_multiple_servers(self, name=None, wait_until=None, **kwargs):
+ """
+ This is the right way to create_multiple servers and manage to get the
+ created servers into the servers list to be cleaned up after all.
+ """
+ kwargs['name'] = kwargs.get('name', self._generate_name())
+ resp, body = self.create_test_server(**kwargs)
+
+ return resp, body
+
+ @attr(type='gate')
+ def test_multiple_create(self):
+ resp, body = self._create_multiple_servers(wait_until='ACTIVE',
+ min_count=1,
+ max_count=2)
+ # NOTE(maurosr): do status response check and also make sure that
+ # reservation_id is not in the response body when the request send
+ # contains return_reservation_id=False
+ self.assertEqual('202', resp['status'])
+ self.assertNotIn('reservation_id', body)
+
+ @attr(type=['negative', 'gate'])
+ def test_min_count_less_than_one(self):
+ invalid_min_count = 0
+ self.assertRaises(exceptions.BadRequest, self._create_multiple_servers,
+ min_count=invalid_min_count)
+
+ @attr(type=['negative', 'gate'])
+ def test_min_count_non_integer(self):
+ invalid_min_count = 2.5
+ self.assertRaises(exceptions.BadRequest, self._create_multiple_servers,
+ min_count=invalid_min_count)
+
+ @attr(type=['negative', 'gate'])
+ def test_max_count_less_than_one(self):
+ invalid_max_count = 0
+ self.assertRaises(exceptions.BadRequest, self._create_multiple_servers,
+ max_count=invalid_max_count)
+
+ @attr(type=['negative', 'gate'])
+ def test_max_count_non_integer(self):
+ invalid_max_count = 2.5
+ self.assertRaises(exceptions.BadRequest, self._create_multiple_servers,
+ max_count=invalid_max_count)
+
+ @attr(type=['negative', 'gate'])
+ def test_max_count_less_than_min_count(self):
+ min_count = 3
+ max_count = 2
+ self.assertRaises(exceptions.BadRequest, self._create_multiple_servers,
+ min_count=min_count,
+ max_count=max_count)
+
+ @attr(type='gate')
+ def test_multiple_create_with_reservation_return(self):
+ resp, body = self._create_multiple_servers(wait_until='ACTIVE',
+ min_count=1,
+ max_count=2,
+ return_reservation_id=True)
+ self.assertEqual(resp['status'], '202')
+ self.assertIn('reservation_id', body)
+
+
+class MultipleCreateTestXML(MultipleCreateTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/v3/servers/test_servers.py b/tempest/api/compute/v3/servers/test_servers.py
new file mode 100644
index 0000000..d72476d
--- /dev/null
+++ b/tempest/api/compute/v3/servers/test_servers.py
@@ -0,0 +1,133 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# 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.api.compute import base
+from tempest.common.utils import data_utils
+from tempest.test import attr
+
+
+class ServersTestJSON(base.BaseV2ComputeTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(ServersTestJSON, cls).setUpClass()
+ cls.client = cls.servers_client
+
+ def tearDown(self):
+ self.clear_servers()
+ super(ServersTestJSON, self).tearDown()
+
+ @attr(type='gate')
+ def test_create_server_with_admin_password(self):
+ # If an admin password is provided on server creation, the server's
+ # root password should be set to that password.
+ resp, server = self.create_test_server(adminPass='testpassword')
+
+ # Verify the password is set correctly in the response
+ self.assertEqual('testpassword', server['adminPass'])
+
+ @attr(type='gate')
+ def test_create_with_existing_server_name(self):
+ # Creating a server with a name that already exists is allowed
+
+ # TODO(sdague): clear out try, we do cleanup one layer up
+ server_name = data_utils.rand_name('server')
+ resp, server = self.create_test_server(name=server_name,
+ wait_until='ACTIVE')
+ id1 = server['id']
+ resp, server = self.create_test_server(name=server_name,
+ wait_until='ACTIVE')
+ id2 = server['id']
+ self.assertNotEqual(id1, id2, "Did not create a new server")
+ resp, server = self.client.get_server(id1)
+ name1 = server['name']
+ resp, server = self.client.get_server(id2)
+ name2 = server['name']
+ self.assertEqual(name1, name2)
+
+ @attr(type='gate')
+ def test_create_specify_keypair(self):
+ # Specify a keypair while creating a server
+
+ key_name = data_utils.rand_name('key')
+ resp, keypair = self.keypairs_client.create_keypair(key_name)
+ resp, body = self.keypairs_client.list_keypairs()
+ resp, server = self.create_test_server(key_name=key_name)
+ self.assertEqual('202', resp['status'])
+ self.client.wait_for_server_status(server['id'], 'ACTIVE')
+ resp, server = self.client.get_server(server['id'])
+ self.assertEqual(key_name, server['key_name'])
+
+ @attr(type='gate')
+ def test_update_server_name(self):
+ # The server name should be changed to the the provided value
+ resp, server = self.create_test_server(wait_until='ACTIVE')
+
+ # Update the server with a new name
+ resp, server = self.client.update_server(server['id'],
+ name='newname')
+ self.assertEqual(200, resp.status)
+ self.client.wait_for_server_status(server['id'], 'ACTIVE')
+
+ # Verify the name of the server has changed
+ resp, server = self.client.get_server(server['id'])
+ self.assertEqual('newname', server['name'])
+
+ @attr(type='gate')
+ def test_update_access_server_address(self):
+ # The server's access addresses should reflect the provided values
+ resp, server = self.create_test_server(wait_until='ACTIVE')
+
+ # Update the IPv4 and IPv6 access addresses
+ resp, body = self.client.update_server(server['id'],
+ accessIPv4='1.1.1.1',
+ accessIPv6='::babe:202:202')
+ self.assertEqual(200, resp.status)
+ self.client.wait_for_server_status(server['id'], 'ACTIVE')
+
+ # Verify the access addresses have been updated
+ resp, server = self.client.get_server(server['id'])
+ self.assertEqual('1.1.1.1', server['accessIPv4'])
+ self.assertEqual('::babe:202:202', server['accessIPv6'])
+
+ @attr(type='gate')
+ def test_delete_server_while_in_building_state(self):
+ # Delete a server while it's VM state is Building
+ resp, server = self.create_test_server(wait_until='BUILD')
+ resp, _ = self.client.delete_server(server['id'])
+ self.assertEqual('204', resp['status'])
+
+ @attr(type='gate')
+ def test_delete_active_server(self):
+ # Delete a server while it's VM state is Active
+ resp, server = self.create_test_server(wait_until='ACTIVE')
+ resp, _ = self.client.delete_server(server['id'])
+ self.assertEqual('204', resp['status'])
+
+ @attr(type='gate')
+ def test_create_server_with_ipv6_addr_only(self):
+ # Create a server without an IPv4 address(only IPv6 address).
+ resp, server = self.create_test_server(accessIPv6='2001:2001::3')
+ self.assertEqual('202', resp['status'])
+ self.client.wait_for_server_status(server['id'], 'ACTIVE')
+ resp, server = self.client.get_server(server['id'])
+ self.assertEqual('2001:2001::3', server['accessIPv6'])
+
+
+class ServersTestXML(ServersTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/v3/test_quotas.py b/tempest/api/compute/v3/test_quotas.py
new file mode 100644
index 0000000..475d055
--- /dev/null
+++ b/tempest/api/compute/v3/test_quotas.py
@@ -0,0 +1,73 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# 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.api.compute import base
+from tempest.test import attr
+
+
+class QuotasTestJSON(base.BaseV2ComputeTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(QuotasTestJSON, cls).setUpClass()
+ cls.client = cls.quotas_client
+ cls.admin_client = cls._get_identity_admin_client()
+ resp, tenants = cls.admin_client.list_tenants()
+ cls.tenant_id = [tnt['id'] for tnt in tenants if tnt['name'] ==
+ cls.client.tenant_name][0]
+ cls.default_quota_set = set(('injected_file_content_bytes',
+ 'metadata_items', 'injected_files',
+ 'ram', 'floating_ips',
+ 'fixed_ips', 'key_pairs',
+ 'injected_file_path_bytes',
+ 'instances', 'security_group_rules',
+ 'cores', 'security_groups'))
+
+ @attr(type='smoke')
+ def test_get_quotas(self):
+ # User can get the quota set for it's tenant
+ expected_quota_set = self.default_quota_set | set(['id'])
+ resp, quota_set = self.client.get_quota_set(self.tenant_id)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(sorted(expected_quota_set),
+ sorted(quota_set.keys()))
+ self.assertEqual(quota_set['id'], self.tenant_id)
+
+ @attr(type='smoke')
+ def test_get_default_quotas(self):
+ # User can get the default quota set for it's tenant
+ expected_quota_set = self.default_quota_set | set(['id'])
+ resp, quota_set = self.client.get_default_quota_set(self.tenant_id)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(sorted(expected_quota_set),
+ sorted(quota_set.keys()))
+ self.assertEqual(quota_set['id'], self.tenant_id)
+
+ @attr(type='smoke')
+ def test_compare_tenant_quotas_with_default_quotas(self):
+ # Tenants are created with the default quota values
+ resp, defualt_quota_set = \
+ self.client.get_default_quota_set(self.tenant_id)
+ self.assertEqual(200, resp.status)
+ resp, tenant_quota_set = self.client.get_quota_set(self.tenant_id)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(defualt_quota_set, tenant_quota_set)
+
+
+class QuotasTestXML(QuotasTestJSON):
+ _interface = 'xml'
diff --git a/tempest/services/compute/v3/json/quotas_client.py b/tempest/services/compute/v3/json/quotas_client.py
new file mode 100644
index 0000000..a910dec
--- /dev/null
+++ b/tempest/services/compute/v3/json/quotas_client.py
@@ -0,0 +1,103 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 NTT Data
+# 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.
+
+import json
+
+from tempest.common.rest_client import RestClient
+
+
+class QuotasClientJSON(RestClient):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(QuotasClientJSON, self).__init__(config, username, password,
+ auth_url, tenant_name)
+ self.service = self.config.compute.catalog_type
+
+ def get_quota_set(self, tenant_id):
+ """List the quota set for a tenant."""
+
+ url = 'os-quota-sets/%s' % str(tenant_id)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body['quota_set']
+
+ def get_default_quota_set(self, tenant_id):
+ """List the default quota set for a tenant."""
+
+ url = 'os-quota-sets/%s/defaults' % str(tenant_id)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body['quota_set']
+
+ def update_quota_set(self, tenant_id, force=None,
+ injected_file_content_bytes=None,
+ metadata_items=None, ram=None, floating_ips=None,
+ fixed_ips=None, key_pairs=None, instances=None,
+ security_group_rules=None, injected_files=None,
+ cores=None, injected_file_path_bytes=None,
+ security_groups=None):
+ """
+ Updates the tenant's quota limits for one or more resources
+ """
+ post_body = {}
+
+ if force is not None:
+ post_body['force'] = force
+
+ if injected_file_content_bytes is not None:
+ post_body['injected_file_content_bytes'] = \
+ injected_file_content_bytes
+
+ if metadata_items is not None:
+ post_body['metadata_items'] = metadata_items
+
+ if ram is not None:
+ post_body['ram'] = ram
+
+ if floating_ips is not None:
+ post_body['floating_ips'] = floating_ips
+
+ if fixed_ips is not None:
+ post_body['fixed_ips'] = fixed_ips
+
+ if key_pairs is not None:
+ post_body['key_pairs'] = key_pairs
+
+ if instances is not None:
+ post_body['instances'] = instances
+
+ if security_group_rules is not None:
+ post_body['security_group_rules'] = security_group_rules
+
+ if injected_files is not None:
+ post_body['injected_files'] = injected_files
+
+ if cores is not None:
+ post_body['cores'] = cores
+
+ if injected_file_path_bytes is not None:
+ post_body['injected_file_path_bytes'] = injected_file_path_bytes
+
+ if security_groups is not None:
+ post_body['security_groups'] = security_groups
+
+ post_body = json.dumps({'quota_set': post_body})
+ resp, body = self.put('os-quota-sets/%s' % str(tenant_id), post_body,
+ self.headers)
+
+ body = json.loads(body)
+ return resp, body['quota_set']
diff --git a/tempest/services/compute/v3/xml/quotas_client.py b/tempest/services/compute/v3/xml/quotas_client.py
new file mode 100644
index 0000000..ef5362c
--- /dev/null
+++ b/tempest/services/compute/v3/xml/quotas_client.py
@@ -0,0 +1,126 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 NTT Data
+# 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 lxml import etree
+
+from tempest.common.rest_client import RestClientXML
+from tempest.services.compute.xml.common import Document
+from tempest.services.compute.xml.common import Element
+from tempest.services.compute.xml.common import xml_to_json
+from tempest.services.compute.xml.common import XMLNS_11
+
+
+class QuotasClientXML(RestClientXML):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(QuotasClientXML, self).__init__(config, username, password,
+ auth_url, tenant_name)
+ self.service = self.config.compute.catalog_type
+
+ def _format_quota(self, q):
+ quota = {}
+ for k, v in q.items():
+ try:
+ v = int(v)
+ except ValueError:
+ pass
+
+ quota[k] = v
+
+ return quota
+
+ def _parse_array(self, node):
+ return [self._format_quota(xml_to_json(x)) for x in node]
+
+ def get_quota_set(self, tenant_id):
+ """List the quota set for a tenant."""
+
+ url = 'os-quota-sets/%s' % str(tenant_id)
+ resp, body = self.get(url, self.headers)
+ body = xml_to_json(etree.fromstring(body))
+ body = self._format_quota(body)
+ return resp, body
+
+ def get_default_quota_set(self, tenant_id):
+ """List the default quota set for a tenant."""
+
+ url = 'os-quota-sets/%s/defaults' % str(tenant_id)
+ resp, body = self.get(url, self.headers)
+ body = xml_to_json(etree.fromstring(body))
+ body = self._format_quota(body)
+ return resp, body
+
+ def update_quota_set(self, tenant_id, force=None,
+ injected_file_content_bytes=None,
+ metadata_items=None, ram=None, floating_ips=None,
+ fixed_ips=None, key_pairs=None, instances=None,
+ security_group_rules=None, injected_files=None,
+ cores=None, injected_file_path_bytes=None,
+ security_groups=None):
+ """
+ Updates the tenant's quota limits for one or more resources
+ """
+ post_body = Element("quota_set",
+ xmlns=XMLNS_11)
+
+ if force is not None:
+ post_body.add_attr('force', force)
+
+ if injected_file_content_bytes is not None:
+ post_body.add_attr('injected_file_content_bytes',
+ injected_file_content_bytes)
+
+ if metadata_items is not None:
+ post_body.add_attr('metadata_items', metadata_items)
+
+ if ram is not None:
+ post_body.add_attr('ram', ram)
+
+ if floating_ips is not None:
+ post_body.add_attr('floating_ips', floating_ips)
+
+ if fixed_ips is not None:
+ post_body.add_attr('fixed_ips', fixed_ips)
+
+ if key_pairs is not None:
+ post_body.add_attr('key_pairs', key_pairs)
+
+ if instances is not None:
+ post_body.add_attr('instances', instances)
+
+ if security_group_rules is not None:
+ post_body.add_attr('security_group_rules', security_group_rules)
+
+ if injected_files is not None:
+ post_body.add_attr('injected_files', injected_files)
+
+ if cores is not None:
+ post_body.add_attr('cores', cores)
+
+ if injected_file_path_bytes is not None:
+ post_body.add_attr('injected_file_path_bytes',
+ injected_file_path_bytes)
+
+ if security_groups is not None:
+ post_body.add_attr('security_groups', security_groups)
+
+ resp, body = self.put('os-quota-sets/%s' % str(tenant_id),
+ str(Document(post_body)),
+ self.headers)
+ body = xml_to_json(etree.fromstring(body))
+ body = self._format_quota(body)
+ return resp, body