Merge "Change server create to use tracked create_server"
diff --git a/cli/__init__.py b/cli/__init__.py
index 2548f24..37aec93 100644
--- a/cli/__init__.py
+++ b/cli/__init__.py
@@ -58,6 +58,11 @@
         return self.cmd_with_auth(
             'nova', action, flags, params, admin, fail_ok)
 
+    def nova_manage(self, action, flags='', params='', fail_ok=False):
+        """Executes nova-manage command for the given action."""
+        return self.cmd(
+            'nova-manage', action, flags, params, fail_ok)
+
     def cmd_with_auth(self, cmd, action, flags='', params='',
                       admin=True, fail_ok=False):
         """Executes given command with auth attributes appended."""
diff --git a/cli/simple_read_only/test_compute.py b/cli/simple_read_only/test_compute.py
index bcdd2c5..e1109ff 100644
--- a/cli/simple_read_only/test_compute.py
+++ b/cli/simple_read_only/test_compute.py
@@ -56,12 +56,13 @@
 
     def test_admin_absolute_limites(self):
         self.nova('absolute-limits')
+        self.nova('absolute-limits', params='--reserved')
 
     def test_admin_aggregate_list(self):
         self.nova('aggregate-list')
 
     def test_admin_availability_zone_list(self):
-        self.nova('availability-zone-list')
+        self.assertIn("internal", self.nova('availability-zone-list'))
 
     def test_admin_cloudpipe_list(self):
         self.nova('cloudpipe-list')
@@ -90,7 +91,7 @@
                           params='--flavor m1.tiny')
 
     def test_admin_flavor_list(self):
-        self.nova('flavor-list')
+        self.assertIn("Memory_MB", self.nova('flavor-list'))
 
     def test_admin_floating_ip_bulk_list(self):
         self.nova('floating-ip-bulk-list')
diff --git a/cli/simple_read_only/test_compute_manage.py b/cli/simple_read_only/test_compute_manage.py
new file mode 100644
index 0000000..17b3bf6
--- /dev/null
+++ b/cli/simple_read_only/test_compute_manage.py
@@ -0,0 +1,61 @@
+# 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 logging
+import subprocess
+
+import testtools
+
+import cli
+
+
+LOG = logging.getLogger(__name__)
+
+
+class SimpleReadOnlyNovaManageTest(cli.ClientTestBase):
+
+    """
+    This is a first pass at a simple read only nova-manage test. This
+    only exercises client commands that are read only.
+
+    This should test commands:
+    * with and without optional parameters
+    * initially just check return codes, and later test command outputs
+
+    """
+
+    def test_admin_fake_action(self):
+        self.assertRaises(subprocess.CalledProcessError,
+                          self.nova_manage,
+                          'this-does-nova-exist')
+
+    #NOTE(jogo): Commands in order listed in 'nova-manage -h'
+
+    # test flags
+    def test_help_flag(self):
+        self.nova_manage('', '-h')
+
+    @testtools.skip("version is empty, bug 1138844")
+    def test_version_flag(self):
+        self.assertNotEqual("", self.nova_manage('', '--version'))
+
+    # test actions
+    def test_version(self):
+        self.assertNotEqual("", self.nova_manage('version'))
+
+    def test_flavor_list(self):
+        self.assertNotEqual("", self.nova_manage('flavor list'))
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 5ce3be6..6147aa1 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -192,6 +192,13 @@
     def copy(self, url, headers=None):
         return self.request('COPY', url, headers)
 
+    def get_versions(self):
+        resp, body = self.get('')
+        body = self._parse_resp(body)
+        body = body['versions']
+        versions = map(lambda x: x['id'], body)
+        return resp, versions
+
     def _log_request(self, method, req_url, headers, body):
         self.LOG.info('Request: ' + method + ' ' + req_url)
         if headers:
diff --git a/tempest/services/compute/json/security_groups_client.py b/tempest/services/compute/json/security_groups_client.py
index 95f2831..7f430d8 100644
--- a/tempest/services/compute/json/security_groups_client.py
+++ b/tempest/services/compute/json/security_groups_client.py
@@ -95,3 +95,12 @@
     def delete_security_group_rule(self, group_rule_id):
         """Deletes the provided Security Group rule."""
         return self.delete('os-security-group-rules/%s' % str(group_rule_id))
