VirtualBox

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

Last change on this file since 85159 was 85159, checked in by vboxsync, 4 years ago

ValidationKit/tdStorageBenchmark1: Don't use the .disk suffix but rather .img to make file deletion for flat VMDK images work after the latest change for bugref:9770

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