r1494 + getRSUforRackRow(): initialize variable to 0
[racktables] / inc / functions.php
1 <?php
2 /*
3 *
4 * This file is a library of computational functions for RackTables.
5 *
6 */
7
8 $loclist[0] = 'front';
9 $loclist[1] = 'interior';
10 $loclist[2] = 'rear';
11 $loclist['front'] = 0;
12 $loclist['interior'] = 1;
13 $loclist['rear'] = 2;
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;
26
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)
31 {
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'];
36 else
37 return "[${objectData['objtype_name']}]";
38 }
39
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
42 // template.
43 function rectHeight ($rackData, $startRow, $template_idx)
44 {
45 $height = 0;
46 // The first met object_id is used to match all the folowing IDs.
47 $object_id = 0;
48 global $template;
49 do
50 {
51 for ($locidx = 0; $locidx < 3; $locidx++)
52 {
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
55 // the first row.
56 if ($template[$template_idx][$locidx])
57 {
58 if (isset ($rackData[$startRow - $height][$locidx]['skipped']))
59 break 2;
60 if (isset ($rackData[$startRow - $height][$locidx]['rowspan']))
61 break 2;
62 if (isset ($rackData[$startRow - $height][$locidx]['colspan']))
63 break 2;
64 if ($rackData[$startRow - $height][$locidx]['state'] != 'T')
65 break 2;
66 if ($object_id == 0)
67 $object_id = $rackData[$startRow - $height][$locidx]['object_id'];
68 if ($object_id != $rackData[$startRow - $height][$locidx]['object_id'])
69 break 2;
70 }
71 }
72 // If the first row can't offer anything, bail out.
73 if ($height == 0 and $object_id == 0)
74 break;
75 $height++;
76 }
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";
81 return $height;
82 }
83
84 // This function marks atoms to be avoided by rectHeight() and assigns rowspan/colspan
85 // attributes.
86 function markSpan (&$rackData, $startRow, $maxheight, $template_idx)
87 {
88 global $template, $templateWidth;
89 $colspan = 0;
90 for ($height = 0; $height < $maxheight; $height++)
91 {
92 for ($locidx = 0; $locidx < 3; $locidx++)
93 {
94 if ($template[$template_idx][$locidx])
95 {
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.
99 if ($colspan != 0)
100 $rackData[$startRow - $height][$locidx]['skipped'] = TRUE;
101 else
102 {
103 $colspan = $templateWidth[$template_idx];
104 if ($colspan >= 1)
105 $rackData[$startRow - $height][$locidx]['colspan'] = $colspan;
106 if ($maxheight >= 1)
107 $rackData[$startRow - $height][$locidx]['rowspan'] = $maxheight;
108 }
109 }
110 }
111 }
112 return;
113 }
114
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)
119 {
120 if ($rackData == NULL)
121 {
122 showError ('Invalid rackData', __FUNCTION__);
123 return;
124 }
125 for ($i = $rackData['height']; $i > 0; $i--)
126 while (markBestSpan ($rackData, $i));
127 }
128
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)
132 {
133 global $template, $templateWidth;
134 for ($j = 0; $j < 6; $j++)
135 {
136 $height[$j] = rectHeight ($rackData, $i, $j);
137 $square[$j] = $height[$j] * $templateWidth[$j];
138 }
139 // find the widest rectangle of those with maximal height
140 $maxsquare = max ($square);
141 if (!$maxsquare)
142 return FALSE;
143 $best_template_index = 0;
144 for ($j = 0; $j < 6; $j++)
145 if ($square[$j] == $maxsquare)
146 {
147 $best_template_index = $j;
148 $bestheight = $height[$j];
149 break;
150 }
151 // distribute span marks
152 markSpan ($rackData, $i, $bestheight, $best_template_index);
153 return TRUE;
154 }
155
156 function delRow ($row_id = 0)
157 {
158 if ($row_id == 0)
159 {
160 showError ('Not all required args are present.', __FUNCTION__);
161 return;
162 }
163 if (!isset ($_REQUEST['confirmed']) || $_REQUEST['confirmed'] != 'true')
164 {
165 echo "Press <a href='?op=del_row&row_id=${row_id}&confirmed=true'>here</a> to confirm rack row deletion.";
166 return;
167 }
168 global $dbxlink;
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)
172 {
173 showError ('Marked ' . $result.rowCount() . ' rows as deleted, but expected 1', __FUNCTION__);
174 return;
175 }
176 echo 'OK<br>';
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>.";
179 }
180
181 function delRack ($rack_id = 0)
182 {
183 if ($rack_id == 0)
184 {
185 showError ('Not all required args are present.', __FUNCTION__);
186 return;
187 }
188 if (!isset ($_REQUEST['confirmed']) || $_REQUEST['confirmed'] != 'true')
189 {
190 echo "Press <a href='?op=del_rack&rack_id=${rack_id}&confirmed=true'>here</a> to confirm rack deletion.";
191 return;
192 }
193 global $dbxlink;
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)
197 {
198 showError ('Marked ' . $result.rowCount() . ' rows as deleted, but expected 1', __FUNCTION__);
199 return;
200 }
201 echo 'OK<br>';
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>.";
204 }
205
206 function delObject ($object_id = 0)
207 {
208 if ($object_id == 0)
209 {
210 showError ('Not all required args are present.', __FUNCTION__);
211 return;
212 }
213 if (!isset ($_REQUEST['confirmed']) || $_REQUEST['confirmed'] != 'true')
214 {
215 echo "Press <a href='?op=del_object&object_id=${object_id}&confirmed=true'>here</a> to confirm object deletion.";
216 return;
217 }
218 global $dbxlink;
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)
222 {
223 showError ('Marked ' . $result.rowCount() . ' rows as deleted, but expected 1', __FUNCTION__);
224 return;
225 }
226 echo 'OK<br>';
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>.";
229 }
230
231 // We can mount 'F' atoms and unmount our own 'T' atoms.
232 function applyObjectMountMask (&$rackData, $object_id)
233 {
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'])
237 {
238 case 'F':
239 $rackData[$unit_no][$locidx]['enabled'] = TRUE;
240 break;
241 case 'T':
242 $rackData[$unit_no][$locidx]['enabled'] = ($rackData[$unit_no][$locidx]['object_id'] == $object_id);
243 break;
244 default:
245 $rackData[$unit_no][$locidx]['enabled'] = FALSE;
246 }
247 }
248
249 // Design change means transition between 'F' and 'A' and back.
250 function applyRackDesignMask (&$rackData)
251 {
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'])
255 {
256 case 'F':
257 case 'A':
258 $rackData[$unit_no][$locidx]['enabled'] = TRUE;
259 break;
260 default:
261 $rackData[$unit_no][$locidx]['enabled'] = FALSE;
262 }
263 }
264
265 // The same for 'F' and 'U'.
266 function applyRackProblemMask (&$rackData)
267 {
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'])
271 {
272 case 'F':
273 case 'U':
274 $rackData[$unit_no][$locidx]['enabled'] = TRUE;
275 break;
276 default:
277 $rackData[$unit_no][$locidx]['enabled'] = FALSE;
278 }
279 }
280
281 // This mask should allow toggling 'T' and 'W' on object's rackspace.
282 function applyObjectProblemMask (&$rackData)
283 {
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'])
287 {
288 case 'T':
289 case 'W':
290 $rackData[$unit_no][$locidx]['enabled'] = ($rackData[$unit_no][$locidx]['object_id'] == $object_id);
291 break;
292 default:
293 $rackData[$unit_no][$locidx]['enabled'] = FALSE;
294 }
295 }
296
297 // This function highlights specified object (and removes previous highlight).
298 function highlightObject (&$rackData, $object_id)
299 {
300 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
301 for ($locidx = 0; $locidx < 3; $locidx++)
302 if
303 (
304 $rackData[$unit_no][$locidx]['state'] == 'T' and
305 $rackData[$unit_no][$locidx]['object_id'] == $object_id
306 )
307 $rackData[$unit_no][$locidx]['hl'] = 'h';
308 else
309 unset ($rackData[$unit_no][$locidx]['hl']);
310 }
311
312 // This function marks atoms to selected or not depending on their current state.
313 function markupAtomGrid (&$data, $checked_state)
314 {
315 for ($unit_no = $data['height']; $unit_no > 0; $unit_no--)
316 for ($locidx = 0; $locidx < 3; $locidx++)
317 {
318 if (!($data[$unit_no][$locidx]['enabled'] === TRUE))
319 continue;
320 if ($data[$unit_no][$locidx]['state'] == $checked_state)
321 $data[$unit_no][$locidx]['checked'] = ' checked';
322 else
323 $data[$unit_no][$locidx]['checked'] = '';
324 }
325 }
326
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)
332 {
333 $rack_id = $rackData['id'];
334 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
335 for ($locidx = 0; $locidx < 3; $locidx++)
336 {
337 if ($rackData[$unit_no][$locidx]['enabled'] != TRUE)
338 continue;
339 $inputname = "atom_${rack_id}_${unit_no}_${locidx}";
340 if (isset ($_REQUEST[$inputname]) and $_REQUEST[$inputname] == 'on')
341 $rackData[$unit_no][$locidx]['checked'] = ' checked';
342 else
343 $rackData[$unit_no][$locidx]['checked'] = '';
344 }
345 }
346
347 function binMaskFromDec ($maskL)
348 {
349 $binmask=0;
350 for ($i=0; $i<$maskL; $i++)
351 {
352 $binmask*=2;
353 $binmask+=1;
354 }
355 for ($i=$maskL; $i<32; $i++)
356 {
357 $binmask*=2;
358 }
359 return $binmask;
360 }
361
362 function binInvMaskFromDec ($maskL)
363 {
364 $binmask=0;
365 for ($i=0; $i<$maskL; $i++)
366 {
367 $binmask*=2;
368 }
369 for ($i=$maskL; $i<32; $i++)
370 {
371 $binmask*=2;
372 $binmask+=1;
373 }
374 return $binmask;
375 }
376
377 function addRange ($range='', $name='', $is_bcast = FALSE)
378 {
379 // $range is in x.x.x.x/x format, split into ip/mask vars
380 $rangeArray = explode('/', $range);
381 $ip = $rangeArray[0];
382 $mask = $rangeArray[1];
383
384 $ipL = ip2long($ip);
385 $maskL = ip2long($mask);
386 if ($ipL == -1 || $ipL === FALSE)
387 return 'Bad ip address';
388 if ($mask < 32 && $mask > 0)
389 $maskL = $mask;
390 else
391 {
392 $maskB = decbin($maskL);
393 if (strlen($maskB)!=32)
394 return 'Bad mask';
395 $ones=0;
396 $zeroes=FALSE;
397 foreach( str_split ($maskB) as $digit)
398 {
399 if ($digit == '0')
400 $zeroes = TRUE;
401 if ($digit == '1')
402 {
403 $ones++;
404 if ($zeroes == TRUE)
405 return 'Bad mask';
406 }
407 }
408 $maskL = $ones;
409 }
410 $binmask = binMaskFromDec($maskL);
411 $ipL = $ipL & $binmask;
412
413 $query =
414 "select ".
415 "id, ip, mask, name ".
416 "from IPRanges ";
417
418
419 global $dbxlink;
420
421 $result = $dbxlink->query ($query);
422
423 while ($row = $result->fetch (PDO::FETCH_ASSOC))
424 {
425 $otherip = $row['ip'];
426 $othermask = binMaskFromDec($row['mask']);
427 // 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";
428 // 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";
429 // echo "\n";
430 // flush();
431 if (($otherip & $othermask) == ($ipL & $othermask))
432 return "This subnet intersects with ".long2ip($row['ip'])."/${row['mask']}";
433 if (($otherip & $binmask) == ($ipL & $binmask))
434 return "This subnet intersects with ".long2ip($row['ip'])."/${row['mask']}";
435 }
436 $result->closeCursor();
437 $query =
438 "insert into IPRanges set ip=".sprintf('%u', $ipL).", mask='$maskL', name='$name'";
439 $result = $dbxlink->exec ($query);
440
441 if ($is_bcast and $maskL < 31)
442 {
443 $network_addr = long2ip ($ipL);
444 $broadcast_addr = long2ip ($ipL | binInvMaskFromDec ($maskL));
445 updateAddress ($network_addr, 'network', 'yes');
446 updateAddress ($broadcast_addr, 'broadcast', 'yes');
447 }
448 return '';
449 }
450
451 function getIPRange ($id = 0)
452 {
453 global $dbxlink;
454 $query =
455 "select ".
456 "id as IPRanges_id, ".
457 "INET_NTOA(ip) as IPRanges_ip, ".
458 "mask as IPRanges_mask, ".
459 "name as IPRanges_name ".
460 "from IPRanges ".
461 "where id = '$id'";
462 $result = $dbxlink->query ($query);
463 if ($result == NULL)
464 return NULL;
465 $ret = array();
466 $row = $result->fetch (PDO::FETCH_ASSOC);
467 if ($row == NULL)
468 return $ret;
469 $ret['id'] = $row['IPRanges_id'];
470 $ret['ip'] = $row['IPRanges_ip'];
471 $ret['ip_bin'] = ip2long ($row['IPRanges_ip']);
472 $ret['mask_bin'] = binMaskFromDec($row['IPRanges_mask']);
473 $ret['mask_bin_inv'] = binInvMaskFromDec($row['IPRanges_mask']);
474 $ret['name'] = $row['IPRanges_name'];
475 $ret['mask'] = $row['IPRanges_mask'];
476 $ret['addrlist'] = array();
477 $result->closeCursor();
478 // We risk losing some significant bits in an unsigned 32bit integer,
479 // unless it is converted to a string.
480 $db_first = "'" . sprintf ('%u', 0x00000000 + $ret['ip_bin'] & $ret['mask_bin']) . "'";
481 $db_last = "'" . sprintf ('%u', 0x00000000 + $ret['ip_bin'] | ($ret['mask_bin_inv'])) . "'";
482
483 // Don't try to build up the whole structure in a single pass. Request
484 // the list of user comments and reservations and merge allocations in
485 // at a latter point.
486 $query =
487 "select INET_NTOA(ip) as ip, name, reserved from IPAddress " .
488 "where ip between ${db_first} and ${db_last} " .
489 "and (reserved = 'yes' or name != '')";
490 $ipa_res = $dbxlink->query ($query);
491 if ($ipa_res == NULL)
492 return $ret;
493 while ($row = $ipa_res->fetch (PDO::FETCH_ASSOC))
494 {
495 $ip_bin = ip2long ($row['ip']);
496 $ret['addrlist'][$ip_bin] = $row;
497 $tmp = array();
498 foreach (array ('ip', 'name', 'reserved') as $cname)
499 $tmp[$cname] = $row[$cname];
500 $tmp['references'] = array();
501 $ret['addrlist'][$ip_bin] = $tmp;
502 }
503 $ipa_res->closeCursor();
504
505 $query =
506 "select INET_NTOA(ipb.ip) as ip, ro.id as object_id, " .
507 "ro.name as object_name, ipb.name, ipb.type, objtype_id, " .
508 "dict_value as objtype_name from " .
509 "IPBonds as ipb inner join RackObject as ro on ipb.object_id = ro.id " .
510 "left join Dictionary on objtype_id=dict_key natural join Chapter " .
511 "where ip between ${db_first} and ${db_last} " .
512 "and chapter_name = 'RackObjectType'" .
513 "order by ipb.type, object_name";
514 $ipb_res = $dbxlink->query ($query);
515 while ($row = $ipb_res->fetch (PDO::FETCH_ASSOC))
516 {
517 $ip_bin = ip2long ($row['ip']);
518 if (!isset ($ret['addrlist'][$ip_bin]))
519 {
520 $ret['addrlist'][$ip_bin] = array();
521 $ret['addrlist'][$ip_bin]['ip'] = $row['ip'];
522 $ret['addrlist'][$ip_bin]['name'] = '';
523 $ret['addrlist'][$ip_bin]['reserved'] = 'no';
524 $ret['addrlist'][$ip_bin]['references'] = array();
525 }
526 $tmp = array();
527 foreach (array ('object_id', 'type', 'name') as $cname)
528 $tmp[$cname] = $row[$cname];
529 $quasiobject['name'] = $row['object_name'];
530 $quasiobject['objtype_id'] = $row['objtype_id'];
531 $quasiobject['objtype_name'] = $row['objtype_name'];
532 $tmp['object_name'] = displayedName ($quasiobject);
533 $ret['addrlist'][$ip_bin]['references'][] = $tmp;
534 }
535 $ipb_res->closeCursor();
536
537 return $ret;
538 }
539
540 // Don't require any records in IPAddress, but if there is one,
541 // merge the data between getting allocation list. Collect enough data
542 // to call displayedName() ourselves.
543 function getIPAddress ($ip=0)
544 {
545 $ret = array();
546 $ret['bonds'] = array();
547 $ret['outpf'] = array();
548 $ret['inpf'] = array();
549 $ret['exists'] = 0;
550 $ret['name'] = '';
551 $ret['reserved'] = 'no';
552 global $dbxlink;
553 $query =
554 "select ".
555 "name, reserved ".
556 "from IPAddress ".
557 "where ip = INET_ATON('$ip') and (reserved = 'yes' or name != '')";
558 $result = $dbxlink->query ($query);
559 if ($result == NULL)
560 return NULL;
561 if ($row = $result->fetch (PDO::FETCH_ASSOC))
562 {
563 $ret['exists'] = 1;
564 $ret['name'] = $row['name'];
565 $ret['reserved'] = $row['reserved'];
566 }
567 $result->closeCursor();
568
569 $query =
570 "select ".
571 "IPBonds.object_id as object_id, ".
572 "IPBonds.name as name, ".
573 "IPBonds.type as type, ".
574 "objtype_id, dict_value as objtype_name, " .
575 "RackObject.name as object_name ".
576 "from IPBonds join RackObject on IPBonds.object_id=RackObject.id ".
577 "left join Dictionary on objtype_id=dict_key natural join Chapter " .
578 "where IPBonds.ip=INET_ATON('$ip') ".
579 "and chapter_name = 'RackObjectType' " .
580 "order by RackObject.id, IPBonds.name";
581 $result = $dbxlink->query ($query);
582 $count=0;
583 while ($row = $result->fetch (PDO::FETCH_ASSOC))
584 {
585 $ret['bonds'][$count]['object_id'] = $row['object_id'];
586 $ret['bonds'][$count]['name'] = $row['name'];
587 $ret['bonds'][$count]['type'] = $row['type'];
588 $qo = array();
589 $qo['name'] = $row['object_name'];
590 $qo['objtype_id'] = $row['objtype_id'];
591 $qo['objtype_name'] = $row['objtype_name'];
592 $ret['bonds'][$count]['object_name'] = displayedName ($qo);
593 $count++;
594 $ret['exists'] = 1;
595 }
596 $result->closeCursor();
597
598 return $ret;
599 }
600
601 function bindIpToObject ($ip='', $object_id=0, $name='', $type='')
602 {
603 global $dbxlink;
604
605 $range = getRangeByIp($ip);
606 if (!$range)
607 return 'Non-existant ip address. Try adding IP range first';
608
609 $result = useInsertBlade
610 (
611 'IPBonds',
612 array
613 (
614 'ip' => "INET_ATON('$ip')",
615 'object_id' => "'${object_id}'",
616 'name' => "'${name}'",
617 'type' => "'${type}'"
618 )
619 );
620 return $result ? '' : 'useInsertBlade() failed in bindIpToObject()';
621 }
622
623 // This function looks up 'has_problems' flag for 'T' atoms
624 // and modifies 'hl' key. May be, this should be better done
625 // in getRackData(). We don't honour 'skipped' key, because
626 // the function is also used for thumb creation.
627 function markupObjectProblems (&$rackData)
628 {
629 for ($i = $rackData['height']; $i > 0; $i--)
630 for ($locidx = 0; $locidx < 3; $locidx++)
631 if ($rackData[$i][$locidx]['state'] == 'T')
632 {
633 $object = getObjectInfo ($rackData[$i][$locidx]['object_id']);
634 if ($object['has_problems'] == 'yes')
635 {
636 // Object can be already highlighted.
637 if (isset ($rackData[$i][$locidx]['hl']))
638 $rackData[$i][$locidx]['hl'] = $rackData[$i][$locidx]['hl'] . 'w';
639 else
640 $rackData[$i][$locidx]['hl'] = 'w';
641 }
642 }
643 }
644
645 function search_cmpObj ($a, $b)
646 {
647 return ($a['score'] > $b['score'] ? -1 : 1);
648 }
649
650 // This function performs search and then calculates score for each result.
651 // Given previous search results in $objects argument, it adds new results
652 // to the array and updates score for existing results, if it is greater than
653 // existing score.
654 function mergeSearchResults (&$objects, $terms, $fieldname)
655 {
656 global $dbxlink;
657 $query =
658 "select name, label, asset_no, barcode, ro.id, dict_key as objtype_id, " .
659 "dict_value as objtype_name, asset_no from RackObject as ro inner join Dictionary " .
660 "on objtype_id = dict_key natural join Chapter where chapter_name = 'RackObjectType' and ";
661 $count = 0;
662 foreach (explode (' ', $terms) as $term)
663 {
664 if ($count) $query .= ' or ';
665 $query .= "${fieldname} like '%$term%'";
666 $count++;
667 }
668 $query .= "";
669 $result = $dbxlink->query($query);
670 if ($result == NULL)
671 {
672 showError ("SQL query failed", __FUNCTION__);
673 return NULL;
674 }
675 // FIXME: this dead call was executed 4 times per 1 object search!
676 // $typeList = getObjectTypeList();
677 $clist = array ('id', 'name', 'label', 'asset_no', 'barcode', 'objtype_id', 'objtype_name');
678 while ($row = $result->fetch(PDO::FETCH_ASSOC))
679 {
680 foreach ($clist as $cname)
681 $object[$cname] = $row[$cname];
682 $object['score'] = 0;
683 $object['dname'] = displayedName ($object);
684 unset ($object['objtype_id']);
685 foreach (explode (' ', $terms) as $term)
686 if (strstr ($object['name'], $term))
687 $object['score'] += 1;
688 unset ($object['name']);
689 if (!isset ($objects[$row['id']]))
690 $objects[$row['id']] = $object;
691 elseif ($objects[$row['id']]['score'] < $object['score'])
692 $objects[$row['id']]['score'] = $object['score'];
693 }
694 return $objects;
695 }
696
697 function getSearchResults ($terms)
698 {
699 $objects = array();
700 mergeSearchResults ($objects, $terms, 'name');
701 mergeSearchResults ($objects, $terms, 'label');
702 mergeSearchResults ($objects, $terms, 'asset_no');
703 mergeSearchResults ($objects, $terms, 'barcode');
704 if (count ($objects) == 1)
705 usort ($objects, 'search_cmpObj');
706 return $objects;
707 }
708
709 // This function removes all colons and dots from a string.
710 function l2addressForDatabase ($string)
711 {
712 if (empty ($string))
713 return 'NULL';
714 $pieces = explode (':', $string);
715 // This workaround is for SunOS ifconfig.
716 foreach ($pieces as &$byte)
717 if (strlen ($byte) == 1)
718 $byte = '0' . $byte;
719 // And this workaround is for PHP.
720 unset ($byte);
721 $string = implode ('', $pieces);
722 $pieces = explode ('.', $string);
723 $string = implode ('', $pieces);
724 $string = strtoupper ($string);
725 return "'$string'";
726 }
727
728 function l2addressFromDatabase ($string)
729 {
730 switch (strlen ($string))
731 {
732 case 12: // Ethernet
733 case 16: // FireWire
734 $ret = implode (':', str_split ($string, 2));
735 break;
736 default:
737 $ret = $string;
738 break;
739 }
740 return $ret;
741 }
742
743 // The following 2 functions return previous and next rack IDs for
744 // a given rack ID. The order of racks is the same as in renderRackspace()
745 // or renderRow().
746 function getPrevIDforRack ($row_id = 0, $rack_id = 0)
747 {
748 if ($row_id <= 0 or $rack_id <= 0)
749 {
750 showError ('Invalid arguments passed', __FUNCTION__);
751 return NULL;
752 }
753 $rackList = getRacksForRow ($row_id);
754 doubleLink ($rackList);
755 if (isset ($rackList[$rack_id]['prev_key']))
756 return $rackList[$rack_id]['prev_key'];
757 return NULL;
758 }
759
760 function getNextIDforRack ($row_id = 0, $rack_id = 0)
761 {
762 if ($row_id <= 0 or $rack_id <= 0)
763 {
764 showError ('Invalid arguments passed', __FUNCTION__);
765 return NULL;
766 }
767 $rackList = getRacksForRow ($row_id);
768 doubleLink ($rackList);
769 if (isset ($rackList[$rack_id]['next_key']))
770 return $rackList[$rack_id]['next_key'];
771 return NULL;
772 }
773
774 // This function finds previous and next array keys for each array key and
775 // modifies its argument accordingly.
776 function doubleLink (&$array)
777 {
778 $prev_key = NULL;
779 foreach (array_keys ($array) as $key)
780 {
781 if ($prev_key)
782 {
783 $array[$key]['prev_key'] = $prev_key;
784 $array[$prev_key]['next_key'] = $key;
785 }
786 $prev_key = $key;
787 }
788 }
789
790 // After applying usort() to a rack list we will lose original array keys.
791 // This function restores the keys so they are equal to rack IDs.
792 function restoreRackIDs ($racks)
793 {
794 $ret = array();
795 foreach ($racks as $rack)
796 $ret[$rack['id']] = $rack;
797 return $ret;
798 }
799
800 function sortTokenize ($a, $b)
801 {
802 $aold='';
803 while ($a != $aold)
804 {
805 $aold=$a;
806 $a = ereg_replace('[^a-zA-Z0-9]',' ',$a);
807 $a = ereg_replace('([0-9])([a-zA-Z])','\\1 \\2',$a);
808 $a = ereg_replace('([a-zA-Z])([0-9])','\\1 \\2',$a);
809 }
810
811 $bold='';
812 while ($b != $bold)
813 {
814 $bold=$b;
815 $b = ereg_replace('[^a-zA-Z0-9]',' ',$b);
816 $b = ereg_replace('([0-9])([a-zA-Z])','\\1 \\2',$b);
817 $b = ereg_replace('([a-zA-Z])([0-9])','\\1 \\2',$b);
818 }
819
820
821
822 $ar = explode(' ', $a);
823 $br = explode(' ', $b);
824 for ($i=0; $i<count($ar) && $i<count($br); $i++)
825 {
826 $ret = 0;
827 if (is_numeric($ar[$i]) and is_numeric($br[$i]))
828 $ret = ($ar[$i]==$br[$i])?0:($ar[$i]<$br[$i]?-1:1);
829 else
830 $ret = strcasecmp($ar[$i], $br[$i]);
831 if ($ret != 0)
832 return $ret;
833 }
834 if ($i<count($ar))
835 return 1;
836 if ($i<count($br))
837 return -1;
838 return 0;
839 }
840
841 function sortByName ($a, $b)
842 {
843 return sortTokenize($a['name'], $b['name']);
844 }
845
846 function sortRacks ($a, $b)
847 {
848 return sortTokenize($a['row_name'] . ': ' . $a['name'], $b['row_name'] . ': ' . $b['name']);
849 }
850
851 function eq ($a, $b)
852 {
853 return $a==$b;
854 }
855
856 function neq ($a, $b)
857 {
858 return $a!=$b;
859 }
860
861 function countRefsOfType ($refs, $type, $eq)
862 {
863 $count=0;
864 foreach ($refs as $ref)
865 {
866 if ($eq($ref['type'], $type))
867 $count++;
868 }
869 return $count;
870 }
871
872 function sortEmptyPorts ($a, $b)
873 {
874 $objname_cmp = sortTokenize($a['Object_name'], $b['Object_name']);
875 if ($objname_cmp == 0)
876 {
877 return sortTokenize($a['Port_name'], $b['Port_name']);
878 }
879 return $objname_cmp;
880 }
881
882 function sortObjectAddressesAndNames ($a, $b)
883 {
884 $objname_cmp = sortTokenize($a['object_name'], $b['object_name']);
885 if ($objname_cmp == 0)
886 {
887 $objname_cmp = sortTokenize($a['port_name'], $b['port_name']);
888 if ($objname_cmp == 0)
889 {
890 sortTokenize($a['ip'], $b['ip']);
891 }
892 return $objname_cmp;
893 }
894 return $objname_cmp;
895 }
896
897
898
899 function sortAddresses ($a, $b)
900 {
901 $name_cmp = sortTokenize($a['name'], $b['name']);
902 if ($name_cmp == 0)
903 {
904 return sortTokenize($a['ip'], $b['ip']);
905 }
906 return $name_cmp;
907 }
908
909 // This function expands port compat list into a matrix.
910 function buildPortCompatMatrixFromList ($portTypeList, $portCompatList)
911 {
912 $matrix = array();
913 // Create type matrix and markup compatible types.
914 foreach (array_keys ($portTypeList) as $type1)
915 foreach (array_keys ($portTypeList) as $type2)
916 $matrix[$type1][$type2] = FALSE;
917 foreach ($portCompatList as $pair)
918 $matrix[$pair['type1']][$pair['type2']] = TRUE;
919 return $matrix;
920 }
921
922 function newPortForwarding($object_id, $localip, $localport, $remoteip, $remoteport, $proto, $description)
923 {
924 global $dbxlink;
925
926 $range = getRangeByIp($localip);
927 if (!$range)
928 return "$localip: Non existant ip";
929
930 $range = getRangeByIp($remoteip);
931 if (!$range)
932 return "$remoteip: Non existant ip";
933
934 if ( ($localport <= 0) or ($localport >= 65536) )
935 return "$localport: invaild port";
936
937 if ( ($remoteport <= 0) or ($remoteport >= 65536) )
938 return "$remoteport: invaild port";
939
940 $query =
941 "insert into PortForwarding set object_id='$object_id', localip=INET_ATON('$localip'), remoteip=INET_ATON('$remoteip'), localport='$localport', remoteport='$remoteport', proto='$proto', description='$description'";
942 $result = $dbxlink->exec ($query);
943
944 return '';
945 }
946
947 function deletePortForwarding($object_id, $localip, $localport, $remoteip, $remoteport, $proto)
948 {
949 global $dbxlink;
950
951 $query =
952 "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'";
953 $result = $dbxlink->exec ($query);
954 return '';
955 }
956
957 function updatePortForwarding($object_id, $localip, $localport, $remoteip, $remoteport, $proto, $description)
958 {
959 global $dbxlink;
960
961 $query =
962 "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'";
963 $result = $dbxlink->exec ($query);
964 return '';
965 }
966
967 function getObjectForwards($object_id)
968 {
969 global $dbxlink;
970
971 $ret = array();
972 $ret['out'] = array();
973 $ret['in'] = array();
974 $query =
975 "select ".
976 "dict_value as proto, ".
977 "proto as proto_bin, ".
978 "INET_NTOA(localip) as localip, ".
979 "localport, ".
980 "INET_NTOA(remoteip) as remoteip, ".
981 "remoteport, ".
982 "ipa1.name as local_addr_name, " .
983 "ipa2.name as remote_addr_name, " .
984 "description ".
985 "from PortForwarding inner join Dictionary on proto = dict_key natural join Chapter ".
986 "left join IPAddress as ipa1 on PortForwarding.localip = ipa1.ip " .
987 "left join IPAddress as ipa2 on PortForwarding.remoteip = ipa2.ip " .
988 "where object_id='$object_id' and chapter_name = 'Protocols' ".
989 "order by localip, localport, proto, remoteip, remoteport";
990 $result2 = $dbxlink->query ($query);
991 $count=0;
992 while ($row = $result2->fetch (PDO::FETCH_ASSOC))
993 {
994 foreach (array ('proto', 'proto_bin', 'localport', 'localip', 'remoteport', 'remoteip', 'description', 'local_addr_name', 'remote_addr_name') as $cname)
995 $ret['out'][$count][$cname] = $row[$cname];
996 $count++;
997 }
998 $result2->closeCursor();
999
1000 $query =
1001 "select ".
1002 "dict_value as proto, ".
1003 "proto as proto_bin, ".
1004 "INET_NTOA(localip) as localip, ".
1005 "localport, ".
1006 "INET_NTOA(remoteip) as remoteip, ".
1007 "remoteport, ".
1008 "PortForwarding.object_id as object_id, ".
1009 "RackObject.name as object_name, ".
1010 "description ".
1011 "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 ".
1012 "where IPBonds.object_id='$object_id' and chapter_name = 'Protocols' ".
1013 "order by remoteip, remoteport, proto, localip, localport";
1014 $result3 = $dbxlink->query ($query);
1015 $count=0;
1016 while ($row = $result3->fetch (PDO::FETCH_ASSOC))
1017 {
1018 foreach (array ('proto', 'proto_bin', 'localport', 'localip', 'remoteport', 'remoteip', 'object_id', 'object_name', 'description') as $cname)
1019 $ret['in'][$count][$cname] = $row[$cname];
1020 $count++;
1021 }
1022 $result3->closeCursor();
1023
1024 return $ret;
1025 }
1026
1027 // This function returns an array of single element of object's FQDN attribute,
1028 // if FQDN is set. The next choice is object's common name, if it looks like a
1029 // hostname. Otherwise an array of all 'regular' IP addresses of the
1030 // object is returned (which may appear 0 and more elements long).
1031 function findAllEndpoints ($object_id, $fallback = '')
1032 {
1033 $values = getAttrValues ($object_id);
1034 foreach ($values as $record)
1035 if ($record['name'] == 'FQDN' && !empty ($record['value']))
1036 return array ($record['value']);
1037 $addresses = getObjectAddresses ($object_id);
1038 $regular = array();
1039 foreach ($addresses as $idx => $address)
1040 if ($address['type'] == 'regular')
1041 $regular[] = $address['ip'];
1042 if (!count ($regular) && !empty ($fallback))
1043 return array ($fallback);
1044 return $regular;
1045 }
1046
1047 // Some records in the dictionary may be written as plain text or as Wiki
1048 // link in the following syntax:
1049 // 1. word
1050 // 2. [[word URL]] // FIXME: this isn't working
1051 // 3. [[word word word | URL]]
1052 // This function parses the line and returns text suitable for either A
1053 // (rendering <A HREF>) or O (for <OPTION>).
1054 function parseWikiLink ($line, $which)
1055 {
1056 if (preg_match ('/^\[\[.+\]\]$/', $line) == 0)
1057 return $line;
1058 $line = preg_replace ('/^\[\[(.+)\]\]$/', '$1', $line);
1059 $s = explode ('|', $line);
1060 $o_value = trim ($s[0]);
1061 $a_value = trim ($s[1]);
1062 if ($which == 'a')
1063 return "<a href='${a_value}'>${o_value}</a>";
1064 if ($which == 'o')
1065 return $o_value;
1066 }
1067
1068 function buildVServiceName ($vsinfo = NULL)
1069 {
1070 if ($vsinfo == NULL)
1071 {
1072 showError ('NULL argument', __FUNCTION__);
1073 return NULL;
1074 }
1075 return $vsinfo['vip'] . ':' . $vsinfo['vport'] . '/' . $vsinfo['proto'];
1076 }
1077
1078 // rackspace usage for a single rack
1079 // (T + W + U) / (height * 3 - A)
1080 function getRSUforRack ($data = NULL)
1081 {
1082 if ($data == NULL)
1083 {
1084 showError ('Invalid argument', __FUNCTION__);
1085 return NULL;
1086 }
1087 $counter = array ('A' => 0, 'U' => 0, 'T' => 0, 'W' => 0, 'F' => 0);
1088 for ($unit_no = $data['height']; $unit_no > 0; $unit_no--)
1089 for ($locidx = 0; $locidx < 3; $locidx++)
1090 $counter[$data[$unit_no][$locidx]['state']]++;
1091 return ($counter['T'] + $counter['W'] + $counter['U']) / ($counter['T'] + $counter['W'] + $counter['U'] + $counter['F']);
1092 }
1093
1094 // Same for row.
1095 function getRSUforRackRow ($rowData = NULL)
1096 {
1097 if ($rowData == NULL)
1098 {
1099 showError ('Invalid argument', __FUNCTION__);
1100 return NULL;
1101 }
1102 $counter = array ('A' => 0, 'U' => 0, 'T' => 0, 'W' => 0, 'F' => 0);
1103 $total_height = 0;
1104 foreach (array_keys ($rowData) as $rack_id)
1105 {
1106 $data = getRackData ($rack_id);
1107 $total_height += $data['height'];
1108 for ($unit_no = $data['height']; $unit_no > 0; $unit_no--)
1109 for ($locidx = 0; $locidx < 3; $locidx++)
1110 $counter[$data[$unit_no][$locidx]['state']]++;
1111 }
1112 return ($counter['T'] + $counter['W'] + $counter['U']) / ($counter['T'] + $counter['W'] + $counter['U'] + $counter['F']);
1113 }
1114
1115 ?>