schedgroup.py revision cf22150eaeeb72431bf1cf65c309a431454fb22b
# -*- coding: utf-8 -*-
# $Id$
"""
Test Manager - Scheduling Group.
"""
__copyright__ = \
"""
Copyright (C) 2012-2014 Oracle Corporation
This file is part of VirtualBox Open Source Edition (OSE), as
available from http://www.virtualbox.org. This file is free software;
you can redistribute it and/or modify it under the terms of the GNU
General Public License (GPL) as published by the Free Software
Foundation, in version 2 as it comes in the "COPYING" file of the
VirtualBox OSE distribution. VirtualBox OSE is distributed in the
hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
The contents of this file may alternatively be used under the terms
of the Common Development and Distribution License Version 1.0
(CDDL) only, as it comes in the "COPYING.CDDL" file of the
VirtualBox OSE distribution, in which case the provisions of the
CDDL are applicable instead of those of the GPL.
You may elect to license modified versions of this file under the
terms and conditions of either the GPL or the CDDL or both.
"""
__version__ = "$Revision$"
# Standard python imports.
import unittest;
# Validation Kit imports.
from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMExceptionBase;
from testmanager.core.buildsource import BuildSourceData;
from testmanager.core.testcase import TestCaseData;
from testmanager.core.testcaseargs import TestCaseArgsData;
from testmanager.core.testbox import TestBoxData, TestBoxLogic;
from testmanager.core.testgroup import TestGroupData;
class SchedGroupMemberData(ModelDataBase):
"""
SchedGroupMember Data.
"""
ksIdAttr = 'idSchedGroup';
ksParam_idSchedGroup = 'SchedGroupMember_idSchedGroup';
ksParam_idTestGroup = 'SchedGroupMember_idTestGroup';
ksParam_tsEffective = 'SchedGroupMember_tsEffective';
ksParam_tsExpire = 'SchedGroupMember_tsExpire';
ksParam_uidAuthor = 'SchedGroupMember_uidAuthor';
ksParam_iSchedPriority = 'SchedGroupMember_iSchedPriority';
ksParam_bmHourlySchedule = 'SchedGroupMember_bmHourlySchedule';
ksParam_idTestGroupPreReq = 'SchedGroupMember_idTestGroupPreReq';
kasAllowNullAttributes = [ 'idSchedGroup', 'idTestGroup', 'tsEffective', 'tsExpire',
'uidAuthor', 'bmHourlySchedule', 'idTestGroupPreReq' ];
kiMin_iSchedPriority = 0;
kiMax_iSchedPriority = 32;
def __init__(self):
ModelDataBase.__init__(self);
#
# Initialize with defaults.
# See the database for explanations of each of these fields.
#
self.idSchedGroup = None;
self.idTestGroup = None;
self.tsEffective = None;
self.tsExpire = None;
self.uidAuthor = None;
self.iSchedPriority = 16;
self.bmHourlySchedule = None;
self.idTestGroupPreReq = None;
def initFromDbRow(self, aoRow):
"""
Re-initializes the data with a row from a SELECT * FROM SchedGroupMembers.
Returns self. Raises exception if the row is None or otherwise invalid.
"""
if aoRow is None:
raise TMExceptionBase('SchedGroupMember not found.');
self.idSchedGroup = aoRow[0];
self.idTestGroup = aoRow[1];
self.tsEffective = aoRow[2];
self.tsExpire = aoRow[3];
self.uidAuthor = aoRow[4];
self.iSchedPriority = aoRow[5];
self.bmHourlySchedule = aoRow[6]; ## @todo figure out how bitmaps are returned...
self.idTestGroupPreReq = aoRow[7];
return self;
class SchedGroupMemberDataEx(SchedGroupMemberData):
"""
Extended SchedGroupMember data class.
This adds the testgroups.
"""
def __init__(self):
SchedGroupMemberData.__init__(self);
self.oTestGroup = None;
def initFromDbRow(self, aoRow):
"""
Re-initializes the data with a row from a query like this:
SELECT SchedGroupMembers.*, TestGroups.*
FROM SchedGroupMembers
JOIN TestGroups
ON (SchedGroupMembers.idTestGroup = TestGroups.idTestGroup);
Returns self. Raises exception if the row is None or otherwise invalid.
"""
SchedGroupMemberData.initFromDbRow(self, aoRow);
self.oTestGroup = TestGroupData().initFromDbRow(aoRow[8:]);
return self;
def getDataAttributes(self):
asAttributes = SchedGroupMemberData.getDataAttributes(self);
asAttributes.remove('oTestGroup');
return asAttributes;
def _validateAndConvertWorker(self, asAllowNullAttributes, oDb):
dErrors = SchedGroupMemberData._validateAndConvertWorker(self, asAllowNullAttributes, oDb);
if self.ksParam_idTestGroup not in dErrors:
self.oTestGroup = TestGroupData();
try:
self.oTestGroup.initFromDbWithId(oDb, self.idTestGroup);
except Exception as oXcpt:
self.oTestGroup = TestGroupData()
dErrors[self.ksParam_idTestGroup] = str(oXcpt);
return dErrors;
class SchedGroupData(ModelDataBase):
"""
SchedGroup Data.
"""
## @name TestBoxState_T
# @{
ksScheduler_BestEffortContinousItegration = 'bestEffortContinousItegration';
ksScheduler_Reserved = 'reserved';
## @}
ksIdAttr = 'idSchedGroup';
ksParam_idSchedGroup = 'SchedGroup_idSchedGroup';
ksParam_tsEffective = 'SchedGroup_tsEffective';
ksParam_tsExpire = 'SchedGroup_tsExpire';
ksParam_uidAuthor = 'SchedGroup_uidAuthor';
ksParam_sName = 'SchedGroup_sName';
ksParam_sDescription = 'SchedGroup_sDescription';
ksParam_fEnabled = 'SchedGroup_fEnabled';
ksParam_enmScheduler = 'SchedGroup_enmScheduler';
ksParam_idBuildSrc = 'SchedGroup_idBuildSrc';
ksParam_idBuildSrcTestSuite = 'SchedGroup_idBuildSrcTestSuite';
kasAllowNullAttributes = ['idSchedGroup', 'tsEffective', 'tsExpire', 'uidAuthor', 'sDescription',
'idBuildSrc', 'idBuildSrcTestSuite'];
kasValidValues_enmScheduler = [ksScheduler_BestEffortContinousItegration, ];
# Scheduler types
kasSchedulerDesc = \
[
( ksScheduler_BestEffortContinousItegration, 'Best-Effort-Continous-Itegration (BECI) scheduler.', ''),
]
def __init__(self):
ModelDataBase.__init__(self);
#
# Initialize with defaults.
# See the database for explanations of each of these fields.
#
self.idSchedGroup = None;
self.tsEffective = None;
self.tsExpire = None;
self.uidAuthor = None;
self.sName = None;
self.sDescription = None;
self.fEnabled = None;
self.enmScheduler = SchedGroupData.ksScheduler_BestEffortContinousItegration;
self.idBuildSrc = None;
self.idBuildSrcTestSuite = None;
def initFromDbRow(self, aoRow):
"""
Re-initializes the data with a row from a SELECT * FROM SchedGroups.
Returns self. Raises exception if the row is None or otherwise invalid.
"""
if aoRow is None:
raise TMExceptionBase('SchedGroup not found.');
self.idSchedGroup = aoRow[0];
self.tsEffective = aoRow[1];
self.tsExpire = aoRow[2];
self.uidAuthor = aoRow[3];
self.sName = aoRow[4];
self.sDescription = aoRow[5];
self.fEnabled = aoRow[6];
self.enmScheduler = aoRow[7];
self.idBuildSrc = aoRow[8];
self.idBuildSrcTestSuite = aoRow[9];
return self;
def initFromDbWithId(self, oDb, idSchedGroup, tsNow = None, sPeriodBack = None):
"""
Initialize the object from the database.
"""
oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
'SELECT *\n'
'FROM SchedGroups\n'
'WHERE idSchedGroup = %s\n'
, ( idSchedGroup,), tsNow, sPeriodBack));
aoRow = oDb.fetchOne()
if aoRow is None:
raise TMExceptionBase('idSchedGroup=%s not found (tsNow=%s, sPeriodBack=%s)' % (idSchedGroup, tsNow, sPeriodBack));
return self.initFromDbRow(aoRow);
class SchedGroupDataEx(SchedGroupData):
"""
Extended scheduling group data.
Note! Similar to TestGroupDataEx.
"""
ksParam_aoMembers = 'SchedGroup_aoMembers';
kasAltArrayNull = [ 'aoMembers', ];
## Helper parameter containing the comma separated list with the IDs of
# potential members found in the parameters.
ksParam_aidTestGroups = 'TestGroupDataEx_aidTestGroups';
def __init__(self):
SchedGroupData.__init__(self);
self.aoMembers = []; # SchedGroupMemberDataEx.
# Two build sources for convenience sake.
self.oBuildSrc = None;
self.oBuildSrcValidationKit = None;
# List of test boxes that uses this group for convenience.
self.aoTestBoxes = None;
def _initExtraMembersFromDb(self, oDb, tsNow = None, sPeriodBack = None):
"""
Worker shared by the initFromDb* methods.
Returns self. Raises exception if no row or database error.
"""
#
# It all upfront so the object has some kind of consistency if anything
# below raises exceptions.
#
self.oBuildSrc = None;
self.oBuildSrcValidationKit = None;
self.aoTestBoxes = [];
self.aoMembers = [];
#
# Build source.
#
if self.idBuildSrc:
self.oBuildSrc = BuildSourceData().initFromDbWithId(oDb, self.idBuildSrc, tsNow, sPeriodBack);
if self.idBuildSrcTestSuite:
self.oBuildSrcValidationKit = BuildSourceData().initFromDbWithId(oDb, self.idBuildSrcTestSuite,
tsNow, sPeriodBack);
#
# Test Boxes.
#
## @todo sPeriodBack!
if tsNow is None:
oDb.execute('SELECT *\n'
'FROM TestBoxes\n'
'WHERE TestBoxes.idSchedGroup = %s\n'
' AND TestBoxes.tsExpire = \'infinity\'::TIMESTAMP\n'
'ORDER BY TestBoxes.sName, TestBoxes.idTestBox\n'
, (self.idSchedGroup,));
else:
oDb.execute('SELECT *\n'
'FROM TestBoxes\n'
'WHERE TestBoxes.idSchedGroup = %s\n'
' AND TestBoxes.tsExpire > %s\n'
' AND TestBoxes.tsEffective <= %s\n'
'ORDER BY TestBoxes.sName, TestBoxes.idTestBox\n'
, (self.idSchedGroup, tsNow, tsNow, tsNow, tsNow));
for aoRow in oDb.fetchAll():
self.aoTestBoxes.append(TestBoxData().initFromDbRow(aoRow));
#
# Test groups.
#
## @todo sPeriodBack!
if tsNow is None:
oDb.execute('SELECT SchedGroupMembers.*, TestGroups.*\n'
'FROM SchedGroupMembers\n'
'LEFT OUTER JOIN TestGroups ON (SchedGroupMembers.idTestGroup = TestGroups.idTestGroup)\n'
'WHERE SchedGroupMembers.idSchedGroup = %s\n'
' AND SchedGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
' AND TestGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
'ORDER BY SchedGroupMembers.idTestGroupPreReq, SchedGroupMembers.idTestGroup\n'
, (self.idSchedGroup,));
else:
oDb.execute('SELECT SchedGroupMembers.*, TestGroups.*\n'
'FROM SchedGroupMembers\n'
'LEFT OUTER JOIN TestGroups ON (SchedGroupMembers.idTestGroup = TestGroups.idTestGroup)\n'
'WHERE SchedGroupMembers.idSchedGroup = %s\n'
' AND SchedGroupMembers.tsExpire > %s\n'
' AND SchedGroupMembers.tsEffective <= %s\n'
' AND TestGroups.tsExpire > %s\n'
' AND TestGroups.tsEffective <= %s\n'
'ORDER BY SchedGroupMembers.idTestGroupPreReq, SchedGroupMembers.idTestGroup\n'
, (self.idSchedGroup, tsNow, tsNow, tsNow, tsNow));
for aoRow in oDb.fetchAll():
self.aoMembers.append(SchedGroupMemberDataEx().initFromDbRow(aoRow));
return self;
def initFromDbRowEx(self, aoRow, oDb, tsNow = None):
"""
Reinitialize from a SELECT * FROM SchedGroups row. Will query the
necessary additional data from oDb using tsNow.
Returns self. Raises exception if no row or database error.
"""
SchedGroupData.initFromDbRow(self, aoRow);
return self._initExtraMembersFromDb(oDb, tsNow);
def initFromDbWithId(self, oDb, idSchedGroup, tsNow = None, sPeriodBack = None):
"""
Initialize the object from the database.
"""
SchedGroupData.initFromDbWithId(self, oDb, idSchedGroup, tsNow, sPeriodBack);
return self._initExtraMembersFromDb(oDb, tsNow, sPeriodBack);
def getDataAttributes(self):
asAttributes = SchedGroupData.getDataAttributes(self);
asAttributes.remove('oBuildSrc');
asAttributes.remove('oBuildSrcValidationKit');
asAttributes.remove('aoTestBoxes');
return asAttributes;
def getAttributeParamNullValues(self, sAttr):
if sAttr != 'aoMembers':
return SchedGroupData.getAttributeParamNullValues(self, sAttr);
return ['', [], None];
def convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict):
if sAttr != 'aoMembers':
return SchedGroupData.convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict);
aoNewValue = [];
aidSelected = oDisp.getListOfIntParams(sParam, iMin = 1, iMax = 0x7ffffffe, aiDefaults = [])
sIds = oDisp.getStringParam(self.ksParam_aidTestGroups, sDefault = '');
for idTestGroup in sIds.split(','):
try: idTestGroup = int(idTestGroup);
except: pass;
oDispWrapper = self.DispWrapper(oDisp, '%s[%s][%%s]' % (SchedGroupDataEx.ksParam_aoMembers, idTestGroup,))
oMember = SchedGroupMemberDataEx().initFromParams(oDispWrapper, fStrict = False);
if idTestGroup in aidSelected:
aoNewValue.append(oMember);
return aoNewValue;
def _validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb):
if sAttr != 'aoMembers':
return SchedGroupData._validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb);
asErrors = [];
aoNewMembers = [];
for oOldMember in oValue:
oNewMember = SchedGroupMemberDataEx().initFromOther(oOldMember);
aoNewMembers.append(oNewMember);
dErrors = oNewMember.validateAndConvert(oDb);
if len(dErrors) > 0:
asErrors.append(str(dErrors));
if len(asErrors) == 0:
for i in range(len(aoNewMembers)):
idTestGroup = aoNewMembers[i];
for j in range(i + 1, len(aoNewMembers)):
if aoNewMembers[j].idTestGroup == idTestGroup:
asErrors.append('Duplicate test group #%d!' % (idTestGroup, ));
break;
return (aoNewMembers, None if len(asErrors) == 0 else '<br>\n'.join(asErrors));
def _validateAndConvertWorker(self, asAllowNullAttributes, oDb):
dErrors = SchedGroupData._validateAndConvertWorker(self, asAllowNullAttributes, oDb);
#
# Fetch the extended build source bits.
#
if self.ksParam_idBuildSrc not in dErrors:
if self.idBuildSrc in self.getAttributeParamNullValues('idBuildSrc') \
or self.idBuildSrc is None:
self.oBuildSrc = None;
else:
try:
self.oBuildSrc = BuildSourceData().initFromDbWithId(oDb, self.idBuildSrc);
except Exception as oXcpt:
self.oBuildSrc = BuildSourceData();
dErrors[self.ksParam_idBuildSrc] = str(oXcpt);
if self.ksParam_idBuildSrcTestSuite not in dErrors:
if self.idBuildSrcTestSuite in self.getAttributeParamNullValues('idBuildSrcTestSuite') \
or self.idBuildSrcTestSuite is None:
self.oBuildSrcValidationKit = None;
else:
try:
self.oBuildSrcValidationKit = BuildSourceData().initFromDbWithId(oDb, self.idBuildSrcTestSuite);
except Exception as oXcpt:
self.oBuildSrcValidationKit = BuildSourceData();
dErrors[self.ksParam_idBuildSrcTestSuite] = str(oXcpt);
return dErrors;
class SchedGroupLogic(ModelLogicBase): # pylint: disable=R0903
"""
SchedGroup logic.
"""
#
# Standard methods.
#
def fetchForListing(self, iStart, cMaxRows, tsNow):
"""
Fetches build sources.
Returns an array (list) of BuildSourceData items, empty list if none.
Raises exception on error.
"""
if tsNow is None:
self._oDb.execute('SELECT *\n'
'FROM SchedGroups\n'
'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
'ORDER BY idSchedGroup DESC\n'
'LIMIT %s OFFSET %s\n'
, (cMaxRows, iStart,));
else:
self._oDb.execute('SELECT *\n'
'FROM SchedGroups\n'
'WHERE tsExpire > %s\n'
' AND tsEffective <= %s\n'
'ORDER BY idSchedGroup DESC\n'
'LIMIT %s OFFSET %s\n'
, (tsNow, tsNow, cMaxRows, iStart,));
aoRet = [];
for aoRow in self._oDb.fetchAll():
aoRet.append(SchedGroupDataEx().initFromDbRowEx(aoRow, self._oDb, tsNow));
return aoRet;
def addEntry(self, oData, uidAuthor, fCommit = False):
"""Add Scheduling Group record"""
#
# Validate.
#
dDataErrors = oData.validateAndConvert(self._oDb);
if len(dDataErrors) > 0:
raise TMExceptionBase('Invalid data passed to addEntry: %s' % (dDataErrors,));
if self.exists(oData.sName):
raise TMExceptionBase('Scheduling group "%s" already exists.' % (oData.sName,));
#
# Add it.
#
self._oDb.execute('INSERT INTO SchedGroups (\n'
' uidAuthor,\n'
' sName,\n'
' sDescription,\n'
' fEnabled,\n'
' enmScheduler,\n'
' idBuildSrc,\n'
' idBuildSrcTestSuite)\n'
'VALUES (%s, %s, %s, %s, %s, %s, %s)\n'
'RETURNING idSchedGroup\n'
, (uidAuthor,
oData.sName,
oData.sDescription,
oData.fEnabled,
oData.enmScheduler,
oData.idBuildSrc,
oData.idBuildSrcTestSuite));
idSchedGroup = self._oDb.fetchOne()[0];
oData.idSchedGroup = idSchedGroup;
for oMember in oData.aoMembers:
oMember.idSchedGroup = idSchedGroup;
self._addSchedGroupMember(uidAuthor, oMember);
self._oDb.maybeCommit(fCommit);
return True;
def editEntry(self, oData, uidAuthor, fCommit = False):
"""Edit Scheduling Group record"""
#
# Validate input and retrieve the old data.
#
dErrors = oData.validateAndConvert(self._oDb);
if len(dErrors) > 0:
raise TMExceptionBase('editEntry got invalid data: %s' % (dErrors,));
self._assertUnique(oData.sName, oData.idSchedGroup);
oOldData = SchedGroupDataEx().initFromDbWithId(self._oDb, oData.idSchedGroup);
#
# Make the changes.
#
if not oData.isEqualEx(oOldData, [ 'tsEffective', 'tsExpire', 'uidAuthor', 'aoMembers', 'aoTestBoxes',
'oBuildSrc', 'oBuildSrcValidationKit', ]):
self._historizeEntry(oData.idSchedGroup);
self._readdEntry(uidAuthor, oData);
# Remove groups.
for oOld in oOldData.aoMembers:
fRemove = True;
for oNew in oData.aoMembers:
if oNew.idTestGroup == oOld.idTestGroup:
fRemove = False;
break;
if fRemove:
self._removeSchedGroupMember(uidAuthor, oOld);
# Add / modify groups.
for oMember in oData.aoMembers:
oOldMember = None;
for oOld in oOldData.aoMembers:
if oOld.idTestGroup == oMember.idTestGroup:
oOldMember = oOld;
break;
oMember.idSchedGroup = oData.idSchedGroup;
if oOldMember is None:
self._addSchedGroupMember(uidAuthor, oMember);
elif not oMember.isEqualEx(oOldMember, ['tsEffective', 'tsExpire', 'uidAuthor', 'oTestGroup']):
self._historizeSchedGroupMember(oMember);
self._addSchedGroupMember(uidAuthor, oMember);
self._oDb.maybeCommit(fCommit);
return True;
def removeEntry(self, uidAuthor, idSchedGroup, fCascade = False, fCommit = False):
"""
Deletes a scheduling group.
"""
#
# Input validation and retrival of current data.
#
if idSchedGroup == 1:
raise TMExceptionBase('Cannot remove the default scheduling group (id 1).');
oData = SchedGroupDataEx().initFromDbWithId(self._oDb, idSchedGroup);
#
# We use cascade a little different here... We don't actually delete
# associated testboxes or testgroups.
#
if len(oData.aoTestBoxes) > 0:
if fCascade is not True:
# Complain about there being associated testboxes.
asTestBoxes = ['%s (#%d)' % (oTestBox.sName, oTestBox.idTestBox) for oTestBox in oData.aoTestBoxes];
raise TMExceptionBase('Scheduling group #%d is associated with one or more test boxes: %s'
% (idSchedGroup, ', '.join(asTestBoxes),));
else:
# Reassign testboxes to scheduling group #1 (the default group).
oTbLogic = TestBoxLogic(self._oDb);
for oTestBox in oData.aoTestBoxes:
oTbCopy = TestBoxData().initFromOther(oTestBox);
oTbCopy.idSchedGroup = 1;
oTbLogic.editEntry(oTbCopy, uidAuthor, fCommit = False);
oData = SchedGroupDataEx().initFromDbWithId(self._oDb, idSchedGroup);
if len(oData.aoTestBoxes) != 0:
raise TMExceptionBase('More testboxes was added to the scheduling group as we were trying to delete it.');
#
# Remove the group and all member records.
#
for oMember in oData.aoMembers:
self._removeSchedGroupMember(uidAuthor, oMember);
self._oDb.execute('UPDATE SchedGroupMembers\n'
'SET tsExpire = CURRENT_TIMESTAMP\n'
'WHERE idSchedGroup = %s\n'
' AND tsExpire = \'infinity\'::TIMESTAMP\n'
, (idSchedGroup,));
(tsCur, tsCurMinusOne) = self._oDb.getCurrentTimestamps();
if oData.tsEffective != tsCur and oData.tsEffective != tsCurMinusOne:
self._historizeEntry(idSchedGroup, tsCurMinusOne);
self._readdEntry(uidAuthor, oData, tsCurMinusOne);
self._historizeEntry(idSchedGroup);
self._oDb.execute('UPDATE SchedGroups\n'
'SET tsExpire = CURRENT_TIMESTAMP\n'
'WHERE idSchedGroup = %s\n'
' AND tsExpire = \'infinity\'::TIMESTAMP\n'
, (idSchedGroup,))
self._oDb.maybeCommit(fCommit)
return True;
#
# Other methods.
#
def fetchOrderedByName(self, tsNow = None):
"""
Return list of objects of type SchedGroups ordered by name.
May raise exception on database error.
"""
if tsNow is None:
self._oDb.execute('SELECT *\n'
'FROM SchedGroups\n'
'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
'ORDER BY sName ASC\n');
else:
self._oDb.execute('SELECT *\n'
'FROM SchedGroups\n'
'WHERE tsExpire > %s\n'
' AND tsEffective <= %s\n'
'ORDER BY sName ASC\n'
, (tsNow, tsNow,));
aoRet = []
for _ in range(self._oDb.getRowCount()):
aoRet.append(SchedGroupData().initFromDbRow(self._oDb.fetchOne()));
return aoRet;
def getAll(self, tsEffective = None):
"""
Gets the list of all scheduling groups.
Returns an array of SchedGroupData instances.
"""
if tsEffective is None:
self._oDb.execute('SELECT *\n'
'FROM SchedGroups\n'
'WHERE tsExpire = \'infinity\'::TIMESTAMP\n');
else:
self._oDb.execute('SELECT *\n'
'FROM SchedGroups\n'
'WHERE tsExpire > %s\n'
' AND tsEffective <= %s\n'
, (tsEffective, tsEffective));
aoRet = [];
for aoRow in self._oDb.fetchAll():
aoRet.append(SchedGroupData().initFromDbRow(aoRow));
return aoRet;
def getSchedGroupsForCombo(self, tsEffective = None):
"""
Gets the list of active scheduling groups for a combo box.
Returns an array of (value [idSchedGroup], drop-down-name [sName],
hover-text [sDescription]) tuples.
"""
if tsEffective is None:
self._oDb.execute('SELECT idSchedGroup, sName, sDescription\n'
'FROM SchedGroups\n'
'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
'ORDER BY sName');
else:
self._oDb.execute('SELECT idSchedGroup, sName, sDescription\n'
'FROM SchedGroups\n'
'WHERE tsExpire > %s\n'
' AND tsEffective <= %s\n'
'ORDER BY sName'
, (tsEffective, tsEffective));
return self._oDb.fetchAll();
def getMembers(self, idSchedGroup, tsEffective = None):
"""
Gets the scheduling groups members for the given scheduling group.
Returns an array of SchedGroupMemberDataEx instances (sorted by
priority and idTestGroup). May raise exception DB error.
"""
if tsEffective is None:
self._oDb.execute('SELECT *\n'
'FROM SchedGroupMembers, TestGroups\n'
'WHERE SchedGroupMembers.idSchedGroup = %s\n'
' AND SchedGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
' AND TestGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
'ORDER BY SchedGroupMembers.iSchedPriority, SchedGroupMembers.idTestGroup\n'
, (idSchedGroup,));
else:
self._oDb.execute('SELECT *\n'
'FROM SchedGroupMembers, TestGroups\n'
'WHERE SchedGroupMembers.idSchedGroup = %s\n'
' AND SchedGroupMembers.tsExpire < %s\n'
' AND SchedGroupMembers.tsEffective >= %s\n'
' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
' AND TestGroups.tsExpire < %s\n'
' AND TestGroups.tsEffective >= %s\n'
'ORDER BY SchedGroupMembers.iSchedPriority DESC, SchedGroupMembers.idTestGroup\n'
, (idSchedGroup, tsEffective, tsEffective, tsEffective, tsEffective, ));
aaoRows = self._oDb.fetchAll();
aoRet = [];
for aoRow in aaoRows:
aoRet.append(SchedGroupMemberDataEx().initFromDbRow(aoRow));
return aoRet;
def getTestCasesForGroup(self, idSchedGroup, cMax = None):
"""
Gets the enabled testcases w/ testgroup+priority for the given scheduling group.
Returns an array TestCaseData instance (group id, testcase priority and
testcase ids) with an extra iSchedPriority member.
May raise exception on DB error or if the result exceeds cMax.
"""
self._oDb.execute('SELECT TestGroupMembers.idTestGroup, TestGroupMembers.iSchedPriority, TestCases.*\n'
'FROM SchedGroupMembers, TestGroups, TestGroupMembers, TestCases\n'
'WHERE SchedGroupMembers.idSchedGroup = %s\n'
' AND SchedGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
' AND TestGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
' AND TestGroupMembers.idTestGroup = TestGroups.idTestGroup\n'
' AND TestGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
' AND TestCases.idTestCase = TestGroupMembers.idTestCase\n'
' AND TestCases.tsExpire = \'infinity\'::TIMESTAMP\n'
' AND TestCases.fEnabled = TRUE\n'
'ORDER BY TestGroupMembers.idTestGroup, TestGroupMembers.iSchedPriority, TestCases.idTestCase\n'
, (idSchedGroup,));
if cMax is not None and self._oDb.getRowCount() > cMax:
raise TMExceptionBase('Too many testcases for scheduling group %s: %s, max %s'
% (idSchedGroup, cMax, self._oDb.getRowCount(),));
aoRet = [];
for aoRow in self._oDb.fetchAll():
oTestCase = TestCaseData().initFromDbRow(aoRow[2:]);
oTestCase.idTestGroup = aoRow[0];
oTestCase.iSchedPriority = aoRow[1];
aoRet.append(oTestCase);
return aoRet;
def getTestCaseArgsForGroup(self, idSchedGroup, cMax = None):
"""
Gets the testcase argument variation w/ testgroup+priority for the given scheduling group.
Returns an array TestCaseArgsData instance (sorted by group and
variation id) with an extra iSchedPriority member.
May raise exception on DB error or if the result exceeds cMax.
"""
self._oDb.execute('SELECT TestGroupMembers.idTestGroup, TestGroupMembers.iSchedPriority, TestCaseArgs.*\n'
'FROM SchedGroupMembers, TestGroups, TestGroupMembers, TestCaseArgs, TestCases\n'
'WHERE SchedGroupMembers.idSchedGroup = %s\n'
' AND SchedGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
' AND TestGroups.idTestGroup = SchedGroupMembers.idTestGroup\n'
' AND TestGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
' AND TestGroupMembers.idTestGroup = TestGroups.idTestGroup\n'
' AND TestGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
' AND TestCaseArgs.idTestCase = TestGroupMembers.idTestCase\n'
' AND TestCaseArgs.tsExpire = \'infinity\'::TIMESTAMP\n'
' AND ( TestGroupMembers.aidTestCaseArgs is NULL\n'
' OR TestCaseArgs.idTestCaseArgs = ANY(TestGroupMembers.aidTestCaseArgs) )\n'
' AND TestCases.idTestCase = TestCaseArgs.idTestCase\n'
' AND TestCases.tsExpire = \'infinity\'::TIMESTAMP\n'
' AND TestCases.fEnabled = TRUE\n'
'ORDER BY TestGroupMembers.idTestGroup, TestGroupMembers.idTestCase, TestCaseArgs.idTestCaseArgs\n'
, (idSchedGroup,));
if cMax is not None and self._oDb.getRowCount() > cMax:
raise TMExceptionBase('Too many argument variations for scheduling group %s: %s, max %s'
% (idSchedGroup, cMax, self._oDb.getRowCount(),));
aoRet = [];
for aoRow in self._oDb.fetchAll():
oVariation = TestCaseArgsData().initFromDbRow(aoRow[2:]);
oVariation.idTestGroup = aoRow[0];
oVariation.iSchedPriority = aoRow[1];
aoRet.append(oVariation);
return aoRet;
def exists(self, sName):
"""Checks if a group with the given name exists."""
self._oDb.execute('SELECT *\n'
'FROM SchedGroups\n'
'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
' AND sName = %s\n'
, (sName,));
return self._oDb.getRowCount() > 0;
def getById(self, idSchedGroup):
"""Get Scheduling Group data by idSchedGroup"""
self._oDb.execute('SELECT *\n'
'FROM SchedGroups\n'
'WHERE tsExpire = \'infinity\'::timestamp\n'
' AND idSchedGroup = %s;', (idSchedGroup,))
aRows = self._oDb.fetchAll()
if len(aRows) not in (0, 1):
raise self._oDb.integrityException(
'Found more than one scheduling groups with the same credentials. Database structure is corrupted.')
try:
return SchedGroupData().initFromDbRow(aRows[0])
except IndexError:
return None
def remove(self, uidAuthor, idScedGroup, fNeedCommit=True):
"""Historize Scheduling Group record"""
self._oDb.execute('UPDATE SchedGroups\n'
'SET tsExpire = CURRENT_TIMESTAMP,\n'
' uidAuthor = %s\n'
'WHERE idSchedGroup = %s\n'
' AND tsExpire = \'infinity\'::TIMESTAMP\n',
(uidAuthor, idScedGroup))
if fNeedCommit:
self._oDb.commit()
return True
#
# Internal helpers.
#
def _assertUnique(self, sName, idSchedGroupIgnore = None):
"""
Checks that the scheduling group name is unique.
Raises exception if the name is already in use.
"""
if idSchedGroupIgnore is None:
self._oDb.execute('SELECT idSchedGroup\n'
'FROM SchedGroups\n'
'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
' AND sName = %s\n'
, ( sName, ) );
else:
self._oDb.execute('SELECT idSchedGroup\n'
'FROM SchedGroups\n'
'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
' AND sName = %s\n'
' AND idSchedGroup <> %s\n'
, ( sName, idSchedGroupIgnore, ) );
if self._oDb.getRowCount() > 0:
raise TMExceptionBase('Scheduling group name (%s) is already in use.' % (sName,));
return True;
def _readdEntry(self, uidAuthor, oData, tsEffective = None):
"""
Re-adds the SchedGroups entry. Used by editEntry and removeEntry.
"""
if tsEffective is None:
tsEffective = self._oDb.getCurrentTimestamp();
self._oDb.execute('INSERT INTO SchedGroups (\n'
' uidAuthor,\n'
' tsEffective,\n'
' idSchedGroup,\n'
' sName,\n'
' sDescription,\n'
' fEnabled,\n'
' enmScheduler,\n'
' idBuildSrc,\n'
' idBuildSrcTestSuite )\n'
'VALUES ( %s, %s, %s, %s, %s, %s, %s, %s, %s )\n'
, ( uidAuthor,
tsEffective,
oData.idSchedGroup,
oData.sName,
oData.sDescription,
oData.fEnabled,
oData.enmScheduler,
oData.idBuildSrc,
oData.idBuildSrcTestSuite, ));
return True;
def _historizeEntry(self, idSchedGroup, tsExpire = None):
"""
Historizes the current entry for the given scheduling group.
"""
if tsExpire is None:
tsExpire = self._oDb.getCurrentTimestamp();
self._oDb.execute('UPDATE SchedGroups\n'
'SET tsExpire = %s\n'
'WHERE idSchedGroup = %s\n'
' AND tsExpire = \'infinity\'::TIMESTAMP\n'
, ( tsExpire, idSchedGroup, ));
return True;
def _addSchedGroupMember(self, uidAuthor, oMember, tsEffective = None):
"""
addEntry worker for adding a scheduling group member.
"""
if tsEffective is None:
tsEffective = self._oDb.getCurrentTimestamp();
self._oDb.execute('INSERT INTO SchedGroupMembers(\n'
' idSchedGroup,\n'
' idTestGroup,\n'
' tsEffective,\n'
' uidAuthor,\n'
' iSchedPriority,\n'
' bmHourlySchedule,\n'
' idTestGroupPreReq)\n'
'VALUES (%s, %s, %s, %s, %s, %s, %s)\n'
, ( oMember.idSchedGroup,
oMember.idTestGroup,
tsEffective,
uidAuthor,
oMember.iSchedPriority,
oMember.bmHourlySchedule,
oMember.idTestGroupPreReq, ));
return True;
def _removeSchedGroupMember(self, uidAuthor, oMember):
"""
Removes a scheduling group member.
"""
# Try record who removed it by adding an dummy entry that expires immediately.
(tsCur, tsCurMinusOne) = self._oDb.getCurrentTimestamps();
if oMember.tsEffective != tsCur and oMember.tsEffective != tsCurMinusOne:
self._historizeSchedGroupMember(oMember, tsCurMinusOne);
self._addSchedGroupMember(uidAuthor, oMember, tsCurMinusOne); # lazy bird.
self._historizeSchedGroupMember(oMember);
else:
self._historizeSchedGroupMember(oMember);
return True;
def _historizeSchedGroupMember(self, oMember, tsExpire = None):
"""
Historizes the current entry for the given scheduling group.
"""
if tsExpire is None:
tsExpire = self._oDb.getCurrentTimestamp();
self._oDb.execute('UPDATE SchedGroupMembers\n'
'SET tsExpire = %s\n'
'WHERE idSchedGroup = %s\n'
' AND idTestGroup = %s\n'
' AND tsExpire = \'infinity\'::TIMESTAMP\n'
, ( tsExpire, oMember.idSchedGroup, oMember.idTestGroup, ));
return True;
#
# Unit testing.
#
# pylint: disable=C0111
class SchedGroupMemberDataTestCase(ModelDataBaseTestCase):
def setUp(self):
self.aoSamples = [SchedGroupMemberData(),];
class SchedGroupDataTestCase(ModelDataBaseTestCase):
def setUp(self):
self.aoSamples = [SchedGroupData(),];
if __name__ == '__main__':
unittest.main();
# not reached.