VirtualBox

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

Last change on this file since 62052 was 62052, checked in by vboxsync, 9 years ago

ValidationKit/tests/storage: Fixes to get the testcase working on a host

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 18.0 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: tdStorageBenchmark1.py 62052 2016-07-06 12:41:01Z vboxsync $
4
5"""
6VirtualBox Validation Kit - Storage benchmark.
7"""
8
9__copyright__ = \
10"""
11Copyright (C) 2012-2015 Oracle Corporation
12
13This file is part of VirtualBox Open Source Edition (OSE), as
14available from http://www.215389.xyz. 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: 62052 $"
31
32
33# Standard Python imports.
34import os;
35import socket;
36import sys;
37import StringIO;
38
39# Only the main script needs to modify the path.
40try: __file__
41except: __file__ = sys.argv[0];
42g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
43sys.path.append(g_ksValidationKitDir);
44
45# Validation Kit imports.
46from common import constants;
47from common import utils;
48from testdriver import reporter;
49from testdriver import base;
50from testdriver import vbox;
51from testdriver import vboxcon;
52
53import remoteexecutor;
54import storagecfg;
55
56def _ControllerTypeToName(eControllerType):
57 """ Translate a controller type to a name. """
58 if eControllerType == vboxcon.StorageControllerType_PIIX3 or eControllerType == vboxcon.StorageControllerType_PIIX4:
59 sType = "IDE Controller";
60 elif eControllerType == vboxcon.StorageControllerType_IntelAhci:
61 sType = "SATA Controller";
62 elif eControllerType == vboxcon.StorageControllerType_LsiLogicSas:
63 sType = "SAS Controller";
64 elif eControllerType == vboxcon.StorageControllerType_LsiLogic or eControllerType == vboxcon.StorageControllerType_BusLogic:
65 sType = "SCSI Controller";
66 else:
67 sType = "Storage Controller";
68 return sType;
69
70class FioTest(object):
71 """
72 Flexible I/O tester testcase.
73 """
74
75 kdHostIoEngine = {
76 'solaris': ('solarisaio', False),
77 'linux': ('libaio', True)
78 };
79
80 def __init__(self, oExecutor, dCfg = None):
81 self.oExecutor = oExecutor;
82 self.sCfgFileId = None;
83 self.dCfg = dCfg;
84
85 def prepare(self, cMsTimeout = 30000):
86 """ Prepares the testcase """
87
88 sTargetOs = self.dCfg.get('TargetOs', 'linux');
89 sIoEngine, fDirectIo = self.kdHostIoEngine.get(sTargetOs);
90 if sIoEngine is None:
91 return False;
92
93 cfgBuf = StringIO.StringIO();
94 cfgBuf.write('[global]\n');
95 cfgBuf.write('bs=' + self.dCfg.get('RecordSize', '4k') + '\n');
96 cfgBuf.write('ioengine=' + sIoEngine + '\n');
97 cfgBuf.write('iodepth=' + self.dCfg.get('QueueDepth', '32') + '\n');
98 cfgBuf.write('size=' + self.dCfg.get('TestsetSize', '2g') + '\n');
99 if fDirectIo:
100 cfgBuf.write('direct=1\n');
101 else:
102 cfgBuf.write('direct=0\n');
103 cfgBuf.write('directory=' + self.dCfg.get('FilePath', '/mnt') + '\n');
104
105 cfgBuf.write('[seq-write]\n');
106 cfgBuf.write('rw=write\n');
107 cfgBuf.write('stonewall\n');
108
109 cfgBuf.write('[rand-write]\n');
110 cfgBuf.write('rw=randwrite\n');
111 cfgBuf.write('stonewall\n');
112
113 cfgBuf.write('[seq-read]\n');
114 cfgBuf.write('rw=read\n');
115 cfgBuf.write('stonewall\n');
116
117 cfgBuf.write('[rand-read]\n');
118 cfgBuf.write('rw=randread\n');
119 cfgBuf.write('stonewall\n');
120
121 self.sCfgFileId = self.oExecutor.copyString(cfgBuf.getvalue(), 'aio-test', cMsTimeout);
122 return self.sCfgFileId is not None;
123
124 def run(self, cMsTimeout = 30000):
125 """ Runs the testcase """
126 _ = cMsTimeout
127 fRc, sOutput = self.oExecutor.execBinary('fio', (self.sCfgFileId,));
128 # @todo: Parse output.
129 _ = sOutput;
130 return fRc;
131
132 def cleanup(self):
133 """ Cleans up any leftovers from the testcase. """
134
135 def reportResult(self):
136 """
137 Reports the test results to the test manager.
138 """
139 return True;
140
141class IozoneTest(object):
142 """
143 I/O zone testcase.
144 """
145 def __init__(self, oExecutor, dCfg = None):
146 self.oExecutor = oExecutor;
147 self.sResult = None;
148 self.lstTests = [ ('initial writers', 'FirstWrite'),
149 ('rewriters', 'Rewrite'),
150 ('re-readers', 'ReRead'),
151 ('stride readers', 'StrideRead'),
152 ('random readers', 'RandomRead'),
153 ('mixed workload', 'MixedWorkload'),
154 ('random writers', 'RandomWrite'),
155 ('pwrite writers', 'PWrite'),
156 ('pread readers', 'PRead'),
157 ('readers', 'FirstRead')];
158 self.sRecordSize = dCfg.get('RecordSize', '4k');
159 self.sTestsetSize = dCfg.get('TestsetSize', '2g');
160 self.sQueueDepth = dCfg.get('QueueDepth', '32');
161 self.sFilePath = dCfg.get('FilePath', '/mnt/iozone');
162 self.fDirectIo = True;
163
164 sTargetOs = dCfg.get('TargetOs');
165 if sTargetOs == 'solaris':
166 self.fDirectIo = False;
167
168 def prepare(self, cMsTimeout = 30000):
169 """ Prepares the testcase """
170 _ = cMsTimeout;
171 return True; # Nothing to do.
172
173 def run(self, cMsTimeout = 30000):
174 """ Runs the testcase """
175 tupArgs = ('-r', self.sRecordSize, '-s', self.sTestsetSize, \
176 '-t', '1', '-T', '-H', self.sQueueDepth, '-F', self.sFilePath + '/iozone.tmp');
177 if self.fDirectIo:
178 tupArgs += ('-I',);
179 fRc, sOutput = self.oExecutor.execBinary('iozone', tupArgs);
180 if fRc:
181 self.sResult = sOutput;
182
183 _ = cMsTimeout;
184 return fRc;
185
186 def cleanup(self):
187 """ Cleans up any leftovers from the testcase. """
188 return True;
189
190 def reportResult(self):
191 """
192 Reports the test results to the test manager.
193 """
194
195 fRc = True;
196 if self.sResult is not None:
197 try:
198 asLines = self.sResult.splitlines();
199 for sLine in asLines:
200 sLine = sLine.strip();
201 if sLine.startswith('Children') is True:
202 # Extract the value
203 idxValue = sLine.rfind('=');
204 if idxValue is -1:
205 raise Exception('IozoneTest: Invalid state');
206
207 idxValue += 1;
208 while sLine[idxValue] == ' ':
209 idxValue += 1;
210
211 idxValueEnd = idxValue;
212 while sLine[idxValueEnd] == '.' or sLine[idxValueEnd].isdigit():
213 idxValueEnd += 1;
214
215 for sNeedle, sTestVal in self.lstTests:
216 if sLine.rfind(sNeedle) is not -1:
217 reporter.testValue(sTestVal, sLine[idxValue:idxValueEnd],
218 constants.valueunit.g_asNames[constants.valueunit.KILOBYTES_PER_SEC]);
219 except:
220 fRc = False;
221 else:
222 fRc = False;
223
224 return fRc;
225
226
227class tdStorageBenchmark(vbox.TestDriver): # pylint: disable=R0902
228 """
229 Storage benchmark.
230 """
231
232 def __init__(self):
233 vbox.TestDriver.__init__(self);
234 self.asRsrcs = None;
235 self.oGuestToGuestVM = None;
236 self.oGuestToGuestSess = None;
237 self.oGuestToGuestTxs = None;
238 self.asTestVMsDef = ['tst-debian'];
239 self.asTestVMs = self.asTestVMsDef;
240 self.asSkipVMs = [];
241 self.asVirtModesDef = ['hwvirt', 'hwvirt-np', 'raw',]
242 self.asVirtModes = self.asVirtModesDef
243 self.acCpusDef = [1, 2,]
244 self.acCpus = self.acCpusDef;
245 self.asStorageCtrlsDef = ['AHCI', 'IDE', 'LsiLogicSAS', 'LsiLogic', 'BusLogic'];
246 self.asStorageCtrls = self.asStorageCtrlsDef;
247 self.asDiskFormatsDef = ['VDI', 'VMDK', 'VHD', 'QED', 'Parallels', 'QCOW', 'iSCSI'];
248 self.asDiskFormats = self.asDiskFormatsDef;
249 self.asTestsDef = ['iozone', 'fio'];
250 self.asTests = self.asTestsDef;
251 self.asIscsiTargetsDef = ['aurora|iqn.2011-03.home.aurora:aurora.storagebench|1'];
252 self.asIscsiTargets = self.asIscsiTargetsDef;
253 self.fTestHost = False;
254
255 #
256 # Overridden methods.
257 #
258 def showUsage(self):
259 rc = vbox.TestDriver.showUsage(self);
260 reporter.log('');
261 reporter.log('tdStorageBenchmark1 Options:');
262 reporter.log(' --virt-modes <m1[:m2[:]]');
263 reporter.log(' Default: %s' % (':'.join(self.asVirtModesDef)));
264 reporter.log(' --cpu-counts <c1[:c2[:]]');
265 reporter.log(' Default: %s' % (':'.join(str(c) for c in self.acCpusDef)));
266 reporter.log(' --storage-ctrls <type1[:type2[:...]]>');
267 reporter.log(' Default: %s' % (':'.join(self.asStorageCtrls)));
268 reporter.log(' --disk-formats <type1[:type2[:...]]>');
269 reporter.log(' Default: %s' % (':'.join(self.asDiskFormats)));
270 reporter.log(' --iscsi-targets <target1[:target2[:...]]>');
271 reporter.log(' Default: %s' % (':'.join(self.asIscsiTargets)));
272 reporter.log(' --tests <test1[:test2[:...]]>');
273 reporter.log(' Default: %s' % (':'.join(self.asTests)));
274 reporter.log(' --test-vms <vm1[:vm2[:...]]>');
275 reporter.log(' Test the specified VMs in the given order. Use this to change');
276 reporter.log(' the execution order or limit the choice of VMs');
277 reporter.log(' Default: %s (all)' % (':'.join(self.asTestVMsDef)));
278 reporter.log(' --skip-vms <vm1[:vm2[:...]]>');
279 reporter.log(' Skip the specified VMs when testing.');
280 reporter.log(' --test-host');
281 reporter.log(' Do all configured tests on the host first and report the results');
282 reporter.log(' to get a baseline');
283 return rc;
284
285 def parseOption(self, asArgs, iArg): # pylint: disable=R0912,R0915
286 if asArgs[iArg] == '--virt-modes':
287 iArg += 1;
288 if iArg >= len(asArgs): raise base.InvalidOption('The "--virt-modes" takes a colon separated list of modes');
289 self.asVirtModes = asArgs[iArg].split(':');
290 for s in self.asVirtModes:
291 if s not in self.asVirtModesDef:
292 raise base.InvalidOption('The "--virt-modes" value "%s" is not valid; valid values are: %s' \
293 % (s, ' '.join(self.asVirtModesDef)));
294 elif asArgs[iArg] == '--cpu-counts':
295 iArg += 1;
296 if iArg >= len(asArgs): raise base.InvalidOption('The "--cpu-counts" takes a colon separated list of cpu counts');
297 self.acCpus = [];
298 for s in asArgs[iArg].split(':'):
299 try: c = int(s);
300 except: raise base.InvalidOption('The "--cpu-counts" value "%s" is not an integer' % (s,));
301 if c <= 0: raise base.InvalidOption('The "--cpu-counts" value "%s" is zero or negative' % (s,));
302 self.acCpus.append(c);
303 elif asArgs[iArg] == '--storage-ctrls':
304 iArg += 1;
305 if iArg >= len(asArgs):
306 raise base.InvalidOption('The "--storage-ctrls" takes a colon separated list of Storage controller types');
307 self.asStorageCtrls = asArgs[iArg].split(':');
308 elif asArgs[iArg] == '--disk-formats':
309 iArg += 1;
310 if iArg >= len(asArgs): raise base.InvalidOption('The "--disk-formats" takes a colon separated list of disk formats');
311 self.asDiskFormats = asArgs[iArg].split(':');
312 elif asArgs[iArg] == '--iscsi-targets':
313 iArg += 1;
314 if iArg >= len(asArgs):
315 raise base.InvalidOption('The "--iscsi-targets" takes a colon separated list of iscsi targets');
316 self.asIscsiTargets = asArgs[iArg].split(':');
317 elif asArgs[iArg] == '--tests':
318 iArg += 1;
319 if iArg >= len(asArgs): raise base.InvalidOption('The "--tests" takes a colon separated list of disk formats');
320 self.asTests = asArgs[iArg].split(':');
321 elif asArgs[iArg] == '--test-vms':
322 iArg += 1;
323 if iArg >= len(asArgs): raise base.InvalidOption('The "--test-vms" takes colon separated list');
324 self.asTestVMs = asArgs[iArg].split(':');
325 for s in self.asTestVMs:
326 if s not in self.asTestVMsDef:
327 raise base.InvalidOption('The "--test-vms" value "%s" is not valid; valid values are: %s' \
328 % (s, ' '.join(self.asTestVMsDef)));
329 elif asArgs[iArg] == '--skip-vms':
330 iArg += 1;
331 if iArg >= len(asArgs): raise base.InvalidOption('The "--skip-vms" takes colon separated list');
332 self.asSkipVMs = asArgs[iArg].split(':');
333 for s in self.asSkipVMs:
334 if s not in self.asTestVMsDef:
335 reporter.log('warning: The "--test-vms" value "%s" does not specify any of our test VMs.' % (s));
336 elif asArgs[iArg] == '--test-host':
337 self.fTestHost = True;
338 else:
339 return vbox.TestDriver.parseOption(self, asArgs, iArg);
340 return iArg + 1;
341
342 def completeOptions(self):
343 # Remove skipped VMs from the test list.
344 for sVM in self.asSkipVMs:
345 try: self.asTestVMs.remove(sVM);
346 except: pass;
347
348 return vbox.TestDriver.completeOptions(self);
349
350 def getResourceSet(self):
351 # Construct the resource list the first time it's queried.
352 if self.asRsrcs is None:
353 self.asRsrcs = [];
354 if 'tst-debian' in self.asTestVMs:
355 self.asRsrcs.append('4.2/storage/debian.vdi');
356
357 return self.asRsrcs;
358
359 def actionConfig(self):
360
361 # Make sure vboxapi has been imported so we can use the constants.
362 if not self.importVBoxApi():
363 return False;
364
365 #
366 # Configure the VMs we're going to use.
367 #
368
369 # Linux VMs
370 if 'tst-debian' in self.asTestVMs:
371 oVM = self.createTestVM('tst-debian', 1, '4.2/storage/debian.vdi', sKind = 'Debian_64', fIoApic = True, \
372 eNic0AttachType = vboxcon.NetworkAttachmentType_NAT, \
373 eNic0Type = vboxcon.NetworkAdapterType_Am79C973);
374 if oVM is None:
375 return False;
376
377 return True;
378
379 def actionExecute(self):
380 """
381 Execute the testcase.
382 """
383 fRc = self.test1();
384 return fRc;
385
386
387 #
388 # Test execution helpers.
389 #
390
391 def test1Benchmark(self, sTargetOs, sBenchmark, oTxsSession = None):
392 """
393 Runs the given benchmark on the test host.
394 """
395 lstBinaryPaths = ['/bin', '/sbin', '/usr/bin', '/usr/sbin', \
396 '/opt/csw/bin', '/usr/ccs/bin', '/usr/sfw/bin'];
397 oExecutor = remoteexecutor.RemoteExecutor(oTxsSession, lstBinaryPaths, self.sScratchPath);
398
399 # Create a basic pool with the default configuration.
400 oStorCfg = storagecfg.StorageCfg(oExecutor, socket.gethostname().lower());
401 fRc, sPoolId = oStorCfg.createStoragePool();
402 if fRc:
403 fRc, sMountpoint = oStorCfg.createVolume(sPoolId);
404 if fRc:
405 # Create a basic config
406 dCfg = {
407 'RecordSize': '64k',
408 'TestsetSize': '20g',
409 'QueueDepth': '32',
410 'FilePath': sMountpoint,
411 'TargetOs': sTargetOs
412 };
413
414 oTst = None;
415 if sBenchmark == 'iozone':
416 oTst = IozoneTest(oExecutor, dCfg);
417 elif sBenchmark == 'fio':
418 oTst = FioTest(oExecutor, dCfg); # pylint: disable=R0204
419
420 if oTst is not None:
421 reporter.testStart(sBenchmark);
422 fRc = oTst.prepare();
423 if fRc:
424 fRc = oTst.run();
425 if fRc:
426 fRc = oTst.reportResult();
427 else:
428 reporter.testFailure('Running the testcase failed');
429 else:
430 reporter.testFailure('Preparing the testcase failed');
431
432 oTst.cleanup();
433 reporter.testDone();
434 else:
435 reporter.testFailure('Creating a storage volume on the target failed');
436
437 oStorCfg.cleanup();
438 else:
439 reporter.testFailure('Creating a storage pool on the target failed');
440
441 return fRc;
442
443 def test1Benchmarks(self, sTargetOs, oTxsSession = None):
444 """
445 Runs all the configured benchmarks on the target.
446 """
447 reporter.testStart('Host');
448 for sTest in self.asTests:
449 self.test1Benchmark(sTargetOs, sTest, oTxsSession);
450 reporter.testDone();
451
452 def test1(self):
453 """
454 Executes test #1.
455 """
456
457 # Test the host first if requested
458 fRc = True;
459 if self.fTestHost:
460 fRc = self.test1Benchmarks(utils.getHostOs());
461
462 # Loop thru the test VMs.
463 #for sVM in self.asTestVMs:
464 # # run test on the VM.
465 # if not self.test1OneVM(sVM):
466 # fRc = False;
467 # else:
468 # fRc = True;
469
470 return fRc;
471
472
473
474if __name__ == '__main__':
475 sys.exit(tdStorageBenchmark().main(sys.argv));
476
Note: See TracBrowser for help on using the repository browser.

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