Merge "Apply a naming rule of GET to compute clients(f*)"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 56172f8..6424f55 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -648,6 +648,10 @@
 # Is the v1 image API enabled (boolean value)
 #api_v1 = true
 
+# Is the deactivate-image feature enabled. The feature has been
+# integrated since Kilo. (boolean value)
+#deactivate_image = false
+
 
 [input-scenario]
 
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index d3b1f5e..a03439a 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -78,7 +78,7 @@
             return server_id
 
     def _volume_clean_up(self, server_id, volume_id):
-        body = self.volumes_client.get_volume(volume_id)
+        body = self.volumes_client.show_volume(volume_id)
         if body['status'] == 'in-use':
             self.servers_client.detach_volume(server_id, volume_id)
             self.volumes_client.wait_for_volume_status(volume_id, 'available')
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index c2ed0ce..10b08a1 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -42,6 +42,10 @@
         # Check if the server is in a clean state after test
         try:
             self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+        except lib_exc.NotFound:
+            # The server was deleted by previous test, create a new one
+            server = self.create_test_server(wait_until='ACTIVE')
+            self.__class__.server_id = server['id']
         except Exception:
             # Rebuild server if something happened to it during a test
             self.__class__.server_id = self.rebuild_server(self.server_id)
@@ -287,8 +291,9 @@
             'backup_type': "daily",
             'instance_uuid': self.server_id,
         }
-        image_list = self.os.image_client.image_list_detail(
-            properties,
+        image_list = self.os.image_client.list_images(
+            detail=True,
+            properties=properties,
             status='active',
             sort_key='created_at',
             sort_dir='asc')
@@ -310,8 +315,9 @@
         self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
         self.os.image_client.wait_for_resource_deletion(image1_id)
         oldest_backup_exist = False
-        image_list = self.os.image_client.image_list_detail(
-            properties,
+        image_list = self.os.image_client.list_images(
+            detail=True,
+            properties=properties,
             status='active',
             sort_key='created_at',
             sort_dir='asc')
diff --git a/tempest/api/identity/admin/v2/test_users.py b/tempest/api/identity/admin/v2/test_users.py
index bcac853..8eac80c 100644
--- a/tempest/api/identity/admin/v2/test_users.py
+++ b/tempest/api/identity/admin/v2/test_users.py
@@ -50,7 +50,7 @@
                                        self.alt_email, enabled=False)
         self.data.users.append(user)
         self.assertEqual(name, user['name'])
-        self.assertEqual('false', str(user['enabled']).lower())
+        self.assertEqual(False, user['enabled'])
         self.assertEqual(self.alt_email, user['email'])
 
     @test.idempotent_id('39d05857-e8a5-4ed4-ba83-0b52d3ab97ee')
@@ -71,13 +71,13 @@
                                               enabled=False)
         self.assertEqual(u_name2, update_user['name'])
         self.assertEqual(u_email2, update_user['email'])
-        self.assertEqual('false', str(update_user['enabled']).lower())
+        self.assertEqual(False, update_user['enabled'])
         # GET by id after updating
         updated_user = self.client.get_user(user['id'])
         # Assert response body of GET after updating
         self.assertEqual(u_name2, updated_user['name'])
         self.assertEqual(u_email2, updated_user['email'])
-        self.assertEqual('false', str(updated_user['enabled']).lower())
+        self.assertEqual(False, update_user['enabled'])
 
     @test.idempotent_id('29ed26f4-a74e-4425-9a85-fdb49fa269d2')
     def test_delete_user(self):
diff --git a/tempest/api/identity/admin/v3/test_domains.py b/tempest/api/identity/admin/v3/test_domains.py
index b775e91..b1a9d3b 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -79,12 +79,12 @@
         self.assertIsNotNone(updated_domain['id'])
         self.assertEqual(new_name, updated_domain['name'])
         self.assertEqual(new_desc, updated_domain['description'])
-        self.assertEqual('true', str(updated_domain['enabled']).lower())
+        self.assertEqual(True, updated_domain['enabled'])
 
         fetched_domain = self.client.get_domain(domain['id'])
         self.assertEqual(new_name, fetched_domain['name'])
         self.assertEqual(new_desc, fetched_domain['description'])
