Index: /trunk/src/VBox/ValidationKit/testmanager/core/build.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/build.py	(revision 65225)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/build.py	(revision 65226)
@@ -152,5 +152,5 @@
         self.dCache = None;
 
-    def fetchForListing(self, iStart, cMaxRows, tsNow):
+    def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
         """
         Fetches testboxes for listing.
@@ -515,5 +515,5 @@
     #
 
-    def fetchForListing(self, iStart, cMaxRows, tsNow):
+    def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
         """
         Fetches builds for listing.
@@ -522,4 +522,6 @@
         Raises exception on error.
         """
+        _ = aiSortColumns;
+
         if tsNow is None:
             self._oDb.execute('SELECT   *\n'
Index: /trunk/src/VBox/ValidationKit/testmanager/core/buildblacklist.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/buildblacklist.py	(revision 65225)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/buildblacklist.py	(revision 65226)
@@ -128,5 +128,5 @@
         self.dCache = None;
 
-    def fetchForListing(self, iStart, cMaxRows, tsNow):
+    def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
         """
         Fetches Build Blacklist records.
@@ -135,4 +135,5 @@
         Raises exception on error.
         """
+        _ = aiSortColumns;
 
         if tsNow is None:
Index: /trunk/src/VBox/ValidationKit/testmanager/core/buildsource.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/buildsource.py	(revision 65225)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/buildsource.py	(revision 65226)
@@ -163,5 +163,5 @@
     #
 
-    def fetchForListing(self, iStart, cMaxRows, tsNow):
+    def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
         """
         Fetches build sources.
@@ -170,4 +170,6 @@
         Raises exception on error.
         """
+        _ = aiSortColumns;
+
         if tsNow is None:
             self._oDb.execute('SELECT   *\n'
Index: /trunk/src/VBox/ValidationKit/testmanager/core/failurecategory.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/failurecategory.py	(revision 65225)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/failurecategory.py	(revision 65226)
@@ -111,5 +111,5 @@
         self.dCache = None;
 
-    def fetchForListing(self, iStart, cMaxRows, tsNow):
+    def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
         """
         Fetches Failure Category records.
@@ -118,4 +118,5 @@
         Raises exception on error.
         """
+        _ = aiSortColumns;
 
         if tsNow is None:
Index: /trunk/src/VBox/ValidationKit/testmanager/core/failurereason.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/failurereason.py	(revision 65225)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/failurereason.py	(revision 65226)
@@ -149,5 +149,5 @@
         self.oUserAccountLogic = None;
 
-    def fetchForListing(self, iStart, cMaxRows, tsNow):
+    def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
         """
         Fetches Failure Category records.
@@ -156,4 +156,5 @@
         Raises exception on error.
         """
+        _ = aiSortColumns;
         self._ensureCachesPresent();
 
@@ -188,5 +189,5 @@
         return aoRows
 
-    def fetchForListingInCategory(self, iStart, cMaxRows, tsNow, idFailureCategory):
+    def fetchForListingInCategory(self, iStart, cMaxRows, tsNow, idFailureCategory, aiSortColumns = None):
         """
         Fetches Failure Category records.
@@ -195,4 +196,5 @@
         Raises exception on error.
         """
+        _ = aiSortColumns;
         self._ensureCachesPresent();
 
Index: /trunk/src/VBox/ValidationKit/testmanager/core/globalresource.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/globalresource.py	(revision 65225)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/globalresource.py	(revision 65226)
@@ -124,9 +124,10 @@
         self.dCache = None;
 
-    def fetchForListing(self, iStart, cMaxRows, tsNow):
+    def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
         """
         Returns an array (list) of FailureReasonData items, empty list if none.
         Raises exception on error.
         """
+        _ = aiSortColumns;
 
         if tsNow is None:
Index: /trunk/src/VBox/ValidationKit/testmanager/core/schedgroup.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/schedgroup.py	(revision 65225)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/schedgroup.py	(revision 65226)
@@ -434,5 +434,5 @@
     #
 
-    def fetchForListing(self, iStart, cMaxRows, tsNow):
+    def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
         """
         Fetches build sources.
