VirtualBox

Changeset 61502 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Jun 6, 2016 5:53:01 PM (9 years ago)
Author:
vboxsync
Message:

testmanager: Testboxes can now be members of more than one scheduling group.

Location:
trunk/src/VBox/ValidationKit/testmanager
Files:
12 edited

Legend:

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

    r61468 r61502  
    413413        Child classes can override to do special parameter conversion jobs.
    414414        """
    415         sPrefix = self.getHungarianPrefix(sAttr);
     415        sPrefix       = self.getHungarianPrefix(sAttr);
    416416        asValidValues = getattr(self, 'kasValidValues_' + sAttr, None);
     417        fAllowNull    = sAttr in getattr(self, 'kasAllowNullAttributes', list());
    417418        if fStrict:
    418419            if sPrefix == 'f':
     
    423424                oNewValue = oDisp.getListOfStrParams(sParam, []);
    424425            else:
    425                 oNewValue = oDisp.getStringParam(sParam, asValidValues, None);
     426                oNewValue = oDisp.getStringParam(sParam, asValidValues, None, fAllowNull = fAllowNull);
    426427        else:
    427428            if sPrefix[0] == 'a':
     
    429430            else:
    430431                assert oValue is not None, 'sAttr=%s' % (sAttr,);
    431                 oNewValue = oDisp.getStringParam(sParam, asValidValues, oValue);
     432                oNewValue = oDisp.getStringParam(sParam, asValidValues, oValue, fAllowNull = fAllowNull);
    432433        return oNewValue;
    433434
     
    10531054            self.oDisp    = oDisp;
    10541055            self.sAttrFmt = sAttrFmt;
    1055         def getStringParam(self, sName, asValidValues = None, sDefault = None):
     1056        def getStringParam(self, sName, asValidValues = None, sDefault = None, fAllowNull = False):
    10561057            """See WuiDispatcherBase.getStringParam."""
    1057             return self.oDisp.getStringParam(self.sAttrFmt % (sName,), asValidValues, sDefault);
     1058            return self.oDisp.getStringParam(self.sAttrFmt % (sName,), asValidValues, sDefault, fAllowNull = fAllowNull);
    10581059        def getListOfStrParams(self, sName, asDefaults = None):
    10591060            """See WuiDispatcherBase.getListOfStrParams."""
     
    11331134    def testInitFromParams(self):
    11341135        class DummyDisp(object):
    1135             def getStringParam(self, sName, asValidValues = None, sDefault = None):
    1136                 _ = sName; _ = asValidValues;
     1136            def getStringParam(self, sName, asValidValues = None, sDefault = None, fAllowNull = False):
     1137                _ = sName; _ = asValidValues; _ = fAllowNull;
    11371138                return sDefault;
    11381139            def getListOfStrParams(self, sName, asDefaults = None):
  • trunk/src/VBox/ValidationKit/testmanager/core/db.py

    r61284 r61502  
    100100    """
    101101    return dbTimestampToZuluDatetime(datetime.datetime.utcnow());
     102
     103def dbTimestampMinusOneTick(oValue):
     104    """
     105    Returns a new timestamp that's one tick before the given one.
     106    """
     107    oValue = dbTimestampToZuluDatetime(oValue);
     108    return oValue - datetime.timedelta(microseconds = 1);
    102109
    103110def isDbInterval(oValue):
  • trunk/src/VBox/ValidationKit/testmanager/core/schedgroup.py

    r61472 r61502  
    259259    def __init__(self):
    260260        SchedGroupData.__init__(self);
    261         self.aoMembers          = [];   # SchedGroupMemberDataEx.
     261        self.aoMembers          = [];       # type: SchedGroupMemberDataEx
    262262
    263263        # Two build sources for convenience sake.
    264         self.oBuildSrc          = None;
    265         self.oBuildSrcValidationKit = None;
     264        self.oBuildSrc          = None;     # type: TestBoxData
     265        self.oBuildSrcValidationKit = None; # type: TestBoxData
    266266        # List of test boxes that uses this group for convenience.
    267         self.aoTestBoxes        = None;
     267        self.aoTestBoxes        = None;     # type: list[TestBoxData]
    268268
    269269    def _initExtraMembersFromDb(self, oDb, tsNow = None, sPeriodBack = None):
     
    294294        # Test Boxes.
    295295        #
    296         ## @todo sPeriodBack!
    297         if tsNow is None:
    298             oDb.execute('SELECT *\n'
    299                         'FROM   TestBoxesWithStrings\n'
    300                         'WHERE  TestBoxesWithStrings.idSchedGroup = %s\n'
    301                         '   AND TestBoxesWithStrings.tsExpire     = \'infinity\'::TIMESTAMP\n'
    302                         'ORDER BY TestBoxesWithStrings.sName, TestBoxesWithStrings.idTestBox\n'
    303                         , (self.idSchedGroup,));
    304         else:
    305             oDb.execute('SELECT *\n'
    306                         'FROM   TestBoxesWithStrings\n'
    307                         'WHERE  TestBoxesWithStrings.idSchedGroup = %s\n'
    308                         '   AND TestBoxesWithStrings.tsExpire     > %s\n'
    309                         '   AND TestBoxesWithStrings.tsEffective  <= %s\n'
    310                         'ORDER BY TestBoxesWithStrings.sName, TestBoxesWithStrings.idTestBox\n'
    311                         , (self.idSchedGroup, tsNow, tsNow, tsNow, tsNow));
     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,));
    312305        for aoRow in oDb.fetchAll():
    313306            self.aoTestBoxes.append(TestBoxData().initFromDbRow(aoRow));
     
    316309        # Test groups.
    317310        #
    318         ## @todo sPeriodBack!
    319         if tsNow is None:
    320             oDb.execute('SELECT SchedGroupMembers.*, TestGroups.*\n'
    321                         'FROM   SchedGroupMembers\n'
    322                         'LEFT OUTER JOIN TestGroups ON (SchedGroupMembers.idTestGroup = TestGroups.idTestGroup)\n'
    323                         'WHERE  SchedGroupMembers.idSchedGroup = %s\n'
    324                         '   AND SchedGroupMembers.tsExpire     = \'infinity\'::TIMESTAMP\n'
    325                         '   AND TestGroups.tsExpire            = \'infinity\'::TIMESTAMP\n'
    326                         'ORDER BY SchedGroupMembers.idTestGroupPreReq, SchedGroupMembers.idTestGroup\n'
    327                         , (self.idSchedGroup,));
    328         else:
    329             oDb.execute('SELECT SchedGroupMembers.*, TestGroups.*\n'
    330                         'FROM   SchedGroupMembers\n'
    331                         'LEFT OUTER JOIN TestGroups ON (SchedGroupMembers.idTestGroup = TestGroups.idTestGroup)\n'
    332                         'WHERE  SchedGroupMembers.idSchedGroup = %s\n'
    333                         '   AND SchedGroupMembers.tsExpire     > %s\n'
    334                         '   AND SchedGroupMembers.tsEffective <= %s\n'
    335                         '   AND TestGroups.tsExpire            > %s\n'
    336                         '   AND TestGroups.tsEffective        <= %s\n'
    337                         'ORDER BY SchedGroupMembers.idTestGroupPreReq, SchedGroupMembers.idTestGroup\n'
    338                         , (self.idSchedGroup, tsNow, tsNow, tsNow, tsNow));
    339 
     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,));
    340319        for aoRow in oDb.fetchAll():
    341320            self.aoMembers.append(SchedGroupMemberDataEx().initFromDbRow(aoRow));
  • trunk/src/VBox/ValidationKit/testmanager/core/schedulerbase.py

    r61474 r61502  
    4444from testmanager.core.schedgroup        import SchedGroupData, SchedGroupLogic;
    4545from testmanager.core.systemlog         import SystemLogData, SystemLogLogic;
    46 from testmanager.core.testbox           import TestBoxData;
     46from testmanager.core.testbox           import TestBoxData, TestBoxDataEx;
    4747from testmanager.core.testboxstatus     import TestBoxStatusData, TestBoxStatusLogic;
    4848from testmanager.core.testcase          import TestCaseLogic;
     
    783783        # Instantiate the specified scheduler and let it do the rest.
    784784        #
    785         oSchedGrpData = SchedGroupData().initFromDbWithId(oDb, oTestBox.idSchedGroup, oTestSet.tsCreated);
     785        oSchedGrpData = SchedGroupData().initFromDbWithId(oDb, oTestSet.idSchedGroup, oTestSet.tsCreated);
    786786        assert oSchedGrpData.fEnabled   is True;
    787787        assert oSchedGrpData.idBuildSrc is not None;
     
    838838
    839839    def _createTestSet(self, oTask, oTestEx, oTestBoxData, oBuild, oValidationKitBuild, tsNow):
     840        # type: (SchedQueueData, TestCaseArgsDataEx, TestBoxData, BuildDataEx, BuildDataEx, datetime.datetime) -> int
    840841        """
    841842        Creates a test set for using the given data.
    842843        Will not commit, someone up the callstack will that later on.
     844
    843845        Returns the test set ID, may raise an exception on database error.
    844846        """
     
    918920                              oTestBoxData.idGenTestBox,
    919921                              oTestBoxData.idTestBox,
    920                               oTestBoxData.idSchedGroup,
     922                              oTask.idSchedGroup,
    921923                              oTask.idTestGroup,
    922924                              oTestEx.oTestCase.idGenTestCase,
     
    12891291
    12901292    @staticmethod
    1291     def scheduleNewTask(oDb, oTestBoxData, sBaseUrl, iVerbosity = 0):
    1292         """
    1293         Schedules a new task.
    1294         """
     1293    def _pickSchedGroup(oTestBoxDataEx, iWorkItem):
     1294        """
     1295        Picks the next scheduling group for the given testbox.
     1296        """
     1297        if len(oTestBoxDataEx.aoInSchedGroups) == 1:
     1298            oSchedGroup = oTestBoxDataEx.aoInSchedGroups[0].oSchedGroup;
     1299            if oSchedGroup.fEnabled and oSchedGroup.idBuildSrc is not None:
     1300                return (oSchedGroup, 0);
     1301
     1302        elif len(oTestBoxDataEx.aoInSchedGroups) > 0:
     1303            # Construct priority table of currently enabled scheduling groups.
     1304            aaoList1 = [];
     1305            for oInGroup in oTestBoxDataEx.aoInSchedGroups:
     1306                oSchedGroup = oInGroup.oSchedGroup;
     1307                if oSchedGroup.fEnabled and oSchedGroup.idBuildSrc is not None:
     1308                    iSchedPriority = oInGroup.iSchedPriority;
     1309                    if iSchedPriority > 31:     # paranoia
     1310                        iSchedPriority = 31;
     1311                    elif iSchedPriority < 0:    # paranoia
     1312                        iSchedPriority = 0;
     1313
     1314                    for iSchedPriority in xrange(min(iSchedPriority, len(aaoList1))):
     1315                        aaoList1[iSchedPriority].append(oSchedGroup);
     1316                    while len(aaoList1) <= iSchedPriority:
     1317                        aaoList1.append([oSchedGroup,]);
     1318
     1319            # Flatten it into a single list, mixing the priorities a little so it doesn't
     1320            # take forever before low priority stuff is executed.
     1321            aoFlat = [];
     1322            iLo    = 0;
     1323            iHi    = len(aaoList1) - 1;
     1324            while iHi >= iLo:
     1325                aoFlat += aaoList1[iHi];
     1326                if iLo < iHi:
     1327                    aoFlat += aaoList1[iLo];
     1328                iLo += 1;
     1329                iHi -= 1;
     1330
     1331            # Pick the next one.
     1332            iWorkItem += 1;
     1333            if iWorkItem >= len(aoFlat):
     1334                iWorkItem = 0;
     1335            if iWorkItem < len(aoFlat):
     1336                return (aoFlat[iWorkItem], iWorkItem);
     1337
     1338        # No active group.
     1339        return (None, 0);
     1340
     1341    @staticmethod
     1342    def scheduleNewTask(oDb, oTestBoxData, iWorkItem, sBaseUrl, iVerbosity = 0):
     1343        # type: (TMDatabaseConnection, TestBoxData, int, str, int) -> None
     1344        """
     1345        Schedules a new task for a testbox.
     1346        """
     1347        oTBStatusLogic = TestBoxStatusLogic(oDb);
     1348
    12951349        try:
    12961350            #
     
    12981352            # related to our scheduling queue.  Also, since this is a very
    12991353            # expensive operation we lock the testbox status row to fend of
    1300             # repeated retires by fault testbox script.
     1354            # repeated retires by faulty testbox scripts.
    13011355            #
    13021356            tsSecStart = utils.timestampSecond();
     
    13051359            oDb.execute('SELECT idTestBox FROM TestBoxStatuses WHERE idTestBox = %s FOR UPDATE NOWAIT'
    13061360                        % (oTestBoxData.idTestBox,));
    1307             oDb.execute('SELECT idSchedGroup FROM SchedQueues WHERE idSchedGroup = %s FOR UPDATE'
    1308                         % (oTestBoxData.idSchedGroup,));
     1361            oDb.execute('SELECT SchedQueues.idSchedGroup\n'
     1362                        '  FROM SchedQueues, TestBoxesInSchedGroups\n'
     1363                        'WHERE  TestBoxesInSchedGroups.idTestBox    = %s\n'
     1364                        '   AND TestBoxesInSchedGroups.tsExpire     = \'infinity\'::TIMESTAMP\n'
     1365                        '   AND TestBoxesInSchedGroups.idSchedGroup = SchedQueues.idSchedGroup\n'
     1366                        ' FOR UPDATE'
     1367                        % (oTestBoxData.idTestBox,));
    13091368
    13101369            # We need the current timestamp.
    13111370            tsNow = oDb.getCurrentTimestamp();
    13121371
    1313             # Re-read the testbox data ...
    1314             oTestBoxDataCur = TestBoxData().initFromDbWithId(oDb, oTestBoxData.idTestBox, tsNow);
    1315             if    oTestBoxDataCur.fEnabled \
    1316               and oTestBoxDataCur.idGenTestBox == oTestBoxData.idGenTestBox \
    1317               and oTestBoxDataCur.idSchedGroup == oTestBoxData.idSchedGroup: # (paranoia wrt idSchedGroup)
    1318 
    1319                 # ... and schedule group data.
    1320                 oSchedGrpData = SchedGroupData().initFromDbWithId(oDb, oTestBoxDataCur.idSchedGroup, tsNow);
    1321                 if oSchedGrpData.fEnabled  and  oSchedGrpData.idBuildSrc is not None:
     1372            # Re-read the testbox data with scheduling group relations.
     1373            oTestBoxDataEx = TestBoxDataEx().initFromDbWithId(oDb, oTestBoxData.idTestBox, tsNow);
     1374            if    oTestBoxDataEx.fEnabled \
     1375              and oTestBoxDataEx.idGenTestBox == oTestBoxData.idGenTestBox:
     1376
     1377                # Now, pick the scheduling group.
     1378                (oSchedGroup, iWorkItem) = SchedulerBase._pickSchedGroup(oTestBoxDataEx, iWorkItem);
     1379                if oSchedGroup is not None:
     1380                    assert oSchedGroup.fEnabled and oSchedGroup.idBuildSrc is not None;
    13221381
    13231382                    #
    13241383                    # Instantiate the specified scheduler and let it do the rest.
    13251384                    #
    1326                     oScheduler = SchedulerBase._instantiate(oDb, oSchedGrpData, iVerbosity, tsSecStart);
    1327                     dResponse = oScheduler.scheduleNewTaskWorker(oTestBoxDataCur, tsNow, sBaseUrl);
     1385                    oScheduler = SchedulerBase._instantiate(oDb, oSchedGroup, iVerbosity, tsSecStart);
     1386                    dResponse = oScheduler.scheduleNewTaskWorker(oTestBoxDataEx, tsNow, sBaseUrl);
    13281387                    if dResponse is not None:
     1388                        oTBStatusLogic.updateWorkItem(oTestBoxDataEx.idTestBox, iWorkItem);
    13291389                        oDb.commit();
    13301390                        return dResponse;
  • trunk/src/VBox/ValidationKit/testmanager/core/testbox.pgsql

    r61468 r61502  
    2626
    2727
    28 ---
    29 -- Checks if the test box name is unique, ignoring a_idTestCaseIgnore.
    30 -- Raises exception if duplicates are found.
    31 --
    32 -- @internal
    33 --
    34 CREATE OR REPLACE FUNCTION TestBoxLogic_checkUniqueName(a_sName TEXT, a_idTestBoxIgnore INTEGER)
    35     RETURNS VOID AS $$
    36     DECLARE
    37         v_cRows INTEGER;
    38     BEGIN
    39         SELECT  COUNT(*) INTO v_cRows
    40         FROM    TestBoxes
    41         WHERE   sName      =  a_sName
    42             AND tsExpire   =  'infinity'::TIMESTAMP
    43             AND idTestBox <> a_idTestBoxIgnore;
    44         IF v_cRows <> 0 THEN
    45             RAISE EXCEPTION 'Duplicate test box name "%" (% times)', a_sName, v_cRows;
    46         END IF;
    47     END;
    48 $$ LANGUAGE plpgsql;
    49 
    50 
    51 ---
    52 -- Historize a row.
    53 -- @internal
    54 --
    55 CREATE OR REPLACE FUNCTION TestBoxLogic_historizeEntry(a_idGenTestBox INTEGER, a_tsExpire TIMESTAMP WITH TIME ZONE)
    56     RETURNS VOID AS $$
    57     DECLARE
    58         v_cUpdatedRows INTEGER;
    59     BEGIN
    60         UPDATE  TestBoxes
    61           SET   tsExpire        = a_tsExpire
    62           WHERE idGenTestBox    = a_idGenTestBox
    63             AND tsExpire        = 'infinity'::TIMESTAMP;
    64         GET DIAGNOSTICS v_cUpdatedRows = ROW_COUNT;
    65         IF v_cUpdatedRows <> 1 THEN
    66             IF v_cUpdatedRows = 0 THEN
    67                 RAISE EXCEPTION 'Test box generation ID % is no longer valid', a_idGenTestBox;
    68             END IF;
    69             RAISE EXCEPTION 'Integrity error in TestBoxes: % current rows with idGenTestBox=%', v_cUpdatedRows, a_idGenTestBox;
    70         END IF;
    71     END;
    72 $$ LANGUAGE plpgsql;
    73 
    74 
    75 ---
    76 -- Translate string via the string table.
    77 --
    78 -- @returns NULL if a_sValue is NULL, otherwise a string ID.
    79 --
    80 CREATE OR REPLACE FUNCTION TestBoxLogic_lookupOrFindString(a_sValue TEXT)
    81     RETURNS INTEGER AS $$
    82     DECLARE
    83         v_idStr        INTEGER;
    84         v_cRows        INTEGER;
    85     BEGIN
    86         IF a_sValue IS NULL THEN
    87             RETURN NULL;
    88         END IF;
    89 
    90         SELECT      idStr
    91             INTO    v_idStr
    92             FROM    TestBoxStrTab
    93             WHERE   sValue = a_sValue;
    94         GET DIAGNOSTICS v_cRows = ROW_COUNT;
    95         IF v_cRows = 0 THEN
    96             INSERT INTO TestBoxStrTab (sValue)
    97                 VALUES (a_sValue)
    98                 RETURNING idStr INTO v_idStr;
    99         END IF;
    100         RETURN v_idStr;
    101     END;
    102 $$ LANGUAGE plpgsql;
    103 
    104 
    105 ---
    106 -- Only adds the user settable parts of the row, i.e. not what TestBoxLogic_updateOnSignOn touches.
    107 --
    108 CREATE OR REPLACE function TestBoxLogic_addEntry(a_uidAuthor            INTEGER,
     28--
     29-- Old type signatures.
     30--
     31DROP FUNCTION IF EXISTS TestBoxLogic_addEntry(a_uidAuthor            INTEGER,
    10932                                                 a_ip                   inet,
    11033                                                 a_uuidSystem           uuid,
     
    12043                                                 OUT r_idTestBox        INTEGER,
    12144                                                 OUT r_idGenTestBox     INTEGER,
    122                                                  OUT r_tsEffective      TIMESTAMP WITH TIME ZONE
    123                                                  ) AS $$
    124     DECLARE
    125          v_idStrDescription INTEGER;
    126          v_idStrComment     INTEGER;
    127     BEGIN
    128         PERFORM TestBoxLogic_checkUniqueName(a_sName, -1);
    129 
    130         SELECT TestBoxLogic_lookupOrFindString(a_sDescription) INTO v_idStrDescription;
    131         SELECT TestBoxLogic_lookupOrFindString(a_sComment)     INTO v_idStrComment;
    132 
    133         INSERT INTO TestBoxes (
    134                     tsEffective,         -- 1
    135                     uidAuthor,           -- 2
    136                     ip,                  -- 3
    137                     uuidSystem,          -- 4
    138                     sName,               -- 5
    139                     idStrDescription,    -- 6
    140                     idSchedGroup,        -- 7
    141                     fEnabled,            -- 8
    142                     enmLomKind,          -- 9
    143                     ipLom,               -- 10
    144                     pctScaleTimeout,     -- 11
    145                     idStrComment,        -- 12
    146                     enmPendingCmd )      -- 13
    147             VALUES (CURRENT_TIMESTAMP,   -- 1
    148                     a_uidAuthor,         -- 2
    149                     a_ip,                -- 3
    150                     a_uuidSystem,        -- 4
    151                     a_sName,             -- 5
    152                     v_idStrDescription,  -- 6
    153                     a_idSchedGroup,      -- 7
    154                     a_fEnabled,          -- 8
    155                     a_enmLomKind,        -- 9
    156                     a_ipLom,             -- 10
    157                     a_pctScaleTimeout,   -- 11
    158                     v_idStrComment,      -- 12
    159                     a_enmPendingCmd )    -- 13
    160             RETURNING idTestBox, idGenTestBox, tsEffective INTO r_idTestBox, r_idGenTestBox, r_tsEffective;
    161     END;
    162 $$ LANGUAGE plpgsql;
    163 
    164 ---
    165 -- Only adds the user settable parts of the row, i.e. not what TestBoxLogic_updateOnSignOn touches.
    166 --
    167 CREATE OR REPLACE function TestBoxLogic_editEntry(a_uidAuthor           INTEGER,
     45                                                 OUT r_tsEffective      TIMESTAMP WITH TIME ZONE);
     46DROP FUNCTION IF EXISTS TestBoxLogic_editEntry(a_uidAuthor           INTEGER,
    16847                                                  a_idTestBox           INTEGER,
    16948                                                  a_ip                  inet,
     
    17958                                                  a_enmPendingCmd       TestBoxCmd_T,
    18059                                                  OUT r_idGenTestBox    INTEGER,
     60                                                  OUT r_tsEffective     TIMESTAMP WITH TIME ZONE);
     61DROP FUNCTION IF EXISTS TestBoxLogic_removeEntry(INTEGER, INTEGER, BOOLEAN);
     62DROP FUNCTION IF EXISTS TestBoxLogic_addGroupEntry(a_uidAuthor           INTEGER,
     63                                                   a_idTestBox           INTEGER,
     64                                                   a_idSchedGroup        INTEGER,
     65                                                   a_iSchedPriority      INTEGER,
     66                                                   OUT r_tsEffective     TIMESTAMP WITH TIME ZONE);
     67DROP FUNCTION IF EXISTS    TestBoxLogic_editGroupEntry(a_uidAuthor          INTEGER,
     68                                                       a_idTestBox          INTEGER,
     69                                                       a_idSchedGroup       INTEGER,
     70                                                       a_iSchedPriority     INTEGER,
     71                                                       OUT r_tsEffective    INTEGER);
     72
     73
     74---
     75-- Checks if the test box name is unique, ignoring a_idTestCaseIgnore.
     76-- Raises exception if duplicates are found.
     77--
     78-- @internal
     79--
     80CREATE OR REPLACE FUNCTION TestBoxLogic_checkUniqueName(a_sName TEXT, a_idTestBoxIgnore INTEGER)
     81    RETURNS VOID AS $$
     82    DECLARE
     83        v_cRows INTEGER;
     84    BEGIN
     85        SELECT  COUNT(*) INTO v_cRows
     86        FROM    TestBoxes
     87        WHERE   sName      =  a_sName
     88            AND tsExpire   =  'infinity'::TIMESTAMP
     89            AND idTestBox <> a_idTestBoxIgnore;
     90        IF v_cRows <> 0 THEN
     91            RAISE EXCEPTION 'Duplicate test box name "%" (% times)', a_sName, v_cRows;
     92        END IF;
     93    END;
     94$$ LANGUAGE plpgsql;
     95
     96
     97---
     98-- Checks that the given scheduling group exists.
     99-- Raises exception if it doesn't.
     100--
     101-- @internal
     102--
     103CREATE OR REPLACE FUNCTION TestBoxLogic_checkSchedGroupExists(a_idSchedGroup INTEGER)
     104    RETURNS VOID AS $$
     105    DECLARE
     106        v_cRows INTEGER;
     107    BEGIN
     108        SELECT  COUNT(*) INTO v_cRows
     109        FROM    SchedGroups
     110        WHERE   idSchedGroup = a_idSchedGroup
     111            AND tsExpire     =  'infinity'::TIMESTAMP;
     112        IF v_cRows <> 1 THEN
     113            IF v_cRows = 0 THEN
     114                RAISE EXCEPTION 'Scheduling group with ID % was not found', a_idSchedGroup;
     115            END IF;
     116            RAISE EXCEPTION 'Integrity error in SchedGroups: % current rows with idSchedGroup=%', v_cRows, a_idSchedGroup;
     117        END IF;
     118    END;
     119$$ LANGUAGE plpgsql;
     120
     121
     122---
     123-- Checks that the given testbxo + scheduling group pair does not currently exists.
     124-- Raises exception if it does.
     125--
     126-- @internal
     127--
     128CREATE OR REPLACE FUNCTION TestBoxLogic_checkTestBoxNotInSchedGroup(a_idTestBox INTEGER, a_idSchedGroup INTEGER)
     129    RETURNS VOID AS $$
     130    DECLARE
     131        v_cRows INTEGER;
     132    BEGIN
     133        SELECT  COUNT(*) INTO v_cRows
     134        FROM    TestBoxesInSchedGroups
     135        WHERE   idTestBox    = a_idTestBox
     136            AND idSchedGroup = a_idSchedGroup
     137            AND tsExpire     =  'infinity'::TIMESTAMP;
     138        IF v_cRows <> 0 THEN
     139            RAISE EXCEPTION 'TestBox % is already a member of scheduling group %', a_idTestBox, a_idSchedGroup;
     140        END IF;
     141    END;
     142$$ LANGUAGE plpgsql;
     143
     144
     145---
     146-- Historize a row.
     147-- @internal
     148--
     149CREATE OR REPLACE FUNCTION TestBoxLogic_historizeEntry(a_idGenTestBox INTEGER, a_tsExpire TIMESTAMP WITH TIME ZONE)
     150    RETURNS VOID AS $$
     151    DECLARE
     152        v_cUpdatedRows INTEGER;
     153    BEGIN
     154        UPDATE  TestBoxes
     155          SET   tsExpire        = a_tsExpire
     156          WHERE idGenTestBox    = a_idGenTestBox
     157            AND tsExpire        = 'infinity'::TIMESTAMP;
     158        GET DIAGNOSTICS v_cUpdatedRows = ROW_COUNT;
     159        IF v_cUpdatedRows <> 1 THEN
     160            IF v_cUpdatedRows = 0 THEN
     161                RAISE EXCEPTION 'Test box generation ID % is no longer valid', a_idGenTestBox;
     162            END IF;
     163            RAISE EXCEPTION 'Integrity error in TestBoxes: % current rows with idGenTestBox=%', v_cUpdatedRows, a_idGenTestBox;
     164        END IF;
     165    END;
     166$$ LANGUAGE plpgsql;
     167
     168
     169---
     170-- Historize a in-scheduling-group row.
     171-- @internal
     172--
     173CREATE OR REPLACE FUNCTION TestBoxLogic_historizeGroupEntry(a_idTestBox INTEGER,
     174                                                            a_idSchedGroup INTEGER,
     175                                                            a_tsExpire TIMESTAMP WITH TIME ZONE)
     176    RETURNS VOID AS $$
     177    DECLARE
     178        v_cUpdatedRows INTEGER;
     179    BEGIN
     180        UPDATE  TestBoxesInSchedGroups
     181          SET   tsExpire        = a_tsExpire
     182          WHERE idTestBox       = a_idTestBox
     183            AND idSchedGroup    = a_idSchedGroup
     184            AND tsExpire        = 'infinity'::TIMESTAMP;
     185        GET DIAGNOSTICS v_cUpdatedRows = ROW_COUNT;
     186        IF v_cUpdatedRows <> 1 THEN
     187            IF v_cUpdatedRows = 0 THEN
     188                RAISE EXCEPTION 'TestBox ID % / SchedGroup ID % is no longer a valid combination', a_idTestBox, a_idSchedGroup;
     189            END IF;
     190            RAISE EXCEPTION 'Integrity error in TestBoxesInSchedGroups: % current rows for % / %',
     191                v_cUpdatedRows, a_idTestBox, a_idSchedGroup;
     192        END IF;
     193    END;
     194$$ LANGUAGE plpgsql;
     195
     196
     197---
     198-- Translate string via the string table.
     199--
     200-- @returns NULL if a_sValue is NULL, otherwise a string ID.
     201--
     202CREATE OR REPLACE FUNCTION TestBoxLogic_lookupOrFindString(a_sValue TEXT)
     203    RETURNS INTEGER AS $$
     204    DECLARE
     205        v_idStr        INTEGER;
     206        v_cRows        INTEGER;
     207    BEGIN
     208        IF a_sValue IS NULL THEN
     209            RETURN NULL;
     210        END IF;
     211
     212        SELECT      idStr
     213            INTO    v_idStr
     214            FROM    TestBoxStrTab
     215            WHERE   sValue = a_sValue;
     216        GET DIAGNOSTICS v_cRows = ROW_COUNT;
     217        IF v_cRows = 0 THEN
     218            INSERT INTO TestBoxStrTab (sValue)
     219                VALUES (a_sValue)
     220                RETURNING idStr INTO v_idStr;
     221        END IF;
     222        RETURN v_idStr;
     223    END;
     224$$ LANGUAGE plpgsql;
     225
     226
     227---
     228-- Only adds the user settable parts of the row, i.e. not what TestBoxLogic_updateOnSignOn touches.
     229--
     230CREATE OR REPLACE function TestBoxLogic_addEntry(a_uidAuthor            INTEGER,
     231                                                 a_ip                   inet,
     232                                                 a_uuidSystem           uuid,
     233                                                 a_sName                TEXT,
     234                                                 a_sDescription         TEXT,
     235                                                 a_fEnabled             BOOLEAN,
     236                                                 a_enmLomKind           LomKind_T,
     237                                                 a_ipLom                inet,
     238                                                 a_pctScaleTimeout      INTEGER,  -- Actually smallint, but default typing fun.
     239                                                 a_sComment             TEXT,
     240                                                 a_enmPendingCmd        TestBoxCmd_T,
     241                                                 OUT r_idTestBox        INTEGER,
     242                                                 OUT r_idGenTestBox     INTEGER,
     243                                                 OUT r_tsEffective      TIMESTAMP WITH TIME ZONE
     244                                                 ) AS $$
     245    DECLARE
     246         v_idStrDescription INTEGER;
     247         v_idStrComment     INTEGER;
     248    BEGIN
     249        PERFORM TestBoxLogic_checkUniqueName(a_sName, -1);
     250
     251        SELECT TestBoxLogic_lookupOrFindString(a_sDescription) INTO v_idStrDescription;
     252        SELECT TestBoxLogic_lookupOrFindString(a_sComment)     INTO v_idStrComment;
     253
     254        INSERT INTO TestBoxes (
     255                    tsEffective,         -- 1
     256                    uidAuthor,           -- 2
     257                    ip,                  -- 3
     258                    uuidSystem,          -- 4
     259                    sName,               -- 5
     260                    idStrDescription,    -- 6
     261                    fEnabled,            -- 7
     262                    enmLomKind,          -- 8
     263                    ipLom,               -- 9
     264                    pctScaleTimeout,     -- 10
     265                    idStrComment,        -- 11
     266                    enmPendingCmd )      -- 12
     267            VALUES (CURRENT_TIMESTAMP,   -- 1
     268                    a_uidAuthor,         -- 2
     269                    a_ip,                -- 3
     270                    a_uuidSystem,        -- 4
     271                    a_sName,             -- 5
     272                    v_idStrDescription,  -- 6
     273                    a_fEnabled,          -- 7
     274                    a_enmLomKind,        -- 8
     275                    a_ipLom,             -- 9
     276                    a_pctScaleTimeout,   -- 10
     277                    v_idStrComment,      -- 11
     278                    a_enmPendingCmd )    -- 12
     279            RETURNING idTestBox, idGenTestBox, tsEffective INTO r_idTestBox, r_idGenTestBox, r_tsEffective;
     280    END;
     281$$ LANGUAGE plpgsql;
     282
     283
     284CREATE OR REPLACE function TestBoxLogic_addGroupEntry(a_uidAuthor           INTEGER,
     285                                                      a_idTestBox           INTEGER,
     286                                                      a_idSchedGroup        INTEGER,
     287                                                      a_iSchedPriority      INTEGER,
     288                                                      OUT r_tsEffective     TIMESTAMP WITH TIME ZONE
     289                                                      ) AS $$
     290    BEGIN
     291        PERFORM TestBoxLogic_checkSchedGroupExists(a_idSchedGroup);
     292        PERFORM TestBoxLogic_checkTestBoxNotInSchedGroup(a_idTestBox, a_idSchedGroup);
     293
     294        INSERT INTO TestBoxesInSchedGroups (
     295                    idTestBox,
     296                    idSchedGroup,
     297                    tsEffective,
     298                    tsExpire,
     299                    uidAuthor,
     300                    iSchedPriority)
     301            VALUES (a_idTestBox,
     302                    a_idSchedGroup,
     303                    CURRENT_TIMESTAMP,
     304                    'infinity'::TIMESTAMP,
     305                    a_uidAuthor,
     306                    a_iSchedPriority)
     307            RETURNING tsEffective INTO r_tsEffective;
     308    END;
     309$$ LANGUAGE plpgsql;
     310
     311
     312---
     313-- Only adds the user settable parts of the row, i.e. not what TestBoxLogic_updateOnSignOn touches.
     314--
     315CREATE OR REPLACE function TestBoxLogic_editEntry(a_uidAuthor           INTEGER,
     316                                                  a_idTestBox           INTEGER,
     317                                                  a_ip                  inet,
     318                                                  a_uuidSystem          uuid,
     319                                                  a_sName               TEXT,
     320                                                  a_sDescription        TEXT,
     321                                                  a_fEnabled            BOOLEAN,
     322                                                  a_enmLomKind          LomKind_T,
     323                                                  a_ipLom               inet,
     324                                                  a_pctScaleTimeout     INTEGER, -- Actually smallint, but default typing fun.
     325                                                  a_sComment            TEXT,
     326                                                  a_enmPendingCmd       TestBoxCmd_T,
     327                                                  OUT r_idGenTestBox    INTEGER,
    181328                                                  OUT r_tsEffective     TIMESTAMP WITH TIME ZONE
    182329                                                  ) AS $$
     
    204351        v_Row.sName             := a_sName;
    205352        v_Row.idStrDescription  := v_idStrDescription;
    206         v_Row.idSchedGroup      := a_idSchedGroup;
    207353        v_Row.fEnabled          := a_fEnabled;
    208354        v_Row.enmLomKind        := a_enmLomKind;
     
    225371
    226372
    227 DROP FUNCTION IF EXISTS TestBoxLogic_removeEntry(INTEGER, INTEGER, BOOLEAN);
     373CREATE OR REPLACE function TestBoxLogic_editGroupEntry(a_uidAuthor          INTEGER,
     374                                                       a_idTestBox          INTEGER,
     375                                                       a_idSchedGroup       INTEGER,
     376                                                       a_iSchedPriority     INTEGER,
     377                                                       OUT r_tsEffective    TIMESTAMP WITH TIME ZONE
     378                                                       ) AS $$
     379    DECLARE
     380        v_Row               TestBoxesInSchedGroups%ROWTYPE;
     381        v_idStrDescription  INTEGER;
     382        v_idStrComment      INTEGER;
     383    BEGIN
     384        PERFORM TestBoxLogic_checkSchedGroupExists(a_idSchedGroup);
     385
     386        -- Fetch and historize the current row - there must be one.
     387        UPDATE      TestBoxesInSchedGroups
     388            SET     tsExpire     = CURRENT_TIMESTAMP
     389            WHERE   idTestBox    = a_idTestBox
     390                AND idSchedGroup = a_idSchedGroup
     391                AND tsExpire     = 'infinity'::TIMESTAMP
     392            RETURNING * INTO STRICT v_Row;
     393
     394        -- Modify the row with the new data.
     395        v_Row.uidAuthor         := a_uidAuthor;
     396        v_Row.iSchedPriority    := a_iSchedPriority;
     397        v_Row.tsEffective       := v_Row.tsExpire;
     398        r_tsEffective           := v_Row.tsExpire;
     399        v_Row.tsExpire          := 'infinity'::TIMESTAMP;
     400
     401        -- Insert the modified row.
     402        INSERT INTO TestBoxesInSchedGroups VALUES (v_Row.*);
     403    END;
     404$$ LANGUAGE plpgsql;
     405
     406
    228407CREATE OR REPLACE FUNCTION TestBoxLogic_removeEntry(a_uidAuthor INTEGER, a_idTestBox INTEGER, a_fCascade BOOLEAN)
    229408    RETURNS VOID AS $$
     
    242421            RAISE EXCEPTION 'CASCADE test box deletion is not implemented';
    243422        END IF;
     423
     424        --
     425        -- Delete all current groups, skipping history since we're also deleting the testbox.
     426        --
     427        UPDATE      TestBoxesInSchedGroups
     428            SET     tsExpire = CURRENT_TIMESTAMP
     429            WHERE   idTestBox   = a_idTestBox
     430                AND tsExpire    = 'infinity'::TIMESTAMP;
    244431
    245432        --
     
    271458        WHEN TOO_MANY_ROWS THEN
    272459            RAISE EXCEPTION 'Integrity error in TestBoxes: Too many current rows for %', a_idTestBox;
     460    END;
     461$$ LANGUAGE plpgsql;
     462
     463
     464CREATE OR REPLACE FUNCTION TestBoxLogic_removeGroupEntry(a_uidAuthor INTEGER, a_idTestBox INTEGER, a_idSchedGroup INTEGER)
     465    RETURNS VOID AS $$
     466    DECLARE
     467        v_Row           TestBoxesInSchedGroups%ROWTYPE;
     468        v_tsEffective   TIMESTAMP WITH TIME ZONE;
     469    BEGIN
     470        --
     471        -- To preserve the information about who deleted the record, we try to
     472        -- add a dummy record which expires immediately.  I say try because of
     473        -- the primary key, we must let the new record be valid for 1 us. :-(
     474        --
     475        SELECT  * INTO STRICT v_Row
     476        FROM    TestBoxesInSchedGroups
     477        WHERE   idTestBox    = a_idTestBox
     478            AND idSchedGroup = a_idSchedGroup
     479            AND tsExpire     = 'infinity'::TIMESTAMP;
     480
     481        v_tsEffective := CURRENT_TIMESTAMP - INTERVAL '1 microsecond';
     482        IF v_Row.tsEffective < v_tsEffective THEN
     483            PERFORM TestBoxLogic_historizeGroupEntry(a_idTestBox, a_idSchedGroup, v_tsEffective);
     484
     485            v_Row.tsEffective   := v_tsEffective;
     486            v_Row.tsExpire      := CURRENT_TIMESTAMP;
     487            v_Row.uidAuthor     := a_uidAuthor;
     488            INSERT INTO TestBoxesInSchedGroups VALUES (v_Row.*);
     489        ELSE
     490            PERFORM TestBoxLogic_historizeGroupEntry(a_idTestBox, a_idSchedGroup, CURRENT_TIMESTAMP);
     491        END IF;
     492
     493    EXCEPTION
     494        WHEN NO_DATA_FOUND THEN
     495            RAISE EXCEPTION 'TestBox #% does is not currently a member of scheduling group #%', a_idTestBox, a_idSchedGroup;
     496        WHEN TOO_MANY_ROWS THEN
     497            RAISE EXCEPTION 'Integrity error in TestBoxesInSchedGroups: Too many current rows for % / %',
     498                a_idTestBox, a_idSchedGroup;
    273499    END;
    274500$$ LANGUAGE plpgsql;
  • trunk/src/VBox/ValidationKit/testmanager/core/testbox.py

    r61468 r61502  
    3131
    3232# Standard python imports.
     33import copy;
    3334import unittest;
    3435
    3536# Validation Kit imports.
     37from testmanager.core               import db;
    3638from testmanager.core.base          import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMInFligthCollision, \
    37                                            TMInvalidData, TMTooManyRows, TMRowNotFound, ChangeLogEntry, AttributeChangeEntry;
     39                                           TMInvalidData, TMTooManyRows, TMRowNotFound, \
     40                                           ChangeLogEntry, AttributeChangeEntry;
    3841from testmanager.core.useraccount   import UserAccountLogic;
     42
     43
     44class TestBoxInSchedGroupData(ModelDataBase):
     45    """
     46    TestBox in SchedGroup data.
     47    """
     48
     49    ksParam_idTestBox           = 'TestBoxInSchedGroup_idTestBox';
     50    ksParam_idSchedGroup        = 'TestBoxInSchedGroup_idSchedGroup';
     51    ksParam_tsEffective         = 'TestBoxInSchedGroup_tsEffective';
     52    ksParam_tsExpire            = 'TestBoxInSchedGroup_tsExpire';
     53    ksParam_uidAuthor           = 'TestBoxInSchedGroup_uidAuthor';
     54    ksParam_iSchedPriority      = 'TestBoxInSchedGroup_iSchedPriority';
     55
     56    kasAllowNullAttributes      = [ 'idTestBox', 'tsEffective', 'tsExpire', 'uidAuthor', ]
     57
     58    kiMin_iSchedPriority        = 0;
     59    kiMax_iSchedPriority        = 32;
     60
     61    kcDbColumns                 = 6;
     62
     63    def __init__(self):
     64        ModelDataBase.__init__(self);
     65        self.idTestBox          = None;
     66        self.idSchedGroup       = None;
     67        self.tsEffective        = None;
     68        self.tsExpire           = None;
     69        self.uidAuthor          = None;
     70        self.iSchedPriority     = 16;
     71
     72    def initFromDbRow(self, aoRow):
     73        """
     74        Expecting the result from a query like this:
     75            SELECT * FROM TestBoxesInSchedGroups
     76        """
     77        if aoRow is None:
     78            raise TMRowNotFound('TestBox/SchedGroup not found.');
     79
     80        self.idTestBox          = aoRow[0];
     81        self.idSchedGroup       = aoRow[1];
     82        self.tsEffective        = aoRow[2];
     83        self.tsExpire           = aoRow[3];
     84        self.uidAuthor          = aoRow[4];
     85        self.iSchedPriority     = aoRow[5];
     86
     87        return self;
     88
     89class TestBoxInSchedGroupDataEx(TestBoxInSchedGroupData):
     90    """
     91    Extended version of TestBoxInSchedGroupData that contains the scheduling group.
     92    """
     93
     94    def __init__(self):
     95        TestBoxInSchedGroupData.__init__(self);
     96        self.oSchedGroup        = None; # type: SchedGroupData
     97
     98    def initFromDbRowEx(self, aoRow, oDb, tsNow = None, sPeriodBack = None):
     99        """
     100        Extended version of initFromDbRow that fills in the rest from the database.
     101        """
     102        from testmanager.core.schedgroup import SchedGroupData;
     103        self.initFromDbRow(aoRow);
     104        self.oSchedGroup        = SchedGroupData().initFromDbWithId(oDb, self.idSchedGroup, tsNow, sPeriodBack);
     105        return self;
    39106
    40107
     
    92159    ksParam_sName               = 'TestBox_sName';
    93160    ksParam_sDescription        = 'TestBox_sDescription';
    94     ksParam_idSchedGroup        = 'TestBox_idSchedGroup';
    95161    ksParam_fEnabled            = 'TestBox_fEnabled';
    96162    ksParam_enmLomKind          = 'TestBox_enmLomKind';
     
    119185    kasInternalAttributes       = [ 'idStrDescription', 'idStrComment', 'idStrOs', 'idStrOsVersion', 'idStrCpuVendor',
    120186                                    'idStrCpuArch', 'idStrCpuName', 'idStrReport', ];
     187    kasMachineSettableOnly      = [ 'sOs', 'sOsVersion', 'sCpuVendor', 'sCpuArch', 'sCpuName', 'lCpuRevision', 'cCpus',
     188                                    'fCpuHwVirt', 'fCpuNestedPaging', 'fCpu64BitGuest', 'fChipsetIoMmu', 'fRawMode',
     189                                    'cMbMemory', 'cMbScratch', 'sReport', 'iTestBoxScriptRev', 'iPythonHexVersion', ];
    121190    kasAllowNullAttributes      = ['idTestBox', 'tsEffective', 'tsExpire', 'uidAuthor', 'idGenTestBox', 'sDescription',
    122                                    'ipLom', 'sComment', 'sOs', 'sOsVersion', 'sCpuVendor', 'sCpuArch', 'sCpuName',
    123                                    'lCpuRevision', 'cCpus', 'fCpuHwVirt', 'fCpuNestedPaging', 'fCpu64BitGuest', 'fChipsetIoMmu',
    124                                    'fRawMode', 'cMbMemory', 'cMbScratch', 'sReport', 'iTestBoxScriptRev', 'iPythonHexVersion',
    125                                   ] + kasInternalAttributes;
     191                                   'ipLom', 'sComment', ] + kasMachineSettableOnly + kasInternalAttributes;
    126192
    127193    kasValidValues_enmLomKind   = kasLomKindValues;
     
    131197    kcchMax_sReport             = 65535;
    132198
    133 
    134     kcDbColumns                 = 41; # including the 7 string joins columns
     199    kcDbColumns                 = 40; # including the 7 string joins columns
     200
    135201
    136202    def __init__(self):
     
    150216        self.sName               = None;
    151217        self.idStrDescription    = None;
    152         self.idSchedGroup        = 1;
    153218        self.fEnabled            = False;
    154219        self.enmLomKind          = self.ksLomKind_None;
     
    202267        self.sName               = aoRow[7];
    203268        self.idStrDescription    = aoRow[8];
    204         self.idSchedGroup        = aoRow[9];
    205         self.fEnabled            = aoRow[10];
    206         self.enmLomKind          = aoRow[11];
    207         self.ipLom               = aoRow[12];
    208         self.pctScaleTimeout     = aoRow[13];
    209         self.idStrComment        = aoRow[14];
    210         self.idStrOs             = aoRow[15];
    211         self.idStrOsVersion      = aoRow[16];
    212         self.idStrCpuVendor      = aoRow[17];
    213         self.idStrCpuArch        = aoRow[18];
    214         self.idStrCpuName        = aoRow[19];
    215         self.lCpuRevision        = aoRow[20];
    216         self.cCpus               = aoRow[21];
    217         self.fCpuHwVirt          = aoRow[22];
    218         self.fCpuNestedPaging    = aoRow[23];
    219         self.fCpu64BitGuest      = aoRow[24];
    220         self.fChipsetIoMmu       = aoRow[25];
    221         self.fRawMode            = aoRow[26];
    222         self.cMbMemory           = aoRow[27];
    223         self.cMbScratch          = aoRow[28];
    224         self.idStrReport         = aoRow[29];
    225         self.iTestBoxScriptRev   = aoRow[30];
    226         self.iPythonHexVersion   = aoRow[31];
    227         self.enmPendingCmd       = aoRow[32];
     269        self.fEnabled            = aoRow[9];
     270        self.enmLomKind          = aoRow[10];
     271        self.ipLom               = aoRow[11];
     272        self.pctScaleTimeout     = aoRow[12];
     273        self.idStrComment        = aoRow[13];
     274        self.idStrOs             = aoRow[14];
     275        self.idStrOsVersion      = aoRow[15];
     276        self.idStrCpuVendor      = aoRow[16];
     277        self.idStrCpuArch        = aoRow[17];
     278        self.idStrCpuName        = aoRow[18];
     279        self.lCpuRevision        = aoRow[19];
     280        self.cCpus               = aoRow[20];
     281        self.fCpuHwVirt          = aoRow[21];
     282        self.fCpuNestedPaging    = aoRow[22];
     283        self.fCpu64BitGuest      = aoRow[23];
     284        self.fChipsetIoMmu       = aoRow[24];
     285        self.fRawMode            = aoRow[25];
     286        self.cMbMemory           = aoRow[26];
     287        self.cMbScratch          = aoRow[27];
     288        self.idStrReport         = aoRow[28];
     289        self.iTestBoxScriptRev   = aoRow[29];
     290        self.iPythonHexVersion   = aoRow[30];
     291        self.enmPendingCmd       = aoRow[31];
    228292
    229293        # String table values.
    230294        if len(aoRow) > 32:
    231             self.sDescription    = aoRow[33];
    232             self.sComment        = aoRow[34];
    233             self.sOs             = aoRow[35];
    234             self.sOsVersion      = aoRow[36];
    235             self.sCpuVendor      = aoRow[37];
    236             self.sCpuArch        = aoRow[38];
    237             self.sCpuName        = aoRow[39];
    238             self.sReport         = aoRow[40];
     295            self.sDescription    = aoRow[32];
     296            self.sComment        = aoRow[33];
     297            self.sOs             = aoRow[34];
     298            self.sOsVersion      = aoRow[35];
     299            self.sCpuVendor      = aoRow[36];
     300            self.sCpuArch        = aoRow[37];
     301            self.sCpuName        = aoRow[38];
     302            self.sReport         = aoRow[39];
    239303
    240304        return self;
     
    254318        return self.initFromDbRow(aoRow);
    255319
    256     def initFromDbWithGenId(self, oDb, idGenTestBox):
     320    def initFromDbWithGenId(self, oDb, idGenTestBox, tsNow = None):
    257321        """
    258322        Initialize the object from the database.
    259323        """
     324        _ = tsNow;                      # Only useful for extended data classes.
    260325        oDb.execute('SELECT TestBoxesWithStrings.*\n'
    261326                    'FROM   TestBoxesWithStrings\n'
     
    444509
    445510
     511class TestBoxDataEx(TestBoxData):
     512    """
     513    TestBox data.
     514    """
     515
     516    ksParam_aoInSchedGroups = 'TestBox_aoInSchedGroups';
     517
     518    # Use [] instead of None.
     519    kasAltArrayNull = [ 'aoInSchedGroups', ];
     520
     521    ## Helper parameter containing the comma separated list with the IDs of
     522    #  potential members found in the parameters.
     523    ksParam_aidSchedGroups = 'TestBoxDataEx_aidSchedGroups';
     524
     525    def __init__(self):
     526        TestBoxData.__init__(self);
     527        self.aoInSchedGroups        = [];   # type: list[TestBoxInSchedGroupData]
     528
     529    def _initExtraMembersFromDb(self, oDb, tsNow = None, sPeriodBack = None):
     530        """
     531        Worker shared by the initFromDb* methods.
     532        Returns self.  Raises exception if no row or database error.
     533        """
     534        oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
     535                                                       'SELECT  *\n'
     536                                                       'FROM    TestBoxesInSchedGroups\n'
     537                                                       'WHERE   idTestBox = %s\n'
     538                                                       , (self.idTestBox,), tsNow, sPeriodBack)
     539                    + 'ORDER BY idSchedGroup\n' );
     540        self.aoInSchedGroups = [];
     541        for aoRow in oDb.fetchAll():
     542            self.aoInSchedGroups.append(TestBoxInSchedGroupDataEx().initFromDbRowEx(aoRow, oDb, tsNow, sPeriodBack));
     543        return self;
     544
     545    def initFromDbRowEx(self, aoRow, oDb, tsNow = None):
     546        """
     547        Reinitialize from a SELECT * FROM TestBoxesWithStrings row.  Will query the
     548        necessary additional data from oDb using tsNow.
     549        Returns self.  Raises exception if no row or database error.
     550        """
     551        TestBoxData.initFromDbRow(self, aoRow);
     552        return self._initExtraMembersFromDb(oDb, tsNow);
     553
     554    def initFromDbWithId(self, oDb, idTestBox, tsNow = None, sPeriodBack = None):
     555        """
     556        Initialize the object from the database.
     557        """
     558        TestBoxData.initFromDbWithId(self, oDb, idTestBox, tsNow, sPeriodBack);
     559        return self._initExtraMembersFromDb(oDb, tsNow, sPeriodBack);
     560
     561    def initFromDbWithGenId(self, oDb, idGenTestBox, tsNow = None):
     562        """
     563        Initialize the object from the database.
     564        """
     565        TestBoxData.initFromDbWithGenId(self, oDb, idGenTestBox);
     566        if tsNow is None and not oDb.isTsInfinity(self.tsExpire):
     567            tsNow = self.tsEffective;
     568        return self._initExtraMembersFromDb(oDb, tsNow);
     569
     570    def getAttributeParamNullValues(self, sAttr): # Necessary?
     571        if sAttr in ['aoInSchedGroups', ]:
     572            return [[], ''];
     573        return TestBoxData.getAttributeParamNullValues(self, sAttr);
     574
     575    def convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict):
     576        """
     577        For dealing with the in-scheduling-group list.
     578        """
     579        if sAttr != 'aoInSchedGroups':
     580            return TestBoxData.convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict);
     581
     582        aoNewValues = [];
     583        aidSelected = oDisp.getListOfIntParams(sParam, iMin = 1, iMax = 0x7ffffffe, aiDefaults = []);
     584        asIds       = oDisp.getStringParam(self.ksParam_aidSchedGroups, sDefault = '').split(',');
     585        for idSchedGroup in asIds:
     586            try:    idSchedGroup = int(idSchedGroup);
     587            except: pass;
     588            oDispWrapper = self.DispWrapper(oDisp, '%s[%s][%%s]' % (TestBoxDataEx.ksParam_aoInSchedGroups, idSchedGroup,))
     589            oMember = TestBoxInSchedGroupData().initFromParams(oDispWrapper, fStrict = False);
     590            if idSchedGroup in aidSelected:
     591                aoNewValues.append(oMember);
     592        return aoNewValues;
     593
     594    def _validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb): # pylint: disable=R0914
     595        """
     596        Validate special arrays and requirement expressions.
     597
     598        Some special needs for the in-scheduling-group list.
     599        """
     600        if sAttr != 'aoInSchedGroups':
     601            return TestBoxData._validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb);
     602
     603        asErrors = [];
     604        aoNewValues = [];
     605
     606        # Note! We'll be returning an error dictionary instead of an string here.
     607        dErrors = {};
     608
     609        for iInGrp, oInSchedGroup in enumerate(self.aoInSchedGroups):
     610            oInSchedGroup = copy.copy(oInSchedGroup);
     611            oInSchedGroup.idTestBox = self.idTestBox;
     612            dCurErrors = oInSchedGroup.validateAndConvert(oDb, ModelDataBase.ksValidateFor_Other);
     613            if len(dCurErrors) == 0:
     614                pass; ## @todo figure out the ID?
     615            else:
     616                asErrors = [];
     617                for sKey in dCurErrors:
     618                    asErrors.append('%s: %s' % (sKey[len('TestBoxInSchedGroup_'):], dCurErrors[sKey]));
     619                dErrors[iInGrp] = '<br>\n'.join(asErrors)
     620            aoNewValues.append(oInSchedGroup);
     621
     622        for iInGrp, oInSchedGroup in enumerate(self.aoInSchedGroups):
     623            for iInGrp2 in xrange(iInGrp + 1, len(self.aoInSchedGroups)):
     624                if self.aoInSchedGroups[iInGrp2].idSchedGroup == oInSchedGroup.idSchedGroup:
     625                    sMsg = 'Duplicate scheduling group #%s".' % (oInSchedGroup.idSchedGroup,);
     626                    if iInGrp in dErrors:   dErrors[iInGrp]  += '<br>\n' + sMsg;
     627                    else:                   dErrors[iInGrp]   = sMsg;
     628                    if iInGrp2 in dErrors:  dErrors[iInGrp2] += '<br>\n' + sMsg;
     629                    else:                   dErrors[iInGrp2]  = sMsg;
     630                    break;
     631
     632        return (aoNewValues, dErrors if len(dErrors) > 0 else None);
     633
    446634
    447635class TestBoxLogic(ModelLogicBase):
     
    484672        Raises exception on error.
    485673        """
    486         class TestBoxDataForListing(TestBoxData):
     674        class TestBoxDataForListing(TestBoxDataEx):
    487675            """ We add two members for the listing. """
    488676            def __init__(self):
    489                 TestBoxData.__init__(self);
     677                TestBoxDataEx.__init__(self);
    490678                self.tsCurrent = None;  # CURRENT_TIMESTAMP
    491                 self.oStatus   = None;  # TestBoxStatusData
     679                self.oStatus   = None;  # type: TestBoxStatusData
    492680
    493681        from testmanager.core.testboxstatus import TestBoxStatusData;
     
    517705        aoRows = [];
    518706        for aoOne in self._oDb.fetchAll():
    519             oTestBox = TestBoxDataForListing().initFromDbRow(aoOne);
     707            oTestBox = TestBoxDataForListing().initFromDbRowEx(aoOne, self._oDb, tsNow);
    520708            oTestBox.tsCurrent = self._oDb.getCurrentTimestamp();
    521709            if aoOne[TestBoxData.kcDbColumns] is not None:
     
    532720        Raises exception on error.
    533721        """
     722
     723        ## @todo calc changes to scheduler group!
    534724
    535725        if tsNow is None:
     
    570760        return (aoEntries, len(aoRows) > cMaxRows);
    571761
     762    def _validateAndConvertData(self, oData, enmValidateFor):
     763        # type: (TestBoxDataEx, str) -> None
     764        """
     765        Helper for addEntry and editEntry that validates the scheduling group IDs in
     766        addtion to what's covered by the default validateAndConvert of the data object.
     767
     768        Raises exception on invalid input.
     769        """
     770        dDataErrors = oData.validateAndConvert(self._oDb, enmValidateFor);
     771        if len(dDataErrors) > 0:
     772            raise TMInvalidData('TestBoxLogic.addEntry: %s' % (dDataErrors,));
     773        if len(oData.aoInSchedGroups):
     774            sSchedGrps = ', '.join('(%s)' % oCur.idSchedGroup for oCur in oData.aoInSchedGroups);
     775            self._oDb.execute('SELECT   SchedGroupIDs.idSchedGroup\n'
     776                              'FROM     (VALUES ' + sSchedGrps + ' ) AS SchedGroupIDs(idSchedGroup)\n'
     777                              '         LEFT OUTER JOIN SchedGroups\n'
     778                              '                      ON     SchedGroupIDs.idSchedGroup = SchedGroups.idSchedGroup\n'
     779                              '                         AND SchedGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
     780                              'WHERE    SchedGroups.idSchedGroup IS NULL\n');
     781            aaoRows = self._oDb.fetchAll();
     782            if len(aaoRows) > 0:
     783                raise TMInvalidData('TestBoxLogic.addEntry missing scheduling groups: %s'
     784                                    % (', '.join(str(aoRow[0]) for aoRow in aaoRows),));
     785        return None;
    572786
    573787    def addEntry(self, oData, uidAuthor, fCommit = False):
     788        # type: (TestBoxDataEx, int, bool) -> (int, int, datetime.datetime)
    574789        """
    575790        Creates a testbox in the database.
     
    577792        of the created testbox on success.  Throws error on failure.
    578793        """
    579         dDataErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Add);
    580         if len(dDataErrors) > 0:
    581             raise TMInvalidData('Invalid data passed to create(): %s' % (dDataErrors,));
    582 
     794
     795        #
     796        # Validate. Extra work because of missing foreign key (due to history).
     797        #
     798        self._validateAndConvertData(oData, oData.ksValidateFor_Add);
     799
     800        #
     801        # Do it.
     802        #
    583803        self._oDb.callProc('TestBoxLogic_addEntry'
    584804                           , ( uidAuthor,
     
    587807                               oData.sName,
    588808                               oData.sDescription,
    589                                oData.idSchedGroup,
    590809                               oData.fEnabled,
    591810                               oData.enmLomKind,
     
    594813                               oData.sComment,
    595814                               oData.enmPendingCmd, ) );
    596         aoRow = self._oDb.fetchOne();
     815        (idTestBox, idGenTestBox, tsEffective) = self._oDb.fetchOne();
     816
     817        for oInSchedGrp in oData.aoInSchedGroups:
     818            self._oDb.callProc('TestBoxLogic_addGroupEntry',
     819                               ( uidAuthor, idTestBox, oInSchedGrp.idSchedGroup, oInSchedGrp.iSchedPriority,) );
     820
    597821        self._oDb.maybeCommit(fCommit);
    598         return (aoRow[0], aoRow[1], aoRow[2]);
     822        return (idTestBox, idGenTestBox, tsEffective);
    599823
    600824
     
    605829        """
    606830
    607         dDataErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Edit);
    608         if len(dDataErrors) > 0:
    609             raise TMInvalidData('Invalid data passed to create(): %s' % (dDataErrors,));
    610 
    611         self._oDb.callProc('TestBoxLogic_editEntry'
    612                            , ( uidAuthor,
    613                                oData.idTestBox,
    614                                oData.ip,            # Should we allow setting the IP?
    615                                oData.uuidSystem,
    616                                oData.sName,
    617                                oData.sDescription,
    618                                oData.idSchedGroup,
    619                                oData.fEnabled,
    620                                oData.enmLomKind,
    621                                oData.ipLom,
    622                                oData.pctScaleTimeout,
    623                                oData.sComment,
    624                                oData.enmPendingCmd, ));
    625         aoRow = self._oDb.fetchOne();
     831        #
     832        # Validate.
     833        #
     834        self._validateAndConvertData(oData, oData.ksValidateFor_Edit);
     835
     836        #
     837        # Get current data.
     838        #
     839        oOldData = TestBoxDataEx().initFromDbWithId(self._oDb, oData.idTestBox);
     840
     841        #
     842        # Do it.
     843        #
     844        if not oData.isEqualEx(oOldData, [ 'tsEffective', 'tsExpire', 'uidAuthor', 'aoInSchedGroups', ]
     845                                         + TestBoxData.kasMachineSettableOnly ):
     846            self._oDb.callProc('TestBoxLogic_editEntry'
     847                               , ( uidAuthor,
     848                                   oData.idTestBox,
     849                                   oData.ip,            # Should we allow setting the IP?
     850                                   oData.uuidSystem,
     851                                   oData.sName,
     852                                   oData.sDescription,
     853                                   oData.fEnabled,
     854                                   oData.enmLomKind,
     855                                   oData.ipLom,
     856                                   oData.pctScaleTimeout,
     857                                   oData.sComment,
     858                                   oData.enmPendingCmd, ));
     859            (idGenTestBox, tsEffective) = self._oDb.fetchOne();
     860        else:
     861            idGenTestBox = oOldData.idGenTestBox;
     862            tsEffective  = oOldData.tsEffective;
     863
     864        # Calc in-group changes.
     865        aoRemoved = list(oOldData.aoInSchedGroups);
     866        aoNew     = [];
     867        aoUpdated = [];
     868        for oNewInGroup in oData.aoInSchedGroups:
     869            oOldInGroup = None;
     870            for iCur, oCur in enumerate(aoRemoved):
     871                if oCur.idSchedGroup == oNewInGroup.idSchedGroup:
     872                    oOldInGroup = aoRemoved.pop(iCur);
     873                    break;
     874            if oOldInGroup is None:
     875                aoNew.append(oNewInGroup);
     876            elif oNewInGroup.iSchedPriority != oOldInGroup.iSchedPriority:
     877                aoUpdated.append(oNewInGroup);
     878
     879        # Remove in-groups.
     880        for oInGroup in aoRemoved:
     881            self._oDb.callProc('TestBoxLogic_removeGroupEntry', (uidAuthor, oData.idTestBox, oInGroup.idSchedGroup, ));
     882
     883        # Add new ones.
     884        for oInGroup in aoNew:
     885            self._oDb.callProc('TestBoxLogic_addGroupEntry',
     886                               ( uidAuthor, oData.idTestBox, oInGroup.idSchedGroup, oInGroup.iSchedPriority, ) );
     887
     888        # Edit existing ones.
     889        for oInGroup in aoUpdated:
     890            self._oDb.callProc('TestBoxLogic_editGroupEntry',
     891                               ( uidAuthor, oData.idTestBox, oInGroup.idSchedGroup, oInGroup.iSchedPriority, ) );
     892
    626893        self._oDb.maybeCommit(fCommit);
    627         return (aoRow[0], aoRow[1]);
     894        return (idGenTestBox, tsEffective);
    628895
    629896
    630897    def removeEntry(self, uidAuthor, idTestBox, fCascade = False, fCommit = False):
    631898        """
    632         Delete user account
     899        Delete test box and scheduling group associations.
    633900        """
    634901        self._oDb.callProc('TestBoxLogic_removeEntry'
     
    698965
    699966    def cachedLookup(self, idTestBox):
     967        # type: (int) -> TestBoxDataEx
    700968        """
    701969        Looks up the most recent TestBoxData object for idTestBox via
    702970        an object cache.
    703971
    704         Returns a shared TestBoxData object.  None if not found.
     972        Returns a shared TestBoxDataEx object.  None if not found.
    705973        Raises exception on DB error.
    706974        """
     
    709977        oEntry = self.dCache.get(idTestBox, None);
    710978        if oEntry is None:
     979            fNeedNow = False;
    711980            self._oDb.execute('SELECT   TestBoxesWithStrings.*\n'
    712981                              'FROM     TestBoxesWithStrings\n'
     
    722991                                  'LIMIT 1\n'
    723992                                  , (idTestBox, ));
     993                fNeedNow = True;
    724994            elif self._oDb.getRowCount() > 1:
    725995                raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), idTestBox));
     
    727997            if self._oDb.getRowCount() == 1:
    728998                aaoRow = self._oDb.fetchOne();
    729                 oEntry = TestBoxData().initFromDbRow(aaoRow);
     999                if not fNeedNow:
     1000                    oEntry = TestBoxDataEx().initFromDbRowEx(aaoRow, self._oDb);
     1001                else:
     1002                    oEntry = TestBoxDataEx().initFromDbRow(aaoRow);
     1003                    oEntry.initFromDbRowEx(aaoRow, self._oDb, tsNow = db.dbTimestampMinusOneTick(oEntry.tsExpire));
    7301004                self.dCache[idTestBox] = oEntry;
    7311005        return oEntry;
  • trunk/src/VBox/ValidationKit/testmanager/core/testboxcontroller.py

    r61474 r61502  
    571571          and oTestBoxData.fEnabled \
    572572          and oStatusData.enmState == TestBoxStatusData.ksTestBoxState_Idle: # (paranoia)
    573             dResponse = SchedulerBase.scheduleNewTask(oDb, oTestBoxData, self._oSrvGlue.getBaseUrl());
     573            dResponse = SchedulerBase.scheduleNewTask(oDb, oTestBoxData, oStatusData.iWorkItem, self._oSrvGlue.getBaseUrl());
    574574            if dResponse is not None:
    575575                return self._writeResponse(dResponse);
  • trunk/src/VBox/ValidationKit/testmanager/core/testboxstatus.py

    r61468 r61502  
    6262    ksParam_enmState            = 'TestBoxStatus_enmState';
    6363    ksParam_idTestSet           = 'TestBoxStatus_idTestSet';
     64    ksParam_iWorkItem           = 'TestBoxStatus_iWorkItem';
    6465
    6566    kasAllowNullAttributes      = ['idTestSet', ];
     
    7172        ksTestBoxState_DoingSpecialCmd,
    7273    ];
     74
     75    kcDbColumns                 = 6;
    7376
    7477    def __init__(self):
     
    8487        self.enmState            = self.ksTestBoxState_Idle;
    8588        self.idTestSet           = None;
     89        self.iWorkItem           = None;
    8690
    8791    def initFromDbRow(self, aoRow):
     
    99103        self.enmState            = aoRow[3];
    100104        self.idTestSet           = aoRow[4];
     105        self.iWorkItem           = aoRow[5];
    101106        return self;
    102107
     
    161166        """
    162167        self._oDb.execute('SELECT   TestBoxStatuses.*,\n'
    163                           '         TestBoxes.*,\n'
    164                           '         Str1.sValue,\n'
    165                           '         Str2.sValue,\n'
    166                           '         Str3.sValue,\n'
    167                           '         Str4.sValue,\n'
    168                           '         Str5.sValue,\n'
    169                           '         Str6.sValue,\n'
    170                           '         Str7.sValue,\n'
    171                           '         Str8.sValue\n'
     168                          '         TestBoxesWithStrings.*,\n'
    172169                          'FROM     TestBoxStatuses,\n'
    173                           '         TestBoxes\n'
    174                           '         LEFT OUTER JOIN TestBoxStrTab Str1 ON idStrDescription = Str1.idStr\n'
    175                           '         LEFT OUTER JOIN TestBoxStrTab Str2 ON idStrComment     = Str2.idStr\n'
    176                           '         LEFT OUTER JOIN TestBoxStrTab Str3 ON idStrOs          = Str3.idStr\n'
    177                           '         LEFT OUTER JOIN TestBoxStrTab Str4 ON idStrOsVersion   = Str4.idStr\n'
    178                           '         LEFT OUTER JOIN TestBoxStrTab Str5 ON idStrCpuVendor   = Str5.idStr\n'
    179                           '         LEFT OUTER JOIN TestBoxStrTab Str6 ON idStrCpuArch     = Str6.idStr\n'
    180                           '         LEFT OUTER JOIN TestBoxStrTab Str7 ON idStrCpuName     = Str7.idStr\n'
    181                           '         LEFT OUTER JOIN TestBoxStrTab Str8 ON idStrReport      = Str8.idStr\n'
    182                           'WHERE    TestBoxStatuses.idTestBox = %s\n'
    183                           '     AND TestBoxes.idTestBox  = %s\n'
    184                           '     AND TestBoxes.tsExpire   = \'infinity\'::TIMESTAMP\n'
    185                           '     AND TestBoxes.uuidSystem = %s\n'
    186                           '     AND TestBoxes.ip         = %s\n'
     170                          '         TestBoxesWithStrings\n'
     171                          'WHERE    TestBoxStatuses.idTestBox       = %s\n'
     172                          '     AND TestBoxesWithStrings.idTestBox  = %s\n'
     173                          '     AND TestBoxesWithStrings.tsExpire   = \'infinity\'::TIMESTAMP\n'
     174                          '     AND TestBoxesWithStrings.uuidSystem = %s\n'
     175                          '     AND TestBoxesWithStrings.ip         = %s\n'
    187176                          , (idTestBox,
    188177                             idTestBox,
     
    196185            return (None, None);
    197186        aoRow = self._oDb.fetchOne();
    198         return (TestBoxStatusData().initFromDbRow(aoRow[0:5]), TestBoxData().initFromDbRow(aoRow[5:]));
     187        return (TestBoxStatusData().initFromDbRow(aoRow[:TestBoxStatusData.kcDbColumns]),
     188                TestBoxData().initFromDbRow(aoRow[TestBoxStatusData.kcDbColumns:]));
    199189
    200190
     
    207197                          '         idGenTestBox,\n'
    208198                          '         enmState,\n'
    209                           '         idTestSet)\n'
     199                          '         idTestSet,\n'
     200                          '         iWorkItem)\n'
    210201                          'VALUES ( %s,\n'
    211202                          '         %s,\n'
    212203                          '         \'idle\'::TestBoxState_T,\n'
    213                           '         NULL)\n',
     204                          '         NULL,\n',
     205                          '         0)\n',
    214206                          (idTestBox, idGenTestBox) );
    215207        self._oDb.maybeCommit(fCommit);
     
    251243                          '                       WHERE  idTestSetGangLeader = %s)\n'
    252244                          , (sNewState, idTestSetGangLeader,) );
     245        self._oDb.maybeCommit(fCommit);
     246        return True;
     247
     248    def updateWorkItem(self, idTestBox, iWorkItem, fCommit = False):
     249        """
     250        Updates the testbox state.
     251        """
     252        self._oDb.execute('UPDATE   TestBoxStatuses\n'
     253                          'SET      iWorkItem = %s\n'
     254                          'WHERE    idTestBox = %s\n'
     255                          , ( iWorkItem, idTestBox,));
    253256        self._oDb.maybeCommit(fCommit);
    254257        return True;
  • trunk/src/VBox/ValidationKit/testmanager/webui/wuiadmin.py

    r61432 r61502  
    526526    def _actionTestBoxAdd(self):
    527527        """ Action wrapper. """
    528         from testmanager.core.testbox                  import TestBoxData;
     528        from testmanager.core.testbox                  import TestBoxDataEx;
    529529        from testmanager.webui.wuiadmintestbox         import WuiTestBox;
    530         return self._actionGenericFormAdd(TestBoxData, WuiTestBox);
     530        return self._actionGenericFormAdd(TestBoxDataEx, WuiTestBox);
    531531
    532532    def _actionTestBoxAddPost(self):
    533533        """ Action wrapper. """
    534         from testmanager.core.testbox                  import TestBoxData, TestBoxLogic;
     534        from testmanager.core.testbox                  import TestBoxDataEx, TestBoxLogic;
    535535        from testmanager.webui.wuiadmintestbox         import WuiTestBox;
    536         return self._actionGenericFormAddPost(TestBoxData, TestBoxLogic, WuiTestBox, self.ksActionTestBoxList);
     536        return self._actionGenericFormAddPost(TestBoxDataEx, TestBoxLogic, WuiTestBox, self.ksActionTestBoxList);
    537537
    538538    def _actionTestBoxDetails(self):
    539539        """ Action wrapper. """
    540         from testmanager.core.testbox                  import TestBoxData, TestBoxLogic;
     540        from testmanager.core.testbox                  import TestBoxDataEx, TestBoxLogic;
    541541        from testmanager.webui.wuiadmintestbox         import WuiTestBox;
    542         return self._actionGenericFormDetails(TestBoxData, TestBoxLogic, WuiTestBox, 'idTestBox', 'idGenTestBox');
     542        return self._actionGenericFormDetails(TestBoxDataEx, TestBoxLogic, WuiTestBox, 'idTestBox', 'idGenTestBox');
    543543
    544544    def _actionTestBoxEdit(self):
    545545        """ Action wrapper. """
    546         from testmanager.core.testbox                  import TestBoxData;
     546        from testmanager.core.testbox                  import TestBoxDataEx;
    547547        from testmanager.webui.wuiadmintestbox         import WuiTestBox;
    548         return self._actionGenericFormEdit(TestBoxData, WuiTestBox, TestBoxData.ksParam_idTestBox);
     548        return self._actionGenericFormEdit(TestBoxDataEx, WuiTestBox, TestBoxDataEx.ksParam_idTestBox);
    549549
    550550    def _actionTestBoxEditPost(self):
    551551        """ Action wrapper. """
    552         from testmanager.core.testbox                  import TestBoxData, TestBoxLogic;
     552        from testmanager.core.testbox                  import TestBoxDataEx, TestBoxLogic;
    553553        from testmanager.webui.wuiadmintestbox         import WuiTestBox;
    554         return self._actionGenericFormEditPost(TestBoxData, TestBoxLogic,WuiTestBox, self.ksActionTestBoxList);
     554        return self._actionGenericFormEditPost(TestBoxDataEx, TestBoxLogic,WuiTestBox, self.ksActionTestBoxList);
    555555
    556556    def _actionTestBoxRemovePost(self):
  • trunk/src/VBox/ValidationKit/testmanager/webui/wuiadmintestbox.py

    r61472 r61502  
    3939from testmanager.core.db                import TMDatabaseConnection;
    4040from testmanager.core.schedgroup        import SchedGroupLogic, SchedGroupData;
    41 from testmanager.core.testbox           import TestBoxData;
     41from testmanager.core.testbox           import TestBoxData, TestBoxDataEx;
    4242from testmanager.core.testset           import TestSetData;
    4343from testmanager.core.db                import isDbTimestampInfinity;
     
    102102        oForm.addText(       TestBoxData.ksParam_sName,             oData.sName, 'TestBox Name');
    103103        oForm.addText(       TestBoxData.ksParam_sDescription,      oData.sDescription, 'TestBox Description');
    104         oForm.addComboBox(   TestBoxData.ksParam_idSchedGroup,      oData.idSchedGroup, 'Scheduling Group',
    105                              SchedGroupLogic(TMDatabaseConnection()).getSchedGroupsForCombo());
    106104        oForm.addCheckBox(   TestBoxData.ksParam_fEnabled,          oData.fEnabled, 'Enabled');
    107105        oForm.addComboBox(   TestBoxData.ksParam_enmLomKind,        oData.enmLomKind, 'Lights-out-management',
     
    110108        oForm.addInt(        TestBoxData.ksParam_pctScaleTimeout,   oData.pctScaleTimeout, 'Timeout scale factor (%)');
    111109
    112         ## @todo Pretty format the read-only fields and use hidden fields for
    113         #        passing the actual values. (Yes, we need the values so we can
    114         #        display the form correctly on input error.)
     110        oForm.addListOfSchedGroupsForTestBox(TestBoxDataEx.ksParam_aoInSchedGroups,
     111                                             oData.aoInSchedGroups,
     112                                             SchedGroupLogic(TMDatabaseConnection()).fetchOrderedByName(),
     113                                             'Scheduling Group');
     114        # Command, comment and submit button.
     115        if self._sMode == WuiFormContentBase.ksMode_Edit:
     116            oForm.addComboBox(TestBoxData.ksParam_enmPendingCmd,    oData.enmPendingCmd, 'Pending command',
     117                              TestBoxData.kaoTestBoxCmdDescs);
     118        else:
     119            oForm.addComboBoxRO(TestBoxData.ksParam_enmPendingCmd,  oData.enmPendingCmd, 'Pending command',
     120                                TestBoxData.kaoTestBoxCmdDescs);
     121        oForm.addMultilineText(TestBoxData.ksParam_sComment,        oData.sComment, 'Comment');
     122        if self._sMode != WuiFormContentBase.ksMode_Show:
     123            oForm.addSubmit('Create TestBox' if self._sMode == WuiFormContentBase.ksMode_Add else 'Change TestBox');
     124
     125        return True;
     126
     127
     128    def _generatePostFormContent(self, oData):
     129        from testmanager.webui.wuihlpform import WuiHlpForm;
     130
     131        oForm = WuiHlpForm('testbox-machine-settable', '', fReadOnly = True);
    115132        oForm.addTextRO(     TestBoxData.ksParam_sOs,               oData.sOs, 'TestBox OS');
    116133        oForm.addTextRO(     TestBoxData.ksParam_sOsVersion,        oData.sOsVersion, 'TestBox OS version');
     
    138155        oForm.addIntRO(      TestBoxData.ksParam_iPythonHexVersion, oData.iPythonHexVersion,
    139156                             'Python version (hex)', sPostHtml = webutils.escapeElem(sHexVer));
    140 
    141         if self._sMode == WuiFormContentBase.ksMode_Edit:
    142             oForm.addComboBox(TestBoxData.ksParam_enmPendingCmd,    oData.enmPendingCmd, 'Pending command',
    143                               TestBoxData.kaoTestBoxCmdDescs);
    144         else:
    145             oForm.addComboBoxRO(TestBoxData.ksParam_enmPendingCmd,  oData.enmPendingCmd, 'Pending command',
    146                                 TestBoxData.kaoTestBoxCmdDescs);
    147 
    148         oForm.addMultilineText(TestBoxData.ksParam_sComment,        oData.sComment, 'Comment');
    149 
    150         if self._sMode != WuiFormContentBase.ksMode_Show:
    151             oForm.addSubmit('Create TestBox' if self._sMode == WuiFormContentBase.ksMode_Add else 'Change TestBox');
    152 
    153         return True;
     157        return [('Machine Only Settables', oForm.finalize()),];
     158
    154159
    155160
     
    173178
    174179    def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp):
     180        # type: (list[TestBoxDataForListing], int, int, datetime.datetime, ignore, WuiAdmin) -> None
    175181        WuiListContentWithActionBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective,
    176182                                              sTitle = 'TestBoxes', sId = 'users', fnDPrint = fnDPrint, oDisp = oDisp);
     
    237243        oComment = self._formatCommentCell(oEntry.sComment);
    238244
    239         # Group link.
    240         oGroup = self._dSchedGroups.get(oEntry.idSchedGroup);
    241         oGroupLink = WuiTmLink(oGroup.sName if oGroup is not None else str(oEntry.idSchedGroup),
    242                                WuiAdmin.ksScriptName,
    243                                { WuiAdmin.ksParamAction: WuiAdmin.ksActionSchedGroupEdit,
    244                                  SchedGroupData.ksParam_idSchedGroup: oEntry.idSchedGroup, },
    245                                sTitle = '#%u' % (oEntry.idSchedGroup,),
    246                                fBracketed = False);
     245        # Group links.
     246        aoGroups = [];
     247        for oInGroup in oEntry.aoInSchedGroups:
     248            oSchedGroup = oInGroup.oSchedGroup;
     249            aoGroups.append(WuiTmLink(oSchedGroup.sName, WuiAdmin.ksScriptName,
     250                                      { WuiAdmin.ksParamAction: WuiAdmin.ksActionSchedGroupEdit,
     251                                        SchedGroupData.ksParam_idSchedGroup: oSchedGroup.idSchedGroup, },
     252                                      sTitle = '#%u' % (oSchedGroup.idSchedGroup,),
     253                                      fBracketed = len(oEntry.aoInSchedGroups) > 1));
    247254
    248255        # Reformat the OS version to take less space.
     
    338345                 WuiSvnLink(oEntry.iTestBoxScriptRev),
    339346                 oEntry.formatPythonVersion(),
    340                  oGroupLink,
     347                 aoGroups,
    341348                 aoOs,
    342349                 oCpu,
  • trunk/src/VBox/ValidationKit/testmanager/webui/wuibase.py

    r61439 r61502  
    316316    #
    317317
    318     def getStringParam(self, sName, asValidValues = None, sDefault = None):
     318    def getStringParam(self, sName, asValidValues = None, sDefault = None, fAllowNull = False):
    319319        """
    320320        Gets a string parameter.
     
    328328                raise WuiException('%s parameter "%s" is given multiple times: "%s"' % (self._sAction, sName, sValue));
    329329            sValue = sValue.strip();
    330         elif sDefault is None:
     330        elif sDefault is None and fAllowNull is not True:
    331331            raise WuiException('%s is missing parameters: "%s"' % (self._sAction, sName,));
    332332        else:
  • trunk/src/VBox/ValidationKit/testmanager/webui/wuihlpform.py

    r61468 r61502  
    763763        For WuiAdminSchedGroup.
    764764        """
    765         if self._fReadOnly:
    766             fReadOnly = True;
     765        if fReadOnly is None or self._fReadOnly:
     766            fReadOnly = self._fReadOnly;
    767767        assert len(aoSchedGroupMembers) <= len(aoAllTestGroups);
    768768        self._addLabel(sName, sLabel);
     
    857857                         u'</table>\n');
    858858
     859    def addListOfSchedGroupsForTestBox(self, sName, aoInSchedGroups, aoAllSchedGroups, sLabel,  # pylint: disable=R0914
     860                                       fReadOnly = None):
     861        # type: (str, TestBoxInSchedGroupDataEx, SchedGroupData, str, bool) -> str
     862        """
     863        For WuiTestGroup.
     864        """
     865        from testmanager.core.testbox import TestBoxInSchedGroupData, TestBoxDataEx;
     866
     867        if fReadOnly is None or self._fReadOnly:
     868            fReadOnly = self._fReadOnly;
     869        assert len(aoInSchedGroups) <= len(aoAllSchedGroups);
     870
     871        # Only show selected groups in read-only mode.
     872        if fReadOnly:
     873            aoAllSchedGroups = [oCur.oSchedGroup for oCur in aoInSchedGroups]
     874
     875        self._addLabel(sName, sLabel);
     876        if len(aoAllSchedGroups) == 0:
     877            return self._add('<li>No scheduling groups available.</li>\n')
     878
     879        # Add special parameter with all the scheduling group IDs in the form.
     880        self._add(u'<input name="%s" type="hidden" value="%s">\n'
     881                  % ( TestBoxDataEx.ksParam_aidSchedGroups,
     882                      ','.join([unicode(oSchedGroup.idSchedGroup) for oSchedGroup in aoAllSchedGroups]), ));
     883
     884        # Table header.
     885        self._add(u'<table class="tmformtbl">\n'
     886                  u' <thead>\n'
     887                  u'  <tr>\n'
     888                  u'    <th rowspan="2"></th>\n'
     889                  u'    <th rowspan="2">Schedulding Grooup</th>\n'
     890                  u'    <th rowspan="2">Priority [0..31]</th>\n'
     891                  u'  </tr>\n'
     892                  u' </thead>\n'
     893                  u' <tbody>\n'
     894                  );
     895
     896        # Table body.
     897        if self._fReadOnly:
     898            fReadOnly = True;
     899        sCheckBoxAttr = ' readonly onclick="return false" onkeydown="return false"' if fReadOnly else '';
     900
     901        oDefMember = TestBoxInSchedGroupData();
     902        aoInSchedGroups = list(aoInSchedGroups); # Copy it so we can pop.
     903        for iSchedGroup, oSchedGroup in enumerate(aoAllSchedGroups):
     904
     905            # Is it a member?
     906            oMember = None;
     907            for i, _ in enumerate(aoInSchedGroups):
     908                if aoInSchedGroups[i].idSchedGroup == oSchedGroup.idSchedGroup:
     909                    oMember = aoInSchedGroups.pop(i);
     910                    break;
     911
     912            # Start on the rows...
     913            sPrefix = u'%s[%d]' % (sName, oSchedGroup.idSchedGroup,);
     914            self._add(u'  <tr class="%s">\n'
     915                      u'    <td>\n'
     916                      u'      <input name="%s[%s]" type="hidden" value="%s">\n' # idSchedGroup
     917                      u'      <input name="%s[%s]" type="hidden" value="%s">\n' # idTestBox
     918                      u'      <input name="%s[%s]" type="hidden" value="%s">\n' # tsExpire
     919                      u'      <input name="%s[%s]" type="hidden" value="%s">\n' # tsEffective
     920                      u'      <input name="%s[%s]" type="hidden" value="%s">\n' # uidAuthor
     921                      u'      <input name="%s" type="checkbox"%s%s value="%d" class="tmform-checkbox" title="#%d - %s">\n' #(list)
     922                      u'    </td>\n'
     923                      % ( 'tmodd' if iSchedGroup & 1 else 'tmeven',
     924                          sPrefix, TestBoxInSchedGroupData.ksParam_idSchedGroup,  oSchedGroup.idSchedGroup,
     925                          sPrefix, TestBoxInSchedGroupData.ksParam_idTestBox,   -1 if oMember is None else oMember.idTestBox,
     926                          sPrefix, TestBoxInSchedGroupData.ksParam_tsExpire,    '' if oMember is None else oMember.tsExpire,
     927                          sPrefix, TestBoxInSchedGroupData.ksParam_tsEffective, '' if oMember is None else oMember.tsEffective,
     928                          sPrefix, TestBoxInSchedGroupData.ksParam_uidAuthor,   '' if oMember is None else oMember.uidAuthor,
     929                          TestBoxDataEx.ksParam_aoInSchedGroups, '' if oMember is None else ' checked', sCheckBoxAttr,
     930                          oSchedGroup.idSchedGroup, oSchedGroup.idSchedGroup, escapeElem(oSchedGroup.sName),
     931                          ));
     932            self._add(u'    <td align="left">%s</td>\n' % ( escapeElem(oSchedGroup.sName), ));
     933
     934            self._add(u'    <td align="center">\n'
     935                      u'      <input name="%s[%s]" type="text" value="%s" style="max-width:3em;" %s>\n'
     936                      u'    </td>\n'
     937                      % ( sPrefix, TestBoxInSchedGroupData.ksParam_iSchedPriority,
     938                          (oMember if oMember is not None else oDefMember).iSchedPriority,
     939                          ' readonly class="tmform-input-readonly"' if fReadOnly else '', ));
     940            self._add(u'  </tr>\n');
     941
     942        return self._add(u' </tbody>\n'
     943                         u'</table>\n');
     944
     945
    859946    #
    860947    # Buttons.
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