-        self.assertEqual('true', str(fetched_domain['enabled']).lower())
+        self.assertEqual(True, fetched_domain['enabled'])
 
     @test.idempotent_id('036df86e-bb5d-42c0-a7c2-66b9db3a6046')
     def test_create_domain_with_disabled_status(self):
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index 1672d47..9ca10a4 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -126,4 +126,4 @@
         self.assertEqual(interface2, endpoint['interface'])
         self.assertEqual(url2, endpoint['url'])
         self.assertEqual(region2, endpoint['region'])
-        self.assertEqual('false', str(endpoint['enabled']).lower())
+        self.assertEqual(False, endpoint['enabled'])
diff --git a/tempest/api/identity/admin/v3/test_users.py b/tempest/api/identity/admin/v3/test_users.py
index 7fe369e..c2456c4 100644
--- a/tempest/api/identity/admin/v3/test_users.py
+++ b/tempest/api/identity/admin/v3/test_users.py
@@ -53,7 +53,7 @@
         self.assertEqual(project['id'],
                          update_user['project_id'])
         self.assertEqual(u_email2, update_user['email'])
-        self.assertEqual('false', str(update_user['enabled']).lower())
+        self.assertEqual(False, update_user['enabled'])
         # GET by id after updation
         new_user_get = self.client.get_user(user['id'])
         # Assert response body of GET after updation
@@ -62,7 +62,7 @@
         self.assertEqual(project['id'],
                          new_user_get['project_id'])
         self.assertEqual(u_email2, new_user_get['email'])
-        self.assertEqual('false', str(new_user_get['enabled']).lower())
+        self.assertEqual(False, new_user_get['enabled'])
 
     @test.idempotent_id('2d223a0e-e457-4a70-9fb1-febe027a0ff9')
     def test_update_user_password(self):
diff --git a/tempest/api/image/admin/__init__.py b/tempest/api/image/admin/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/image/admin/__init__.py
diff --git a/tempest/api/image/admin/v2/__init__.py b/tempest/api/image/admin/v2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/image/admin/v2/__init__.py
diff --git a/tempest/api/image/admin/v2/test_images.py b/tempest/api/image/admin/v2/test_images.py
new file mode 100644
index 0000000..83efc7d
--- /dev/null
+++ b/tempest/api/image/admin/v2/test_images.py
@@ -0,0 +1,55 @@
+# Copyright 2015 Red Hat, Inc.
+# 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 six import moves
+from tempest_lib.common.utils import data_utils
+import testtools
+
+from tempest.api.image import base
+from tempest import config
+from tempest import test
+
+
+CONF = config.CONF
+
+
+class BasicAdminOperationsImagesTest(base.BaseV2ImageAdminTest):
+
+    """
+    Here we test admin operations of images
+    """
+    @testtools.skipUnless(CONF.image_feature_enabled.deactivate_image,
+                          'deactivate-image is not available.')
+    @test.idempotent_id('951ebe01-969f-4ea9-9898-8a3f1f442ab0')
+    def test_admin_deactivate_reactivate_image(self):
+        # Create image by non-admin tenant
+        image_name = data_utils.rand_name('image')
+        body = self.client.create_image(name=image_name,
+                                        container_format='bare',
+                                        disk_format='raw',
+                                        visibility='private')
+        image_id = body['id']
+        self.addCleanup(self.client.delete_image, image_id)
+        # upload an image file
+        image_file = moves.cStringIO(data_utils.random_bytes())
+        self.client.store_image(image_id, image_file)
+        # deactivate image
+        self.admin_client.deactivate_image(image_id)
+        body = self.client.show_image(image_id)
+        self.assertEqual("deactivated", body['status'])
+        # reactivate image
+        self.admin_client.reactivate_image(image_id)
+        body = self.client.show_image(image_id)
+        self.assertEqual("active", body['status'])
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 00959d9..dc38cab 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -158,3 +158,23 @@
         image_id = image['id']
         self.addCleanup(self.os_img_client.delete_image, image_id)
         return image_id
+
+
+class BaseV1ImageAdminTest(BaseImageTest):
+    credentials = ['admin', 'primary']
+
+    @classmethod
+    def setup_clients(cls):
+        super(BaseV1ImageAdminTest, cls).setup_clients()
+        cls.client = cls.os.image_client
+        cls.admin_client = cls.os_adm.image_client
+
+
+class BaseV2ImageAdminTest(BaseImageTest):
+    credentials = ['admin', 'primary']
+
+    @classmethod
+    def setup_clients(cls):
+        super(BaseV2ImageAdminTest, cls).setup_clients()
+        cls.client = cls.os.image_client_v2
+        cls.admin_client = cls.os_adm.image_client_v2
diff --git a/tempest/api/image/v1/test_image_members.py b/tempest/api/image/v1/test_image_members.py
index 4969858..eb6969b 100644
--- a/tempest/api/image/v1/test_image_members.py
+++ b/tempest/api/image/v1/test_image_members.py
@@ -36,7 +36,7 @@
         self.client.add_member(self.alt_tenant_id, image)
         share_image = self._create_image()
         self.client.add_member(self.alt_tenant_id, share_image)
