Merge "Encryption Test Scenario for clone volume"
Zuul authored 5 years ago
Gerrit Code Review committed 5 years ago
| 0 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 1 | # not use this file except in compliance with the License. You may obtain | |
| 2 | # a copy of the License at | |
| 3 | # | |
| 4 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 5 | # | |
| 6 | # Unless required by applicable law or agreed to in writing, software | |
| 7 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 8 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 9 | # License for the specific language governing permissions and limitations | |
| 10 | # under the License. | |
| 11 | ||
| 12 | from tempest.common import utils | |
| 13 | from tempest.common import waiters | |
| 14 | from tempest import config | |
| 15 | from tempest.lib.common.utils import data_utils | |
| 16 | from tempest.lib import decorators | |
| 17 | from tempest.scenario import manager | |
| 18 | ||
| 19 | CONF = config.CONF | |
| 20 | ||
| 21 | ||
| 22 | class TestEncryptedCinderVolumes(manager.EncryptionScenarioTest, | |
| 23 | manager.ScenarioTest): | |
| 24 | ||
| 25 | @classmethod | |
| 26 | def skip_checks(cls): | |
| 27 | super(TestEncryptedCinderVolumes, cls).skip_checks() | |
| 28 | if not CONF.compute_feature_enabled.attach_encrypted_volume: | |
| 29 | raise cls.skipException('Encrypted volume attach is not supported') | |
| 30 | ||
| 31 | @classmethod | |
| 32 | def resource_setup(cls): | |
| 33 | super(TestEncryptedCinderVolumes, cls).resource_setup() | |
| 34 | ||
| 35 | @classmethod | |
| 36 | def resource_cleanup(cls): | |
| 37 | super(TestEncryptedCinderVolumes, cls).resource_cleanup() | |
| 38 | ||
| 39 | def launch_instance(self): | |
| 40 | image = self.image_create() | |
| 41 | keypair = self.create_keypair() | |
| 42 | ||
| 43 | return self.create_server(image_id=image, key_name=keypair['name']) | |
| 44 | ||
| 45 | def attach_detach_volume(self, server, volume): | |
| 46 | attached_volume = self.nova_volume_attach(server, volume) | |
| 47 | self.nova_volume_detach(server, attached_volume) | |
| 48 | ||
| 49 | def _delete_server(self, server): | |
| 50 | self.servers_client.delete_server(server['id']) | |
| 51 | waiters.wait_for_server_termination(self.servers_client, server['id']) | |
| 52 | ||
| 53 | def create_encrypted_volume_from_image(self, encryption_provider, | |
| 54 | volume_type='luks', | |
| 55 | key_size=256, | |
| 56 | cipher='aes-xts-plain64', | |
| 57 | control_location='front-end', | |
| 58 | **kwargs): | |
| 59 | """Create an encrypted volume from image. | |
| 60 | ||
| 61 | :param image_id: ID of the image to create volume from, | |
| 62 | CONF.compute.image_ref by default | |
| 63 | :param name: name of the volume, | |
| 64 | '$classname-volume-origin' by default | |
| 65 | :param **kwargs: additional parameters | |
| 66 | """ | |
| 67 | volume_type = self.create_volume_type(name=volume_type) | |
| 68 | self.create_encryption_type(type_id=volume_type['id'], | |
| 69 | provider=encryption_provider, | |
| 70 | key_size=key_size, | |
| 71 | cipher=cipher, | |
| 72 | control_location=control_location) | |
| 73 | image_id = kwargs.pop('image_id', CONF.compute.image_ref) | |
| 74 | name = kwargs.pop('name', None) | |
| 75 | if not name: | |
| 76 | namestart = self.__class__.__name__ + '-volume-origin' | |
| 77 | name = data_utils.rand_name(namestart) | |
| 78 | return self.create_volume(volume_type=volume_type['name'], | |
| 79 | name=name, imageRef=image_id, | |
| 80 | **kwargs) | |
| 81 | ||
| 82 | @decorators.idempotent_id('5bb622ab-5060-48a8-8840-d589a548b9e4') | |
| 83 | @utils.services('volume') | |
| 84 | @utils.services('compute') | |
| 85 | def test_attach_cloned_encrypted_volume(self): | |
| 86 | ||
| 87 | """This test case attempts to reproduce the following steps: | |
| 88 | ||
| 89 | * Create an encrypted volume | |
| 90 | * Create clone from volume | |
| 91 | * Boot an instance and attach/dettach cloned volume | |
| 92 | ||
| 93 | """ | |
| 94 | ||
| 95 | volume = self.create_encrypted_volume('luks', volume_type='luks') | |
| 96 | kwargs = { | |
| 97 | 'display_name': data_utils.rand_name(self.__class__.__name__), | |
| 98 | 'source_volid': volume['id'], | |
| 99 | 'volume_type': volume['volume_type'], | |
| 100 | 'size': volume['size'] | |
| 101 | } | |
| 102 | volume_s = self.volumes_client.create_volume(**kwargs)['volume'] | |
| 103 | self.addCleanup(self.volumes_client.wait_for_resource_deletion, | |
| 104 | volume_s['id']) | |
| 105 | self.addCleanup(self.volumes_client.delete_volume, volume_s['id']) | |
| 106 | waiters.wait_for_volume_resource_status( | |
| 107 | self.volumes_client, volume_s['id'], 'available') | |
| 108 | volume_source = self.volumes_client.show_volume( | |
| 109 | volume_s['id'])['volume'] | |
| 110 | server = self.launch_instance() | |
| 111 | self.attach_detach_volume(server, volume_source) | |
| 112 | ||
| 113 | @decorators.idempotent_id('5bb622ab-5060-48a8-8840-d589a548b7e4') | |
| 114 | @utils.services('volume') | |
| 115 | @utils.services('compute') | |
| 116 | @utils.services('image') | |
| 117 | def test_boot_cloned_encrypted_volume(self): | |
| 118 | ||
| 119 | """This test case attempts to reproduce the following steps: | |
| 120 | ||
| 121 | * Create an encrypted volume from image | |
| 122 | * Boot an instance from the volume | |
| 123 | * Write data to the volume | |
| 124 | * Detach volume | |
| 125 | * Create a clone from the first volume | |
| 126 | * Create another encrypted volume from source_volumeid | |
| 127 | * Boot an instance from cloned volume | |
| 128 | * Verify the data | |
| 129 | """ | |
| 130 | ||
| 131 | keypair = self.create_keypair() | |
| 132 | security_group = self._create_security_group() | |
| 133 | ||
| 134 | volume = self.create_encrypted_volume_from_image('luks') | |
| 135 | ||
| 136 | # create an instance from volume | |
| 137 | instance_1st = self.boot_instance_from_resource( | |
| 138 | source_id=volume['id'], | |
| 139 | source_type='volume', | |
| 140 | keypair=keypair, | |
| 141 | security_group=security_group) | |
| 142 | ||
| 143 | # write content to volume on instance | |
| 144 | ip_instance_1st = self.get_server_ip(instance_1st) | |
| 145 | timestamp = self.create_timestamp(ip_instance_1st, | |
| 146 | private_key=keypair['private_key'], | |
| 147 | server=instance_1st) | |
| 148 | # delete instance | |
| 149 | self._delete_server(instance_1st) | |
| 150 | ||
| 151 | # create clone | |
| 152 | kwargs = { | |
| 153 | 'display_name': data_utils.rand_name(self.__class__.__name__), | |
| 154 | 'source_volid': volume['id'], | |
| 155 | 'volume_type': volume['volume_type'], | |
| 156 | 'size': volume['size'] | |
| 157 | } | |
| 158 | volume_s = self.volumes_client.create_volume(**kwargs)['volume'] | |
| 159 | ||
| 160 | self.addCleanup(self.volumes_client.wait_for_resource_deletion, | |
| 161 | volume_s['id']) | |
| 162 | self.addCleanup(self.volumes_client.delete_volume, volume_s['id']) | |
| 163 | waiters.wait_for_volume_resource_status( | |
| 164 | self.volumes_client, volume_s['id'], 'available') | |
| 165 | ||
| 166 | # create an instance from volume | |
| 167 | instance_2nd = self.boot_instance_from_resource( | |
| 168 | source_id=volume_s['id'], | |
| 169 | source_type='volume', | |
| 170 | keypair=keypair, | |
| 171 | security_group=security_group) | |
| 172 | ||
| 173 | # check the content of written file | |
| 174 | ip_instance_2nd = self.get_server_ip(instance_2nd) | |
| 175 | timestamp2 = self.get_timestamp(ip_instance_2nd, | |
| 176 | private_key=keypair['private_key'], | |
| 177 | server=instance_2nd) | |
| 178 | ||
| 179 | self.assertEqual(timestamp, timestamp2) | |
| 180 | ||
| 181 | # delete instance | |
| 182 | self._delete_server(instance_2nd) |