@@ -441,4 +441,5 @@
         Raises exception on error.
         """
+        _ = aiSortColumns;
 
         if tsNow is None:
Index: /trunk/src/VBox/ValidationKit/testmanager/core/systemchangelog.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/systemchangelog.py	(revision 65225)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/systemchangelog.py	(revision 65226)
@@ -128,5 +128,5 @@
 
 
-    def fetchForListingEx(self, iStart, cMaxRows, tsNow, cDaysBack):
+    def fetchForListingEx(self, iStart, cMaxRows, tsNow, cDaysBack, aiSortColumns = None):
         """
         Fetches SystemLog entries.
@@ -135,4 +135,5 @@
         Raises exception on error.
         """
+        _ = aiSortColumns;
 
         #
Index: /trunk/src/VBox/ValidationKit/testmanager/core/systemlog.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/systemlog.py	(revision 65225)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/systemlog.py	(revision 65226)
@@ -103,5 +103,5 @@
         ModelLogicBase.__init__(self, oDb);
 
-    def fetchForListing(self, iStart, cMaxRows, tsNow):
+    def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
         """
         Fetches SystemLog entries.
@@ -110,4 +110,5 @@
         Raises exception on error.
         """
+        _ = aiSortColumns;
         if tsNow is None:
             self._oDb.execute('SELECT   *\n'
Index: /trunk/src/VBox/ValidationKit/testmanager/core/testbox.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/testbox.py	(revision 65225)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/testbox.py	(revision 65226)
@@ -671,4 +671,31 @@
     """
 
+    kiSortColumn_sName              =  0;
+    kiSortColumn_sOs                =  1;
+    kiSortColumn_sOsVersion         =  2;
+    kiSortColumn_sCpuVendor         =  3;
+    kiSortColumn_sCpuArch           =  4;
+    kiSortColumn_lCpuRevision       =  5;
+    kiSortColumn_cCpus              =  6;
+    kiSortColumn_cMbMemory          =  7;
+    kiSortColumn_cMbScratch         =  8;
+    kiSortColumn_fNestedPaging      =  9;
+    kiSortColumn_iTestBoxScriptRev  = 10;
+    kiSortColumn_iPythonHexVersion  = 11;
+    kcMaxSortColumns                = 12;
+    kdSortColumnMap                 = {
+        kiSortColumn_sName:             'TestBoxesWithStrings.sName',
+        kiSortColumn_sOs:               'TestBoxesWithStrings.sOs',
+        kiSortColumn_sOsVersion:        'TestBoxesWithStrings.sOsVersion',
+        kiSortColumn_sCpuVendor:        'TestBoxesWithStrings.sCpuVendor',
+        kiSortColumn_sCpuArch:          'TestBoxesWithStrings.sCpuArch',
+        kiSortColumn_lCpuRevision:      'TestBoxesWithStrings.lCpuRevision',
+        kiSortColumn_cCpus:             'TestBoxesWithStrings.cCpus',
+        kiSortColumn_cMbMemory:         'TestBoxesWithStrings.cMbMemory',
+        kiSortColumn_cMbScratch:        'TestBoxesWithStrings.cMbScratch',
+        kiSortColumn_fNestedPaging:     'TestBoxesWithStrings.fNestedPaging',
+        kiSortColumn_iTestBoxScriptRev: 'TestBoxesWithStrings.iTestBoxScriptRev',
+        kiSortColumn_iPythonHexVersion: 'TestBoxesWithStrings.iPythonHexVersion',
+    };
 
     def __init__(self, oDb):
@@ -694,5 +721,5 @@
         return oData;
 
