Enhance test_server_actions_rbac with create image actions.

Add RBAC test cases for the following server actions:
os_compute_api:servers:create_image
os_compute_api:servers:create_image:allow_volume_backed

Change-Id: Ic2926610b570439c26e8d48073aae93a01f974b8
Partially-Implements: blueprint blueprint initial-tests-nova
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_actions_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_actions_rbac.py
index 0e1b00b..d466ded 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_actions_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_actions_rbac.py
@@ -18,6 +18,8 @@
 
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 from tempest import test
@@ -36,17 +38,34 @@
     def setup_clients(cls):
         super(ServerActionsRbacTest, cls).setup_clients()
         cls.client = cls.servers_client
+        cls.snapshots_client = cls.snapshots_extensions_client
 
     @classmethod
     def resource_setup(cls):
         cls.set_validation_resources()
         super(ServerActionsRbacTest, cls).resource_setup()
+        # Create test server
         cls.server_id = cls.create_test_server(wait_until='ACTIVE',
                                                validatable=True)['id']
         cls.flavor_ref = CONF.compute.flavor_ref
         cls.flavor_ref_alt = CONF.compute.flavor_ref_alt
         cls.image_ref = CONF.compute.image_ref
 
+        # Create a volume
+        volume_name = data_utils.rand_name(cls.__name__ + '-volume')
+        name_field = 'name'
+        if not CONF.volume_feature_enabled.api_v2:
+            name_field = 'display_name'
+
+        params = {name_field: volume_name,
+                  'imageRef': CONF.compute.image_ref,
+                  'size': CONF.volume.volume_size}
+        volume = cls.volumes_client.create_volume(**params)['volume']
+        waiters.wait_for_volume_resource_status(cls.volumes_client,
+                                                volume['id'], 'available')
+        cls.volumes.append(volume)
+        cls.volume_id = volume['id']
+
     def setUp(self):
         super(ServerActionsRbacTest, self).setUp()
         try:
@@ -64,6 +83,56 @@
             self.__class__.server_id = self.rebuild_server(
                 self.server_id, validatable=True)
 
+    @classmethod
+    def resource_cleanup(cls):
+        # If a test case creates an image from a server that is created with
+        # a volume, a volume snapshot will automatically be created by default.
+        # We need to delete the volume snapshot.
+        try:
+            body = cls.snapshots_extensions_client.list_snapshots()
+            volume_snapshots = body['snapshots']
+        except Exception:
+            LOG.info("Cannot retrieve snapshots for cleanup.")
+        else:
+            for snapshot in volume_snapshots:
+                if snapshot['volumeId'] == cls.volume_id:
+                    # Wait for snapshot status to become 'available' before
+                    # deletion
+                    waiters.wait_for_volume_resource_status(
+                        cls.snapshots_client, snapshot['id'], 'available')
+                    test_utils.call_and_ignore_notfound_exc(
+                        cls.snapshots_client.delete_snapshot, snapshot['id'])
+
+            for snapshot in volume_snapshots:
+                if snapshot['volumeId'] == cls.volume_id:
+                    test_utils.call_and_ignore_notfound_exc(
+                        cls.snapshots_client.wait_for_resource_deletion,
+                        snapshot['id'])
+
+        super(ServerActionsRbacTest, cls).resource_cleanup()
+
+    def _create_test_server_with_volume(self, volume_id):
+        # Create a server with the volume created earlier
+        server_name = data_utils.rand_name(self.__class__.__name__ + "-server")
+        bd_map_v2 = [{'uuid': volume_id,
+                      'source_type': 'volume',
+                      'destination_type': 'volume',
+                      'boot_index': 0,
+                      'delete_on_termination': True}]
+        device_mapping = {'block_device_mapping_v2': bd_map_v2}
+
+        # Since the server is booted from volume, the imageRef does not need
+        # to be specified.
+        server = self.client.create_server(name=server_name,
+                                           imageRef='',
+                                           flavorRef=CONF.compute.flavor_ref,
+                                           **device_mapping)['server']
+
+        waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
+
+        self.servers.append(server)
+        return server
+
     def _test_start_server(self):
         self.client.start_server(self.server_id)
         waiters.wait_for_server_status(self.client, self.server_id,
@@ -206,6 +275,29 @@
         self.rbac_utils.switch_role(self, switchToRbacRole=True)
         self.client.show_server(self.server_id)
 
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:servers:create_image")
+    @decorators.idempotent_id('ba0ac859-99f4-4055-b5e0-e0905a44d331')
+    def test_create_image(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+
+        # This function will also call show image
+        self.create_image_from_server(self.server_id,
+                                      wait_until='ACTIVE')
+
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:servers:create_image:allow_volume_backed")
+    @decorators.idempotent_id('8b869f73-49b3-4cc4-a0ce-ef64f8e1d6f9')
+    def test_create_image_volume_backed(self):
+        server = self._create_test_server_with_volume(self.volume_id)
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+
+        # This function will also call show image
+        self.create_image_from_server(server['id'],
+                                      wait_until='ACTIVE')
+
 
 class ServerActionsV216RbacTest(rbac_base.BaseV2ComputeRbacTest):