Changeset 61502 in vbox for trunk/src/VBox
- Timestamp:
- Jun 6, 2016 5:53:01 PM (9 years ago)
- Location:
- trunk/src/VBox/ValidationKit/testmanager
- Files:
-
- 12 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/ValidationKit/testmanager/core/base.py
r61468 r61502 413 413 Child classes can override to do special parameter conversion jobs. 414 414 """ 415 sPrefix = self.getHungarianPrefix(sAttr);415 sPrefix = self.getHungarianPrefix(sAttr); 416 416 asValidValues = getattr(self, 'kasValidValues_' + sAttr, None); 417 fAllowNull = sAttr in getattr(self, 'kasAllowNullAttributes', list()); 417 418 if fStrict: 418 419 if sPrefix == 'f': … … 423 424 oNewValue = oDisp.getListOfStrParams(sParam, []); 424 425 else: 425 oNewValue = oDisp.getStringParam(sParam, asValidValues, None );426 oNewValue = oDisp.getStringParam(sParam, asValidValues, None, fAllowNull = fAllowNull); 426 427 else: 427 428 if sPrefix[0] == 'a': … … 429 430 else: 430 431 assert oValue is not None, 'sAttr=%s' % (sAttr,); 431 oNewValue = oDisp.getStringParam(sParam, asValidValues, oValue );432 oNewValue = oDisp.getStringParam(sParam, asValidValues, oValue, fAllowNull = fAllowNull); 432 433 return oNewValue; 433 434 … … 1053 1054 self.oDisp = oDisp; 1054 1055 self.sAttrFmt = sAttrFmt; 1055 def getStringParam(self, sName, asValidValues = None, sDefault = None ):1056 def getStringParam(self, sName, asValidValues = None, sDefault = None, fAllowNull = False): 1056 1057 """See WuiDispatcherBase.getStringParam.""" 1057 return self.oDisp.getStringParam(self.sAttrFmt % (sName,), asValidValues, sDefault );1058 return self.oDisp.getStringParam(self.sAttrFmt % (sName,), asValidValues, sDefault, fAllowNull = fAllowNull); 1058 1059 def getListOfStrParams(self, sName, asDefaults = None): 1059 1060 """See WuiDispatcherBase.getListOfStrParams.""" … … 1133 1134 def testInitFromParams(self): 1134 1135 class DummyDisp(object): 1135 def getStringParam(self, sName, asValidValues = None, sDefault = None ):1136 _ = sName; _ = asValidValues; 1136 def getStringParam(self, sName, asValidValues = None, sDefault = None, fAllowNull = False): 1137 _ = sName; _ = asValidValues; _ = fAllowNull; 1137 1138 return sDefault; 1138 1139 def getListOfStrParams(self, sName, asDefaults = None): -
trunk/src/VBox/ValidationKit/testmanager/core/db.py
r61284 r61502 100 100 """ 101 101 return dbTimestampToZuluDatetime(datetime.datetime.utcnow()); 102 103 def dbTimestampMinusOneTick(oValue): 104 """ 105 Returns a new timestamp that's one tick before the given one. 106 """ 107 oValue = dbTimestampToZuluDatetime(oValue); 108 return oValue - datetime.timedelta(microseconds = 1); 102 109 103 110 def isDbInterval(oValue): -
trunk/src/VBox/ValidationKit/testmanager/core/schedgroup.py
r61472 r61502 259 259 def __init__(self): 260 260 SchedGroupData.__init__(self); 261 self.aoMembers = []; # SchedGroupMemberDataEx.261 self.aoMembers = []; # type: SchedGroupMemberDataEx 262 262 263 263 # Two build sources for convenience sake. 264 self.oBuildSrc = None; 265 self.oBuildSrcValidationKit = None; 264 self.oBuildSrc = None; # type: TestBoxData 265 self.oBuildSrcValidationKit = None; # type: TestBoxData 266 266 # List of test boxes that uses this group for convenience. 267 self.aoTestBoxes = None; 267 self.aoTestBoxes = None; # type: list[TestBoxData] 268 268 269 269 def _initExtraMembersFromDb(self, oDb, tsNow = None, sPeriodBack = None): … … 294 294 # Test Boxes. 295 295 # 296 ## @todo sPeriodBack! 297 if tsNow is None: 298 oDb.execute('SELECT *\n' 299 'FROM TestBoxesWithStrings\n' 300 'WHERE TestBoxesWithStrings.idSchedGroup = %s\n' 301 ' AND TestBoxesWithStrings.tsExpire = \'infinity\'::TIMESTAMP\n' 302 'ORDER BY TestBoxesWithStrings.sName, TestBoxesWithStrings.idTestBox\n' 303 , (self.idSchedGroup,)); 304 else: 305 oDb.execute('SELECT *\n' 306 'FROM TestBoxesWithStrings\n' 307 'WHERE TestBoxesWithStrings.idSchedGroup = %s\n' 308 ' AND TestBoxesWithStrings.tsExpire > %s\n' 309 ' AND TestBoxesWithStrings.tsEffective <= %s\n' 310 'ORDER BY TestBoxesWithStrings.sName, TestBoxesWithStrings.idTestBox\n' 311 , (self.idSchedGroup, tsNow, tsNow, tsNow, tsNow)); 296 oDb.execute('SELECT TestBoxesWithStrings.*\n' 297 'FROM TestBoxesWithStrings,\n' 298 ' TestBoxesInSchedGroups\n' 299 'WHERE TestBoxesInSchedGroups.idSchedGroup = %s\n' 300 + self.formatSimpleNowAndPeriod(oDb, tsNow, sPeriodBack, sTablePrefix = 'TestBoxesInSchedGroups.') + 301 ' AND TestBoxesWithStrings.idTestBox = TestBoxesInSchedGroups.idTestBox\n' 302 + self.formatSimpleNowAndPeriod(oDb, tsNow, sPeriodBack, sTablePrefix = 'TestBoxesWithStrings.') + 303 'ORDER BY TestBoxesWithStrings.sName, TestBoxesWithStrings.idTestBox\n' 304 , (self.idSchedGroup,)); 312 305 for aoRow in oDb.fetchAll(): 313 306 self.aoTestBoxes.append(TestBoxData().initFromDbRow(aoRow)); … … 316 309 # Test groups. 317 310 # 318 ## @todo sPeriodBack! 319 if tsNow is None: 320 oDb.execute('SELECT SchedGroupMembers.*, TestGroups.*\n' 321 'FROM SchedGroupMembers\n' 322 'LEFT OUTER JOIN TestGroups ON (SchedGroupMembers.idTestGroup = TestGroups.idTestGroup)\n' 323 'WHERE SchedGroupMembers.idSchedGroup = %s\n' 324 ' AND SchedGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n' 325 ' AND TestGroups.tsExpire = \'infinity\'::TIMESTAMP\n' 326 'ORDER BY SchedGroupMembers.idTestGroupPreReq, SchedGroupMembers.idTestGroup\n' 327 , (self.idSchedGroup,)); 328 else: 329 oDb.execute('SELECT SchedGroupMembers.*, TestGroups.*\n' 330 'FROM SchedGroupMembers\n' 331 'LEFT OUTER JOIN TestGroups ON (SchedGroupMembers.idTestGroup = TestGroups.idTestGroup)\n' 332 'WHERE SchedGroupMembers.idSchedGroup = %s\n' 333 ' AND SchedGroupMembers.tsExpire > %s\n' 334 ' AND SchedGroupMembers.tsEffective <= %s\n' 335 ' AND TestGroups.tsExpire > %s\n' 336 ' AND TestGroups.tsEffective <= %s\n' 337 'ORDER BY SchedGroupMembers.idTestGroupPreReq, SchedGroupMembers.idTestGroup\n' 338 , (self.idSchedGroup, tsNow, tsNow, tsNow, tsNow)); 339 311 oDb.execute('SELECT SchedGroupMembers.*, TestGroups.*\n' 312 'FROM SchedGroupMembers\n' 313 'LEFT OUTER JOIN TestGroups ON (SchedGroupMembers.idTestGroup = TestGroups.idTestGroup)\n' 314 'WHERE SchedGroupMembers.idSchedGroup = %s\n' 315 + self.formatSimpleNowAndPeriod(oDb, tsNow, sPeriodBack, sTablePrefix = 'SchedGroupMembers.') 316 + self.formatSimpleNowAndPeriod(oDb, tsNow, sPeriodBack, sTablePrefix = 'TestGroups.') + 317 'ORDER BY SchedGroupMembers.idTestGroupPreReq, SchedGroupMembers.idTestGroup\n' 318 , (self.idSchedGroup,)); 340 319 for aoRow in oDb.fetchAll(): 341 320 self.aoMembers.append(SchedGroupMemberDataEx().initFromDbRow(aoRow)); -
trunk/src/VBox/ValidationKit/testmanager/core/schedulerbase.py
r61474 r61502 44 44 from testmanager.core.schedgroup import SchedGroupData, SchedGroupLogic; 45 45 from testmanager.core.systemlog import SystemLogData, SystemLogLogic; 46 from testmanager.core.testbox import TestBoxData ;46 from testmanager.core.testbox import TestBoxData, TestBoxDataEx; 47 47 from testmanager.core.testboxstatus import TestBoxStatusData, TestBoxStatusLogic; 48 48 from testmanager.core.testcase import TestCaseLogic; … … 783 783 # Instantiate the specified scheduler and let it do the rest. 784 784 # 785 oSchedGrpData = SchedGroupData().initFromDbWithId(oDb, oTest Box.idSchedGroup, oTestSet.tsCreated);785 oSchedGrpData = SchedGroupData().initFromDbWithId(oDb, oTestSet.idSchedGroup, oTestSet.tsCreated); 786 786 assert oSchedGrpData.fEnabled is True; 787 787 assert oSchedGrpData.idBuildSrc is not None; … … 838 838 839 839 def _createTestSet(self, oTask, oTestEx, oTestBoxData, oBuild, oValidationKitBuild, tsNow): 840 # type: (SchedQueueData, TestCaseArgsDataEx, TestBoxData, BuildDataEx, BuildDataEx, datetime.datetime) -> int 840 841 """ 841 842 Creates a test set for using the given data. 842 843 Will not commit, someone up the callstack will that later on. 844 843 845 Returns the test set ID, may raise an exception on database error. 844 846 """ … … 918 920 oTestBoxData.idGenTestBox, 919 921 oTestBoxData.idTestBox, 920 oT estBoxData.idSchedGroup,922 oTask.idSchedGroup, 921 923 oTask.idTestGroup, 922 924 oTestEx.oTestCase.idGenTestCase, … … 1289 1291 1290 1292 @staticmethod 1291 def scheduleNewTask(oDb, oTestBoxData, sBaseUrl, iVerbosity = 0): 1292 """ 1293 Schedules a new task. 1294 """ 1293 def _pickSchedGroup(oTestBoxDataEx, iWorkItem): 1294 """ 1295 Picks the next scheduling group for the given testbox. 1296 """ 1297 if len(oTestBoxDataEx.aoInSchedGroups) == 1: 1298 oSchedGroup = oTestBoxDataEx.aoInSchedGroups[0].oSchedGroup; 1299 if oSchedGroup.fEnabled and oSchedGroup.idBuildSrc is not None: 1300 return (oSchedGroup, 0); 1301 1302 elif len(oTestBoxDataEx.aoInSchedGroups) > 0: 1303 # Construct priority table of currently enabled scheduling groups. 1304 aaoList1 = []; 1305 for oInGroup in oTestBoxDataEx.aoInSchedGroups: 1306 oSchedGroup = oInGroup.oSchedGroup; 1307 if oSchedGroup.fEnabled and oSchedGroup.idBuildSrc is not None: 1308 iSchedPriority = oInGroup.iSchedPriority; 1309 if iSchedPriority > 31: # paranoia 1310 iSchedPriority = 31; 1311 elif iSchedPriority < 0: # paranoia 1312 iSchedPriority = 0; 1313 1314 for iSchedPriority in xrange(min(iSchedPriority, len(aaoList1))): 1315 aaoList1[iSchedPriority].append(oSchedGroup); 1316 while len(aaoList1) <= iSchedPriority: 1317 aaoList1.append([oSchedGroup,]); 1318 1319 # Flatten it into a single list, mixing the priorities a little so it doesn't 1320 # take forever before low priority stuff is executed. 1321 aoFlat = []; 1322 iLo = 0; 1323 iHi = len(aaoList1) - 1; 1324 while iHi >= iLo: 1325 aoFlat += aaoList1[iHi]; 1326 if iLo < iHi: 1327 aoFlat += aaoList1[iLo]; 1328 iLo += 1; 1329 iHi -= 1; 1330 1331 # Pick the next one. 1332 iWorkItem += 1; 1333 if iWorkItem >= len(aoFlat): 1334 iWorkItem = 0; 1335 if iWorkItem < len(aoFlat): 1336 return (aoFlat[iWorkItem], iWorkItem); 1337 1338 # No active group. 1339 return (None, 0); 1340 1341 @staticmethod 1342 def scheduleNewTask(oDb, oTestBoxData, iWorkItem, sBaseUrl, iVerbosity = 0): 1343 # type: (TMDatabaseConnection, TestBoxData, int, str, int) -> None 1344 """ 1345 Schedules a new task for a testbox. 1346 """ 1347 oTBStatusLogic = TestBoxStatusLogic(oDb); 1348 1295 1349 try: 1296 1350 # … … 1298 1352 # related to our scheduling queue. Also, since this is a very 1299 1353 # expensive operation we lock the testbox status row to fend of 1300 # repeated retires by fault testbox script.1354 # repeated retires by faulty testbox scripts. 1301 1355 # 1302 1356 tsSecStart = utils.timestampSecond(); … … 1305 1359 oDb.execute('SELECT idTestBox FROM TestBoxStatuses WHERE idTestBox = %s FOR UPDATE NOWAIT' 1306 1360 % (oTestBoxData.idTestBox,)); 1307 oDb.execute('SELECT idSchedGroup FROM SchedQueues WHERE idSchedGroup = %s FOR UPDATE' 1308 % (oTestBoxData.idSchedGroup,)); 1361 oDb.execute('SELECT SchedQueues.idSchedGroup\n' 1362 ' FROM SchedQueues, TestBoxesInSchedGroups\n' 1363 'WHERE TestBoxesInSchedGroups.idTestBox = %s\n' 1364 ' AND TestBoxesInSchedGroups.tsExpire = \'infinity\'::TIMESTAMP\n' 1365 ' AND TestBoxesInSchedGroups.idSchedGroup = SchedQueues.idSchedGroup\n' 1366 ' FOR UPDATE' 1367 % (oTestBoxData.idTestBox,)); 1309 1368 1310 1369 # We need the current timestamp. 1311 1370 tsNow = oDb.getCurrentTimestamp(); 1312 1371 1313 # Re-read the testbox data ...1314 oTestBoxData Cur = TestBoxData().initFromDbWithId(oDb, oTestBoxData.idTestBox, tsNow);1315 if oTestBoxData Cur.fEnabled \1316 and oTestBoxData Cur.idGenTestBox == oTestBoxData.idGenTestBox \1317 and oTestBoxDataCur.idSchedGroup == oTestBoxData.idSchedGroup: # (paranoia wrt idSchedGroup) 1318 1319 # ... and schedule group data.1320 oSchedGrpData = SchedGroupData().initFromDbWithId(oDb, oTestBoxDataCur.idSchedGroup, tsNow);1321 if oSchedGrpData.fEnabled and oSchedGrpData.idBuildSrc is not None:1372 # Re-read the testbox data with scheduling group relations. 1373 oTestBoxDataEx = TestBoxDataEx().initFromDbWithId(oDb, oTestBoxData.idTestBox, tsNow); 1374 if oTestBoxDataEx.fEnabled \ 1375 and oTestBoxDataEx.idGenTestBox == oTestBoxData.idGenTestBox: 1376 1377 # Now, pick the scheduling group. 1378 (oSchedGroup, iWorkItem) = SchedulerBase._pickSchedGroup(oTestBoxDataEx, iWorkItem); 1379 if oSchedGroup is not None: 1380 assert oSchedGroup.fEnabled and oSchedGroup.idBuildSrc is not None; 1322 1381 1323 1382 # 1324 1383 # Instantiate the specified scheduler and let it do the rest. 1325 1384 # 1326 oScheduler = SchedulerBase._instantiate(oDb, oSchedGr pData, iVerbosity, tsSecStart);1327 dResponse = oScheduler.scheduleNewTaskWorker(oTestBoxData Cur, tsNow, sBaseUrl);1385 oScheduler = SchedulerBase._instantiate(oDb, oSchedGroup, iVerbosity, tsSecStart); 1386 dResponse = oScheduler.scheduleNewTaskWorker(oTestBoxDataEx, tsNow, sBaseUrl); 1328 1387 if dResponse is not None: 1388 oTBStatusLogic.updateWorkItem(oTestBoxDataEx.idTestBox, iWorkItem); 1329 1389 oDb.commit(); 1330 1390 return dResponse; -
trunk/src/VBox/ValidationKit/testmanager/core/testbox.pgsql
r61468 r61502 26 26 27 27 28 --- 29 -- Checks if the test box name is unique, ignoring a_idTestCaseIgnore. 30 -- Raises exception if duplicates are found. 31 -- 32 -- @internal 33 -- 34 CREATE OR REPLACE FUNCTION TestBoxLogic_checkUniqueName(a_sName TEXT, a_idTestBoxIgnore INTEGER) 35 RETURNS VOID AS $$ 36 DECLARE 37 v_cRows INTEGER; 38 BEGIN 39 SELECT COUNT(*) INTO v_cRows 40 FROM TestBoxes 41 WHERE sName = a_sName 42 AND tsExpire = 'infinity'::TIMESTAMP 43 AND idTestBox <> a_idTestBoxIgnore; 44 IF v_cRows <> 0 THEN 45 RAISE EXCEPTION 'Duplicate test box name "%" (% times)', a_sName, v_cRows; 46 END IF; 47 END; 48 $$ LANGUAGE plpgsql; 49 50 51 --- 52 -- Historize a row. 53 -- @internal 54 -- 55 CREATE OR REPLACE FUNCTION TestBoxLogic_historizeEntry(a_idGenTestBox INTEGER, a_tsExpire TIMESTAMP WITH TIME ZONE) 56 RETURNS VOID AS $$ 57 DECLARE 58 v_cUpdatedRows INTEGER; 59 BEGIN 60 UPDATE TestBoxes 61 SET tsExpire = a_tsExpire 62 WHERE idGenTestBox = a_idGenTestBox 63 AND tsExpire = 'infinity'::TIMESTAMP; 64 GET DIAGNOSTICS v_cUpdatedRows = ROW_COUNT; 65 IF v_cUpdatedRows <> 1 THEN 66 IF v_cUpdatedRows = 0 THEN 67 RAISE EXCEPTION 'Test box generation ID % is no longer valid', a_idGenTestBox; 68 END IF; 69 RAISE EXCEPTION 'Integrity error in TestBoxes: % current rows with idGenTestBox=%', v_cUpdatedRows, a_idGenTestBox; 70 END IF; 71 END; 72 $$ LANGUAGE plpgsql; 73 74 75 --- 76 -- Translate string via the string table. 77 -- 78 -- @returns NULL if a_sValue is NULL, otherwise a string ID. 79 -- 80 CREATE OR REPLACE FUNCTION TestBoxLogic_lookupOrFindString(a_sValue TEXT) 81 RETURNS INTEGER AS $$ 82 DECLARE 83 v_idStr INTEGER; 84 v_cRows INTEGER; 85 BEGIN 86 IF a_sValue IS NULL THEN 87 RETURN NULL; 88 END IF; 89 90 SELECT idStr 91 INTO v_idStr 92 FROM TestBoxStrTab 93 WHERE sValue = a_sValue; 94 GET DIAGNOSTICS v_cRows = ROW_COUNT; 95 IF v_cRows = 0 THEN 96 INSERT INTO TestBoxStrTab (sValue) 97 VALUES (a_sValue) 98 RETURNING idStr INTO v_idStr; 99 END IF; 100 RETURN v_idStr; 101 END; 102 $$ LANGUAGE plpgsql; 103 104 105 --- 106 -- Only adds the user settable parts of the row, i.e. not what TestBoxLogic_updateOnSignOn touches. 107 -- 108 CREATE OR REPLACE function TestBoxLogic_addEntry(a_uidAuthor INTEGER, 28 -- 29 -- Old type signatures. 30 -- 31 DROP FUNCTION IF EXISTS TestBoxLogic_addEntry(a_uidAuthor INTEGER, 109 32 a_ip inet, 110 33 a_uuidSystem uuid, … … 120 43 OUT r_idTestBox INTEGER, 121 44 OUT r_idGenTestBox INTEGER, 122 OUT r_tsEffective TIMESTAMP WITH TIME ZONE 123 ) AS $$ 124 DECLARE 125 v_idStrDescription INTEGER; 126 v_idStrComment INTEGER; 127 BEGIN 128 PERFORM TestBoxLogic_checkUniqueName(a_sName, -1); 129 130 SELECT TestBoxLogic_lookupOrFindString(a_sDescription) INTO v_idStrDescription; 131 SELECT TestBoxLogic_lookupOrFindString(a_sComment) INTO v_idStrComment; 132 133 INSERT INTO TestBoxes ( 134 tsEffective, -- 1 135 uidAuthor, -- 2 136 ip, -- 3 137 uuidSystem, -- 4 138 sName, -- 5 139 idStrDescription, -- 6 140 idSchedGroup, -- 7 141 fEnabled, -- 8 142 enmLomKind, -- 9 143 ipLom, -- 10 144 pctScaleTimeout, -- 11 145 idStrComment, -- 12 146 enmPendingCmd ) -- 13 147 VALUES (CURRENT_TIMESTAMP, -- 1 148 a_uidAuthor, -- 2 149 a_ip, -- 3 150 a_uuidSystem, -- 4 151 a_sName, -- 5 152 v_idStrDescription, -- 6 153 a_idSchedGroup, -- 7 154 a_fEnabled, -- 8 155 a_enmLomKind, -- 9 156 a_ipLom, -- 10 157 a_pctScaleTimeout, -- 11 158 v_idStrComment, -- 12 159 a_enmPendingCmd ) -- 13 160 RETURNING idTestBox, idGenTestBox, tsEffective INTO r_idTestBox, r_idGenTestBox, r_tsEffective; 161 END; 162 $$ LANGUAGE plpgsql; 163 164 --- 165 -- Only adds the user settable parts of the row, i.e. not what TestBoxLogic_updateOnSignOn touches. 166 -- 167 CREATE OR REPLACE function TestBoxLogic_editEntry(a_uidAuthor INTEGER, 45 OUT r_tsEffective TIMESTAMP WITH TIME ZONE); 46 DROP FUNCTION IF EXISTS TestBoxLogic_editEntry(a_uidAuthor INTEGER, 168 47 a_idTestBox INTEGER, 169 48 a_ip inet, … … 179 58 a_enmPendingCmd TestBoxCmd_T, 180 59 OUT r_idGenTestBox INTEGER, 60 OUT r_tsEffective TIMESTAMP WITH TIME ZONE); 61 DROP FUNCTION IF EXISTS TestBoxLogic_removeEntry(INTEGER, INTEGER, BOOLEAN); 62 DROP FUNCTION IF EXISTS TestBoxLogic_addGroupEntry(a_uidAuthor INTEGER, 63 a_idTestBox INTEGER, 64 a_idSchedGroup INTEGER, 65 a_iSchedPriority INTEGER, 66 OUT r_tsEffective TIMESTAMP WITH TIME ZONE); 67 DROP FUNCTION IF EXISTS TestBoxLogic_editGroupEntry(a_uidAuthor INTEGER, 68 a_idTestBox INTEGER, 69 a_idSchedGroup INTEGER, 70 a_iSchedPriority INTEGER, 71 OUT r_tsEffective INTEGER); 72 73 74 --- 75 -- Checks if the test box name is unique, ignoring a_idTestCaseIgnore. 76 -- Raises exception if duplicates are found. 77 -- 78 -- @internal 79 -- 80 CREATE OR REPLACE FUNCTION TestBoxLogic_checkUniqueName(a_sName TEXT, a_idTestBoxIgnore INTEGER) 81 RETURNS VOID AS $$ 82 DECLARE 83 v_cRows INTEGER; 84 BEGIN 85 SELECT COUNT(*) INTO v_cRows 86 FROM TestBoxes 87 WHERE sName = a_sName 88 AND tsExpire = 'infinity'::TIMESTAMP 89 AND idTestBox <> a_idTestBoxIgnore; 90 IF v_cRows <> 0 THEN 91 RAISE EXCEPTION 'Duplicate test box name "%" (% times)', a_sName, v_cRows; 92 END IF; 93 END; 94 $$ LANGUAGE plpgsql; 95 96 97 --- 98 -- Checks that the given scheduling group exists. 99 -- Raises exception if it doesn't. 100 -- 101 -- @internal 102 -- 103 CREATE OR REPLACE FUNCTION TestBoxLogic_checkSchedGroupExists(a_idSchedGroup INTEGER) 104 RETURNS VOID AS $$ 105 DECLARE 106 v_cRows INTEGER; 107 BEGIN 108 SELECT COUNT(*) INTO v_cRows 109 FROM SchedGroups 110 WHERE idSchedGroup = a_idSchedGroup 111 AND tsExpire = 'infinity'::TIMESTAMP; 112 IF v_cRows <> 1 THEN 113 IF v_cRows = 0 THEN 114 RAISE EXCEPTION 'Scheduling group with ID % was not found', a_idSchedGroup; 115 END IF; 116 RAISE EXCEPTION 'Integrity error in SchedGroups: % current rows with idSchedGroup=%', v_cRows, a_idSchedGroup; 117 END IF; 118 END; 119 $$ LANGUAGE plpgsql; 120 121 122 --- 123 -- Checks that the given testbxo + scheduling group pair does not currently exists. 124 -- Raises exception if it does. 125 -- 126 -- @internal 127 -- 128 CREATE OR REPLACE FUNCTION TestBoxLogic_checkTestBoxNotInSchedGroup(a_idTestBox INTEGER, a_idSchedGroup INTEGER) 129 RETURNS VOID AS $$ 130 DECLARE 131 v_cRows INTEGER; 132 BEGIN 133 SELECT COUNT(*) INTO v_cRows 134 FROM TestBoxesInSchedGroups 135 WHERE idTestBox = a_idTestBox 136 AND idSchedGroup = a_idSchedGroup 137 AND tsExpire = 'infinity'::TIMESTAMP; 138 IF v_cRows <> 0 THEN 139 RAISE EXCEPTION 'TestBox % is already a member of scheduling group %', a_idTestBox, a_idSchedGroup; 140 END IF; 141 END; 142 $$ LANGUAGE plpgsql; 143 144 145 --- 146 -- Historize a row. 147 -- @internal 148 -- 149 CREATE OR REPLACE FUNCTION TestBoxLogic_historizeEntry(a_idGenTestBox INTEGER, a_tsExpire TIMESTAMP WITH TIME ZONE) 150 RETURNS VOID AS $$ 151 DECLARE 152 v_cUpdatedRows INTEGER; 153 BEGIN 154 UPDATE TestBoxes 155 SET tsExpire = a_tsExpire 156 WHERE idGenTestBox = a_idGenTestBox 157 AND tsExpire = 'infinity'::TIMESTAMP; 158 GET DIAGNOSTICS v_cUpdatedRows = ROW_COUNT; 159 IF v_cUpdatedRows <> 1 THEN 160 IF v_cUpdatedRows = 0 THEN 161 RAISE EXCEPTION 'Test box generation ID % is no longer valid', a_idGenTestBox; 162 END IF; 163 RAISE EXCEPTION 'Integrity error in TestBoxes: % current rows with idGenTestBox=%', v_cUpdatedRows, a_idGenTestBox; 164 END IF; 165 END; 166 $$ LANGUAGE plpgsql; 167 168 169 --- 170 -- Historize a in-scheduling-group row. 171 -- @internal 172 -- 173 CREATE OR REPLACE FUNCTION TestBoxLogic_historizeGroupEntry(a_idTestBox INTEGER, 174 a_idSchedGroup INTEGER, 175 a_tsExpire TIMESTAMP WITH TIME ZONE) 176 RETURNS VOID AS $$ 177 DECLARE 178 v_cUpdatedRows INTEGER; 179 BEGIN 180 UPDATE TestBoxesInSchedGroups 181 SET tsExpire = a_tsExpire 182 WHERE idTestBox = a_idTestBox 183 AND idSchedGroup = a_idSchedGroup 184 AND tsExpire = 'infinity'::TIMESTAMP; 185 GET DIAGNOSTICS v_cUpdatedRows = ROW_COUNT; 186 IF v_cUpdatedRows <> 1 THEN 187 IF v_cUpdatedRows = 0 THEN 188 RAISE EXCEPTION 'TestBox ID % / SchedGroup ID % is no longer a valid combination', a_idTestBox, a_idSchedGroup; 189 END IF; 190 RAISE EXCEPTION 'Integrity error in TestBoxesInSchedGroups: % current rows for % / %', 191 v_cUpdatedRows, a_idTestBox, a_idSchedGroup; 192 END IF; 193 END; 194 $$ LANGUAGE plpgsql; 195 196 197 --- 198 -- Translate string via the string table. 199 -- 200 -- @returns NULL if a_sValue is NULL, otherwise a string ID. 201 -- 202 CREATE OR REPLACE FUNCTION TestBoxLogic_lookupOrFindString(a_sValue TEXT) 203 RETURNS INTEGER AS $$ 204 DECLARE 205 v_idStr INTEGER; 206 v_cRows INTEGER; 207 BEGIN 208 IF a_sValue IS NULL THEN 209 RETURN NULL; 210 END IF; 211 212 SELECT idStr 213 INTO v_idStr 214 FROM TestBoxStrTab 215 WHERE sValue = a_sValue; 216 GET DIAGNOSTICS v_cRows = ROW_COUNT; 217 IF v_cRows = 0 THEN 218 INSERT INTO TestBoxStrTab (sValue) 219 VALUES (a_sValue) 220 RETURNING idStr INTO v_idStr; 221 END IF; 222 RETURN v_idStr; 223 END; 224 $$ LANGUAGE plpgsql; 225 226 227 --- 228 -- Only adds the user settable parts of the row, i.e. not what TestBoxLogic_updateOnSignOn touches. 229 -- 230 CREATE OR REPLACE function TestBoxLogic_addEntry(a_uidAuthor INTEGER, 231 a_ip inet, 232 a_uuidSystem uuid, 233 a_sName TEXT, 234 a_sDescription TEXT, 235 a_fEnabled BOOLEAN, 236 a_enmLomKind LomKind_T, 237 a_ipLom inet, 238 a_pctScaleTimeout INTEGER, -- Actually smallint, but default typing fun. 239 a_sComment TEXT, 240 a_enmPendingCmd TestBoxCmd_T, 241 OUT r_idTestBox INTEGER, 242 OUT r_idGenTestBox INTEGER, 243 OUT r_tsEffective TIMESTAMP WITH TIME ZONE 244 ) AS $$ 245 DECLARE 246 v_idStrDescription INTEGER; 247 v_idStrComment INTEGER; 248 BEGIN 249 PERFORM TestBoxLogic_checkUniqueName(a_sName, -1); 250 251 SELECT TestBoxLogic_lookupOrFindString(a_sDescription) INTO v_idStrDescription; 252 SELECT TestBoxLogic_lookupOrFindString(a_sComment) INTO v_idStrComment; 253 254 INSERT INTO TestBoxes ( 255 tsEffective, -- 1 256 uidAuthor, -- 2 257 ip, -- 3 258 uuidSystem, -- 4 259 sName, -- 5 260 idStrDescription, -- 6 261 fEnabled, -- 7 262 enmLomKind, -- 8 263 ipLom, -- 9 264 pctScaleTimeout, -- 10 265 idStrComment, -- 11 266 enmPendingCmd ) -- 12 267 VALUES (CURRENT_TIMESTAMP, -- 1 268 a_uidAuthor, -- 2 269 a_ip, -- 3 270 a_uuidSystem, -- 4 271 a_sName, -- 5 272 v_idStrDescription, -- 6 273 a_fEnabled, -- 7 274 a_enmLomKind, -- 8 275 a_ipLom, -- 9 276 a_pctScaleTimeout, -- 10 277 v_idStrComment, -- 11 278 a_enmPendingCmd ) -- 12 279 RETURNING idTestBox, idGenTestBox, tsEffective INTO r_idTestBox, r_idGenTestBox, r_tsEffective; 280 END; 281 $$ LANGUAGE plpgsql; 282 283 284 CREATE OR REPLACE function TestBoxLogic_addGroupEntry(a_uidAuthor INTEGER, 285 a_idTestBox INTEGER, 286 a_idSchedGroup INTEGER, 287 a_iSchedPriority INTEGER, 288 OUT r_tsEffective TIMESTAMP WITH TIME ZONE 289 ) AS $$ 290 BEGIN 291 PERFORM TestBoxLogic_checkSchedGroupExists(a_idSchedGroup); 292 PERFORM TestBoxLogic_checkTestBoxNotInSchedGroup(a_idTestBox, a_idSchedGroup); 293 294 INSERT INTO TestBoxesInSchedGroups ( 295 idTestBox, 296 idSchedGroup, 297 tsEffective, 298 tsExpire, 299 uidAuthor, 300 iSchedPriority) 301 VALUES (a_idTestBox, 302 a_idSchedGroup, 303 CURRENT_TIMESTAMP, 304 'infinity'::TIMESTAMP, 305 a_uidAuthor, 306 a_iSchedPriority) 307 RETURNING tsEffective INTO r_tsEffective; 308 END; 309 $$ LANGUAGE plpgsql; 310 311 312 --- 313 -- Only adds the user settable parts of the row, i.e. not what TestBoxLogic_updateOnSignOn touches. 314 -- 315 CREATE OR REPLACE function TestBoxLogic_editEntry(a_uidAuthor INTEGER, 316 a_idTestBox INTEGER, 317 a_ip inet, 318 a_uuidSystem uuid, 319 a_sName TEXT, 320 a_sDescription TEXT, 321 a_fEnabled BOOLEAN, 322 a_enmLomKind LomKind_T, 323 a_ipLom inet, 324 a_pctScaleTimeout INTEGER, -- Actually smallint, but default typing fun. 325 a_sComment TEXT, 326 a_enmPendingCmd TestBoxCmd_T, 327 OUT r_idGenTestBox INTEGER, 181 328 OUT r_tsEffective TIMESTAMP WITH TIME ZONE 182 329 ) AS $$ … … 204 351 v_Row.sName := a_sName; 205 352 v_Row.idStrDescription := v_idStrDescription; 206 v_Row.idSchedGroup := a_idSchedGroup;207 353 v_Row.fEnabled := a_fEnabled; 208 354 v_Row.enmLomKind := a_enmLomKind; … … 225 371 226 372 227 DROP FUNCTION IF EXISTS TestBoxLogic_removeEntry(INTEGER, INTEGER, BOOLEAN); 373 CREATE OR REPLACE function TestBoxLogic_editGroupEntry(a_uidAuthor INTEGER, 374 a_idTestBox INTEGER, 375 a_idSchedGroup INTEGER, 376 a_iSchedPriority INTEGER, 377 OUT r_tsEffective TIMESTAMP WITH TIME ZONE 378 ) AS $$ 379 DECLARE 380 v_Row TestBoxesInSchedGroups%ROWTYPE; 381 v_idStrDescription INTEGER; 382 v_idStrComment INTEGER; 383 BEGIN 384 PERFORM TestBoxLogic_checkSchedGroupExists(a_idSchedGroup); 385 386 -- Fetch and historize the current row - there must be one. 387 UPDATE TestBoxesInSchedGroups 388 SET tsExpire = CURRENT_TIMESTAMP 389 WHERE idTestBox = a_idTestBox 390 AND idSchedGroup = a_idSchedGroup 391 AND tsExpire = 'infinity'::TIMESTAMP 392 RETURNING * INTO STRICT v_Row; 393 394 -- Modify the row with the new data. 395 v_Row.uidAuthor := a_uidAuthor; 396 v_Row.iSchedPriority := a_iSchedPriority; 397 v_Row.tsEffective := v_Row.tsExpire; 398 r_tsEffective := v_Row.tsExpire; 399 v_Row.tsExpire := 'infinity'::TIMESTAMP; 400 401 -- Insert the modified row. 402 INSERT INTO TestBoxesInSchedGroups VALUES (v_Row.*); 403 END; 404 $$ LANGUAGE plpgsql; 405 406 228 407 CREATE OR REPLACE FUNCTION TestBoxLogic_removeEntry(a_uidAuthor INTEGER, a_idTestBox INTEGER, a_fCascade BOOLEAN) 229 408 RETURNS VOID AS $$ … … 242 421 RAISE EXCEPTION 'CASCADE test box deletion is not implemented'; 243 422 END IF; 423 424 -- 425 -- Delete all current groups, skipping history since we're also deleting the testbox. 426 -- 427 UPDATE TestBoxesInSchedGroups 428 SET tsExpire = CURRENT_TIMESTAMP 429 WHERE idTestBox = a_idTestBox 430 AND tsExpire = 'infinity'::TIMESTAMP; 244 431 245 432 -- … … 271 458 WHEN TOO_MANY_ROWS THEN 272 459 RAISE EXCEPTION 'Integrity error in TestBoxes: Too many current rows for %', a_idTestBox; 460 END; 461 $$ LANGUAGE plpgsql; 462 463 464 CREATE OR REPLACE FUNCTION TestBoxLogic_removeGroupEntry(a_uidAuthor INTEGER, a_idTestBox INTEGER, a_idSchedGroup INTEGER) 465 RETURNS VOID AS $$ 466 DECLARE 467 v_Row TestBoxesInSchedGroups%ROWTYPE; 468 v_tsEffective TIMESTAMP WITH TIME ZONE; 469 BEGIN 470 -- 471 -- To preserve the information about who deleted the record, we try to 472 -- add a dummy record which expires immediately. I say try because of 473 -- the primary key, we must let the new record be valid for 1 us. :-( 474 -- 475 SELECT * INTO STRICT v_Row 476 FROM TestBoxesInSchedGroups 477 WHERE idTestBox = a_idTestBox 478 AND idSchedGroup = a_idSchedGroup 479 AND tsExpire = 'infinity'::TIMESTAMP; 480 481 v_tsEffective := CURRENT_TIMESTAMP - INTERVAL '1 microsecond'; 482 IF v_Row.tsEffective < v_tsEffective THEN 483 PERFORM TestBoxLogic_historizeGroupEntry(a_idTestBox, a_idSchedGroup, v_tsEffective); 484 485 v_Row.tsEffective := v_tsEffective; 486 v_Row.tsExpire := CURRENT_TIMESTAMP; 487 v_Row.uidAuthor := a_uidAuthor; 488 INSERT INTO TestBoxesInSchedGroups VALUES (v_Row.*); 489 ELSE 490 PERFORM TestBoxLogic_historizeGroupEntry(a_idTestBox, a_idSchedGroup, CURRENT_TIMESTAMP); 491 END IF; 492 493 EXCEPTION 494 WHEN NO_DATA_FOUND THEN 495 RAISE EXCEPTION 'TestBox #% does is not currently a member of scheduling group #%', a_idTestBox, a_idSchedGroup; 496 WHEN TOO_MANY_ROWS THEN 497 RAISE EXCEPTION 'Integrity error in TestBoxesInSchedGroups: Too many current rows for % / %', 498 a_idTestBox, a_idSchedGroup; 273 499 END; 274 500 $$ LANGUAGE plpgsql; -
trunk/src/VBox/ValidationKit/testmanager/core/testbox.py
r61468 r61502 31 31 32 32 # Standard python imports. 33 import copy; 33 34 import unittest; 34 35 35 36 # Validation Kit imports. 37 from testmanager.core import db; 36 38 from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMInFligthCollision, \ 37 TMInvalidData, TMTooManyRows, TMRowNotFound, ChangeLogEntry, AttributeChangeEntry; 39 TMInvalidData, TMTooManyRows, TMRowNotFound, \ 40 ChangeLogEntry, AttributeChangeEntry; 38 41 from testmanager.core.useraccount import UserAccountLogic; 42 43 44 class 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 89 class 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; 39 106 40 107 … … 92 159 ksParam_sName = 'TestBox_sName'; 93 160 ksParam_sDescription = 'TestBox_sDescription'; 94 ksParam_idSchedGroup = 'TestBox_idSchedGroup';95 161 ksParam_fEnabled = 'TestBox_fEnabled'; 96 162 ksParam_enmLomKind = 'TestBox_enmLomKind'; … … 119 185 kasInternalAttributes = [ 'idStrDescription', 'idStrComment', 'idStrOs', 'idStrOsVersion', 'idStrCpuVendor', 120 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', ]; 121 190 kasAllowNullAttributes = ['idTestBox', 'tsEffective', 'tsExpire', 'uidAuthor', 'idGenTestBox', 'sDescription', 122 'ipLom', 'sComment', 'sOs', 'sOsVersion', 'sCpuVendor', 'sCpuArch', 'sCpuName', 123 'lCpuRevision', 'cCpus', 'fCpuHwVirt', 'fCpuNestedPaging', 'fCpu64BitGuest', 'fChipsetIoMmu', 124 'fRawMode', 'cMbMemory', 'cMbScratch', 'sReport', 'iTestBoxScriptRev', 'iPythonHexVersion', 125 ] + kasInternalAttributes; 191 'ipLom', 'sComment', ] + kasMachineSettableOnly + kasInternalAttributes; 126 192 127 193 kasValidValues_enmLomKind = kasLomKindValues; … … 131 197 kcchMax_sReport = 65535; 132 198 133 134 kcDbColumns = 41; # including the 7 string joins columns 199 kcDbColumns = 40; # including the 7 string joins columns 200 135 201 136 202 def __init__(self): … … 150 216 self.sName = None; 151 217 self.idStrDescription = None; 152 self.idSchedGroup = 1;153 218 self.fEnabled = False; 154 219 self.enmLomKind = self.ksLomKind_None; … … 202 267 self.sName = aoRow[7]; 203 268 self.idStrDescription = aoRow[8]; 204 self.idSchedGroup = aoRow[9]; 205 self.fEnabled = aoRow[10]; 206 self.enmLomKind = aoRow[11]; 207 self.ipLom = aoRow[12]; 208 self.pctScaleTimeout = aoRow[13]; 209 self.idStrComment = aoRow[14]; 210 self.idStrOs = aoRow[15]; 211 self.idStrOsVersion = aoRow[16]; 212 self.idStrCpuVendor = aoRow[17]; 213 self.idStrCpuArch = aoRow[18]; 214 self.idStrCpuName = aoRow[19]; 215 self.lCpuRevision = aoRow[20]; 216 self.cCpus = aoRow[21]; 217 self.fCpuHwVirt = aoRow[22]; 218 self.fCpuNestedPaging = aoRow[23]; 219 self.fCpu64BitGuest = aoRow[24]; 220 self.fChipsetIoMmu = aoRow[25]; 221 self.fRawMode = aoRow[26]; 222 self.cMbMemory = aoRow[27]; 223 self.cMbScratch = aoRow[28]; 224 self.idStrReport = aoRow[29]; 225 self.iTestBoxScriptRev = aoRow[30]; 226 self.iPythonHexVersion = aoRow[31]; 227 self.enmPendingCmd = aoRow[32]; 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]; 228 292 229 293 # String table values. 230 294 if len(aoRow) > 32: 231 self.sDescription = aoRow[3 3];232 self.sComment = aoRow[3 4];233 self.sOs = aoRow[3 5];234 self.sOsVersion = aoRow[3 6];235 self.sCpuVendor = aoRow[3 7];236 self.sCpuArch = aoRow[3 8];237 self.sCpuName = aoRow[3 9];238 self.sReport = aoRow[ 40];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]; 239 303 240 304 return self; … … 254 318 return self.initFromDbRow(aoRow); 255 319 256 def initFromDbWithGenId(self, oDb, idGenTestBox ):320 def initFromDbWithGenId(self, oDb, idGenTestBox, tsNow = None): 257 321 """ 258 322 Initialize the object from the database. 259 323 """ 324 _ = tsNow; # Only useful for extended data classes. 260 325 oDb.execute('SELECT TestBoxesWithStrings.*\n' 261 326 'FROM TestBoxesWithStrings\n' … … 444 509 445 510 511 class TestBoxDataEx(TestBoxData): 512 """ 513 TestBox data. 514 """ 515 516 ksParam_aoInSchedGroups = 'TestBox_aoInSchedGroups'; 517 518 # Use [] instead of None. 519 kasAltArrayNull = [ 'aoInSchedGroups', ]; 520 521 ## Helper parameter containing the comma separated list with the IDs of 522 # potential members found in the parameters. 523 ksParam_aidSchedGroups = 'TestBoxDataEx_aidSchedGroups'; 524 525 def __init__(self): 526 TestBoxData.__init__(self); 527 self.aoInSchedGroups = []; # type: list[TestBoxInSchedGroupData] 528 529 def _initExtraMembersFromDb(self, oDb, tsNow = None, sPeriodBack = None): 530 """ 531 Worker shared by the initFromDb* methods. 532 Returns self. Raises exception if no row or database error. 533 """ 534 oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb, 535 'SELECT *\n' 536 'FROM TestBoxesInSchedGroups\n' 537 'WHERE idTestBox = %s\n' 538 , (self.idTestBox,), tsNow, sPeriodBack) 539 + 'ORDER BY idSchedGroup\n' ); 540 self.aoInSchedGroups = []; 541 for aoRow in oDb.fetchAll(): 542 self.aoInSchedGroups.append(TestBoxInSchedGroupDataEx().initFromDbRowEx(aoRow, oDb, tsNow, sPeriodBack)); 543 return self; 544 545 def initFromDbRowEx(self, aoRow, oDb, tsNow = None): 546 """ 547 Reinitialize from a SELECT * FROM TestBoxesWithStrings row. Will query the 548 necessary additional data from oDb using tsNow. 549 Returns self. Raises exception if no row or database error. 550 """ 551 TestBoxData.initFromDbRow(self, aoRow); 552 return self._initExtraMembersFromDb(oDb, tsNow); 553 554 def initFromDbWithId(self, oDb, idTestBox, tsNow = None, sPeriodBack = None): 555 """ 556 Initialize the object from the database. 557 """ 558 TestBoxData.initFromDbWithId(self, oDb, idTestBox, tsNow, sPeriodBack); 559 return self._initExtraMembersFromDb(oDb, tsNow, sPeriodBack); 560 561 def initFromDbWithGenId(self, oDb, idGenTestBox, tsNow = None): 562 """ 563 Initialize the object from the database. 564 """ 565 TestBoxData.initFromDbWithGenId(self, oDb, idGenTestBox); 566 if tsNow is None and not oDb.isTsInfinity(self.tsExpire): 567 tsNow = self.tsEffective; 568 return self._initExtraMembersFromDb(oDb, tsNow); 569 570 def getAttributeParamNullValues(self, sAttr): # Necessary? 571 if sAttr in ['aoInSchedGroups', ]: 572 return [[], '']; 573 return TestBoxData.getAttributeParamNullValues(self, sAttr); 574 575 def convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict): 576 """ 577 For dealing with the in-scheduling-group list. 578 """ 579 if sAttr != 'aoInSchedGroups': 580 return TestBoxData.convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict); 581 582 aoNewValues = []; 583 aidSelected = oDisp.getListOfIntParams(sParam, iMin = 1, iMax = 0x7ffffffe, aiDefaults = []); 584 asIds = oDisp.getStringParam(self.ksParam_aidSchedGroups, sDefault = '').split(','); 585 for idSchedGroup in asIds: 586 try: idSchedGroup = int(idSchedGroup); 587 except: pass; 588 oDispWrapper = self.DispWrapper(oDisp, '%s[%s][%%s]' % (TestBoxDataEx.ksParam_aoInSchedGroups, idSchedGroup,)) 589 oMember = TestBoxInSchedGroupData().initFromParams(oDispWrapper, fStrict = False); 590 if idSchedGroup in aidSelected: 591 aoNewValues.append(oMember); 592 return aoNewValues; 593 594 def _validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb): # pylint: disable=R0914 595 """ 596 Validate special arrays and requirement expressions. 597 598 Some special needs for the in-scheduling-group list. 599 """ 600 if sAttr != 'aoInSchedGroups': 601 return TestBoxData._validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb); 602 603 asErrors = []; 604 aoNewValues = []; 605 606 # Note! We'll be returning an error dictionary instead of an string here. 607 dErrors = {}; 608 609 for iInGrp, oInSchedGroup in enumerate(self.aoInSchedGroups): 610 oInSchedGroup = copy.copy(oInSchedGroup); 611 oInSchedGroup.idTestBox = self.idTestBox; 612 dCurErrors = oInSchedGroup.validateAndConvert(oDb, ModelDataBase.ksValidateFor_Other); 613 if len(dCurErrors) == 0: 614 pass; ## @todo figure out the ID? 615 else: 616 asErrors = []; 617 for sKey in dCurErrors: 618 asErrors.append('%s: %s' % (sKey[len('TestBoxInSchedGroup_'):], dCurErrors[sKey])); 619 dErrors[iInGrp] = '<br>\n'.join(asErrors) 620 aoNewValues.append(oInSchedGroup); 621 622 for iInGrp, oInSchedGroup in enumerate(self.aoInSchedGroups): 623 for iInGrp2 in xrange(iInGrp + 1, len(self.aoInSchedGroups)): 624 if self.aoInSchedGroups[iInGrp2].idSchedGroup == oInSchedGroup.idSchedGroup: 625 sMsg = 'Duplicate scheduling group #%s".' % (oInSchedGroup.idSchedGroup,); 626 if iInGrp in dErrors: dErrors[iInGrp] += '<br>\n' + sMsg; 627 else: dErrors[iInGrp] = sMsg; 628 if iInGrp2 in dErrors: dErrors[iInGrp2] += '<br>\n' + sMsg; 629 else: dErrors[iInGrp2] = sMsg; 630 break; 631 632 return (aoNewValues, dErrors if len(dErrors) > 0 else None); 633 446 634 447 635 class TestBoxLogic(ModelLogicBase): … … 484 672 Raises exception on error. 485 673 """ 486 class TestBoxDataForListing(TestBoxData ):674 class TestBoxDataForListing(TestBoxDataEx): 487 675 """ We add two members for the listing. """ 488 676 def __init__(self): 489 TestBoxData .__init__(self);677 TestBoxDataEx.__init__(self); 490 678 self.tsCurrent = None; # CURRENT_TIMESTAMP 491 self.oStatus = None; # TestBoxStatusData679 self.oStatus = None; # type: TestBoxStatusData 492 680 493 681 from testmanager.core.testboxstatus import TestBoxStatusData; … … 517 705 aoRows = []; 518 706 for aoOne in self._oDb.fetchAll(): 519 oTestBox = TestBoxDataForListing().initFromDbRow (aoOne);707 oTestBox = TestBoxDataForListing().initFromDbRowEx(aoOne, self._oDb, tsNow); 520 708 oTestBox.tsCurrent = self._oDb.getCurrentTimestamp(); 521 709 if aoOne[TestBoxData.kcDbColumns] is not None: … … 532 720 Raises exception on error. 533 721 """ 722 723 ## @todo calc changes to scheduler group! 534 724 535 725 if tsNow is None: … … 570 760 return (aoEntries, len(aoRows) > cMaxRows); 571 761 762 def _validateAndConvertData(self, oData, enmValidateFor): 763 # type: (TestBoxDataEx, str) -> None 764 """ 765 Helper for addEntry and editEntry that validates the scheduling group IDs in 766 addtion to what's covered by the default validateAndConvert of the data object. 767 768 Raises exception on invalid input. 769 """ 770 dDataErrors = oData.validateAndConvert(self._oDb, enmValidateFor); 771 if len(dDataErrors) > 0: 772 raise TMInvalidData('TestBoxLogic.addEntry: %s' % (dDataErrors,)); 773 if len(oData.aoInSchedGroups): 774 sSchedGrps = ', '.join('(%s)' % oCur.idSchedGroup for oCur in oData.aoInSchedGroups); 775 self._oDb.execute('SELECT SchedGroupIDs.idSchedGroup\n' 776 'FROM (VALUES ' + sSchedGrps + ' ) AS SchedGroupIDs(idSchedGroup)\n' 777 ' LEFT OUTER JOIN SchedGroups\n' 778 ' ON SchedGroupIDs.idSchedGroup = SchedGroups.idSchedGroup\n' 779 ' AND SchedGroups.tsExpire = \'infinity\'::TIMESTAMP\n' 780 'WHERE SchedGroups.idSchedGroup IS NULL\n'); 781 aaoRows = self._oDb.fetchAll(); 782 if len(aaoRows) > 0: 783 raise TMInvalidData('TestBoxLogic.addEntry missing scheduling groups: %s' 784 % (', '.join(str(aoRow[0]) for aoRow in aaoRows),)); 785 return None; 572 786 573 787 def addEntry(self, oData, uidAuthor, fCommit = False): 788 # type: (TestBoxDataEx, int, bool) -> (int, int, datetime.datetime) 574 789 """ 575 790 Creates a testbox in the database. … … 577 792 of the created testbox on success. Throws error on failure. 578 793 """ 579 dDataErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Add); 580 if len(dDataErrors) > 0: 581 raise TMInvalidData('Invalid data passed to create(): %s' % (dDataErrors,)); 582 794 795 # 796 # Validate. Extra work because of missing foreign key (due to history). 797 # 798 self._validateAndConvertData(oData, oData.ksValidateFor_Add); 799 800 # 801 # Do it. 802 # 583 803 self._oDb.callProc('TestBoxLogic_addEntry' 584 804 , ( uidAuthor, … … 587 807 oData.sName, 588 808 oData.sDescription, 589 oData.idSchedGroup,590 809 oData.fEnabled, 591 810 oData.enmLomKind, … … 594 813 oData.sComment, 595 814 oData.enmPendingCmd, ) ); 596 aoRow = self._oDb.fetchOne(); 815 (idTestBox, idGenTestBox, tsEffective) = self._oDb.fetchOne(); 816 817 for oInSchedGrp in oData.aoInSchedGroups: 818 self._oDb.callProc('TestBoxLogic_addGroupEntry', 819 ( uidAuthor, idTestBox, oInSchedGrp.idSchedGroup, oInSchedGrp.iSchedPriority,) ); 820 597 821 self._oDb.maybeCommit(fCommit); 598 return ( aoRow[0], aoRow[1], aoRow[2]);822 return (idTestBox, idGenTestBox, tsEffective); 599 823 600 824 … … 605 829 """ 606 830 607 dDataErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Edit); 608 if len(dDataErrors) > 0: 609 raise TMInvalidData('Invalid data passed to create(): %s' % (dDataErrors,)); 610 611 self._oDb.callProc('TestBoxLogic_editEntry' 612 , ( uidAuthor, 613 oData.idTestBox, 614 oData.ip, # Should we allow setting the IP? 615 oData.uuidSystem, 616 oData.sName, 617 oData.sDescription, 618 oData.idSchedGroup, 619 oData.fEnabled, 620 oData.enmLomKind, 621 oData.ipLom, 622 oData.pctScaleTimeout, 623 oData.sComment, 624 oData.enmPendingCmd, )); 625 aoRow = self._oDb.fetchOne(); 831 # 832 # Validate. 833 # 834 self._validateAndConvertData(oData, oData.ksValidateFor_Edit); 835 836 # 837 # Get current data. 838 # 839 oOldData = TestBoxDataEx().initFromDbWithId(self._oDb, oData.idTestBox); 840 841 # 842 # Do it. 843 # 844 if not oData.isEqualEx(oOldData, [ 'tsEffective', 'tsExpire', 'uidAuthor', 'aoInSchedGroups', ] 845 + TestBoxData.kasMachineSettableOnly ): 846 self._oDb.callProc('TestBoxLogic_editEntry' 847 , ( uidAuthor, 848 oData.idTestBox, 849 oData.ip, # Should we allow setting the IP? 850 oData.uuidSystem, 851 oData.sName, 852 oData.sDescription, 853 oData.fEnabled, 854 oData.enmLomKind, 855 oData.ipLom, 856 oData.pctScaleTimeout, 857 oData.sComment, 858 oData.enmPendingCmd, )); 859 (idGenTestBox, tsEffective) = self._oDb.fetchOne(); 860 else: 861 idGenTestBox = oOldData.idGenTestBox; 862 tsEffective = oOldData.tsEffective; 863 864 # Calc in-group changes. 865 aoRemoved = list(oOldData.aoInSchedGroups); 866 aoNew = []; 867 aoUpdated = []; 868 for oNewInGroup in oData.aoInSchedGroups: 869 oOldInGroup = None; 870 for iCur, oCur in enumerate(aoRemoved): 871 if oCur.idSchedGroup == oNewInGroup.idSchedGroup: 872 oOldInGroup = aoRemoved.pop(iCur); 873 break; 874 if oOldInGroup is None: 875 aoNew.append(oNewInGroup); 876 elif oNewInGroup.iSchedPriority != oOldInGroup.iSchedPriority: 877 aoUpdated.append(oNewInGroup); 878 879 # Remove in-groups. 880 for oInGroup in aoRemoved: 881 self._oDb.callProc('TestBoxLogic_removeGroupEntry', (uidAuthor, oData.idTestBox, oInGroup.idSchedGroup, )); 882 883 # Add new ones. 884 for oInGroup in aoNew: 885 self._oDb.callProc('TestBoxLogic_addGroupEntry', 886 ( uidAuthor, oData.idTestBox, oInGroup.idSchedGroup, oInGroup.iSchedPriority, ) ); 887 888 # Edit existing ones. 889 for oInGroup in aoUpdated: 890 self._oDb.callProc('TestBoxLogic_editGroupEntry', 891 ( uidAuthor, oData.idTestBox, oInGroup.idSchedGroup, oInGroup.iSchedPriority, ) ); 892 626 893 self._oDb.maybeCommit(fCommit); 627 return ( aoRow[0], aoRow[1]);894 return (idGenTestBox, tsEffective); 628 895 629 896 630 897 def removeEntry(self, uidAuthor, idTestBox, fCascade = False, fCommit = False): 631 898 """ 632 Delete user account899 Delete test box and scheduling group associations. 633 900 """ 634 901 self._oDb.callProc('TestBoxLogic_removeEntry' … … 698 965 699 966 def cachedLookup(self, idTestBox): 967 # type: (int) -> TestBoxDataEx 700 968 """ 701 969 Looks up the most recent TestBoxData object for idTestBox via 702 970 an object cache. 703 971 704 Returns a shared TestBoxData object. None if not found.972 Returns a shared TestBoxDataEx object. None if not found. 705 973 Raises exception on DB error. 706 974 """ … … 709 977 oEntry = self.dCache.get(idTestBox, None); 710 978 if oEntry is None: 979 fNeedNow = False; 711 980 self._oDb.execute('SELECT TestBoxesWithStrings.*\n' 712 981 'FROM TestBoxesWithStrings\n' … … 722 991 'LIMIT 1\n' 723 992 , (idTestBox, )); 993 fNeedNow = True; 724 994 elif self._oDb.getRowCount() > 1: 725 995 raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), idTestBox)); … … 727 997 if self._oDb.getRowCount() == 1: 728 998 aaoRow = self._oDb.fetchOne(); 729 oEntry = TestBoxData().initFromDbRow(aaoRow); 999 if not fNeedNow: 1000 oEntry = TestBoxDataEx().initFromDbRowEx(aaoRow, self._oDb); 1001 else: 1002 oEntry = TestBoxDataEx().initFromDbRow(aaoRow); 1003 oEntry.initFromDbRowEx(aaoRow, self._oDb, tsNow = db.dbTimestampMinusOneTick(oEntry.tsExpire)); 730 1004 self.dCache[idTestBox] = oEntry; 731 1005 return oEntry; -
trunk/src/VBox/ValidationKit/testmanager/core/testboxcontroller.py
r61474 r61502 571 571 and oTestBoxData.fEnabled \ 572 572 and oStatusData.enmState == TestBoxStatusData.ksTestBoxState_Idle: # (paranoia) 573 dResponse = SchedulerBase.scheduleNewTask(oDb, oTestBoxData, self._oSrvGlue.getBaseUrl());573 dResponse = SchedulerBase.scheduleNewTask(oDb, oTestBoxData, oStatusData.iWorkItem, self._oSrvGlue.getBaseUrl()); 574 574 if dResponse is not None: 575 575 return self._writeResponse(dResponse); -
trunk/src/VBox/ValidationKit/testmanager/core/testboxstatus.py
r61468 r61502 62 62 ksParam_enmState = 'TestBoxStatus_enmState'; 63 63 ksParam_idTestSet = 'TestBoxStatus_idTestSet'; 64 ksParam_iWorkItem = 'TestBoxStatus_iWorkItem'; 64 65 65 66 kasAllowNullAttributes = ['idTestSet', ]; … … 71 72 ksTestBoxState_DoingSpecialCmd, 72 73 ]; 74 75 kcDbColumns = 6; 73 76 74 77 def __init__(self): … … 84 87 self.enmState = self.ksTestBoxState_Idle; 85 88 self.idTestSet = None; 89 self.iWorkItem = None; 86 90 87 91 def initFromDbRow(self, aoRow): … … 99 103 self.enmState = aoRow[3]; 100 104 self.idTestSet = aoRow[4]; 105 self.iWorkItem = aoRow[5]; 101 106 return self; 102 107 … … 161 166 """ 162 167 self._oDb.execute('SELECT TestBoxStatuses.*,\n' 163 ' TestBoxes.*,\n' 164 ' Str1.sValue,\n' 165 ' Str2.sValue,\n' 166 ' Str3.sValue,\n' 167 ' Str4.sValue,\n' 168 ' Str5.sValue,\n' 169 ' Str6.sValue,\n' 170 ' Str7.sValue,\n' 171 ' Str8.sValue\n' 168 ' TestBoxesWithStrings.*,\n' 172 169 'FROM TestBoxStatuses,\n' 173 ' TestBoxes\n' 174 ' LEFT OUTER JOIN TestBoxStrTab Str1 ON idStrDescription = Str1.idStr\n' 175 ' LEFT OUTER JOIN TestBoxStrTab Str2 ON idStrComment = Str2.idStr\n' 176 ' LEFT OUTER JOIN TestBoxStrTab Str3 ON idStrOs = Str3.idStr\n' 177 ' LEFT OUTER JOIN TestBoxStrTab Str4 ON idStrOsVersion = Str4.idStr\n' 178 ' LEFT OUTER JOIN TestBoxStrTab Str5 ON idStrCpuVendor = Str5.idStr\n' 179 ' LEFT OUTER JOIN TestBoxStrTab Str6 ON idStrCpuArch = Str6.idStr\n' 180 ' LEFT OUTER JOIN TestBoxStrTab Str7 ON idStrCpuName = Str7.idStr\n' 181 ' LEFT OUTER JOIN TestBoxStrTab Str8 ON idStrReport = Str8.idStr\n' 182 'WHERE TestBoxStatuses.idTestBox = %s\n' 183 ' AND TestBoxes.idTestBox = %s\n' 184 ' AND TestBoxes.tsExpire = \'infinity\'::TIMESTAMP\n' 185 ' AND TestBoxes.uuidSystem = %s\n' 186 ' AND TestBoxes.ip = %s\n' 170 ' TestBoxesWithStrings\n' 171 'WHERE TestBoxStatuses.idTestBox = %s\n' 172 ' AND TestBoxesWithStrings.idTestBox = %s\n' 173 ' AND TestBoxesWithStrings.tsExpire = \'infinity\'::TIMESTAMP\n' 174 ' AND TestBoxesWithStrings.uuidSystem = %s\n' 175 ' AND TestBoxesWithStrings.ip = %s\n' 187 176 , (idTestBox, 188 177 idTestBox, … … 196 185 return (None, None); 197 186 aoRow = self._oDb.fetchOne(); 198 return (TestBoxStatusData().initFromDbRow(aoRow[0:5]), TestBoxData().initFromDbRow(aoRow[5:])); 187 return (TestBoxStatusData().initFromDbRow(aoRow[:TestBoxStatusData.kcDbColumns]), 188 TestBoxData().initFromDbRow(aoRow[TestBoxStatusData.kcDbColumns:])); 199 189 200 190 … … 207 197 ' idGenTestBox,\n' 208 198 ' enmState,\n' 209 ' idTestSet)\n' 199 ' idTestSet,\n' 200 ' iWorkItem)\n' 210 201 'VALUES ( %s,\n' 211 202 ' %s,\n' 212 203 ' \'idle\'::TestBoxState_T,\n' 213 ' NULL)\n', 204 ' NULL,\n', 205 ' 0)\n', 214 206 (idTestBox, idGenTestBox) ); 215 207 self._oDb.maybeCommit(fCommit); … … 251 243 ' WHERE idTestSetGangLeader = %s)\n' 252 244 , (sNewState, idTestSetGangLeader,) ); 245 self._oDb.maybeCommit(fCommit); 246 return True; 247 248 def updateWorkItem(self, idTestBox, iWorkItem, fCommit = False): 249 """ 250 Updates the testbox state. 251 """ 252 self._oDb.execute('UPDATE TestBoxStatuses\n' 253 'SET iWorkItem = %s\n' 254 'WHERE idTestBox = %s\n' 255 , ( iWorkItem, idTestBox,)); 253 256 self._oDb.maybeCommit(fCommit); 254 257 return True; -
trunk/src/VBox/ValidationKit/testmanager/webui/wuiadmin.py
r61432 r61502 526 526 def _actionTestBoxAdd(self): 527 527 """ Action wrapper. """ 528 from testmanager.core.testbox import TestBoxData ;528 from testmanager.core.testbox import TestBoxDataEx; 529 529 from testmanager.webui.wuiadmintestbox import WuiTestBox; 530 return self._actionGenericFormAdd(TestBoxData , WuiTestBox);530 return self._actionGenericFormAdd(TestBoxDataEx, WuiTestBox); 531 531 532 532 def _actionTestBoxAddPost(self): 533 533 """ Action wrapper. """ 534 from testmanager.core.testbox import TestBoxData , TestBoxLogic;534 from testmanager.core.testbox import TestBoxDataEx, TestBoxLogic; 535 535 from testmanager.webui.wuiadmintestbox import WuiTestBox; 536 return self._actionGenericFormAddPost(TestBoxData , TestBoxLogic, WuiTestBox, self.ksActionTestBoxList);536 return self._actionGenericFormAddPost(TestBoxDataEx, TestBoxLogic, WuiTestBox, self.ksActionTestBoxList); 537 537 538 538 def _actionTestBoxDetails(self): 539 539 """ Action wrapper. """ 540 from testmanager.core.testbox import TestBoxData , TestBoxLogic;540 from testmanager.core.testbox import TestBoxDataEx, TestBoxLogic; 541 541 from testmanager.webui.wuiadmintestbox import WuiTestBox; 542 return self._actionGenericFormDetails(TestBoxData , TestBoxLogic, WuiTestBox, 'idTestBox', 'idGenTestBox');542 return self._actionGenericFormDetails(TestBoxDataEx, TestBoxLogic, WuiTestBox, 'idTestBox', 'idGenTestBox'); 543 543 544 544 def _actionTestBoxEdit(self): 545 545 """ Action wrapper. """ 546 from testmanager.core.testbox import TestBoxData ;546 from testmanager.core.testbox import TestBoxDataEx; 547 547 from testmanager.webui.wuiadmintestbox import WuiTestBox; 548 return self._actionGenericFormEdit(TestBoxData , WuiTestBox, TestBoxData.ksParam_idTestBox);548 return self._actionGenericFormEdit(TestBoxDataEx, WuiTestBox, TestBoxDataEx.ksParam_idTestBox); 549 549 550 550 def _actionTestBoxEditPost(self): 551 551 """ Action wrapper. """ 552 from testmanager.core.testbox import TestBoxData , TestBoxLogic;552 from testmanager.core.testbox import TestBoxDataEx, TestBoxLogic; 553 553 from testmanager.webui.wuiadmintestbox import WuiTestBox; 554 return self._actionGenericFormEditPost(TestBoxData , TestBoxLogic,WuiTestBox, self.ksActionTestBoxList);554 return self._actionGenericFormEditPost(TestBoxDataEx, TestBoxLogic,WuiTestBox, self.ksActionTestBoxList); 555 555 556 556 def _actionTestBoxRemovePost(self): -
trunk/src/VBox/ValidationKit/testmanager/webui/wuiadmintestbox.py
r61472 r61502 39 39 from testmanager.core.db import TMDatabaseConnection; 40 40 from testmanager.core.schedgroup import SchedGroupLogic, SchedGroupData; 41 from testmanager.core.testbox import TestBoxData ;41 from testmanager.core.testbox import TestBoxData, TestBoxDataEx; 42 42 from testmanager.core.testset import TestSetData; 43 43 from testmanager.core.db import isDbTimestampInfinity; … … 102 102 oForm.addText( TestBoxData.ksParam_sName, oData.sName, 'TestBox Name'); 103 103 oForm.addText( TestBoxData.ksParam_sDescription, oData.sDescription, 'TestBox Description'); 104 oForm.addComboBox( TestBoxData.ksParam_idSchedGroup, oData.idSchedGroup, 'Scheduling Group',105 SchedGroupLogic(TMDatabaseConnection()).getSchedGroupsForCombo());106 104 oForm.addCheckBox( TestBoxData.ksParam_fEnabled, oData.fEnabled, 'Enabled'); 107 105 oForm.addComboBox( TestBoxData.ksParam_enmLomKind, oData.enmLomKind, 'Lights-out-management', … … 110 108 oForm.addInt( TestBoxData.ksParam_pctScaleTimeout, oData.pctScaleTimeout, 'Timeout scale factor (%)'); 111 109 112 ## @todo Pretty format the read-only fields and use hidden fields for 113 # passing the actual values. (Yes, we need the values so we can 114 # display the form correctly on input error.) 110 oForm.addListOfSchedGroupsForTestBox(TestBoxDataEx.ksParam_aoInSchedGroups, 111 oData.aoInSchedGroups, 112 SchedGroupLogic(TMDatabaseConnection()).fetchOrderedByName(), 113 'Scheduling Group'); 114 # Command, comment and submit button. 115 if self._sMode == WuiFormContentBase.ksMode_Edit: 116 oForm.addComboBox(TestBoxData.ksParam_enmPendingCmd, oData.enmPendingCmd, 'Pending command', 117 TestBoxData.kaoTestBoxCmdDescs); 118 else: 119 oForm.addComboBoxRO(TestBoxData.ksParam_enmPendingCmd, oData.enmPendingCmd, 'Pending command', 120 TestBoxData.kaoTestBoxCmdDescs); 121 oForm.addMultilineText(TestBoxData.ksParam_sComment, oData.sComment, 'Comment'); 122 if self._sMode != WuiFormContentBase.ksMode_Show: 123 oForm.addSubmit('Create TestBox' if self._sMode == WuiFormContentBase.ksMode_Add else 'Change TestBox'); 124 125 return True; 126 127 128 def _generatePostFormContent(self, oData): 129 from testmanager.webui.wuihlpform import WuiHlpForm; 130 131 oForm = WuiHlpForm('testbox-machine-settable', '', fReadOnly = True); 115 132 oForm.addTextRO( TestBoxData.ksParam_sOs, oData.sOs, 'TestBox OS'); 116 133 oForm.addTextRO( TestBoxData.ksParam_sOsVersion, oData.sOsVersion, 'TestBox OS version'); … … 138 155 oForm.addIntRO( TestBoxData.ksParam_iPythonHexVersion, oData.iPythonHexVersion, 139 156 'Python version (hex)', sPostHtml = webutils.escapeElem(sHexVer)); 140 141 if self._sMode == WuiFormContentBase.ksMode_Edit: 142 oForm.addComboBox(TestBoxData.ksParam_enmPendingCmd, oData.enmPendingCmd, 'Pending command', 143 TestBoxData.kaoTestBoxCmdDescs); 144 else: 145 oForm.addComboBoxRO(TestBoxData.ksParam_enmPendingCmd, oData.enmPendingCmd, 'Pending command', 146 TestBoxData.kaoTestBoxCmdDescs); 147 148 oForm.addMultilineText(TestBoxData.ksParam_sComment, oData.sComment, 'Comment'); 149 150 if self._sMode != WuiFormContentBase.ksMode_Show: 151 oForm.addSubmit('Create TestBox' if self._sMode == WuiFormContentBase.ksMode_Add else 'Change TestBox'); 152 153 return True; 157 return [('Machine Only Settables', oForm.finalize()),]; 158 154 159 155 160 … … 173 178 174 179 def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp): 180 # type: (list[TestBoxDataForListing], int, int, datetime.datetime, ignore, WuiAdmin) -> None 175 181 WuiListContentWithActionBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, 176 182 sTitle = 'TestBoxes', sId = 'users', fnDPrint = fnDPrint, oDisp = oDisp); … … 237 243 oComment = self._formatCommentCell(oEntry.sComment); 238 244 239 # Group link. 240 oGroup = self._dSchedGroups.get(oEntry.idSchedGroup); 241 oGroupLink = WuiTmLink(oGroup.sName if oGroup is not None else str(oEntry.idSchedGroup), 242 WuiAdmin.ksScriptName, 243 { WuiAdmin.ksParamAction: WuiAdmin.ksActionSchedGroupEdit, 244 SchedGroupData.ksParam_idSchedGroup: oEntry.idSchedGroup, }, 245 sTitle = '#%u' % (oEntry.idSchedGroup,), 246 fBracketed = False); 245 # Group links. 246 aoGroups = []; 247 for oInGroup in oEntry.aoInSchedGroups: 248 oSchedGroup = oInGroup.oSchedGroup; 249 aoGroups.append(WuiTmLink(oSchedGroup.sName, WuiAdmin.ksScriptName, 250 { WuiAdmin.ksParamAction: WuiAdmin.ksActionSchedGroupEdit, 251 SchedGroupData.ksParam_idSchedGroup: oSchedGroup.idSchedGroup, }, 252 sTitle = '#%u' % (oSchedGroup.idSchedGroup,), 253 fBracketed = len(oEntry.aoInSchedGroups) > 1)); 247 254 248 255 # Reformat the OS version to take less space. … … 338 345 WuiSvnLink(oEntry.iTestBoxScriptRev), 339 346 oEntry.formatPythonVersion(), 340 oGroupLink,347 aoGroups, 341 348 aoOs, 342 349 oCpu, -
trunk/src/VBox/ValidationKit/testmanager/webui/wuibase.py
r61439 r61502 316 316 # 317 317 318 def getStringParam(self, sName, asValidValues = None, sDefault = None ):318 def getStringParam(self, sName, asValidValues = None, sDefault = None, fAllowNull = False): 319 319 """ 320 320 Gets a string parameter. … … 328 328 raise WuiException('%s parameter "%s" is given multiple times: "%s"' % (self._sAction, sName, sValue)); 329 329 sValue = sValue.strip(); 330 elif sDefault is None :330 elif sDefault is None and fAllowNull is not True: 331 331 raise WuiException('%s is missing parameters: "%s"' % (self._sAction, sName,)); 332 332 else: -
trunk/src/VBox/ValidationKit/testmanager/webui/wuihlpform.py
r61468 r61502 763 763 For WuiAdminSchedGroup. 764 764 """ 765 if self._fReadOnly:766 fReadOnly = True;765 if fReadOnly is None or self._fReadOnly: 766 fReadOnly = self._fReadOnly; 767 767 assert len(aoSchedGroupMembers) <= len(aoAllTestGroups); 768 768 self._addLabel(sName, sLabel); … … 857 857 u'</table>\n'); 858 858 859 def addListOfSchedGroupsForTestBox(self, sName, aoInSchedGroups, aoAllSchedGroups, sLabel, # pylint: disable=R0914 860 fReadOnly = None): 861 # type: (str, TestBoxInSchedGroupDataEx, SchedGroupData, str, bool) -> str 862 """ 863 For WuiTestGroup. 864 """ 865 from testmanager.core.testbox import TestBoxInSchedGroupData, TestBoxDataEx; 866 867 if fReadOnly is None or self._fReadOnly: 868 fReadOnly = self._fReadOnly; 869 assert len(aoInSchedGroups) <= len(aoAllSchedGroups); 870 871 # Only show selected groups in read-only mode. 872 if fReadOnly: 873 aoAllSchedGroups = [oCur.oSchedGroup for oCur in aoInSchedGroups] 874 875 self._addLabel(sName, sLabel); 876 if len(aoAllSchedGroups) == 0: 877 return self._add('<li>No scheduling groups available.</li>\n') 878 879 # Add special parameter with all the scheduling group IDs in the form. 880 self._add(u'<input name="%s" type="hidden" value="%s">\n' 881 % ( TestBoxDataEx.ksParam_aidSchedGroups, 882 ','.join([unicode(oSchedGroup.idSchedGroup) for oSchedGroup in aoAllSchedGroups]), )); 883 884 # Table header. 885 self._add(u'<table class="tmformtbl">\n' 886 u' <thead>\n' 887 u' <tr>\n' 888 u' <th rowspan="2"></th>\n' 889 u' <th rowspan="2">Schedulding Grooup</th>\n' 890 u' <th rowspan="2">Priority [0..31]</th>\n' 891 u' </tr>\n' 892 u' </thead>\n' 893 u' <tbody>\n' 894 ); 895 896 # Table body. 897 if self._fReadOnly: 898 fReadOnly = True; 899 sCheckBoxAttr = ' readonly onclick="return false" onkeydown="return false"' if fReadOnly else ''; 900 901 oDefMember = TestBoxInSchedGroupData(); 902 aoInSchedGroups = list(aoInSchedGroups); # Copy it so we can pop. 903 for iSchedGroup, oSchedGroup in enumerate(aoAllSchedGroups): 904 905 # Is it a member? 906 oMember = None; 907 for i, _ in enumerate(aoInSchedGroups): 908 if aoInSchedGroups[i].idSchedGroup == oSchedGroup.idSchedGroup: 909 oMember = aoInSchedGroups.pop(i); 910 break; 911 912 # Start on the rows... 913 sPrefix = u'%s[%d]' % (sName, oSchedGroup.idSchedGroup,); 914 self._add(u' <tr class="%s">\n' 915 u' <td>\n' 916 u' <input name="%s[%s]" type="hidden" value="%s">\n' # idSchedGroup 917 u' <input name="%s[%s]" type="hidden" value="%s">\n' # idTestBox 918 u' <input name="%s[%s]" type="hidden" value="%s">\n' # tsExpire 919 u' <input name="%s[%s]" type="hidden" value="%s">\n' # tsEffective 920 u' <input name="%s[%s]" type="hidden" value="%s">\n' # uidAuthor 921 u' <input name="%s" type="checkbox"%s%s value="%d" class="tmform-checkbox" title="#%d - %s">\n' #(list) 922 u' </td>\n' 923 % ( 'tmodd' if iSchedGroup & 1 else 'tmeven', 924 sPrefix, TestBoxInSchedGroupData.ksParam_idSchedGroup, oSchedGroup.idSchedGroup, 925 sPrefix, TestBoxInSchedGroupData.ksParam_idTestBox, -1 if oMember is None else oMember.idTestBox, 926 sPrefix, TestBoxInSchedGroupData.ksParam_tsExpire, '' if oMember is None else oMember.tsExpire, 927 sPrefix, TestBoxInSchedGroupData.ksParam_tsEffective, '' if oMember is None else oMember.tsEffective, 928 sPrefix, TestBoxInSchedGroupData.ksParam_uidAuthor, '' if oMember is None else oMember.uidAuthor, 929 TestBoxDataEx.ksParam_aoInSchedGroups, '' if oMember is None else ' checked', sCheckBoxAttr, 930 oSchedGroup.idSchedGroup, oSchedGroup.idSchedGroup, escapeElem(oSchedGroup.sName), 931 )); 932 self._add(u' <td align="left">%s</td>\n' % ( escapeElem(oSchedGroup.sName), )); 933 934 self._add(u' <td align="center">\n' 935 u' <input name="%s[%s]" type="text" value="%s" style="max-width:3em;" %s>\n' 936 u' </td>\n' 937 % ( sPrefix, TestBoxInSchedGroupData.ksParam_iSchedPriority, 938 (oMember if oMember is not None else oDefMember).iSchedPriority, 939 ' readonly class="tmform-input-readonly"' if fReadOnly else '', )); 940 self._add(u' </tr>\n'); 941 942 return self._add(u' </tbody>\n' 943 u'</table>\n'); 944 945 859 946 # 860 947 # Buttons.
Note:
See TracChangeset
for help on using the changeset viewer.