VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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