4 * This file is a library of computational functions for RackTables.
9 $loclist[1] = 'interior';
11 $loclist['front'] = 0;
12 $loclist['interior'] = 1;
14 $template[0] = array (TRUE, TRUE, TRUE);
15 $template[1] = array (TRUE, TRUE, FALSE);
16 $template[2] = array (FALSE, TRUE, TRUE);
17 $template[3] = array (TRUE, FALSE, FALSE);
18 $template[4] = array (FALSE, TRUE, FALSE);
19 $template[5] = array (FALSE, FALSE, TRUE);
20 $templateWidth[0] = 3;
21 $templateWidth[1] = 2;
22 $templateWidth[2] = 2;
23 $templateWidth[3] = 1;
24 $templateWidth[4] = 1;
25 $templateWidth[5] = 1;
27 // Objects of some types should be explicitly shown as
28 // anonymous (labelless). This function is a single place where the
29 // decision about displayed name is made.
30 function displayedName ($objectData)
32 if ($objectData['name'] != '')
33 return $objectData['name'];
34 elseif (in_array ($objectData['objtype_id'], explode (',', getConfigVar ('NAMEFUL_OBJTYPES'))))
35 return "ANONYMOUS " . $objectData['objtype_name'];
37 return "[${objectData['objtype_name']}]";
40 // This function finds height of solid rectangle of atoms, which are all
41 // assigned to the same object. Rectangle base is defined by specified
43 function rectHeight ($rackData, $startRow, $template_idx)
46 // The first met object_id is used to match all the folowing IDs.
51 for ($locidx = 0; $locidx < 3; $locidx++
)
53 // At least one value in template is TRUE, but the following block
54 // can meet 'skipped' atoms. Let's ensure we have something after processing
56 if ($template[$template_idx][$locidx])
58 if (isset ($rackData[$startRow - $height][$locidx]['skipped']))
60 if ($rackData[$startRow - $height][$locidx]['state'] != 'T')
63 $object_id = $rackData[$startRow - $height][$locidx]['object_id'];
64 if ($object_id != $rackData[$startRow - $height][$locidx]['object_id'])
68 // If the first row can't offer anything, bail out.
69 if ($height == 0 and $object_id == 0)
73 while ($startRow - $height > 0);
74 // echo "for startRow==${startRow} and template==(${template[0]}, ${template[1]}, ${template[2]}) height==${height}<br>\n";
78 // This function marks atoms to be avoided by rectHeight() and assigns rowspan/colspan
80 function markSpan (&$rackData, $startRow, $maxheight, $template_idx)
82 global $template, $templateWidth;
84 for ($height = 0; $height < $maxheight; $height++
)
86 for ($locidx = 0; $locidx < 3; $locidx++
)
88 if ($template[$template_idx][$locidx])
90 // Add colspan/rowspan to the first row met and mark the following ones to skip.
92 $rackData[$startRow - $height][$locidx]['skipped'] = TRUE;
95 $colspan = $templateWidth[$template_idx];
97 $rackData[$startRow - $height][$locidx]['colspan'] = $colspan;
99 $rackData[$startRow - $height][$locidx]['rowspan'] = $maxheight;
107 // This function finds rowspan/solspan/skipped atom attributes for renderRack()
108 // What we actually have to do is to find all possible rectangles for each objects
109 // and then find the widest of those with the maximal square.
110 function markAllSpans (&$rackData = NULL)
112 if ($rackData == NULL)
114 showError ('Invalid rackData in markAllSpans()');
117 for ($i = $rackData['height']; $i > 0; $i--)
119 // calculate height of 6 possible span templates (array is presorted by width descending)
121 for ($j = 0; $j < 6; $j++
)
122 $height[$j] = rectHeight ($rackData, $i, $j);
123 // find the widest rectangle of those with maximal height
124 $maxheight = max ($height);
127 $best_template_index = 0;
128 for ($j = 0; $j < 6; $j++
)
129 if ($height[$j] == $maxheight)
131 $best_template_index = $j;
134 // distribute span marks
135 markSpan ($rackData, $i, $maxheight, $best_template_index);
140 function delRow ($row_id = 0)
144 showError ('Not all required args to delRow() are present.');
147 if (!isset ($_REQUEST['confirmed']) ||
$_REQUEST['confirmed'] != 'true')
149 echo "Press <a href='?op=del_row&row_id=${row_id}&confirmed=true'>here</a> to confirm rack row deletion.";
153 echo 'Deleting rack row information: ';
154 $result = $dbxlink->query ("update RackRow set deleted = 'yes' where id=${row_id} limit 1");
155 if ($result->rowCount() != 1)
157 showError ('Marked ' . $result.rowCount() . ' rows as deleted, but expected 1');
161 recordHistory ('RackRow', "id = ${row_id}");
162 echo "Information was deleted. You may return to <a href='?op=list_rows&editmode=on'>rack row list</a>.";
165 function delRack ($rack_id = 0)
169 showError ('Not all required args to delRack() are present.');
172 if (!isset ($_REQUEST['confirmed']) ||
$_REQUEST['confirmed'] != 'true')
174 echo "Press <a href='?op=del_rack&rack_id=${rack_id}&confirmed=true'>here</a> to confirm rack deletion.";
178 echo 'Deleting rack information: ';
179 $result = $dbxlink->query ("update Rack set deleted = 'yes' where id=${rack_id} limit 1");
180 if ($result->rowCount() != 1)
182 showError ('Marked ' . $result.rowCount() . ' rows as deleted, but expected 1');
186 recordHistory ('Rack', "id = ${rack_id}");
187 echo "Information was deleted. You may return to <a href='?op=list_racks&editmode=on'>rack list</a>.";
190 function delObject ($object_id = 0)
194 showError ('Not all required args to delObject() are present.');
197 if (!isset ($_REQUEST['confirmed']) ||
$_REQUEST['confirmed'] != 'true')
199 echo "Press <a href='?op=del_object&object_id=${object_id}&confirmed=true'>here</a> to confirm object deletion.";
203 echo 'Deleting object information: ';
204 $result = $dbxlink->query ("update RackObject set deleted = 'yes' where id=${object_id} limit 1");
205 if ($result->rowCount() != 1)
207 showError ('Marked ' . $result.rowCount() . ' rows as deleted, but expected 1');
211 recordHistory ('RackObject', "id = ${object_id}");
212 echo "Information was deleted. You may return to <a href='?op=list_objects&editmode=on'>object list</a>.";
215 // We can mount 'F' atoms and unmount our own 'T' atoms.
216 function applyObjectMountMask (&$rackData, $object_id)
218 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
219 for ($locidx = 0; $locidx < 3; $locidx++
)
220 switch ($rackData[$unit_no][$locidx]['state'])
223 $rackData[$unit_no][$locidx]['enabled'] = TRUE;
226 $rackData[$unit_no][$locidx]['enabled'] = ($rackData[$unit_no][$locidx]['object_id'] == $object_id);
229 $rackData[$unit_no][$locidx]['enabled'] = FALSE;
233 // Design change means transition between 'F' and 'A' and back.
234 function applyRackDesignMask (&$rackData)
236 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
237 for ($locidx = 0; $locidx < 3; $locidx++
)
238 switch ($rackData[$unit_no][$locidx]['state'])
242 $rackData[$unit_no][$locidx]['enabled'] = TRUE;
245 $rackData[$unit_no][$locidx]['enabled'] = FALSE;
249 // The same for 'F' and 'U'.
250 function applyRackProblemMask (&$rackData)
252 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
253 for ($locidx = 0; $locidx < 3; $locidx++
)
254 switch ($rackData[$unit_no][$locidx]['state'])
258 $rackData[$unit_no][$locidx]['enabled'] = TRUE;
261 $rackData[$unit_no][$locidx]['enabled'] = FALSE;
265 // This mask should allow toggling 'T' and 'W' on object's rackspace.
266 function applyObjectProblemMask (&$rackData)
268 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
269 for ($locidx = 0; $locidx < 3; $locidx++
)
270 switch ($rackData[$unit_no][$locidx]['state'])
274 $rackData[$unit_no][$locidx]['enabled'] = ($rackData[$unit_no][$locidx]['object_id'] == $object_id);
277 $rackData[$unit_no][$locidx]['enabled'] = FALSE;
281 // This function highlights specified object (and removes previous highlight).
282 function highlightObject (&$rackData, $object_id)
284 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
285 for ($locidx = 0; $locidx < 3; $locidx++
)
288 $rackData[$unit_no][$locidx]['state'] == 'T' and
289 $rackData[$unit_no][$locidx]['object_id'] == $object_id
291 $rackData[$unit_no][$locidx]['hl'] = 'h';
293 unset ($rackData[$unit_no][$locidx]['hl']);
296 // This function marks atoms to selected or not depending on their current state.
297 function markupAtomGrid (&$data, $checked_state)
299 for ($unit_no = $data['height']; $unit_no > 0; $unit_no--)
300 for ($locidx = 0; $locidx < 3; $locidx++
)
302 if (!($data[$unit_no][$locidx]['enabled'] === TRUE))
304 if ($data[$unit_no][$locidx]['state'] == $checked_state)
305 $data[$unit_no][$locidx]['checked'] = ' checked';
307 $data[$unit_no][$locidx]['checked'] = '';
311 // This function is almost a clone of processGridForm(), but doesn't save anything to database
312 // Return value is the changed rack data.
313 // Here we assume that correct filter has already been applied, so we just
314 // set or unset checkbox inputs w/o changing atom state.
315 function mergeGridFormToRack (&$rackData)
317 $rack_id = $rackData['id'];
318 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
319 for ($locidx = 0; $locidx < 3; $locidx++
)
321 if ($rackData[$unit_no][$locidx]['enabled'] != TRUE)
323 $inputname = "atom_${rack_id}_${unit_no}_${locidx}";
324 if (isset ($_REQUEST[$inputname]) and $_REQUEST[$inputname] == 'on')
325 $rackData[$unit_no][$locidx]['checked'] = ' checked';
327 $rackData[$unit_no][$locidx]['checked'] = '';
331 function binMaskFromDec ($maskL)
334 for ($i=0; $i<$maskL; $i++
)
339 for ($i=$maskL; $i<32; $i++
)
346 function binInvMaskFromDec ($maskL)
349 for ($i=0; $i<$maskL; $i++
)
353 for ($i=$maskL; $i<32; $i++
)
361 function addRange ($range='', $name='', $is_bcast = FALSE)
363 // $range is in x.x.x.x/x format, split into ip/mask vars
364 $rangeArray = explode('/', $range);
365 $ip = $rangeArray[0];
366 $mask = $rangeArray[1];
369 $maskL = ip2long($mask);
370 if ($ipL == -1 ||
$ipL === FALSE)
371 return 'Bad ip address';
372 if ($mask < 32 && $mask > 0)
376 $maskB = decbin($maskL);
377 if (strlen($maskB)!=32)
381 foreach( str_split ($maskB) as $digit)
394 $binmask = binMaskFromDec($maskL);
395 $ipL = $ipL & $binmask;
399 "id, ip, mask, name ".
405 $result = $dbxlink->query ($query);
407 while ($row = $result->fetch (PDO
::FETCH_ASSOC
))
409 $otherip = $row['ip'];
410 $othermask = binMaskFromDec($row['mask']);
411 // echo "checking $otherip & $othermask ".($otherip & $othermask)." == $ipL & $othermask ".($ipL & $othermask)." ".decbin($otherip)." ".decbin($othermask)." ".decbin($otherip & $othermask)." ".decbin($ipL)." ".decbin($othermask)." ".decbin($ipL & $othermask)."\n";
412 // echo "checking $otherip & $binmask ".($otherip & $binmask)." == $ipL & $binmask ".($ipL & $binmask)." ".decbin($otherip)." ".decbin($binmask)." ".decbin($otherip & $binmask)." ".decbin($ipL)." ".decbin($binmask)." ".decbin($ipL & $binmask)."\n";
415 if (($otherip & $othermask) == ($ipL & $othermask))
416 return "This subnet intersects with ".long2ip($row['ip'])."/${row['mask']}";
417 if (($otherip & $binmask) == ($ipL & $binmask))
418 return "This subnet intersects with ".long2ip($row['ip'])."/${row['mask']}";
420 $result->closeCursor();
422 "insert into IPRanges set ip=".sprintf('%u', $ipL).", mask='$maskL', name='$name'";
423 $result = $dbxlink->exec ($query);
425 if ($is_bcast and $maskL < 31)
427 $network_addr = long2ip ($ipL);
428 $broadcast_addr = long2ip ($ipL |
binInvMaskFromDec ($maskL));
429 updateAddress ($network_addr, 'network', 'yes');
430 updateAddress ($broadcast_addr, 'broadcast', 'yes');
435 function getIPRange ($id = 0)
440 "id as IPRanges_id, ".
441 "INET_NTOA(ip) as IPRanges_ip, ".
442 "mask as IPRanges_mask, ".
443 "name as IPRanges_name ".
446 $result = $dbxlink->query ($query);
450 $row = $result->fetch (PDO
::FETCH_ASSOC
);
453 $ret['id'] = $row['IPRanges_id'];
454 $ret['ip'] = $row['IPRanges_ip'];
455 $ret['ip_bin'] = ip2long ($row['IPRanges_ip']);
456 $ret['mask_bin'] = binMaskFromDec($row['IPRanges_mask']);
457 $ret['mask_bin_inv'] = binInvMaskFromDec($row['IPRanges_mask']);
458 $ret['name'] = $row['IPRanges_name'];
459 $ret['mask'] = $row['IPRanges_mask'];
460 $ret['addrlist'] = array();
461 $result->closeCursor();
462 // We risk losing some significant bits in an unsigned 32bit integer,
463 // unless it is converted to a string.
464 $db_first = "'" . sprintf ('%u', 0x00000000 +
$ret['ip_bin'] & $ret['mask_bin']) . "'";
465 $db_last = "'" . sprintf ('%u', 0x00000000 +
$ret['ip_bin'] |
($ret['mask_bin_inv'])) . "'";
467 // Don't try to build up the whole structure in a single pass. Request
468 // the list of user comments and reservations and merge allocations in
469 // at a latter point.
471 "select INET_NTOA(ip) as ip, name, reserved from IPAddress " .
472 "where ip between ${db_first} and ${db_last} " .
473 "and (reserved = 'yes' or name != '')";
474 $ipa_res = $dbxlink->query ($query);
475 if ($ipa_res == NULL)
477 while ($row = $ipa_res->fetch (PDO
::FETCH_ASSOC
))
479 $ip_bin = ip2long ($row['ip']);
480 $ret['addrlist'][$ip_bin] = $row;
482 foreach (array ('ip', 'name', 'reserved') as $cname)
483 $tmp[$cname] = $row[$cname];
484 $tmp['references'] = array();
485 $ret['addrlist'][$ip_bin] = $tmp;
487 $ipa_res->closeCursor();
490 "select INET_NTOA(ipb.ip) as ip, ro.id as object_id, " .
491 "ro.name as object_name, ipb.name, ipb.type, objtype_id, " .
492 "dict_value as objtype_name from " .
493 "IPBonds as ipb inner join RackObject as ro on ipb.object_id = ro.id " .
494 "left join Dictionary on objtype_id=dict_key natural join Chapter " .
495 "where ip between ${db_first} and ${db_last} " .
496 "and chapter_name = 'RackObjectType'" .
497 "order by ipb.type, object_name";
498 $ipb_res = $dbxlink->query ($query);
499 while ($row = $ipb_res->fetch (PDO
::FETCH_ASSOC
))
501 $ip_bin = ip2long ($row['ip']);
502 if (!isset ($ret['addrlist'][$ip_bin]))
504 $ret['addrlist'][$ip_bin] = array();
505 $ret['addrlist'][$ip_bin]['ip'] = $row['ip'];
506 $ret['addrlist'][$ip_bin]['name'] = '';
507 $ret['addrlist'][$ip_bin]['reserved'] = 'no';
508 $ret['addrlist'][$ip_bin]['references'] = array();
511 foreach (array ('object_id', 'type', 'name') as $cname)
512 $tmp[$cname] = $row[$cname];
513 $quasiobject['name'] = $row['object_name'];
514 $quasiobject['objtype_id'] = $row['objtype_id'];
515 $quasiobject['objtype_name'] = $row['objtype_name'];
516 $tmp['object_name'] = displayedName ($quasiobject);
517 $ret['addrlist'][$ip_bin]['references'][] = $tmp;
519 $ipb_res->closeCursor();
524 // Don't require any records in IPAddress, but if there is one,
525 // merge the data between getting allocation list. Collect enough data
526 // to call displayedName() ourselves.
527 function getIPAddress ($ip=0)
530 $ret['bonds'] = array();
531 $ret['outpf'] = array();
532 $ret['inpf'] = array();
535 $ret['reserved'] = 'no';
541 "where ip = INET_ATON('$ip') and (reserved = 'yes' or name != '')";
542 $result = $dbxlink->query ($query);
545 if ($row = $result->fetch (PDO
::FETCH_ASSOC
))
548 $ret['name'] = $row['name'];
549 $ret['reserved'] = $row['reserved'];
551 $result->closeCursor();
555 "IPBonds.object_id as object_id, ".
556 "IPBonds.name as name, ".
557 "IPBonds.type as type, ".
558 "objtype_id, dict_value as objtype_name, " .
559 "RackObject.name as object_name ".
560 "from IPBonds join RackObject on IPBonds.object_id=RackObject.id ".
561 "left join Dictionary on objtype_id=dict_key natural join Chapter " .
562 "where IPBonds.ip=INET_ATON('$ip') ".
563 "and chapter_name = 'RackObjectType' " .
564 "order by RackObject.id, IPBonds.name";
565 $result = $dbxlink->query ($query);
567 while ($row = $result->fetch (PDO
::FETCH_ASSOC
))
569 $ret['bonds'][$count]['object_id'] = $row['object_id'];
570 $ret['bonds'][$count]['name'] = $row['name'];
571 $ret['bonds'][$count]['type'] = $row['type'];
573 $qo['name'] = $row['object_name'];
574 $qo['objtype_id'] = $row['objtype_id'];
575 $qo['objtype_name'] = $row['objtype_name'];
576 $ret['bonds'][$count]['object_name'] = displayedName ($qo);
580 $result->closeCursor();
585 function bindIpToObject ($ip='', $object_id=0, $name='', $type='')
589 $range = getRangeByIp($ip);
591 return 'Non-existant ip address. Try adding IP range first';
593 $result = useInsertBlade
598 'ip' => "INET_ATON('$ip')",
599 'object_id' => "'${object_id}'",
600 'name' => "'${name}'",
601 'type' => "'${type}'"
604 return $result ?
'' : 'useInsertBlade() failed in bindIpToObject()';
607 // This function looks up 'has_problems' flag for 'T' atoms
608 // and modifies 'hl' key. May be, this should be better done
609 // in getRackData(). We don't honour 'skipped' key, because
610 // the function is also used for thumb creation.
611 function markupObjectProblems (&$rackData)
613 for ($i = $rackData['height']; $i > 0; $i--)
614 for ($locidx = 0; $locidx < 3; $locidx++
)
615 if ($rackData[$i][$locidx]['state'] == 'T')
617 $object = getObjectInfo ($rackData[$i][$locidx]['object_id']);
618 if ($object['has_problems'] == 'yes')
620 // Object can be already highlighted.
621 if (isset ($rackData[$i][$locidx]['hl']))
622 $rackData[$i][$locidx]['hl'] = $rackData[$i][$locidx]['hl'] . 'w';
624 $rackData[$i][$locidx]['hl'] = 'w';
629 function search_cmpObj ($a, $b)
631 return ($a['score'] > $b['score'] ?
-1 : 1);
634 // This function performs search and then calculates score for each result.
635 // Given previous search results in $objects argument, it adds new results
636 // to the array and updates score for existing results, if it is greater than
638 function mergeSearchResults (&$objects, $terms, $fieldname)
642 "select name, label, asset_no, barcode, ro.id, dict_key as objtype_id, " .
643 "dict_value as objtype_name, asset_no from RackObject as ro inner join Dictionary " .
644 "on objtype_id = dict_key natural join Chapter where chapter_name = 'RackObjectType' and ";
646 foreach (explode (' ', $terms) as $term)
648 if ($count) $query .= ' or ';
649 $query .= "${fieldname} like '%$term%'";
653 $result = $dbxlink->query($query);
656 showError ("SQL query failed in mergeSearchResults()");
659 // FIXME: this dead call was executed 4 times per 1 object search!
660 // $typeList = getObjectTypeList();
661 $clist = array ('id', 'name', 'label', 'asset_no', 'barcode', 'objtype_id', 'objtype_name');
662 while ($row = $result->fetch(PDO
::FETCH_ASSOC
))
664 foreach ($clist as $cname)
665 $object[$cname] = $row[$cname];
666 $object['score'] = 0;
667 $object['dname'] = displayedName ($object);
668 unset ($object['objtype_id']);
669 foreach (explode (' ', $terms) as $term)
670 if (strstr ($object['name'], $term))
671 $object['score'] +
= 1;
672 unset ($object['name']);
673 if (!isset ($objects[$row['id']]))
674 $objects[$row['id']] = $object;
675 elseif ($objects[$row['id']]['score'] < $object['score'])
676 $objects[$row['id']]['score'] = $object['score'];
681 function getSearchResults ($terms)
684 mergeSearchResults ($objects, $terms, 'name');
685 mergeSearchResults ($objects, $terms, 'label');
686 mergeSearchResults ($objects, $terms, 'asset_no');
687 mergeSearchResults ($objects, $terms, 'barcode');
688 if (count ($objects) == 1)
689 usort ($objects, 'search_cmpObj');
693 // This function removes all colons and dots from a string.
694 function l2addressForDatabase ($string)
698 $pieces = explode (':', $string);
699 // This workaround is for SunOS ifconfig.
700 foreach ($pieces as &$byte)
701 if (strlen ($byte) == 1)
703 // And this workaround is for PHP.
705 $string = implode ('', $pieces);
706 $pieces = explode ('.', $string);
707 $string = implode ('', $pieces);
708 $string = strtoupper ($string);
712 function l2addressFromDatabase ($string)
714 switch (strlen ($string))
718 $ret = implode (':', str_split ($string, 2));
727 // The following 2 functions return previous and next rack IDs for
728 // a given rack ID. The order of racks is the same as in renderRackspace()
730 function getPrevIDforRack ($row_id = 0, $rack_id = 0)
732 if ($row_id <= 0 or $rack_id <= 0)
734 showError ('Invalid arguments passed to getPrevIDforRack()');
737 $rackList = getRacksForRow ($row_id);
738 doubleLink ($rackList);
739 if (isset ($rackList[$rack_id]['prev_key']))
740 return $rackList[$rack_id]['prev_key'];
744 function getNextIDforRack ($row_id = 0, $rack_id = 0)
746 if ($row_id <= 0 or $rack_id <= 0)
748 showError ('Invalid arguments passed to getNextIDforRack()');
751 $rackList = getRacksForRow ($row_id);
752 doubleLink ($rackList);
753 if (isset ($rackList[$rack_id]['next_key']))
754 return $rackList[$rack_id]['next_key'];
758 // This function finds previous and next array keys for each array key and
759 // modifies its argument accordingly.
760 function doubleLink (&$array)
763 foreach (array_keys ($array) as $key)
767 $array[$key]['prev_key'] = $prev_key;
768 $array[$prev_key]['next_key'] = $key;
774 // After applying usort() to a rack list we will lose original array keys.
775 // This function restores the keys so they are equal to rack IDs.
776 function restoreRackIDs ($racks)
779 foreach ($racks as $rack)
780 $ret[$rack['id']] = $rack;
784 function sortTokenize ($a, $b)
790 $a = ereg_replace('[^a-zA-Z0-9]',' ',$a);
791 $a = ereg_replace('([0-9])([a-zA-Z])','\\1 \\2',$a);
792 $a = ereg_replace('([a-zA-Z])([0-9])','\\1 \\2',$a);
799 $b = ereg_replace('[^a-zA-Z0-9]',' ',$b);
800 $b = ereg_replace('([0-9])([a-zA-Z])','\\1 \\2',$b);
801 $b = ereg_replace('([a-zA-Z])([0-9])','\\1 \\2',$b);
806 $ar = explode(' ', $a);
807 $br = explode(' ', $b);
808 for ($i=0; $i<count($ar) && $i<count($br); $i++
)
811 if (is_numeric($ar[$i]) and is_numeric($br[$i]))
812 $ret = ($ar[$i]==$br[$i])?
0:($ar[$i]<$br[$i]?
-1:1);
814 $ret = strcasecmp($ar[$i], $br[$i]);
825 function sortByName ($a, $b)
827 return sortTokenize($a['name'], $b['name']);
830 function sortRacks ($a, $b)
832 return sortTokenize($a['row_name'] . ': ' . $a['name'], $b['row_name'] . ': ' . $b['name']);
840 function neq ($a, $b)
845 function countRefsOfType ($refs, $type, $eq)
848 foreach ($refs as $ref)
850 if ($eq($ref['type'], $type))
856 function sortEmptyPorts ($a, $b)
858 $objname_cmp = sortTokenize($a['Object_name'], $b['Object_name']);
859 if ($objname_cmp == 0)
861 return sortTokenize($a['Port_name'], $b['Port_name']);
866 function sortObjectAddressesAndNames ($a, $b)
868 $objname_cmp = sortTokenize($a['object_name'], $b['object_name']);
869 if ($objname_cmp == 0)
871 $objname_cmp = sortTokenize($a['port_name'], $b['port_name']);
872 if ($objname_cmp == 0)
874 sortTokenize($a['ip'], $b['ip']);
883 function sortAddresses ($a, $b)
885 $name_cmp = sortTokenize($a['name'], $b['name']);
888 return sortTokenize($a['ip'], $b['ip']);
893 // This function expands port compat list into a matrix.
894 function buildPortCompatMatrixFromList ($portTypeList, $portCompatList)
897 // Create type matrix and markup compatible types.
898 foreach (array_keys ($portTypeList) as $type1)
899 foreach (array_keys ($portTypeList) as $type2)
900 $matrix[$type1][$type2] = FALSE;
901 foreach ($portCompatList as $pair)
902 $matrix[$pair['type1']][$pair['type2']] = TRUE;
906 function newPortForwarding($object_id, $localip, $localport, $remoteip, $remoteport, $proto, $description)
910 $range = getRangeByIp($localip);
912 return "$localip: Non existant ip";
914 $range = getRangeByIp($remoteip);
916 return "$remoteip: Non existant ip";
918 if ( ($localport <= 0) or ($localport >= 65536) )
919 return "$localport: invaild port";
921 if ( ($remoteport <= 0) or ($remoteport >= 65536) )
922 return "$remoteport: invaild port";
925 "insert into PortForwarding set object_id='$object_id', localip=INET_ATON('$localip'), remoteip=INET_ATON('$remoteip'), localport='$localport', remoteport='$remoteport', proto='$proto', description='$description'";
926 $result = $dbxlink->exec ($query);
931 function deletePortForwarding($object_id, $localip, $localport, $remoteip, $remoteport, $proto)
936 "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'";
937 $result = $dbxlink->exec ($query);
941 function updatePortForwarding($object_id, $localip, $localport, $remoteip, $remoteport, $proto, $description)
946 "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'";
947 $result = $dbxlink->exec ($query);
951 function getObjectForwards($object_id)
956 $ret['out'] = array();
957 $ret['in'] = array();
960 "dict_value as proto, ".
961 "proto as proto_bin, ".
962 "INET_NTOA(localip) as localip, ".
964 "INET_NTOA(remoteip) as remoteip, ".
966 "ipa1.name as local_addr_name, " .
967 "ipa2.name as remote_addr_name, " .
969 "from PortForwarding inner join Dictionary on proto = dict_key natural join Chapter ".
970 "left join IPAddress as ipa1 on PortForwarding.localip = ipa1.ip " .
971 "left join IPAddress as ipa2 on PortForwarding.remoteip = ipa2.ip " .
972 "where object_id='$object_id' and chapter_name = 'Protocols' ".
973 "order by localip, localport, proto, remoteip, remoteport";
974 $result2 = $dbxlink->query ($query);
976 while ($row = $result2->fetch (PDO
::FETCH_ASSOC
))
978 foreach (array ('proto', 'proto_bin', 'localport', 'localip', 'remoteport', 'remoteip', 'description', 'local_addr_name', 'remote_addr_name') as $cname)
979 $ret['out'][$count][$cname] = $row[$cname];
982 $result2->closeCursor();
986 "dict_value as proto, ".
987 "proto as proto_bin, ".
988 "INET_NTOA(localip) as localip, ".
990 "INET_NTOA(remoteip) as remoteip, ".
992 "PortForwarding.object_id as object_id, ".
993 "RackObject.name as object_name, ".
995 "from ((PortForwarding join IPBonds on remoteip=IPBonds.ip) join RackObject on PortForwarding.object_id=RackObject.id) inner join Dictionary on proto = dict_key natural join Chapter ".
996 "where IPBonds.object_id='$object_id' and chapter_name = 'Protocols' ".
997 "order by remoteip, remoteport, proto, localip, localport";
998 $result3 = $dbxlink->query ($query);
1000 while ($row = $result3->fetch (PDO
::FETCH_ASSOC
))
1002 foreach (array ('proto', 'proto_bin', 'localport', 'localip', 'remoteport', 'remoteip', 'object_id', 'object_name', 'description') as $cname)
1003 $ret['in'][$count][$cname] = $row[$cname];
1006 $result3->closeCursor();
1011 // This function returns an array of single element of object's FQDN attribute,
1012 // if FQDN is set. The next choice is object's common name, if it looks like a
1013 // hostname. Otherwise an array of all 'regular' IP addresses of the
1014 // object is returned (which may appear 0 and more elements long).
1015 function findAllEndpoints ($object_id, $fallback = '')
1017 $values = getAttrValues ($object_id);
1018 foreach ($values as $record)
1019 if ($record['name'] == 'FQDN' && !empty ($record['value']))
1020 return array ($record['value']);
1021 $addresses = getObjectAddresses ($object_id);
1023 foreach ($addresses as $idx => $address)
1024 if ($address['type'] == 'regular')
1025 $regular[] = $address['ip'];
1026 if (!count ($regular) && !empty ($fallback))
1027 return array ($fallback);
1031 // Some records in the dictionary may be written as plain text or as Wiki
1032 // link in the following syntax:
1034 // 2. [[word URL]] // FIXME: this isn't working
1035 // 3. [[word word word | URL]]
1036 // This function parses the line and returns text suitable for either A
1037 // (rendering <A HREF>) or O (for <OPTION>).
1038 function parseWikiLink ($line, $which)
1040 if (preg_match ('/^\[\[.+\]\]$/', $line) == 0)
1042 $line = preg_replace ('/^\[\[(.+)\]\]$/', '$1', $line);
1043 $s = explode ('|', $line);
1044 $o_value = trim ($s[0]);
1045 $a_value = trim ($s[1]);
1047 return "<a href='${a_value}'>${o_value}</a>";
1052 function buildVServiceName ($vsinfo = NULL)
1054 if ($vsinfo == NULL)
1056 showError ('NULL argument', __FUNCTION__
);
1059 return $vsinfo['vip'] . ':' . $vsinfo['vport'] . '/' . $vsinfo['proto'];