blob: cffa0442425ced6a6ea374103423f57e25afea5b [file] [log] [blame]
Rajat Dhasmana638f2302021-05-12 06:23:45 -04001# Copyright 2021 Red Hat, Inc.
Rajat Dhasmana21d63a32020-01-14 17:41:22 +00002# 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
whoami-rajat027295a2021-12-12 12:31:59 -050016import contextlib
17
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000018from oslo_log import log
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000019
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000020from tempest.common import waiters
21from tempest import config
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000022from tempest.lib.common.utils import data_utils
23from tempest.lib.common.utils import test_utils
24from tempest.lib import exceptions as lib_exc
Rajat Dhasmana638f2302021-05-12 06:23:45 -040025
26from tempest.scenario import manager
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000027
28CONF = config.CONF
29
30LOG = log.getLogger(__name__)
31
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000032
Rajat Dhasmana638f2302021-05-12 06:23:45 -040033class ScenarioTest(manager.ScenarioTest):
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000034
Rajat Dhasmana638f2302021-05-12 06:23:45 -040035 credentials = ['primary', 'admin']
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000036
37 @classmethod
38 def setup_clients(cls):
39 super(ScenarioTest, cls).setup_clients()
Rajat Dhasmana638f2302021-05-12 06:23:45 -040040 cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000041
Rajat Dhasmana638f2302021-05-12 06:23:45 -040042 def _attached_volume_name(
43 self, disks_list_before_attach, ip_address, private_key):
44 ssh = self.get_remote_client(ip_address, private_key=private_key)
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000045
Rajat Dhasmana638f2302021-05-12 06:23:45 -040046 def _wait_for_volume_available_on_system():
47 disks_list_after_attach = ssh.list_disks()
48 return len(disks_list_after_attach) > len(disks_list_before_attach)
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000049
Rajat Dhasmana638f2302021-05-12 06:23:45 -040050 if not test_utils.call_until_true(_wait_for_volume_available_on_system,
51 CONF.compute.build_timeout,
52 CONF.compute.build_interval):
53 raise lib_exc.TimeoutException
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000054
Rajat Dhasmana638f2302021-05-12 06:23:45 -040055 disks_list_after_attach = ssh.list_disks()
56 volume_name = [item for item in disks_list_after_attach
57 if item not in disks_list_before_attach][0]
58 return volume_name
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000059
whoami-rajat027295a2021-12-12 12:31:59 -050060 @contextlib.contextmanager
61 def mount_dev_path(self, ssh_client, dev_name, mount_path):
62 if dev_name is not None:
63 ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
64 mount_path))
65 yield
66 ssh_client.exec_command('sudo umount %s' % mount_path)
67 else:
68 yield
69
Rajat Dhasmana638f2302021-05-12 06:23:45 -040070 def _get_file_md5(self, ip_address, filename, dev_name=None,
71 mount_path='/mnt', private_key=None, server=None):
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000072
Rajat Dhasmana638f2302021-05-12 06:23:45 -040073 ssh_client = self.get_remote_client(ip_address,
74 private_key=private_key,
75 server=server)
whoami-rajat027295a2021-12-12 12:31:59 -050076 with self.mount_dev_path(ssh_client, dev_name, mount_path):
77 md5_sum = ssh_client.exec_command(
78 'sudo md5sum %s/%s|cut -c 1-32' % (mount_path, filename))
Rajat Dhasmana638f2302021-05-12 06:23:45 -040079 return md5_sum
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000080
Rajat Dhasmana638f2302021-05-12 06:23:45 -040081 def _count_files(self, ip_address, dev_name=None, mount_path='/mnt',
82 private_key=None, server=None):
83 ssh_client = self.get_remote_client(ip_address,
84 private_key=private_key,
85 server=server)
whoami-rajat027295a2021-12-12 12:31:59 -050086 with self.mount_dev_path(ssh_client, dev_name, mount_path):
87 count = ssh_client.exec_command(
88 'sudo ls -l %s | wc -l' % mount_path)
Rajat Dhasmana638f2302021-05-12 06:23:45 -040089 # We subtract 2 from the count since `wc -l` also includes the count
90 # of new line character and while creating the filesystem, a
91 # lost+found folder is also created
92 return int(count) - 2
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000093
Rajat Dhasmana638f2302021-05-12 06:23:45 -040094 def _make_fs(self, ip_address, private_key, server, dev_name, fs='ext4'):
95 ssh_client = self.get_remote_client(ip_address,
96 private_key=private_key,
97 server=server)
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000098
Rajat Dhasmana638f2302021-05-12 06:23:45 -040099 ssh_client.make_fs(dev_name, fs=fs)
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000100
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400101 def create_md5_new_file(self, ip_address, filename, dev_name=None,
102 mount_path='/mnt', private_key=None, server=None):
103 ssh_client = self.get_remote_client(ip_address,
104 private_key=private_key,
105 server=server)
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000106
whoami-rajat027295a2021-12-12 12:31:59 -0500107 with self.mount_dev_path(ssh_client, dev_name, mount_path):
108 ssh_client.exec_command(
109 'sudo dd bs=1024 count=100 if=/dev/urandom of=/%s/%s' %
110 (mount_path, filename))
111 md5 = ssh_client.exec_command(
112 'sudo md5sum -b %s/%s|cut -c 1-32' % (mount_path, filename))
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400113 return md5
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000114
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400115 def get_md5_from_file(self, instance, instance_ip, filename,
116 dev_name=None):
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000117
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400118 md5_sum = self._get_file_md5(instance_ip, filename=filename,
119 dev_name=dev_name,
120 private_key=self.keypair['private_key'],
121 server=instance)
122 count = self._count_files(instance_ip, dev_name=dev_name,
123 private_key=self.keypair['private_key'],
124 server=instance)
125 return count, md5_sum
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000126
whoami-rajat6dc9a2f2021-05-18 07:01:33 -0400127 def write_data_to_device(self, ip_address, out_dev, in_dev='/dev/urandom',
128 bs=1024, count=100, private_key=None,
129 server=None, sha_sum=False):
130 ssh_client = self.get_remote_client(
131 ip_address, private_key=private_key, server=server)
132
133 # Write data to device
134 write_command = (
135 'sudo dd bs=%(bs)s count=%(count)s if=%(in_dev)s of=%(out_dev)s '
136 '&& sudo dd bs=%(bs)s count=%(count)s if=%(out_dev)s' %
137 {'bs': str(bs), 'count': str(count), 'in_dev': in_dev,
138 'out_dev': out_dev})
139 if sha_sum:
140 # If we want to read sha1sum instead of the device data
141 write_command += ' | sha1sum | head -c 40'
142 data = ssh_client.exec_command(write_command)
143
144 return data
145
146 def read_data_from_device(self, ip_address, in_dev, bs=1024, count=100,
147 private_key=None, server=None, sha_sum=False):
148 ssh_client = self.get_remote_client(
149 ip_address, private_key=private_key, server=server)
150
151 # Read data from device
152 read_command = ('sudo dd bs=%(bs)s count=%(count)s if=%(in_dev)s' %
153 {'bs': bs, 'count': count, 'in_dev': in_dev})
154 if sha_sum:
155 # If we want to read sha1sum instead of the device data
156 read_command += ' | sha1sum | head -c 40'
157 data = ssh_client.exec_command(read_command)
158
159 return data
160
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400161 def _attach_and_get_volume_device_name(self, server, volume, instance_ip,
162 private_key):
163 ssh_client = self.get_remote_client(
164 instance_ip, private_key=private_key,
165 server=server)
166 # List disks before volume attachment
167 disks_list_before_attach = ssh_client.list_disks()
168 # Attach volume
169 attachment = self.attach_volume(server, volume)
170 # Find the difference between disks before and after attachment that
171 # gives us the volume device name
172 volume_device_name = self._attached_volume_name(
173 disks_list_before_attach, instance_ip, private_key)
174 return volume_device_name, attachment
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000175
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400176 def create_volume_type(self, client=None, name=None, extra_specs=None):
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000177 if not client:
178 client = self.os_admin.volume_types_client_latest
179 if not name:
180 class_name = self.__class__.__name__
181 name = data_utils.rand_name(class_name + '-volume-type')
182 randomized_name = data_utils.rand_name('scenario-type-' + name)
183
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400184 LOG.debug("Creating a volume type: %s with extra_specs %s",
185 randomized_name, extra_specs)
186 if extra_specs is None:
187 extra_specs = {}
188 volume_type = self.admin_volume_types_client.create_volume_type(
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000189 name=randomized_name, extra_specs=extra_specs)['volume_type']
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400190 self.addCleanup(self.cleanup_volume_type, volume_type)
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000191 return volume_type
192
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400193 def attach_volume(self, server, volume, device=None, tag=None):
194 """Attaches volume to server and waits for 'in-use' volume status.
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000195
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400196 The volume will be detached when the test tears down.
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000197
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400198 :param server: The server to which the volume will be attached.
199 :param volume: The volume to attach.
200 :param device: Optional mountpoint for the attached volume. Note that
201 this is not guaranteed for all hypervisors and is not recommended.
202 :param tag: Optional device role tag to apply to the volume.
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000203 """
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400204 attach_kwargs = dict(volumeId=volume['id'])
205 if device:
206 attach_kwargs['device'] = device
207 if tag:
208 attach_kwargs['tag'] = tag
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000209
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400210 attachment = self.servers_client.attach_volume(
211 server['id'], **attach_kwargs)['volumeAttachment']
212 # On teardown detach the volume and for multiattach volumes wait for
213 # the attachment to be removed. For non-multiattach volumes wait for
214 # the state of the volume to change to available. This is so we don't
215 # error out when trying to delete the volume during teardown.
216 if volume['multiattach']:
217 att = waiters.wait_for_volume_attachment_create(
218 self.volumes_client, volume['id'], server['id'])
219 self.addCleanup(waiters.wait_for_volume_attachment_remove,
220 self.volumes_client, volume['id'],
221 att['attachment_id'])
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000222 else:
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400223 self.addCleanup(waiters.wait_for_volume_resource_status,
224 self.volumes_client, volume['id'], 'available')
225 waiters.wait_for_volume_resource_status(self.volumes_client,
226 volume['id'], 'in-use')
227 # Ignore 404s on detach in case the server is deleted or the volume
228 # is already detached.
229 self.addCleanup(self._detach_volume, server, volume)
230 return attachment
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000231
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400232 def _detach_volume(self, server, volume):
233 """Helper method to detach a volume.
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000234
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400235 Ignores 404 responses if the volume or server do not exist, or the
236 volume is already detached from the server.
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000237 """
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400238 try:
239 volume = self.volumes_client.show_volume(volume['id'])['volume']
240 # Check the status. You can only detach an in-use volume, otherwise
241 # the compute API will return a 400 response.
242 if volume['status'] == 'in-use':
243 self.servers_client.detach_volume(server['id'], volume['id'])
244 except lib_exc.NotFound:
245 # Ignore 404s on detach in case the server is deleted or the volume
246 # is already detached.
247 pass