Exemplo n.º 1
0
 def __init__(self,
              aoEntries,
              iPage,
              cItemsPerPage,
              tsEffective,
              fnDPrint,
              oDisp,
              cDaysBack,
              aiSelectedSortColumns=None):
     WuiListContentBase.__init__(
         self,
         aoEntries,
         iPage,
         cItemsPerPage,
         tsEffective,
         'System Changelog',
         fnDPrint=fnDPrint,
         oDisp=oDisp,
         aiSelectedSortColumns=aiSelectedSortColumns)
     self._asColumnHeaders = ['When', 'User', 'Event', 'Details']
     self._asColumnAttribs = ['align="center"', 'align="center"', '', '']
     self._oBuildBlacklistLogic = BuildBlacklistLogic(oDisp.getDb())
     self._oBuildLogic = BuildLogic(oDisp.getDb())
     self._oBuildSourceLogic = BuildSourceLogic(oDisp.getDb())
     self._oFailureCategoryLogic = FailureCategoryLogic(oDisp.getDb())
     self._oFailureReasonLogic = FailureReasonLogic(oDisp.getDb())
     self._oGlobalResourceLogic = GlobalResourceLogic(oDisp.getDb())
     self._oSchedGroupLogic = SchedGroupLogic(oDisp.getDb())
     self._oTestBoxLogic = TestBoxLogic(oDisp.getDb())
     self._oTestCaseLogic = TestCaseLogic(oDisp.getDb())
     self._oTestGroupLogic = TestGroupLogic(oDisp.getDb())
     self._oUserAccountLogic = UserAccountLogic(oDisp.getDb())
     self._sPrevDate = ''
     _ = cDaysBack
Exemplo n.º 2
0
    def removeEntry(self, uidAuthor, idSchedGroup, fCascade = False, fCommit = False):
        """
        Deletes a scheduling group.
        """

        #
        # Input validation and retrival of current data.
        #
        if idSchedGroup == 1:
            raise TMExceptionBase('Cannot remove the default scheduling group (id 1).');
        oData = SchedGroupDataEx().initFromDbWithId(self._oDb, idSchedGroup);

        #
        # We use cascade a little different here... We don't actually delete
        # associated testboxes or testgroups.
        #
        if len(oData.aoTestBoxes) > 0:
            if fCascade is not True:
                # Complain about there being associated testboxes.
                asTestBoxes = ['%s (#%d)' % (oTestBox.sName, oTestBox.idTestBox) for oTestBox in oData.aoTestBoxes];
                raise TMExceptionBase('Scheduling group #%d is associated with one or more test boxes: %s'
                                      % (idSchedGroup, ', '.join(asTestBoxes),));
            else:
                # Reassign testboxes to scheduling group #1 (the default group).
                oTbLogic = TestBoxLogic(self._oDb);
                for oTestBox in oData.aoTestBoxes:
                    oTbCopy = TestBoxData().initFromOther(oTestBox);
                    oTbCopy.idSchedGroup = 1;
                    oTbLogic.editEntry(oTbCopy, uidAuthor, fCommit = False);

                oData = SchedGroupDataEx().initFromDbWithId(self._oDb, idSchedGroup);
                if len(oData.aoTestBoxes) != 0:
                    raise TMExceptionBase('More testboxes was added to the scheduling group as we were trying to delete it.');

        #
        # Remove the group and all member records.
        #
        for oMember in oData.aoMembers:
            self._removeSchedGroupMember(uidAuthor, oMember);
        self._oDb.execute('UPDATE   SchedGroupMembers\n'
                          'SET      tsExpire     = CURRENT_TIMESTAMP\n'
                          'WHERE    idSchedGroup = %s\n'
                          '     AND tsExpire     = \'infinity\'::TIMESTAMP\n'
                          , (idSchedGroup,));

        (tsCur, tsCurMinusOne) = self._oDb.getCurrentTimestamps();
        if oData.tsEffective != tsCur and oData.tsEffective != tsCurMinusOne:
            self._historizeEntry(idSchedGroup, tsCurMinusOne);
            self._readdEntry(uidAuthor, oData, tsCurMinusOne);
            self._historizeEntry(idSchedGroup);
        self._oDb.execute('UPDATE   SchedGroups\n'
                          'SET      tsExpire     = CURRENT_TIMESTAMP\n'
                          'WHERE    idSchedGroup = %s\n'
                          '     AND tsExpire     = \'infinity\'::TIMESTAMP\n'
                          , (idSchedGroup,))

        self._oDb.maybeCommit(fCommit)
        return True;
Exemplo n.º 3
0
    def removeEntry(self, uidAuthor, idSchedGroup, fCascade = False, fCommit = False):
        """
        Deletes a scheduling group.
        """

        #
        # Input validation and retrival of current data.
        #
        if idSchedGroup == 1:
            raise TMRowInUse('Cannot remove the default scheduling group (id 1).');
        oData = SchedGroupDataEx().initFromDbWithId(self._oDb, idSchedGroup);

        #
        # We use cascade a little different here... We don't actually delete
        # associated testboxes or testgroups.
        #
        if oData.aoTestBoxes:
            if fCascade is not True:
                # Complain about there being associated testboxes.
                asTestBoxes = ['%s (#%d)' % (oTestBox.sName, oTestBox.idTestBox) for oTestBox in oData.aoTestBoxes];
                raise TMRowInUse('Scheduling group #%d is associated with one or more test boxes: %s'
                                 % (idSchedGroup, ', '.join(asTestBoxes),));
            else:
                # Reassign testboxes to scheduling group #1 (the default group).
                oTbLogic = TestBoxLogic(self._oDb);
                for oTestBox in oData.aoTestBoxes:
                    oTbCopy = TestBoxData().initFromOther(oTestBox);
                    oTbCopy.idSchedGroup = 1;
                    oTbLogic.editEntry(oTbCopy, uidAuthor, fCommit = False);

                oData = SchedGroupDataEx().initFromDbWithId(self._oDb, idSchedGroup);
                if oData.aoTestBoxes:
                    raise TMRowInUse('More testboxes was added to the scheduling group as we were trying to delete it.');

        #
        # Remove the group and all member records.
        #
        for oMember in oData.aoMembers:
            self._removeSchedGroupMember(uidAuthor, oMember);
        self._oDb.execute('UPDATE   SchedGroupMembers\n'
                          'SET      tsExpire     = CURRENT_TIMESTAMP\n'
                          'WHERE    idSchedGroup = %s\n'
                          '     AND tsExpire     = \'infinity\'::TIMESTAMP\n'
                          , (idSchedGroup,));

        (tsCur, tsCurMinusOne) = self._oDb.getCurrentTimestamps();
        if oData.tsEffective != tsCur and oData.tsEffective != tsCurMinusOne:
            self._historizeEntry(idSchedGroup, tsCurMinusOne);
            self._readdEntry(uidAuthor, oData, tsCurMinusOne);
            self._historizeEntry(idSchedGroup);
        self._oDb.execute('UPDATE   SchedGroups\n'
                          'SET      tsExpire     = CURRENT_TIMESTAMP\n'
                          'WHERE    idSchedGroup = %s\n'
                          '     AND tsExpire     = \'infinity\'::TIMESTAMP\n'
                          , (idSchedGroup,))

        self._oDb.maybeCommit(fCommit)
        return True;
