Index: /trunk/src/VBox/ValidationKit/common/constants/tbreq.py
===================================================================
--- /trunk/src/VBox/ValidationKit/common/constants/tbreq.py	(revision 61467)
+++ /trunk/src/VBox/ValidationKit/common/constants/tbreq.py	(revision 61468)
@@ -75,5 +75,5 @@
 SIGNON_PARAM_HAS_64_BIT_GUEST   = 'HAS_64_BIT_GUST';
 SIGNON_PARAM_HAS_IOMMU          = 'HAS_IOMMU';
-## TODO: SIGNON_PARAM_WITH_RAW_MODE
+SIGNON_PARAM_WITH_RAW_MODE      = 'WITH_RAW_MODE';
 SIGNON_PARAM_MEM_SIZE           = 'MEM_SIZE';
 SIGNON_PARAM_SCRATCH_SIZE       = 'SCRATCH_SIZE';
Index: /trunk/src/VBox/ValidationKit/testmanager/core/base.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/base.py	(revision 61467)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/base.py	(revision 61468)
@@ -133,4 +133,8 @@
 
 
+    ## List of internal attributes which should be ignored by
+    ## getDataAttributes and related machinery
+    kasInternalAttributes       = [];
+
     def __init__(self):
         ModelBase.__init__(self);
@@ -149,4 +153,6 @@
         for sAttr in asAttrs:
             if sAttr[0] == '_' or sAttr[0] == 'k':
+                continue;
+            if sAttr in self.kasInternalAttributes:
                 continue;
             oValue = getattr(self, sAttr);
Index: /trunk/src/VBox/ValidationKit/testmanager/core/report.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/report.py	(revision 61467)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/report.py	(revision 61468)
@@ -1198,6 +1198,6 @@
         if len(asIdGenTestBoxes) > 0:
             self._oDb.execute('SELECT   *\n'
-                              'FROM     TestBoxes\n'
-                              'WHERE    idGenTestBox in (' + ','.join(asIdGenTestBoxes) + ')\n'
+                              'FROM     TestBoxesWithStrings\n'
+                              'WHERE    idGenTestBox IN (' + ','.join(asIdGenTestBoxes) + ')\n'
                               'ORDER BY sName');
             for _ in range(self._oDb.getRowCount()):
Index: /trunk/src/VBox/ValidationKit/testmanager/core/schedgroup.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/schedgroup.py	(revision 61467)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/schedgroup.py	(revision 61468)
@@ -291,16 +291,16 @@
         if tsNow is None:
             oDb.execute('SELECT *\n'
-                        'FROM   TestBoxes\n'
-                        'WHERE  TestBoxes.idSchedGroup = %s\n'
-                        '   AND TestBoxes.tsExpire     = \'infinity\'::TIMESTAMP\n'
-                        'ORDER BY TestBoxes.sName, TestBoxes.idTestBox\n'
+                        'FROM   TestBoxesWithStrings\n'
+                        'WHERE  TestBoxesWithStrings.idSchedGroup = %s\n'
+                        '   AND TestBoxesWithStrings.tsExpire     = \'infinity\'::TIMESTAMP\n'
+                        'ORDER BY TestBoxesWithStrings.sName, TestBoxesWithStrings.idTestBox\n'
                         , (self.idSchedGroup,));
         else:
             oDb.execute('SELECT *\n'
-                        'FROM   TestBoxes\n'
-                        'WHERE  TestBoxes.idSchedGroup = %s\n'
-                        '   AND TestBoxes.tsExpire     > %s\n'
-                        '   AND TestBoxes.tsEffective  <= %s\n'
-                        'ORDER BY TestBoxes.sName, TestBoxes.idTestBox\n'
+                        'FROM   TestBoxesWithStrings\n'
+                        'WHERE  TestBoxesWithStrings.idSchedGroup = %s\n'
+                        '   AND TestBoxesWithStrings.tsExpire     > %s\n'
+                        '   AND TestBoxesWithStrings.tsEffective  <= %s\n'
+                        'ORDER BY TestBoxesWithStrings.sName, TestBoxesWithStrings.idTestBox\n'
                         , (self.idSchedGroup, tsNow, tsNow, tsNow, tsNow));
         for aoRow in oDb.fetchAll():
Index: /trunk/src/VBox/ValidationKit/testmanager/core/testbox.pgsql
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/testbox.pgsql	(revision 61468)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/testbox.pgsql	(revision 61468)
@@ -0,0 +1,400 @@
+-- $Id$
+--- @file
+-- VBox Test Manager Database Stored Procedures - TestBoxes.
+--
+
+--
+-- Copyright (C) 2012-2016 Oracle Corporation
+--
+-- This file is part of VirtualBox Open Source Edition (OSE), as
+-- available from http://www.virtualbox.org. This file is free software;
+-- you can redistribute it and/or modify it under the terms of the GNU
+-- General Public License (GPL) as published by the Free Software
+-- Foundation, in version 2 as it comes in the "COPYING" file of the
+-- VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+-- hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+-- VirtualBox OSE distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+
+
+---
+-- Checks if the test box name is unique, ignoring a_idTestCaseIgnore.
+-- Raises exception if duplicates are found.
+--
+-- @internal
+--
+CREATE OR REPLACE FUNCTION TestBoxLogic_checkUniqueName(a_sName TEXT, a_idTestBoxIgnore INTEGER)
+    RETURNS VOID AS $$
+    DECLARE
+        v_cRows INTEGER;
+    BEGIN
+        SELECT  COUNT(*) INTO v_cRows
+        FROM    TestBoxes
+        WHERE   sName      =  a_sName
+            AND tsExpire   =  'infinity'::TIMESTAMP
+            AND idTestBox <> a_idTestBoxIgnore;
+        IF v_cRows <> 0 THEN
+            RAISE EXCEPTION 'Duplicate test box name "%" (% times)', a_sName, v_cRows;
+        END IF;
+    END;
+$$ LANGUAGE plpgsql;
+
+
+---
+-- Historize a row.
+-- @internal
+--
+CREATE OR REPLACE FUNCTION TestBoxLogic_historizeEntry(a_idGenTestBox INTEGER, a_tsExpire TIMESTAMP WITH TIME ZONE)
+    RETURNS VOID AS $$
+    DECLARE
+        v_cUpdatedRows INTEGER;
+    BEGIN
+        UPDATE  TestBoxes
+          SET   tsExpire        = a_tsExpire
+          WHERE idGenTestBox    = a_idGenTestBox
+            AND tsExpire        = 'infinity'::TIMESTAMP;
+        GET DIAGNOSTICS v_cUpdatedRows = ROW_COUNT;
+        IF v_cUpdatedRows <> 1 THEN
+            IF v_cUpdatedRows = 0 THEN
+                RAISE EXCEPTION 'Test box generation ID % is no longer valid', a_idGenTestBox;
+            END IF;
+            RAISE EXCEPTION 'Integrity error in TestBoxes: % current rows with idGenTestBox=%', v_cUpdatedRows, a_idGenTestBox;
+        END IF;
+    END;
+$$ LANGUAGE plpgsql;
+
+
+---
+-- Translate string via the string table.
+--
+-- @returns NULL if a_sValue is NULL, otherwise a string ID.
+--
+CREATE OR REPLACE FUNCTION TestBoxLogic_lookupOrFindString(a_sValue TEXT)
+    RETURNS INTEGER AS $$
+    DECLARE
+        v_idStr        INTEGER;
+        v_cRows        INTEGER;
+    BEGIN
+        IF a_sValue IS NULL THEN
+            RETURN NULL;
+        END IF;
+
+        SELECT      idStr
+            INTO    v_idStr
+            FROM    TestBoxStrTab
+            WHERE   sValue = a_sValue;
+        GET DIAGNOSTICS v_cRows = ROW_COUNT;
+        IF v_cRows = 0 THEN
+            INSERT INTO TestBoxStrTab (sValue)
+                VALUES (a_sValue)
+                RETURNING idStr INTO v_idStr;
+        END IF;
+        RETURN v_idStr;
+    END;
+$$ LANGUAGE plpgsql;
+
+
+---
+-- Only adds the user settable parts of the row, i.e. not what TestBoxLogic_updateOnSignOn touches.
+--
+CREATE OR REPLACE function TestBoxLogic_addEntry(a_uidAuthor            INTEGER,
+                                                 a_ip                   inet,
+                                                 a_uuidSystem           uuid,
+                                                 a_sName                TEXT,
+                                                 a_sDescription         TEXT,
+                                                 a_idSchedGroup         INTEGER,
+                                                 a_fEnabled             BOOLEAN,
+                                                 a_enmLomKind           LomKind_T,
+                                                 a_ipLom                inet,
+                                                 a_pctScaleTimeout      INTEGER,  -- Actually smallint, but default typing fun.
+                                                 a_sComment             TEXT,
+                                                 a_enmPendingCmd        TestBoxCmd_T,
+                                                 OUT r_idTestBox        INTEGER,
+                                                 OUT r_idGenTestBox     INTEGER,
+                                                 OUT r_tsEffective      TIMESTAMP WITH TIME ZONE
+                                                 ) AS $$
+    DECLARE
+         v_idStrDescription INTEGER;
+         v_idStrComment     INTEGER;
+    BEGIN
+        PERFORM TestBoxLogic_checkUniqueName(a_sName, -1);
+
+        SELECT TestBoxLogic_lookupOrFindString(a_sDescription) INTO v_idStrDescription;
+        SELECT TestBoxLogic_lookupOrFindString(a_sComment)     INTO v_idStrComment;
+
+        INSERT INTO TestBoxes (
+                    tsEffective,         -- 1
+                    uidAuthor,           -- 2
+                    ip,                  -- 3
+                    uuidSystem,          -- 4
+                    sName,               -- 5
+                    idStrDescription,    -- 6
+                    idSchedGroup,        -- 7
+                    fEnabled,            -- 8
+                    enmLomKind,          -- 9
+                    ipLom,               -- 10
+                    pctScaleTimeout,     -- 11
+                    idStrComment,        -- 12
+                    enmPendingCmd )      -- 13
+            VALUES (CURRENT_TIMESTAMP,   -- 1
+                    a_uidAuthor,         -- 2
+                    a_ip,                -- 3
+                    a_uuidSystem,        -- 4
+                    a_sName,             -- 5
+                    v_idStrDescription,  -- 6
+                    a_idSchedGroup,      -- 7
+                    a_fEnabled,          -- 8
+                    a_enmLomKind,        -- 9
+                    a_ipLom,             -- 10
+                    a_pctScaleTimeout,   -- 11
+                    v_idStrComment,      -- 12
+                    a_enmPendingCmd )    -- 13
+            RETURNING idTestBox, idGenTestBox, tsEffective INTO r_idTestBox, r_idGenTestBox, r_tsEffective;
+    END;
+$$ LANGUAGE plpgsql;
+
+---
+-- Only adds the user settable parts of the row, i.e. not what TestBoxLogic_updateOnSignOn touches.
+--
+CREATE OR REPLACE function TestBoxLogic_editEntry(a_uidAuthor           INTEGER,
+                                                  a_idTestBox           INTEGER,
+                                                  a_ip                  inet,
+                                                  a_uuidSystem          uuid,
+                                                  a_sName               TEXT,
+                                                  a_sDescription        TEXT,
+                                                  a_idSchedGroup        INTEGER,
+                                                  a_fEnabled            BOOLEAN,
+                                                  a_enmLomKind          LomKind_T,
+                                                  a_ipLom               inet,
+                                                  a_pctScaleTimeout     INTEGER, -- Actually smallint, but default typing fun.
+                                                  a_sComment            TEXT,
+                                                  a_enmPendingCmd       TestBoxCmd_T,
+                                                  OUT r_idGenTestBox    INTEGER,
+                                                  OUT r_tsEffective     TIMESTAMP WITH TIME ZONE
+                                                  ) AS $$
+    DECLARE
+        v_Row               TestBoxes%ROWTYPE;
+        v_idStrDescription  INTEGER;
+        v_idStrComment      INTEGER;
+    BEGIN
+        PERFORM TestBoxLogic_checkUniqueName(a_sName, a_idTestBox);
+
+        SELECT TestBoxLogic_lookupOrFindString(a_sDescription) INTO v_idStrDescription;
+        SELECT TestBoxLogic_lookupOrFindString(a_sComment)     INTO v_idStrComment;
+
+        -- Fetch and historize the current row - there must be one.
+        UPDATE      TestBoxes
+            SET     tsExpire  = CURRENT_TIMESTAMP
+            WHERE   idTestBox = a_idTestBox
+                AND tsExpire  = 'infinity'::TIMESTAMP
+            RETURNING * INTO STRICT v_Row;
+
+        -- Modify the row with the new data.
+        v_Row.uidAuthor         := a_uidAuthor;
+        v_Row.ip                := a_ip;
+        v_Row.uuidSystem        := a_uuidSystem;
+        v_Row.sName             := a_sName;
+        v_Row.idStrDescription  := v_idStrDescription;
+        v_Row.idSchedGroup      := a_idSchedGroup;
+        v_Row.fEnabled          := a_fEnabled;
+        v_Row.enmLomKind        := a_enmLomKind;
+        v_Row.ipLom             := a_ipLom;
+        v_Row.pctScaleTimeout   := a_pctScaleTimeout;
+        v_Row.idStrComment      := v_idStrComment;
+        v_Row.enmPendingCmd     := a_enmPendingCmd;
+        v_Row.tsEffective       := v_Row.tsExpire;
+        r_tsEffective           := v_Row.tsExpire;
+        v_Row.tsExpire          := 'infinity'::TIMESTAMP;
+
+        -- Get a new generation ID.
+        SELECT NEXTVAL('TestBoxGenIdSeq') INTO v_Row.idGenTestBox;
+        r_idGenTestBox  := v_Row.idGenTestBox;
+
+        -- Insert the modified row.
+        INSERT INTO TestBoxes VALUES (v_Row.*);
+    END;
+$$ LANGUAGE plpgsql;
+
+
+DROP FUNCTION IF EXISTS TestBoxLogic_removeEntry(INTEGER, INTEGER, BOOLEAN);
+CREATE OR REPLACE FUNCTION TestBoxLogic_removeEntry(a_uidAuthor INTEGER, a_idTestBox INTEGER, a_fCascade BOOLEAN)
+    RETURNS VOID AS $$
+    DECLARE
+        v_Row           TestBoxes%ROWTYPE;
+        v_tsEffective   TIMESTAMP WITH TIME ZONE;
+        v_Rec           RECORD;
+        v_sErrors       TEXT;
+    BEGIN
+        --
+        -- Check preconditions.
+        --
+        IF a_fCascade <> TRUE THEN
+            -- @todo implement checks which throws useful exceptions.
+        ELSE
+            RAISE EXCEPTION 'CASCADE test box deletion is not implemented';
+        END IF;
+
+        --
+        -- To preserve the information about who deleted the record, we try to
+        -- add a dummy record which expires immediately.  I say try because of
+        -- the primary key, we must let the new record be valid for 1 us. :-(
+        --
+        SELECT  * INTO STRICT v_Row
+        FROM    TestBoxes
+        WHERE   idTestBox  = a_idTestBox
+            AND tsExpire   = 'infinity'::TIMESTAMP;
+
+        v_tsEffective := CURRENT_TIMESTAMP - INTERVAL '1 microsecond';
+        IF v_Row.tsEffective < v_tsEffective THEN
+            PERFORM TestBoxLogic_historizeEntry(v_Row.idGenTestBox, v_tsEffective);
+
+            v_Row.tsEffective   := v_tsEffective;
+            v_Row.tsExpire      := CURRENT_TIMESTAMP;
+            v_Row.uidAuthor     := a_uidAuthor;
+            SELECT NEXTVAL('TestBoxGenIdSeq') INTO v_Row.idGenTestBox;
+            INSERT INTO TestBoxes VALUES (v_Row.*);
+        ELSE
+            PERFORM TestBoxLogic_historizeEntry(v_Row.idGenTestBox, CURRENT_TIMESTAMP);
+        END IF;
+
+    EXCEPTION
+        WHEN NO_DATA_FOUND THEN
+            RAISE EXCEPTION 'Test box with ID % does not currently exist', a_idTestBox;
+        WHEN TOO_MANY_ROWS THEN
+            RAISE EXCEPTION 'Integrity error in TestBoxes: Too many current rows for %', a_idTestBox;
+    END;
+$$ LANGUAGE plpgsql;
+
+
+---
+-- Sign on update
+--
+CREATE OR REPLACE function TestBoxLogic_updateOnSignOn(a_idTestBox          INTEGER,
+                                                       a_ip                 inet,
+                                                       a_sOs                TEXT,
+                                                       a_sOsVersion         TEXT,
+                                                       a_sCpuVendor         TEXT,
+                                                       a_sCpuArch           TEXT,
+                                                       a_sCpuName           TEXT,
+                                                       a_lCpuRevision       bigint,
+                                                       a_cCpus              INTEGER, -- Actually smallint, but default typing fun.
+                                                       a_fCpuHwVirt         boolean,
+                                                       a_fCpuNestedPaging   boolean,
+                                                       a_fCpu64BitGuest     boolean,
+                                                       a_fChipsetIoMmu      boolean,
+                                                       a_fRawMode           boolean,
+                                                       a_cMbMemory          bigint,
+                                                       a_cMbScratch         bigint,
+                                                       a_sReport            TEXT,
+                                                       a_iTestBoxScriptRev  INTEGER,
+                                                       a_iPythonHexVersion  INTEGER,
+                                                       OUT r_idGenTestBox   INTEGER
+                                                       ) AS $$
+    DECLARE
+        v_Row               TestBoxes%ROWTYPE;
+        v_idStrOs           INTEGER;
+        v_idStrOsVersion    INTEGER;
+        v_idStrCpuVendor    INTEGER;
+        v_idStrCpuArch      INTEGER;
+        v_idStrCpuName      INTEGER;
+        v_idStrReport       INTEGER;
+    BEGIN
+        SELECT TestBoxLogic_lookupOrFindString(a_sOs)           INTO v_idStrOs;
+        SELECT TestBoxLogic_lookupOrFindString(a_sOsVersion)    INTO v_idStrOsVersion;
+        SELECT TestBoxLogic_lookupOrFindString(a_sCpuVendor)    INTO v_idStrCpuVendor;
+        SELECT TestBoxLogic_lookupOrFindString(a_sCpuArch)      INTO v_idStrCpuArch;
+        SELECT TestBoxLogic_lookupOrFindString(a_sCpuName)      INTO v_idStrCpuName;
+        SELECT TestBoxLogic_lookupOrFindString(a_sReport)       INTO v_idStrReport;
+
+        -- Fetch and historize the current row - there must be one.
+        UPDATE      TestBoxes
+            SET     tsExpire     = CURRENT_TIMESTAMP
+            WHERE   idTestBox    = a_idTestBox
+                AND tsExpire     = 'infinity'::TIMESTAMP
+            RETURNING * INTO STRICT v_Row;
+
+        -- Modify the row with the new data.
+        v_Row.uidAuthor             := NULL;
+        v_Row.ip                    := a_ip;
+        v_Row.idStrOs               := v_idStrOs;
+        v_Row.idStrOsVersion        := v_idStrOsVersion;
+        v_Row.idStrCpuVendor        := v_idStrCpuVendor;
+        v_Row.idStrCpuArch          := v_idStrCpuArch;
+        v_Row.idStrCpuName          := v_idStrCpuName;
+        v_Row.lCpuRevision          := a_lCpuRevision;
+        v_Row.cCpus                 := a_cCpus;
+        v_Row.fCpuHwVirt            := a_fCpuHwVirt;
+        v_Row.fCpuNestedPaging      := a_fCpuNestedPaging;
+        v_Row.fCpu64BitGuest        := a_fCpu64BitGuest;
+        v_Row.fChipsetIoMmu         := a_fChipsetIoMmu;
+        v_Row.fRawMode              := a_fRawMode;
+        v_Row.cMbMemory             := a_cMbMemory;
+        v_Row.cMbScratch            := a_cMbScratch;
+        v_Row.idStrReport           := v_idStrReport;
+        v_Row.iTestBoxScriptRev     := a_iTestBoxScriptRev;
+        v_Row.iPythonHexVersion     := a_iPythonHexVersion;
+        v_Row.tsEffective           := v_Row.tsExpire;
+        v_Row.tsExpire              := 'infinity'::TIMESTAMP;
+
+        -- Get a new generation ID.
+        SELECT NEXTVAL('TestBoxGenIdSeq') INTO v_Row.idGenTestBox;
+        r_idGenTestBox  := v_Row.idGenTestBox;
+
+        -- Insert the modified row.
+        INSERT INTO TestBoxes VALUES (v_Row.*);
+    END;
+$$ LANGUAGE plpgsql;
+
+
+---
+-- Set new command.
+--
+CREATE OR REPLACE function TestBoxLogic_setCommand(a_uidAuthor          INTEGER,
+                                                   a_idTestBox          INTEGER,
+                                                   a_enmOldCmd          TestBoxCmd_T,
+                                                   a_enmNewCmd          TestBoxCmd_T,
+                                                   a_sComment           TEXT,
+                                                   OUT r_idGenTestBox   INTEGER,
+                                                   OUT r_tsEffective    TIMESTAMP WITH TIME ZONE
+                                                   ) AS $$
+    DECLARE
+        v_Row               TestBoxes%ROWTYPE;
+        v_idStrComment      INTEGER;
+    BEGIN
+        SELECT TestBoxLogic_lookupOrFindString(a_sComment) INTO v_idStrComment;
+
+        -- Fetch and historize the current row - there must be one.
+        UPDATE      TestBoxes
+            SET     tsExpire      = CURRENT_TIMESTAMP
+            WHERE   idTestBox     = a_idTestBox
+                AND tsExpire      = 'infinity'::TIMESTAMP
+                AND enmPendingCmd = a_enmOldCmd
+            RETURNING * INTO STRICT v_Row;
+
+        -- Modify the row with the new data.
+        v_Row.enmPendingCmd         := a_enmNewCmd;
+        IF v_idStrComment IS NOT NULL THEN
+            v_Row.idStrComment      := v_idStrComment;
+        END IF;
+        v_Row.tsEffective           := v_Row.tsExpire;
+        r_tsEffective               := v_Row.tsExpire;
+        v_Row.tsExpire              := 'infinity'::TIMESTAMP;
+
+        -- Get a new generation ID.
+        SELECT NEXTVAL('TestBoxGenIdSeq') INTO v_Row.idGenTestBox;
+        r_idGenTestBox  := v_Row.idGenTestBox;
+
+        -- Insert the modified row.
+        INSERT INTO TestBoxes VALUES (v_Row.*);
+    END;
+$$ LANGUAGE plpgsql;
+
+
Index: /trunk/src/VBox/ValidationKit/testmanager/core/testbox.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/testbox.py	(revision 61467)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/testbox.py	(revision 61468)
@@ -34,6 +34,7 @@
 
 # Validation Kit imports.
-from testmanager.core.base  import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMExceptionBase, TMInFligthCollision, \
-                                   TMInvalidData, TMTooManyRows, TMRowNotFound, ChangeLogEntry, AttributeChangeEntry;
+from testmanager.core.base          import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMInFligthCollision, \
+                                           TMInvalidData, TMTooManyRows, TMRowNotFound, ChangeLogEntry, AttributeChangeEntry;
+from testmanager.core.useraccount   import UserAccountLogic;
 
 
@@ -96,4 +97,5 @@
     ksParam_ipLom               = 'TestBox_ipLom';
     ksParam_pctScaleTimeout     = 'TestBox_pctScaleTimeout';
+    ksParam_sComment            = 'TestBox_sComment';
     ksParam_sOs                 = 'TestBox_sOs';
     ksParam_sOsVersion          = 'TestBox_sOsVersion';
@@ -107,4 +109,5 @@
     ksParam_fCpu64BitGuest      = 'TestBox_fCpu64BitGuest';
     ksParam_fChipsetIoMmu       = 'TestBox_fChipsetIoMmu';
+    ksParam_fRawMode            = 'TestBox_fRawMode';
     ksParam_cMbMemory           = 'TestBox_cMbMemory';
     ksParam_cMbScratch          = 'TestBox_cMbScratch';
@@ -114,8 +117,12 @@
     ksParam_enmPendingCmd       = 'TestBox_enmPendingCmd';
 
+    kasInternalAttributes       = [ 'idStrDescription', 'idStrComment', 'idStrOs', 'idStrOsVersion', 'idStrCpuVendor',
+                                    'idStrCpuArch', 'idStrCpuName', 'idStrReport', ];
     kasAllowNullAttributes      = ['idTestBox', 'tsEffective', 'tsExpire', 'uidAuthor', 'idGenTestBox', 'sDescription',
-                                   'ipLom', 'sOs', 'sOsVersion', 'sCpuVendor', 'sCpuArch', 'sCpuName', 'lCpuRevision', 'cCpus',
-                                   'fCpuHwVirt', 'fCpuNestedPaging', 'fCpu64BitGuest', 'fChipsetIoMmu', 'cMbMemory',
-                                   'cMbScratch', 'sReport', 'iTestBoxScriptRev', 'iPythonHexVersion' ];
+                                   'ipLom', 'sComment', 'sOs', 'sOsVersion', 'sCpuVendor', 'sCpuArch', 'sCpuName',
+                                   'lCpuRevision', 'cCpus', 'fCpuHwVirt', 'fCpuNestedPaging', 'fCpu64BitGuest', 'fChipsetIoMmu',
+                                   'fRawMode', 'cMbMemory', 'cMbScratch', 'sReport', 'iTestBoxScriptRev', 'iPythonHexVersion',
+                                  ] + kasInternalAttributes;
+
     kasValidValues_enmLomKind   = kasLomKindValues;
     kasValidValues_enmPendingCmd = kasTestBoxCmdValues;
