VirtualBox

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

Last change on this file since 56295 was 56295, checked in by vboxsync, 10 years ago

ValidationKit: Updated (C) year.

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