+
+    def list_security_group_rules(self, security_group_id):
+        """List all rules for a security group."""
+        resp, body = self.get('os-security-groups')
+        body = json.loads(body)
+        for sg in body['security_groups']:
+            if sg['id'] == security_group_id:
+                return resp, sg['rules']
+        raise exceptions.NotFound('No such Security Group')
diff --git a/tempest/services/compute/xml/common.py b/tempest/services/compute/xml/common.py
index bbc4e38..4b1b11a 100644
--- a/tempest/services/compute/xml/common.py
+++ b/tempest/services/compute/xml/common.py
@@ -100,7 +100,8 @@
     """
     json = {}
     for attr in node.keys():
-        json[attr] = node.get(attr)
+        if not attr.startswith("xmlns"):
+            json[attr] = node.get(attr)
     if not node.getchildren():
         return node.text or json
     for child in node.getchildren():
diff --git a/tempest/services/compute/xml/security_groups_client.py b/tempest/services/compute/xml/security_groups_client.py
index ac70f1b..7db60a1 100644
--- a/tempest/services/compute/xml/security_groups_client.py
+++ b/tempest/services/compute/xml/security_groups_client.py
@@ -23,6 +23,7 @@
 from tempest.services.compute.xml.common import Element
 from tempest.services.compute.xml.common import Text
 from tempest.services.compute.xml.common import xml_to_json
+from tempest.services.compute.xml.common import XMLNS_11
 
 
 class SecurityGroupsClientXML(RestClientXML):
@@ -128,3 +129,16 @@
         """Deletes the provided Security Group rule."""
         return self.delete('os-security-group-rules/%s' %
                            str(group_rule_id), self.headers)
+
+    def list_security_group_rules(self, security_group_id):
+        """List all rules for a security group."""
+        url = "os-security-groups"
+        resp, body = self.get(url, self.headers)
+        body = etree.fromstring(body)
+        secgroups = body.getchildren()
+        for secgroup in secgroups:
+            if secgroup.get('id') == security_group_id:
+                node = secgroup.find('{%s}rules' % XMLNS_11)
+                rules = [xml_to_json(x) for x in node.getchildren()]
+                return resp, rules
+        raise exceptions.NotFound('No such Security Group')
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index 655a345..fceeb28 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -160,7 +160,7 @@
         return array
 
     def list_servers(self, params=None):
-        url = 'servers/detail'
+        url = 'servers'
         if params:
             url += '?%s' % urllib.urlencode(params)
 
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index 45e93e2..77c9cd2 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -121,28 +121,25 @@
         body = json.loads(''.join([c for c in body_iter]))
         return resp, body['image']
 
-    def create_image(self, name, container_format, disk_format, is_public=True,
-                     location=None, properties=None, data=None):
+    def create_image(self, name, container_format, disk_format, **kwargs):
         params = {
             "name": name,
             "container_format": container_format,
             "disk_format": disk_format,
-            "is_public": is_public,
         }
+
         headers = {}
 
-        if location is not None:
-            params['location'] = location
-
-        if properties is not None:
-            params['properties'] = properties
+        for option in ['is_public', 'location', 'properties']:
+            if option in kwargs:
+                params[option] = kwargs.get(option)
 
         headers.update(self._image_meta_to_headers(params))
 
-        if data is not None:
-            return self._create_with_data(headers, data)
+        if 'data' in kwargs:
+            return self._create_with_data(headers, kwargs.get('data'))
 
-        resp, body = self.post('v1/images', data, headers)
+        resp, body = self.post('v1/images', None, headers)
         body = json.loads(body)
         return resp, body['image']
 
diff --git a/tempest/services/image/v2/json/image_client.py b/tempest/services/image/v2/json/image_client.py
index bcae79b..2c50a8d 100644
--- a/tempest/services/image/v2/json/image_client.py
+++ b/tempest/services/image/v2/json/image_client.py
@@ -63,17 +63,20 @@
 
         jsonschema.validate(body, schema)
 
-    def create_image(self, name, container_format, disk_format, is_public=True,
-                     properties=None):
+    def create_image(self, name, container_format, disk_format, **kwargs):
         params = {
             "name": name,
             "container_format": container_format,
             "disk_format": disk_format,
         }
-        if is_public:
-            params["visibility"] = "public"
-        else:
-            params["visibility"] = "private"
+
+        for option in ['visibility']:
+            if option in kwargs:
+                value = kwargs.get(option)
+                if isinstance(value, dict) or isinstance(value, tuple):
+                    params.update(value)
+                else:
+                    params[option] = value
 
         data = json.dumps(params)
         self._validate_schema(data)
diff --git a/tempest/tests/compute/admin/test_quotas.py b/tempest/tests/compute/admin/test_quotas.py
index 46a0c20..befcad3 100644
--- a/tempest/tests/compute/admin/test_quotas.py
+++ b/tempest/tests/compute/admin/test_quotas.py
@@ -44,7 +44,7 @@
         cls.default_quota_set = {'injected_file_content_bytes': 10240,
                                  'metadata_items': 128, 'injected_files': 5,
                                  'ram': 51200, 'floating_ips': 10,
-                                 'key_pairs': 100,
+                                 'fixed_ips': 10, 'key_pairs': 100,
                                  'injected_file_path_bytes': 255,
                                  'instances': 10, 'security_group_rules': 20,
                                  'cores': 20, 'security_groups': 10}
@@ -60,6 +60,9 @@
 
     @attr(type='smoke')
     def test_get_default_quotas(self):
+        # Tempest two step
+        self.skipTest('Skipped until the Bug 1125468 is resolved')
+
         # Admin can get the default resource quota set for a tenant
         expected_quota_set = self.default_quota_set.copy()
         expected_quota_set['id'] = self.demo_tenant_id
@@ -71,13 +74,16 @@
             self.fail("Admin could not get the default quota set for a tenant")
 
     def test_update_all_quota_resources_for_tenant(self):
+        # Tempest two step
+        self.skipTest('Skipped until the Bug 1125468 is resolved')
+
         # Admin can update all the resource quota limits for a tenant
         new_quota_set = {'injected_file_content_bytes': 20480,
                          'metadata_items': 256, 'injected_files': 10,
-                         'ram': 10240, 'floating_ips': 20, 'key_pairs': 200,
-                         'injected_file_path_bytes': 512, 'instances': 20,
-                         'security_group_rules': 20, 'cores': 2,
-                         'security_groups': 20}
+                         'ram': 10240, 'floating_ips': 20, 'fixed_ips': 10,
+                         'key_pairs': 200, 'injected_file_path_bytes': 512,
+                         'instances': 20, 'security_group_rules': 20,
+                         'cores': 2, 'security_groups': 20}
         try:
             # Update limits for all quota resources
             resp, quota_set = self.adm_client.update_quota_set(
@@ -97,6 +103,9 @@
 
     #TODO(afazekas): merge these test cases
     def test_get_updated_quotas(self):
+        # Tempest two step
+        self.skipTest('Skipped until the Bug 1125468 is resolved')
+
         # Verify that GET shows the updated quota set
         self.adm_client.update_quota_set(self.demo_tenant_id,
                                          ram='5120')
@@ -135,15 +144,10 @@
 
         self.adm_client.update_quota_set(self.demo_tenant_id,
                                          ram=mem_quota)
-        try:
-            self.create_server()
-        except exceptions.OverLimit:
-            pass
-        else:
-            self.fail("Could create servers over the memory quota limit")
-        finally:
-            self.adm_client.update_quota_set(self.demo_tenant_id,
-                                             ram=default_mem_quota)
+
+        self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
+                        ram=default_mem_quota)
+        self.assertRaises(exceptions.OverLimit, self.create_server)
 
 #TODO(afazekas): Add test that tried to update the quota_set as a regular user
 
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 dc85f4b..99d9a5d 100644
--- a/tempest/tests/compute/security_groups/test_security_group_rules.py
+++ b/tempest/tests/compute/security_groups/test_security_group_rules.py
@@ -232,6 +232,49 @@
                           self.client.delete_security_group_rule,
                           rand_name('999'))
 
+    @attr(type='positive')
+    def test_security_group_rules_list(self):
+        # Positive test: Created Security Group rules should be
+        # in the list of all rules
+        # 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']
+        # Delete the Security Group at the end of this method
+        self.addCleanup(self.client.delete_security_group, securitygroup_id)
+
+        # Add a first rule to the created Security Group
+        ip_protocol1 = 'tcp'
+        from_port1 = 22
+        to_port1 = 22
+        resp, rule = \
+            self.client.create_security_group_rule(securitygroup_id,
+                                                   ip_protocol1,
+                                                   from_port1, to_port1)
+        rule1_id = rule['id']
+        # Delete the Security Group rule1 at the end of this method
+        self.addCleanup(self.client.delete_security_group_rule, rule1_id)
+
+        # Add a second rule to the created Security Group
+        ip_protocol2 = 'icmp'
+        from_port2 = -1
+        to_port2 = -1
+        resp, rule = \
+            self.client.create_security_group_rule(securitygroup_id,
+                                                   ip_protocol2,
+                                                   from_port2, to_port2)
+        rule2_id = rule['id']
+        # Delete the Security Group rule2 at the end of this method
+        self.addCleanup(self.client.delete_security_group_rule, rule2_id)
+
+        # Get rules of the created Security Group
+        resp, rules = \
+            self.client.list_security_group_rules(securitygroup_id)
+        self.assertTrue(any([i for i in rules if i['id'] == rule1_id]))
+        self.assertTrue(any([i for i in rules if i['id'] == rule2_id]))
+
 
 class SecurityGroupRulesTestXML(SecurityGroupRulesTestJSON):
     _interface = 'xml'
diff --git a/tempest/tests/compute/servers/test_list_server_filters.py b/tempest/tests/compute/servers/test_list_server_filters.py
index 897ca34..ff599fe 100644
--- a/tempest/tests/compute/servers/test_list_server_filters.py
+++ b/tempest/tests/compute/servers/test_list_server_filters.py
@@ -73,12 +73,6 @@
         cls.client.wait_for_server_status(cls.s3['id'], 'ACTIVE')
         resp, cls.s3 = cls.client.get_server(cls.s3['id'])
 
-        # The list server call returns minimal results, so we need
-        # a less detailed version of each server also
-        cls.s1_min = cls._convert_to_min_details(cls.s1)
-        cls.s2_min = cls._convert_to_min_details(cls.s2)
-        cls.s3_min = cls._convert_to_min_details(cls.s3)
-
     @classmethod
     def tearDownClass(cls):
         cls.client.delete_server(cls.s1['id'])
@@ -86,10 +80,6 @@
         cls.client.delete_server(cls.s3['id'])
         super(ListServerFiltersTestJSON, cls).tearDownClass()
 
-    def _server_id_in_results(self, server_id, results):
-        ids = [row['id'] for row in results]
-        return server_id in ids
-
     @utils.skip_unless_attr('multiple_images', 'Only one image found')
     @attr(type='positive')
     def test_list_servers_filter_by_image(self):
@@ -98,9 +88,9 @@
         resp, body = self.client.list_servers(params)
         servers = body['servers']
 
-        self.assertTrue(self._server_id_in_results(self.s1['id'], servers))
-        self.assertFalse(self._server_id_in_results(self.s2['id'], servers))
-        self.assertTrue(self._server_id_in_results(self.s3['id'], servers))
+        self.assertIn(self.s1['id'], map(lambda x: x['id'], servers))
+        self.assertNotIn(self.s2['id'], map(lambda x: x['id'], servers))
+        self.assertIn(self.s3['id'], map(lambda x: x['id'], servers))
 
     @attr(type='positive')
     def test_list_servers_filter_by_flavor(self):
@@ -109,9 +99,9 @@
         resp, body = self.client.list_servers(params)
         servers = body['servers']
 
-        self.assertFalse(self._server_id_in_results(self.s1['id'], servers))
-        self.assertFalse(self._server_id_in_results(self.s2['id'], servers))
-        self.assertTrue(self._server_id_in_results(self.s3['id'], servers))
+        self.assertNotIn(self.s1['id'], map(lambda x: x['id'], servers))
+        self.assertNotIn(self.s2['id'], map(lambda x: x['id'], servers))
+        self.assertIn(self.s3['id'], map(lambda x: x['id'], servers))
 
     @attr(type='positive')
     def test_list_servers_filter_by_server_name(self):
@@ -120,9 +110,9 @@
         resp, body = self.client.list_servers(params)
         servers = body['servers']
 
-        self.assertTrue(self._server_id_in_results(self.s1['id'], servers))
-        self.assertFalse(self._server_id_in_results(self.s2['id'], servers))
-        self.assertFalse(self._server_id_in_results(self.s3['id'], servers))
+        self.assertIn(self.s1_name, map(lambda x: x['name'], servers))
+        self.assertNotIn(self.s2_name, map(lambda x: x['name'], servers))
+        self.assertNotIn(self.s3_name, map(lambda x: x['name'], servers))
 
     @attr(type='positive')
     def test_list_servers_filter_by_server_status(self):
@@ -131,12 +121,12 @@
         resp, body = self.client.list_servers(params)
         servers = body['servers']
 
-        self.assertTrue(self._server_id_in_results(self.s1['id'], servers))
-        self.assertTrue(self._server_id_in_results(self.s2['id'], servers))
-        self.assertTrue(self._server_id_in_results(self.s3['id'], servers))
+        self.assertIn(self.s1['id'], map(lambda x: x['id'], servers))
+        self.assertIn(self.s2['id'], map(lambda x: x['id'], servers))
+        self.assertIn(self.s3['id'], map(lambda x: x['id'], servers))
 
     @attr(type='positive')
-    def test_list_servers_limit_results(self):
+    def test_list_servers_detailed_filter_by_limit(self):
         # Verify only the expected number of servers are returned
         params = {'limit': 1}
         resp, servers = self.client.list_servers_with_detail(params)
@@ -150,9 +140,9 @@
         resp, body = self.client.list_servers_with_detail(params)
         servers = body['servers']
 
-        self.assertTrue(self._server_id_in_results(self.s1['id'], servers))
-        self.assertFalse(self._server_id_in_results(self.s2['id'], servers))
-        self.assertTrue(self._server_id_in_results(self.s3['id'], servers))
+        self.assertIn(self.s1['id'], map(lambda x: x['id'], servers))
+        self.assertNotIn(self.s2['id'], map(lambda x: x['id'], servers))
+        self.assertIn(self.s3['id'], map(lambda x: x['id'], servers))
 
     @attr(type='positive')
     def test_list_servers_detailed_filter_by_flavor(self):
@@ -161,9 +151,9 @@
         resp, body = self.client.list_servers_with_detail(params)
         servers = body['servers']
 
-        self.assertFalse(self._server_id_in_results(self.s1['id'], servers))
-        self.assertFalse(self._server_id_in_results(self.s2['id'], servers))
-        self.assertTrue(self._server_id_in_results(self.s3['id'], servers))
+        self.assertNotIn(self.s1['id'], map(lambda x: x['id'], servers))
+        self.assertNotIn(self.s2['id'], map(lambda x: x['id'], servers))
+        self.assertIn(self.s3['id'], map(lambda x: x['id'], servers))
 
     @attr(type='positive')
     def test_list_servers_detailed_filter_by_server_name(self):
@@ -172,9 +162,9 @@
         resp, body = self.client.list_servers_with_detail(params)
         servers = body['servers']
 
-        self.assertTrue(self._server_id_in_results(self.s1['id'], servers))
-        self.assertFalse(self._server_id_in_results(self.s2['id'], servers))
-        self.assertFalse(self._server_id_in_results(self.s3['id'], servers))
+        self.assertIn(self.s1_name, map(lambda x: x['name'], servers))
+        self.assertNotIn(self.s2_name, map(lambda x: x['name'], servers))
+        self.assertNotIn(self.s3_name, map(lambda x: x['name'], servers))
 
     @attr(type='positive')
     def test_list_servers_detailed_filter_by_server_status(self):
@@ -183,9 +173,10 @@
         resp, body = self.client.list_servers_with_detail(params)
         servers = body['servers']
 
-        self.assertTrue(self._server_id_in_results(self.s1['id'], servers))
-        self.assertTrue(self._server_id_in_results(self.s2['id'], servers))
-        self.assertTrue(self._server_id_in_results(self.s3['id'], servers))
+        self.assertIn(self.s1['id'], map(lambda x: x['id'], servers))
+        self.assertIn(self.s2['id'], map(lambda x: x['id'], servers))
+        self.assertIn(self.s3['id'], map(lambda x: x['id'], servers))
+        self.assertEqual(['ACTIVE'] * 3, [x['status'] for x in servers])
 
     @attr(type='positive')
     def test_list_servers_detailed_limit_results(self):
@@ -194,14 +185,6 @@
         resp, servers = self.client.list_servers_with_detail(params)
         self.assertEqual(1, len(servers['servers']))
 
-    @classmethod
-    def _convert_to_min_details(self, server):
-        min_detail = {}
-        min_detail['name'] = server['name']
-        min_detail['links'] = server['links']
-        min_detail['id'] = server['id']
-        return min_detail
-
 
 class ListServerFiltersTestXML(ListServerFiltersTestJSON):
     _interface = 'xml'
diff --git a/tempest/tests/compute/servers/test_server_actions.py b/tempest/tests/compute/servers/test_server_actions.py
index 5046ec2..d5b2650 100644
--- a/tempest/tests/compute/servers/test_server_actions.py
+++ b/tempest/tests/compute/servers/test_server_actions.py
@@ -41,7 +41,7 @@
         # Check if the server is in a clean state after test
         try:
             self.client.wait_for_server_status(self.server_id, 'ACTIVE')
-        except exceptions:
+        except Exception:
             # Rebuild server if something happened to it during a test
             self.rebuild_servers()
 
diff --git a/tempest/tests/compute/servers/test_server_rescue.py b/tempest/tests/compute/servers/test_server_rescue.py
index 70e3b7c..9230305 100644
--- a/tempest/tests/compute/servers/test_server_rescue.py
+++ b/tempest/tests/compute/servers/test_server_rescue.py
@@ -123,7 +123,7 @@
         self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
 
     @attr(type='negative')
-    @testtools.skip("Skipped until BUG:1126163 is resolved")
+    @testtools.skip("Skipped until Bug:1126163 is resolved")
     def test_rescued_vm_reboot(self):
         self.assertRaises(exceptions.BadRequest, self.servers_client.reboot,
                           self.rescue_id, 'HARD')
@@ -136,6 +136,7 @@
                           self.image_ref_alt)
 
     @attr(type='positive')
+    @testtools.skip("Skipped due to Bug:1126187")
     def test_rescued_vm_attach_volume(self):
         client = self.volumes_extensions_client
 
@@ -164,7 +165,7 @@
         self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
 
     @attr(type='positive')
-    @testtools.skip("Skipped until BUG:1126187 is resolved")
+    @testtools.skip("Skipped until Bug:1126187 is resolved")
     def test_rescued_vm_detach_volume(self):
         # Attach the volume to the server
         self.servers_client.attach_volume(self.server_id,
@@ -216,7 +217,7 @@
         self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
 
     @attr(type='positive')
-    @testtools.skip("Skipped until BUG: 1126257 is resolved")
+    @testtools.skip("Skipped until Bug: 1126257 is resolved")
     def test_rescued_vm_add_remove_security_group(self):
         #Add Security group
         resp, body = self.servers_client.add_security_group(self.server_id,
diff --git a/tempest/tests/compute/test_authorization.py b/tempest/tests/compute/test_authorization.py
index 5270b51..c958517 100644
--- a/tempest/tests/compute/test_authorization.py
+++ b/tempest/tests/compute/test_authorization.py
@@ -312,15 +312,11 @@
         # A get metadata for another user's server should fail
         req_metadata = {'meta1': 'data1'}
         self.client.set_server_metadata(self.server['id'], req_metadata)
-        try:
-            resp, meta = \
-            self.alt_client.get_server_metadata_item(self.server['id'],
-                                                     'meta1')
-        except exceptions.NotFound:
-            pass
-        finally:
-            resp, body = \
-            self.client.delete_server_metadata_item(self.server['id'], 'meta1')
+        self.addCleanup(self.client.delete_server_metadata_item,
+                        self.server['id'], 'meta1')
+        self.assertRaises(exceptions.NotFound,
+                          self.alt_client.get_server_metadata_item,
+                          self.server['id'], 'meta1')
 
     def test_get_metadata_of_alt_account_image_fails(self):
         # A get metadata for another user's image should fail
diff --git a/tempest/tests/compute/test_quotas.py b/tempest/tests/compute/test_quotas.py
index dbff275..233d639 100644
--- a/tempest/tests/compute/test_quotas.py
+++ b/tempest/tests/compute/test_quotas.py
@@ -33,11 +33,14 @@
 
     @attr(type='smoke')
     def test_get_default_quotas(self):
+        # Tempest two step
+        self.skipTest('Skipped until the Bug 1125468 is resolved')
+
         # User can get the default quota set for it's tenant
         expected_quota_set = {'injected_file_content_bytes': 10240,
                               'metadata_items': 128, 'injected_files': 5,
                               'ram': 51200, 'floating_ips': 10,
-                              'key_pairs': 100,
+                              'fixed_ips': 10, 'key_pairs': 100,
                               'injected_file_path_bytes': 255, 'instances': 10,
                               'security_group_rules': 20, 'cores': 20,
                               'id': self.tenant_id, 'security_groups': 10}
diff --git a/tempest/tests/image/base.py b/tempest/tests/image/base.py
new file mode 100644
index 0000000..65d81b6
--- /dev/null
+++ b/tempest/tests/image/base.py
@@ -0,0 +1,94 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# 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
+#    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
+import time
+
+from tempest import clients
+from tempest.common.utils.data_utils import rand_name
+from tempest import exceptions
+import tempest.test
+
+LOG = logging.getLogger(__name__)
+
+
+class BaseImageTest(tempest.test.BaseTestCase):
+    """Base test class for Image API tests."""
+
+    @classmethod
+    def setUpClass(cls):
+        cls.os = clients.Manager()
+        cls.created_images = []
+
+    @classmethod
+    def tearDownClass(cls):
+        for image_id in cls.created_images:
+            try:
+                cls.client.delete_image(image_id)
+            except exceptions.NotFound:
+                pass
+
+        for image_id in cls.created_images:
+                cls.client.wait_for_resource_deletion(image_id)
+
+    @classmethod
+    def create_image(cls, **kwargs):
+        """Wrapper that returns a test image."""
+        name = rand_name(cls.__name__ + "-instance")
+
+        if 'name' in kwargs:
+            name = kwargs.pop('name')
+
+        container_format = kwargs.pop('container_format')
+        disk_format = kwargs.pop('disk_format')
+
+        resp, image = cls.client.create_image(name, container_format,
+                                              disk_format, **kwargs)
+        cls.created_images.append(image['id'])
+        return resp, image
+
+    @classmethod
+    def _check_version(cls, version):
+        __, versions = cls.client.get_versions()
+        if version == 'v2.0':
+            if 'v2.0' in versions:
+                return True
+        elif version == 'v1.0':
+            if 'v1.1' in versions or 'v1.0' in versions:
+                return True
+        return False
+
+
+class BaseV1ImageTest(BaseImageTest):
+
+    @classmethod
+    def setUpClass(cls):
+        super(BaseV1ImageTest, cls).setUpClass()
+        cls.client = cls.os.image_client
+        if not cls._check_version('v1.0'):
+            msg = "Glance API v1 not supported"
+            raise cls.skipException(msg)
+
+
+class BaseV2ImageTest(BaseImageTest):
+
+    @classmethod
+    def setUpClass(cls):
+        super(BaseV2ImageTest, cls).setUpClass()
+        cls.client = cls.os.image_client_v2
+        if not cls._check_version('v2.0'):
+            msg = "Glance API v2 not supported"
+            raise cls.skipException(msg)
diff --git a/tempest/tests/image/v1/test_image_members.py b/tempest/tests/image/v1/test_image_members.py
index 30fa6c6..92052fc 100644
--- a/tempest/tests/image/v1/test_image_members.py
+++ b/tempest/tests/image/v1/test_image_members.py
@@ -17,42 +17,32 @@
 import cStringIO as StringIO
 
 from tempest import clients
-import tempest.test
+from tempest.tests.image import base
 
 
-class ImageMembersTests(tempest.test.BaseTestCase):
+class ImageMembersTests(base.BaseV1ImageTest):
 
     @classmethod
     def setUpClass(cls):
-        cls.os = clients.Manager()
-        cls.client = cls.os.image_client
+        super(ImageMembersTests, cls).setUpClass()
         admin = clients.AdminManager(interface='json')
         cls.admin_client = admin.identity_client
-        cls.created_images = []
         cls.tenants = cls._get_tenants()
 
     @classmethod
-    def tearDownClass(cls):
-        for image_id in cls.created_images:
-            cls.client.delete_image(image_id)
-            cls.client.wait_for_resource_deletion(image_id)
-
-    @classmethod
     def _get_tenants(cls):
         resp, tenants = cls.admin_client.list_tenants()
         tenants = map(lambda x: x['id'], tenants)
         return tenants
 
-    def _create_image(self, name=None):
+    def _create_image(self):
         image_file = StringIO.StringIO('*' * 1024)
-        if name is not None:
-            name = 'New Standard Image with Members'
-        resp, image = self.client.create_image(name,
-                                               'bare', 'raw',
-                                               is_public=True, data=image_file)
+        resp, image = self.create_image(container_format='bare',
+                                        disk_format='raw',
+                                        is_public=True,
+                                        data=image_file)
         self.assertEquals(201, resp.status)
         image_id = image['id']
-        self.created_images.append(image_id)
         return image_id
 
     def test_add_image_member(self):
@@ -69,8 +59,7 @@
         image = self._create_image()
         resp = self.client.add_member(self.tenants[0], image)
         self.assertEquals(204, resp.status)
-        name = 'Shared Image'
-        share_image = self._create_image(name=name)
+        share_image = self._create_image()
         resp = self.client.add_member(self.tenants[0], share_image)
         self.assertEquals(204, resp.status)
         resp, body = self.client.get_shared_images(self.tenants[0])
@@ -81,8 +70,7 @@
         self.assertIn(image, images)
 
     def test_remove_member(self):
-        name = 'Shared Image for Delete Test'
-        image_id = self._create_image(name=name)
+        image_id = self._create_image()
         resp = self.client.add_member(self.tenants[0], image_id)
         self.assertEquals(204, resp.status)
         resp = self.client.delete_member(self.tenants[0], image_id)
diff --git a/tempest/tests/image/v1/test_images.py b/tempest/tests/image/v1/test_images.py
index 84bb650..af09b79 100644
--- a/tempest/tests/image/v1/test_images.py
+++ b/tempest/tests/image/v1/test_images.py
@@ -19,26 +19,12 @@
 
 from tempest import clients
 from tempest import exceptions
-import tempest.test
 from tempest.test import attr
+from tempest.tests.image import base
 
 
-class CreateRegisterImagesTest(tempest.test.BaseTestCase):
-
-    """
-    Here we test the registration and creation of images
-    """
-
-    @classmethod
-    def setUpClass(cls):
-        cls.os = clients.Manager()
-        cls.client = cls.os.image_client
-        cls.created_images = []
-
-    @classmethod
-    def tearDownClass(cls):
-        for image_id in cls.created_images:
-            cls.client.delete(image_id)
+class CreateRegisterImagesTest(base.BaseV1ImageTest):
+    """Here we test the registration and creation of images."""
 
     @attr(type='negative')
     def test_register_with_invalid_container_format(self):
@@ -55,19 +41,17 @@
     def test_register_then_upload(self):
         # Register, then upload an image
         properties = {'prop1': 'val1'}
-        resp, body = self.client.create_image('New Name', 'bare', 'raw',
-                                              is_public=True,
-                                              properties=properties)
+        resp, body = self.create_image(name='New Name',
+                                       container_format='bare',
+                                       disk_format='raw',
+                                       is_public=True,
+                                       properties=properties)
         self.assertTrue('id' in body)
         image_id = body.get('id')
         self.created_images.append(image_id)
-        self.assertTrue('name' in body)
         self.assertEqual('New Name', body.get('name'))
-        self.assertTrue('is_public' in body)
         self.assertTrue(body.get('is_public'))
-        self.assertTrue('status' in body)
         self.assertEqual('queued', body.get('status'))
-        self.assertTrue('properties' in body)
         for key, val in properties.items():
             self.assertEqual(val, body.get('properties')[key])
 
@@ -80,22 +64,20 @@
     @attr(type='image')
     def test_register_remote_image(self):
         # Register a new remote image
-        resp, body = self.client.create_image('New Remote Image', 'bare',
-                                              'raw', is_public=True,
-                                              location='http://example.com'
-                                                       '/someimage.iso')
+        resp, body = self.create_image(name='New Remote Image',
+                                       container_format='bare',
+                                       disk_format='raw', is_public=True,
+                                       location='http://example.com'
+                                                '/someimage.iso')
         self.assertTrue('id' in body)
         image_id = body.get('id')
         self.created_images.append(image_id)
-        self.assertTrue('name' in body)
         self.assertEqual('New Remote Image', body.get('name'))
-        self.assertTrue('is_public' in body)
         self.assertTrue(body.get('is_public'))
-        self.assertTrue('status' in body)
         self.assertEqual('active', body.get('status'))
 
 
-class ListImagesTest(tempest.test.BaseTestCase):
+class ListImagesTest(base.BaseV1ImageTest):
 
     """
     Here we test the listing of image information
