VirtualBox

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

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

wuireport.py: shorter, better.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.0 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: testbox.py 61303 2016-05-30 15:25:41Z 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: 61303 $"
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 def getArchBitString(self):
273 """ Returns 32-bit, 64-bit, <none>, or sCpuArch. """
274 if self.sCpuArch is None:
275 return '<none>';
276 if self.sCpuArch in [ 'x86',]:
277 return '32-bit';
278 if self.sCpuArch in [ 'amd64',]:
279 return '64-bit';
280 return self.sCpuArch;
281
282 def getPrettyCpuVendor(self):
283 """ Returns the CPU model for a x86 or amd64 testboxes."""
284 if self.sCpuVendor is None:
285 return '<none>';
286 if self.sCpuVendor == 'GenuineIntel': return 'Intel';
287 if self.sCpuVendor == 'AuthenticAMD': return 'AMD';
288 if self.sCpuVendor == 'CentaurHauls': return 'VIA';
289 return self.sCpuVendor;
290
291
292
293class TestBoxLogic(ModelLogicBase):
294 """
295 TestBox logic.
296 """
297
298
299 def __init__(self, oDb):
300 ModelLogicBase.__init__(self, oDb);
301 self.dCache = None;
302
303 def tryFetchTestBoxByUuid(self, sTestBoxUuid):
304 """
305 Tries to fetch a testbox by its UUID alone.
306 """
307 self._oDb.execute('SELECT *\n'
308 'FROM TestBoxes\n'
309 'WHERE uuidSystem = %s\n'
310 ' AND tsExpire = \'infinity\'::timestamp\n'
311 'ORDER BY tsEffective DESC\n',
312 (sTestBoxUuid,));
313 if self._oDb.getRowCount() == 0:
314 return None;
315 if self._oDb.getRowCount() != 1:
316 raise TMTooManyRows('Database integrity error: %u hits' % (self._oDb.getRowCount(),));
317 oData = TestBoxData();
318 oData.initFromDbRow(self._oDb.fetchOne());
319 return oData;
320
321 def fetchForListing(self, iStart, cMaxRows, tsNow):
322 """
323 Fetches testboxes for listing.
324
325 Returns an array (list) of TestBoxData items, empty list if none. The
326 TestBoxData instances have an extra oStatus member that is either None or
327 a TestBoxStatusData instance, and a member tsCurrent holding
328 CURRENT_TIMESTAMP.
329
330 Raises exception on error.
331 """
332 from testmanager.core.testboxstatus import TestBoxStatusData;
333
334 if tsNow is None:
335 self._oDb.execute('SELECT TestBoxes.*, TestBoxStatuses.*\n'
336 'FROM TestBoxes\n'
337 'LEFT OUTER JOIN TestBoxStatuses ON (\n'
338 ' TestBoxStatuses.idTestBox = TestBoxes.idTestBox )\n'
339 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
340 'ORDER BY sName\n'
341 'LIMIT %s OFFSET %s\n'
342 , (cMaxRows, iStart,));
343 else:
344 self._oDb.execute('SELECT TestBoxes.*, TestBoxStatuses.*\n'
345 'FROM TestBoxes\n'
346 'LEFT OUTER JOIN TestBoxStatuses ON (\n'
347 ' TestBoxStatuses.idTestBox = TestBoxes.idTestBox )\n'
348 'WHERE tsExpire > %s\n'
349 ' AND tsEffective <= %s\n'
350 'ORDER BY sName\n'
351 'LIMIT %s OFFSET %s\n'
352 , (tsNow, tsNow, cMaxRows, iStart,));
353
354 aoRows = [];
355 for aoOne in self._oDb.fetchAll():
356 oTestBox = TestBoxData().initFromDbRow(aoOne);
357 oTestBox.tsCurrent = self._oDb.getCurrentTimestamp(); # pylint: disable=W0201
358 oTestBox.oStatus = None; # pylint: disable=W0201
359 if aoOne[31] is not None:
360 oTestBox.oStatus = TestBoxStatusData().initFromDbRow(aoOne[31:]); # pylint: disable=W0201
361 aoRows.append(oTestBox);
362 return aoRows;
363
364 def fetchForChangeLog(self, idTestBox, iStart, cMaxRows, tsNow): # pylint: disable=R0914
365 """
366 Fetches change log entries for a testbox.
367
368 Returns an array of ChangeLogEntry instance and an indicator whether
369 there are more entries.
370 Raises exception on error.
371 """
372
373 if tsNow is None:
374 tsNow = self._oDb.getCurrentTimestamp();
375
376 self._oDb.execute('SELECT TestBoxes.*, Users.sUsername\n'
377 'FROM TestBoxes\n'
378 'LEFT OUTER JOIN Users \n'
379 ' ON ( TestBoxes.uidAuthor = Users.uid\n'
380 ' AND Users.tsEffective <= TestBoxes.tsEffective\n'
381 ' AND Users.tsExpire > TestBoxes.tsEffective)\n'
382 'WHERE TestBoxes.tsEffective <= %s\n'
383 ' AND TestBoxes.idTestBox = %s\n'
384 'ORDER BY TestBoxes.tsExpire DESC\n'
385 'LIMIT %s OFFSET %s\n'
386 , (tsNow, idTestBox, cMaxRows + 1, iStart,));
387
388 aoRows = [];
389 for _ in range(self._oDb.getRowCount()):
390 oRow = self._oDb.fetchOne();
391 aoRows.append([TestBoxData().initFromDbRow(oRow), oRow[-1],]);
392
393 # Calculate the changes.
394 aoEntries = [];
395 for i in range(0, len(aoRows) - 1):
396 (oNew, sAuthor) = aoRows[i];
397 (oOld, _ ) = aoRows[i + 1];
398 aoChanges = [];
399 for sAttr in oNew.getDataAttributes():
400 if sAttr not in [ 'tsEffective', 'tsExpire', 'uidAuthor']:
401 oOldAttr = getattr(oOld, sAttr);
402 oNewAttr = getattr(oNew, sAttr);
403 if oOldAttr != oNewAttr:
404 aoChanges.append(AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
405 aoEntries.append(ChangeLogEntry(oNew.uidAuthor, sAuthor, oNew.tsEffective, oNew.tsExpire, oNew, oOld, aoChanges));
406
407 # If we're at the end of the log, add the initial entry.
408 if len(aoRows) <= cMaxRows and len(aoRows) > 0:
409 (oNew, sAuthor) = aoRows[-1];
410 aoEntries.append(ChangeLogEntry(oNew.uidAuthor, aoRows[-1][1], oNew.tsEffective, oNew.tsExpire, oNew, None, []));
411
412 return (aoEntries, len(aoRows) > cMaxRows);
413
414 def addEntry(self, oData, uidAuthor, fCommit = False):
415 """
416 Creates a testbox in the database.
417 Returns the testbox ID, testbox generation ID and effective timestamp
418 of the created testbox on success. Throws error on failure.
419 """
420 dDataErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Add);
421 if len(dDataErrors) > 0:
422 raise TMInvalidData('Invalid data passed to create(): %s' % (dDataErrors,));
423
424 self._oDb.execute('INSERT INTO TestBoxes (\n'
425 ' idTestBox,\n'
426 ' tsEffective,\n'
427 ' tsExpire,\n'
428 ' uidAuthor,\n'
429 ' idGenTestBox,\n'
430 ' ip,\n'
431 ' uuidSystem,\n'
432 ' sName,\n'
433 ' sDescription,\n'
434 ' idSchedGroup,\n'
435 ' fEnabled,\n'
436 ' enmLomKind,\n'
437 ' ipLom,\n'
438 ' pctScaleTimeout,\n'
439 ' sOs,\n'
440 ' sOsVersion,\n'
441 ' sCpuVendor,\n'
442 ' sCpuArch,\n'
443 ' sCpuName,\n'
444 ' lCpuRevision,\n'
445 ' cCpus,\n'
446 ' fCpuHwVirt,\n'
447 ' fCpuNestedPaging,\n'
448 ' fCpu64BitGuest,\n'
449 ' fChipsetIoMmu,\n'
450 ' cMbMemory,\n'
451 ' cMbScratch,\n'
452 ' sReport,\n'
453 ' iTestBoxScriptRev,\n'
454 ' iPythonHexVersion,\n'
455 ' enmPendingCmd\n'
456 ' )'
457 'VALUES (\n'
458 ' DEFAULT,\n'
459 ' CURRENT_TIMESTAMP,\n'
460 ' DEFAULT,\n'
461 ' %s,\n' # uidAuthor
462 ' DEFAULT,\n'
463 ' %s,\n' # ip
464 ' %s,\n' # uuidSystem
465 ' %s,\n' # sName
466 ' %s,\n' # sDescription
467 ' %s,\n' # idSchedGroup
468 ' %s,\n' # fEnabled
469 ' %s,\n' # enmLomKind
470 ' %s,\n' # ipLom
471 ' %s,\n' # pctScaleTimeout
472 ' %s,\n' # sOs
473 ' %s,\n' # sOsVersion
474 ' %s,\n' # sCpuVendor
475 ' %s,\n' # sCpuArch
476 ' %s,\n' # sCpuName
477 ' %s,\n' # lCpuRevision
478 ' %s,\n' # cCpus
479 ' %s,\n' # fCpuHwVirt
480 ' %s,\n' # fCpuNestedPaging
481 ' %s,\n' # fCpu64BitGuest
482 ' %s,\n' # fChipsetIoMmu
483 ' %s,\n' # cMbMemory
484 ' %s,\n' # cMbScratch
485 ' %s,\n' # sReport
486 ' %s,\n' # iTestBoxScriptRev
487 ' %s,\n' # iPythonHexVersion
488 ' %s\n' # enmPendingCmd
489 ' )\n'
490 'RETURNING idTestBox, idGenTestBox, tsEffective'
491 , (uidAuthor,
492 oData.ip,
493 oData.uuidSystem,
494 oData.sName,
495 oData.sDescription,
496 oData.idSchedGroup,
497 oData.fEnabled,
498 oData.enmLomKind,
499 oData.ipLom,
500 oData.pctScaleTimeout,
501 oData.sOs,
502 oData.sOsVersion,
503 oData.sCpuVendor,
504 oData.sCpuArch,
505 oData.sCpuName,
506 oData.lCpuRevision,
507 oData.cCpus,
508 oData.fCpuHwVirt,
509 oData.fCpuNestedPaging,
510 oData.fCpu64BitGuest,
511 oData.fChipsetIoMmu,
512 oData.cMbMemory,
513 oData.cMbScratch,
514 oData.sReport,
515 oData.iTestBoxScriptRev,
516 oData.iPythonHexVersion,
517 oData.enmPendingCmd
518 )
519 );
520 oRow = self._oDb.fetchOne();
521 self._oDb.maybeCommit(fCommit);
522 return (oRow[0], oRow[1], oRow[2]);
523
524 def editEntry(self, oData, uidAuthor, fCommit = False):
525 """
526 Data edit update, web UI is the primary user.
527 Returns the new generation ID and effective date.
528 """
529
530 dDataErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Edit);
531 if len(dDataErrors) > 0:
532 raise TMInvalidData('Invalid data passed to create(): %s' % (dDataErrors,));
533
534 ## @todo check if the data changed.
535
536 self._oDb.execute('UPDATE ONLY TestBoxes\n'
537 'SET tsExpire = CURRENT_TIMESTAMP\n'
538 'WHERE idGenTestBox = %s\n'
539 ' AND tsExpire = \'infinity\'::timestamp\n'
540 'RETURNING tsExpire\n',
541 (oData.idGenTestBox,));
542 try:
543 tsEffective = self._oDb.fetchOne()[0];
544
545 # Would be easier to do this using an insert or update hook, I think. Much easier.
546
547 ##
548 ## @todo The table is growing too fast. Rows are too long. Mixing data from here and there. Split it and
549 ## rethink storage and update strategy!
550 ##
551
552 self._oDb.execute('INSERT INTO TestBoxes (\n'
553 ' idGenTestBox,\n'
554 ' idTestBox,\n'
555 ' tsEffective,\n'
556 ' uidAuthor,\n'
557 ' ip,\n'
558 ' uuidSystem,\n'
559 ' sName,\n'
560 ' sDescription,\n'
561 ' idSchedGroup,\n'
562 ' fEnabled,\n'
563 ' enmLomKind,\n'
564 ' ipLom,\n'
565 ' pctScaleTimeout,\n'
566 ' sOs,\n'
567 ' sOsVersion,\n'
568 ' sCpuVendor,\n'
569 ' sCpuArch,\n'
570 ' sCpuName,\n'
571 ' lCpuRevision,\n'
572 ' cCpus,\n'
573 ' fCpuHwVirt,\n'
574 ' fCpuNestedPaging,\n'
575 ' fCpu64BitGuest,\n'
576 ' fChipsetIoMmu,\n'
577 ' cMbMemory,\n'
578 ' cMbScratch,\n'
579 ' sReport,\n'
580 ' iTestBoxScriptRev,\n'
581 ' iPythonHexVersion,\n'
582 ' enmPendingCmd\n'
583 ' )\n'
584 'SELECT NEXTVAL(\'TestBoxGenIdSeq\'),\n'
585 ' idTestBox,\n'
586 ' %s,\n' # tsEffective
587 ' %s,\n' # uidAuthor
588 ' %s,\n' # ip
589 ' %s,\n' # uuidSystem
590 ' %s,\n' # sName
591 ' %s,\n' # sDescription
592 ' %s,\n' # idSchedGroup
593 ' %s,\n' # fEnabled
594 ' %s,\n' # enmLomKind
595 ' %s,\n' # ipLom
596 ' %s,\n' # pctScaleTimeout
597 ' sOs,\n'
598 ' sOsVersion,\n'
599 ' sCpuVendor,\n'
600 ' sCpuArch,\n'
601 ' sCpuName,\n'
602 ' lCpuRevision,\n'
603 ' cCpus,\n'
604 ' fCpuHwVirt,\n'
605 ' fCpuNestedPaging,\n'
606 ' fCpu64BitGuest,\n'
607 ' fChipsetIoMmu,\n'
608 ' cMbMemory,\n'
609 ' cMbScratch,\n'
610 ' sReport,\n'
611 ' iTestBoxScriptRev,\n'
612 ' iPythonHexVersion,\n'
613 ' %s\n' # enmPendingCmd
614 'FROM TestBoxes\n'
615 'WHERE idGenTestBox = %s\n'
616 'RETURNING idGenTestBox, tsEffective'
617 , (tsEffective,
618 uidAuthor,
619 oData.ip,
620 oData.uuidSystem,
621 oData.sName,
622 oData.sDescription,
623 oData.idSchedGroup,
624 oData.fEnabled,
625 oData.enmLomKind,
626 oData.ipLom,
627 oData.pctScaleTimeout,
628 oData.enmPendingCmd,
629 oData.idGenTestBox,
630 ));
631 aRow = self._oDb.fetchOne();
632 if aRow is None:
633 raise TMExceptionBase('Insert failed? oRow=None');
634 idGenTestBox = aRow[0];
635 tsEffective = aRow[1];
636 self._oDb.maybeCommit(fCommit);
637 except:
638 self._oDb.rollback();
639 raise;
640
641 return (idGenTestBox, tsEffective);
642
643 def updateOnSignOn(self, idTestBox, idGenTestBox, sTestBoxAddr, sOs, sOsVersion, # pylint: disable=R0913,R0914
644 sCpuVendor, sCpuArch, sCpuName, lCpuRevision, cCpus, fCpuHwVirt, fCpuNestedPaging, fCpu64BitGuest,
645 fChipsetIoMmu, cMbMemory, cMbScratch, sReport, iTestBoxScriptRev, iPythonHexVersion):
646 """
647 Update the testbox attributes automatically on behalf of the testbox script.
648 Returns the new generation id on success, raises an exception on failure.
649 """
650 try:
651 # Would be easier to do this using an insert or update hook, I think. Much easier.
652 self._oDb.execute('UPDATE ONLY TestBoxes\n'
653 'SET tsExpire = CURRENT_TIMESTAMP\n'
654 'WHERE idGenTestBox = %s\n'
655 ' AND tsExpire = \'infinity\'::timestamp\n'
656 'RETURNING tsExpire\n',
657 (idGenTestBox,));
658 tsEffective = self._oDb.fetchOne()[0];
659
660 self._oDb.execute('INSERT INTO TestBoxes (\n'
661 ' idGenTestBox,\n'
662 ' idTestBox,\n'
663 ' tsEffective,\n'
664 ' uidAuthor,\n'
665 ' ip,\n'
666 ' uuidSystem,\n'
667 ' sName,\n'
668 ' sDescription,\n'
669 ' idSchedGroup,\n'
670 ' fEnabled,\n'
671 ' enmLomKind,\n'
672 ' ipLom,\n'
673 ' pctScaleTimeout,\n'
674 ' sOs,\n'
675 ' sOsVersion,\n'
676 ' sCpuVendor,\n'
677 ' sCpuArch,\n'
678 ' sCpuName,\n'
679 ' lCpuRevision,\n'
680 ' cCpus,\n'
681 ' fCpuHwVirt,\n'
682 ' fCpuNestedPaging,\n'
683 ' fCpu64BitGuest,\n'
684 ' fChipsetIoMmu,\n'
685 ' cMbMemory,\n'
686 ' cMbScratch,\n'
687 ' sReport,\n'
688 ' iTestBoxScriptRev,\n'
689 ' iPythonHexVersion,\n'
690 ' enmPendingCmd\n'
691 ' )\n'
692 'SELECT NEXTVAL(\'TestBoxGenIdSeq\'),\n'
693 ' %s,\n'
694 ' %s,\n'
695 ' NULL,\n' # uidAuthor
696 ' %s,\n'
697 ' uuidSystem,\n'
698 ' sName,\n'
699 ' sDescription,\n'
700 ' idSchedGroup,\n'
701 ' fEnabled,\n'
702 ' enmLomKind,\n'
703 ' ipLom,\n'
704 ' pctScaleTimeout,\n'
705 ' %s,\n' # sOs
706 ' %s,\n' # sOsVersion
707 ' %s,\n' # sCpuVendor
708 ' %s,\n' # sCpuArch
709 ' %s,\n' # sCpuName
710 ' %s,\n' # lCpuRevision
711 ' %s,\n' # cCpus
712 ' %s,\n' # fCpuHwVirt
713 ' %s,\n' # fCpuNestedPaging
714 ' %s,\n' # fCpu64BitGuest
715 ' %s,\n' # fChipsetIoMmu
716 ' %s,\n' # cMbMemory
717 ' %s,\n' # cMbScratch
718 ' %s,\n' # sReport
719 ' %s,\n' # iTestBoxScriptRev
720 ' %s,\n' # iPythonHexVersion
721 ' enmPendingCmd\n'
722 'FROM TestBoxes\n'
723 'WHERE idGenTestBox = %s\n'
724 'RETURNING idGenTestBox'
725 , (idTestBox,
726 tsEffective,
727 sTestBoxAddr,
728 sOs,
729 sOsVersion,
730 sCpuVendor,
731 sCpuArch,
732 sCpuName,
733 lCpuRevision,
734 cCpus,
735 fCpuHwVirt,
736 fCpuNestedPaging,
737 fCpu64BitGuest,
738 fChipsetIoMmu,
739 cMbMemory,
740 cMbScratch,
741 sReport,
742 iTestBoxScriptRev,
743 iPythonHexVersion,
744 idGenTestBox,
745 ));
746 idGenTestBox = self._oDb.fetchOne()[0];
747 self._oDb.commit();
748 except:
749 self._oDb.rollback();
750 raise;
751
752 return idGenTestBox;
753
754 def setCommand(self, idTestBox, sOldCommand, sNewCommand, uidAuthor = None, fCommit = False, sComment = None,
755 fNoRollbackOnInFlightCollision = False):
756 """
757 Sets or resets the pending command on a testbox.
758 Returns (idGenTestBox, tsEffective) of the new row.
759 """
760 _ = sComment;
761 try:
762 # Would be easier to do this using an insert or update hook, I think. Much easier.
763 self._oDb.execute('UPDATE ONLY TestBoxes\n'
764 'SET tsExpire = CURRENT_TIMESTAMP\n'
765 'WHERE idTestBox = %s\n'
766 ' AND tsExpire = \'infinity\'::timestamp\n'
767 ' AND enmPendingCmd = %s\n'
768 'RETURNING tsExpire\n',
769 (idTestBox, sOldCommand,));
770 if self._oDb.getRowCount() == 0:
771 raise TMInFligthCollision();
772 tsEffective = self._oDb.fetchOne()[0];
773
774 self._oDb.execute('INSERT INTO TestBoxes (\n'
775 ' idGenTestBox,\n'
776 ' idTestBox,\n'
777 ' tsEffective,\n'
778 ' uidAuthor,\n'
779 ' ip,\n'
780 ' uuidSystem,\n'
781 ' sName,\n'
782 ' sDescription,\n'
783 ' idSchedGroup,\n'
784 ' fEnabled,\n'
785 ' enmLomKind,\n'
786 ' ipLom,\n'
787 ' pctScaleTimeout,\n'
788 ' sOs,\n'
789 ' sOsVersion,\n'
790 ' sCpuVendor,\n'
791 ' sCpuArch,\n'
792 ' sCpuName,\n'
793 ' lCpuRevision,\n'
794 ' cCpus,\n'
795 ' fCpuHwVirt,\n'
796 ' fCpuNestedPaging,\n'
797 ' fCpu64BitGuest,\n'
798 ' fChipsetIoMmu,\n'
799 ' cMbMemory,\n'
800 ' cMbScratch,\n'
801 ' sReport,\n'
802 ' iTestBoxScriptRev,\n'
803 ' iPythonHexVersion,\n'
804 ' enmPendingCmd\n'
805 ' )\n'
806 'SELECT NEXTVAL(\'TestBoxGenIdSeq\'),\n'
807 ' %s,\n' # idTestBox
808 ' %s,\n' # tsEffective
809 ' %s,\n' # uidAuthor
810 ' ip,\n'
811 ' uuidSystem,\n'
812 ' sName,\n'
813 ' sDescription,\n'
814 ' idSchedGroup,\n'
815 ' fEnabled,\n'
816 ' enmLomKind,\n'
817 ' ipLom,\n'
818 ' pctScaleTimeout,\n'
819 ' sOs,\n'
820 ' sOsVersion,\n'
821 ' sCpuVendor,\n'
822 ' sCpuArch,\n'
823 ' sCpuName,\n'
824 ' lCpuRevision,\n'
825 ' cCpus,\n'
826 ' fCpuHwVirt,\n'
827 ' fCpuNestedPaging,\n'
828 ' fCpu64BitGuest,\n'
829 ' fChipsetIoMmu,\n'
830 ' cMbMemory,\n'
831 ' cMbScratch,\n'
832 ' sReport,\n'
833 ' iTestBoxScriptRev,\n'
834 ' iPythonHexVersion,\n'
835 ' %s\n' # enmPendingCmd
836 'FROM TestBoxes\n'
837 'WHERE idTestBox = %s\n'
838 ' AND tsExpire = %s\n'
839 ' AND enmPendingCmd = %s\n'
840 'RETURNING idGenTestBox'
841 , (idTestBox,
842 tsEffective,
843 uidAuthor,
844 sNewCommand,
845 idTestBox,
846 tsEffective,
847 sOldCommand,
848 ));
849 idGenTestBox = self._oDb.fetchOne()[0];
850 if fCommit is True:
851 self._oDb.commit();
852
853 except TMInFligthCollision: # This is pretty stupid, but don't want to touch testboxcontroller.py now.
854 if not fNoRollbackOnInFlightCollision:
855 self._oDb.rollback();
856 raise;
857 except:
858 self._oDb.rollback();
859 raise;
860
861 return (idGenTestBox, tsEffective);
862
863
864
865 def getAll(self):
866 """
867 Retrieve list of all registered Test Box records from DB.
868 """
869 self._oDb.execute('SELECT *\n'
870 'FROM TestBoxes\n'
871 'WHERE tsExpire=\'infinity\'::timestamp;')
872
873 aaoRows = self._oDb.fetchAll()
874 aoRet = []
875 for aoRow in aaoRows:
876 aoRet.append(TestBoxData().initFromDbRow(aoRow))
877 return aoRet
878
879 def _historize(self, idTestBox, uidAuthor, tsExpire = None):
880 """ Historizes the current entry. """
881 if tsExpire is None:
882 self._oDb.execute('UPDATE TestBoxes\n'
883 'SET tsExpire = CURRENT_TIMESTAMP,\n'
884 ' uidAuthor = %s\n'
885 'WHERE idTestBox = %s\n'
886 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
887 , (uidAuthor, idTestBox,));
888 else:
889 self._oDb.execute('UPDATE TestBoxes\n'
890 'SET tsExpire = %s,\n'
891 ' uidAuthor = %s\n'
892 'WHERE idTestBox = %s\n'
893 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
894 , (uidAuthor, tsExpire, idTestBox,));
895 return True;
896
897
898 def removeEntry(self, uidAuthor, idTestBox, fCascade = False, fCommit=False):
899 """
900 Delete user account
901 """
902 _ = fCascade;
903
904 fRc = self._historize(idTestBox, uidAuthor, None);
905 if fRc:
906 self._oDb.maybeCommit(fCommit);
907
908 return fRc
909
910
911 def cachedLookup(self, idTestBox):
912 """
913 Looks up the most recent TestBoxData object for idTestBox via
914 an object cache.
915
916 Returns a shared TestBoxData object. None if not found.
917 Raises exception on DB error.
918 """
919 if self.dCache is None:
920 self.dCache = self._oDb.getCache('TestBoxData');
921 oEntry = self.dCache.get(idTestBox, None);
922 if oEntry is None:
923 self._oDb.execute('SELECT *\n'
924 'FROM TestBoxes\n'
925 'WHERE idTestBox = %s\n'
926 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
927 , (idTestBox, ));
928 if self._oDb.getRowCount() == 0:
929 # Maybe it was deleted, try get the last entry.
930 self._oDb.execute('SELECT *\n'
931 'FROM TestBoxes\n'
932 'WHERE idTestBox = %s\n'
933 'ORDER BY tsExpire DESC\n'
934 'LIMIT 1\n'
935 , (idTestBox, ));
936 elif self._oDb.getRowCount() > 1:
937 raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), idTestBox));
938
939 if self._oDb.getRowCount() == 1:
940 aaoRow = self._oDb.fetchOne();
941 oEntry = TestBoxData().initFromDbRow(aaoRow);
942 self.dCache[idTestBox] = oEntry;
943 return oEntry;
944
945
946
947 #
948 # The virtual test sheriff interface.
949 #
950
951 def hasTestBoxRecentlyBeenRebooted(self, idTestBox, cHoursBack = 2, tsNow = None):
952 """
953 Checks if the testbox has been rebooted in the specified time period.
954
955 This does not include already pending reboots, though under some
956 circumstances it may. These being the test box entry being edited for
957 other reasons.
958
959 Returns True / False.
960 """
961 if tsNow is None:
962 tsNow = self._oDb.getCurrentTimestamp();
963 self._oDb.execute('SELECT COUNT(idTestBox)\n'
964 'FROM TestBoxes\n'
965 'WHERE idTestBox = %s\n'
966 ' AND tsExpire < %s\n'
967 ' AND tsExpire >= %s - interval \'%s hours\'\n'
968 ' AND enmPendingCmd IN (%s, %s)\n'
969 , ( idTestBox, tsNow, tsNow, cHoursBack,
970 TestBoxData.ksTestBoxCmd_Reboot, TestBoxData.ksTestBoxCmd_UpgradeAndReboot, ));
971 return self._oDb.fetchOne()[0] > 0;
972
973
974 def rebootTestBox(self, idTestBox, uidAuthor, sComment, sOldCommand = TestBoxData.ksTestBoxCmd_None, fCommit = False):
975 """
976 Issues a reboot command for the given test box.
977 Return True on succes, False on in-flight collision.
978 May raise DB exception with rollback on other trouble.
979 """
980 try:
981 self.setCommand(idTestBox, sOldCommand, TestBoxData.ksTestBoxCmd_Reboot,
982 uidAuthor = uidAuthor, fCommit = fCommit, sComment = sComment,
983 fNoRollbackOnInFlightCollision = True);
984 except TMInFligthCollision:
985 return False;
986 except:
987 raise;
988 return True;
989
990
991 def disableTestBox(self, idTestBox, uidAuthor, sComment, fCommit = False):
992 """
993 Disables the given test box.
994
995 Raises exception on trouble, without rollback.
996 """
997 oTestBox = TestBoxData().initFromDbWithId(self._oDb, idTestBox);
998 if oTestBox.fEnabled:
999 oTestBox.fEnabled = False;
1000 if sComment is not None:
1001 _ = sComment; # oTestBox.sComment = sComment;
1002 self.editEntry(oTestBox, uidAuthor = uidAuthor, fCommit = fCommit);
1003 return None;
1004
1005
1006#
1007# Unit testing.
1008#
1009
1010# pylint: disable=C0111
1011class TestBoxDataTestCase(ModelDataBaseTestCase):
1012 def setUp(self):
1013 self.aoSamples = [TestBoxData(),];
1014
1015if __name__ == '__main__':
1016 unittest.main();
1017 # not reached.
1018
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