@@ -124,4 +131,6 @@
     kcchMax_sReport             = 65535;
 
+
+    kcDbColumns                 = 41; # including the 7 string joins columns
 
     def __init__(self):
@@ -140,5 +149,5 @@
         self.uuidSystem          = None;
         self.sName               = None;
-        self.sDescription        = None;
+        self.idStrDescription    = None;
         self.idSchedGroup        = 1;
         self.fEnabled            = False;
@@ -146,9 +155,10 @@
         self.ipLom               = None;
         self.pctScaleTimeout     = 100;
-        self.sOs                 = None;
-        self.sOsVersion          = None;
-        self.sCpuVendor          = None;
-        self.sCpuArch            = None;
-        self.sCpuName            = None;
+        self.idStrComment        = None;
+        self.idStrOs             = None;
+        self.idStrOsVersion      = None;
+        self.idStrCpuVendor      = None;
+        self.idStrCpuArch        = None;
+        self.idStrCpuName        = None;
         self.lCpuRevision        = None;
         self.cCpus               = 1;
@@ -157,17 +167,27 @@
         self.fCpu64BitGuest      = False;
         self.fChipsetIoMmu       = False;
+        self.fRawMode            = None;
         self.cMbMemory           = 1;
         self.cMbScratch          = 0;
-        self.sReport             = None;
+        self.idStrReport         = None;
         self.iTestBoxScriptRev   = 0;
         self.iPythonHexVersion   = 0;
         self.enmPendingCmd       = self.ksTestBoxCmd_None;
+        # String table values.
+        self.sDescription        = None;
+        self.sComment            = None;
+        self.sOs                 = None;
+        self.sOsVersion          = None;
+        self.sCpuVendor          = None;
+        self.sCpuArch            = None;
+        self.sCpuName            = None;
+        self.sReport             = None;
 
     def initFromDbRow(self, aoRow):
         """
         Internal worker for initFromDbWithId and initFromDbWithGenId as well as
-        from TestBoxLogic.  Expecting a SELECT * FROM TestBoxes result.
-        """
-
+        from TestBoxLogic.  Expecting the result from a query like this:
+            SELECT TestBoxesWithStrings.* FROM TestBoxesWithStrings
+        """
         if aoRow is None:
             raise TMRowNotFound('TestBox not found.');
@@ -181,5 +201,5 @@
         self.uuidSystem          = aoRow[6];
         self.sName               = aoRow[7];
-        self.sDescription        = aoRow[8];
+        self.idStrDescription    = aoRow[8];
         self.idSchedGroup        = aoRow[9];
         self.fEnabled            = aoRow[10];
@@ -187,21 +207,35 @@
         self.ipLom               = aoRow[12];
         self.pctScaleTimeout     = aoRow[13];
-        self.sOs                 = aoRow[14];
-        self.sOsVersion          = aoRow[15];
-        self.sCpuVendor          = aoRow[16];
-        self.sCpuArch            = aoRow[17];
-        self.sCpuName            = aoRow[18];
-        self.lCpuRevision        = aoRow[19];
-        self.cCpus               = aoRow[20];
-        self.fCpuHwVirt          = aoRow[21];
-        self.fCpuNestedPaging    = aoRow[22];
-        self.fCpu64BitGuest      = aoRow[23];
-        self.fChipsetIoMmu       = aoRow[24];
-        self.cMbMemory           = aoRow[25];
-        self.cMbScratch          = aoRow[26];
-        self.sReport             = aoRow[27];
-        self.iTestBoxScriptRev   = aoRow[28];
-        self.iPythonHexVersion   = aoRow[29];
-        self.enmPendingCmd       = aoRow[30];
+        self.idStrComment        = aoRow[14];
+        self.idStrOs             = aoRow[15];
+        self.idStrOsVersion      = aoRow[16];
+        self.idStrCpuVendor      = aoRow[17];
+        self.idStrCpuArch        = aoRow[18];
+        self.idStrCpuName        = aoRow[19];
+        self.lCpuRevision        = aoRow[20];
+        self.cCpus               = aoRow[21];
+        self.fCpuHwVirt          = aoRow[22];
+        self.fCpuNestedPaging    = aoRow[23];
+        self.fCpu64BitGuest      = aoRow[24];
+        self.fChipsetIoMmu       = aoRow[25];
+        self.fRawMode            = aoRow[26];
+        self.cMbMemory           = aoRow[27];
+        self.cMbScratch          = aoRow[28];
+        self.idStrReport         = aoRow[29];
+        self.iTestBoxScriptRev   = aoRow[30];
+        self.iPythonHexVersion   = aoRow[31];
+        self.enmPendingCmd       = aoRow[32];
+
+        # String table values.
+        if len(aoRow) > 32:
+            self.sDescription    = aoRow[33];
+            self.sComment        = aoRow[34];
+            self.sOs             = aoRow[35];
+            self.sOsVersion      = aoRow[36];
+            self.sCpuVendor      = aoRow[37];
+            self.sCpuArch        = aoRow[38];
+            self.sCpuName        = aoRow[39];
+            self.sReport         = aoRow[40];
+
         return self;
 
@@ -211,6 +245,6 @@
         """
         oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
