VirtualBox

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

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

ValidationKit/tests/storage: A bit more...

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