-        body = self.client.get_shared_images(self.alt_tenant_id)
+        body = self.client.list_shared_images(self.alt_tenant_id)
         images = body['shared_images']
         images = map(lambda x: x['image_id'], images)
         self.assertIn(share_image, images)
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index 6c25c93..3f71fcb 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -211,9 +211,10 @@
 
     @test.idempotent_id('e5dc26d9-9aa2-48dd-bda5-748e1445da98')
     def test_index_status_active_detail(self):
-        images_list = self.client.image_list_detail(status='active',
-                                                    sort_key='size',
-                                                    sort_dir='desc')
+        images_list = self.client.list_images(detail=True,
+                                              status='active',
+                                              sort_key='size',
+                                              sort_dir='desc')
         top_size = images_list[0]['size']  # We have non-zero sized images
         for image in images_list:
             size = image['size']
@@ -223,7 +224,8 @@
 
     @test.idempotent_id('097af10a-bae8-4342-bff4-edf89969ed2a')
     def test_index_name(self):
-        images_list = self.client.image_list_detail(
+        images_list = self.client.list_images(
+            detail=True,
             name='New Remote Image dup')
         result_set = set(map(lambda x: x['id'], images_list))
         for image in images_list:
diff --git a/tempest/api/image/v2/test_images_member.py b/tempest/api/image/v2/test_images_member.py
index 07c2073..eb6ffeb 100644
--- a/tempest/api/image/v2/test_images_member.py
+++ b/tempest/api/image/v2/test_images_member.py
@@ -19,15 +19,15 @@
     @test.idempotent_id('5934c6ea-27dc-4d6e-9421-eeb5e045494a')
     def test_image_share_accept(self):
         image_id = self._create_image()
-        member = self.os_img_client.add_member(image_id,
-                                               self.alt_tenant_id)
+        member = self.os_img_client.add_image_member(image_id,
+                                                     self.alt_tenant_id)
         self.assertEqual(member['member_id'], self.alt_tenant_id)
         self.assertEqual(member['image_id'], image_id)
         self.assertEqual(member['status'], 'pending')
         self.assertNotIn(image_id, self._list_image_ids_as_alt())
-        self.alt_img_client.update_member_status(image_id,
-                                                 self.alt_tenant_id,
-                                                 'accepted')
+        self.alt_img_client.update_image_member(image_id,
+                                                self.alt_tenant_id,
+                                                {'status': 'accepted'})
         self.assertIn(image_id, self._list_image_ids_as_alt())
         body = self.os_img_client.list_image_members(image_id)
         members = body['members']
@@ -40,29 +40,29 @@
     @test.idempotent_id('d9e83e5f-3524-4b38-a900-22abcb26e90e')
     def test_image_share_reject(self):
         image_id = self._create_image()
-        member = self.os_img_client.add_member(image_id,
-                                               self.alt_tenant_id)
+        member = self.os_img_client.add_image_member(image_id,
+                                                     self.alt_tenant_id)
         self.assertEqual(member['member_id'], self.alt_tenant_id)
         self.assertEqual(member['image_id'], image_id)
         self.assertEqual(member['status'], 'pending')
         self.assertNotIn(image_id, self._list_image_ids_as_alt())
-        self.alt_img_client.update_member_status(image_id,
-                                                 self.alt_tenant_id,
-                                                 'rejected')
+        self.alt_img_client.update_image_member(image_id,
+                                                self.alt_tenant_id,
+                                                {'status': 'rejected'})
         self.assertNotIn(image_id, self._list_image_ids_as_alt())
 
     @test.idempotent_id('a6ee18b9-4378-465e-9ad9-9a6de58a3287')
     def test_get_image_member(self):
         image_id = self._create_image()
-        self.os_img_client.add_member(image_id,
-                                      self.alt_tenant_id)
-        self.alt_img_client.update_member_status(image_id,
-                                                 self.alt_tenant_id,
-                                                 'accepted')
+        self.os_img_client.add_image_member(image_id,
+                                            self.alt_tenant_id)
+        self.alt_img_client.update_image_member(image_id,
+                                                self.alt_tenant_id,
+                                                {'status': 'accepted'})
 
         self.assertIn(image_id, self._list_image_ids_as_alt())
-        member = self.os_img_client.get_member(image_id,
-                                               self.alt_tenant_id)
+        member = self.os_img_client.show_image_member(image_id,
+                                                      self.alt_tenant_id)
         self.assertEqual(self.alt_tenant_id, member['member_id'])
         self.assertEqual(image_id, member['image_id'])
         self.assertEqual('accepted', member['status'])
@@ -70,14 +70,14 @@
     @test.idempotent_id('72989bc7-2268-48ed-af22-8821e835c914')
     def test_remove_image_member(self):
         image_id = self._create_image()
-        self.os_img_client.add_member(image_id,
-                                      self.alt_tenant_id)
-        self.alt_img_client.update_member_status(image_id,
-                                                 self.alt_tenant_id,
-                                                 'accepted')
+        self.os_img_client.add_image_member(image_id,
+                                            self.alt_tenant_id)
+        self.alt_img_client.update_image_member(image_id,
+                                                self.alt_tenant_id,
+                                                {'status': 'accepted'})
 
         self.assertIn(image_id, self._list_image_ids_as_alt())
-        self.os_img_client.remove_member(image_id, self.alt_tenant_id)
+        self.os_img_client.remove_image_member(image_id, self.alt_tenant_id)
         self.assertNotIn(image_id, self._list_image_ids_as_alt())
 
     @test.idempotent_id('634dcc3f-f6e2-4409-b8fd-354a0bb25d83')
diff --git a/tempest/api/image/v2/test_images_member_negative.py b/tempest/api/image/v2/test_images_member_negative.py
index d95e5c1..ae8913c 100644
--- a/tempest/api/image/v2/test_images_member_negative.py
+++ b/tempest/api/image/v2/test_images_member_negative.py
@@ -22,22 +22,23 @@
     @test.idempotent_id('b79efb37-820d-4cf0-b54c-308b00cf842c')
     def test_image_share_invalid_status(self):
         image_id = self._create_image()
-        member = self.os_img_client.add_member(image_id,
-                                               self.alt_tenant_id)
+        member = self.os_img_client.add_image_member(image_id,
+                                                     self.alt_tenant_id)
         self.assertEqual(member['status'], 'pending')
         self.assertRaises(lib_exc.BadRequest,
-                          self.alt_img_client.update_member_status,
-                          image_id, self.alt_tenant_id, 'notavalidstatus')
+                          self.alt_img_client.update_image_member,
+                          image_id, self.alt_tenant_id,
+                          {'status': 'notavalidstatus'})
 
     @test.attr(type=['negative'])
     @test.idempotent_id('27002f74-109e-4a37-acd0-f91cd4597967')
     def test_image_share_owner_cannot_accept(self):
         image_id = self._create_image()