Exemplo n.º 4
0
    def __init__(self, oData, sMode, oDisp):
        assert isinstance(oData, SchedGroupData)
        if sMode == WuiFormContentBase.ksMode_Add:
            sTitle = 'New Scheduling Group'
        elif sMode == WuiFormContentBase.ksMode_Edit:
            sTitle = 'Edit Scheduling Group'
        else:
            assert sMode == WuiFormContentBase.ksMode_Show
            sTitle = 'Scheduling Group'
        WuiFormContentBase.__init__(self, oData, sMode, 'SchedGroup', oDisp,
                                    sTitle)

        # Read additional bits form the DB, unless we're in
        if sMode != WuiFormContentBase.ksMode_Show:
            self._aoAllRelevantTestGroups = TestGroupLogic(
                oDisp.getDb()).getAll()
            self._aoAllRelevantTestBoxes = TestBoxLogic(
                oDisp.getDb()).getAll()
        else:
            self._aoAllRelevantTestGroups = [
                oMember.oTestGroup for oMember in oData.aoMembers
            ]
            self._aoAllRelevantTestBoxes = [
                oMember.oTestBox for oMember in oData.aoTestBoxes
            ]
Exemplo n.º 5
0
    def _actionTestBoxListPost(self):
        """Actions on a list of testboxes."""

        # Parameters.
        aidTestBoxes = self.getListOfIntParams(TestBoxData.ksParam_idTestBox, iMin = 1, aiDefaults = []);
        sListAction  = self.getStringParam(self.ksParamListAction);
        if sListAction in [asDesc[0] for asDesc in WuiTestBoxList.kasTestBoxActionDescs]:
            idAction = None;
        else:
            asActionPrefixes = [ 'setgroup-', ];
            i = 0;
            while i < len(asActionPrefixes) and not sListAction.startswith(asActionPrefixes[i]):
                i += 1;
            if i >= len(asActionPrefixes):
                raise WuiException('Parameter "%s" has an invalid value: "%s"' % (self.ksParamListAction, sListAction,));
            idAction = sListAction[len(asActionPrefixes[i]):];
            if not idAction.isdigit():
                raise WuiException('Parameter "%s" has an invalid value: "%s"' % (self.ksParamListAction, sListAction,));
            idAction = int(idAction);
            sListAction = sListAction[:len(asActionPrefixes[i]) - 1];
        self._checkForUnknownParameters();


        # Take action.
        if sListAction is 'none':
            pass;
        else:
            oLogic = TestBoxLogic(self._oDb);
            aoTestBoxes = []
            for idTestBox in aidTestBoxes:
                aoTestBoxes.append(TestBoxData().initFromDbWithId(self._oDb, idTestBox));

            if sListAction in [ 'enable', 'disable' ]:
                fEnable = sListAction == 'enable';
                for oTestBox in aoTestBoxes:
                    if oTestBox.fEnabled != fEnable:
                        oTestBox.fEnabled = fEnable;
                        oLogic.editEntry(oTestBox, self._oCurUser.uid, fCommit = False);
            elif sListAction == 'setgroup':
                for oTestBox in aoTestBoxes:
                    if oTestBox.idSchedGroup != idAction:
                        oTestBox.idSchedGroup = idAction;
                        oLogic.editEntry(oTestBox, self._oCurUser.uid, fCommit = False);
            else:
                for oTestBox in aoTestBoxes:
                    if oTestBox.enmPendingCmd != sListAction:
                        oTestBox.enmPendingCmd = sListAction;
                        oLogic.editEntry(oTestBox, self._oCurUser.uid, fCommit = False);
            self._oDb.commit();

        # Re-display the list.
        self._sPageTitle  = None;
        self._sPageBody   = None;
        self._sRedirectTo = self._sActionUrlBase + self.ksActionTestBoxList;
        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)
Exemplo n.º 7
0
    def _actionTestBoxListPost(self):
        """Actions on a list of testboxes."""

        # Parameters.
        aidTestBoxes = self.getListOfIntParams(TestBoxData.ksParam_idTestBox, iMin = 1, aiDefaults = []);
        sListAction  = self.getStringParam(self.ksParamListAction);
        if sListAction in [asDesc[0] for asDesc in WuiTestBoxList.kasTestBoxActionDescs]:
            idAction = None;
        else:
            asActionPrefixes = [ 'setgroup-', ];
            i = 0;
            while i < len(asActionPrefixes) and not sListAction.startswith(asActionPrefixes[i]):
                i += 1;
            if i >= len(asActionPrefixes):
                raise WuiException('Parameter "%s" has an invalid value: "%s"' % (self.ksParamListAction, sListAction,));
            idAction = sListAction[len(asActionPrefixes[i]):];
            if not idAction.isdigit():
                raise WuiException('Parameter "%s" has an invalid value: "%s"' % (self.ksParamListAction, sListAction,));
            idAction = int(idAction);
            sListAction = sListAction[:len(asActionPrefixes[i]) - 1];
        self._checkForUnknownParameters();


        # Take action.
        if sListAction is 'none':
            pass;
        else:
            oLogic = TestBoxLogic(self._oDb);
            aoTestBoxes = []
            for idTestBox in aidTestBoxes:
                aoTestBoxes.append(TestBoxData().initFromDbWithId(self._oDb, idTestBox));

            if sListAction in [ 'enable', 'disable' ]:
                fEnable = sListAction == 'enable';
                for oTestBox in aoTestBoxes:
                    if oTestBox.fEnabled != fEnable:
                        oTestBox.fEnabled = fEnable;
                        oLogic.editEntry(oTestBox, self._oCurUser.uid, fCommit = False);
            elif sListAction == 'setgroup':
                for oTestBox in aoTestBoxes:
                    if oTestBox.idSchedGroup != idAction:
                        oTestBox.idSchedGroup = idAction;
                        oLogic.editEntry(oTestBox, self._oCurUser.uid, fCommit = False);
            else:
                for oTestBox in aoTestBoxes:
                    if oTestBox.enmPendingCmd != sListAction:
                        oTestBox.enmPendingCmd = sListAction;
                        oLogic.editEntry(oTestBox, self._oCurUser.uid, fCommit = False);
            self._oDb.commit();

        # Re-display the list.
        self._sPageTitle  = None;
        self._sPageBody   = None;
        self._sRedirectTo = self._sActionUrlBase + self.ksActionTestBoxList;
        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);
 def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, cDaysBack, aiSelectedSortColumns = None):
     WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, 'System Changelog',
                                 fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns);
     self._asColumnHeaders = [ 'When', 'User', 'Event', 'Details' ];
     self._asColumnAttribs = [ 'align="center"', 'align="center"', '', '' ];
     self._oBuildBlacklistLogic  = BuildBlacklistLogic(oDisp.getDb());
     self._oBuildLogic           = BuildLogic(oDisp.getDb());
     self._oBuildSourceLogic     = BuildSourceLogic(oDisp.getDb());
     self._oFailureCategoryLogic = FailureCategoryLogic(oDisp.getDb());
     self._oFailureReasonLogic   = FailureReasonLogic(oDisp.getDb());
     self._oGlobalResourceLogic  = GlobalResourceLogic(oDisp.getDb());
     self._oSchedGroupLogic      = SchedGroupLogic(oDisp.getDb());
     self._oTestBoxLogic         = TestBoxLogic(oDisp.getDb());
     self._oTestCaseLogic        = TestCaseLogic(oDisp.getDb());
     self._oTestGroupLogic       = TestGroupLogic(oDisp.getDb());
     self._oUserAccountLogic     = UserAccountLogic(oDisp.getDb());
     self._sPrevDate             = '';
     _ = cDaysBack;
    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)
