VirtualBox

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

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

upper case hex fam/mod/step.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.9 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: testbox.py 61308 2016-05-30 16:29:42Z 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: 61308 $"
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 getPrettyCpuVersion(self):
370 """ Pretty formatting of the family/model/stepping with microarch optimizations. """
371 if self.lCpuRevision is None or self.sCpuVendor is None:
372 return u'<none>';
373 sMarch = self.queryCpuMicroarch();
374 if sMarch is not None:
375 return '%s m%02X s%02X' % (sMarch, self.getCpuModel(), self.getCpuStepping());
376 return 'fam%02X m%02X s%02X' % (self.getCpuFamily(), self.getCpuModel(), self.getCpuStepping());
377
378 def getArchBitString(self):
379 """ Returns 32-bit, 64-bit, <none>, or sCpuArch. """
380 if self.sCpuArch is None:
381 return '<none>';
382 if self.sCpuArch in [ 'x86',]:
383 return '32-bit';
384 if self.sCpuArch in [ 'amd64',]:
385 return '64-bit';
386 return self.sCpuArch;
387
388 def getPrettyCpuVendor(self):
389 """ Pretty vendor name."""
390 if self.sCpuVendor is None:
391 return '<none>';
392 if self.sCpuVendor == 'GenuineIntel': return 'Intel';
393 if self.sCpuVendor == 'AuthenticAMD': return 'AMD';
394 if self.sCpuVendor == 'CentaurHauls': return 'VIA';
395 return self.sCpuVendor;
396
397
398
399class TestBoxLogic(ModelLogicBase):
400 """
401 TestBox logic.
402 """
403
404
405 def __init__(self, oDb):
406 ModelLogicBase.__init__(self, oDb);
407 self.dCache = None;
408
409 def tryFetchTestBoxByUuid(self, sTestBoxUuid):
410 """
411 Tries to fetch a testbox by its UUID alone.
412 """
413 self._oDb.execute('SELECT *\n'
414 'FROM TestBoxes\n'
415 'WHERE uuidSystem = %s\n'
416 ' AND tsExpire = \'infinity\'::timestamp\n'
417 'ORDER BY tsEffective DESC\n',
418 (sTestBoxUuid,));
419 if self._oDb.getRowCount() == 0:
420 return None;
421 if self._oDb.getRowCount() != 1:
422 raise TMTooManyRows('Database integrity error: %u hits' % (self._oDb.getRowCount(),));
423 oData = TestBoxData();
424 oData.initFromDbRow(self._oDb.fetchOne());
425 return oData;
426
427 def fetchForListing(self, iStart, cMaxRows, tsNow):
428 """
429 Fetches testboxes for listing.
430
431 Returns an array (list) of TestBoxData items, empty list if none. The
432 TestBoxData instances have an extra oStatus member that is either None or
433 a TestBoxStatusData instance, and a member tsCurrent holding
434 CURRENT_TIMESTAMP.
435
436 Raises exception on error.
437 """
438 from testmanager.core.testboxstatus import TestBoxStatusData;
439
440 if tsNow is None:
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 = \'infinity\'::TIMESTAMP\n'
446 'ORDER BY sName\n'
447 'LIMIT %s OFFSET %s\n'
448 , (cMaxRows, iStart,));
449 else:
450 self._oDb.execute('SELECT TestBoxes.*, TestBoxStatuses.*\n'
451 'FROM TestBoxes\n'
452 'LEFT OUTER JOIN TestBoxStatuses ON (\n'
453 ' TestBoxStatuses.idTestBox = TestBoxes.idTestBox )\n'
454 'WHERE tsExpire > %s\n'
455 ' AND tsEffective <= %s\n'
456 'ORDER BY sName\n'
457 'LIMIT %s OFFSET %s\n'
458 , (tsNow, tsNow, cMaxRows, iStart,));
459
460 aoRows = [];
461 for aoOne in self._oDb.fetchAll():
462 oTestBox = TestBoxData().initFromDbRow(aoOne);
463 oTestBox.tsCurrent = self._oDb.getCurrentTimestamp(); # pylint: disable=W0201
464 oTestBox.oStatus = None; # pylint: disable=W0201
465 if aoOne[31] is not None:
466 oTestBox.oStatus = TestBoxStatusData().initFromDbRow(aoOne[31:]); # pylint: disable=W0201
467 aoRows.append(oTestBox);
468 return aoRows;
469
470 def fetchForChangeLog(self, idTestBox, iStart, cMaxRows, tsNow): # pylint: disable=R0914
471 """
472 Fetches change log entries for a testbox.
473
474 Returns an array of ChangeLogEntry instance and an indicator whether
475 there are more entries.
476 Raises exception on error.
477 """
478
479 if tsNow is None:
480 tsNow = self._oDb.getCurrentTimestamp();
481
482 self._oDb.execute('SELECT TestBoxes.*, Users.sUsername\n'
483 'FROM TestBoxes\n'
484 'LEFT OUTER JOIN Users \n'
485 ' ON ( TestBoxes.uidAuthor = Users.uid\n'
486 ' AND Users.tsEffective <= TestBoxes.tsEffective\n'
487 ' AND Users.tsExpire > TestBoxes.tsEffective)\n'
488 'WHERE TestBoxes.tsEffective <= %s\n'
489 ' AND TestBoxes.idTestBox = %s\n'
490 'ORDER BY TestBoxes.tsExpire DESC\n'
491 'LIMIT %s OFFSET %s\n'
492 , (tsNow, idTestBox, cMaxRows + 1, iStart,));
493
494 aoRows = [];
495 for _ in range(self._oDb.getRowCount()):
496 oRow = self._oDb.fetchOne();
497 aoRows.append([TestBoxData().initFromDbRow(oRow), oRow[-1],]);
498
499 # Calculate the changes.
500 aoEntries = [];
501 for i in range(0, len(aoRows) - 1):
502 (oNew, sAuthor) = aoRows[i];
503 (oOld, _ ) = aoRows[i + 1];
504 aoChanges = [];
505 for sAttr in oNew.getDataAttributes():
506 if sAttr not in [ 'tsEffective', 'tsExpire', 'uidAuthor']:
507 oOldAttr = getattr(oOld, sAttr);
508 oNewAttr = getattr(oNew, sAttr);
509 if oOldAttr != oNewAttr:
510 aoChanges.append(AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
511 aoEntries.append(ChangeLogEntry(oNew.uidAuthor, sAuthor, oNew.tsEffective, oNew.tsExpire, oNew, oOld, aoChanges));
512
513 # If we're at the end of the log, add the initial entry.
514 if len(aoRows) <= cMaxRows and len(aoRows) > 0:
515 (oNew, sAuthor) = aoRows[-1];
516 aoEntries.append(ChangeLogEntry(oNew.uidAuthor, aoRows[-1][1], oNew.tsEffective, oNew.tsExpire, oNew, None, []));
517
518 return (aoEntries, len(aoRows) > cMaxRows);
519
520 def addEntry(self, oData, uidAuthor, fCommit = False):
521 """
522 Creates a testbox in the database.
523 Returns the testbox ID, testbox generation ID and effective timestamp
524 of the created testbox on success. Throws error on failure.
525 """
526 dDataErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Add);
527 if len(dDataErrors) > 0:
528 raise TMInvalidData('Invalid data passed to create(): %s' % (dDataErrors,));
529
530 self._oDb.execute('INSERT INTO TestBoxes (\n'
531 ' idTestBox,\n'
532 ' tsEffective,\n'
533 ' tsExpire,\n'
534 ' uidAuthor,\n'
535 ' idGenTestBox,\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 ' )'
563 'VALUES (\n'
564 ' DEFAULT,\n'
565 ' CURRENT_TIMESTAMP,\n'
566 ' DEFAULT,\n'
567 ' %s,\n' # uidAuthor
568 ' DEFAULT,\n'
569 ' %s,\n' # ip
570 ' %s,\n' # uuidSystem
571 ' %s,\n' # sName
572 ' %s,\n' # sDescription
573 ' %s,\n' # idSchedGroup
574 ' %s,\n' # fEnabled
575 ' %s,\n' # enmLomKind
576 ' %s,\n' # ipLom
577 ' %s,\n' # pctScaleTimeout
578 ' %s,\n' # sOs
579 ' %s,\n' # sOsVersion
580 ' %s,\n' # sCpuVendor
581 ' %s,\n' # sCpuArch
582 ' %s,\n' # sCpuName
583 ' %s,\n' # lCpuRevision
584 ' %s,\n' # cCpus
585 ' %s,\n' # fCpuHwVirt
586 ' %s,\n' # fCpuNestedPaging
587 ' %s,\n' # fCpu64BitGuest
588 ' %s,\n' # fChipsetIoMmu
589 ' %s,\n' # cMbMemory
590 ' %s,\n' # cMbScratch
591 ' %s,\n' # sReport
592 ' %s,\n' # iTestBoxScriptRev
593 ' %s,\n' # iPythonHexVersion
594 ' %s\n' # enmPendingCmd
595 ' )\n'
596 'RETURNING idTestBox, idGenTestBox, 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.sOs,
608 oData.sOsVersion,
609 oData.sCpuVendor,
610 oData.sCpuArch,
611 oData.sCpuName,
612 oData.lCpuRevision,
613 oData.cCpus,
614 oData.fCpuHwVirt,
615 oData.fCpuNestedPaging,
616 oData.fCpu64BitGuest,
617 oData.fChipsetIoMmu,
618 oData.cMbMemory,
619 oData.cMbScratch,
620 oData.sReport,
621 oData.iTestBoxScriptRev,
622 oData.iPythonHexVersion,
623 oData.enmPendingCmd
624 )
625 );
626 oRow = self._oDb.fetchOne();
627 self._oDb.maybeCommit(fCommit);
628 return (oRow[0], oRow[1], oRow[2]);
629
630 def editEntry(self, oData, uidAuthor, fCommit = False):
631 """
632 Data edit update, web UI is the primary user.
633 Returns the new generation ID and effective date.
634 """
635
636 dDataErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Edit);
637 if len(dDataErrors) > 0:
638 raise TMInvalidData('Invalid data passed to create(): %s' % (dDataErrors,));
639
640 ## @todo check if the data changed.
641
642 self._oDb.execute('UPDATE ONLY TestBoxes\n'
643 'SET tsExpire = CURRENT_TIMESTAMP\n'
644 'WHERE idGenTestBox = %s\n'
645 ' AND tsExpire = \'infinity\'::timestamp\n'
646 'RETURNING tsExpire\n',
647 (oData.idGenTestBox,));
648 try:
649 tsEffective = self._oDb.fetchOne()[0];
650
651 # Would be easier to do this using an insert or update hook, I think. Much easier.
652
653 ##
654 ## @todo The table is growing too fast. Rows are too long. Mixing data from here and there. Split it and
655 ## rethink storage and update strategy!
656 ##
657
658 self._oDb.execute('INSERT INTO TestBoxes (\n'
659 ' idGenTestBox,\n'
660 ' idTestBox,\n'
661 ' tsEffective,\n'
662 ' uidAuthor,\n'
663 ' ip,\n'
664 ' uuidSystem,\n'
665 ' sName,\n'
666 ' sDescription,\n'
667 ' idSchedGroup,\n'
668 ' fEnabled,\n'
669 ' enmLomKind,\n'
670 ' ipLom,\n'
671 ' pctScaleTimeout,\n'
672 ' sOs,\n'
673 ' sOsVersion,\n'
674 ' sCpuVendor,\n'
675 ' sCpuArch,\n'
676 ' sCpuName,\n'
677 ' lCpuRevision,\n'
678 ' cCpus,\n'
679 ' fCpuHwVirt,\n'
680 ' fCpuNestedPaging,\n'
681 ' fCpu64BitGuest,\n'
682 ' fChipsetIoMmu,\n'
683 ' cMbMemory,\n'
684 ' cMbScratch,\n'
685 ' sReport,\n'
686 ' iTestBoxScriptRev,\n'
687 ' iPythonHexVersion,\n'
688 ' enmPendingCmd\n'
689 ' )\n'
690 'SELECT NEXTVAL(\'TestBoxGenIdSeq\'),\n'
691 ' idTestBox,\n'
692 ' %s,\n' # tsEffective
693 ' %s,\n' # uidAuthor
694 ' %s,\n' # ip
695 ' %s,\n' # uuidSystem
696 ' %s,\n' # sName
697 ' %s,\n' # sDescription
698 ' %s,\n' # idSchedGroup
699 ' %s,\n' # fEnabled
700 ' %s,\n' # enmLomKind
701 ' %s,\n' # ipLom
702 ' %s,\n' # pctScaleTimeout
703 ' sOs,\n'
704 ' sOsVersion,\n'
705 ' sCpuVendor,\n'
706 ' sCpuArch,\n'
707 ' sCpuName,\n'
708 ' lCpuRevision,\n'
709 ' cCpus,\n'
710 ' fCpuHwVirt,\n'
711 ' fCpuNestedPaging,\n'
712 ' fCpu64BitGuest,\n'
713 ' fChipsetIoMmu,\n'
714 ' cMbMemory,\n'
715 ' cMbScratch,\n'
716 ' sReport,\n'
717 ' iTestBoxScriptRev,\n'
718 ' iPythonHexVersion,\n'
719 ' %s\n' # enmPendingCmd
720 'FROM TestBoxes\n'
721 'WHERE idGenTestBox = %s\n'
722 'RETURNING idGenTestBox, tsEffective'
723 , (tsEffective,
724 uidAuthor,
725 oData.ip,
726 oData.uuidSystem,
727 oData.sName,
728 oData.sDescription,
729 oData.idSchedGroup,
730 oData.fEnabled,
731 oData.enmLomKind,
732 oData.ipLom,
733 oData.pctScaleTimeout,
734 oData.enmPendingCmd,
735 oData.idGenTestBox,
736 ));
737 aRow = self._oDb.fetchOne();
738 if aRow is None:
739 raise TMExceptionBase('Insert failed? oRow=None');
740 idGenTestBox = aRow[0];
741 tsEffective = aRow[1];
742 self._oDb.maybeCommit(fCommit);
743 except:
744 self._oDb.rollback();
745 raise;
746
747 return (idGenTestBox, tsEffective);
748
749 def updateOnSignOn(self, idTestBox, idGenTestBox, sTestBoxAddr, sOs, sOsVersion, # pylint: disable=R0913,R0914
750 sCpuVendor, sCpuArch, sCpuName, lCpuRevision, cCpus, fCpuHwVirt, fCpuNestedPaging, fCpu64BitGuest,
751 fChipsetIoMmu, cMbMemory, cMbScratch, sReport, iTestBoxScriptRev, iPythonHexVersion):
752 """
753 Update the testbox attributes automatically on behalf of the testbox script.
754 Returns the new generation id on success, raises an exception on failure.
755 """
756 try:
757 # Would be easier to do this using an insert or update hook, I think. Much easier.
758 self._oDb.execute('UPDATE ONLY TestBoxes\n'
759 'SET tsExpire = CURRENT_TIMESTAMP\n'
760 'WHERE idGenTestBox = %s\n'
761 ' AND tsExpire = \'infinity\'::timestamp\n'
762 'RETURNING tsExpire\n',
763 (idGenTestBox,));
764 tsEffective = self._oDb.fetchOne()[0];
765
766 self._oDb.execute('INSERT INTO TestBoxes (\n'
767 ' idGenTestBox,\n'
768 ' idTestBox,\n'
769 ' tsEffective,\n'
770 ' uidAuthor,\n'
771 ' ip,\n'
772 ' uuidSystem,\n'
773 ' sName,\n'
774 ' sDescription,\n'
775 ' idSchedGroup,\n'
776 ' fEnabled,\n'
777 ' enmLomKind,\n'
778 ' ipLom,\n'
779 ' pctScaleTimeout,\n'
780 ' sOs,\n'
781 ' sOsVersion,\n'
782 ' sCpuVendor,\n'
783 ' sCpuArch,\n'
784 ' sCpuName,\n'
785 ' lCpuRevision,\n'
786 ' cCpus,\n'
787 ' fCpuHwVirt,\n'
788 ' fCpuNestedPaging,\n'
789 ' fCpu64BitGuest,\n'
790 ' fChipsetIoMmu,\n'
791 ' cMbMemory,\n'
792 ' cMbScratch,\n'
793 ' sReport,\n'
794 ' iTestBoxScriptRev,\n'
795 ' iPythonHexVersion,\n'
796 ' enmPendingCmd\n'
797 ' )\n'
798 'SELECT NEXTVAL(\'TestBoxGenIdSeq\'),\n'
799 ' %s,\n'
800 ' %s,\n'
801 ' NULL,\n' # uidAuthor
802 ' %s,\n'
803 ' uuidSystem,\n'
804 ' sName,\n'
805 ' sDescription,\n'
806 ' idSchedGroup,\n'
807 ' fEnabled,\n'
808 ' enmLomKind,\n'
809 ' ipLom,\n'
810 ' pctScaleTimeout,\n'
811 ' %s,\n' # sOs
812 ' %s,\n' # sOsVersion
813 ' %s,\n' # sCpuVendor
814 ' %s,\n' # sCpuArch
815 ' %s,\n' # sCpuName
816 ' %s,\n' # lCpuRevision
817 ' %s,\n' # cCpus
818 ' %s,\n' # fCpuHwVirt
819 ' %s,\n' # fCpuNestedPaging
820 ' %s,\n' # fCpu64BitGuest
821 ' %s,\n' # fChipsetIoMmu
822 ' %s,\n' # cMbMemory
823 ' %s,\n' # cMbScratch
824 ' %s,\n' # sReport
825 ' %s,\n' # iTestBoxScriptRev
826 ' %s,\n' # iPythonHexVersion
827 ' enmPendingCmd\n'
828 'FROM TestBoxes\n'
829 'WHERE idGenTestBox = %s\n'
830 'RETURNING idGenTestBox'
831 , (idTestBox,
832 tsEffective,
833 sTestBoxAddr,
834 sOs,
835 sOsVersion,
836 sCpuVendor,
837 sCpuArch,
838 sCpuName,
839 lCpuRevision,
840 cCpus,
841 fCpuHwVirt,
842 fCpuNestedPaging,
843 fCpu64BitGuest,
844 fChipsetIoMmu,
845 cMbMemory,
846 cMbScratch,
847 sReport,
848 iTestBoxScriptRev,
849 iPythonHexVersion,
850 idGenTestBox,
851 ));
852 idGenTestBox = self._oDb.fetchOne()[0];
853 self._oDb.commit();
854 except:
855 self._oDb.rollback();
856 raise;
857
858 return idGenTestBox;
859
860 def setCommand(self, idTestBox, sOldCommand, sNewCommand, uidAuthor = None, fCommit = False, sComment = None,
861 fNoRollbackOnInFlightCollision = False):
862 """
863 Sets or resets the pending command on a testbox.
864 Returns (idGenTestBox, tsEffective) of the new row.
865 """
866 _ = sComment;
867 try:
868 # Would be easier to do this using an insert or update hook, I think. Much easier.
869 self._oDb.execute('UPDATE ONLY TestBoxes\n'
870 'SET tsExpire = CURRENT_TIMESTAMP\n'
871 'WHERE idTestBox = %s\n'
872 ' AND tsExpire = \'infinity\'::timestamp\n'
873 ' AND enmPendingCmd = %s\n'
874 'RETURNING tsExpire\n',
875 (idTestBox, sOldCommand,));
876 if self._oDb.getRowCount() == 0:
877 raise TMInFligthCollision();
878 tsEffective = self._oDb.fetchOne()[0];
879
880 self._oDb.execute('INSERT INTO TestBoxes (\n'
881 ' idGenTestBox,\n'
882 ' idTestBox,\n'
883 ' tsEffective,\n'
884 ' uidAuthor,\n'
885 ' ip,\n'
886 ' uuidSystem,\n'
887 ' sName,\n'
888 ' sDescription,\n'
889 ' idSchedGroup,\n'
890 ' fEnabled,\n'
891 ' enmLomKind,\n'
892 ' ipLom,\n'
893 ' pctScaleTimeout,\n'
894 ' sOs,\n'
895 ' sOsVersion,\n'
896 ' sCpuVendor,\n'
897 ' sCpuArch,\n'
898 ' sCpuName,\n'
899 ' lCpuRevision,\n'
900 ' cCpus,\n'
901 ' fCpuHwVirt,\n'
902 ' fCpuNestedPaging,\n'
903 ' fCpu64BitGuest,\n'
904 ' fChipsetIoMmu,\n'
905 ' cMbMemory,\n'
906 ' cMbScratch,\n'
907 ' sReport,\n'
908 ' iTestBoxScriptRev,\n'
909 ' iPythonHexVersion,\n'
910 ' enmPendingCmd\n'
911 ' )\n'
912 'SELECT NEXTVAL(\'TestBoxGenIdSeq\'),\n'
913 ' %s,\n' # idTestBox
914 ' %s,\n' # tsEffective
915 ' %s,\n' # uidAuthor
916 ' ip,\n'
917 ' uuidSystem,\n'
918 ' sName,\n'
919 ' sDescription,\n'
920 ' idSchedGroup,\n'
921 ' fEnabled,\n'
922 ' enmLomKind,\n'
923 ' ipLom,\n'
924 ' pctScaleTimeout,\n'
925 ' sOs,\n'
926 ' sOsVersion,\n'
927 ' sCpuVendor,\n'
928 ' sCpuArch,\n'
929 ' sCpuName,\n'
930 ' lCpuRevision,\n'
931 ' cCpus,\n'
932 ' fCpuHwVirt,\n'
933 ' fCpuNestedPaging,\n'
934 ' fCpu64BitGuest,\n'
935 ' fChipsetIoMmu,\n'
936 ' cMbMemory,\n'
937 ' cMbScratch,\n'
938 ' sReport,\n'
939 ' iTestBoxScriptRev,\n'
940 ' iPythonHexVersion,\n'
941 ' %s\n' # enmPendingCmd
942 'FROM TestBoxes\n'
943 'WHERE idTestBox = %s\n'
944 ' AND tsExpire = %s\n'
945 ' AND enmPendingCmd = %s\n'
946 'RETURNING idGenTestBox'
947 , (idTestBox,
948 tsEffective,
949 uidAuthor,
950 sNewCommand,
951 idTestBox,
952 tsEffective,
953 sOldCommand,
954 ));
955 idGenTestBox = self._oDb.fetchOne()[0];
956 if fCommit is True:
957 self._oDb.commit();
958
959 except TMInFligthCollision: # This is pretty stupid, but don't want to touch testboxcontroller.py now.
960 if not fNoRollbackOnInFlightCollision:
961 self._oDb.rollback();
962 raise;
963 except:
964 self._oDb.rollback();
965 raise;
966
967 return (idGenTestBox, tsEffective);
968
969
970
971 def getAll(self):
972 """
973 Retrieve list of all registered Test Box records from DB.
974 """
975 self._oDb.execute('SELECT *\n'
976 'FROM TestBoxes\n'
977 'WHERE tsExpire=\'infinity\'::timestamp;')
978
979 aaoRows = self._oDb.fetchAll()
980 aoRet = []
981 for aoRow in aaoRows:
982 aoRet.append(TestBoxData().initFromDbRow(aoRow))
983 return aoRet
984
985 def _historize(self, idTestBox, uidAuthor, tsExpire = None):
986 """ Historizes the current entry. """
987 if tsExpire is None:
988 self._oDb.execute('UPDATE TestBoxes\n'
989 'SET tsExpire = CURRENT_TIMESTAMP,\n'
990 ' uidAuthor = %s\n'
991 'WHERE idTestBox = %s\n'
992 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
993 , (uidAuthor, idTestBox,));
994 else:
995 self._oDb.execute('UPDATE TestBoxes\n'
996 'SET tsExpire = %s,\n'
997 ' uidAuthor = %s\n'
998 'WHERE idTestBox = %s\n'
999 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
1000 , (uidAuthor, tsExpire, idTestBox,));
1001 return True;
1002
1003
1004 def removeEntry(self, uidAuthor, idTestBox, fCascade = False, fCommit=False):
1005 """
1006 Delete user account
1007 """
1008 _ = fCascade;
1009
1010 fRc = self._historize(idTestBox, uidAuthor, None);
1011 if fRc:
1012 self._oDb.maybeCommit(fCommit);
1013
1014 return fRc
1015
1016
1017 def cachedLookup(self, idTestBox):
1018 """
1019 Looks up the most recent TestBoxData object for idTestBox via
1020 an object cache.
1021
1022 Returns a shared TestBoxData object. None if not found.
1023 Raises exception on DB error.
1024 """
1025 if self.dCache is None:
1026 self.dCache = self._oDb.getCache('TestBoxData');
1027 oEntry = self.dCache.get(idTestBox, None);
1028 if oEntry is None:
1029 self._oDb.execute('SELECT *\n'
1030 'FROM TestBoxes\n'
1031 'WHERE idTestBox = %s\n'
1032 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
1033 , (idTestBox, ));
1034 if self._oDb.getRowCount() == 0:
1035 # Maybe it was deleted, try get the last entry.
1036 self._oDb.execute('SELECT *\n'
1037 'FROM TestBoxes\n'
1038 'WHERE idTestBox = %s\n'
1039 'ORDER BY tsExpire DESC\n'
1040 'LIMIT 1\n'
1041 , (idTestBox, ));
1042 elif self._oDb.getRowCount() > 1:
1043 raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), idTestBox));
1044
1045 if self._oDb.getRowCount() == 1:
1046 aaoRow = self._oDb.fetchOne();
1047 oEntry = TestBoxData().initFromDbRow(aaoRow);
1048 self.dCache[idTestBox] = oEntry;
1049 return oEntry;
1050
1051
1052
1053 #
1054 # The virtual test sheriff interface.
1055 #
1056
1057 def hasTestBoxRecentlyBeenRebooted(self, idTestBox, cHoursBack = 2, tsNow = None):
1058 """
1059 Checks if the testbox has been rebooted in the specified time period.
1060
1061 This does not include already pending reboots, though under some
1062 circumstances it may. These being the test box entry being edited for
1063 other reasons.
1064
1065 Returns True / False.
1066 """
1067 if tsNow is None:
1068 tsNow = self._oDb.getCurrentTimestamp();
1069 self._oDb.execute('SELECT COUNT(idTestBox)\n'
1070 'FROM TestBoxes\n'
1071 'WHERE idTestBox = %s\n'
1072 ' AND tsExpire < %s\n'
1073 ' AND tsExpire >= %s - interval \'%s hours\'\n'
1074 ' AND enmPendingCmd IN (%s, %s)\n'
1075 , ( idTestBox, tsNow, tsNow, cHoursBack,
1076 TestBoxData.ksTestBoxCmd_Reboot, TestBoxData.ksTestBoxCmd_UpgradeAndReboot, ));
1077 return self._oDb.fetchOne()[0] > 0;
1078
1079
1080 def rebootTestBox(self, idTestBox, uidAuthor, sComment, sOldCommand = TestBoxData.ksTestBoxCmd_None, fCommit = False):
1081 """
1082 Issues a reboot command for the given test box.
1083 Return True on succes, False on in-flight collision.
1084 May raise DB exception with rollback on other trouble.
1085 """
1086 try:
1087 self.setCommand(idTestBox, sOldCommand, TestBoxData.ksTestBoxCmd_Reboot,
1088 uidAuthor = uidAuthor, fCommit = fCommit, sComment = sComment,
1089 fNoRollbackOnInFlightCollision = True);
1090 except TMInFligthCollision:
1091 return False;
1092 except:
1093 raise;
1094 return True;
1095
1096
1097 def disableTestBox(self, idTestBox, uidAuthor, sComment, fCommit = False):
1098 """
1099 Disables the given test box.
1100
1101 Raises exception on trouble, without rollback.
1102 """
1103 oTestBox = TestBoxData().initFromDbWithId(self._oDb, idTestBox);
1104 if oTestBox.fEnabled:
1105 oTestBox.fEnabled = False;
1106 if sComment is not None:
1107 _ = sComment; # oTestBox.sComment = sComment;
1108 self.editEntry(oTestBox, uidAuthor = uidAuthor, fCommit = fCommit);
1109 return None;
1110
1111
1112#
1113# Unit testing.
1114#
1115
1116# pylint: disable=C0111
1117class TestBoxDataTestCase(ModelDataBaseTestCase):
1118 def setUp(self):
1119 self.aoSamples = [TestBoxData(),];
1120
1121if __name__ == '__main__':
1122 unittest.main();
1123 # not reached.
1124
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