-        member = self.os_img_client.add_member(image_id,
-                                               self.alt_tenant_id)
+        member = self.os_img_client.add_image_member(image_id,
+                                                     self.alt_tenant_id)
         self.assertEqual(member['status'], 'pending')
         self.assertNotIn(image_id, self._list_image_ids_as_alt())
         self.assertRaises(lib_exc.Forbidden,
-                          self.os_img_client.update_member_status,
-                          image_id, self.alt_tenant_id, 'accepted')
+                          self.os_img_client.update_image_member,
+                          image_id, self.alt_tenant_id, {'status': 'accepted'})
         self.assertNotIn(image_id, self._list_image_ids_as_alt())
diff --git a/tempest/api/object_storage/test_object_slo.py b/tempest/api/object_storage/test_object_slo.py
index 7df0dde..6fc7821 100644
--- a/tempest/api/object_storage/test_object_slo.py
+++ b/tempest/api/object_storage/test_object_slo.py
@@ -110,6 +110,7 @@
         self.assertHeaders(resp, 'Object', method)
 
     @test.idempotent_id('2c3f24a6-36e8-4711-9aa2-800ee1fc7b5b')
+    @test.requires_ext(extension='slo', service='object')
     def test_upload_manifest(self):
         # create static large object from multipart manifest
         manifest = self._create_manifest()