@@ -103,9 +85,7 @@
 
     @classmethod
     def setUpClass(cls):
-        cls.os = clients.Manager()
-        cls.client = cls.os.image_client
-        cls.created_images = []
+        super(ListImagesTest, cls).setUpClass()
 
         # We add a few images here to test the listing functionality of
         # the images API
@@ -132,12 +112,6 @@
         cls.dup_set = set((img3, img4))
 
     @classmethod
-    def tearDownClass(cls):
-        for image_id in cls.created_images:
-            cls.client.delete_image(image_id)
-            cls.client.wait_for_resource_deletion(image_id)
-
-    @classmethod
     def _create_remote_image(cls, name, container_format, disk_format):
         """
         Create a new remote image and return the ID of the newly-registered
@@ -145,12 +119,12 @@
         """
         name = 'New Remote Image %s' % name
         location = 'http://example.com/someimage_%s.iso' % name
-        resp, image = cls.client.create_image(name,
-                                              container_format, disk_format,
-                                              is_public=True,
-                                              location=location)
+        resp, image = cls.create_image(name=name,
+                                       container_format=container_format,
+                                       disk_format=disk_format,
+                                       is_public=True,
+                                       location=location)
         image_id = image['id']
-        cls.created_images.append(image_id)
         return image_id
 
     @classmethod
