Merge "Adds create volume from image test"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 9280c57..ac18490 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -35,9 +35,9 @@
# This should be the username of a user WITH administrative privileges
admin_username = admin
-# The above non-administrative user's password
+# The above administrative user's password
admin_password = secret
-# The above non-administrative user's tenant name
+# The above administrative user's tenant name
admin_tenant_name = admin
[compute]
diff --git a/run_tests.sh b/run_tests.sh
index 3f394e3..25b9729 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -33,7 +33,7 @@
config_file=""
update=0
-if ! options=$(getopt -o VNnfuswcphdsC: -l virtual-env,no-virtual-env,no-site-packages,force,update,smoke,whitebox,nova-coverage,pep8,help,debug,stdout,config: -- "$@")
+if ! options=$(getopt -o VNnfuswcphdSC: -l virtual-env,no-virtual-env,no-site-packages,force,update,smoke,whitebox,nova-coverage,pep8,help,debug,stdout,config: -- "$@")
then
# parse error
usage
diff --git a/tempest/clients.py b/tempest/clients.py
index b3b5906..7d9a263 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -48,8 +48,11 @@
from tempest.services.compute.xml.servers_client import ServersClientXML
from tempest.services.compute.xml.volumes_extensions_client import \
VolumesExtensionsClientXML
+from tempest.services.identity.v3.json.endpoints_client import \
+ EndPointClientJSON
from tempest.services.identity.json.identity_client import IdentityClientJSON
from tempest.services.identity.json.identity_client import TokenClientJSON
+from tempest.services.identity.v3.xml.endpoints_client import EndPointClientXML
from tempest.services.identity.xml.identity_client import IdentityClientXML
from tempest.services.identity.xml.identity_client import TokenClientXML
from tempest.services.image.v1.json.image_client import ImageClientJSON
@@ -157,6 +160,11 @@
"xml": InterfacesClientXML,
}
+ENDPOINT_CLIENT = {
+ "json": EndPointClientJSON,
+ "xml": EndPointClientXML,
+}
+
class Manager(object):
@@ -219,6 +227,7 @@
self.security_groups_client = \
SECURITY_GROUPS_CLIENT[interface](*client_args)
self.interfaces_client = INTERFACES_CLIENT[interface](*client_args)
+ self.endpoints_client = ENDPOINT_CLIENT[interface](*client_args)
except KeyError:
msg = "Unsupported interface type `%s'" % interface
raise exceptions.InvalidConfiguration(msg)
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 02b5c9b..fba3b0f 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -135,12 +135,14 @@
headers = {'Content-Type': 'application/json'}
body = json.dumps(creds)
- resp, body = self.http_obj.request(auth_url, 'POST',
- headers=headers, body=body)
+ self._log_request('POST', auth_url, headers, body)
+ resp, resp_body = self.http_obj.request(auth_url, 'POST',
+ headers=headers, body=body)
+ self._log_response(resp, resp_body)
if resp.status == 200:
try:
- auth_data = json.loads(body)['access']
+ auth_data = json.loads(resp_body)['access']
token = auth_data['token']['id']
except Exception, e:
print "Failed to obtain token for user: %s" % e
@@ -155,7 +157,6 @@
mgmt_url = _ep[self.endpoint_url]
if not mgmt_url:
mgmt_url = ep['endpoints'][0][self.endpoint_url]
- tenant_id = auth_data['token']['tenant']['id']
break
if mgmt_url is None:
@@ -166,6 +167,8 @@
elif resp.status == 401:
raise exceptions.AuthenticationFailure(user=user,
password=password)
+ raise exceptions.IdentityError('Unexpected status code {0}'.format(
+ resp.status))
def post(self, url, body, headers):
return self.request('POST', url, headers, body)
@@ -176,6 +179,9 @@
def delete(self, url, headers=None):
return self.request('DELETE', url, headers)
+ def patch(self, url, body, headers):
+ return self.request('PATCH', url, headers, body)
+
def put(self, url, body, headers):
return self.request('PUT', url, headers, body)
@@ -230,7 +236,7 @@
def response_checker(self, method, url, headers, body, resp, resp_body):
if (resp.status in set((204, 205, 304)) or resp.status < 200 or
- method.upper() == 'HEAD') and resp_body:
+ method.upper() == 'HEAD') and resp_body:
raise exceptions.ResponseWithNonEmptyBody(status=resp.status)
#NOTE(afazekas):
# If the HTTP Status Code is 205
@@ -388,7 +394,7 @@
def is_absolute_limit(self, resp, resp_body):
if (not isinstance(resp_body, collections.Mapping) or
- 'retry-after' not in resp):
+ 'retry-after' not in resp):
return True
over_limit = resp_body.get('overLimit', None)
if not over_limit:
@@ -422,6 +428,6 @@
def is_absolute_limit(self, resp, resp_body):
if (not isinstance(resp_body, collections.Mapping) or
- 'retry-after' not in resp):
+ 'retry-after' not in resp):
return True
return 'exceed' in resp_body.get('message', 'blabla')
diff --git a/tempest/services/compute/json/flavors_client.py b/tempest/services/compute/json/flavors_client.py
index 955068f..1b965f3 100644
--- a/tempest/services/compute/json/flavors_client.py
+++ b/tempest/services/compute/json/flavors_client.py
@@ -106,3 +106,29 @@
"""Unsets extra Specs from the mentioned flavor."""
return self.delete('flavors/%s/os-extra_specs/%s' % (str(flavor_id),
key))
+
+ def add_flavor_access(self, flavor_id, tenant_id):
+ """Add flavor access for the specified tenant."""
+ post_body = {
+ 'addTenantAccess': {
+ 'tenant': tenant_id
+ }
+ }
+ post_body = json.dumps(post_body)
+ resp, body = self.post('flavors/%s/action' % flavor_id,
+ post_body, self.headers)
+ body = json.loads(body)
+ return resp, body['flavor_access']
+
+ def remove_flavor_access(self, flavor_id, tenant_id):
+ """Remove flavor access from the specified tenant."""
+ post_body = {
+ 'removeTenantAccess': {
+ 'tenant': tenant_id
+ }
+ }
+ post_body = json.dumps(post_body)
+ resp, body = self.post('flavors/%s/action' % flavor_id,
+ post_body, self.headers)
+ body = json.loads(body)
+ return resp, body['flavor_access']
diff --git a/tempest/services/compute/xml/flavors_client.py b/tempest/services/compute/xml/flavors_client.py
index ece362b..a6451df 100644
--- a/tempest/services/compute/xml/flavors_client.py
+++ b/tempest/services/compute/xml/flavors_client.py
@@ -147,3 +147,28 @@
"""Unsets an extra spec based on the mentioned flavor and key."""
return self.delete('flavors/%s/os-extra_specs/%s' % (str(flavor_id),
key))
+
+ def _parse_array_access(self, node):
+ return [xml_to_json(x) for x in node]
+
+ def add_flavor_access(self, flavor_id, tenant_id):
+ """Add flavor access for the specified tenant."""
+ doc = Document()
+ server = Element("addTenantAccess")
+ doc.append(server)
+ server.add_attr("tenant", tenant_id)
+ resp, body = self.post('flavors/%s/action' % str(flavor_id),
+ str(doc), self.headers)
+ body = self._parse_array_access(etree.fromstring(body))
+ return resp, body
+
+ def remove_flavor_access(self, flavor_id, tenant_id):
+ """Remove flavor access from the specified tenant."""
+ doc = Document()
+ server = Element("removeTenantAccess")
+ doc.append(server)
+ server.add_attr("tenant", tenant_id)
+ resp, body = self.post('flavors/%s/action' % str(flavor_id),
+ str(doc), self.headers)
+ body = self._parse_array_access(etree.fromstring(body))
+ return resp, body
diff --git a/tempest/services/identity/v3/__init__.py b/tempest/services/identity/v3/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/identity/v3/__init__.py
diff --git a/tempest/services/identity/v3/json/__init__.py b/tempest/services/identity/v3/json/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/identity/v3/json/__init__.py
diff --git a/tempest/services/identity/v3/json/endpoints_client.py b/tempest/services/identity/v3/json/endpoints_client.py
new file mode 100755
index 0000000..3cb8f90
--- /dev/null
+++ b/tempest/services/identity/v3/json/endpoints_client.py
@@ -0,0 +1,87 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013 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 json
+from urlparse import urlparse
+
+from tempest.common.rest_client import RestClient
+
+
+class EndPointClientJSON(RestClient):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(EndPointClientJSON, self).__init__(config,
+ username, password,
+ auth_url, tenant_name)
+ self.service = self.config.identity.catalog_type
+ self.endpoint_url = 'adminURL'
+
+ def request(self, method, url, headers=None, body=None, wait=None):
+ """Overriding the existing HTTP request in super class rest_client."""
+ self._set_auth()
+ self.base_url = self.base_url.replace(urlparse(self.base_url).path,
+ "/v3")
+ return super(EndPointClientJSON, self).request(method, url,
+ headers=headers,
+ body=body)
+
+ def list_endpoints(self):
+ """GET endpoints."""
+ resp, body = self.get('endpoints')
+ body = json.loads(body)
+ return resp, body['endpoints']
+
+ def create_endpoint(self, service_id, interface, url, **kwargs):
+ """Create endpoint."""
+ region = kwargs.get('region', None)
+ enabled = kwargs.get('enabled', None)
+ post_body = {
+ 'service_id': service_id,
+ 'interface': interface,
+ 'url': url,
+ 'region': region,
+ 'enabled': enabled
+ }
+ post_body = json.dumps({'endpoint': post_body})
+ resp, body = self.post('endpoints', post_body, self.headers)
+ body = json.loads(body)
+ return resp, body['endpoint']
+
+ def update_endpoint(self, endpoint_id, service_id=None, interface=None,
+ url=None, region=None, enabled=None):
+ """Updates an endpoint with given parameters."""
+ post_body = {}
+ if service_id is not None:
+ post_body['service_id'] = service_id
+ if interface is not None:
+ post_body['interface'] = interface
+ if url is not None:
+ post_body['url'] = url
+ if region is not None:
+ post_body['region'] = region
+ if enabled is not None:
+ post_body['enabled'] = enabled
+ post_body = json.dumps({'endpoint': post_body})
+ resp, body = self.patch('endpoints/%s' % endpoint_id, post_body,
+ self.headers)
+ body = json.loads(body)
+ return resp, body['endpoint']
+
+ def delete_endpoint(self, endpoint_id):
+ """Delete endpoint."""
+ resp_header, resp_body = self.delete('endpoints/%s' % endpoint_id)
+ return resp_header, resp_body
diff --git a/tempest/services/identity/v3/xml/__init__.py b/tempest/services/identity/v3/xml/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/identity/v3/xml/__init__.py
diff --git a/tempest/services/identity/v3/xml/endpoints_client.py b/tempest/services/identity/v3/xml/endpoints_client.py
new file mode 100755
index 0000000..8400976
--- /dev/null
+++ b/tempest/services/identity/v3/xml/endpoints_client.py
@@ -0,0 +1,107 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013 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 urlparse import urlparse
+
+import httplib2
+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
+
+XMLNS = "http://docs.openstack.org/identity/api/v3"
+
+
+class EndPointClientXML(RestClientXML):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(EndPointClientXML, self).__init__(config, username, password,
+ auth_url, tenant_name)
+ self.service = self.config.identity.catalog_type
+ self.endpoint_url = 'adminURL'
+
+ def _parse_array(self, node):
+ array = []
+ for child in node.getchildren():
+ tag_list = child.tag.split('}', 1)
+ if tag_list[1] == "endpoint":
+ array.append(xml_to_json(child))
+ return array
+
+ def _parse_body(self, body):
+ json = xml_to_json(body)
+ return json
+
+ def request(self, method, url, headers=None, body=None, wait=None):
+ """Overriding the existing HTTP request in super class RestClient."""
+ dscv = self.config.identity.disable_ssl_certificate_validation
+ self.http_obj = httplib2.Http(disable_ssl_certificate_validation=dscv)
+ self._set_auth()
+ self.base_url = self.base_url.replace(urlparse(self.base_url).path,
+ "/v3")
+ return super(EndPointClientXML, self).request(method, url,
+ headers=headers,
+ body=body)
+
+ def list_endpoints(self):
+ """Get the list of endpoints."""
+ resp, body = self.get("endpoints", self.headers)
+ body = self._parse_array(etree.fromstring(body))
+ return resp, body
+
+ def create_endpoint(self, service_id, interface, url, **kwargs):
+ """Create endpoint."""
+ region = kwargs.get('region', None)
+ enabled = kwargs.get('enabled', None)
+ create_endpoint = Element("endpoint",
+ xmlns=XMLNS,
+ service_id=service_id,
+ interface=interface,
+ url=url, region=region,
+ enabled=enabled)
+ resp, body = self.post('endpoints', str(Document(create_endpoint)),
+ self.headers)
+ body = self._parse_body(etree.fromstring(body))
+ return resp, body
+
+ def update_endpoint(self, endpoint_id, service_id=None, interface=None,
+ url=None, region=None, enabled=None):
+ """Updates an endpoint with given parameters."""
+ doc = Document()
+ endpoint = Element("endpoint")
+ doc.append(endpoint)
+
+ if service_id:
+ endpoint.add_attr("service_id", service_id)
+ if interface:
+ endpoint.add_attr("interface", interface)
+ if url:
+ endpoint.add_attr("url", url)
+ if region:
+ endpoint.add_attr("region", region)
+ if enabled is not None:
+ endpoint.add_attr("enabled", enabled)
+ resp, body = self.patch('endpoints/%s' % str(endpoint_id),
+ str(doc), self.headers)
+ body = self._parse_body(etree.fromstring(body))
+ return resp, body
+
+ def delete_endpoint(self, endpoint_id):
+ """Delete endpoint."""
+ resp_header, resp_body = self.delete('endpoints/%s' % endpoint_id)
+ return resp_header, resp_body
diff --git a/tempest/smoke.py b/tempest/smoke.py
deleted file mode 100644
index 0d4043f..0000000
--- a/tempest/smoke.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2012 OpenStack, LLC
-# 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 logging
-
-from tempest import test
-
-LOG = logging.getLogger(__name__)
-
-
-class SmokeTest(object):
-
- """
- Base test case class mixin for "smoke tests"
-
- Smoke tests are tests that have the following characteristics:
-
- * Test basic operations of an API, typically in an order that
- a regular user would perform those operations
- * Test only the correct inputs and action paths -- no fuzz or
- random input data is sent, only valid inputs.
- * Use only the default client tool for calling an API
- """
- pass
-
-
-class DefaultClientSmokeTest(test.DefaultClientTest, SmokeTest):
-
- """
- Base smoke test case class that provides the default clients to
- access the various OpenStack APIs.
- """
-
- @classmethod
- def tearDownClass(cls):
- # NOTE(jaypipes): Because smoke tests are typically run in a specific
- # order, and because test methods in smoke tests generally create
- # resources in a particular order, we destroy resources in the reverse
- # order in which resources are added to the smoke test class object
- while cls.os_resources:
- thing = cls.os_resources.pop()
- LOG.debug("Deleting %r from shared resources of %s" %
- (thing, cls.__name__))
-
- try:
- # OpenStack resources are assumed to have a delete()
- # method which destroys the resource...
- thing.delete()
- except Exception as e:
- # If the resource is already missing, mission accomplished.
- if e.__class__.__name__ == 'NotFound':
- continue
- raise
-
- def is_deletion_complete():
- # Deletion testing is only required for objects whose
- # existence cannot be checked via retrieval.
- if isinstance(thing, dict):
- return True
- try:
- thing.get()
- except Exception as e:
- # Clients are expected to return an exception
- # called 'NotFound' if retrieval fails.
- if e.__class__.__name__ == 'NotFound':
- return True
- raise
- return False
-
- # Block until resource deletion has completed or timed-out
- test.call_until_true(is_deletion_complete, 10, 1)
diff --git a/tempest/test.py b/tempest/test.py
index e0639b6..ccb2251 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -115,42 +115,91 @@
return False
-class DefaultClientTest(TestCase):
+def status_timeout(things, thing_id, expected_status):
+ """
+ Given a thing and an expected status, do a loop, sleeping
+ for a configurable amount of time, checking for the
+ expected status to show. At any time, if the returned
+ status of the thing is ERROR, fail out.
+ """
+ def check_status():
+ # python-novaclient has resources available to its client
+ # that all implement a get() method taking an identifier
+ # for the singular resource to retrieve.
+ thing = things.get(thing_id)
+ new_status = thing.status
+ if new_status == 'ERROR':
+ self.fail("%s failed to get to expected status."
+ "In ERROR state."
+ % thing)
+ elif new_status == expected_status:
+ return True # All good.
+ LOG.debug("Waiting for %s to get to %s status. "
+ "Currently in %s status",
+ thing, expected_status, new_status)
+ conf = config.TempestConfig()
+ if not call_until_true(check_status,
+ conf.compute.build_timeout,
+ conf.compute.build_interval):
+ self.fail("Timed out waiting for thing %s to become %s"
+ % (thing_id, expected_status))
+
+
+class DefaultClientSmokeTest(TestCase):
"""
- Base test case class that provides the default clients to access
- the various OpenStack APIs.
+ Base smoke test case class that provides the default clients to
+ access the various OpenStack APIs.
+
+ Smoke tests are tests that have the following characteristics:
+
+ * Test basic operations of an API, typically in an order that
+ a regular user would perform those operations
+ * Test only the correct inputs and action paths -- no fuzz or
+ random input data is sent, only valid inputs.
+ * Use only the default client tool for calling an API
"""
manager_class = manager.DefaultClientManager
- def status_timeout(self, things, thing_id, expected_status):
- """
- Given a thing and an expected status, do a loop, sleeping
- for a configurable amount of time, checking for the
- expected status to show. At any time, if the returned
- status of the thing is ERROR, fail out.
- """
- def check_status():
- # python-novaclient has resources available to its client
- # that all implement a get() method taking an identifier
- # for the singular resource to retrieve.
- thing = things.get(thing_id)
- new_status = thing.status
- if new_status == 'ERROR':
- self.fail("%s failed to get to expected status."
- "In ERROR state."
- % thing)
- elif new_status == expected_status:
- return True # All good.
- LOG.debug("Waiting for %s to get to %s status. "
- "Currently in %s status",
- thing, expected_status, new_status)
- if not call_until_true(check_status,
- self.config.compute.build_timeout,
- self.config.compute.build_interval):
- self.fail("Timed out waiting for thing %s to become %s"
- % (thing_id, expected_status))
+ @classmethod
+ def tearDownClass(cls):
+ # NOTE(jaypipes): Because smoke tests are typically run in a specific
+ # order, and because test methods in smoke tests generally create
+ # resources in a particular order, we destroy resources in the reverse
+ # order in which resources are added to the smoke test class object
+ while cls.os_resources:
+ thing = cls.os_resources.pop()
+ LOG.debug("Deleting %r from shared resources of %s" %
+ (thing, cls.__name__))
+
+ try:
+ # OpenStack resources are assumed to have a delete()
+ # method which destroys the resource...
+ thing.delete()
+ except Exception as e:
+ # If the resource is already missing, mission accomplished.
+ if e.__class__.__name__ == 'NotFound':
+ continue
+ raise
+
+ def is_deletion_complete():
+ # Deletion testing is only required for objects whose
+ # existence cannot be checked via retrieval.
+ if isinstance(thing, dict):
+ return True
+ try:
+ thing.get()
+ except Exception as e:
+ # Clients are expected to return an exception
+ # called 'NotFound' if retrieval fails.
+ if e.__class__.__name__ == 'NotFound':
+ return True
+ raise
+ return False
+
+ # Block until resource deletion has completed or timed-out
+ call_until_true(is_deletion_complete, 10, 1)
class ComputeFuzzClientTest(TestCase):
@@ -161,46 +210,3 @@
"""
manager_class = manager.ComputeFuzzClientManager
-
- def status_timeout(self, client_get_method, thing_id, expected_status):
- """
- Given a method to get a resource and an expected status, do a loop,
- sleeping for a configurable amount of time, checking for the
- expected status to show. At any time, if the returned
- status of the thing is ERROR, fail out.
-
- :param client_get_method: The callable that will retrieve the thing
- with ID :param:thing_id
- :param thing_id: The ID of the thing to get
- :param expected_status: String value of the expected status of the
- thing that we are looking for.
-
- :code ..
-
- Usage:
-
- def test_some_server_action(self):
- client = self.servers_client
- resp, server = client.create_server('random_server')
- self.status_timeout(client.get_server, server['id'], 'ACTIVE')
- """
- def check_status():
- # Tempest REST client has resources available to its client
- # that all implement a various get_$resource() methods taking
- # an identifier for the singular resource to retrieve.
- thing = client_get_method(thing_id)
- new_status = thing['status']
- if new_status == 'ERROR':
- self.fail("%s failed to get to expected status."
- "In ERROR state."
- % thing)
- elif new_status == expected_status:
- return True # All good.
- LOG.debug("Waiting for %s to get to %s status. "
- "Currently in %s status",
- thing, expected_status, new_status)
- if not call_until_true(check_status,
- self.config.compute.build_timeout,
- self.config.compute.build_interval):
- self.fail("Timed out waiting for thing %s to become %s"
- % (thing_id, expected_status))
diff --git a/tempest/tests/compute/admin/test_flavors.py b/tempest/tests/compute/admin/test_flavors.py
index 32b06f8..7957009 100644
--- a/tempest/tests/compute/admin/test_flavors.py
+++ b/tempest/tests/compute/admin/test_flavors.py
@@ -48,6 +48,11 @@
cls.swap = 1024
cls.rxtx = 2
+ def flavor_clean_up(self, flavor_id):
+ resp, body = self.client.delete_flavor(flavor_id)
+ self.assertEqual(resp.status, 202)
+ self.client.wait_for_resource_deletion(flavor_id)
+
@attr(type='positive')
def test_create_flavor(self):
# Create a flavor and ensure it is listed
@@ -55,43 +60,37 @@
flavor_name = rand_name(self.flavor_name_prefix)
new_flavor_id = rand_int_id(start=1000)
- try:
- #Create the flavor
- resp, flavor = self.client.create_flavor(flavor_name,
- self.ram, self.vcpus,
- self.disk,
- new_flavor_id,
- ephemeral=self.ephemeral,
- swap=self.swap,
- rxtx=self.rxtx)
- self.assertEqual(200, resp.status)
- self.assertEqual(flavor['name'], flavor_name)
- self.assertEqual(flavor['vcpus'], self.vcpus)
- self.assertEqual(flavor['disk'], self.disk)
- self.assertEqual(flavor['ram'], self.ram)
- self.assertEqual(int(flavor['id']), new_flavor_id)
- self.assertEqual(flavor['swap'], self.swap)
- self.assertEqual(flavor['rxtx_factor'], self.rxtx)
- self.assertEqual(flavor['OS-FLV-EXT-DATA:ephemeral'],
- self.ephemeral)
- if self._interface == "xml":
- XMLNS_OS_FLV_ACCESS = "http://docs.openstack.org/compute/ext/"\
- "flavor_access/api/v2"
- key = "{" + XMLNS_OS_FLV_ACCESS + "}is_public"
- self.assertEqual(flavor[key], "True")
- if self._interface == "json":
- self.assertEqual(flavor['os-flavor-access:is_public'], True)
+ #Create the flavor
+ resp, flavor = self.client.create_flavor(flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx)
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+ self.assertEqual(200, resp.status)
+ self.assertEqual(flavor['name'], flavor_name)
+ self.assertEqual(flavor['vcpus'], self.vcpus)
+ self.assertEqual(flavor['disk'], self.disk)
+ self.assertEqual(flavor['ram'], self.ram)
+ self.assertEqual(int(flavor['id']), new_flavor_id)
+ self.assertEqual(flavor['swap'], self.swap)
+ self.assertEqual(flavor['rxtx_factor'], self.rxtx)
+ self.assertEqual(flavor['OS-FLV-EXT-DATA:ephemeral'],
+ self.ephemeral)
+ if self._interface == "xml":
+ XMLNS_OS_FLV_ACCESS = "http://docs.openstack.org/compute/ext/"\
+ "flavor_access/api/v2"
+ key = "{" + XMLNS_OS_FLV_ACCESS + "}is_public"
+ self.assertEqual(flavor[key], "True")
+ if self._interface == "json":
+ self.assertEqual(flavor['os-flavor-access:is_public'], True)
- #Verify flavor is retrieved
- resp, flavor = self.client.get_flavor_details(new_flavor_id)
- self.assertEqual(resp.status, 200)
- self.assertEqual(flavor['name'], flavor_name)
-
- finally:
- #Delete the flavor
- resp, body = self.client.delete_flavor(new_flavor_id)
- self.assertEqual(resp.status, 202)
- self.client.wait_for_resource_deletion(new_flavor_id)
+ #Verify flavor is retrieved
+ resp, flavor = self.client.get_flavor_details(new_flavor_id)
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(flavor['name'], flavor_name)
@attr(type='positive')
def test_create_flavor_verify_entry_in_list_details(self):
@@ -100,29 +99,23 @@
flavor_name = rand_name(self.flavor_name_prefix)
new_flavor_id = rand_int_id(start=1000)
- try:
- #Create the flavor
- resp, flavor = self.client.create_flavor(flavor_name,
- self.ram, self.vcpus,
- self.disk,
- new_flavor_id,
- ephemeral=self.ephemeral,
- swap=self.swap,
- rxtx=self.rxtx)
- flag = False
- #Verify flavor is retrieved
- resp, flavors = self.client.list_flavors_with_detail()
- self.assertEqual(resp.status, 200)
- for flavor in flavors:
- if flavor['name'] == flavor_name:
- flag = True
- self.assertTrue(flag)
-
- finally:
- #Delete the flavor
- resp, body = self.client.delete_flavor(new_flavor_id)
- self.assertEqual(resp.status, 202)
- self.client.wait_for_resource_deletion(new_flavor_id)
+ #Create the flavor
+ resp, flavor = self.client.create_flavor(flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx)
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+ flag = False
+ #Verify flavor is retrieved
+ resp, flavors = self.client.list_flavors_with_detail()
+ self.assertEqual(resp.status, 200)
+ for flavor in flavors:
+ if flavor['name'] == flavor_name:
+ flag = True
+ self.assertTrue(flag)
@attr(type='negative')
def test_get_flavor_details_for_deleted_flavor(self):
@@ -138,11 +131,11 @@
ephemeral=self.ephemeral,
swap=self.swap,
rxtx=self.rxtx)
- self.assertEquals(200, resp.status)
-
# Delete the flavor
- resp, _ = self.client.delete_flavor(new_flavor_id)
- self.assertEqual(resp.status, 202)
+ new_flavor_id = flavor['id']
+ resp_delete, body = self.client.delete_flavor(new_flavor_id)
+ self.assertEquals(200, resp.status)
+ self.assertEquals(202, resp_delete.status)
# Deleted flavors can be seen via detailed GET
resp, flavor = self.client.get_flavor_details(new_flavor_id)
@@ -164,46 +157,40 @@
flavor_name = rand_name(self.flavor_name_prefix)
new_flavor_id = rand_int_id(start=1000)
- try:
- #Create the flavor
- resp, flavor = self.client.create_flavor(flavor_name,
- self.ram, self.vcpus,
- self.disk,
- new_flavor_id)
- self.assertEqual(200, resp.status)
- self.assertEqual(flavor['name'], flavor_name)
- self.assertEqual(flavor['ram'], self.ram)
- self.assertEqual(flavor['vcpus'], self.vcpus)
- self.assertEqual(flavor['disk'], self.disk)
- self.assertEqual(int(flavor['id']), new_flavor_id)
- self.assertEqual(flavor['swap'], '')
- self.assertEqual(int(flavor['rxtx_factor']), 1)
- self.assertEqual(int(flavor['OS-FLV-EXT-DATA:ephemeral']), 0)
- if self._interface == "xml":
- XMLNS_OS_FLV_ACCESS = "http://docs.openstack.org/compute/ext/"\
- "flavor_access/api/v2"
- key = "{" + XMLNS_OS_FLV_ACCESS + "}is_public"
- self.assertEqual(flavor[key], "True")
- if self._interface == "json":
- self.assertEqual(flavor['os-flavor-access:is_public'], True)
+ #Create the flavor
+ resp, flavor = self.client.create_flavor(flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id)
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+ self.assertEqual(200, resp.status)
+ self.assertEqual(flavor['name'], flavor_name)
+ self.assertEqual(flavor['ram'], self.ram)
+ self.assertEqual(flavor['vcpus'], self.vcpus)
+ self.assertEqual(flavor['disk'], self.disk)
+ self.assertEqual(int(flavor['id']), new_flavor_id)
+ self.assertEqual(flavor['swap'], '')
+ self.assertEqual(int(flavor['rxtx_factor']), 1)
+ self.assertEqual(int(flavor['OS-FLV-EXT-DATA:ephemeral']), 0)
+ if self._interface == "xml":
+ XMLNS_OS_FLV_ACCESS = "http://docs.openstack.org/compute/ext/"\
+ "flavor_access/api/v2"
+ key = "{" + XMLNS_OS_FLV_ACCESS + "}is_public"
+ self.assertEqual(flavor[key], "True")
+ if self._interface == "json":
+ self.assertEqual(flavor['os-flavor-access:is_public'], True)
- #Verify flavor is retrieved
- resp, flavor = self.client.get_flavor_details(new_flavor_id)
- self.assertEqual(resp.status, 200)
- self.assertEqual(flavor['name'], flavor_name)
- #Check if flavor is present in list
- resp, flavors = self.client.list_flavors_with_detail()
- self.assertEqual(resp.status, 200)
- for flavor in flavors:
- if flavor['name'] == flavor_name:
- flag = True
- self.assertTrue(flag)
-
- finally:
- #Delete the flavor
- resp, body = self.client.delete_flavor(new_flavor_id)
- self.assertEqual(resp.status, 202)
- self.client.wait_for_resource_deletion(new_flavor_id)
+ #Verify flavor is retrieved
+ resp, flavor = self.client.get_flavor_details(new_flavor_id)
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(flavor['name'], flavor_name)
+ #Check if flavor is present in list
+ resp, flavors = self.client.list_flavors_with_detail()
+ self.assertEqual(resp.status, 200)
+ for flavor in flavors:
+ if flavor['name'] == flavor_name:
+ flag = True
+ self.assertTrue(flag)
@attr(type='positive')
def test_flavor_not_public_verify_entry_not_in_list_details(self):
@@ -213,25 +200,21 @@
flavor_name = rand_name(self.flavor_name_prefix)
new_flavor_id = rand_int_id(start=1000)
- try:
- #Create the flavor
- resp, flavor = self.client.create_flavor(flavor_name,
- self.ram, self.vcpus,
- self.disk,
- new_flavor_id,
- is_public="False")
- flag = False
- #Verify flavor is retrieved
- resp, flavors = self.client.list_flavors_with_detail()
- self.assertEqual(resp.status, 200)
- for flavor in flavors:
- if flavor['name'] == flavor_name:
- flag = True
- self.assertFalse(flag)
- finally:
- #Delete the flavor
- resp, body = self.client.delete_flavor(new_flavor_id)
- self.assertEqual(resp.status, 202)
+ #Create the flavor
+ resp, flavor = self.client.create_flavor(flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ is_public="False")
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+ flag = False
+ #Verify flavor is retrieved
+ resp, flavors = self.client.list_flavors_with_detail()
+ self.assertEqual(resp.status, 200)
+ for flavor in flavors:
+ if flavor['name'] == flavor_name:
+ flag = True
+ self.assertFalse(flag)
def test_list_public_flavor_with_other_user(self):
#Create a Flavor with public access.
@@ -239,76 +222,65 @@
flavor_name = rand_name(self.flavor_name_prefix)
new_flavor_id = rand_int_id(start=1000)
- try:
#Create the flavor
- resp, flavor = self.client.create_flavor(flavor_name,
- self.ram, self.vcpus,
- self.disk,
- new_flavor_id,
- is_public="True")
- flag = False
- self.new_client = self.flavors_client
- #Verify flavor is retrieved with new user
- resp, flavors = self.new_client.list_flavors_with_detail()
- self.assertEqual(resp.status, 200)
- for flavor in flavors:
- if flavor['name'] == flavor_name:
- flag = True
- self.assertTrue(flag)
- finally:
- #Delete the flavor
- resp, body = self.client.delete_flavor(new_flavor_id)
- self.assertEqual(resp.status, 202)
- self.client.wait_for_resource_deletion(new_flavor_id)
+ resp, flavor = self.client.create_flavor(flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ is_public="True")
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+ flag = False
+ self.new_client = self.flavors_client
+ #Verify flavor is retrieved with new user
+ resp, flavors = self.new_client.list_flavors_with_detail()
+ self.assertEqual(resp.status, 200)
+ for flavor in flavors:
+ if flavor['name'] == flavor_name:
+ flag = True
+ self.assertTrue(flag)
@attr(type='positive')
def test_is_public_string_variations(self):
- try:
- flavor_id_not_public = rand_int_id(start=1000)
- flavor_name_not_public = rand_name(self.flavor_name_prefix)
- flavor_id_public = rand_int_id(start=1000)
- flavor_name_public = rand_name(self.flavor_name_prefix)
+ flavor_id_not_public = rand_int_id(start=1000)
+ flavor_name_not_public = rand_name(self.flavor_name_prefix)
+ flavor_id_public = rand_int_id(start=1000)
+ flavor_name_public = rand_name(self.flavor_name_prefix)
- # Create a non public flavor
- resp, flavor = self.client.create_flavor(flavor_name_not_public,
- self.ram, self.vcpus,
- self.disk,
- flavor_id_not_public,
- is_public="False")
+ # Create a non public flavor
+ resp, flavor = self.client.create_flavor(flavor_name_not_public,
+ self.ram, self.vcpus,
+ self.disk,
+ flavor_id_not_public,
+ is_public="False")
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
- # Create a public flavor
- resp, flavor = self.client.create_flavor(flavor_name_public,
- self.ram, self.vcpus,
- self.disk,
- flavor_id_public,
- is_public="True")
+ # Create a public flavor
+ resp, flavor = self.client.create_flavor(flavor_name_public,
+ self.ram, self.vcpus,
+ self.disk,
+ flavor_id_public,
+ is_public="True")
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
- def _flavor_lookup(flavors, flavor_name):
- for flavor in flavors:
- if flavor['name'] == flavor_name:
- return flavor
- return None
+ def _flavor_lookup(flavors, flavor_name):
+ for flavor in flavors:
+ if flavor['name'] == flavor_name:
+ return flavor
+ return None
- def _test_string_variations(variations, flavor_name):
- for string in variations:
- params = {'is_public': string}
- r, flavors = self.client.list_flavors_with_detail(params)
- self.assertEqual(r.status, 200)
- flavor = _flavor_lookup(flavors, flavor_name)
- self.assertNotEqual(flavor, None)
+ def _test_string_variations(variations, flavor_name):
+ for string in variations:
+ params = {'is_public': string}
+ r, flavors = self.client.list_flavors_with_detail(params)
+ self.assertEqual(r.status, 200)
+ flavor = _flavor_lookup(flavors, flavor_name)
+ self.assertNotEqual(flavor, None)
- _test_string_variations(['f', 'false', 'no', '0'],
- flavor_name_not_public)
+ _test_string_variations(['f', 'false', 'no', '0'],
+ flavor_name_not_public)
- _test_string_variations(['t', 'true', 'yes', '1'],
- flavor_name_public)
-
- finally:
- # Delete flavors
- for flavor_id in [flavor_id_not_public, flavor_id_public]:
- resp, body = self.client.delete_flavor(flavor_id)
- self.assertEqual(resp.status, 202)
- self.client.wait_for_resource_deletion(flavor_id)
+ _test_string_variations(['t', 'true', 'yes', '1'],
+ flavor_name_public)
@attr(type='negative')
def test_invalid_is_public_string(self):
diff --git a/tempest/tests/compute/admin/test_flavors_access.py b/tempest/tests/compute/admin/test_flavors_access.py
new file mode 100644
index 0000000..4cc3f57
--- /dev/null
+++ b/tempest/tests/compute/admin/test_flavors_access.py
@@ -0,0 +1,128 @@
+# 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.common.utils.data_utils import rand_int_id
+from tempest.common.utils.data_utils import rand_name
+from tempest import exceptions
+from tempest.test import attr
+from tempest.tests import compute
+from tempest.tests.compute import base
+
+
+class FlavorsAccessTestJSON(base.BaseComputeAdminTest):
+
+ """
+ Tests Flavor Access API extension.
+ Add and remove Flavor Access require admin privileges.
+ """
+
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(FlavorsAccessTestJSON, cls).setUpClass()
+ if not compute.FLAVOR_EXTRA_DATA_ENABLED:
+ msg = "FlavorExtraData extension not enabled."
+ raise cls.skipException(msg)
+
+ cls.client = cls.os_adm.flavors_client
+ admin_client = cls._get_identity_admin_client()
+ resp, tenants = admin_client.list_tenants()
+ cls.tenant_id = [tnt['id'] for tnt in tenants if tnt['name'] ==
+ cls.flavors_client.tenant_name][0]
+
+ cls.flavor_name_prefix = 'test_flavor_access_'
+ cls.ram = 512
+ cls.vcpus = 1
+ cls.disk = 10
+
+ @attr('positive')
+ def test_flavor_access_add_remove(self):
+ #Test to add and remove flavor access to a given tenant.
+ flavor_name = rand_name(self.flavor_name_prefix)
+ new_flavor_id = rand_int_id(start=1000)
+ resp, new_flavor = self.client.create_flavor(flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ is_public='False')
+ self.addCleanup(self.client.delete_flavor, new_flavor['id'])
+ #Add flavor access to a tenant.
+ resp_body = {
+ "tenant_id": str(self.tenant_id),
+ "flavor_id": str(new_flavor['id']),
+ }
+ add_resp, add_body = \
+ self.client.add_flavor_access(new_flavor['id'], self.tenant_id)
+ self.assertEqual(add_resp.status, 200)
+ self.assertIn(resp_body, add_body)
+
+ #The flavor is present in list.
+ resp, flavors = self.flavors_client.list_flavors_with_detail()
+ self.assertEqual(resp.status, 200)
+ self.assertIn(new_flavor['id'], map(lambda x: x['id'], flavors))
+
+ #Remove flavor access from a tenant.
+ remove_resp, remove_body = \
+ self.client.remove_flavor_access(new_flavor['id'], self.tenant_id)
+ self.assertEqual(remove_resp.status, 200)
+ self.assertNotIn(resp_body, remove_body)
+
+ #The flavor is not present in list.
+ resp, flavors = self.flavors_client.list_flavors_with_detail()
+ self.assertEqual(resp.status, 200)
+ self.assertNotIn(new_flavor['id'], map(lambda x: x['id'], flavors))
+
+ @attr('negative')
+ def test_flavor_non_admin_add(self):
+ #Test to add flavor access as a user without admin privileges.
+ flavor_name = rand_name(self.flavor_name_prefix)
+ new_flavor_id = rand_int_id(start=1000)
+ resp, new_flavor = self.client.create_flavor(flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ is_public='False')
+ self.addCleanup(self.client.delete_flavor, new_flavor['id'])
+ self.assertRaises(exceptions.Unauthorized,
+ self.flavors_client.add_flavor_access,
+ new_flavor['id'],
+ self.tenant_id)
+
+ @attr('negative')
+ def test_flavor_non_admin_remove(self):
+ #Test to remove flavor access as a user without admin privileges.
+ flavor_name = rand_name(self.flavor_name_prefix)
+ new_flavor_id = rand_int_id(start=1000)
+ resp, new_flavor = self.client.create_flavor(flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ is_public='False')
+ self.addCleanup(self.client.delete_flavor, new_flavor['id'])
+ #Add flavor access to a tenant.
+ self.client.add_flavor_access(new_flavor['id'], self.tenant_id)
+ self.addCleanup(self.client.remove_flavor_access,
+ new_flavor['id'], self.tenant_id)
+ self.assertRaises(exceptions.Unauthorized,
+ self.flavors_client.remove_flavor_access,
+ new_flavor['id'],
+ self.tenant_id)
+
+
+class FlavorsAdminTestXML(FlavorsAccessTestJSON):
+ _interface = 'xml'
diff --git a/tempest/tests/compute/security_groups/test_security_group_rules.py b/tempest/tests/compute/security_groups/test_security_group_rules.py
index 99d9a5d..c2032d4 100644
--- a/tempest/tests/compute/security_groups/test_security_group_rules.py
+++ b/tempest/tests/compute/security_groups/test_security_group_rules.py
@@ -33,30 +33,24 @@
def test_security_group_rules_create(self):
# Positive test: Creation of Security Group rule
# should be successfull
- try:
- #Creating a Security Group to add rules to it
- s_name = rand_name('securitygroup-')
- s_description = rand_name('description-')
- resp, securitygroup = \
- self.client.create_security_group(s_name, s_description)
- securitygroup_id = securitygroup['id']
- #Adding rules to the created Security Group
- parent_group_id = securitygroup['id']
- ip_protocol = 'tcp'
- from_port = 22
- to_port = 22
- resp, rule = \
- self.client.create_security_group_rule(parent_group_id,
- ip_protocol,
- from_port,
- to_port)
- self.assertEqual(200, resp.status)
- finally:
- #Deleting the Security Group rule, created in this method
- group_rule_id = rule['id']
- self.client.delete_security_group_rule(group_rule_id)
- #Deleting the Security Group created in this method
- resp, _ = self.client.delete_security_group(securitygroup_id)
+ #Creating a Security Group to add rules to it
+ s_name = rand_name('securitygroup-')
+ s_description = rand_name('description-')
+ resp, securitygroup = \
+ self.client.create_security_group(s_name, s_description)
+ securitygroup_id = securitygroup['id']
+ self.addCleanup(self.client.delete_security_group, securitygroup_id)
+ #Adding rules to the created Security Group
+ ip_protocol = 'tcp'
+ from_port = 22
+ to_port = 22
+ resp, rule = \
+ self.client.create_security_group_rule(securitygroup_id,
+ ip_protocol,
+ from_port,
+ to_port)
+ self.addCleanup(self.client.delete_security_group_rule, rule['id'])
+ self.assertEqual(200, resp.status)
@attr(type='positive')
def test_security_group_rules_create_with_optional_arguments(self):
@@ -64,75 +58,38 @@
# with optional arguments
# should be successfull
- rule_id = None
secgroup1 = None
secgroup2 = None
- try:
- #Creating a Security Group to add rules to it
- s_name = rand_name('securitygroup-')
- s_description = rand_name('description-')
- resp, securitygroup = \
- self.client.create_security_group(s_name, s_description)
- secgroup1 = securitygroup['id']
- #Creating a Security Group so as to assign group_id to the rule
- s_name2 = rand_name('securitygroup-')
- s_description2 = rand_name('description-')
- resp, securitygroup = \
- self.client.create_security_group(s_name2, s_description2)
- secgroup2 = securitygroup['id']
- #Adding rules to the created Security Group with optional arguments
- parent_group_id = secgroup1
- ip_protocol = 'tcp'
- from_port = 22
- to_port = 22
- cidr = '10.2.3.124/24'
- group_id = secgroup2
- resp, rule = \
- self.client.create_security_group_rule(parent_group_id,
- ip_protocol,
- from_port,
- to_port,
- cidr=cidr,
- group_id=group_id)
- rule_id = rule['id']
- self.assertEqual(200, resp.status)
- finally:
- #Deleting the Security Group rule, created in this method
- if rule_id:
- self.client.delete_security_group_rule(rule_id)
- #Deleting the Security Groups created in this method
- if secgroup1:
- self.client.delete_security_group(secgroup1)
- if secgroup2:
- self.client.delete_security_group(secgroup2)
-
- @attr(type='positive')
- def test_security_group_rules_create_delete(self):
- # Positive test: Deletion of Security Group rule
- # should be successfull
- try:
- #Creating a Security Group to add rule to it
- s_name = rand_name('securitygroup-')
- s_description = rand_name('description-')
- resp, securitygroup = \
- self.client.create_security_group(s_name, s_description)
- securitygroup_id = securitygroup['id']
- #Adding rules to the created Security Group
- parent_group_id = securitygroup['id']
- ip_protocol = 'tcp'
- from_port = 22
- to_port = 22
- resp, rule = \
- self.client.create_security_group_rule(parent_group_id,
- ip_protocol,
- from_port,
- to_port)
- finally:
- #Deleting the Security Group rule, created in this method
- group_rule_id = rule['id']
- self.client.delete_security_group_rule(group_rule_id)
- #Deleting the Security Group created in this method
- resp, _ = self.client.delete_security_group(securitygroup_id)
+ #Creating a Security Group to add rules to it
+ s_name = rand_name('securitygroup-')
+ s_description = rand_name('description-')
+ resp, securitygroup = \
+ self.client.create_security_group(s_name, s_description)
+ secgroup1 = securitygroup['id']
+ self.addCleanup(self.client.delete_security_group, secgroup1)
+ #Creating a Security Group so as to assign group_id to the rule
+ s_name2 = rand_name('securitygroup-')
+ s_description2 = rand_name('description-')
+ resp, securitygroup = \
+ self.client.create_security_group(s_name2, s_description2)
+ secgroup2 = securitygroup['id']
+ self.addCleanup(self.client.delete_security_group, secgroup2)
+ #Adding rules to the created Security Group with optional arguments
+ parent_group_id = secgroup1
+ ip_protocol = 'tcp'
+ from_port = 22
+ to_port = 22
+ cidr = '10.2.3.124/24'
+ group_id = secgroup2
+ resp, rule = \
+ self.client.create_security_group_rule(parent_group_id,
+ ip_protocol,
+ from_port,
+ to_port,
+ cidr=cidr,
+ group_id=group_id)
+ self.addCleanup(self.client.delete_security_group_rule, rule['id'])
+ self.assertEqual(200, resp.status)
@attr(type='negative')
def test_security_group_rules_create_with_invalid_id(self):
diff --git a/tempest/tests/compute/security_groups/test_security_groups.py b/tempest/tests/compute/security_groups/test_security_groups.py
index 70a01a0..d0afde4 100644
--- a/tempest/tests/compute/security_groups/test_security_groups.py
+++ b/tempest/tests/compute/security_groups/test_security_groups.py
@@ -29,79 +29,80 @@
super(SecurityGroupsTestJSON, cls).setUpClass()
cls.client = cls.security_groups_client
+ def _delete_security_group(self, securitygroup_id):
+ resp, _ = self.client.delete_security_group(securitygroup_id)
+ self.assertEqual(202, resp.status)
+
@attr(type='positive')
def test_security_groups_create_list_delete(self):
# Positive test:Should return the list of Security Groups
- try:
- #Create 3 Security Groups
- security_group_list = list()
- for i in range(3):
- s_name = rand_name('securitygroup-')
- s_description = rand_name('description-')
- resp, securitygroup = \
- self.client.create_security_group(s_name, s_description)
- self.assertEqual(200, resp.status)
- security_group_list.append(securitygroup)
- #Fetch all Security Groups and verify the list
- #has all created Security Groups
- resp, fetched_list = self.client.list_security_groups()
- self.assertEqual(200, resp.status)
- #Now check if all the created Security Groups are in fetched list
- missing_sgs = \
- [sg for sg in security_group_list if sg not in fetched_list]
- self.assertFalse(missing_sgs,
- "Failed to find Security Group %s in fetched "
- "list" % ', '.join(m_group['name']
- for m_group in missing_sgs))
- finally:
- #Delete all the Security Groups created in this method
- for securitygroup in security_group_list:
- resp, _ = \
- self.client.delete_security_group(securitygroup['id'])
- self.assertEqual(202, resp.status)
-
- @attr(type='positive')
- def test_security_group_create_delete(self):
- # Security Group should be created, verified and deleted
- try:
+ #Create 3 Security Groups
+ security_group_list = list()
+ for i in range(3):
s_name = rand_name('securitygroup-')
s_description = rand_name('description-')
resp, securitygroup = \
self.client.create_security_group(s_name, s_description)
self.assertEqual(200, resp.status)
- self.assertTrue('id' in securitygroup)
- securitygroup_id = securitygroup['id']
- self.assertFalse(securitygroup_id is None)
- self.assertTrue('name' in securitygroup)
- securitygroup_name = securitygroup['name']
- self.assertEqual(securitygroup_name, s_name,
- "The created Security Group name is "
- "not equal to the requested name")
- finally:
- #Delete Security Group created in this method
- resp, _ = self.client.delete_security_group(securitygroup['id'])
- self.assertEqual(202, resp.status)
+ self.addCleanup(self._delete_security_group,
+ securitygroup['id'])
+ security_group_list.append(securitygroup)
+ #Fetch all Security Groups and verify the list
+ #has all created Security Groups
+ resp, fetched_list = self.client.list_security_groups()
+ self.assertEqual(200, resp.status)
+ #Now check if all the created Security Groups are in fetched list
+ missing_sgs = \
+ [sg for sg in security_group_list if sg not in fetched_list]
+ self.assertFalse(missing_sgs,
+ "Failed to find Security Group %s in fetched "
+ "list" % ', '.join(m_group['name']
+ for m_group in missing_sgs))
+
+ #TODO(afazekas): scheduled for delete,
+ #test_security_group_create_get_delete covers it
+ @attr(type='positive')
+ def test_security_group_create_delete(self):
+ # Security Group should be created, verified and deleted
+ s_name = rand_name('securitygroup-')
+ s_description = rand_name('description-')
+ resp, securitygroup = \
+ self.client.create_security_group(s_name, s_description)
+ self.assertTrue('id' in securitygroup)
+ securitygroup_id = securitygroup['id']
+ self.addCleanup(self._delete_security_group,
+ securitygroup_id)
+ self.assertEqual(200, resp.status)
+ self.assertFalse(securitygroup_id is None)
+ self.assertTrue('name' in securitygroup)
+ securitygroup_name = securitygroup['name']
+ self.assertEqual(securitygroup_name, s_name,
+ "The created Security Group name is "
+ "not equal to the requested name")
@attr(type='positive')
def test_security_group_create_get_delete(self):
# Security Group should be created, fetched and deleted
- try:
- s_name = rand_name('securitygroup-')
- s_description = rand_name('description-')
- resp, securitygroup = \
- self.client.create_security_group(s_name, s_description)
- self.assertEqual(200, resp.status)
- #Now fetch the created Security Group by its 'id'
- resp, fetched_group = \
- self.client.get_security_group(securitygroup['id'])
- self.assertEqual(200, resp.status)
- self.assertEqual(securitygroup, fetched_group,
- "The fetched Security Group is different "
- "from the created Group")
- finally:
- #Delete the Security Group created in this method
- resp, _ = self.client.delete_security_group(securitygroup['id'])
- self.assertEqual(202, resp.status)
+ s_name = rand_name('securitygroup-')
+ s_description = rand_name('description-')
+ resp, securitygroup = \
+ self.client.create_security_group(s_name, s_description)
+ self.addCleanup(self._delete_security_group,
+ securitygroup['id'])
+
+ self.assertEqual(200, resp.status)
+ self.assertTrue('name' in securitygroup)
+ securitygroup_name = securitygroup['name']
+ self.assertEqual(securitygroup_name, s_name,
+ "The created Security Group name is "
+ "not equal to the requested name")
+ #Now fetch the created Security Group by its 'id'
+ resp, fetched_group = \
+ self.client.get_security_group(securitygroup['id'])
+ self.assertEqual(200, resp.status)
+ self.assertEqual(securitygroup, fetched_group,
+ "The fetched Security Group is different "
+ "from the created Group")
@attr(type='negative')
def test_security_group_get_nonexistant_group(self):
diff --git a/tempest/tests/compute/servers/test_server_advanced_ops.py b/tempest/tests/compute/servers/test_server_advanced_ops.py
index f949f2e..ac0d7be 100644
--- a/tempest/tests/compute/servers/test_server_advanced_ops.py
+++ b/tempest/tests/compute/servers/test_server_advanced_ops.py
@@ -24,7 +24,7 @@
LOG = logging.getLogger(__name__)
-class TestServerAdvancedOps(test.DefaultClientTest):
+class TestServerAdvancedOps(test.DefaultClientSmokeTest):
"""
This test case stresses some advanced server instance operations:
@@ -66,16 +66,16 @@
self.assertEqual(self.instance.status, 'BUILD')
instance_id = self.get_resource('instance').id
- self.status_timeout(self.compute_client.servers, instance_id, 'ACTIVE')
+ test.status_timeout(self.compute_client.servers, instance_id, 'ACTIVE')
instance = self.get_resource('instance')
instance_id = instance.id
resize_flavor = self.config.compute.flavor_ref_alt
LOG.debug("Resizing instance %s from flavor %s to flavor %s",
instance.id, instance.flavor, resize_flavor)
instance.resize(resize_flavor)
- self.status_timeout(self.compute_client.servers, instance_id,
+ test.status_timeout(self.compute_client.servers, instance_id,
'VERIFY_RESIZE')
LOG.debug("Confirming resize of instance %s", instance_id)
instance.confirm_resize()
- self.status_timeout(self.compute_client.servers, instance_id, 'ACTIVE')
+ test.status_timeout(self.compute_client.servers, instance_id, 'ACTIVE')
diff --git a/tempest/tests/compute/servers/test_server_basic_ops.py b/tempest/tests/compute/servers/test_server_basic_ops.py
index 2183193..c7fad7a 100644
--- a/tempest/tests/compute/servers/test_server_basic_ops.py
+++ b/tempest/tests/compute/servers/test_server_basic_ops.py
@@ -18,12 +18,12 @@
import logging
from tempest.common.utils.data_utils import rand_name
-from tempest import smoke
+from tempest import test
LOG = logging.getLogger(__name__)
-class TestServerBasicOps(smoke.DefaultClientSmokeTest):
+class TestServerBasicOps(test.DefaultClientSmokeTest):
"""
This smoke test case follows this basic set of operations:
@@ -101,7 +101,7 @@
def wait_on_active(self):
instance_id = self.get_resource('instance').id
- self.status_timeout(self.compute_client.servers, instance_id, 'ACTIVE')
+ test.status_timeout(self.compute_client.servers, instance_id, 'ACTIVE')
def pause_server(self):
instance = self.get_resource('instance')
@@ -109,7 +109,7 @@
LOG.debug("Pausing instance %s. Current status: %s",
instance_id, instance.status)
instance.pause()
- self.status_timeout(self.compute_client.servers, instance_id, 'PAUSED')
+ test.status_timeout(self.compute_client.servers, instance_id, 'PAUSED')
def unpause_server(self):
instance = self.get_resource('instance')
@@ -117,7 +117,7 @@
LOG.debug("Unpausing instance %s. Current status: %s",
instance_id, instance.status)
instance.unpause()
- self.status_timeout(self.compute_client.servers, instance_id, 'ACTIVE')
+ test.status_timeout(self.compute_client.servers, instance_id, 'ACTIVE')
def suspend_server(self):
instance = self.get_resource('instance')
@@ -125,7 +125,7 @@
LOG.debug("Suspending instance %s. Current status: %s",
instance_id, instance.status)
instance.suspend()
- self.status_timeout(self.compute_client.servers,
+ test.status_timeout(self.compute_client.servers,
instance_id, 'SUSPENDED')
def resume_server(self):
@@ -134,7 +134,7 @@
LOG.debug("Resuming instance %s. Current status: %s",
instance_id, instance.status)
instance.resume()
- self.status_timeout(self.compute_client.servers, instance_id, 'ACTIVE')
+ test.status_timeout(self.compute_client.servers, instance_id, 'ACTIVE')
def terminate_instance(self):
instance = self.get_resource('instance')
diff --git a/tempest/tests/compute/test_live_block_migration.py b/tempest/tests/compute/test_live_block_migration.py
index e5a7d5b..e438098 100644
--- a/tempest/tests/compute/test_live_block_migration.py
+++ b/tempest/tests/compute/test_live_block_migration.py
@@ -28,6 +28,7 @@
@attr(category='live-migration')
class LiveBlockMigrationTestJSON(base.BaseComputeAdminTest):
+ _host_key = 'OS-EXT-SRV-ATTR:host'
_interface = 'json'
live_migration_available = (
@@ -58,7 +59,7 @@
return body
def _get_host_for_server(self, server_id):
- return self._get_server_details(server_id)['OS-EXT-SRV-ATTR:host']
+ return self._get_server_details(server_id)[self._host_key]
def _migrate_server_to(self, server_id, dest_host):
_resp, body = self.admin_servers_client.live_migrate_server(
@@ -129,4 +130,6 @@
class LiveBlockMigrationTestXML(LiveBlockMigrationTestJSON):
+ _host_key = (
+ '{http://docs.openstack.org/compute/ext/extended_status/api/v1.1}host')
_interface = 'xml'
diff --git a/tempest/tests/identity/admin/v3/__init__.py b/tempest/tests/identity/admin/v3/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/tests/identity/admin/v3/__init__.py
diff --git a/tempest/tests/identity/admin/v3/test_endpoints.py b/tempest/tests/identity/admin/v3/test_endpoints.py
new file mode 100755
index 0000000..98fab57
--- /dev/null
+++ b/tempest/tests/identity/admin/v3/test_endpoints.py
@@ -0,0 +1,149 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.common.utils.data_utils import rand_name
+from tempest.test import attr
+from tempest.tests.identity import base
+
+
+class EndPointsTestJSON(base.BaseIdentityAdminTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(EndPointsTestJSON, cls).setUpClass()
+ cls.identity_client = cls.client
+ cls.client = cls.endpoints_client
+ cls.service_ids = list()
+ s_name = rand_name('service-')
+ s_type = rand_name('type--')
+ s_description = rand_name('description-')
+ resp, cls.service_data =\
+ cls.identity_client.create_service(s_name, s_type,
+ description=s_description)
+ cls.service_id = cls.service_data['id']
+ cls.service_ids.append(cls.service_id)
+ #Create endpoints so as to use for LIST and GET test cases
+ cls.setup_endpoints = list()
+ for i in range(2):
+ region = rand_name('region')
+ url = rand_name('url')
+ interface = 'public'
+ resp, endpoint = cls.client.create_endpoint(
+ cls.service_id, interface, url, region=region, enabled=True)
+ cls.setup_endpoints.append(endpoint)
+
+ @classmethod
+ def tearDownClass(cls):
+ for e in cls.setup_endpoints:
+ cls.client.delete_endpoint(e['id'])
+ for s in cls.service_ids:
+ cls.identity_client.delete_service(s)
+
+ @attr('positive')
+ def test_list_endpoints(self):
+ # Get a list of endpoints
+ resp, fetched_endpoints = self.client.list_endpoints()
+ #Asserting LIST Endpoint
+ self.assertEqual(resp['status'], '200')
+ missing_endpoints =\
+ [e for e in self.setup_endpoints if e not in fetched_endpoints]
+ self.assertEqual(0, len(missing_endpoints),
+ "Failed to find endpoint %s in fetched list" %
+ ', '.join(str(e) for e in missing_endpoints))
+
+ @attr('positive')
+ def test_create_delete_endpoint(self):
+ region = rand_name('region')
+ url = rand_name('url')
+ interface = 'public'
+ create_flag = False
+ matched = False
+ try:
+ resp, endpoint =\
+ self.client.create_endpoint(self.service_id, interface, url,
+ region=region, enabled=True)
+ create_flag = True
+ #Asserting Create Endpoint response body
+ self.assertEqual(resp['status'], '201')
+ self.assertEqual(region, endpoint['region'])
+ self.assertEqual(url, endpoint['url'])
+ #Checking if created endpoint is present in the list of endpoints
+ resp, fetched_endpoints = self.client.list_endpoints()
+ for e in fetched_endpoints:
+ if endpoint['id'] == e['id']:
+ matched = True
+ if not matched:
+ self.fail("Created endpoint does not appear in the list"
+ " of endpoints")
+ finally:
+ if create_flag:
+ matched = False
+ #Deleting the endpoint created in this method
+ resp_header, resp_body =\
+ self.client.delete_endpoint(endpoint['id'])
+ self.assertEqual(resp_header['status'], '204')
+ self.assertEqual(resp_body, '')
+ #Checking whether endpoint is deleted successfully
+ resp, fetched_endpoints = self.client.list_endpoints()
+ for e in fetched_endpoints:
+ if endpoint['id'] == e['id']:
+ matched = True
+ if matched:
+ self.fail("Delete endpoint is not successful")
+
+ @attr('smoke')
+ def test_update_endpoint(self):
+ #Creating an endpoint so as to check update endpoint
+ #with new values
+ region1 = rand_name('region')
+ url1 = rand_name('url')
+ interface1 = 'public'
+ resp, endpoint_for_update =\
+ self.client.create_endpoint(self.service_id, interface1,
+ url1, region=region1,
+ enabled=True)
+ #Creating service so as update endpoint with new service ID
+ s_name = rand_name('service-')
+ s_type = rand_name('type--')
+ s_description = rand_name('description-')
+ resp, self.service2 =\
+ self.identity_client.create_service(s_name, s_type,
+ description=s_description)
+ self.service_ids.append(self.service2['id'])
+ #Updating endpoint with new values
+ service_id = self.service2['id']
+ region2 = rand_name('region')
+ url2 = rand_name('url')
+ interface2 = 'internal'
+ resp, endpoint = \
+ self.client.update_endpoint(endpoint_for_update['id'],
+ service_id=self.service2['id'],
+ interface=interface2, url=url2,
+ region=region2, enabled=False)
+ self.assertEqual(resp['status'], '200')
+ #Asserting if the attributes of endpoint are updated
+ self.assertEqual(self.service2['id'], endpoint['service_id'])
+ self.assertEqual(interface2, endpoint['interface'])
+ self.assertEqual(url2, endpoint['url'])
+ self.assertEqual(region2, endpoint['region'])
+ self.assertEqual('False', str(endpoint['enabled']))
+ self.addCleanup(self.client.delete_endpoint, endpoint_for_update['id'])
+
+
+class EndPointsTestXML(EndPointsTestJSON):
+ _interface = 'xml'
diff --git a/tempest/tests/identity/base.py b/tempest/tests/identity/base.py
index 168b2ff..64b8993 100644
--- a/tempest/tests/identity/base.py
+++ b/tempest/tests/identity/base.py
@@ -28,6 +28,7 @@
os = clients.AdminManager(interface=cls._interface)
cls.client = os.identity_client
cls.token_client = os.token_client
+ cls.endpoints_client = os.endpoints_client
if not cls.client.has_admin_extensions():
raise cls.skipException("Admin extensions disabled")
diff --git a/tempest/tests/network/common.py b/tempest/tests/network/common.py
index 0bb806f..1cff2c4 100644
--- a/tempest/tests/network/common.py
+++ b/tempest/tests/network/common.py
@@ -21,7 +21,6 @@
from quantumclient.common import exceptions as exc
from tempest.common.utils.data_utils import rand_name
-from tempest import smoke
from tempest import test
@@ -103,7 +102,7 @@
self.client.delete_port(self.id)
-class TestNetworkSmokeCommon(smoke.DefaultClientSmokeTest):
+class TestNetworkSmokeCommon(test.DefaultClientSmokeTest):
"""
Base class for network smoke tests
"""
@@ -274,7 +273,7 @@
self.set_resource(name, server)
except AttributeError:
self.fail("Server not successfully created.")
- self.status_timeout(client.servers, server.id, 'ACTIVE')
+ test.status_timeout(client.servers, server.id, 'ACTIVE')
# The instance retrieved on creation is missing network
# details, necessitating retrieval after it becomes active to
# ensure correct details.
diff --git a/tempest/tests/network/test_network_basic_ops.py b/tempest/tests/network/test_network_basic_ops.py
index a38a5c0..3afe8e3 100644
--- a/tempest/tests/network/test_network_basic_ops.py
+++ b/tempest/tests/network/test_network_basic_ops.py
@@ -17,11 +17,10 @@
# under the License.
from tempest.common.utils.data_utils import rand_name
-from tempest.tests.network.common import DeletableRouter
-from tempest.tests.network.common import TestNetworkSmokeCommon
+import tempest.tests.network.common as net_common
-class TestNetworkBasicOps(TestNetworkSmokeCommon):
+class TestNetworkBasicOps(net_common.TestNetworkSmokeCommon):
"""
This smoke test suite assumes that Nova has been configured to
@@ -124,7 +123,7 @@
network_id = self.config.network.public_network_id
if router_id:
result = self.network_client.show_router(router_id)
- return AttributeDict(**result['router'])
+ return net_common.AttributeDict(**result['router'])
elif network_id:
router = self._create_router(tenant_id)
router.add_gateway(network_id)
@@ -143,8 +142,8 @@
),
)
result = self.network_client.create_router(body=body)
- router = DeletableRouter(client=self.network_client,
- **result['router'])
+ router = net_common.DeletableRouter(client=self.network_client,
+ **result['router'])
self.assertEqual(router.name, name)
self.set_resource(name, router)
return router