VirtualBox

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

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

wuireport.py: march

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