Merge "Add support for image deactivate and reactivate"
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/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/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/services/image/v2/json/image_client.py b/tempest/services/image/v2/json/image_client.py
index cec81fb..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)