-    def fetchForListing(self, iStart, cMaxRows, tsNow):
+    def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
         """
         Fetches testboxes for listing.
@@ -713,4 +740,7 @@
 
         from testmanager.core.testboxstatus import TestBoxStatusData;
+
+        if aiSortColumns is None or len(aiSortColumns) == 0:
+            aiSortColumns = [self.kiSortColumn_sName,];
 
         if tsNow is None:
@@ -721,5 +751,5 @@
                               '                      ON TestBoxStatuses.idTestBox = TestBoxesWithStrings.idTestBox\n'
                               'WHERE    TestBoxesWithStrings.tsExpire = \'infinity\'::TIMESTAMP\n'
-                              'ORDER BY TestBoxesWithStrings.sName\n'
+                              'ORDER BY ' + (', '.join([self.kdSortColumnMap[i] for i in aiSortColumns])) + '\n'
                               'LIMIT %s OFFSET %s\n'
                               , (cMaxRows, iStart,));
@@ -732,5 +762,5 @@
                               'WHERE    tsExpire     > %s\n'
                               '     AND tsEffective <= %s\n'
-                              'ORDER BY TestBoxesWithStrings.sName\n'
+                              'ORDER BY ' + (', '.join([self.kdSortColumnMap[i] for i in aiSortColumns])) + '\n'
                               'LIMIT %s OFFSET %s\n'
                               , ( tsNow, tsNow, cMaxRows, iStart,));
Index: /trunk/src/VBox/ValidationKit/testmanager/core/testcase.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/testcase.py	(revision 65225)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/testcase.py	(revision 65226)
@@ -965,5 +965,5 @@
         return aoRet
 
-    def fetchForListing(self, iStart, cMaxRows, tsNow):
+    def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
         """
         Fetches test cases.
@@ -972,4 +972,5 @@
         Raises exception on error.
         """
+        _ = aiSortColumns;
         if tsNow is None:
             self._oDb.execute('SELECT   *\n'
Index: /trunk/src/VBox/ValidationKit/testmanager/core/testgroup.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/testgroup.py	(revision 65225)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/testgroup.py	(revision 65226)
@@ -380,5 +380,5 @@
     #
 
-    def fetchForListing(self, iStart, cMaxRows, tsNow):
+    def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
         """
         Fetches test groups.
@@ -387,4 +387,5 @@
         Raises exception on error.
         """
+        _ = aiSortColumns;
         if tsNow is None:
             self._oDb.execute('SELECT   *\n'
Index: /trunk/src/VBox/ValidationKit/testmanager/core/useraccount.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/useraccount.py	(revision 65225)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/useraccount.py	(revision 65226)
@@ -123,5 +123,5 @@
         self.dCache = None;
 
-    def fetchForListing(self, iStart, cMaxRows, tsNow):
+    def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
         """
         Fetches user accounts.
@@ -130,4 +130,5 @@
         Raises exception on error.
         """
+        _ = aiSortColumns;
         if tsNow is None:
             self._oDb.execute('SELECT   *\n'
Index: /trunk/src/VBox/ValidationKit/testmanager/core/vcsrevisions.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/vcsrevisions.py	(revision 65225)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/vcsrevisions.py	(revision 65226)
@@ -113,5 +113,5 @@
     #
 
-    def fetchForListing(self, iStart, cMaxRows, tsNow):
+    def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
         """
         Fetches VCS revisions for listing.