@@ -163,11 +137,11 @@
         """
         image_file = StringIO.StringIO('*' * size)
         name = 'New Standard Image %s' % name
-        resp, image = cls.client.create_image(name,
-                                              container_format, disk_format,
-                                              is_public=True, data=image_file)
+        resp, image = cls.create_image(name=name,
+                                       container_format=container_format,
+                                       disk_format=disk_format,
+                                       is_public=True, data=image_file)
         image_id = image['id']
-        cls.created_images.append(image_id)
         return image_id
 
     @attr(type='image')
diff --git a/tempest/tests/image/v2/test_images.py b/tempest/tests/image/v2/test_images.py
index 9abf28d..19a7a95 100644
--- a/tempest/tests/image/v2/test_images.py
+++ b/tempest/tests/image/v2/test_images.py
@@ -21,27 +21,16 @@
 
 from tempest import clients
 from tempest import exceptions
-import tempest.test
 from tempest.test import attr
+from tempest.tests.image import base
 
 
-class CreateRegisterImagesTest(tempest.test.BaseTestCase):
+class CreateRegisterImagesTest(base.BaseV2ImageTest):
 
     """
     Here we test the registration and creation of images
     """
 
-    @classmethod
-    def setUpClass(cls):
-        cls.os = clients.Manager()
-        cls.client = cls.os.image_client_v2
-        cls.created_images = []
-
-    @classmethod
-    def tearDownClass(cls):
-        for image_id in cls.created_images:
-            cls.client.delete(image_id)
-
     @attr(type='negative')
     def test_register_with_invalid_container_format(self):
         # Negative tests for invalid data supplied to POST /images
@@ -56,8 +45,10 @@
     @attr(type='image')
     def test_register_then_upload(self):
         # Register, then upload an image
-        resp, body = self.client.create_image('New Name', 'bare', 'raw',
-                                              is_public=True)
+        resp, body = self.create_image(name='New Name',
+                                       container_format='bare',
+                                       disk_format='raw',
+                                       visibility='public')
         self.assertTrue('id' in body)
         image_id = body.get('id')
         self.created_images.append(image_id)
@@ -77,7 +68,7 @@
         self.assertEqual(1024, body.get('size'))
 
 
-class ListImagesTest(tempest.test.BaseTestCase):
+class ListImagesTest(base.BaseV2ImageTest):
 
     """
     Here we test the listing of image information
