VirtualBox

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

Last change on this file since 61472 was 61472, checked in by vboxsync, 9 years ago

testmanager: adding sComment columns to TestCases, TestGroups and SchedGroups.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.8 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: schedgroup.py 61472 2016-06-05 17:46:15Z vboxsync $
3
4"""
5Test Manager - Scheduling Group.
6"""
7
8__copyright__ = \
9"""
10Copyright (C) 2012-2015 Oracle Corporation
11
12This file is part of VirtualBox Open Source Edition (OSE), as
13available from http://www.215389.xyz. This file is free software;
14you can redistribute it and/or modify it under the terms of the GNU
15General Public License (GPL) as published by the Free Software
16Foundation, in version 2 as it comes in the "COPYING" file of the
17VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19
20The contents of this file may alternatively be used under the terms
21of the Common Development and Distribution License Version 1.0
22(CDDL) only, as it comes in the "COPYING.CDDL" file of the
23VirtualBox OSE distribution, in which case the provisions of the
24CDDL are applicable instead of those of the GPL.
25
26You may elect to license modified versions of this file under the
27terms and conditions of either the GPL or the CDDL or both.
28"""
29__version__ = "$Revision: 61472 $"
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_BestEffortContinousItegration = 'bestEffortContinousItegration';
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_BestEffortContinousItegration, ];
178
179 kcDbColumns = 11;
180
181 # Scheduler types
182 kasSchedulerDesc = \
183 [
184 ( ksScheduler_BestEffortContinousItegration, 'Best-Effort-Continous-Itegration (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_BestEffortContinousItegration;
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 = []; # SchedGroupMemberDataEx.
262
263 # Two build sources for convenience sake.
264 self.oBuildSrc = None;
265 self.oBuildSrcValidationKit = None;
266 # List of test boxes that uses this group for convenience.
267 self.aoTestBoxes = None;
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 ## @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));
312 for aoRow in oDb.fetchAll():
313 self.aoTestBoxes.append(TestBoxData().initFromDbRow(aoRow));
314
315 #
316 # Test groups.
317 #
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
340 for aoRow in oDb.fetchAll():
341 self.aoMembers.append(SchedGroupMemberDataEx().initFromDbRow(aoRow));
342 return self;
343
344 def initFromDbRowEx(self, aoRow, oDb, tsNow = None):
345 """
346 Reinitialize from a SELECT * FROM SchedGroups row. Will query the
347 necessary additional data from oDb using tsNow.
348 Returns self. Raises exception if no row or database error.
349 """
350 SchedGroupData.initFromDbRow(self, aoRow);
351 return self._initExtraMembersFromDb(oDb, tsNow);
352
353 def initFromDbWithId(self, oDb, idSchedGroup, tsNow = None, sPeriodBack = None):
354 """
355 Initialize the object from the database.
356 """
357 SchedGroupData.initFromDbWithId(self, oDb, idSchedGroup, tsNow, sPeriodBack);
358 return self._initExtraMembersFromDb(oDb, tsNow, sPeriodBack);
359
360 def getDataAttributes(self):
361 asAttributes = SchedGroupData.getDataAttributes(self);
362 asAttributes.remove('oBuildSrc');
363 asAttributes.remove('oBuildSrcValidationKit');
364 asAttributes.remove('aoTestBoxes');
365 return asAttributes;
366
367 def getAttributeParamNullValues(self, sAttr):
368 if sAttr != 'aoMembers':
369 return SchedGroupData.getAttributeParamNullValues(self, sAttr);
370 return ['', [], None];
371
372 def convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict):
373 if sAttr != 'aoMembers':
374 return SchedGroupData.convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict);
375
376 aoNewValue = [];
377 aidSelected = oDisp.getListOfIntParams(sParam, iMin = 1, iMax = 0x7ffffffe, aiDefaults = [])
378 sIds = oDisp.getStringParam(self.ksParam_aidTestGroups, sDefault = '');
379 for idTestGroup in sIds.split(','):
380 try: idTestGroup = int(idTestGroup);
381 except: pass;
382 oDispWrapper = self.DispWrapper(oDisp, '%s[%s][%%s]' % (SchedGroupDataEx.ksParam_aoMembers, idTestGroup,))
383 oMember = SchedGroupMemberDataEx().initFromParams(oDispWrapper, fStrict = False);
384 if idTestGroup in aidSelected:
385 aoNewValue.append(oMember);
386 return aoNewValue;
387
388 def _validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb):
389 if sAttr != 'aoMembers':
390 return SchedGroupData._validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb);
391
392 asErrors = [];
393 aoNewMembers = [];
394 for oOldMember in oValue:
395 oNewMember = SchedGroupMemberDataEx().initFromOther(oOldMember);
396 aoNewMembers.append(oNewMember);
397
398 dErrors = oNewMember.validateAndConvert(oDb, ModelDataBase.ksValidateFor_Other);
399 if len(dErrors) > 0:
400 asErrors.append(str(dErrors));
401
402 if len(asErrors) == 0:
403 for i, _ in enumerate(aoNewMembers):
404 idTestGroup = aoNewMembers[i];
405 for j in range(i + 1, len(aoNewMembers)):
406 if aoNewMembers[j].idTestGroup == idTestGroup:
407 asErrors.append('Duplicate test group #%d!' % (idTestGroup, ));
408 break;
409
410 return (aoNewMembers, None if len(asErrors) == 0 else '<br>\n'.join(asErrors));
411
412 def _validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor = ModelDataBase.ksValidateFor_Other):
413 dErrors = SchedGroupData._validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor);
414
415 #
416 # Fetch the extended build source bits.
417 #
418 if self.ksParam_idBuildSrc not in dErrors:
419 if self.idBuildSrc in self.getAttributeParamNullValues('idBuildSrc') \
420 or self.idBuildSrc is None:
421 self.oBuildSrc = None;
422 else:
423 try:
424 self.oBuildSrc = BuildSourceData().initFromDbWithId(oDb, self.idBuildSrc);
425 except Exception as oXcpt:
426 self.oBuildSrc = BuildSourceData();
427 dErrors[self.ksParam_idBuildSrc] = str(oXcpt);
428
429 if self.ksParam_idBuildSrcTestSuite not in dErrors:
430 if self.idBuildSrcTestSuite in self.getAttributeParamNullValues('idBuildSrcTestSuite') \
431 or self.idBuildSrcTestSuite is None:
432 self.oBuildSrcValidationKit = None;
433 else:
434 try:
435 self.oBuildSrcValidationKit = BuildSourceData().initFromDbWithId(oDb, self.idBuildSrcTestSuite);
436 except Exception as oXcpt:
437 self.oBuildSrcValidationKit = BuildSourceData();
438 dErrors[self.ksParam_idBuildSrcTestSuite] = str(oXcpt);
439
440 return dErrors;
441
442
443
444class SchedGroupLogic(ModelLogicBase): # pylint: disable=R0903
445 """
446 SchedGroup logic.
447 """
448
449 #
450 # Standard methods.
451 #
452
453 def fetchForListing(self, iStart, cMaxRows, tsNow):
454 """
455 Fetches build sources.
456
457 Returns an array (list) of BuildSourceData items, empty list if none.
458 Raises exception on error.
459 """
460
461 if tsNow is None:
462 self._oDb.execute('SELECT *\n'
463 'FROM SchedGroups\n'
464 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
465 'ORDER BY idSchedGroup DESC\n'
466 'LIMIT %s OFFSET %s\n'
467 , (cMaxRows, iStart,));
468 else:
469 self._oDb.execute('SELECT *\n'
470 'FROM SchedGroups\n'
471 'WHERE tsExpire > %s\n'
472 ' AND tsEffective <= %s\n'
473 'ORDER BY idSchedGroup DESC\n'
474 'LIMIT %s OFFSET %s\n'
475 , (tsNow, tsNow, cMaxRows, iStart,));
476
477 aoRet = [];
478 for aoRow in self._oDb.fetchAll():
479 aoRet.append(SchedGroupDataEx().initFromDbRowEx(aoRow, self._oDb, tsNow));
480 return aoRet;
481
482 def addEntry(self, oData, uidAuthor, fCommit = False):
483 """Add Scheduling Group record"""
484
485 #
486 # Validate.
487 #
488 dDataErrors = oData.validateAndConvert(self._oDb, idPrimaryMustBeNullOrNot = True);
489 if len(dDataErrors) > 0:
490 raise TMInvalidData('Invalid data passed to addEntry: %s' % (dDataErrors,));
491 if self.exists(oData.sName):
492 raise TMRowAlreadyExists('Scheduling group "%s" already exists.' % (oData.sName,));
493
494 #
495 # Add it.
496 #
497 self._oDb.execute('INSERT INTO SchedGroups (\n'
498 ' uidAuthor,\n'
499 ' sName,\n'
500 ' sDescription,\n'
501 ' fEnabled,\n'
502 ' enmScheduler,\n'
503 ' idBuildSrc,\n'
504 ' idBuildSrcTestSuite,\n'
505 ' sComment)\n'
506 'VALUES (%s, %s, %s, %s, %s, %s, %s, %s)\n'
507 'RETURNING idSchedGroup\n'
508 , ( uidAuthor,
509 oData.sName,
510 oData.sDescription,
511 oData.fEnabled,
512 oData.enmScheduler,
513 oData.idBuildSrc,
514 oData.idBuildSrcTestSuite,
515 oData.sComment ));
516 idSchedGroup = self._oDb.fetchOne()[0];
517 oData.idSchedGroup = idSchedGroup;
518
519 for oMember in oData.aoMembers:
520 oMember.idSchedGroup = idSchedGroup;
521 self._addSchedGroupMember(uidAuthor, oMember);
522
523 self._oDb.maybeCommit(fCommit);
524 return True;
525
526 def editEntry(self, oData, uidAuthor, fCommit = False):
527 """Edit Scheduling Group record"""
528
529 #
530 # Validate input and retrieve the old data.
531 #
532 dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Edit);
533 if len(dErrors) > 0:
534 raise TMInvalidData('editEntry got invalid data: %s' % (dErrors,));
535 self._assertUnique(oData.sName, oData.idSchedGroup);
536 oOldData = SchedGroupDataEx().initFromDbWithId(self._oDb, oData.idSchedGroup);
537
538 #
539 # Make the changes.
540 #
541 if not oData.isEqualEx(oOldData, [ 'tsEffective', 'tsExpire', 'uidAuthor', 'aoMembers', 'aoTestBoxes',
542 'oBuildSrc', 'oBuildSrcValidationKit', ]):
543 self._historizeEntry(oData.idSchedGroup);
544 self._readdEntry(uidAuthor, oData);
545
546 # Remove groups.
547 for oOld in oOldData.aoMembers:
548 fRemove = True;
549 for oNew in oData.aoMembers:
550 if oNew.idTestGroup == oOld.idTestGroup:
551 fRemove = False;
552 break;
553 if fRemove:
554 self._removeSchedGroupMember(uidAuthor, oOld);
555
556 # Add / modify groups.
557 for oMember in oData.aoMembers:
558 oOldMember = None;
559 for oOld in oOldData.aoMembers:
560 if oOld.idTestGroup == oMember.idTestGroup:
561 oOldMember = oOld;
562 break;
563
564 oMember.idSchedGroup = oData.idSchedGroup;
565 if oOldMember is None:
566 self._addSchedGroupMember(uidAuthor, oMember);
567 elif not oMember.isEqualEx(oOldMember, ['tsEffective', 'tsExpire', 'uidAuthor', 'oTestGroup']):
568 self._historizeSchedGroupMember(oMember);
569 self._addSchedGroupMember(uidAuthor, oMember);
570
571 self._oDb.maybeCommit(fCommit);
572 return True;
573
574 def removeEntry(self, uidAuthor, idSchedGroup, fCascade = False, fCommit = False):
575 """
576 Deletes a scheduling group.
577 """
578
579 #
580 # Input validation and retrival of current data.
581 #
582 if idSchedGroup == 1:
583 raise TMRowInUse('Cannot remove the default scheduling group (id 1).');
584 oData = SchedGroupDataEx().initFromDbWithId(self._oDb, idSchedGroup);
585
586 #
587 # We use cascade a little different here... We don't actually delete
588 # associated testboxes or testgroups.
589 #
590 if len(oData.aoTestBoxes) > 0:
591 if fCascade is not True:
592 # Complain about there being associated testboxes.
593 asTestBoxes = ['%s (#%d)' % (oTestBox.sName, oTestBox.idTestBox) for oTestBox in oData.aoTestBoxes];
594 raise TMRowInUse('Scheduling group #%d is associated with one or more test boxes: %s'
595 % (idSchedGroup, ', '.join(asTestBoxes),));
596 else:
597 # Reassign testboxes to scheduling group #1 (the default group).
598 oTbLogic = TestBoxLogic(self._oDb);
599 for oTestBox in oData.aoTestBoxes:
600 oTbCopy = TestBoxData().initFromOther(oTestBox);
601 oTbCopy.idSchedGroup = 1;
602 oTbLogic.editEntry(oTbCopy, uidAuthor, fCommit = False);
603
604 oData = SchedGroupDataEx().initFromDbWithId(self._oDb, idSchedGroup);
605 if len(oData.aoTestBoxes) != 0:
606 raise TMRowInUse('More testboxes was added to the scheduling group as we were trying to delete it.');
607
608 #
609 # Remove the group and all member records.
610 #
611 for oMember in oData.aoMembers:
612 self._removeSchedGroupMember(uidAuthor, oMember);
613 self._oDb.execute('UPDATE SchedGroupMembers\n'
614 'SET tsExpire = CURRENT_TIMESTAMP\n'
615 'WHERE idSchedGroup = %s\n'
616 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
617 , (idSchedGroup,));
618
619 (tsCur, tsCurMinusOne) = self._oDb.getCurrentTimestamps();
620 if oData.tsEffective != tsCur and oData.tsEffective != tsCurMinusOne:
621 self._historizeEntry(idSchedGroup, tsCurMinusOne);
622 self._readdEntry(uidAuthor, oData, tsCurMinusOne);
623 self._historizeEntry(idSchedGroup);
624 self._oDb.execute('UPDATE SchedGroups\n'
625 'SET tsExpire = CURRENT_TIMESTAMP\n'
626 'WHERE idSchedGroup = %s\n'
627 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
628 , (idSchedGroup,))
629
630 self._oDb.maybeCommit(fCommit)
631 return True;
632
633
634
635 #
636 # Other methods.
637 #
638
639 def fetchOrderedByName(self, tsNow = None):
640 """
641 Return list of objects of type SchedGroups ordered by name.
642 May raise exception on database error.
643 """
644 if tsNow is None:
645 self._oDb.execute('SELECT *\n'
646 'FROM SchedGroups\n'
647 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
648 'ORDER BY sName ASC\n');
649 else:
650 self._oDb.execute('SELECT *\n'
651 'FROM SchedGroups\n'
652 'WHERE tsExpire > %s\n'
653 ' AND tsEffective <= %s\n'
654 'ORDER BY sName ASC\n'
655 , (tsNow, tsNow,));
656 aoRet = []
657 for _ in range(self._oDb.getRowCount()):
658 aoRet.append(SchedGroupData().initFromDbRow(self._oDb.fetchOne()));
659 return aoRet;
660
661
662 def getAll(self, tsEffective = None):
663 """
664 Gets the list of all scheduling groups.
665 Returns an array of SchedGroupData instances.
666 """
667 if tsEffective is None:
668 self._oDb.execute('SELECT *\n'
669 'FROM SchedGroups\n'
670 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n');
671 else:
672 self._oDb.execute('SELECT *\n'
673 'FROM SchedGroups\n'
674 'WHERE tsExpire > %s\n'
675 ' AND tsEffective <= %s\n'
676 , (tsEffective, tsEffective));
677 aoRet = [];
678 for aoRow in self._oDb.fetchAll():
679 aoRet.append(SchedGroupData().initFromDbRow(aoRow));
680 return aoRet;
681
682 def getSchedGroupsForCombo(self, tsEffective = None):
683 """
684 Gets the list of active scheduling groups for a combo box.
685 Returns an array of (value [idSchedGroup], drop-down-name [sName],
686 hover-text [sDescription]) tuples.
687 """
688 if tsEffective is None:
689 self._oDb.execute('SELECT idSchedGroup, sName, sDescription\n'
690 'FROM SchedGroups\n'
691 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
692 'ORDER BY sName');
693 else:
694 self._oDb.execute('SELECT idSchedGroup, sName, sDescription\n'
695 'FROM SchedGroups\n'
696 'WHERE tsExpire > %s\n'
697 ' AND tsEffective <= %s\n'
698 'ORDER BY sName'
699 , (tsEffective, tsEffective));
700 return self._oDb.fetchAll();
701
702
703 def getMembers(self, idSchedGroup, tsEffective = None):
704 """
705 Gets the scheduling groups members for the given scheduling group.
706
707 Returns an array of SchedGroupMemberDataEx instances (sorted by
708 priority and idTestGroup). May raise exception DB error.
709 """
710
711 if tsEffective is None:
712 self._oDb.execute('SELECT *\n'
713 'FROM SchedGroupMembers, TestGroups\n'
714 'WHERE SchedGroupMembers.idSchedGroup = %s\n'
715 ' AND SchedGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
716 ' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
717 ' AND TestGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
718 'ORDER BY SchedGroupMembers.iSchedPriority, SchedGroupMembers.idTestGroup\n'
719 , (idSchedGroup,));
720 else:
721 self._oDb.execute('SELECT *\n'
722 'FROM SchedGroupMembers, TestGroups\n'
723 'WHERE SchedGroupMembers.idSchedGroup = %s\n'
724 ' AND SchedGroupMembers.tsExpire < %s\n'
725 ' AND SchedGroupMembers.tsEffective >= %s\n'
726 ' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
727 ' AND TestGroups.tsExpire < %s\n'
728 ' AND TestGroups.tsEffective >= %s\n'
729 'ORDER BY SchedGroupMembers.iSchedPriority DESC, SchedGroupMembers.idTestGroup\n'
730 , (idSchedGroup, tsEffective, tsEffective, tsEffective, tsEffective, ));
731 aaoRows = self._oDb.fetchAll();
732 aoRet = [];
733 for aoRow in aaoRows:
734 aoRet.append(SchedGroupMemberDataEx().initFromDbRow(aoRow));
735 return aoRet;
736
737 def getTestCasesForGroup(self, idSchedGroup, cMax = None):
738 """
739 Gets the enabled testcases w/ testgroup+priority for the given scheduling group.
740
741 Returns an array TestCaseData instance (group id, testcase priority and
742 testcase ids) with an extra iSchedPriority member.
743 May raise exception on DB error or if the result exceeds cMax.
744 """
745
746 self._oDb.execute('SELECT TestGroupMembers.idTestGroup, TestGroupMembers.iSchedPriority, TestCases.*\n'
747 'FROM SchedGroupMembers, TestGroups, TestGroupMembers, TestCases\n'
748 'WHERE SchedGroupMembers.idSchedGroup = %s\n'
749 ' AND SchedGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
750 ' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
751 ' AND TestGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
752 ' AND TestGroupMembers.idTestGroup = TestGroups.idTestGroup\n'
753 ' AND TestGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
754 ' AND TestCases.idTestCase = TestGroupMembers.idTestCase\n'
755 ' AND TestCases.tsExpire = \'infinity\'::TIMESTAMP\n'
756 ' AND TestCases.fEnabled = TRUE\n'
757 'ORDER BY TestGroupMembers.idTestGroup, TestGroupMembers.iSchedPriority, TestCases.idTestCase\n'
758 , (idSchedGroup,));
759
760 if cMax is not None and self._oDb.getRowCount() > cMax:
761 raise TMExceptionBase('Too many testcases for scheduling group %s: %s, max %s'
762 % (idSchedGroup, cMax, self._oDb.getRowCount(),));
763
764 aoRet = [];
765 for aoRow in self._oDb.fetchAll():
766 oTestCase = TestCaseData().initFromDbRow(aoRow[2:]);
767 oTestCase.idTestGroup = aoRow[0];
768 oTestCase.iSchedPriority = aoRow[1];
769 aoRet.append(oTestCase);
770 return aoRet;
771
772 def getTestCaseArgsForGroup(self, idSchedGroup, cMax = None):
773 """
774 Gets the testcase argument variation w/ testgroup+priority for the given scheduling group.
775
776 Returns an array TestCaseArgsData instance (sorted by group and
777 variation id) with an extra iSchedPriority member.
778 May raise exception on DB error or if the result exceeds cMax.
779 """
780
781 self._oDb.execute('SELECT TestGroupMembers.idTestGroup, TestGroupMembers.iSchedPriority, TestCaseArgs.*\n'
782 'FROM SchedGroupMembers, TestGroups, TestGroupMembers, TestCaseArgs, TestCases\n'
783 'WHERE SchedGroupMembers.idSchedGroup = %s\n'
784 ' AND SchedGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
785 ' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
786 ' AND TestGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
787 ' AND TestGroupMembers.idTestGroup = TestGroups.idTestGroup\n'
788 ' AND TestGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
789 ' AND TestCaseArgs.idTestCase = TestGroupMembers.idTestCase\n'
790 ' AND TestCaseArgs.tsExpire = \'infinity\'::TIMESTAMP\n'
791 ' AND ( TestGroupMembers.aidTestCaseArgs is NULL\n'
792 ' OR TestCaseArgs.idTestCaseArgs = ANY(TestGroupMembers.aidTestCaseArgs) )\n'
793 ' AND TestCases.idTestCase = TestCaseArgs.idTestCase\n'
794 ' AND TestCases.tsExpire = \'infinity\'::TIMESTAMP\n'
795 ' AND TestCases.fEnabled = TRUE\n'
796 'ORDER BY TestGroupMembers.idTestGroup, TestGroupMembers.idTestCase, TestCaseArgs.idTestCaseArgs\n'
797 , (idSchedGroup,));
798
799 if cMax is not None and self._oDb.getRowCount() > cMax:
800 raise TMExceptionBase('Too many argument variations for scheduling group %s: %s, max %s'
801 % (idSchedGroup, cMax, self._oDb.getRowCount(),));
802
803 aoRet = [];
804 for aoRow in self._oDb.fetchAll():
805 oVariation = TestCaseArgsData().initFromDbRow(aoRow[2:]);
806 oVariation.idTestGroup = aoRow[0];
807 oVariation.iSchedPriority = aoRow[1];
808 aoRet.append(oVariation);
809 return aoRet;
810
811 def exists(self, sName):
812 """Checks if a group with the given name exists."""
813 self._oDb.execute('SELECT idSchedGroup\n'
814 'FROM SchedGroups\n'
815 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
816 ' AND sName = %s\n'
817 'LIMIT 1\n'
818 , (sName,));
819 return self._oDb.getRowCount() > 0;
820
821 def getById(self, idSchedGroup):
822 """Get Scheduling Group data by idSchedGroup"""
823 self._oDb.execute('SELECT *\n'
824 'FROM SchedGroups\n'
825 'WHERE tsExpire = \'infinity\'::timestamp\n'
826 ' AND idSchedGroup = %s;', (idSchedGroup,))
827 aRows = self._oDb.fetchAll()
828 if len(aRows) not in (0, 1):
829 raise self._oDb.integrityException(
830 'Found more than one scheduling groups with the same credentials. Database structure is corrupted.')
831 try:
832 return SchedGroupData().initFromDbRow(aRows[0])
833 except IndexError:
834 return None
835
836
837 #
838 # Internal helpers.
839 #
840
841 def _assertUnique(self, sName, idSchedGroupIgnore = None):
842 """
843 Checks that the scheduling group name is unique.
844 Raises exception if the name is already in use.
845 """
846 if idSchedGroupIgnore is None:
847 self._oDb.execute('SELECT idSchedGroup\n'
848 'FROM SchedGroups\n'
849 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
850 ' AND sName = %s\n'
851 , ( sName, ) );
852 else:
853 self._oDb.execute('SELECT idSchedGroup\n'
854 'FROM SchedGroups\n'
855 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
856 ' AND sName = %s\n'
857 ' AND idSchedGroup <> %s\n'
858 , ( sName, idSchedGroupIgnore, ) );
859 if self._oDb.getRowCount() > 0:
860 raise TMRowInUse('Scheduling group name (%s) is already in use.' % (sName,));
861 return True;
862
863 def _readdEntry(self, uidAuthor, oData, tsEffective = None):
864 """
865 Re-adds the SchedGroups entry. Used by editEntry and removeEntry.
866 """
867 if tsEffective is None:
868 tsEffective = self._oDb.getCurrentTimestamp();
869 self._oDb.execute('INSERT INTO SchedGroups (\n'
870 ' uidAuthor,\n'
871 ' tsEffective,\n'
872 ' idSchedGroup,\n'
873 ' sName,\n'
874 ' sDescription,\n'
875 ' fEnabled,\n'
876 ' enmScheduler,\n'
877 ' idBuildSrc,\n'
878 ' idBuildSrcTestSuite,\n'
879 ' sComment )\n'
880 'VALUES ( %s, %s, %s, %s, %s, %s, %s, %s, %s, %s )\n'
881 , ( uidAuthor,
882 tsEffective,
883 oData.idSchedGroup,
884 oData.sName,
885 oData.sDescription,
886 oData.fEnabled,
887 oData.enmScheduler,
888 oData.idBuildSrc,
889 oData.idBuildSrcTestSuite,
890 oData.sComment, ));
891 return True;
892
893 def _historizeEntry(self, idSchedGroup, tsExpire = None):
894 """
895 Historizes the current entry for the given scheduling group.
896 """
897 if tsExpire is None:
898 tsExpire = self._oDb.getCurrentTimestamp();
899 self._oDb.execute('UPDATE SchedGroups\n'
900 'SET tsExpire = %s\n'
901 'WHERE idSchedGroup = %s\n'
902 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
903 , ( tsExpire, idSchedGroup, ));
904 return True;
905
906 def _addSchedGroupMember(self, uidAuthor, oMember, tsEffective = None):
907 """
908 addEntry worker for adding a scheduling group member.
909 """
910 if tsEffective is None:
911 tsEffective = self._oDb.getCurrentTimestamp();
912 self._oDb.execute('INSERT INTO SchedGroupMembers(\n'
913 ' idSchedGroup,\n'
914 ' idTestGroup,\n'
915 ' tsEffective,\n'
916 ' uidAuthor,\n'
917 ' iSchedPriority,\n'
918 ' bmHourlySchedule,\n'
919 ' idTestGroupPreReq)\n'
920 'VALUES (%s, %s, %s, %s, %s, %s, %s)\n'
921 , ( oMember.idSchedGroup,
922 oMember.idTestGroup,
923 tsEffective,
924 uidAuthor,
925 oMember.iSchedPriority,
926 oMember.bmHourlySchedule,
927 oMember.idTestGroupPreReq, ));
928 return True;
929
930 def _removeSchedGroupMember(self, uidAuthor, oMember):
931 """
932 Removes a scheduling group member.
933 """
934
935 # Try record who removed it by adding an dummy entry that expires immediately.
936 (tsCur, tsCurMinusOne) = self._oDb.getCurrentTimestamps();
937 if oMember.tsEffective != tsCur and oMember.tsEffective != tsCurMinusOne:
938 self._historizeSchedGroupMember(oMember, tsCurMinusOne);
939 self._addSchedGroupMember(uidAuthor, oMember, tsCurMinusOne); # lazy bird.
940 self._historizeSchedGroupMember(oMember);
941 else:
942 self._historizeSchedGroupMember(oMember);
943 return True;
944
945 def _historizeSchedGroupMember(self, oMember, tsExpire = None):
946 """
947 Historizes the current entry for the given scheduling group.
948 """
949 if tsExpire is None:
950 tsExpire = self._oDb.getCurrentTimestamp();
951 self._oDb.execute('UPDATE SchedGroupMembers\n'
952 'SET tsExpire = %s\n'
953 'WHERE idSchedGroup = %s\n'
954 ' AND idTestGroup = %s\n'
955 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
956 , ( tsExpire, oMember.idSchedGroup, oMember.idTestGroup, ));
957 return True;
958
959
960
961
962#
963# Unit testing.
964#
965
966# pylint: disable=C0111
967class SchedGroupMemberDataTestCase(ModelDataBaseTestCase):
968 def setUp(self):
969 self.aoSamples = [SchedGroupMemberData(),];
970
971class SchedGroupDataTestCase(ModelDataBaseTestCase):
972 def setUp(self):
973 self.aoSamples = [SchedGroupData(),];
974
975if __name__ == '__main__':
976 unittest.main();
977 # not reached.
978
Note: See TracBrowser for help on using the repository browser.

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