VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testmanager/core/schedgroup.py@ 78351

Last change on this file since 78351 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.5 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: schedgroup.py 76553 2019-01-01 01:45:53Z vboxsync $
3
4"""
5Test Manager - Scheduling Group.
6"""
7
8__copyright__ = \
9"""
10Copyright (C) 2012-2019 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: 76553 $"
30
31
32# Standard python imports.
33import unittest;
34
35# Validation Kit imports.
36from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMExceptionBase, \
37 TMRowInUse, TMInvalidData, TMRowAlreadyExists, TMRowNotFound;
38from testmanager.core.buildsource import BuildSourceData;
39from testmanager.core.testcase import TestCaseData;
40from testmanager.core.testcaseargs import TestCaseArgsData;
41from testmanager.core.testbox import TestBoxData, TestBoxLogic;
42from testmanager.core.testgroup import TestGroupData;
43
44
45
46class SchedGroupMemberData(ModelDataBase):
47 """
48 SchedGroupMember Data.
49 """
50
51 ksIdAttr = 'idSchedGroup';
52
53 ksParam_idSchedGroup = 'SchedGroupMember_idSchedGroup';
54 ksParam_idTestGroup = 'SchedGroupMember_idTestGroup';
55 ksParam_tsEffective = 'SchedGroupMember_tsEffective';
56 ksParam_tsExpire = 'SchedGroupMember_tsExpire';
57 ksParam_uidAuthor = 'SchedGroupMember_uidAuthor';
58 ksParam_iSchedPriority = 'SchedGroupMember_iSchedPriority';
59 ksParam_bmHourlySchedule = 'SchedGroupMember_bmHourlySchedule';
60 ksParam_idTestGroupPreReq = 'SchedGroupMember_idTestGroupPreReq';
61
62 kasAllowNullAttributes = [ 'idSchedGroup', 'idTestGroup', 'tsEffective', 'tsExpire',
63 'uidAuthor', 'bmHourlySchedule', 'idTestGroupPreReq' ];
64 kiMin_iSchedPriority = 0;
65 kiMax_iSchedPriority = 32;
66
67 kcDbColumns = 8
68
69 def __init__(self):
70 ModelDataBase.__init__(self);
71
72 #
73 # Initialize with defaults.
74 # See the database for explanations of each of these fields.
75 #
76 self.idSchedGroup = None;
77 self.idTestGroup = None;
78 self.tsEffective = None;
79 self.tsExpire = None;
80 self.uidAuthor = None;
81 self.iSchedPriority = 16;
82 self.bmHourlySchedule = None;
83 self.idTestGroupPreReq = None;
84
85 def initFromDbRow(self, aoRow):
86 """
87 Re-initializes the data with a row from a SELECT * FROM SchedGroupMembers.
88
89 Returns self. Raises exception if the row is None or otherwise invalid.
90 """
91
92 if aoRow is None:
93 raise TMRowNotFound('SchedGroupMember not found.');
94
95 self.idSchedGroup = aoRow[0];
96 self.idTestGroup = aoRow[1];
97 self.tsEffective = aoRow[2];
98 self.tsExpire = aoRow[3];
99 self.uidAuthor = aoRow[4];
100 self.iSchedPriority = aoRow[5];
101 self.bmHourlySchedule = aoRow[6]; ## @todo figure out how bitmaps are returned...
102 self.idTestGroupPreReq = aoRow[7];
103 return self;
104
105
106class SchedGroupMemberDataEx(SchedGroupMemberData):
107 """
108 Extended SchedGroupMember data class.
109 This adds the testgroups.
110 """
111
112 def __init__(self):
113 SchedGroupMemberData.__init__(self);
114 self.oTestGroup = None;
115
116 def initFromDbRow(self, aoRow):
117 """
118 Re-initializes the data with a row from a query like this:
119
120 SELECT SchedGroupMembers.*, TestGroups.*
121 FROM SchedGroupMembers
122 JOIN TestGroups
123 ON (SchedGroupMembers.idTestGroup = TestGroups.idTestGroup);
124
125 Returns self. Raises exception if the row is None or otherwise invalid.
126 """
127 SchedGroupMemberData.initFromDbRow(self, aoRow);
128 self.oTestGroup = TestGroupData().initFromDbRow(aoRow[SchedGroupMemberData.kcDbColumns:]);
129 return self;
130
131 def getDataAttributes(self):
132 asAttributes = SchedGroupMemberData.getDataAttributes(self);
133 asAttributes.remove('oTestGroup');
134 return asAttributes;
135
136 def _validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor = ModelDataBase.ksValidateFor_Other):
137 dErrors = SchedGroupMemberData._validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor);
138 if self.ksParam_idTestGroup not in dErrors:
139 self.oTestGroup = TestGroupData();
140 try:
141 self.oTestGroup.initFromDbWithId(oDb, self.idTestGroup);
142 except Exception as oXcpt:
143 self.oTestGroup = TestGroupData()
144 dErrors[self.ksParam_idTestGroup] = str(oXcpt);
145 return dErrors;
146
147
148
149class SchedGroupData(ModelDataBase):
150 """
151 SchedGroup Data.
152 """
153
154 ## @name TestBoxState_T
155 # @{
156 ksScheduler_BestEffortContinuousIntegration = 'bestEffortContinousItegration'; # sic*2
157 ksScheduler_Reserved = 'reserved';
158 ## @}
159
160
161 ksIdAttr = 'idSchedGroup';
162
163 ksParam_idSchedGroup = 'SchedGroup_idSchedGroup';
164 ksParam_tsEffective = 'SchedGroup_tsEffective';
165 ksParam_tsExpire = 'SchedGroup_tsExpire';
166 ksParam_uidAuthor = 'SchedGroup_uidAuthor';
167 ksParam_sName = 'SchedGroup_sName';
168 ksParam_sDescription = 'SchedGroup_sDescription';
169 ksParam_fEnabled = 'SchedGroup_fEnabled';
170 ksParam_enmScheduler = 'SchedGroup_enmScheduler';
171 ksParam_idBuildSrc = 'SchedGroup_idBuildSrc';
172 ksParam_idBuildSrcTestSuite = 'SchedGroup_idBuildSrcTestSuite';
173 ksParam_sComment = 'SchedGroup_sComment';
174
175 kasAllowNullAttributes = ['idSchedGroup', 'tsEffective', 'tsExpire', 'uidAuthor', 'sDescription',
176 'idBuildSrc', 'idBuildSrcTestSuite', 'sComment' ];
177 kasValidValues_enmScheduler = [ ksScheduler_BestEffortContinuousIntegration, ];
178
179 kcDbColumns = 11;
180
181 # Scheduler types
182 kasSchedulerDesc = \
183 [
184 ( ksScheduler_BestEffortContinuousIntegration, 'Best-Effort-Continuous-Integration (BECI) scheduler.', ''),
185 ]
186
187 def __init__(self):
188 ModelDataBase.__init__(self);
189
190 #
191 # Initialize with defaults.
192 # See the database for explanations of each of these fields.
193 #
194 self.idSchedGroup = None;
195 self.tsEffective = None;
196 self.tsExpire = None;
197 self.uidAuthor = None;
198 self.sName = None;
199 self.sDescription = None;
200 self.fEnabled = None;
201 self.enmScheduler = SchedGroupData.ksScheduler_BestEffortContinuousIntegration;
202 self.idBuildSrc = None;
203 self.idBuildSrcTestSuite = None;
204 self.sComment = None;
205
206 def initFromDbRow(self, aoRow):
207 """
208 Re-initializes the data with a row from a SELECT * FROM SchedGroups.
209
210 Returns self. Raises exception if the row is None or otherwise invalid.
211 """
212
213 if aoRow is None:
214 raise TMRowNotFound('SchedGroup not found.');
215
216 self.idSchedGroup = aoRow[0];
217 self.tsEffective = aoRow[1];
218 self.tsExpire = aoRow[2];
219 self.uidAuthor = aoRow[3];
220 self.sName = aoRow[4];
221 self.sDescription = aoRow[5];
222 self.fEnabled = aoRow[6];
223 self.enmScheduler = aoRow[7];
224 self.idBuildSrc = aoRow[8];
225 self.idBuildSrcTestSuite = aoRow[9];
226 self.sComment = aoRow[10];
227 return self;
228
229 def initFromDbWithId(self, oDb, idSchedGroup, tsNow = None, sPeriodBack = None):
230 """
231 Initialize the object from the database.
232 """
233 oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
234 'SELECT *\n'
235 'FROM SchedGroups\n'
236 'WHERE idSchedGroup = %s\n'
237 , ( idSchedGroup,), tsNow, sPeriodBack));
238 aoRow = oDb.fetchOne()
239 if aoRow is None:
240 raise TMRowNotFound('idSchedGroup=%s not found (tsNow=%s, sPeriodBack=%s)' % (idSchedGroup, tsNow, sPeriodBack));
241 return self.initFromDbRow(aoRow);
242
243
244class SchedGroupDataEx(SchedGroupData):
245 """
246 Extended scheduling group data.
247
248 Note! Similar to TestGroupDataEx.
249 """
250
251 ksParam_aoMembers = 'SchedGroup_aoMembers';
252 kasAltArrayNull = [ 'aoMembers', ];
253
254 ## Helper parameter containing the comma separated list with the IDs of
255 # potential members found in the parameters.
256 ksParam_aidTestGroups = 'TestGroupDataEx_aidTestGroups';
257
258
259 def __init__(self):
260 SchedGroupData.__init__(self);
261 self.aoMembers = []; # type: SchedGroupMemberDataEx
262
263 # Two build sources for convenience sake.
264 self.oBuildSrc = None; # type: TestBoxData
265 self.oBuildSrcValidationKit = None; # type: TestBoxData
266 # List of test boxes that uses this group for convenience.
267 self.aoTestBoxes = None; # type: list[TestBoxData]
268
269 def _initExtraMembersFromDb(self, oDb, tsNow = None, sPeriodBack = None):
270 """
271 Worker shared by the initFromDb* methods.
272 Returns self. Raises exception if no row or database error.
273 """
274 #
275 # It all upfront so the object has some kind of consistency if anything
276 # below raises exceptions.
277 #
278 self.oBuildSrc = None;
279 self.oBuildSrcValidationKit = None;
280 self.aoTestBoxes = [];
281 self.aoMembers = [];
282
283 #
284 # Build source.
285 #
286 if self.idBuildSrc:
287 self.oBuildSrc = BuildSourceData().initFromDbWithId(oDb, self.idBuildSrc, tsNow, sPeriodBack);
288
289 if self.idBuildSrcTestSuite:
290 self.oBuildSrcValidationKit = BuildSourceData().initFromDbWithId(oDb, self.idBuildSrcTestSuite,
291 tsNow, sPeriodBack);
292
293 #
294 # Test Boxes.
295 #
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,));
305 for aoRow in oDb.fetchAll():
306 self.aoTestBoxes.append(TestBoxData().initFromDbRow(aoRow));
307
308 #
309 # Test groups.
310 #
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,));
319 for aoRow in oDb.fetchAll():
320 self.aoMembers.append(SchedGroupMemberDataEx().initFromDbRow(aoRow));
321 return self;
322
323 def initFromDbRowEx(self, aoRow, oDb, tsNow = None):
324 """
325 Reinitialize from a SELECT * FROM SchedGroups row. Will query the
326 necessary additional data from oDb using tsNow.
327 Returns self. Raises exception if no row or database error.
328 """
329 SchedGroupData.initFromDbRow(self, aoRow);
330 return self._initExtraMembersFromDb(oDb, tsNow);
331
332 def initFromDbWithId(self, oDb, idSchedGroup, tsNow = None, sPeriodBack = None):
333 """
334 Initialize the object from the database.
335 """
336 SchedGroupData.initFromDbWithId(self, oDb, idSchedGroup, tsNow, sPeriodBack);
337 return self._initExtraMembersFromDb(oDb, tsNow, sPeriodBack);
338
339 def getDataAttributes(self):
340 asAttributes = SchedGroupData.getDataAttributes(self);
341 asAttributes.remove('oBuildSrc');
342 asAttributes.remove('oBuildSrcValidationKit');
343 asAttributes.remove('aoTestBoxes');
344 return asAttributes;
345
346 def getAttributeParamNullValues(self, sAttr):
347 if sAttr != 'aoMembers':
348 return SchedGroupData.getAttributeParamNullValues(self, sAttr);
349 return ['', [], None];
350
351 def convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict):
352 if sAttr != 'aoMembers':
353 return SchedGroupData.convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict);
354
355 aoNewValue = [];
356 aidSelected = oDisp.getListOfIntParams(sParam, iMin = 1, iMax = 0x7ffffffe, aiDefaults = [])
357 sIds = oDisp.getStringParam(self.ksParam_aidTestGroups, sDefault = '');
358 for idTestGroup in sIds.split(','):
359 try: idTestGroup = int(idTestGroup);
360 except: pass;
361 oDispWrapper = self.DispWrapper(oDisp, '%s[%s][%%s]' % (SchedGroupDataEx.ksParam_aoMembers, idTestGroup,))
362 oMember = SchedGroupMemberDataEx().initFromParams(oDispWrapper, fStrict = False);
363 if idTestGroup in aidSelected:
364 aoNewValue.append(oMember);
365 return aoNewValue;
366
367 def _validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb):
368 if sAttr != 'aoMembers':
369 return SchedGroupData._validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb);
370
371 asErrors = [];
372 aoNewMembers = [];
373 for oOldMember in oValue:
374 oNewMember = SchedGroupMemberDataEx().initFromOther(oOldMember);
375 aoNewMembers.append(oNewMember);
376
377 dErrors = oNewMember.validateAndConvert(oDb, ModelDataBase.ksValidateFor_Other);
378 if dErrors:
379 asErrors.append(str(dErrors));
380
381 if not asErrors:
382 for i, _ in enumerate(aoNewMembers):
383 idTestGroup = aoNewMembers[i];
384 for j in range(i + 1, len(aoNewMembers)):
385 if aoNewMembers[j].idTestGroup == idTestGroup:
386 asErrors.append('Duplicate test group #%d!' % (idTestGroup, ));
387 break;
388
389 return (aoNewMembers, None if not asErrors else '<br>\n'.join(asErrors));
390
391 def _validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor = ModelDataBase.ksValidateFor_Other):
392 dErrors = SchedGroupData._validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor);
393
394 #
395 # Fetch the extended build source bits.
396 #
397 if self.ksParam_idBuildSrc not in dErrors:
398 if self.idBuildSrc in self.getAttributeParamNullValues('idBuildSrc') \
399 or self.idBuildSrc is None:
400 self.oBuildSrc = None;
401 else:
402 try:
403 self.oBuildSrc = BuildSourceData().initFromDbWithId(oDb, self.idBuildSrc);
404 except Exception as oXcpt:
405 self.oBuildSrc = BuildSourceData();
406 dErrors[self.ksParam_idBuildSrc] = str(oXcpt);
407
408 if self.ksParam_idBuildSrcTestSuite not in dErrors:
409 if self.idBuildSrcTestSuite in self.getAttributeParamNullValues('idBuildSrcTestSuite') \
410 or self.idBuildSrcTestSuite is None:
411 self.oBuildSrcValidationKit = None;
412 else:
413 try:
414 self.oBuildSrcValidationKit = BuildSourceData().initFromDbWithId(oDb, self.idBuildSrcTestSuite);
415 except Exception as oXcpt:
416 self.oBuildSrcValidationKit = BuildSourceData();
417 dErrors[self.ksParam_idBuildSrcTestSuite] = str(oXcpt);
418
419 return dErrors;
420
421
422
423class SchedGroupLogic(ModelLogicBase): # pylint: disable=R0903
424 """
425 SchedGroup logic.
426 """
427
428 def __init__(self, oDb):
429 ModelLogicBase.__init__(self, oDb);
430 self.dCache = None;
431
432 #
433 # Standard methods.
434 #
435
436 def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
437 """
438 Fetches build sources.
439
440 Returns an array (list) of BuildSourceData items, empty list if none.
441 Raises exception on error.
442 """
443 _ = aiSortColumns;
444
445 if tsNow is None:
446 self._oDb.execute('SELECT *\n'
447 'FROM SchedGroups\n'
448 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
449 'ORDER BY idSchedGroup DESC\n'
450 'LIMIT %s OFFSET %s\n'
451 , (cMaxRows, iStart,));
452 else:
453 self._oDb.execute('SELECT *\n'
454 'FROM SchedGroups\n'
455 'WHERE tsExpire > %s\n'
456 ' AND tsEffective <= %s\n'
457 'ORDER BY idSchedGroup DESC\n'
458 'LIMIT %s OFFSET %s\n'
459 , (tsNow, tsNow, cMaxRows, iStart,));
460
461 aoRet = [];
462 for aoRow in self._oDb.fetchAll():
463 aoRet.append(SchedGroupDataEx().initFromDbRowEx(aoRow, self._oDb, tsNow));
464 return aoRet;
465
466 def addEntry(self, oData, uidAuthor, fCommit = False):
467 """Add Scheduling Group record"""
468
469 #
470 # Validate.
471 #
472 dDataErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Add);
473 if dDataErrors:
474 raise TMInvalidData('Invalid data passed to addEntry: %s' % (dDataErrors,));
475 if self.exists(oData.sName):
476 raise TMRowAlreadyExists('Scheduling group "%s" already exists.' % (oData.sName,));
477
478 #
479 # Add it.
480 #
481 self._oDb.execute('INSERT INTO SchedGroups (\n'
482 ' uidAuthor,\n'
483 ' sName,\n'
484 ' sDescription,\n'
485 ' fEnabled,\n'
486 ' enmScheduler,\n'
487 ' idBuildSrc,\n'
488 ' idBuildSrcTestSuite,\n'
489 ' sComment)\n'
490 'VALUES (%s, %s, %s, %s, %s, %s, %s, %s)\n'
491 'RETURNING idSchedGroup\n'
492 , ( uidAuthor,
493 oData.sName,
494 oData.sDescription,
495 oData.fEnabled,
496 oData.enmScheduler,
497 oData.idBuildSrc,
498 oData.idBuildSrcTestSuite,
499 oData.sComment ));
500 idSchedGroup = self._oDb.fetchOne()[0];
501 oData.idSchedGroup = idSchedGroup;
502
503 for oMember in oData.aoMembers:
504 oMember.idSchedGroup = idSchedGroup;
505 self._addSchedGroupMember(uidAuthor, oMember);
506
507 self._oDb.maybeCommit(fCommit);
508 return True;
509
510 def editEntry(self, oData, uidAuthor, fCommit = False):
511 """Edit Scheduling Group record"""
512
513 #
514 # Validate input and retrieve the old data.
515 #
516 dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Edit);
517 if dErrors:
518 raise TMInvalidData('editEntry got invalid data: %s' % (dErrors,));
519 self._assertUnique(oData.sName, oData.idSchedGroup);
520 oOldData = SchedGroupDataEx().initFromDbWithId(self._oDb, oData.idSchedGroup);
521
522 #
523 # Make the changes.
524 #
525 if not oData.isEqualEx(oOldData, [ 'tsEffective', 'tsExpire', 'uidAuthor', 'aoMembers', 'aoTestBoxes',
526 'oBuildSrc', 'oBuildSrcValidationKit', ]):
527 self._historizeEntry(oData.idSchedGroup);
528 self._readdEntry(uidAuthor, oData);
529
530 # Remove groups.
531 for oOld in oOldData.aoMembers:
532 fRemove = True;
533 for oNew in oData.aoMembers:
534 if oNew.idTestGroup == oOld.idTestGroup:
535 fRemove = False;
536 break;
537 if fRemove:
538 self._removeSchedGroupMember(uidAuthor, oOld);
539
540 # Add / modify groups.
541 for oMember in oData.aoMembers:
542 oOldMember = None;
543 for oOld in oOldData.aoMembers:
544 if oOld.idTestGroup == oMember.idTestGroup:
545 oOldMember = oOld;
546 break;
547
548 oMember.idSchedGroup = oData.idSchedGroup;
549 if oOldMember is None:
550 self._addSchedGroupMember(uidAuthor, oMember);
551 elif not oMember.isEqualEx(oOldMember, ['tsEffective', 'tsExpire', 'uidAuthor', 'oTestGroup']):
552 self._historizeSchedGroupMember(oMember);
553 self._addSchedGroupMember(uidAuthor, oMember);
554
555 self._oDb.maybeCommit(fCommit);
556 return True;
557
558 def removeEntry(self, uidAuthor, idSchedGroup, fCascade = False, fCommit = False):
559 """
560 Deletes a scheduling group.
561 """
562
563 #
564 # Input validation and retrival of current data.
565 #
566 if idSchedGroup == 1:
567 raise TMRowInUse('Cannot remove the default scheduling group (id 1).');
568 oData = SchedGroupDataEx().initFromDbWithId(self._oDb, idSchedGroup);
569
570 #
571 # We use cascade a little different here... We don't actually delete
572 # associated testboxes or testgroups.
573 #
574 if oData.aoTestBoxes:
575 if fCascade is not True:
576 # Complain about there being associated testboxes.
577 asTestBoxes = ['%s (#%d)' % (oTestBox.sName, oTestBox.idTestBox) for oTestBox in oData.aoTestBoxes];
578 raise TMRowInUse('Scheduling group #%d is associated with one or more test boxes: %s'
579 % (idSchedGroup, ', '.join(asTestBoxes),));
580 else:
581 # Reassign testboxes to scheduling group #1 (the default group).
582 oTbLogic = TestBoxLogic(self._oDb);
583 for oTestBox in oData.aoTestBoxes:
584 oTbCopy = TestBoxData().initFromOther(oTestBox);
585 oTbCopy.idSchedGroup = 1;
586 oTbLogic.editEntry(oTbCopy, uidAuthor, fCommit = False);
587
588 oData = SchedGroupDataEx().initFromDbWithId(self._oDb, idSchedGroup);
589 if oData.aoTestBoxes:
590 raise TMRowInUse('More testboxes was added to the scheduling group as we were trying to delete it.');
591
592 #
593 # Remove the group and all member records.
594 #
595 for oMember in oData.aoMembers:
596 self._removeSchedGroupMember(uidAuthor, oMember);
597 self._oDb.execute('UPDATE SchedGroupMembers\n'
598 'SET tsExpire = CURRENT_TIMESTAMP\n'
599 'WHERE idSchedGroup = %s\n'
600 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
601 , (idSchedGroup,));
602
603 (tsCur, tsCurMinusOne) = self._oDb.getCurrentTimestamps();
604 if oData.tsEffective != tsCur and oData.tsEffective != tsCurMinusOne:
605 self._historizeEntry(idSchedGroup, tsCurMinusOne);
606 self._readdEntry(uidAuthor, oData, tsCurMinusOne);
607 self._historizeEntry(idSchedGroup);
608 self._oDb.execute('UPDATE SchedGroups\n'
609 'SET tsExpire = CURRENT_TIMESTAMP\n'
610 'WHERE idSchedGroup = %s\n'
611 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
612 , (idSchedGroup,))
613
614 self._oDb.maybeCommit(fCommit)
615 return True;
616
617
618 def cachedLookup(self, idSchedGroup):
619 """
620 Looks up the most recent SchedGroupData object for idSchedGroup
621 via an object cache.
622
623 Returns a shared SchedGroupData object. None if not found.
624 Raises exception on DB error.
625 """
626 if self.dCache is None:
627 self.dCache = self._oDb.getCache('SchedGroup');
628
629 oEntry = self.dCache.get(idSchedGroup, None);
630 if oEntry is None:
631 self._oDb.execute('SELECT *\n'
632 'FROM SchedGroups\n'
633 'WHERE idSchedGroup = %s\n'
634 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
635 , (idSchedGroup, ));
636 if self._oDb.getRowCount() == 0:
637 # Maybe it was deleted, try get the last entry.
638 self._oDb.execute('SELECT *\n'
639 'FROM SchedGroups\n'
640 'WHERE idSchedGroup = %s\n'
641 'ORDER BY tsExpire DESC\n'
642 'LIMIT 1\n'
643 , (idSchedGroup, ));
644 elif self._oDb.getRowCount() > 1:
645 raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), idSchedGroup));
646
647 if self._oDb.getRowCount() == 1:
648 oEntry = SchedGroupData().initFromDbRow(self._oDb.fetchOne());
649 self.dCache[idSchedGroup] = oEntry;
650 return oEntry;
651
652
653 #
654 # Other methods.
655 #
656
657 def fetchOrderedByName(self, tsNow = None):
658 """
659 Return list of objects of type SchedGroups ordered by name.
660 May raise exception on database error.
661 """
662 if tsNow is None:
663 self._oDb.execute('SELECT *\n'
664 'FROM SchedGroups\n'
665 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
666 'ORDER BY sName ASC\n');
667 else:
668 self._oDb.execute('SELECT *\n'
669 'FROM SchedGroups\n'
670 'WHERE tsExpire > %s\n'
671 ' AND tsEffective <= %s\n'
672 'ORDER BY sName ASC\n'
673 , (tsNow, tsNow,));
674 aoRet = []
675 for _ in range(self._oDb.getRowCount()):
676 aoRet.append(SchedGroupData().initFromDbRow(self._oDb.fetchOne()));
677 return aoRet;
678
679
680 def getAll(self, tsEffective = None):
681 """
682 Gets the list of all scheduling groups.
683 Returns an array of SchedGroupData instances.
684 """
685 if tsEffective is None:
686 self._oDb.execute('SELECT *\n'
687 'FROM SchedGroups\n'
688 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n');
689 else:
690 self._oDb.execute('SELECT *\n'
691 'FROM SchedGroups\n'
692 'WHERE tsExpire > %s\n'
693 ' AND tsEffective <= %s\n'
694 , (tsEffective, tsEffective));
695 aoRet = [];
696 for aoRow in self._oDb.fetchAll():
697 aoRet.append(SchedGroupData().initFromDbRow(aoRow));
698 return aoRet;
699
700 def getSchedGroupsForCombo(self, tsEffective = None):
701 """
702 Gets the list of active scheduling groups for a combo box.
703 Returns an array of (value [idSchedGroup], drop-down-name [sName],
704 hover-text [sDescription]) tuples.
705 """
706 if tsEffective is None:
707 self._oDb.execute('SELECT idSchedGroup, sName, sDescription\n'
708 'FROM SchedGroups\n'
709 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
710 'ORDER BY sName');
711 else:
712 self._oDb.execute('SELECT idSchedGroup, sName, sDescription\n'
713 'FROM SchedGroups\n'
714 'WHERE tsExpire > %s\n'
715 ' AND tsEffective <= %s\n'
716 'ORDER BY sName'
717 , (tsEffective, tsEffective));
718 return self._oDb.fetchAll();
719
720
721 def getMembers(self, idSchedGroup, tsEffective = None):
722 """
723 Gets the scheduling groups members for the given scheduling group.
724
725 Returns an array of SchedGroupMemberDataEx instances (sorted by
726 priority (descending) and idTestGroup). May raise exception DB error.
727 """
728
729 if tsEffective is None:
730 self._oDb.execute('SELECT *\n'
731 'FROM SchedGroupMembers, TestGroups\n'
732 'WHERE SchedGroupMembers.idSchedGroup = %s\n'
733 ' AND SchedGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
734 ' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
735 ' AND TestGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
736 'ORDER BY SchedGroupMembers.iSchedPriority DESC, SchedGroupMembers.idTestGroup\n'
737 , (idSchedGroup,));
738 else:
739 self._oDb.execute('SELECT *\n'
740 'FROM SchedGroupMembers, TestGroups\n'
741 'WHERE SchedGroupMembers.idSchedGroup = %s\n'
742 ' AND SchedGroupMembers.tsExpire < %s\n'
743 ' AND SchedGroupMembers.tsEffective >= %s\n'
744 ' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
745 ' AND TestGroups.tsExpire < %s\n'
746 ' AND TestGroups.tsEffective >= %s\n'
747 'ORDER BY SchedGroupMembers.iSchedPriority DESC, SchedGroupMembers.idTestGroup\n'
748 , (idSchedGroup, tsEffective, tsEffective, tsEffective, tsEffective, ));
749 aaoRows = self._oDb.fetchAll();
750 aoRet = [];
751 for aoRow in aaoRows:
752 aoRet.append(SchedGroupMemberDataEx().initFromDbRow(aoRow));
753 return aoRet;
754
755 def getTestCasesForGroup(self, idSchedGroup, cMax = None):
756 """
757 Gets the enabled testcases w/ testgroup+priority for the given scheduling group.
758
759 Returns an array of TestCaseData instances (ordered by group id, descending
760 testcase priority, and testcase IDs) with an extra iSchedPriority member.
761 May raise exception on DB error or if the result exceeds cMax.
762 """
763
764 self._oDb.execute('SELECT TestGroupMembers.idTestGroup, TestGroupMembers.iSchedPriority, TestCases.*\n'
765 'FROM SchedGroupMembers, TestGroups, TestGroupMembers, TestCases\n'
766 'WHERE SchedGroupMembers.idSchedGroup = %s\n'
767 ' AND SchedGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
768 ' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
769 ' AND TestGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
770 ' AND TestGroupMembers.idTestGroup = TestGroups.idTestGroup\n'
771 ' AND TestGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
772 ' AND TestCases.idTestCase = TestGroupMembers.idTestCase\n'
773 ' AND TestCases.tsExpire = \'infinity\'::TIMESTAMP\n'
774 ' AND TestCases.fEnabled = TRUE\n'
775 'ORDER BY TestGroupMembers.idTestGroup, TestGroupMembers.iSchedPriority DESC, TestCases.idTestCase\n'
776 , (idSchedGroup,));
777
778 if cMax is not None and self._oDb.getRowCount() > cMax:
779 raise TMExceptionBase('Too many testcases for scheduling group %s: %s, max %s'
780 % (idSchedGroup, cMax, self._oDb.getRowCount(),));
781
782 aoRet = [];
783 for aoRow in self._oDb.fetchAll():
784 oTestCase = TestCaseData().initFromDbRow(aoRow[2:]);
785 oTestCase.idTestGroup = aoRow[0];
786 oTestCase.iSchedPriority = aoRow[1];
787 aoRet.append(oTestCase);
788 return aoRet;
789
790 def getTestCaseArgsForGroup(self, idSchedGroup, cMax = None):
791 """
792 Gets the testcase argument variation w/ testgroup+priority for the given scheduling group.
793
794 Returns an array TestCaseArgsData instance (sorted by group and
795 variation id) with an extra iSchedPriority member.
796 May raise exception on DB error or if the result exceeds cMax.
797 """
798
799 self._oDb.execute('SELECT TestGroupMembers.idTestGroup, TestGroupMembers.iSchedPriority, TestCaseArgs.*\n'
800 'FROM SchedGroupMembers, TestGroups, TestGroupMembers, TestCaseArgs, TestCases\n'
801 'WHERE SchedGroupMembers.idSchedGroup = %s\n'
802 ' AND SchedGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
803 ' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
804 ' AND TestGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
805 ' AND TestGroupMembers.idTestGroup = TestGroups.idTestGroup\n'
806 ' AND TestGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
807 ' AND TestCaseArgs.idTestCase = TestGroupMembers.idTestCase\n'
808 ' AND TestCaseArgs.tsExpire = \'infinity\'::TIMESTAMP\n'
809 ' AND ( TestGroupMembers.aidTestCaseArgs is NULL\n'
810 ' OR TestCaseArgs.idTestCaseArgs = ANY(TestGroupMembers.aidTestCaseArgs) )\n'
811 ' AND TestCases.idTestCase = TestCaseArgs.idTestCase\n'
812 ' AND TestCases.tsExpire = \'infinity\'::TIMESTAMP\n'
813 ' AND TestCases.fEnabled = TRUE\n'
814 'ORDER BY TestGroupMembers.idTestGroup, TestGroupMembers.idTestCase, TestCaseArgs.idTestCaseArgs\n'
815 , (idSchedGroup,));
816
817 if cMax is not None and self._oDb.getRowCount() > cMax:
818 raise TMExceptionBase('Too many argument variations for scheduling group %s: %s, max %s'
819 % (idSchedGroup, cMax, self._oDb.getRowCount(),));
820
821 aoRet = [];
822 for aoRow in self._oDb.fetchAll():
823 oVariation = TestCaseArgsData().initFromDbRow(aoRow[2:]);
824 oVariation.idTestGroup = aoRow[0];
825 oVariation.iSchedPriority = aoRow[1];
826 aoRet.append(oVariation);
827 return aoRet;
828
829 def exists(self, sName):
830 """Checks if a group with the given name exists."""
831 self._oDb.execute('SELECT idSchedGroup\n'
832 'FROM SchedGroups\n'
833 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
834 ' AND sName = %s\n'
835 'LIMIT 1\n'
836 , (sName,));
837 return self._oDb.getRowCount() > 0;
838
839 def getById(self, idSchedGroup):
840 """Get Scheduling Group data by idSchedGroup"""
841 self._oDb.execute('SELECT *\n'
842 'FROM SchedGroups\n'
843 'WHERE tsExpire = \'infinity\'::timestamp\n'
844 ' AND idSchedGroup = %s;', (idSchedGroup,))
845 aRows = self._oDb.fetchAll()
846 if len(aRows) not in (0, 1):
847 raise self._oDb.integrityException(
848 'Found more than one scheduling groups with the same credentials. Database structure is corrupted.')
849 try:
850 return SchedGroupData().initFromDbRow(aRows[0])
851 except IndexError:
852 return None
853
854
855 #
856 # Internal helpers.
857 #
858
859 def _assertUnique(self, sName, idSchedGroupIgnore = None):
860 """
861 Checks that the scheduling group name is unique.
862 Raises exception if the name is already in use.
863 """
864 if idSchedGroupIgnore is None:
865 self._oDb.execute('SELECT idSchedGroup\n'
866 'FROM SchedGroups\n'
867 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
868 ' AND sName = %s\n'
869 , ( sName, ) );
870 else:
871 self._oDb.execute('SELECT idSchedGroup\n'
872 'FROM SchedGroups\n'
873 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
874 ' AND sName = %s\n'
875 ' AND idSchedGroup <> %s\n'
876 , ( sName, idSchedGroupIgnore, ) );
877 if self._oDb.getRowCount() > 0:
878 raise TMRowInUse('Scheduling group name (%s) is already in use.' % (sName,));
879 return True;
880
881 def _readdEntry(self, uidAuthor, oData, tsEffective = None):
882 """
883 Re-adds the SchedGroups entry. Used by editEntry and removeEntry.
884 """
885 if tsEffective is None:
886 tsEffective = self._oDb.getCurrentTimestamp();
887 self._oDb.execute('INSERT INTO SchedGroups (\n'
888 ' uidAuthor,\n'
889 ' tsEffective,\n'
890 ' idSchedGroup,\n'
891 ' sName,\n'
892 ' sDescription,\n'
893 ' fEnabled,\n'
894 ' enmScheduler,\n'
895 ' idBuildSrc,\n'
896 ' idBuildSrcTestSuite,\n'
897 ' sComment )\n'
898 'VALUES ( %s, %s, %s, %s, %s, %s, %s, %s, %s, %s )\n'
899 , ( uidAuthor,
900 tsEffective,
901 oData.idSchedGroup,
902 oData.sName,
903 oData.sDescription,
904 oData.fEnabled,
905 oData.enmScheduler,
906 oData.idBuildSrc,
907 oData.idBuildSrcTestSuite,
908 oData.sComment, ));
909 return True;
910
911 def _historizeEntry(self, idSchedGroup, tsExpire = None):
912 """
913 Historizes the current entry for the given scheduling group.
914 """
915 if tsExpire is None:
916 tsExpire = self._oDb.getCurrentTimestamp();
917 self._oDb.execute('UPDATE SchedGroups\n'
918 'SET tsExpire = %s\n'
919 'WHERE idSchedGroup = %s\n'
920 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
921 , ( tsExpire, idSchedGroup, ));
922 return True;
923
924 def _addSchedGroupMember(self, uidAuthor, oMember, tsEffective = None):
925 """
926 addEntry worker for adding a scheduling group member.
927 """
928 if tsEffective is None:
929 tsEffective = self._oDb.getCurrentTimestamp();
930 self._oDb.execute('INSERT INTO SchedGroupMembers(\n'
931 ' idSchedGroup,\n'
932 ' idTestGroup,\n'
933 ' tsEffective,\n'
934 ' uidAuthor,\n'
935 ' iSchedPriority,\n'
936 ' bmHourlySchedule,\n'
937 ' idTestGroupPreReq)\n'
938 'VALUES (%s, %s, %s, %s, %s, %s, %s)\n'
939 , ( oMember.idSchedGroup,
940 oMember.idTestGroup,
941 tsEffective,
942 uidAuthor,
943 oMember.iSchedPriority,
944 oMember.bmHourlySchedule,
945 oMember.idTestGroupPreReq, ));
946 return True;
947
948 def _removeSchedGroupMember(self, uidAuthor, oMember):
949 """
950 Removes a scheduling group member.
951 """
952
953 # Try record who removed it by adding an dummy entry that expires immediately.
954 (tsCur, tsCurMinusOne) = self._oDb.getCurrentTimestamps();
955 if oMember.tsEffective != tsCur and oMember.tsEffective != tsCurMinusOne:
956 self._historizeSchedGroupMember(oMember, tsCurMinusOne);
957 self._addSchedGroupMember(uidAuthor, oMember, tsCurMinusOne); # lazy bird.
958 self._historizeSchedGroupMember(oMember);
959 else:
960 self._historizeSchedGroupMember(oMember);
961 return True;
962
963 def _historizeSchedGroupMember(self, oMember, tsExpire = None):
964 """
965 Historizes the current entry for the given scheduling group.
966 """
967 if tsExpire is None:
968 tsExpire = self._oDb.getCurrentTimestamp();
969 self._oDb.execute('UPDATE SchedGroupMembers\n'
970 'SET tsExpire = %s\n'
971 'WHERE idSchedGroup = %s\n'
972 ' AND idTestGroup = %s\n'
973 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
974 , ( tsExpire, oMember.idSchedGroup, oMember.idTestGroup, ));
975 return True;
976
977
978
979
980#
981# Unit testing.
982#
983
984# pylint: disable=C0111
985class SchedGroupMemberDataTestCase(ModelDataBaseTestCase):
986 def setUp(self):
987 self.aoSamples = [SchedGroupMemberData(),];
988
989class SchedGroupDataTestCase(ModelDataBaseTestCase):
990 def setUp(self):
991 self.aoSamples = [SchedGroupData(),];
992
993if __name__ == '__main__':
994 unittest.main();
995 # not reached.
996
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