quote (htmlentities ($value)), 1, -1); } // This function returns detailed information about either all or one // rack row depending on its argument. function getRackRowInfo ($rackrow_id = 0) { global $dbxlink; $query = "select dict_key, dict_value, count(Rack.id) as count, " . "if(isnull(sum(Rack.height)),0,sum(Rack.height)) as sum " . "from Chapter natural join Dictionary left join Rack on Rack.row_id = dict_key " . "where chapter_name = 'RackRow' " . ($rackrow_id > 0 ? "and dict_key = ${rackrow_id} " : '') . "group by dict_key order by dict_value"; $result = $dbxlink->query ($query); if ($result == NULL) { showError ('SQL query failed', __FUNCTION__); return NULL; } $ret = array(); $clist = array ('dict_key', 'dict_value', 'count', 'sum'); while ($row = $result->fetch (PDO::FETCH_ASSOC)) foreach ($clist as $dummy => $cname) $ret[$row['dict_key']][$cname] = $row[$cname]; $result->closeCursor(); if ($rackrow_id > 0) return current ($ret); else return $ret; } // This function returns id->name map for all object types. The map is used // to build drop-downs. function readChapter ($chapter_name = '') { if (empty ($chapter_name)) { showError ('invalid argument', __FUNCTION__); return NULL; } global $dbxlink; $query = "select dict_key, dict_value from Dictionary natural join Chapter " . "where chapter_name = '${chapter_name}'"; $result = $dbxlink->query ($query); if ($result == NULL) { $errorInfo = $dbxlink->errorInfo(); 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['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; } function getAttrMap () { global $dbxlink; $query = "select a.attr_id, a.attr_type, a.attr_name, am.objtype_id, " . "d.dict_value as objtype_name, am.chapter_no, c2.chapter_name from " . "Attribute as a natural left join AttributeMap as am " . "left join Dictionary as d on am.objtype_id = d.dict_key " . "left join Chapter as c1 on d.chapter_no = c1.chapter_no " . "left join Chapter as c2 on am.chapter_no = c2.chapter_no " . "where c1.chapter_name = 'RackObjectType' or c1.chapter_name is null " . "order by attr_name"; $result = $dbxlink->query ($query); if ($result == NULL) { $errorInfo = $dbxlink->errorInfo(); showError ("SQL query '${query}'\nwith message '${errorInfo[2]}'\nfailed", __FUNCTION__); return NULL; } $ret = array(); while ($row = $result->fetch (PDO::FETCH_ASSOC)) { $attr_id = $row['attr_id']; if (!isset ($ret[$attr_id])) { $ret[$attr_id]['id'] = $attr_id; $ret[$attr_id]['type'] = $row['attr_type']; $ret[$attr_id]['name'] = $row['attr_name']; $ret[$attr_id]['application'] = array(); } if ($row['objtype_id'] == '') continue; $application['objtype_id'] = $row['objtype_id']; $application['objtype_name'] = $row['objtype_name']; if ($row['attr_type'] == 'dict') { $application['chapter_no'] = $row['chapter_no']; $application['chapter_name'] = $row['chapter_name']; } $ret[$attr_id]['application'][] = $application; } $result->closeCursor(); return $ret; } function commitUpdateAttribute ($attr_id = 0, $attr_name = '') { if ($attr_id <= 0 or empty ($attr_name)) { showError ('Invalid args', __FUNCTION__); die; } global $dbxlink; $query = "update Attribute set attr_name = '${attr_name}' " . "where attr_id = ${attr_id} limit 1"; $result = $dbxlink->query ($query); if ($result == NULL) { showError ("SQL query '${query}' failed", __FUNCTION__); die; } return TRUE; } function commitAddAttribute ($attr_name = '', $attr_type = '') { if (empty ($attr_name)) { showError ('Invalid args', __FUNCTION__); die; } switch ($attr_type) { case 'uint': case 'float': case 'string': case 'dict': break; default: showError ('Invalid args', __FUNCTION__); die; } return useInsertBlade ( 'Attribute', array ('attr_name' => "'${attr_name}'", 'attr_type' => "'${attr_type}'") ); } function commitDeleteAttribute ($attr_id = 0) { if ($attr_id <= 0) { showError ('Invalid args', __FUNCTION__); die; } return useDeleteBlade ('Attribute', 'attr_id', $attr_id); } // FIXME: don't store garbage in chapter_no for non-dictionary types. 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', __FUNCTION__); die; } return useInsertBlade ( 'AttributeMap', array ( 'attr_id' => $attr_id, 'objtype_id' => $objtype_id, 'chapter_no' => $chapter_no ) ); } function commitReduceAttrMap ($attr_id = 0, $objtype_id) { if ($attr_id <= 0 or $objtype_id <= 0) { showError ('Invalid args', __FUNCTION__); die; } global $dbxlink; $query = "delete from AttributeMap where attr_id=${attr_id} " . "and objtype_id=${objtype_id} limit 1"; $result = $dbxlink->query ($query); if ($result == NULL) { showError ('SQL query failed', __FUNCTION__); die; } return TRUE; } // This function returns all optional attributes for requested object // as an array of records. NULL is returned on error and empty array // is returned, if there are no attributes found. function getAttrValues ($object_id) { if ($object_id <= 0) { showError ('Invalid argument', __FUNCTION__); return NULL; } global $dbxlink; $ret = array(); $query = "select A.attr_id, A.attr_name, A.attr_type, C.chapter_name, " . "AV.uint_value, AV.float_value, AV.string_value, D.dict_value from " . "RackObject as RO inner join AttributeMap as AM on RO.objtype_id = AM.objtype_id " . "inner join Attribute as A using (attr_id) " . "left join AttributeValue as AV on AV.attr_id = AM.attr_id and AV.object_id = RO.id " . "left join Dictionary as D on D.dict_key = AV.uint_value and AM.chapter_no = D.chapter_no " . "left join Chapter as C on AM.chapter_no = C.chapter_no " . "where RO.id = ${object_id} order by A.attr_type"; $result = $dbxlink->query ($query); if ($result == NULL) { $errorInfo = $dbxlink->errorInfo(); showError ("SQL query '${query}'\nwith message '${errorInfo[2]}'\nfailed", __FUNCTION__); return NULL; } while ($row = $result->fetch (PDO::FETCH_ASSOC)) { $record = array(); $record['id'] = $row['attr_id']; $record['name'] = $row['attr_name']; $record['type'] = $row['attr_type']; switch ($row['attr_type']) { case 'uint': case 'float': case 'string': case 'dict': $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; default: $record['value'] = NULL; break; } $ret[$row['attr_id']] = $record; } $result->closeCursor(); return $ret; } function commitResetAttrValue ($object_id = 0, $attr_id = 0) { if ($object_id <= 0 or $attr_id <= 0) { showError ('Invalid arguments', __FUNCTION__); die; } global $dbxlink; $query = "delete from AttributeValue where object_id = ${object_id} and attr_id = ${attr_id} limit 1"; $result = $dbxlink->query ($query); if ($result == NULL) { showError ('SQL query failed', __FUNCTION__); die; } return TRUE; } // FIXME: don't share common code with use commitResetAttrValue() function commitUpdateAttrValue ($object_id = 0, $attr_id = 0, $value = '') { if ($object_id <= 0 or $attr_id <= 0) { showError ('Invalid arguments', __FUNCTION__); die; } if (empty ($value)) return commitResetAttrValue ($object_id, $attr_id); global $dbxlink; $query1 = "select attr_type from Attribute where attr_id = ${attr_id}"; $result = $dbxlink->query ($query1); if ($result == NULL) { showError ('SQL query #1 failed', __FUNCTION__); die; } $row = $result->fetch (PDO::FETCH_NUM); if ($row == NULL) { showError ('SQL query #1 returned no results', __FUNCTION__); die; } $attr_type = $row[0]; $result->closeCursor(); switch ($attr_type) { case 'uint': case 'float': case 'string': $column = $attr_type . '_value'; break; case 'dict': $column = 'uint_value'; break; default: showError ("Unknown attribute type '${attr_type}' met", __FUNCTION__); die; } $query2 = "delete from AttributeValue where " . "object_id = ${object_id} and attr_id = ${attr_id} limit 1"; $result = $dbxlink->query ($query2); if ($result == NULL) { showError ('SQL query #2 failed', __FUNCTION__); die; } // We know $value isn't empty here. $query3 = "insert into AttributeValue set ${column} = '${value}', " . "object_id = ${object_id}, attr_id = ${attr_id} "; $result = $dbxlink->query ($query3); if ($result == NULL) { showError ('SQL query #3 failed', __FUNCTION__); die; } return TRUE; } function commitUseupPort ($port_id = 0) { if ($port_id <= 0) { showError ("Invalid argument", __FUNCTION__); die; } global $dbxlink; $query = "update Port set reservation_comment = NULL where id = ${port_id} limit 1"; $result = $dbxlink->exec ($query); if ($result == NULL) { showError ("SQL query failed", __FUNCTION__); die; } 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. // The function returns either TRUE or FALSE (we expect one row // to be inserted). function useInsertBlade ($tablename, $values) { global $dbxlink; $namelist = $valuelist = ''; foreach ($values as $name => $value) { $namelist = $namelist . ($namelist == '' ? "(${name}" : ", ${name}"); $valuelist = $valuelist . ($valuelist == '' ? "(${value}" : ", ${value}"); } $query = "insert into ${tablename} ${namelist}) values ${valuelist})"; $result = $dbxlink->exec ($query); if ($result != 1) return FALSE; return TRUE; } // This swiss-knife blade deletes one record from the specified table // using the specified key name and value. function useDeleteBlade ($tablename, $keyname, $keyvalue, $quotekey = TRUE) { global $dbxlink; if ($quotekey == TRUE) $query = "delete from ${tablename} where ${keyname}='$keyvalue' limit 1"; else $query = "delete from ${tablename} where ${keyname}=$keyvalue limit 1"; $result = $dbxlink->exec ($query); if ($result === NULL) return FALSE; elseif ($result != 1) return FALSE; else return TRUE; } function loadConfigCache () { global $dbxlink; $query = 'SELECT varname, varvalue, vartype, is_hidden, emptyok, description FROM Config ORDER BY varname'; $result = $dbxlink->query ($query); if ($result == NULL) { $errorInfo = $dbxlink->errorInfo(); showError ("SQL query '${query}'\nwith message '${errorInfo[2]}'\nfailed", __FUNCTION__); return NULL; } $cache = array(); while ($row = $result->fetch (PDO::FETCH_ASSOC)) $cache[$row['varname']] = $row; $result->closeCursor(); return $cache; } // setConfigVar() is expected to perform all necessary filtering function storeConfigVar ($varname = NULL, $varvalue = NULL) { global $dbxlink; if (empty ($varname) || $varvalue === NULL) { 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", __FUNCTION__); return FALSE; } $rc = $result->rowCount(); $result->closeCursor(); if ($rc == 0 or $rc == 1) return TRUE; showError ("Something went wrong for args '${varname}', '${varvalue}'", __FUNCTION__); return FALSE; } // Database version detector. Should behave corretly on any // working dataset a user might have. function getDatabaseVersion () { global $dbxlink; $query = "select varvalue from Config where varname = 'DB_VERSION' and vartype = 'string'"; $result = $dbxlink->query ($query); if ($result == NULL) { $errorInfo = $dbxlink->errorInfo(); if ($errorInfo[0] == '42S02') // ER_NO_SUCH_TABLE return '0.14.4'; 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 (__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, rsp.id as pool_id, rsp.name as pool_name, object_id, count(rs.id) as rscount from ' . 'IPVirtualService as vs inner join IPRSPool as rsp on vs.id = rsp.vs_id ' . 'inner join IPRealServer as rs on rs.rspool_id = rsp.id ' . 'inner join IPLoadBalancer as lb on rsp.id = lb.rspool_id ' . 'group by rsp.id, object_id order by 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(); } $ret[$vsid]['lblist'][$row['object_id']][$row['pool_id']] = array ('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 // real servers list function getRSPoolInfo ($id = 0) { global $dbxlink; $query1 = "select ${id} as id, vs_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', 'vs_id') as $c) $ret[$c] = $row[$c]; $result1->closeCursor(); $ret['lblist'] = array(); $ret['rslist'] = array(); $query2 = "select object_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']][$c] = $row[$c]; $result2->closeCursor(); $query3 = "select id, inet_ntoa(rsip) as rsip, rsport, rsconfig from IPRealServer where rspool_id = ${id} order by 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 ('rsip', 'rsport', 'rsconfig') as $c) $ret['rslist'][$row['id']][$c] = $row[$c]; $result3->closeCursor(); return $ret; } function addRStoRSPool ($pool_id = 0, $rsip = '', $rsport = 0, $rsconfig = '') { if ($pool_id <= 0 or $rsport <= 0) { showError ('Invalid arguments', __FUNCTION__); die; } if (long2ip (ip2long ($rsip)) !== $rsip) { showError ("Invalid IP address '${rsip}'", __FUNCTION__); die; } return useInsertBlade ( 'IPRealServer', array ( 'rsip' => "inet_aton('${rsip}')", 'rsport' => $rsport, 'rspool_id' => $pool_id, 'rsconfig' => (empty ($rsconfig) ? 'NULL' : "'${rsconfig}'")) ); } function commitDeleteRS ($id = 0) { if ($id <= 0) return FALSE; return useDeleteBlade ('IPRealServer', 'id', $id); } 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; } // Return the list of virtual services, indexed by vs_id. function getVSList () { global $dbxlink; $query = "select id, inet_ntoa(vip) as vip, vport, proto, name, vsconfig, rsconfig " . "from IPVirtualService order by vip, vport, proto"; $result = $dbxlink->query ($query); if ($result == NULL) { showError ('SQL query failed', __FUNCTION__); return NULL; } $vslist = array (); while ($row = $result->fetch (PDO::FETCH_ASSOC)) foreach (array ('vip', 'vport', 'proto', 'name', 'vsconfig', 'rsconfig') as $cname) $vslist[$row['id']][$cname] = $row[$cname]; $result->closeCursor(); return $vslist; } // Return the list of RS pool, indexed by pool id. function getRSPoolList () { global $dbxlink; $query = "select id, vs_id, name, vsconfig, rsconfig from IPRSPool order by vs_id, name"; $result = $dbxlink->query ($query); if ($result == NULL) { showError ('SQL query failed', __FUNCTION__); return NULL; } $pool_list = array (); while ($row = $result->fetch (PDO::FETCH_ASSOC)) foreach (array ('vs_id', 'name', 'vsconfig', 'rsconfig') as $cname) $pool_list[$row['id']][$cname] = $row[$cname]; $result->closeCursor(); return $pool_list; } ?>