Merge "Enable the Nova V3 API Tests"
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs.py b/tempest/api/compute/admin/test_flavors_extra_specs.py
index 5b4ca87..49b0429 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs.py
@@ -25,7 +25,7 @@
 
     """
     Tests Flavor Extra Spec API extension.
-    SET, UNSET Flavor Extra specs require admin privileges.
+    SET, UNSET, UPDATE Flavor Extra specs require admin privileges.
     GET Flavor Extra specs can be performed even by without admin privileges.
     """
 
@@ -86,13 +86,6 @@
         self.assertEqual(update_resp.status, 200)
         self.assertEqual({"key1": "value"}, update_body)
 
-        # GET a key value and verify
-        show_resp, get_body = \
-            self.client.get_flavor_extra_spec_with_key(self.flavor['id'],
-                                                       "key1")
-        self.assertEqual(show_resp.status, 200)
-        self.assertEqual(get_body, 'value')
-
         # GET extra specs and verify the value of the key2
         # is the same as before
         get_resp, get_body = \
@@ -109,7 +102,7 @@
         self.assertEqual(unset_resp.status, 200)
 
     @attr(type='gate')
-    def test_flavor_non_admin_get_all_keys_and_specified_key(self):
+    def test_flavor_non_admin_get_all_keys(self):
         specs = {"key1": "value1", "key2": "value2"}
         set_resp, set_body = self.client.set_flavor_extra_spec(
             self.flavor['id'], specs)
@@ -120,12 +113,19 @@
         for key in specs:
             self.assertEqual(body[key], specs[key])
 
-        get_resp, get_body = \
-            self.flavors_client.get_flavor_extra_spec_with_key(
-                self.flavor['id'],
-                "key1")
-        self.assertEqual(get_resp.status, 200)
-        self.assertEqual("value1", get_body)
+    @attr(type='gate')
+    def test_flavor_non_admin_get_specific_key(self):
+        specs = {"key1": "value1", "key2": "value2"}
+        resp, body = self.client.set_flavor_extra_spec(
+            self.flavor['id'], specs)
+        self.assertEqual(resp.status, 200)
+        self.assertEqual(body['key1'], 'value1')
+        self.assertIn('key2', body)
+        resp, body = self.flavors_client.get_flavor_extra_spec_with_key(
+            self.flavor['id'], 'key1')
+        self.assertEqual(resp.status, 200)
+        self.assertEqual(body['key1'], 'value1')
+        self.assertNotIn('key2', body)
 
 
 class FlavorsExtraSpecsTestXML(FlavorsExtraSpecsTestJSON):
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
index 044d6ad..d7e1f9f 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
@@ -24,7 +24,11 @@
 
 
 class FlavorsExtraSpecsNegativeTestJSON(base.BaseV2ComputeAdminTest):
-    """the Negative tests for FlavorsExtraSpecs."""
+
+    """
+    Negative Tests Flavor Extra Spec API extension.
+    SET, UNSET, UPDATE Flavor Extra specs require admin privileges.
+    """
 
     _interface = 'json'
 
@@ -68,6 +72,21 @@
                           specs)
 
     @attr(type=['negative', 'gate'])
