VirtualBox

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

Last change on this file since 92489 was 90596, checked in by vboxsync, 3 years ago

ValKit: More Python 3.9 API changes needed (array.array.tostring() -> .tobytes()) [build fix] bugref:10079

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.0 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: remoteexecutor.py 90596 2021-08-10 13:01:34Z 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: 90596 $"
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 if sys.version_info < (3, 9, 0):
67 # Removed since Python 3.9.
68 return str(sText.tostring()); # pylint: disable=no-member
69 return str(sText.tobytes());
70 except:
71 pass;
72 elif isinstance(sText, bytes):
73 return sText.decode('utf-8');
74
75 return sText;
76
77 def read(self, cb):
78 """file.read"""
79 return self.sInput.read(cb);
80
81 def write(self, sText):
82 """file.write"""
83 self.sOutput += self._toString(sText);
84 return None;
85
86 def getOutput(self):
87 """
88 Returns the output of the buffer.
89 """
90 return self.sOutput;
91
92 def close(self):
93 """ file.close """
94 return;
95
96class RemoteExecutor(object):
97 """
98 Helper for executing tests remotely through TXS or locally
99 """
100
101 def __init__(self, oTxsSession = None, asBinaryPaths = None, sScratchPath = None):
102 self.oTxsSession = oTxsSession;
103 self.asPaths = asBinaryPaths;
104 self.sScratchPath = sScratchPath;
105 if self.asPaths is None:
106 self.asPaths = [ ];
107
108 def _getBinaryPath(self, sBinary):
109 """
110 Returns the complete path of the given binary if found
111 from the configured search path or None if not found.
112 """
113 for sPath in self.asPaths:
114 sFile = sPath + '/' + sBinary;
115 if self.isFile(sFile):
116 return sFile;
117 return sBinary;
118
119 def _sudoExecuteSync(self, asArgs, sInput):
120 """
121 Executes a sudo child process synchronously.
122 Returns a tuple [True, 0] if the process executed successfully
123 and returned 0, otherwise [False, rc] is returned.
124 """
125 reporter.log('Executing [sudo]: %s' % (asArgs, ));
126 reporter.flushall();
127 fRc = True;
128 sOutput = '';
129 sError = '';
130 try:
131 oProcess = utils.sudoProcessPopen(asArgs, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
132 stderr=subprocess.PIPE, shell = False, close_fds = False);
133
134 sOutput, sError = oProcess.communicate(sInput);
135 iExitCode = oProcess.poll();
136
137 if iExitCode != 0:
138 fRc = False;
139 except:
140 reporter.errorXcpt();
141 fRc = False;
142 reporter.log('Exit code [sudo]: %s (%s)' % (fRc, asArgs));
143 return (fRc, str(sOutput), str(sError));
144
145 def _execLocallyOrThroughTxs(self, sExec, asArgs, sInput, cMsTimeout):
146 """
147 Executes the given program locally or through TXS based on the
148 current config.
149 """
150 fRc = False;
151 sOutput = None;
152 if self.oTxsSession is not None:
153 reporter.log('Executing [remote]: %s %s %s' % (sExec, asArgs, sInput));
154 reporter.flushall();
155 oStdOut = StdInOutBuffer();
156 oStdErr = StdInOutBuffer();
157 oTestPipe = reporter.FileWrapperTestPipe();
158 oStdIn = None;
159 if sInput is not None:
160 oStdIn = StdInOutBuffer(sInput);
161 else:
162 oStdIn = '/dev/null'; # pylint: disable=redefined-variable-type
163 fRc = self.oTxsSession.syncExecEx(sExec, (sExec,) + asArgs,
164 oStdIn = oStdIn, oStdOut = oStdOut,
165 oStdErr = oStdErr, oTestPipe = oTestPipe,
166 cMsTimeout = cMsTimeout);
167 sOutput = oStdOut.getOutput();
168 sError = oStdErr.getOutput();
169 if fRc is False:
170 reporter.log('Exit code [remote]: %s (stdout: %s stderr: %s)' % (fRc, sOutput, sError));
171 else:
172 reporter.log('Exit code [remote]: %s' % (fRc,));
173 else:
174 fRc, sOutput, sError = self._sudoExecuteSync([sExec, ] + list(asArgs), sInput);
175 return (fRc, sOutput, sError);
176
177 def execBinary(self, sExec, asArgs, sInput = None, cMsTimeout = 3600000):
178 """
179 Executes the given binary with the given arguments
180 providing some optional input through stdin and
181 returning whether the process exited successfully and the output
182 in a string.
183 """
184
185 fRc = True;
186 sOutput = None;
187 sError = None;
188 sBinary = self._getBinaryPath(sExec);
189 if sBinary is not None:
190 fRc, sOutput, sError = self._execLocallyOrThroughTxs(sBinary, asArgs, sInput, cMsTimeout);
191 else:
192 fRc = False;
193 return (fRc, sOutput, sError);
194
195 def execBinaryNoStdOut(self, sExec, asArgs, sInput = None):
196 """
197 Executes the given binary with the given arguments
198 providing some optional input through stdin and
199 returning whether the process exited successfully.
200 """
201 fRc, _, _ = self.execBinary(sExec, asArgs, sInput);
202 return fRc;
203
204 def copyFile(self, sLocalFile, sFilename, cMsTimeout = 30000):
205 """
206 Copies the local file to the remote destination
207 if configured
208
209 Returns a file ID which can be used as an input parameter
210 to execBinary() resolving to the real filepath on the remote side
211 or locally.
212 """
213 sFileId = None;
214 if self.oTxsSession is not None:
215 sFileId = '${SCRATCH}/' + sFilename;
216 fRc = self.oTxsSession.syncUploadFile(sLocalFile, sFileId, cMsTimeout);
217 if not fRc:
218 sFileId = None;
219 else:
220 sFileId = self.sScratchPath + '/' + sFilename;
221 try:
222 shutil.copy(sLocalFile, sFileId);
223 except:
224 sFileId = None;
225
226 return sFileId;
227
228 def copyString(self, sContent, sFilename, cMsTimeout = 30000):
229 """
230 Creates a file remotely or locally with the given content.
231
232 Returns a file ID which can be used as an input parameter
233 to execBinary() resolving to the real filepath on the remote side
234 or locally.
235 """
236 sFileId = None;
237 if self.oTxsSession is not None:
238 sFileId = '${SCRATCH}/' + sFilename;
239 fRc = self.oTxsSession.syncUploadString(sContent, sFileId, cMsTimeout);
240 if not fRc:
241 sFileId = None;
242 else:
243 sFileId = self.sScratchPath + '/' + sFilename;
244 try:
245 oFile = open(sFileId, 'wb');
246 oFile.write(sContent);
247 oFile.close();
248 except:
249 sFileId = None;
250
251 return sFileId;
252
253 def mkDir(self, sDir, fMode = 0o700, cMsTimeout = 30000):
254 """
255 Creates a new directory at the given location.
256 """
257 fRc = True;
258 if self.oTxsSession is not None:
259 fRc = self.oTxsSession.syncMkDir(sDir, fMode, cMsTimeout);
260 elif not os.path.isdir(sDir):
261 fRc = os.mkdir(sDir, fMode);
262
263 return fRc;
264
265 def rmDir(self, sDir, cMsTimeout = 30000):
266 """
267 Removes the given directory.
268 """
269 fRc = True;
270 if self.oTxsSession is not None:
271 fRc = self.oTxsSession.syncRmDir(sDir, cMsTimeout);
272 else:
273 fRc = self.execBinaryNoStdOut('rmdir', (sDir,));
274
275 return fRc;
276
277 def rmTree(self, sDir, cMsTimeout = 30000):
278 """
279 Recursively removes all files and sub directories including the given directory.
280 """
281 fRc = True;
282 if self.oTxsSession is not None:
283 fRc = self.oTxsSession.syncRmTree(sDir, cMsTimeout);
284 else:
285 try:
286 shutil.rmtree(sDir, ignore_errors=True);
287 except:
288 fRc = False;
289
290 return fRc;
291
292 def isFile(self, sPath, cMsTimeout = 30000):
293 """
294 Checks that the given file exists.
295 """
296 fRc = True;
297 if self.oTxsSession is not None:
298 fRc = self.oTxsSession.syncIsFile(sPath, cMsTimeout);
299 else:
300 try:
301 fRc = os.path.isfile(sPath);
302 except:
303 fRc = False;
304
305 return fRc;
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