VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/tests/storage/remoteexecutor.py@ 86918

Last change on this file since 86918 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.8 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: remoteexecutor.py 82968 2020-02-04 10:35:17Z vboxsync $
3
4"""
5VirtualBox Validation Kit - Storage benchmark, test execution helpers.
6"""
7
8__copyright__ = \
9"""
10Copyright (C) 2016-2020 Oracle Corporation
11
12This file is part of VirtualBox Open Source Edition (OSE), as
13available from http://www.virtualbox.org. This file is free software;
14you can redistribute it and/or modify it under the terms of the GNU
15General Public License (GPL) as published by the Free Software
16Foundation, in version 2 as it comes in the "COPYING" file of the
17VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19
20The contents of this file may alternatively be used under the terms
21of the Common Development and Distribution License Version 1.0
22(CDDL) only, as it comes in the "COPYING.CDDL" file of the
23VirtualBox OSE distribution, in which case the provisions of the
24CDDL are applicable instead of those of the GPL.
25
26You may elect to license modified versions of this file under the
27terms and conditions of either the GPL or the CDDL or both.
28"""
29__version__ = "$Revision: 82968 $"
30
31
32# Standard Python imports.
33import array;
34import os;
35import shutil;
36import sys;
37if sys.version_info[0] >= 3:
38 from io import StringIO as StringIO; # pylint: disable=import-error,no-name-in-module,useless-import-alias
39else:
40 from StringIO import StringIO as StringIO; # pylint: disable=import-error,no-name-in-module,useless-import-alias
41import subprocess;
42
43# Validation Kit imports.
44from common import utils;
45from testdriver import reporter;
46
47
48
49class StdInOutBuffer(object):
50 """ Standard input output buffer """
51
52 def __init__(self, sInput = None):
53 self.sInput = StringIO();
54 if sInput is not None:
55 self.sInput.write(self._toString(sInput));
56 self.sInput.seek(0);
57 self.sOutput = '';
58
59 def _toString(self, sText):
60 """
61 Converts any possible array to
62 a string.
63 """
64 if isinstance(sText, array.array):
65 try:
66 return str(sText.tostring()); # tostring() returns bytes with python3.
67 except:
68 pass;
69 elif isinstance(sText, bytes):
70 return sText.decode('utf-8');
71
72 return sText;
73
74 def read(self, cb):
75 """file.read"""
76 return self.sInput.read(cb);
77
78 def write(self, sText):
79 """file.write"""
80 self.sOutput += self._toString(sText);
81 return None;
82
83 def getOutput(self):
84 """
85 Returns the output of the buffer.
86 """
87 return self.sOutput;
88
89 def close(self):
90 """ file.close """
91 return;
92
93class RemoteExecutor(object):
94 """
95 Helper for executing tests remotely through TXS or locally
96 """
97
98 def __init__(self, oTxsSession = None, asBinaryPaths = None, sScratchPath = None):
99 self.oTxsSession = oTxsSession;
100 self.asPaths = asBinaryPaths;
101 self.sScratchPath = sScratchPath;
102 if self.asPaths is None:
103 self.asPaths = [ ];
104
105 def _getBinaryPath(self, sBinary):
106 """
107 Returns the complete path of the given binary if found
108 from the configured search path or None if not found.
109 """
110 for sPath in self.asPaths:
111 sFile = sPath + '/' + sBinary;
112 if self.isFile(sFile):
113 return sFile;
114 return sBinary;
115
116 def _sudoExecuteSync(self, asArgs, sInput):
117 """
118 Executes a sudo child process synchronously.
119 Returns a tuple [True, 0] if the process executed successfully
120 and returned 0, otherwise [False, rc] is returned.
121 """
122 reporter.log('Executing [sudo]: %s' % (asArgs, ));
123 reporter.flushall();
124 fRc = True;
125 sOutput = '';
126 sError = '';
127 try:
128 oProcess = utils.sudoProcessPopen(asArgs, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
129 stderr=subprocess.PIPE, shell = False, close_fds = False);
130
131 sOutput, sError = oProcess.communicate(sInput);
132 iExitCode = oProcess.poll();
133
134 if iExitCode != 0:
135 fRc = False;
136 except:
137 reporter.errorXcpt();
138 fRc = False;
139 reporter.log('Exit code [sudo]: %s (%s)' % (fRc, asArgs));
140 return (fRc, str(sOutput), str(sError));
141
142 def _execLocallyOrThroughTxs(self, sExec, asArgs, sInput, cMsTimeout):
143 """
144 Executes the given program locally or through TXS based on the
145 current config.
146 """
147 fRc = False;
148 sOutput = None;
149 if self.oTxsSession is not None:
150 reporter.log('Executing [remote]: %s %s %s' % (sExec, asArgs, sInput));
151 reporter.flushall();
152 oStdOut = StdInOutBuffer();
153 oStdErr = StdInOutBuffer();
154 oTestPipe = reporter.FileWrapperTestPipe();
155 oStdIn = None;
156 if sInput is not None:
157 oStdIn = StdInOutBuffer(sInput);
158 else:
159 oStdIn = '/dev/null'; # pylint: disable=redefined-variable-type
160 fRc = self.oTxsSession.syncExecEx(sExec, (sExec,) + asArgs,
161 oStdIn = oStdIn, oStdOut = oStdOut,
162 oStdErr = oStdErr, oTestPipe = oTestPipe,
163 cMsTimeout = cMsTimeout);
164 sOutput = oStdOut.getOutput();
165 sError = oStdErr.getOutput();
166 if fRc is False:
167 reporter.log('Exit code [remote]: %s (stdout: %s stderr: %s)' % (fRc, sOutput, sError));
168 else:
169 reporter.log('Exit code [remote]: %s' % (fRc,));
170 else:
171 fRc, sOutput, sError = self._sudoExecuteSync([sExec, ] + list(asArgs), sInput);
172 return (fRc, sOutput, sError);
173
174 def execBinary(self, sExec, asArgs, sInput = None, cMsTimeout = 3600000):
175 """
176 Executes the given binary with the given arguments
177 providing some optional input through stdin and
178 returning whether the process exited successfully and the output
179 in a string.
180 """
181
182 fRc = True;
183 sOutput = None;
184 sError = None;
185 sBinary = self._getBinaryPath(sExec);
186 if sBinary is not None:
187 fRc, sOutput, sError = self._execLocallyOrThroughTxs(sBinary, asArgs, sInput, cMsTimeout);
188 else:
189 fRc = False;
190 return (fRc, sOutput, sError);
191
192 def execBinaryNoStdOut(self, sExec, asArgs, sInput = None):
193 """
194 Executes the given binary with the given arguments
195 providing some optional input through stdin and
196 returning whether the process exited successfully.
197 """
198 fRc, _, _ = self.execBinary(sExec, asArgs, sInput);
199 return fRc;
200
201 def copyFile(self, sLocalFile, sFilename, cMsTimeout = 30000):
202 """
203 Copies the local file to the remote destination
204 if configured
205
206 Returns a file ID which can be used as an input parameter
207 to execBinary() resolving to the real filepath on the remote side
208 or locally.
209 """
210 sFileId = None;
211 if self.oTxsSession is not None:
212 sFileId = '${SCRATCH}/' + sFilename;
213 fRc = self.oTxsSession.syncUploadFile(sLocalFile, sFileId, cMsTimeout);
214 if not fRc:
215 sFileId = None;
216 else:
217 sFileId = self.sScratchPath + '/' + sFilename;
218 try:
219 shutil.copy(sLocalFile, sFileId);
220 except:
221 sFileId = None;
222
223 return sFileId;
224
225 def copyString(self, sContent, sFilename, cMsTimeout = 30000):
226 """
227 Creates a file remotely or locally with the given content.
228
229 Returns a file ID which can be used as an input parameter
230 to execBinary() resolving to the real filepath on the remote side
231 or locally.
232 """
233 sFileId = None;
234 if self.oTxsSession is not None:
235 sFileId = '${SCRATCH}/' + sFilename;
236 fRc = self.oTxsSession.syncUploadString(sContent, sFileId, cMsTimeout);
237 if not fRc:
238 sFileId = None;
239 else:
240 sFileId = self.sScratchPath + '/' + sFilename;
241 try:
242 oFile = open(sFileId, 'wb');
243 oFile.write(sContent);
244 oFile.close();
245 except:
246 sFileId = None;
247
248 return sFileId;
249
250 def mkDir(self, sDir, fMode = 0o700, cMsTimeout = 30000):
251 """
252 Creates a new directory at the given location.
253 """
254 fRc = True;
255 if self.oTxsSession is not None:
256 fRc = self.oTxsSession.syncMkDir(sDir, fMode, cMsTimeout);
257 elif not os.path.isdir(sDir):
258 fRc = os.mkdir(sDir, fMode);
259
260 return fRc;
261
262 def rmDir(self, sDir, cMsTimeout = 30000):
263 """
264 Removes the given directory.
265 """
266 fRc = True;
267 if self.oTxsSession is not None:
268 fRc = self.oTxsSession.syncRmDir(sDir, cMsTimeout);
269 else:
270 fRc = self.execBinaryNoStdOut('rmdir', (sDir,));
271
272 return fRc;
273
274 def rmTree(self, sDir, cMsTimeout = 30000):
275 """
276 Recursively removes all files and sub directories including the given directory.
277 """
278 fRc = True;
279 if self.oTxsSession is not None:
280 fRc = self.oTxsSession.syncRmTree(sDir, cMsTimeout);
281 else:
282 try:
283 shutil.rmtree(sDir, ignore_errors=True);
284 except:
285 fRc = False;
286
287 return fRc;
288
289 def isFile(self, sPath, cMsTimeout = 30000):
290 """
291 Checks that the given file exists.
292 """
293 fRc = True;
294 if self.oTxsSession is not None:
295 fRc = self.oTxsSession.syncIsFile(sPath, cMsTimeout);
296 else:
297 try:
298 fRc = os.path.isfile(sPath);
299 except:
300 fRc = False;
301
302 return fRc;
303
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