VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testboxscript/testboxcommand.py@ 95351

Last change on this file since 95351 was 94125, checked in by vboxsync, 3 years ago

ValKit/testboxscript: pylint 2.9.6 adjustments (mostly about using 'with' statements).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.0 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: testboxcommand.py 94125 2022-03-08 14:15:09Z vboxsync $
3
4"""
5TestBox Script - Command Processor.
6"""
7
8__copyright__ = \
9"""
10Copyright (C) 2012-2022 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: 94125 $"
30
31# Standard python imports.
32import os;
33import sys;
34import threading;
35
36# Validation Kit imports.
37from common import constants;
38from common import utils, webutils;
39import testboxcommons;
40from testboxcommons import TestBoxException;
41from testboxscript import TBS_EXITCODE_NEED_UPGRADE;
42from testboxupgrade import upgradeFromZip;
43from testboxtasks import TestBoxExecTask, TestBoxCleanupTask, TestBoxTestDriverTask;
44
45# Figure where we are.
46try: __file__
47except: __file__ = sys.argv[0];
48g_ksTestScriptDir = os.path.dirname(os.path.abspath(__file__));
49
50
51
52class TestBoxCommand(object):
53 """
54 Implementation of Test Box command.
55 """
56
57 ## The time to wait on the current task to abort.
58 kcSecStopTimeout = 360
59 ## The time to wait on the current task to abort before rebooting.
60 kcSecStopBeforeRebootTimeout = 360
61
62 def __init__(self, oTestBoxScript):
63 """
64 Class instance init
65 """
66 self._oTestBoxScript = oTestBoxScript;
67 self._oCurTaskLock = threading.RLock();
68 self._oCurTask = None;
69
70 # List of available commands and their handlers
71 self._dfnCommands = \
72 {
73 constants.tbresp.CMD_IDLE: self._cmdIdle,
74 constants.tbresp.CMD_WAIT: self._cmdWait,
75 constants.tbresp.CMD_EXEC: self._cmdExec,
76 constants.tbresp.CMD_ABORT: self._cmdAbort,
77 constants.tbresp.CMD_REBOOT: self._cmdReboot,
78 constants.tbresp.CMD_UPGRADE: self._cmdUpgrade,
79 constants.tbresp.CMD_UPGRADE_AND_REBOOT: self._cmdUpgradeAndReboot,
80 constants.tbresp.CMD_SPECIAL: self._cmdSpecial,
81 }
82
83 def _cmdIdle(self, oResponse, oConnection):
84 """
85 Idle response, no ACK.
86 """
87 oResponse.checkParameterCount(1);
88
89 # The dispatch loop will delay for us, so nothing to do here.
90 _ = oConnection; # Leave the connection open.
91 return True;
92
93 def _cmdWait(self, oResponse, oConnection):
94 """
95 Gang scheduling wait response, no ACK.
96 """
97 oResponse.checkParameterCount(1);
98
99 # The dispatch loop will delay for us, so nothing to do here.
100 _ = oConnection; # Leave the connection open.
101 return True;
102
103 def _cmdExec(self, oResponse, oConnection):
104 """
105 Execute incoming command
106 """
107
108 # Check if required parameters given and make a little sense.
109 idResult = oResponse.getIntChecked( constants.tbresp.EXEC_PARAM_RESULT_ID, 1);
110 sScriptZips = oResponse.getStringChecked(constants.tbresp.EXEC_PARAM_SCRIPT_ZIPS);
111 sScriptCmdLine = oResponse.getStringChecked(constants.tbresp.EXEC_PARAM_SCRIPT_CMD_LINE);
112 cSecTimeout = oResponse.getIntChecked( constants.tbresp.EXEC_PARAM_TIMEOUT, 30);
113 oResponse.checkParameterCount(5);
114
115 sScriptFile = utils.argsGetFirst(sScriptCmdLine);
116 if sScriptFile is None:
117 raise TestBoxException('Bad script command line: "%s"' % (sScriptCmdLine,));
118 if len(os.path.basename(sScriptFile)) < len('t.py'):
119 raise TestBoxException('Script file name too short: "%s"' % (sScriptFile,));
120 if len(sScriptZips) < len('x.zip'):
121 raise TestBoxException('Script zip name too short: "%s"' % (sScriptFile,));
122
123 # One task at the time.
124 if self.isRunning():
125 raise TestBoxException('Already running other command');
126
127 # Don't bother running the task without the shares mounted.
128 self._oTestBoxScript.mountShares(); # Raises exception on failure.
129
130 # Kick off the task and ACK the command.
131 with self._oCurTaskLock:
132 self._oCurTask = TestBoxExecTask(self._oTestBoxScript, idResult = idResult, sScriptZips = sScriptZips,
133 sScriptCmdLine = sScriptCmdLine, cSecTimeout = cSecTimeout);
134 oConnection.sendAckAndClose(constants.tbresp.CMD_EXEC);
135 return True;
136
137 def _cmdAbort(self, oResponse, oConnection):
138 """
139 Abort background task
140 """
141 oResponse.checkParameterCount(1);
142 oConnection.sendAck(constants.tbresp.CMD_ABORT);
143
144 oCurTask = self._getCurTask();
145 if oCurTask is not None:
146 oCurTask.terminate();
147 oCurTask.flushLogOnConnection(oConnection);
148 oConnection.close();
149 oCurTask.wait(self.kcSecStopTimeout);
150
151 return True;
152
153 def doReboot(self):
154 """
155 Worker common to _cmdReboot and _doUpgrade that performs a system reboot.
156 """
157 # !! Not more exceptions beyond this point !!
158 testboxcommons.log('Rebooting');
159
160 # Stop anything that might be executing at this point.
161 oCurTask = self._getCurTask();
162 if oCurTask is not None:
163 oCurTask.terminate();
164 oCurTask.wait(self.kcSecStopBeforeRebootTimeout);
165
166 # Invoke shutdown command line utility.
167 sOs = utils.getHostOs();
168 asCmd2 = None;
169 if sOs == 'win':
170 asCmd = ['shutdown', '/r', '/t', '0', '/c', '"ValidationKit triggered reboot"', '/d', '4:1'];
171 elif sOs == 'os2':
172 asCmd = ['setboot', '/B'];
173 elif sOs in ('solaris',):
174 asCmd = ['/usr/sbin/reboot', '-p'];
175 asCmd2 = ['/usr/sbin/reboot']; # Hack! S10 doesn't have -p, but don't know how to reliably detect S10.
176 else:
177 asCmd = ['/sbin/shutdown', '-r', 'now'];
178 try:
179 utils.sudoProcessOutputChecked(asCmd);
180 except Exception as oXcpt:
181 if asCmd2 is not None:
182 try:
183 utils.sudoProcessOutputChecked(asCmd2);
184 except Exception as oXcpt:
185 testboxcommons.log('Error executing reboot command "%s" as well as "%s": %s' % (asCmd, asCmd2, oXcpt));
186 return False;
187 testboxcommons.log('Error executing reboot command "%s": %s' % (asCmd, oXcpt));
188 return False;
189
190 # Quit the script.
191 while True:
192 sys.exit(32);
193 return True;
194
195 def _cmdReboot(self, oResponse, oConnection):
196 """
197 Reboot Test Box
198 """
199 oResponse.checkParameterCount(1);
200 oConnection.sendAckAndClose(constants.tbresp.CMD_REBOOT);
201 return self.doReboot();
202
203 def _doUpgrade(self, oResponse, oConnection, fReboot):
204 """
205 Common worker for _cmdUpgrade and _cmdUpgradeAndReboot.
206 Will sys.exit on success!
207 """
208
209 #
210 # The server specifies a ZIP archive with the new scripts. It's ASSUMED
211 # that the zip is of selected files at g_ksValidationKitDir in SVN. It's
212 # further ASSUMED that we're executing from
213 #
214 sZipUrl = oResponse.getStringChecked(constants.tbresp.UPGRADE_PARAM_URL)
215 oResponse.checkParameterCount(2);
216
217 if utils.isRunningFromCheckout():
218 raise TestBoxException('Cannot upgrade when running from the tree!');
219 oConnection.sendAckAndClose(constants.tbresp.CMD_UPGRADE_AND_REBOOT if fReboot else constants.tbresp.CMD_UPGRADE);
220
221 testboxcommons.log('Upgrading...');
222
223 #
224 # Download the file and install it.
225 #
226 sDstFile = os.path.join(g_ksTestScriptDir, 'VBoxTestBoxScript.zip');
227 if os.path.exists(sDstFile):
228 os.unlink(sDstFile);
229 fRc = webutils.downloadFile(sZipUrl, sDstFile, self._oTestBoxScript.getPathBuilds(), testboxcommons.log);
230 if fRc is not True:
231 return False;
232
233 if upgradeFromZip(sDstFile) is not True:
234 return False;
235
236 #
237 # Restart the system or the script (we have a parent script which
238 # respawns us when we quit).
239 #
240 if fReboot:
241 self.doReboot();
242 sys.exit(TBS_EXITCODE_NEED_UPGRADE);
243 return False; # shuts up pylint (it will probably complain later when it learns DECL_NO_RETURN).
244
245 def _cmdUpgrade(self, oResponse, oConnection):
246 """
247 Upgrade Test Box Script
248 """
249 return self._doUpgrade(oResponse, oConnection, False);
250
251 def _cmdUpgradeAndReboot(self, oResponse, oConnection):
252 """
253 Upgrade Test Box Script
254 """
255 return self._doUpgrade(oResponse, oConnection, True);
256
257 def _cmdSpecial(self, oResponse, oConnection):
258 """
259 Reserved for future fun.
260 """
261 oConnection.sendReplyAndClose(constants.tbreq.COMMAND_NOTSUP, constants.tbresp.CMD_SPECIAL);
262 testboxcommons.log('Special command %s not supported...' % (oResponse,));
263 return False;
264
265
266 def handleCommand(self, oResponse, oConnection):
267 """
268 Handles a command from the test manager.
269
270 Some commands will close the connection, others (generally the simple
271 ones) wont, leaving the caller the option to use it for log flushing.
272
273 Returns success indicator.
274 Raises no exception.
275 """
276 try:
277 sCmdName = oResponse.getStringChecked(constants.tbresp.ALL_PARAM_RESULT);
278 except Exception as oXcpt:
279 oConnection.close();
280 return False;
281
282 # Do we know the command?
283 fRc = False;
284 if sCmdName in self._dfnCommands:
285 testboxcommons.log(sCmdName);
286 try:
287 # Execute the handler.
288 fRc = self._dfnCommands[sCmdName](oResponse, oConnection)
289 except Exception as oXcpt:
290 # NACK the command if an exception is raised during parameter validation.
291 testboxcommons.log1Xcpt('Exception executing "%s": %s' % (sCmdName, oXcpt));
292 if oConnection.isConnected():
293 try:
294 oConnection.sendReplyAndClose(constants.tbreq.COMMAND_NACK, sCmdName);
295 except Exception as oXcpt2:
296 testboxcommons.log('Failed to NACK "%s": %s' % (sCmdName, oXcpt2));
297 elif sCmdName in [constants.tbresp.STATUS_DEAD, constants.tbresp.STATUS_NACK]:
298 testboxcommons.log('Received status instead of command: %s' % (sCmdName, ));
299 else:
300 # NOTSUP the unknown command.
301 testboxcommons.log('Received unknown command: %s' % (sCmdName, ));
302 try:
303 oConnection.sendReplyAndClose(constants.tbreq.COMMAND_NOTSUP, sCmdName);
304 except Exception as oXcpt:
305 testboxcommons.log('Failed to NOTSUP "%s": %s' % (sCmdName, oXcpt));
306 return fRc;
307
308 def resumeIncompleteCommand(self):
309 """
310 Resumes an incomplete command at startup.
311
312 The EXEC commands saves essential state information in the scratch area
313 so we can resume them in case the testbox panics or is rebooted.
314 Current "resume" means doing cleanups, but we may need to implement
315 test scenarios involving rebooting the testbox later.
316
317 Returns (idTestBox, sTestBoxName, True) if a command was resumed,
318 otherwise (-1, '', False). Raises no exceptions.
319 """
320
321 try:
322 oTask = TestBoxCleanupTask(self._oTestBoxScript);
323 except:
324 return (-1, '', False);
325
326 with self._oCurTaskLock:
327 self._oCurTask = oTask;
328
329 return (oTask.idTestBox, oTask.sTestBoxName, True);
330
331 def isRunning(self):
332 """
333 Check if we're running a task or not.
334 """
335 oCurTask = self._getCurTask();
336 return oCurTask is not None and oCurTask.isRunning();
337
338 def flushLogOnConnection(self, oGivenConnection):
339 """
340 Flushes the log of any running task with a log buffer.
341 """
342 oCurTask = self._getCurTask();
343 if oCurTask is not None and isinstance(oCurTask, TestBoxTestDriverTask):
344 return oCurTask.flushLogOnConnection(oGivenConnection);
345 return None;
346
347 def _getCurTask(self):
348 """ Gets the current task in a paranoidly safe manny. """
349 with self._oCurTaskLock:
350 oCurTask = self._oCurTask;
351 return oCurTask;
352
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