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 (isset ($rackData[$startRow - $height][$locidx]['rowspan']))
62 if (isset ($rackData[$startRow - $height][$locidx]['colspan']))
64 if ($rackData[$startRow - $height][$locidx]['state'] != 'T')
67 $object_id = $rackData[$startRow - $height][$locidx]['object_id'];
68 if ($object_id != $rackData[$startRow - $height][$locidx]['object_id'])
72 // If the first row can't offer anything, bail out.
73 if ($height == 0 and $object_id == 0)
77 while ($startRow - $height > 0);
78 # echo "for startRow==${startRow} and template==(" . ($template[$template_idx][0] ? 'T' : 'F');
79 # echo ', ' . ($template[$template_idx][1] ? 'T' : 'F') . ', ' . ($template[$template_idx][2] ? 'T' : 'F');
80 # echo ") height==${height}<br>\n";
84 // This function marks atoms to be avoided by rectHeight() and assigns rowspan/colspan
86 function markSpan (&$rackData, $startRow, $maxheight, $template_idx)
88 global $template, $templateWidth;
90 for ($height = 0; $height < $maxheight; $height++
)
92 for ($locidx = 0; $locidx < 3; $locidx++
)
94 if ($template[$template_idx][$locidx])
96 // Add colspan/rowspan to the first row met and mark the following ones to skip.
97 // Explicitly show even single-cell spanned atoms, because rectHeight()
98 // is expeciting this data for correct calculation.
100 $rackData[$startRow - $height][$locidx]['skipped'] = TRUE;
103 $colspan = $templateWidth[$template_idx];
105 $rackData[$startRow - $height][$locidx]['colspan'] = $colspan;
107 $rackData[$startRow - $height][$locidx]['rowspan'] = $maxheight;
115 // This function sets rowspan/solspan/skipped atom attributes for renderRack()
116 // What we actually have to do is to find _all_ possible rectangles for each unit
117 // and then select the widest of those with the maximal square.
118 function markAllSpans (&$rackData = NULL)
120 if ($rackData == NULL)
122 showError ('Invalid rackData', __FUNCTION__
);
125 for ($i = $rackData['height']; $i > 0; $i--)
126 while (markBestSpan ($rackData, $i));
129 // Calculate height of 6 possible span templates (array is presorted by width
130 // descending) and mark the best (if any).
131 function markBestSpan (&$rackData, $i)
133 global $template, $templateWidth;
134 for ($j = 0; $j < 6; $j++
)
136 $height[$j] = rectHeight ($rackData, $i, $j);
137 $square[$j] = $height[$j] * $templateWidth[$j];
139 // find the widest rectangle of those with maximal height
140 $maxsquare = max ($square);
143 $best_template_index = 0;
144 for ($j = 0; $j < 6; $j++
)
145 if ($square[$j] == $maxsquare)
147 $best_template_index = $j;
148 $bestheight = $height[$j];
151 // distribute span marks
152 markSpan ($rackData, $i, $bestheight, $best_template_index);
156 function delRow ($row_id = 0)
160 showError ('Not all required args are present.', __FUNCTION__
);
163 if (!isset ($_REQUEST['confirmed']) ||
$_REQUEST['confirmed'] != 'true')
165 echo "Press <a href='?op=del_row&row_id=${row_id}&confirmed=true'>here</a> to confirm rack row deletion.";
169 echo 'Deleting rack row information: ';
170 $result = $dbxlink->query ("update RackRow set deleted = 'yes' where id=${row_id} limit 1");
171 if ($result->rowCount() != 1)
173 showError ('Marked ' . $result.rowCount() . ' rows as deleted, but expected 1', __FUNCTION__
);
177 recordHistory ('RackRow', "id = ${row_id}");
178 echo "Information was deleted. You may return to <a href='?op=list_rows&editmode=on'>rack row list</a>.";
181 function delRack ($rack_id = 0)
185 showError ('Not all required args are present.', __FUNCTION__
);
188 if (!isset ($_REQUEST['confirmed']) ||
$_REQUEST['confirmed'] != 'true')
190 echo "Press <a href='?op=del_rack&rack_id=${rack_id}&confirmed=true'>here</a> to confirm rack deletion.";
194 echo 'Deleting rack information: ';
195 $result = $dbxlink->query ("update Rack set deleted = 'yes' where id=${rack_id} limit 1");
196 if ($result->rowCount() != 1)
198 showError ('Marked ' . $result.rowCount() . ' rows as deleted, but expected 1', __FUNCTION__
);
202 recordHistory ('Rack', "id = ${rack_id}");
203 echo "Information was deleted. You may return to <a href='?op=list_racks&editmode=on'>rack list</a>.";
206 function delObject ($object_id = 0)
210 showError ('Not all required args are present.', __FUNCTION__
);
213 if (!isset ($_REQUEST['confirmed']) ||
$_REQUEST['confirmed'] != 'true')
215 echo "Press <a href='?op=del_object&object_id=${object_id}&confirmed=true'>here</a> to confirm object deletion.";
219 echo 'Deleting object information: ';
220 $result = $dbxlink->query ("update RackObject set deleted = 'yes' where id=${object_id} limit 1");
221 if ($result->rowCount() != 1)
223 showError ('Marked ' . $result.rowCount() . ' rows as deleted, but expected 1', __FUNCTION__
);
227 recordHistory ('RackObject', "id = ${object_id}");
228 echo "Information was deleted. You may return to <a href='?op=list_objects&editmode=on'>object list</a>.";
231 // We can mount 'F' atoms and unmount our own 'T' atoms.
232 function applyObjectMountMask (&$rackData, $object_id)
234 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
235 for ($locidx = 0; $locidx < 3; $locidx++
)
236 switch ($rackData[$unit_no][$locidx]['state'])
239 $rackData[$unit_no][$locidx]['enabled'] = TRUE;
242 $rackData[$unit_no][$locidx]['enabled'] = ($rackData[$unit_no][$locidx]['object_id'] == $object_id);
245 $rackData[$unit_no][$locidx]['enabled'] = FALSE;
249 // Design change means transition between 'F' and 'A' and back.
250 function applyRackDesignMask (&$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 // The same for 'F' and 'U'.
266 function applyRackProblemMask (&$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'] = TRUE;
277 $rackData[$unit_no][$locidx]['enabled'] = FALSE;
281 // This mask should allow toggling 'T' and 'W' on object's rackspace.
282 function applyObjectProblemMask (&$rackData)
284 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
285 for ($locidx = 0; $locidx < 3; $locidx++
)
286 switch ($rackData[$unit_no][$locidx]['state'])
290 $rackData[$unit_no][$locidx]['enabled'] = ($rackData[$unit_no][$locidx]['object_id'] == $object_id);
293 $rackData[$unit_no][$locidx]['enabled'] = FALSE;
297 // This function highlights specified object (and removes previous highlight).
298 function highlightObject (&$rackData, $object_id)
300 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
301 for ($locidx = 0; $locidx < 3; $locidx++
)
304 $rackData[$unit_no][$locidx]['state'] == 'T' and
305 $rackData[$unit_no][$locidx]['object_id'] == $object_id
307 $rackData[$unit_no][$locidx]['hl'] = 'h';
309 unset ($rackData[$unit_no][$locidx]['hl']);
312 // This function marks atoms to selected or not depending on their current state.
313 function markupAtomGrid (&$data, $checked_state)
315 for ($unit_no = $data['height']; $unit_no > 0; $unit_no--)
316 for ($locidx = 0; $locidx < 3; $locidx++
)
318 if (!($data[$unit_no][$locidx]['enabled'] === TRUE))
320 if ($data[$unit_no][$locidx]['state'] == $checked_state)
321 $data[$unit_no][$locidx]['checked'] = ' checked';
323 $data[$unit_no][$locidx]['checked'] = '';
327 // This function is almost a clone of processGridForm(), but doesn't save anything to database
328 // Return value is the changed rack data.
329 // Here we assume that correct filter has already been applied, so we just
330 // set or unset checkbox inputs w/o changing atom state.
331 function mergeGridFormToRack (&$rackData)
333 $rack_id = $rackData['id'];
334 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
335 for ($locidx = 0; $locidx < 3; $locidx++
)
337 if ($rackData[$unit_no][$locidx]['enabled'] != TRUE)
339 $inputname = "atom_${rack_id}_${unit_no}_${locidx}";
340 if (isset ($_REQUEST[$inputname]) and $_REQUEST[$inputname] == 'on')
341 $rackData[$unit_no][$locidx]['checked'] = ' checked';
343 $rackData[$unit_no][$locidx]['checked'] = '';
347 function binMaskFromDec ($maskL)
350 for ($i=0; $i<$maskL; $i++
)
355 for ($i=$maskL; $i<32; $i++
)
362 function binInvMaskFromDec ($maskL)
365 for ($i=0; $i<$maskL; $i++
)
369 for ($i=$maskL; $i<32; $i++
)
377 function addRange ($range='', $name='', $is_bcast = FALSE)
379 // $range is in x.x.x.x/x format, split into ip/mask vars
380 $rangeArray = explode('/', $range);
381 if (count ($rangeArray) != 2)
382 return "Invalid IP subnet '${range}'";
383 $ip = $rangeArray[0];
384 $mask = $rangeArray[1];
386 if (empty ($ip) or empty ($mask))
387 return "Invalid IP subnet '${range}'";
389 $maskL = ip2long($mask);
390 if ($ipL == -1 ||
$ipL === FALSE)
391 return 'Bad ip address';
392 if ($mask < 32 && $mask > 0)
396 $maskB = decbin($maskL);
397 if (strlen($maskB)!=32)
401 foreach( str_split ($maskB) as $digit)
414 $binmask = binMaskFromDec($maskL);
415 $ipL = $ipL & $binmask;
419 "id, ip, mask, name ".
425 $result = $dbxlink->query ($query);
427 while ($row = $result->fetch (PDO
::FETCH_ASSOC
))
429 $otherip = $row['ip'];
430 $othermask = binMaskFromDec($row['mask']);
431 // 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";
432 // 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";
435 if (($otherip & $othermask) == ($ipL & $othermask))
436 return "This subnet intersects with ".long2ip($row['ip'])."/${row['mask']}";
437 if (($otherip & $binmask) == ($ipL & $binmask))
438 return "This subnet intersects with ".long2ip($row['ip'])."/${row['mask']}";
440 $result->closeCursor();
442 "insert into IPRanges set ip=".sprintf('%u', $ipL).", mask='$maskL', name='$name'";
443 $result = $dbxlink->exec ($query);
445 if ($is_bcast and $maskL < 31)
447 $network_addr = long2ip ($ipL);
448 $broadcast_addr = long2ip ($ipL |
binInvMaskFromDec ($maskL));
449 updateAddress ($network_addr, 'network', 'yes');
450 updateAddress ($broadcast_addr, 'broadcast', 'yes');
455 function getIPRange ($id = 0)
460 "id as IPRanges_id, ".
461 "INET_NTOA(ip) as IPRanges_ip, ".
462 "mask as IPRanges_mask, ".
463 "name as IPRanges_name ".
466 $result = useSelectBlade ($query);
468 $row = $result->fetch (PDO
::FETCH_ASSOC
);
471 $ret['id'] = $row['IPRanges_id'];
472 $ret['ip'] = $row['IPRanges_ip'];
473 $ret['ip_bin'] = ip2long ($row['IPRanges_ip']);
474 $ret['mask_bin'] = binMaskFromDec($row['IPRanges_mask']);
475 $ret['mask_bin_inv'] = binInvMaskFromDec($row['IPRanges_mask']);
476 $ret['name'] = $row['IPRanges_name'];
477 $ret['mask'] = $row['IPRanges_mask'];
478 $ret['addrlist'] = array();
479 $result->closeCursor();
481 // We risk losing some significant bits in an unsigned 32bit integer,
482 // unless it is converted to a string.
483 $db_first = "'" . sprintf ('%u', 0x00000000 +
$ret['ip_bin'] & $ret['mask_bin']) . "'";
484 $db_last = "'" . sprintf ('%u', 0x00000000 +
$ret['ip_bin'] |
($ret['mask_bin_inv'])) . "'";
486 // Don't try to build up the whole structure in a single pass. Request
487 // the list of user comments and reservations and merge allocations in
488 // at a latter point.
490 "select INET_NTOA(ip) as ip, name, reserved from IPAddress " .
491 "where ip between ${db_first} and ${db_last} " .
492 "and (reserved = 'yes' or name != '')";
493 $result = $dbxlink->query ($query);
494 while ($row = $result->fetch (PDO
::FETCH_ASSOC
))
496 $ip_bin = ip2long ($row['ip']);
497 $ret['addrlist'][$ip_bin] = $row;
499 foreach (array ('ip', 'name', 'reserved') as $cname)
500 $tmp[$cname] = $row[$cname];
501 $tmp['references'] = array();
502 $tmp['lbrefs'] = array();
503 $tmp['rsrefs'] = array();
504 $ret['addrlist'][$ip_bin] = $tmp;
506 $result->closeCursor();
510 "select INET_NTOA(ipb.ip) as ip, ro.id as object_id, " .
511 "ro.name as object_name, ipb.name, ipb.type, objtype_id, " .
512 "dict_value as objtype_name from " .
513 "IPBonds as ipb inner join RackObject as ro on ipb.object_id = ro.id " .
514 "left join Dictionary on objtype_id=dict_key natural join Chapter " .
515 "where ip between ${db_first} and ${db_last} " .
516 "and chapter_name = 'RackObjectType'" .
517 "order by ipb.type, object_name";
518 $result = useSelectBlade ($query);
519 while ($row = $result->fetch (PDO
::FETCH_ASSOC
))
521 $ip_bin = ip2long ($row['ip']);
522 if (!isset ($ret['addrlist'][$ip_bin]))
524 $ret['addrlist'][$ip_bin] = array();
525 $ret['addrlist'][$ip_bin]['ip'] = $row['ip'];
526 $ret['addrlist'][$ip_bin]['name'] = '';
527 $ret['addrlist'][$ip_bin]['reserved'] = 'no';
528 $ret['addrlist'][$ip_bin]['references'] = array();
529 $ret['addrlist'][$ip_bin]['lbrefs'] = array();
530 $ret['addrlist'][$ip_bin]['rsrefs'] = array();
533 foreach (array ('object_id', 'type', 'name') as $cname)
534 $tmp[$cname] = $row[$cname];
535 $quasiobject['name'] = $row['object_name'];
536 $quasiobject['objtype_id'] = $row['objtype_id'];
537 $quasiobject['objtype_name'] = $row['objtype_name'];
538 $tmp['object_name'] = displayedName ($quasiobject);
539 $ret['addrlist'][$ip_bin]['references'][] = $tmp;
541 $result->closeCursor();
544 $query = "select vs_id, inet_ntoa(vip) as ip, vport, proto, " .
545 "object_id, objtype_id, ro.name, dict_value as objtype_name from " .
546 "IPVirtualService as vs inner join IPLoadBalancer as lb on vs.id = lb.vs_id " .
547 "inner join RackObject as ro on lb.object_id = ro.id " .
548 "left join Dictionary on objtype_id=dict_key " .
549 "natural join Chapter " .
550 "where vip between ${db_first} and ${db_last} " .
551 "and chapter_name = 'RackObjectType'" .
552 "order by vport, proto, ro.name, object_id";
553 $result = useSelectBlade ($query);
554 while ($row = $result->fetch (PDO
::FETCH_ASSOC
))
556 $ip_bin = ip2long ($row['ip']);
557 if (!isset ($ret['addrlist'][$ip_bin]))
559 $ret['addrlist'][$ip_bin] = array();
560 $ret['addrlist'][$ip_bin]['ip'] = $row['ip'];
561 $ret['addrlist'][$ip_bin]['name'] = '';
562 $ret['addrlist'][$ip_bin]['reserved'] = 'no';
563 $ret['addrlist'][$ip_bin]['references'] = array();
564 $ret['addrlist'][$ip_bin]['lbrefs'] = array();
565 $ret['addrlist'][$ip_bin]['rsrefs'] = array();
567 $tmp = $qbject = array();
568 foreach (array ('object_id', 'vport', 'proto', 'vs_id') as $cname)
569 $tmp[$cname] = $row[$cname];
570 foreach (array ('name', 'objtype_id', 'objtype_name') as $cname)
571 $qobject[$cname] = $row[$cname];
572 $tmp['object_name'] = displayedName ($qobject);
573 $ret['addrlist'][$ip_bin]['lbrefs'][] = $tmp;
575 $result->closeCursor();
578 $query = "select inet_ntoa(rsip) as ip, rsport, rspool_id, rsp.name as rspool_name from " .
579 "IPRealServer as rs inner join IPRSPool as rsp on rs.rspool_id = rsp.id " .
580 "where rsip between ${db_first} and ${db_last} " .
581 "order by ip, rsport, rspool_id";
582 $result = useSelectBlade ($query);
583 while ($row = $result->fetch (PDO
::FETCH_ASSOC
))
585 $ip_bin = ip2long ($row['ip']);
586 if (!isset ($ret['addrlist'][$ip_bin]))
588 $ret['addrlist'][$ip_bin] = array();
589 $ret['addrlist'][$ip_bin]['ip'] = $row['ip'];
590 $ret['addrlist'][$ip_bin]['name'] = '';
591 $ret['addrlist'][$ip_bin]['reserved'] = 'no';
592 $ret['addrlist'][$ip_bin]['references'] = array();
593 $ret['addrlist'][$ip_bin]['lbrefs'] = array();
594 $ret['addrlist'][$ip_bin]['rsrefs'] = array();
597 foreach (array ('rspool_id', 'rsport', 'rspool_name') as $cname)
598 $tmp[$cname] = $row[$cname];
599 $ret['addrlist'][$ip_bin]['rsrefs'][] = $tmp;
605 // Don't require any records in IPAddress, but if there is one,
606 // merge the data between getting allocation list. Collect enough data
607 // to call displayedName() ourselves.
608 function getIPAddress ($ip=0)
611 $ret['bonds'] = array();
612 // FIXME: below two aren't neither filled in with data nor rendered (ticket:23)
613 $ret['outpf'] = array();
614 $ret['inpf'] = array();
615 $ret['vslist'] = array();
616 $ret['rslist'] = array();
619 $ret['reserved'] = 'no';
625 "where ip = INET_ATON('$ip') and (reserved = 'yes' or name != '')";
626 $result = $dbxlink->query ($query);
629 showError ('Query #1 failed', __FUNCTION__
);
632 if ($row = $result->fetch (PDO
::FETCH_ASSOC
))
635 $ret['name'] = $row['name'];
636 $ret['reserved'] = $row['reserved'];
638 $result->closeCursor();
643 "IPBonds.object_id as object_id, ".
644 "IPBonds.name as name, ".
645 "IPBonds.type as type, ".
646 "objtype_id, dict_value as objtype_name, " .
647 "RackObject.name as object_name ".
648 "from IPBonds join RackObject on IPBonds.object_id=RackObject.id ".
649 "left join Dictionary on objtype_id=dict_key natural join Chapter " .
650 "where IPBonds.ip=INET_ATON('$ip') ".
651 "and chapter_name = 'RackObjectType' " .
652 "order by RackObject.id, IPBonds.name";
653 $result = $dbxlink->query ($query);
655 while ($row = $result->fetch (PDO
::FETCH_ASSOC
))
657 $ret['bonds'][$count]['object_id'] = $row['object_id'];
658 $ret['bonds'][$count]['name'] = $row['name'];
659 $ret['bonds'][$count]['type'] = $row['type'];
661 $qo['name'] = $row['object_name'];
662 $qo['objtype_id'] = $row['objtype_id'];
663 $qo['objtype_name'] = $row['objtype_name'];
664 $ret['bonds'][$count]['object_name'] = displayedName ($qo);
668 $result->closeCursor();
671 $query = "select id, vport, proto, name from IPVirtualService where vip = inet_aton('${ip}')";
672 $result = $dbxlink->query ($query);
673 while ($row = $result->fetch (PDO
::FETCH_ASSOC
))
677 $ret['vslist'][] = $new;
679 $result->closeCursor();
682 $query = "select inservice, rsport, IPRSPool.id as pool_id, IPRSPool.name as poolname from " .
683 "IPRealServer inner join IPRSPool on rspool_id = IPRSPool.id " .
684 "where rsip = inet_aton('${ip}')";
685 $result = $dbxlink->query ($query);
686 while ($row = $result->fetch (PDO
::FETCH_ASSOC
))
690 $ret['rslist'][] = $new;
692 $result->closeCursor();
698 function bindIpToObject ($ip='', $object_id=0, $name='', $type='')
702 $range = getRangeByIp($ip);
704 return 'Non-existant ip address. Try adding IP range first';
706 $result = useInsertBlade
711 'ip' => "INET_ATON('$ip')",
712 'object_id' => "'${object_id}'",
713 'name' => "'${name}'",
714 'type' => "'${type}'"
717 return $result ?
'' : 'useInsertBlade() failed in bindIpToObject()';
720 // This function looks up 'has_problems' flag for 'T' atoms
721 // and modifies 'hl' key. May be, this should be better done
722 // in getRackData(). We don't honour 'skipped' key, because
723 // the function is also used for thumb creation.
724 function markupObjectProblems (&$rackData)
726 for ($i = $rackData['height']; $i > 0; $i--)
727 for ($locidx = 0; $locidx < 3; $locidx++
)
728 if ($rackData[$i][$locidx]['state'] == 'T')
730 $object = getObjectInfo ($rackData[$i][$locidx]['object_id']);
731 if ($object['has_problems'] == 'yes')
733 // Object can be already highlighted.
734 if (isset ($rackData[$i][$locidx]['hl']))
735 $rackData[$i][$locidx]['hl'] = $rackData[$i][$locidx]['hl'] . 'w';
737 $rackData[$i][$locidx]['hl'] = 'w';
742 function search_cmpObj ($a, $b)
744 return ($a['score'] > $b['score'] ?
-1 : 1);
747 // This function performs search and then calculates score for each result.
748 // Given previous search results in $objects argument, it adds new results
749 // to the array and updates score for existing results, if it is greater than
751 function mergeSearchResults (&$objects, $terms, $fieldname)
755 "select name, label, asset_no, barcode, ro.id, dict_key as objtype_id, " .
756 "dict_value as objtype_name, asset_no from RackObject as ro inner join Dictionary " .
757 "on objtype_id = dict_key natural join Chapter where chapter_name = 'RackObjectType' and ";
759 foreach (explode (' ', $terms) as $term)
761 if ($count) $query .= ' or ';
762 $query .= "${fieldname} like '%$term%'";
765 $result = useSelectBlade ($query);
766 // FIXME: this dead call was executed 4 times per 1 object search!
767 // $typeList = getObjectTypeList();
768 $clist = array ('id', 'name', 'label', 'asset_no', 'barcode', 'objtype_id', 'objtype_name');
769 while ($row = $result->fetch(PDO
::FETCH_ASSOC
))
771 foreach ($clist as $cname)
772 $object[$cname] = $row[$cname];
773 $object['score'] = 0;
774 $object['dname'] = displayedName ($object);
775 unset ($object['objtype_id']);
776 foreach (explode (' ', $terms) as $term)
777 if (strstr ($object['name'], $term))
778 $object['score'] +
= 1;
779 unset ($object['name']);
780 if (!isset ($objects[$row['id']]))
781 $objects[$row['id']] = $object;
782 elseif ($objects[$row['id']]['score'] < $object['score'])
783 $objects[$row['id']]['score'] = $object['score'];
788 function getObjectSearchResults ($terms)
791 mergeSearchResults ($objects, $terms, 'name');
792 mergeSearchResults ($objects, $terms, 'label');
793 mergeSearchResults ($objects, $terms, 'asset_no');
794 mergeSearchResults ($objects, $terms, 'barcode');
795 if (count ($objects) == 1)
796 usort ($objects, 'search_cmpObj');
800 // This function removes all colons and dots from a string.
801 function l2addressForDatabase ($string)
805 $pieces = explode (':', $string);
806 // This workaround is for SunOS ifconfig.
807 foreach ($pieces as &$byte)
808 if (strlen ($byte) == 1)
810 // And this workaround is for PHP.
812 $string = implode ('', $pieces);
813 $pieces = explode ('.', $string);
814 $string = implode ('', $pieces);
815 $string = strtoupper ($string);
819 function l2addressFromDatabase ($string)
821 switch (strlen ($string))
825 $ret = implode (':', str_split ($string, 2));
834 // The following 2 functions return previous and next rack IDs for
835 // a given rack ID. The order of racks is the same as in renderRackspace()
837 function getPrevIDforRack ($row_id = 0, $rack_id = 0)
839 if ($row_id <= 0 or $rack_id <= 0)
841 showError ('Invalid arguments passed', __FUNCTION__
);
844 $rackList = getRacksForRow ($row_id);
845 doubleLink ($rackList);
846 if (isset ($rackList[$rack_id]['prev_key']))
847 return $rackList[$rack_id]['prev_key'];
851 function getNextIDforRack ($row_id = 0, $rack_id = 0)
853 if ($row_id <= 0 or $rack_id <= 0)
855 showError ('Invalid arguments passed', __FUNCTION__
);
858 $rackList = getRacksForRow ($row_id);
859 doubleLink ($rackList);
860 if (isset ($rackList[$rack_id]['next_key']))
861 return $rackList[$rack_id]['next_key'];
865 // This function finds previous and next array keys for each array key and
866 // modifies its argument accordingly.
867 function doubleLink (&$array)
870 foreach (array_keys ($array) as $key)
874 $array[$key]['prev_key'] = $prev_key;
875 $array[$prev_key]['next_key'] = $key;
881 // After applying usort() to a rack list we will lose original array keys.
882 // This function restores the keys so they are equal to rack IDs.
883 function restoreRackIDs ($racks)
886 foreach ($racks as $rack)
887 $ret[$rack['id']] = $rack;
891 function sortTokenize ($a, $b)
897 $a = ereg_replace('[^a-zA-Z0-9]',' ',$a);
898 $a = ereg_replace('([0-9])([a-zA-Z])','\\1 \\2',$a);
899 $a = ereg_replace('([a-zA-Z])([0-9])','\\1 \\2',$a);
906 $b = ereg_replace('[^a-zA-Z0-9]',' ',$b);
907 $b = ereg_replace('([0-9])([a-zA-Z])','\\1 \\2',$b);
908 $b = ereg_replace('([a-zA-Z])([0-9])','\\1 \\2',$b);
913 $ar = explode(' ', $a);
914 $br = explode(' ', $b);
915 for ($i=0; $i<count($ar) && $i<count($br); $i++
)
918 if (is_numeric($ar[$i]) and is_numeric($br[$i]))
919 $ret = ($ar[$i]==$br[$i])?
0:($ar[$i]<$br[$i]?
-1:1);
921 $ret = strcasecmp($ar[$i], $br[$i]);
932 function sortByName ($a, $b)
934 return sortTokenize($a['name'], $b['name']);
937 function sortRacks ($a, $b)
939 return sortTokenize($a['row_name'] . ': ' . $a['name'], $b['row_name'] . ': ' . $b['name']);
947 function neq ($a, $b)
952 function countRefsOfType ($refs, $type, $eq)
955 foreach ($refs as $ref)
957 if ($eq($ref['type'], $type))
963 function sortEmptyPorts ($a, $b)
965 $objname_cmp = sortTokenize($a['Object_name'], $b['Object_name']);
966 if ($objname_cmp == 0)
968 return sortTokenize($a['Port_name'], $b['Port_name']);
973 function sortObjectAddressesAndNames ($a, $b)
975 $objname_cmp = sortTokenize($a['object_name'], $b['object_name']);
976 if ($objname_cmp == 0)
978 $name_a = (isset ($a['port_name'])) ?
$a['port_name'] : '';
979 $name_b = (isset ($b['port_name'])) ?
$b['port_name'] : '';
980 $objname_cmp = sortTokenize($name_a, $name_b);
981 if ($objname_cmp == 0)
982 sortTokenize($a['ip'], $b['ip']);
990 function sortAddresses ($a, $b)
992 $name_cmp = sortTokenize($a['name'], $b['name']);
995 return sortTokenize($a['ip'], $b['ip']);
1000 // This function expands port compat list into a matrix.
1001 function buildPortCompatMatrixFromList ($portTypeList, $portCompatList)
1004 // Create type matrix and markup compatible types.
1005 foreach (array_keys ($portTypeList) as $type1)
1006 foreach (array_keys ($portTypeList) as $type2)
1007 $matrix[$type1][$type2] = FALSE;
1008 foreach ($portCompatList as $pair)
1009 $matrix[$pair['type1']][$pair['type2']] = TRUE;
1013 function newPortForwarding($object_id, $localip, $localport, $remoteip, $remoteport, $proto, $description)
1017 $range = getRangeByIp($localip);
1019 return "$localip: Non existant ip";
1021 $range = getRangeByIp($remoteip);
1023 return "$remoteip: Non existant ip";
1025 if ( ($localport <= 0) or ($localport >= 65536) )
1026 return "$localport: invaild port";
1028 if ( ($remoteport <= 0) or ($remoteport >= 65536) )
1029 return "$remoteport: invaild port";
1032 "insert into PortForwarding set object_id='$object_id', localip=INET_ATON('$localip'), remoteip=INET_ATON('$remoteip'), localport='$localport', remoteport='$remoteport', proto='$proto', description='$description'";
1033 $result = $dbxlink->exec ($query);
1038 function deletePortForwarding($object_id, $localip, $localport, $remoteip, $remoteport, $proto)
1043 "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'";
1044 $result = $dbxlink->exec ($query);
1048 function updatePortForwarding($object_id, $localip, $localport, $remoteip, $remoteport, $proto, $description)
1053 "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'";
1054 $result = $dbxlink->exec ($query);
1058 function getObjectForwards($object_id)
1063 $ret['out'] = array();
1064 $ret['in'] = array();
1067 "dict_value as proto, ".
1068 "proto as proto_bin, ".
1069 "INET_NTOA(localip) as localip, ".
1071 "INET_NTOA(remoteip) as remoteip, ".
1073 "ipa1.name as local_addr_name, " .
1074 "ipa2.name as remote_addr_name, " .
1076 "from PortForwarding inner join Dictionary on proto = dict_key natural join Chapter ".
1077 "left join IPAddress as ipa1 on PortForwarding.localip = ipa1.ip " .
1078 "left join IPAddress as ipa2 on PortForwarding.remoteip = ipa2.ip " .
1079 "where object_id='$object_id' and chapter_name = 'Protocols' ".
1080 "order by localip, localport, proto, remoteip, remoteport";
1081 $result2 = $dbxlink->query ($query);
1083 while ($row = $result2->fetch (PDO
::FETCH_ASSOC
))
1085 foreach (array ('proto', 'proto_bin', 'localport', 'localip', 'remoteport', 'remoteip', 'description', 'local_addr_name', 'remote_addr_name') as $cname)
1086 $ret['out'][$count][$cname] = $row[$cname];
1089 $result2->closeCursor();
1093 "dict_value as proto, ".
1094 "proto as proto_bin, ".
1095 "INET_NTOA(localip) as localip, ".
1097 "INET_NTOA(remoteip) as remoteip, ".
1099 "PortForwarding.object_id as object_id, ".
1100 "RackObject.name as object_name, ".
1102 "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 ".
1103 "where IPBonds.object_id='$object_id' and chapter_name = 'Protocols' ".
1104 "order by remoteip, remoteport, proto, localip, localport";
1105 $result3 = $dbxlink->query ($query);
1107 while ($row = $result3->fetch (PDO
::FETCH_ASSOC
))
1109 foreach (array ('proto', 'proto_bin', 'localport', 'localip', 'remoteport', 'remoteip', 'object_id', 'object_name', 'description') as $cname)
1110 $ret['in'][$count][$cname] = $row[$cname];
1113 $result3->closeCursor();
1118 // This function returns an array of single element of object's FQDN attribute,
1119 // if FQDN is set. The next choice is object's common name, if it looks like a
1120 // hostname. Otherwise an array of all 'regular' IP addresses of the
1121 // object is returned (which may appear 0 and more elements long).
1122 function findAllEndpoints ($object_id, $fallback = '')
1124 $values = getAttrValues ($object_id);
1125 foreach ($values as $record)
1126 if ($record['name'] == 'FQDN' && !empty ($record['value']))
1127 return array ($record['value']);
1128 $addresses = getObjectAddresses ($object_id);
1130 foreach ($addresses as $idx => $address)
1131 if ($address['type'] == 'regular')
1132 $regular[] = $address['ip'];
1133 if (!count ($regular) && !empty ($fallback))
1134 return array ($fallback);
1138 // Some records in the dictionary may be written as plain text or as Wiki
1139 // link in the following syntax:
1141 // 2. [[word URL]] // FIXME: this isn't working
1142 // 3. [[word word word | URL]]
1143 // This function parses the line and returns text suitable for either A
1144 // (rendering <A HREF>) or O (for <OPTION>).
1145 function parseWikiLink ($line, $which, $strip_optgroup = FALSE)
1147 if (preg_match ('/^\[\[.+\]\]$/', $line) == 0)
1149 if ($strip_optgroup)
1150 return ereg_replace ('^.+\^', '', $line);
1154 $line = preg_replace ('/^\[\[(.+)\]\]$/', '$1', $line);
1155 $s = explode ('|', $line);
1156 $o_value = trim ($s[0]);
1157 if ($strip_optgroup)
1158 $o_value = ereg_replace ('^.+\^', '', $o_value);
1159 $a_value = trim ($s[1]);
1161 return "<a href='${a_value}'>${o_value}</a>";
1166 function buildVServiceName ($vsinfo = NULL)
1168 if ($vsinfo == NULL)
1170 showError ('NULL argument', __FUNCTION__
);
1173 return $vsinfo['vip'] . ':' . $vsinfo['vport'] . '/' . $vsinfo['proto'];
1176 // rackspace usage for a single rack
1177 // (T + W + U) / (height * 3 - A)
1178 function getRSUforRack ($data = NULL)
1182 showError ('Invalid argument', __FUNCTION__
);
1185 $counter = array ('A' => 0, 'U' => 0, 'T' => 0, 'W' => 0, 'F' => 0);
1186 for ($unit_no = $data['height']; $unit_no > 0; $unit_no--)
1187 for ($locidx = 0; $locidx < 3; $locidx++
)
1188 $counter[$data[$unit_no][$locidx]['state']]++
;
1189 return ($counter['T'] +
$counter['W'] +
$counter['U']) / ($counter['T'] +
$counter['W'] +
$counter['U'] +
$counter['F']);
1193 function getRSUforRackRow ($rowData = NULL)
1195 if ($rowData === NULL)
1197 showError ('Invalid argument', __FUNCTION__
);
1200 if (!count ($rowData))
1202 $counter = array ('A' => 0, 'U' => 0, 'T' => 0, 'W' => 0, 'F' => 0);
1204 foreach (array_keys ($rowData) as $rack_id)
1206 $data = getRackData ($rack_id);
1207 $total_height +
= $data['height'];
1208 for ($unit_no = $data['height']; $unit_no > 0; $unit_no--)
1209 for ($locidx = 0; $locidx < 3; $locidx++
)
1210 $counter[$data[$unit_no][$locidx]['state']]++
;
1212 return ($counter['T'] +
$counter['W'] +
$counter['U']) / ($counter['T'] +
$counter['W'] +
$counter['U'] +
$counter['F']);
1215 function getObjectCount ($rackData)
1218 for ($i = $rackData['height']; $i > 0; $i--)
1219 for ($locidx = 0; $locidx < 3; $locidx++
)
1222 $rackData[$i][$locidx]['state'] == 'T' and
1223 !in_array ($rackData[$i][$locidx]['object_id'], $objects)
1225 $objects[] = $rackData[$i][$locidx]['object_id'];
1226 return count ($objects);
1229 // Perform substitutions and return resulting string
1230 function apply_macros ($macros, $subject)
1233 foreach ($macros as $search => $replace)
1234 $ret = str_replace ($search, $replace, $ret);
1238 // Make sure the string is always wrapped with LF characters
1239 function lf_wrap ($str)
1241 $ret = trim ($str, "\r\n");
1247 // Adopted from Mantis BTS code.
1248 function string_insert_hrefs ($s)
1250 if (getConfigVar ('DETECT_URLS') != 'yes')
1252 # Find any URL in a string and replace it by a clickable link
1253 $s = preg_replace( '/(([[:alpha:]][-+.[:alnum:]]*):\/\/(%[[:digit:]A-Fa-f]{2}|[-_.!~*\';\/?%^\\\\:@&={\|}+$#\(\),\[\][:alnum:]])+)/se',
1254 "'<a href=\"'.rtrim('\\1','.').'\">\\1</a> [<a href=\"'.rtrim('\\1','.').'\" target=\"_blank\">^</a>]'",
1256 $s = preg_replace( '/\b' . email_regex_simple() . '\b/i',
1257 '<a href="mailto:\0">\0</a>',
1263 function email_regex_simple ()
1265 return "(([a-z0-9!#*+\/=?^_{|}~-]+(?:\.[a-z0-9!#*+\/=?^_{|}~-]+)*)" . # recipient
1266 "\@((?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?))"; # @domain
1269 // Parse AUTOPORTS_CONFIG and return a list of generated pairs (port_type, port_name)
1270 // for the requested object_type_id.
1271 function getAutoPorts ($type_id)
1274 $typemap = explode (';', str_replace (' ', '', getConfigVar ('AUTOPORTS_CONFIG')));
1275 foreach ($typemap as $equation)
1277 $tmp = explode ('=', $equation);
1278 if (count ($tmp) != 2)
1280 $objtype_id = $tmp[0];
1281 if ($objtype_id != $type_id)
1283 $portlist = $tmp[1];
1284 foreach (explode ('+', $portlist) as $product)
1286 $tmp = explode ('*', $product);
1287 if (count ($tmp) != 3)
1290 $port_type = $tmp[1];
1292 for ($i = 0; $i < $nports; $i++
)
1293 $ret[] = array ('type' => $port_type, 'name' => @sprintf
($format, $i));
1299 // Find if a particular tag id exists on the tree, then attach the
1300 // given child tag to it. If the parent tag doesn't exist, return FALSE.
1301 function attachChildTag (&$tree, $parent_id, $child_id, $child_info)
1303 foreach ($tree as $tagid => $taginfo)
1305 if ($tagid == $parent_id)
1307 $tree[$tagid]['kids'][$child_id] = $child_info;
1310 elseif (attachChildTag ($tree[$tagid]['kids'], $parent_id, $child_id, $child_info))
1316 // Build a tree from the tag list and return it.
1317 function getTagTree ()
1320 $taglist = getTagList();
1321 while (count ($taglist) > 0)
1324 foreach ($taglist as $tagid => $taginfo)
1326 $taginfo['kids'] = array();
1327 if ($taginfo['parent_id'] == NULL)
1329 $tagtree[$tagid] = $taginfo;
1331 unset ($taglist[$tagid]);
1333 elseif (attachChildTag ($tagtree, $taginfo['parent_id'], $tagid, $taginfo))
1336 unset ($taglist[$tagid]);
1339 if (!$picked) // Only orphaned items on the list.
1345 function serializeTags ($trail)
1349 foreach ($trail as $taginfo)
1351 $ret .= $comma . $taginfo['tag'];
1357 // a helper for getTrailExpansion()
1358 function traceTrail ($tree, $trail)
1360 // For each tag find its path from the root, then combine items
1361 // of all paths and add them to the trail, if they aren't there yet.
1363 foreach ($tree as $taginfo1)
1366 foreach ($trail as $taginfo2)
1367 if ($taginfo1['id'] == $taginfo2['id'])
1372 if (count ($taginfo1['kids']) > 0)
1374 $subsearch = traceTrail ($taginfo1['kids'], $trail);
1375 if (count ($subsearch))
1378 $ret = array_merge ($ret, $subsearch);
1387 // For each tag add all its parent tags onto the list. Don't expect anything
1388 // except user's tags on the trail.
1389 function getTrailExpansion ($trail)
1391 $tree = getTagTree();
1392 return traceTrail ($tree, $trail);
1395 // Return the list of missing implicit tags.
1396 function getImplicitTags ($oldtags)
1399 $newtags = getTrailExpansion ($oldtags);
1400 foreach ($newtags as $newtag)
1402 $already_exists = FALSE;
1403 foreach ($oldtags as $oldtag)
1404 if ($newtag['id'] == $oldtag['id'])
1406 $already_exists = TRUE;
1409 if ($already_exists)
1411 $ret[] = array ('id' => $newtag['id'], 'tag' => $newtag['tag'], 'parent_id' => $newtag['parent_id']);
1416 // Minimize the trail: exclude all implicit tags and return the resulting trail.
1417 function getExplicitTagsOnly ($trail, $tree = NULL)
1420 $tree = getTagTree();
1422 foreach ($tree as $taginfo)
1424 if (isset ($taginfo['kids']))
1426 $harvest = getExplicitTagsOnly ($trail, $taginfo['kids']);
1427 if (count ($harvest) > 0)
1429 $ret = array_merge ($ret, $harvest);
1433 // This tag isn't implicit, test is for being explicit.
1434 foreach ($trail as $testtag)
1435 if ($taginfo['id'] == $testtag['id'])
1444 // Maximize the trail: for each tag add all tags, for which it is direct or indirect parent.
1445 // Unlike other functions, this one accepts and returns a list of integer tag IDs, not
1446 // a list of tag structures.
1447 function complementByKids ($idlist, $tree = NULL, $getall = FALSE)
1450 $tree = getTagTree();
1451 $getallkids = $getall;
1453 foreach ($tree as $taginfo)
1455 foreach ($idlist as $test_id)
1456 if ($getall or $taginfo['id'] == $test_id)
1458 $ret[] = $taginfo['id'];
1459 // Once matched node makes all sub-nodes match, but don't make
1460 // a mistake of matching every other node at the current level.
1464 if (isset ($taginfo['kids']))
1465 $ret = array_merge ($ret, complementByKids ($idlist, $taginfo['kids'], $getallkids));
1466 $getallkids = FALSE;
1471 function loadRackObjectAutoTags()
1473 assertUIntArg ('object_id');
1474 $object_id = $_REQUEST['object_id'];
1475 $oinfo = getObjectInfo ($object_id);
1477 $ret[] = array ('tag' => '$id_' . $_REQUEST['object_id']);
1478 $ret[] = array ('tag' => '$any_object');
1482 function loadIPv4PrefixAutoTags()
1484 assertUIntArg ('id');
1485 $subnet = getIPRange ($_REQUEST['id']);
1487 $ret[] = array ('tag' => '$id_' . $_REQUEST['id']);
1488 $ret[] = array ('tag' => '$ipv4net-' . str_replace ('.', '-', $subnet['ip']) . '-' . $subnet['mask']);
1489 // FIXME: find and list tags for all parent networks
1490 $ret[] = array ('tag' => '$any_ipv4net');
1491 $ret[] = array ('tag' => '$any_net');
1495 function loadRackAutoTags()
1497 assertUIntArg ('rack_id');
1499 $ret[] = array ('tag' => '$id_' . $_REQUEST['rack_id']);
1500 $ret[] = array ('tag' => '$any_rack');
1504 function loadIPv4AddressAutoTags()
1506 assertIPv4Arg ('ip');
1508 $ret[] = array ('tag' => '$ipv4net-' . str_replace ('.', '-', $subnet['ip']) . '-32');
1509 // FIXME: find and list tags for all parent networks
1510 $ret[] = array ('tag' => '$any_ipv4net');
1511 $ret[] = array ('tag' => '$any_net');
1515 function loadIPv4VSAutoTags()
1517 assertUIntArg ('id');
1519 $ret[] = array ('tag' => '$id_' . $_REQUEST['id']);
1520 $ret[] = array ('tag' => '$any_ipv4vs');
1521 $ret[] = array ('tag' => '$any_vs');
1525 function loadIPv4RSPoolAutoTags()
1527 assertUIntArg ('pool_id');
1529 $ret[] = array ('tag' => '$id_' . $_REQUEST['pool_id']);
1530 $ret[] = array ('tag' => '$any_ipv4rspool');
1531 $ret[] = array ('tag' => '$any_rspool');
1535 function getGlobalAutoTags()
1537 global $remote_username, $accounts;
1540 foreach ($accounts as $a)
1541 if ($a['user_name'] == $remote_username)
1543 $user_id = $a['user_id'];
1546 $ret[] = array ('tag' => '$username_' . $remote_username);
1547 $ret[] = array ('tag' => '$userid_' . $user_id);
1551 // Build a tag trail from supplied tag id list and return it.
1552 function buildTrailFromIds ($tagidlist)
1554 $taglist = getTagList();
1556 foreach ($tagidlist as $tag_id)
1557 if (isset ($taglist[$tag_id]))
1558 $ret[] = $taglist[$tag_id];