class WuiAdminSystemChangelogList(WuiListContentBase):
    """
    WUI System Changelog Content Generator.
    """

    def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, cDaysBack, aiSelectedSortColumns = None):
        WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, 'System Changelog',
                                    fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns);
        self._asColumnHeaders = [ 'When', 'User', 'Event', 'Details' ];
        self._asColumnAttribs = [ 'align="center"', 'align="center"', '', '' ];
        self._oBuildBlacklistLogic  = BuildBlacklistLogic(oDisp.getDb());
        self._oBuildLogic           = BuildLogic(oDisp.getDb());
        self._oBuildSourceLogic     = BuildSourceLogic(oDisp.getDb());
        self._oFailureCategoryLogic = FailureCategoryLogic(oDisp.getDb());
        self._oFailureReasonLogic   = FailureReasonLogic(oDisp.getDb());
        self._oGlobalResourceLogic  = GlobalResourceLogic(oDisp.getDb());
        self._oSchedGroupLogic      = SchedGroupLogic(oDisp.getDb());
        self._oTestBoxLogic         = TestBoxLogic(oDisp.getDb());
        self._oTestCaseLogic        = TestCaseLogic(oDisp.getDb());
        self._oTestGroupLogic       = TestGroupLogic(oDisp.getDb());
        self._oUserAccountLogic     = UserAccountLogic(oDisp.getDb());
        self._sPrevDate             = '';
        _ = cDaysBack;

    #   oDetails = self._createBlacklistingDetailsLink(oEntry.idWhat, oEntry.tsEffective);
    def _createBlacklistingDetailsLink(self, idBlacklisting, tsEffective):
        """ Creates a link to the build source details. """
        oBlacklisting = self._oBuildBlacklistLogic.cachedLookup(idBlacklisting);
        if oBlacklisting is not None:
            from testmanager.webui.wuiadmin import WuiAdmin;
            return WuiAdminLink('Blacklisting #%u' % (oBlacklisting.idBlacklisting,),
                                WuiAdmin.ksActionBuildBlacklistDetails, tsEffective,
                                { BuildBlacklistData.ksParam_idBlacklisting: oBlacklisting.idBlacklisting },
                                fBracketed = False);
        return WuiElementText('[blacklisting #%u not found]' % (idBlacklisting,));

    def _createBuildDetailsLink(self, idBuild, tsEffective):
        """ Creates a link to the build details. """
        oBuild = self._oBuildLogic.cachedLookup(idBuild);
        if oBuild is not None:
            from testmanager.webui.wuiadmin import WuiAdmin;
            return WuiAdminLink('%s %sr%u' % ( oBuild.oCat.sProduct, oBuild.sVersion, oBuild.iRevision),
                                WuiAdmin.ksActionBuildDetails, tsEffective,
                                { BuildData.ksParam_idBuild: oBuild.idBuild },
                                fBracketed = False,
                                sTitle = 'build #%u for %s, type %s'
                                       % (oBuild.idBuild, ' & '.join(oBuild.oCat.asOsArches), oBuild.oCat.sType));
        return WuiElementText('[build #%u not found]' % (idBuild,));

    def _createBuildSourceDetailsLink(self, idBuildSrc, tsEffective):
        """ Creates a link to the build source details. """
        oBuildSource = self._oBuildSourceLogic.cachedLookup(idBuildSrc);
        if oBuildSource is not None:
            from testmanager.webui.wuiadmin import WuiAdmin;
            return WuiAdminLink(oBuildSource.sName, WuiAdmin.ksActionBuildSrcDetails, tsEffective,
                                { BuildSourceData.ksParam_idBuildSrc: oBuildSource.idBuildSrc },
                                fBracketed = False,
                                sTitle = 'Build source #%u' % (oBuildSource.idBuildSrc,));
        return WuiElementText('[build source #%u not found]' % (idBuildSrc,));

    def _createFailureCategoryDetailsLink(self, idFailureCategory, tsEffective):
        """ Creates a link to the failure category details. """
        oFailureCategory = self._oFailureCategoryLogic.cachedLookup(idFailureCategory);
        if oFailureCategory is not None:
            from testmanager.webui.wuiadmin import WuiAdmin;
            return WuiAdminLink(oFailureCategory.sShort, WuiAdmin.ksActionFailureCategoryDetails, tsEffective,
                                { FailureCategoryData.ksParam_idFailureCategory: oFailureCategory.idFailureCategory },
                                fBracketed = False,
                                sTitle = 'Failure category #%u' % (oFailureCategory.idFailureCategory,));
        return WuiElementText('[failure category #%u not found]' % (idFailureCategory,));

    def _createFailureReasonDetailsLink(self, idFailureReason, tsEffective):
        """ Creates a link to the failure reason details. """
        oFailureReason = self._oFailureReasonLogic.cachedLookup(idFailureReason);
        if oFailureReason is not None:
            from testmanager.webui.wuiadmin import WuiAdmin;
            return WuiAdminLink(oFailureReason.sShort, WuiAdmin.ksActionFailureReasonDetails, tsEffective,
                                { FailureReasonData.ksParam_idFailureReason: oFailureReason.idFailureReason },
                                fBracketed = False,
                                sTitle = 'Failure reason #%u, category %s'
                                       % (oFailureReason.idFailureReason, oFailureReason.oCategory.sShort));
        return WuiElementText('[failure reason #%u not found]' % (idFailureReason,));

    def _createGlobalResourceDetailsLink(self, idGlobalRsrc, tsEffective):
        """ Creates a link to the global resource details. """
        oGlobalResource = self._oGlobalResourceLogic.cachedLookup(idGlobalRsrc);
        if oGlobalResource is not None:
            return WuiAdminLink(oGlobalResource.sName, '@todo', tsEffective,
                                { GlobalResourceData.ksParam_idGlobalRsrc: oGlobalResource.idGlobalRsrc },
                                fBracketed = False,
                                sTitle = 'Global resource #%u' % (oGlobalResource.idGlobalRsrc,));
        return WuiElementText('[global resource #%u not found]' % (idGlobalRsrc,));

    def _createSchedGroupDetailsLink(self, idSchedGroup, tsEffective):
        """ Creates a link to the scheduling group details. """
        oSchedGroup = self._oSchedGroupLogic.cachedLookup(idSchedGroup);
        if oSchedGroup is not None:
            from testmanager.webui.wuiadmin import WuiAdmin;
            return WuiAdminLink(oSchedGroup.sName, WuiAdmin.ksActionSchedGroupDetails, tsEffective,
                                { SchedGroupData.ksParam_idSchedGroup: oSchedGroup.idSchedGroup },
                                fBracketed = False,
                                sTitle = 'Scheduling group #%u' % (oSchedGroup.idSchedGroup,));
        return WuiElementText('[scheduling group #%u not found]' % (idSchedGroup,));

    def _createTestBoxDetailsLink(self, idTestBox, tsEffective):
        """ Creates a link to the testbox details. """
        oTestBox = self._oTestBoxLogic.cachedLookup(idTestBox);
        if oTestBox is not None:
            from testmanager.webui.wuiadmin import WuiAdmin;
            return WuiAdminLink(oTestBox.sName, WuiAdmin.ksActionTestBoxDetails, tsEffective,
                                { TestBoxData.ksParam_idTestBox: oTestBox.idTestBox },
                                fBracketed = False, sTitle = 'Testbox #%u' % (oTestBox.idTestBox,));
        return WuiElementText('[testbox #%u not found]' % (idTestBox,));

    def _createTestCaseDetailsLink(self, idTestCase, tsEffective):
        """ Creates a link to the test case details. """
        oTestCase = self._oTestCaseLogic.cachedLookup(idTestCase);
        if oTestCase is not None:
            from testmanager.webui.wuiadmin import WuiAdmin;
            return WuiAdminLink(oTestCase.sName, WuiAdmin.ksActionTestCaseDetails, tsEffective,
                                { TestCaseData.ksParam_idTestCase: oTestCase.idTestCase },
                                fBracketed = False, sTitle = 'Test case #%u' % (oTestCase.idTestCase,));
        return WuiElementText('[test case #%u not found]' % (idTestCase,));

    def _createTestGroupDetailsLink(self, idTestGroup, tsEffective):
        """ Creates a link to the test group details. """
        oTestGroup = self._oTestGroupLogic.cachedLookup(idTestGroup);
        if oTestGroup is not None:
            from testmanager.webui.wuiadmin import WuiAdmin;
            return WuiAdminLink(oTestGroup.sName, WuiAdmin.ksActionTestGroupDetails, tsEffective,
                                { TestGroupData.ksParam_idTestGroup: oTestGroup.idTestGroup },
                                fBracketed = False, sTitle = 'Test group #%u' % (oTestGroup.idTestGroup,));
        return WuiElementText('[test group #%u not found]' % (idTestGroup,));

    def _createTestSetResultsDetailsLink(self, idTestSet, tsEffective):
        """ Creates a link to the test set results. """
        _ = tsEffective;
        from testmanager.webui.wuimain import WuiMain;
        return WuiMainLink('test set #%u' % idTestSet, WuiMain.ksActionTestSetDetails,
                           { TestSetData.ksParam_idTestSet: idTestSet }, fBracketed = False);

    def _createTestSetDetailsLinkByResult(self, idTestResult, tsEffective):
        """ Creates a link to the test set results. """
        _ = tsEffective;
        from testmanager.webui.wuimain import WuiMain;
        return WuiMainLink('test result #%u' % idTestResult, WuiMain.ksActionTestSetDetailsFromResult,
                           { TestSetData.ksParam_idTestResult: idTestResult }, fBracketed = False);

    def _createUserAccountDetailsLink(self, uid, tsEffective):
        """ Creates a link to the user account details. """
        oUser = self._oUserAccountLogic.cachedLookup(uid);
        if oUser is not None:
            return WuiAdminLink(oUser.sUsername, '@todo', tsEffective, { UserAccountData.ksParam_uid: oUser.uid },
                                fBracketed = False, sTitle = '%s (#%u)' % (oUser.sFullName, oUser.uid));
        return WuiElementText('[user #%u not found]' % (uid,));

    def _formatDescGeneric(self, sDesc, oEntry):
        """
        Generically format system log the description.
        """
        oRet = WuiHtmlKeeper();
        asWords = sDesc.split();
        for sWord in asWords:
            offEqual = sWord.find('=');
            if offEqual > 0:
                sKey = sWord[:offEqual];
                try:    idValue = int(sWord[offEqual+1:].rstrip('.,'));
                except: pass;
                else:
                    if sKey == 'idTestSet':
                        oRet.append(self._createTestSetResultsDetailsLink(idValue, oEntry.tsEffective));
                        continue;
                    if sKey == 'idTestBox':
                        oRet.append(self._createTestBoxDetailsLink(idValue, oEntry.tsEffective));
                        continue;
                    if sKey == 'idSchedGroup':
                        oRet.append(self._createSchedGroupDetailsLink(idValue, oEntry.tsEffective));
                        continue;

            oRet.append(WuiElementText(sWord));
        return oRet;

    def _formatListEntryHtml(self, iEntry): # pylint: disable=too-many-statements
        """
        Overridden parent method.
        """
        oEntry    = self._aoEntries[iEntry];
        sRowClass = 'tmodd' if (iEntry + 1) & 1 else 'tmeven';
        sHtml     = u'';

        #
        # Format the timestamp.
        #
        sDate = self.formatTsShort(oEntry.tsEffective);
        if sDate[:10] != self._sPrevDate:
            self._sPrevDate = sDate[:10];
            sHtml += '  <tr class="%s tmdaterow" align="left"><td colspan="7">%s</td></tr>\n' % (sRowClass, sDate[:10],);
        sDate = sDate[11:]

        #
        # System log events.
        # pylint: disable=redefined-variable-type
        #
        aoChanges = None;
        if   oEntry.sEvent == SystemLogData.ksEvent_CmdNacked:
            sEvent = 'Command not acknowleged';
            oDetails = oEntry.sDesc;

        elif oEntry.sEvent == SystemLogData.ksEvent_TestBoxUnknown:
            sEvent = 'Unknown testbox';
            oDetails = oEntry.sDesc;

        elif oEntry.sEvent == SystemLogData.ksEvent_TestSetAbandoned:
            sEvent = 'Abandoned ' if oEntry.sDesc.startswith('idTestSet') else 'Abandoned test set';
            oDetails = self._formatDescGeneric(oEntry.sDesc, oEntry);

        elif oEntry.sEvent == SystemLogData.ksEvent_UserAccountUnknown:
            sEvent = 'Unknown user account';
            oDetails = oEntry.sDesc;

        elif oEntry.sEvent == SystemLogData.ksEvent_XmlResultMalformed:
            sEvent = 'Malformed XML result';
            oDetails = oEntry.sDesc;

        elif oEntry.sEvent == SystemLogData.ksEvent_SchedQueueRecreate:
            sEvent = 'Recreating scheduling queue';
            asWords = oEntry.sDesc.split();
            if len(asWords) > 3 and asWords[0] == 'User' and asWords[1][0] == '#':
                try:    idAuthor = int(asWords[1][1:]);
                except: pass;
                else:
                    oEntry.oAuthor = self._oUserAccountLogic.cachedLookup(idAuthor);
                    if oEntry.oAuthor is not None:
                        i = 2;
                        if asWords[i] == 'recreated':   i += 1;
                        oEntry.sDesc = ' '.join(asWords[i:]);
            oDetails = self._formatDescGeneric(oEntry.sDesc.replace('sched queue #', 'for scheduling group idSchedGroup='),
                                               oEntry);
        #
        # System changelog events.
        #
        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_Blacklisting:
            sEvent = 'Modified blacklisting';
            oDetails = self._createBlacklistingDetailsLink(oEntry.idWhat, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_Build:
            sEvent = 'Modified build';
            oDetails = self._createBuildDetailsLink(oEntry.idWhat, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_BuildSource:
            sEvent = 'Modified build source';
            oDetails = self._createBuildSourceDetailsLink(oEntry.idWhat, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_GlobalRsrc:
            sEvent = 'Modified global resource';
            oDetails = self._createGlobalResourceDetailsLink(oEntry.idWhat, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_FailureCategory:
            sEvent = 'Modified failure category';
            oDetails = self._createFailureCategoryDetailsLink(oEntry.idWhat, oEntry.tsEffective);
            (aoChanges, _) = self._oFailureCategoryLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_FailureReason:
            sEvent = 'Modified failure reason';
            oDetails = self._createFailureReasonDetailsLink(oEntry.idWhat, oEntry.tsEffective);
            (aoChanges, _) = self._oFailureReasonLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_SchedGroup:
            sEvent = 'Modified scheduling group';
            oDetails = self._createSchedGroupDetailsLink(oEntry.idWhat, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestBox:
            sEvent = 'Modified testbox';
            oDetails = self._createTestBoxDetailsLink(oEntry.idWhat, oEntry.tsEffective);
            (aoChanges, _) = self._oTestBoxLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestCase:
            sEvent = 'Modified test case';
            oDetails = self._createTestCaseDetailsLink(oEntry.idWhat, oEntry.tsEffective);
            (aoChanges, _) = self._oTestCaseLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestGroup:
            sEvent = 'Modified test group';
            oDetails = self._createTestGroupDetailsLink(oEntry.idWhat, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestResult:
            sEvent = 'Modified test failure reason';
            oDetails = self._createTestSetDetailsLinkByResult(oEntry.idWhat, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_User:
            sEvent = 'Modified user account';
            oDetails = self._createUserAccountDetailsLink(oEntry.idWhat, oEntry.tsEffective);

        else:
            sEvent   = '%s(%s)' % (oEntry.sEvent, oEntry.idWhat,);
            oDetails = '!Unknown event!' + (oEntry.sDesc if oEntry.sDesc else '');

        #
        # Do the formatting.
        #

        if aoChanges:
            oChangeEntry    = aoChanges[0];
            cAttribsChanged = len(oChangeEntry.aoChanges) + 1;
            if oChangeEntry.oOldRaw is None and sEvent.startswith('Modified '):
                sEvent = 'Created ' + sEvent[9:];

        else:
            oChangeEntry    = None;
            cAttribsChanged = -1;

        sHtml += u'  <tr class="%s">\n' \
                 u'    <td rowspan="%d" align="center" >%s</td>\n' \
                 u'    <td rowspan="%d" align="center" >%s</td>\n' \
                 u'    <td colspan="5" class="%s%s">%s %s</td>\n' \
                 u'  </tr>\n' \
               % ( sRowClass,
                  1 + cAttribsChanged + 1, sDate,
                  1 + cAttribsChanged + 1, webutils.escapeElem(oEntry.oAuthor.sUsername if oEntry.oAuthor is not None else ''),
                  sRowClass, ' tmsyschlogevent' if oChangeEntry is not None else '', webutils.escapeElem(sEvent),
                  oDetails.toHtml() if isinstance(oDetails, WuiHtmlBase) else oDetails,
                  );

        if oChangeEntry is not None:
            sHtml += u'  <tr class="%s tmsyschlogspacerrowabove">\n' \
                     u'    <td xrowspan="%d" style="border-right: 0px; border-bottom: 0px;"></td>\n' \
                     u'    <td colspan="3" style="border-right: 0px;"></td>\n' \
                     u'    <td rowspan="%d" class="%s tmsyschlogspacer"></td>\n' \
                     u'  </tr>\n' \
                   % (sRowClass, cAttribsChanged + 1, cAttribsChanged + 1, sRowClass);
            for j, oChange in enumerate(oChangeEntry.aoChanges):
                fLastRow = j + 1 == len(oChangeEntry.aoChanges);
                sHtml += u'  <tr class="%s%s tmsyschlogattr%s">\n' \
                       % ( sRowClass, 'odd' if j & 1 else 'even', ' tmsyschlogattrfinal' if fLastRow else '',);
                if j == 0:
                    sHtml += u'    <td class="%s tmsyschlogspacer" rowspan="%d"></td>\n' % (sRowClass, cAttribsChanged - 1,);

                if isinstance(oChange, AttributeChangeEntryPre):
                    sHtml += u'    <td class="%s%s">%s</td>\n' \
                             u'    <td><div class="tdpre"><pre>%s</pre></div></td>\n' \
                             u'    <td class="%s%s"><div class="tdpre"><pre>%s</pre></div></td>\n' \
                           % ( ' tmtopleft' if j == 0 else '', ' tmbottomleft' if fLastRow else '',
                               webutils.escapeElem(oChange.sAttr),
                               webutils.escapeElem(oChange.sOldText),
                               ' tmtopright' if j == 0 else '', ' tmbottomright' if fLastRow else '',
                               webutils.escapeElem(oChange.sNewText), );
                else:
                    sHtml += u'    <td class="%s%s">%s</td>\n' \
                             u'    <td>%s</td>\n' \
                             u'    <td class="%s%s">%s</td>\n' \
                           % ( ' tmtopleft' if j == 0 else '', ' tmbottomleft' if fLastRow else '',
                               webutils.escapeElem(oChange.sAttr),
                               webutils.escapeElem(oChange.sOldText),
                               ' tmtopright' if j == 0 else '', ' tmbottomright' if fLastRow else '',
                               webutils.escapeElem(oChange.sNewText), );
                sHtml += u'  </tr>\n';

        if oChangeEntry is not None:
            sHtml += u'  <tr class="%s tmsyschlogspacerrowbelow "><td colspan="5"></td></tr>\n\n' % (sRowClass,);
        return sHtml;


    def _generateTableHeaders(self):
        """
        Overridden parent method.
        """

        sHtml = u'<thead class="tmheader">\n' \
                u' <tr>\n' \
                u'  <th rowspan="2">When</th>\n' \
                u'  <th rowspan="2">Who</th>\n' \
                u'  <th colspan="5">Event</th>\n' \
                u' </tr>\n' \
                u' <tr>\n' \
                u'  <th style="border-right: 0px;"></th>\n' \
                u'  <th>Attribute</th>\n' \
                u'  <th>Old</th>\n' \
                u'  <th style="border-right: 0px;">New</th>\n' \
                u'  <th></th>\n' \
                u' </tr>\n' \
                u'</thead>\n';
        return sHtml;
class WuiAdminSystemChangelogList(WuiListContentBase):
    """
    WUI System Changelog Content Generator.
    """

    def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, cDaysBack, aiSelectedSortColumns = None):
        WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, 'System Changelog',
                                    fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns);
        self._asColumnHeaders = [ 'When', 'User', 'Event', 'Details' ];
        self._asColumnAttribs = [ 'align="center"', 'align="center"', '', '' ];
        self._oBuildBlacklistLogic  = BuildBlacklistLogic(oDisp.getDb());
        self._oBuildLogic           = BuildLogic(oDisp.getDb());
        self._oBuildSourceLogic     = BuildSourceLogic(oDisp.getDb());
        self._oFailureCategoryLogic = FailureCategoryLogic(oDisp.getDb());
        self._oFailureReasonLogic   = FailureReasonLogic(oDisp.getDb());
        self._oGlobalResourceLogic  = GlobalResourceLogic(oDisp.getDb());
        self._oSchedGroupLogic      = SchedGroupLogic(oDisp.getDb());
        self._oTestBoxLogic         = TestBoxLogic(oDisp.getDb());
        self._oTestCaseLogic        = TestCaseLogic(oDisp.getDb());
        self._oTestGroupLogic       = TestGroupLogic(oDisp.getDb());
        self._oUserAccountLogic     = UserAccountLogic(oDisp.getDb());
        self._sPrevDate             = '';
        _ = cDaysBack;

    #   oDetails = self._createBlacklistingDetailsLink(oEntry.idWhat, oEntry.tsEffective);
    def _createBlacklistingDetailsLink(self, idBlacklisting, tsEffective):
        """ Creates a link to the build source details. """
        oBlacklisting = self._oBuildBlacklistLogic.cachedLookup(idBlacklisting);
        if oBlacklisting is not None:
            from testmanager.webui.wuiadmin import WuiAdmin;
            return WuiAdminLink('Blacklisting #%u' % (oBlacklisting.idBlacklisting,),
                                WuiAdmin.ksActionBuildBlacklistDetails, tsEffective,
                                { BuildBlacklistData.ksParam_idBlacklisting: oBlacklisting.idBlacklisting },
                                fBracketed = False);
        return WuiElementText('[blacklisting #%u not found]' % (idBlacklisting,));

    def _createBuildDetailsLink(self, idBuild, tsEffective):
        """ Creates a link to the build details. """
        oBuild = self._oBuildLogic.cachedLookup(idBuild);
        if oBuild is not None:
            from testmanager.webui.wuiadmin import WuiAdmin;
            return WuiAdminLink('%s %sr%u' % ( oBuild.oCat.sProduct, oBuild.sVersion, oBuild.iRevision),
                                WuiAdmin.ksActionBuildDetails, tsEffective,
                                { BuildData.ksParam_idBuild: oBuild.idBuild },
                                fBracketed = False,
                                sTitle = 'build #%u for %s, type %s'
                                       % (oBuild.idBuild, ' & '.join(oBuild.oCat.asOsArches), oBuild.oCat.sType));
        return WuiElementText('[build #%u not found]' % (idBuild,));

    def _createBuildSourceDetailsLink(self, idBuildSrc, tsEffective):
        """ Creates a link to the build source details. """
        oBuildSource = self._oBuildSourceLogic.cachedLookup(idBuildSrc);
        if oBuildSource is not None:
            from testmanager.webui.wuiadmin import WuiAdmin;
            return WuiAdminLink(oBuildSource.sName, WuiAdmin.ksActionBuildSrcDetails, tsEffective,
                                { BuildSourceData.ksParam_idBuildSrc: oBuildSource.idBuildSrc },
                                fBracketed = False,
                                sTitle = 'Build source #%u' % (oBuildSource.idBuildSrc,));
        return WuiElementText('[build source #%u not found]' % (idBuildSrc,));

    def _createFailureCategoryDetailsLink(self, idFailureCategory, tsEffective):
        """ Creates a link to the failure category details. """
        oFailureCategory = self._oFailureCategoryLogic.cachedLookup(idFailureCategory);
        if oFailureCategory is not None:
            from testmanager.webui.wuiadmin import WuiAdmin;
            return WuiAdminLink(oFailureCategory.sShort, WuiAdmin.ksActionFailureCategoryDetails, tsEffective,
                                { FailureCategoryData.ksParam_idFailureCategory: oFailureCategory.idFailureCategory },
                                fBracketed = False,
                                sTitle = 'Failure category #%u' % (oFailureCategory.idFailureCategory,));
        return WuiElementText('[failure category #%u not found]' % (idFailureCategory,));

    def _createFailureReasonDetailsLink(self, idFailureReason, tsEffective):
        """ Creates a link to the failure reason details. """
        oFailureReason = self._oFailureReasonLogic.cachedLookup(idFailureReason);
        if oFailureReason is not None:
            from testmanager.webui.wuiadmin import WuiAdmin;
            return WuiAdminLink(oFailureReason.sShort, WuiAdmin.ksActionFailureReasonDetails, tsEffective,
                                { FailureReasonData.ksParam_idFailureReason: oFailureReason.idFailureReason },
                                fBracketed = False,
                                sTitle = 'Failure reason #%u, category %s'
                                       % (oFailureReason.idFailureReason, oFailureReason.oCategory.sShort));
        return WuiElementText('[failure reason #%u not found]' % (idFailureReason,));

    def _createGlobalResourceDetailsLink(self, idGlobalRsrc, tsEffective):
        """ Creates a link to the global resource details. """
        oGlobalResource = self._oGlobalResourceLogic.cachedLookup(idGlobalRsrc);
        if oGlobalResource is not None:
            return WuiAdminLink(oGlobalResource.sName, '@todo', tsEffective,
                                { GlobalResourceData.ksParam_idGlobalRsrc: oGlobalResource.idGlobalRsrc },
                                fBracketed = False,
                                sTitle = 'Global resource #%u' % (oGlobalResource.idGlobalRsrc,));
        return WuiElementText('[global resource #%u not found]' % (idGlobalRsrc,));

    def _createSchedGroupDetailsLink(self, idSchedGroup, tsEffective):
        """ Creates a link to the scheduling group details. """
        oSchedGroup = self._oSchedGroupLogic.cachedLookup(idSchedGroup);
        if oSchedGroup is not None:
            from testmanager.webui.wuiadmin import WuiAdmin;
            return WuiAdminLink(oSchedGroup.sName, WuiAdmin.ksActionSchedGroupDetails, tsEffective,
                                { SchedGroupData.ksParam_idSchedGroup: oSchedGroup.idSchedGroup },
                                fBracketed = False,
                                sTitle = 'Scheduling group #%u' % (oSchedGroup.idSchedGroup,));
        return WuiElementText('[scheduling group #%u not found]' % (idSchedGroup,));

    def _createTestBoxDetailsLink(self, idTestBox, tsEffective):
        """ Creates a link to the testbox details. """
        oTestBox = self._oTestBoxLogic.cachedLookup(idTestBox);
        if oTestBox is not None:
            from testmanager.webui.wuiadmin import WuiAdmin;
            return WuiAdminLink(oTestBox.sName, WuiAdmin.ksActionTestBoxDetails, tsEffective,
                                { TestBoxData.ksParam_idTestBox: oTestBox.idTestBox },
                                fBracketed = False, sTitle = 'Testbox #%u' % (oTestBox.idTestBox,));
        return WuiElementText('[testbox #%u not found]' % (idTestBox,));

    def _createTestCaseDetailsLink(self, idTestCase, tsEffective):
        """ Creates a link to the test case details. """
        oTestCase = self._oTestCaseLogic.cachedLookup(idTestCase);
        if oTestCase is not None:
            from testmanager.webui.wuiadmin import WuiAdmin;
            return WuiAdminLink(oTestCase.sName, WuiAdmin.ksActionTestCaseDetails, tsEffective,
                                { TestCaseData.ksParam_idTestCase: oTestCase.idTestCase },
                                fBracketed = False, sTitle = 'Test case #%u' % (oTestCase.idTestCase,));
        return WuiElementText('[test case #%u not found]' % (idTestCase,));

    def _createTestGroupDetailsLink(self, idTestGroup, tsEffective):
        """ Creates a link to the test group details. """
        oTestGroup = self._oTestGroupLogic.cachedLookup(idTestGroup);
        if oTestGroup is not None:
            from testmanager.webui.wuiadmin import WuiAdmin;
            return WuiAdminLink(oTestGroup.sName, WuiAdmin.ksActionTestGroupDetails, tsEffective,
                                { TestGroupData.ksParam_idTestGroup: oTestGroup.idTestGroup },
                                fBracketed = False, sTitle = 'Test group #%u' % (oTestGroup.idTestGroup,));
        return WuiElementText('[test group #%u not found]' % (idTestGroup,));

    def _createTestSetResultsDetailsLink(self, idTestSet, tsEffective):
        """ Creates a link to the test set results. """
        _ = tsEffective;
        from testmanager.webui.wuimain import WuiMain;
        return WuiMainLink('test set #%u' % idTestSet, WuiMain.ksActionTestSetDetails,
                           { TestSetData.ksParam_idTestSet: idTestSet }, fBracketed = False);

    def _createTestSetDetailsLinkByResult(self, idTestResult, tsEffective):
        """ Creates a link to the test set results. """
        _ = tsEffective;
        from testmanager.webui.wuimain import WuiMain;
        return WuiMainLink('test result #%u' % idTestResult, WuiMain.ksActionTestSetDetailsFromResult,
                           { TestSetData.ksParam_idTestResult: idTestResult }, fBracketed = False);

    def _createUserAccountDetailsLink(self, uid, tsEffective):
        """ Creates a link to the user account details. """
        oUser = self._oUserAccountLogic.cachedLookup(uid);
        if oUser is not None:
            return WuiAdminLink(oUser.sUsername, '@todo', tsEffective, { UserAccountData.ksParam_uid: oUser.uid },
                                fBracketed = False, sTitle = '%s (#%u)' % (oUser.sFullName, oUser.uid));
        return WuiElementText('[user #%u not found]' % (uid,));

    def _formatDescGeneric(self, sDesc, oEntry):
        """
        Generically format system log the description.
        """
        oRet = WuiHtmlKeeper();
        asWords = sDesc.split();
        for sWord in asWords:
            offEqual = sWord.find('=');
            if offEqual > 0:
                sKey = sWord[:offEqual];
                try:    idValue = int(sWord[offEqual+1:].rstrip('.,'));
                except: pass;
                else:
                    if sKey == 'idTestSet':
                        oRet.append(self._createTestSetResultsDetailsLink(idValue, oEntry.tsEffective));
                        continue;
                    if sKey == 'idTestBox':
                        oRet.append(self._createTestBoxDetailsLink(idValue, oEntry.tsEffective));
                        continue;
                    if sKey == 'idSchedGroup':
                        oRet.append(self._createSchedGroupDetailsLink(idValue, oEntry.tsEffective));
                        continue;

            oRet.append(WuiElementText(sWord));
        return oRet;

    def _formatListEntryHtml(self, iEntry): # pylint: disable=too-many-statements
        """
        Overridden parent method.
        """
        oEntry    = self._aoEntries[iEntry];
        sRowClass = 'tmodd' if (iEntry + 1) & 1 else 'tmeven';
        sHtml     = u'';

        #
        # Format the timestamp.
        #
        sDate = self.formatTsShort(oEntry.tsEffective);
        if sDate[:10] != self._sPrevDate:
            self._sPrevDate = sDate[:10];
            sHtml += '  <tr class="%s tmdaterow" align="left"><td colspan="7">%s</td></tr>\n' % (sRowClass, sDate[:10],);
        sDate = sDate[11:]

        #
        # System log events.
        # pylint: disable=redefined-variable-type
        #
        aoChanges = None;
        if   oEntry.sEvent == SystemLogData.ksEvent_CmdNacked:
            sEvent = 'Command not acknowleged';
            oDetails = oEntry.sDesc;

        elif oEntry.sEvent == SystemLogData.ksEvent_TestBoxUnknown:
            sEvent = 'Unknown testbox';
            oDetails = oEntry.sDesc;

        elif oEntry.sEvent == SystemLogData.ksEvent_TestSetAbandoned:
            sEvent = 'Abandoned ' if oEntry.sDesc.startswith('idTestSet') else 'Abandoned test set';
            oDetails = self._formatDescGeneric(oEntry.sDesc, oEntry);

        elif oEntry.sEvent == SystemLogData.ksEvent_UserAccountUnknown:
            sEvent = 'Unknown user account';
            oDetails = oEntry.sDesc;

        elif oEntry.sEvent == SystemLogData.ksEvent_XmlResultMalformed:
            sEvent = 'Malformed XML result';
            oDetails = oEntry.sDesc;

        elif oEntry.sEvent == SystemLogData.ksEvent_SchedQueueRecreate:
            sEvent = 'Recreating scheduling queue';
            asWords = oEntry.sDesc.split();
            if len(asWords) > 3 and asWords[0] == 'User' and asWords[1][0] == '#':
                try:    idAuthor = int(asWords[1][1:]);
                except: pass;
                else:
                    oEntry.oAuthor = self._oUserAccountLogic.cachedLookup(idAuthor);
                    if oEntry.oAuthor is not None:
                        i = 2;
                        if asWords[i] == 'recreated':   i += 1;
                        oEntry.sDesc = ' '.join(asWords[i:]);
            oDetails = self._formatDescGeneric(oEntry.sDesc.replace('sched queue #', 'for scheduling group idSchedGroup='),
                                               oEntry);
        #
        # System changelog events.
        #
        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_Blacklisting:
            sEvent = 'Modified blacklisting';
            oDetails = self._createBlacklistingDetailsLink(oEntry.idWhat, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_Build:
            sEvent = 'Modified build';
            oDetails = self._createBuildDetailsLink(oEntry.idWhat, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_BuildSource:
            sEvent = 'Modified build source';
            oDetails = self._createBuildSourceDetailsLink(oEntry.idWhat, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_GlobalRsrc:
            sEvent = 'Modified global resource';
            oDetails = self._createGlobalResourceDetailsLink(oEntry.idWhat, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_FailureCategory:
            sEvent = 'Modified failure category';
            oDetails = self._createFailureCategoryDetailsLink(oEntry.idWhat, oEntry.tsEffective);
            (aoChanges, _) = self._oFailureCategoryLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_FailureReason:
            sEvent = 'Modified failure reason';
            oDetails = self._createFailureReasonDetailsLink(oEntry.idWhat, oEntry.tsEffective);
            (aoChanges, _) = self._oFailureReasonLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_SchedGroup:
            sEvent = 'Modified scheduling group';
            oDetails = self._createSchedGroupDetailsLink(oEntry.idWhat, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestBox:
            sEvent = 'Modified testbox';
            oDetails = self._createTestBoxDetailsLink(oEntry.idWhat, oEntry.tsEffective);
            (aoChanges, _) = self._oTestBoxLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestCase:
            sEvent = 'Modified test case';
            oDetails = self._createTestCaseDetailsLink(oEntry.idWhat, oEntry.tsEffective);
            (aoChanges, _) = self._oTestCaseLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestGroup:
            sEvent = 'Modified test group';
            oDetails = self._createTestGroupDetailsLink(oEntry.idWhat, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestResult:
            sEvent = 'Modified test failure reason';
            oDetails = self._createTestSetDetailsLinkByResult(oEntry.idWhat, oEntry.tsEffective);

        elif oEntry.sEvent == SystemChangelogLogic.ksWhat_User:
            sEvent = 'Modified user account';
            oDetails = self._createUserAccountDetailsLink(oEntry.idWhat, oEntry.tsEffective);

        else:
            sEvent   = '%s(%s)' % (oEntry.sEvent, oEntry.idWhat,);
            oDetails = '!Unknown event!' + (oEntry.sDesc if oEntry.sDesc else '');

        #
        # Do the formatting.
        #

        if aoChanges:
            oChangeEntry    = aoChanges[0];
            cAttribsChanged = len(oChangeEntry.aoChanges) + 1;
            if oChangeEntry.oOldRaw is None and sEvent.startswith('Modified '):
                sEvent = 'Created ' + sEvent[9:];

        else:
            oChangeEntry    = None;
            cAttribsChanged = -1;

        sHtml += u'  <tr class="%s">\n' \
                 u'    <td rowspan="%d" align="center" >%s</td>\n' \
                 u'    <td rowspan="%d" align="center" >%s</td>\n' \
                 u'    <td colspan="5" class="%s%s">%s %s</td>\n' \
                 u'  </tr>\n' \
               % ( sRowClass,
                  1 + cAttribsChanged + 1, sDate,
                  1 + cAttribsChanged + 1, webutils.escapeElem(oEntry.oAuthor.sUsername if oEntry.oAuthor is not None else ''),
                  sRowClass, ' tmsyschlogevent' if oChangeEntry is not None else '', webutils.escapeElem(sEvent),
                  oDetails.toHtml() if isinstance(oDetails, WuiHtmlBase) else oDetails,
                  );

        if oChangeEntry is not None:
            sHtml += u'  <tr class="%s tmsyschlogspacerrowabove">\n' \
                     u'    <td xrowspan="%d" style="border-right: 0px; border-bottom: 0px;"></td>\n' \
                     u'    <td colspan="3" style="border-right: 0px;"></td>\n' \
                     u'    <td rowspan="%d" class="%s tmsyschlogspacer"></td>\n' \
                     u'  </tr>\n' \
                   % (sRowClass, cAttribsChanged + 1, cAttribsChanged + 1, sRowClass);
            for j, oChange in enumerate(oChangeEntry.aoChanges):
                fLastRow = j + 1 == len(oChangeEntry.aoChanges);
                sHtml += u'  <tr class="%s%s tmsyschlogattr%s">\n' \
                       % ( sRowClass, 'odd' if j & 1 else 'even', ' tmsyschlogattrfinal' if fLastRow else '',);
                if j == 0:
                    sHtml += u'    <td class="%s tmsyschlogspacer" rowspan="%d"></td>\n' % (sRowClass, cAttribsChanged - 1,);

                if isinstance(oChange, AttributeChangeEntryPre):
                    sHtml += u'    <td class="%s%s">%s</td>\n' \
                             u'    <td><div class="tdpre"><pre>%s</pre></div></td>\n' \
                             u'    <td class="%s%s"><div class="tdpre"><pre>%s</pre></div></td>\n' \
                           % ( ' tmtopleft' if j == 0 else '', ' tmbottomleft' if fLastRow else '',
                               webutils.escapeElem(oChange.sAttr),
                               webutils.escapeElem(oChange.sOldText),
                               ' tmtopright' if j == 0 else '', ' tmbottomright' if fLastRow else '',
                               webutils.escapeElem(oChange.sNewText), );
                else:
                    sHtml += u'    <td class="%s%s">%s</td>\n' \
                             u'    <td>%s</td>\n' \
                             u'    <td class="%s%s">%s</td>\n' \
                           % ( ' tmtopleft' if j == 0 else '', ' tmbottomleft' if fLastRow else '',
                               webutils.escapeElem(oChange.sAttr),
                               webutils.escapeElem(oChange.sOldText),
                               ' tmtopright' if j == 0 else '', ' tmbottomright' if fLastRow else '',
                               webutils.escapeElem(oChange.sNewText), );
                sHtml += u'  </tr>\n';

        if oChangeEntry is not None:
            sHtml += u'  <tr class="%s tmsyschlogspacerrowbelow "><td colspan="5"></td></tr>\n\n' % (sRowClass,);
        return sHtml;


    def _generateTableHeaders(self):
        """
        Overridden parent method.
        """

        sHtml = u'<thead class="tmheader">\n' \
                u' <tr>\n' \
                u'  <th rowspan="2">When</th>\n' \
                u'  <th rowspan="2">Who</th>\n' \
                u'  <th colspan="5">Event</th>\n' \
                u' </tr>\n' \
                u' <tr>\n' \
                u'  <th style="border-right: 0px;"></th>\n' \
                u'  <th>Attribute</th>\n' \
                u'  <th>Old</th>\n' \
                u'  <th style="border-right: 0px;">New</th>\n' \
                u'  <th></th>\n' \
                u' </tr>\n' \
                u'</thead>\n';
        return sHtml;
    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);