VirtualBox

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

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

ValidationKit/tests/storage: Start rework for doing storage related tests

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