VirtualBox

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

Last change on this file since 65423 was 65423, checked in by vboxsync, 8 years ago

WuiListContentBase: More testbox sorting (weekly meeting again).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 50.4 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: testbox.py 65423 2017-01-24 14:40:08Z vboxsync $
3
4"""
5Test Manager - TestBox.
6"""
7
8__copyright__ = \
9"""
10Copyright (C) 2012-2016 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: 65423 $"
30
31
32# Standard python imports.
33import copy;
34import unittest;
35
36# Validation Kit imports.
37from testmanager.core import db;
38from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMInFligthCollision, \
39 TMInvalidData, TMTooManyRows, TMRowNotFound, \
40 ChangeLogEntry, AttributeChangeEntry, AttributeChangeEntryPre;
41from testmanager.core.useraccount import UserAccountLogic;
42
43
44class TestBoxInSchedGroupData(ModelDataBase):
45 """
46 TestBox in SchedGroup data.
47 """
48
49 ksParam_idTestBox = 'TestBoxInSchedGroup_idTestBox';
50 ksParam_idSchedGroup = 'TestBoxInSchedGroup_idSchedGroup';
51 ksParam_tsEffective = 'TestBoxInSchedGroup_tsEffective';
52 ksParam_tsExpire = 'TestBoxInSchedGroup_tsExpire';
53 ksParam_uidAuthor = 'TestBoxInSchedGroup_uidAuthor';
54 ksParam_iSchedPriority = 'TestBoxInSchedGroup_iSchedPriority';
55
56 kasAllowNullAttributes = [ 'idTestBox', 'tsEffective', 'tsExpire', 'uidAuthor', ]
57
58 kiMin_iSchedPriority = 0;
59 kiMax_iSchedPriority = 32;
60
61 kcDbColumns = 6;
62
63 def __init__(self):
64 ModelDataBase.__init__(self);
65 self.idTestBox = None;
66 self.idSchedGroup = None;
67 self.tsEffective = None;
68 self.tsExpire = None;
69 self.uidAuthor = None;
70 self.iSchedPriority = 16;
71
72 def initFromDbRow(self, aoRow):
73 """
74 Expecting the result from a query like this:
75 SELECT * FROM TestBoxesInSchedGroups
76 """
77 if aoRow is None:
78 raise TMRowNotFound('TestBox/SchedGroup not found.');
79
80 self.idTestBox = aoRow[0];
81 self.idSchedGroup = aoRow[1];
82 self.tsEffective = aoRow[2];
83 self.tsExpire = aoRow[3];
84 self.uidAuthor = aoRow[4];
85 self.iSchedPriority = aoRow[5];
86
87 return self;
88
89class TestBoxInSchedGroupDataEx(TestBoxInSchedGroupData):
90 """
91 Extended version of TestBoxInSchedGroupData that contains the scheduling group.
92 """
93
94 def __init__(self):
95 TestBoxInSchedGroupData.__init__(self);
96 self.oSchedGroup = None; # type: SchedGroupData
97
98 def initFromDbRowEx(self, aoRow, oDb, tsNow = None, sPeriodBack = None):
99 """
100 Extended version of initFromDbRow that fills in the rest from the database.
101 """
102 from testmanager.core.schedgroup import SchedGroupData;
103 self.initFromDbRow(aoRow);
104 self.oSchedGroup = SchedGroupData().initFromDbWithId(oDb, self.idSchedGroup, tsNow, sPeriodBack);
105 return self;
106
107
108# pylint: disable=C0103
109class TestBoxData(ModelDataBase): # pylint: disable=R0902
110 """
111 TestBox Data.
112 """
113
114 ## LomKind_T
115 ksLomKind_None = 'none';
116 ksLomKind_ILOM = 'ilom';
117 ksLomKind_ELOM = 'elom';
118 ksLomKind_AppleXserveLom = 'apple-xserver-lom';
119 kasLomKindValues = [ ksLomKind_None, ksLomKind_ILOM, ksLomKind_ELOM, ksLomKind_AppleXserveLom];
120 kaoLomKindDescs = \
121 [
122 ( ksLomKind_None, 'None', ''),
123 ( ksLomKind_ILOM, 'ILOM', ''),
124 ( ksLomKind_ELOM, 'ELOM', ''),
125 ( ksLomKind_AppleXserveLom, 'Apple Xserve LOM', ''),
126 ];
127
128
129 ## TestBoxCmd_T
130 ksTestBoxCmd_None = 'none';
131 ksTestBoxCmd_Abort = 'abort';
132 ksTestBoxCmd_Reboot = 'reboot';
133 ksTestBoxCmd_Upgrade = 'upgrade';
134 ksTestBoxCmd_UpgradeAndReboot = 'upgrade-and-reboot';
135 ksTestBoxCmd_Special = 'special';
136 kasTestBoxCmdValues = [ ksTestBoxCmd_None, ksTestBoxCmd_Abort, ksTestBoxCmd_Reboot, ksTestBoxCmd_Upgrade,
137 ksTestBoxCmd_UpgradeAndReboot, ksTestBoxCmd_Special];
138 kaoTestBoxCmdDescs = \
139 [
140 ( ksTestBoxCmd_None, 'None', ''),
141 ( ksTestBoxCmd_Abort, 'Abort current test', ''),
142 ( ksTestBoxCmd_Reboot, 'Reboot TestBox', ''),
143 ( ksTestBoxCmd_Upgrade, 'Upgrade TestBox Script', ''),
144 ( ksTestBoxCmd_UpgradeAndReboot, 'Upgrade TestBox Script and reboot', ''),
145 ( ksTestBoxCmd_Special, 'Special (reserved)', ''),
146 ];
147
148
149 ksIdAttr = 'idTestBox';
150 ksIdGenAttr = 'idGenTestBox';
151
152 ksParam_idTestBox = 'TestBox_idTestBox';
153 ksParam_tsEffective = 'TestBox_tsEffective';
154 ksParam_tsExpire = 'TestBox_tsExpire';
155 ksParam_uidAuthor = 'TestBox_uidAuthor';
156 ksParam_idGenTestBox = 'TestBox_idGenTestBox';
157 ksParam_ip = 'TestBox_ip';
158 ksParam_uuidSystem = 'TestBox_uuidSystem';
159 ksParam_sName = 'TestBox_sName';
160 ksParam_sDescription = 'TestBox_sDescription';
161 ksParam_fEnabled = 'TestBox_fEnabled';
162 ksParam_enmLomKind = 'TestBox_enmLomKind';
163 ksParam_ipLom = 'TestBox_ipLom';
164 ksParam_pctScaleTimeout = 'TestBox_pctScaleTimeout';
165 ksParam_sComment = 'TestBox_sComment';
166 ksParam_sOs = 'TestBox_sOs';
167 ksParam_sOsVersion = 'TestBox_sOsVersion';
168 ksParam_sCpuVendor = 'TestBox_sCpuVendor';
169 ksParam_sCpuArch = 'TestBox_sCpuArch';
170 ksParam_sCpuName = 'TestBox_sCpuName';
171 ksParam_lCpuRevision = 'TestBox_lCpuRevision';
172 ksParam_cCpus = 'TestBox_cCpus';
173 ksParam_fCpuHwVirt = 'TestBox_fCpuHwVirt';
174 ksParam_fCpuNestedPaging = 'TestBox_fCpuNestedPaging';
175 ksParam_fCpu64BitGuest = 'TestBox_fCpu64BitGuest';
176 ksParam_fChipsetIoMmu = 'TestBox_fChipsetIoMmu';
177 ksParam_fRawMode = 'TestBox_fRawMode';
178 ksParam_cMbMemory = 'TestBox_cMbMemory';
179 ksParam_cMbScratch = 'TestBox_cMbScratch';
180 ksParam_sReport = 'TestBox_sReport';
181 ksParam_iTestBoxScriptRev = 'TestBox_iTestBoxScriptRev';
182 ksParam_iPythonHexVersion = 'TestBox_iPythonHexVersion';
183 ksParam_enmPendingCmd = 'TestBox_enmPendingCmd';
184
185 kasInternalAttributes = [ 'idStrDescription', 'idStrComment', 'idStrOs', 'idStrOsVersion', 'idStrCpuVendor',
186 'idStrCpuArch', 'idStrCpuName', 'idStrReport', ];
187 kasMachineSettableOnly = [ 'sOs', 'sOsVersion', 'sCpuVendor', 'sCpuArch', 'sCpuName', 'lCpuRevision', 'cCpus',
188 'fCpuHwVirt', 'fCpuNestedPaging', 'fCpu64BitGuest', 'fChipsetIoMmu', 'fRawMode',
189 'cMbMemory', 'cMbScratch', 'sReport', 'iTestBoxScriptRev', 'iPythonHexVersion', ];
190 kasAllowNullAttributes = ['idTestBox', 'tsEffective', 'tsExpire', 'uidAuthor', 'idGenTestBox', 'sDescription',
191 'ipLom', 'sComment', ] + kasMachineSettableOnly + kasInternalAttributes;
192
193 kasValidValues_enmLomKind = kasLomKindValues;
194 kasValidValues_enmPendingCmd = kasTestBoxCmdValues;
195 kiMin_pctScaleTimeout = 11;
196 kiMax_pctScaleTimeout = 19999;
197 kcchMax_sReport = 65535;
198
199 kcDbColumns = 40; # including the 7 string joins columns
200
201
202 def __init__(self):
203 ModelDataBase.__init__(self);
204
205 #
206 # Initialize with defaults.
207 # See the database for explanations of each of these fields.
208 #
209 self.idTestBox = None;
210 self.tsEffective = None;
211 self.tsExpire = None;
212 self.uidAuthor = None;
213 self.idGenTestBox = None;
214 self.ip = None;
215 self.uuidSystem = None;
216 self.sName = None;
217 self.idStrDescription = None;
218 self.fEnabled = False;
219 self.enmLomKind = self.ksLomKind_None;
220 self.ipLom = None;
221 self.pctScaleTimeout = 100;
222 self.idStrComment = None;
223 self.idStrOs = None;
224 self.idStrOsVersion = None;
225 self.idStrCpuVendor = None;
226 self.idStrCpuArch = None;
227 self.idStrCpuName = None;
228 self.lCpuRevision = None;
229 self.cCpus = 1;
230 self.fCpuHwVirt = False;
231 self.fCpuNestedPaging = False;
232 self.fCpu64BitGuest = False;
233 self.fChipsetIoMmu = False;
234 self.fRawMode = None;
235 self.cMbMemory = 1;
236 self.cMbScratch = 0;
237 self.idStrReport = None;
238 self.iTestBoxScriptRev = 0;
239 self.iPythonHexVersion = 0;
240 self.enmPendingCmd = self.ksTestBoxCmd_None;
241 # String table values.
242 self.sDescription = None;
243 self.sComment = None;
244 self.sOs = None;
245 self.sOsVersion = None;
246 self.sCpuVendor = None;
247 self.sCpuArch = None;
248 self.sCpuName = None;
249 self.sReport = None;
250
251 def initFromDbRow(self, aoRow):
252 """
253 Internal worker for initFromDbWithId and initFromDbWithGenId as well as
254 from TestBoxLogic. Expecting the result from a query like this:
255 SELECT TestBoxesWithStrings.* FROM TestBoxesWithStrings
256 """
257 if aoRow is None:
258 raise TMRowNotFound('TestBox not found.');
259
260 self.idTestBox = aoRow[0];
261 self.tsEffective = aoRow[1];
262 self.tsExpire = aoRow[2];
263 self.uidAuthor = aoRow[3];
264 self.idGenTestBox = aoRow[4];
265 self.ip = aoRow[5];
266 self.uuidSystem = aoRow[6];
267 self.sName = aoRow[7];
268 self.idStrDescription = aoRow[8];
269 self.fEnabled = aoRow[9];
270 self.enmLomKind = aoRow[10];
271 self.ipLom = aoRow[11];
272 self.pctScaleTimeout = aoRow[12];
273 self.idStrComment = aoRow[13];
274 self.idStrOs = aoRow[14];
275 self.idStrOsVersion = aoRow[15];
276 self.idStrCpuVendor = aoRow[16];
277 self.idStrCpuArch = aoRow[17];
278 self.idStrCpuName = aoRow[18];
279 self.lCpuRevision = aoRow[19];
280 self.cCpus = aoRow[20];
281 self.fCpuHwVirt = aoRow[21];
282 self.fCpuNestedPaging = aoRow[22];
283 self.fCpu64BitGuest = aoRow[23];
284 self.fChipsetIoMmu = aoRow[24];
285 self.fRawMode = aoRow[25];
286 self.cMbMemory = aoRow[26];
287 self.cMbScratch = aoRow[27];
288 self.idStrReport = aoRow[28];
289 self.iTestBoxScriptRev = aoRow[29];
290 self.iPythonHexVersion = aoRow[30];
291 self.enmPendingCmd = aoRow[31];
292
293 # String table values.
294 if len(aoRow) > 32:
295 self.sDescription = aoRow[32];
296 self.sComment = aoRow[33];
297 self.sOs = aoRow[34];
298 self.sOsVersion = aoRow[35];
299 self.sCpuVendor = aoRow[36];
300 self.sCpuArch = aoRow[37];
301 self.sCpuName = aoRow[38];
302 self.sReport = aoRow[39];
303
304 return self;
305
306 def initFromDbWithId(self, oDb, idTestBox, tsNow = None, sPeriodBack = None):
307 """
308 Initialize the object from the database.
309 """
310 oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
311 'SELECT TestBoxesWithStrings.*\n'
312 'FROM TestBoxesWithStrings\n'
313 'WHERE idTestBox = %s\n'
314 , ( idTestBox, ), tsNow, sPeriodBack));
315 aoRow = oDb.fetchOne()
316 if aoRow is None:
317 raise TMRowNotFound('idTestBox=%s not found (tsNow=%s sPeriodBack=%s)' % (idTestBox, tsNow, sPeriodBack,));
318 return self.initFromDbRow(aoRow);
319
320 def initFromDbWithGenId(self, oDb, idGenTestBox, tsNow = None):
321 """
322 Initialize the object from the database.
323 """
324 _ = tsNow; # Only useful for extended data classes.
325 oDb.execute('SELECT TestBoxesWithStrings.*\n'
326 'FROM TestBoxesWithStrings\n'
327 'WHERE idGenTestBox = %s\n'
328 , (idGenTestBox, ) );
329 return self.initFromDbRow(oDb.fetchOne());
330
331 def _validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor = ModelDataBase.ksValidateFor_Other):
332 # Override to do extra ipLom checks.
333 dErrors = ModelDataBase._validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor);
334 if self.ksParam_ipLom not in dErrors \
335 and self.ksParam_enmLomKind not in dErrors \
336 and self.enmLomKind != self.ksLomKind_None \
337 and self.ipLom is None:
338 dErrors[self.ksParam_ipLom] = 'Light-out-management IP is mandatory and a LOM is selected.'
339 return dErrors;
340
341 @staticmethod
342 def formatPythonVersionEx(iPythonHexVersion):
343 """ Unbuttons the version number and formats it as a version string. """
344 if iPythonHexVersion is None:
345 return 'N/A';
346 return 'v%d.%d.%d.%d' \
347 % ( iPythonHexVersion >> 24,
348 (iPythonHexVersion >> 16) & 0xff,
349 (iPythonHexVersion >> 8) & 0xff,
350 iPythonHexVersion & 0xff);
351
352 def formatPythonVersion(self):
353 """ Unbuttons the version number and formats it as a version string. """
354 return self.formatPythonVersionEx(self.iPythonHexVersion);
355
356
357 @staticmethod
358 def getCpuFamilyEx(lCpuRevision):
359 """ Returns the CPU family for a x86 or amd64 testboxes."""
360 if lCpuRevision is None:
361 return 0;
362 return (lCpuRevision >> 24 & 0xff);
363
364 def getCpuFamily(self):
365 """ Returns the CPU family for a x86 or amd64 testboxes."""
366 return self.getCpuFamilyEx(self.lCpuRevision);
367
368 @staticmethod
369 def getCpuModelEx(lCpuRevision):
370 """ Returns the CPU model for a x86 or amd64 testboxes."""
371 if lCpuRevision is None:
372 return 0;
373 return (lCpuRevision >> 8 & 0xffff);
374
375 def getCpuModel(self):
376 """ Returns the CPU model for a x86 or amd64 testboxes."""
377 return self.getCpuModelEx(self.lCpuRevision);
378
379 @staticmethod
380 def getCpuSteppingEx(lCpuRevision):
381 """ Returns the CPU stepping for a x86 or amd64 testboxes."""
382 if lCpuRevision is None:
383 return 0;
384 return (lCpuRevision & 0xff);
385
386 def getCpuStepping(self):
387 """ Returns the CPU stepping for a x86 or amd64 testboxes."""
388 return self.getCpuSteppingEx(self.lCpuRevision);
389
390
391 # The following is a translation of the g_aenmIntelFamily06 array in CPUMR3CpuId.cpp:
392 kdIntelFamily06 = {
393 0x00: 'P6',
394 0x01: 'P6',
395 0x03: 'P6_II',
396 0x05: 'P6_II',
397 0x06: 'P6_II',
398 0x07: 'P6_III',
399 0x08: 'P6_III',
400 0x09: 'P6_M_Banias',
401 0x0a: 'P6_III',
402 0x0b: 'P6_III',
403 0x0d: 'P6_M_Dothan',
404 0x0e: 'Core_Yonah',
405 0x0f: 'Core2_Merom',
406 0x15: 'P6_M_Dothan',
407 0x16: 'Core2_Merom',
408 0x17: 'Core2_Penryn',
409 0x1a: 'Core7_Nehalem',
410 0x1c: 'Atom_Bonnell',
411 0x1d: 'Core2_Penryn',
412 0x1e: 'Core7_Nehalem',
413 0x1f: 'Core7_Nehalem',
414 0x25: 'Core7_Westmere',
415 0x26: 'Atom_Lincroft',
416 0x27: 'Atom_Saltwell',
417 0x2a: 'Core7_SandyBridge',
418 0x2c: 'Core7_Westmere',
419 0x2d: 'Core7_SandyBridge',
420 0x2e: 'Core7_Nehalem',
421 0x2f: 'Core7_Westmere',
422 0x35: 'Atom_Saltwell',
423 0x36: 'Atom_Saltwell',
424 0x37: 'Atom_Silvermont',
425 0x3a: 'Core7_IvyBridge',
426 0x3c: 'Core7_Haswell',
427 0x3d: 'Core7_Broadwell',
428 0x3e: 'Core7_IvyBridge',
429 0x3f: 'Core7_Haswell',
430 0x45: 'Core7_Haswell',
431 0x46: 'Core7_Haswell',
432 0x47: 'Core7_Broadwell',
433 0x4a: 'Atom_Silvermont',
434 0x4c: 'Atom_Airmount',
435 0x4d: 'Atom_Silvermont',
436 0x4e: 'Core7_Skylake',
437 0x4f: 'Core7_Broadwell',
438 0x55: 'Core7_Skylake',
439 0x56: 'Core7_Broadwell',
440 0x5a: 'Atom_Silvermont',
441 0x5c: 'Atom_Goldmont',
442 0x5d: 'Atom_Silvermont',
443 0x5e: 'Core7_Skylake',
444 0x66: 'Core7_Cannonlake',
445 };
446 # Also from CPUMR3CpuId.cpp, but the switch.
447 kdIntelFamily15 = {
448 0x00: 'NB_Willamette',
449 0x01: 'NB_Willamette',
450 0x02: 'NB_Northwood',
451 0x03: 'NB_Prescott',
452 0x04: 'NB_Prescott2M',
453 0x05: 'NB_Unknown',
454 0x06: 'NB_CedarMill',
455 0x07: 'NB_Gallatin',
456 };
457
458 @staticmethod
459 def queryCpuMicroarchEx(lCpuRevision, sCpuVendor):
460 """ Try guess the microarch name for the cpu. Returns None if we cannot. """
461 if lCpuRevision is None or sCpuVendor is None:
462 return None;
463 uFam = TestBoxData.getCpuFamilyEx(lCpuRevision);
464 uMod = TestBoxData.getCpuModelEx(lCpuRevision);
465 if sCpuVendor == 'GenuineIntel':
466 if uFam == 6:
467 return TestBoxData.kdIntelFamily06.get(uMod, None);
468 if uFam == 15:
469 return TestBoxData.kdIntelFamily15.get(uMod, None);
470 elif sCpuVendor == 'AuthenticAMD':
471 if uFam == 0xf:
472 if uMod < 0x10: return 'K8_130nm';
473 if uMod >= 0x60 and uMod < 0x80: return 'K8_65nm';
474 if uMod >= 0x40: return 'K8_90nm_AMDV';
475 if uMod in [0x21, 0x23, 0x2b, 0x37, 0x3f]: return 'K8_90nm_DualCore';
476 return 'AMD_K8_90nm';
477 if uFam == 0x10: return 'K10';
478 if uFam == 0x11: return 'K10_Lion';
479 if uFam == 0x12: return 'K10_Llano';
480 if uFam == 0x14: return 'Bobcat';
481 if uFam == 0x15:
482 if uMod <= 0x01: return 'Bulldozer';
483 if uMod in [0x02, 0x10, 0x13]: return 'Piledriver';
484 return None;
485 if uFam == 0x16:
486 return 'Jaguar';
487 elif sCpuVendor == 'CentaurHauls':
488 if uFam == 0x05:
489 if uMod == 0x01: return 'Centaur_C6';
490 if uMod == 0x04: return 'Centaur_C6';
491 if uMod == 0x08: return 'Centaur_C2';
492 if uMod == 0x09: return 'Centaur_C3';
493 if uFam == 0x06:
494 if uMod == 0x05: return 'VIA_C3_M2';
495 if uMod == 0x06: return 'VIA_C3_C5A';
496 if uMod == 0x07: return 'VIA_C3_C5B' if TestBoxData.getCpuSteppingEx(lCpuRevision) < 8 else 'VIA_C3_C5C';
497 if uMod == 0x08: return 'VIA_C3_C5N';
498 if uMod == 0x09: return 'VIA_C3_C5XL' if TestBoxData.getCpuSteppingEx(lCpuRevision) < 8 else 'VIA_C3_C5P';
499 if uMod == 0x0a: return 'VIA_C7_C5J';
500 if uMod == 0x0f: return 'VIA_Isaiah';
501 return None;
502
503 def queryCpuMicroarch(self):
504 """ Try guess the microarch name for the cpu. Returns None if we cannot. """
505 return self.queryCpuMicroarchEx(self.lCpuRevision, self.sCpuVendor);
506
507 @staticmethod
508 def getPrettyCpuVersionEx(lCpuRevision, sCpuVendor):
509 """ Pretty formatting of the family/model/stepping with microarch optimizations. """
510 if lCpuRevision is None or sCpuVendor is None:
511 return u'<none>';
512 sMarch = TestBoxData.queryCpuMicroarchEx(lCpuRevision, sCpuVendor);
513 if sMarch is not None:
514 return '%s %02x:%x' \
515 % (sMarch, TestBoxData.getCpuModelEx(lCpuRevision), TestBoxData.getCpuSteppingEx(lCpuRevision));
516 return 'fam%02X m%02X s%02X' \
517 % ( TestBoxData.getCpuFamilyEx(lCpuRevision), TestBoxData.getCpuModelEx(lCpuRevision),
518 TestBoxData.getCpuSteppingEx(lCpuRevision));
519
520 def getPrettyCpuVersion(self):
521 """ Pretty formatting of the family/model/stepping with microarch optimizations. """
522 return self.getPrettyCpuVersionEx(self.lCpuRevision, self.sCpuVendor);
523
524 def getArchBitString(self):
525 """ Returns 32-bit, 64-bit, <none>, or sCpuArch. """
526 if self.sCpuArch is None:
527 return '<none>';
528 if self.sCpuArch in [ 'x86',]:
529 return '32-bit';
530 if self.sCpuArch in [ 'amd64',]:
531 return '64-bit';
532 return self.sCpuArch;
533
534 def getPrettyCpuVendor(self):
535 """ Pretty vendor name."""
536 if self.sCpuVendor is None:
537 return '<none>';
538 if self.sCpuVendor == 'GenuineIntel': return 'Intel';
539 if self.sCpuVendor == 'AuthenticAMD': return 'AMD';
540 if self.sCpuVendor == 'CentaurHauls': return 'VIA';
541 return self.sCpuVendor;
542
543
544class TestBoxDataEx(TestBoxData):
545 """
546 TestBox data.
547 """
548
549 ksParam_aoInSchedGroups = 'TestBox_aoInSchedGroups';
550
551 # Use [] instead of None.
552 kasAltArrayNull = [ 'aoInSchedGroups', ];
553
554 ## Helper parameter containing the comma separated list with the IDs of
555 # potential members found in the parameters.
556 ksParam_aidSchedGroups = 'TestBoxDataEx_aidSchedGroups';
557
558 def __init__(self):
559 TestBoxData.__init__(self);
560 self.aoInSchedGroups = []; # type: list[TestBoxInSchedGroupData]
561
562 def _initExtraMembersFromDb(self, oDb, tsNow = None, sPeriodBack = None):
563 """
564 Worker shared by the initFromDb* methods.
565 Returns self. Raises exception if no row or database error.
566 """
567 oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
568 'SELECT *\n'
569 'FROM TestBoxesInSchedGroups\n'
570 'WHERE idTestBox = %s\n'
571 , (self.idTestBox,), tsNow, sPeriodBack)
572 + 'ORDER BY idSchedGroup\n' );
573 self.aoInSchedGroups = [];
574 for aoRow in oDb.fetchAll():
575 self.aoInSchedGroups.append(TestBoxInSchedGroupDataEx().initFromDbRowEx(aoRow, oDb, tsNow, sPeriodBack));
576 return self;
577
578 def initFromDbRowEx(self, aoRow, oDb, tsNow = None):
579 """
580 Reinitialize from a SELECT * FROM TestBoxesWithStrings row. Will query the
581 necessary additional data from oDb using tsNow.
582 Returns self. Raises exception if no row or database error.
583 """
584 TestBoxData.initFromDbRow(self, aoRow);
585 return self._initExtraMembersFromDb(oDb, tsNow);
586
587 def initFromDbWithId(self, oDb, idTestBox, tsNow = None, sPeriodBack = None):
588 """
589 Initialize the object from the database.
590 """
591 TestBoxData.initFromDbWithId(self, oDb, idTestBox, tsNow, sPeriodBack);
592 return self._initExtraMembersFromDb(oDb, tsNow, sPeriodBack);
593
594 def initFromDbWithGenId(self, oDb, idGenTestBox, tsNow = None):
595 """
596 Initialize the object from the database.
597 """
598 TestBoxData.initFromDbWithGenId(self, oDb, idGenTestBox);
599 if tsNow is None and not oDb.isTsInfinity(self.tsExpire):
600 tsNow = self.tsEffective;
601 return self._initExtraMembersFromDb(oDb, tsNow);
602
603 def getAttributeParamNullValues(self, sAttr): # Necessary?
604 if sAttr in ['aoInSchedGroups', ]:
605 return [[], ''];
606 return TestBoxData.getAttributeParamNullValues(self, sAttr);
607
608 def convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict):
609 """
610 For dealing with the in-scheduling-group list.
611 """
612 if sAttr != 'aoInSchedGroups':
613 return TestBoxData.convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict);
614
615 aoNewValues = [];
616 aidSelected = oDisp.getListOfIntParams(sParam, iMin = 1, iMax = 0x7ffffffe, aiDefaults = []);
617 asIds = oDisp.getStringParam(self.ksParam_aidSchedGroups, sDefault = '').split(',');
618 for idSchedGroup in asIds:
619 try: idSchedGroup = int(idSchedGroup);
620 except: pass;
621 oDispWrapper = self.DispWrapper(oDisp, '%s[%s][%%s]' % (TestBoxDataEx.ksParam_aoInSchedGroups, idSchedGroup,))
622 oMember = TestBoxInSchedGroupData().initFromParams(oDispWrapper, fStrict = False);
623 if idSchedGroup in aidSelected:
624 aoNewValues.append(oMember);
625 return aoNewValues;
626
627 def _validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb): # pylint: disable=R0914
628 """
629 Validate special arrays and requirement expressions.
630
631 Some special needs for the in-scheduling-group list.
632 """
633 if sAttr != 'aoInSchedGroups':
634 return TestBoxData._validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb);
635
636 asErrors = [];
637 aoNewValues = [];
638
639 # Note! We'll be returning an error dictionary instead of an string here.
640 dErrors = {};
641
642 for iInGrp, oInSchedGroup in enumerate(self.aoInSchedGroups):
643 oInSchedGroup = copy.copy(oInSchedGroup);
644 oInSchedGroup.idTestBox = self.idTestBox;
645 dCurErrors = oInSchedGroup.validateAndConvert(oDb, ModelDataBase.ksValidateFor_Other);
646 if len(dCurErrors) == 0:
647 pass; ## @todo figure out the ID?
648 else:
649 asErrors = [];
650 for sKey in dCurErrors:
651 asErrors.append('%s: %s' % (sKey[len('TestBoxInSchedGroup_'):], dCurErrors[sKey]));
652 dErrors[iInGrp] = '<br>\n'.join(asErrors)
653 aoNewValues.append(oInSchedGroup);
654
655 for iInGrp, oInSchedGroup in enumerate(self.aoInSchedGroups):
656 for iInGrp2 in xrange(iInGrp + 1, len(self.aoInSchedGroups)):
657 if self.aoInSchedGroups[iInGrp2].idSchedGroup == oInSchedGroup.idSchedGroup:
658 sMsg = 'Duplicate scheduling group #%s".' % (oInSchedGroup.idSchedGroup,);
659 if iInGrp in dErrors: dErrors[iInGrp] += '<br>\n' + sMsg;
660 else: dErrors[iInGrp] = sMsg;
661 if iInGrp2 in dErrors: dErrors[iInGrp2] += '<br>\n' + sMsg;
662 else: dErrors[iInGrp2] = sMsg;
663 break;
664
665 return (aoNewValues, dErrors if len(dErrors) > 0 else None);
666
667
668class TestBoxLogic(ModelLogicBase):
669 """
670 TestBox logic.
671 """
672
673 kiSortColumn_sName = 1;
674 kiSortColumn_sOs = 2;
675 kiSortColumn_sOsVersion = 3;
676 kiSortColumn_sCpuVendor = 4;
677 kiSortColumn_sCpuArch = 5;
678 kiSortColumn_lCpuRevision = 6;
679 kiSortColumn_cCpus = 7;
680 kiSortColumn_cMbMemory = 8;
681 kiSortColumn_cMbScratch = 9;
682 kiSortColumn_fCpuNestedPaging = 10;
683 kiSortColumn_iTestBoxScriptRev = 11;
684 kiSortColumn_iPythonHexVersion = 12;
685 kiSortColumn_enmPendingCmd = 13;
686 kiSortColumn_fEnabled = 14;
687 kiSortColumn_enmState = 15;
688 kiSortColumn_tsUpdated = 16;
689 kcMaxSortColumns = 17;
690 kdSortColumnMap = {
691 0: 'TestBoxesWithStrings.sName',
692 kiSortColumn_sName: 'TestBoxesWithStrings.sName',
693 -kiSortColumn_sName: 'TestBoxesWithStrings.sName DESC',
694 kiSortColumn_sOs: 'TestBoxesWithStrings.sOs',
695 -kiSortColumn_sOs: 'TestBoxesWithStrings.sOs DESC',
696 kiSortColumn_sOsVersion: 'TestBoxesWithStrings.sOsVersion',
697 -kiSortColumn_sOsVersion: 'TestBoxesWithStrings.sOsVersion DESC',
698 kiSortColumn_sCpuVendor: 'TestBoxesWithStrings.sCpuVendor',
699 -kiSortColumn_sCpuVendor: 'TestBoxesWithStrings.sCpuVendor DESC',
700 kiSortColumn_sCpuArch: 'TestBoxesWithStrings.sCpuArch',
701 -kiSortColumn_sCpuArch: 'TestBoxesWithStrings.sCpuArch DESC',
702 kiSortColumn_lCpuRevision: 'TestBoxesWithStrings.lCpuRevision',
703 -kiSortColumn_lCpuRevision: 'TestBoxesWithStrings.lCpuRevision DESC',
704 kiSortColumn_cCpus: 'TestBoxesWithStrings.cCpus',
705 -kiSortColumn_cCpus: 'TestBoxesWithStrings.cCpus DESC',
706 kiSortColumn_cMbMemory: 'TestBoxesWithStrings.cMbMemory',
707 -kiSortColumn_cMbMemory: 'TestBoxesWithStrings.cMbMemory DESC',
708 kiSortColumn_cMbScratch: 'TestBoxesWithStrings.cMbScratch',
709 -kiSortColumn_cMbScratch: 'TestBoxesWithStrings.cMbScratch DESC',
710 kiSortColumn_fCpuNestedPaging: 'TestBoxesWithStrings.fCpuNestedPaging',
711 -kiSortColumn_fCpuNestedPaging: 'TestBoxesWithStrings.fCpuNestedPaging DESC',
712 kiSortColumn_iTestBoxScriptRev: 'TestBoxesWithStrings.iTestBoxScriptRev',
713 -kiSortColumn_iTestBoxScriptRev: 'TestBoxesWithStrings.iTestBoxScriptRev DESC',
714 kiSortColumn_iPythonHexVersion: 'TestBoxesWithStrings.iPythonHexVersion',
715 -kiSortColumn_iPythonHexVersion: 'TestBoxesWithStrings.iPythonHexVersion DESC',
716 kiSortColumn_enmPendingCmd: 'TestBoxesWithStrings.enmPendingCmd',
717 -kiSortColumn_enmPendingCmd: 'TestBoxesWithStrings.enmPendingCmd DESC',
718 kiSortColumn_fEnabled: 'TestBoxesWithStrings.fEnabled',
719 -kiSortColumn_fEnabled: 'TestBoxesWithStrings.fEnabled DESC',
720 kiSortColumn_enmState: 'TestBoxStatuses.enmState',
721 -kiSortColumn_enmState: 'TestBoxStatuses.enmState DESC',
722 kiSortColumn_tsUpdated: 'TestBoxStatuses.tsUpdated',
723 -kiSortColumn_tsUpdated: 'TestBoxStatuses.tsUpdated DESC',
724 };
725
726 def __init__(self, oDb):
727 ModelLogicBase.__init__(self, oDb);
728 self.dCache = None;
729
730 def tryFetchTestBoxByUuid(self, sTestBoxUuid):
731 """
732 Tries to fetch a testbox by its UUID alone.
733 """
734 self._oDb.execute('SELECT TestBoxesWithStrings.*\n'
735 'FROM TestBoxesWithStrings\n'
736 'WHERE uuidSystem = %s\n'
737 ' AND tsExpire = \'infinity\'::timestamp\n'
738 'ORDER BY tsEffective DESC\n',
739 (sTestBoxUuid,));
740 if self._oDb.getRowCount() == 0:
741 return None;
742 if self._oDb.getRowCount() != 1:
743 raise TMTooManyRows('Database integrity error: %u hits' % (self._oDb.getRowCount(),));
744 oData = TestBoxData();
745 oData.initFromDbRow(self._oDb.fetchOne());
746 return oData;
747
748 def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
749 """
750 Fetches testboxes for listing.
751
752 Returns an array (list) of TestBoxDataForListing items, empty list if none.
753 The TestBoxDataForListing instances are just TestBoxData with two extra
754 members, an extra oStatus member that is either None or a TestBoxStatusData
755 instance, and a member tsCurrent holding CURRENT_TIMESTAMP.
756
757 Raises exception on error.
758 """
759 class TestBoxDataForListing(TestBoxDataEx):
760 """ We add two members for the listing. """
761 def __init__(self):
762 TestBoxDataEx.__init__(self);
763 self.tsCurrent = None; # CURRENT_TIMESTAMP
764 self.oStatus = None; # type: TestBoxStatusData
765
766 from testmanager.core.testboxstatus import TestBoxStatusData;
767
768 if aiSortColumns is None or len(aiSortColumns) == 0:
769 aiSortColumns = [self.kiSortColumn_sName,];
770
771 if tsNow is None:
772 self._oDb.execute('SELECT TestBoxesWithStrings.*,\n'
773 ' TestBoxStatuses.*\n'
774 'FROM TestBoxesWithStrings\n'
775 ' LEFT OUTER JOIN TestBoxStatuses\n'
776 ' ON TestBoxStatuses.idTestBox = TestBoxesWithStrings.idTestBox\n'
777 'WHERE TestBoxesWithStrings.tsExpire = \'infinity\'::TIMESTAMP\n'
778 'ORDER BY ' + (', '.join([self.kdSortColumnMap[i] for i in aiSortColumns])) + '\n'
779 'LIMIT %s OFFSET %s\n'
780 , (cMaxRows, iStart,));
781 else:
782 self._oDb.execute('SELECT TestBoxesWithStrings.*,\n'
783 ' TestBoxStatuses.*\n'
784 'FROM TestBoxesWithStrings\n'
785 ' LEFT OUTER JOIN TestBoxStatuses\n'
786 ' ON TestBoxStatuses.idTestBox = TestBoxesWithStrings.idTestBox\n'
787 'WHERE tsExpire > %s\n'
788 ' AND tsEffective <= %s\n'
789 'ORDER BY ' + (', '.join([self.kdSortColumnMap[i] for i in aiSortColumns])) + '\n'
790 'LIMIT %s OFFSET %s\n'
791 , ( tsNow, tsNow, cMaxRows, iStart,));
792
793 aoRows = [];
794 for aoOne in self._oDb.fetchAll():
795 oTestBox = TestBoxDataForListing().initFromDbRowEx(aoOne, self._oDb, tsNow);
796 oTestBox.tsCurrent = self._oDb.getCurrentTimestamp();
797 if aoOne[TestBoxData.kcDbColumns] is not None:
798 oTestBox.oStatus = TestBoxStatusData().initFromDbRow(aoOne[TestBoxData.kcDbColumns:]);
799 aoRows.append(oTestBox);
800 return aoRows;
801
802 def fetchForChangeLog(self, idTestBox, iStart, cMaxRows, tsNow): # pylint: disable=R0914
803 """
804 Fetches change log entries for a testbox.
805
806 Returns an array of ChangeLogEntry instance and an indicator whether
807 there are more entries.
808 Raises exception on error.
809 """
810
811 ## @todo calc changes to scheduler group!
812
813 if tsNow is None:
814 tsNow = self._oDb.getCurrentTimestamp();
815
816 self._oDb.execute('SELECT TestBoxesWithStrings.*\n'
817 'FROM TestBoxesWithStrings\n'
818 'WHERE TestBoxesWithStrings.tsEffective <= %s\n'
819 ' AND TestBoxesWithStrings.idTestBox = %s\n'
820 'ORDER BY TestBoxesWithStrings.tsExpire DESC\n'
821 'LIMIT %s OFFSET %s\n'
822 , (tsNow, idTestBox, cMaxRows + 1, iStart,));
823
824 aoRows = [];
825 for aoDbRow in self._oDb.fetchAll():
826 aoRows.append(TestBoxData().initFromDbRow(aoDbRow));
827
828 # Calculate the changes.
829 aoEntries = [];
830 for i in xrange(0, len(aoRows) - 1):
831 oNew = aoRows[i];
832 oOld = aoRows[i + 1];
833 aoChanges = [];
834 for sAttr in oNew.getDataAttributes():
835 if sAttr not in [ 'tsEffective', 'tsExpire', 'uidAuthor', ]:
836 oOldAttr = getattr(oOld, sAttr);
837 oNewAttr = getattr(oNew, sAttr);
838 if oOldAttr != oNewAttr:
839 if sAttr == 'sReport':
840 aoChanges.append(AttributeChangeEntryPre(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
841 else:
842 aoChanges.append(AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
843 aoEntries.append(ChangeLogEntry(oNew.uidAuthor, None, oNew.tsEffective, oNew.tsExpire, oNew, oOld, aoChanges));
844
845 # If we're at the end of the log, add the initial entry.
846 if len(aoRows) <= cMaxRows and len(aoRows) > 0:
847 oNew = aoRows[-1];
848 aoEntries.append(ChangeLogEntry(oNew.uidAuthor, None, oNew.tsEffective, oNew.tsExpire, oNew, None, []));
849
850 UserAccountLogic(self._oDb).resolveChangeLogAuthors(aoEntries);
851 return (aoEntries, len(aoRows) > cMaxRows);
852
853 def _validateAndConvertData(self, oData, enmValidateFor):
854 # type: (TestBoxDataEx, str) -> None
855 """
856 Helper for addEntry and editEntry that validates the scheduling group IDs in
857 addtion to what's covered by the default validateAndConvert of the data object.
858
859 Raises exception on invalid input.
860 """
861 dDataErrors = oData.validateAndConvert(self._oDb, enmValidateFor);
862 if len(dDataErrors) > 0:
863 raise TMInvalidData('TestBoxLogic.addEntry: %s' % (dDataErrors,));
864 if isinstance(oData, TestBoxDataEx):
865 if len(oData.aoInSchedGroups):
866 sSchedGrps = ', '.join('(%s)' % oCur.idSchedGroup for oCur in oData.aoInSchedGroups);
867 self._oDb.execute('SELECT SchedGroupIDs.idSchedGroup\n'
868 'FROM (VALUES ' + sSchedGrps + ' ) AS SchedGroupIDs(idSchedGroup)\n'
869 ' LEFT OUTER JOIN SchedGroups\n'
870 ' ON SchedGroupIDs.idSchedGroup = SchedGroups.idSchedGroup\n'
871 ' AND SchedGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
872 'WHERE SchedGroups.idSchedGroup IS NULL\n');
873 aaoRows = self._oDb.fetchAll();
874 if len(aaoRows) > 0:
875 raise TMInvalidData('TestBoxLogic.addEntry missing scheduling groups: %s'
876 % (', '.join(str(aoRow[0]) for aoRow in aaoRows),));
877 return None;
878
879 def addEntry(self, oData, uidAuthor, fCommit = False):
880 # type: (TestBoxDataEx, int, bool) -> (int, int, datetime.datetime)
881 """
882 Creates a testbox in the database.
883 Returns the testbox ID, testbox generation ID and effective timestamp
884 of the created testbox on success. Throws error on failure.
885 """
886
887 #
888 # Validate. Extra work because of missing foreign key (due to history).
889 #
890 self._validateAndConvertData(oData, oData.ksValidateFor_Add);
891
892 #
893 # Do it.
894 #
895 self._oDb.callProc('TestBoxLogic_addEntry'
896 , ( uidAuthor,
897 oData.ip, # Should we allow setting the IP?
898 oData.uuidSystem,
899 oData.sName,
900 oData.sDescription,
901 oData.fEnabled,
902 oData.enmLomKind,
903 oData.ipLom,
904 oData.pctScaleTimeout,
905 oData.sComment,
906 oData.enmPendingCmd, ) );
907 (idTestBox, idGenTestBox, tsEffective) = self._oDb.fetchOne();
908
909 for oInSchedGrp in oData.aoInSchedGroups:
910 self._oDb.callProc('TestBoxLogic_addGroupEntry',
911 ( uidAuthor, idTestBox, oInSchedGrp.idSchedGroup, oInSchedGrp.iSchedPriority,) );
912
913 self._oDb.maybeCommit(fCommit);
914 return (idTestBox, idGenTestBox, tsEffective);
915
916
917 def editEntry(self, oData, uidAuthor, fCommit = False):
918 """
919 Data edit update, web UI is the primary user.
920
921 oData is either TestBoxDataEx or TestBoxData. The latter is for enabling
922 Returns the new generation ID and effective date.
923 """
924
925 #
926 # Validate.
927 #
928 self._validateAndConvertData(oData, oData.ksValidateFor_Edit);
929
930 #
931 # Get current data.
932 #
933 oOldData = TestBoxDataEx().initFromDbWithId(self._oDb, oData.idTestBox);
934
935 #
936 # Do it.
937 #
938 if not oData.isEqualEx(oOldData, [ 'tsEffective', 'tsExpire', 'uidAuthor', 'aoInSchedGroups', ]
939 + TestBoxData.kasMachineSettableOnly ):
940 self._oDb.callProc('TestBoxLogic_editEntry'
941 , ( uidAuthor,
942 oData.idTestBox,
943 oData.ip, # Should we allow setting the IP?
944 oData.uuidSystem,
945 oData.sName,
946 oData.sDescription,
947 oData.fEnabled,
948 oData.enmLomKind,
949 oData.ipLom,
950 oData.pctScaleTimeout,
951 oData.sComment,
952 oData.enmPendingCmd, ));
953 (idGenTestBox, tsEffective) = self._oDb.fetchOne();
954 else:
955 idGenTestBox = oOldData.idGenTestBox;
956 tsEffective = oOldData.tsEffective;
957
958 if isinstance(oData, TestBoxDataEx):
959 # Calc in-group changes.
960 aoRemoved = list(oOldData.aoInSchedGroups);
961 aoNew = [];
962 aoUpdated = [];
963 for oNewInGroup in oData.aoInSchedGroups:
964 oOldInGroup = None;
965 for iCur, oCur in enumerate(aoRemoved):
966 if oCur.idSchedGroup == oNewInGroup.idSchedGroup:
967 oOldInGroup = aoRemoved.pop(iCur);
968 break;
969 if oOldInGroup is None:
970 aoNew.append(oNewInGroup);
971 elif oNewInGroup.iSchedPriority != oOldInGroup.iSchedPriority:
972 aoUpdated.append(oNewInGroup);
973
974 # Remove in-groups.
975 for oInGroup in aoRemoved:
976 self._oDb.callProc('TestBoxLogic_removeGroupEntry', (uidAuthor, oData.idTestBox, oInGroup.idSchedGroup, ));
977
978 # Add new ones.
979 for oInGroup in aoNew:
980 self._oDb.callProc('TestBoxLogic_addGroupEntry',
981 ( uidAuthor, oData.idTestBox, oInGroup.idSchedGroup, oInGroup.iSchedPriority, ) );
982
983 # Edit existing ones.
984 for oInGroup in aoUpdated:
985 self._oDb.callProc('TestBoxLogic_editGroupEntry',
986 ( uidAuthor, oData.idTestBox, oInGroup.idSchedGroup, oInGroup.iSchedPriority, ) );
987 else:
988 assert isinstance(oData, TestBoxData);
989
990 self._oDb.maybeCommit(fCommit);
991 return (idGenTestBox, tsEffective);
992
993
994 def removeEntry(self, uidAuthor, idTestBox, fCascade = False, fCommit = False):
995 """
996 Delete test box and scheduling group associations.
997 """
998 self._oDb.callProc('TestBoxLogic_removeEntry'
999 , ( uidAuthor, idTestBox, fCascade,));
1000 self._oDb.maybeCommit(fCommit);
1001 return True;
1002
1003
1004 def updateOnSignOn(self, idTestBox, idGenTestBox, sTestBoxAddr, sOs, sOsVersion, # pylint: disable=R0913,R0914
1005 sCpuVendor, sCpuArch, sCpuName, lCpuRevision, cCpus, fCpuHwVirt, fCpuNestedPaging, fCpu64BitGuest,
1006 fChipsetIoMmu, fRawMode, cMbMemory, cMbScratch, sReport, iTestBoxScriptRev, iPythonHexVersion):
1007 """
1008 Update the testbox attributes automatically on behalf of the testbox script.
1009 Returns the new generation id on success, raises an exception on failure.
1010 """
1011 _ = idGenTestBox;
1012 self._oDb.callProc('TestBoxLogic_updateOnSignOn'
1013 , ( idTestBox,
1014 sTestBoxAddr,
1015 sOs,
1016 sOsVersion,
1017 sCpuVendor,
1018 sCpuArch,
1019 sCpuName,
1020 lCpuRevision,
1021 cCpus,
1022 fCpuHwVirt,
1023 fCpuNestedPaging,
1024 fCpu64BitGuest,
1025 fChipsetIoMmu,
1026 fRawMode,
1027 cMbMemory,
1028 cMbScratch,
1029 sReport,
1030 iTestBoxScriptRev,
1031 iPythonHexVersion,));
1032 return self._oDb.fetchOne()[0];
1033
1034
1035 def setCommand(self, idTestBox, sOldCommand, sNewCommand, uidAuthor = None, fCommit = False, sComment = None):
1036 """
1037 Sets or resets the pending command on a testbox.
1038 Returns (idGenTestBox, tsEffective) of the new row.
1039 """
1040 ## @todo throw TMInFligthCollision again...
1041 self._oDb.callProc('TestBoxLogic_setCommand'
1042 , ( uidAuthor, idTestBox, sOldCommand, sNewCommand, sComment,));
1043 aoRow = self._oDb.fetchOne();
1044 self._oDb.maybeCommit(fCommit);
1045 return (aoRow[0], aoRow[1]);
1046
1047
1048 def getAll(self):
1049 """
1050 Retrieve list of all registered Test Box records from DB.
1051 """
1052 self._oDb.execute('SELECT *\n'
1053 'FROM TestBoxesWithStrings\n'
1054 'WHERE tsExpire=\'infinity\'::timestamp;')
1055
1056 aaoRows = self._oDb.fetchAll()
1057 aoRet = []
1058 for aoRow in aaoRows:
1059 aoRet.append(TestBoxData().initFromDbRow(aoRow))
1060 return aoRet
1061
1062
1063 def cachedLookup(self, idTestBox):
1064 # type: (int) -> TestBoxDataEx
1065 """
1066 Looks up the most recent TestBoxData object for idTestBox via
1067 an object cache.
1068
1069 Returns a shared TestBoxDataEx object. None if not found.
1070 Raises exception on DB error.
1071 """
1072 if self.dCache is None:
1073 self.dCache = self._oDb.getCache('TestBoxData');
1074 oEntry = self.dCache.get(idTestBox, None);
1075 if oEntry is None:
1076 fNeedNow = False;
1077 self._oDb.execute('SELECT TestBoxesWithStrings.*\n'
1078 'FROM TestBoxesWithStrings\n'
1079 'WHERE idTestBox = %s\n'
1080 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
1081 , (idTestBox, ));
1082 if self._oDb.getRowCount() == 0:
1083 # Maybe it was deleted, try get the last entry.
1084 self._oDb.execute('SELECT TestBoxesWithStrings.*\n'
1085 'FROM TestBoxesWithStrings\n'
1086 'WHERE idTestBox = %s\n'
1087 'ORDER BY tsExpire DESC\n'
1088 'LIMIT 1\n'
1089 , (idTestBox, ));
1090 fNeedNow = True;
1091 elif self._oDb.getRowCount() > 1:
1092 raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), idTestBox));
1093
1094 if self._oDb.getRowCount() == 1:
1095 aaoRow = self._oDb.fetchOne();
1096 if not fNeedNow:
1097 oEntry = TestBoxDataEx().initFromDbRowEx(aaoRow, self._oDb);
1098 else:
1099 oEntry = TestBoxDataEx().initFromDbRow(aaoRow);
1100 oEntry.initFromDbRowEx(aaoRow, self._oDb, tsNow = db.dbTimestampMinusOneTick(oEntry.tsExpire));
1101 self.dCache[idTestBox] = oEntry;
1102 return oEntry;
1103
1104
1105
1106 #
1107 # The virtual test sheriff interface.
1108 #
1109
1110 def hasTestBoxRecentlyBeenRebooted(self, idTestBox, cHoursBack = 2, tsNow = None):
1111 """
1112 Checks if the testbox has been rebooted in the specified time period.
1113
1114 This does not include already pending reboots, though under some
1115 circumstances it may. These being the test box entry being edited for
1116 other reasons.
1117
1118 Returns True / False.
1119 """
1120 if tsNow is None:
1121 tsNow = self._oDb.getCurrentTimestamp();
1122 self._oDb.execute('SELECT COUNT(idTestBox)\n'
1123 'FROM TestBoxes\n'
1124 'WHERE idTestBox = %s\n'
1125 ' AND tsExpire < %s\n'
1126 ' AND tsExpire >= %s - interval \'%s hours\'\n'
1127 ' AND enmPendingCmd IN (%s, %s)\n'
1128 , ( idTestBox, tsNow, tsNow, cHoursBack,
1129 TestBoxData.ksTestBoxCmd_Reboot, TestBoxData.ksTestBoxCmd_UpgradeAndReboot, ));
1130 return self._oDb.fetchOne()[0] > 0;
1131
1132
1133 def rebootTestBox(self, idTestBox, uidAuthor, sComment, sOldCommand = TestBoxData.ksTestBoxCmd_None, fCommit = False):
1134 """
1135 Issues a reboot command for the given test box.
1136 Return True on succes, False on in-flight collision.
1137 May raise DB exception on other trouble.
1138 """
1139 try:
1140 self.setCommand(idTestBox, sOldCommand, TestBoxData.ksTestBoxCmd_Reboot,
1141 uidAuthor = uidAuthor, fCommit = fCommit, sComment = sComment);
1142 except TMInFligthCollision:
1143 return False;
1144 except:
1145 raise;
1146 return True;
1147
1148
1149 def disableTestBox(self, idTestBox, uidAuthor, sComment, fCommit = False):
1150 """
1151 Disables the given test box.
1152
1153 Raises exception on trouble, without rollback.
1154 """
1155 oTestBox = TestBoxData().initFromDbWithId(self._oDb, idTestBox);
1156 if oTestBox.fEnabled:
1157 oTestBox.fEnabled = False;
1158 if sComment is not None:
1159 oTestBox.sComment = sComment;
1160 self.editEntry(oTestBox, uidAuthor = uidAuthor, fCommit = fCommit);
1161 return None;
1162
1163
1164#
1165# Unit testing.
1166#
1167
1168# pylint: disable=C0111
1169class TestBoxDataTestCase(ModelDataBaseTestCase):
1170 def setUp(self):
1171 self.aoSamples = [TestBoxData(),];
1172
1173if __name__ == '__main__':
1174 unittest.main();
1175 # not reached.
1176
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