+    def test_flavor_non_admin_update_specific_key(self):
+        # non admin user is not allowed to update flavor extra spec
+        specs = {"key1": "value1", "key2": "value2"}
+        resp, body = self.client.set_flavor_extra_spec(
+            self.flavor['id'], specs)
+        self.assertEqual(resp.status, 200)
+        self.assertEqual(body['key1'], 'value1')
+        self.assertRaises(exceptions.Unauthorized,
+                          self.flavors_client.
+                          update_flavor_extra_spec,
+                          self.flavor['id'],
+                          'key1',
+                          key1='value1_new')
+
+    @attr(type=['negative', 'gate'])
     def test_flavor_non_admin_unset_keys(self):
         specs = {"key1": "value1", "key2": "value2"}
         set_resp, set_body = self.client.set_flavor_extra_spec(
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 4b6816b..a4a877c 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -233,6 +233,7 @@
         cls.images_client = cls.os.image_client
         cls.services_client = cls.os.services_v3_client
         cls.extensions_client = cls.os.extensions_v3_client
+        cls.availability_zone_client = cls.os.availability_zone_v3_client
 
     @classmethod
     def create_image_from_server(cls, server_id, **kwargs):
@@ -293,3 +294,5 @@
         cls.os_adm = os_adm
         cls.severs_admin_client = cls.os_adm.servers_v3_client
         cls.services_admin_client = cls.os_adm.services_v3_client
+        cls.availability_zone_admin_client = \
+            cls.os_adm.availability_zone_v3_client
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index db3acb5..b4e778c 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -19,8 +19,7 @@
 from tempest.api import compute
 from tempest.api.compute import base
 from tempest import clients
-from tempest.common.utils.data_utils import parse_image_id
-from tempest.common.utils.data_utils import rand_name
+from tempest.common.utils import data_utils
 from tempest import exceptions
 from tempest.openstack.common import log as logging
 from tempest.test import attr
@@ -86,7 +85,7 @@
     @attr(type=['negative', 'gate'])
     def test_create_image_specify_multibyte_character_image_name(self):
         # Return an error if the image name has multi-byte characters
-        snapshot_name = rand_name('\xef\xbb\xbf')
+        snapshot_name = data_utils.rand_name('\xef\xbb\xbf')
         self.assertRaises(exceptions.BadRequest,
                           self.client.create_image, self.server_id,
                           snapshot_name)
@@ -94,7 +93,7 @@
     @attr(type=['negative', 'gate'])
     def test_create_image_specify_invalid_metadata(self):
         # Return an error when creating image with invalid metadata
-        snapshot_name = rand_name('test-snap-')
+        snapshot_name = data_utils.rand_name('test-snap-')
         meta = {'': ''}
         self.assertRaises(exceptions.BadRequest, self.client.create_image,
                           self.server_id, snapshot_name, meta)
@@ -102,7 +101,7 @@
     @attr(type=['negative', 'gate'])
     def test_create_image_specify_metadata_over_limits(self):
         # Return an error when creating image with meta data over 256 chars
-        snapshot_name = rand_name('test-snap-')
+        snapshot_name = data_utils.rand_name('test-snap-')
         meta = {'a' * 260: 'b' * 260}
         self.assertRaises(exceptions.BadRequest, self.client.create_image,
                           self.server_id, snapshot_name, meta)
@@ -112,15 +111,15 @@
         # Disallow creating another image when first image is being saved
 
         # Create first snapshot
-        snapshot_name = rand_name('test-snap-')
+        snapshot_name = data_utils.rand_name('test-snap-')
         resp, body = self.client.create_image(self.server_id,
                                               snapshot_name)
         self.assertEqual(202, resp.status)
-        image_id = parse_image_id(resp['location'])
+        image_id = data_utils.parse_image_id(resp['location'])
         self.image_ids.append(image_id)
 
         # Create second snapshot
-        alt_snapshot_name = rand_name('test-snap-')
+        alt_snapshot_name = data_utils.rand_name('test-snap-')
         self.assertRaises(exceptions.Conflict, self.client.create_image,
                           self.server_id, alt_snapshot_name)
         self.client.wait_for_image_status(image_id, 'ACTIVE')
@@ -129,7 +128,7 @@
     def test_create_image_specify_name_over_256_chars(self):
         # Return an error if snapshot name over 256 characters is passed
 
-        snapshot_name = rand_name('a' * 260)
+        snapshot_name = data_utils.rand_name('a' * 260)
         self.assertRaises(exceptions.BadRequest, self.client.create_image,
                           self.server_id, snapshot_name)
 
@@ -137,10 +136,10 @@
     def test_delete_image_that_is_not_yet_active(self):
         # Return an error while trying to delete an image what is creating
 
-        snapshot_name = rand_name('test-snap-')
+        snapshot_name = data_utils.rand_name('test-snap-')
         resp, body = self.client.create_image(self.server_id, snapshot_name)
         self.assertEqual(202, resp.status)
-        image_id = parse_image_id(resp['location'])
+        image_id = data_utils.parse_image_id(resp['location'])
         self.image_ids.append(image_id)
 
         # Do not wait, attempt to delete the image, ensure it's successful
diff --git a/tempest/api/compute/security_groups/test_security_group_rules.py b/tempest/api/compute/security_groups/test_security_group_rules.py
index d61acfb..2ccc3a8 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules.py
@@ -15,11 +15,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import uuid
-
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
-from tempest import exceptions
 from tempest.test import attr
 
 
@@ -32,7 +29,7 @@
         cls.client = cls.security_groups_client
         cls.neutron_available = cls.config.service_available.neutron
 
-    @attr(type='gate')
+    @attr(type='smoke')
     def test_security_group_rules_create(self):
         # Positive test: Creation of Security Group rule
         # should be successful
@@ -55,7 +52,7 @@
         self.addCleanup(self.client.delete_security_group_rule, rule['id'])
         self.assertEqual(200, resp.status)
 
-    @attr(type='gate')
+    @attr(type='smoke')
     def test_security_group_rules_create_with_optional_arguments(self):
         # Positive test: Creation of Security Group rule
         # with optional arguments
@@ -94,110 +91,7 @@
         self.addCleanup(self.client.delete_security_group_rule, rule['id'])
         self.assertEqual(200, resp.status)
 
-    @attr(type=['negative', 'smoke'])
-    def test_security_group_rules_create_with_invalid_id(self):
-        # Negative test: Creation of Security Group rule should FAIL
-        # with invalid Parent group id
-        # Adding rules to the invalid Security Group id
-        parent_group_id = data_utils.rand_int_id(start=999)
-        if self.neutron_available:
-            parent_group_id = str(uuid.uuid4())
-        ip_protocol = 'tcp'
-        from_port = 22
-        to_port = 22
-        self.assertRaises(exceptions.NotFound,
-                          self.client.create_security_group_rule,
-                          parent_group_id, ip_protocol, from_port, to_port)
-
-    @attr(type=['negative', 'gate'])
-    def test_security_group_rules_create_with_invalid_ip_protocol(self):
-        # Negative test: Creation of Security Group rule should FAIL
-        # with invalid ip_protocol
-        # Creating a Security Group to add rule to it
-        s_name = data_utils.rand_name('securitygroup-')
-        s_description = data_utils.rand_name('description-')
-        resp, securitygroup = self.client.create_security_group(s_name,
-                                                                s_description)
-        # Adding rules to the created Security Group
-        parent_group_id = securitygroup['id']
-        ip_protocol = data_utils.rand_name('999')
-        from_port = 22
-        to_port = 22
-
-        self.addCleanup(self.client.delete_security_group, securitygroup['id'])
-        self.assertRaises(exceptions.BadRequest,
-                          self.client.create_security_group_rule,
-                          parent_group_id, ip_protocol, from_port, to_port)
-
-    @attr(type=['negative', 'gate'])
-    def test_security_group_rules_create_with_invalid_from_port(self):
-        # Negative test: Creation of Security Group rule should FAIL
-        # with invalid from_port
-        # Creating a Security Group to add rule to it
-        s_name = data_utils.rand_name('securitygroup-')
-        s_description = data_utils.rand_name('description-')
-        resp, securitygroup = self.client.create_security_group(s_name,
-                                                                s_description)
-        # Adding rules to the created Security Group
-        parent_group_id = securitygroup['id']
-        ip_protocol = 'tcp'
-        from_port = data_utils.rand_int_id(start=999, end=65535)
-        to_port = 22
-        self.addCleanup(self.client.delete_security_group, securitygroup['id'])
-        self.assertRaises(exceptions.BadRequest,
-                          self.client.create_security_group_rule,
-                          parent_group_id, ip_protocol, from_port, to_port)
-
-    @attr(type=['negative', 'gate'])
-    def test_security_group_rules_create_with_invalid_to_port(self):
-        # Negative test: Creation of Security Group rule should FAIL
-        # with invalid to_port
-        # Creating a Security Group to add rule to it
-        s_name = data_utils.rand_name('securitygroup-')
-        s_description = data_utils.rand_name('description-')
-        resp, securitygroup = self.client.create_security_group(s_name,
-                                                                s_description)
-        # Adding rules to the created Security Group
-        parent_group_id = securitygroup['id']
-        ip_protocol = 'tcp'
-        from_port = 22
-        to_port = data_utils.rand_int_id(start=65536)
-        self.addCleanup(self.client.delete_security_group, securitygroup['id'])
-        self.assertRaises(exceptions.BadRequest,
-                          self.client.create_security_group_rule,
-                          parent_group_id, ip_protocol, from_port, to_port)
-
-    @attr(type=['negative', 'gate'])
-    def test_security_group_rules_create_with_invalid_port_range(self):
-        # Negative test: Creation of Security Group rule should FAIL
-        # with invalid port range.
-        # Creating a Security Group to add rule to it.
-        s_name = data_utils.rand_name('securitygroup-')
-        s_description = data_utils.rand_name('description-')
-        resp, securitygroup = self.client.create_security_group(s_name,
-                                                                s_description)
-        # Adding a rule to the created Security Group
-        secgroup_id = securitygroup['id']
-        ip_protocol = 'tcp'
-        from_port = 22
-        to_port = 21
-        self.addCleanup(self.client.delete_security_group, securitygroup['id'])
-        self.assertRaises(exceptions.BadRequest,
-                          self.client.create_security_group_rule,
-                          secgroup_id, ip_protocol, from_port, to_port)
-
-    @attr(type=['negative', 'smoke'])
-    def test_security_group_rules_delete_with_invalid_id(self):
-        # Negative test: Deletion of Security Group rule should be FAIL
-        # with invalid rule id
-        group_rule_id = data_utils.rand_int_id(start=999)
-        if self.neutron_available:
-            group_rule_id = str(uuid.uuid4())
-        self.assertRaises(exceptions.NotFound,
-                          self.client.delete_security_group_rule,
-                          group_rule_id)
-
-    @attr(type='gate')
+    @attr(type='smoke')
     def test_security_group_rules_list(self):
         # Positive test: Created Security Group rules should be
         # in the list of all rules
@@ -240,6 +134,44 @@
         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]))
 
