VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/tests/storage/tdStorageBenchmark1.py@ 107381

Last change on this file since 107381 was 106061, checked in by vboxsync, 5 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 62.9 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: tdStorageBenchmark1.py 106061 2024-09-16 14:03:52Z vboxsync $
4
5"""
6VirtualBox Validation Kit - Storage benchmark.
7"""
8
9__copyright__ = \
10"""
11Copyright (C) 2012-2024 Oracle and/or its affiliates.
12
13This file is part of VirtualBox base platform packages, as
14available from https://www.virtualbox.org.
15
16This program is free software; you can redistribute it and/or
17modify it under the terms of the GNU General Public License
18as published by the Free Software Foundation, in version 3 of the
19License.
20
21This program is distributed in the hope that it will be useful, but
22WITHOUT ANY WARRANTY; without even the implied warranty of
23MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24General Public License for more details.
25
26You should have received a copy of the GNU General Public License
27along with this program; if not, see <https://www.gnu.org/licenses>.
28
29The contents of this file may alternatively be used under the terms
30of the Common Development and Distribution License Version 1.0
31(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
32in the VirtualBox distribution, in which case the provisions of the
33CDDL are applicable instead of those of the GPL.
34
35You may elect to license modified versions of this file under the
36terms and conditions of either the GPL or the CDDL or both.
37
38SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
39"""
40__version__ = "$Revision: 106061 $"
41
42
43# Standard Python imports.
44import os;
45import socket;
46import sys;
47if sys.version_info[0] >= 3:
48 from io import StringIO as StringIO; # pylint: disable=import-error,no-name-in-module,useless-import-alias
49else:
50 from StringIO import StringIO as StringIO; # pylint: disable=import-error,no-name-in-module,useless-import-alias
51
52# Only the main script needs to modify the path.
53try: __file__ # pylint: disable=used-before-assignment
54except: __file__ = sys.argv[0];
55g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
56sys.path.append(g_ksValidationKitDir);
57
58# Validation Kit imports.
59from common import constants;
60from common import utils;
61from testdriver import reporter;
62from testdriver import base;
63from testdriver import vbox;
64from testdriver import vboxcon;
65from testdriver import vboxwrappers;
66
67import remoteexecutor;
68import storagecfg;
69
70
71class FioTest(object):
72 """
73 Flexible I/O tester testcase.
74 """
75
76 kdHostIoEngine = {
77 'solaris': ('solarisaio', False),
78 'linux': ('libaio', True)
79 };
80
81 def __init__(self, oExecutor, dCfg = None):
82 self.oExecutor = oExecutor;
83 self.sCfgFileId = None;
84 self.dCfg = dCfg;
85 self.sError = None;
86 self.sResult = None;
87
88 def prepare(self, cMsTimeout = 30000):
89 """ Prepares the testcase """
90 reporter.testStart('Fio');
91
92 sTargetOs = self.dCfg.get('TargetOs', 'linux');
93 sIoEngine, fDirectIo = self.kdHostIoEngine.get(sTargetOs);
94 if sIoEngine is None:
95 return False;
96
97 cfgBuf = StringIO();
98 cfgBuf.write('[global]\n');
99 cfgBuf.write('bs=' + str(self.dCfg.get('RecordSize', 4096)) + '\n');
100 cfgBuf.write('ioengine=' + sIoEngine + '\n');
101 cfgBuf.write('iodepth=' + str(self.dCfg.get('QueueDepth', 32)) + '\n');
102 cfgBuf.write('size=' + str(self.dCfg.get('TestsetSize', 2147483648)) + '\n');
103 if fDirectIo:
104 cfgBuf.write('direct=1\n');
105 else:
106 cfgBuf.write('direct=0\n');
107 cfgBuf.write('directory=' + self.dCfg.get('FilePath', '/mnt') + '\n');
108 cfgBuf.write('filename=fio.test.file');
109
110 cfgBuf.write('[seq-write]\n');
111 cfgBuf.write('rw=write\n');
112 cfgBuf.write('stonewall\n');
113
114 cfgBuf.write('[rand-write]\n');
115 cfgBuf.write('rw=randwrite\n');
116 cfgBuf.write('stonewall\n');
117
118 cfgBuf.write('[seq-read]\n');
119 cfgBuf.write('rw=read\n');
120 cfgBuf.write('stonewall\n');
121
122 cfgBuf.write('[rand-read]\n');
123 cfgBuf.write('rw=randread\n');
124 cfgBuf.write('stonewall\n');
125
126 self.sCfgFileId = self.oExecutor.copyString(cfgBuf.getvalue(), 'aio-test', cMsTimeout);
127 return self.sCfgFileId is not None;
128
129 def run(self, cMsTimeout = 30000):
130 """ Runs the testcase """
131 fRc, sOutput, sError = self.oExecutor.execBinary('fio', (self.sCfgFileId,), cMsTimeout = cMsTimeout);
132 if fRc:
133 self.sResult = sOutput;
134 else:
135 self.sError = ('Binary: fio\n' +
136 '\nOutput:\n\n' +
137 sOutput +
138 '\nError:\n\n' +
139 sError);
140 return fRc;
141
142 def cleanup(self):
143 """ Cleans up any leftovers from the testcase. """
144 reporter.testDone();
145 return True;
146
147 def reportResult(self):
148 """
149 Reports the test results to the test manager.
150 """
151 return True;
152
153 def getErrorReport(self):
154 """
155 Returns the error report in case the testcase failed.
156 """
157 return self.sError;
158
159class IozoneTest(object):
160 """
161 I/O zone testcase.
162 """
163 def __init__(self, oExecutor, dCfg = None):
164 self.oExecutor = oExecutor;
165 self.sResult = None;
166 self.sError = None;
167 self.lstTests = [ ('initial writers', 'FirstWrite'),
168 ('rewriters', 'Rewrite'),
169 ('re-readers', 'ReRead'),
170 ('stride readers', 'StrideRead'),
171 ('reverse readers', 'ReverseRead'),
172 ('random readers', 'RandomRead'),
173 ('mixed workload', 'MixedWorkload'),
174 ('random writers', 'RandomWrite'),
175 ('pwrite writers', 'PWrite'),
176 ('pread readers', 'PRead'),
177 ('fwriters', 'FWrite'),
178 ('freaders', 'FRead'),
179 ('readers', 'FirstRead')];
180 self.sRecordSize = str(int(dCfg.get('RecordSize', 4096) / 1024));
181 self.sTestsetSize = str(int(dCfg.get('TestsetSize', 2147483648) / 1024));
182 self.sQueueDepth = str(int(dCfg.get('QueueDepth', 32)));
183 self.sFilePath = dCfg.get('FilePath', '/mnt/iozone');
184 self.fDirectIo = True;
185
186 sTargetOs = dCfg.get('TargetOs');
187 if sTargetOs == 'solaris':
188 self.fDirectIo = False;
189
190 def prepare(self, cMsTimeout = 30000):
191 """ Prepares the testcase """
192 reporter.testStart('IoZone');
193 _ = cMsTimeout;
194 return True; # Nothing to do.
195
196 def run(self, cMsTimeout = 30000):
197 """ Runs the testcase """
198 tupArgs = ('-r', self.sRecordSize, '-s', self.sTestsetSize, \
199 '-t', '1', '-T', '-F', self.sFilePath + '/iozone.tmp');
200 if self.fDirectIo:
201 tupArgs += ('-I',);
202 fRc, sOutput, sError = self.oExecutor.execBinary('iozone', tupArgs, cMsTimeout = cMsTimeout);
203 if fRc:
204 self.sResult = sOutput;
205 else:
206 self.sError = ('Binary: iozone\n' +
207 '\nOutput:\n\n' +
208 sOutput +
209 '\nError:\n\n' +
210 sError);
211 return fRc;
212
213 def cleanup(self):
214 """ Cleans up any leftovers from the testcase. """
215 reporter.testDone();
216 return True;
217
218 def reportResult(self):
219 """
220 Reports the test results to the test manager.
221 """
222
223 fRc = True;
224 if self.sResult is not None:
225 try:
226 asLines = self.sResult.splitlines();
227 for sLine in asLines:
228 sLine = sLine.strip();
229 if sLine.startswith('Children') is True:
230 # Extract the value
231 idxValue = sLine.rfind('=');
232 if idxValue == -1:
233 raise Exception('IozoneTest: Invalid state');
234
235 idxValue += 1;
236 while sLine[idxValue] == ' ':
237 idxValue += 1;
238
239 # Get the reported value, cut off after the decimal point
240 # it is not supported by the testmanager yet and is not really
241 # relevant anyway.
242 idxValueEnd = idxValue;
243 while sLine[idxValueEnd].isdigit():
244 idxValueEnd += 1;
245
246 for sNeedle, sTestVal in self.lstTests:
247 if sLine.rfind(sNeedle) != -1:
248 reporter.testValue(sTestVal, sLine[idxValue:idxValueEnd],
249 constants.valueunit.g_asNames[constants.valueunit.KILOBYTES_PER_SEC]);
250 break;
251 except:
252 fRc = False;
253 else:
254 fRc = False;
255
256 return fRc;
257
258 def getErrorReport(self):
259 """
260 Returns the error report in case the testcase failed.
261 """
262 return self.sError;
263
264class IoPerfTest(object):
265 """
266 IoPerf testcase.
267 """
268 def __init__(self, oExecutor, dCfg = None):
269 self.oExecutor = oExecutor;
270 self.sResult = None;
271 self.sError = None;
272 self.sRecordSize = str(dCfg.get('RecordSize', 4094));
273 self.sTestsetSize = str(dCfg.get('TestsetSize', 2147483648));
274 self.sQueueDepth = str(dCfg.get('QueueDepth', 32));
275 self.sFilePath = dCfg.get('FilePath', '/mnt');
276 self.fDirectIo = True;
277 self.fReportIoStats = dCfg.get('ReportIoStats', True);
278 self.asGstIoPerfPaths = [
279 '${CDROM}/validationkit/${OS/ARCH}/IoPerf${EXESUFF}',
280 '${CDROM}/${OS/ARCH}/IoPerf${EXESUFF}',
281 ];
282
283 sTargetOs = dCfg.get('TargetOs');
284 if sTargetOs == 'solaris':
285 self.fDirectIo = False;
286
287 def _locateGstIoPerf(self):
288 """
289 Returns guest side path to FsPerf.
290 """
291 for sIoPerfPath in self.asGstIoPerfPaths:
292 if self.oExecutor.isFile(sIoPerfPath):
293 return sIoPerfPath;
294 reporter.log('Unable to find guest FsPerf in any of these places: %s' % ('\n '.join(self.asGstIoPerfPaths),));
295 return self.asGstIoPerfPaths[0];
296
297 def prepare(self, cMsTimeout = 30000):
298 """ Prepares the testcase """
299 _ = cMsTimeout;
300 return True; # Nothing to do.
301
302 def run(self, cMsTimeout = 30000):
303 """ Runs the testcase """
304 tupArgs = ('--block-size', self.sRecordSize, '--test-set-size', self.sTestsetSize, \
305 '--maximum-requests', self.sQueueDepth, '--dir', self.sFilePath + '/ioperfdir-1');
306 if self.fDirectIo:
307 tupArgs += ('--use-cache', 'off');
308 if not self.fReportIoStats:
309 tupArgs += ('--report-io-stats', 'off');
310 fRc, sOutput, sError = self.oExecutor.execBinary(self._locateGstIoPerf(), tupArgs, cMsTimeout = cMsTimeout);
311 if fRc:
312 self.sResult = sOutput;
313 else:
314 if sError is None:
315 sError = '';
316 if sOutput is None:
317 sOutput = '';
318 self.sError = ('Binary: IoPerf\n' +
319 '\nOutput:\n\n' +
320 sOutput +
321 '\nError:\n\n' +
322 sError);
323 return fRc;
324
325 def cleanup(self):
326 """ Cleans up any leftovers from the testcase. """
327 return True;
328
329 def reportResult(self):
330 """
331 Reports the test results to the test manager.
332 """
333 # Should be done using the test pipe already.
334 return True;
335
336 def getErrorReport(self):
337 """
338 Returns the error report in case the testcase failed.
339 """
340 return self.sError;
341
342class StorTestCfgMgr(object):
343 """
344 Manages the different testcases.
345 """
346
347 def __init__(self, aasTestLvls, aasTestsBlacklist, fnIsCfgSupported = None):
348 self.aasTestsBlacklist = aasTestsBlacklist;
349 self.at4TestLvls = [];
350 self.iTestLvl = 0;
351 self.fnIsCfgSupported = fnIsCfgSupported;
352 for asTestLvl in aasTestLvls:
353 if isinstance(asTestLvl, tuple):
354 asTestLvl, fSubTestStartAuto, fnTestFmt = asTestLvl;
355 self.at4TestLvls.append((0, fSubTestStartAuto, fnTestFmt, asTestLvl));
356 else:
357 self.at4TestLvls.append((0, True, None, asTestLvl));
358
359 self.at4TestLvls.reverse();
360
361 # Get the first non blacklisted test.
362 asTestCfg = self.getCurrentTestCfg();
363 while asTestCfg and self.isTestCfgBlacklisted(asTestCfg):
364 asTestCfg = self.advanceTestCfg();
365
366 iLvl = 0;
367 for sCfg in asTestCfg:
368 sSubTest = self.getTestIdString(sCfg, iLvl);
369 if sSubTest is not None:
370 reporter.testStart('%s' % (sSubTest,));
371 iLvl += 1;
372
373 def __del__(self):
374 # Make sure the tests are marked as done.
375 while self.iTestLvl < len(self.at4TestLvls):
376 reporter.testDone();
377 self.iTestLvl += 1;
378
379 def getTestIdString(self, oCfg, iLvl):
380 """
381 Returns a potentially formatted string for the test name.
382 """
383
384 # The order of the test levels is reversed so get the level starting
385 # from the end.
386 _, fSubTestStartAuto, fnTestFmt, _ = self.at4TestLvls[len(self.at4TestLvls) - 1 - iLvl];
387 if not fSubTestStartAuto:
388 return None;
389 if fnTestFmt is not None:
390 return fnTestFmt(oCfg);
391 return oCfg;
392
393 def isTestCfgBlacklisted(self, asTestCfg):
394 """
395 Returns whether the given test config is black listed.
396 """
397 fBlacklisted = False;
398
399 for asTestBlacklist in self.aasTestsBlacklist:
400 iLvl = 0;
401 fBlacklisted = True;
402 while iLvl < len(asTestBlacklist) and iLvl < len(asTestCfg):
403 if asTestBlacklist[iLvl] != asTestCfg[iLvl] and asTestBlacklist[iLvl] != '*':
404 fBlacklisted = False;
405 break;
406
407 iLvl += 1;
408
409 if not fBlacklisted and self.fnIsCfgSupported is not None:
410 fBlacklisted = not self.fnIsCfgSupported(asTestCfg);
411
412 return fBlacklisted;
413
414 def advanceTestCfg(self):
415 """
416 Advances to the next test config and returns it as an
417 array of strings or an empty config if there is no test left anymore.
418 """
419 iTestCfg, fSubTestStartAuto, fnTestFmt, asTestCfg = self.at4TestLvls[self.iTestLvl];
420 iTestCfg += 1;
421 self.at4TestLvls[self.iTestLvl] = (iTestCfg, fSubTestStartAuto, fnTestFmt, asTestCfg);
422 while iTestCfg == len(asTestCfg) and self.iTestLvl < len(self.at4TestLvls):
423 self.at4TestLvls[self.iTestLvl] = (0, fSubTestStartAuto, fnTestFmt, asTestCfg);
424 self.iTestLvl += 1;
425 if self.iTestLvl < len(self.at4TestLvls):
426 iTestCfg, fSubTestStartAuto, fnTestFmt, asTestCfg = self.at4TestLvls[self.iTestLvl];
427 iTestCfg += 1;
428 self.at4TestLvls[self.iTestLvl] = (iTestCfg, fSubTestStartAuto, fnTestFmt, asTestCfg);
429 if iTestCfg < len(asTestCfg):
430 self.iTestLvl = 0;
431 break;
432 else:
433 break; # We reached the end of our tests.
434
435 return self.getCurrentTestCfg();
436
437 def getCurrentTestCfg(self):
438 """
439 Returns the current not black listed test config as an array of strings.
440 """
441 asTestCfg = [];
442
443 if self.iTestLvl < len(self.at4TestLvls):
444 for t4TestLvl in self.at4TestLvls:
445 iTestCfg, _, _, asTestLvl = t4TestLvl;
446 asTestCfg.append(asTestLvl[iTestCfg]);
447
448 asTestCfg.reverse()
449
450 return asTestCfg;
451
452 def getNextTestCfg(self):
453 """
454 Returns the next not blacklisted test config or an empty list if
455 there is no test left.
456 """
457 asTestCfgCur = self.getCurrentTestCfg();
458
459 asTestCfg = self.advanceTestCfg();
460 while asTestCfg and self.isTestCfgBlacklisted(asTestCfg):
461 asTestCfg = self.advanceTestCfg();
462
463 # Compare the current and next config and close the approriate test
464 # categories.
465 #reporter.testDone(fSkippedLast);
466 if asTestCfg:
467 idxSame = 0;
468 while asTestCfgCur[idxSame] == asTestCfg[idxSame]:
469 idxSame += 1;
470
471 for i in range(idxSame, len(asTestCfg) - 1):
472 reporter.testDone();
473
474 for i in range(idxSame, len(asTestCfg)):
475 sSubTest = self.getTestIdString(asTestCfg[i], i);
476 if sSubTest is not None:
477 reporter.testStart('%s' % (sSubTest,));
478
479 else:
480 # No more tests, mark all tests as done
481 for i in range(0, len(asTestCfgCur) - 1):
482 reporter.testDone();
483
484 return asTestCfg;
485
486class tdStorageBenchmark(vbox.TestDriver): # pylint: disable=too-many-instance-attributes
487 """
488 Storage benchmark.
489 """
490
491 # Global storage configs for the testbox
492 kdStorageCfgs = {
493 # Testbox configs (Flag whether to test raw mode on the testbox, disk configuration)
494 'testboxstor1.de.oracle.com': (True, storagecfg.DiskCfg('solaris', storagecfg.g_ksDiskCfgRegExp, r'c[3-9]t\dd0\Z')),
495 # Windows testbox doesn't return testboxstor2.de.oracle.com from socket.getfqdn()
496 'testboxstor2': (False, storagecfg.DiskCfg('win', storagecfg.g_ksDiskCfgStatic, 'D:/StorageTest')),
497
498 # Local test configs for the testcase developer
499 'adaris': (True, storagecfg.DiskCfg('linux', storagecfg.g_ksDiskCfgStatic, \
500 '/home/alexander/StorageScratch')),
501 'daedalus': (True, storagecfg.DiskCfg('darwin', storagecfg.g_ksDiskCfgStatic, \
502 '/Volumes/VirtualBox/Testsuite/StorageScratch')),
503 'windows10': (True, storagecfg.DiskCfg('win', storagecfg.g_ksDiskCfgStatic, \
504 'L:\\Testsuite\\StorageTest')),
505 };
506
507 # Available test sets.
508 kdTestSets = {
509 # Mostly for developing and debugging the testcase.
510 'Fast': {
511 'RecordSize': 65536,
512 'TestsetSize': 104857600, # 100 MiB
513 'QueueDepth': 32,
514 'DiskSizeGb': 2
515 },
516 # For quick functionality tests where benchmark results are not required.
517 'Functionality': {
518 'RecordSize': 65536,
519 'TestsetSize': 2147483648, # 2 GiB
520 'QueueDepth': 32,
521 'DiskSizeGb': 10
522 },
523 # For benchmarking the I/O stack.
524 'Benchmark': {
525 'RecordSize': 65536,
526 'TestsetSize': 21474836480, # 20 Gib
527 'QueueDepth': 32,
528 'DiskSizeGb': 30
529 },
530 # For stress testing which takes a lot of time.
531 'Stress': {
532 'RecordSize': 65536,
533 'TestsetSize': 2199023255552, # 2 TiB
534 'QueueDepth': 32,
535 'DiskSizeGb': 10000
536 },
537 };
538
539 # Dictionary mapping the virtualization mode mnemonics to a little less cryptic
540 # strings used in test descriptions.
541 kdVirtModeDescs = {
542 'raw' : 'Raw-mode',
543 'hwvirt' : 'HwVirt',
544 'hwvirt-np' : 'NestedPaging'
545 };
546
547 kdHostIoCacheDescs = {
548 'default' : 'HostCacheDef',
549 'hostiocache' : 'HostCacheOn',
550 'no-hostiocache' : 'HostCacheOff'
551 };
552
553 # Password ID for encryption.
554 ksPwId = 'EncPwId';
555
556 # Array indexes for the test configs.
557 kiVmName = 0;
558 kiStorageCtrl = 1;
559 kiHostIoCache = 2;
560 kiDiskFmt = 3;
561 kiDiskVar = 4;
562 kiCpuCount = 5;
563 kiVirtMode = 6;
564 kiTestSet = 7;
565 kiIoTest = 8;
566
567 def __init__(self):
568 vbox.TestDriver.__init__(self);
569 self.asRsrcs = None;
570 self.asTestVMsDef = ['tst-storage', 'tst-storage32'];
571 self.asTestVMs = self.asTestVMsDef;
572 self.asSkipVMs = [];
573 self.asVirtModesDef = ['hwvirt', 'hwvirt-np', 'raw',]
574 self.asVirtModes = self.asVirtModesDef;
575 self.acCpusDef = [1, 2];
576 self.acCpus = self.acCpusDef;
577 self.asStorageCtrlsDef = ['AHCI', 'IDE', 'LsiLogicSAS', 'LsiLogic', 'BusLogic', 'NVMe', 'VirtIoScsi'];
578 self.asStorageCtrls = self.asStorageCtrlsDef;
579 self.asHostIoCacheDef = ['default', 'hostiocache', 'no-hostiocache'];
580 self.asHostIoCache = self.asHostIoCacheDef;
581 self.asDiskFormatsDef = ['VDI', 'VMDK', 'VHD', 'QED', 'Parallels', 'QCOW', 'iSCSI'];
582 self.asDiskFormats = self.asDiskFormatsDef;
583 self.asDiskVariantsDef = ['Dynamic', 'Fixed', 'DynamicSplit2G', 'FixedSplit2G', 'Network'];
584 self.asDiskVariants = self.asDiskVariantsDef;
585 self.asTestsDef = ['ioperf'];
586 self.asTests = self.asTestsDef;
587 self.asTestSetsDef = ['Fast', 'Functionality', 'Benchmark', 'Stress'];
588 self.asTestSets = self.asTestSetsDef;
589 self.asIscsiTargetsDef = [ ]; # @todo: Configure one target for basic iSCSI testing
590 self.asIscsiTargets = self.asIscsiTargetsDef;
591 self.cDiffLvlsDef = 0;
592 self.cDiffLvls = self.cDiffLvlsDef;
593 self.fTestHost = False;
594 self.fUseScratch = False;
595 self.fRecreateStorCfg = True;
596 self.fReportBenchmarkResults = True;
597 self.fTestRawMode = False;
598 self.oStorCfg = None;
599 self.sIoLogPathDef = self.sScratchPath;
600 self.sIoLogPath = self.sIoLogPathDef;
601 self.fIoLog = False;
602 self.fUseRamDiskDef = False;
603 self.fUseRamDisk = self.fUseRamDiskDef;
604 self.fEncryptDiskDef = False;
605 self.fEncryptDisk = self.fEncryptDiskDef;
606 self.sEncryptPwDef = 'TestTestTest';
607 self.sEncryptPw = self.sEncryptPwDef;
608 self.sEncryptAlgoDef = 'AES-XTS256-PLAIN64';
609 self.sEncryptAlgo = self.sEncryptAlgoDef;
610
611 #
612 # Overridden methods.
613 #
614 def showUsage(self):
615 rc = vbox.TestDriver.showUsage(self);
616 reporter.log('');
617 reporter.log('tdStorageBenchmark1 Options:');
618 reporter.log(' --virt-modes <m1[:m2[:]]');
619 reporter.log(' Default: %s' % (':'.join(self.asVirtModesDef)));
620 reporter.log(' --cpu-counts <c1[:c2[:]]');
621 reporter.log(' Default: %s' % (':'.join(str(c) for c in self.acCpusDef)));
622 reporter.log(' --storage-ctrls <type1[:type2[:...]]>');
623 reporter.log(' Default: %s' % (':'.join(self.asStorageCtrlsDef)));
624 reporter.log(' --host-io-cache <setting1[:setting2[:...]]>');
625 reporter.log(' Default: %s' % (':'.join(self.asHostIoCacheDef)));
626 reporter.log(' --disk-formats <type1[:type2[:...]]>');
627 reporter.log(' Default: %s' % (':'.join(self.asDiskFormatsDef)));
628 reporter.log(' --disk-variants <variant1[:variant2[:...]]>');
629 reporter.log(' Default: %s' % (':'.join(self.asDiskVariantsDef)));
630 reporter.log(' --iscsi-targets <target1[:target2[:...]]>');
631 reporter.log(' Default: %s' % (':'.join(self.asIscsiTargetsDef)));
632 reporter.log(' --tests <test1[:test2[:...]]>');
633 reporter.log(' Default: %s' % (':'.join(self.asTestsDef)));
634 reporter.log(' --test-sets <set1[:set2[:...]]>');
635 reporter.log(' Default: %s' % (':'.join(self.asTestSetsDef)));
636 reporter.log(' --diff-levels <number of diffs>');
637 reporter.log(' Default: %s' % (self.cDiffLvlsDef));
638 reporter.log(' --test-vms <vm1[:vm2[:...]]>');
639 reporter.log(' Test the specified VMs in the given order. Use this to change');
640 reporter.log(' the execution order or limit the choice of VMs');
641 reporter.log(' Default: %s (all)' % (':'.join(self.asTestVMsDef)));
642 reporter.log(' --skip-vms <vm1[:vm2[:...]]>');
643 reporter.log(' Skip the specified VMs when testing.');
644 reporter.log(' --test-host');
645 reporter.log(' Do all configured tests on the host first and report the results');
646 reporter.log(' to get a baseline');
647 reporter.log(' --use-scratch');
648 reporter.log(' Use the scratch directory for testing instead of setting up');
649 reporter.log(' fresh volumes on dedicated disks (for development)');
650 reporter.log(' --always-wipe-storage-cfg');
651 reporter.log(' Recreate the host storage config before each test');
652 reporter.log(' --dont-wipe-storage-cfg');
653 reporter.log(' Don\'t recreate the host storage config before each test');
654 reporter.log(' --report-benchmark-results');
655 reporter.log(' Report all benchmark results');
656 reporter.log(' --dont-report-benchmark-results');
657 reporter.log(' Don\'t report any benchmark results');
658 reporter.log(' --io-log-path <path>');
659 reporter.log(' Default: %s' % (self.sIoLogPathDef));
660 reporter.log(' --enable-io-log');
661 reporter.log(' Whether to enable I/O logging for each test');
662 reporter.log(' --use-ramdisk');
663 reporter.log(' Default: %s' % (self.fUseRamDiskDef));
664 reporter.log(' --encrypt-disk');
665 reporter.log(' Default: %s' % (self.fEncryptDiskDef));
666 reporter.log(' --encrypt-password');
667 reporter.log(' Default: %s' % (self.sEncryptPwDef));
668 reporter.log(' --encrypt-algorithm');
669 reporter.log(' Default: %s' % (self.sEncryptAlgoDef));
670 return rc;
671
672 def parseOption(self, asArgs, iArg): # pylint: disable=too-many-branches,too-many-statements
673 if asArgs[iArg] == '--virt-modes':
674 iArg += 1;
675 if iArg >= len(asArgs): raise base.InvalidOption('The "--virt-modes" takes a colon separated list of modes');
676 self.asVirtModes = asArgs[iArg].split(':');
677 for s in self.asVirtModes:
678 if s not in self.asVirtModesDef:
679 raise base.InvalidOption('The "--virt-modes" value "%s" is not valid; valid values are: %s' \
680 % (s, ' '.join(self.asVirtModesDef)));
681 elif asArgs[iArg] == '--cpu-counts':
682 iArg += 1;
683 if iArg >= len(asArgs): raise base.InvalidOption('The "--cpu-counts" takes a colon separated list of cpu counts');
684 self.acCpus = [];
685 for s in asArgs[iArg].split(':'):
686 try: c = int(s);
687 except: raise base.InvalidOption('The "--cpu-counts" value "%s" is not an integer' % (s,));
688 if c <= 0: raise base.InvalidOption('The "--cpu-counts" value "%s" is zero or negative' % (s,));
689 self.acCpus.append(c);
690 elif asArgs[iArg] == '--storage-ctrls':
691 iArg += 1;
692 if iArg >= len(asArgs):
693 raise base.InvalidOption('The "--storage-ctrls" takes a colon separated list of Storage controller types');
694 self.asStorageCtrls = asArgs[iArg].split(':');
695 elif asArgs[iArg] == '--host-io-cache':
696 iArg += 1;
697 if iArg >= len(asArgs):
698 raise base.InvalidOption('The "--host-io-cache" takes a colon separated list of I/O cache settings');
699 self.asHostIoCache = asArgs[iArg].split(':');
700 elif asArgs[iArg] == '--disk-formats':
701 iArg += 1;
702 if iArg >= len(asArgs): raise base.InvalidOption('The "--disk-formats" takes a colon separated list of disk formats');
703 self.asDiskFormats = asArgs[iArg].split(':');
704 elif asArgs[iArg] == '--disk-variants':
705 iArg += 1;
706 if iArg >= len(asArgs):
707 raise base.InvalidOption('The "--disk-variants" takes a colon separated list of disk variants');
708 self.asDiskVariants = asArgs[iArg].split(':');
709 elif asArgs[iArg] == '--iscsi-targets':
710 iArg += 1;
711 if iArg >= len(asArgs):
712 raise base.InvalidOption('The "--iscsi-targets" takes a colon separated list of iscsi targets');
713 self.asIscsiTargets = asArgs[iArg].split(':');
714 elif asArgs[iArg] == '--tests':
715 iArg += 1;
716 if iArg >= len(asArgs): raise base.InvalidOption('The "--tests" takes a colon separated list of tests to run');
717 self.asTests = asArgs[iArg].split(':');
718 elif asArgs[iArg] == '--test-sets':
719 iArg += 1;
720 if iArg >= len(asArgs): raise base.InvalidOption('The "--test-sets" takes a colon separated list of test sets');
721 self.asTestSets = asArgs[iArg].split(':');
722 elif asArgs[iArg] == '--diff-levels':
723 iArg += 1;
724 if iArg >= len(asArgs): raise base.InvalidOption('The "--diff-levels" takes an integer');
725 try: self.cDiffLvls = int(asArgs[iArg]);
726 except: raise base.InvalidOption('The "--diff-levels" value "%s" is not an integer' % (asArgs[iArg],));
727 elif asArgs[iArg] == '--test-vms':
728 iArg += 1;
729 if iArg >= len(asArgs): raise base.InvalidOption('The "--test-vms" takes colon separated list');
730 self.asTestVMs = asArgs[iArg].split(':');
731 for s in self.asTestVMs:
732 if s not in self.asTestVMsDef:
733 raise base.InvalidOption('The "--test-vms" value "%s" is not valid; valid values are: %s' \
734 % (s, ' '.join(self.asTestVMsDef)));
735 elif asArgs[iArg] == '--skip-vms':
736 iArg += 1;
737 if iArg >= len(asArgs): raise base.InvalidOption('The "--skip-vms" takes colon separated list');
738 self.asSkipVMs = asArgs[iArg].split(':');
739 for s in self.asSkipVMs:
740 if s not in self.asTestVMsDef:
741 reporter.log('warning: The "--test-vms" value "%s" does not specify any of our test VMs.' % (s));
742 elif asArgs[iArg] == '--test-host':
743 self.fTestHost = True;
744 elif asArgs[iArg] == '--use-scratch':
745 self.fUseScratch = True;
746 elif asArgs[iArg] == '--always-wipe-storage-cfg':
747 self.fRecreateStorCfg = True;
748 elif asArgs[iArg] == '--dont-wipe-storage-cfg':
749 self.fRecreateStorCfg = False;
750 elif asArgs[iArg] == '--report-benchmark-results':
751 self.fReportBenchmarkResults = True;
752 elif asArgs[iArg] == '--dont-report-benchmark-results':
753 self.fReportBenchmarkResults = False;
754 elif asArgs[iArg] == '--io-log-path':
755 iArg += 1;
756 if iArg >= len(asArgs): raise base.InvalidOption('The "--io-log-path" takes a path argument');
757 self.sIoLogPath = asArgs[iArg];
758 elif asArgs[iArg] == '--enable-io-log':
759 self.fIoLog = True;
760 elif asArgs[iArg] == '--use-ramdisk':
761 self.fUseRamDisk = True;
762 elif asArgs[iArg] == '--encrypt-disk':
763 self.fEncryptDisk = True;
764 elif asArgs[iArg] == '--encrypt-password':
765 iArg += 1;
766 if iArg >= len(asArgs): raise base.InvalidOption('The "--encrypt-password" takes a string');
767 self.sEncryptPw = asArgs[iArg];
768 elif asArgs[iArg] == '--encrypt-algorithm':
769 iArg += 1;
770 if iArg >= len(asArgs): raise base.InvalidOption('The "--encrypt-algorithm" takes a string');
771 self.sEncryptAlgo = asArgs[iArg];
772 else:
773 return vbox.TestDriver.parseOption(self, asArgs, iArg);
774 return iArg + 1;
775
776 def completeOptions(self):
777 # Remove skipped VMs from the test list.
778 for sVM in self.asSkipVMs:
779 try: self.asTestVMs.remove(sVM);
780 except: pass;
781
782 return vbox.TestDriver.completeOptions(self);
783
784 def getResourceSet(self):
785 # Construct the resource list the first time it's queried.
786 if self.asRsrcs is None:
787 self.asRsrcs = [];
788 if 'tst-storage' in self.asTestVMs:
789 self.asRsrcs.append('5.0/storage/tst-storage.vdi');
790 if 'tst-storage32' in self.asTestVMs:
791 self.asRsrcs.append('5.0/storage/tst-storage32.vdi');
792
793 return self.asRsrcs;
794
795 def actionConfig(self):
796
797 # Make sure vboxapi has been imported so we can use the constants.
798 if not self.importVBoxApi():
799 return False;
800
801 #
802 # Configure the VMs we're going to use.
803 #
804
805 # Linux VMs
806 if 'tst-storage' in self.asTestVMs:
807 oVM = self.createTestVM('tst-storage', 1, '5.0/storage/tst-storage.vdi', sKind = 'ArchLinux_64', fIoApic = True, \
808 eNic0AttachType = vboxcon.NetworkAttachmentType_NAT, \
809 eNic0Type = vboxcon.NetworkAdapterType_Am79C973, \
810 sDvdImage = self.sVBoxValidationKitIso);
811 if oVM is None:
812 return False;
813
814 if 'tst-storage32' in self.asTestVMs:
815 oVM = self.createTestVM('tst-storage32', 1, '5.0/storage/tst-storage32.vdi', sKind = 'ArchLinux', fIoApic = True, \
816 eNic0AttachType = vboxcon.NetworkAttachmentType_NAT, \
817 eNic0Type = vboxcon.NetworkAdapterType_Am79C973, \
818 sDvdImage = self.sVBoxValidationKitIso);
819 if oVM is None:
820 return False;
821
822 return True;
823
824 def actionExecute(self):
825 """
826 Execute the testcase.
827 """
828 fRc = self.test1();
829 return fRc;
830
831
832 #
833 # Test execution helpers.
834 #
835
836 def prepareStorage(self, oStorCfg, fRamDisk = False, cbPool = None):
837 """
838 Prepares the host storage for disk images or direct testing on the host.
839
840 Will return the created mount point path on success, or None on failure.
841 """
842 # Create a basic pool with the default configuration.
843 sMountPoint = None;
844 fRc, sPoolId = oStorCfg.createStoragePool(cbPool = cbPool, fRamDisk = fRamDisk);
845 if fRc:
846 fRc, sMountPoint = oStorCfg.createVolume(sPoolId);
847 if not fRc:
848 sMountPoint = None;
849 oStorCfg.cleanup();
850
851 if not fRc:
852 if cbPool is None:
853 reporter.error('Failed to prepare host storage (fRamDisk=%s)' % (fRamDisk,));
854 else:
855 reporter.error('Failed to prepare host storage (fRamDisk=%s, cbPool=%d)' % (fRamDisk, cbPool,));
856 return sMountPoint;
857
858 def cleanupStorage(self, oStorCfg):
859 """
860 Cleans up any created storage space for a test.
861 """
862 return oStorCfg.cleanup();
863
864 def getGuestDisk(self, oSession, oTxsSession, eStorageController):
865 """
866 Gets the path of the disk in the guest to use for testing.
867 """
868 lstDisks = None;
869
870 # The naming scheme for NVMe is different and we don't have
871 # to query the guest for unformatted disks here because the disk with the OS
872 # is not attached to a NVMe controller.
873 if eStorageController == vboxcon.StorageControllerType_NVMe:
874 lstDisks = [ '/dev/nvme0n1' ];
875 else:
876 # Find a unformatted disk (no partition).
877 # @todo: This is a hack because LIST and STAT are not yet implemented
878 # in TXS (get to this eventually)
879 lstBlkDev = [ '/dev/sda', '/dev/sdb' ];
880 for sBlkDev in lstBlkDev:
881 fRc = oTxsSession.syncExec('/usr/bin/ls', ('ls', sBlkDev + '1'));
882 if not fRc:
883 lstDisks = [ sBlkDev ];
884 break;
885
886 _ = oSession;
887 return lstDisks;
888
889 def mountValidationKitIso(self, oVmExec):
890 """
891 Hack to get the vlaidation kit ISO mounted in the guest as it was left out
892 originally and I don't feel like respinning the disk image.
893 """
894 fRc = oVmExec.mkDir('/media');
895 if fRc:
896 fRc = oVmExec.mkDir('/media/cdrom');
897 if fRc:
898 fRc = oVmExec.execBinaryNoStdOut('mount', ('/dev/sr0', '/media/cdrom'));
899
900 return fRc;
901
902 def getDiskFormatVariantsForTesting(self, sDiskFmt, asVariants):
903 """
904 Returns a list of disk variants for testing supported by the given
905 disk format and selected for testing.
906 """
907 lstDskFmts = self.oVBoxMgr.getArray(self.oVBox.systemProperties, 'mediumFormats');
908 for oDskFmt in lstDskFmts:
909 if oDskFmt.id == sDiskFmt:
910 lstDskVariants = [];
911 lstCaps = self.oVBoxMgr.getArray(oDskFmt, 'capabilities');
912
913 if vboxcon.MediumFormatCapabilities_CreateDynamic in lstCaps \
914 and 'Dynamic' in asVariants:
915 lstDskVariants.append('Dynamic');
916
917 if vboxcon.MediumFormatCapabilities_CreateFixed in lstCaps \
918 and 'Fixed' in asVariants:
919 lstDskVariants.append('Fixed');
920
921 if vboxcon.MediumFormatCapabilities_CreateSplit2G in lstCaps \
922 and vboxcon.MediumFormatCapabilities_CreateDynamic in lstCaps \
923 and 'DynamicSplit2G' in asVariants:
924 lstDskVariants.append('DynamicSplit2G');
925
926 if vboxcon.MediumFormatCapabilities_CreateSplit2G in lstCaps \
927 and vboxcon.MediumFormatCapabilities_CreateFixed in lstCaps \
928 and 'FixedSplit2G' in asVariants:
929 lstDskVariants.append('FixedSplit2G');
930
931 if vboxcon.MediumFormatCapabilities_TcpNetworking in lstCaps \
932 and 'Network' in asVariants:
933 lstDskVariants.append('Network'); # Solely for iSCSI to get a non empty list
934
935 return lstDskVariants;
936
937 return [];
938
939 def convDiskToMediumVariant(self, sDiskVariant):
940 """
941 Returns a tuple of medium variant flags matching the given disk variant.
942 """
943 tMediumVariant = None;
944 if sDiskVariant == 'Dynamic':
945 tMediumVariant = (vboxcon.MediumVariant_Standard, );
946 elif sDiskVariant == 'Fixed':
947 tMediumVariant = (vboxcon.MediumVariant_Fixed, );
948 elif sDiskVariant == 'DynamicSplit2G':
949 tMediumVariant = (vboxcon.MediumVariant_Standard, vboxcon.MediumVariant_VmdkSplit2G);
950 elif sDiskVariant == 'FixedSplit2G':
951 tMediumVariant = (vboxcon.MediumVariant_Fixed, vboxcon.MediumVariant_VmdkSplit2G);
952
953 return tMediumVariant;
954
955 def getStorageCtrlFromName(self, sStorageCtrl):
956 """
957 Resolves the storage controller string to the matching constant.
958 """
959 eStorageCtrl = None;
960
961 if sStorageCtrl == 'AHCI':
962 eStorageCtrl = vboxcon.StorageControllerType_IntelAhci;
963 elif sStorageCtrl == 'IDE':
964 eStorageCtrl = vboxcon.StorageControllerType_PIIX4;
965 elif sStorageCtrl == 'LsiLogicSAS':
966 eStorageCtrl = vboxcon.StorageControllerType_LsiLogicSas;
967 elif sStorageCtrl == 'LsiLogic':
968 eStorageCtrl = vboxcon.StorageControllerType_LsiLogic;
969 elif sStorageCtrl == 'BusLogic':
970 eStorageCtrl = vboxcon.StorageControllerType_BusLogic;
971 elif sStorageCtrl == 'NVMe':
972 eStorageCtrl = vboxcon.StorageControllerType_NVMe;
973 elif sStorageCtrl == 'VirtIoScsi':
974 eStorageCtrl = vboxcon.StorageControllerType_VirtioSCSI;
975
976 return eStorageCtrl;
977
978 def getStorageDriverFromEnum(self, eStorageCtrl, fHardDisk):
979 """
980 Returns the appropriate driver name for the given storage controller
981 and a flag whether the driver has the generic SCSI driver attached.
982 """
983 if eStorageCtrl == vboxcon.StorageControllerType_IntelAhci:
984 if fHardDisk:
985 return ('ahci', False);
986 return ('ahci', True);
987 if eStorageCtrl == vboxcon.StorageControllerType_PIIX4:
988 return ('piix3ide', False);
989 if eStorageCtrl == vboxcon.StorageControllerType_LsiLogicSas:
990 return ('lsilogicsas', True);
991 if eStorageCtrl == vboxcon.StorageControllerType_LsiLogic:
992 return ('lsilogicscsi', True);
993 if eStorageCtrl == vboxcon.StorageControllerType_BusLogic:
994 return ('buslogic', True);
995 if eStorageCtrl == vboxcon.StorageControllerType_NVMe:
996 return ('nvme', False);
997 if eStorageCtrl == vboxcon.StorageControllerType_VirtioSCSI:
998 return ('virtio-scsi', True);
999
1000 return ('<invalid>', False);
1001
1002 def isTestCfgSupported(self, asTestCfg):
1003 """
1004 Returns whether a specific test config is supported.
1005 """
1006
1007 # Check whether the disk variant is supported by the selected format.
1008 asVariants = self.getDiskFormatVariantsForTesting(asTestCfg[self.kiDiskFmt], [ asTestCfg[self.kiDiskVar] ]);
1009 if not asVariants:
1010 return False;
1011
1012 # For iSCSI check whether we have targets configured.
1013 if asTestCfg[self.kiDiskFmt] == 'iSCSI' and not self.asIscsiTargets:
1014 return False;
1015
1016 # Check for virt mode, CPU count and selected VM.
1017 if asTestCfg[self.kiVirtMode] == 'raw' \
1018 and ( asTestCfg[self.kiCpuCount] > 1 \
1019 or asTestCfg[self.kiVmName] == 'tst-storage' \
1020 or not self.fTestRawMode):
1021 return False;
1022
1023 # IDE does not support the no host I/O cache setting
1024 if asTestCfg[self.kiHostIoCache] == 'no-hostiocache' \
1025 and asTestCfg[self.kiStorageCtrl] == 'IDE':
1026 return False;
1027
1028 return True;
1029
1030 def fnFormatCpuString(self, cCpus):
1031 """
1032 Formats the CPU count to be readable.
1033 """
1034 if cCpus == 1:
1035 return '1 cpu';
1036 return '%u cpus' % (cCpus);
1037
1038 def fnFormatVirtMode(self, sVirtMode):
1039 """
1040 Formats the virtualization mode to be a little less cryptic for use in test
1041 descriptions.
1042 """
1043 return self.kdVirtModeDescs[sVirtMode];
1044
1045 def fnFormatHostIoCache(self, sHostIoCache):
1046 """
1047 Formats the host I/O cache mode to be a little less cryptic for use in test
1048 descriptions.
1049 """
1050 return self.kdHostIoCacheDescs[sHostIoCache];
1051
1052 def testBenchmark(self, sTargetOs, sBenchmark, sMountpoint, oExecutor, dTestSet, \
1053 cMsTimeout = 3600000):
1054 """
1055 Runs the given benchmark on the test host.
1056 """
1057
1058 dTestSet['FilePath'] = sMountpoint;
1059 dTestSet['TargetOs'] = sTargetOs;
1060 dTestSet['ReportIoStats'] = self.fReportBenchmarkResults;
1061
1062 oTst = None;
1063 if sBenchmark == 'iozone':
1064 oTst = IozoneTest(oExecutor, dTestSet);
1065 elif sBenchmark == 'fio':
1066 oTst = FioTest(oExecutor, dTestSet); # pylint: disable=redefined-variable-type
1067 elif sBenchmark == 'ioperf':
1068 oTst = IoPerfTest(oExecutor, dTestSet); # pylint: disable=redefined-variable-type
1069
1070 if oTst is not None:
1071 fRc = oTst.prepare();
1072 if fRc:
1073 fRc = oTst.run(cMsTimeout);
1074 if fRc:
1075 if self.fReportBenchmarkResults:
1076 fRc = oTst.reportResult();
1077 else:
1078 reporter.testFailure('Running the testcase failed');
1079 reporter.addLogString(oTst.getErrorReport(), sBenchmark + '.log',
1080 'log/release/client', 'Benchmark raw output');
1081 else:
1082 reporter.testFailure('Preparing the testcase failed');
1083
1084 oTst.cleanup();
1085
1086 return fRc;
1087
1088 def createHd(self, oSession, sDiskFormat, sDiskVariant, iDiffLvl, oHdParent, \
1089 sDiskPath, cbDisk):
1090 """
1091 Creates a new disk with the given parameters returning the medium object
1092 on success.
1093 """
1094
1095 oHd = None;
1096 if sDiskFormat == "iSCSI" and iDiffLvl == 0:
1097 listNames = [];
1098 listValues = [];
1099 listValues = self.asIscsiTargets[0].split('|');
1100 listNames.append('TargetAddress');
1101 listNames.append('TargetName');
1102 listNames.append('LUN');
1103
1104 if self.fpApiVer >= 5.0:
1105 oHd = oSession.oVBox.createMedium(sDiskFormat, sDiskPath, vboxcon.AccessMode_ReadWrite, \
1106 vboxcon.DeviceType_HardDisk);
1107 else:
1108 oHd = oSession.oVBox.createHardDisk(sDiskFormat, sDiskPath);
1109 oHd.type = vboxcon.MediumType_Normal;
1110 oHd.setProperties(listNames, listValues);
1111 else:
1112 if iDiffLvl == 0:
1113 tMediumVariant = self.convDiskToMediumVariant(sDiskVariant);
1114 oHd = oSession.createBaseHd(sDiskPath + '/base.img', sDiskFormat, cbDisk, \
1115 cMsTimeout = 3600 * 1000, tMediumVariant = tMediumVariant);
1116 else:
1117 sDiskPath = sDiskPath + '/diff_%u.img' % (iDiffLvl);
1118 oHd = oSession.createDiffHd(oHdParent, sDiskPath, None);
1119
1120 if oHd is not None and iDiffLvl == 0 and self.fEncryptDisk:
1121 try:
1122 oIProgress = oHd.changeEncryption('', self.sEncryptAlgo, self.sEncryptPw, self.ksPwId);
1123 oProgress = vboxwrappers.ProgressWrapper(oIProgress, self.oVBoxMgr, self, 'Encrypting "%s"' % (sDiskPath,));
1124 oProgress.wait(60*60000); # Wait for up to one hour, fixed disks take longer to encrypt.
1125 if oProgress.logResult() is False:
1126 raise base.GenError('Encrypting disk "%s" failed' % (sDiskPath, ));
1127 except:
1128 reporter.errorXcpt('changeEncryption("%s","%s","%s") failed on "%s"' \
1129 % ('', self.sEncryptAlgo, self.sEncryptPw, oSession.sName) );
1130 self.oVBox.deleteHdByMedium(oHd);
1131 oHd = None;
1132 else:
1133 reporter.log('Encrypted "%s"' % (sDiskPath,));
1134
1135 return oHd;
1136
1137 def startVmAndConnect(self, sVmName):
1138 """
1139 Our own implementation of startVmAndConnectToTxsViaTcp to make it possible
1140 to add passwords to a running VM when encryption is used.
1141 """
1142 oSession = self.startVmByName(sVmName);
1143 if oSession is not None:
1144 # Add password to the session in case encryption is used.
1145 fRc = True;
1146 if self.fEncryptDisk:
1147 try:
1148 if self.fpApiVer >= 7.0:
1149 oSession.o.console.addEncryptionPassword(self.ksPwId, self.sEncryptPw, False);
1150 else:
1151 oSession.o.console.addDiskEncryptionPassword(self.ksPwId, self.sEncryptPw, False);
1152 except:
1153 reporter.logXcpt();
1154 fRc = False;
1155
1156 # Connect to TXS.
1157 if fRc:
1158 reporter.log2('startVmAndConnect: Started(/prepared) "%s", connecting to TXS ...' % (sVmName,));
1159 (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, 15*60000, fNatForwardingForTxs = True);
1160 if fRc is True:
1161 if fRc is True:
1162 # Success!
1163 return (oSession, oTxsSession);
1164 else:
1165 reporter.error('startVmAndConnect: txsDoConnectViaTcp failed');
1166 # If something went wrong while waiting for TXS to be started - take VM screenshot before terminate it
1167
1168 self.terminateVmBySession(oSession);
1169
1170 return (None, None);
1171
1172 def testOneCfg(self, sVmName, eStorageController, sHostIoCache, sDiskFormat, # pylint: disable=too-many-arguments,too-many-locals,too-many-statements
1173 sDiskVariant, sDiskPath, cCpus, sIoTest, sVirtMode, sTestSet):
1174 """
1175 Runs the specified VM thru test #1.
1176
1177 Returns a success indicator on the general test execution. This is not
1178 the actual test result.
1179 """
1180 oVM = self.getVmByName(sVmName);
1181
1182 dTestSet = self.kdTestSets.get(sTestSet);
1183 cbDisk = dTestSet.get('DiskSizeGb') * 1024*1024*1024;
1184 fHwVirt = sVirtMode != 'raw';
1185 fNestedPaging = sVirtMode == 'hwvirt-np';
1186
1187 fRc = True;
1188 if sDiskFormat == 'iSCSI':
1189 sDiskPath = self.asIscsiTargets[0];
1190 elif self.fUseScratch:
1191 sDiskPath = self.sScratchPath;
1192 else:
1193 # If requested recreate the storage space to start with a clean config
1194 # for benchmarks
1195 if self.fRecreateStorCfg:
1196 sMountPoint = self.prepareStorage(self.oStorCfg, self.fUseRamDisk, 2 * cbDisk);
1197 if sMountPoint is not None:
1198 # Create a directory where every normal user can write to.
1199 self.oStorCfg.mkDirOnVolume(sMountPoint, 'test', 0o777);
1200 sDiskPath = sMountPoint + '/test';
1201 else:
1202 fRc = False;
1203 reporter.testFailure('Failed to prepare storage for VM');
1204
1205 if not fRc:
1206 return fRc;
1207
1208 lstDisks = []; # List of disks we have to delete afterwards.
1209
1210 for iDiffLvl in range(self.cDiffLvls + 1):
1211 sIoLogFile = None;
1212
1213 if iDiffLvl == 0:
1214 reporter.testStart('Base');
1215 else:
1216 reporter.testStart('Diff %u' % (iDiffLvl));
1217
1218 # Reconfigure the VM
1219 oSession = self.openSession(oVM);
1220 if oSession is not None:
1221 #
1222 # Disable audio controller which shares the interrupt line with the BusLogic controller and is suspected to cause
1223 # rare test failures because the device initialization fails.
1224 #
1225 fRc = oSession.setupAudio(vboxcon.AudioControllerType_AC97, False);
1226 # Attach HD
1227 fRc = fRc and oSession.ensureControllerAttached(self.controllerTypeToName(eStorageController));
1228 fRc = fRc and oSession.setStorageControllerType(eStorageController,
1229 self.controllerTypeToName(eStorageController));
1230
1231 if sHostIoCache == 'hostiocache':
1232 fRc = fRc and oSession.setStorageControllerHostIoCache(self.controllerTypeToName(eStorageController), True);
1233 elif sHostIoCache == 'no-hostiocache':
1234 fRc = fRc and oSession.setStorageControllerHostIoCache(self.controllerTypeToName(eStorageController), False);
1235
1236 iDevice = 0;
1237 if eStorageController in (vboxcon.StorageControllerType_PIIX3, vboxcon.StorageControllerType_PIIX4,):
1238 iDevice = 1; # Master is for the OS.
1239
1240 oHdParent = None;
1241 if iDiffLvl > 0:
1242 oHdParent = lstDisks[0];
1243 oHd = self.createHd(oSession, sDiskFormat, sDiskVariant, iDiffLvl, oHdParent, sDiskPath, cbDisk);
1244 if oHd is not None:
1245 lstDisks.insert(0, oHd);
1246 try:
1247 if oSession.fpApiVer >= 4.0:
1248 oSession.o.machine.attachDevice(self.controllerTypeToName(eStorageController),
1249 0, iDevice, vboxcon.DeviceType_HardDisk, oHd);
1250 else:
1251 oSession.o.machine.attachDevice(self.controllerTypeToName(eStorageController),
1252 0, iDevice, vboxcon.DeviceType_HardDisk, oHd.id);
1253 except:
1254 reporter.errorXcpt('attachDevice("%s",%s,%s,HardDisk,"%s") failed on "%s"' \
1255 % (self.controllerTypeToName(eStorageController), 1, 0, oHd.id, oSession.sName) );
1256 fRc = False;
1257 else:
1258 reporter.log('attached "%s" to %s' % (sDiskPath, oSession.sName));
1259 else:
1260 fRc = False;
1261
1262 # Set up the I/O logging config if enabled
1263 if fRc and self.fIoLog:
1264 try:
1265 oSession.o.machine.setExtraData('VBoxInternal2/EnableDiskIntegrityDriver', '1');
1266
1267 iLun = 0;
1268 if eStorageController in (vboxcon.StorageControllerType_PIIX3, vboxcon.StorageControllerType_PIIX4,):
1269 iLun = 1
1270 sDrv, fDrvScsi = self.getStorageDriverFromEnum(eStorageController, True);
1271 if fDrvScsi:
1272 sCfgmPath = 'VBoxInternal/Devices/%s/0/LUN#%u/AttachedDriver/Config' % (sDrv, iLun);
1273 else:
1274 sCfgmPath = 'VBoxInternal/Devices/%s/0/LUN#%u/Config' % (sDrv, iLun);
1275
1276 sIoLogFile = '%s/%s.iolog' % (self.sIoLogPath, sDrv);
1277 print(sCfgmPath);
1278 print(sIoLogFile);
1279 oSession.o.machine.setExtraData('%s/IoLog' % (sCfgmPath,), sIoLogFile);
1280 except:
1281 reporter.logXcpt();
1282
1283 fRc = fRc and oSession.enableVirtExX86(fHwVirt);
1284 fRc = fRc and oSession.enableNestedPagingX86(fNestedPaging);
1285 fRc = fRc and oSession.setCpuCount(cCpus);
1286 fRc = fRc and oSession.saveSettings();
1287 fRc = oSession.close() and fRc and True; # pychecker hack.
1288 oSession = None;
1289 else:
1290 fRc = False;
1291
1292 # Start up.
1293 if fRc is True:
1294 self.logVmInfo(oVM);
1295 oSession, oTxsSession = self.startVmAndConnect(sVmName);
1296 if oSession is not None:
1297 self.addTask(oTxsSession);
1298
1299 # Fudge factor - Allow the guest to finish starting up.
1300 self.sleep(5);
1301
1302 # Prepare the storage on the guest
1303 lstBinaryPaths = ['/bin', '/sbin', '/usr/bin', '/usr/sbin' ];
1304 oExecVm = remoteexecutor.RemoteExecutor(oTxsSession, lstBinaryPaths, '${SCRATCH}');
1305 fRc = self.mountValidationKitIso(oExecVm);
1306 if fRc:
1307 oGstDiskCfg = storagecfg.DiskCfg('linux', storagecfg.g_ksDiskCfgList,
1308 self.getGuestDisk(oSession, oTxsSession, eStorageController));
1309 oStorCfgVm = storagecfg.StorageCfg(oExecVm, oGstDiskCfg);
1310
1311 iTry = 0;
1312 while iTry < 3:
1313 sMountPoint = self.prepareStorage(oStorCfgVm);
1314 if sMountPoint is not None:
1315 reporter.log('Prepared storage on %s try' % (iTry + 1,));
1316 break;
1317 iTry = iTry + 1;
1318 self.sleep(5);
1319
1320 if sMountPoint is not None:
1321 # 3 hours max (Benchmark and QED takes a lot of time)
1322 self.testBenchmark('linux', sIoTest, sMountPoint, oExecVm, dTestSet, cMsTimeout = 3 * 3600 * 1000);
1323 self.cleanupStorage(oStorCfgVm);
1324 else:
1325 reporter.testFailure('Failed to prepare storage for the guest benchmark');
1326
1327 # cleanup.
1328 self.removeTask(oTxsSession);
1329 self.terminateVmBySession(oSession);
1330
1331 # Add the I/O log if it exists and the test failed
1332 if reporter.testErrorCount() > 0 \
1333 and sIoLogFile is not None \
1334 and os.path.exists(sIoLogFile):
1335 reporter.addLogFile(sIoLogFile, 'misc/other', 'I/O log');
1336 os.remove(sIoLogFile);
1337 else:
1338 reporter.testFailure('Failed to mount validation kit ISO');
1339
1340 else:
1341 fRc = False;
1342
1343 # Remove disk
1344 oSession = self.openSession(oVM);
1345 if oSession is not None:
1346 try:
1347 oSession.o.machine.detachDevice(self.controllerTypeToName(eStorageController), 0, iDevice);
1348
1349 # Remove storage controller if it is not an IDE controller.
1350 if eStorageController not in (vboxcon.StorageControllerType_PIIX3, vboxcon.StorageControllerType_PIIX4,):
1351 oSession.o.machine.removeStorageController(self.controllerTypeToName(eStorageController));
1352
1353 oSession.saveSettings();
1354 oSession.saveSettings();
1355 oSession.close();
1356 oSession = None;
1357 except:
1358 reporter.errorXcpt('failed to detach/delete disk %s from storage controller' % (sDiskPath));
1359 else:
1360 fRc = False;
1361
1362 reporter.testDone();
1363
1364 # Delete all disks
1365 for oHd in lstDisks:
1366 self.oVBox.deleteHdByMedium(oHd);
1367
1368 # Cleanup storage area
1369 if sDiskFormat != 'iSCSI' and not self.fUseScratch and self.fRecreateStorCfg:
1370 self.cleanupStorage(self.oStorCfg);
1371
1372 return fRc;
1373
1374 def testStorage(self, sDiskPath = None):
1375 """
1376 Runs the storage testcase through the selected configurations
1377 """
1378
1379 aasTestCfgs = [];
1380 aasTestCfgs.insert(self.kiVmName, self.asTestVMs);
1381 aasTestCfgs.insert(self.kiStorageCtrl, self.asStorageCtrls);
1382 aasTestCfgs.insert(self.kiHostIoCache, (self.asHostIoCache, True, self.fnFormatHostIoCache));
1383 aasTestCfgs.insert(self.kiDiskFmt, self.asDiskFormats);
1384 aasTestCfgs.insert(self.kiDiskVar, self.asDiskVariants);
1385 aasTestCfgs.insert(self.kiCpuCount, (self.acCpus, True, self.fnFormatCpuString));
1386 aasTestCfgs.insert(self.kiVirtMode, (self.asVirtModes, True, self.fnFormatVirtMode));
1387 aasTestCfgs.insert(self.kiTestSet, self.asTestSets);
1388 aasTestCfgs.insert(self.kiIoTest, (self.asTests, False, None));
1389
1390 aasTestsBlacklist = [];
1391 aasTestsBlacklist.append(['tst-storage', 'BusLogic']); # 64bit Linux is broken with BusLogic
1392
1393 oTstCfgMgr = StorTestCfgMgr(aasTestCfgs, aasTestsBlacklist, self.isTestCfgSupported);
1394
1395 fRc = True;
1396 asTestCfg = oTstCfgMgr.getCurrentTestCfg();
1397 while asTestCfg:
1398 fRc = self.testOneCfg(asTestCfg[self.kiVmName], self.getStorageCtrlFromName(asTestCfg[self.kiStorageCtrl]), \
1399 asTestCfg[self.kiHostIoCache], asTestCfg[self.kiDiskFmt], asTestCfg[self.kiDiskVar],
1400 sDiskPath, asTestCfg[self.kiCpuCount], asTestCfg[self.kiIoTest], \
1401 asTestCfg[self.kiVirtMode], asTestCfg[self.kiTestSet]) and fRc and True; # pychecker hack.
1402
1403 asTestCfg = oTstCfgMgr.getNextTestCfg();
1404
1405 return fRc;
1406
1407 def test1(self):
1408 """
1409 Executes test #1.
1410 """
1411
1412 fRc = True;
1413 tupTstCfg = self.kdStorageCfgs.get(socket.getfqdn().lower());
1414 if tupTstCfg is None:
1415 tupTstCfg = self.kdStorageCfgs.get(socket.gethostname().lower());
1416
1417 # Test the host first if requested
1418 if tupTstCfg is not None or self.fUseScratch:
1419 self.fTestRawMode = tupTstCfg[0];
1420 oDiskCfg = tupTstCfg[1];
1421 lstBinaryPaths = ['/bin', '/sbin', '/usr/bin', '/usr/sbin', \
1422 '/opt/csw/bin', '/usr/ccs/bin', '/usr/sfw/bin'];
1423 oExecutor = remoteexecutor.RemoteExecutor(None, lstBinaryPaths, self.sScratchPath);
1424 if not self.fUseScratch:
1425 self.oStorCfg = storagecfg.StorageCfg(oExecutor, oDiskCfg);
1426
1427 # Try to cleanup any leftovers from a previous run first.
1428 fRc = self.oStorCfg.cleanupLeftovers();
1429 if not fRc:
1430 reporter.error('Failed to cleanup any leftovers from a previous run');
1431
1432 if self.fTestHost:
1433 reporter.testStart('Host');
1434 if self.fUseScratch:
1435 sMountPoint = self.sScratchPath;
1436 else:
1437 sMountPoint = self.prepareStorage(self.oStorCfg);
1438 if sMountPoint is not None:
1439 for sIoTest in self.asTests:
1440 for sTestSet in self.asTestSets:
1441 reporter.testStart(sTestSet);
1442 dTestSet = self.kdTestSets.get(sTestSet);
1443 self.testBenchmark(utils.getHostOs(), sIoTest, sMountPoint, oExecutor, dTestSet);
1444 reporter.testDone();
1445 self.cleanupStorage(self.oStorCfg);
1446 else:
1447 reporter.testFailure('Failed to prepare host storage');
1448 fRc = False;
1449 reporter.testDone();
1450 else:
1451 # Create the storage space first if it is not done before every test.
1452 sMountPoint = None;
1453 if self.fUseScratch:
1454 sMountPoint = self.sScratchPath;
1455 elif not self.fRecreateStorCfg:
1456 reporter.testStart('Create host storage');
1457 sMountPoint = self.prepareStorage(self.oStorCfg);
1458 if sMountPoint:
1459 fMode = 0o777;
1460 sDir = os.path.join(sMountPoint, 'test');
1461 fDirExist = os.path.exists(sDir)
1462 fMountPointExist = os.path.exists(sMountPoint)
1463 reporter.log("fExist for sMountPoint: %s, fExist for sDir (test folder): %s" % \
1464 (fMountPointExist,
1465 fDirExist))
1466 if fDirExist or self.oStorCfg.mkDirOnVolume(sMountPoint, 'test', fMode):
1467 sMountPoint = sDir
1468 else:
1469 reporter.error('Creating volume directory "%s" (mode %x) failed' % (sDir, fMode,));
1470 else:
1471 fRc = False;
1472 reporter.testDone();
1473
1474 if fRc:
1475 # Run the storage tests.
1476 if not self.testStorage(sMountPoint):
1477 fRc = False;
1478
1479 if not self.fRecreateStorCfg and not self.fUseScratch:
1480 self.cleanupStorage(self.oStorCfg);
1481 else:
1482 reporter.testFailure('Could not get disk configuration for host: %s' % (socket.getfqdn().lower()));
1483 fRc = False;
1484
1485 return fRc;
1486
1487if __name__ == '__main__':
1488 sys.exit(tdStorageBenchmark().main(sys.argv));
1489
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette