VirtualBox

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

Last change on this file since 72293 was 70660, checked in by vboxsync, 7 years ago

ValidationKit: Python 3 and pylint 1.8.1 adjustments/fixes.

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