Merge "Moves negative tests from api/compute/keypairs/test_keypairs"
diff --git a/etc/whitelist.yaml b/etc/whitelist.yaml
index 24ee5e1..e5a0d4d 100644
--- a/etc/whitelist.yaml
+++ b/etc/whitelist.yaml
@@ -31,6 +31,8 @@
message: "Getting disk size of instance"
- module: "nova.virt.libvirt.driver"
message: "No such file or directory: '/opt/stack/data/nova/instances"
+ - module: "nova.virt.libvirt.driver"
+ message: "Nova requires libvirt version 0\\.9\\.11 or greater"
- module: "nova.compute.manager"
message: "error during stop\\(\\) in sync_power_state"
- module: "nova.compute.manager"
@@ -46,6 +48,8 @@
message: "Container HEAD failed: .*404 Not Found"
- module: "glance.api.middleware.cache"
message: "however the registry did not contain metadata for that image"
+ - module: "oslo.messaging.notify._impl_messaging"
+ message: ".*"
ceilometer-acompute:
- module: "ceilometer.compute.pollsters.disk"
@@ -57,15 +61,27 @@
- module: "ceilometer.compute.pollsters.disk"
message: "Domain not found: no domain with matching uuid"
- module: "ceilometer.compute.pollsters.net"
+ message: "Domain not found: no domain with matching uuid"
+ - module: "ceilometer.compute.pollsters.net"
message: "No module named libvirt"
- module: "ceilometer.compute.pollsters.net"
message: "Unable to write to monitor: Broken pipe"
- module: "ceilometer.compute.pollsters.cpu"
message: "Domain not found: no domain with matching uuid"
+ - module: "ceilometer.compute.pollsters.net"
+ message: ".*"
+ - module: "ceilometer.compute.pollsters.disk"
+ message: ".*"
ceilometer-alarm-evaluator:
- module: "ceilometer.alarm.service"
message: "alarm evaluation cycle failed"
+ - module: "ceilometer.alarm.evaluator.threshold"
+ message: ".*"
+
+ceilometer-api:
+ - module: "wsme.api"
+ message: ".*"
h-api:
- module: "root"
@@ -121,6 +137,8 @@
message: "Exception during message handling"
- module: "nova.openstack.common.rpc.common"
message: "'NoneType' object has no attribute '__getitem__'"
+ - module: "nova.openstack.common.rpc.common"
+ message: "Instance .* could not be found"
c-api:
- module: "cinder.api.middleware.fault"
@@ -136,7 +154,7 @@
- module: "cinder.brick.iscsi.iscsi"
message: "Failed to create iscsi target for volume id"
- module: "cinder.brick.local_dev.lvm"
- message: "/dev/dm-1: stat failed: No such file or directory"
+ message: "stat failed: No such file or directory"
- module: "cinder.brick.local_dev.lvm"
message: "LV stack-volumes.*in use: not deactivating"
- module: "cinder.brick.local_dev.lvm"
@@ -157,6 +175,8 @@
message: "duplicate key value violates unique constraint"
- module: "ceilometer.collector.dispatcher.database"
message: "Failed to record metering data: QueuePool limit"
+ - module: "ceilometer.dispatcher.database"
+ message: "\\(DataError\\) integer out of range"
- module: "ceilometer.collector.dispatcher.database"
message: "Failed to record metering data: .* integer out of range"
- module: "ceilometer.collector.dispatcher.database"
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index a3bccc3..5028cad 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -33,7 +33,6 @@
def setUpClass(cls):
super(ServersAdminTestJSON, cls).setUpClass()
cls.client = cls.os_adm.servers_client
- cls.non_adm_client = cls.servers_client
cls.flavors_client = cls.os_adm.flavors_client
cls.identity_client = cls._get_identity_admin_client()
tenant = cls.identity_client.get_tenant_by_name(
@@ -87,42 +86,6 @@
self.assertEqual('204', resp['status'])
self.servers_client.wait_for_server_termination(server['id'])
- @attr(type=['negative', 'gate'])
- def test_resize_server_using_overlimit_ram(self):
- flavor_name = data_utils.rand_name("flavor-")
- flavor_id = self._get_unused_flavor_id()
- resp, quota_set = self.quotas_client.get_default_quota_set(
- self.tenant_id)
- ram = int(quota_set['ram']) + 1
- vcpus = 8
- disk = 10
- resp, flavor_ref = self.flavors_client.create_flavor(flavor_name,
- ram, vcpus, disk,
- flavor_id)
- self.addCleanup(self.flavors_client.delete_flavor, flavor_id)
- self.assertRaises(exceptions.OverLimit,
- self.client.resize,
- self.servers[0]['id'],
- flavor_ref['id'])
-
- @attr(type=['negative', 'gate'])
- def test_resize_server_using_overlimit_vcpus(self):
- flavor_name = data_utils.rand_name("flavor-")
- flavor_id = self._get_unused_flavor_id()
- ram = 512
- resp, quota_set = self.quotas_client.get_default_quota_set(
- self.tenant_id)
- vcpus = int(quota_set['cores']) + 1
- disk = 10
- resp, flavor_ref = self.flavors_client.create_flavor(flavor_name,
- ram, vcpus, disk,
- flavor_id)
- self.addCleanup(self.flavors_client.delete_flavor, flavor_id)
- self.assertRaises(exceptions.OverLimit,
- self.client.resize,
- self.servers[0]['id'],
- flavor_ref['id'])
-
@attr(type='gate')
def test_reset_state_server(self):
# Reset server's state to 'error'
@@ -141,23 +104,6 @@
resp, server = self.client.get_server(self.s1_id)
self.assertEqual(server['status'], 'ACTIVE')
- @attr(type=['negative', 'gate'])
- def test_reset_state_server_invalid_state(self):
- self.assertRaises(exceptions.BadRequest,
- self.client.reset_state, self.s1_id,
- state='invalid')
-
- @attr(type=['negative', 'gate'])
- def test_reset_state_server_invalid_type(self):
- self.assertRaises(exceptions.BadRequest,
- self.client.reset_state, self.s1_id,
- state=1)
-
- @attr(type=['negative', 'gate'])
- def test_reset_state_server_nonexistent_server(self):
- self.assertRaises(exceptions.NotFound,
- self.client.reset_state, '999')
-
@attr(type='gate')
@skip_because(bug="1240043")
def test_get_server_diagnostics_by_admin(self):
@@ -170,13 +116,6 @@
for key in basic_attrs:
self.assertIn(key, str(diagnostic.keys()))
- @attr(type=['negative', 'gate'])
- def test_get_server_diagnostics_by_non_admin(self):
- # Non-admin user can not view server diagnostics according to policy
- self.assertRaises(exceptions.Unauthorized,
- self.non_adm_client.get_server_diagnostics,
- self.s1_id)
-
class ServersAdminTestXML(ServersAdminTestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
new file mode 100644
index 0000000..77d873b
--- /dev/null
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -0,0 +1,143 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Huawei Technologies Co.,LTD.
+#
+# 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 uuid
+
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest.test import attr
+
+
+class ServersAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
+
+ """
+ Tests Servers API using admin privileges
+ """
+
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(ServersAdminNegativeTestJSON, cls).setUpClass()
+ cls.client = cls.os_adm.servers_client
+ cls.non_adm_client = cls.servers_client
+ cls.flavors_client = cls.os_adm.flavors_client
+ cls.identity_client = cls._get_identity_admin_client()
+ tenant = cls.identity_client.get_tenant_by_name(
+ cls.client.tenant_name)
+ cls.tenant_id = tenant['id']
+
+ cls.s1_name = data_utils.rand_name('server')
+ resp, server = cls.create_test_server(name=cls.s1_name,
+ wait_until='ACTIVE')
+ cls.s1_id = server['id']
+
+ def _get_unused_flavor_id(self):
+ flavor_id = data_utils.rand_int_id(start=1000)
+ while True:
+ try:
+ resp, body = self.flavors_client.get_flavor_details(flavor_id)
+ except exceptions.NotFound:
+ break
+ flavor_id = data_utils.rand_int_id(start=1000)
+ return flavor_id
+
+ @attr(type=['negative', 'gate'])
+ def test_resize_server_using_overlimit_ram(self):
+ flavor_name = data_utils.rand_name("flavor-")
+ flavor_id = self._get_unused_flavor_id()
+ resp, quota_set = self.quotas_client.get_default_quota_set(
+ self.tenant_id)
+ ram = int(quota_set['ram']) + 1
+ vcpus = 8
+ disk = 10
+ resp, flavor_ref = self.flavors_client.create_flavor(flavor_name,
+ ram, vcpus, disk,
+ flavor_id)
+ self.addCleanup(self.flavors_client.delete_flavor, flavor_id)
+ self.assertRaises(exceptions.OverLimit,
+ self.client.resize,
+ self.servers[0]['id'],
+ flavor_ref['id'])
+
+ @attr(type=['negative', 'gate'])
+ def test_resize_server_using_overlimit_vcpus(self):
+ flavor_name = data_utils.rand_name("flavor-")
+ flavor_id = self._get_unused_flavor_id()
+ ram = 512
+ resp, quota_set = self.quotas_client.get_default_quota_set(
+ self.tenant_id)
+ vcpus = int(quota_set['cores']) + 1
+ disk = 10
+ resp, flavor_ref = self.flavors_client.create_flavor(flavor_name,
+ ram, vcpus, disk,
+ flavor_id)
+ self.addCleanup(self.flavors_client.delete_flavor, flavor_id)
+ self.assertRaises(exceptions.OverLimit,
+ self.client.resize,
+ self.servers[0]['id'],
+ flavor_ref['id'])
+
+ @attr(type=['negative', 'gate'])
+ def test_reset_state_server_invalid_state(self):
+ self.assertRaises(exceptions.BadRequest,
+ self.client.reset_state, self.s1_id,
+ state='invalid')
+
+ @attr(type=['negative', 'gate'])
+ def test_reset_state_server_invalid_type(self):
+ self.assertRaises(exceptions.BadRequest,
+ self.client.reset_state, self.s1_id,
+ state=1)
+
+ @attr(type=['negative', 'gate'])
+ def test_reset_state_server_nonexistent_server(self):
+ self.assertRaises(exceptions.NotFound,
+ self.client.reset_state, '999')
+
+ @attr(type=['negative', 'gate'])
+ def test_get_server_diagnostics_by_non_admin(self):
+ # Non-admin user can not view server diagnostics according to policy
+ self.assertRaises(exceptions.Unauthorized,
+ self.non_adm_client.get_server_diagnostics,
+ self.s1_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_migrate_non_existent_server(self):
+ # migrate a non existent server
+ self.assertRaises(exceptions.NotFound,
+ self.client.migrate_server,
+ str(uuid.uuid4()))
+
+ @attr(type=['negative', 'gate'])
+ def test_migrate_server_invalid_state(self):
+ # create server.
+ resp, server = self.create_test_server(wait_until='ACTIVE')
+ self.assertEqual(202, resp.status)
+ server_id = server['id']
+ # suspend the server.
+ resp, _ = self.client.suspend_server(server_id)
+ self.assertEqual(202, resp.status)
+ self.client.wait_for_server_status(server_id, 'SUSPENDED')
+ # migrate an suspended server should fail
+ self.assertRaises(exceptions.Conflict,
+ self.client.migrate_server,
+ server_id)
+
+
+class ServersAdminNegativeTestXML(ServersAdminNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 4c823ad..7ef5466 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -225,6 +225,7 @@
def setUpClass(cls):
super(BaseV3ComputeTest, cls).setUpClass()
if not cls.config.compute_feature_enabled.api_v3:
+ cls.tearDownClass()
skip_msg = ("%s skipped as nova v3 api is not available" %
cls.__name__)
raise cls.skipException(skip_msg)
@@ -235,6 +236,7 @@
cls.extensions_client = cls.os.extensions_v3_client
cls.availability_zone_client = cls.os.availability_zone_v3_client
cls.interfaces_client = cls.os.interfaces_v3_client
+ cls.hypervisor_client = cls.os.hypervisor_v3_client
@classmethod
def create_image_from_server(cls, server_id, **kwargs):
@@ -297,3 +299,4 @@
cls.services_admin_client = cls.os_adm.services_v3_client
cls.availability_zone_admin_client = \
cls.os_adm.availability_zone_v3_client
+ cls.hypervisor_admin_client = cls.os_adm.hypervisor_v3_client
diff --git a/tempest/api/compute/v3/admin/test_hypervisor.py b/tempest/api/compute/v3/admin/test_hypervisor.py
new file mode 100644
index 0000000..3da3369
--- /dev/null
+++ b/tempest/api/compute/v3/admin/test_hypervisor.py
@@ -0,0 +1,105 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corporation
+# 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 HypervisorAdminV3TestJSON(base.BaseV3ComputeAdminTest):
+
+ """
+ Tests Hypervisors API that require admin privileges
+ """
+
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(HypervisorAdminV3TestJSON, cls).setUpClass()
+ cls.client = cls.hypervisor_admin_client
+
+ def _list_hypervisors(self):
+ # List of hypervisors
+ resp, hypers = self.client.get_hypervisor_list()
+ self.assertEqual(200, resp.status)
+ return hypers
+
+ @attr(type='gate')
+ def test_get_hypervisor_list(self):
+ # List of hypervisor and available hypervisors hostname
+ hypers = self._list_hypervisors()
+ self.assertTrue(len(hypers) > 0)
+
+ @attr(type='gate')
+ def test_get_hypervisor_list_details(self):
+ # Display the details of the all hypervisor
+ resp, hypers = self.client.get_hypervisor_list_details()
+ self.assertEqual(200, resp.status)
+ self.assertTrue(len(hypers) > 0)
+
+ @attr(type='gate')
+ def test_get_hypervisor_show_details(self):
+ # Display the details of the specified hypervisor
+ hypers = self._list_hypervisors()
+ self.assertTrue(len(hypers) > 0)
+
+ resp, details = (self.client.
+ get_hypervisor_show_details(hypers[0]['id']))
+ self.assertEqual(200, resp.status)
+ self.assertTrue(len(details) > 0)
+ self.assertEqual(details['hypervisor_hostname'],
+ hypers[0]['hypervisor_hostname'])
+
+ @attr(type='gate')
+ def test_get_hypervisor_show_servers(self):
+ # Show instances about the specific hypervisors
+ hypers = self._list_hypervisors()
+ self.assertTrue(len(hypers) > 0)
+
+ hypervisor_id = hypers[0]['id']
+ resp, hypervisors = self.client.get_hypervisor_servers(hypervisor_id)
+ self.assertEqual(200, resp.status)
+ self.assertTrue(len(hypervisors) > 0)
+
+ @attr(type='gate')
+ def test_get_hypervisor_stats(self):
+ # Verify the stats of the all hypervisor
+ resp, stats = self.client.get_hypervisor_stats()
+ self.assertEqual(200, resp.status)
+ self.assertTrue(len(stats) > 0)
+
+ @attr(type='gate')
+ def test_get_hypervisor_uptime(self):
+ # Verify that GET shows the specified hypervisor uptime
+ hypers = self._list_hypervisors()
+
+ resp, uptime = self.client.get_hypervisor_uptime(hypers[0]['id'])
+ self.assertEqual(200, resp.status)
+ self.assertTrue(len(uptime) > 0)
+
+ @attr(type='gate')
+ def test_search_hypervisor(self):
+ hypers = self._list_hypervisors()
+ self.assertTrue(len(hypers) > 0)
+ resp, hypers = self.client.search_hypervisor(
+ hypers[0]['hypervisor_hostname'])
+ self.assertEqual(200, resp.status)
+ self.assertTrue(len(hypers) > 0)
+
+
+class HypervisorAdminV3TestXML(HypervisorAdminV3TestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/v3/admin/test_hypervisor_negative.py b/tempest/api/compute/v3/admin/test_hypervisor_negative.py
new file mode 100644
index 0000000..847679e
--- /dev/null
+++ b/tempest/api/compute/v3/admin/test_hypervisor_negative.py
@@ -0,0 +1,144 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Huawei Technologies Co.,LTD.
+# 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 uuid
+
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest.test import attr
+
+
+class HypervisorAdminNegativeV3TestJSON(base.BaseV3ComputeAdminTest):
+
+ """
+ Tests Hypervisors API that require admin privileges
+ """
+
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(HypervisorAdminNegativeV3TestJSON, cls).setUpClass()
+ cls.client = cls.hypervisor_admin_client
+ cls.non_adm_client = cls.hypervisor_client
+
+ def _list_hypervisors(self):
+ # List of hypervisors
+ resp, hypers = self.client.get_hypervisor_list()
+ self.assertEqual(200, resp.status)
+ return hypers
+
+ @attr(type=['negative', 'gate'])
+ def test_show_nonexistent_hypervisor(self):
+ nonexistent_hypervisor_id = str(uuid.uuid4())
+
+ self.assertRaises(
+ exceptions.NotFound,
+ self.client.get_hypervisor_show_details,
+ nonexistent_hypervisor_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_show_hypervisor_with_non_admin_user(self):
+ hypers = self._list_hypervisors()
+ self.assertTrue(len(hypers) > 0)
+
+ self.assertRaises(
+ exceptions.Unauthorized,
+ self.non_adm_client.get_hypervisor_show_details,
+ hypers[0]['id'])
+
+ @attr(type=['negative', 'gate'])
+ def test_show_servers_with_non_admin_user(self):
+ hypers = self._list_hypervisors()
+ self.assertTrue(len(hypers) > 0)
+
+ self.assertRaises(
+ exceptions.Unauthorized,
+ self.non_adm_client.get_hypervisor_servers,
+ hypers[0]['id'])
+
+ @attr(type=['negative', 'gate'])
+ def test_show_servers_with_nonexistent_hypervisor(self):
+ nonexistent_hypervisor_id = str(uuid.uuid4())
+
+ self.assertRaises(
+ exceptions.NotFound,
+ self.client.get_hypervisor_servers,
+ nonexistent_hypervisor_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_get_hypervisor_stats_with_non_admin_user(self):
+ self.assertRaises(
+ exceptions.Unauthorized,
+ self.non_adm_client.get_hypervisor_stats)
+
+ @attr(type=['negative', 'gate'])
+ def test_get_nonexistent_hypervisor_uptime(self):
+ nonexistent_hypervisor_id = str(uuid.uuid4())
+
+ self.assertRaises(
+ exceptions.NotFound,
+ self.client.get_hypervisor_uptime,
+ nonexistent_hypervisor_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_get_hypervisor_uptime_with_non_admin_user(self):
+ hypers = self._list_hypervisors()
+ self.assertTrue(len(hypers) > 0)
+
+ self.assertRaises(
+ exceptions.Unauthorized,
+ self.non_adm_client.get_hypervisor_uptime,
+ hypers[0]['id'])
+
+ @attr(type=['negative', 'gate'])
+ def test_get_hypervisor_list_with_non_admin_user(self):
+ # List of hypervisor and available services with non admin user
+ self.assertRaises(
+ exceptions.Unauthorized,
+ self.non_adm_client.get_hypervisor_list)
+
+ @attr(type=['negative', 'gate'])
+ def test_get_hypervisor_list_details_with_non_admin_user(self):
+ # List of hypervisor details and available services with non admin user
+ self.assertRaises(
+ exceptions.Unauthorized,
+ self.non_adm_client.get_hypervisor_list_details)
+
+ @attr(type=['negative', 'gate'])
+ def test_search_nonexistent_hypervisor(self):
+ nonexistent_hypervisor_name = data_utils.rand_name('test_hypervisor')
+
+ resp, hypers = self.client.search_hypervisor(
+ nonexistent_hypervisor_name)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(0, len(hypers))
+
+ @attr(type=['negative', 'gate'])
+ def test_search_hypervisor_with_non_admin_user(self):
+ hypers = self._list_hypervisors()
+ self.assertTrue(len(hypers) > 0)
+
+ self.assertRaises(
+ exceptions.Unauthorized,
+ self.non_adm_client.search_hypervisor,
+ hypers[0]['hypervisor_hostname'])
+
+
+class HypervisorAdminNegativeV3TestXML(HypervisorAdminNegativeV3TestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/v3/servers/test_servers_negative.py b/tempest/api/compute/v3/servers/test_servers_negative.py
new file mode 100644
index 0000000..5ec0cbe
--- /dev/null
+++ b/tempest/api/compute/v3/servers/test_servers_negative.py
@@ -0,0 +1,449 @@
+# 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 sys
+import uuid
+
+from tempest.api.compute import base
+from tempest import clients
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest.test import attr
+
+
+class ServersNegativeTestJSON(base.BaseV2ComputeTest):
+ _interface = 'json'
+
+ def setUp(self):
+ super(ServersNegativeTestJSON, self).setUp()
+ try:
+ self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+ except Exception:
+ self.rebuild_server()
+
+ @classmethod
+ def setUpClass(cls):
+ super(ServersNegativeTestJSON, cls).setUpClass()
+ cls.client = cls.servers_client
+ cls.img_client = cls.images_client
+ cls.alt_os = clients.AltManager()
+ cls.alt_client = cls.alt_os.servers_client
+ resp, server = cls.create_test_server(wait_until='ACTIVE')
+ cls.server_id = server['id']
+
+ @attr(type=['negative', 'gate'])
+ def test_server_name_blank(self):
+ # Create a server with name parameter empty
+
+ self.assertRaises(exceptions.BadRequest,
+ self.create_test_server,
+ name='')
+
+ @attr(type=['negative', 'gate'])
+ def test_personality_file_contents_not_encoded(self):
+ # Use an unencoded file when creating a server with personality
+
+ file_contents = 'This is a test file.'
+ person = [{'path': '/etc/testfile.txt',
+ 'contents': file_contents}]
+
+ self.assertRaises(exceptions.BadRequest,
+ self.create_test_server,
+ personality=person)
+
+ @attr(type=['negative', 'gate'])
+ def test_create_with_invalid_image(self):
+ # Create a server with an unknown image
+
+ self.assertRaises(exceptions.BadRequest,
+ self.create_test_server,
+ image_id=-1)
+
+ @attr(type=['negative', 'gate'])
+ def test_create_with_invalid_flavor(self):
+ # Create a server with an unknown flavor
+
+ self.assertRaises(exceptions.BadRequest,
+ self.create_test_server,
+ flavor=-1,)
+
+ @attr(type=['negative', 'gate'])
+ def test_invalid_access_ip_v4_address(self):
+ # An access IPv4 address must match a valid address pattern
+
+ IPv4 = '1.1.1.1.1.1'
+ self.assertRaises(exceptions.BadRequest,
+ self.create_test_server, accessIPv4=IPv4)
+
+ @attr(type=['negative', 'gate'])
+ def test_invalid_ip_v6_address(self):
+ # An access IPv6 address must match a valid address pattern
+
+ IPv6 = 'notvalid'
+
+ self.assertRaises(exceptions.BadRequest,
+ self.create_test_server, accessIPv6=IPv6)
+
+ @attr(type=['negative', 'gate'])
+ def test_resize_nonexistent_server(self):
+ nonexistent_server = str(uuid.uuid4())
+ self.assertRaises(exceptions.NotFound,
+ self.client.resize,
+ nonexistent_server, self.flavor_ref)
+
+ @attr(type=['negative', 'gate'])
+ def test_resize_server_with_non_existent_flavor(self):
+ # Resize a server with non-existent flavor
+ nonexistent_flavor = str(uuid.uuid4())
+ self.assertRaises(exceptions.BadRequest, self.client.resize,
+ self.server_id, flavor_ref=nonexistent_flavor)
+
+ @attr(type=['negative', 'gate'])
+ def test_resize_server_with_null_flavor(self):
+ # Resize a server with null flavor
+ self.assertRaises(exceptions.BadRequest, self.client.resize,
+ self.server_id, flavor_ref="")
+
+ @attr(type=['negative', 'gate'])
+ def test_reboot_non_existent_server(self):
+ # Reboot a non existent server
+ nonexistent_server = str(uuid.uuid4())
+ self.assertRaises(exceptions.NotFound, self.client.reboot,
+ nonexistent_server, 'SOFT')
+
+ @attr(type=['negative', 'gate'])
+ def test_pause_paused_server(self):
+ # Pause a paused server.
+ self.client.pause_server(self.server_id)
+ self.addCleanup(self.client.unpause_server,
+ self.server_id)
+ self.client.wait_for_server_status(self.server_id, 'PAUSED')
+ self.assertRaises(exceptions.Conflict,
+ self.client.pause_server,
+ self.server_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_rebuild_reboot_deleted_server(self):
+ # Rebuild and Reboot a deleted server
+ _, server = self.create_test_server()
+ self.client.delete_server(server['id'])
+ self.client.wait_for_server_termination(server['id'])
+
+ self.assertRaises(exceptions.NotFound,
+ self.client.rebuild,
+ server['id'], self.image_ref_alt)
+ self.assertRaises(exceptions.NotFound, self.client.reboot,
+ server['id'], 'SOFT')
+
+ @attr(type=['negative', 'gate'])
+ def test_rebuild_non_existent_server(self):
+ # Rebuild a non existent server
+ nonexistent_server = str(uuid.uuid4())
+ meta = {'rebuild': 'server'}
+ new_name = data_utils.rand_name('server')
+ file_contents = 'Test server rebuild.'
+ personality = [{'path': '/etc/rebuild.txt',
+ 'contents': base64.b64encode(file_contents)}]
+ self.assertRaises(exceptions.NotFound,
+ self.client.rebuild,
+ nonexistent_server,
+ self.image_ref_alt,
+ name=new_name, meta=meta,
+ personality=personality,
+ adminPass='rebuild')
+
+ @attr(type=['negative', 'gate'])
+ def test_create_numeric_server_name(self):
+ # Create a server with a numeric name
+ if self.__class__._interface == "xml":
+ raise self.skipException("Not testable in XML")
+
+ server_name = 12345
+ self.assertRaises(exceptions.BadRequest,
+ self.create_test_server,
+ name=server_name)
+
+ @attr(type=['negative', 'gate'])
+ def test_create_server_name_length_exceeds_256(self):
+ # Create a server with name length exceeding 256 characters
+
+ server_name = 'a' * 256
+ self.assertRaises(exceptions.BadRequest,
+ self.create_test_server,
+ name=server_name)
+
+ @attr(type=['negative', 'gate'])
+ def test_create_with_invalid_network_uuid(self):
+ # Pass invalid network uuid while creating a server
+
+ networks = [{'fixed_ip': '10.0.1.1', 'uuid': 'a-b-c-d-e-f-g-h-i-j'}]
+
+ self.assertRaises(exceptions.BadRequest,
+ self.create_test_server,
+ networks=networks)
+
+ @attr(type=['negative', 'gate'])
+ def test_create_with_non_existant_keypair(self):
+ # Pass a non-existent keypair while creating a server
+
+ key_name = data_utils.rand_name('key')
+ self.assertRaises(exceptions.BadRequest,
+ self.create_test_server,
+ key_name=key_name)
+
+ @attr(type=['negative', 'gate'])
+ def test_create_server_metadata_exceeds_length_limit(self):
+ # Pass really long metadata while creating a server
+
+ metadata = {'a': 'b' * 260}
+ self.assertRaises(exceptions.OverLimit,
+ self.create_test_server,
+ meta=metadata)
+
+ @attr(type=['negative', 'gate'])
+ def test_update_name_of_non_existent_server(self):
+ # Update name of a non-existent server
+
+ server_name = data_utils.rand_name('server')
+ new_name = data_utils.rand_name('server') + '_updated'
+
+ self.assertRaises(exceptions.NotFound, self.client.update_server,
+ server_name, name=new_name)
+
+ @attr(type=['negative', 'gate'])
+ def test_update_server_set_empty_name(self):
+ # Update name of the server to an empty string
+
+ server_name = data_utils.rand_name('server')
+ new_name = ''
+
+ self.assertRaises(exceptions.BadRequest, self.client.update_server,
+ server_name, name=new_name)
+
+ @attr(type=['negative', 'gate'])
+ def test_update_server_of_another_tenant(self):
+ # Update name of a server that belongs to another tenant
+
+ new_name = self.server_id + '_new'
+ self.assertRaises(exceptions.NotFound,
+ self.alt_client.update_server, self.server_id,
+ name=new_name)
+
+ @attr(type=['negative', 'gate'])
+ def test_update_server_name_length_exceeds_256(self):
+ # Update name of server exceed the name length limit
+
+ new_name = 'a' * 256
+ self.assertRaises(exceptions.BadRequest,
+ self.client.update_server,
+ self.server_id,
+ name=new_name)
+
+ @attr(type=['negative', 'gate'])
+ def test_delete_non_existent_server(self):
+ # Delete a non existent server
+
+ self.assertRaises(exceptions.NotFound, self.client.delete_server,
+ '999erra43')
+
+ @attr(type=['negative', 'gate'])
+ def test_delete_a_server_of_another_tenant(self):
+ # Delete a server that belongs to another tenant
+ self.assertRaises(exceptions.NotFound,
+ self.alt_client.delete_server,
+ self.server_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_delete_server_pass_negative_id(self):
+ # Pass an invalid string parameter to delete server
+
+ self.assertRaises(exceptions.NotFound, self.client.delete_server, -1)
+
+ @attr(type=['negative', 'gate'])
+ def test_delete_server_pass_id_exceeding_length_limit(self):
+ # Pass a server ID that exceeds length limit to delete server
+
+ self.assertRaises(exceptions.NotFound, self.client.delete_server,
+ sys.maxint + 1)
+
+ @attr(type=['negative', 'gate'])
+ def test_create_with_nonexistent_security_group(self):
+ # Create a server with a nonexistent security group
+
+ security_groups = [{'name': 'does_not_exist'}]
+ self.assertRaises(exceptions.BadRequest,
+ self.create_test_server,
+ security_groups=security_groups)
+
+ @attr(type=['negative', 'gate'])
+ def test_get_non_existent_server(self):
+ # Get a non existent server details
+
+ self.assertRaises(exceptions.NotFound, self.client.get_server,
+ '999erra43')
+
+ @attr(type=['negative', 'gate'])
+ def test_stop_non_existent_server(self):
+ # Stop a non existent server
+ nonexistent_server = str(uuid.uuid4())
+ self.assertRaises(exceptions.NotFound, self.servers_client.stop,
+ nonexistent_server)
+
+ @attr(type=['negative', 'gate'])
+ def test_pause_non_existent_server(self):
+ # pause a non existent server
+ nonexistent_server = str(uuid.uuid4())
+ self.assertRaises(exceptions.NotFound, self.client.pause_server,
+ nonexistent_server)
+
+ @attr(type=['negative', 'gate'])
+ def test_unpause_non_existent_server(self):
+ # unpause a non existent server
+ nonexistent_server = str(uuid.uuid4())
+ self.assertRaises(exceptions.NotFound, self.client.unpause_server,
+ nonexistent_server)
+
+ @attr(type=['negative', 'gate'])
+ def test_unpause_server_invalid_state(self):
+ # unpause an active server.
+ self.assertRaises(exceptions.Conflict,
+ self.client.unpause_server,
+ self.server_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_suspend_non_existent_server(self):
+ # suspend a non existent server
+ nonexistent_server = str(uuid.uuid4())
+ self.assertRaises(exceptions.NotFound, self.client.suspend_server,
+ nonexistent_server)
+
+ @attr(type=['negative', 'gate'])
+ def test_suspend_server_invalid_state(self):
+ # suspend a suspended server.
+ resp, _ = self.client.suspend_server(self.server_id)
+ self.addCleanup(self.client.resume_server,
+ self.server_id)
+ self.assertEqual(202, resp.status)
+ self.client.wait_for_server_status(self.server_id, 'SUSPENDED')
+ self.assertRaises(exceptions.Conflict,
+ self.client.suspend_server,
+ self.server_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_resume_non_existent_server(self):
+ # resume a non existent server
+ nonexistent_server = str(uuid.uuid4())
+ self.assertRaises(exceptions.NotFound, self.client.resume_server,
+ nonexistent_server)
+
+ @attr(type=['negative', 'gate'])
+ def test_resume_server_invalid_state(self):
+ # resume an active server.
+ self.assertRaises(exceptions.Conflict,
+ self.client.resume_server,
+ self.server_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_get_console_output_of_non_existent_server(self):
+ # get the console output for a non existent server
+ nonexistent_server = str(uuid.uuid4())
+ self.assertRaises(exceptions.NotFound,
+ self.client.get_console_output,
+ nonexistent_server, 10)
+
+ @attr(type=['negative', 'gate'])
+ def test_force_delete_nonexistent_server_id(self):
+ non_existent_server_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.NotFound,
+ self.client.force_delete_server,
+ non_existent_server_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_force_delete_server_invalid_state(self):
+ # we can only force-delete a server in 'soft-delete' state
+ self.assertRaises(exceptions.Conflict,
+ self.client.force_delete_server,
+ self.server_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_restore_nonexistent_server_id(self):
+ non_existent_server_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.NotFound,
+ self.client.restore_soft_deleted_server,
+ non_existent_server_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_restore_server_invalid_state(self):
+ # we can only restore-delete a server in 'soft-delete' state
+ self.assertRaises(exceptions.Conflict,
+ self.client.restore_soft_deleted_server,
+ self.server_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_shelve_non_existent_server(self):
+ # shelve a non existent server
+ nonexistent_server = str(uuid.uuid4())
+ self.assertRaises(exceptions.NotFound, self.client.shelve_server,
+ nonexistent_server)
+
+ @attr(type=['negative', 'gate'])
+ def test_shelve_shelved_server(self):
+ # shelve a shelved server.
+ resp, server = self.client.shelve_server(self.server_id)
+ self.assertEqual(202, resp.status)
+ self.addCleanup(self.client.unshelve_server, self.server_id)
+
+ offload_time = self.config.compute.shelved_offload_time
+ if offload_time >= 0:
+ self.client.wait_for_server_status(self.server_id,
+ 'SHELVED_OFFLOADED',
+ extra_timeout=offload_time)
+ else:
+ self.client.wait_for_server_status(self.server_id,
+ 'SHELVED')
+
+ resp, server = self.client.get_server(self.server_id)
+ image_name = server['name'] + '-shelved'
+ params = {'name': image_name}
+ resp, images = self.images_client.list_images(params)
+ self.assertEqual(1, len(images))
+ self.assertEqual(image_name, images[0]['name'])
+
+ self.assertRaises(exceptions.Conflict,
+ self.client.shelve_server,
+ self.server_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_unshelve_non_existent_server(self):
+ # unshelve a non existent server
+ nonexistent_server = str(uuid.uuid4())
+ self.assertRaises(exceptions.NotFound, self.client.unshelve_server,
+ nonexistent_server)
+
+ @attr(type=['negative', 'gate'])
+ def test_unshelve_server_invalid_state(self):
+ # unshelve an active server.
+ self.assertRaises(exceptions.Conflict,
+ self.client.unshelve_server,
+ self.server_id)
+
+
+class ServersNegativeTestXML(ServersNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index 133bae0..6408c15 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -1,8 +1,8 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2013 OpenStack Foundation
+# Copyright 2013 IBM Corp
# All Rights Reserved.
-# Copyright 2013 IBM Corp.
#
# 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
@@ -21,28 +21,16 @@
from tempest.api.image import base
from tempest.common.utils import data_utils
-from tempest import exceptions
from tempest.test import attr
-class CreateRegisterImagesTest(base.BaseV2ImageTest):
+class BasicOperationsImagesTest(base.BaseV2ImageTest):
"""
- Here we test the registration and creation of images
+ Here we test the basic operations of images
"""
@attr(type='gate')
- def test_register_with_invalid_container_format(self):
- # Negative tests for invalid data supplied to POST /images
- self.assertRaises(exceptions.BadRequest, self.client.create_image,
- 'test', 'wrong', 'vhd')
-
- @attr(type='gate')
- def test_register_with_invalid_disk_format(self):
- self.assertRaises(exceptions.BadRequest, self.client.create_image,
- 'test', 'bare', 'wrong')
-
- @attr(type='gate')
def test_register_upload_get_image_file(self):
"""
@@ -83,6 +71,28 @@
self.assertEqual(200, resp.status)
self.assertEqual(file_content, body)
+ @attr(type='gate')
+ def test_delete_image(self):
+ # Deletes a image by image_id
+
+ # Create image
+ image_name = data_utils.rand_name('image')
+ resp, body = self.client.create_image(name=image_name,
+ container_format='bare',
+ disk_format='raw',
+ visibility='public')
+ self.assertEqual(201, resp.status)
+ image_id = body['id']
+
+ # Delete Image
+ self.client.delete_image(image_id)
+ self.client.wait_for_resource_deletion(image_id)
+
+ # Verifying deletion
+ resp, images = self.client.image_list()
+ self.assertEqual(resp.status, 200)
+ self.assertNotIn(image_id, images)
+
class ListImagesTest(base.BaseV2ImageTest):
diff --git a/tempest/api/image/v2/test_images_negative.py b/tempest/api/image/v2/test_images_negative.py
index 5bdaa99..1cd6f29 100644
--- a/tempest/api/image/v2/test_images_negative.py
+++ b/tempest/api/image/v2/test_images_negative.py
@@ -82,3 +82,14 @@
image_id = ""
self.assertRaises(exceptions.NotFound, self.client.delete_image,
image_id)
+
+ @attr(type=['negative', 'gate'])
+ def test_register_with_invalid_container_format(self):
+ # Negative tests for invalid data supplied to POST /images
+ self.assertRaises(exceptions.BadRequest, self.client.create_image,
+ 'test', 'wrong', 'vhd')
+
+ @attr(type=['negative', 'gate'])
+ def test_register_with_invalid_disk_format(self):
+ self.assertRaises(exceptions.BadRequest, self.client.create_image,
+ 'test', 'bare', 'wrong')
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index 6bc350a..03e8469 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -29,6 +29,7 @@
def setUpClass(cls):
super(VolumeMultiBackendTest, cls).setUpClass()
if not cls.config.volume_feature_enabled.multi_backend:
+ cls.tearDownClass()
raise cls.skipException("Cinder multi-backend feature disabled")
cls.backend1_name = cls.config.volume.backend1_name
@@ -89,12 +90,14 @@
@classmethod
def tearDownClass(cls):
# volumes deletion
- for volume_id in cls.volume_id_list:
+ volume_id_list = getattr(cls, 'volume_id_list', [])
+ for volume_id in volume_id_list:
cls.volume_client.delete_volume(volume_id)
cls.volume_client.wait_for_resource_deletion(volume_id)
# volume types deletion
- for volume_type_id in cls.volume_type_id_list:
+ volume_type_id_list = getattr(cls, 'volume_type_id_list', [])
+ for volume_type_id in volume_type_id_list:
cls.type_client.delete_volume_type(volume_type_id)
super(VolumeMultiBackendTest, cls).tearDownClass()
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 30c2c74..8581d16 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -140,6 +140,35 @@
self.assertEqual(200, resp.status)
self.assertIn('available', body['status'])
+ def _is_true(self, val):
+ return val in ['true', 'True', True]
+
+ @attr(type='gate')
+ def test_volume_readonly_update(self):
+ # Update volume readonly true
+ readonly = True
+ resp, body = self.client.update_volume_readonly(self.volume['id'],
+ readonly)
+ self.assertEqual(202, resp.status)
+
+ # Get Volume information
+ resp, fetched_volume = self.client.get_volume(self.volume['id'])
+ bool_flag = self._is_true(fetched_volume['metadata']['readonly'])
+ self.assertEqual(200, resp.status)
+ self.assertEqual(True, bool_flag)
+
+ # Update volume readonly false
+ readonly = False
+ resp, body = self.client.update_volume_readonly(self.volume['id'],
+ readonly)
+ self.assertEqual(202, resp.status)
+
+ # Get Volume information
+ resp, fetched_volume = self.client.get_volume(self.volume['id'])
+ bool_flag = self._is_true(fetched_volume['metadata']['readonly'])
+ self.assertEqual(200, resp.status)
+ self.assertEqual(False, bool_flag)
+
class VolumesActionsTestXML(VolumesActionsTest):
_interface = "xml"
diff --git a/tempest/cli/simple_read_only/test_cinder.py b/tempest/cli/simple_read_only/test_cinder.py
index 25157a4..eb5df63 100644
--- a/tempest/cli/simple_read_only/test_cinder.py
+++ b/tempest/cli/simple_read_only/test_cinder.py
@@ -136,3 +136,139 @@
if not region:
region = self.config.identity.region
self.cinder('list', flags='--os-region-name ' + region)
+
+
+class SimpleReadOnlyCinderClientV2Test(tempest.cli.ClientTestBase):
+ """Basic, read-only tests for Cinder v2 CLI client.
+
+ Checks return values and output of read-only commands.
+ These tests do not presume any content, nor do they create
+ their own. They only verify the structure of output if present.
+ """
+
+ version_flag = '--os-volume-api-version'
+ version_support = 2
+
+ def _get_resp_from_diff_versions(self, version_tuple, cmd, param=None):
+ # Get each result from different versions.
+ # Note(wingwj): If you want to get each cli response on v1&v2 version,
+ # the value of version_tuple should be (1, 2).
+ # The function will be also available for v3 in future
+
+ resp_set = []
+ for i in set(version_tuple):
+ if not isinstance(i, int) or \
+ i not in range(1, self.version_support + 1):
+ LOG.error("version_num must be a positive integer, "
+ "Cinder supports %s versions now"
+ % self.version_support)
+ continue
+ else:
+ if not param:
+ item = self.cinder(cmd,
+ flags=self.version_flag + ' ' + str(i))
+ else:
+ item = self.cinder(cmd, params=param,
+ flags=self.version_flag + ' ' + str(i))
+ resp_set.append(item)
+
+ return resp_set
+
+ def _check_accordance_between_two_versions(self, input1, input2,
+ expected=True):
+ # Check input1 and input2 is not Empty first
+ self.assertNotEqual("", input1)
+ self.assertNotEqual("", input2)
+ # Compare input1 and input2 based on 'expected' parameter
+ if expected:
+ self.assertEqual(input1, input2)
+ else:
+ self.assertNotEqual(input1, input2)
+
+ def test_cinder_volumes_list_in_v2(self):
+ cmd = 'list'
+ v1, v2 = self._get_resp_from_diff_versions((1, 2), cmd)
+ # This CLI representation in v1/v2 is different.
+ self._check_accordance_between_two_versions(v1, v2, False)
+
+ def test_cinder_absolute_limit_list_in_v2(self):
+ cmd = 'absolute-limits'
+ v1, v2 = self._get_resp_from_diff_versions((1, 2), cmd)
+ self._check_accordance_between_two_versions(v1, v2, True)
+
+ def test_cinder_backup_list_in_v2(self):
+ cmd = 'backup-list'
+ v1, v2 = self._get_resp_from_diff_versions((1, 2), cmd)
+ self._check_accordance_between_two_versions(v1, v2, True)
+
+ def test_cinder_extra_specs_list_in_v2(self):
+ cmd = 'extra-specs-list'
+ v1, v2 = self._get_resp_from_diff_versions((1, 2), cmd)
+ self._check_accordance_between_two_versions(v1, v2, True)
+
+ def test_cinder_quota_class_show_in_v2(self):
+ cmd = 'quota-class-show'
+ v1, v2 = self._get_resp_from_diff_versions((1, 2), cmd, 'abc')
+ self._check_accordance_between_two_versions(v1, v2, True)
+
+ def test_cinder_quota_defaults_in_v2(self):
+ cmd = 'quota-defaults'
+ v1, v2 = self._get_resp_from_diff_versions(
+ (1, 2), cmd, self.identity.admin_tenant_name)
+ self._check_accordance_between_two_versions(v1, v2, True)
+
+ def test_cinder_quota_show_in_v2(self):
+ cmd = 'quota-show'
+ v1, v2 = self._get_resp_from_diff_versions(
+ (1, 2), cmd, self.identity.admin_tenant_name)
+ self._check_accordance_between_two_versions(v1, v2, True)
+
+ def test_cinder_rate_limits_in_v2(self):
+ cmd = 'rate-limits'
+ v1, v2 = self._get_resp_from_diff_versions((1, 2), cmd)
+ self._check_accordance_between_two_versions(v1, v2, True)
+
+ def test_cinder_snapshot_list_in_v2(self):
+ cmd = 'snapshot-list'
+ v1, v2 = self._get_resp_from_diff_versions((1, 2), cmd)
+ # This CLI representation in v1/v2 is different.
+ self._check_accordance_between_two_versions(v1, v2, False)
+
+ def test_cinder_type_list_in_v2(self):
+ cmd = 'type-list'
+ v1, v2 = self._get_resp_from_diff_versions((1, 2), cmd)
+ self._check_accordance_between_two_versions(v1, v2, True)
+
+ def test_cinder_availability_zone_list_in_v2(self):
+ cmd = 'availability-zone-list'
+ v1, v2 = self._get_resp_from_diff_versions((1, 2), cmd)
+ self._check_accordance_between_two_versions(v1, v2, True)
+
+ def test_cinder_service_list_in_v2(self):
+ cmd = 'service-list'
+ v1, v2 = self._get_resp_from_diff_versions((1, 2), cmd)
+ self.assertNotEqual("", v1)
+ self.assertNotEqual("", v2)
+
+ # The 'Updated_at' and 'State' item may be changed
+ # due to the periodical update-task. Need to omit.
+ v1 = self.parser.listing(v1)
+ v2 = self.parser.listing(v2)
+ for i in v1:
+ del i['Updated_at']
+ del i['State']
+ for j in v2:
+ del j['Updated_at']
+ del j['State']
+ self.assertEqual(v1, v2)
+
+ def test_cinder_transfer_list_in_v2(self):
+ cmd = 'transfer-list'
+ v1, v2 = self._get_resp_from_diff_versions((1, 2), cmd)
+ self._check_accordance_between_two_versions(v1, v2, True)
+
+ def test_cinder_bash_completion_in_v2(self):
+ cmd = 'bash-completion'
+ v1, v2 = self._get_resp_from_diff_versions((1, 2), cmd)
+ # This CLI representation in v1/v2 is different.
+ self._check_accordance_between_two_versions(v1, v2, False)
diff --git a/tempest/clients.py b/tempest/clients.py
index 0f54fc0..291b946 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -54,6 +54,8 @@
AvailabilityZoneV3ClientJSON
from tempest.services.compute.v3.json.extensions_client import \
ExtensionsV3ClientJSON
+from tempest.services.compute.v3.json.hypervisor_client import \
+ HypervisorV3ClientJSON
from tempest.services.compute.v3.json.interfaces_client import \
InterfacesV3ClientJSON
from tempest.services.compute.v3.json.servers_client import \
@@ -64,6 +66,8 @@
AvailabilityZoneV3ClientXML
from tempest.services.compute.v3.xml.extensions_client import \
ExtensionsV3ClientXML
+from tempest.services.compute.v3.xml.hypervisor_client import \
+ HypervisorV3ClientXML
from tempest.services.compute.v3.xml.interfaces_client import \
InterfacesV3ClientXML
from tempest.services.compute.v3.xml.servers_client import ServersV3ClientXML
@@ -228,6 +232,7 @@
self.services_client = ServicesClientXML(*client_args)
self.tenant_usages_client = TenantUsagesClientXML(*client_args)
self.policy_client = PolicyClientXML(*client_args)
+ self.hypervisor_v3_client = HypervisorV3ClientXML(*client_args)
self.hypervisor_client = HypervisorClientXML(*client_args)
self.token_v3_client = V3TokenClientXML(*client_args)
self.network_client = NetworkClientXML(*client_args)
@@ -275,6 +280,7 @@
self.services_client = ServicesClientJSON(*client_args)
self.tenant_usages_client = TenantUsagesClientJSON(*client_args)
self.policy_client = PolicyClientJSON(*client_args)
+ self.hypervisor_v3_client = HypervisorV3ClientJSON(*client_args)
self.hypervisor_client = HypervisorClientJSON(*client_args)
self.token_v3_client = V3TokenClientJSON(*client_args)
self.network_client = NetworkClientJSON(*client_args)
@@ -312,11 +318,12 @@
managed client objects
"""
- def __init__(self):
+ def __init__(self, interface='json'):
conf = config.TempestConfig()
super(AltManager, self).__init__(conf.identity.alt_username,
conf.identity.alt_password,
- conf.identity.alt_tenant_name)
+ conf.identity.alt_tenant_name,
+ interface=interface)
class AdminManager(Manager):
diff --git a/tempest/services/compute/v3/json/hypervisor_client.py b/tempest/services/compute/v3/json/hypervisor_client.py
new file mode 100644
index 0000000..fa1255a
--- /dev/null
+++ b/tempest/services/compute/v3/json/hypervisor_client.py
@@ -0,0 +1,71 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corporation.
+# 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 HypervisorV3ClientJSON(RestClient):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(HypervisorV3ClientJSON, self).__init__(config, username,
+ password, auth_url,
+ tenant_name)
+ self.service = self.config.compute.catalog_v3_type
+
+ def get_hypervisor_list(self):
+ """List hypervisors information."""
+ resp, body = self.get('os-hypervisors')
+ body = json.loads(body)
+ return resp, body['hypervisors']
+
+ def get_hypervisor_list_details(self):
+ """Show detailed hypervisors information."""
+ resp, body = self.get('os-hypervisors/detail')
+ body = json.loads(body)
+ return resp, body['hypervisors']
+
+ def get_hypervisor_show_details(self, hyper_id):
+ """Display the details of the specified hypervisor."""
+ resp, body = self.get('os-hypervisors/%s' % hyper_id)
+ body = json.loads(body)
+ return resp, body['hypervisor']
+
+ def get_hypervisor_servers(self, hyper_name):
+ """List instances belonging to the specified hypervisor."""
+ resp, body = self.get('os-hypervisors/%s/servers' % hyper_name)
+ body = json.loads(body)
+ return resp, body['hypervisor']
+
+ def get_hypervisor_stats(self):
+ """Get hypervisor statistics over all compute nodes."""
+ resp, body = self.get('os-hypervisors/statistics')
+ body = json.loads(body)
+ return resp, body['hypervisor_statistics']
+
+ def get_hypervisor_uptime(self, hyper_id):
+ """Display the uptime of the specified hypervisor."""
+ resp, body = self.get('os-hypervisors/%s/uptime' % hyper_id)
+ body = json.loads(body)
+ return resp, body['hypervisor']
+
+ def search_hypervisor(self, hyper_name):
+ """Search specified hypervisor."""
+ resp, body = self.get('os-hypervisors/search?query=%s' % hyper_name)
+ body = json.loads(body)
+ return resp, body['hypervisors']
diff --git a/tempest/services/compute/v3/xml/hypervisor_client.py b/tempest/services/compute/v3/xml/hypervisor_client.py
new file mode 100644
index 0000000..ce0207d
--- /dev/null
+++ b/tempest/services/compute/v3/xml/hypervisor_client.py
@@ -0,0 +1,79 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corporation
+# 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 xml_to_json
+
+
+class HypervisorV3ClientXML(RestClientXML):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(HypervisorV3ClientXML, self).__init__(config, username,
+ password, auth_url,
+ tenant_name)
+ self.service = self.config.compute.catalog_v3_type
+
+ def _parse_array(self, node):
+ return [xml_to_json(x) for x in node]
+
+ def get_hypervisor_list(self):
+ """List hypervisors information."""
+ resp, body = self.get('os-hypervisors', self.headers)
+ hypervisors = self._parse_array(etree.fromstring(body))
+ return resp, hypervisors
+
+ def get_hypervisor_list_details(self):
+ """Show detailed hypervisors information."""
+ resp, body = self.get('os-hypervisors/detail', self.headers)
+ hypervisors = self._parse_array(etree.fromstring(body))
+ return resp, hypervisors
+
+ def get_hypervisor_show_details(self, hyper_id):
+ """Display the details of the specified hypervisor."""
+ resp, body = self.get('os-hypervisors/%s' % hyper_id,
+ self.headers)
+ hypervisor = xml_to_json(etree.fromstring(body))
+ return resp, hypervisor
+
+ def get_hypervisor_servers(self, hyper_name):
+ """List instances belonging to the specified hypervisor."""
+ resp, body = self.get('os-hypervisors/%s/servers' % hyper_name,
+ self.headers)
+ hypervisors = self._parse_array(etree.fromstring(body))
+ return resp, hypervisors
+
+ def get_hypervisor_stats(self):
+ """Get hypervisor statistics over all compute nodes."""
+ resp, body = self.get('os-hypervisors/statistics', self.headers)
+ stats = xml_to_json(etree.fromstring(body))
+ return resp, stats
+
+ def get_hypervisor_uptime(self, hyper_id):
+ """Display the uptime of the specified hypervisor."""
+ resp, body = self.get('os-hypervisors/%s/uptime' % hyper_id,
+ self.headers)
+ uptime = xml_to_json(etree.fromstring(body))
+ return resp, uptime
+
+ def search_hypervisor(self, hyper_name):
+ """Search specified hypervisor."""
+ resp, body = self.get('os-hypervisors/search?query=%s' % hyper_name,
+ self.headers)
+ hypervisors = self._parse_array(etree.fromstring(body))
+ return resp, hypervisors
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index 3e5413c..7d40d0e 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -193,6 +193,10 @@
server = self._parse_server(etree.fromstring(body))
return resp, server
+ def migrate_server(self, server_id, **kwargs):
+ """Migrates the given server ."""
+ return self.action(server_id, 'migrate', None, **kwargs)
+
def lock_server(self, server_id, **kwargs):
"""Locks the given server."""
return self.action(server_id, 'lock', None, **kwargs)
diff --git a/tempest/services/volume/json/volumes_client.py b/tempest/services/volume/json/volumes_client.py
index 93b28a2..b4a1a68 100644
--- a/tempest/services/volume/json/volumes_client.py
+++ b/tempest/services/volume/json/volumes_client.py
@@ -246,3 +246,13 @@
resp, body = self.post(url, post_body, self.headers)
body = json.loads(body)
return resp, body['transfer']
+
+ def update_volume_readonly(self, volume_id, readonly):
+ """Update the Specified Volume readonly."""
+ post_body = {
+ 'readonly': readonly
+ }
+ post_body = json.dumps({'os-update_readonly_flag': post_body})
+ url = 'volumes/%s/action' % (volume_id)
+ resp, body = self.post(url, post_body, self.headers)
+ return resp, body
diff --git a/tempest/services/volume/xml/volumes_client.py b/tempest/services/volume/xml/volumes_client.py
index b1e54ed..21254aa 100644
--- a/tempest/services/volume/xml/volumes_client.py
+++ b/tempest/services/volume/xml/volumes_client.py
@@ -337,3 +337,13 @@
resp, body = self.post(url, str(Document(post_body)), self.headers)
volume = xml_to_json(etree.fromstring(body))
return resp, volume
+
+ def update_volume_readonly(self, volume_id, readonly):
+ """Update the Specified Volume readonly."""
+ post_body = Element("os-update_readonly_flag",
+ readonly=readonly)
+ url = 'volumes/%s/action' % str(volume_id)
+ resp, body = self.post(url, str(Document(post_body)), self.headers)
+ if body:
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
diff --git a/tools/check_logs.py b/tools/check_logs.py
index 68ffced..ded51b4 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -63,7 +63,7 @@
whitelisted = True
break
if not whitelisted or dump_all_errors:
- if not print_log_name:
+ if print_log_name:
print("Log File: %s" % name)
print_log_name = False
if not whitelisted: