r1724 + generateAutoPorts(): welcome new ophandler
[racktables] / inc / database.php
index 781997445515e324b774a82b4d8c46094c338881..4f2fba50782b7e608418801d3e9c7df13b19d2f7 100644 (file)
@@ -5,10 +5,15 @@
 *
 */
 
-function escapeString ($value)
+function escapeString ($value, $do_db_escape = TRUE)
 {
-       global $dbxlink;
-       return substr ($dbxlink->quote (htmlentities ($value)), 1, -1);
+       $ret = htmlentities ($value, ENT_QUOTES);
+       if ($do_db_escape)
+       {
+               global $dbxlink;
+               $ret = substr ($dbxlink->quote ($ret), 1, -1);
+       }
+       return $ret;
 }
 
 // This function returns detailed information about either all or one
@@ -26,7 +31,7 @@ function getRackRowInfo ($rackrow_id = 0)
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in getRackRowInfo()');
+               showError ('SQL query failed', __FUNCTION__);
                return NULL;
        }
        $ret = array();
@@ -63,14 +68,23 @@ function getObjectList ($type_id = 0)
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in getObjectList()');
+               showError ('SQL query failed', __FUNCTION__);
                return;
        }
        $ret = array();
-       $clist = array ('id', 'name', 'label', 'barcode', 'objtype_name', 'objtype_id', 'asset_no', 'rack_id', 'Rack_name');
        while ($row = $result->fetch (PDO::FETCH_ASSOC))
        {
-               foreach ($clist as $dummy => $cname)
+               foreach (array (
+                       'id',
+                       'name',
+                       'label',
+                       'barcode',
+                       'objtype_name',
+                       'objtype_id',
+                       'asset_no',
+                       'rack_id',
+                       'Rack_name'
+                       ) as $cname)
                        $ret[$row['id']][$cname] = $row[$cname];
                $ret[$row['id']]['dname'] = displayedName ($ret[$row['id']]);
        }
@@ -82,7 +96,8 @@ function getRacksForRow ($row_id = 0)
 {
        global $dbxlink;
        $query =
-               "select Rack.id, Rack.name, height, Rack.comment, row_id, dict_value as row_name " .
+               "select Rack.id, Rack.name, height, Rack.comment, row_id, " .
+               "'yes' as left_is_front, 'yes' as bottom_is_unit1, dict_value as row_name " .
                "from Rack left join Dictionary on row_id = dict_key natural join Chapter " .
                "where chapter_name = 'RackRow' and Rack.deleted = 'no' " .
                (($row_id == 0) ? "" : "and row_id = ${row_id} ") .
@@ -90,16 +105,26 @@ function getRacksForRow ($row_id = 0)
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in getRacksForRow()');
+               showError ('SQL query failed', __FUNCTION__);
                return;
        }
        $ret = array();
-       $clist = array ('id', 'name', 'height', 'comment', 'row_id', 'row_name');
+       $clist = array
+       (
+               'id',
+               'name',
+               'height',
+               'comment',
+               'row_id',
+               'left_is_front',
+               'bottom_is_unit1',
+               'row_name'
+       );
        while ($row = $result->fetch (PDO::FETCH_ASSOC))
-               foreach ($clist as $dummy => $cname)
+               foreach ($clist as $cname)
                        $ret[$row['id']][$cname] = $row[$cname];
        $result->closeCursor();
-       usort ($ret, 'sortByName');
+       usort ($ret, 'sortRacks');
        $ret = restoreRackIDs ($ret);
        return $ret;
 }
@@ -111,35 +136,43 @@ function getRackData ($rack_id = 0, $silent = FALSE)
        if ($rack_id == 0)
        {
                if ($silent == FALSE)
-                       showError ('Invalid rack_id in getRackData()');
+                       showError ('Invalid rack_id', __FUNCTION__);
                return NULL;
        }
        global $dbxlink;
        $query =
-               "select Rack.id, Rack.name, row_id, height, Rack.comment, dict_value as row_name from " .
+               "select Rack.id, Rack.name, row_id, height, Rack.comment, " .
+               "'yes' as left_is_front, 'yes' as bottom_is_unit1, dict_value as row_name from " .
                "Rack left join Dictionary on Rack.row_id = dict_key natural join Chapter " .
                "where chapter_name = 'RackRow' and Rack.id='${rack_id}' and Rack.deleted = 'no' limit 1";
        $result1 = $dbxlink->query ($query);
        if ($result1 == NULL)
        {
                if ($silent == FALSE)
-                       showError ("SQL query #1 failed in getRackData()");
+                       showError ("SQL query #1 failed", __FUNCTION__);
                return NULL;
        }
        if (($row = $result1->fetch (PDO::FETCH_ASSOC)) == NULL)
        {
                if ($silent == FALSE)
-                       showError ('Query #1 succeded, but returned no data in getRackData()');
+                       showError ('Query #1 succeded, but returned no data', __FUNCTION__);
                return NULL;
        }
 
        // load metadata
-       $rack['id'] = $row['id'];
-       $rack['name'] = $row['name'];
-       $rack['height'] = $row['height'];
-       $rack['comment'] = $row['comment'];
-       $rack['row_id'] = $row['row_id'];
-       $rack['row_name'] = $row['row_name'];
+       $clist = array
+       (
+               'id',
+               'name',
+               'height',
+               'comment',
+               'row_id',
+               'left_is_front',
+               'bottom_is_unit1',
+               'row_name'
+       );
+       foreach ($clist as $cname)
+               $rack[$cname] = $row[$cname];
        $result1->closeCursor();
 
        // start with default rackspace
@@ -156,7 +189,7 @@ function getRackData ($rack_id = 0, $silent = FALSE)
        if ($result2 == NULL)
        {
                if ($silent == FALSE)
-                       showError ('SQL query failure #2 in getRackData()');
+                       showError ('SQL query failure #2', __FUNCTION__);
                return NULL;
        }
        global $loclist;
@@ -174,7 +207,7 @@ function getObjectInfo ($object_id = 0)
 {
        if ($object_id == 0)
        {
-               showError ('Invalid object_id in getObjectInfo()');
+               showError ('Invalid object_id', __FUNCTION__);
                return;
        }
        global $dbxlink;
@@ -185,12 +218,13 @@ function getObjectInfo ($object_id = 0)
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failure in getObjectInfo()');
+               $ei = $dbxlink->errorInfo();
+               showError ("SQL query failed with error ${ei[1]} (${ei[2]})", __FUNCTION__);
                return NULL;
        }
        if (($row = $result->fetch (PDO::FETCH_ASSOC)) == NULL)
        {
-               showError ('Query succeded, but returned no data in getObjectInfo()');
+               showError ('Query succeded, but returned no data', __FUNCTION__);
                $ret = NULL;
        }
        else
@@ -212,18 +246,14 @@ function getObjectInfo ($object_id = 0)
 
 function getPortTypes ()
 {
-       $chapter = readChapter ('PortType');
-       $ret = array();
-       foreach ($chapter as $entry)
-               $ret[$entry['dict_key']] = $entry['dict_value'];
-       return $ret;
+       return readChapter ('PortType');
 }
 
 function getObjectPortsAndLinks ($object_id = 0)
 {
        if ($object_id == 0)
        {
-               showError ('Invalid object_id in getObjectPorts()');
+               showError ('Invalid object_id', __FUNCTION__);
                return;
        }
        global $dbxlink;
@@ -274,6 +304,12 @@ function getObjectPortsAndLinks ($object_id = 0)
                        $ret[$count]['remote_name'] = htmlentities ($row['RemotePort_name'], ENT_QUOTES);
                        $ret[$count]['remote_object_id'] = $row['RemotePort_object_id'];
                        $ret[$count]['remote_object_name'] = $row['RackObject_name'];
+                       // Save on displayedName() calls.
+                       if (empty ($row['RackObject_name']) and !empty ($row['RemotePort_object_id']))
+                       {
+                               $oi = getObjectInfo ($row['RemotePort_object_id']);
+                               $ret[$count]['remote_object_name'] = displayedName ($oi);
+                       }
                        $count++;
                }
        }
@@ -288,7 +324,7 @@ function commitAddRack ($name, $height, $row_id, $comment)
        $result1 = $dbxlink->query ($query);
        if ($result1 == NULL)
        {
-               showError ('SQL query failed in commitAddRack()');
+               showError ('SQL query failed', __FUNCTION__);
                return FALSE;
        }
        // last_insert_id() is MySQL-specific
@@ -296,7 +332,7 @@ function commitAddRack ($name, $height, $row_id, $comment)
        $result2 = $dbxlink->query ($query);
        if ($result2 == NULL)
        {
-               showError ('Cannot get last ID in commitAddRack()');
+               showError ('Cannot get last ID', __FUNCTION__);
                return FALSE;
        }
        // we always have a row
@@ -320,12 +356,12 @@ function commitAddObject ($new_name, $new_label, $new_barcode, $new_type_id, $ne
        if ($result1 == NULL)
        {
                $errorInfo = $dbxlink->errorInfo();
-               showError ("SQL query '${query}' failed in commitAddObject(): " . $errorInfo[2]);
+               showError ("SQL query '${query}' failed: ${errorInfo[2]}", __FUNCTION__);
                die;
        }
        if ($result1->rowCount() != 1)
        {
-               showError ('Adding new object failed in commitAddObject()');
+               showError ('Adding new object failed', __FUNCTION__);
                return FALSE;
        }
        $query = 'select last_insert_id()';
@@ -333,13 +369,15 @@ function commitAddObject ($new_name, $new_label, $new_barcode, $new_type_id, $ne
        if ($result2 == NULL)
        {
                $errorInfo = $dbxlink->errorInfo();
-               showError ("SQL query '${query}' failed in commitAddObject(): " . $errorInfo[2]);
+               showError ("SQL query '${query}' failed: ${errorInfo[2]}", __FUNCTION__);
                die;
        }
        // we always have a row
        $row = $result2->fetch (PDO::FETCH_NUM);
        $last_insert_id = $row[0];
        $result2->closeCursor();
+       // Do AutoPorts magic
+       executeAutoPorts ($last_insert_id, $new_type_id);
        return recordHistory ('RackObject', "id = ${last_insert_id}");
 }
 
@@ -347,7 +385,7 @@ function commitUpdateObject ($object_id = 0, $new_name = '', $new_label = '', $n
 {
        if ($object_id == 0 || $new_type_id == 0)
        {
-               showError ('Not all required args to commitUpdateObject() are present.');
+               showError ('Not all required args are present.', __FUNCTION__);
                return FALSE;
        }
        global $dbxlink;
@@ -360,12 +398,12 @@ function commitUpdateObject ($object_id = 0, $new_name = '', $new_label = '', $n
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ("SQL query '${query}' failed in commitUpdateObject");
+               showError ("SQL query '${query}' failed", __FUNCTION__);
                return FALSE;
        }
        if ($result->rowCount() != 1)
        {
-               showError ('Error updating object information in commitUpdateObject()');
+               showError ('Error updating object information', __FUNCTION__);
                return FALSE;
        }
        $result->closeCursor();
@@ -376,7 +414,7 @@ function commitUpdateRack ($rack_id, $new_name, $new_height, $new_row_id, $new_c
 {
        if (empty ($rack_id) || empty ($new_name) || empty ($new_height))
        {
-               showError ('Not all required args to commitUpdateRack() are present.');
+               showError ('Not all required args are present.', __FUNCTION__);
                return FALSE;
        }
        global $dbxlink;
@@ -385,7 +423,7 @@ function commitUpdateRack ($rack_id, $new_name, $new_height, $new_row_id, $new_c
        $result1 = $dbxlink->query ($query);
        if ($result1->rowCount() != 1)
        {
-               showError ('Error updating rack information in commitUpdateRack()');
+               showError ('Error updating rack information', __FUNCTION__);
                return FALSE;
        }
        return recordHistory ('Rack', "id = ${rack_id}");
@@ -434,7 +472,7 @@ function processGridForm (&$rackData, $unchecked_state, $checked_state, $object_
                                "unit_no = ${unit_no} and atom = '${atom}' limit 1";
                        $r = $dbxlink->query ($query);
                        if ($r == NULL)
-                               return array ('code' => 500, 'message' => "${rack_name}: SQL DELETE query failed in processGridForm()");
+                               return array ('code' => 500, 'message' => __FUNCTION__ . ": ${rack_name}: SQL DELETE query failed");
                        if ($newstate != 'F')
                        {
                                $query =
@@ -442,7 +480,7 @@ function processGridForm (&$rackData, $unchecked_state, $checked_state, $object_
                                        "values(${rack_id}, ${unit_no}, '${atom}', '${newstate}') ";
                                $r = $dbxlink->query ($query);
                                if ($r == NULL)
-                                       return array ('code' => 500, 'message' => "${rack_name}: SQL INSERT query failed in processGridForm()");
+                                       return array ('code' => 500, 'message' => __FUNCTION__ . ": ${rack_name}: SQL INSERT query failed");
                        }
                        if ($newstate == 'T' and $object_id != 0)
                        {
@@ -459,7 +497,10 @@ function processGridForm (&$rackData, $unchecked_state, $checked_state, $object_
                }
        }
        if ($rackchanged)
+       {
+               resetThumbCache ($rack_id);
                return array ('code' => 200, 'message' => "${rack_name}: All changes were successfully saved.");
+       }
        else
                return array ('code' => 300, 'message' => "${rack_name}: No changes.");
 }
@@ -470,7 +511,7 @@ function getMoleculeForObject ($object_id = 0)
 {
        if ($object_id == 0)
        {
-               showError ("object_id == 0 in getMoleculeForObject()");
+               showError ("object_id == 0", __FUNCTION__);
                return NULL;
        }
        global $dbxlink;
@@ -480,7 +521,7 @@ function getMoleculeForObject ($object_id = 0)
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ("SQL query failed in getMoleculeForObject()");
+               showError ("SQL query failed", __FUNCTION__);
                return NULL;
        }
        $ret = $result->fetchAll (PDO::FETCH_ASSOC);
@@ -493,7 +534,7 @@ function getMolecule ($mid = 0)
 {
        if ($mid == 0)
        {
-               showError ("mid == 0 in getMolecule()");
+               showError ("mid == 0", __FUNCTION__);
                return NULL;
        }
        global $dbxlink;
@@ -503,7 +544,7 @@ function getMolecule ($mid = 0)
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ("SQL query failed in getMolecule()");
+               showError ("SQL query failed", __FUNCTION__);
                return NULL;
        }
        $ret = $result->fetchAll (PDO::FETCH_ASSOC);
@@ -520,14 +561,14 @@ function createMolecule ($molData)
        $result1 = $dbxlink->query ($query);
        if ($result1->rowCount() != 1)
        {
-               showError ('Error inserting into Molecule in createMolecule()');
+               showError ('Error inserting into Molecule', __FUNCTION__);
                return NULL;
        }
        $query = 'select last_insert_id()';
        $result2 = $dbxlink->query ($query);
        if ($result2 == NULL)
        {
-               showError ('Cannot get last ID in createMolecule().');
+               showError ('Cannot get last ID.', __FUNCTION__);
                return NULL;
        }
        $row = $result2->fetch (PDO::FETCH_NUM);
@@ -544,7 +585,7 @@ function createMolecule ($molData)
                $result3 = $dbxlink->query ($query);
                if ($result3 == NULL or $result3->rowCount() != 1)
                {
-                       showError ('Error inserting into Atom in createMolecule()');
+                       showError ('Error inserting into Atom', __FUNCTION__);
                        return NULL;
                }
        }
@@ -563,7 +604,7 @@ function recordHistory ($tableName, $whereClause)
        $result = $dbxlink->query ($query);
        if ($result == NULL or $result->rowCount() != 1)
        {
-               showError ("SQL query failed in recordHistory() for table ${tableName}");
+               showError ("SQL query '${query}' failed for table ${tableName}", __FUNCTION__);
                return FALSE;
        }
        return TRUE;
@@ -580,7 +621,7 @@ function getRackspaceHistory ()
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in getRackspaceHistory()');
+               showError ('SQL query failed', __FUNCTION__);
                return;
        }
        $ret = $result->fetchAll(PDO::FETCH_ASSOC);
@@ -593,7 +634,7 @@ function getOperationMolecules ($op_id = 0)
 {
        if ($op_id <= 0)
        {
-               showError ("Missing argument to getOperationMolecules()");
+               showError ("Missing argument", __FUNCTION__);
                return;
        }
        global $dbxlink;
@@ -601,14 +642,14 @@ function getOperationMolecules ($op_id = 0)
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ("SQL query failed in getOperationMolecules()");
+               showError ("SQL query failed", __FUNCTION__);
                return;
        }
        // We expect one row.
        $row = $result->fetch (PDO::FETCH_ASSOC);
        if ($row == NULL)
        {
-               showError ("SQL query succeded, but returned no results in getOperationMolecules().");
+               showError ("SQL query succeded, but returned no results.", __FUNCTION__);
                return;
        }
        $omid = $row['old_molecule_id'];
@@ -617,11 +658,11 @@ function getOperationMolecules ($op_id = 0)
        return array ($omid, $nmid);
 }
 
-function getResidentRacksData ($object_id = 0)
+function getResidentRacksData ($object_id = 0, $fetch_rackdata = TRUE)
 {
        if ($object_id <= 0)
        {
-               showError ('Invalid object_id in getResidentRacksData()');
+               showError ('Invalid object_id', __FUNCTION__);
                return;
        }
        $query = "select distinct rack_id from RackSpace where object_id = ${object_id} order by rack_id";
@@ -629,7 +670,7 @@ function getResidentRacksData ($object_id = 0)
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ("SQL query failed in getResidentRacksData()");
+               showError ("SQL query failed", __FUNCTION__);
                return;
        }
        $rows = $result->fetchAll (PDO::FETCH_NUM);
@@ -637,10 +678,15 @@ function getResidentRacksData ($object_id = 0)
        $ret = array();
        foreach ($rows as $row)
        {
+               if (!$fetch_rackdata)
+               {
+                       $ret[$row[0]] = $row[0];
+                       continue;
+               }
                $rackData = getRackData ($row[0]);
                if ($rackData == NULL)
                {
-                       showError ('getRackData() failed in getResidentRacksData()');
+                       showError ('getRackData() failed', __FUNCTION__);
                        return NULL;
                }
                $ret[$row[0]] = $rackData;
@@ -661,7 +707,7 @@ function getObjectGroupInfo ($group_id = 0)
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in getObjectGroupSummary');
+               showError ('SQL query failed', __FUNCTION__);
                return NULL;
        }
        $ret = array();
@@ -686,11 +732,11 @@ function getUnmountedObjects ()
                'select dict_value as objtype_name, dict_key as objtype_id, name, label, barcode, id, asset_no from ' .
                'RackObject inner join Dictionary on objtype_id = dict_key natural join Chapter ' .
                'left join RackSpace on id = object_id '.
-               'where rack_id is null and chapter_name = "RackObjectType" order by dict_value, name';
+               'where rack_id is null and chapter_name = "RackObjectType" order by dict_value, name, label, asset_no, barcode';
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failure in getUnmountedObjects()');
+               showError ('SQL query failure', __FUNCTION__);
                return NULL;
        }
        $ret = array();
@@ -715,7 +761,7 @@ function getProblematicObjects ()
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failure in getProblematicObjects()');
+               showError ('SQL query failure', __FUNCTION__);
                return NULL;
        }
        $ret = array();
@@ -734,7 +780,7 @@ function commitAddPort ($object_id = 0, $port_name, $port_type_id, $port_label,
 {
        if ($object_id <= 0)
        {
-               showError ('Invalid object_id in commitAddPort()');
+               showError ('Invalid object_id', __FUNCTION__);
                return;
        }
        $port_l2address = l2addressForDatabase ($port_l2address);
@@ -777,9 +823,9 @@ function commitUpdatePort ($port_id, $port_name, $port_label, $port_l2address, $
 function delObjectPort ($port_id)
 {
        if (unlinkPort ($port_id) != '')
-               return 'unlinkPort() failed in delObjectPort()';
+               return __FUNCTION__ . ': unlinkPort() failed';
        if (useDeleteBlade ('Port', 'id', $port_id) != TRUE)
-               return 'useDeleteBlade() failed in delObjectPort()';
+               return __FUNCTION__ . ': useDeleteBlade() failed';
        return '';
 }
 
@@ -795,7 +841,7 @@ function getObjectAddressesAndNames ()
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ("SQL query failure in getObjectAddressesAndNames($type_id)");
+               showError ("SQL query failure", __FUNCTION__);
                return NULL;
        }
        $ret = array();
@@ -836,7 +882,7 @@ function getEmptyPortsOfType ($type_id)
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ("SQL query failure in getEmptyPortsOfType($type_id)");
+               showError ("SQL query failure, type_id == ${type_id}", __FUNCTION__);
                return NULL;
        }
        $ret = array();
@@ -885,11 +931,13 @@ function unlinkPort ($port)
        
 }
 
+// FIXME: after falling back to using existing getObjectInfo we don't
+// need that large query. Shrink it some later.
 function getObjectAddresses ($object_id = 0)
 {
        if ($object_id == 0)
        {
-               showError ('Invalid object_id in getObjectAddresses()');
+               showError ('Invalid object_id', __FUNCTION__);
                return;
        }
        global $dbxlink;
@@ -914,7 +962,7 @@ function getObjectAddresses ($object_id = 0)
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ("SQL query failed in getObjectAddresses()");
+               showError ("SQL query failed", __FUNCTION__);
                return NULL;
        }
        else
@@ -923,7 +971,12 @@ function getObjectAddresses ($object_id = 0)
                $count=0;
                $refcount=0;
                $prev_ip = 0;
-               while ($row = $result->fetch (PDO::FETCH_ASSOC))
+               // We are going to call getObjectInfo() for some rows,
+               // hence the connector must be unloaded from the
+               // current data.
+               $rows = $result->fetchAll (PDO::FETCH_ASSOC);
+               $result->closeCursor();
+               foreach ($rows as $row)
                {
                        if ($prev_ip != $row['IPBonds_ip'])
                        {
@@ -943,12 +996,17 @@ function getObjectAddresses ($object_id = 0)
                                $ret[$count]['references'][$refcount]['type'] = $row['RemoteBonds_type'];
                                $ret[$count]['references'][$refcount]['name'] = $row['RemoteBonds_name'];
                                $ret[$count]['references'][$refcount]['object_id'] = $row['RemoteBonds_object_id'];
-                               $ret[$count]['references'][$refcount]['object_name'] = $row['RemoteObject_name'];
+                               if (empty ($row['RemoteBonds_object_id']))
+                                       $ret[$count]['references'][$refcount]['object_name'] = $row['RemoteObject_name'];
+                               else
+                               {
+                                       $oi = getObjectInfo ($row['RemoteBonds_object_id']);
+                                       $ret[$count]['references'][$refcount]['object_name'] = displayedName ($oi);
+                               }
                                $refcount++;
                        }
                }
        }
-       $result->closeCursor();
        return $ret;
 }
 
@@ -1056,14 +1114,15 @@ function updateAddress ($ip=0, $name='', $reserved='no')
                return 'useInsertBlade() failed in updateAddress()';
 }
 
+// FIXME: This function doesn't wipe relevant records from IPAddress table.
 function commitDeleteRange ($id = 0)
 {
        if ($id <= 0)
-               return 'Invalid range ID in commitDeleteRange()';
+               return __FUNCTION__ . ': Invalid range ID';
        if (useDeleteBlade ('IPRanges', 'id', $id))
                return '';
        else
-               return 'SQL query failed in commitDeleteRange';
+               return __FUNCTION__ . ': SQL query failed';
 }
 
 function updateBond ($ip='', $object_id=0, $name='', $type='')
@@ -1092,11 +1151,11 @@ function getUserAccounts ()
        global $dbxlink;
        $query =
                'select user_id, user_name, user_password_hash, user_realname, user_enabled ' .
-               'from UserAccount order by user_id';
+               'from UserAccount order by user_name';
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in getUserAccounts()');
+               showError ('SQL query failed', __FUNCTION__);
                return NULL;
        }
        $ret = array();
@@ -1115,11 +1174,11 @@ function getUserPermissions ()
        $query =
                "select UserPermission.user_id, user_name, page, tab, access from " .
                "UserPermission natural left join UserAccount where (user_name is not null) or " .
-               "(user_name is null and UserPermission.user_id = 0) order by user_id, page, tab";
+               "(user_name is null and UserPermission.user_id = 0) order by user_name, page, tab";
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in getUserPermissions()');
+               showError ('SQL query failed', __FUNCTION__);
                return NULL;
        }
        $ret = array();
@@ -1142,7 +1201,7 @@ function searchByl2address ($l2addr)
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in objectIDbyl2address()');
+               showError ('SQL query failed', __FUNCTION__);
                return NULL;
        }
        $rows = $result->fetchAll (PDO::FETCH_ASSOC);
@@ -1151,7 +1210,7 @@ function searchByl2address ($l2addr)
                return NULL;
        if (count ($rows) == 1) // Target found.
                return $rows[0];
-       showError ('More than one results found in objectIDbyl2address(). This is probably a broken unique key.');
+       showError ('More than one results was found. This is probably a broken unique key.', __FUNCTION__);
        return NULL;
 }
 
@@ -1163,7 +1222,7 @@ function getPortID ($object_id, $port_name)
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in getPortID()');
+               showError ('SQL query failed', __FUNCTION__);
                return NULL;
        }
        $rows = $result->fetchAll (PDO::FETCH_NUM);
@@ -1197,7 +1256,7 @@ function commitUpdateUserAccount ($id, $new_username, $new_realname, $new_passwo
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in commitUpdateUserAccount()');
+               showError ('SQL query failed', __FUNCTION__);
                die;
        }
        return TRUE;
@@ -1212,7 +1271,7 @@ function commitEnableUserAccount ($id, $new_enabled_value)
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in commitEnableUserAccount()');
+               showError ('SQL query failed', __FUNCTION__);
                die;
        }
        return TRUE;
@@ -1242,7 +1301,7 @@ function commitRevokePermission ($userid, $page, $tab)
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in commitRevokePermission()');
+               showError ('SQL query failed', __FUNCTION__);
                die;
        }
        return TRUE;
@@ -1262,7 +1321,7 @@ function getPortCompat ()
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in getPortCompat()');
+               showError ('SQL query failed', __FUNCTION__);
                return NULL;
        }
        $ret = $result->fetchAll (PDO::FETCH_ASSOC);
@@ -1275,14 +1334,14 @@ function removePortCompat ($type1 = 0, $type2 = 0)
        global $dbxlink;
        if ($type1 == 0 or $type2 == 0)
        {
-               showError ('Invalid arguments to removePortCompat');
+               showError ('Invalid arguments', __FUNCTION__);
                die;
        }
        $query = "delete from PortCompat where type1 = ${type1} and type2 = ${type2} limit 1";
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in removePortCompat()');
+               showError ('SQL query failed', __FUNCTION__);
                die;
        }
        return TRUE;
@@ -1292,7 +1351,7 @@ function addPortCompat ($type1 = 0, $type2 = 0)
 {
        if ($type1 <= 0 or $type2 <= 0)
        {
-               showError ('Invalid arguments to addPortCompat');
+               showError ('Invalid arguments', __FUNCTION__);
                die;
        }
        return useInsertBlade
@@ -1308,17 +1367,17 @@ function addPortCompat ($type1 = 0, $type2 = 0)
 function getDict ()
 {
        global $dbxlink;
-       $query =
+       $query1 =
                "select chapter_name, Chapter.chapter_no, dict_key, dict_value, sticky from " .
                "Chapter natural left join Dictionary order by chapter_name, dict_value";
-       $result = $dbxlink->query ($query);
-       if ($result == NULL)
+       $result1 = $dbxlink->query ($query1);
+       if ($result1 == NULL)
        {
-               showError ('SQL query failed in getDict()');
+               showError ('SQL query #1 failed', __FUNCTION__);
                return NULL;
        }
        $dict = array();
-       while ($row = $result->fetch (PDO::FETCH_ASSOC))
+       while ($row = $result1->fetch (PDO::FETCH_ASSOC))
        {
                $chapter_no = $row['chapter_no'];
                if (!isset ($dict[$chapter_no]))
@@ -1329,17 +1388,149 @@ function getDict ()
                        $dict[$chapter_no]['word'] = array();
                }
                if ($row['dict_key'] != NULL)
+               {
                        $dict[$chapter_no]['word'][$row['dict_key']] = $row['dict_value'];
+                       $dict[$chapter_no]['refcnt'][$row['dict_key']] = 0;
+               }
        }
-       $result->closeCursor();
+       $result1->closeCursor();
+// Find the list of all assigned values of dictionary-addressed attributes, each with
+// chapter/word keyed reference counters. Use the structure to adjust reference counters
+// of the returned disctionary words.
+       $query2 = "select a.attr_id, am.chapter_no, uint_value, count(object_id) as refcnt " .
+               "from Attribute as a inner join AttributeMap as am on a.attr_id = am.attr_id " .
+               "inner join AttributeValue as av on a.attr_id = av.attr_id " .
+               "inner join Dictionary as d on am.chapter_no = d.chapter_no and av.uint_value = d.dict_key " .
+               "where attr_type = 'dict' group by a.attr_id, am.chapter_no, uint_value " .
+               "order by a.attr_id, am.chapter_no, uint_value";
+       $result2 = $dbxlink->query ($query2);
+       if ($result2 == NULL)
+       {
+               showError ('SQL query #2 failed', __FUNCTION__);
+               return NULL;
+       }
+       $refcnt = array();
+       while ($row = $result2->fetch (PDO::FETCH_ASSOC))
+               $dict[$row['chapter_no']]['refcnt'][$row['uint_value']] = $row['refcnt'];
+       $result2->closeCursor();
        return $dict;
 }
 
