Merge "Missing image-del func in test_create_delete_image"
diff --git a/cli/__init__.py b/cli/__init__.py
index 7a92260..a3038d2 100644
--- a/cli/__init__.py
+++ b/cli/__init__.py
@@ -87,6 +87,15 @@
flags = creds + ' ' + flags
return self.cmd(cmd, action, flags, params, fail_ok)
+ def check_output(self, cmd, **kwargs):
+ # substitutes subprocess.check_output which is not in python2.6
+ kwargs['stdout'] = subprocess.PIPE
+ proc = subprocess.Popen(cmd, **kwargs)
+ output = proc.communicate()[0]
+ if proc.returncode != 0:
+ raise CommandFailed(proc.returncode, cmd, output)
+ return output
+
def cmd(self, cmd, action, flags='', params='', fail_ok=False,
merge_stderr=False):
"""Executes specified command for the given action."""
@@ -96,10 +105,10 @@
cmd = shlex.split(cmd)
try:
if merge_stderr:
- result = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+ result = self.check_output(cmd, stderr=subprocess.STDOUT)
else:
- devnull = open('/dev/null', 'w')
- result = subprocess.check_output(cmd, stderr=devnull)
+ with open('/dev/null', 'w') as devnull:
+ result = self.check_output(cmd, stderr=devnull)
except subprocess.CalledProcessError, e:
LOG.error("command output:\n%s" % e.output)
raise
@@ -110,3 +119,10 @@
for item in items:
for field in field_names:
self.assertIn(field, item)
+
+
+class CommandFailed(subprocess.CalledProcessError):
+ # adds output attribute for python2.6
+ def __init__(self, returncode, cmd, output):
+ super(CommandFailed, self).__init__(returncode, cmd)
+ self.output = output
diff --git a/tempest/clients.py b/tempest/clients.py
index 8f8d5e6..732a982 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -22,6 +22,8 @@
from tempest.services import botoclients
from tempest.services.compute.json.aggregates_client import \
AggregatesClientJSON
+from tempest.services.compute.json.availability_zone_client import \
+ AvailabilityZoneClientJSON
from tempest.services.compute.json.extensions_client import \
ExtensionsClientJSON
from tempest.services.compute.json.fixed_ips_client import FixedIPsClientJSON
@@ -40,6 +42,8 @@
from tempest.services.compute.json.servers_client import ServersClientJSON
from tempest.services.compute.json.volumes_extensions_client import \
VolumesExtensionsClientJSON
+from tempest.services.compute.xml.availability_zone_client import \
+ AvailabilityZoneClientXML
from tempest.services.compute.xml.extensions_client import ExtensionsClientXML
from tempest.services.compute.xml.fixed_ips_client import FixedIPsClientXML
from tempest.services.compute.xml.flavors_client import FlavorsClientXML
@@ -174,6 +178,11 @@
"xml": FixedIPsClientXML
}
+AVAILABILITY_ZONE_CLIENT = {
+ "json": AvailabilityZoneClientJSON,
+ "xml": AvailabilityZoneClientXML,
+}
+
class Manager(object):
@@ -238,6 +247,8 @@
self.interfaces_client = INTERFACES_CLIENT[interface](*client_args)
self.endpoints_client = ENDPOINT_CLIENT[interface](*client_args)
self.fixed_ips_client = FIXED_IPS_CLIENT[interface](*client_args)
+ self.availability_zone_client = \
+ AVAILABILITY_ZONE_CLIENT[interface](*client_args)
except KeyError:
msg = "Unsupported interface type `%s'" % interface
raise exceptions.InvalidConfiguration(msg)
diff --git a/tempest/services/compute/json/availability_zone_client.py b/tempest/services/compute/json/availability_zone_client.py
new file mode 100644
index 0000000..b11871b
--- /dev/null
+++ b/tempest/services/compute/json/availability_zone_client.py
@@ -0,0 +1,39 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 NEC 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 AvailabilityZoneClientJSON(RestClient):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(AvailabilityZoneClientJSON, self).__init__(config, username,
+ password, auth_url,
+ tenant_name)
+ self.service = self.config.compute.catalog_type
+
+ def get_availability_zone_list(self):
+ resp, body = self.get('os-availability-zone')
+ body = json.loads(body)
+ return resp, body['availabilityZoneInfo']
+
+ def get_availability_zone_list_detail(self):
+ resp, body = self.get('os-availability-zone/detail')
+ body = json.loads(body)
+ return resp, body['availabilityZoneInfo']
diff --git a/tempest/services/compute/xml/availability_zone_client.py b/tempest/services/compute/xml/availability_zone_client.py
new file mode 100644
index 0000000..ae93774
--- /dev/null
+++ b/tempest/services/compute/xml/availability_zone_client.py
@@ -0,0 +1,43 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 NEC 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 AvailabilityZoneClientXML(RestClientXML):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(AvailabilityZoneClientXML, self).__init__(config, username,
+ password, auth_url,
+ tenant_name)
+ self.service = self.config.compute.catalog_type
+
+ def _parse_array(self, node):
+ return [xml_to_json(x) for x in node]
+
+ def get_availability_zone_list(self):
+ resp, body = self.get('os-availability-zone', self.headers)
+ availability_zone = self._parse_array(etree.fromstring(body))
+ return resp, availability_zone
+
+ def get_availability_zone_list_detail(self):
+ resp, body = self.get('os-availability-zone/detail', self.headers)
+ availability_zone = self._parse_array(etree.fromstring(body))
+ return resp, availability_zone
diff --git a/tempest/test.py b/tempest/test.py
index ccb2251..b0038e0 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -56,6 +56,11 @@
#NOTE(afazekas): inspection workaround
BaseTestCase.config = config.TempestConfig()
+ @classmethod
+ def setUpClass(cls):
+ if hasattr(super(BaseTestCase, cls), 'setUpClass'):
+ super(BaseTestCase, cls).setUpClass()
+
class TestCase(BaseTestCase):
"""Base test case class for all Tempest tests
diff --git a/tempest/tests/compute/admin/test_availability_zone.py b/tempest/tests/compute/admin/test_availability_zone.py
new file mode 100644
index 0000000..98ad49c
--- /dev/null
+++ b/tempest/tests/compute/admin/test_availability_zone.py
@@ -0,0 +1,69 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 NEC 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 import exceptions
+from tempest.test import attr
+from tempest.tests.compute import base
+
+
+class AvailabilityZoneAdminTestJSON(base.BaseComputeAdminTest):
+
+ """
+ Tests Availability Zone API List that require admin privileges
+ """
+
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(AvailabilityZoneAdminTestJSON, cls).setUpClass()
+ cls.client = cls.os_adm.availability_zone_client
+ cls.non_adm_client = cls.availability_zone_client
+
+ @attr('positive')
+ def test_get_availability_zone_list(self):
+ # List of availability zone
+ resp, availability_zone = self.client.get_availability_zone_list()
+ self.assertEqual(200, resp.status)
+ self.assertTrue(len(availability_zone) > 0)
+
+ @attr('positive')
+ def test_get_availability_zone_list_detail(self):
+ # List of availability zones and available services
+ resp, availability_zone = \
+ self.client.get_availability_zone_list_detail()
+ self.assertEqual(200, resp.status)
+ self.assertTrue(len(availability_zone) > 0)
+
+ @attr('positive')
+ def test_get_availability_zone_list_with_non_admin_user(self):
+ # List of availability zone with non admin user
+ resp, availability_zone = \
+ self.non_adm_client.get_availability_zone_list()
+ self.assertEqual(200, resp.status)
+ self.assertTrue(len(availability_zone) > 0)
+
+ @attr('negative')
+ def test_get_availability_zone_list_detail_with_non_admin_user(self):
+ # List of availability zones and available services with non admin user
+ self.assertRaises(
+ exceptions.Unauthorized,
+ self.non_adm_client.get_availability_zone_list_detail)
+
+
+class AvailabilityZoneAdminTestXML(AvailabilityZoneAdminTestJSON):
+ _interface = 'xml'
diff --git a/tempest/tests/compute/base.py b/tempest/tests/compute/base.py
index 7716922..221cfb6 100644
--- a/tempest/tests/compute/base.py
+++ b/tempest/tests/compute/base.py
@@ -61,6 +61,7 @@
cls.volumes_client = os.volumes_client
cls.interfaces_client = os.interfaces_client
cls.fixed_ips_client = os.fixed_ips_client
+ cls.availability_zone_client = os.availability_zone_client
cls.build_interval = cls.config.compute.build_interval
cls.build_timeout = cls.config.compute.build_timeout
cls.ssh_user = cls.config.compute.ssh_user
diff --git a/tempest/tests/compute/images/test_images_oneserver.py b/tempest/tests/compute/images/test_images_oneserver.py
index bcd09f6..dfc16f4 100644
--- a/tempest/tests/compute/images/test_images_oneserver.py
+++ b/tempest/tests/compute/images/test_images_oneserver.py
@@ -72,7 +72,6 @@
snapshot_name)
@attr(type='negative')
- @testtools.skip("Until Bug #1005423 is fixed")
def test_create_image_specify_invalid_metadata(self):
# Return an error when creating image with invalid metadata
snapshot_name = rand_name('test-snap-')
@@ -81,12 +80,11 @@
self.server['id'], snapshot_name, meta)
@attr(type='negative')
- @testtools.skip("Until Bug #1005423 is fixed")
def test_create_image_specify_metadata_over_limits(self):
# Return an error when creating image with meta data over 256 chars
snapshot_name = rand_name('test-snap-')
meta = {'a' * 260: 'b' * 260}
- self.assertRaises(exceptions.OverLimit, self.client.create_image,
+ self.assertRaises(exceptions.BadRequest, self.client.create_image,
self.server['id'], snapshot_name, meta)
@attr(type='negative')
diff --git a/tempest/tests/compute/limits/test_absolute_limits.py b/tempest/tests/compute/limits/test_absolute_limits.py
index 2b31680..6933fd7 100644
--- a/tempest/tests/compute/limits/test_absolute_limits.py
+++ b/tempest/tests/compute/limits/test_absolute_limits.py
@@ -15,6 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest import exceptions
+from tempest.test import attr
from tempest.tests.compute import base
@@ -25,6 +27,7 @@
def setUpClass(cls):
super(AbsoluteLimitsTestJSON, cls).setUpClass()
cls.client = cls.limits_client
+ cls.server_client = cls.servers_client
def test_absLimits_get(self):
# To check if all limits are present in the response
@@ -45,6 +48,24 @@
"Failed to find element %s in absolute limits list"
% ', '.join(ele for ele in missing_elements))
+ @attr(type='negative')
+ def test_max_image_meta_exceed_limit(self):
+ #We should not create vm with image meta over maxImageMeta limit
+ # Get max limit value
+ max_meta = self.client.get_specific_absolute_limit('maxImageMeta')
+
+ #Create server should fail, since we are passing > metadata Limit!
+ max_meta_data = int(max_meta) + 1
+
+ meta_data = {}
+ for xx in range(max_meta_data):
+ meta_data[str(xx)] = str(xx)
+
+ self.assertRaises(exceptions.OverLimit,
+ self.server_client.create_server,
+ name='test', meta=meta_data, flavor_ref='84',
+ image_ref='9e6a2e3b-1601-42a5-985f-c3a2f93a5ec3')
+
class AbsoluteLimitsTestXML(AbsoluteLimitsTestJSON):
_interface = 'xml'
diff --git a/tempest/tests/compute/servers/test_multiple_create.py b/tempest/tests/compute/servers/test_multiple_create.py
index ad5d604..47a38b1 100644
--- a/tempest/tests/compute/servers/test_multiple_create.py
+++ b/tempest/tests/compute/servers/test_multiple_create.py
@@ -48,7 +48,7 @@
created_servers = self._get_created_servers(kwargs['name'])
# NOTE(maurosr): append it to cls.servers list from base.BaseCompute
# class.
- self.servers.append(created_servers)
+ self.servers.extend(created_servers)
# NOTE(maurosr): get a server list, check status of the ones with names
# that match and wait for them become active. At a first look, since
# they are building in parallel, wait inside the for doesn't seem be
diff --git a/tempest/tests/compute/servers/test_server_rescue.py b/tempest/tests/compute/servers/test_server_rescue.py
index 7015d60..04c5b27 100644
--- a/tempest/tests/compute/servers/test_server_rescue.py
+++ b/tempest/tests/compute/servers/test_server_rescue.py
@@ -15,8 +15,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import testtools
-
from tempest.common.utils.data_utils import rand_name
import tempest.config
from tempest import exceptions
@@ -192,8 +190,12 @@
self.assertEqual(202, resp.status)
@attr(type='positive')
- @testtools.skip("Skipped until Bug #1126257 is resolved")
def test_rescued_vm_add_remove_security_group(self):
+ # Rescue the server
+ self.servers_client.rescue_server(
+ self.server_id, self.password)
+ self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
+
#Add Security group
resp, body = self.servers_client.add_security_group(self.server_id,
self.sg_name)
@@ -201,7 +203,7 @@
#Delete Security group
resp, body = self.servers_client.remove_security_group(self.server_id,
- self.sg_id)
+ self.sg_name)
self.assertEqual(202, resp.status)
# Unrescue the server