@@ -124,6 +125,7 @@
         self._assertHeadersSLO(resp, 'PUT')
 
     @test.idempotent_id('e69ad766-e1aa-44a2-bdd2-bf62c09c1456')
+    @test.requires_ext(extension='slo', service='object')
     def test_list_large_object_metadata(self):
         # list static large object metadata using multipart manifest
         object_name = self._create_large_object()
@@ -135,6 +137,7 @@
         self._assertHeadersSLO(resp, 'HEAD')
 
     @test.idempotent_id('49bc49bc-dd1b-4c0f-904e-d9f10b830ee8')
+    @test.requires_ext(extension='slo', service='object')
     def test_retrieve_large_object(self):
         # list static large object using multipart manifest
         object_name = self._create_large_object()
@@ -149,6 +152,7 @@
         self.assertEqual(body, sum_data)
 
     @test.idempotent_id('87b6dfa1-abe9-404d-8bf0-6c3751e6aa77')
+    @test.requires_ext(extension='slo', service='object')
     def test_delete_large_object(self):
         # delete static large object using multipart manifest
         object_name = self._create_large_object()
diff --git a/tempest/config.py b/tempest/config.py
index bbd6772..0fa5bf5 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -433,6 +433,10 @@
     cfg.BoolOpt('api_v1',
                 default=True,
                 help="Is the v1 image API enabled"),