+function getDictStats ()
+{
+       $stock_chapters = array (1, 2, 3, 11, 12, 13, 14, 16, 17, 18, 19, 20, 21, 22, 23);
+       global $dbxlink;
+       $query =
+               "select Chapter.chapter_no, chapter_name, count(dict_key) as wc from " .
+               "Chapter natural left join Dictionary group by Chapter.chapter_no";
+       $result1 = $dbxlink->query ($query);
+       if ($result1 == NULL)
+       {
+               showError ('SQL query #1 failed', __FUNCTION__);
+               return NULL;
+       }
+       $tc = $tw = $uc = $uw = 0;
+       while ($row = $result1->fetch (PDO::FETCH_ASSOC))
+       {
+               $tc++;
+               $tw += $row['wc'];;
+               if (in_array ($row['chapter_no'], $stock_chapters))
+                       continue;
+               $uc++;
+               $uw += $row['wc'];;
+       }
+       $result1->closeCursor();
+       $query = "select count(attr_id) as attrc from RackObject as ro left join " .
+               "AttributeValue as av on ro.id = av.object_id group by ro.id";
+       $result2 = $dbxlink->query ($query);
+       if ($result2 == NULL)
+       {
+               showError ('SQL query #2 failed', __FUNCTION__);
+               return NULL;
+       }
+       $to = $ta = $so = 0;
+       while ($row = $result2->fetch (PDO::FETCH_ASSOC))
+       {
+               $to++;
+               if ($row['attrc'] != 0)
+               {
+                       $so++;
+                       $ta += $row['attrc'];
+               }
+       }
+       $result2->closeCursor();
+       $ret = array();
+       $ret['Total chapters in dictionary'] = $tc;
+       $ret['Total words in dictionary'] = $tw;
+       $ret['User chapters'] = $uc;
+       $ret['Words in user chapters'] = $uw;
+       $ret['Total objects'] = $to;
+       $ret['Objects with stickers'] = $so;
+       $ret['Total stickers attached']  = $ta;
+       return $ret;
+}
+
+function getIPv4Stats()
+{
+       global $dbxlink;
+       $ret = array();
+       $subject = array();
+       $subject[] = array ('q' => 'select count(id) from IPRanges', 'txt' => 'Networks');
+       $subject[] = array ('q' => 'select count(ip) from IPAddress', 'txt' => 'Addresses commented/reserved');
+       $subject[] = array ('q' => 'select count(ip) from IPBonds', 'txt' => 'Addresses allocated');
+       $subject[] = array ('q' => 'select count(*) from PortForwarding', 'txt' => 'NAT rules');
+       $subject[] = array ('q' => 'select count(id) from IPVirtualService', 'txt' => 'Virtual services');
+       $subject[] = array ('q' => 'select count(id) from IPRSPool', 'txt' => 'Real server pools');
+       $subject[] = array ('q' => 'select count(id) from IPRealServer', 'txt' => 'Real servers');
+       $subject[] = array ('q' => 'select count(distinct object_id) from IPLoadBalancer', 'txt' => 'Load balancers');
+
+       foreach ($subject as $item)
+       {
+               $result = $dbxlink->query ($item['q']);
+               if ($result == NULL)
+               {
+                       showError ("SQL query '${item['q']}' failed", __FUNCTION__);
+                       return NULL;
+               }
+               $row = $result->fetch (PDO::FETCH_NUM);
+               $ret[$item['txt']] = $row[0];
+               $result->closeCursor();
+               unset ($result);
+       }
+       return $ret;
+}
+
+function getRackspaceStats()
+{
+       global $dbxlink;
+       $ret = array();
+       $subject = array();
+       $subject[] = array ('q' => 'select count(*) from Dictionary where chapter_no = 3', 'txt' => 'Rack rows');
+       $subject[] = array ('q' => 'select count(*) from Rack', 'txt' => 'Racks');
+       $subject[] = array ('q' => 'select avg(height) from Rack', 'txt' => 'Average rack height');
+       $subject[] = array ('q' => 'select sum(height) from Rack', 'txt' => 'Total rack units in field');
+
+       foreach ($subject as $item)
+       {
+               $result = $dbxlink->query ($item['q']);
+               if ($result == NULL)
+               {
+                       showError ("SQL query '${item['q']}' failed", __FUNCTION__);
+                       return NULL;
+               }
+               $row = $result->fetch (PDO::FETCH_NUM);
+               $ret[$item['txt']] = empty ($row[0]) ? 0 : $row[0];
+               $result->closeCursor();
+               unset ($result);
+       }
+       return $ret;
+}
+
 function commitUpdateDictionary ($chapter_no = 0, $dict_key = 0, $dict_value = '')
 {
        if ($chapter_no <= 0 or $dict_key <= 0 or empty ($dict_value))
        {
-               showError ('Invalid args to commitUpdateDictionary()');
+               showError ('Invalid args', __FUNCTION__);
                die;
        }
        global $dbxlink;
@@ -1349,7 +1540,7 @@ function commitUpdateDictionary ($chapter_no = 0, $dict_key = 0, $dict_value = '
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in commitUpdateDictionary()');
+               showError ('SQL query failed', __FUNCTION__);
                die;
        }
        return TRUE;
@@ -1359,7 +1550,7 @@ function commitSupplementDictionary ($chapter_no = 0, $dict_value = '')
 {
        if ($chapter_no <= 0 or empty ($dict_value))
        {
-               showError ('Invalid args to commitSupplementDictionary()');
+               showError ('Invalid args', __FUNCTION__);
                die;
        }
        return useInsertBlade
@@ -1373,7 +1564,7 @@ function commitReduceDictionary ($chapter_no = 0, $dict_key = 0)
 {
        if ($chapter_no <= 0 or $dict_key <= 0)
        {
-               showError ('Invalid args to commitReduceDictionary()');
+               showError ('Invalid args', __FUNCTION__);
                die;
        }
        global $dbxlink;
@@ -1383,7 +1574,7 @@ function commitReduceDictionary ($chapter_no = 0, $dict_key = 0)
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in commitReduceDictionary()');
+               showError ('SQL query failed', __FUNCTION__);
                die;
        }
        return TRUE;
@@ -1393,7 +1584,7 @@ function commitAddChapter ($chapter_name = '')
 {
        if (empty ($chapter_name))
        {
-               showError ('Invalid args to commitAddChapter()');
+               showError ('Invalid args', __FUNCTION__);
                die;
        }
        return useInsertBlade
@@ -1407,7 +1598,7 @@ function commitUpdateChapter ($chapter_no = 0, $chapter_name = '')
 {
        if ($chapter_no <= 0 or empty ($chapter_name))
        {
-               showError ('Invalid args to commitUpdateChapter()');
+               showError ('Invalid args', __FUNCTION__);
                die;
        }
        global $dbxlink;
@@ -1417,7 +1608,7 @@ function commitUpdateChapter ($chapter_no = 0, $chapter_name = '')
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in commitUpdateChapter()');
+               showError ('SQL query failed', __FUNCTION__);
                die;
        }
        return TRUE;
@@ -1427,7 +1618,7 @@ function commitDeleteChapter ($chapter_no = 0)
 {
        if ($chapter_no <= 0)
        {
-               showError ('Invalid args to commitDeleteChapter()');
+               showError ('Invalid args', __FUNCTION__);
                die;
        }
        global $dbxlink;
@@ -1436,35 +1627,38 @@ function commitDeleteChapter ($chapter_no = 0)
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in commitDeleteChapter()');
+               showError ('SQL query failed', __FUNCTION__);
                die;
        }
        return TRUE;
 }
 
-// This is a dictionary accessor.
+// This is a dictionary accessor. We perform link rendering, so the user sees
+// nice <select> drop-downs.
 function readChapter ($chapter_name = '')
 {
        if (empty ($chapter_name))
        {
-               showError ('invalid argument to readChapter()');
+               showError ('invalid argument', __FUNCTION__);
                return NULL;
        }
        global $dbxlink;
        $query =
                "select dict_key, dict_value from Dictionary natural join Chapter " .
-               "where chapter_name = '${chapter_name}' order by dict_value";
+               "where chapter_name = '${chapter_name}'";
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
                $errorInfo = $dbxlink->errorInfo();
-               showError ("SQL query '${query}'\nwith message '${errorInfo[2]}'\nfailed in readChapter('${chapter_name}')");
+               showError ("SQL query '${query}'\nwith message '${errorInfo[2]}'\nfailed for chapter_no = '${chapter_name}'", __FUNCTION__);
                return NULL;
        }
        $chapter = array();
        while ($row = $result->fetch (PDO::FETCH_ASSOC))
-               $chapter[] = $row;
+               $chapter[$row['dict_key']] = parseWikiLink ($row['dict_value'], 'o');
        $result->closeCursor();
+       // SQL ORDER BY had no sense, because we need to sort after link rendering, not before.
+       asort ($chapter);
        return $chapter;
 }
 
@@ -1484,7 +1678,7 @@ function getAttrMap ()
        if ($result == NULL)
        {
                $errorInfo = $dbxlink->errorInfo();
-               showError ("SQL query '${query}'\nwith message '${errorInfo[2]}'\nfailed in getAttrMap()");
+               showError ("SQL query '${query}'\nwith message '${errorInfo[2]}'\nfailed", __FUNCTION__);
                return NULL;
        }
        $ret = array();
@@ -1517,7 +1711,7 @@ function commitUpdateAttribute ($attr_id = 0, $attr_name = '')
 {
        if ($attr_id <= 0 or empty ($attr_name))
        {
-               showError ('Invalid args to commitUpdateAttribute()');
+               showError ('Invalid args', __FUNCTION__);
                die;
        }
        global $dbxlink;
@@ -1527,7 +1721,7 @@ function commitUpdateAttribute ($attr_id = 0, $attr_name = '')
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ("SQL query '${query}' failed in commitUpdateAttribute()");
+               showError ("SQL query '${query}' failed", __FUNCTION__);
                die;
        }
        return TRUE;
@@ -1537,7 +1731,7 @@ function commitAddAttribute ($attr_name = '', $attr_type = '')
 {
        if (empty ($attr_name))
        {
-               showError ('Invalid args to commitAddAttribute()');
+               showError ('Invalid args', __FUNCTION__);
                die;
        }
        switch ($attr_type)
@@ -1548,7 +1742,7 @@ function commitAddAttribute ($attr_name = '', $attr_type = '')
                case 'dict':
                        break;
                default:
-                       showError ('Invalid args to commitAddAttribute()');
+                       showError ('Invalid args', __FUNCTION__);
                        die;
        }
        return useInsertBlade
@@ -1562,7 +1756,7 @@ function commitDeleteAttribute ($attr_id = 0)
 {
        if ($attr_id <= 0)
        {
-               showError ('Invalid args to commitDeleteAttribute()');
+               showError ('Invalid args', __FUNCTION__);
                die;
        }
        return useDeleteBlade ('Attribute', 'attr_id', $attr_id);
@@ -1573,7 +1767,7 @@ function commitSupplementAttrMap ($attr_id = 0, $objtype_id = 0, $chapter_no = 0
 {
        if ($attr_id <= 0 or $objtype_id <= 0 or $chapter_no <= 0)
        {
-               showError ('Invalid args to commitSupplementAttrMap()');
+               showError ('Invalid args', __FUNCTION__);
                die;
        }
        return useInsertBlade
@@ -1592,7 +1786,7 @@ function commitReduceAttrMap ($attr_id = 0, $objtype_id)
 {
        if ($attr_id <= 0 or $objtype_id <= 0)
        {
-               showError ('Invalid args to commitReduceAttrMap()');
+               showError ('Invalid args', __FUNCTION__);
                die;
        }
        global $dbxlink;
@@ -1602,7 +1796,7 @@ function commitReduceAttrMap ($attr_id = 0, $objtype_id)
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in commitReduceAttrMap()');
+               showError ('SQL query failed', __FUNCTION__);
                die;
        }
        return TRUE;
@@ -1615,7 +1809,7 @@ function getAttrValues ($object_id)
 {
        if ($object_id <= 0)
        {
-               showError ('Invalid argument to getAttrValues()');
+               showError ('Invalid argument', __FUNCTION__);
                return NULL;
        }
        global $dbxlink;
@@ -1633,7 +1827,7 @@ function getAttrValues ($object_id)
        if ($result == NULL)
        {
                $errorInfo = $dbxlink->errorInfo();
-               showError ("SQL query '${query}'\nwith message '${errorInfo[2]}'\nfailed in getAttrValues()");
+               showError ("SQL query '${query}'\nwith message '${errorInfo[2]}'\nfailed", __FUNCTION__);
                return NULL;
        }
        while ($row = $result->fetch (PDO::FETCH_ASSOC))
@@ -1648,7 +1842,8 @@ function getAttrValues ($object_id)
                        case 'float':
                        case 'string':
                        case 'dict':
-                               $record['value'] = $row[$row['attr_type'] . '_value'];
+                               $record['value'] = parseWikiLink ($row[$row['attr_type'] . '_value'], 'o');
+                               $record['a_value'] = parseWikiLink ($row[$row['attr_type'] . '_value'], 'a');
                                $record['chapter_name'] = $row['chapter_name'];
                                $record['key'] = $row['uint_value'];
                                break;
@@ -1666,7 +1861,7 @@ function commitResetAttrValue ($object_id = 0, $attr_id = 0)
 {
        if ($object_id <= 0 or $attr_id <= 0)
        {
-               showError ('Invalid arguments to commitResetAttrValue()');
+               showError ('Invalid arguments', __FUNCTION__);
                die;
        }
        global $dbxlink;
@@ -1674,7 +1869,7 @@ function commitResetAttrValue ($object_id = 0, $attr_id = 0)
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ('SQL query failed in commitResetAttrValue()');
+               showError ('SQL query failed', __FUNCTION__);
                die;
        }
        return TRUE;
@@ -1685,7 +1880,7 @@ function commitUpdateAttrValue ($object_id = 0, $attr_id = 0, $value = '')
 {
        if ($object_id <= 0 or $attr_id <= 0)
        {
-               showError ('Invalid arguments to commitUpdateAttrValue()');
+               showError ('Invalid arguments', __FUNCTION__);
                die;
        }
        if (empty ($value))
@@ -1695,13 +1890,13 @@ function commitUpdateAttrValue ($object_id = 0, $attr_id = 0, $value = '')
        $result = $dbxlink->query ($query1);
        if ($result == NULL)
        {
-               showError ('SQL query #1 failed in commitUpdateAttrValue()');
+               showError ('SQL query #1 failed', __FUNCTION__);
                die;
        }
        $row = $result->fetch (PDO::FETCH_NUM);
        if ($row == NULL)
        {
-               showError ('SQL query #1 returned no results in commitUpdateAttrValue()');
+               showError ('SQL query #1 returned no results', __FUNCTION__);
                die;
        }
        $attr_type = $row[0];
@@ -1717,7 +1912,7 @@ function commitUpdateAttrValue ($object_id = 0, $attr_id = 0, $value = '')
                        $column = 'uint_value';
                        break;
                default:
-                       showError ("Unknown attribute type '${attr_type}' met in commitUpdateAttrValue()");
+                       showError ("Unknown attribute type '${attr_type}' met", __FUNCTION__);
                        die;
        }
        $query2 =
@@ -1726,7 +1921,7 @@ function commitUpdateAttrValue ($object_id = 0, $attr_id = 0, $value = '')
        $result = $dbxlink->query ($query2);
        if ($result == NULL)
        {
-               showError ('SQL query #2 failed in commitUpdateAttrValue()');
+               showError ('SQL query #2 failed', __FUNCTION__);
                die;
        }
        // We know $value isn't empty here.
@@ -1736,7 +1931,7 @@ function commitUpdateAttrValue ($object_id = 0, $attr_id = 0, $value = '')
        $result = $dbxlink->query ($query3);
        if ($result == NULL)
        {
-               showError ('SQL query #3 failed in commitUpdateAttrValue()');
+               showError ('SQL query #3 failed', __FUNCTION__);
                die;
        }
        return TRUE;
@@ -1746,7 +1941,7 @@ function commitUseupPort ($port_id = 0)
 {
        if ($port_id <= 0)
        {
-               showError ("Invalid argument to commitUseupPort()");
+               showError ("Invalid argument", __FUNCTION__);
                die;
        }
        global $dbxlink;
@@ -1754,32 +1949,13 @@ function commitUseupPort ($port_id = 0)
        $result = $dbxlink->exec ($query);
        if ($result == NULL)
        {
-               showError ("SQL query failed in commitUseupPort()");
+               showError ("SQL query failed", __FUNCTION__);
                die;
        }
        return TRUE;
        
 }
 
-function commitUpdateUI ($varname, $vartype, $emptyok, $varvalue, $description)
-{
-       if (empty ($varname) || empty ($vartype) || empty ($emptyok) || empty ($varvalue)|| empty ($description))
-       {
-               showError ('Not all required args to commitUpdateUI() are present.');
-               return FALSE;
-       }
-       global $dbxlink;
-       $query = "UPDATE Config SET vartype='${vartype}', emptyok='${emptyok}', varvalue='${varvalue}', description='${description}' " .
-                       "WHERE varname='${varname}'";
-       $result = $dbxlink->query ($query);
-       if ($result->rowCount() != 1)
-       {
-               showError ('Error updating config in commitUpdateUI()');
-               return FALSE;
-       }
-       return TRUE;
-}
-
 // This is a swiss-knife blade to insert a record into a table.
 // The first argument is table name.
 // The second argument is an array of "name" => "value" pairs.
@@ -1827,7 +2003,7 @@ function loadConfigCache ()
        if ($result == NULL)
        {
                $errorInfo = $dbxlink->errorInfo();
-               showError ("SQL query '${query}'\nwith message '${errorInfo[2]}'\nfailed in loadConfigCache()");
+               showError ("SQL query '${query}'\nwith message '${errorInfo[2]}'\nfailed", __FUNCTION__);
                return NULL;
        }
        $cache = array();
@@ -1840,23 +2016,24 @@ function loadConfigCache ()
 // setConfigVar() is expected to perform all necessary filtering
 function storeConfigVar ($varname = NULL, $varvalue = NULL)
 {
-       if ($varname == NULL || $varvalue == NULL)
+       global $dbxlink;
+       if (empty ($varname) || $varvalue === NULL)
        {
-               showError ('Invalid arguments to storeConfigVar()');
+               showError ('Invalid arguments', __FUNCTION__);
                return FALSE;
        }
        $query = "update Config set varvalue='${varvalue}' where varname='${varname}' limit 1";
        $result = $dbxlink->query ($query);
        if ($result == NULL)
        {
-               showError ("SQL query '${query}' failed in storeConfigVar()");
+               showError ("SQL query '${query}' failed", __FUNCTION__);
                return FALSE;
        }
        $rc = $result->rowCount();
        $result->closeCursor();
-       if ($rc == 1)
+       if ($rc == 0 or $rc == 1)
                return TRUE;
-       showError ("Something went wrong in storeConfigVar() when updatating '${varname}'");
+       showError ("Something went wrong for args '${varname}', '${varvalue}'", __FUNCTION__);
        return FALSE;
 }
 
@@ -1872,17 +2049,613 @@ function getDatabaseVersion ()
                $errorInfo = $dbxlink->errorInfo();
                if ($errorInfo[0] == '42S02') // ER_NO_SUCH_TABLE
                        return '0.14.4';
-               die ('SQL query #1 failed in getDatabaseVersion() with error ' . $errorInfo[2]);
+               die (__FUNCTION__ . ': SQL query #1 failed with error ' . $errorInfo[2]);
        }
        $rows = $result->fetchAll (PDO::FETCH_NUM);
        if (count ($rows) != 1 || empty ($rows[0][0]))
        {
                $result->closeCursor();
-               die ('Cannot guess database version. Config table is present, but DB_VERSION is missing or invalid. Giving up.');
+               die (__FUNCTION__ . ': Cannot guess database version. Config table is present, but DB_VERSION is missing or invalid. Giving up.');
        }
        $ret = $rows[0][0];
        $result->closeCursor();
        return $ret;
 }
 
+// Return an array of virtual services. For each of them list real server pools
+// with their load balancers and other stats.
+function getSLBSummary ()
+{
+       global $dbxlink;
+       $query = 'select vs.id as vsid, inet_ntoa(vip) as vip, vport, proto, vs.name, object_id, ' .
+               'lb.rspool_id, pool.name as pool_name, count(rs.id) as rscount ' .
+               'from IPVirtualService as vs inner join IPLoadBalancer as lb on vs.id = lb.vs_id ' .
+               'inner join IPRSPool as pool on lb.rspool_id = pool.id ' .
+               'left join IPRealServer as rs on rs.rspool_id = lb.rspool_id ' .
+               'group by vs.id, object_id order by vs.vip, object_id';
+       $result = $dbxlink->query ($query);
+       if ($result == NULL)
+       {
+               $errorInfo = $dbxlink->errorInfo();
+               showError ("SQL query '${query}' failed with message '${errorInfo[2]}'", __FUNCTION__);
+               return NULL;
+       }
+       $ret = array();
+       while ($row = $result->fetch (PDO::FETCH_ASSOC))
+       {
+               $vsid = $row['vsid'];
+               $object_id = $row['object_id'];
+               if (!isset ($ret[$vsid]))
+               {
+                       $ret[$vsid] = array();
+                       foreach (array ('vip', 'vport', 'proto', 'name') as $cname)
+                               $ret[$vsid][$cname] = $row[$cname];
+                       $ret[$vsid]['lblist'] = array();
+               }
+               // There's only one assigned RS pool possible for each LB-VS combination.
+               $ret[$vsid]['lblist'][$row['object_id']] = array
+               (
+                       'id' => $row['rspool_id'],
+                       'size' => $row['rscount'],
+                       'name' => $row['pool_name']
+               );
+       }
+       $result->closeCursor();
+       return $ret;
+}
+
+// Get the detailed composition of a particular virtual service, namely the list
+// of all pools, each shown with the list of objects servicing it. VS/RS configs
+// will be returned as well.
+function getVServiceInfo ($vsid = 0)
+{
+       global $dbxlink;
+       $query1 = "select inet_ntoa(vip) as vip, vport, proto, name, vsconfig, rsconfig " .
+               "from IPVirtualService where id = ${vsid}";
+       $result1 = $dbxlink->query ($query1);
+       if ($result1 == NULL)
+       {
+               showError ('SQL query #1 failed', __FUNCTION__);
+               return NULL;
+       }
+       $vsinfo = array ();
+       $row = $result1->fetch (PDO::FETCH_ASSOC);
+       if (!$row)
+               return NULL;
+       foreach (array ('vip', 'vport', 'proto', 'name', 'vsconfig', 'rsconfig') as $cname)
+               $vsinfo[$cname] = $row[$cname];
+       $vsinfo['rspool'] = array();
+       $result1->closeCursor();
+       $query2 = "select pool.id, name, pool.vsconfig, pool.rsconfig, object_id, " .
+               "lb.vsconfig as lb_vsconfig, lb.rsconfig as lb_rsconfig from " .
+               "IPRSPool as pool left join IPLoadBalancer as lb on pool.id = lb.rspool_id " .
+               "where vs_id = ${vsid} order by pool.name, object_id";
+       $result2 = $dbxlink->query ($query2);
+       if ($result2 == NULL)
+       {
+               showError ('SQL query #2 failed', __FUNCTION__);
+               return NULL;
+       }
+       while ($row = $result2->fetch (PDO::FETCH_ASSOC))
+       {
+               if (!isset ($vsinfo['rspool'][$row['id']]))
+               {
+                       $vsinfo['rspool'][$row['id']]['name'] = $row['name'];
+                       $vsinfo['rspool'][$row['id']]['vsconfig'] = $row['vsconfig'];
+                       $vsinfo['rspool'][$row['id']]['rsconfig'] = $row['rsconfig'];
+                       $vsinfo['rspool'][$row['id']]['lblist'] = array();
+               }
+               if ($row['object_id'] == NULL)
+                       continue;
+               $vsinfo['rspool'][$row['id']]['lblist'][$row['object_id']] = array
+               (
+                       'vsconfig' => $row['lb_vsconfig'],
+                       'rsconfig' => $row['lb_rsconfig']
+               );
+       }
+       $result2->closeCursor();
+       return $vsinfo;
+}
+
+// Collect and return the following info about the given real server pool:
+// basic information
+// parent virtual service information
+// load balancers list (each with a list of VSes)
+// real servers list
+
+function getRSPoolInfo ($id = 0)
+{
+       global $dbxlink;
+       $query1 = "select id, name, vsconfig, rsconfig from " .
+               "IPRSPool where id = ${id}";
+       $result1 = $dbxlink->query ($query1);
+       if ($result1 == NULL)
+       {
+               showError ('SQL query #1 failed', __FUNCTION__);
+               return NULL;
+       }
+       $ret = array();
+       $row = $result1->fetch (PDO::FETCH_ASSOC);
+       if (!$row)
+               return NULL;
+       foreach (array ('id', 'name', 'vsconfig', 'rsconfig') as $c)
+               $ret[$c] = $row[$c];
+       $result1->closeCursor();
+       $ret['lblist'] = array();
+       $ret['rslist'] = array();
+       $query2 = "select object_id, vs_id, vsconfig, rsconfig from IPLoadBalancer where rspool_id = ${id} order by object_id";
+       $result2 = $dbxlink->query ($query2);
+       if ($result2 == NULL)
+       {
+               showError ('SQL query #2 failed', __FUNCTION__);
+               return NULL;
+       }
+       while ($row = $result2->fetch (PDO::FETCH_ASSOC))
+               foreach (array ('vsconfig', 'rsconfig') as $c)
+                       $ret['lblist'][$row['object_id']][$row['vs_id']][$c] = $row[$c];
+       $result2->closeCursor();
+       $query3 = "select id, inservice, inet_ntoa(rsip) as rsip, rsport, rsconfig from " .
+               "IPRealServer where rspool_id = ${id} order by IPRealServer.rsip, rsport";
+       $result3 = $dbxlink->query ($query3);
+       if ($result3 == NULL)
+       {
+               showError ('SQL query #3 failed', __FUNCTION__);
+               return NULL;
+       }
+       while ($row = $result3->fetch (PDO::FETCH_ASSOC))
+               foreach (array ('inservice', 'rsip', 'rsport', 'rsconfig') as $c)
+                       $ret['rslist'][$row['id']][$c] = $row[$c];
+       $result3->closeCursor();
+       return $ret;
+}
+
+function addRStoRSPool ($pool_id = 0, $rsip = '', $rsport = 0, $inservice = 'no', $rsconfig = '')
+{
+       if ($pool_id <= 0 or $rsport <= 0)
+       {
+               showError ('Invalid arguments', __FUNCTION__);
+               die;
+       }
+       return useInsertBlade
+       (
+               'IPRealServer',
+               array
+               (
+                       'rsip' => "inet_aton('${rsip}')",
+                       'rsport' => $rsport,
+                       'rspool_id' => $pool_id,
+                       'inservice' => ($inservice == 'yes' ? "'yes'" : "'no'"),
+                       'rsconfig' => (empty ($rsconfig) ? 'NULL' : "'${rsconfig}'")
+               )
+       );
+}
+
+function commitCreateVS ($vip = '', $vport = 0, $proto = '', $name = '', $vsconfig, $rsconfig)
+{
+       if (empty ($vip) or $vport <= 0 or empty ($proto))
+       {
+               showError ('Invalid arguments', __FUNCTION__);
+               die;
+       }
+       return useInsertBlade
+       (
+               'IPVirtualService',
+               array
+               (
+                       'vip' => "inet_aton('${vip}')",
+                       'vport' => $vport,
+                       'proto' => "'${proto}'",
+                       'name' => (empty ($name) ? 'NULL' : "'${name}'"),
+                       'vsconfig' => (empty ($vsconfig) ? 'NULL' : "'${vsconfig}'"),
+                       'rsconfig' => (empty ($rsconfig) ? 'NULL' : "'${rsconfig}'")
+               )
+       );
+}
+
+function addLBtoRSPool ($pool_id = 0, $object_id = 0, $vs_id = 0, $vsconfig = '', $rsconfig = '')
+{
+       if ($pool_id <= 0 or $object_id <= 0 or $vs_id <= 0)
+       {
+               showError ('Invalid arguments', __FUNCTION__);
+               die;
+       }
+       return useInsertBlade
+       (
+               'IPLoadBalancer',
+               array
+               (
+                       'object_id' => $object_id,
+                       'rspool_id' => $pool_id,
+                       'vs_id' => $vs_id,
+                       'vsconfig' => (empty ($vsconfig) ? 'NULL' : "'${vsconfig}'"),
+                       'rsconfig' => (empty ($rsconfig) ? 'NULL' : "'${rsconfig}'")
+               )
+       );
+}
+
+function commitDeleteRS ($id = 0)
+{
+       if ($id <= 0)
+               return FALSE;
+       return useDeleteBlade ('IPRealServer', 'id', $id);
+}
+
+function commitDeleteVS ($id = 0)
+{
+       if ($id <= 0)
+               return FALSE;
+       return useDeleteBlade ('IPVirtualService', 'id', $id);
+}
+
+function commitDeleteLB ($object_id = 0, $pool_id = 0, $vs_id = 0)
+{
+       global $dbxlink;
+       if ($object_id <= 0 or $pool_id <= 0 or $vs_id <= 0)
+               return FALSE;
+       $query = "delete from IPLoadBalancer where object_id = ${object_id} and " .
+               "rspool_id = ${pool_id} and vs_id = ${vs_id} limit 1";
+       $result = $dbxlink->exec ($query);
+       if ($result === NULL)
+               return FALSE;
+       elseif ($result != 1)
+               return FALSE;
+       else
+               return TRUE;
+}
+
+function commitUpdateRS ($rsid = 0, $rsip = '', $rsport = 0, $rsconfig = '')
+{
+       if ($rsid <= 0 or $rsport <= 0)
+       {
+               showError ('Invalid args', __FUNCTION__);
+               die;
+       }
+       if (long2ip (ip2long ($rsip)) !== $rsip)
+       {
+               showError ("Invalid IP address '${rsip}'", __FUNCTION__);
+               die;
+       }
+       global $dbxlink;
+       $query =
+               "update IPRealServer set rsip = inet_aton('${rsip}'), rsport = ${rsport}, rsconfig = " .
+               (empty ($rsconfig) ? 'NULL' : "'${rsconfig}'") .
+               " where id = ${rsid} limit 1";
+       $result = $dbxlink->query ($query);
+       if ($result == NULL)
+       {
+               showError ("SQL query '${query}' failed", __FUNCTION__);
+               die;
+       }
+       return TRUE;
+}
+
+function commitUpdateLB ($object_id = 0, $pool_id = 0, $vs_id = 0, $vsconfig = '', $rsconfig = '')
+{
+       if ($object_id <= 0 or $pool_id <= 0 or $vs_id <= 0)
+       {
+               showError ('Invalid args', __FUNCTION__);
+               die;
+       }
+       global $dbxlink;
+       $query =
+               "update IPLoadBalancer set vsconfig = " .
+               (empty ($vsconfig) ? 'NULL' : "'${vsconfig}'") .
+               ', rsconfig = ' .
+               (empty ($rsconfig) ? 'NULL' : "'${rsconfig}'") .
+               " where object_id = ${object_id} and rspool_id = ${pool_id} " .
+               "and vs_id = ${vs_id} limit 1";
+       $result = $dbxlink->exec ($query);
+       if ($result === NULL)
+               return FALSE;
+       else
+               return TRUE;
+}
+
+function commitUpdateVS ($vsid = 0, $vip = '', $vport = 0, $proto = '', $name = '', $vsconfig = '', $rsconfig = '')
+{
+       if ($vsid <= 0 or empty ($vip) or $vport <= 0 or empty ($proto))
+       {
+               showError ('Invalid args', __FUNCTION__);
+               die;
+       }
+       global $dbxlink;
+       $query = "update IPVirtualService set " .
+               "vip = inet_aton('${vip}'), " .
+               "vport = ${vport}, " .
+               "proto = '${proto}', " .
+               'name = ' . (empty ($name) ? 'NULL,' : "'${name}', ") .
+               'vsconfig = ' . (empty ($vsconfig) ? 'NULL,' : "'${vsconfig}', ") .
+               'rsconfig = ' . (empty ($rsconfig) ? 'NULL' : "'${rsconfig}'") .
+               " where id = ${vsid} limit 1";
+       $result = $dbxlink->exec ($query);
+       if ($result === NULL)
+               return FALSE;
+       else
+               return TRUE;
+}
+
+// Return the list of virtual services, indexed by vs_id.
+// Each record will be shown with its basic info plus RS pools counter.
+function getVSList ()
+{
+       global $dbxlink;
+       $query = "select vs.id, inet_ntoa(vip) as vip, vport, proto, vs.name, vs.vsconfig, vs.rsconfig, count(rspool_id) as poolcount " .
+               "from IPVirtualService as vs left join IPLoadBalancer as lb on vs.id = lb.vs_id " .
+               "group by vs.id order by vs.vip, proto, vport";
+       $result = $dbxlink->query ($query);
+       if ($result == NULL)
+       {
+               showError ('SQL query failed', __FUNCTION__);
+               return NULL;
+       }
+       $ret = array ();
+       while ($row = $result->fetch (PDO::FETCH_ASSOC))
+               foreach (array ('vip', 'vport', 'proto', 'name', 'vsconfig', 'rsconfig', 'poolcount') as $cname)
+                       $ret[$row['id']][$cname] = $row[$cname];
+       $result->closeCursor();
+       return $ret;
+}
+
+// Return the list of RS pool, indexed by pool id.
+function getRSPoolList ()
+{
+       global $dbxlink;
+       $query = "select pool.id, pool.name, count(rspool_id) as refcnt, pool.vsconfig, pool.rsconfig " .
+               "from IPRSPool as pool left join IPLoadBalancer as lb on pool.id = lb.rspool_id " .
+               "group by pool.id order by pool.id, name";
+       $result = $dbxlink->query ($query);
+       if ($result == NULL)
+       {
+               showError ('SQL query failed', __FUNCTION__);
+               return NULL;
+       }
+       $ret = array ();
+       while ($row = $result->fetch (PDO::FETCH_ASSOC))
+               foreach (array ('name', 'refcnt', 'vsconfig', 'rsconfig') as $cname)
+                       $ret[$row['id']][$cname] = $row[$cname];
+       $result->closeCursor();
+       return $ret;
+}
+
+function loadThumbCache ($rack_id = 0)
+{
+       global $dbxlink;
+       $ret = NULL;
+       $query = "select thumb_data from Rack where id = ${rack_id} and thumb_data is not null limit 1";
+       $result = $dbxlink->query ($query);
+       if ($result == NULL)
+       {
+               showError ('SQL query failed', __FUNCTION__);
+               return NULL;
+       }
+       $row = $result->fetch (PDO::FETCH_ASSOC);
+       if ($row)
+               $ret = base64_decode ($row['thumb_data']);
+       $result->closeCursor();
+       return $ret;
+}
+
+function saveThumbCache ($rack_id = 0, $cache = NULL)
+{
+       global $dbxlink;
+       if ($rack_id == 0 or $cache == NULL)
+       {
+               showError ('Invalid arguments', __FUNCTION__);
+               return;
+       }
+       $data = base64_encode ($cache);
+       $query = "update Rack set thumb_data = '${data}' where id = ${rack_id} limit 1";
+       $result = $dbxlink->exec ($query);
+}
+
+function resetThumbCache ($rack_id = 0)
+{
+       global $dbxlink;
+       if ($rack_id == 0)
+       {
+               showError ('Invalid argument', __FUNCTION__);
+               return;
+       }
+       $query = "update Rack set thumb_data = NULL where id = ${rack_id} limit 1";
+       $result = $dbxlink->exec ($query);
+}
+
+// Return the list of attached RS pools for the given object. As long as we have
+// the LB-VS UNIQUE in IPLoadBalancer table, it is Ok to key returned records
+// by vs_id, because there will be only one RS pool listed for each VS of the
+// current object.
+function getRSPoolsForObject ($object_id = 0)
+{
+       if ($object_id <= 0)
+       {
+               showError ('Invalid object_id', __FUNCTION__);
+               return NULL;
+       }
+       global $dbxlink;
+       $query = 'select vs_id, inet_ntoa(vip) as vip, vport, proto, vs.name, pool.id as pool_id, ' .
+               'pool.name as pool_name, count(rsip) as rscount, lb.vsconfig, lb.rsconfig from ' .
+               'IPLoadBalancer as lb inner join IPRSPool as pool on lb.rspool_id = pool.id ' .
+               'inner join IPVirtualService as vs on lb.vs_id = vs.id ' .
+               'left join IPRealServer as rs on lb.rspool_id = rs.rspool_id ' .
+               "where lb.object_id = ${object_id} " .
+               'group by lb.rspool_id, lb.vs_id order by vs.vip, vport, proto, pool.name';
+       $result = $dbxlink->query ($query);
+       if ($result == NULL)
+       {
+               showError ('SQL query failed', __FUNCTION__);
+               return NULL;
+       }
+       $ret = array ();
+       while ($row = $result->fetch (PDO::FETCH_ASSOC))
+               foreach (array ('vip', 'vport', 'proto', 'name', 'pool_id', 'pool_name', 'rscount', 'vsconfig', 'rsconfig') as $cname)
+                       $ret[$row['vs_id']][$cname] = $row[$cname];
+       $result->closeCursor();
+       return $ret;
+}
+
+function commitCreateRSPool ($name = '', $vsconfig = '', $rsconfig = '')
+{
+       return useInsertBlade
+       (
+               'IPRSPool',
+               array
+               (
+                       'name' => (empty ($name) ? 'NULL' : "'${name}'"),
+                       'vsconfig' => (empty ($vsconfig) ? 'NULL' : "'${vsconfig}'"),
+                       'rsconfig' => (empty ($rsconfig) ? 'NULL' : "'${rsconfig}'")
+               )
+       );
+}
+
+function commitDeleteRSPool ($pool_id = 0)
+{
+       global $dbxlink;
+       if ($pool_id <= 0)
+               return FALSE;
+       $query = "delete from IPRSPool where id = ${pool_id} limit 1";
+       $result = $dbxlink->exec ($query);
+       if ($result === NULL)
+               return FALSE;
+       elseif ($result != 1)
+               return FALSE;
+       else
+               return TRUE;
+}
+
+function commitUpdateRSPool ($pool_id = 0, $name = '', $vsconfig = '', $rsconfig = '')
+{
+       if ($pool_id <= 0)
+       {
+               showError ('Invalid arg', __FUNCTION__);
+               die;
+       }
+       global $dbxlink;
+       $query = "update IPRSPool set " .
+               'name = ' . (empty ($name) ? 'NULL,' : "'${name}', ") .
+               'vsconfig = ' . (empty ($vsconfig) ? 'NULL,' : "'${vsconfig}', ") .
+               'rsconfig = ' . (empty ($rsconfig) ? 'NULL' : "'${rsconfig}'") .
+               " where id = ${pool_id} limit 1";
+       $result = $dbxlink->exec ($query);
+       if ($result === NULL)
+               return FALSE;
+       elseif ($result != 1)
+               return FALSE;
+       else
+               return TRUE;
+}
+
+function getRSList ()
+{
+       global $dbxlink;
+       $query = "select id, inservice, inet_ntoa(rsip) as rsip, rsport, rspool_id, rsconfig " .
+               "from IPRealServer order by rspool_id, IPRealServer.rsip, rsport";
+       $result = $dbxlink->query ($query);
+       if ($result == NULL)
+       {
+               showError ('SQL query failed', __FUNCTION__);
+               return NULL;
+       }
+       $ret = array ();
+       while ($row = $result->fetch (PDO::FETCH_ASSOC))
+               foreach (array ('inservice', 'rsip', 'rsport', 'rspool_id', 'rsconfig') as $cname)
+                       $ret[$row['id']][$cname] = $row[$cname];
+       $result->closeCursor();
+       return $ret;
+}
+
+// Return the list of all currently configured load balancers with their pool count.
+function getLBList ()
+{
+       global $dbxlink;
+       $query = "select object_id, count(rspool_id) as poolcount " .
+               "from IPLoadBalancer group by object_id order by object_id";
+       $result = $dbxlink->query ($query);
+       if ($result == NULL)
+       {
+               showError ('SQL query failed', __FUNCTION__);
+               return NULL;
+       }
+       $ret = array ();
+       while ($row = $result->fetch (PDO::FETCH_ASSOC))
+               $ret[$row['object_id']] = $row['poolcount'];
+       $result->closeCursor();
+       return $ret;
+}
+
+// For the given object return: it vsconfig/rsconfig; the list of RS pools
+// attached (each with vsconfig/rsconfig in turn), each with the list of
+// virtual services terminating the pool. Each pool also lists all real
+// servers with rsconfig.
+function buildLBConfig ($object_id)
+{
+       if ($object_id <= 0)
+       {
+               showError ('Invalid arg', __FUNCTION__);
+               return NULL;
+       }
+       global $dbxlink;
+       $ret = array();
+       $query = 'select vs_id, inet_ntoa(vip) as vip, vport, proto, vs.name as vs_name, ' .
+               'vs.vsconfig as vs_vsconfig, vs.rsconfig as vs_rsconfig, ' .
+               'lb.vsconfig as lb_vsconfig, lb.rsconfig as lb_rsconfig, pool.id as pool_id, pool.name as pool_name, ' .
+               'pool.vsconfig as pool_vsconfig, pool.rsconfig as pool_rsconfig, ' .
+               'rs.id as rs_id, inet_ntoa(rsip) as rsip, rsport, rs.rsconfig as rs_rsconfig from ' .
+               'IPLoadBalancer as lb inner join IPRSPool as pool on lb.rspool_id = pool.id ' .
+               'inner join IPVirtualService as vs on lb.vs_id = vs.id ' .
+               'inner join IPRealServer as rs on lb.rspool_id = rs.rspool_id ' .
+               "where lb.object_id = ${object_id} and rs.inservice = 'yes' " .
+               "order by vs.vip, vport, proto, pool.name, rs.rsip, rs.rsport";
+       $result = $dbxlink->query ($query);
+       if ($result == NULL)
+       {
+               showError ('SQL query failed', __FUNCTION__);
+               return NULL;
+       }
+       while ($row = $result->fetch (PDO::FETCH_ASSOC))
+       {
+               $vs_id = $row['vs_id'];
+               if (!isset ($ret[$vs_id]))
+               {
+                       foreach (array ('vip', 'vport', 'proto', 'vs_name', 'vs_vsconfig', 'vs_rsconfig', 'lb_vsconfig', 'lb_rsconfig', 'pool_vsconfig', 'pool_rsconfig', 'pool_id', 'pool_name') as $c)
+                               $ret[$vs_id][$c] = $row[$c];
+                       $ret[$vs_id]['rslist'] = array();
+               }
+               foreach (array ('rsip', 'rsport', 'rs_rsconfig') as $c)
+                       $ret[$vs_id]['rslist'][$row['rs_id']][$c] = $row[$c];
+       }
+       $result->closeCursor();
+       return $ret;
+}
+
+function commitSetInService ($rs_id = 0, $inservice = '')
+{
+       if ($rs_id <= 0 or empty ($inservice))
+       {
+               showError ('Invalid args', __FUNCTION__);
+               return NULL;
+       }
+       global $dbxlink;
+       $query = "update IPRealServer set inservice = '${inservice}' where id = ${rs_id} limit 1";
+       $result = $dbxlink->exec ($query);
+       if ($result === NULL)
+               return FALSE;
+       elseif ($result != 1)
+               return FALSE;
+       else
+               return TRUE;
+}
+
+function executeAutoPorts ($object_id = 0, $type_id = 0)
+{
+       if ($object_id == 0 or $type_id == 0)
+       {
+               showError ('Invalid arguments', __FUNCTION__);
+               die;
+       }
+       $ret = TRUE;
+       foreach (getAutoPorts ($type_id) as $autoport)
+               $ret = $ret and '' == commitAddPort ($object_id, $autoport['name'], $autoport['type'], '', '');
+       return $ret;
+}
+
 ?>