@@ -120,5 +120,5 @@
         Raises exception on error.
         """
-        _ = tsNow;
+        _ = tsNow; _ = aiSortColumns;
         self._oDb.execute('SELECT   *\n'
                           'FROM     VcsRevisions\n'
Index: /trunk/src/VBox/ValidationKit/testmanager/webui/wuiadmintestbox.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/webui/wuiadmintestbox.py	(revision 65225)
+++ /trunk/src/VBox/ValidationKit/testmanager/webui/wuiadmintestbox.py	(revision 65226)
@@ -39,5 +39,5 @@
 from testmanager.core.db                import TMDatabaseConnection;
 from testmanager.core.schedgroup        import SchedGroupLogic, SchedGroupData;
-from testmanager.core.testbox           import TestBoxData, TestBoxDataEx;
+from testmanager.core.testbox           import TestBoxData, TestBoxDataEx, TestBoxLogic;
 from testmanager.core.testset           import TestSetData;
 from testmanager.core.db                import isDbTimestampInfinity;
@@ -192,4 +192,22 @@
                                        '', '', '', 'align="left"', 'align="right"', 'align="right"', 'align="right"',
                                        'align="center"' ]);
+        self._aaiColumnSorting.extend([
+            (TestBoxLogic.kiSortColumn_sName,),
+            None, # LOM
+            None, # Status
+            None, # Cmd
+            None, # Note
+            (TestBoxLogic.kiSortColumn_iTestBoxScriptRev,),
+            (TestBoxLogic.kiSortColumn_iPythonHexVersion,),
+            None, # Group
+            (TestBoxLogic.kiSortColumn_sOs, TestBoxLogic.kiSortColumn_sOsVersion, TestBoxLogic.kiSortColumn_sCpuArch,),
+            (TestBoxLogic.kiSortColumn_sCpuVendor, TestBoxLogic.kiSortColumn_lCpuRevision,),
+            (TestBoxLogic.kiSortColumn_fNestedPaging,),
+            (TestBoxLogic.kiSortColumn_cCpus,),
+            (TestBoxLogic.kiSortColumn_cMbMemory,),
+            (TestBoxLogic.kiSortColumn_cMbScratch,),
+            None, # Actions
+        ]);
+        assert len(self._aaiColumnSorting) == len(self._asColumnHeaders);
         self._aoActions     = list(self.kasTestBoxActionDescs);
         self._sAction       = oDisp.ksActionTestBoxListPost;
Index: /trunk/src/VBox/ValidationKit/testmanager/webui/wuibase.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/webui/wuibase.py	(revision 65225)
+++ /trunk/src/VBox/ValidationKit/testmanager/webui/wuibase.py	(revision 65226)
@@ -82,4 +82,7 @@
     ## The name of the list-action parameter (WuiListContentWithActionBase).
     ksParamListAction    = 'ListAction';
+
+    ## One or more columns to sort by.
+    ksParamSortColumns   = 'SortBy';
 
     ## The name of the change log enabled/disabled parameter.
@@ -801,7 +804,13 @@
         cItemsPerPage   = self.getIntParam(self.ksParamItemsPerPage, iMin = 2, iMax =   9999, iDefault = 300);
         iPage           = self.getIntParam(self.ksParamPageNo,       iMin = 0, iMax = 999999, iDefault = 0);
+        aiSortColumnsDup = self.getListOfIntParams(self.ksParamSortColumns, iMin = 0,
+                                                   iMax = getattr(oLogicType, 'kcMaxSortColumns', 0), aiDefaults = []);
+        aiSortColumns   = [];
+        for iSortColumn in aiSortColumnsDup:
+            if iSortColumn not in aiSortColumns:
+                aiSortColumns.append(iSortColumn);
         self._checkForUnknownParameters();
 
-        aoEntries  = oLogicType(self._oDb).fetchForListing(iPage * cItemsPerPage, cItemsPerPage + 1, tsEffective);
+        aoEntries  = oLogicType(self._oDb).fetchForListing(iPage * cItemsPerPage, cItemsPerPage + 1, tsEffective, aiSortColumns);
         oContent   = oListContentType(aoEntries, iPage, cItemsPerPage, tsEffective,
                                       fnDPrint = self._oSrvGlue.dprint, oDisp = self);
Index: /trunk/src/VBox/ValidationKit/testmanager/webui/wuicontentbase.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/webui/wuicontentbase.py	(revision 65225)
+++ /trunk/src/VBox/ValidationKit/testmanager/webui/wuicontentbase.py	(revision 65226)
@@ -749,4 +749,5 @@
         self._asColumnHeaders   = [];
         self._asColumnAttribs   = [];
+        self._aaiColumnSorting  = [];   ##< list of list of integers
 
     def _formatCommentCell(self, sComment, cMaxLines = 3, cchMaxLine = 63):
@@ -944,7 +945,12 @@
 
         sHtml  = '  <thead class="tmheader"><tr>';
-        for oHeader in self._asColumnHeaders:
+        for iHeader, oHeader in enumerate(self._asColumnHeaders):
             if isinstance(oHeader, WuiHtmlBase):
                 sHtml += '<th>' + oHeader.toHtml() + '</th>';
+            elif iHeader < len(self._aaiColumnSorting) and self._aaiColumnSorting[iHeader] is not None:
+                sHtml += '<th>'
+                sHtml += '<a href="javascript:ahrefActionSortByColumns(\'%s\', [%s]);">' \
+                       % (WuiDispatcherBase.ksParamSortColumns, ','.join([str(i) for i in self._aaiColumnSorting[iHeader]]));
+                sHtml += webutils.escapeElem(oHeader) + '</a></th>';
             else:
                 sHtml += '<th>' + webutils.escapeElem(oHeader) + '</th>';
@@ -1024,4 +1030,5 @@
                                              % ('' if sId is None else sId)), ];
         self._asColumnAttribs = [ 'align="center"', ];
+        self._aaiColumnSorting = [ None, ];
 
     def _getCheckBoxColumn(self, iEntry, sValue):
