blob: 3b25bb141558f21cbee41b2d84d36bcd2d24c60f [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
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000016from oslo_log import log
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000017
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000018from tempest.common import waiters
19from tempest import config
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000020from tempest.lib.common.utils import data_utils
21from tempest.lib.common.utils import test_utils
22from tempest.lib import exceptions as lib_exc
Rajat Dhasmana638f2302021-05-12 06:23:45 -040023
24from tempest.scenario import manager
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000025
26CONF = config.CONF
27
28LOG = log.getLogger(__name__)
29
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000030
Rajat Dhasmana638f2302021-05-12 06:23:45 -040031class ScenarioTest(manager.ScenarioTest):
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000032
Rajat Dhasmana638f2302021-05-12 06:23:45 -040033 credentials = ['primary', 'admin']
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000034
35 @classmethod
36 def setup_clients(cls):
37 super(ScenarioTest, cls).setup_clients()
Rajat Dhasmana638f2302021-05-12 06:23:45 -040038 cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000039
Rajat Dhasmana638f2302021-05-12 06:23:45 -040040 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 Dhasmana21d63a32020-01-14 17:41:22 +000043
Rajat Dhasmana638f2302021-05-12 06:23:45 -040044 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 Dhasmana21d63a32020-01-14 17:41:22 +000047
Rajat Dhasmana638f2302021-05-12 06:23:45 -040048 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 Dhasmana21d63a32020-01-14 17:41:22 +000052
Rajat Dhasmana638f2302021-05-12 06:23:45 -040053 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 Dhasmana21d63a32020-01-14 17:41:22 +000057
Rajat Dhasmana638f2302021-05-12 06:23:45 -040058 def _get_file_md5(self, ip_address, filename, dev_name=None,
59 mount_path='/mnt', private_key=None, server=None):
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000060
Rajat Dhasmana638f2302021-05-12 06:23:45 -040061 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 Dhasmana21d63a32020-01-14 17:41:22 +000067
Rajat Dhasmana638f2302021-05-12 06:23:45 -040068 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 Dhasmana21d63a32020-01-14 17:41:22 +000073
Rajat Dhasmana638f2302021-05-12 06:23:45 -040074 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 Dhasmana21d63a32020-01-14 17:41:22 +000089
Rajat Dhasmana638f2302021-05-12 06:23:45 -040090 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 Dhasmana21d63a32020-01-14 17:41:22 +000094
Rajat Dhasmana638f2302021-05-12 06:23:45 -040095 ssh_client.make_fs(dev_name, fs=fs)
Rajat Dhasmana21d63a32020-01-14 17:41:22 +000096
Rajat Dhasmana638f2302021-05-12 06:23:45 -040097 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 Dhasmana21d63a32020-01-14 17:41:22 +0000102
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400103 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 Dhasmana21d63a32020-01-14 17:41:22 +0000115
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400116 def get_md5_from_file(self, instance, instance_ip, filename,
117 dev_name=None):
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000118
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400119 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 Dhasmana21d63a32020-01-14 17:41:22 +0000127
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400128 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 Dhasmana21d63a32020-01-14 17:41:22 +0000142
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400143 def create_volume_type(self, client=None, name=None, extra_specs=None):
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000144 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 Dhasmana638f2302021-05-12 06:23:45 -0400151 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 Dhasmana21d63a32020-01-14 17:41:22 +0000156 name=randomized_name, extra_specs=extra_specs)['volume_type']
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400157 self.addCleanup(self.cleanup_volume_type, volume_type)
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000158 return volume_type
159
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400160 def attach_volume(self, server, volume, device=None, tag=None):
161 """Attaches volume to server and waits for 'in-use' volume status.
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000162
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400163 The volume will be detached when the test tears down.
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000164
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400165 :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 Dhasmana21d63a32020-01-14 17:41:22 +0000170 """
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400171 attach_kwargs = dict(volumeId=volume['id'])
172 if device:
173 attach_kwargs['device'] = device
174 if tag:
175 attach_kwargs['tag'] = tag
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000176
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400177 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 Dhasmana21d63a32020-01-14 17:41:22 +0000189 else:
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400190 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 Dhasmana21d63a32020-01-14 17:41:22 +0000198
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400199 def _detach_volume(self, server, volume):
200 """Helper method to detach a volume.
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000201
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400202 Ignores 404 responses if the volume or server do not exist, or the
203 volume is already detached from the server.
Rajat Dhasmana21d63a32020-01-14 17:41:22 +0000204 """
Rajat Dhasmana638f2302021-05-12 06:23:45 -0400205 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