VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testmanager/core/testbox.py@ 61282

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

virtual test sheriff: Kick-off and the bad-testbox rebooting and disabling. (completely untested)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.7 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: testbox.py 61282 2016-05-29 19:49:31Z vboxsync $
3
4"""
5Test Manager - TestBox.
6"""
7
8__copyright__ = \
9"""
10Copyright (C) 2012-2015 Oracle Corporation
11
12This file is part of VirtualBox Open Source Edition (OSE), as
13available from http://www.215389.xyz. 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: 61282 $"
30
31
32# Standard python imports.
33import unittest;
34
35# Validation Kit imports.
36from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMExceptionBase, TMInFligthCollision, \
37 TMInvalidData, TMTooManyRows, TMRowNotFound, ChangeLogEntry, AttributeChangeEntry;
38
39
40# pylint: disable=C0103
41class TestBoxData(ModelDataBase): # pylint: disable=R0902
42 """
43 TestBox Data.
44 """
45
46 ## LomKind_T
47 ksLomKind_None = 'none';
48 ksLomKind_ILOM = 'ilom';
49 ksLomKind_ELOM = 'elom';
50 ksLomKind_AppleXserveLom = 'apple-xserver-lom';
51 kasLomKindValues = [ ksLomKind_None, ksLomKind_ILOM, ksLomKind_ELOM, ksLomKind_AppleXserveLom];
52 kaoLomKindDescs = \
53 [
54 ( ksLomKind_None, 'None', ''),
55 ( ksLomKind_ILOM, 'ILOM', ''),
56 ( ksLomKind_ELOM, 'ELOM', ''),
57 ( ksLomKind_AppleXserveLom, 'Apple Xserve LOM', ''),
58 ];
59
60
61 ## TestBoxCmd_T
62 ksTestBoxCmd_None = 'none';
63 ksTestBoxCmd_Abort = 'abort';
64 ksTestBoxCmd_Reboot = 'reboot';
65 ksTestBoxCmd_Upgrade = 'upgrade';
66 ksTestBoxCmd_UpgradeAndReboot = 'upgrade-and-reboot';
67 ksTestBoxCmd_Special = 'special';
68 kasTestBoxCmdValues = [ ksTestBoxCmd_None, ksTestBoxCmd_Abort, ksTestBoxCmd_Reboot, ksTestBoxCmd_Upgrade,
69 ksTestBoxCmd_UpgradeAndReboot, ksTestBoxCmd_Special];
70 kaoTestBoxCmdDescs = \
71 [
72 ( ksTestBoxCmd_None, 'None', ''),
73 ( ksTestBoxCmd_Abort, 'Abort current test', ''),
74 ( ksTestBoxCmd_Reboot, 'Reboot TestBox', ''),
75 ( ksTestBoxCmd_Upgrade, 'Upgrade TestBox Script', ''),
76 ( ksTestBoxCmd_UpgradeAndReboot, 'Upgrade TestBox Script and reboot', ''),
77 ( ksTestBoxCmd_Special, 'Special (reserved)', ''),
78 ];
79
80
81 ksIdAttr = 'idTestBox';
82 ksIdGenAttr = 'idGenTestBox';
83
84 ksParam_idTestBox = 'TestBox_idTestBox';
85 ksParam_tsEffective = 'TestBox_tsEffective';
86 ksParam_tsExpire = 'TestBox_tsExpire';
87 ksParam_uidAuthor = 'TestBox_uidAuthor';
88 ksParam_idGenTestBox = 'TestBox_idGenTestBox';
89 ksParam_ip = 'TestBox_ip';
90 ksParam_uuidSystem = 'TestBox_uuidSystem';
91 ksParam_sName = 'TestBox_sName';
92 ksParam_sDescription = 'TestBox_sDescription';
93 ksParam_idSchedGroup = 'TestBox_idSchedGroup';
94 ksParam_fEnabled = 'TestBox_fEnabled';
95 ksParam_enmLomKind = 'TestBox_enmLomKind';
96 ksParam_ipLom = 'TestBox_ipLom';
97 ksParam_pctScaleTimeout = 'TestBox_pctScaleTimeout';
98 ksParam_sOs = 'TestBox_sOs';
99 ksParam_sOsVersion = 'TestBox_sOsVersion';
100 ksParam_sCpuVendor = 'TestBox_sCpuVendor';
101 ksParam_sCpuArch = 'TestBox_sCpuArch';
102 ksParam_sCpuName = 'TestBox_sCpuName';
103 ksParam_lCpuRevision = 'TestBox_lCpuRevision';
104 ksParam_cCpus = 'TestBox_cCpus';
105 ksParam_fCpuHwVirt = 'TestBox_fCpuHwVirt';
106 ksParam_fCpuNestedPaging = 'TestBox_fCpuNestedPaging';
107 ksParam_fCpu64BitGuest = 'TestBox_fCpu64BitGuest';
108 ksParam_fChipsetIoMmu = 'TestBox_fChipsetIoMmu';
109 ksParam_cMbMemory = 'TestBox_cMbMemory';
110 ksParam_cMbScratch = 'TestBox_cMbScratch';
111 ksParam_sReport = 'TestBox_sReport';
112 ksParam_iTestBoxScriptRev = 'TestBox_iTestBoxScriptRev';
113 ksParam_iPythonHexVersion = 'TestBox_iPythonHexVersion';
114 ksParam_enmPendingCmd = 'TestBox_enmPendingCmd';
115
116 kasAllowNullAttributes = ['idTestBox', 'tsEffective', 'tsExpire', 'uidAuthor', 'idGenTestBox', 'sDescription',
117 'ipLom', 'sOs', 'sOsVersion', 'sCpuVendor', 'sCpuArch', 'sCpuName', 'lCpuRevision', 'cCpus',
118 'fCpuHwVirt', 'fCpuNestedPaging', 'fCpu64BitGuest', 'fChipsetIoMmu', 'cMbMemory',
119 'cMbScratch', 'sReport', 'iTestBoxScriptRev', 'iPythonHexVersion' ];
120 kasValidValues_enmLomKind = kasLomKindValues;
121 kasValidValues_enmPendingCmd = kasTestBoxCmdValues;
122 kiMin_pctScaleTimeout = 11;
123 kiMax_pctScaleTimeout = 19999;
124 kcchMax_sReport = 65535;
125
126
127 def __init__(self):
128 ModelDataBase.__init__(self);
129
130 #
131 # Initialize with defaults.
132 # See the database for explanations of each of these fields.
133 #
134 self.idTestBox = None;
135 self.tsEffective = None;
136 self.tsExpire = None;
137 self.uidAuthor = None;
138 self.idGenTestBox = None;
139 self.ip = None;
140 self.uuidSystem = None;
141 self.sName = None;
142 self.sDescription = None;
143 self.idSchedGroup = 1;
144 self.fEnabled = False;
145 self.enmLomKind = self.ksLomKind_None;
146 self.ipLom = None;
147 self.pctScaleTimeout = 100;
148 self.sOs = None;
149 self.sOsVersion = None;
150 self.sCpuVendor = None;
151 self.sCpuArch = None;
152 self.sCpuName = None;
153 self.lCpuRevision = None;
154 self.cCpus = 1;
155 self.fCpuHwVirt = False;
156 self.fCpuNestedPaging = False;
157 self.fCpu64BitGuest = False;
158 self.fChipsetIoMmu = False;
159 self.cMbMemory = 1;
160 self.cMbScratch = 0;
161 self.sReport = None;
162 self.iTestBoxScriptRev = 0;
163 self.iPythonHexVersion = 0;
164 self.enmPendingCmd = self.ksTestBoxCmd_None;
165
166 def initFromDbRow(self, aoRow):
167 """
168 Internal worker for initFromDbWithId and initFromDbWithGenId as well as
169 from TestBoxLogic. Expecting a SELECT * FROM TestBoxes result.
170 """
171
172 if aoRow is None:
173 raise TMRowNotFound('TestBox not found.');
174
175 self.idTestBox = aoRow[0];
176 self.tsEffective = aoRow[1];
177 self.tsExpire = aoRow[2];
178 self.uidAuthor = aoRow[3];
179 self.idGenTestBox = aoRow[4];
180 self.ip = aoRow[5];
181 self.uuidSystem = aoRow[6];
182 self.sName = aoRow[7];
183 self.sDescription = aoRow[8];
184 self.idSchedGroup = aoRow[9];
185 self.fEnabled = aoRow[10];
186 self.enmLomKind = aoRow[11];
187 self.ipLom = aoRow[12];
188 self.pctScaleTimeout = aoRow[13];
189 self.sOs = aoRow[14];
190 self.sOsVersion = aoRow[15];
191 self.sCpuVendor = aoRow[16];
192 self.sCpuArch = aoRow[17];
193 self.sCpuName = aoRow[18];
194 self.lCpuRevision = aoRow[19];
195 self.cCpus = aoRow[20];
196 self.fCpuHwVirt = aoRow[21];
197 self.fCpuNestedPaging = aoRow[22];
198 self.fCpu64BitGuest = aoRow[23];
199 self.fChipsetIoMmu = aoRow[24];
200 self.cMbMemory = aoRow[25];
201 self.cMbScratch = aoRow[26];
202 self.sReport = aoRow[27];
203 self.iTestBoxScriptRev = aoRow[28];
204 self.iPythonHexVersion = aoRow[29];
205 self.enmPendingCmd = aoRow[30];
206 return self;
207
208 def initFromDbWithId(self, oDb, idTestBox, tsNow = None, sPeriodBack = None):
209 """
210 Initialize the object from the database.
211 """
212 oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
213 'SELECT *\n'
214 'FROM TestBoxes\n'
215 'WHERE idTestBox = %s\n'
216 , ( idTestBox, ), tsNow, sPeriodBack));
217 aoRow = oDb.fetchOne()
218 if aoRow is None:
219 raise TMRowNotFound('idTestBox=%s not found (tsNow=%s sPeriodBack=%s)' % (idTestBox, tsNow, sPeriodBack,));
220 return self.initFromDbRow(aoRow);
221
222 def initFromDbWithGenId(self, oDb, idGenTestBox):
223 """
224 Initialize the object from the database.
225 """
226 oDb.execute('SELECT *\n'
227 'FROM TestBoxes\n'
228 'WHERE idGenTestBox = %s\n'
229 , (idGenTestBox, ) );
230 return self.initFromDbRow(oDb.fetchOne());
231
232 def _validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor = ModelDataBase.ksValidateFor_Other):
233 # Override to do extra ipLom checks.
234 dErrors = ModelDataBase._validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor);
235 if self.ksParam_ipLom not in dErrors \
236 and self.ksParam_enmLomKind not in dErrors \
237 and self.enmLomKind != self.ksLomKind_None \
238 and self.ipLom is None:
239 dErrors[self.ksParam_ipLom] = 'Light-out-management IP is mandatory and a LOM is selected.'
240 return dErrors;
241
242 def formatPythonVersion(self):
243 """
244 Unbuttons the version number and formats it as a version string.
245 """
246 if self.iPythonHexVersion is None:
247 return 'N/A';
248 return 'v%d.%d.%d.%d' \
249 % ( self.iPythonHexVersion >> 24,
250 (self.iPythonHexVersion >> 16) & 0xff,
251 (self.iPythonHexVersion >> 8) & 0xff,
252 self.iPythonHexVersion & 0xff);
253
254 def getCpuFamily(self):
255 """ Returns the CPU family for a x86 or amd64 testboxes."""
256 if self.lCpuRevision is None:
257 return 0;
258 return (self.lCpuRevision >> 24 & 0xff);
259
260 def getCpuModel(self):
261 """ Returns the CPU model for a x86 or amd64 testboxes."""
262 if self.lCpuRevision is None:
263 return 0;
264 return (self.lCpuRevision >> 8 & 0xffff);
265
266 def getCpuStepping(self):
267 """ Returns the CPU stepping for a x86 or amd64 testboxes."""
268 if self.lCpuRevision is None:
269 return 0;
270 return (self.lCpuRevision & 0xff);
271
272
273class TestBoxLogic(ModelLogicBase):
274 """
275 TestBox logic.
276 """
277
278
279 def __init__(self, oDb):
280 ModelLogicBase.__init__(self, oDb);
281
282 def tryFetchTestBoxByUuid(self, sTestBoxUuid):
283 """
284 Tries to fetch a testbox by its UUID alone.
285 """
286 self._oDb.execute('SELECT *\n'
287 'FROM TestBoxes\n'
288 'WHERE uuidSystem = %s\n'
289 ' AND tsExpire = \'infinity\'::timestamp\n'
290 'ORDER BY tsEffective DESC\n',
291 (sTestBoxUuid,));
292 if self._oDb.getRowCount() == 0:
293 return None;
294 if self._oDb.getRowCount() != 1:
295 raise TMTooManyRows('Database integrity error: %u hits' % (self._oDb.getRowCount(),));
296 oData = TestBoxData();
297 oData.initFromDbRow(self._oDb.fetchOne());
298 return oData;
299
300 def fetchForListing(self, iStart, cMaxRows, tsNow):
301 """
302 Fetches testboxes for listing.
303
304 Returns an array (list) of TestBoxData items, empty list if none. The
305 TestBoxData instances have an extra oStatus member that is either None or
306 a TestBoxStatusData instance, and a member tsCurrent holding
307 CURRENT_TIMESTAMP.
308
309 Raises exception on error.
310 """
311 from testmanager.core.testboxstatus import TestBoxStatusData;
312
313 if tsNow is None:
314 self._oDb.execute('SELECT TestBoxes.*, TestBoxStatuses.*\n'
315 'FROM TestBoxes\n'
316 'LEFT OUTER JOIN TestBoxStatuses ON (\n'
317 ' TestBoxStatuses.idTestBox = TestBoxes.idTestBox )\n'
318 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
319 'ORDER BY sName\n'
320 'LIMIT %s OFFSET %s\n'
321 , (cMaxRows, iStart,));
322 else:
323 self._oDb.execute('SELECT TestBoxes.*, TestBoxStatuses.*\n'
324 'FROM TestBoxes\n'
325 'LEFT OUTER JOIN TestBoxStatuses ON (\n'
326 ' TestBoxStatuses.idTestBox = TestBoxes.idTestBox )\n'
327 'WHERE tsExpire > %s\n'
328 ' AND tsEffective <= %s\n'
329 'ORDER BY sName\n'
330 'LIMIT %s OFFSET %s\n'
331 , (tsNow, tsNow, cMaxRows, iStart,));
332
333 aoRows = [];
334 for aoOne in self._oDb.fetchAll():
335 oTestBox = TestBoxData().initFromDbRow(aoOne);
336 oTestBox.tsCurrent = self._oDb.getCurrentTimestamp(); # pylint: disable=W0201
337 oTestBox.oStatus = None; # pylint: disable=W0201
338 if aoOne[31] is not None:
339 oTestBox.oStatus = TestBoxStatusData().initFromDbRow(aoOne[31:]); # pylint: disable=W0201
340 aoRows.append(oTestBox);
341 return aoRows;
342
343 def fetchForChangeLog(self, idTestBox, iStart, cMaxRows, tsNow): # pylint: disable=R0914
344 """
345 Fetches change log entries for a testbox.
346
347 Returns an array of ChangeLogEntry instance and an indicator whether
348 there are more entries.
349 Raises exception on error.
350 """
351
352 if tsNow is None:
353 tsNow = self._oDb.getCurrentTimestamp();
354
355 self._oDb.execute('SELECT TestBoxes.*, Users.sUsername\n'
356 'FROM TestBoxes\n'
357 'LEFT OUTER JOIN Users \n'
358 ' ON ( TestBoxes.uidAuthor = Users.uid\n'
359 ' AND Users.tsEffective <= TestBoxes.tsEffective\n'
360 ' AND Users.tsExpire > TestBoxes.tsEffective)\n'
361 'WHERE TestBoxes.tsEffective <= %s\n'
362 ' AND TestBoxes.idTestBox = %s\n'
363 'ORDER BY TestBoxes.tsExpire DESC\n'
364 'LIMIT %s OFFSET %s\n'
365 , (tsNow, idTestBox, cMaxRows + 1, iStart,));
366
367 aoRows = [];
368 for _ in range(self._oDb.getRowCount()):
369 oRow = self._oDb.fetchOne();
370 aoRows.append([TestBoxData().initFromDbRow(oRow), oRow[-1],]);
371
372 # Calculate the changes.
373 aoEntries = [];
374 for i in range(0, len(aoRows) - 1):
375 (oNew, sAuthor) = aoRows[i];
376 (oOld, _ ) = aoRows[i + 1];
377 aoChanges = [];
378 for sAttr in oNew.getDataAttributes():
379 if sAttr not in [ 'tsEffective', 'tsExpire', 'uidAuthor']:
380 oOldAttr = getattr(oOld, sAttr);
381 oNewAttr = getattr(oNew, sAttr);
382 if oOldAttr != oNewAttr:
383 aoChanges.append(AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
384 aoEntries.append(ChangeLogEntry(oNew.uidAuthor, sAuthor, oNew.tsEffective, oNew.tsExpire, oNew, oOld, aoChanges));
385
386 # If we're at the end of the log, add the initial entry.
387 if len(aoRows) <= cMaxRows and len(aoRows) > 0:
388 (oNew, sAuthor) = aoRows[-1];
389 aoEntries.append(ChangeLogEntry(oNew.uidAuthor, aoRows[-1][1], oNew.tsEffective, oNew.tsExpire, oNew, None, []));
390
391 return (aoEntries, len(aoRows) > cMaxRows);
392
393 def addEntry(self, oData, uidAuthor, fCommit = False):
394 """
395 Creates a testbox in the database.
396 Returns the testbox ID, testbox generation ID and effective timestamp
397 of the created testbox on success. Throws error on failure.
398 """
399 dDataErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Add);
400 if len(dDataErrors) > 0:
401 raise TMInvalidData('Invalid data passed to create(): %s' % (dDataErrors,));
402
403 self._oDb.execute('INSERT INTO TestBoxes (\n'
404 ' idTestBox,\n'
405 ' tsEffective,\n'
406 ' tsExpire,\n'
407 ' uidAuthor,\n'
408 ' idGenTestBox,\n'
409 ' ip,\n'
410 ' uuidSystem,\n'
411 ' sName,\n'
412 ' sDescription,\n'
413 ' idSchedGroup,\n'
414 ' fEnabled,\n'
415 ' enmLomKind,\n'
416 ' ipLom,\n'
417 ' pctScaleTimeout,\n'
418 ' sOs,\n'
419 ' sOsVersion,\n'
420 ' sCpuVendor,\n'
421 ' sCpuArch,\n'
422 ' sCpuName,\n'
423 ' lCpuRevision,\n'
424 ' cCpus,\n'
425 ' fCpuHwVirt,\n'
426 ' fCpuNestedPaging,\n'
427 ' fCpu64BitGuest,\n'
428 ' fChipsetIoMmu,\n'
429 ' cMbMemory,\n'
430 ' cMbScratch,\n'
431 ' sReport,\n'
432 ' iTestBoxScriptRev,\n'
433 ' iPythonHexVersion,\n'
434 ' enmPendingCmd\n'
435 ' )'
436 'VALUES (\n'
437 ' DEFAULT,\n'
438 ' CURRENT_TIMESTAMP,\n'
439 ' DEFAULT,\n'
440 ' %s,\n' # uidAuthor
441 ' DEFAULT,\n'
442 ' %s,\n' # ip
443 ' %s,\n' # uuidSystem
444 ' %s,\n' # sName
445 ' %s,\n' # sDescription
446 ' %s,\n' # idSchedGroup
447 ' %s,\n' # fEnabled
448 ' %s,\n' # enmLomKind
449 ' %s,\n' # ipLom
450 ' %s,\n' # pctScaleTimeout
451 ' %s,\n' # sOs
452 ' %s,\n' # sOsVersion
453 ' %s,\n' # sCpuVendor
454 ' %s,\n' # sCpuArch
455 ' %s,\n' # sCpuName
456 ' %s,\n' # lCpuRevision
457 ' %s,\n' # cCpus
458 ' %s,\n' # fCpuHwVirt
459 ' %s,\n' # fCpuNestedPaging
460 ' %s,\n' # fCpu64BitGuest
461 ' %s,\n' # fChipsetIoMmu
462 ' %s,\n' # cMbMemory
463 ' %s,\n' # cMbScratch
464 ' %s,\n' # sReport
465 ' %s,\n' # iTestBoxScriptRev
466 ' %s,\n' # iPythonHexVersion
467 ' %s\n' # enmPendingCmd
468 ' )\n'
469 'RETURNING idTestBox, idGenTestBox, tsEffective'
470 , (uidAuthor,
471 oData.ip,
472 oData.uuidSystem,
473 oData.sName,
474 oData.sDescription,
475 oData.idSchedGroup,
476 oData.fEnabled,
477 oData.enmLomKind,
478 oData.ipLom,
479 oData.pctScaleTimeout,
480 oData.sOs,
481 oData.sOsVersion,
482 oData.sCpuVendor,
483 oData.sCpuArch,
484 oData.sCpuName,
485 oData.lCpuRevision,
486 oData.cCpus,
487 oData.fCpuHwVirt,
488 oData.fCpuNestedPaging,
489 oData.fCpu64BitGuest,
490 oData.fChipsetIoMmu,
491 oData.cMbMemory,
492 oData.cMbScratch,
493 oData.sReport,
494 oData.iTestBoxScriptRev,
495 oData.iPythonHexVersion,
496 oData.enmPendingCmd
497 )
498 );
499 oRow = self._oDb.fetchOne();
500 self._oDb.maybeCommit(fCommit);
501 return (oRow[0], oRow[1], oRow[2]);
502
503 def editEntry(self, oData, uidAuthor, fCommit = False):
504 """
505 Data edit update, web UI is the primary user.
506 Returns the new generation ID and effective date.
507 """
508
509 dDataErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Edit);
510 if len(dDataErrors) > 0:
511 raise TMInvalidData('Invalid data passed to create(): %s' % (dDataErrors,));
512
513 ## @todo check if the data changed.
514
515 self._oDb.execute('UPDATE ONLY TestBoxes\n'
516 'SET tsExpire = CURRENT_TIMESTAMP\n'
517 'WHERE idGenTestBox = %s\n'
518 ' AND tsExpire = \'infinity\'::timestamp\n'
519 'RETURNING tsExpire\n',
520 (oData.idGenTestBox,));
521 try:
522 tsEffective = self._oDb.fetchOne()[0];
523
524 # Would be easier to do this using an insert or update hook, I think. Much easier.
525
526 ##
527 ## @todo The table is growing too fast. Rows are too long. Mixing data from here and there. Split it and
528 ## rethink storage and update strategy!
529 ##
530
531 self._oDb.execute('INSERT INTO TestBoxes (\n'
532 ' idGenTestBox,\n'
533 ' idTestBox,\n'
534 ' tsEffective,\n'
535 ' uidAuthor,\n'
536 ' ip,\n'
537 ' uuidSystem,\n'
538 ' sName,\n'
539 ' sDescription,\n'
540 ' idSchedGroup,\n'
541 ' fEnabled,\n'
542 ' enmLomKind,\n'
543 ' ipLom,\n'
544 ' pctScaleTimeout,\n'
545 ' sOs,\n'
546 ' sOsVersion,\n'
547 ' sCpuVendor,\n'
548 ' sCpuArch,\n'
549 ' sCpuName,\n'
550 ' lCpuRevision,\n'
551 ' cCpus,\n'
552 ' fCpuHwVirt,\n'
553 ' fCpuNestedPaging,\n'
554 ' fCpu64BitGuest,\n'
555 ' fChipsetIoMmu,\n'
556 ' cMbMemory,\n'
557 ' cMbScratch,\n'
558 ' sReport,\n'
559 ' iTestBoxScriptRev,\n'
560 ' iPythonHexVersion,\n'
561 ' enmPendingCmd\n'
562 ' )\n'
563 'SELECT NEXTVAL(\'TestBoxGenIdSeq\'),\n'
564 ' idTestBox,\n'
565 ' %s,\n' # tsEffective
566 ' %s,\n' # uidAuthor
567 ' %s,\n' # ip
568 ' %s,\n' # uuidSystem
569 ' %s,\n' # sName
570 ' %s,\n' # sDescription
571 ' %s,\n' # idSchedGroup
572 ' %s,\n' # fEnabled
573 ' %s,\n' # enmLomKind
574 ' %s,\n' # ipLom
575 ' %s,\n' # pctScaleTimeout
576 ' sOs,\n'
577 ' sOsVersion,\n'
578 ' sCpuVendor,\n'
579 ' sCpuArch,\n'
580 ' sCpuName,\n'
581 ' lCpuRevision,\n'
582 ' cCpus,\n'
583 ' fCpuHwVirt,\n'
584 ' fCpuNestedPaging,\n'
585 ' fCpu64BitGuest,\n'
586 ' fChipsetIoMmu,\n'
587 ' cMbMemory,\n'
588 ' cMbScratch,\n'
589 ' sReport,\n'
590 ' iTestBoxScriptRev,\n'
591 ' iPythonHexVersion,\n'
592 ' %s\n' # enmPendingCmd
593 'FROM TestBoxes\n'
594 'WHERE idGenTestBox = %s\n'
595 'RETURNING idGenTestBox, tsEffective'
596 , (tsEffective,
597 uidAuthor,
598 oData.ip,
599 oData.uuidSystem,
600 oData.sName,
601 oData.sDescription,
602 oData.idSchedGroup,
603 oData.fEnabled,
604 oData.enmLomKind,
605 oData.ipLom,
606 oData.pctScaleTimeout,
607 oData.enmPendingCmd,
608 oData.idGenTestBox,
609 ));
610 aRow = self._oDb.fetchOne();
611 if aRow is None:
612 raise TMExceptionBase('Insert failed? oRow=None');
613 idGenTestBox = aRow[0];
614 tsEffective = aRow[1];
615 self._oDb.maybeCommit(fCommit);
616 except:
617 self._oDb.rollback();
618 raise;
619
620 return (idGenTestBox, tsEffective);
621
622 def updateOnSignOn(self, idTestBox, idGenTestBox, sTestBoxAddr, sOs, sOsVersion, # pylint: disable=R0913,R0914
623 sCpuVendor, sCpuArch, sCpuName, lCpuRevision, cCpus, fCpuHwVirt, fCpuNestedPaging, fCpu64BitGuest,
624 fChipsetIoMmu, cMbMemory, cMbScratch, sReport, iTestBoxScriptRev, iPythonHexVersion):
625 """
626 Update the testbox attributes automatically on behalf of the testbox script.
627 Returns the new generation id on success, raises an exception on failure.
628 """
629 try:
630 # Would be easier to do this using an insert or update hook, I think. Much easier.
631 self._oDb.execute('UPDATE ONLY TestBoxes\n'
632 'SET tsExpire = CURRENT_TIMESTAMP\n'
633 'WHERE idGenTestBox = %s\n'
634 ' AND tsExpire = \'infinity\'::timestamp\n'
635 'RETURNING tsExpire\n',
636 (idGenTestBox,));
637 tsEffective = self._oDb.fetchOne()[0];
638
639 self._oDb.execute('INSERT INTO TestBoxes (\n'
640 ' idGenTestBox,\n'
641 ' idTestBox,\n'
642 ' tsEffective,\n'
643 ' uidAuthor,\n'
644 ' ip,\n'
645 ' uuidSystem,\n'
646 ' sName,\n'
647 ' sDescription,\n'
648 ' idSchedGroup,\n'
649 ' fEnabled,\n'
650 ' enmLomKind,\n'
651 ' ipLom,\n'
652 ' pctScaleTimeout,\n'
653 ' sOs,\n'
654 ' sOsVersion,\n'
655 ' sCpuVendor,\n'
656 ' sCpuArch,\n'
657 ' sCpuName,\n'
658 ' lCpuRevision,\n'
659 ' cCpus,\n'
660 ' fCpuHwVirt,\n'
661 ' fCpuNestedPaging,\n'
662 ' fCpu64BitGuest,\n'
663 ' fChipsetIoMmu,\n'
664 ' cMbMemory,\n'
665 ' cMbScratch,\n'
666 ' sReport,\n'
667 ' iTestBoxScriptRev,\n'
668 ' iPythonHexVersion,\n'
669 ' enmPendingCmd\n'
670 ' )\n'
671 'SELECT NEXTVAL(\'TestBoxGenIdSeq\'),\n'
672 ' %s,\n'
673 ' %s,\n'
674 ' NULL,\n' # uidAuthor
675 ' %s,\n'
676 ' uuidSystem,\n'
677 ' sName,\n'
678 ' sDescription,\n'
679 ' idSchedGroup,\n'
680 ' fEnabled,\n'
681 ' enmLomKind,\n'
682 ' ipLom,\n'
683 ' pctScaleTimeout,\n'
684 ' %s,\n' # sOs
685 ' %s,\n' # sOsVersion
686 ' %s,\n' # sCpuVendor
687 ' %s,\n' # sCpuArch
688 ' %s,\n' # sCpuName
689 ' %s,\n' # lCpuRevision
690 ' %s,\n' # cCpus
691 ' %s,\n' # fCpuHwVirt
692 ' %s,\n' # fCpuNestedPaging
693 ' %s,\n' # fCpu64BitGuest
694 ' %s,\n' # fChipsetIoMmu
695 ' %s,\n' # cMbMemory
696 ' %s,\n' # cMbScratch
697 ' %s,\n' # sReport
698 ' %s,\n' # iTestBoxScriptRev
699 ' %s,\n' # iPythonHexVersion
700 ' enmPendingCmd\n'
701 'FROM TestBoxes\n'
702 'WHERE idGenTestBox = %s\n'
703 'RETURNING idGenTestBox'
704 , (idTestBox,
705 tsEffective,
706 sTestBoxAddr,
707 sOs,
708 sOsVersion,
709 sCpuVendor,
710 sCpuArch,
711 sCpuName,
712 lCpuRevision,
713 cCpus,
714 fCpuHwVirt,
715 fCpuNestedPaging,
716 fCpu64BitGuest,
717 fChipsetIoMmu,
718 cMbMemory,
719 cMbScratch,
720 sReport,
721 iTestBoxScriptRev,
722 iPythonHexVersion,
723 idGenTestBox,
724 ));
725 idGenTestBox = self._oDb.fetchOne()[0];
726 self._oDb.commit();
727 except:
728 self._oDb.rollback();
729 raise;
730
731 return idGenTestBox;
732
733 def setCommand(self, idTestBox, sOldCommand, sNewCommand, uidAuthor = None, fCommit = False, sComment = None,
734 fNoRollbackOnInFlightCollision = False):
735 """
736 Sets or resets the pending command on a testbox.
737 Returns (idGenTestBox, tsEffective) of the new row.
738 """
739 _ = sComment;
740 try:
741 # Would be easier to do this using an insert or update hook, I think. Much easier.
742 self._oDb.execute('UPDATE ONLY TestBoxes\n'
743 'SET tsExpire = CURRENT_TIMESTAMP\n'
744 'WHERE idTestBox = %s\n'
745 ' AND tsExpire = \'infinity\'::timestamp\n'
746 ' AND enmPendingCmd = %s\n'
747 'RETURNING tsExpire\n',
748 (idTestBox, sOldCommand,));
749 if self._oDb.getRowCount() == 0:
750 raise TMInFligthCollision();
751 tsEffective = self._oDb.fetchOne()[0];
752
753 self._oDb.execute('INSERT INTO TestBoxes (\n'
754 ' idGenTestBox,\n'
755 ' idTestBox,\n'
756 ' tsEffective,\n'
757 ' uidAuthor,\n'
758 ' ip,\n'
759 ' uuidSystem,\n'
760 ' sName,\n'
761 ' sDescription,\n'
762 ' idSchedGroup,\n'
763 ' fEnabled,\n'
764 ' enmLomKind,\n'
765 ' ipLom,\n'
766 ' pctScaleTimeout,\n'
767 ' sOs,\n'
768 ' sOsVersion,\n'
769 ' sCpuVendor,\n'
770 ' sCpuArch,\n'
771 ' sCpuName,\n'
772 ' lCpuRevision,\n'
773 ' cCpus,\n'
774 ' fCpuHwVirt,\n'
775 ' fCpuNestedPaging,\n'
776 ' fCpu64BitGuest,\n'
777 ' fChipsetIoMmu,\n'
778 ' cMbMemory,\n'
779 ' cMbScratch,\n'
780 ' sReport,\n'
781 ' iTestBoxScriptRev,\n'
782 ' iPythonHexVersion,\n'
783 ' enmPendingCmd\n'
784 ' )\n'
785 'SELECT NEXTVAL(\'TestBoxGenIdSeq\'),\n'
786 ' %s,\n' # idTestBox
787 ' %s,\n' # tsEffective
788 ' %s,\n' # uidAuthor
789 ' ip,\n'
790 ' uuidSystem,\n'
791 ' sName,\n'
792 ' sDescription,\n'
793 ' idSchedGroup,\n'
794 ' fEnabled,\n'
795 ' enmLomKind,\n'
796 ' ipLom,\n'
797 ' pctScaleTimeout,\n'
798 ' sOs,\n'
799 ' sOsVersion,\n'
800 ' sCpuVendor,\n'
801 ' sCpuArch,\n'
802 ' sCpuName,\n'
803 ' lCpuRevision,\n'
804 ' cCpus,\n'
805 ' fCpuHwVirt,\n'
806 ' fCpuNestedPaging,\n'
807 ' fCpu64BitGuest,\n'
808 ' fChipsetIoMmu,\n'
809 ' cMbMemory,\n'
810 ' cMbScratch,\n'
811 ' sReport,\n'
812 ' iTestBoxScriptRev,\n'
813 ' iPythonHexVersion,\n'
814 ' %s\n' # enmPendingCmd
815 'FROM TestBoxes\n'
816 'WHERE idTestBox = %s\n'
817 ' AND tsExpire = %s\n'
818 ' AND enmPendingCmd = %s\n'
819 'RETURNING idGenTestBox'
820 , (idTestBox,
821 tsEffective,
822 uidAuthor,
823 sNewCommand,
824 idTestBox,
825 tsEffective,
826 sOldCommand,
827 ));
828 idGenTestBox = self._oDb.fetchOne()[0];
829 if fCommit is True:
830 self._oDb.commit();
831
832 except TMInFligthCollision: # This is pretty stupid, but don't want to touch testboxcontroller.py now.
833 if not fNoRollbackOnInFlightCollision:
834 self._oDb.rollback();
835 raise;
836 except:
837 self._oDb.rollback();
838 raise;
839
840 return (idGenTestBox, tsEffective);
841
842
843
844 def getAll(self):
845 """
846 Retrieve list of all registered Test Box records from DB.
847 """
848 self._oDb.execute('SELECT *\n'
849 'FROM TestBoxes\n'
850 'WHERE tsExpire=\'infinity\'::timestamp;')
851
852 aaoRows = self._oDb.fetchAll()
853 aoRet = []
854 for aoRow in aaoRows:
855 aoRet.append(TestBoxData().initFromDbRow(aoRow))
856 return aoRet
857
858 def _historize(self, idTestBox, uidAuthor, tsExpire = None):
859 """ Historizes the current entry. """
860 if tsExpire is None:
861 self._oDb.execute('UPDATE TestBoxes\n'
862 'SET tsExpire = CURRENT_TIMESTAMP,\n'
863 ' uidAuthor = %s\n'
864 'WHERE idTestBox = %s\n'
865 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
866 , (uidAuthor, idTestBox,));
867 else:
868 self._oDb.execute('UPDATE TestBoxes\n'
869 'SET tsExpire = %s,\n'
870 ' uidAuthor = %s\n'
871 'WHERE idTestBox = %s\n'
872 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
873 , (uidAuthor, tsExpire, idTestBox,));
874 return True;
875
876
877 def removeEntry(self, uidAuthor, idTestBox, fCascade = False, fCommit=False):
878 """
879 Delete user account
880 """
881 _ = fCascade;
882
883 fRc = self._historize(idTestBox, uidAuthor, None);
884 if fRc:
885 self._oDb.maybeCommit(fCommit);
886
887 return fRc
888
889
890 #
891 # The virtual test sheriff interface.
892 #
893
894 def hasTestBoxRecentlyBeenRebooted(self, idTestBox, cHoursBack = 2, tsNow = None):
895 """
896 Checks if the testbox has been rebooted in the specified time period.
897
898 This does not include already pending reboots, though under some
899 circumstances it may. These being the test box entry being edited for
900 other reasons.
901
902 Returns True / False.
903 """
904 if tsNow is None:
905 tsNow = self._oDb.getCurrentTimestamp();
906 self._oDb.execute('SELECT COUNT(idTestBox)\n'
907 'FROM TestBoxes\n'
908 'WHERE idTestBox = %s\n'
909 ' AND tsExpire < %s\n'
910 ' AND tsExpire >= %s - interval \'%u hours\'\n'
911 ' AND enmPendingCmd IN (%s, %s)\n'
912 , ( idTestBox, tsNow, tsNow, cHoursBack,
913 TestBoxData.ksTestBoxCmd_Reboot, TestBoxData.ksTestBoxCmd_UpgradeAndReboot, ));
914 return self._oDb.fetchOne()[0] > 0;
915
916
917 def rebootTestBox(self, idTestBox, uidAuthor, sComment, sOldCommand = TestBoxData.ksTestBoxCmd_None, fCommit = False):
918 """
919 Issues a reboot command for the given test box.
920 Return True on succes, False on in-flight collision.
921 May raise DB exception with rollback on other trouble.
922 """
923 try:
924 self.setCommand(idTestBox, sOldCommand, TestBoxData.ksTestBoxCmd_Reboot,
925 uidAuthor = uidAuthor, fCommit = fCommit, sComment = sComment,
926 fNoRollbackOnInFlightCollision = True);
927 except TMInFligthCollision:
928 return False;
929 except:
930 raise;
931 return True;
932
933
934 def disableTestBox(self, idTestBox, uidAuthor, sComment, fCommit = False):
935 """
936 Disables the given test box.
937
938 Raises exception on trouble, without rollback.
939 """
940 oTestBox = TestBoxData().initFromDbWithId(self._oDb, idTestBox);
941 if oTestBox.fEnabled:
942 oTestBox.fEnabled = False;
943 if sComment is not None:
944 _ = sComment; # oTestBox.sComment = sComment;
945 self.editEntry(oTestBox, uidAuthor = uidAuthor, fCommit = fCommit);
946 return None;
947
948
949#
950# Unit testing.
951#
952
953# pylint: disable=C0111
954class TestBoxDataTestCase(ModelDataBaseTestCase):
955 def setUp(self):
956 self.aoSamples = [TestBoxData(),];
957
958if __name__ == '__main__':
959 unittest.main();
960 # not reached.
961
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