VirtualBox

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

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

testmanager: failiure reason fixes, some exception throwing cleanups, delinting with pylint 1.5.5.

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

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