def _doGangGathering(self, oDb, oStatusData):
     """
     _doRequestCommand worker for handling a box in gang-gathering state.
     This only checks for timeout.  It will update the oStatusData if a
     timeout is detected, so that the box will be idle upon return.
     """
     oStatusLogic = TestBoxStatusLogic(oDb);
     if     oStatusLogic.timeSinceLastChangeInSecs(oStatusData) > config.g_kcSecGangGathering \
        and SchedulerBase.tryCancelGangGathering(oDb, oStatusData): # <-- Updates oStatusData.
         self._doGangGatheringTimedOut(oDb, oStatusData);
     return None;
 def _doGangGathering(self, oDb, oStatusData):
     """
     _doRequestCommand worker for handling a box in gang-gathering state.
     This only checks for timeout.  It will update the oStatusData if a
     timeout is detected, so that the box will be idle upon return.
     """
     oStatusLogic = TestBoxStatusLogic(oDb)
     if     oStatusLogic.timeSinceLastChangeInSecs(oStatusData) > config.g_kcSecGangGathering \
        and SchedulerBase.tryCancelGangGathering(oDb, oStatusData): # <-- Updates oStatusData.
         self._doGangGatheringTimedOut(oDb, oStatusData)
     return None
    def _actionExecCompleted(self):
        """
        Implement EXEC completion.

        Because the action is request by the worker thread of the testbox
        script we cannot pass pending commands back to it like originally
        planned.  So, we just complete the test set and update the status.
        """
        #
        # Parameter validation.
        #
        sStatus = self._getStringParam(
            constants.tbreq.EXEC_COMPLETED_PARAM_RESULT,
            TestBoxController.kasValidResults)
        self._checkForUnknownParameters()

        (oDb, oStatusData, _) = self._connectToDbAndValidateTb([
            TestBoxStatusData.ksTestBoxState_Testing,
            TestBoxStatusData.ksTestBoxState_GangTesting
        ])
        if oStatusData is None:
            return False

        #
        # Complete the status.
        #
        oDb.rollback()
        oDb.begin()
        oTestSetLogic = TestSetLogic(oDb)
        idTestSetGangLeader = oTestSetLogic.complete(
            oStatusData.idTestSet,
            self.kadTbResultToStatus[sStatus],
            fCommit=False)

        oStatusLogic = TestBoxStatusLogic(oDb)
        if oStatusData.enmState == TestBoxStatusData.ksTestBoxState_Testing:
            assert idTestSetGangLeader is None
            GlobalResourceLogic(oDb).freeGlobalResourcesByTestBox(
                self._idTestBox)
            oStatusLogic.updateState(self._idTestBox,
                                     TestBoxStatusData.ksTestBoxState_Idle,
                                     fCommit=False)
        else:
            assert idTestSetGangLeader is not None
            oStatusLogic.updateState(
                self._idTestBox,
                TestBoxStatusData.ksTestBoxState_GangCleanup,
                oStatusData.idTestSet,
                fCommit=False)
            if oStatusLogic.isWholeGangDoneTesting(idTestSetGangLeader):
                GlobalResourceLogic(oDb).freeGlobalResourcesByTestBox(
                    self._idTestBox)
                oStatusLogic.updateState(self._idTestBox,
                                         TestBoxStatusData.ksTestBoxState_Idle,
                                         fCommit=False)

        oDb.commit()
        return self._resultResponse(constants.tbresp.STATUS_ACK)
    def _cleanupOldTest(self, oDb, oStatusData):
        """
        Cleans up any old test set that may be left behind and changes the
        state to 'idle'.  See scenario #9:
        file://../../docs/AutomaticTestingRevamp.html#cleaning-up-abandoned-testcase

        Note. oStatusData.enmState is set to idle, but tsUpdated is not changed.
        """

        # Cleanup any abandoned test.
        if oStatusData.idTestSet is not None:
            SystemLogLogic(oDb).addEntry(
                SystemLogData.ksEvent_TestSetAbandoned,
                "idTestSet=%u idTestBox=%u enmState=%s %s" %
                (oStatusData.idTestSet, oStatusData.idTestBox,
                 oStatusData.enmState, self._sAction),
                fCommit=False)
            TestSetLogic(oDb).completeAsAbandoned(oStatusData.idTestSet,
                                                  fCommit=False)
            GlobalResourceLogic(oDb).freeGlobalResourcesByTestBox(
                self._idTestBox, fCommit=False)

        # Change to idle status
        if oStatusData.enmState != TestBoxStatusData.ksTestBoxState_Idle:
            TestBoxStatusLogic(oDb).updateState(
                self._idTestBox,
                TestBoxStatusData.ksTestBoxState_Idle,
                fCommit=False)
            oStatusData.tsUpdated = oDb.getCurrentTimestamp()
            oStatusData.enmState = TestBoxStatusData.ksTestBoxState_Idle

        # Commit.
        oDb.commit()

        return True
    def _doCommandAckNck(self, sCmd):
        """ Implements ACK, NACK and NACK(ENOTSUP). """

        (oDb, _, _) = self._connectToDbAndValidateTb()
        if oDb is None:
            return False

        #
        # If the command maps to a TestBoxCmd_T value, it means we have to
        # check and update TestBoxes.  If it's an ACK, the testbox status will
        # need updating as well.
        #
        sPendingCmd = TestBoxController.kdTbRespCmdToCmd[sCmd]
        if sPendingCmd is not None:
            oTestBoxLogic = TestBoxLogic(oDb)
            oTestBoxLogic.setCommand(self._idTestBox,
                                     sOldCommand=sPendingCmd,
                                     sNewCommand=TestBoxData.ksTestBoxCmd_None,
                                     fCommit=False)

            if    self._sAction == constants.tbreq.COMMAND_ACK \
              and TestBoxController.kdCmdToState[sPendingCmd] is not None:
                oStatusLogic = TestBoxStatusLogic(oDb)
                oStatusLogic.updateState(
                    self._idTestBox,
                    TestBoxController.kdCmdToState[sPendingCmd],
                    fCommit=False)

            # Commit the two updates.
            oDb.commit()

        #
        # Log NACKs.
        #
        if self._sAction != constants.tbreq.COMMAND_ACK:
            oSysLogLogic = SystemLogLogic(oDb)
            oSysLogLogic.addEntry(SystemLogData.ksEvent_CmdNacked,
                                  'idTestBox=%s sCmd=%s' %
                                  (self._idTestBox, sPendingCmd),
                                  24,
                                  fCommit=True)

        return self._resultResponse(constants.tbresp.STATUS_ACK)