+    cfg.BoolOpt('deactivate_image',
+                default=False,
+                help="Is the deactivate-image feature enabled."
+                     " The feature has been integrated since Kilo."),
 ]
 
 network_group = cfg.OptGroup(name='network',
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index 6e67c4b..fba839a 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -13,7 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 import functools
-import netaddr
 
 from oslo_log import log as logging
 import six
@@ -28,12 +27,13 @@
 
 
 class TestGettingAddress(manager.NetworkScenarioTest):
-    """Create network with 2 subnets: IPv4 and IPv6 in a given address mode
+    """Create network with subnets: one IPv4 and
+    one or few IPv6 in a given address mode
     Boot 2 VMs on this network
     Allocate and assign 2 FIP4
-    Check that vNIC of server matches port data from OpenStack DB
-    Ping4 tenant IPv4 of one VM from another one
-    Will do the same with ping6 when available in VM
+    Check that vNICs of all VMs gets all addresses actually assigned
+    Ping4 to one VM from another one
+    If ping6 available in VM, do ping6 to all v6 addresses
     """
 
     @classmethod
@@ -65,41 +65,41 @@
             'key_name': self.keypair['name'],
             'security_groups': [{'name': self.sec_grp['name']}]}
 
-    def prepare_network(self, address6_mode):
+    def prepare_network(self, address6_mode, n_subnets6=1):
         """Creates network with
-         one IPv6 subnet in the given mode and
+         given number of IPv6 subnets in the given mode and
          one IPv4 subnet
-         Creates router with ports on both subnets
+         Creates router with ports on all subnets
         """
         self.network = self._create_network(tenant_id=self.tenant_id)
         sub4 = self._create_subnet(network=self.network,
                                    namestart='sub4',
                                    ip_version=4,)
-        # since https://bugs.launchpad.net/neutron/+bug/1394112 we need
-        # to specify gateway_ip manually
-        net_range = netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr)
-        gateway_ip = (netaddr.IPAddress(net_range) + 1).format()
-        sub6 = self._create_subnet(network=self.network,
-                                   namestart='sub6',
-                                   ip_version=6,
-                                   gateway_ip=gateway_ip,
-                                   ipv6_ra_mode=address6_mode,
-                                   ipv6_address_mode=address6_mode)
 
         router = self._get_router(tenant_id=self.tenant_id)
         sub4.add_to_router(router_id=router['id'])
-        sub6.add_to_router(router_id=router['id'])
         self.addCleanup(sub4.delete)
-        self.addCleanup(sub6.delete)
+
+        for _ in range(n_subnets6):
+            sub6 = self._create_subnet(network=self.network,
+                                       namestart='sub6',
+                                       ip_version=6,
+                                       ipv6_ra_mode=address6_mode,
+                                       ipv6_address_mode=address6_mode)
+
+            sub6.add_to_router(router_id=router['id'])
+            self.addCleanup(sub6.delete)
 
     @staticmethod
     def define_server_ips(srv):
+        ips = {'4': None, '6': []}
         for net_name, nics in six.iteritems(srv['addresses']):
             for nic in nics:
                 if nic['version'] == 6:
-                    srv['accessIPv6'] = nic['addr']
+                    ips['6'].append(nic['addr'])
                 else:
-                    srv['accessIPv4'] = nic['addr']
+                    ips['4'] = nic['addr']
+        return ips
 
     def prepare_server(self):
         username = CONF.compute.image_ssh_user
@@ -109,53 +109,56 @@
 
         srv = self.create_server(create_kwargs=create_kwargs)
         fip = self.create_floating_ip(thing=srv)
-        self.define_server_ips(srv=srv)
+        ips = self.define_server_ips(srv=srv)
         ssh = self.get_remote_client(
             server_or_ip=fip.floating_ip_address,
             username=username)
-        return ssh, srv
+        return ssh, ips
 
-    def _prepare_and_test(self, address6_mode):
-        self.prepare_network(address6_mode=address6_mode)
+    def _prepare_and_test(self, address6_mode, n_subnets6=1):
+        self.prepare_network(address6_mode=address6_mode,
+                             n_subnets6=n_subnets6)
 
-        ssh1, srv1 = self.prepare_server()
-        ssh2, srv2 = self.prepare_server()
+        sshv4_1, ips_from_api_1 = self.prepare_server()
+        sshv4_2, ips_from_api_2 = self.prepare_server()
 
         def guest_has_address(ssh, addr):
             return addr in ssh.get_ip_list()
 
-        srv1_v6_addr_assigned = functools.partial(
-            guest_has_address, ssh1, srv1['accessIPv6'])
-        srv2_v6_addr_assigned = functools.partial(
-            guest_has_address, ssh2, srv2['accessIPv6'])
+        # get addresses assigned to vNIC as reported by 'ip address' utility
+        ips_from_ip_1 = sshv4_1.get_ip_list()
+        ips_from_ip_2 = sshv4_2.get_ip_list()
+        self.assertIn(ips_from_api_1['4'], ips_from_ip_1)
+        self.assertIn(ips_from_api_2['4'], ips_from_ip_2)
+        for i in range(n_subnets6):
+            # v6 should be configured since the image supports it
+            # It can take time for ipv6 automatic address to get assigned
+            srv1_v6_addr_assigned = functools.partial(
+                guest_has_address, sshv4_1, ips_from_api_1['6'][i])
 
-        result = ssh1.get_ip_list()
-        self.assertIn(srv1['accessIPv4'], result)
-        # v6 should be configured since the image supports it
-        # It can take time for ipv6 automatic address to get assigned
-        self.assertTrue(
-            test.call_until_true(srv1_v6_addr_assigned,
-                                 CONF.compute.ping_timeout, 1))
-        result = ssh2.get_ip_list()
-        self.assertIn(srv2['accessIPv4'], result)
-        # v6 should be configured since the image supports it
-        # It can take time for ipv6 automatic address to get assigned
-        self.assertTrue(
-            test.call_until_true(srv2_v6_addr_assigned,
-                                 CONF.compute.ping_timeout, 1))
-        result = ssh1.ping_host(srv2['accessIPv4'])
+            srv2_v6_addr_assigned = functools.partial(
+                guest_has_address, sshv4_2, ips_from_api_2['6'][i])
+
+            self.assertTrue(test.call_until_true(srv1_v6_addr_assigned,
+                                                 CONF.compute.ping_timeout, 1))
+
+            self.assertTrue(test.call_until_true(srv2_v6_addr_assigned,
+                                                 CONF.compute.ping_timeout, 1))
+
+        result = sshv4_1.ping_host(ips_from_api_2['4'])
         self.assertIn('0% packet loss', result)
-        result = ssh2.ping_host(srv1['accessIPv4'])
+        result = sshv4_2.ping_host(ips_from_api_1['4'])
         self.assertIn('0% packet loss', result)
 
         # Some VM (like cirros) may not have ping6 utility
-        result = ssh1.exec_command('whereis ping6')
+        result = sshv4_1.exec_command('whereis ping6')
         is_ping6 = False if result == 'ping6:\n' else True
         if is_ping6:
-            result = ssh1.ping_host(srv2['accessIPv6'])
-            self.assertIn('0% packet loss', result)
-            result = ssh2.ping_host(srv1['accessIPv6'])
-            self.assertIn('0% packet loss', result)
+            for i in range(n_subnets6):
+                result = sshv4_1.ping_host(ips_from_api_2['6'][i])
+                self.assertIn('0% packet loss', result)
+                result = sshv4_2.ping_host(ips_from_api_1['6'][i])
+                self.assertIn('0% packet loss', result)
         else:
             LOG.warning('Ping6 is not available, skipping')
 
@@ -168,3 +171,13 @@
     @test.services('compute', 'network')
     def test_dhcp6_stateless_from_os(self):
         self._prepare_and_test(address6_mode='dhcpv6-stateless')
+
+    @test.idempotent_id('7ab23f41-833b-4a16-a7c9-5b42fe6d4123')
+    @test.services('compute', 'network')
+    def test_multi_prefix_dhcpv6_stateless(self):
+        self._prepare_and_test(address6_mode='dhcpv6-stateless', n_subnets6=2)
+
+    @test.idempotent_id('dec222b1-180c-4098-b8c5-cc1b8342d611')
+    @test.services('compute', 'network')
+    def test_multi_prefix_slaac(self):
+        self._prepare_and_test(address6_mode='slaac', n_subnets6=2)
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index 7c58f01..add26a9 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -201,20 +201,12 @@
         self.expected_success(200, resp.status)
         return service_client.ResponseBody(resp, body)
 
-    def list_images(self, **kwargs):
+    def list_images(self, detail=False, properties=dict(),
+                    changes_since=None, **kwargs):
         url = 'v1/images'
 
-        if len(kwargs) > 0:
-            url += '?%s' % urllib.urlencode(kwargs)
-
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBodyList(resp, body['images'])
-
-    def image_list_detail(self, properties=dict(), changes_since=None,
-                          **kwargs):
-        url = 'v1/images/detail'
+        if detail:
+            url += '/detail'
 
         params = {}
         for key, value in properties.items():
@@ -265,8 +257,9 @@
         body = json.loads(body)
         return service_client.ResponseBody(resp, body)
 
-    def get_shared_images(self, member_id):
-        url = 'v1/shared-images/%s' % member_id
+    def list_shared_images(self, tenant_id):
+        """List shared images with the specified tenant"""
+        url = 'v1/shared-images/%s' % tenant_id
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
diff --git a/tempest/services/image/v2/json/image_client.py b/tempest/services/image/v2/json/image_client.py
index a4cb48c..9e37f6e 100644
--- a/tempest/services/image/v2/json/image_client.py
+++ b/tempest/services/image/v2/json/image_client.py
@@ -96,6 +96,18 @@
         body = json.loads(body)
         return service_client.ResponseBody(resp, body)
 
+    def deactivate_image(self, image_id):
+        url = 'v2/images/%s/actions/deactivate' % image_id
+        resp, body = self.post(url, None)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def reactivate_image(self, image_id):
+        url = 'v2/images/%s/actions/reactivate' % image_id
+        resp, body = self.post(url, None)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
     def delete_image(self, image_id):
         url = 'v2/images/%s' % image_id
         resp, _ = self.delete(url)
@@ -166,7 +178,7 @@
         body = json.loads(body)
         return service_client.ResponseBody(resp, body)
 
-    def add_member(self, image_id, member_id):
+    def add_image_member(self, image_id, member_id):
         url = 'v2/images/%s/members' % image_id
         data = json.dumps({'member': member_id})
         resp, body = self.post(url, data)
@@ -174,22 +186,21 @@
         body = json.loads(body)
         return service_client.ResponseBody(resp, body)
 
-    def update_member_status(self, image_id, member_id, status):
-        """Valid status are: ``pending``, ``accepted``,  ``rejected``."""
+    def update_image_member(self, image_id, member_id, body):
         url = 'v2/images/%s/members/%s' % (image_id, member_id)
-        data = json.dumps({'status': status})
+        data = json.dumps(body)
         resp, body = self.put(url, data)
         self.expected_success(200, resp.status)
         body = json.loads(body)
         return service_client.ResponseBody(resp, body)
 
-    def get_member(self, image_id, member_id):
+    def show_image_member(self, image_id, member_id):
         url = 'v2/images/%s/members/%s' % (image_id, member_id)
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
         return service_client.ResponseBody(resp, json.loads(body))
 
-    def remove_member(self, image_id, member_id):
+    def remove_image_member(self, image_id, member_id):
         url = 'v2/images/%s/members/%s' % (image_id, member_id)
         resp, _ = self.delete(url)
         self.expected_success(204, resp.status)