r2197 - initial spare networks code
[racktables] / inc / functions.php
index c242420dce837a73357075c55388e97c0643520e..b4cc1d909618cf4f2b0f24ffbb32dd6a2ae141c0 100644 (file)
@@ -378,48 +378,6 @@ function search_cmpObj ($a, $b)
        return ($a['score'] > $b['score'] ? -1 : 1);
 }
 
-// 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 getObjectSearchResults ($terms)
 {
        $objects = array();
@@ -574,27 +532,6 @@ function sortRacks ($a, $b)
        return sortTokenize($a['row_name'] . ': ' . $a['name'], $b['row_name'] . ': ' . $b['name']);
 }
 
-function eq ($a, $b)
-{
-       return $a==$b;
-}
-
-function neq ($a, $b)
-{
-       return $a!=$b;
-}
-
-function countRefsOfType ($refs, $type, $eq)
-{
-       $count=0;
-       foreach ($refs as $ref)
-       {
-               if ($eq($ref['type'], $type))
-                       $count++;
-       }
-       return $count;
-}
-
 function sortEmptyPorts ($a, $b)
 {
        $objname_cmp = sortTokenize($a['Object_name'], $b['Object_name']);
@@ -620,8 +557,6 @@ function sortObjectAddressesAndNames ($a, $b)
        return $objname_cmp;
 }
 
-
-
 function sortAddresses ($a, $b)
 {
        $name_cmp = sortTokenize($a['name'], $b['name']);
@@ -645,114 +580,6 @@ function buildPortCompatMatrixFromList ($portTypeList, $portCompatList)
        return $matrix;
 }
 
-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 returns an array of single element of object's FQDN attribute,
 // if FQDN is set. The next choice is object's common name, if it looks like a
 // hostname. Otherwise an array of all 'regular' IP addresses of the
@@ -763,11 +590,10 @@ function findAllEndpoints ($object_id, $fallback = '')
        foreach ($values as $record)
                if ($record['name'] == 'FQDN' && !empty ($record['value']))
                        return array ($record['value']);
-       $addresses = getObjectAddresses ($object_id);
        $regular = array();
-       foreach ($addresses as $idx => $address)
-               if ($address['type'] == 'regular')
-                       $regular[] = $address['ip'];
+       foreach (getObjectIPv4Allocations ($object_id) as $dottedquad => $alloc)
+               if ($alloc['type'] == 'regular')
+                       $regular[] = $dottedquad;
        if (!count ($regular) && !empty ($fallback))
                return array ($fallback);
        return $regular;
@@ -860,7 +686,8 @@ function getRSUforRackRow ($rowData = NULL)
        return ($counter['T'] + $counter['W'] + $counter['U']) / ($counter['T'] + $counter['W'] + $counter['U'] + $counter['F']);
 }
 
-function getObjectCount ($rackData)
+// Return a list of object IDs, which can be found in the given rackspace block.
+function stuffInRackspace ($rackData)
 {
        $objects = array();
        for ($i = $rackData['height']; $i > 0; $i--)
@@ -871,7 +698,7 @@ function getObjectCount ($rackData)
                                !in_array ($rackData[$i][$locidx]['object_id'], $objects)
                        )
                                $objects[] = $rackData[$i][$locidx]['object_id'];
-       return count ($objects);
+       return $objects;
 }
 
 // Make sure the string is always wrapped with LF characters
@@ -937,32 +764,40 @@ function getAutoPorts ($type_id)
 
 // Find if a particular tag id exists on the tree, then attach the
 // given child tag to it. If the parent tag doesn't exist, return FALSE.
-function attachChildTag (&$tree, $parent_id, $child_id, $child_info)
+function attachChildTag (&$tree, $parent_id, $child_id, $child_info, $threshold = 0)
 {
+       $self = __FUNCTION__;
        foreach ($tree as $tagid => $taginfo)
        {
                if ($tagid == $parent_id)
                {
-                       $tree[$tagid]['kids'][$child_id] = $child_info;
+                       if (!$threshold or ($threshold and $tree[$tagid]['kidc'] + 1 < $threshold))
+                               $tree[$tagid]['kids'][$child_id] = $child_info;
+                       // Reset the list only once.
+                       if (++$tree[$tagid]['kidc'] == $threshold)
+                               $tree[$tagid]['kids'] = array();
                        return TRUE;
                }
-               elseif (attachChildTag ($tree[$tagid]['kids'], $parent_id, $child_id, $child_info))
+               elseif ($self ($tree[$tagid]['kids'], $parent_id, $child_id, $child_info, $threshold))
                        return TRUE;
        }
        return FALSE;
 }
 