Пример #6
0
    def _doGangCleanup(self, oDb, oStatusData):
        """
        _doRequestCommand worker for handling a box in gang-cleanup.
        This will check if all testboxes has completed their run, pretending to
        be busy until that happens.  Once all are completed, resources will be
        freed and the testbox returns to idle state (we update oStatusData).
        """
        oStatusLogic = TestBoxStatusLogic(oDb)
        oTestSet = TestSetData().initFromDbWithId(oDb, oStatusData.idTestSet);
        if oStatusLogic.isWholeGangDoneTesting(oTestSet.idTestSetGangLeader):
            oDb.begin();

            GlobalResourceLogic(oDb).freeGlobalResourcesByTestBox(self._idTestBox, fCommit = False);
            TestBoxStatusLogic(oDb).updateState(self._idTestBox, TestBoxStatusData.ksTestBoxState_Idle, fCommit = False);

            oStatusData.tsUpdated = oDb.getCurrentTimestamp();
            oStatusData.enmState = TestBoxStatusData.ksTestBoxState_Idle;

            oDb.commit();
        return None;
Пример #7
0
    def _connectToDbAndValidateTb(self, asValidStates = None):
        """
        Connects to the database and validates the testbox.

        Returns (TMDatabaseConnection, TestBoxStatusData, TestBoxData) on success.
        Returns (None, None, None) on failure after sending the box an appropriate response.
        May raise exception on DB error.
        """
        oDb    = TMDatabaseConnection(self._oSrvGlue.dprint);
        oLogic = TestBoxStatusLogic(oDb);
        (oStatusData, oTestBoxData) = oLogic.tryFetchStatusAndConfig(self._idTestBox, self._sTestBoxUuid, self._sTestBoxAddr);
        if oStatusData is None:
            self._resultResponse(constants.tbresp.STATUS_DEAD);
        elif asValidStates is not None and oStatusData.enmState not in asValidStates:
            self._resultResponse(constants.tbresp.STATUS_NACK);
        elif self._idTestSet is not None and self._idTestSet != oStatusData.idTestSet:
            self._resultResponse(constants.tbresp.STATUS_NACK);
        else:
            return (oDb, oStatusData, oTestBoxData);
        return (None, None, None);
    def _doGangCleanup(self, oDb, oStatusData):
        """
        _doRequestCommand worker for handling a box in gang-cleanup.
        This will check if all testboxes has completed their run, pretending to
        be busy until that happens.  Once all are completed, resources will be
        freed and the testbox returns to idle state (we update oStatusData).
        """
        oStatusLogic = TestBoxStatusLogic(oDb)
        oTestSet = TestSetData().initFromDbWithId(oDb, oStatusData.idTestSet);
        if oStatusLogic.isWholeGangDoneTesting(oTestSet.idTestSetGangLeader):
            oDb.begin();

            GlobalResourceLogic(oDb).freeGlobalResourcesByTestBox(self._idTestBox, fCommit = False);
            TestBoxStatusLogic(oDb).updateState(self._idTestBox, TestBoxStatusData.ksTestBoxState_Idle, fCommit = False);

            oStatusData.tsUpdated = oDb.getCurrentTimestamp();
            oStatusData.enmState = TestBoxStatusData.ksTestBoxState_Idle;

            oDb.commit();
        return None;
    def _connectToDbAndValidateTb(self, asValidStates = None):
        """
        Connects to the database and validates the testbox.

        Returns (TMDatabaseConnection, TestBoxStatusData, TestBoxData) on success.
        Returns (None, None, None) on failure after sending the box an appropriate response.
        May raise exception on DB error.
        """
        oDb    = TMDatabaseConnection(self._oSrvGlue.dprint);
        oLogic = TestBoxStatusLogic(oDb);
        (oStatusData, oTestBoxData) = oLogic.tryFetchStatusAndConfig(self._idTestBox, self._sTestBoxUuid, self._sTestBoxAddr);
        if oStatusData is None:
            self._resultResponse(constants.tbresp.STATUS_DEAD);
        elif asValidStates is not None and oStatusData.enmState not in asValidStates:
            self._resultResponse(constants.tbresp.STATUS_NACK);
        elif self._idTestSet is not None and self._idTestSet != oStatusData.idTestSet:
            self._resultResponse(constants.tbresp.STATUS_NACK);
        else:
            return (oDb, oStatusData, oTestBoxData);
        return (None, None, None);
    def _doCommandAckNck(self, sCmd):
        """ Implements ACK, NACK and NACK(ENOTSUP). """

        (oDb, _, _) = self._connectToDbAndValidateTb();
        if oDb is None:
            return False;

        #
        # If the command maps to a TestBoxCmd_T value, it means we have to
        # check and update TestBoxes.  If it's an ACK, the testbox status will
        # need updating as well.
        #
        sPendingCmd = TestBoxController.kdTbRespCmdToCmd[sCmd];
        if sPendingCmd is not None:
            oTestBoxLogic = TestBoxLogic(oDb)
            oTestBoxLogic.setCommand(self._idTestBox, sOldCommand = sPendingCmd,
                                     sNewCommand = TestBoxData.ksTestBoxCmd_None, fCommit = False);

            if    self._sAction == constants.tbreq.COMMAND_ACK \
              and TestBoxController.kdCmdToState[sPendingCmd] is not None:
                oStatusLogic = TestBoxStatusLogic(oDb);
                oStatusLogic.updateState(self._idTestBox, TestBoxController.kdCmdToState[sPendingCmd], fCommit = False);

            # Commit the two updates.
            oDb.commit();

        #
        # Log NACKs.
        #
        if self._sAction != constants.tbreq.COMMAND_ACK:
            oSysLogLogic = SystemLogLogic(oDb);
            oSysLogLogic.addEntry(SystemLogData.ksEvent_CmdNacked,
                                  'idTestBox=%s sCmd=%s' % (self._idTestBox, sPendingCmd),
                                  24, fCommit = True);

        return self._resultResponse(constants.tbresp.STATUS_ACK);
