-
Notifications
You must be signed in to change notification settings - Fork 86
/
Copy pathconftest.py
313 lines (259 loc) · 10.9 KB
/
conftest.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
#
# Copyright(c) 2019-2022 Intel Corporation
# Copyright(c) 2023-2025 Huawei Technologies Co., Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#
import os
import posixpath
import sys
import traceback
from datetime import timedelta
import paramiko
import pytest
import yaml
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "test-framework"))
from core.test_run import Blocked
from core.test_run_utils import TestRun
from api.cas import installer
from api.cas import casadm
from api.cas.cas_service import opencas_drop_in_directory
from storage_devices.raid import Raid
from storage_devices.ramdisk import RamDisk
from test_tools.os_tools import kill_all_io
from test_tools.udev import Udev
from test_tools.disk_tools import PartitionTable, create_partition_table
from test_tools.device_mapper import DeviceMapper
from test_tools.mdadm import Mdadm
from test_tools.fs_tools import remove
from test_tools import initramfs, git
from log.logger import create_log, Log
from test_utils.common.singleton import Singleton
from storage_devices.lvm import Lvm, LvmConfiguration
from storage_devices.disk import Disk
def pytest_addoption(parser):
TestRun.addoption(parser)
parser.addoption("--dut-config", action="append", type=str)
parser.addoption(
"--log-path",
action="store",
default=f"{os.path.join(os.path.dirname(__file__), '../results')}",
)
parser.addoption("--fuzzy-iter-count", action="store")
def pytest_configure(config):
TestRun.configure(config)
def pytest_generate_tests(metafunc):
TestRun.generate_tests(metafunc)
def pytest_collection_modifyitems(config, items):
if config.option.collectonly:
for item in items:
multidut = next(item.iter_markers(name="multidut"), None)
if multidut:
number = multidut.args[0]
print(f"multidut {item.nodeid} {number}")
sys.stdout.flush()
def pytest_runtest_setup(item):
# There should be dut config file added to config package and
# pytest should be executed with option --dut-config=conf_name'.
#
# 'ip' field should be filled with valid IP string to use remote ssh executor
# or it should be commented out when user want to execute tests on local machine
#
# User can also have own test wrapper, which runs test prepare, cleanup, etc.
# Then it should be placed in plugins package
test_name = item.name.split("[")[0]
TestRun.LOGGER = create_log(item.config.getoption("--log-path"), test_name)
duts = item.config.getoption("--dut-config")
required_duts = next(item.iter_markers(name="multidut"), None)
required_duts = required_duts.args[0] if required_duts is not None else 1
if required_duts > len(duts):
raise Exception(
f"Test requires {required_duts} DUTs, only {len(duts)} DUT configs provided"
)
else:
duts = duts[:required_duts]
TestRun.duts = []
for dut in duts:
try:
with open(dut) as cfg:
dut_config = yaml.safe_load(cfg)
except Exception as ex:
raise Exception(
f"{ex}\nYou need to specify DUT config. See the example_dut_config.py file"
)
dut_config["plugins_dir"] = os.path.join(os.path.dirname(__file__), "..", "lib")
dut_config["opt_plugins"] = {"test_wrapper": {}, "serial_log": {}, "power_control": {}}
dut_config["extra_logs"] = {"cas": "/var/log/opencas.log"}
try:
TestRun.prepare(item, dut_config)
TestRun.presetup()
try:
TestRun.executor.wait_for_connection(timedelta(seconds=20))
except (paramiko.AuthenticationException, Blocked):
raise
except Exception:
try:
TestRun.plugin_manager.get_plugin("power_control").power_cycle()
TestRun.executor.wait_for_connection()
except Exception:
raise Exception("Failed to connect to DUT.")
TestRun.setup()
except Exception as ex:
raise Exception(
f"Exception occurred during test setup:\n{str(ex)}\n{traceback.format_exc()}"
)
TestRun.usr = Opencas(
repo_dir=os.path.join(os.path.dirname(__file__), "..", "..", ".."),
working_dir=dut_config["working_dir"],
)
if item.config.getoption("--fuzzy-iter-count"):
TestRun.usr.fuzzy_iter_count = int(item.config.getoption("--fuzzy-iter-count"))
TestRun.LOGGER.info(f"DUT info: {TestRun.dut}")
TestRun.dut.plugin_manager = TestRun.plugin_manager
TestRun.dut.executor = TestRun.executor
TestRun.duts.append(TestRun.dut)
base_prepare(item)
TestRun.LOGGER.write_to_command_log("Test body")
TestRun.LOGGER.start_group("Test body")
def base_prepare(item):
with TestRun.LOGGER.step("Cleanup before test"):
TestRun.executor.run("pkill --signal=SIGKILL fsck")
Udev.enable()
kill_all_io(graceful=False)
DeviceMapper.remove_all()
if installer.check_if_installed():
try:
from api.cas.init_config import InitConfig
InitConfig.create_default_init_config()
unmount_cas_devices()
casadm.stop_all_caches()
casadm.remove_all_detached_cores()
except Exception:
pass # TODO: Reboot DUT if test is executed remotely
remove(str(opencas_drop_in_directory), recursive=True, ignore_errors=True)
from storage_devices.drbd import Drbd
if Drbd.is_installed():
__drbd_cleanup()
lvms = Lvm.discover()
if lvms:
Lvm.remove_all()
LvmConfiguration.remove_filters_from_config()
initramfs.update()
raids = Raid.discover()
if len(TestRun.disks):
test_run_disk_ids = {dev.device_id for dev in TestRun.disks.values()}
for raid in raids:
# stop only those RAIDs, which are comprised of test disks
if filter(lambda dev: dev.device_id in test_run_disk_ids, raid.array_devices):
raid.remove_partitions()
raid.unmount()
raid.stop()
for device in raid.array_devices:
Mdadm.zero_superblock(posixpath.join("/dev", device.get_device_id()))
Udev.settle()
RamDisk.remove_all()
for disk in TestRun.disks.values():
disk_serial = Disk.get_disk_serial_number(disk.path)
if disk.serial_number and disk.serial_number != disk_serial:
raise Exception(
f"Serial for {disk.path} doesn't match the one from the config."
f"Serial from config {disk.serial_number}, actual serial {disk_serial}"
)
disk.remove_partitions()
disk.unmount()
Mdadm.zero_superblock(posixpath.join("/dev", disk.get_device_id()))
create_partition_table(disk, PartitionTable.gpt)
TestRun.usr.already_updated = True
TestRun.LOGGER.add_build_info(f"Commit hash:")
TestRun.LOGGER.add_build_info(f"{git.get_current_commit_hash()}")
TestRun.LOGGER.add_build_info(f"Commit message:")
TestRun.LOGGER.add_build_info(f"{git.get_current_commit_message()}")
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
res = (yield).get_result()
TestRun.makereport(item, call, res)
def pytest_runtest_teardown():
"""
This method is executed always in the end of each test, even if it fails or raises exception in
prepare stage.
"""
TestRun.LOGGER.end_all_groups()
with TestRun.LOGGER.step("Cleanup after test"):
try:
if TestRun.executor:
if not TestRun.executor.is_active():
TestRun.executor.wait_for_connection()
Udev.enable()
kill_all_io(graceful=False)
unmount_cas_devices()
if installer.check_if_installed():
casadm.remove_all_detached_cores()
casadm.stop_all_caches()
from api.cas.init_config import InitConfig
InitConfig.create_default_init_config()
from storage_devices.drbd import Drbd
if installer.check_if_installed() and Drbd.is_installed():
try:
casadm.stop_all_caches()
finally:
__drbd_cleanup()
elif Drbd.is_installed():
Drbd.down_all()
lvms = Lvm.discover()
if lvms:
Lvm.remove_all()
LvmConfiguration.remove_filters_from_config()
initramfs.update()
DeviceMapper.remove_all()
RamDisk.remove_all()
except Exception as ex:
TestRun.LOGGER.warning(
f"Exception occurred during platform cleanup.\n"
f"{str(ex)}\n{traceback.format_exc()}"
)
TestRun.LOGGER.end()
for dut in TestRun.duts:
with TestRun.use_dut(dut):
if TestRun.executor:
os.makedirs(
os.path.join(
TestRun.LOGGER.base_dir,
"dut_info",
dut.ip if dut.ip is not None else dut.config.get("host"),
),
exist_ok=True,
)
TestRun.LOGGER.get_additional_logs()
Log.destroy()
TestRun.teardown()
def unmount_cas_devices():
output = TestRun.executor.run("cat /proc/mounts | grep cas")
# If exit code is '1' but stdout is empty, there is no mounted cas devices
if output.exit_code == 1:
return
elif output.exit_code != 0:
raise Exception(
f"Failed to list mounted cas devices. \
stdout: {output.stdout} \n stderr :{output.stderr}"
)
for line in output.stdout.splitlines():
cas_device_path = line.split()[0]
TestRun.LOGGER.info(f"Unmounting {cas_device_path}")
output = TestRun.executor.run(f"umount {cas_device_path}")
if output.exit_code != 0:
raise Exception(
f"Failed to unmount {cas_device_path}. \
stdout: {output.stdout} \n stderr :{output.stderr}"
)
def __drbd_cleanup():
from storage_devices.drbd import Drbd
Drbd.down_all()
# If drbd instance had been configured on top of the CAS, the previous attempt to stop
# failed. As drbd has been stopped try to stop CAS one more time.
if installer.check_if_installed():
casadm.stop_all_caches()
class Opencas(metaclass=Singleton):
def __init__(self, repo_dir, working_dir):
self.repo_dir = repo_dir
self.working_dir = working_dir
self.already_updated = False
self.fuzzy_iter_count = 1000