VirtualBox

Changeset 83384 in vbox


Ignore:
Timestamp:
Mar 24, 2020 2:46:06 PM (5 years ago)
Author:
vboxsync
Message:

TestManager/SchedQueue: Fixed various validation issues with the testbox-in-sched-group stuff. Added change log to the scheduling queue details page.

Location:
trunk/src/VBox/ValidationKit/testmanager/core
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/ValidationKit/testmanager/core/base.py

    r83359 r83384  
    404404                                              enmValidateFor = enmValidateFor);
    405405
     406    def validateAndConvertEx(self, asAllowNullAttributes, oDb, enmValidateFor = ksValidateFor_Other):
     407        """
     408        Same as validateAndConvert but with custom allow-null list.
     409        """
     410        return self._validateAndConvertWorker(asAllowNullAttributes, oDb, enmValidateFor = enmValidateFor);
     411
    406412    def convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict):
    407413        """
  • trunk/src/VBox/ValidationKit/testmanager/core/schedgroup.py

    r83364 r83384  
    3535# Validation Kit imports.
    3636from testmanager.core.base          import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMExceptionBase, \
    37                                            TMRowInUse, TMInvalidData, TMRowAlreadyExists, TMRowNotFound;
     37                                           TMRowInUse, TMInvalidData, TMRowAlreadyExists, TMRowNotFound, \
     38                                           ChangeLogEntry, AttributeChangeEntry, AttributeChangeEntryPre;
    3839from testmanager.core.buildsource   import BuildSourceData;
     40from testmanager.core               import db;
    3941from testmanager.core.testcase      import TestCaseData;
    4042from testmanager.core.testcaseargs  import TestCaseArgsData;
    4143from testmanager.core.testbox       import TestBoxLogic, TestBoxDataForSchedGroup;
    4244from testmanager.core.testgroup     import TestGroupData;
     45from testmanager.core.useraccount   import UserAccountLogic;
    4346
    4447
     
    144147                dErrors[self.ksParam_idTestGroup] = str(oXcpt);
    145148        return dErrors;
     149
    146150
    147151
     
    300304        #
    301305        # Test groups.
     306        # The fetchForChangeLog method makes ASSUMPTIONS about sorting!
    302307        #
    303308        oDb.execute('SELECT SchedGroupMembers.*, TestGroups.*\n'
     
    307312                    + self.formatSimpleNowAndPeriod(oDb, tsNow, sPeriodBack, sTablePrefix = 'SchedGroupMembers.')
    308313                    + self.formatSimpleNowAndPeriod(oDb, tsNow, sPeriodBack, sTablePrefix = 'TestGroups.') +
    309                     'ORDER BY SchedGroupMembers.idTestGroupPreReq, SchedGroupMembers.idTestGroup\n'
     314                    'ORDER BY SchedGroupMembers.idTestGroupPreReq ASC NULLS FIRST,\n'
     315                    '         TestGroups.sName,\n'
     316                    '         SchedGroupMembers.idTestGroup\n'
    310317                    , (self.idSchedGroup,));
    311318        for aoRow in oDb.fetchAll():
     
    351358                oMember = SchedGroupMemberDataEx().initFromParams(oDispWrapper, fStrict = False);
    352359                if idTestGroup in aidSelected:
     360                    oMember.idTestGroup = idTestGroup;
    353361                    aoNewValue.append(oMember);
    354362        elif sAttr == 'aoTestBoxes':
     
    359367                except: pass;
    360368                oDispWrapper = self.DispWrapper(oDisp, '%s[%s][%%s]' % (SchedGroupDataEx.ksParam_aoTestBoxes, idTestBox,))
    361                 oTestBox = TestBoxDataForSchedGroup().initFromParams(oDispWrapper, fStrict = False);
     369                oBoxInGrp = TestBoxDataForSchedGroup().initFromParams(oDispWrapper, fStrict = False);
    362370                if idTestBox in aidSelected:
    363                     aoNewValue.append(oTestBox);
     371                    oBoxInGrp.idTestBox = idTestBox;
     372                    aoNewValue.append(oBoxInGrp);
    364373        else:
    365374            return SchedGroupData.convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict);
     
    370379            return SchedGroupData._validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb);
    371380
     381        if oValue in aoNilValues:
     382            return ([], None);
     383
    372384        asErrors     = [];
    373385        aoNewMembers = [];
    374386        if sAttr == 'aoMembers':
     387            asAllowNulls = ['bmHourlySchedule', 'idTestGroupPreReq', 'tsEffective', 'tsExpire', 'uidAuthor', ];
     388            if self.idSchedGroup in [None, '-1', -1]:
     389                asAllowNulls.append('idSchedGroup'); # Probably new group, so allow null scheduling group.
     390
    375391            for oOldMember in oValue:
    376392                oNewMember = SchedGroupMemberDataEx().initFromOther(oOldMember);
    377393                aoNewMembers.append(oNewMember);
    378394
    379                 dErrors = oNewMember.validateAndConvert(oDb, ModelDataBase.ksValidateFor_Other);
     395                dErrors = oNewMember.validateAndConvertEx(asAllowNulls, oDb, ModelDataBase.ksValidateFor_Other);
    380396                if dErrors:
    381397                    asErrors.append(str(dErrors));
     
    389405                            break;
    390406        else:
     407            asAllowNulls = list(TestBoxDataForSchedGroup.kasAllowNullAttributes);
     408            if self.idSchedGroup in [None, '-1', -1]:
     409                asAllowNulls.append('idSchedGroup'); # Probably new group, so allow null scheduling group.
     410
    391411            for oOldMember in oValue:
    392412                oNewMember = TestBoxDataForSchedGroup().initFromOther(oOldMember);
    393413                aoNewMembers.append(oNewMember);
    394414
    395                 dErrors = oNewMember.validateAndConvert(oDb, ModelDataBase.ksValidateFor_Other);
     415                dErrors = oNewMember.validateAndConvertEx(asAllowNulls, oDb, ModelDataBase.ksValidateFor_Other);
    396416                if dErrors:
    397417                    asErrors.append(str(dErrors));
     
    402422                    for j in range(i + 1, len(aoNewMembers)):
    403423                        if aoNewMembers[j].idTestBox == idTestBox:
    404                             asErrors.append('Duplicate test group #%d!' % (idTestBox, ));
     424                            asErrors.append('Duplicate test box #%d!' % (idTestBox, ));
    405425                            break;
    406 
    407426
    408427        return (aoNewMembers, None if not asErrors else '<br>\n'.join(asErrors));
     
    482501            aoRet.append(SchedGroupDataEx().initFromDbRowEx(aoRow, self._oDb, tsNow));
    483502        return aoRet;
     503
     504    def fetchForChangeLog(self, idSchedGroup, iStart, cMaxRows, tsNow): # pylint: disable=too-many-locals
     505        """
     506        Fetches change log entries for a scheduling group.
     507
     508        Returns an array of ChangeLogEntry instance and an indicator whether
     509        there are more entries.
     510        Raises exception on error.
     511        """
     512
     513        ## @todo calc changes to scheduler group!
     514
     515        if tsNow is None:
     516            tsNow = self._oDb.getCurrentTimestamp();
     517
     518        #
     519        # First gather the change log timeline using the effective dates.
     520        # (ASSUMES that we'll always have a separate delete entry, rather
     521        # than just setting tsExpire.)
     522        #
     523        self._oDb.execute('''
     524(
     525SELECT      tsEffective,
     526            uidAuthor
     527FROM        SchedGroups
     528WHERE       idSchedGroup = %s
     529        AND tsEffective <= %s
     530ORDER BY    tsEffective DESC
     531) UNION (
     532SELECT      tsEffective,
     533            uidAuthor
     534FROM        SchedGroupMembers
     535WHERE       idSchedGroup = %s
     536        AND tsEffective <= %s
     537ORDER BY    tsEffective DESC
     538) UNION (
     539SELECT      tsEffective,
     540            uidAuthor
     541FROM        TestBoxesInSchedGroups
     542WHERE       idSchedGroup = %s
     543        AND tsEffective <= %s
     544ORDER BY    tsEffective DESC
     545)
     546ORDER BY    tsEffective DESC
     547LIMIT %s OFFSET %s
     548''', (idSchedGroup, tsNow, idSchedGroup, tsNow, idSchedGroup, tsNow, cMaxRows + 1, iStart, ));
     549
     550        aoEntries = [] # type: list[ChangeLogEntry]
     551        tsPrevious = tsNow;
     552        for aoDbRow in self._oDb.fetchAll():
     553            (tsEffective, uidAuthor) = aoDbRow;
     554            aoEntries.append(ChangeLogEntry(uidAuthor, None, tsEffective, tsPrevious, None, None, []));
     555            tsPrevious = db.dbTimestampPlusOneTick(tsEffective);
     556
     557        if True:
     558            #
     559            # Fetch data for each for each change log entry point.
     560            #
     561            # We add one tick to the timestamp here to skip past delete records
     562            # that only there to record the user doing the deletion.
     563            #
     564            for iEntry, oEntry in enumerate(aoEntries):
     565                oEntry.oNewRaw = SchedGroupDataEx().initFromDbWithId(self._oDb, idSchedGroup,
     566                                                                     db.dbTimestampPlusOneTick(oEntry.tsEffective));
     567                if iEntry > 0:
     568                    aoEntries[iEntry - 1].oOldRaw = oEntry.oNewRaw;
     569
     570            # Chop off the +1 entry, if any.
     571            fMore = len(aoEntries) > cMaxRows;
     572            if fMore:
     573                aoEntries = aoEntries[:-1];
     574
     575            # Figure out the changes.
     576            for oEntry in aoEntries:
     577                oOld = oEntry.oOldRaw;
     578                if not oOld:
     579                    break;
     580                oNew      = oEntry.oNewRaw;
     581                aoChanges = oEntry.aoChanges;
     582                for sAttr in oNew.getDataAttributes():
     583                    if sAttr in [ 'tsEffective', 'tsExpire', 'uidAuthor', ]:
     584                        continue;
     585                    oOldAttr = getattr(oOld, sAttr);
     586                    oNewAttr = getattr(oNew, sAttr);
     587                    if oOldAttr == oNewAttr:
     588                        continue;
     589                    if sAttr in [ 'aoMembers', 'aoTestBoxes', ]:
     590                        iNew = 0;
     591                        iOld = 0;
     592                        asNewAttr = [];
     593                        asOldAttr = [];
     594                        if sAttr == 'aoMembers':
     595                            # ASSUMES aoMembers is sorted by idTestGroupPreReq (nulls first), oTestGroup.sName, idTestGroup!
     596                            while iNew < len(oNewAttr) and iOld < len(oOldAttr):
     597                                if oNewAttr[iNew].idTestGroup == oOldAttr[iOld].idTestGroup:
     598                                    if oNewAttr[iNew].idTestGroupPreReq != oOldAttr[iOld].idTestGroupPreReq:
     599                                        if oNewAttr[iNew].idTestGroupPreReq is None:
     600                                            asOldAttr.append('Dropped test group #%s (%s) dependency on #%s'
     601                                                             % (oNewAttr[iNew].idTestGroup, oNewAttr[iNew].oTestGroup.sName,
     602                                                                oOldAttr[iOld].idTestGroupPreReq));
     603                                        elif oOldAttr[iOld].idTestGroupPreReq is None:
     604                                            asNewAttr.append('Added test group #%s (%s) dependency on #%s'
     605                                                             % (oNewAttr[iNew].idTestGroup, oNewAttr[iNew].oTestGroup.sName,
     606                                                                oNewAttr[iOld].idTestGroupPreReq));
     607                                        else:
     608                                            asNewAttr.append('Test group #%s (%s) dependency on #%s'
     609                                                             % (oNewAttr[iNew].idTestGroup, oNewAttr[iNew].oTestGroup.sName,
     610                                                                oNewAttr[iNew].idTestGroupPreReq));
     611                                            asOldAttr.append('Test group #%s (%s) dependency on #%s'
     612                                                             % (oNewAttr[iNew].idTestGroup, oNewAttr[iNew].oTestGroup.sName,
     613                                                                oOldAttr[iOld].idTestGroupPreReq));
     614                                    if oNewAttr[iNew].iSchedPriority != oOldAttr[iOld].iSchedPriority:
     615                                        asNewAttr.append('Test group #%s (%s) priority %s'
     616                                                         % (oNewAttr[iNew].idTestGroup, oNewAttr[iNew].oTestGroup.sName,
     617                                                            oNewAttr[iNew].iSchedPriority));
     618                                        asOldAttr.append('Test group #%s (%s) priority %s'
     619                                                         % (oNewAttr[iNew].idTestGroup, oNewAttr[iNew].oTestGroup.sName,
     620                                                            oOldAttr[iOld].iSchedPriority));
     621                                    iNew += 1;
     622                                    iOld += 1;
     623                                elif oNewAttr[iNew].oTestGroup.sName      <  oOldAttr[iOld].oTestGroup.sName \
     624                                  or (    oNewAttr[iNew].oTestGroup.sName == oOldAttr[iOld].oTestGroup.sName
     625                                      and oNewAttr[iNew].idTestGroup      <  oOldAttr[iOld].idTestGroup):
     626                                    asNewAttr.append('New test group #%s - %s'
     627                                                     % (oNewAttr[iNew].idTestGroup, oNewAttr[iNew].oTestGroup.sName));
     628                                    iNew += 1;
     629                                else:
     630                                    asOldAttr.append('Removed test group #%s - %s'
     631                                                     % (oOldAttr[iOld].idTestGroup, oOldAttr[iOld].oTestGroup.sName));
     632                                    iOld += 1;
     633                            while iNew < len(oNewAttr):
     634                                asNewAttr.append('New test group #%s - %s'
     635                                                 % (oNewAttr[iNew].idTestGroup, oNewAttr[iNew].oTestGroup.sName));
     636                                iNew += 1;
     637                            while iOld < len(oOldAttr):
     638                                asOldAttr.append('Removed test group #%s - %s'
     639                                                 % (oOldAttr[iOld].idTestGroup, oOldAttr[iOld].oTestGroup.sName));
     640                                iOld += 1;
     641                        else:
     642                            dNewIds    = { oBoxInGrp.idTestBox: oBoxInGrp for oBoxInGrp in oNewAttr };
     643                            dOldIds    = { oBoxInGrp.idTestBox: oBoxInGrp for oBoxInGrp in oOldAttr };
     644                            hCommonIds = set(dNewIds.keys()) & set(dOldIds.keys());
     645                            for idTestBox in hCommonIds:
     646                                oNewBoxInGrp = dNewIds[idTestBox];
     647                                oOldBoxInGrp = dOldIds[idTestBox];
     648                                if oNewBoxInGrp.iSchedPriority != oOldBoxInGrp.iSchedPriority:
     649                                    asNewAttr.append('Test box \'%s\' (#%s) priority %s'
     650                                                     % (getattr(oNewBoxInGrp.oTestBox, 'sName', '[Partial DB]'),
     651                                                        oNewBoxInGrp.idTestBox, oNewBoxInGrp.iSchedPriority));
     652                                    asOldAttr.append('Test box \'%s\' (#%s) priority %s'
     653                                                     % (getattr(oOldBoxInGrp.oTestBox, 'sName', '[Partial DB]'),
     654                                                        oOldBoxInGrp.idTestBox, oOldBoxInGrp.iSchedPriority));
     655                            asNewAttr = sorted(asNewAttr);
     656                            asOldAttr = sorted(asOldAttr);
     657                            for idTestBox in set(dNewIds.keys()) - hCommonIds:
     658                                oNewBoxInGrp = dNewIds[idTestBox];
     659                                asNewAttr.append('New test box \'%s\' (#%s) priority %s'
     660                                                 % (getattr(oNewBoxInGrp.oTestBox, 'sName', '[Partial DB]'),
     661                                                    oNewBoxInGrp.idTestBox, oNewBoxInGrp.iSchedPriority));
     662                            for idTestBox in set(dOldIds.keys()) - hCommonIds:
     663                                oOldBoxInGrp = dOldIds[idTestBox];
     664                                asOldAttr.append('Removed test box \'%s\' (#%s) priority %s'
     665                                                 % (getattr(oOldBoxInGrp.oTestBox, 'sName', '[Partial DB]'),
     666                                                    oOldBoxInGrp.idTestBox, oOldBoxInGrp.iSchedPriority));
     667
     668                        if asNewAttr or asOldAttr:
     669                            aoChanges.append(AttributeChangeEntryPre(sAttr, oNewAttr, oOldAttr,
     670                                                                     '\n'.join(asNewAttr), '\n'.join(asOldAttr)));
     671                    else:
     672                        aoChanges.append(AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
     673
     674        else:
     675            ##
     676            ## @todo Incomplete: A more complicate apporach, probably faster though.
     677            ##
     678            def findEntry(tsEffective, iPrev = 0):
     679                """ Find entry with matching effective + expiration time """
     680                self._oDb.dprint('findEntry: iPrev=%s len(aoEntries)=%s tsEffective=%s' % (iPrev, len(aoEntries), tsEffective));
     681                while iPrev < len(aoEntries):
     682                    self._oDb.dprint('%s iPrev=%u' % (aoEntries[iPrev].tsEffective, iPrev, ));
     683                    if aoEntries[iPrev].tsEffective > tsEffective:
     684                        iPrev += 1;
     685                    elif aoEntries[iPrev].tsEffective == tsEffective:
     686                        self._oDb.dprint('hit %u' % (iPrev,));
     687                        return iPrev;
     688                    else:
     689                        break;
     690                self._oDb.dprint('%s not found!' % (tsEffective,));
     691                return -1;
     692
     693            fMore = True;
     694
     695            #
     696            # Track scheduling group changes.  Not terribly efficient for large cMaxRows
     697            # values, but not in the mood for figure out if there is any way to optimize that.
     698            #
     699            self._oDb.execute('''
     700SELECT      *
     701FROM        SchedGroups
     702WHERE       idSchedGroup = %s
     703        AND tsEffective <= %s
     704ORDER BY    tsEffective DESC
     705LIMIT %s''', (idSchedGroup, aoEntries[0].tsEffective, cMaxRows + 1,));
     706
     707            iEntry  = 0;
     708            aaoRows = self._oDb.fetchAll();
     709            for iRow, oRow in enumerate(aaoRows):
     710                oNew   = SchedGroupData().initFromDbRow(oRow);
     711                iEntry = findEntry(oNew.tsEffective, iEntry);
     712                self._oDb.dprint('iRow=%s iEntry=%s' % (iRow, iEntry));
     713                if iEntry < 0:
     714                    break;
     715                oEntry = aoEntries[iEntry];
     716                aoChanges = oEntry.aoChanges;
     717                oEntry.oNewRaw = oNew;
     718                if iRow + 1 < len(aaoRows):
     719                    oOld = SchedGroupData().initFromDbRow(aaoRows[iRow + 1]);
     720                    self._oDb.dprint('oOld=%s' % (oOld,));
     721                    for sAttr in oNew.getDataAttributes():
     722                        if sAttr not in [ 'tsEffective', 'tsExpire', 'uidAuthor', ]:
     723                            oOldAttr = getattr(oOld, sAttr);
     724                            oNewAttr = getattr(oNew, sAttr);
     725                            if oOldAttr != oNewAttr:
     726                                aoChanges.append(AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
     727                else:
     728                    self._oDb.dprint('New');
     729
     730            #
     731            # ...
     732            #
     733
     734        # FInally
     735        UserAccountLogic(self._oDb).resolveChangeLogAuthors(aoEntries);
     736        return (aoEntries, fMore);
     737
    484738
    485739    def addEntry(self, oData, uidAuthor, fCommit = False):
  • trunk/src/VBox/ValidationKit/testmanager/core/testbox.py

    r83364 r83384  
    5959    ksParam_iSchedPriority      = 'TestBoxInSchedGroup_iSchedPriority';
    6060
    61     kasAllowNullAttributes      = [ 'idTestBox', 'tsEffective', 'tsExpire', 'uidAuthor', ]
     61    kasAllowNullAttributes      = [ 'tsEffective', 'tsExpire', 'uidAuthor', ]
    6262
    6363    kiMin_iSchedPriority        = 0;
     
    858858        if not aiSortColumns:
    859859            aiSortColumns = [self.kiSortColumn_sName,];
     860        asSortColumns = [self.kdSortColumnMap[i] for i in aiSortColumns];
     861        asSortColumns.append('TestBoxesInSchedGroups.idTestBox');
    860862
    861863        if tsNow is None:
     
    869871WHERE   TestBoxesInSchedGroups.idSchedGroup = %s
    870872    AND TestBoxesInSchedGroups.tsExpire     = 'infinity'::TIMESTAMP
    871 ORDER BY ''' + ', '.join([self.kdSortColumnMap[i] for i in aiSortColumns]) + '''
    872 ''', (idSchedGroup, ));
     873ORDER BY ''' + ', '.join(asSortColumns), (idSchedGroup, ));
    873874        else:
    874875            self._oDb.execute('''
     
    881882                    AND TestBoxesWithStrings.tsEffective <= %s
    882883WHERE   TestBoxesInSchedGroups.idSchedGroup = %s
    883     AND TestBoxesInSchedGroups.tsExpire     > %
    884     AND TestBoxesInSchedGroups.tsEffective <= %
    885 ORDER BY ''' + ', '.join([self.kdSortColumnMap[i] for i in aiSortColumns]) + '''
    886 ''', (tsNow, tsNow, idSchedGroup, tsNow, tsNow, ));
     884    AND TestBoxesInSchedGroups.tsExpire     > %s
     885    AND TestBoxesInSchedGroups.tsEffective <= %s
     886ORDER BY ''' + ', '.join(asSortColumns), (tsNow, tsNow, idSchedGroup, tsNow, tsNow, ));
    887887
    888888        aoRows = [];
Note: See TracChangeset for help on using the changeset viewer.

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