Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 1 | # Copyright 2021 Red Hat, Inc. |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 2 | # All Rights Reserved. |
| 3 | # |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may |
| 5 | # not use this file except in compliance with the License. You may obtain |
| 6 | # a copy of the License at |
| 7 | # |
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | # |
| 10 | # Unless required by applicable law or agreed to in writing, software |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 13 | # License for the specific language governing permissions and limitations |
| 14 | # under the License. |
| 15 | |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 16 | from oslo_log import log |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 17 | |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 18 | from tempest.common import waiters |
| 19 | from tempest import config |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 20 | from tempest.lib.common.utils import data_utils |
| 21 | from tempest.lib.common.utils import test_utils |
| 22 | from tempest.lib import exceptions as lib_exc |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 23 | |
| 24 | from tempest.scenario import manager |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 25 | |
| 26 | CONF = config.CONF |
| 27 | |
| 28 | LOG = log.getLogger(__name__) |
| 29 | |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 30 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 31 | class ScenarioTest(manager.ScenarioTest): |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 32 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 33 | credentials = ['primary', 'admin'] |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 34 | |
| 35 | @classmethod |
| 36 | def setup_clients(cls): |
| 37 | super(ScenarioTest, cls).setup_clients() |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 38 | cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 39 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 40 | def _attached_volume_name( |
| 41 | self, disks_list_before_attach, ip_address, private_key): |
| 42 | ssh = self.get_remote_client(ip_address, private_key=private_key) |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 43 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 44 | def _wait_for_volume_available_on_system(): |
| 45 | disks_list_after_attach = ssh.list_disks() |
| 46 | return len(disks_list_after_attach) > len(disks_list_before_attach) |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 47 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 48 | if not test_utils.call_until_true(_wait_for_volume_available_on_system, |
| 49 | CONF.compute.build_timeout, |
| 50 | CONF.compute.build_interval): |
| 51 | raise lib_exc.TimeoutException |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 52 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 53 | disks_list_after_attach = ssh.list_disks() |
| 54 | volume_name = [item for item in disks_list_after_attach |
| 55 | if item not in disks_list_before_attach][0] |
| 56 | return volume_name |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 57 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 58 | def _get_file_md5(self, ip_address, filename, dev_name=None, |
| 59 | mount_path='/mnt', private_key=None, server=None): |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 60 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 61 | ssh_client = self.get_remote_client(ip_address, |
| 62 | private_key=private_key, |
| 63 | server=server) |
| 64 | if dev_name is not None: |
| 65 | ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name, |
| 66 | mount_path)) |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 67 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 68 | md5_sum = ssh_client.exec_command( |
| 69 | 'sudo md5sum %s/%s|cut -c 1-32' % (mount_path, filename)) |
| 70 | if dev_name is not None: |
| 71 | ssh_client.exec_command('sudo umount %s' % mount_path) |
| 72 | return md5_sum |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 73 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 74 | def _count_files(self, ip_address, dev_name=None, mount_path='/mnt', |
| 75 | private_key=None, server=None): |
| 76 | ssh_client = self.get_remote_client(ip_address, |
| 77 | private_key=private_key, |
| 78 | server=server) |
| 79 | if dev_name is not None: |
| 80 | ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name, |
| 81 | mount_path)) |
| 82 | count = ssh_client.exec_command('sudo ls -l %s | wc -l' % mount_path) |
| 83 | if dev_name is not None: |
| 84 | ssh_client.exec_command('sudo umount %s' % mount_path) |
| 85 | # We subtract 2 from the count since `wc -l` also includes the count |
| 86 | # of new line character and while creating the filesystem, a |
| 87 | # lost+found folder is also created |
| 88 | return int(count) - 2 |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 89 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 90 | def _make_fs(self, ip_address, private_key, server, dev_name, fs='ext4'): |
| 91 | ssh_client = self.get_remote_client(ip_address, |
| 92 | private_key=private_key, |
| 93 | server=server) |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 94 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 95 | ssh_client.make_fs(dev_name, fs=fs) |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 96 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 97 | def create_md5_new_file(self, ip_address, filename, dev_name=None, |
| 98 | mount_path='/mnt', private_key=None, server=None): |
| 99 | ssh_client = self.get_remote_client(ip_address, |
| 100 | private_key=private_key, |
| 101 | server=server) |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 102 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 103 | if dev_name is not None: |
| 104 | ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name, |
| 105 | mount_path)) |
| 106 | ssh_client.exec_command( |
| 107 | 'sudo dd bs=1024 count=100 if=/dev/urandom of=/%s/%s' % |
| 108 | (mount_path, filename)) |
| 109 | md5 = ssh_client.exec_command( |
| 110 | 'sudo md5sum -b %s/%s|cut -c 1-32' % (mount_path, filename)) |
| 111 | ssh_client.exec_command('sudo sync') |
| 112 | if dev_name is not None: |
| 113 | ssh_client.exec_command('sudo umount %s' % mount_path) |
| 114 | return md5 |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 115 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 116 | def get_md5_from_file(self, instance, instance_ip, filename, |
| 117 | dev_name=None): |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 118 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 119 | md5_sum = self._get_file_md5(instance_ip, filename=filename, |
| 120 | dev_name=dev_name, |
| 121 | private_key=self.keypair['private_key'], |
| 122 | server=instance) |
| 123 | count = self._count_files(instance_ip, dev_name=dev_name, |
| 124 | private_key=self.keypair['private_key'], |
| 125 | server=instance) |
| 126 | return count, md5_sum |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 127 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 128 | def _attach_and_get_volume_device_name(self, server, volume, instance_ip, |
| 129 | private_key): |
| 130 | ssh_client = self.get_remote_client( |
| 131 | instance_ip, private_key=private_key, |
| 132 | server=server) |
| 133 | # List disks before volume attachment |
| 134 | disks_list_before_attach = ssh_client.list_disks() |
| 135 | # Attach volume |
| 136 | attachment = self.attach_volume(server, volume) |
| 137 | # Find the difference between disks before and after attachment that |
| 138 | # gives us the volume device name |
| 139 | volume_device_name = self._attached_volume_name( |
| 140 | disks_list_before_attach, instance_ip, private_key) |
| 141 | return volume_device_name, attachment |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 142 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 143 | def create_volume_type(self, client=None, name=None, extra_specs=None): |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 144 | if not client: |
| 145 | client = self.os_admin.volume_types_client_latest |
| 146 | if not name: |
| 147 | class_name = self.__class__.__name__ |
| 148 | name = data_utils.rand_name(class_name + '-volume-type') |
| 149 | randomized_name = data_utils.rand_name('scenario-type-' + name) |
| 150 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 151 | LOG.debug("Creating a volume type: %s with extra_specs %s", |
| 152 | randomized_name, extra_specs) |
| 153 | if extra_specs is None: |
| 154 | extra_specs = {} |
| 155 | volume_type = self.admin_volume_types_client.create_volume_type( |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 156 | name=randomized_name, extra_specs=extra_specs)['volume_type'] |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 157 | self.addCleanup(self.cleanup_volume_type, volume_type) |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 158 | return volume_type |
| 159 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 160 | def attach_volume(self, server, volume, device=None, tag=None): |
| 161 | """Attaches volume to server and waits for 'in-use' volume status. |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 162 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 163 | The volume will be detached when the test tears down. |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 164 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 165 | :param server: The server to which the volume will be attached. |
| 166 | :param volume: The volume to attach. |
| 167 | :param device: Optional mountpoint for the attached volume. Note that |
| 168 | this is not guaranteed for all hypervisors and is not recommended. |
| 169 | :param tag: Optional device role tag to apply to the volume. |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 170 | """ |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 171 | attach_kwargs = dict(volumeId=volume['id']) |
| 172 | if device: |
| 173 | attach_kwargs['device'] = device |
| 174 | if tag: |
| 175 | attach_kwargs['tag'] = tag |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 176 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 177 | attachment = self.servers_client.attach_volume( |
| 178 | server['id'], **attach_kwargs)['volumeAttachment'] |
| 179 | # On teardown detach the volume and for multiattach volumes wait for |
| 180 | # the attachment to be removed. For non-multiattach volumes wait for |
| 181 | # the state of the volume to change to available. This is so we don't |
| 182 | # error out when trying to delete the volume during teardown. |
| 183 | if volume['multiattach']: |
| 184 | att = waiters.wait_for_volume_attachment_create( |
| 185 | self.volumes_client, volume['id'], server['id']) |
| 186 | self.addCleanup(waiters.wait_for_volume_attachment_remove, |
| 187 | self.volumes_client, volume['id'], |
| 188 | att['attachment_id']) |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 189 | else: |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 190 | self.addCleanup(waiters.wait_for_volume_resource_status, |
| 191 | self.volumes_client, volume['id'], 'available') |
| 192 | waiters.wait_for_volume_resource_status(self.volumes_client, |
| 193 | volume['id'], 'in-use') |
| 194 | # Ignore 404s on detach in case the server is deleted or the volume |
| 195 | # is already detached. |
| 196 | self.addCleanup(self._detach_volume, server, volume) |
| 197 | return attachment |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 198 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 199 | def _detach_volume(self, server, volume): |
| 200 | """Helper method to detach a volume. |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 201 | |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 202 | Ignores 404 responses if the volume or server do not exist, or the |
| 203 | volume is already detached from the server. |
Rajat Dhasmana | 21d63a3 | 2020-01-14 17:41:22 +0000 | [diff] [blame] | 204 | """ |
Rajat Dhasmana | 638f230 | 2021-05-12 06:23:45 -0400 | [diff] [blame^] | 205 | try: |
| 206 | volume = self.volumes_client.show_volume(volume['id'])['volume'] |
| 207 | # Check the status. You can only detach an in-use volume, otherwise |
| 208 | # the compute API will return a 400 response. |
| 209 | if volume['status'] == 'in-use': |
| 210 | self.servers_client.detach_volume(server['id'], volume['id']) |
| 211 | except lib_exc.NotFound: |
| 212 | # Ignore 404s on detach in case the server is deleted or the volume |
| 213 | # is already detached. |
| 214 | pass |