Пример #11
0
    def _doGangGatheringTimedOut(self, oDb, oStatusData):
        """
        _doRequestCommand worker for handling a box in gang-gathering-timed-out state.
        This will do clean-ups similar to _cleanupOldTest and update the state likewise.
        """
        oDb.begin();

        TestSetLogic(oDb).completeAsGangGatheringTimeout(oStatusData.idTestSet, fCommit = False);
        GlobalResourceLogic(oDb).freeGlobalResourcesByTestBox(self._idTestBox, fCommit = False);
        TestBoxStatusLogic(oDb).updateState(self._idTestBox, TestBoxStatusData.ksTestBoxState_Idle, fCommit = False);

        oStatusData.tsUpdated = oDb.getCurrentTimestamp();
        oStatusData.enmState  = TestBoxStatusData.ksTestBoxState_Idle;

        oDb.commit();
        return None;
    def _actionExecCompleted(self):
        """
        Implement EXEC completion.

        Because the action is request by the worker thread of the testbox
        script we cannot pass pending commands back to it like originally
        planned.  So, we just complete the test set and update the status.
        """
        #
        # Parameter validation.
        #
        sStatus = self._getStringParam(constants.tbreq.EXEC_COMPLETED_PARAM_RESULT, TestBoxController.kasValidResults);
        self._checkForUnknownParameters();

        (oDb, oStatusData, _) = self._connectToDbAndValidateTb([TestBoxStatusData.ksTestBoxState_Testing,
                                                                TestBoxStatusData.ksTestBoxState_GangTesting]);
        if oStatusData is None:
            return False;

        #
        # Complete the status.
        #
        oDb.rollback();
        oDb.begin();
        oTestSetLogic = TestSetLogic(oDb);
        idTestSetGangLeader = oTestSetLogic.complete(oStatusData.idTestSet, self.kadTbResultToStatus[sStatus], fCommit = False);

        oStatusLogic = TestBoxStatusLogic(oDb);
        if oStatusData.enmState == TestBoxStatusData.ksTestBoxState_Testing:
            assert idTestSetGangLeader is None;
            GlobalResourceLogic(oDb).freeGlobalResourcesByTestBox(self._idTestBox);
            oStatusLogic.updateState(self._idTestBox, TestBoxStatusData.ksTestBoxState_Idle, fCommit = False);
        else:
            assert idTestSetGangLeader is not None;
            oStatusLogic.updateState(self._idTestBox, TestBoxStatusData.ksTestBoxState_GangCleanup, oStatusData.idTestSet,
                                     fCommit = False);
            if oStatusLogic.isWholeGangDoneTesting(idTestSetGangLeader):
                GlobalResourceLogic(oDb).freeGlobalResourcesByTestBox(self._idTestBox);
                oStatusLogic.updateState(self._idTestBox, TestBoxStatusData.ksTestBoxState_Idle, fCommit = False);

        oDb.commit();
        return self._resultResponse(constants.tbresp.STATUS_ACK);
    def _doRequestCommand(self, fIdle):
        """
        Common code for handling command request.
        """

        (oDb, oStatusData, oTestBoxData) = self._connectToDbAndValidateTb()
        if oDb is None:
            return False

        #
        # Status clean up.
        #
        # Only when BUSY will the TestBox Script request and execute commands
        # concurrently.  So, it must be idle when sending REQUEST_COMMAND_IDLE.
        #
        if fIdle:
            if oStatusData.enmState == TestBoxStatusData.ksTestBoxState_GangGathering:
                self._doGangGathering(oDb, oStatusData)
            elif oStatusData.enmState == TestBoxStatusData.ksTestBoxState_GangGatheringTimedOut:
                self._doGangGatheringTimedOut(oDb, oStatusData)
            elif oStatusData.enmState == TestBoxStatusData.ksTestBoxState_GangTesting:
                dResponse = SchedulerBase.composeExecResponse(
                    oDb, oTestBoxData.idTestBox, self._oSrvGlue.getBaseUrl())
                if dResponse is not None:
                    return dResponse
            elif oStatusData.enmState == TestBoxStatusData.ksTestBoxState_GangCleanup:
                self._doGangCleanup(oDb, oStatusData)
            elif oStatusData.enmState != TestBoxStatusData.ksTestBoxState_Idle:  # (includes ksTestBoxState_GangGatheringTimedOut)
                self._cleanupOldTest(oDb, oStatusData)

        #
        # Check for pending command.
        #
        if oTestBoxData.enmPendingCmd != TestBoxData.ksTestBoxCmd_None:
            asValidCmds = TestBoxController.kasIdleCmds if fIdle else TestBoxController.kasBusyCmds
            if oTestBoxData.enmPendingCmd in asValidCmds:
                dResponse = {
                    constants.tbresp.ALL_PARAM_RESULT:
                    TestBoxController.kdCmdToTbRespCmd[
                        oTestBoxData.enmPendingCmd]
                }
                if oTestBoxData.enmPendingCmd in [
                        TestBoxData.ksTestBoxCmd_Upgrade,
                        TestBoxData.ksTestBoxCmd_UpgradeAndReboot
                ]:
                    dResponse[constants.tbresp.
                              UPGRADE_PARAM_URL] = self._oSrvGlue.getBaseUrl(
                              ) + TestBoxController.ksUpgradeZip
                return self._writeResponse(dResponse)

            if oTestBoxData.enmPendingCmd == TestBoxData.ksTestBoxCmd_Abort and fIdle:
                TestBoxLogic(oDb).setCommand(
                    self._idTestBox,
                    sOldCommand=oTestBoxData.enmPendingCmd,
                    sNewCommand=TestBoxData.ksTestBoxCmd_None,
                    fCommit=True)

        #
        # If doing gang stuff, return 'CMD_WAIT'.
        #
        ## @todo r=bird: Why is GangTesting included here? Figure out when testing gang testing.
        if oStatusData.enmState in [
                TestBoxStatusData.ksTestBoxState_GangGathering,
                TestBoxStatusData.ksTestBoxState_GangTesting,
                TestBoxStatusData.ksTestBoxState_GangCleanup
        ]:
            return self._resultResponse(constants.tbresp.CMD_WAIT)

        #
        # If idling and enabled try schedule a new task.
        #
        if    fIdle \
          and oTestBoxData.fEnabled \
          and not TestSetLogic(oDb).isTestBoxExecutingToRapidly(oTestBoxData.idTestBox) \
          and oStatusData.enmState == TestBoxStatusData.ksTestBoxState_Idle: # (paranoia)
            dResponse = SchedulerBase.scheduleNewTask(
                oDb, oTestBoxData, oStatusData.iWorkItem,
                self._oSrvGlue.getBaseUrl())
            if dResponse is not None:
                return self._writeResponse(dResponse)

        #
        # Touch the status row every couple of mins so we can tell that the box is alive.
        #
        oStatusLogic = TestBoxStatusLogic(oDb)
        if    oStatusData.enmState != TestBoxStatusData.ksTestBoxState_GangGathering \
          and oStatusLogic.timeSinceLastChangeInSecs(oStatusData) >= TestBoxStatusLogic.kcSecIdleTouchStatus:
            oStatusLogic.touchStatus(oTestBoxData.idTestBox, fCommit=True)

        return self._idleResponse()
    def _actionSignOn(self):  # pylint: disable=R0914
        """ Implement sign-on """

        #
        # Validate parameters (raises exception on failure).
        #
        sOs = self._getStringParam(constants.tbreq.SIGNON_PARAM_OS,
                                   coreconsts.g_kasOses)
        sOsVersion = self._getStringParam(
            constants.tbreq.SIGNON_PARAM_OS_VERSION)
        sCpuVendor = self._getStringParam(
            constants.tbreq.SIGNON_PARAM_CPU_VENDOR)
        sCpuArch = self._getStringParam(constants.tbreq.SIGNON_PARAM_CPU_ARCH,
                                        coreconsts.g_kasCpuArches)
        sCpuName = self._getStringParam(constants.tbreq.SIGNON_PARAM_CPU_NAME,
                                        fStrip=True,
                                        sDefValue='')
        # new
        lCpuRevision = self._getLongParam(
            constants.tbreq.SIGNON_PARAM_CPU_REVISION, lMin=0, lDefValue=0)
        # new
        cCpus = self._getIntParam(constants.tbreq.SIGNON_PARAM_CPU_COUNT, 1,
                                  16384)
        fCpuHwVirt = self._getBoolParam(
            constants.tbreq.SIGNON_PARAM_HAS_HW_VIRT)
        fCpuNestedPaging = self._getBoolParam(
            constants.tbreq.SIGNON_PARAM_HAS_NESTED_PAGING)
        fCpu64BitGuest = self._getBoolParam(
            constants.tbreq.SIGNON_PARAM_HAS_64_BIT_GUEST, fDefValue=True)
        fChipsetIoMmu = self._getBoolParam(
            constants.tbreq.SIGNON_PARAM_HAS_IOMMU)
        fRawMode = self._getBoolParam(
            constants.tbreq.SIGNON_PARAM_WITH_RAW_MODE, fDefValue=None)
        cMbMemory = self._getLongParam(constants.tbreq.SIGNON_PARAM_MEM_SIZE,
                                       8, 1073741823)
        # 8MB..1PB
        cMbScratch = self._getLongParam(
            constants.tbreq.SIGNON_PARAM_SCRATCH_SIZE, 0, 1073741823)
        # 0..1PB
        sReport = self._getStringParam(constants.tbreq.SIGNON_PARAM_REPORT,
                                       fStrip=True,
                                       sDefValue='')
        # new
        iTestBoxScriptRev = self._getIntParam(
            constants.tbreq.SIGNON_PARAM_SCRIPT_REV, 1, 100000000)
        iPythonHexVersion = self._getIntParam(
            constants.tbreq.SIGNON_PARAM_PYTHON_VERSION, 0x020300f0,
            0x030f00f0)
        self._checkForUnknownParameters()

        # Null conversions for new parameters.
        if not sReport:
            sReport = None
        if not sCpuName:
            sCpuName = None
        if lCpuRevision <= 0:
            lCpuRevision = None

        #
        # Connect to the database and validate the testbox.
        #
        oDb = TMDatabaseConnection(self._oSrvGlue.dprint)
        oTestBoxLogic = TestBoxLogic(oDb)
        oTestBox = oTestBoxLogic.tryFetchTestBoxByUuid(self._sTestBoxUuid)
        if oTestBox is None:
            oSystemLogLogic = SystemLogLogic(oDb)
            oSystemLogLogic.addEntry(SystemLogData.ksEvent_TestBoxUnknown,
                                     'addr=%s  uuid=%s  os=%s  %d cpus' \
                                     % (self._sTestBoxAddr, self._sTestBoxUuid, sOs, cCpus),
                                     24, fCommit = True)
            return self._resultResponse(constants.tbresp.STATUS_NACK)

        #
        # Update the row in TestBoxes if something changed.
        #
        if oTestBox.cMbScratch is not None and oTestBox.cMbScratch != 0:
            cPctScratchDiff = (cMbScratch -
                               oTestBox.cMbScratch) * 100 / oTestBox.cMbScratch
        else:
            cPctScratchDiff = 100

        # pylint: disable=R0916
        if   self._sTestBoxAddr != oTestBox.ip \
          or sOs                != oTestBox.sOs \
          or sOsVersion         != oTestBox.sOsVersion \
          or sCpuVendor         != oTestBox.sCpuVendor \
          or sCpuArch           != oTestBox.sCpuArch \
          or sCpuName           != oTestBox.sCpuName \
          or lCpuRevision       != oTestBox.lCpuRevision \
          or cCpus              != oTestBox.cCpus \
          or fCpuHwVirt         != oTestBox.fCpuHwVirt \
          or fCpuNestedPaging   != oTestBox.fCpuNestedPaging \
          or fCpu64BitGuest     != oTestBox.fCpu64BitGuest \
          or fChipsetIoMmu      != oTestBox.fChipsetIoMmu \
          or fRawMode           != oTestBox.fRawMode \
          or cMbMemory          != oTestBox.cMbMemory \
          or abs(cPctScratchDiff) >= min(4 + cMbScratch / 10240, 12) \
          or sReport            != oTestBox.sReport \
          or iTestBoxScriptRev  != oTestBox.iTestBoxScriptRev \
          or iPythonHexVersion  != oTestBox.iPythonHexVersion:
            oTestBoxLogic.updateOnSignOn(oTestBox.idTestBox,
                                         oTestBox.idGenTestBox,
                                         sTestBoxAddr=self._sTestBoxAddr,
                                         sOs=sOs,
                                         sOsVersion=sOsVersion,
                                         sCpuVendor=sCpuVendor,
                                         sCpuArch=sCpuArch,
                                         sCpuName=sCpuName,
                                         lCpuRevision=lCpuRevision,
                                         cCpus=cCpus,
                                         fCpuHwVirt=fCpuHwVirt,
                                         fCpuNestedPaging=fCpuNestedPaging,
                                         fCpu64BitGuest=fCpu64BitGuest,
                                         fChipsetIoMmu=fChipsetIoMmu,
                                         fRawMode=fRawMode,
                                         cMbMemory=cMbMemory,
                                         cMbScratch=cMbScratch,
                                         sReport=sReport,
                                         iTestBoxScriptRev=iTestBoxScriptRev,
                                         iPythonHexVersion=iPythonHexVersion)

        #
        # Update the testbox status, making sure there is a status.
        #
        oStatusLogic = TestBoxStatusLogic(oDb)
        oStatusData = oStatusLogic.tryFetchStatus(oTestBox.idTestBox)
        if oStatusData is not None:
            self._cleanupOldTest(oDb, oStatusData)
        else:
            oStatusLogic.insertIdleStatus(oTestBox.idTestBox,
                                          oTestBox.idGenTestBox,
                                          fCommit=True)

        #
        # ACK the request.
        #
        dResponse = \
        {
            constants.tbresp.ALL_PARAM_RESULT:  constants.tbresp.STATUS_ACK,
            constants.tbresp.SIGNON_PARAM_ID:   oTestBox.idTestBox,
            constants.tbresp.SIGNON_PARAM_NAME: oTestBox.sName,
        }
        return self._writeResponse(dResponse)
    def _doRequestCommand(self, fIdle):
        """
        Common code for handling command request.
        """

        (oDb, oStatusData, oTestBoxData) = self._connectToDbAndValidateTb();
        if oDb is None:
            return False;

        #
        # Status clean up.
        #
        # Only when BUSY will the TestBox Script request and execute commands
        # concurrently.  So, it must be idle when sending REQUEST_COMMAND_IDLE.
        #
        if fIdle:
            if oStatusData.enmState == TestBoxStatusData.ksTestBoxState_GangGathering:
                self._doGangGathering(oDb, oStatusData);
            elif oStatusData.enmState == TestBoxStatusData.ksTestBoxState_GangGatheringTimedOut:
                self._doGangGatheringTimedOut(oDb, oStatusData);
            elif oStatusData.enmState == TestBoxStatusData.ksTestBoxState_GangTesting:
                dResponse = SchedulerBase.composeExecResponse(oDb, oTestBoxData.idTestBox, self._oSrvGlue.getBaseUrl());
                if dResponse is not None:
                    return dResponse;
            elif oStatusData.enmState == TestBoxStatusData.ksTestBoxState_GangCleanup:
                self._doGangCleanup(oDb, oStatusData);
            elif oStatusData.enmState != TestBoxStatusData.ksTestBoxState_Idle: # (includes ksTestBoxState_GangGatheringTimedOut)
                self._cleanupOldTest(oDb, oStatusData);

        #
        # Check for pending command.
        #
        if oTestBoxData.enmPendingCmd != TestBoxData.ksTestBoxCmd_None:
            asValidCmds = TestBoxController.kasIdleCmds if fIdle else TestBoxController.kasBusyCmds;
            if oTestBoxData.enmPendingCmd in asValidCmds:
                dResponse = { constants.tbresp.ALL_PARAM_RESULT: TestBoxController.kdCmdToTbRespCmd[oTestBoxData.enmPendingCmd] };
                if oTestBoxData.enmPendingCmd in [TestBoxData.ksTestBoxCmd_Upgrade, TestBoxData.ksTestBoxCmd_UpgradeAndReboot]:
                    dResponse[constants.tbresp.UPGRADE_PARAM_URL] = self._oSrvGlue.getBaseUrl() + TestBoxController.ksUpgradeZip;
                return self._writeResponse(dResponse);

            if oTestBoxData.enmPendingCmd == TestBoxData.ksTestBoxCmd_Abort and fIdle:
                TestBoxLogic(oDb).setCommand(self._idTestBox, sOldCommand = oTestBoxData.enmPendingCmd,
                                             sNewCommand = TestBoxData.ksTestBoxCmd_None, fCommit = True);

        #
        # If doing gang stuff, return 'CMD_WAIT'.
        #
        ## @todo r=bird: Why is GangTesting included here? Figure out when testing gang testing.
        if oStatusData.enmState in [TestBoxStatusData.ksTestBoxState_GangGathering,
                                    TestBoxStatusData.ksTestBoxState_GangTesting,
                                    TestBoxStatusData.ksTestBoxState_GangCleanup]:
            return self._resultResponse(constants.tbresp.CMD_WAIT);

        #
        # If idling and enabled try schedule a new task.
        #
        if fIdle \
          and oTestBoxData.fEnabled \
          and oStatusData.enmState == TestBoxStatusData.ksTestBoxState_Idle: # (paranoia)
            dResponse = SchedulerBase.scheduleNewTask(oDb, oTestBoxData, self._oSrvGlue.getBaseUrl());
            if dResponse is not None:
                return self._writeResponse(dResponse);

        #
        # Touch the status row every couple of mins so we can tell that the box is alive.
        #
        oStatusLogic = TestBoxStatusLogic(oDb);
        if    oStatusData.enmState != TestBoxStatusData.ksTestBoxState_GangGathering \
          and oStatusLogic.timeSinceLastChangeInSecs(oStatusData) >= TestBoxStatusLogic.kcSecIdleTouchStatus:
            oStatusLogic.touchStatus(oTestBoxData.idTestBox, fCommit = True);

        return self._idleResponse();
    def _actionSignOn(self):        # pylint: disable=R0914
        """ Implement sign-on """

        #
        # Validate parameters (raises exception on failure).
        #
        sOs                 = self._getStringParam(constants.tbreq.SIGNON_PARAM_OS, coreconsts.g_kasOses);
        sOsVersion          = self._getStringParam(constants.tbreq.SIGNON_PARAM_OS_VERSION);
        sCpuVendor          = self._getStringParam(constants.tbreq.SIGNON_PARAM_CPU_VENDOR);
        sCpuArch            = self._getStringParam(constants.tbreq.SIGNON_PARAM_CPU_ARCH, coreconsts.g_kasCpuArches);
        sCpuName            = self._getStringParam(constants.tbreq.SIGNON_PARAM_CPU_NAME, fStrip = True, sDefValue = ''); # new
        lCpuRevision        = self._getLongParam(  constants.tbreq.SIGNON_PARAM_CPU_REVISION, lMin = 0, lDefValue = 0);   # new
        cCpus               = self._getIntParam(   constants.tbreq.SIGNON_PARAM_CPU_COUNT, 1, 16384);
        fCpuHwVirt          = self._getBoolParam(  constants.tbreq.SIGNON_PARAM_HAS_HW_VIRT);
        fCpuNestedPaging    = self._getBoolParam(  constants.tbreq.SIGNON_PARAM_HAS_NESTED_PAGING);
        fCpu64BitGuest      = self._getBoolParam(  constants.tbreq.SIGNON_PARAM_HAS_64_BIT_GUEST, fDefValue = True);
        fChipsetIoMmu       = self._getBoolParam(  constants.tbreq.SIGNON_PARAM_HAS_IOMMU);
        cMbMemory           = self._getLongParam(  constants.tbreq.SIGNON_PARAM_MEM_SIZE,     8, 1073741823); # 8MB..1PB
        cMbScratch          = self._getLongParam(  constants.tbreq.SIGNON_PARAM_SCRATCH_SIZE, 0, 1073741823); # 0..1PB
        sReport             = self._getStringParam(constants.tbreq.SIGNON_PARAM_REPORT, fStrip = True, sDefValue = '');   # new
        iTestBoxScriptRev   = self._getIntParam(   constants.tbreq.SIGNON_PARAM_SCRIPT_REV, 1, 100000000);
        iPythonHexVersion   = self._getIntParam(   constants.tbreq.SIGNON_PARAM_PYTHON_VERSION, 0x020300f0, 0x030f00f0);
        self._checkForUnknownParameters();

        # Null conversions for new parameters.
        if len(sReport) == 0:
            sReport = None;
        if len(sCpuName) == 0:
            sCpuName = None;
        if lCpuRevision <= 0:
            lCpuRevision = None;

        #
        # Connect to the database and validate the testbox.
        #
        oDb = TMDatabaseConnection(self._oSrvGlue.dprint);
        oTestBoxLogic = TestBoxLogic(oDb);
        oTestBox      = oTestBoxLogic.tryFetchTestBoxByUuid(self._sTestBoxUuid);
        if oTestBox is None:
            oSystemLogLogic = SystemLogLogic(oDb);
            oSystemLogLogic.addEntry(SystemLogData.ksEvent_TestBoxUnknown,
                                     'addr=%s  uuid=%s  os=%s  %d cpus' \
                                     % (self._sTestBoxAddr, self._sTestBoxUuid, sOs, cCpus),
                                     24, fCommit = True);
            return self._resultResponse(constants.tbresp.STATUS_NACK);

        #
        # Update the row in TestBoxes if something changed.
        #
        if   self._sTestBoxAddr != oTestBox.ip \
          or sOs                != oTestBox.sOs \
          or sOsVersion         != oTestBox.sOsVersion \
          or sCpuVendor         != oTestBox.sCpuVendor \
          or sCpuArch           != oTestBox.sCpuArch \
          or sCpuName           != oTestBox.sCpuName \
          or lCpuRevision       != oTestBox.lCpuRevision \
          or cCpus              != oTestBox.cCpus \
          or fCpuHwVirt         != oTestBox.fCpuHwVirt \
          or fCpuNestedPaging   != oTestBox.fCpuNestedPaging \
          or fCpu64BitGuest     != oTestBox.fCpu64BitGuest \
          or fChipsetIoMmu      != oTestBox.fChipsetIoMmu \
          or cMbMemory          != oTestBox.cMbMemory \
          or cMbScratch         != oTestBox.cMbScratch \
          or sReport            != oTestBox.sReport \
          or iTestBoxScriptRev  != oTestBox.iTestBoxScriptRev \
          or iPythonHexVersion  != oTestBox.iPythonHexVersion:
            oTestBoxLogic.updateOnSignOn(oTestBox.idTestBox,
                                         oTestBox.idGenTestBox,
                                         sTestBoxAddr      = self._sTestBoxAddr,
                                         sOs               = sOs,
                                         sOsVersion        = sOsVersion,
                                         sCpuVendor        = sCpuVendor,
                                         sCpuArch          = sCpuArch,
                                         sCpuName          = sCpuName,
                                         lCpuRevision      = lCpuRevision,
                                         cCpus             = cCpus,
                                         fCpuHwVirt        = fCpuHwVirt,
                                         fCpuNestedPaging  = fCpuNestedPaging,
                                         fCpu64BitGuest    = fCpu64BitGuest,
                                         fChipsetIoMmu     = fChipsetIoMmu,
                                         cMbMemory         = cMbMemory,
                                         cMbScratch        = cMbScratch,
                                         sReport           = sReport,
                                         iTestBoxScriptRev = iTestBoxScriptRev,
                                         iPythonHexVersion = iPythonHexVersion);

        #
        # Update the testbox status, making sure there is a status.
        #
        oStatusLogic = TestBoxStatusLogic(oDb);
        oStatusData  = oStatusLogic.tryFetchStatus(oTestBox.idTestBox);
        if oStatusData is not None:
            self._cleanupOldTest(oDb, oStatusData);
        else:
            oStatusLogic.insertIdleStatus(oTestBox.idTestBox, oTestBox.idGenTestBox, fCommit = True);

        #
        # ACK the request.
        #
        dResponse = \
        {
            constants.tbresp.ALL_PARAM_RESULT:  constants.tbresp.STATUS_ACK,
            constants.tbresp.SIGNON_PARAM_ID:   oTestBox.idTestBox,
            constants.tbresp.SIGNON_PARAM_NAME: oTestBox.sName,
        }
        return self._writeResponse(dResponse);