-                                                       'SELECT *\n'
-                                                       'FROM   TestBoxes\n'
+                                                       'SELECT TestBoxesWithStrings.*\n'
+                                                       'FROM   TestBoxesWithStrings\n'
                                                        'WHERE  idTestBox    = %s\n'
                                                        , ( idTestBox, ), tsNow, sPeriodBack));
@@ -224,6 +258,6 @@
         Initialize the object from the database.
         """
-        oDb.execute('SELECT *\n'
-                    'FROM   TestBoxes\n'
+        oDb.execute('SELECT TestBoxesWithStrings.*\n'
+                    'FROM   TestBoxesWithStrings\n'
                     'WHERE  idGenTestBox = %s\n'
                     , (idGenTestBox, ) );
@@ -425,8 +459,8 @@
         Tries to fetch a testbox by its UUID alone.
         """
-        self._oDb.execute('SELECT   *\n'
-                          'FROM     TestBoxes\n'
+        self._oDb.execute('SELECT   TestBoxesWithStrings.*\n'
+                          'FROM     TestBoxesWithStrings\n'
                           'WHERE    uuidSystem = %s\n'
-                          '     AND tsExpire = \'infinity\'::timestamp\n'
+                          '     AND tsExpire   = \'infinity\'::timestamp\n'
                           'ORDER BY tsEffective DESC\n',
                           (sTestBoxUuid,));
@@ -443,40 +477,48 @@
         Fetches testboxes for listing.
 
-        Returns an array (list) of TestBoxData items, empty list if none. The
-        TestBoxData instances have an extra oStatus member that is either None or
-        a TestBoxStatusData instance, and a member tsCurrent holding
-        CURRENT_TIMESTAMP.
+        Returns an array (list) of TestBoxDataForListing items, empty list if none.
+        The TestBoxDataForListing instances are just TestBoxData with two extra
+        members, an extra oStatus member that is either None or a TestBoxStatusData
+        instance, and a member tsCurrent holding CURRENT_TIMESTAMP.
 
         Raises exception on error.
         """
+        class TestBoxDataForListing(TestBoxData):
+            """ We add two members for the listing. """
+            def __init__(self):
+                TestBoxData.__init__(self);
+                self.tsCurrent = None;  # CURRENT_TIMESTAMP
+                self.oStatus   = None;  # TestBoxStatusData
+
         from testmanager.core.testboxstatus import TestBoxStatusData;
 
         if tsNow is None:
-            self._oDb.execute('SELECT   TestBoxes.*, TestBoxStatuses.*\n'
-                              'FROM     TestBoxes\n'
-                              'LEFT OUTER JOIN TestBoxStatuses ON (\n'
-                              '         TestBoxStatuses.idTestBox = TestBoxes.idTestBox )\n'
-                              'WHERE    tsExpire = \'infinity\'::TIMESTAMP\n'
-                              'ORDER BY sName\n'
+            self._oDb.execute('SELECT   TestBoxesWithStrings.*,\n'
+                              '         TestBoxStatuses.*\n'
+                              'FROM     TestBoxesWithStrings\n'
+                              '         LEFT OUTER JOIN TestBoxStatuses\n'
+                              '                      ON TestBoxStatuses.idTestBox = TestBoxesWithStrings.idTestBox\n'
+                              'WHERE    TestBoxesWithStrings.tsExpire = \'infinity\'::TIMESTAMP\n'
+                              'ORDER BY TestBoxesWithStrings.sName\n'
                               'LIMIT %s OFFSET %s\n'
                               , (cMaxRows, iStart,));
         else:
-            self._oDb.execute('SELECT   TestBoxes.*, TestBoxStatuses.*\n'
-                              'FROM     TestBoxes\n'
-                              'LEFT OUTER JOIN TestBoxStatuses ON (\n'
-                              '         TestBoxStatuses.idTestBox = TestBoxes.idTestBox )\n'
+            self._oDb.execute('SELECT   TestBoxesWithStrings.*,\n'
+                              '         TestBoxStatuses.*\n'
+                              'FROM     TestBoxesWithStrings\n'
+                              '         LEFT OUTER JOIN TestBoxStatuses\n'
+                              '                      ON TestBoxStatuses.idTestBox = TestBoxesWithStrings.idTestBox\n'
                               'WHERE    tsExpire     > %s\n'
                               '     AND tsEffective <= %s\n'
-                              'ORDER BY sName\n'
+                              'ORDER BY TestBoxesWithStrings.sName\n'
                               'LIMIT %s OFFSET %s\n'
-                              , (tsNow, tsNow, cMaxRows, iStart,));
+                              , ( tsNow, tsNow, cMaxRows, iStart,));
 
         aoRows = [];
         for aoOne in self._oDb.fetchAll():
-            oTestBox = TestBoxData().initFromDbRow(aoOne);
-            oTestBox.tsCurrent = self._oDb.getCurrentTimestamp();                  # pylint: disable=W0201
-            oTestBox.oStatus   = None;                                             # pylint: disable=W0201
-            if aoOne[31] is not None:
-                oTestBox.oStatus = TestBoxStatusData().initFromDbRow(aoOne[31:]);  # pylint: disable=W0201
+            oTestBox = TestBoxDataForListing().initFromDbRow(aoOne);
+            oTestBox.tsCurrent = self._oDb.getCurrentTimestamp();
+            if aoOne[TestBoxData.kcDbColumns] is not None:
+                oTestBox.oStatus = TestBoxStatusData().initFromDbRow(aoOne[TestBoxData.kcDbColumns:]);
             aoRows.append(oTestBox);
         return aoRows;
@@ -494,41 +536,38 @@
             tsNow = self._oDb.getCurrentTimestamp();
 
-        self._oDb.execute('SELECT   TestBoxes.*, Users.sUsername\n'
-                          'FROM     TestBoxes\n'
-                          'LEFT OUTER JOIN Users \n'
-                          '     ON (    TestBoxes.uidAuthor = Users.uid\n'
-                          '         AND Users.tsEffective <= TestBoxes.tsEffective\n'
-                          '         AND Users.tsExpire    >  TestBoxes.tsEffective)\n'
-                          'WHERE    TestBoxes.tsEffective <= %s\n'
-                          '     AND TestBoxes.idTestBox = %s\n'
-                          'ORDER BY TestBoxes.tsExpire DESC\n'
+        self._oDb.execute('SELECT   TestBoxesWithStrings.*\n'
+                          'FROM     TestBoxesWithStrings\n'
+                          'WHERE    TestBoxesWithStrings.tsEffective <= %s\n'
+                          '     AND TestBoxesWithStrings.idTestBox    = %s\n'
+                          'ORDER BY TestBoxesWithStrings.tsExpire DESC\n'
                           'LIMIT %s OFFSET %s\n'
                           , (tsNow, idTestBox, cMaxRows + 1, iStart,));
 
         aoRows = [];
-        for _ in range(self._oDb.getRowCount()):
-            oRow = self._oDb.fetchOne();
-            aoRows.append([TestBoxData().initFromDbRow(oRow), oRow[-1],]);
+        for aoDbRow in self._oDb.fetchAll():
+            aoRows.append(TestBoxData().initFromDbRow(aoDbRow));
 
         # Calculate the changes.
         aoEntries = [];
-        for i in range(0, len(aoRows) - 1):
-            (oNew, sAuthor) = aoRows[i];
-            (oOld, _      ) = aoRows[i + 1];
+        for i in xrange(0, len(aoRows) - 1):
+            oNew      = aoRows[i];
+            oOld      = aoRows[i + 1];
             aoChanges = [];
             for sAttr in oNew.getDataAttributes():
-                if sAttr not in [ 'tsEffective', 'tsExpire', 'uidAuthor']:
+                if sAttr not in [ 'tsEffective', 'tsExpire', 'uidAuthor', ]:
                     oOldAttr = getattr(oOld, sAttr);
                     oNewAttr = getattr(oNew, sAttr);
                     if oOldAttr != oNewAttr:
                         aoChanges.append(AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
-            aoEntries.append(ChangeLogEntry(oNew.uidAuthor, sAuthor, oNew.tsEffective, oNew.tsExpire, oNew, oOld, aoChanges));
+            aoEntries.append(ChangeLogEntry(oNew.uidAuthor, None, oNew.tsEffective, oNew.tsExpire, oNew, oOld, aoChanges));
 
         # If we're at the end of the log, add the initial entry.
         if len(aoRows) <= cMaxRows and len(aoRows) > 0:
-            (oNew, sAuthor) = aoRows[-1];
-            aoEntries.append(ChangeLogEntry(oNew.uidAuthor, aoRows[-1][1], oNew.tsEffective, oNew.tsExpire, oNew, None, []));
-
+            oNew = aoRows[-1];
+            aoEntries.append(ChangeLogEntry(oNew.uidAuthor, None, oNew.tsEffective, oNew.tsExpire, oNew, None, []));
+
+        UserAccountLogic(self._oDb).resolveChangeLogAuthors(aoEntries);
         return (aoEntries, len(aoRows) > cMaxRows);
+
 
     def addEntry(self, oData, uidAuthor, fCommit = False):
@@ -542,103 +581,21 @@
             raise TMInvalidData('Invalid data passed to create(): %s' % (dDataErrors,));
 
-        self._oDb.execute('INSERT INTO TestBoxes (\n'
-                          '         idTestBox,\n'
-                          '         tsEffective,\n'
-                          '         tsExpire,\n'
-                          '         uidAuthor,\n'
-                          '         idGenTestBox,\n'
-                          '         ip,\n'
-                          '         uuidSystem,\n'
-                          '         sName,\n'
-                          '         sDescription,\n'
-                          '         idSchedGroup,\n'
-                          '         fEnabled,\n'
-                          '         enmLomKind,\n'
-                          '         ipLom,\n'
-                          '         pctScaleTimeout,\n'
-                          '         sOs,\n'
-                          '         sOsVersion,\n'
-                          '         sCpuVendor,\n'
-                          '         sCpuArch,\n'
-                          '         sCpuName,\n'
-                          '         lCpuRevision,\n'
-                          '         cCpus,\n'
-                          '         fCpuHwVirt,\n'
-                          '         fCpuNestedPaging,\n'
-                          '         fCpu64BitGuest,\n'
-                          '         fChipsetIoMmu,\n'
-                          '         cMbMemory,\n'
-                          '         cMbScratch,\n'
-                          '         sReport,\n'
-                          '         iTestBoxScriptRev,\n'
-                          '         iPythonHexVersion,\n'
-                          '         enmPendingCmd\n'
-                          '         )'
-                          'VALUES (\n'
-                          '         DEFAULT,\n'
-                          '         CURRENT_TIMESTAMP,\n'
-                          '         DEFAULT,\n'
-                          '         %s,\n'          # uidAuthor
-                          '         DEFAULT,\n'
-                          '         %s,\n'          # ip
-                          '         %s,\n'          # uuidSystem
-                          '         %s,\n'          # sName
-                          '         %s,\n'          # sDescription
-                          '         %s,\n'          # idSchedGroup
-                          '         %s,\n'          # fEnabled
-                          '         %s,\n'          # enmLomKind
-                          '         %s,\n'          # ipLom
-                          '         %s,\n'          # pctScaleTimeout
-                          '         %s,\n'          # sOs
-                          '         %s,\n'          # sOsVersion
-                          '         %s,\n'          # sCpuVendor
-                          '         %s,\n'          # sCpuArch
-                          '         %s,\n'          # sCpuName
-                          '         %s,\n'          # lCpuRevision
-                          '         %s,\n'          # cCpus
-                          '         %s,\n'          # fCpuHwVirt
-                          '         %s,\n'          # fCpuNestedPaging
-                          '         %s,\n'          # fCpu64BitGuest
-                          '         %s,\n'          # fChipsetIoMmu
-                          '         %s,\n'          # cMbMemory
-                          '         %s,\n'          # cMbScratch
-                          '         %s,\n'          # sReport
-                          '         %s,\n'          # iTestBoxScriptRev
-                          '         %s,\n'          # iPythonHexVersion
-                          '         %s\n'           # enmPendingCmd
-                          '         )\n'
-                          'RETURNING idTestBox, idGenTestBox, tsEffective'
-                          , (uidAuthor,
-                             oData.ip,
-                             oData.uuidSystem,
-                             oData.sName,
-                             oData.sDescription,
-                             oData.idSchedGroup,
-                             oData.fEnabled,
-                             oData.enmLomKind,
-                             oData.ipLom,
-                             oData.pctScaleTimeout,
-                             oData.sOs,
-                             oData.sOsVersion,
-                             oData.sCpuVendor,
-                             oData.sCpuArch,
-                             oData.sCpuName,
-                             oData.lCpuRevision,
-                             oData.cCpus,
-                             oData.fCpuHwVirt,
-                             oData.fCpuNestedPaging,
-                             oData.fCpu64BitGuest,
-                             oData.fChipsetIoMmu,
-                             oData.cMbMemory,
-                             oData.cMbScratch,
-                             oData.sReport,
-                             oData.iTestBoxScriptRev,
-                             oData.iPythonHexVersion,
-                             oData.enmPendingCmd
-                             )
-                          );
-        oRow = self._oDb.fetchOne();
+        self._oDb.callProc('TestBoxLogic_addEntry'
+                           , ( uidAuthor,
+                               oData.ip,            # Should we allow setting the IP?
+                               oData.uuidSystem,
+                               oData.sName,
+                               oData.sDescription,
+                               oData.idSchedGroup,
+                               oData.fEnabled,
+                               oData.enmLomKind,
+                               oData.ipLom,
+                               oData.pctScaleTimeout,
+                               oData.sComment,
+                               oData.enmPendingCmd, ) );
+        aoRow = self._oDb.fetchOne();
         self._oDb.maybeCommit(fCommit);
-        return (oRow[0], oRow[1], oRow[2]);
+        return (aoRow[0], aoRow[1], aoRow[2]);
+
 
     def editEntry(self, oData, uidAuthor, fCommit = False):
@@ -652,333 +609,75 @@
             raise TMInvalidData('Invalid data passed to create(): %s' % (dDataErrors,));
 
-        ## @todo check if the data changed.
-
-        self._oDb.execute('UPDATE ONLY TestBoxes\n'
-                          'SET      tsExpire = CURRENT_TIMESTAMP\n'
-                          'WHERE    idGenTestBox = %s\n'
-                          '     AND tsExpire = \'infinity\'::timestamp\n'
-                          'RETURNING tsExpire\n',
-                          (oData.idGenTestBox,));
-        try:
-            tsEffective = self._oDb.fetchOne()[0];
-
-            # Would be easier to do this using an insert or update hook, I think. Much easier.
-
-            ##
-            ## @todo The table is growing too fast.  Rows are too long.  Mixing data from here and there.  Split it and
-            ##       rethink storage and update strategy!
-            ##
-
-            self._oDb.execute('INSERT INTO TestBoxes (\n'
-                              '         idGenTestBox,\n'
-                              '         idTestBox,\n'
-                              '         tsEffective,\n'
-                              '         uidAuthor,\n'
-                              '         ip,\n'
-                              '         uuidSystem,\n'
-                              '         sName,\n'
-                              '         sDescription,\n'
-                              '         idSchedGroup,\n'
-                              '         fEnabled,\n'
-                              '         enmLomKind,\n'
-                              '         ipLom,\n'
-                              '         pctScaleTimeout,\n'
-                              '         sOs,\n'
-                              '         sOsVersion,\n'
-                              '         sCpuVendor,\n'
-                              '         sCpuArch,\n'
-                              '         sCpuName,\n'
-                              '         lCpuRevision,\n'
-                              '         cCpus,\n'
-                              '         fCpuHwVirt,\n'
-                              '         fCpuNestedPaging,\n'
-                              '         fCpu64BitGuest,\n'
-                              '         fChipsetIoMmu,\n'
-                              '         cMbMemory,\n'
-                              '         cMbScratch,\n'
-                              '         sReport,\n'
-                              '         iTestBoxScriptRev,\n'
-                              '         iPythonHexVersion,\n'
-                              '         enmPendingCmd\n'
-                              '         )\n'
-                              'SELECT   NEXTVAL(\'TestBoxGenIdSeq\'),\n'
-                              '         idTestBox,\n'
-                              '         %s,\n'          # tsEffective
-                              '         %s,\n'          # uidAuthor
-                              '         %s,\n'          # ip
-                              '         %s,\n'          # uuidSystem
-                              '         %s,\n'          # sName
-                              '         %s,\n'          # sDescription
-                              '         %s,\n'          # idSchedGroup
-                              '         %s,\n'          # fEnabled
-                              '         %s,\n'          # enmLomKind
-                              '         %s,\n'          # ipLom
-                              '         %s,\n'          # pctScaleTimeout
-                              '         sOs,\n'
-                              '         sOsVersion,\n'
-                              '         sCpuVendor,\n'
-                              '         sCpuArch,\n'
-                              '         sCpuName,\n'
-                              '         lCpuRevision,\n'
-                              '         cCpus,\n'
-                              '         fCpuHwVirt,\n'
-                              '         fCpuNestedPaging,\n'
-                              '         fCpu64BitGuest,\n'
-                              '         fChipsetIoMmu,\n'
-                              '         cMbMemory,\n'
-                              '         cMbScratch,\n'
-                              '         sReport,\n'
-                              '         iTestBoxScriptRev,\n'
-                              '         iPythonHexVersion,\n'
-                              '         %s\n'           # enmPendingCmd
-                              'FROM     TestBoxes\n'
-                              'WHERE    idGenTestBox = %s\n'
-                              'RETURNING idGenTestBox, tsEffective'
-                              , (tsEffective,
-                                 uidAuthor,
-                                 oData.ip,
-                                 oData.uuidSystem,
-                                 oData.sName,
-                                 oData.sDescription,
-                                 oData.idSchedGroup,
-                                 oData.fEnabled,
-                                 oData.enmLomKind,
-                                 oData.ipLom,
-                                 oData.pctScaleTimeout,
-                                 oData.enmPendingCmd,
-                                 oData.idGenTestBox,
-                              ));
-            aRow = self._oDb.fetchOne();
-            if aRow is None:
-                raise TMExceptionBase('Insert failed? oRow=None');
-            idGenTestBox = aRow[0];
-            tsEffective  = aRow[1];
-            self._oDb.maybeCommit(fCommit);
-        except:
-            self._oDb.rollback();
-            raise;
-
-        return (idGenTestBox, tsEffective);
+        self._oDb.callProc('TestBoxLogic_editEntry'
+                           , ( uidAuthor,
+                               oData.idTestBox,
+                               oData.ip,            # Should we allow setting the IP?
+                               oData.uuidSystem,
+                               oData.sName,
+                               oData.sDescription,
+                               oData.idSchedGroup,
+                               oData.fEnabled,
+                               oData.enmLomKind,
+                               oData.ipLom,
+                               oData.pctScaleTimeout,
+                               oData.sComment,
+                               oData.enmPendingCmd, ));
+        aoRow = self._oDb.fetchOne();
+        self._oDb.maybeCommit(fCommit);
+        return (aoRow[0], aoRow[1]);
+
+
+    def removeEntry(self, uidAuthor, idTestBox, fCascade = False, fCommit = False):
+        """
+        Delete user account
+        """
+        self._oDb.callProc('TestBoxLogic_removeEntry'
+                           , ( uidAuthor, idTestBox, fCascade,));
+        self._oDb.maybeCommit(fCommit);
+        return True;
+
 
     def updateOnSignOn(self, idTestBox, idGenTestBox, sTestBoxAddr, sOs, sOsVersion, # pylint: disable=R0913,R0914
                        sCpuVendor, sCpuArch, sCpuName, lCpuRevision, cCpus, fCpuHwVirt, fCpuNestedPaging, fCpu64BitGuest,
-                       fChipsetIoMmu, cMbMemory, cMbScratch, sReport, iTestBoxScriptRev, iPythonHexVersion):
+                       fChipsetIoMmu, fRawMode, cMbMemory, cMbScratch, sReport, iTestBoxScriptRev, iPythonHexVersion):
         """
         Update the testbox attributes automatically on behalf of the testbox script.
         Returns the new generation id on success, raises an exception on failure.
         """
-        try:
-            # Would be easier to do this using an insert or update hook, I think. Much easier.
-            self._oDb.execute('UPDATE ONLY TestBoxes\n'
-                              'SET      tsExpire = CURRENT_TIMESTAMP\n'
-                              'WHERE    idGenTestBox = %s\n'
-                              '     AND tsExpire = \'infinity\'::timestamp\n'
-                              'RETURNING tsExpire\n',
-                              (idGenTestBox,));
-            tsEffective = self._oDb.fetchOne()[0];
-
-            self._oDb.execute('INSERT INTO TestBoxes (\n'
-                              '         idGenTestBox,\n'
-                              '         idTestBox,\n'
-                              '         tsEffective,\n'
-                              '         uidAuthor,\n'
-                              '         ip,\n'
-                              '         uuidSystem,\n'
-                              '         sName,\n'
-                              '         sDescription,\n'
-                              '         idSchedGroup,\n'
-                              '         fEnabled,\n'
-                              '         enmLomKind,\n'
-                              '         ipLom,\n'
-                              '         pctScaleTimeout,\n'
-                              '         sOs,\n'
-                              '         sOsVersion,\n'
-                              '         sCpuVendor,\n'
-                              '         sCpuArch,\n'
-                              '         sCpuName,\n'
-                              '         lCpuRevision,\n'
-                              '         cCpus,\n'
-                              '         fCpuHwVirt,\n'
-                              '         fCpuNestedPaging,\n'
-                              '         fCpu64BitGuest,\n'
-                              '         fChipsetIoMmu,\n'
-                              '         cMbMemory,\n'
-                              '         cMbScratch,\n'
-                              '         sReport,\n'
-                              '         iTestBoxScriptRev,\n'
-                              '         iPythonHexVersion,\n'
-                              '         enmPendingCmd\n'
-                              '         )\n'
-                              'SELECT   NEXTVAL(\'TestBoxGenIdSeq\'),\n'
-                              '         %s,\n'
-                              '         %s,\n'
-                              '         NULL,\n'            # uidAuthor
-                              '         %s,\n'
-                              '         uuidSystem,\n'
-                              '         sName,\n'
-                              '         sDescription,\n'
-                              '         idSchedGroup,\n'
-                              '         fEnabled,\n'
-                              '         enmLomKind,\n'
-                              '         ipLom,\n'
-                              '         pctScaleTimeout,\n'
-                              '         %s,\n'              # sOs
-                              '         %s,\n'              # sOsVersion
-                              '         %s,\n'              # sCpuVendor
-                              '         %s,\n'              # sCpuArch
-                              '         %s,\n'              # sCpuName
-                              '         %s,\n'              # lCpuRevision
-                              '         %s,\n'              # cCpus
-                              '         %s,\n'              # fCpuHwVirt
-                              '         %s,\n'              # fCpuNestedPaging
-                              '         %s,\n'              # fCpu64BitGuest
-                              '         %s,\n'              # fChipsetIoMmu
-                              '         %s,\n'              # cMbMemory
-                              '         %s,\n'              # cMbScratch
-                              '         %s,\n'              # sReport
-                              '         %s,\n'              # iTestBoxScriptRev
-                              '         %s,\n'              # iPythonHexVersion
-                              '         enmPendingCmd\n'
-                              'FROM     TestBoxes\n'
-                              'WHERE    idGenTestBox = %s\n'
-                              'RETURNING idGenTestBox'
-                              , (idTestBox,
-                                 tsEffective,
-                                 sTestBoxAddr,
-                                 sOs,
-                                 sOsVersion,
-                                 sCpuVendor,
-                                 sCpuArch,
-                                 sCpuName,
-                                 lCpuRevision,
-                                 cCpus,
-                                 fCpuHwVirt,
-                                 fCpuNestedPaging,
-                                 fCpu64BitGuest,
-                                 fChipsetIoMmu,
-                                 cMbMemory,
-                                 cMbScratch,
-                                 sReport,
-                                 iTestBoxScriptRev,
-                                 iPythonHexVersion,
-                                 idGenTestBox,
-                              ));
-            idGenTestBox = self._oDb.fetchOne()[0];
-            self._oDb.commit();
-        except:
-            self._oDb.rollback();
-            raise;
-
-        return idGenTestBox;
-
-    def setCommand(self, idTestBox, sOldCommand, sNewCommand, uidAuthor = None, fCommit = False, sComment = None,
-                   fNoRollbackOnInFlightCollision = False):
+        _ = idGenTestBox;
+        self._oDb.callProc('TestBoxLogic_updateOnSignOn'
+                           , ( idTestBox,
+                               sTestBoxAddr,
+                               sOs,
+                               sOsVersion,
+                               sCpuVendor,
+                               sCpuArch,
+                               sCpuName,
+                               lCpuRevision,
+                               cCpus,
+                               fCpuHwVirt,
+                               fCpuNestedPaging,
+                               fCpu64BitGuest,
+                               fChipsetIoMmu,
+                               fRawMode,
+                               cMbMemory,
+                               cMbScratch,
+                               sReport,
+                               iTestBoxScriptRev,
+                               iPythonHexVersion,));
+        return self._oDb.fetchOne()[0];
+
+
+    def setCommand(self, idTestBox, sOldCommand, sNewCommand, uidAuthor = None, fCommit = False, sComment = None):
         """
         Sets or resets the pending command on a testbox.
         Returns (idGenTestBox, tsEffective) of the new row.
         """
-        _ = sComment;
-        try:
-            # Would be easier to do this using an insert or update hook, I think. Much easier.
-            self._oDb.execute('UPDATE ONLY TestBoxes\n'
-                              'SET      tsExpire = CURRENT_TIMESTAMP\n'
-                              'WHERE    idTestBox = %s\n'
-                              '     AND tsExpire = \'infinity\'::timestamp\n'
-                              '     AND enmPendingCmd = %s\n'
-                              'RETURNING tsExpire\n',
-                              (idTestBox, sOldCommand,));
-            if self._oDb.getRowCount() == 0:
-                raise TMInFligthCollision();
-            tsEffective = self._oDb.fetchOne()[0];
-
-            self._oDb.execute('INSERT INTO TestBoxes (\n'
-                              '         idGenTestBox,\n'
-                              '         idTestBox,\n'
-                              '         tsEffective,\n'
-                              '         uidAuthor,\n'
-                              '         ip,\n'
-                              '         uuidSystem,\n'
-                              '         sName,\n'
-                              '         sDescription,\n'
-                              '         idSchedGroup,\n'
-                              '         fEnabled,\n'
-                              '         enmLomKind,\n'
-                              '         ipLom,\n'
-                              '         pctScaleTimeout,\n'
-                              '         sOs,\n'
-                              '         sOsVersion,\n'
-                              '         sCpuVendor,\n'
-                              '         sCpuArch,\n'
-                              '         sCpuName,\n'
-                              '         lCpuRevision,\n'
-                              '         cCpus,\n'
-                              '         fCpuHwVirt,\n'
-                              '         fCpuNestedPaging,\n'
-                              '         fCpu64BitGuest,\n'
-                              '         fChipsetIoMmu,\n'
-                              '         cMbMemory,\n'
-                              '         cMbScratch,\n'
-                              '         sReport,\n'
-                              '         iTestBoxScriptRev,\n'
-                              '         iPythonHexVersion,\n'
-                              '         enmPendingCmd\n'
-                              '         )\n'
-                              'SELECT   NEXTVAL(\'TestBoxGenIdSeq\'),\n'
-                              '         %s,\n'      # idTestBox
-                              '         %s,\n'      # tsEffective
-                              '         %s,\n'      # uidAuthor
-                              '         ip,\n'
-                              '         uuidSystem,\n'
-                              '         sName,\n'
-                              '         sDescription,\n'
-                              '         idSchedGroup,\n'
-                              '         fEnabled,\n'
-                              '         enmLomKind,\n'
-                              '         ipLom,\n'
-                              '         pctScaleTimeout,\n'
-                              '         sOs,\n'
-                              '         sOsVersion,\n'
-                              '         sCpuVendor,\n'
-                              '         sCpuArch,\n'
-                              '         sCpuName,\n'
-                              '         lCpuRevision,\n'
-                              '         cCpus,\n'
-                              '         fCpuHwVirt,\n'
-                              '         fCpuNestedPaging,\n'
-                              '         fCpu64BitGuest,\n'
-                              '         fChipsetIoMmu,\n'
-                              '         cMbMemory,\n'
-                              '         cMbScratch,\n'
-                              '         sReport,\n'
-                              '         iTestBoxScriptRev,\n'
-                              '         iPythonHexVersion,\n'
-                              '         %s\n'       # enmPendingCmd
-                              'FROM     TestBoxes\n'
-                              'WHERE    idTestBox = %s\n'
-                              '     AND tsExpire  = %s\n'
-                              '     AND enmPendingCmd = %s\n'
-                              'RETURNING idGenTestBox'
-                              , (idTestBox,
-                                 tsEffective,
-                                 uidAuthor,
-                                 sNewCommand,
-                                 idTestBox,
-                                 tsEffective,
-                                 sOldCommand,
-                              ));
-            idGenTestBox = self._oDb.fetchOne()[0];
-            if fCommit is True:
-                self._oDb.commit();
-
-        except TMInFligthCollision: # This is pretty stupid, but don't want to touch testboxcontroller.py now.
-            if not fNoRollbackOnInFlightCollision:
-                self._oDb.rollback();
-            raise;
-        except:
-            self._oDb.rollback();
-            raise;
-
-        return (idGenTestBox, tsEffective);
-
+        ## @todo throw TMInFligthCollision again...
+        self._oDb.callProc('TestBoxLogic_setCommand'
+                           , ( uidAuthor, idTestBox, sOldCommand, sNewCommand, sComment,));
+        aoRow = self._oDb.fetchOne();
+        self._oDb.maybeCommit(fCommit);
+        return (aoRow[0], aoRow[1]);
 
 
@@ -988,5 +687,5 @@
         """
         self._oDb.execute('SELECT   *\n'
-                          'FROM     TestBoxes\n'
+                          'FROM     TestBoxesWithStrings\n'
                           'WHERE    tsExpire=\'infinity\'::timestamp;')
 
@@ -997,35 +696,4 @@
         return aoRet
 
-    def _historize(self, idTestBox, uidAuthor, tsExpire = None):
-        """ Historizes the current entry. """
-        if tsExpire is None:
-            self._oDb.execute('UPDATE TestBoxes\n'
-                              'SET    tsExpire   = CURRENT_TIMESTAMP,\n'
-                              '       uidAuthor  = %s\n'
-                              'WHERE  idTestBox  = %s\n'
-                              '   AND tsExpire = \'infinity\'::TIMESTAMP\n'
-                              , (uidAuthor, idTestBox,));
-        else:
-            self._oDb.execute('UPDATE TestBoxes\n'
-                              'SET    tsExpire   = %s,\n'
-                              '       uidAuthor  = %s\n'
-                              'WHERE  idTestBox  = %s\n'
-                              '   AND tsExpire = \'infinity\'::TIMESTAMP\n'
-                              , (uidAuthor, tsExpire, idTestBox,));
-        return True;
-
-
-    def removeEntry(self, uidAuthor, idTestBox, fCascade = False, fCommit=False):
-        """
-        Delete user account
-        """
-        _ = fCascade;
-
-        fRc = self._historize(idTestBox, uidAuthor, None);
-        if fRc:
-            self._oDb.maybeCommit(fCommit);
-
-        return fRc
-
 
     def cachedLookup(self, idTestBox):
@@ -1041,6 +709,6 @@
         oEntry = self.dCache.get(idTestBox, None);
         if oEntry is None:
-            self._oDb.execute('SELECT   *\n'
-                              'FROM     TestBoxes\n'
+            self._oDb.execute('SELECT   TestBoxesWithStrings.*\n'
+                              'FROM     TestBoxesWithStrings\n'
                               'WHERE    idTestBox  = %s\n'
                               '     AND tsExpire   = \'infinity\'::TIMESTAMP\n'
@@ -1048,5 +716,5 @@
             if self._oDb.getRowCount() == 0:
                 # Maybe it was deleted, try get the last entry.
-                self._oDb.execute('SELECT   *\n'
+                self._oDb.execute('SELECT   TestBoxesWithStrings.*\n'
                                   'FROM     TestBoxes\n'
                                   'WHERE    idTestBox = %s\n'
@@ -1096,10 +764,9 @@
         Issues a reboot command for the given test box.
         Return True on succes, False on in-flight collision.
-        May raise DB exception with rollback on other trouble.
+        May raise DB exception on other trouble.
         """
         try:
             self.setCommand(idTestBox, sOldCommand, TestBoxData.ksTestBoxCmd_Reboot,
-                            uidAuthor = uidAuthor, fCommit = fCommit, sComment = sComment,
-                            fNoRollbackOnInFlightCollision = True);
+                            uidAuthor = uidAuthor, fCommit = fCommit, sComment = sComment);
         except TMInFligthCollision:
             return False;
Index: /trunk/src/VBox/ValidationKit/testmanager/core/testboxcontroller.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/testboxcontroller.py	(revision 61467)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/testboxcontroller.py	(revision 61468)
@@ -363,4 +363,5 @@
         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
@@ -395,5 +396,5 @@
         # Update the row in TestBoxes if something changed.
         #
-        if oTestBox.cMbScratch != 0:
+        if oTestBox.cMbScratch is not None and oTestBox.cMbScratch != 0:
             cPctScratchDiff = (cMbScratch - oTestBox.cMbScratch) * 100 / oTestBox.cMbScratch;
         else:
@@ -413,4 +414,5 @@
           or fCpu64BitGuest     != oTestBox.fCpu64BitGuest \
           or fChipsetIoMmu      != oTestBox.fChipsetIoMmu \
+          or fRawMode           != oTestBox.fRawMode \
           or cMbMemory          != oTestBox.cMbMemory \
           or abs(cPctScratchDiff) >= min(4 + cMbScratch / 10240, 12) \
@@ -432,4 +434,5 @@
                                          fCpu64BitGuest    = fCpu64BitGuest,
                                          fChipsetIoMmu     = fChipsetIoMmu,
+                                         fRawMode          = fRawMode,
                                          cMbMemory         = cMbMemory,
                                          cMbScratch        = cMbScratch,
Index: /trunk/src/VBox/ValidationKit/testmanager/core/testboxstatus.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/testboxstatus.py	(revision 61467)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/testboxstatus.py	(revision 61468)
@@ -160,9 +160,27 @@
         not found.  May throw an exception on database error.
         """
-        self._oDb.execute('SELECT   *\n'
-                          'FROM     TestBoxStatuses, TestBoxes\n'
+        self._oDb.execute('SELECT   TestBoxStatuses.*,\n'
+                          '         TestBoxes.*,\n'
+                          '         Str1.sValue,\n'
+                          '         Str2.sValue,\n'
+                          '         Str3.sValue,\n'
+                          '         Str4.sValue,\n'
+                          '         Str5.sValue,\n'
+                          '         Str6.sValue,\n'
+                          '         Str7.sValue,\n'
+                          '         Str8.sValue\n'
+                          'FROM     TestBoxStatuses,\n'
+                          '         TestBoxes\n'
+                          '         LEFT OUTER JOIN TestBoxStrTab Str1 ON idStrDescription = Str1.idStr\n'
+                          '         LEFT OUTER JOIN TestBoxStrTab Str2 ON idStrComment     = Str2.idStr\n'
+                          '         LEFT OUTER JOIN TestBoxStrTab Str3 ON idStrOs          = Str3.idStr\n'
+                          '         LEFT OUTER JOIN TestBoxStrTab Str4 ON idStrOsVersion   = Str4.idStr\n'
+                          '         LEFT OUTER JOIN TestBoxStrTab Str5 ON idStrCpuVendor   = Str5.idStr\n'
+                          '         LEFT OUTER JOIN TestBoxStrTab Str6 ON idStrCpuArch     = Str6.idStr\n'
+                          '         LEFT OUTER JOIN TestBoxStrTab Str7 ON idStrCpuName     = Str7.idStr\n'
+                          '         LEFT OUTER JOIN TestBoxStrTab Str8 ON idStrReport      = Str8.idStr\n'
                           'WHERE    TestBoxStatuses.idTestBox = %s\n'
                           '     AND TestBoxes.idTestBox  = %s\n'
-                          '     AND TestBoxes.tsExpire   = \'infinity\'::timestamp\n'
+                          '     AND TestBoxes.tsExpire   = \'infinity\'::TIMESTAMP\n'
                           '     AND TestBoxes.uuidSystem = %s\n'
                           '     AND TestBoxes.ip         = %s\n'
Index: /trunk/src/VBox/ValidationKit/testmanager/core/testcase.pgsql
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/testcase.pgsql	(revision 61467)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/testcase.pgsql	(revision 61468)
@@ -207,4 +207,5 @@
             v_Row.tsEffective   := v_tsEffective;
             v_Row.tsExpire      := CURRENT_TIMESTAMP;
+            v_Row.uidAuthor     := a_uidAuthor;
             SELECT NEXTVAL('TestCaseGenIdSeq') INTO v_Row.idGenTestCase;
             INSERT INTO TestCases VALUES (v_Row.*);
Index: /trunk/src/VBox/ValidationKit/testmanager/core/testcase.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/testcase.py	(revision 61467)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/testcase.py	(revision 61468)
@@ -636,4 +636,5 @@
                 'fCpu64BitGuest':       False,
                 'fChipsetIoMmu':        False,
+                'fRawMode':             False,
                 'cMbMemory':            985034,
                 'cMbScratch':           1234089,
@@ -652,4 +653,5 @@
                 'fCpu64BitGuest':       True,
                 'fChipsetIoMmu':        True,
+                'fRawMode':             True,
                 'cMbMemory':            9999999999,
                 'cMbScratch':           9999999999999,
@@ -677,4 +679,5 @@
             'fCpu64BitGuest':       oTestBoxData.fCpu64BitGuest,
             'fChipsetIoMmu':        oTestBoxData.fChipsetIoMmu,
+            'fRawMode':             oTestBoxData.fRawMode,
             'cMbMemory':            oTestBoxData.cMbMemory,
             'cMbScratch':           oTestBoxData.cMbScratch,
Index: /trunk/src/VBox/ValidationKit/testmanager/core/testresults.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/testresults.py	(revision 61467)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/testresults.py	(revision 61468)
@@ -724,45 +724,45 @@
             '', '' ),
         ksResultsSortByTestBoxOsArch: (
-            ', TestBoxes',
-            ' AND TestSets.idGenTestBox = TestBoxes.idGenTestBox',
-            ' TestBoxes.sOs, TestBoxes.sCpuArch',
+            ', TestBoxesWithStrings',
+            ' AND TestSets.idGenTestBox = TestBoxesWithStrings.idGenTestBox',
+            ' TestBoxesWithStrings.sOs, TestBoxesWithStrings.sCpuArch',
             '', ''  ),
         ksResultsSortByTestBoxOs: (
-            ', TestBoxes',
-            ' AND TestSets.idGenTestBox = TestBoxes.idGenTestBox',
-            ' TestBoxes.sOs',
+            ', TestBoxesWithStrings',
+            ' AND TestSets.idGenTestBox = TestBoxesWithStrings.idGenTestBox',
+            ' TestBoxesWithStrings.sOs',
             '', ''  ),
         ksResultsSortByTestBoxOsVersion: (
-            ', TestBoxes',
-            ' AND TestSets.idGenTestBox = TestBoxes.idGenTestBox',
-            ' TestBoxes.sOs, TestBoxes.sOsVersion DESC',
+            ', TestBoxesWithStrings',
+            ' AND TestSets.idGenTestBox = TestBoxesWithStrings.idGenTestBox',
+            ' TestBoxesWithStrings.sOs, TestBoxesWithStrings.sOsVersion DESC',
             '', ''  ),
         ksResultsSortByTestBoxArch: (
-            ', TestBoxes',
-            ' AND TestSets.idGenTestBox = TestBoxes.idGenTestBox',
-            ' TestBoxes.sCpuArch',
+            ', TestBoxesWithStrings',
+            ' AND TestSets.idGenTestBox = TestBoxesWithStrings.idGenTestBox',
+            ' TestBoxesWithStrings.sCpuArch',
             '', ''  ),
         ksResultsSortByTestBoxCpuVendor: (
-            ', TestBoxes',
-            ' AND TestSets.idGenTestBox = TestBoxes.idGenTestBox',
-            ' TestBoxes.sCpuVendor',
+            ', TestBoxesWithStrings',
+            ' AND TestSets.idGenTestBox = TestBoxesWithStrings.idGenTestBox',
+            ' TestBoxesWithStrings.sCpuVendor',
             '', ''  ),
         ksResultsSortByTestBoxCpuName: (
-            ', TestBoxes',
-            ' AND TestSets.idGenTestBox = TestBoxes.idGenTestBox',
-            ' TestBoxes.sCpuVendor, TestBoxes.sCpuName',
+            ', TestBoxesWithStrings',
+            ' AND TestSets.idGenTestBox = TestBoxesWithStrings.idGenTestBox',
+            ' TestBoxesWithStrings.sCpuVendor, TestBoxesWithStrings.sCpuName',
             '', ''  ),
         ksResultsSortByTestBoxCpuRev: (
-            ', TestBoxes',
-            ' AND TestSets.idGenTestBox = TestBoxes.idGenTestBox',
-            ' TestBoxes.sCpuVendor, TestBoxes.lCpuRevision DESC',
-            ', TestBoxes.lCpuRevision',
-            ', TestBoxes.lCpuRevision' ),
+            ', TestBoxesWithStrings',
+            ' AND TestSets.idGenTestBox = TestBoxesWithStrings.idGenTestBox',
+            ' TestBoxesWithStrings.sCpuVendor, TestBoxesWithStrings.lCpuRevision DESC',
+            ', TestBoxesWithStrings.lCpuRevision',
+            ', TestBoxesWithStrings.lCpuRevision' ),
         ksResultsSortByTestBoxCpuFeatures: (
             ', TestBoxes',
             ' AND TestSets.idGenTestBox = TestBoxes.idGenTestBox',
             ' TestBoxes.fCpuHwVirt DESC, TestBoxes.fCpuNestedPaging DESC, TestBoxes.fCpu64BitGuest DESC, TestBoxes.cCpus DESC',
-            ', TestBoxes.cCpus',
-            ', TestBoxes.cCpus' ),
+            '',
+            '' ),
         ksResultsSortByTestCaseName: (
             ', TestCases',
@@ -779,6 +779,12 @@
     kdResultGroupingMap = {
         ksResultsGroupingTypeNone: (
-            # Grouping tables;                # Grouping field;          # Grouping where addition.  # Sort by overrides.
-            '',                                None,                      None,                      {},
+            # Grouping tables;
+            '',
+            # Grouping field;
+            None,
+            # Grouping where addition.
+            None,
+            # Sort by overrides.
+            {},
         ),
         ksResultsGroupingTypeTestGroup:  ('', 'TestSets.idTestGroup',     None,                      {},),
@@ -794,20 +800,40 @@
         ),
         ksResultsGroupingTypeSchedGroup: (
-            ', TestBoxes',
-            'TestBoxes.idSchedGroup',
-            ' AND TestSets.idGenTestBox = TestBoxes.idGenTestBox',
-            { ksResultsSortByTestBoxName:       ( '', None, ' TestBoxes.sName DESC', '', '' ),
-              ksResultsSortByTestBoxOsArch:     ( '', None, ' TestBoxes.sOs, TestBoxes.sCpuArch', '', '' ),
-              ksResultsSortByTestBoxOs:         ( '', None, ' TestBoxes.sOs', ''  ),
-              ksResultsSortByTestBoxOsVersion:  ( '', None, ' TestBoxes.sOs, TestBoxes.sOsVersion DESC', '', '' ),
-              ksResultsSortByTestBoxArch:       ( '', None, ' TestBoxes.sCpuArch', ''  ),
-              ksResultsSortByTestBoxCpuVendor:  ( '', None, ' TestBoxes.sCpuVendor', ''  ),
-              ksResultsSortByTestBoxCpuName:    ( '', None, ' TestBoxes.sCpuVendor, TestBoxes.sCpuName', '', '' ),
+            ', TestBoxesWithStrings',
+            'TestBoxesWithStrings.idSchedGroup',
+            ' AND TestSets.idGenTestBox = TestBoxesWithStrings.idGenTestBox',
+            {
+
+              ksResultsSortByTestBoxName: (
+                  # Sorting tables.
+                  '',
+                  # Sorting table join(s).
+                  None,
+                  # Start of ORDER BY statement.
+                  ' TestBoxesWithStrings.sName DESC',
+                  # Extra columns to fetch for the above ORDER BY to work in a SELECT DISTINCT statement.
+                  '',
+                  # Columns for the GROUP BY
+                  '' ),
+              ksResultsSortByTestBoxOsArch:     ( '', None, ' TestBoxesWithStrings.sOs, TestBoxesWithStrings.sCpuArch', '', '' ),
+              ksResultsSortByTestBoxOs:         ( '', None, ' TestBoxesWithStrings.sOs', '', ''  ),
+              ksResultsSortByTestBoxOsVersion:  ( '', None, ' TestBoxesWithStrings.sOs, TestBoxesWithStrings.sOsVersion DESC',
+                                                  '', '' ),
+              ksResultsSortByTestBoxArch:       ( '', None, ' TestBoxesWithStrings.sCpuArch', '', ''  ),
+              ksResultsSortByTestBoxCpuVendor:  ( '', None, ' TestBoxesWithStrings.sCpuVendor', '', ''  ),
+              ksResultsSortByTestBoxCpuName:    ( '', None, ' TestBoxesWithStrings.sCpuVendor, TestBoxesWithStrings.sCpuName',
+                                                  '', '' ),
               ksResultsSortByTestBoxCpuRev: (
-                  '', None,  ' TestBoxes.sCpuVendor, TestBoxes.lCpuRevision DESC', ', TestBoxes.lCpuRevision', '' ),
+                  '',
+                  None,
+                  ' TestBoxesWithStrings.sCpuVendor, TestBoxesWithStrings.lCpuRevision DESC',
+                  ', TestBoxesWithStrings.lCpuRevision',
+                  ', TestBoxesWithStrings.lCpuRevision' ),
               ksResultsSortByTestBoxCpuFeatures: (
-                  ' TestBoxes.fCpuHwVirt DESC, TestBoxes.fCpuNestedPaging DESC, TestBoxes.fCpu64BitGuest DESC, '
-                  + 'TestBoxes.cCpus DESC',
-                  ', TestBoxes.cCpus',
+                  '',
+                  None,
+                  ' TestBoxesWithStrings.fCpuHwVirt DESC, TestBoxesWithStrings.fCpuNestedPaging DESC, '
+                  +'TestBoxesWithStrings.fCpu64BitGuest DESC, TestBoxesWithStrings.cCpus DESC',
+                  '',
                   '' ), }
         ),
@@ -884,15 +910,15 @@
                   '       Builds.sVersion,\n' \
                   '       Builds.iRevision,\n' \
-                  '       TestBoxes.sOs,\n' \
-                  '       TestBoxes.sOsVersion,\n' \
-                  '       TestBoxes.sCpuArch,\n' \
-                  '       TestBoxes.sCpuVendor,\n' \
-                  '       TestBoxes.sCpuName,\n' \
-                  '       TestBoxes.cCpus,\n' \
-                  '       TestBoxes.fCpuHwVirt,\n' \
-                  '       TestBoxes.fCpuNestedPaging,\n' \
-                  '       TestBoxes.fCpu64BitGuest,\n' \
-                  '       TestBoxes.idTestBox,\n' \
-                  '       TestBoxes.sName,\n' \
+                  '       TestBoxesWithStrings.sOs,\n' \
+                  '       TestBoxesWithStrings.sOsVersion,\n' \
+                  '       TestBoxesWithStrings.sCpuArch,\n' \
+                  '       TestBoxesWithStrings.sCpuVendor,\n' \
+                  '       TestBoxesWithStrings.sCpuName,\n' \
+                  '       TestBoxesWithStrings.cCpus,\n' \
+                  '       TestBoxesWithStrings.fCpuHwVirt,\n' \
+                  '       TestBoxesWithStrings.fCpuNestedPaging,\n' \
+                  '       TestBoxesWithStrings.fCpu64BitGuest,\n' \
+                  '       TestBoxesWithStrings.idTestBox,\n' \
+                  '       TestBoxesWithStrings.sName,\n' \
                   '       TestResults.tsCreated,\n' \
                   '       COALESCE(TestResults.tsElapsed, CURRENT_TIMESTAMP - TestResults.tsCreated) AS tsElapsedTestResult,\n' \
@@ -913,5 +939,5 @@
                   'FROM   BuildCategories,\n' \
                   '       Builds,\n' \
-                  '       TestBoxes,\n' \
+                  '       TestBoxesWithStrings,\n' \
                   '       TestResults,\n' \
                   '       TestCases,\n' \
@@ -970,5 +996,5 @@
                   '   AND Builds.tsEffective        <= TestSets.tsCreated\n' \
                   '   AND Builds.idBuildCategory     = BuildCategories.idBuildCategory\n' \
-                  '   AND TestSets.idGenTestBox      = TestBoxes.idGenTestBox\n' \
+                  '   AND TestSets.idGenTestBox      = TestBoxesWithStrings.idGenTestBox\n' \
                   '   AND TestSets.idGenTestCase     = TestCases.idGenTestCase\n' \
                   '   AND TestSets.idGenTestCaseArgs = TestCaseArgs.idGenTestCaseArgs\n';
@@ -982,15 +1008,15 @@
                   '         Builds.sVersion,\n' \
                   '         Builds.iRevision,\n' \
-                  '         TestBoxes.sOs,\n' \
-                  '         TestBoxes.sOsVersion,\n' \
-                  '         TestBoxes.sCpuArch,\n' \
-                  '         TestBoxes.sCpuVendor,\n' \
-                  '         TestBoxes.sCpuName,\n' \
-                  '         TestBoxes.cCpus,\n' \
-                  '         TestBoxes.fCpuHwVirt,\n' \
-                  '         TestBoxes.fCpuNestedPaging,\n' \
-                  '         TestBoxes.fCpu64BitGuest,\n' \
-                  '         TestBoxes.idTestBox,\n' \
-                  '         TestBoxes.sName,\n' \
+                  '         TestBoxesWithStrings.sOs,\n' \
+                  '         TestBoxesWithStrings.sOsVersion,\n' \
+                  '         TestBoxesWithStrings.sCpuArch,\n' \
+                  '         TestBoxesWithStrings.sCpuVendor,\n' \
+                  '         TestBoxesWithStrings.sCpuName,\n' \
+                  '         TestBoxesWithStrings.cCpus,\n' \
+                  '         TestBoxesWithStrings.fCpuHwVirt,\n' \
+                  '         TestBoxesWithStrings.fCpuNestedPaging,\n' \
+                  '         TestBoxesWithStrings.fCpu64BitGuest,\n' \
+                  '         TestBoxesWithStrings.idTestBox,\n' \
+                  '         TestBoxesWithStrings.sName,\n' \
                   '         TestResults.tsCreated,\n' \
                   '         tsElapsedTestResult,\n' \
@@ -1007,5 +1033,5 @@
         sQuery += 'ORDER BY ';
         if sSortOrderBy is not None:
-            sQuery += sSortOrderBy + ',\n       ';
+            sQuery += sSortOrderBy.replace('TestBoxes.', 'TestBoxesWithStrings.') + ',\n       ';
         sQuery += '(TestSets.tsDone IS NULL) DESC, TestSets.idTestSet DESC\n';
 
@@ -1154,6 +1180,6 @@
         """
 
-        self._oDb.execute('SELECT TestBoxes.*\n'
-                          'FROM   TestBoxes,\n'
+        self._oDb.execute('SELECT TestBoxesWithStrings.*\n'
+                          'FROM   TestBoxesWithStrings,\n'
                           '       ( SELECT idTestBox         AS idTestBox,\n'
                           '                MAX(idGenTestBox) AS idGenTestBox\n'
@@ -1162,6 +1188,6 @@
                           '         GROUP BY idTestBox\n'
                           '       ) AS TestBoxIDs\n'
-                          'WHERE  TestBoxes.idGenTestBox = TestBoxIDs.idGenTestBox\n'
-                          'ORDER BY TestBoxes.sName\n' );
+                          'WHERE  TestBoxesWithStrings.idGenTestBox = TestBoxIDs.idGenTestBox\n'
+                          'ORDER BY TestBoxesWithStrings.sName\n' );
         aoRet = []
         for aoRow in self._oDb.fetchAll():
Index: /trunk/src/VBox/ValidationKit/testmanager/core/testset.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/testset.py	(revision 61467)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/testset.py	(revision 61468)
@@ -623,10 +623,27 @@
         Returns an array of TestBoxData object representing the gang for the given testset.
         """
-        self._oDb.execute('SELECT   TestBoxes.*\n'
-                          'FROM     TestBoxes, TestSets\n'
+        self._oDb.execute('SELECT   TestBoxes.*,\n'
+                          '         Str1.sValue,\n'
+                          '         Str2.sValue,\n'
+                          '         Str3.sValue,\n'
+                          '         Str4.sValue,\n'
+                          '         Str5.sValue,\n'
+                          '         Str6.sValue,\n'
+                          '         Str7.sValue,\n'
+                          '         Str8.sValue\n'
+                          'FROM     TestBoxes\n'
+                          '         LEFT OUTER JOIN TestBoxStrTab Str1 ON idStrDescription = Str1.idStr\n'
+                          '         LEFT OUTER JOIN TestBoxStrTab Str2 ON idStrComment     = Str2.idStr\n'
+                          '         LEFT OUTER JOIN TestBoxStrTab Str3 ON idStrOs          = Str3.idStr\n'
+                          '         LEFT OUTER JOIN TestBoxStrTab Str4 ON idStrOsVersion   = Str4.idStr\n'
+                          '         LEFT OUTER JOIN TestBoxStrTab Str5 ON idStrCpuVendor   = Str5.idStr\n'
+                          '         LEFT OUTER JOIN TestBoxStrTab Str6 ON idStrCpuArch     = Str6.idStr\n'
+                          '         LEFT OUTER JOIN TestBoxStrTab Str7 ON idStrCpuName     = Str7.idStr\n'
+                          '         LEFT OUTER JOIN TestBoxStrTab Str8 ON idStrReport      = Str8.idStr,\n'
+                          '         TestSets'
                           'WHERE    TestSets.idTestSetGangLeader = %s\n'
                           '     AND TestSets.idGenTestBox        = TestBoxes.idGenTestBox\n'
                           'ORDER BY iGangMemberNo ASC\n'
-                          , (idTestSetGangLeader,));
+                          , ( idTestSetGangLeader,));
         aaoRows = self._oDb.fetchAll();
         aoTestBoxes = [];
Index: /trunk/src/VBox/ValidationKit/testmanager/db/Makefile.kmk
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/db/Makefile.kmk	(revision 61467)
+++ /trunk/src/VBox/ValidationKit/testmanager/db/Makefile.kmk	(revision 61468)
@@ -37,5 +37,5 @@
 GENERATED_FILES = TestManagerDatabaseComments.pgsql
 PSQL := $(firstword $(which $(foreach pgver, 95 94 93 92,psql$(pgver)) ) psql)
-PSQL_OPTS = -U postgres
+PSQL_OPTS = -U postgres --set=ON_ERROR_STOP=1
 
 all: $(GENERATED_FILES)
@@ -54,4 +54,5 @@
 		../core/useraccount.pgsql \
 		../core/testcase.pgsql \
+		../core/testbox.pgsql \
 		../core/globalresource.pgsql
 	@kmk_builtin_echo "Creating testmanager database: For script verification only!"
@@ -60,4 +61,5 @@
 	$(PSQL) $(PSQL_OPTS) -d testmanager -f ../core/useraccount.pgsql
 	$(PSQL) $(PSQL_OPTS) -d testmanager -f ../core/testcase.pgsql
+	$(PSQL) $(PSQL_OPTS) -d testmanager -f ../core/testbox.pgsql
 	$(PSQL) $(PSQL_OPTS) -d testmanager -f ../core/globalresource.pgsql
 	$(PSQL) $(PSQL_OPTS) -d testmanager -f TestManagerDatabaseDefaultUserAccounts.pgsql
@@ -66,8 +68,10 @@
 		../core/useraccount.pgsql \
 		../core/testcase.pgsql \
+		../core/testbox.pgsql \
 		../core/globalresource.pgsql
 	@kmk_builtin_echo "Reloading testmanager database functions"
 	$(PSQL) $(PSQL_OPTS) -d testmanager -f ../core/useraccount.pgsql
 	$(PSQL) $(PSQL_OPTS) -d testmanager -f ../core/testcase.pgsql
+	$(PSQL) $(PSQL_OPTS) -d testmanager -f ../core/testbox.pgsql
 	$(PSQL) $(PSQL_OPTS) -d testmanager -f ../core/globalresource.pgsql
 
Index: /trunk/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseInit.pgsql
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseInit.pgsql	(revision 61467)
+++ /trunk/src/VBox/ValidationKit/testmanager/db/TestManagerDatabaseInit.pgsql	(revision 61468)
@@ -680,4 +680,56 @@
 
 
+--- @table TestBoxStrTab
+-- String table for the test boxes.
+--
+-- This is a string cache for all string members in TestBoxes except the name.
+-- The rational is to avoid duplicating large strings like sReport when the 
+-- testbox reports a new cMbScratch value or the box when the test sheriff 
+-- sends a reboot command or similar.  
+-- 
+-- At the time this table was introduced, we had 400558 TestBoxes rows,  where 
+-- the SUM(LENGTH(sReport)) was 993MB.  There were really just 1066 distinct
+-- sReport values, with a total length of 0x3 MB.
+--
+-- Nothing is ever deleted from this table.
+--
+-- @note Should use a stored procedure to query/insert a string.
+--
+--
+-- TestBox stats prior to conversion:
+--      SELECT COUNT(*) FROM TestBoxes:                     400558 rows
+--      SELECT pg_total_relation_size('TestBoxes'):      740794368 bytes (706 MB)
+--      Average row cost:           740794368 / 400558 =      1849 bytes/row
+--
+-- After conversion:
+--      SELECT COUNT(*) FROM TestBoxes:                     400558 rows
+--      SELECT pg_total_relation_size('TestBoxes'):      144375808 bytes (138 MB)
+--      SELECT COUNT(idStr) FROM TestBoxStrTab:               1292 rows
+--      SELECT pg_total_relation_size('TestBoxStrTab'):    5709824 bytes (5.5 MB)
+--                   (144375808 + 5709824) / 740794368 =        20 %
+--      Average row cost boxes:     144375808 / 400558 =       360 bytes/row
+--      Average row cost strings:       5709824 / 1292 =      4420 bytes/row
+--
+CREATE SEQUENCE TestBoxStrTabIdSeq
+    START 1
+    INCREMENT BY 1
+    NO MAXVALUE
+    NO MINVALUE
+    CACHE 1;
+CREATE TABLE TestBoxStrTab (
+    --- The ID of this string.
+    idStr               INTEGER     PRIMARY KEY DEFAULT NEXTVAL('TestBoxStrTabIdSeq'),
+    --- The string value.
+    sValue              text        NOT NULL,
+    --- Creation time stamp.
+    tsCreated           TIMESTAMP WITH TIME ZONE  DEFAULT current_timestamp  NOT NULL
+);
+-- Note! Must use hash index as the sReport strings are too long for regular indexing.
+CREATE INDEX TestBoxStrTabNameIdx ON TestBoxStrTab USING hash (sValue);
+
+--- Empty string with ID 0.
+INSERT INTO TestBoxStrTab (idStr, sValue) VALUES (0, '');
+
+
 --- @type TestBoxCmd_T
 -- Testbox commands.
@@ -763,5 +815,5 @@
     --- Optional testbox description.
     -- Intended for describing the box as well as making other relevant notes.
-    sDescription        text        DEFAULT NULL,
+    idStrDescription    INTEGER     REFERENCES TestBoxStrTab(idStr)  DEFAULT NULL,
 
     --- Reference to the scheduling group that this testbox is a member of.
@@ -786,16 +838,19 @@
     pctScaleTimeout     smallint    DEFAULT 100  NOT NULL  CHECK (pctScaleTimeout > 10 AND pctScaleTimeout < 20000),
 
+    --- Change comment or similar.
+    idStrComment        INTEGER     REFERENCES TestBoxStrTab(idStr)  DEFAULT NULL,
+
     --- @name Scheduling properties (reported by testbox script).
     -- @{
     --- Same abbrieviations as kBuild, see KBUILD_OSES.
-    sOs                 text        DEFAULT NULL,
+    idStrOs             INTEGER     REFERENCES TestBoxStrTab(idStr)  DEFAULT NULL,
     --- Informational, no fixed format.
-    sOsVersion          text        DEFAULT NULL,
+    idStrOsVersion      INTEGER     REFERENCES TestBoxStrTab(idStr)  DEFAULT NULL,
     --- Same as CPUID reports (GenuineIntel, AuthenticAMD, CentaurHauls, ...).
-    sCpuVendor          text        DEFAULT NULL,
+    idStrCpuVendor      INTEGER     REFERENCES TestBoxStrTab(idStr)  DEFAULT NULL,
     --- Same as kBuild - x86, amd64, ... See KBUILD_ARCHES.
-    sCpuArch            text        DEFAULT NULL,
+    idStrCpuArch        INTEGER     REFERENCES TestBoxStrTab(idStr)  DEFAULT NULL,
     --- The CPU name if available.
-    sCpuName            text        DEFAULT NULL,
+    idStrCpuName        INTEGER     REFERENCES TestBoxStrTab(idStr)  DEFAULT NULL,
     --- Number identifying the CPU family/model/stepping/whatever.
     -- For x86 and AMD64 type CPUs, this will on the following format:
@@ -812,4 +867,6 @@
     --- Set if chipset with usable IOMMU (VT-d / AMD-Vi).
     fChipsetIoMmu       boolean     DEFAULT NULL,
+    --- Set if the test box does raw-mode tests.
+    fRawMode            boolean     DEFAULT NULL,
     --- The (approximate) memory size in megabytes (rounded down to nearest 4 MB).
     cMbMemory           bigint      DEFAULT NULL  CHECK (cMbMemory IS NULL OR cMbMemory > 0),
@@ -817,5 +874,5 @@
     cMbScratch          bigint      DEFAULT NULL  CHECK (cMbScratch IS NULL OR cMbScratch >= 0),
     --- Free form hardware and software report field.
-    sReport             text        DEFAULT NULL,
+    idStrReport         INTEGER     REFERENCES TestBoxStrTab(idStr)  DEFAULT NULL,
     --- @}
 
@@ -840,5 +897,26 @@
 
 
-
+--
+-- Create a view for TestBoxes where the strings are resolved.
+--
+CREATE VIEW TestBoxesWithStrings AS
+    SELECT  TestBoxes.*,
+            Str1.sValue AS sDescription,
+            Str2.sValue AS sComment,
+            Str3.sValue AS sOs,
+            Str4.sValue AS sOsVersion,
+            Str5.sValue AS sCpuVendor,
+            Str6.sValue AS sCpuArch,
+            Str7.sValue AS sCpuName,
+            Str8.sValue AS sReport
+    FROM    TestBoxes
+            LEFT OUTER JOIN TestBoxStrTab Str1 ON idStrDescription = Str1.idStr
+            LEFT OUTER JOIN TestBoxStrTab Str2 ON idStrComment     = Str2.idStr
+            LEFT OUTER JOIN TestBoxStrTab Str3 ON idStrOs          = Str3.idStr
+            LEFT OUTER JOIN TestBoxStrTab Str4 ON idStrOsVersion   = Str4.idStr
+            LEFT OUTER JOIN TestBoxStrTab Str5 ON idStrCpuVendor   = Str5.idStr
+            LEFT OUTER JOIN TestBoxStrTab Str6 ON idStrCpuArch     = Str6.idStr
+            LEFT OUTER JOIN TestBoxStrTab Str7 ON idStrCpuName     = Str7.idStr
+            LEFT OUTER JOIN TestBoxStrTab Str8 ON idStrReport      = Str8.idStr;
 
 
Index: /trunk/src/VBox/ValidationKit/testmanager/db/tmdb-r19-testboxes-3.pgsql
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/db/tmdb-r19-testboxes-3.pgsql	(revision 61468)
+++ /trunk/src/VBox/ValidationKit/testmanager/db/tmdb-r19-testboxes-3.pgsql	(revision 61468)
@@ -0,0 +1,347 @@
+-- $Id$
+--- @file
+-- VBox Test Manager Database - Adds sComment and fRawMode to TestBoxes and
+--                              moves the strings to separate table.
+--
+
+--
+-- Copyright (C) 2013-2016 Oracle Corporation
+--
+-- This file is part of VirtualBox Open Source Edition (OSE), as
+-- available from http://www.virtualbox.org. This file is free software;
+-- you can redistribute it and/or modify it under the terms of the GNU
+-- General Public License (GPL) as published by the Free Software
+-- Foundation, in version 2 as it comes in the "COPYING" file of the
+-- VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+-- hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+--
+-- The contents of this file may alternatively be used under the terms
+-- of the Common Development and Distribution License Version 1.0
+-- (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+-- VirtualBox OSE distribution, in which case the provisions of the
+-- CDDL are applicable instead of those of the GPL.
+--
+-- You may elect to license modified versions of this file under the
+-- terms and conditions of either the GPL or the CDDL or both.
+--
+
+--
+-- Cleanup after failed runs.
+--
+DROP TABLE IF EXISTS OldTestBoxes;
+
+-- Die on error from now on.
+\set ON_ERROR_STOP 1
+\set AUTOCOMMIT 0
+
+-- Sanity check that we haven't already run this script.
+SELECT 'done conversion already?', COUNT(sReport) FROM TestBoxes WHERE tsExpire = 'infinity'::TIMESTAMP;
+
+-- Total grid lock.
+LOCK TABLE TestBoxStatuses      IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestSets             IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE TestBoxes            IN ACCESS EXCLUSIVE MODE;
+LOCK TABLE SchedGroupMembers    IN ACCESS EXCLUSIVE MODE;
+      
+\d+ TestBoxes;
+
+--
+-- Rename the table, drop foreign keys refering to it, and drop constrains 
+-- within the table itself.  The latter is mostly for naming and we do it
+-- up front in case the database we're running against has different names
+-- due to previous conversions. 
+--
+ALTER TABLE TestBoxes RENAME TO OldTestBoxes;
+
+ALTER TABLE OldTestBoxes    DROP CONSTRAINT testboxes_ccpus_check;
+ALTER TABLE OldTestBoxes    DROP CONSTRAINT testboxes_check;
+ALTER TABLE OldTestBoxes    DROP CONSTRAINT testboxes_cmbmemory_check;
+ALTER TABLE OldTestBoxes    DROP CONSTRAINT testboxes_cmbscratch_check;
+ALTER TABLE OldTestBoxes    DROP CONSTRAINT testboxes_pctscaletimeout_check;
+
+ALTER TABLE TestBoxStatuses DROP CONSTRAINT TestBoxStatuses_idGenTestBox_fkey;
+ALTER TABLE TestSets        DROP CONSTRAINT TestSets_idGenTestBox_fkey;
+
+ALTER TABLE OldTestBoxes    DROP CONSTRAINT testboxes_pkey;
+ALTER TABLE OldTestBoxes    DROP CONSTRAINT testboxes_idgentestbox_key;
+                   
+DROP INDEX IF EXISTS TestBoxesUuidIdx;
+DROP INDEX IF EXISTS TestBoxesExpireEffectiveIdx;
+             
+-- This output should be free of index, constraints and references from other tables.
+\d+ OldTestBoxes;
+      
+--
+-- Create the two new tables before starting data migration (don't want to spend time 
+-- on converting strings just to find a typo in the TestBoxes create table syntax).
+--                   
+CREATE SEQUENCE TestBoxStrTabIdSeq
+    START 1
+    INCREMENT BY 1
+    NO MAXVALUE
+    NO MINVALUE
+    CACHE 1;
+CREATE TABLE TestBoxStrTab (
+    --- The ID of this string.
+    idStr               INTEGER     PRIMARY KEY DEFAULT NEXTVAL('TestBoxStrTabIdSeq'),
+    --- The string value.
+    sValue              text        NOT NULL,
+    --- Creation time stamp.
+    tsCreated           TIMESTAMP WITH TIME ZONE  DEFAULT current_timestamp  NOT NULL
+);
+
+CREATE TABLE TestBoxes (
+    --- The fixed testbox ID.
+    -- This is assigned when the testbox is created and will never change.
+    idTestBox           INTEGER     DEFAULT NEXTVAL('TestBoxIdSeq')  NOT NULL,
+    --- When this row starts taking effect (inclusive).
+    tsEffective         TIMESTAMP WITH TIME ZONE  DEFAULT current_timestamp  NOT NULL,
+    --- When this row stops being tsEffective (exclusive).
+    tsExpire            TIMESTAMP WITH TIME ZONE  DEFAULT TIMESTAMP WITH TIME ZONE 'infinity'  NOT NULL,
+    --- The user id of the one who created/modified this entry.
+    -- When modified automatically by the testbox, NULL is used.
+    -- Non-unique foreign key: Users(uid)
+    uidAuthor           INTEGER     DEFAULT NULL,
+    --- Generation ID for this row.
+    -- This is primarily for referencing by TestSets.
+    idGenTestBox        INTEGER     UNIQUE DEFAULT NEXTVAL('TestBoxGenIdSeq')  NOT NULL,
+
+    --- The testbox IP.
+    -- This is from the webserver point of view and automatically updated on
+    -- SIGNON.  The test setup doesn't permit for IP addresses to change while
+    -- the testbox is operational, because this will break gang tests.
+    ip                  inet        NOT NULL,
+    --- The system or firmware UUID.
+    -- This uniquely identifies the testbox when talking to the server.  After
+    -- SIGNON though, the testbox will also provide idTestBox and ip to
+    -- establish its identity beyond doubt.
+    uuidSystem          uuid        NOT NULL,
+    --- The testbox name.
+    -- Usually similar to the DNS name.
+    sName               text        NOT NULL,
+    --- Optional testbox description.
+    -- Intended for describing the box as well as making other relevant notes.
+    idStrDescription    INTEGER     REFERENCES TestBoxStrTab(idStr)  DEFAULT NULL,
+
+    --- Reference to the scheduling group that this testbox is a member of.
+    -- Non-unique foreign key: SchedGroups(idSchedGroup)
+    -- A testbox is always part of a group, the default one nothing else.
+    idSchedGroup        INTEGER     DEFAULT 1  NOT NULL,
+
+    --- Indicates whether this testbox is enabled.
+    -- A testbox gets disabled when we're doing maintenance, debugging a issue
+    -- that happens only on that testbox, or some similar stuff.  This is an
+    -- alternative to deleting the testbox.
+    fEnabled            BOOLEAN     DEFAULT NULL,
+
+    --- The kind of lights-out-management.
+    enmLomKind          LomKind_T   DEFAULT 'none'::LomKind_T  NOT NULL,
+    --- The IP adress of the lights-out-management.
+    -- This can be NULL if enmLomKind is 'none', otherwise it must contain a valid address.
+    ipLom               inet        DEFAULT NULL,
+
+    --- Timeout scale factor, given as a percent.
+    -- This is a crude adjustment of the test case timeout for slower hardware.
+    pctScaleTimeout     smallint    DEFAULT 100  NOT NULL  CHECK (pctScaleTimeout > 10 AND pctScaleTimeout < 20000),
+
+    --- Change comment or similar.
+    idStrComment        INTEGER     REFERENCES TestBoxStrTab(idStr)  DEFAULT NULL,
+
+    --- @name Scheduling properties (reported by testbox script).
+    -- @{
+    --- Same abbrieviations as kBuild, see KBUILD_OSES.
+    idStrOs             INTEGER     REFERENCES TestBoxStrTab(idStr)  DEFAULT NULL,
+    --- Informational, no fixed format.
+    idStrOsVersion      INTEGER     REFERENCES TestBoxStrTab(idStr)  DEFAULT NULL,
+    --- Same as CPUID reports (GenuineIntel, AuthenticAMD, CentaurHauls, ...).
+    idStrCpuVendor      INTEGER     REFERENCES TestBoxStrTab(idStr)  DEFAULT NULL,
+    --- Same as kBuild - x86, amd64, ... See KBUILD_ARCHES.
+    idStrCpuArch        INTEGER     REFERENCES TestBoxStrTab(idStr)  DEFAULT NULL,
+    --- The CPU name if available.
+    idStrCpuName        INTEGER     REFERENCES TestBoxStrTab(idStr)  DEFAULT NULL,
+    --- Number identifying the CPU family/model/stepping/whatever.
+    -- For x86 and AMD64 type CPUs, this will on the following format:
+    --   (EffFamily << 24) | (EffModel << 8) | Stepping.
+    lCpuRevision        bigint      DEFAULT NULL,
+    --- Number of CPUs, CPU cores and CPU threads.
+    cCpus               smallint    DEFAULT NULL  CHECK (cCpus IS NULL OR cCpus > 0),
+    --- Set if capable of hardware virtualization.
+    fCpuHwVirt          boolean     DEFAULT NULL,
+    --- Set if capable of nested paging.
+    fCpuNestedPaging    boolean     DEFAULT NULL,
+    --- Set if CPU capable of 64-bit (VBox) guests.
+    fCpu64BitGuest      boolean     DEFAULT NULL,
+    --- Set if chipset with usable IOMMU (VT-d / AMD-Vi).
+    fChipsetIoMmu       boolean     DEFAULT NULL,
+    --- Set if the test box does raw-mode tests.
+    fRawMode            boolean     DEFAULT NULL,
+    --- The (approximate) memory size in megabytes (rounded down to nearest 4 MB).
+    cMbMemory           bigint      DEFAULT NULL  CHECK (cMbMemory IS NULL OR cMbMemory > 0),
+    --- The amount of scratch space in megabytes (rounded down to nearest 64 MB).
+    cMbScratch          bigint      DEFAULT NULL  CHECK (cMbScratch IS NULL OR cMbScratch >= 0),
+    --- Free form hardware and software report field.
+    idStrReport         INTEGER     REFERENCES TestBoxStrTab(idStr)  DEFAULT NULL,
+    --- @}
+
+    --- The testbox script revision number, serves the purpose of a version number.
+    -- Probably good to have when scheduling upgrades as well for status purposes.
+    iTestBoxScriptRev   INTEGER     DEFAULT 0  NOT NULL,
+    --- The python sys.hexversion (layed out as of 2.7).
+    -- Good to know which python versions we need to support.
+    iPythonHexVersion   INTEGER     DEFAULT NULL,
+
+    --- Pending command.
+    -- @note We put it here instead of in TestBoxStatuses to get history.
+    enmPendingCmd       TestBoxCmd_T  DEFAULT 'none'::TestBoxCmd_T  NOT NULL,
+
+    PRIMARY KEY (idTestBox, tsExpire),
+
+    --- Nested paging requires hardware virtualization.
+    CHECK (fCpuNestedPaging IS NULL OR (fCpuNestedPaging <> TRUE OR fCpuHwVirt = TRUE))
+);
+
+-- Convenience view that simplifies querying a lot.
+CREATE VIEW TestBoxesWithStrings AS
+    SELECT  TestBoxes.*,
+            Str1.sValue AS sDescription,
+            Str2.sValue AS sComment,
+            Str3.sValue AS sOs,
+            Str4.sValue AS sOsVersion,
+            Str5.sValue AS sCpuVendor,
+            Str6.sValue AS sCpuArch,
+            Str7.sValue AS sCpuName,
+            Str8.sValue AS sReport
+    FROM    TestBoxes
+            LEFT OUTER JOIN TestBoxStrTab Str1 ON idStrDescription = Str1.idStr
+            LEFT OUTER JOIN TestBoxStrTab Str2 ON idStrComment     = Str2.idStr
+            LEFT OUTER JOIN TestBoxStrTab Str3 ON idStrOs          = Str3.idStr
+            LEFT OUTER JOIN TestBoxStrTab Str4 ON idStrOsVersion   = Str4.idStr
+            LEFT OUTER JOIN TestBoxStrTab Str5 ON idStrCpuVendor   = Str5.idStr
+            LEFT OUTER JOIN TestBoxStrTab Str6 ON idStrCpuArch     = Str6.idStr
+            LEFT OUTER JOIN TestBoxStrTab Str7 ON idStrCpuName     = Str7.idStr
+            LEFT OUTER JOIN TestBoxStrTab Str8 ON idStrReport      = Str8.idStr;
+
+
+--
+-- Populate the string table.
+--
+
+--- Empty string with ID 0.
+INSERT INTO TestBoxStrTab (idStr, sValue) VALUES (0, '');
+
+INSERT INTO TestBoxStrTab (sValue)
+(         SELECT DISTINCT sDescription    FROM OldTestBoxes WHERE sDescription IS NOT NULL
+) UNION ( SELECT DISTINCT sOs             FROM OldTestBoxes WHERE sOs          IS NOT NULL
+) UNION ( SELECT DISTINCT sOsVersion      FROM OldTestBoxes WHERE sOsVersion   IS NOT NULL
+) UNION ( SELECT DISTINCT sCpuVendor      FROM OldTestBoxes WHERE sCpuVendor   IS NOT NULL
+) UNION ( SELECT DISTINCT sCpuArch        FROM OldTestBoxes WHERE sCpuArch     IS NOT NULL
+) UNION ( SELECT DISTINCT sCpuName        FROM OldTestBoxes WHERE sCpuName     IS NOT NULL
+) UNION ( SELECT DISTINCT sReport         FROM OldTestBoxes WHERE sReport      IS NOT NULL );
+
+-- Index and analyze the string table as we'll be using it a lot below already.
+CREATE INDEX TestBoxStrTabNameIdx ON TestBoxStrTab USING hash (sValue);
+ANALYZE VERBOSE TestBoxStrTab;
+
+SELECT MAX(idStr) FROM TestBoxStrTab;
+SELECT pg_total_relation_size('TestBoxStrTab');
+
+
+--
+-- Populate the test box table.
+--
+
+INSERT INTO TestBoxes ( 
+            idTestBox,          --  0
+            tsEffective,        --  1
+            tsExpire,           --  2
+            uidAuthor,          --  3
+            idGenTestBox,       --  4
+            ip,                 --  5
+            uuidSystem,         --  6
+            sName,              --  7
+            idStrDescription,   --  8
+            idSchedGroup,       --  9
+            fEnabled,           -- 10
+            enmLomKind,         -- 11
+            ipLom,              -- 12
+            pctScaleTimeout,    -- 13
+            idStrComment,       -- 14
+            idStrOs,            -- 15
+            idStrOsVersion,     -- 16
+            idStrCpuVendor,     -- 17
+            idStrCpuArch,       -- 18
+            idStrCpuName,       -- 19
+            lCpuRevision,       -- 20
+            cCpus,              -- 21
+            fCpuHwVirt,         -- 22
+            fCpuNestedPaging,   -- 23
+            fCpu64BitGuest,     -- 24
+            fChipsetIoMmu,      -- 25
+            fRawMode,           -- 26 
+            cMbMemory,          -- 27 
+            cMbScratch,         -- 28 
+            idStrReport,        -- 29 
+            iTestBoxScriptRev,  -- 30 
+            iPythonHexVersion,  -- 31
+            enmPendingCmd       -- 32
+            )
+SELECT      idTestBox, 
+            tsEffective, 
+            tsExpire, 
+            uidAuthor, 
+            idGenTestBox, 
+            ip,  
+            uuidSystem,  
+            sName,  
+            st1.idStr, 
+            idSchedGroup,  
+            fEnabled,  
+            enmLomKind,  
+            ipLom,  
+            pctScaleTimeout,  
+            NULL,
+            st2.idStr,  
+            st3.idStr,  
+            st4.idStr,  
+            st5.idStr,  
+            st6.idStr,
+            lCpuRevision,         
+            cCpus,  
+            fCpuHwVirt,  
+            fCpuNestedPaging,  
+            fCpu64BitGuest,  
+            fChipsetIoMmu,  
+            NULL,
+            cMbMemory,  
+            cMbScratch,  
+            st7.idStr,
+            iTestBoxScriptRev,  
+            iPythonHexVersion,  
+            enmPendingCmd
+FROM        OldTestBoxes
+        LEFT OUTER JOIN TestBoxStrTab st1 ON sDescription = st1.sValue
+        LEFT OUTER JOIN TestBoxStrTab st2 ON sOs          = st2.sValue
+        LEFT OUTER JOIN TestBoxStrTab st3 ON sOsVersion   = st3.sValue
+        LEFT OUTER JOIN TestBoxStrTab st4 ON sCpuVendor   = st4.sValue
+        LEFT OUTER JOIN TestBoxStrTab st5 ON sCpuArch     = st5.sValue
+        LEFT OUTER JOIN TestBoxStrTab st6 ON sCpuName     = st6.sValue
+        LEFT OUTER JOIN TestBoxStrTab st7 ON sReport      = st7.sValue;
+
+-- Restore indexes.
+CREATE UNIQUE INDEX TestBoxesUuidIdx ON TestBoxes (uuidSystem, tsExpire DESC);
+CREATE INDEX TestBoxesExpireEffectiveIdx ON TestBoxes (tsExpire DESC, tsEffective ASC);
+
+-- Restore foreign key references to the table.
+ALTER TABLE TestBoxStatuses ADD  CONSTRAINT TestBoxStatuses_idGenTestBox_fkey  
+    FOREIGN KEY (idGenTestBox) REFERENCES TestBoxes(idGenTestBox);
+ALTER TABLE TestSets        ADD  CONSTRAINT TestSets_idGenTestBox_fkey         
+    FOREIGN KEY (idGenTestBox) REFERENCES TestBoxes(idGenTestBox);
+
+-- Drop the old table.
+DROP TABLE OldTestBoxes;
+
+COMMIT;
+
+\d TestBoxes;
+
+
Index: /trunk/src/VBox/ValidationKit/testmanager/webui/wuiadmintestbox.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/webui/wuiadmintestbox.py	(revision 61467)
+++ /trunk/src/VBox/ValidationKit/testmanager/webui/wuiadmintestbox.py	(revision 61468)
@@ -34,5 +34,5 @@
 
 # Validation Kit imports.
-from common                             import utils;
+from common                             import utils, webutils;
 from testmanager.webui.wuicontentbase   import WuiContentBase, WuiListContentWithActionBase, WuiFormContentBase, WuiLinkBase, \
                                                WuiSvnLink, WuiTmLink, WuiSpanText, WuiRawHtml;
@@ -98,5 +98,5 @@
         oForm.addIntRO(      TestBoxData.ksParam_uidAuthor,         oData.uidAuthor, 'Changed by UID');
 
-        oForm.addText(       TestBoxData.ksParam_ip,                oData.ip, 'TestBox IP Address');
+        oForm.addText(       TestBoxData.ksParam_ip,                oData.ip, 'TestBox IP Address'); ## make read only??
         oForm.addUuid(       TestBoxData.ksParam_uuidSystem,        oData.uuidSystem, 'TestBox System/Firmware UUID');
         oForm.addText(       TestBoxData.ksParam_sName,             oData.sName, 'TestBox Name');
@@ -135,13 +135,8 @@
         oForm.addIntRO(      TestBoxData.ksParam_iTestBoxScriptRev, oData.iTestBoxScriptRev,
                              'TestBox Script SVN revision');
-        # Later:
-        #if not self.isAttributeNull(''):
-        #    sHexVer = '%s.%s.%.%s' % (oData.iPythonHexVersion >> 24, (oData.iPythonHexVersion >> 16) & 0xff,
-        #                             (oData.iPythonHexVersion >> 8) & 0xff, oData.iPythonHexVersion & 0xff);
-        #else:
-        #    sHexVer = str(oData.iPythonHexVersion);
-
+        sHexVer = oData.formatPythonVersion();
         oForm.addIntRO(      TestBoxData.ksParam_iPythonHexVersion, oData.iPythonHexVersion,
-                             'Python version (hex)');
+                             'Python version (hex)', sPostHtml = webutils.escapeElem(sHexVer));
+
         if self._sMode == WuiFormContentBase.ksMode_Edit:
             oForm.addComboBox(TestBoxData.ksParam_enmPendingCmd,    oData.enmPendingCmd, 'Pending command',
@@ -150,4 +145,6 @@
             oForm.addComboBoxRO(TestBoxData.ksParam_enmPendingCmd,  oData.enmPendingCmd, 'Pending command',
                                 TestBoxData.kaoTestBoxCmdDescs);
+
+        oForm.addMultilineText(TestBoxData.ksParam_sComment,        oData.sComment, 'Comment');
 
         if self._sMode != WuiFormContentBase.ksMode_Show:
@@ -178,11 +175,11 @@
         WuiListContentWithActionBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective,
                                               sTitle = 'TestBoxes', sId = 'users', fnDPrint = fnDPrint, oDisp = oDisp);
-        self._asColumnHeaders.extend([ 'Name', 'LOM', 'Status',
-                                       'Cmd', 'Script', 'Python', 'Group',
+        self._asColumnHeaders.extend([ 'Name', 'LOM', 'Status', 'Cmd',
+                                       'Note', 'Script', 'Python', 'Group',
                                        'OS', 'CPU', 'Features', 'CPUs', 'RAM', 'Scratch',
                                        'Actions' ]);
-        self._asColumnAttribs.extend([ 'align="center"', 'align="center"', 'align="center"',
+        self._asColumnAttribs.extend([ 'align="center"', 'align="center"', 'align="center"', 'align="center"'
                                        'align="center"', 'align="center"', 'align="center"', 'align="center"',
-                                       '', '', '', 'align="right"', 'align="right"', 'align="right"',
+                                       '', '', '', 'align="left"', 'align="right"', 'align="right"', 'align="right"',
                                        'align="center"' ]);
         self._aoActions     = list(self.kasTestBoxActionDescs);
@@ -237,4 +234,13 @@
                                    sTitle = '#%u' % (oEntry.oStatus.idTestSet,),
                                    fBracketed = False);
+        # Comment
+        oComment = None;
+        if oEntry.sComment is not None:
+            sComment = oEntry.sComment.strip();
+            if len(sComment) > 64:
+                oComment = WuiRawHtml('<span title="%s">%s...</span>'
+                                      % (webutils.escapeAttr(sComment), webutils.escapeElem(sComment[:60]),));
+            elif len(sComment) > 0:
+                oComment = WuiRawHtml(webutils.escapeElem(sComment));
 
         # Group link.
@@ -336,4 +342,5 @@
                   ],
                  oEntry.enmPendingCmd,
+                 oComment,
                  WuiSvnLink(oEntry.iTestBoxScriptRev),
                  oEntry.formatPythonVersion(),
Index: /trunk/src/VBox/ValidationKit/testmanager/webui/wuihlpform.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/webui/wuihlpform.py	(revision 61467)
+++ /trunk/src/VBox/ValidationKit/testmanager/webui/wuihlpform.py	(revision 61468)
@@ -167,11 +167,11 @@
                          % ( escapeAttr(sName), escapeAttr(sName), sExtraAttribs, escapeElem(unicode(sValue)), sPostHtml ));
 
-    def addWideText(self, sName, sValue, sLabel, sExtraAttribs = ''):
+    def addWideText(self, sName, sValue, sLabel, sExtraAttribs = '', sPostHtml = ''):
         """Adds a wide text input."""
-        return self.addText(sName, sValue, sLabel, 'wide', sExtraAttribs);
-
-    def addWideTextRO(self, sName, sValue, sLabel, sExtraAttribs = ''):
+        return self.addText(sName, sValue, sLabel, 'wide', sExtraAttribs, sPostHtml = sPostHtml);
+
+    def addWideTextRO(self, sName, sValue, sLabel, sExtraAttribs = '', sPostHtml = ''):
         """Adds a wide read-only text input."""
-        return self.addTextRO(sName, sValue, sLabel, 'wide', sExtraAttribs);
+        return self.addTextRO(sName, sValue, sLabel, 'wide', sExtraAttribs, sPostHtml = sPostHtml);
 
     def _adjustMultilineTextAttribs(self, sExtraAttribs, sValue):
@@ -218,31 +218,31 @@
                              escapeElem(sNewValue)))
 
-    def addInt(self, sName, iValue, sLabel, sExtraAttribs = ''):
+    def addInt(self, sName, iValue, sLabel, sExtraAttribs = '', sPostHtml = ''):
         """Adds an integer input."""
-        return self.addText(sName, unicode(iValue), sLabel, 'int', sExtraAttribs);
-
-    def addIntRO(self, sName, iValue, sLabel, sExtraAttribs = ''):
+        return self.addText(sName, unicode(iValue), sLabel, 'int', sExtraAttribs, sPostHtml = sPostHtml);
+
+    def addIntRO(self, sName, iValue, sLabel, sExtraAttribs = '', sPostHtml = ''):
         """Adds an integer input."""
-        return self.addTextRO(sName, unicode(iValue), sLabel, 'int', sExtraAttribs);
-
-    def addLong(self, sName, lValue, sLabel, sExtraAttribs = ''):
+        return self.addTextRO(sName, unicode(iValue), sLabel, 'int', sExtraAttribs, sPostHtml = sPostHtml);
+
+    def addLong(self, sName, lValue, sLabel, sExtraAttribs = '', sPostHtml = ''):
         """Adds a long input."""
-        return self.addText(sName, unicode(lValue), sLabel, 'long', sExtraAttribs);
-
-    def addLongRO(self, sName, lValue, sLabel, sExtraAttribs = ''):
+        return self.addText(sName, unicode(lValue), sLabel, 'long', sExtraAttribs, sPostHtml = sPostHtml);
+
+    def addLongRO(self, sName, lValue, sLabel, sExtraAttribs = '', sPostHtml = ''):
         """Adds a long input."""
-        return self.addTextRO(sName, unicode(lValue), sLabel, 'long', sExtraAttribs);
-
-    def addUuid(self, sName, uuidValue, sLabel, sExtraAttribs = ''):
+        return self.addTextRO(sName, unicode(lValue), sLabel, 'long', sExtraAttribs, sPostHtml = sPostHtml);
+
+    def addUuid(self, sName, uuidValue, sLabel, sExtraAttribs = '', sPostHtml = ''):
         """Adds an UUID input."""
-        return self.addText(sName, unicode(uuidValue), sLabel, 'uuid', sExtraAttribs);
-
-    def addUuidRO(self, sName, uuidValue, sLabel, sExtraAttribs = ''):
+        return self.addText(sName, unicode(uuidValue), sLabel, 'uuid', sExtraAttribs, sPostHtml = sPostHtml);
+
+    def addUuidRO(self, sName, uuidValue, sLabel, sExtraAttribs = '', sPostHtml = ''):
         """Adds a read-only UUID input."""
-        return self.addTextRO(sName, unicode(uuidValue), sLabel, 'uuid', sExtraAttribs);
-
-    def addTimestampRO(self, sName, sTimestamp, sLabel, sExtraAttribs = ''):
+        return self.addTextRO(sName, unicode(uuidValue), sLabel, 'uuid', sExtraAttribs, sPostHtml = sPostHtml);
+
+    def addTimestampRO(self, sName, sTimestamp, sLabel, sExtraAttribs = '', sPostHtml = ''):
         """Adds a read-only database string timstamp input."""
-        return self.addTextRO(sName, sTimestamp, sLabel, 'timestamp', sExtraAttribs);
+        return self.addTextRO(sName, sTimestamp, sLabel, 'timestamp', sExtraAttribs, sPostHtml = sPostHtml);
 
 
