query("CREATE TABLE `innodb_test` (`id` int) ENGINE=InnoDB"); $row = $dbxlink->query("SHOW TABLE STATUS LIKE 'innodb_test'")->fetch(PDO::FETCH_ASSOC); $dbxlink->query("DROP TABLE `innodb_test`"); if ($row['Engine'] == 'InnoDB') return TRUE; return FALSE; } function escapeString ($value, $do_db_escape = TRUE) { $ret = htmlspecialchars ($value, ENT_QUOTES, 'UTF-8'); if ($do_db_escape) { global $dbxlink; $ret = substr ($dbxlink->quote ($ret), 1, -1); } return $ret; } function getRackspace ($tagfilter = array(), $tfmode = 'any') { $whereclause = getWhereClause ($tagfilter); $query = "select dict_key as row_id, dict_value as row_name " . "from Chapter natural join Dictionary left join Rack on Rack.row_id = dict_key " . "left join TagStorage on Rack.id = TagStorage.target_id and target_realm = 'rack' " . "where chapter_name = 'RackRow' " . $whereclause . " order by dict_value"; $result = useSelectBlade ($query, __FUNCTION__); $ret = array(); $clist = array ('row_id', 'row_name'); while ($row = $result->fetch (PDO::FETCH_ASSOC)) foreach ($clist as $cname) $ret[$row['row_id']][$cname] = $row[$cname]; $result->closeCursor(); return $ret; } // Return detailed information about one rack row. function getRackRowInfo ($rackrow_id) { $query = "select dict_key as id, dict_value as name, 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' and dict_key = ${rackrow_id} " . "group by dict_key"; $result = useSelectBlade ($query, __FUNCTION__); if ($row = $result->fetch (PDO::FETCH_ASSOC)) return $row; else return NULL; } // 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; } $query = "select dict_key, dict_value from Dictionary natural join Chapter " . "where chapter_name = '${chapter_name}'"; $result = useSelectBlade ($query, __FUNCTION__); $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 () { $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 = useSelectBlade ($query, __FUNCTION__); $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, $strip_optgroup = FALSE) { if ($object_id <= 0) { showError ('Invalid argument', __FUNCTION__); return NULL; } $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, A.attr_name"; $result = useSelectBlade ($query, __FUNCTION__); 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': $record['value'] = $row[$row['attr_type'] . '_value']; $record['a_value'] = parseWikiLink ($record['value'], 'a'); break; case 'dict': $record['value'] = parseWikiLink ($row[$row['attr_type'] . '_value'], 'o', $strip_optgroup); $record['a_value'] = parseWikiLink ($row[$row['attr_type'] . '_value'], 'a', $strip_optgroup); $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) { global $dbxlink; return 1 === $dbxlink->exec ("delete from ${tablename} where ${keyname}=${keyvalue} limit 1"); } function useSelectBlade ($query, $caller = 'N/A') { global $dbxlink; $result = $dbxlink->query ($query); if ($result == NULL) { $ei = $dbxlink->errorInfo(); showError ("SQL query '${query}'\n failed in useSelectBlade with error ${ei[1]} (${ei[2]})", $caller); return NULL; } return $result; } function loadConfigCache () { $query = 'SELECT varname, varvalue, vartype, is_hidden, emptyok, description FROM Config ORDER BY varname'; $result = useSelectBlade ($query, __FUNCTION__); $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 () { $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 = useSelectBlade ($query, __FUNCTION__); $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) { $query1 = "select inet_ntoa(vip) as vip, vport, proto, name, vsconfig, rsconfig " . "from IPVirtualService where id = ${vsid}"; $result = useSelectBlade ($query1, __FUNCTION__); $vsinfo = array (); $row = $result->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(); $result->closeCursor(); unset ($result); $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"; $result = useSelectBlade ($query2, __FUNCTION__); while ($row = $result->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'] ); } $result->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) { $query1 = "select id, name, vsconfig, rsconfig from " . "IPRSPool where id = ${id}"; $result = useSelectBlade ($query1, __FUNCTION__); $ret = array(); $row = $result->fetch (PDO::FETCH_ASSOC); if (!$row) return NULL; foreach (array ('id', 'name', 'vsconfig', 'rsconfig') as $c) $ret[$c] = $row[$c]; $result->closeCursor(); unset ($result); $ret['lblist'] = array(); $ret['rslist'] = array(); $query2 = "select object_id, vs_id, lb.vsconfig, lb.rsconfig from " . "IPLoadBalancer as lb inner join IPVirtualService as vs on lb.vs_id = vs.id " . "where rspool_id = ${id} order by object_id, vip, vport"; $result = useSelectBlade ($query2, __FUNCTION__); while ($row = $result->fetch (PDO::FETCH_ASSOC)) foreach (array ('vsconfig', 'rsconfig') as $c) $ret['lblist'][$row['object_id']][$row['vs_id']][$c] = $row[$c]; $result->closeCursor(); unset ($result); $query3 = "select id, inservice, inet_ntoa(rsip) as rsip, rsport, rsconfig from " . "IPRealServer where rspool_id = ${id} order by IPRealServer.rsip, rsport"; $result = useSelectBlade ($query3, __FUNCTION__); while ($row = $result->fetch (PDO::FETCH_ASSOC)) foreach (array ('inservice', 'rsip', 'rsport', 'rsconfig') as $c) $ret['rslist'][$row['id']][$c] = $row[$c]; $result->closeCursor(); return $ret; } function addRStoRSPool ($pool_id = 0, $rsip = '', $rsport = 0, $inservice = 'no', $rsconfig = '') { if ($pool_id <= 0) { showError ('Invalid arguments', __FUNCTION__); die; } if (empty ($rsport) or $rsport == 0) $rsport = 'NULL'; 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, $taglist = array()) { if (empty ($vip) or $vport <= 0 or empty ($proto)) return __FUNCTION__ . ': invalid arguments'; if (!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}'") ) )) return __FUNCTION__ . ': SQL insertion failed'; return produceTagsForLastRecord ('ipv4vs', $taglist); } 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) && destroyTagsForEntity ('ipv4vs', $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) { showError ('Invalid args', __FUNCTION__); die; } if (long2ip (ip2long ($rsip)) !== $rsip) { showError ("Invalid IP address '${rsip}'", __FUNCTION__); die; } if (empty ($rsport) or $rsport == 0) $rsport = 'NULL'; 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 ($tagfilter = array(), $tfmode = 'any') { $whereclause = getWhereClause ($tagfilter); $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 " . "left join TagStorage on vs.id = TagStorage.target_id and target_realm = 'ipv4vs' " . "where true ${whereclause} group by vs.id order by vs.vip, proto, vport"; $result = useSelectBlade ($query, __FUNCTION__); $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 ($tagfilter = array(), $tfmode = 'any') { $whereclause = getWhereClause ($tagfilter); $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 " . "left join TagStorage on pool.id = TagStorage.target_id and target_realm = 'ipv4rspool' " . "where true ${whereclause} group by pool.id order by pool.name, pool.id"; $result = useSelectBlade ($query, __FUNCTION__); $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) { $ret = NULL; $query = "select thumb_data from Rack where id = ${rack_id} and thumb_data is not null limit 1"; $result = useSelectBlade ($query, __FUNCTION__); $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; } $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 = useSelectBlade ($query, __FUNCTION__); $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 = '', $taglist = array()) { if (empty ($name)) return __FUNCTION__ . ': invalid arguments'; if (!useInsertBlade ( 'IPRSPool', array ( 'name' => (empty ($name) ? 'NULL' : "'${name}'"), 'vsconfig' => (empty ($vsconfig) ? 'NULL' : "'${vsconfig}'"), 'rsconfig' => (empty ($rsconfig) ? 'NULL' : "'${rsconfig}'") ) )) return __FUNCTION__ . ': SQL insertion failed'; return produceTagsForLastRecord ('ipv4rspool', $taglist); } function commitDeleteRSPool ($pool_id = 0) { global $dbxlink; if ($pool_id <= 0) return FALSE; return useDeleteBlade ('IPRSPool', 'id', $pool_id) && destroyTagsForEntity ('ipv4rspool', $pool_id); } 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 () { $query = "select id, inservice, inet_ntoa(rsip) as rsip, rsport, rspool_id, rsconfig " . "from IPRealServer order by rspool_id, IPRealServer.rsip, rsport"; $result = useSelectBlade ($query, __FUNCTION__); $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 () { $query = "select object_id, count(rspool_id) as poolcount " . "from IPLoadBalancer group by object_id order by object_id"; $result = useSelectBlade ($query, __FUNCTION__); $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 getSLBConfig ($object_id) { if ($object_id <= 0) { showError ('Invalid arg', __FUNCTION__); return NULL; } $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 = useSelectBlade ($query, __FUNCTION__); 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; } // Return only implicitly listed tags, the rest of the chain will be // generated/deducted later at higher levels. // Result is a chain: randomly indexed taginfo list. function loadEntityTags ($entity_realm = '', $entity_id = 0) { if ($entity_realm == '' or $entity_id <= 0) { showError ('Invalid or missing arguments', __FUNCTION__); return NULL; } $ret = array(); $query = "select tt.id, tag from " . "TagStorage as ts inner join TagTree as tt on ts.tag_id = tt.id " . "where target_realm = '${entity_realm}' and target_id = ${entity_id} " . "order by tt.tag"; $result = useSelectBlade ($query, __FUNCTION__); while ($row = $result->fetch (PDO::FETCH_ASSOC)) $ret[$row['id']] = $row; $result->closeCursor(); return getExplicitTagsOnly ($ret); } function loadFileTags ($id) { return loadEntityTags ('file', $id); } function loadRackObjectTags ($id) { return loadEntityTags ('object', $id); } function loadIPv4PrefixTags ($id) { return loadEntityTags ('ipv4net', $id); } function loadRackTags ($id) { return loadEntityTags ('rack', $id); } function loadIPv4VSTags ($id) { return loadEntityTags ('ipv4vs', $id); } function loadIPv4RSPoolTags ($id) { return loadEntityTags ('ipv4rspool', $id); } function loadUserTags ($user_id) { return loadEntityTags ('user', $user_id); } // Return a tag chain with all DB tags on it. function getTagList () { $ret = array(); $query = "select id, parent_id, tag, target_realm as realm, count(target_id) as refcnt " . "from TagTree left join TagStorage on id = tag_id " . "group by id, target_realm order by tag"; $result = useSelectBlade ($query, __FUNCTION__); $ci = 0; // Collation index. The resulting rows are ordered according to default collation, // which is utf8_general_ci for UTF-8. while ($row = $result->fetch (PDO::FETCH_ASSOC)) { if (!isset ($ret[$row['id']])) $ret[$row['id']] = array ( 'id' => $row['id'], 'tag' => $row['tag'], 'ci' => $ci++, 'parent_id' => $row['parent_id'], 'refcnt' => array() ); if ($row['realm']) $ret[$row['id']]['refcnt'][$row['realm']] = $row['refcnt']; } $result->closeCursor(); return $ret; } function commitCreateTag ($tagname = '', $parent_id = 0) { if ($tagname == '' or $parent_id === 0) return "Invalid args to " . __FUNCTION__; $result = useInsertBlade ( 'TagTree', array ( 'tag' => "'${tagname}'", 'parent_id' => $parent_id ) ); if ($result) return ''; else return "SQL query failed in " . __FUNCTION__; } function commitDestroyTag ($tagid = 0) { if ($tagid == 0) return 'Invalid arg to ' . __FUNCTION__; if (useDeleteBlade ('TagTree', 'id', $tagid)) return ''; else return 'useDeleteBlade() failed in ' . __FUNCTION__; } function commitUpdateTag ($tag_id, $tag_name, $parent_id) { if ($parent_id == 0) $parent_id = 'NULL'; global $dbxlink; $query = "update TagTree set tag = '${tag_name}', parent_id = ${parent_id} " . "where id = ${tag_id} limit 1"; $result = $dbxlink->exec ($query); if ($result === NULL) return 'SQL query failed in ' . __FUNCTION__; return ''; } // Drop the whole chain stored. function destroyTagsForEntity ($entity_realm, $entity_id) { global $dbxlink; $query = "delete from TagStorage where target_realm = '${entity_realm}' and target_id = ${entity_id}"; $result = $dbxlink->exec ($query); if ($result === NULL) return FALSE; else return TRUE; } // Drop only one record. This operation doesn't involve retossing other tags, unlike when adding. function deleteTagForEntity ($entity_realm, $entity_id, $tag_id) { global $dbxlink; $query = "delete from TagStorage where target_realm = '${entity_realm}' and target_id = ${entity_id} and tag_id = ${tag_id}"; $result = $dbxlink->exec ($query); if ($result === NULL) return FALSE; else return TRUE; } // Push a record into TagStorage unconditionally. function addTagForEntity ($realm = '', $entity_id, $tag_id) { if (empty ($realm)) return FALSE; return useInsertBlade ( 'TagStorage', array ( 'target_realm' => "'${realm}'", 'target_id' => $entity_id, 'tag_id' => $tag_id, ) ); } // Add records into TagStorage, if this makes sense (IOW, they don't appear // on the implicit list already). Then remove any other records, which // appear on the "implicit" side of the chain. This will make sure, // that both the tag base is still minimal and all requested tags appear on // the resulting tag chain. // Return TRUE, if any changes were committed. function rebuildTagChainForEntity ($realm, $entity_id, $extrachain = array()) { // Put the current explicit sub-chain into a buffer and merge all tags from // the extra chain, which aren't there yet. $newchain = $oldchain = loadEntityTags ($realm, $entity_id); foreach ($extrachain as $extratag) if (!tagOnChain ($extratag, $newchain)) $newchain[] = $extratag; // Then minimize the working buffer and check if it differs from the original // chain we started with. If it is so, save the work and signal the upper layer. $newchain = getExplicitTagsOnly ($newchain); if (tagChainCmp ($oldchain, $newchain)) { destroyTagsForEntity ($realm, $entity_id); foreach ($newchain as $taginfo) addTagForEntity ($realm, $entity_id, $taginfo['id']); return TRUE; } return FALSE; } // Presume, that the target record has no tags attached. function produceTagsForLastRecord ($realm, $tagidlist, $last_insert_id = 0) { if (!count ($tagidlist)) return ''; if (!$last_insert_id) $last_insert_id = lastInsertID(); $errcount = 0; foreach (getExplicitTagsOnly (buildTagChainFromIds ($tagidlist)) as $taginfo) if (addTagForEntity ($realm, $last_insert_id, $taginfo['id']) == FALSE) $errcount++; if (!$errcount) return ''; else return "Experienced ${errcount} errors adding tags in realm '${realm}' for entity ID == ${last_insert_id}"; } function createIPv4Prefix ($range = '', $name = '', $is_bcast = FALSE, $taglist = array()) { // $range is in x.x.x.x/x format, split into ip/mask vars $rangeArray = explode('/', $range); if (count ($rangeArray) != 2) return "Invalid IPv4 prefix '${range}'"; $ip = $rangeArray[0]; $mask = $rangeArray[1]; if (empty ($ip) or empty ($mask)) return "Invalid IPv4 prefix '${range}'"; $ipL = ip2long($ip); $maskL = ip2long($mask); if ($ipL == -1 || $ipL === FALSE) return 'Bad IPv4 address'; if ($mask < 32 && $mask > 0) $maskL = $mask; else { $maskB = decbin($maskL); if (strlen($maskB)!=32) return 'Invalid netmask'; $ones=0; $zeroes=FALSE; foreach( str_split ($maskB) as $digit) { if ($digit == '0') $zeroes = TRUE; if ($digit == '1') { $ones++; if ($zeroes == TRUE) return 'Invalid netmask'; } } $maskL = $ones; } $binmask = binMaskFromDec($maskL); $ipL = $ipL & $binmask; $result = useInsertBlade ( 'IPRanges', array ( 'ip' => sprintf ('%u', $ipL), 'mask' => "'${maskL}'", 'name' => "'${name}'" ) ); if ($result != TRUE) return "Could not add ${range} (already exists?)."; if ($is_bcast and $maskL < 31) { $network_addr = long2ip ($ipL); $broadcast_addr = long2ip ($ipL | binInvMaskFromDec ($maskL)); updateAddress ($network_addr, 'network', 'yes'); updateAddress ($broadcast_addr, 'broadcast', 'yes'); } return produceTagsForLastRecord ('ipv4net', $taglist); } // FIXME: This function doesn't wipe relevant records from IPAddress table. function destroyIPv4Prefix ($id = 0) { if ($id <= 0) return __FUNCTION__ . ': Invalid IPv4 prefix ID'; if (!useDeleteBlade ('IPRanges', 'id', $id)) return __FUNCTION__ . ': SQL query #1 failed'; if (!destroyTagsForEntity ('ipv4net', $id)) return __FUNCTION__ . ': SQL query #2 failed'; return ''; } function loadScript ($name) { $result = useSelectBlade ("select script_text from Script where script_name = '${name}'"); $row = $result->fetch (PDO::FETCH_NUM); if ($row !== FALSE) return $row[0]; else return NULL; } function saveScript ($name, $text) { if (empty ($name)) { showError ('Invalid argument'); return FALSE; } // delete regardless of existence useDeleteBlade ('Script', 'script_name', "'${name}'"); return useInsertBlade ( 'Script', array ( 'script_name' => "'${name}'", 'script_text' => "'${text}'" ) ); } function saveUserPassword ($user_id, $newp) { $newhash = hash (PASSWORD_HASH, $newp); $query = "update UserAccount set user_password_hash = ${newhash} where user_id = ${user_id} limit 1"; } function objectIsPortless ($id = 0) { if ($id <= 0) { showError ('Invalid argument', __FUNCTION__); return; } if (($result = useSelectBlade ("select count(id) from Port where object_id = ${id}", __FUNCTION__)) == NULL) { showError ('SQL query failed', __FUNCTION__); return; } $row = $result->fetch (PDO::FETCH_NUM); $count = $row[0]; $result->closeCursor(); unset ($result); return $count === '0'; } function recordExists ($id = 0, $realm = 'object') { if ($id <= 0) return FALSE; $table = array ( 'object' => 'RackObject', 'ipv4net' => 'IPRanges', 'user' => 'UserAccount', ); $idcol = array ( 'object' => 'id', 'ipv4net' => 'id', 'user' => 'user_id', ); $query = 'select count(*) from ' . $table[$realm] . ' where ' . $idcol[$realm] . ' = ' . $id; if (($result = useSelectBlade ($query, __FUNCTION__)) == NULL) { showError ('SQL query failed', __FUNCTION__); return FALSE; } $row = $result->fetch (PDO::FETCH_NUM); $count = $row[0]; $result->closeCursor(); unset ($result); return $count === '1'; } function tagExistsInDatabase ($tname) { $result = useSelectBlade ("select count(*) from TagTree where lower(tag) = lower('${tname}')"); $row = $result->fetch (PDO::FETCH_NUM); $count = $row[0]; $result->closeCursor(); unset ($result); return $count !== '0'; } function newPortForwarding ($object_id, $localip, $localport, $remoteip, $remoteport, $proto, $description) { if (NULL === getIPv4AddressNetworkId ($localip)) return "$localip: Non existant ip"; if (NULL === getIPv4AddressNetworkId ($localip)) return "$remoteip: Non existant ip"; if ( ($localport <= 0) or ($localport >= 65536) ) return "$localport: invaild port"; if ( ($remoteport <= 0) or ($remoteport >= 65536) ) return "$remoteport: invaild port"; $result = useInsertBlade ( 'PortForwarding', array ( 'object_id' => $object_id, 'localip' => "INET_ATON('${localip}')", 'remoteip' => "INET_ATON('$remoteip')", 'localport' => $localport, 'remoteport' => $remoteport, 'proto' => "'${proto}'", 'description' => "'${description}'", ) ); if ($result) return ''; else return __FUNCTION__ . ': Failed to insert the rule.'; } function deletePortForwarding ($object_id, $localip, $localport, $remoteip, $remoteport, $proto) { global $dbxlink; $query = "delete from PortForwarding where object_id='$object_id' and localip=INET_ATON('$localip') and remoteip=INET_ATON('$remoteip') and localport='$localport' and remoteport='$remoteport' and proto='$proto'"; $result = $dbxlink->exec ($query); return ''; } function updatePortForwarding ($object_id, $localip, $localport, $remoteip, $remoteport, $proto, $description) { global $dbxlink; $query = "update PortForwarding set description='$description' where object_id='$object_id' and localip=INET_ATON('$localip') and remoteip=INET_ATON('$remoteip') and localport='$localport' and remoteport='$remoteport' and proto='$proto'"; $result = $dbxlink->exec ($query); return ''; } function getNATv4ForObject ($object_id) { $ret = array(); $ret['out'] = array(); $ret['in'] = array(); $query = "select ". "proto, ". "INET_NTOA(localip) as localip, ". "localport, ". "INET_NTOA(remoteip) as remoteip, ". "remoteport, ". "ipa1.name as local_addr_name, " . "ipa2.name as remote_addr_name, " . "description ". "from PortForwarding ". "left join IPAddress as ipa1 on PortForwarding.localip = ipa1.ip " . "left join IPAddress as ipa2 on PortForwarding.remoteip = ipa2.ip " . "where object_id='$object_id' ". "order by localip, localport, proto, remoteip, remoteport"; $result = useSelectBlade ($query, __FUNCTION__); $count=0; while ($row = $result->fetch (PDO::FETCH_ASSOC)) { foreach (array ('proto', 'localport', 'localip', 'remoteport', 'remoteip', 'description', 'local_addr_name', 'remote_addr_name') as $cname) $ret['out'][$count][$cname] = $row[$cname]; $count++; } $result->closeCursor(); unset ($result); $query = "select ". "proto, ". "INET_NTOA(localip) as localip, ". "localport, ". "INET_NTOA(remoteip) as remoteip, ". "remoteport, ". "PortForwarding.object_id as object_id, ". "RackObject.name as object_name, ". "description ". "from ((PortForwarding join IPBonds on remoteip=IPBonds.ip) join RackObject on PortForwarding.object_id=RackObject.id) ". "where IPBonds.object_id='$object_id' ". "order by remoteip, remoteport, proto, localip, localport"; $result = useSelectBlade ($query, __FUNCTION__); $count=0; while ($row = $result->fetch (PDO::FETCH_ASSOC)) { foreach (array ('proto', 'localport', 'localip', 'remoteport', 'remoteip', 'object_id', 'object_name', 'description') as $cname) $ret['in'][$count][$cname] = $row[$cname]; $count++; } $result->closeCursor(); return $ret; } // This function performs search and then calculates score for each result. // Given previous search results in $objects argument, it adds new results // to the array and updates score for existing results, if it is greater than // existing score. function mergeSearchResults (&$objects, $terms, $fieldname) { global $dbxlink; $query = "select name, label, asset_no, barcode, ro.id, dict_key as objtype_id, " . "dict_value as objtype_name, asset_no from RackObject as ro inner join Dictionary " . "on objtype_id = dict_key natural join Chapter where chapter_name = 'RackObjectType' and "; $count = 0; foreach (explode (' ', $terms) as $term) { if ($count) $query .= ' or '; $query .= "${fieldname} like '%$term%'"; $count++; } $query .= " order by ${fieldname}"; $result = useSelectBlade ($query, __FUNCTION__); // FIXME: this dead call was executed 4 times per 1 object search! // $typeList = getObjectTypeList(); $clist = array ('id', 'name', 'label', 'asset_no', 'barcode', 'objtype_id', 'objtype_name'); while ($row = $result->fetch (PDO::FETCH_ASSOC)) { foreach ($clist as $cname) $object[$cname] = $row[$cname]; $object['score'] = 0; $object['dname'] = displayedName ($object); unset ($object['objtype_id']); foreach (explode (' ', $terms) as $term) if (strstr ($object['name'], $term)) $object['score'] += 1; unset ($object['name']); if (!isset ($objects[$row['id']])) $objects[$row['id']] = $object; elseif ($objects[$row['id']]['score'] < $object['score']) $objects[$row['id']]['score'] = $object['score']; } return $objects; } function getLostIPv4Addresses () { dragon(); } // File-related functions function getAllFiles () { $query = "SELECT id, name, type, size, ctime, mtime, atime, comment FROM File ORDER BY name"; $result = useSelectBlade ($query, __FUNCTION__); $ret=array(); $count=0; while ($row = $result->fetch (PDO::FETCH_ASSOC)) { $ret[$count]['id'] = $row['id']; $ret[$count]['name'] = $row['name']; $ret[$count]['type'] = $row['type']; $ret[$count]['size'] = $row['size']; $ret[$count]['ctime'] = $row['ctime']; $ret[$count]['mtime'] = $row['mtime']; $ret[$count]['atime'] = $row['atime']; $ret[$count]['comment'] = $row['comment']; $count++; } $result->closeCursor(); return $ret; } // Return a list of files which are not linked to the specified record function getAllUnlinkedFiles ($entity_type = NULL, $entity_id = 0) { if ($entity_type == NULL || $entity_id == 0) { showError ('Invalid parameters', __FUNCTION__); return NULL; } global $dbxlink; $sql = 'SELECT id, name, type, size, ctime, mtime, atime, comment FROM File ' . 'WHERE id NOT IN (SELECT file_id FROM FileLink WHERE entity_type = ? AND entity_id = ?) ' . 'ORDER BY name'; $query = $dbxlink->prepare($sql); $query->bindParam(1, $entity_type); $query->bindParam(2, $entity_id); $query->execute(); $ret=array(); $count=0; while ($row = $query->fetch (PDO::FETCH_ASSOC)) { $ret[$count]['id'] = $row['id']; $ret[$count]['name'] = $row['name']; $ret[$count]['type'] = $row['type']; $ret[$count]['size'] = $row['size']; $ret[$count]['ctime'] = $row['ctime']; $ret[$count]['mtime'] = $row['mtime']; $ret[$count]['atime'] = $row['atime']; $ret[$count]['comment'] = $row['comment']; $count++; } return $ret; } // Return a filtered, detailed file list. Used on the main Files listing page. function getFileList ($entity_type = NULL, $tagfilter = array(), $tfmode = 'any') { $whereclause = getWhereClause ($tagfilter); if ($entity_type == 'no_links') $whereclause .= 'AND File.id NOT IN (SELECT file_id FROM FileLink) '; elseif ($entity_type != 'all') $whereclause .= "AND entity_type = '${entity_type}' "; $query = 'SELECT File.id, name, type, size, ctime, mtime, atime, comment ' . 'FROM File ' . 'LEFT JOIN FileLink ' . 'ON File.id = FileLink.file_id ' . 'LEFT JOIN TagStorage ' . "ON File.id = TagStorage.target_id AND target_realm = 'file' " . 'WHERE size >= 0 ' . $whereclause . 'ORDER BY name'; $result = useSelectBlade ($query, __FUNCTION__); $ret = array(); while ($row = $result->fetch (PDO::FETCH_ASSOC)) { foreach (array ( 'id', 'name', 'type', 'size', 'ctime', 'mtime', 'atime', 'comment' ) as $cname) $ret[$row['id']][$cname] = $row[$cname]; } $result->closeCursor(); return $ret; } function getFilesOfEntity ($entity_type = NULL, $entity_id = 0) { if ($entity_type == NULL || $entity_id == 0) { showError ('Invalid parameters', __FUNCTION__); return NULL; } global $dbxlink; $sql = 'SELECT FileLink.file_id, FileLink.id AS link_id, name, type, size, ctime, mtime, atime, comment ' . 'FROM FileLink LEFT JOIN File ON FileLink.file_id = File.id ' . 'WHERE FileLink.entity_type = ? AND FileLink.entity_id = ? ORDER BY name'; $query = $dbxlink->prepare($sql); $query->bindParam(1, $entity_type); $query->bindParam(2, $entity_id); $query->execute(); $ret = array(); while ($row = $query->fetch (PDO::FETCH_ASSOC)) $ret[$row['file_id']] = array ( 'link_id' => $row['link_id'], 'name' => $row['name'], 'type' => $row['type'], 'size' => $row['size'], 'ctime' => $row['ctime'], 'mtime' => $row['mtime'], 'atime' => $row['atime'], 'comment' => $row['comment'], ); return $ret; } function getFile ($file_id = 0) { if ($file_id == 0) { showError ('Invalid file_id', __FUNCTION__); return NULL; } global $dbxlink; $query = $dbxlink->prepare('SELECT * FROM File WHERE id = ?'); $query->bindParam(1, $file_id); $query->execute(); if (($row = $query->fetch (PDO::FETCH_ASSOC)) == NULL) { showError ('Query succeeded, but returned no data', __FUNCTION__); $ret = NULL; } else { $ret = array(); $ret['id'] = $row['id']; $ret['name'] = $row['name']; $ret['type'] = $row['type']; $ret['size'] = $row['size']; $ret['ctime'] = $row['ctime']; $ret['mtime'] = $row['mtime']; $ret['atime'] = $row['atime']; $ret['contents'] = $row['contents']; $ret['comment'] = $row['comment']; $query->closeCursor(); // Someone accessed this file,date('YmdHis'); update atime $q_atime = $dbxlink->prepare('UPDATE File SET atime = ? WHERE id = ?'); $q_atime->bindParam(1, date('YmdHis')); $q_atime->bindParam(2, $file_id); $q_atime->execute(); } return $ret; } function getFileInfo ($file_id = 0) { if ($file_id == 0) { showError ('Invalid file_id', __FUNCTION__); return NULL; } global $dbxlink; $query = $dbxlink->prepare('SELECT id, name, type, size, ctime, mtime, atime, comment FROM File WHERE id = ?'); $query->bindParam(1, $file_id); $query->execute(); if (($row = $query->fetch (PDO::FETCH_ASSOC)) == NULL) { showError ('Query succeeded, but returned no data', __FUNCTION__); $ret = NULL; } else { $ret = array(); $ret['id'] = $row['id']; $ret['name'] = $row['name']; $ret['type'] = $row['type']; $ret['size'] = $row['size']; $ret['ctime'] = $row['ctime']; $ret['mtime'] = $row['mtime']; $ret['atime'] = $row['atime']; $ret['comment'] = $row['comment']; $query->closeCursor(); } return $ret; } function getFileLinks ($file_id = 0) { if ($file_id <= 0) { showError ('Invalid file_id', __FUNCTION__); return NULL; } global $dbxlink; $query = $dbxlink->prepare('SELECT * FROM FileLink WHERE file_id = ?'); $query->bindParam(1, $file_id); $query->execute(); $rows = $query->fetchAll (PDO::FETCH_ASSOC); $ret = array(); foreach ($rows as $row) { // get info of the parent switch ($row['entity_type']) { case 'ipv4net': $page = 'ipv4net'; $id_name = 'id'; $parent = getIPv4NetworkInfo($row['entity_id']); $name = sprintf("%s (%s/%s)", $parent['name'], $parent['ip'], $parent['mask']); break; case 'ipv4rspool': $page = 'ipv4rspool'; $id_name = 'pool_id'; $parent = getRSPoolInfo($row['entity_id']); $name = $parent['name']; break; case 'ipv4vs': $page = 'ipv4vs'; $id_name = 'vs_id'; $parent = getVServiceInfo($row['entity_id']); $name = $parent['name']; break; case 'object': $page = 'object'; $id_name = 'object_id'; $parent = getObjectInfo($row['entity_id']); $name = $parent['name']; break; case 'rack': $page = 'rack'; $id_name = 'rack_id'; $parent = getRackData($row['entity_id']); $name = $parent['name']; break; case 'user': $page = 'user'; $id_name = 'user_id'; global $accounts; foreach ($accounts as $account) if ($account['user_id'] == $row['entity_id']) $name = $account['user_name']; break; } $ret[$row['id']] = array( 'page' => $page, 'id_name' => $id_name, 'entity_type' => $row['entity_type'], 'entity_id' => $row['entity_id'], 'name' => $name ); } return $ret; } // Return list of possible file parents along with the number of children. // Used on main Files listing page. function getFileLinkInfo () { global $dbxlink; $query = 'SELECT entity_type, COUNT(*) AS count FROM FileLink GROUP BY entity_type'; $result = useSelectBlade ($query, __FUNCTION__); $ret = array(); $ret[0] = array ('entity_type' => 'all', 'name' => 'ALL files'); $clist = array ('entity_type', 'name', 'count'); $total = 0; $i=2; while ($row = $result->fetch (PDO::FETCH_ASSOC)) if ($row['count'] > 0) { $total += $row['count']; $row['name'] = formatEntityName ($row['entity_type']); foreach ($clist as $cname) $ret[$i][$cname] = $row[$cname]; $i++; } $result->closeCursor(); // Find number of files without any linkage $linkless_sql = 'SELECT COUNT(*) ' . 'FROM File ' . 'WHERE id NOT IN (SELECT file_id FROM FileLink)'; $q_linkless = useSelectBlade ($linkless_sql, __FUNCTION__); $ret[1] = array ('entity_type' => 'no_links', 'name' => 'Files w/no links', 'count' => $q_linkless->fetchColumn ()); $q_linkless->closeCursor(); // Find total number of files $total_sql = 'SELECT COUNT(*) FROM File'; $q_total = useSelectBlade ($total_sql, __FUNCTION__); $ret[0]['count'] = $q_total->fetchColumn (); $q_total->closeCursor(); ksort($ret); return $ret; } function commitAddFile ($name, $type, $size, $contents, $comment) { $now = date('YmdHis'); global $dbxlink; $query = $dbxlink->prepare('INSERT INTO File (name, type, size, ctime, mtime, atime, contents, comment) VALUES (?, ?, ?, ?, ?, ?, ?, ?)'); $query->bindParam(1, $name); $query->bindParam(2, $type); $query->bindParam(3, $size); $query->bindParam(4, $now); $query->bindParam(5, $now); $query->bindParam(6, $now); $query->bindParam(7, $contents, PDO::PARAM_LOB); $query->bindParam(8, $comment); $result = $query->execute(); if ($result) return ''; else return 'commitAddFile: SQL query failed'; } function commitLinkFile ($file_id, $parent_type, $parent_id) { global $dbxlink; $query = $dbxlink->prepare('INSERT INTO FileLink (file_id, entity_type, entity_id) VALUES (?, ?, ?)'); $query->bindParam(1, $file_id); $query->bindParam(2, $parent_type); $query->bindParam(3, $parent_id); $result = $query->execute(); if ($result) return ''; else return 'commitLinkFile: SQL query failed'; } function commitUpdateFile ($file_id = 0, $new_comment = '') { if ($file_id == 0) { showError ('Not all required args are present.', __FUNCTION__); return FALSE; } global $dbxlink; $query = $dbxlink->prepare('UPDATE File SET comment = ? WHERE id = ?'); $query->bindParam(1, $new_comment); $query->bindParam(2, $file_id); $result = $query->execute(); if (!$result) { showError ('commitUpdateFile: SQL query failed', __FUNCTION__); return FALSE; } return ''; } function commitUnlinkFile ($link_id) { if (useDeleteBlade ('FileLink', 'id', $link_id) != TRUE) return __FUNCTION__ . ': useDeleteBlade() failed'; return ''; } function commitDeleteFile ($file_id) { if (useDeleteBlade ('File', 'id', $file_id) != TRUE) return __FUNCTION__ . ': useDeleteBlade() failed'; return ''; } ?>