VirtualBox

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

Last change on this file since 63694 was 62484, checked in by vboxsync, 8 years ago

(C) 2016

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 40.9 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: schedgroup.py 62484 2016-07-22 18:35:33Z vboxsync $
3
4"""
5Test Manager - Scheduling Group.
6"""
7
8__copyright__ = \
9"""
10Copyright (C) 2012-2016 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: 62484 $"
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 len(dErrors) > 0:
379 asErrors.append(str(dErrors));
380
381 if len(asErrors) == 0:
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 len(asErrors) == 0 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 #
429 # Standard methods.
430 #
431
432 def fetchForListing(self, iStart, cMaxRows, tsNow):
433 """
434 Fetches build sources.
435
436 Returns an array (list) of BuildSourceData items, empty list if none.
437 Raises exception on error.
438 """
439
440 if tsNow is None:
441 self._oDb.execute('SELECT *\n'
442 'FROM SchedGroups\n'
443 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
444 'ORDER BY idSchedGroup DESC\n'
445 'LIMIT %s OFFSET %s\n'
446 , (cMaxRows, iStart,));
447 else:
448 self._oDb.execute('SELECT *\n'
449 'FROM SchedGroups\n'
450 'WHERE tsExpire > %s\n'
451 ' AND tsEffective <= %s\n'
452 'ORDER BY idSchedGroup DESC\n'
453 'LIMIT %s OFFSET %s\n'
454 , (tsNow, tsNow, cMaxRows, iStart,));
455
456 aoRet = [];
457 for aoRow in self._oDb.fetchAll():
458 aoRet.append(SchedGroupDataEx().initFromDbRowEx(aoRow, self._oDb, tsNow));
459 return aoRet;
460
461 def addEntry(self, oData, uidAuthor, fCommit = False):
462 """Add Scheduling Group record"""
463
464 #
465 # Validate.
466 #
467 dDataErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Add);
468 if len(dDataErrors) > 0:
469 raise TMInvalidData('Invalid data passed to addEntry: %s' % (dDataErrors,));
470 if self.exists(oData.sName):
471 raise TMRowAlreadyExists('Scheduling group "%s" already exists.' % (oData.sName,));
472
473 #
474 # Add it.
475 #
476 self._oDb.execute('INSERT INTO SchedGroups (\n'
477 ' uidAuthor,\n'
478 ' sName,\n'
479 ' sDescription,\n'
480 ' fEnabled,\n'
481 ' enmScheduler,\n'
482 ' idBuildSrc,\n'
483 ' idBuildSrcTestSuite,\n'
484 ' sComment)\n'
485 'VALUES (%s, %s, %s, %s, %s, %s, %s, %s)\n'
486 'RETURNING idSchedGroup\n'
487 , ( uidAuthor,
488 oData.sName,
489 oData.sDescription,
490 oData.fEnabled,
491 oData.enmScheduler,
492 oData.idBuildSrc,
493 oData.idBuildSrcTestSuite,
494 oData.sComment ));
495 idSchedGroup = self._oDb.fetchOne()[0];
496 oData.idSchedGroup = idSchedGroup;
497
498 for oMember in oData.aoMembers:
499 oMember.idSchedGroup = idSchedGroup;
500 self._addSchedGroupMember(uidAuthor, oMember);
501
502 self._oDb.maybeCommit(fCommit);
503 return True;
504
505 def editEntry(self, oData, uidAuthor, fCommit = False):
506 """Edit Scheduling Group record"""
507
508 #
509 # Validate input and retrieve the old data.
510 #
511 dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Edit);
512 if len(dErrors) > 0:
513 raise TMInvalidData('editEntry got invalid data: %s' % (dErrors,));
514 self._assertUnique(oData.sName, oData.idSchedGroup);
515 oOldData = SchedGroupDataEx().initFromDbWithId(self._oDb, oData.idSchedGroup);
516
517 #
518 # Make the changes.
519 #
520 if not oData.isEqualEx(oOldData, [ 'tsEffective', 'tsExpire', 'uidAuthor', 'aoMembers', 'aoTestBoxes',
521 'oBuildSrc', 'oBuildSrcValidationKit', ]):
522 self._historizeEntry(oData.idSchedGroup);
523 self._readdEntry(uidAuthor, oData);
524
525 # Remove groups.
526 for oOld in oOldData.aoMembers:
527 fRemove = True;
528 for oNew in oData.aoMembers:
529 if oNew.idTestGroup == oOld.idTestGroup:
530 fRemove = False;
531 break;
532 if fRemove:
533 self._removeSchedGroupMember(uidAuthor, oOld);
534
535 # Add / modify groups.
536 for oMember in oData.aoMembers:
537 oOldMember = None;
538 for oOld in oOldData.aoMembers:
539 if oOld.idTestGroup == oMember.idTestGroup:
540 oOldMember = oOld;
541 break;
542
543 oMember.idSchedGroup = oData.idSchedGroup;
544 if oOldMember is None:
545 self._addSchedGroupMember(uidAuthor, oMember);
546 elif not oMember.isEqualEx(oOldMember, ['tsEffective', 'tsExpire', 'uidAuthor', 'oTestGroup']):
547 self._historizeSchedGroupMember(oMember);
548 self._addSchedGroupMember(uidAuthor, oMember);
549
550 self._oDb.maybeCommit(fCommit);
551 return True;
552
553 def removeEntry(self, uidAuthor, idSchedGroup, fCascade = False, fCommit = False):
554 """
555 Deletes a scheduling group.
556 """
557
558 #
559 # Input validation and retrival of current data.
560 #
561 if idSchedGroup == 1:
562 raise TMRowInUse('Cannot remove the default scheduling group (id 1).');
563 oData = SchedGroupDataEx().initFromDbWithId(self._oDb, idSchedGroup);
564
565 #
566 # We use cascade a little different here... We don't actually delete
567 # associated testboxes or testgroups.
568 #
569 if len(oData.aoTestBoxes) > 0:
570 if fCascade is not True:
571 # Complain about there being associated testboxes.
572 asTestBoxes = ['%s (#%d)' % (oTestBox.sName, oTestBox.idTestBox) for oTestBox in oData.aoTestBoxes];
573 raise TMRowInUse('Scheduling group #%d is associated with one or more test boxes: %s'
574 % (idSchedGroup, ', '.join(asTestBoxes),));
575 else:
576 # Reassign testboxes to scheduling group #1 (the default group).
577 oTbLogic = TestBoxLogic(self._oDb);
578 for oTestBox in oData.aoTestBoxes:
579 oTbCopy = TestBoxData().initFromOther(oTestBox);
580 oTbCopy.idSchedGroup = 1;
581 oTbLogic.editEntry(oTbCopy, uidAuthor, fCommit = False);
582
583 oData = SchedGroupDataEx().initFromDbWithId(self._oDb, idSchedGroup);
584 if len(oData.aoTestBoxes) != 0:
585 raise TMRowInUse('More testboxes was added to the scheduling group as we were trying to delete it.');
586
587 #
588 # Remove the group and all member records.
589 #
590 for oMember in oData.aoMembers:
591 self._removeSchedGroupMember(uidAuthor, oMember);
592 self._oDb.execute('UPDATE SchedGroupMembers\n'
593 'SET tsExpire = CURRENT_TIMESTAMP\n'
594 'WHERE idSchedGroup = %s\n'
595 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
596 , (idSchedGroup,));
597
598 (tsCur, tsCurMinusOne) = self._oDb.getCurrentTimestamps();
599 if oData.tsEffective != tsCur and oData.tsEffective != tsCurMinusOne:
600 self._historizeEntry(idSchedGroup, tsCurMinusOne);
601 self._readdEntry(uidAuthor, oData, tsCurMinusOne);
602 self._historizeEntry(idSchedGroup);
603 self._oDb.execute('UPDATE SchedGroups\n'
604 'SET tsExpire = CURRENT_TIMESTAMP\n'
605 'WHERE idSchedGroup = %s\n'
606 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
607 , (idSchedGroup,))
608
609 self._oDb.maybeCommit(fCommit)
610 return True;
611
612
613
614 #
615 # Other methods.
616 #
617
618 def fetchOrderedByName(self, tsNow = None):
619 """
620 Return list of objects of type SchedGroups ordered by name.
621 May raise exception on database error.
622 """
623 if tsNow is None:
624 self._oDb.execute('SELECT *\n'
625 'FROM SchedGroups\n'
626 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
627 'ORDER BY sName ASC\n');
628 else:
629 self._oDb.execute('SELECT *\n'
630 'FROM SchedGroups\n'
631 'WHERE tsExpire > %s\n'
632 ' AND tsEffective <= %s\n'
633 'ORDER BY sName ASC\n'
634 , (tsNow, tsNow,));
635 aoRet = []
636 for _ in range(self._oDb.getRowCount()):
637 aoRet.append(SchedGroupData().initFromDbRow(self._oDb.fetchOne()));
638 return aoRet;
639
640
641 def getAll(self, tsEffective = None):
642 """
643 Gets the list of all scheduling groups.
644 Returns an array of SchedGroupData instances.
645 """
646 if tsEffective is None:
647 self._oDb.execute('SELECT *\n'
648 'FROM SchedGroups\n'
649 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n');
650 else:
651 self._oDb.execute('SELECT *\n'
652 'FROM SchedGroups\n'
653 'WHERE tsExpire > %s\n'
654 ' AND tsEffective <= %s\n'
655 , (tsEffective, tsEffective));
656 aoRet = [];
657 for aoRow in self._oDb.fetchAll():
658 aoRet.append(SchedGroupData().initFromDbRow(aoRow));
659 return aoRet;
660
661 def getSchedGroupsForCombo(self, tsEffective = None):
662 """
663 Gets the list of active scheduling groups for a combo box.
664 Returns an array of (value [idSchedGroup], drop-down-name [sName],
665 hover-text [sDescription]) tuples.
666 """
667 if tsEffective is None:
668 self._oDb.execute('SELECT idSchedGroup, sName, sDescription\n'
669 'FROM SchedGroups\n'
670 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
671 'ORDER BY sName');
672 else:
673 self._oDb.execute('SELECT idSchedGroup, sName, sDescription\n'
674 'FROM SchedGroups\n'
675 'WHERE tsExpire > %s\n'
676 ' AND tsEffective <= %s\n'
677 'ORDER BY sName'
678 , (tsEffective, tsEffective));
679 return self._oDb.fetchAll();
680
681
682 def getMembers(self, idSchedGroup, tsEffective = None):
683 """
684 Gets the scheduling groups members for the given scheduling group.
685
686 Returns an array of SchedGroupMemberDataEx instances (sorted by
687 priority and idTestGroup). May raise exception DB error.
688 """
689
690 if tsEffective is None:
691 self._oDb.execute('SELECT *\n'
692 'FROM SchedGroupMembers, TestGroups\n'
693 'WHERE SchedGroupMembers.idSchedGroup = %s\n'
694 ' AND SchedGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
695 ' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
696 ' AND TestGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
697 'ORDER BY SchedGroupMembers.iSchedPriority, SchedGroupMembers.idTestGroup\n'
698 , (idSchedGroup,));
699 else:
700 self._oDb.execute('SELECT *\n'
701 'FROM SchedGroupMembers, TestGroups\n'
702 'WHERE SchedGroupMembers.idSchedGroup = %s\n'
703 ' AND SchedGroupMembers.tsExpire < %s\n'
704 ' AND SchedGroupMembers.tsEffective >= %s\n'
705 ' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
706 ' AND TestGroups.tsExpire < %s\n'
707 ' AND TestGroups.tsEffective >= %s\n'
708 'ORDER BY SchedGroupMembers.iSchedPriority DESC, SchedGroupMembers.idTestGroup\n'
709 , (idSchedGroup, tsEffective, tsEffective, tsEffective, tsEffective, ));
710 aaoRows = self._oDb.fetchAll();
711 aoRet = [];
712 for aoRow in aaoRows:
713 aoRet.append(SchedGroupMemberDataEx().initFromDbRow(aoRow));
714 return aoRet;
715
716 def getTestCasesForGroup(self, idSchedGroup, cMax = None):
717 """
718 Gets the enabled testcases w/ testgroup+priority for the given scheduling group.
719
720 Returns an array TestCaseData instance (group id, testcase priority and
721 testcase ids) with an extra iSchedPriority member.
722 May raise exception on DB error or if the result exceeds cMax.
723 """
724
725 self._oDb.execute('SELECT TestGroupMembers.idTestGroup, TestGroupMembers.iSchedPriority, TestCases.*\n'
726 'FROM SchedGroupMembers, TestGroups, TestGroupMembers, TestCases\n'
727 'WHERE SchedGroupMembers.idSchedGroup = %s\n'
728 ' AND SchedGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
729 ' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
730 ' AND TestGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
731 ' AND TestGroupMembers.idTestGroup = TestGroups.idTestGroup\n'
732 ' AND TestGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
733 ' AND TestCases.idTestCase = TestGroupMembers.idTestCase\n'
734 ' AND TestCases.tsExpire = \'infinity\'::TIMESTAMP\n'
735 ' AND TestCases.fEnabled = TRUE\n'
736 'ORDER BY TestGroupMembers.idTestGroup, TestGroupMembers.iSchedPriority, TestCases.idTestCase\n'
737 , (idSchedGroup,));
738
739 if cMax is not None and self._oDb.getRowCount() > cMax:
740 raise TMExceptionBase('Too many testcases for scheduling group %s: %s, max %s'
741 % (idSchedGroup, cMax, self._oDb.getRowCount(),));
742
743 aoRet = [];
744 for aoRow in self._oDb.fetchAll():
745 oTestCase = TestCaseData().initFromDbRow(aoRow[2:]);
746 oTestCase.idTestGroup = aoRow[0];
747 oTestCase.iSchedPriority = aoRow[1];
748 aoRet.append(oTestCase);
749 return aoRet;
750
751 def getTestCaseArgsForGroup(self, idSchedGroup, cMax = None):
752 """
753 Gets the testcase argument variation w/ testgroup+priority for the given scheduling group.
754
755 Returns an array TestCaseArgsData instance (sorted by group and
756 variation id) with an extra iSchedPriority member.
757 May raise exception on DB error or if the result exceeds cMax.
758 """
759
760 self._oDb.execute('SELECT TestGroupMembers.idTestGroup, TestGroupMembers.iSchedPriority, TestCaseArgs.*\n'
761 'FROM SchedGroupMembers, TestGroups, TestGroupMembers, TestCaseArgs, TestCases\n'
762 'WHERE SchedGroupMembers.idSchedGroup = %s\n'
763 ' AND SchedGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
764 ' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
765 ' AND TestGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
766 ' AND TestGroupMembers.idTestGroup = TestGroups.idTestGroup\n'
767 ' AND TestGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
768 ' AND TestCaseArgs.idTestCase = TestGroupMembers.idTestCase\n'
769 ' AND TestCaseArgs.tsExpire = \'infinity\'::TIMESTAMP\n'
770 ' AND ( TestGroupMembers.aidTestCaseArgs is NULL\n'
771 ' OR TestCaseArgs.idTestCaseArgs = ANY(TestGroupMembers.aidTestCaseArgs) )\n'
772 ' AND TestCases.idTestCase = TestCaseArgs.idTestCase\n'
773 ' AND TestCases.tsExpire = \'infinity\'::TIMESTAMP\n'
774 ' AND TestCases.fEnabled = TRUE\n'
775 'ORDER BY TestGroupMembers.idTestGroup, TestGroupMembers.idTestCase, TestCaseArgs.idTestCaseArgs\n'
776 , (idSchedGroup,));
777
778 if cMax is not None and self._oDb.getRowCount() > cMax:
779 raise TMExceptionBase('Too many argument variations for scheduling group %s: %s, max %s'
780 % (idSchedGroup, cMax, self._oDb.getRowCount(),));
781
782 aoRet = [];
783 for aoRow in self._oDb.fetchAll():
784 oVariation = TestCaseArgsData().initFromDbRow(aoRow[2:]);
785 oVariation.idTestGroup = aoRow[0];
786 oVariation.iSchedPriority = aoRow[1];
787 aoRet.append(oVariation);
788 return aoRet;
789
790 def exists(self, sName):
791 """Checks if a group with the given name exists."""
792 self._oDb.execute('SELECT idSchedGroup\n'
793 'FROM SchedGroups\n'
794 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
795 ' AND sName = %s\n'
796 'LIMIT 1\n'
797 , (sName,));
798 return self._oDb.getRowCount() > 0;
799
800 def getById(self, idSchedGroup):
801 """Get Scheduling Group data by idSchedGroup"""
802 self._oDb.execute('SELECT *\n'
803 'FROM SchedGroups\n'
804 'WHERE tsExpire = \'infinity\'::timestamp\n'
805 ' AND idSchedGroup = %s;', (idSchedGroup,))
806 aRows = self._oDb.fetchAll()
807 if len(aRows) not in (0, 1):
808 raise self._oDb.integrityException(
809 'Found more than one scheduling groups with the same credentials. Database structure is corrupted.')
810 try:
811 return SchedGroupData().initFromDbRow(aRows[0])
812 except IndexError:
813 return None
814
815
816 #
817 # Internal helpers.
818 #
819
820 def _assertUnique(self, sName, idSchedGroupIgnore = None):
821 """
822 Checks that the scheduling group name is unique.
823 Raises exception if the name is already in use.
824 """
825 if idSchedGroupIgnore is None:
826 self._oDb.execute('SELECT idSchedGroup\n'
827 'FROM SchedGroups\n'
828 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
829 ' AND sName = %s\n'
830 , ( sName, ) );
831 else:
832 self._oDb.execute('SELECT idSchedGroup\n'
833 'FROM SchedGroups\n'
834 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
835 ' AND sName = %s\n'
836 ' AND idSchedGroup <> %s\n'
837 , ( sName, idSchedGroupIgnore, ) );
838 if self._oDb.getRowCount() > 0:
839 raise TMRowInUse('Scheduling group name (%s) is already in use.' % (sName,));
840 return True;
841
842 def _readdEntry(self, uidAuthor, oData, tsEffective = None):
843 """
844 Re-adds the SchedGroups entry. Used by editEntry and removeEntry.
845 """
846 if tsEffective is None:
847 tsEffective = self._oDb.getCurrentTimestamp();
848 self._oDb.execute('INSERT INTO SchedGroups (\n'
849 ' uidAuthor,\n'
850 ' tsEffective,\n'
851 ' idSchedGroup,\n'
852 ' sName,\n'
853 ' sDescription,\n'
854 ' fEnabled,\n'
855 ' enmScheduler,\n'
856 ' idBuildSrc,\n'
857 ' idBuildSrcTestSuite,\n'
858 ' sComment )\n'
859 'VALUES ( %s, %s, %s, %s, %s, %s, %s, %s, %s, %s )\n'
860 , ( uidAuthor,
861 tsEffective,
862 oData.idSchedGroup,
863 oData.sName,
864 oData.sDescription,
865 oData.fEnabled,
866 oData.enmScheduler,
867 oData.idBuildSrc,
868 oData.idBuildSrcTestSuite,
869 oData.sComment, ));
870 return True;
871
872 def _historizeEntry(self, idSchedGroup, tsExpire = None):
873 """
874 Historizes the current entry for the given scheduling group.
875 """
876 if tsExpire is None:
877 tsExpire = self._oDb.getCurrentTimestamp();
878 self._oDb.execute('UPDATE SchedGroups\n'
879 'SET tsExpire = %s\n'
880 'WHERE idSchedGroup = %s\n'
881 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
882 , ( tsExpire, idSchedGroup, ));
883 return True;
884
885 def _addSchedGroupMember(self, uidAuthor, oMember, tsEffective = None):
886 """
887 addEntry worker for adding a scheduling group member.
888 """
889 if tsEffective is None:
890 tsEffective = self._oDb.getCurrentTimestamp();
891 self._oDb.execute('INSERT INTO SchedGroupMembers(\n'
892 ' idSchedGroup,\n'
893 ' idTestGroup,\n'
894 ' tsEffective,\n'
895 ' uidAuthor,\n'
896 ' iSchedPriority,\n'
897 ' bmHourlySchedule,\n'
898 ' idTestGroupPreReq)\n'
899 'VALUES (%s, %s, %s, %s, %s, %s, %s)\n'
900 , ( oMember.idSchedGroup,
901 oMember.idTestGroup,
902 tsEffective,
903 uidAuthor,
904 oMember.iSchedPriority,
905 oMember.bmHourlySchedule,
906 oMember.idTestGroupPreReq, ));
907 return True;
908
909 def _removeSchedGroupMember(self, uidAuthor, oMember):
910 """
911 Removes a scheduling group member.
912 """
913
914 # Try record who removed it by adding an dummy entry that expires immediately.
915 (tsCur, tsCurMinusOne) = self._oDb.getCurrentTimestamps();
916 if oMember.tsEffective != tsCur and oMember.tsEffective != tsCurMinusOne:
917 self._historizeSchedGroupMember(oMember, tsCurMinusOne);
918 self._addSchedGroupMember(uidAuthor, oMember, tsCurMinusOne); # lazy bird.
919 self._historizeSchedGroupMember(oMember);
920 else:
921 self._historizeSchedGroupMember(oMember);
922 return True;
923
924 def _historizeSchedGroupMember(self, oMember, tsExpire = None):
925 """
926 Historizes the current entry for the given scheduling group.
927 """
928 if tsExpire is None:
929 tsExpire = self._oDb.getCurrentTimestamp();
930 self._oDb.execute('UPDATE SchedGroupMembers\n'
931 'SET tsExpire = %s\n'
932 'WHERE idSchedGroup = %s\n'
933 ' AND idTestGroup = %s\n'
934 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
935 , ( tsExpire, oMember.idSchedGroup, oMember.idTestGroup, ));
936 return True;
937
938
939
940
941#
942# Unit testing.
943#
944
945# pylint: disable=C0111
946class SchedGroupMemberDataTestCase(ModelDataBaseTestCase):
947 def setUp(self):
948 self.aoSamples = [SchedGroupMemberData(),];
949
950class SchedGroupDataTestCase(ModelDataBaseTestCase):
951 def setUp(self):
952 self.aoSamples = [SchedGroupData(),];
953
954if __name__ == '__main__':
955 unittest.main();
956 # not reached.
957
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