VirtualBox

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

Last change on this file since 103914 was 103857, checked in by vboxsync, 9 months ago

ValKit, storage test, add more logging

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