+    @attr(type='smoke')
+    def test_security_group_rules_delete_when_peer_group_deleted(self):
+        # Positive test:rule will delete when peer group deleting
+        # Creating a Security Group to add rules to it
+        s1_name = data_utils.rand_name('securitygroup1-')
+        s1_description = data_utils.rand_name('description1-')
+        resp, sg1 = \
+            self.client.create_security_group(s1_name, s1_description)
+        self.addCleanup(self.client.delete_security_group, sg1['id'])
+        self.assertEqual(200, resp.status)
+        # Creating other Security Group to access to group1
+        s2_name = data_utils.rand_name('securitygroup2-')
+        s2_description = data_utils.rand_name('description2-')
+        resp, sg2 = \
+            self.client.create_security_group(s2_name, s2_description)
+        self.assertEqual(200, resp.status)
+        sg2_id = sg2['id']
+        # Adding rules to the Group1
+        ip_protocol = 'tcp'
+        from_port = 22
+        to_port = 22
+        resp, rule = \
+            self.client.create_security_group_rule(sg1['id'],
+                                                   ip_protocol,
+                                                   from_port,
+                                                   to_port,
+                                                   group_id=sg2_id)
+
+        self.assertEqual(200, resp.status)
+        # Delete group2
+        resp, body = self.client.delete_security_group(sg2_id)
+        self.assertEqual(202, resp.status)
+        # Get rules of the Group1
+        resp, rules = \
+            self.client.list_security_group_rules(sg1['id'])
+        # The group1 has no rules because group2 has deleted
+        self.assertEqual(0, len(rules))
+
 
 class SecurityGroupRulesTestXML(SecurityGroupRulesTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/security_groups/test_security_group_rules_negative.py b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
new file mode 100644
index 0000000..1c38268
--- /dev/null
+++ b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
@@ -0,0 +1,183 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Huawei Technologies Co.,LTD.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import testtools
+
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import config
+from tempest import exceptions
+from tempest.test import attr
+from tempest.test import skip_because
+
+
+class SecurityGroupRulesNegativeTestJSON(base.BaseV2ComputeTest):
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(SecurityGroupRulesNegativeTestJSON, cls).setUpClass()
+        cls.client = cls.security_groups_client
+
+    @skip_because(bug="1182384",
+                  condition=config.TempestConfig().service_available.neutron)
+    @attr(type=['negative', 'smoke'])
+    def test_create_security_group_rule_with_non_existent_id(self):
+        # Negative test: Creation of Security Group rule should FAIL
+        # with non existent Parent group id
+        # Adding rules to the non existent Security Group id
+        parent_group_id = data_utils.rand_int_id(start=999)
+        ip_protocol = 'tcp'
+        from_port = 22
+        to_port = 22
+        self.assertRaises(exceptions.NotFound,
+                          self.client.create_security_group_rule,
+                          parent_group_id, ip_protocol, from_port, to_port)
+
+    @testtools.skipIf(config.TempestConfig().service_available.neutron,
+                      "Neutron not check the security_group_id")
+    @attr(type=['negative', 'smoke'])
+    def test_create_security_group_rule_with_invalid_id(self):
+        # Negative test: Creation of Security Group rule should FAIL
+        # with Parent group id which is not integer
+        # Adding rules to the non int Security Group id
+        parent_group_id = data_utils.rand_name('non_int_id')
+        ip_protocol = 'tcp'
+        from_port = 22
+        to_port = 22
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_security_group_rule,
+                          parent_group_id, ip_protocol, from_port, to_port)
+
+    @attr(type=['negative', 'smoke'])
+    def test_create_security_group_rule_duplicate(self):
+        # Negative test: Create Security Group rule duplicate should fail
+        # Creating a Security Group to add rule to it
+        s_name = data_utils.rand_name('securitygroup-')
+        s_description = data_utils.rand_name('description-')
+        resp, sg = self.client.create_security_group(s_name, s_description)
+        self.assertEqual(200, resp.status)
+        # Adding rules to the created Security Group
+        parent_group_id = sg['id']
+        ip_protocol = 'tcp'
+        from_port = 22
+        to_port = 22
+
+        self.addCleanup(self.client.delete_security_group, sg['id'])
+        resp, rule = \
+            self.client.create_security_group_rule(parent_group_id,
+                                                   ip_protocol,
+                                                   from_port,
+                                                   to_port)
+        self.addCleanup(self.client.delete_security_group_rule, rule['id'])
+        self.assertEqual(200, resp.status)
+        # Add the same rule to the group should fail
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_security_group_rule,
+                          parent_group_id, ip_protocol, from_port, to_port)
+
+    @attr(type=['negative', 'smoke'])
+    def test_create_security_group_rule_with_invalid_ip_protocol(self):
+        # Negative test: Creation of Security Group rule should FAIL
+        # with invalid ip_protocol
+        # Creating a Security Group to add rule to it
+        s_name = data_utils.rand_name('securitygroup-')
+        s_description = data_utils.rand_name('description-')
+        resp, securitygroup = self.client.create_security_group(s_name,
+                                                                s_description)
+        # Adding rules to the created Security Group
+        parent_group_id = securitygroup['id']
+        ip_protocol = data_utils.rand_name('999')
+        from_port = 22
+        to_port = 22
+
+        self.addCleanup(self.client.delete_security_group, securitygroup['id'])
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_security_group_rule,
+                          parent_group_id, ip_protocol, from_port, to_port)
+
+    @attr(type=['negative', 'smoke'])
+    def test_create_security_group_rule_with_invalid_from_port(self):
+        # Negative test: Creation of Security Group rule should FAIL
+        # with invalid from_port
+        # Creating a Security Group to add rule to it
+        s_name = data_utils.rand_name('securitygroup-')
+        s_description = data_utils.rand_name('description-')
+        resp, securitygroup = self.client.create_security_group(s_name,
+                                                                s_description)
+        # Adding rules to the created Security Group
+        parent_group_id = securitygroup['id']
+        ip_protocol = 'tcp'
+        from_port = data_utils.rand_int_id(start=65536)
+        to_port = 22
+        self.addCleanup(self.client.delete_security_group, securitygroup['id'])
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_security_group_rule,
+                          parent_group_id, ip_protocol, from_port, to_port)
+
+    @attr(type=['negative', 'smoke'])
+    def test_create_security_group_rule_with_invalid_to_port(self):
+        # Negative test: Creation of Security Group rule should FAIL
+        # with invalid to_port
+        # Creating a Security Group to add rule to it
+        s_name = data_utils.rand_name('securitygroup-')
+        s_description = data_utils.rand_name('description-')
+        resp, securitygroup = self.client.create_security_group(s_name,
+                                                                s_description)
+        # Adding rules to the created Security Group
+        parent_group_id = securitygroup['id']
+        ip_protocol = 'tcp'
+        from_port = 22
+        to_port = data_utils.rand_int_id(start=65536)
+        self.addCleanup(self.client.delete_security_group, securitygroup['id'])
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_security_group_rule,
+                          parent_group_id, ip_protocol, from_port, to_port)
+
+    @attr(type=['negative', 'smoke'])
+    def test_create_security_group_rule_with_invalid_port_range(self):
+        # Negative test: Creation of Security Group rule should FAIL
+        # with invalid port range.
+        # Creating a Security Group to add rule to it.
+        s_name = data_utils.rand_name('securitygroup-')
+        s_description = data_utils.rand_name('description-')
+        resp, securitygroup = self.client.create_security_group(s_name,
+                                                                s_description)
+        # Adding a rule to the created Security Group
+        secgroup_id = securitygroup['id']
+        ip_protocol = 'tcp'
+        from_port = 22
+        to_port = 21
+        self.addCleanup(self.client.delete_security_group, securitygroup['id'])
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_security_group_rule,
+                          secgroup_id, ip_protocol, from_port, to_port)
+
+    @skip_because(bug="1182384",
+                  condition=config.TempestConfig().service_available.neutron)
+    @attr(type=['negative', 'smoke'])
+    def test_delete_security_group_rule_with_non_existent_id(self):
+        # Negative test: Deletion of Security Group rule should be FAIL
+        # with non existent id
+        non_existent_rule_id = data_utils.rand_int_id(start=999)
+        self.assertRaises(exceptions.NotFound,
+                          self.client.delete_security_group_rule,
+                          non_existent_rule_id)
+
+
+class SecurityGroupRulesNegativeTestXML(SecurityGroupRulesNegativeTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 1044ae1..5abde56 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -198,7 +198,6 @@
                 required time (%s s).' % (self.server_id, self.build_timeout)
                 raise exceptions.TimeoutException(message)
 
-    @skip_because(bug="1251920")
     @attr(type='gate')
     def test_create_backup(self):
         # Positive test:create backup successfully and rotate backups correctly
@@ -271,6 +270,16 @@
     def test_get_console_output(self):
         # Positive test:Should be able to GET the console output
         # for a given server_id and number of lines
+
+        # This reboot is necessary for outputting some console log after
+        # creating a instance backup. If a instance backup, the console
+        # log file is truncated and we cannot get any console log through
+        # "console-log" API.
+        # The detail is https://bugs.launchpad.net/nova/+bug/1251920
+        resp, body = self.servers_client.reboot(self.server_id, 'HARD')
+        self.assertEqual(202, resp.status)
+        self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
+
         def get_output():
             resp, output = self.servers_client.get_console_output(
                 self.server_id, 10)
diff --git a/tempest/api/compute/v3/admin/test_availability_zone.py b/tempest/api/compute/v3/admin/test_availability_zone.py
index d6488c4..ff2765c 100644
--- a/tempest/api/compute/v3/admin/test_availability_zone.py
+++ b/tempest/api/compute/v3/admin/test_availability_zone.py
@@ -20,7 +20,7 @@
 from tempest.test import attr
 
 
-class AvailabilityZoneAdminTestJSON(base.BaseV2ComputeAdminTest):
+class AvailabilityZoneAdminV3TestJSON(base.BaseV3ComputeAdminTest):
 
     """
     Tests Availability Zone API List that require admin privileges
@@ -30,8 +30,8 @@
 
     @classmethod
     def setUpClass(cls):
-        super(AvailabilityZoneAdminTestJSON, cls).setUpClass()
-        cls.client = cls.os_adm.availability_zone_client
+        super(AvailabilityZoneAdminV3TestJSON, cls).setUpClass()
+        cls.client = cls.availability_zone_admin_client
         cls.non_adm_client = cls.availability_zone_client
 
     @attr(type='gate')
@@ -66,5 +66,5 @@
             self.non_adm_client.get_availability_zone_list_detail)
 
 
-class AvailabilityZoneAdminTestXML(AvailabilityZoneAdminTestJSON):
+class AvailabilityZoneAdminV3TestXML(AvailabilityZoneAdminV3TestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/v3/servers/test_list_server_filters.py b/tempest/api/compute/v3/servers/test_list_server_filters.py
index 5f14460..d333a1d 100644
--- a/tempest/api/compute/v3/servers/test_list_server_filters.py
+++ b/tempest/api/compute/v3/servers/test_list_server_filters.py
@@ -17,7 +17,7 @@
 
 from tempest.api.compute import base
 from tempest.api import utils
-from tempest.common.utils.data_utils import rand_name
+from tempest.common.utils import data_utils
 from tempest import config
 from tempest import exceptions
 from tempest.test import attr
@@ -57,16 +57,16 @@
             raise RuntimeError("Image %s (image_ref_alt) was not found!" %
                                cls.image_ref_alt)
 
-        cls.s1_name = rand_name(cls.__name__ + '-instance')
+        cls.s1_name = data_utils.rand_name(cls.__name__ + '-instance')
         resp, cls.s1 = cls.create_test_server(name=cls.s1_name,
                                               wait_until='ACTIVE')
 
-        cls.s2_name = rand_name(cls.__name__ + '-instance')
+        cls.s2_name = data_utils.rand_name(cls.__name__ + '-instance')
         resp, cls.s2 = cls.create_test_server(name=cls.s2_name,
                                               image_id=cls.image_ref_alt,
                                               wait_until='ACTIVE')
 
-        cls.s3_name = rand_name(cls.__name__ + '-instance')
+        cls.s3_name = data_utils.rand_name(cls.__name__ + '-instance')
         resp, cls.s3 = cls.create_test_server(name=cls.s3_name,
                                               flavor=cls.flavor_ref_alt,
                                               wait_until='ACTIVE')
diff --git a/tempest/api/network/admin/test_l3_agent_scheduler.py b/tempest/api/network/admin/test_l3_agent_scheduler.py
index 5b04cbb..9c187fd 100644
--- a/tempest/api/network/admin/test_l3_agent_scheduler.py
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -15,7 +15,7 @@
 #    under the License.
 
 from tempest.api.network import base
-from tempest.common.utils.data_utils import rand_name
+from tempest.common.utils import data_utils
 from tempest.test import attr
 
 
@@ -50,7 +50,7 @@
 
     @attr(type='smoke')
     def test_list_l3_agents_hosting_router(self):
-        name = rand_name('router-')
+        name = data_utils.rand_name('router-')
         resp, router = self.client.create_router(name)
         self.assertEqual('201', resp['status'])
         resp, body = self.admin_client.list_l3_agents_hosting_router(
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
new file mode 100644
index 0000000..5e838e5
--- /dev/null
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -0,0 +1,110 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Huawei Technologies Co.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.volume.base import BaseVolumeAdminTest
+from tempest.common.utils import data_utils
+from tempest.test import attr
+
+
+class SnapshotsActionsTest(BaseVolumeAdminTest):
+    _interface = "json"
+
+    @classmethod
+    def setUpClass(cls):
+        super(SnapshotsActionsTest, cls).setUpClass()
+        cls.client = cls.snapshots_client
+
+        # Create admin volume client
+        cls.admin_snapshots_client = cls.os_adm.snapshots_client
+
+        # Create a test shared volume for tests
+        vol_name = data_utils.rand_name(cls.__name__ + '-Volume-')
+        resp_vol, cls.volume = \
+            cls.volumes_client.create_volume(size=1, display_name=vol_name)
+        cls.volumes_client.wait_for_volume_status(cls.volume['id'],
+                                                  'available')
+
+        # Create a test shared snapshot for tests
+        snap_name = data_utils.rand_name(cls.__name__ + '-Snapshot-')
+        resp_snap, cls.snapshot = \
+            cls.client.create_snapshot(cls.volume['id'],
+                                       display_name=snap_name)
+        cls.client.wait_for_snapshot_status(cls.snapshot['id'],
+                                            'available')
+
+    @classmethod
+    def tearDownClass(cls):
+        # Delete the test snapshot
+        cls.client.delete_snapshot(cls.snapshot['id'])
+        cls.client.wait_for_resource_deletion(cls.snapshot['id'])
+
+        # Delete the test volume
+        cls.volumes_client.delete_volume(cls.volume['id'])
+        cls.volumes_client.wait_for_resource_deletion(cls.volume['id'])
+
+        super(SnapshotsActionsTest, cls).tearDownClass()
+
+    def tearDown(self):
+        # Set snapshot's status to available after test
+        status = 'available'
+        snapshot_id = self.snapshot['id']
+        self.admin_snapshots_client.reset_snapshot_status(snapshot_id,
+                                                          status)
+        super(SnapshotsActionsTest, self).tearDown()
+
+    def _get_progress_alias(self):
+        return 'os-extended-snapshot-attributes:progress'
+
+    @attr(type='gate')
+    def test_reset_snapshot_status(self):
+        # Reset snapshot status to creating
+        status = 'creating'
+        resp, body = self.admin_snapshots_client.\
+            reset_snapshot_status(self.snapshot['id'], status)
+        self.assertEqual(202, resp.status)
+        resp_get, snapshot_get \
+            = self.admin_snapshots_client.get_snapshot(self.snapshot['id'])
+        self.assertEqual(200, resp_get.status)
+        self.assertEqual(status, snapshot_get['status'])
+
+    @attr(type='gate')
+    def test_update_snapshot_status(self):
+        # Reset snapshot status to creating
+        status = 'creating'
+        self.admin_snapshots_client.\
+            reset_snapshot_status(self.snapshot['id'], status)
+
+        # Update snapshot status to error
+        progress = '80%'
+        status = 'error'
+        progress_alias = self._get_progress_alias()
+        resp, body = self.client.update_snapshot_status(self.snapshot['id'],
+                                                        status, progress)
+        self.assertEqual(202, resp.status)
+        resp_get, snapshot_get \
+            = self.admin_snapshots_client.get_snapshot(self.snapshot['id'])
+        self.assertEqual(200, resp_get.status)
+        self.assertEqual(status, snapshot_get['status'])
+        self.assertEqual(progress, snapshot_get[progress_alias])
+
+
+class SnapshotsActionsTestXML(SnapshotsActionsTest):
+    _interface = "xml"
+
+    def _get_progress_alias(self):
+        return '{http://docs.openstack.org/volume/ext' \
+               '/extended_snapshot_attributes/api/v1}progress'
diff --git a/tempest/clients.py b/tempest/clients.py
index 9029d5f..b2399c7 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -50,12 +50,16 @@
     TenantUsagesClientJSON
 from tempest.services.compute.json.volumes_extensions_client import \
     VolumesExtensionsClientJSON
+from tempest.services.compute.v3.json.availability_zone_client import \
+    AvailabilityZoneV3ClientJSON
 from tempest.services.compute.v3.json.extensions_client import \
     ExtensionsV3ClientJSON
 from tempest.services.compute.v3.json.servers_client import \
     ServersV3ClientJSON
 from tempest.services.compute.v3.json.services_client import \
     ServicesV3ClientJSON
+from tempest.services.compute.v3.xml.availability_zone_client import \
+    AvailabilityZoneV3ClientXML
 from tempest.services.compute.v3.xml.extensions_client import \
     ExtensionsV3ClientXML
 from tempest.services.compute.v3.xml.servers_client import ServersV3ClientXML
@@ -209,6 +213,8 @@
             self.interfaces_client = InterfacesClientXML(*client_args)
             self.endpoints_client = EndPointClientXML(*client_args)
             self.fixed_ips_client = FixedIPsClientXML(*client_args)
+            self.availability_zone_v3_client = AvailabilityZoneV3ClientXML(
+                *client_args)
             self.availability_zone_client = AvailabilityZoneClientXML(
                 *client_args)
             self.services_v3_client = ServicesV3ClientXML(*client_args)
@@ -253,6 +259,8 @@
             self.interfaces_client = InterfacesClientJSON(*client_args)
             self.endpoints_client = EndPointClientJSON(*client_args)
             self.fixed_ips_client = FixedIPsClientJSON(*client_args)
+            self.availability_zone_v3_client = AvailabilityZoneV3ClientJSON(
+                *client_args)
             self.availability_zone_client = AvailabilityZoneClientJSON(
                 *client_args)
             self.services_v3_client = ServicesV3ClientJSON(*client_args)
diff --git a/tempest/common/isolated_creds.py b/tempest/common/isolated_creds.py
index 9459590..5dbb3a7 100644
--- a/tempest/common/isolated_creds.py
+++ b/tempest/common/isolated_creds.py
@@ -412,7 +412,11 @@
                 resp_body = self.network_admin_client.list_ports()
             self.ports = resp_body['ports']
         ports_to_delete = [
-            port for port in self.ports if port['network_id'] == network_id]
+            port
+            for port in self.ports
+            if (port['network_id'] == network_id and
+                port['device_owner'] != 'network:router_interface')
+        ]
         for port in ports_to_delete:
             try:
                 LOG.info('Cleaning up port id %s, name %s' %
diff --git a/tempest/services/compute/json/flavors_client.py b/tempest/services/compute/json/flavors_client.py
index 588e5cd..00d6f8a 100644
--- a/tempest/services/compute/json/flavors_client.py
+++ b/tempest/services/compute/json/flavors_client.py
@@ -103,14 +103,14 @@
         return resp, body['extra_specs']
 
     def get_flavor_extra_spec_with_key(self, flavor_id, key):
-        """Gets a specified key value for the mentioned flavor."""
+        """Gets extra Specs key-value of the mentioned flavor and key."""
         resp, body = self.get('flavors/%s/os-extra_specs/%s' % (str(flavor_id),
                               key))
         body = json.loads(body)
-        return resp, body[key]
+        return resp, body
 
     def update_flavor_extra_spec(self, flavor_id, key, **kwargs):
-        """Gets specified extra Specs details of the mentioned flavor."""
+        """Update specified extra Specs of the mentioned flavor and key."""
         resp, body = self.put('flavors/%s/os-extra_specs/%s' %
                               (flavor_id, key),
                               json.dumps(kwargs), self.headers)
diff --git a/tempest/services/compute/v3/json/availability_zone_client.py b/tempest/services/compute/v3/json/availability_zone_client.py
index b11871b..9a3fe8b 100644
--- a/tempest/services/compute/v3/json/availability_zone_client.py
+++ b/tempest/services/compute/v3/json/availability_zone_client.py
@@ -20,20 +20,20 @@
 from tempest.common.rest_client import RestClient
 
 
-class AvailabilityZoneClientJSON(RestClient):
+class AvailabilityZoneV3ClientJSON(RestClient):
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(AvailabilityZoneClientJSON, self).__init__(config, username,
-                                                         password, auth_url,
-                                                         tenant_name)
-        self.service = self.config.compute.catalog_type
+        super(AvailabilityZoneV3ClientJSON, self).__init__(config, username,
+                                                           password, auth_url,
+                                                           tenant_name)
+        self.service = self.config.compute.catalog_v3_type
 
     def get_availability_zone_list(self):
         resp, body = self.get('os-availability-zone')
         body = json.loads(body)
-        return resp, body['availabilityZoneInfo']
+        return resp, body['availability_zone_info']
 
     def get_availability_zone_list_detail(self):
         resp, body = self.get('os-availability-zone/detail')
         body = json.loads(body)
-        return resp, body['availabilityZoneInfo']
+        return resp, body['availability_zone_info']
diff --git a/tempest/services/compute/v3/xml/availability_zone_client.py b/tempest/services/compute/v3/xml/availability_zone_client.py
index ae93774..35fb2b1 100644
--- a/tempest/services/compute/v3/xml/availability_zone_client.py
+++ b/tempest/services/compute/v3/xml/availability_zone_client.py
@@ -21,13 +21,13 @@
 from tempest.services.compute.xml.common import xml_to_json
 
 
-class AvailabilityZoneClientXML(RestClientXML):
+class AvailabilityZoneV3ClientXML(RestClientXML):
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(AvailabilityZoneClientXML, self).__init__(config, username,
-                                                        password, auth_url,
-                                                        tenant_name)
-        self.service = self.config.compute.catalog_type
+        super(AvailabilityZoneV3ClientXML, self).__init__(config, username,
+                                                          password, auth_url,
+                                                          tenant_name)
+        self.service = self.config.compute.catalog_v3_type
 
     def _parse_array(self, node):
         return [xml_to_json(x) for x in node]
diff --git a/tempest/services/compute/xml/flavors_client.py b/tempest/services/compute/xml/flavors_client.py
index d4c456e..363c1a8 100644
--- a/tempest/services/compute/xml/flavors_client.py
+++ b/tempest/services/compute/xml/flavors_client.py
@@ -154,14 +154,17 @@
         return resp, body
 
     def get_flavor_extra_spec_with_key(self, flavor_id, key):
-        """Gets a specified key detail for the mentioned flavor."""
-        resp, body = self.get('flavors/%s/os-extra_specs/%s' % (str(flavor_id),
-                              key), self.headers)
-        body = xml_to_json(etree.fromstring(body))
+        """Gets extra Specs key-value of the mentioned flavor and key."""
+        resp, xml_body = self.get('flavors/%s/os-extra_specs/%s' %
+                                  (str(flavor_id), key), self.headers)
+        body = {}
+        element = etree.fromstring(xml_body)
+        key = element.get('key')
+        body[key] = xml_to_json(element)
         return resp, body
 
     def update_flavor_extra_spec(self, flavor_id, key, **kwargs):
-        """Gets specified extra Specs details of the mentioned flavor."""
+        """Update extra Specs details of the mentioned flavor and key."""
         doc = Document()
         for (k, v) in kwargs.items():
             element = Element(k)
diff --git a/tempest/services/volume/json/snapshots_client.py b/tempest/services/volume/json/snapshots_client.py
index 10ba3fd..5d980eb 100644
--- a/tempest/services/volume/json/snapshots_client.py
+++ b/tempest/services/volume/json/snapshots_client.py
@@ -131,3 +131,21 @@
         except exceptions.NotFound:
             return True
         return False
+
+    def reset_snapshot_status(self, snapshot_id, status):
+        """Reset the specified snapshot's status."""
+        post_body = json.dumps({'os-reset_status': {"status": status}})
+        resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body,
+                               self.headers)
+        return resp, body
+
+    def update_snapshot_status(self, snapshot_id, status, progress):
+        """Update the specified snapshot's status."""
+        post_body = {
+            'status': status,
+            'progress': progress
+        }
+        post_body = json.dumps({'os-update_snapshot_status': post_body})
+        url = 'snapshots/%s/action' % str(snapshot_id)
+        resp, body = self.post(url, post_body, self.headers)
+        return resp, body
diff --git a/tempest/services/volume/xml/snapshots_client.py b/tempest/services/volume/xml/snapshots_client.py
index b7ba56b..5d59b07 100644
--- a/tempest/services/volume/xml/snapshots_client.py
+++ b/tempest/services/volume/xml/snapshots_client.py
@@ -147,3 +147,26 @@
         except exceptions.NotFound:
             return True
         return False
+
+    def reset_snapshot_status(self, snapshot_id, status):
+        """Reset the specified snapshot's status."""
+        post_body = Element("os-reset_status",
+                            status=status
+                            )
+        url = 'snapshots/%s/action' % str(snapshot_id)
+        resp, body = self.post(url, str(Document(post_body)), self.headers)
+        if body:
+            body = xml_to_json(etree.fromstring(body))
+        return resp, body
+
+    def update_snapshot_status(self, snapshot_id, status, progress):
+        """Update the specified snapshot's status."""
+        post_body = Element("os-update_snapshot_status",
+                            status=status,
+                            progress=progress
+                            )
+        url = 'snapshots/%s/action' % str(snapshot_id)
+        resp, body = self.post(url, str(Document(post_body)), self.headers)
+        if body:
+            body = xml_to_json(etree.fromstring(body))
+        return resp, body