@@ -85,22 +76,13 @@
 
     @classmethod
     def setUpClass(cls):
-        cls.os = clients.Manager()
-        cls.client = cls.os.image_client_v2
-        cls.created_images = []
-
+        super(ListImagesTest, cls).setUpClass()
         # We add a few images here to test the listing functionality of
         # the images API
         for x in xrange(0, 10):
             cls.created_images.append(cls._create_standard_image(x))
 
     @classmethod
-    def tearDownClass(cls):
-        for image_id in cls.created_images:
-            cls.client.delete_image(image_id)
-            cls.client.wait_for_resource_deletion(image_id)
-
-    @classmethod
     def _create_standard_image(cls, number):
         """
         Create a new standard image and return the ID of the newly-registered
@@ -109,8 +91,9 @@
         """
         image_file = StringIO.StringIO('*' * random.randint(1024, 4096))
         name = 'New Standard Image %s' % number
-        resp, body = cls.client.create_image(name, 'bare', 'raw',
-                                             is_public=True)
+        resp, body = cls.create_image(name=name, container_format='bare',
+                                      disk_format='raw',
+                                      visibility='public')
         image_id = body['id']
         resp, body = cls.client.store_image(image_id, data=image_file)
 
diff --git a/tools/find_stack_traces.py b/tools/find_stack_traces.py
new file mode 100755
index 0000000..e6c1990
--- /dev/null
+++ b/tools/find_stack_traces.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corp.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import gzip
+import re
+import StringIO
+import sys
+import urllib2
+
+
+def hunt_for_stacktrace(url):
+    """Return TRACE or ERROR lines out of logs."""
+    page = urllib2.urlopen(url)
+    buf = StringIO.StringIO(page.read())
+    f = gzip.GzipFile(fileobj=buf)
+    content = f.read()
+    traces = re.findall('^(.*? (TRACE|ERROR) .*?)$', content, re.MULTILINE)
+    tracelist = map(lambda x: x[0], traces)
+    # filter out log definitions as false possitives
+    return filter(lambda x: not re.search('logging_exception_prefix', x),
+                  tracelist)
+
+
+def log_url(url, log):
+    return "%s/%s" % (url, log)
+
+
+def collect_logs(url):
+    page = urllib2.urlopen(url)
+    content = page.read()
+    logs = re.findall('(screen-[\w-]+\.txt\.gz)</a>', content)
+    return logs
+
+
+def usage():
+    print """
+Usage: find_stack_traces.py <logurl>
+
+Hunts for stack traces in a devstack run. Must provide it a base log url
+from a tempest devstack run. Should start with http and end with /logs/.
+
+Returns a report listing stack traces out of the various files where
+they are found.
+"""
+    sys.exit(0)
+
+
+def main():
+    if len(sys.argv) == 2:
+        url = sys.argv[1]
+        loglist = collect_logs(url)
+
+        # probably wrong base url
+        if not loglist:
+            usage()
+
+        for log in loglist:
+            logurl = log_url(url, log)
+            traces = hunt_for_stacktrace(logurl)
+            if traces:
+                print "\n\nTRACES found in %s\n" % log
+                for line in traces:
+                    print line
+    else:
+        usage()
+
+if __name__ == '__main__':
+    main()
diff --git a/tools/pip-requires b/tools/pip-requires
index ee21065..e85cced 100644
--- a/tools/pip-requires
+++ b/tools/pip-requires
@@ -13,5 +13,4 @@
 testresources
 keyring
 testrepository
