VirtualBox

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

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

TestManager: Merged CPU vendor and revisions.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 47.0 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: testbox.py 65094 2017-01-04 02:49:19Z 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: 65094 $"
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
674 def __init__(self, oDb):
675 ModelLogicBase.__init__(self, oDb);
676 self.dCache = None;
677
678 def tryFetchTestBoxByUuid(self, sTestBoxUuid):
679 """
680 Tries to fetch a testbox by its UUID alone.
681 """
682 self._oDb.execute('SELECT TestBoxesWithStrings.*\n'
683 'FROM TestBoxesWithStrings\n'
684 'WHERE uuidSystem = %s\n'
685 ' AND tsExpire = \'infinity\'::timestamp\n'
686 'ORDER BY tsEffective DESC\n',
687 (sTestBoxUuid,));
688 if self._oDb.getRowCount() == 0:
689 return None;
690 if self._oDb.getRowCount() != 1:
691 raise TMTooManyRows('Database integrity error: %u hits' % (self._oDb.getRowCount(),));
692 oData = TestBoxData();
693 oData.initFromDbRow(self._oDb.fetchOne());
694 return oData;
695
696 def fetchForListing(self, iStart, cMaxRows, tsNow):
697 """
698 Fetches testboxes for listing.
699
700 Returns an array (list) of TestBoxDataForListing items, empty list if none.
701 The TestBoxDataForListing instances are just TestBoxData with two extra
702 members, an extra oStatus member that is either None or a TestBoxStatusData
703 instance, and a member tsCurrent holding CURRENT_TIMESTAMP.
704
705 Raises exception on error.
706 """
707 class TestBoxDataForListing(TestBoxDataEx):
708 """ We add two members for the listing. """
709 def __init__(self):
710 TestBoxDataEx.__init__(self);
711 self.tsCurrent = None; # CURRENT_TIMESTAMP
712 self.oStatus = None; # type: TestBoxStatusData
713
714 from testmanager.core.testboxstatus import TestBoxStatusData;
715
716 if tsNow is None:
717 self._oDb.execute('SELECT TestBoxesWithStrings.*,\n'
718 ' TestBoxStatuses.*\n'
719 'FROM TestBoxesWithStrings\n'
720 ' LEFT OUTER JOIN TestBoxStatuses\n'
721 ' ON TestBoxStatuses.idTestBox = TestBoxesWithStrings.idTestBox\n'
722 'WHERE TestBoxesWithStrings.tsExpire = \'infinity\'::TIMESTAMP\n'
723 'ORDER BY TestBoxesWithStrings.sName\n'
724 'LIMIT %s OFFSET %s\n'
725 , (cMaxRows, iStart,));
726 else:
727 self._oDb.execute('SELECT TestBoxesWithStrings.*,\n'
728 ' TestBoxStatuses.*\n'
729 'FROM TestBoxesWithStrings\n'
730 ' LEFT OUTER JOIN TestBoxStatuses\n'
731 ' ON TestBoxStatuses.idTestBox = TestBoxesWithStrings.idTestBox\n'
732 'WHERE tsExpire > %s\n'
733 ' AND tsEffective <= %s\n'
734 'ORDER BY TestBoxesWithStrings.sName\n'
735 'LIMIT %s OFFSET %s\n'
736 , ( tsNow, tsNow, cMaxRows, iStart,));
737
738 aoRows = [];
739 for aoOne in self._oDb.fetchAll():
740 oTestBox = TestBoxDataForListing().initFromDbRowEx(aoOne, self._oDb, tsNow);
741 oTestBox.tsCurrent = self._oDb.getCurrentTimestamp();
742 if aoOne[TestBoxData.kcDbColumns] is not None:
743 oTestBox.oStatus = TestBoxStatusData().initFromDbRow(aoOne[TestBoxData.kcDbColumns:]);
744 aoRows.append(oTestBox);
745 return aoRows;
746
747 def fetchForChangeLog(self, idTestBox, iStart, cMaxRows, tsNow): # pylint: disable=R0914
748 """
749 Fetches change log entries for a testbox.
750
751 Returns an array of ChangeLogEntry instance and an indicator whether
752 there are more entries.
753 Raises exception on error.
754 """
755
756 ## @todo calc changes to scheduler group!
757
758 if tsNow is None:
759 tsNow = self._oDb.getCurrentTimestamp();
760
761 self._oDb.execute('SELECT TestBoxesWithStrings.*\n'
762 'FROM TestBoxesWithStrings\n'
763 'WHERE TestBoxesWithStrings.tsEffective <= %s\n'
764 ' AND TestBoxesWithStrings.idTestBox = %s\n'
765 'ORDER BY TestBoxesWithStrings.tsExpire DESC\n'
766 'LIMIT %s OFFSET %s\n'
767 , (tsNow, idTestBox, cMaxRows + 1, iStart,));
768
769 aoRows = [];
770 for aoDbRow in self._oDb.fetchAll():
771 aoRows.append(TestBoxData().initFromDbRow(aoDbRow));
772
773 # Calculate the changes.
774 aoEntries = [];
775 for i in xrange(0, len(aoRows) - 1):
776 oNew = aoRows[i];
777 oOld = aoRows[i + 1];
778 aoChanges = [];
779 for sAttr in oNew.getDataAttributes():
780 if sAttr not in [ 'tsEffective', 'tsExpire', 'uidAuthor', ]:
781 oOldAttr = getattr(oOld, sAttr);
782 oNewAttr = getattr(oNew, sAttr);
783 if oOldAttr != oNewAttr:
784 if sAttr == 'sReport':
785 aoChanges.append(AttributeChangeEntryPre(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
786 else:
787 aoChanges.append(AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
788 aoEntries.append(ChangeLogEntry(oNew.uidAuthor, None, oNew.tsEffective, oNew.tsExpire, oNew, oOld, aoChanges));
789
790 # If we're at the end of the log, add the initial entry.
791 if len(aoRows) <= cMaxRows and len(aoRows) > 0:
792 oNew = aoRows[-1];
793 aoEntries.append(ChangeLogEntry(oNew.uidAuthor, None, oNew.tsEffective, oNew.tsExpire, oNew, None, []));
794
795 UserAccountLogic(self._oDb).resolveChangeLogAuthors(aoEntries);
796 return (aoEntries, len(aoRows) > cMaxRows);
797
798 def _validateAndConvertData(self, oData, enmValidateFor):
799 # type: (TestBoxDataEx, str) -> None
800 """
801 Helper for addEntry and editEntry that validates the scheduling group IDs in
802 addtion to what's covered by the default validateAndConvert of the data object.
803
804 Raises exception on invalid input.
805 """
806 dDataErrors = oData.validateAndConvert(self._oDb, enmValidateFor);
807 if len(dDataErrors) > 0:
808 raise TMInvalidData('TestBoxLogic.addEntry: %s' % (dDataErrors,));
809 if isinstance(oData, TestBoxDataEx):
810 if len(oData.aoInSchedGroups):
811 sSchedGrps = ', '.join('(%s)' % oCur.idSchedGroup for oCur in oData.aoInSchedGroups);
812 self._oDb.execute('SELECT SchedGroupIDs.idSchedGroup\n'
813 'FROM (VALUES ' + sSchedGrps + ' ) AS SchedGroupIDs(idSchedGroup)\n'
814 ' LEFT OUTER JOIN SchedGroups\n'
815 ' ON SchedGroupIDs.idSchedGroup = SchedGroups.idSchedGroup\n'
816 ' AND SchedGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
817 'WHERE SchedGroups.idSchedGroup IS NULL\n');
818 aaoRows = self._oDb.fetchAll();
819 if len(aaoRows) > 0:
820 raise TMInvalidData('TestBoxLogic.addEntry missing scheduling groups: %s'
821 % (', '.join(str(aoRow[0]) for aoRow in aaoRows),));
822 return None;
823
824 def addEntry(self, oData, uidAuthor, fCommit = False):
825 # type: (TestBoxDataEx, int, bool) -> (int, int, datetime.datetime)
826 """
827 Creates a testbox in the database.
828 Returns the testbox ID, testbox generation ID and effective timestamp
829 of the created testbox on success. Throws error on failure.
830 """
831
832 #
833 # Validate. Extra work because of missing foreign key (due to history).
834 #
835 self._validateAndConvertData(oData, oData.ksValidateFor_Add);
836
837 #
838 # Do it.
839 #
840 self._oDb.callProc('TestBoxLogic_addEntry'
841 , ( uidAuthor,
842 oData.ip, # Should we allow setting the IP?
843 oData.uuidSystem,
844 oData.sName,
845 oData.sDescription,
846 oData.fEnabled,
847 oData.enmLomKind,
848 oData.ipLom,
849 oData.pctScaleTimeout,
850 oData.sComment,
851 oData.enmPendingCmd, ) );
852 (idTestBox, idGenTestBox, tsEffective) = self._oDb.fetchOne();
853
854 for oInSchedGrp in oData.aoInSchedGroups:
855 self._oDb.callProc('TestBoxLogic_addGroupEntry',
856 ( uidAuthor, idTestBox, oInSchedGrp.idSchedGroup, oInSchedGrp.iSchedPriority,) );
857
858 self._oDb.maybeCommit(fCommit);
859 return (idTestBox, idGenTestBox, tsEffective);
860
861
862 def editEntry(self, oData, uidAuthor, fCommit = False):
863 """
864 Data edit update, web UI is the primary user.
865
866 oData is either TestBoxDataEx or TestBoxData. The latter is for enabling
867 Returns the new generation ID and effective date.
868 """
869
870 #
871 # Validate.
872 #
873 self._validateAndConvertData(oData, oData.ksValidateFor_Edit);
874
875 #
876 # Get current data.
877 #
878 oOldData = TestBoxDataEx().initFromDbWithId(self._oDb, oData.idTestBox);
879
880 #
881 # Do it.
882 #
883 if not oData.isEqualEx(oOldData, [ 'tsEffective', 'tsExpire', 'uidAuthor', 'aoInSchedGroups', ]
884 + TestBoxData.kasMachineSettableOnly ):
885 self._oDb.callProc('TestBoxLogic_editEntry'
886 , ( uidAuthor,
887 oData.idTestBox,
888 oData.ip, # Should we allow setting the IP?
889 oData.uuidSystem,
890 oData.sName,
891 oData.sDescription,
892 oData.fEnabled,
893 oData.enmLomKind,
894 oData.ipLom,
895 oData.pctScaleTimeout,
896 oData.sComment,
897 oData.enmPendingCmd, ));
898 (idGenTestBox, tsEffective) = self._oDb.fetchOne();
899 else:
900 idGenTestBox = oOldData.idGenTestBox;
901 tsEffective = oOldData.tsEffective;
902
903 if isinstance(oData, TestBoxDataEx):
904 # Calc in-group changes.
905 aoRemoved = list(oOldData.aoInSchedGroups);
906 aoNew = [];
907 aoUpdated = [];
908 for oNewInGroup in oData.aoInSchedGroups:
909 oOldInGroup = None;
910 for iCur, oCur in enumerate(aoRemoved):
911 if oCur.idSchedGroup == oNewInGroup.idSchedGroup:
912 oOldInGroup = aoRemoved.pop(iCur);
913 break;
914 if oOldInGroup is None:
915 aoNew.append(oNewInGroup);
916 elif oNewInGroup.iSchedPriority != oOldInGroup.iSchedPriority:
917 aoUpdated.append(oNewInGroup);
918
919 # Remove in-groups.
920 for oInGroup in aoRemoved:
921 self._oDb.callProc('TestBoxLogic_removeGroupEntry', (uidAuthor, oData.idTestBox, oInGroup.idSchedGroup, ));
922
923 # Add new ones.
924 for oInGroup in aoNew:
925 self._oDb.callProc('TestBoxLogic_addGroupEntry',
926 ( uidAuthor, oData.idTestBox, oInGroup.idSchedGroup, oInGroup.iSchedPriority, ) );
927
928 # Edit existing ones.
929 for oInGroup in aoUpdated:
930 self._oDb.callProc('TestBoxLogic_editGroupEntry',
931 ( uidAuthor, oData.idTestBox, oInGroup.idSchedGroup, oInGroup.iSchedPriority, ) );
932 else:
933 assert isinstance(oData, TestBoxData);
934
935 self._oDb.maybeCommit(fCommit);
936 return (idGenTestBox, tsEffective);
937
938
939 def removeEntry(self, uidAuthor, idTestBox, fCascade = False, fCommit = False):
940 """
941 Delete test box and scheduling group associations.
942 """
943 self._oDb.callProc('TestBoxLogic_removeEntry'
944 , ( uidAuthor, idTestBox, fCascade,));
945 self._oDb.maybeCommit(fCommit);
946 return True;
947
948
949 def updateOnSignOn(self, idTestBox, idGenTestBox, sTestBoxAddr, sOs, sOsVersion, # pylint: disable=R0913,R0914
950 sCpuVendor, sCpuArch, sCpuName, lCpuRevision, cCpus, fCpuHwVirt, fCpuNestedPaging, fCpu64BitGuest,
951 fChipsetIoMmu, fRawMode, cMbMemory, cMbScratch, sReport, iTestBoxScriptRev, iPythonHexVersion):
952 """
953 Update the testbox attributes automatically on behalf of the testbox script.
954 Returns the new generation id on success, raises an exception on failure.
955 """
956 _ = idGenTestBox;
957 self._oDb.callProc('TestBoxLogic_updateOnSignOn'
958 , ( idTestBox,
959 sTestBoxAddr,
960 sOs,
961 sOsVersion,
962 sCpuVendor,
963 sCpuArch,
964 sCpuName,
965 lCpuRevision,
966 cCpus,
967 fCpuHwVirt,
968 fCpuNestedPaging,
969 fCpu64BitGuest,
970 fChipsetIoMmu,
971 fRawMode,
972 cMbMemory,
973 cMbScratch,
974 sReport,
975 iTestBoxScriptRev,
976 iPythonHexVersion,));
977 return self._oDb.fetchOne()[0];
978
979
980 def setCommand(self, idTestBox, sOldCommand, sNewCommand, uidAuthor = None, fCommit = False, sComment = None):
981 """
982 Sets or resets the pending command on a testbox.
983 Returns (idGenTestBox, tsEffective) of the new row.
984 """
985 ## @todo throw TMInFligthCollision again...
986 self._oDb.callProc('TestBoxLogic_setCommand'
987 , ( uidAuthor, idTestBox, sOldCommand, sNewCommand, sComment,));
988 aoRow = self._oDb.fetchOne();
989 self._oDb.maybeCommit(fCommit);
990 return (aoRow[0], aoRow[1]);
991
992
993 def getAll(self):
994 """
995 Retrieve list of all registered Test Box records from DB.
996 """
997 self._oDb.execute('SELECT *\n'
998 'FROM TestBoxesWithStrings\n'
999 'WHERE tsExpire=\'infinity\'::timestamp;')
1000
1001 aaoRows = self._oDb.fetchAll()
1002 aoRet = []
1003 for aoRow in aaoRows:
1004 aoRet.append(TestBoxData().initFromDbRow(aoRow))
1005 return aoRet
1006
1007
1008 def cachedLookup(self, idTestBox):
1009 # type: (int) -> TestBoxDataEx
1010 """
1011 Looks up the most recent TestBoxData object for idTestBox via
1012 an object cache.
1013
1014 Returns a shared TestBoxDataEx object. None if not found.
1015 Raises exception on DB error.
1016 """
1017 if self.dCache is None:
1018 self.dCache = self._oDb.getCache('TestBoxData');
1019 oEntry = self.dCache.get(idTestBox, None);
1020 if oEntry is None:
1021 fNeedNow = False;
1022 self._oDb.execute('SELECT TestBoxesWithStrings.*\n'
1023 'FROM TestBoxesWithStrings\n'
1024 'WHERE idTestBox = %s\n'
1025 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
1026 , (idTestBox, ));
1027 if self._oDb.getRowCount() == 0:
1028 # Maybe it was deleted, try get the last entry.
1029 self._oDb.execute('SELECT TestBoxesWithStrings.*\n'
1030 'FROM TestBoxesWithStrings\n'
1031 'WHERE idTestBox = %s\n'
1032 'ORDER BY tsExpire DESC\n'
1033 'LIMIT 1\n'
1034 , (idTestBox, ));
1035 fNeedNow = True;
1036 elif self._oDb.getRowCount() > 1:
1037 raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), idTestBox));
1038
1039 if self._oDb.getRowCount() == 1:
1040 aaoRow = self._oDb.fetchOne();
1041 if not fNeedNow:
1042 oEntry = TestBoxDataEx().initFromDbRowEx(aaoRow, self._oDb);
1043 else:
1044 oEntry = TestBoxDataEx().initFromDbRow(aaoRow);
1045 oEntry.initFromDbRowEx(aaoRow, self._oDb, tsNow = db.dbTimestampMinusOneTick(oEntry.tsExpire));
1046 self.dCache[idTestBox] = oEntry;
1047 return oEntry;
1048
1049
1050
1051 #
1052 # The virtual test sheriff interface.
1053 #
1054
1055 def hasTestBoxRecentlyBeenRebooted(self, idTestBox, cHoursBack = 2, tsNow = None):
1056 """
1057 Checks if the testbox has been rebooted in the specified time period.
1058
1059 This does not include already pending reboots, though under some
1060 circumstances it may. These being the test box entry being edited for
1061 other reasons.
1062
1063 Returns True / False.
1064 """
1065 if tsNow is None:
1066 tsNow = self._oDb.getCurrentTimestamp();
1067 self._oDb.execute('SELECT COUNT(idTestBox)\n'
1068 'FROM TestBoxes\n'
1069 'WHERE idTestBox = %s\n'
1070 ' AND tsExpire < %s\n'
1071 ' AND tsExpire >= %s - interval \'%s hours\'\n'
1072 ' AND enmPendingCmd IN (%s, %s)\n'
1073 , ( idTestBox, tsNow, tsNow, cHoursBack,
1074 TestBoxData.ksTestBoxCmd_Reboot, TestBoxData.ksTestBoxCmd_UpgradeAndReboot, ));
1075 return self._oDb.fetchOne()[0] > 0;
1076
1077
1078 def rebootTestBox(self, idTestBox, uidAuthor, sComment, sOldCommand = TestBoxData.ksTestBoxCmd_None, fCommit = False):
1079 """
1080 Issues a reboot command for the given test box.
1081 Return True on succes, False on in-flight collision.
1082 May raise DB exception on other trouble.
1083 """
1084 try:
1085 self.setCommand(idTestBox, sOldCommand, TestBoxData.ksTestBoxCmd_Reboot,
1086 uidAuthor = uidAuthor, fCommit = fCommit, sComment = sComment);
1087 except TMInFligthCollision:
1088 return False;
1089 except:
1090 raise;
1091 return True;
1092
1093
1094 def disableTestBox(self, idTestBox, uidAuthor, sComment, fCommit = False):
1095 """
1096 Disables the given test box.
1097
1098 Raises exception on trouble, without rollback.
1099 """
1100 oTestBox = TestBoxData().initFromDbWithId(self._oDb, idTestBox);
1101 if oTestBox.fEnabled:
1102 oTestBox.fEnabled = False;
1103 if sComment is not None:
1104 oTestBox.sComment = sComment;
1105 self.editEntry(oTestBox, uidAuthor = uidAuthor, fCommit = fCommit);
1106 return None;
1107
1108
1109#
1110# Unit testing.
1111#
1112
1113# pylint: disable=C0111
1114class TestBoxDataTestCase(ModelDataBaseTestCase):
1115 def setUp(self):
1116 self.aoSamples = [TestBoxData(),];
1117
1118if __name__ == '__main__':
1119 unittest.main();
1120 # not reached.
1121
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