-// Build a tree from the tag list and return it.
-function getTagTree ()
+// Build a tree from the item list and return it. Input and output data is
+// indexed by item id (nested items in output are recursively stored in 'kids'
+// key, which is in turn indexed by id. Functions, which are ready to handle
+// tree collapsion/expansion themselves, may request non-zero threshold value
+// for smaller resulting tree.
+function treeFromList ($mytaglist, $threshold = 0)
 {
-       global $taglist;
-       $mytaglist = $taglist;
        $ret = array();
        while (count ($mytaglist) > 0)
        {
                $picked = FALSE;
                foreach ($mytaglist as $tagid => $taginfo)
                {
+                       $taginfo['kidc'] = 0;
                        $taginfo['kids'] = array();
                        if ($taginfo['parent_id'] == NULL)
                        {
@@ -970,7 +805,7 @@ function getTagTree ()
                                $picked = TRUE;
                                unset ($mytaglist[$tagid]);
                        }
-                       elseif (attachChildTag ($ret, $taginfo['parent_id'], $tagid, $taginfo))
+                       elseif (attachChildTag ($ret, $taginfo['parent_id'], $tagid, $taginfo, $threshold))
                        {
                                $picked = TRUE;
                                unset ($mytaglist[$tagid]);
@@ -1030,6 +865,7 @@ function serializeTags ($chain, $baseurl = '')
 // a helper for getTagChainExpansion()
 function traceTagChain ($tree, $chain)
 {
+       $self = __FUNCTION__;
        // For each tag find its path from the root, then combine items
        // of all paths and add them to the chain, if they aren't there yet.
        $ret = array();
@@ -1044,7 +880,7 @@ function traceTagChain ($tree, $chain)
                        }
                if (count ($taginfo1['kids']) > 0)
                {
-                       $subsearch = traceTagChain ($taginfo1['kids'], $chain);
+                       $subsearch = $self ($taginfo1['kids'], $chain);
                        if (count ($subsearch))
                        {
                                $hit = TRUE;
@@ -1089,6 +925,7 @@ function getImplicitTags ($oldtags)
 // Minimize the chain: exclude all implicit tags and return the result.
 function getExplicitTagsOnly ($chain, $tree = NULL)
 {
+       $self = __FUNCTION__;
        global $tagtree;
        if ($tree === NULL)
                $tree = $tagtree;
@@ -1097,7 +934,7 @@ function getExplicitTagsOnly ($chain, $tree = NULL)
        {
                if (isset ($taginfo['kids']))
                {
-                       $harvest = getExplicitTagsOnly ($chain, $taginfo['kids']);
+                       $harvest = $self ($chain, $taginfo['kids']);
                        if (count ($harvest) > 0)
                        {
                                $ret = array_merge ($ret, $harvest);
@@ -1117,9 +954,10 @@ function getExplicitTagsOnly ($chain, $tree = NULL)
 
 // Maximize the chain: for each tag add all tags, for which it is direct or indirect parent.
 // Unlike other functions, this one accepts and returns a list of integer tag IDs, not
-// a list of tag structures.
+// a list of tag structures. Same structure (tag ID list) is returned after processing.
 function complementByKids ($idlist, $tree = NULL, $getall = FALSE)
 {
+       $self = __FUNCTION__;
        global $tagtree;
        if ($tree === NULL)
                $tree = $tagtree;
@@ -1137,7 +975,7 @@ function complementByKids ($idlist, $tree = NULL, $getall = FALSE)
                                break;
                        }
                if (isset ($taginfo['kids']))
-                       $ret = array_merge ($ret, complementByKids ($idlist, $taginfo['kids'], $getallkids));
+                       $ret = array_merge ($ret, $self ($idlist, $taginfo['kids'], $getallkids));
                $getallkids = FALSE;
        }
        return $ret;
@@ -1230,6 +1068,42 @@ function loadIPv4RSPoolAutoTags ()
        return $ret;
 }
 
+// Check, if the given tag is present on the chain (will only work
+// for regular tags with tag ID set.
+function tagOnChain ($taginfo, $tagchain)
+{
+       if (!isset ($taginfo['id']))
+               return FALSE;
+       foreach ($tagchain as $test)
+               if ($test['id'] == $taginfo['id'])
+                       return TRUE;
+       return FALSE;
+}
+
+// Idem, but use ID list instead of chain.
+function tagOnIdList ($taginfo, $tagidlist)
+{
+       if (!isset ($taginfo['id']))
+               return FALSE;
+       foreach ($tagidlist as $tagid)
+               if ($taginfo['id'] == $tagid)
+                       return TRUE;
+       return FALSE;
+}
+
+// Return TRUE, if two tags chains differ (order of tags doesn't matter).
+// Assume, that neither of the lists contains duplicates.
+// FIXME: a faster, than O(x^2) method is possible for this calculation.
+function tagChainCmp ($chain1, $chain2)
+{
+       if (count ($chain1) != count ($chain2))
+               return TRUE;
+       foreach ($chain1 as $taginfo1)
+               if (!tagOnChain ($taginfo1, $chain2))
+                       return TRUE;
+       return FALSE;
+}
+
 // If the page-tab-op triplet is final, make $expl_tags and $impl_tags
 // hold all appropriate (explicit and implicit) tags respectively.
 // Otherwise some limited redirection is necessary (only page and tab
@@ -1272,12 +1146,13 @@ function fixContext ()
        }
 }
 
-// Build a tag chain from supplied tag id list and return it.
+// Take a list of user-supplied tag IDs to build a list of valid taginfo
+// records indexed by tag IDs (tag chain).
 function buildTagChainFromIds ($tagidlist)
 {
        global $taglist;
        $ret = array();
-       foreach ($tagidlist as $tag_id)
+       foreach (array_unique ($tagidlist) as $tag_id)
                if (isset ($taglist[$tag_id]))
                        $ret[] = $taglist[$tag_id];
        return $ret;
@@ -1287,6 +1162,7 @@ function buildTagChainFromIds ($tagidlist)
 // (sub)tree will have refcnt leaves on every last branch.
 function getObjectiveTagTree ($tree, $realm)
 {
+       $self = __FUNCTION__;
        $ret = array();
        foreach ($tree as $taginfo)
        {
@@ -1294,7 +1170,7 @@ function getObjectiveTagTree ($tree, $realm)
                $pick = FALSE;
                if (count ($taginfo['kids']))
                {
-                       $subsearch = getObjectiveTagTree ($taginfo['kids'], $realm);
+                       $subsearch = $self ($taginfo['kids'], $realm);
                        $pick = count ($subsearch) > 0;
                }
                if (isset ($taginfo['refcnt'][$realm]))
@@ -1466,6 +1342,8 @@ function buildLVSConfig ($object_id = 0)
                );
                foreach ($vsinfo['rslist'] as $rs)
                {
+                       if (empty ($rs['rsport']))
+                               $rs['rsport'] = $vsinfo['vport'];
                        $macros['%RSIP%'] = $rs['rsip'];
                        $macros['%RSPORT%'] = $rs['rsport'];
                        $newconfig .=  "\treal_server ${rs['rsip']} ${rs['rsport']} {\n";
@@ -1481,7 +1359,8 @@ function buildLVSConfig ($object_id = 0)
                }
                $newconfig .=  "}\n\n\n";
        }
-       return $newconfig;
+       // FIXME: deal somehow with Mac-styled text, the below replacement will screw it up
+       return str_replace ("\r", '', $newconfig);
 }
 
 // Indicate occupation state of each IP address: none, ordinary or problematic.
@@ -1489,14 +1368,18 @@ function markupIPv4AddrList (&$addrlist)
 {
        foreach (array_keys ($addrlist) as $ip_bin)
        {
-               $singlealloc = 0;
-               $nvirtual = countRefsOfType ($addrlist[$ip_bin]['allocs'], 'shared', 'eq');
-               $nloopback = countRefsOfType ($addrlist[$ip_bin]['allocs'], 'virtual', 'eq');
-               $nconnected = countRefsOfType ($addrlist[$ip_bin]['allocs'], 'regular', 'eq');
-               $nrouter = countRefsOfType ($addrlist[$ip_bin]['allocs'], 'router', 'eq');
-               $nsl = ($nvirtual + $nloopback > 0) ? 1 : 0;
-               $nrsv = ($addrlist[$ip_bin]['reserved'] == 'yes') ? 1 : 0;
-               $nrealms = $nrsv + $nsl + $nconnected + $nrouter;
+               $refc = array
+               (
+                       'shared' => 0,  // virtual
+                       'virtual' => 0, // loopback
+                       'regular' => 0, // connected host
+                       'router' => 0   // connected gateway
+               );
+               foreach ($addrlist[$ip_bin]['allocs'] as $a)
+                       $refc[$a['type']]++;
+               $nvirtloopback = ($refc['shared'] + $refc['virtual'] > 0) ? 1 : 0; // modulus of virtual + shared
+               $nreserved = ($addrlist[$ip_bin]['reserved'] == 'yes') ? 1 : 0; // only one reservation is possible ever
+               $nrealms = $nreserved + $nvirtloopback + $refc['regular'] + $refc['router']; // latter two are connected and router allocations
                
                if ($nrealms == 1)
                        $addrlist[$ip_bin]['class'] = 'trbusy';
@@ -1524,4 +1407,112 @@ function findRouters ($addrlist)
        return $ret;
 }
 
+// Assist in tag chain sorting.
+function taginfoCmp ($tagA, $tagB)
+{
+       return $tagA['ci'] - $tagB['ci'];
+}
+
+// Compare networks. When sorting a tree, the records on the list will have
+// distinct base IP addresses.
+function IPv4NetworkCmp ($netA, $netB)
+{
+       return bccomp ("${netA['db_first']}", "${netB['db_first']}");
+}
+
+// Modify the given tag tree so, that each level's items are sorted alphabetically.
+function sortTree (&$tree, $sortfunc = '')
+{
+       if (empty ($sortfunc))
+               return;
+       $self = __FUNCTION__;
+       usort ($tree, $sortfunc);
+       // Don't make a mistake of directly iterating over the items of current level, because this way
+       // the sorting will be performed on a _copy_ if each item, not the item itself.
+       foreach (array_keys ($tree) as $tagid)
+               $self ($tree[$tagid]['kids'], $sortfunc);
+}
+
+function iptree_fill (&$netdata)
+{
+       if (!isset ($netdata['kids']) or empty ($netdata['kids']))
+               return;
+       // If we relly have nested prefixes, they must fit into the tree.
+       $worktree = array
+       (
+               'ip_bin' => $netdata['ip_bin'],
+               'mask' => $netdata['mask']
+       );
+       foreach ($netdata['kids'] as $pfx)
+               iptree_embed ($worktree, $pfx);
+       $netdata['kids'] = iptree_construct ($worktree);
+       $netdata['kidc'] = count ($netdata['kids']);
+}
+
+function iptree_construct ($node)
+{
+       $self = __FUNCTION__;
+
+       if (!isset ($node['right']))
+       {
+               if (!isset ($node['ip']))
+               {
+                       $node['ip'] = long2ip ($node['ip_bin']);
+                       $node['kids'] = array();
+                       $node['name'] = '';
+               }
+               return array ($node);
+       }
+       else
+               return array_merge ($self ($node['left']), $self ($node['right']));
+}
+
+function iptree_embed (&$node, $pfx)
+{
+       $self = __FUNCTION__;
+
+       // hit?
+       if ($node['ip_bin'] == $pfx['ip_bin'] and $node['mask'] == $pfx['mask'])
+       {
+               $node = $pfx;
+               return;
+       }
+       if ($node['mask'] == $pfx['mask'])
+       {
+               showError ('Internal error, the recurring loop lost control', __FUNCTION__);
+               die;
+       }
+
+       // split?
+       if (!isset ($node['right']))
+       {
+               $node['right']['mask'] = $node['left']['mask'] = $node['mask'] + 1;
+               $node['left']['ip_bin'] = $node['ip_bin'];
+               $node['right']['ip_bin'] = $node['ip_bin'] + binInvMaskFromDec ($node['mask'] + 1) + 1;
+       }
+
+       // repeat!
+       if (($node['left']['ip_bin'] & binMaskFromDec ($node['left']['mask'])) == ($pfx['ip_bin'] & binMaskFromDec ($node['left']['mask'])))
+               $self ($node['left'], $pfx);
+       elseif (($node['right']['ip_bin'] & binMaskFromDec ($node['right']['mask'])) == ($pfx['ip_bin'] & binMaskFromDec ($node['left']['mask'])))
+               $self ($node['right'], $pfx);
+       else
+       {
+               showError ('Internal error, cannot decide between left and right', __FUNCTION__);
+               die;
+       }
+}
+
+function treeApplyFunc (&$tree, $func)
+{
+       if (empty ($func))
+               return;
+       $self = __FUNCTION__;
+       foreach (array_keys ($tree) as $key)
+       {
+               $func ($tree[$key]);
+               $self ($tree[$key]['kids'], $func);
+       }
+}
+
 ?>