-
-http://tarballs.openstack.org/oslo-config/oslo.config-1.1.0b1.tar.gz#egg=oslo.config
+oslo.config>=1.1.0
diff --git a/tools/skip_tracker.py b/tools/skip_tracker.py
index e890e92..a4cf394 100755
--- a/tools/skip_tracker.py
+++ b/tools/skip_tracker.py
@@ -61,7 +61,7 @@
     """
     Return the skip tuples in a test file
     """
-    BUG_RE = re.compile(r'.*skip\(.*[bB]ug\s*(\d+)')
+    BUG_RE = re.compile(r'.*skip\(.*bug:*\s*\#*(\d+)', re.IGNORECASE)
     DEF_RE = re.compile(r'.*def (\w+)\(')
     bug_found = False
     results = []
@@ -89,6 +89,7 @@
     results = find_skips()
     unique_bugs = sorted(set([bug for (method, bug) in results]))
     unskips = []
+    duplicates = []
     info("Total bug skips found: %d", len(results))
     info("Total unique bugs causing skips: %d", len(unique_bugs))
     lp = launchpad.Launchpad.login_anonymously('grabbing bugs',
@@ -96,12 +97,26 @@
                                                LPCACHEDIR)
     for bug_no in unique_bugs:
         bug = lp.bugs[bug_no]
+        duplicate = bug.duplicate_of_link
+        if duplicate is not None:
+            dup_id = duplicate.split('/')[-1]
+            duplicates.append((bug_no, dup_id))
         for task in bug.bug_tasks:
             info("Bug #%7s (%12s - %12s)", bug_no,
                  task.importance, task.status)
             if task.status in ('Fix Released', 'Fix Committed'):
                 unskips.append(bug_no)
 
+    for bug_id, dup_id in duplicates:
+        if bug_id not in unskips:
+            dup_bug = lp.bugs[dup_id]
+            for task in dup_bug.bug_tasks:
+                info("Bug #%7s is a duplicate of Bug#%7s (%12s - %12s)",
+                     bug_id, dup_id, task.importance, task.status)
+                if task.status in ('Fix Released', 'Fix Committed'):
+                    unskips.append(bug_id)
+
+    unskips = sorted(set(unskips))
     if unskips:
         print "The following bugs have been fixed and the corresponding skips"
         print "should be removed from the test cases:"