VirtualBox

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

Last change on this file since 70486 was 69111, checked in by vboxsync, 7 years ago

(C) year

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette