r1885 + implement tag filtering for IPv4 RS pools
[racktables] / inc / functions.php
CommitLineData
b325120a 1<?php
e673ee24
DO
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.
30function displayedName ($objectData)
31{
32 if ($objectData['name'] != '')
33 return $objectData['name'];
9c0b0016 34 elseif (in_array ($objectData['objtype_id'], explode (',', getConfigVar ('NAMEFUL_OBJTYPES'))))
a0ec6295 35 return "ANONYMOUS " . $objectData['objtype_name'];
e673ee24 36 else
a0ec6295 37 return "[${objectData['objtype_name']}]";
e673ee24
DO
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.
43function 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;
93e02204
DO
60 if (isset ($rackData[$startRow - $height][$locidx]['rowspan']))
61 break 2;
62 if (isset ($rackData[$startRow - $height][$locidx]['colspan']))
63 break 2;
e673ee24
DO
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);
93e02204
DO
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";
e673ee24
DO
81 return $height;
82}
83
84// This function marks atoms to be avoided by rectHeight() and assigns rowspan/colspan
85// attributes.
86function 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.
93e02204
DO
97 // Explicitly show even single-cell spanned atoms, because rectHeight()
98 // is expeciting this data for correct calculation.
e673ee24
DO
99 if ($colspan != 0)
100 $rackData[$startRow - $height][$locidx]['skipped'] = TRUE;
101 else
102 {
103 $colspan = $templateWidth[$template_idx];
93e02204 104 if ($colspan >= 1)
e673ee24 105 $rackData[$startRow - $height][$locidx]['colspan'] = $colspan;
93e02204 106 if ($maxheight >= 1)
e673ee24
DO
107 $rackData[$startRow - $height][$locidx]['rowspan'] = $maxheight;
108 }
109 }
110 }
111 }
112 return;
113}
114
93e02204
DO
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.
e673ee24
DO
118function markAllSpans (&$rackData = NULL)
119{
120 if ($rackData == NULL)
121 {
61e269b5 122 showError ('Invalid rackData', __FUNCTION__);
e673ee24
DO
123 return;
124 }
125 for ($i = $rackData['height']; $i > 0; $i--)
93e02204
DO
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).
131function markBestSpan (&$rackData, $i)
132{
133 global $template, $templateWidth;
134 for ($j = 0; $j < 6; $j++)
e673ee24 135 {
93e02204
DO
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)
e673ee24 146 {
93e02204
DO
147 $best_template_index = $j;
148 $bestheight = $height[$j];
149 break;
e673ee24 150 }
93e02204
DO
151 // distribute span marks
152 markSpan ($rackData, $i, $bestheight, $best_template_index);
153 return TRUE;
e673ee24
DO
154}
155
156function delRow ($row_id = 0)
157{
158 if ($row_id == 0)
159 {
61e269b5 160 showError ('Not all required args are present.', __FUNCTION__);
e673ee24
DO
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 {
61e269b5 173 showError ('Marked ' . $result.rowCount() . ' rows as deleted, but expected 1', __FUNCTION__);
e673ee24
DO
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
181function delRack ($rack_id = 0)
182{
183 if ($rack_id == 0)
184 {
61e269b5 185 showError ('Not all required args are present.', __FUNCTION__);
e673ee24
DO
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 {
61e269b5 198 showError ('Marked ' . $result.rowCount() . ' rows as deleted, but expected 1', __FUNCTION__);
e673ee24
DO
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
206function delObject ($object_id = 0)
207{
208 if ($object_id == 0)
209 {
61e269b5 210 showError ('Not all required args are present.', __FUNCTION__);
e673ee24
DO
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 {
61e269b5 223 showError ('Marked ' . $result.rowCount() . ' rows as deleted, but expected 1', __FUNCTION__);
e673ee24
DO
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.
232function 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.
250function 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'.
266function 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.
282function 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).
298function 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.
313function 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.
331function 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
347function 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
362function 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
fa05e3de 377function addRange ($range='', $name='', $is_bcast = FALSE, $taglist = array())
e673ee24 378{
2a201216
DY
379 // $range is in x.x.x.x/x format, split into ip/mask vars
380 $rangeArray = explode('/', $range);
6e8868cc
DO
381 if (count ($rangeArray) != 2)
382 return "Invalid IP subnet '${range}'";
2a201216
DY
383 $ip = $rangeArray[0];
384 $mask = $rangeArray[1];
385
c87fd3fe
DO
386 if (empty ($ip) or empty ($mask))
387 return "Invalid IP subnet '${range}'";
e673ee24
DO
388 $ipL = ip2long($ip);
389 $maskL = ip2long($mask);
390 if ($ipL == -1 || $ipL === FALSE)
391 return 'Bad ip address';
392 if ($mask < 32 && $mask > 0)
393 $maskL = $mask;
394 else
395 {
396 $maskB = decbin($maskL);
397 if (strlen($maskB)!=32)
398 return 'Bad mask';
399 $ones=0;
400 $zeroes=FALSE;
401 foreach( str_split ($maskB) as $digit)
402 {
403 if ($digit == '0')
404 $zeroes = TRUE;
405 if ($digit == '1')
406 {
407 $ones++;
408 if ($zeroes == TRUE)
409 return 'Bad mask';
410 }
411 }
412 $maskL = $ones;
413 }
414 $binmask = binMaskFromDec($maskL);
415 $ipL = $ipL & $binmask;
416
417 $query =
418 "select ".
419 "id, ip, mask, name ".
420 "from IPRanges ";
421
422
fa05e3de 423 $result = useSelectBlade ($query);
e673ee24
DO
424
425 while ($row = $result->fetch (PDO::FETCH_ASSOC))
426 {
427 $otherip = $row['ip'];
428 $othermask = binMaskFromDec($row['mask']);
429// 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";
430// 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";
431// echo "\n";
432// flush();
433 if (($otherip & $othermask) == ($ipL & $othermask))
434 return "This subnet intersects with ".long2ip($row['ip'])."/${row['mask']}";
435 if (($otherip & $binmask) == ($ipL & $binmask))
436 return "This subnet intersects with ".long2ip($row['ip'])."/${row['mask']}";
437 }
438 $result->closeCursor();
fa05e3de
DO
439 unset ($result);
440 $result = useInsertBlade
441 (
442 'IPRanges',
443 array
444 (
445 'ip' => sprintf ('%u', $ipL),
446 'mask' => "'${maskL}'",
447 'name' => "'${name}'"
448 )
449 );
2dee289e
DO
450
451 if ($is_bcast and $maskL < 31)
452 {
453 $network_addr = long2ip ($ipL);
454 $broadcast_addr = long2ip ($ipL | binInvMaskFromDec ($maskL));
455 updateAddress ($network_addr, 'network', 'yes');
456 updateAddress ($broadcast_addr, 'broadcast', 'yes');
457 }
fa05e3de
DO
458 if (!count ($taglist))
459 return '';
460 if (($result = useSelectBlade ('select last_insert_id()')) == NULL)
461 return 'Query #3 failed in ' . __FUNCTION__;
462 $row = $result->fetch (PDO::FETCH_NUM);
463 $netid = $row[0];
464 $errcount = 0;
465 foreach ($taglist as $tag_id)
466 if (useInsertBlade
467 (
468 'TagStorage',
469 array
470 (
471 'target_realm' => "'ipv4net'",
472 'target_id' => $netid,
473 'tag_id' => $tag_id
474 )
475 ) == FALSE)
476 $errcount++;
477 if (!$errcount)
478 return '';
479 else
480 return "Experienced ${errcount} errors adding tags for the network";
e673ee24
DO
481}
482
483function getIPRange ($id = 0)
484{
485 global $dbxlink;
486 $query =
487 "select ".
488 "id as IPRanges_id, ".
489 "INET_NTOA(ip) as IPRanges_ip, ".
e673ee24
DO
490 "mask as IPRanges_mask, ".
491 "name as IPRanges_name ".
492 "from IPRanges ".
493 "where id = '$id'";
602663f4 494 $result = useSelectBlade ($query);
5c43a978 495 $ret = array();
55cc3e4e
DO
496 $row = $result->fetch (PDO::FETCH_ASSOC);
497 if ($row == NULL)
498 return $ret;
499 $ret['id'] = $row['IPRanges_id'];
500 $ret['ip'] = $row['IPRanges_ip'];
7e039e88 501 $ret['ip_bin'] = ip2long ($row['IPRanges_ip']);
55cc3e4e
DO
502 $ret['mask_bin'] = binMaskFromDec($row['IPRanges_mask']);
503 $ret['mask_bin_inv'] = binInvMaskFromDec($row['IPRanges_mask']);
504 $ret['name'] = $row['IPRanges_name'];
505 $ret['mask'] = $row['IPRanges_mask'];
506 $ret['addrlist'] = array();
55cc3e4e 507 $result->closeCursor();
602663f4 508 unset ($result);
7e039e88
DO
509 // We risk losing some significant bits in an unsigned 32bit integer,
510 // unless it is converted to a string.
511 $db_first = "'" . sprintf ('%u', 0x00000000 + $ret['ip_bin'] & $ret['mask_bin']) . "'";
512 $db_last = "'" . sprintf ('%u', 0x00000000 + $ret['ip_bin'] | ($ret['mask_bin_inv'])) . "'";
5c43a978
DO
513
514 // Don't try to build up the whole structure in a single pass. Request
515 // the list of user comments and reservations and merge allocations in
516 // at a latter point.
55cc3e4e 517 $query =
7e039e88
DO
518 "select INET_NTOA(ip) as ip, name, reserved from IPAddress " .
519 "where ip between ${db_first} and ${db_last} " .
5c43a978 520 "and (reserved = 'yes' or name != '')";
602663f4
DO
521 $result = $dbxlink->query ($query);
522 while ($row = $result->fetch (PDO::FETCH_ASSOC))
55cc3e4e 523 {
7e039e88
DO
524 $ip_bin = ip2long ($row['ip']);
525 $ret['addrlist'][$ip_bin] = $row;
5c43a978
DO
526 $tmp = array();
527 foreach (array ('ip', 'name', 'reserved') as $cname)
528 $tmp[$cname] = $row[$cname];
529 $tmp['references'] = array();
602663f4
DO
530 $tmp['lbrefs'] = array();
531 $tmp['rsrefs'] = array();
7e039e88 532 $ret['addrlist'][$ip_bin] = $tmp;
5c43a978 533 }
602663f4
DO
534 $result->closeCursor();
535 unset ($result);
e673ee24 536
5c43a978 537 $query =
dda31a62
DO
538 "select INET_NTOA(ipb.ip) as ip, ro.id as object_id, " .
539 "ro.name as object_name, ipb.name, ipb.type, objtype_id, " .
540 "dict_value as objtype_name from " .
5c43a978 541 "IPBonds as ipb inner join RackObject as ro on ipb.object_id = ro.id " .
dda31a62 542 "left join Dictionary on objtype_id=dict_key natural join Chapter " .
7e039e88 543 "where ip between ${db_first} and ${db_last} " .
dda31a62 544 "and chapter_name = 'RackObjectType'" .
5c43a978 545 "order by ipb.type, object_name";
602663f4
DO
546 $result = useSelectBlade ($query);
547 while ($row = $result->fetch (PDO::FETCH_ASSOC))
5c43a978 548 {
7e039e88
DO
549 $ip_bin = ip2long ($row['ip']);
550 if (!isset ($ret['addrlist'][$ip_bin]))
55cc3e4e 551 {
7e039e88
DO
552 $ret['addrlist'][$ip_bin] = array();
553 $ret['addrlist'][$ip_bin]['ip'] = $row['ip'];
554 $ret['addrlist'][$ip_bin]['name'] = '';
555 $ret['addrlist'][$ip_bin]['reserved'] = 'no';
556 $ret['addrlist'][$ip_bin]['references'] = array();
602663f4
DO
557 $ret['addrlist'][$ip_bin]['lbrefs'] = array();
558 $ret['addrlist'][$ip_bin]['rsrefs'] = array();
e673ee24 559 }
5c43a978 560 $tmp = array();
dda31a62 561 foreach (array ('object_id', 'type', 'name') as $cname)
5c43a978 562 $tmp[$cname] = $row[$cname];
dda31a62
DO
563 $quasiobject['name'] = $row['object_name'];
564 $quasiobject['objtype_id'] = $row['objtype_id'];
565 $quasiobject['objtype_name'] = $row['objtype_name'];
566 $tmp['object_name'] = displayedName ($quasiobject);
7e039e88 567 $ret['addrlist'][$ip_bin]['references'][] = $tmp;
e673ee24 568 }
602663f4
DO
569 $result->closeCursor();
570 unset ($result);
571
572 $query = "select vs_id, inet_ntoa(vip) as ip, vport, proto, " .
573 "object_id, objtype_id, ro.name, dict_value as objtype_name from " .
574 "IPVirtualService as vs inner join IPLoadBalancer as lb on vs.id = lb.vs_id " .
575 "inner join RackObject as ro on lb.object_id = ro.id " .
576 "left join Dictionary on objtype_id=dict_key " .
577 "natural join Chapter " .
578 "where vip between ${db_first} and ${db_last} " .
579 "and chapter_name = 'RackObjectType'" .
580 "order by vport, proto, ro.name, object_id";
581 $result = useSelectBlade ($query);
582 while ($row = $result->fetch (PDO::FETCH_ASSOC))
583 {
584 $ip_bin = ip2long ($row['ip']);
585 if (!isset ($ret['addrlist'][$ip_bin]))
586 {
587 $ret['addrlist'][$ip_bin] = array();
588 $ret['addrlist'][$ip_bin]['ip'] = $row['ip'];
589 $ret['addrlist'][$ip_bin]['name'] = '';
590 $ret['addrlist'][$ip_bin]['reserved'] = 'no';
591 $ret['addrlist'][$ip_bin]['references'] = array();
592 $ret['addrlist'][$ip_bin]['lbrefs'] = array();
593 $ret['addrlist'][$ip_bin]['rsrefs'] = array();
594 }
595 $tmp = $qbject = array();
596 foreach (array ('object_id', 'vport', 'proto', 'vs_id') as $cname)
597 $tmp[$cname] = $row[$cname];
598 foreach (array ('name', 'objtype_id', 'objtype_name') as $cname)
599 $qobject[$cname] = $row[$cname];
600 $tmp['object_name'] = displayedName ($qobject);
601 $ret['addrlist'][$ip_bin]['lbrefs'][] = $tmp;
602 }
603 $result->closeCursor();
604 unset ($result);
605
606 $query = "select inet_ntoa(rsip) as ip, rsport, rspool_id, rsp.name as rspool_name from " .
607 "IPRealServer as rs inner join IPRSPool as rsp on rs.rspool_id = rsp.id " .
608 "where rsip between ${db_first} and ${db_last} " .
609 "order by ip, rsport, rspool_id";
610 $result = useSelectBlade ($query);
611 while ($row = $result->fetch (PDO::FETCH_ASSOC))
612 {
613 $ip_bin = ip2long ($row['ip']);
614 if (!isset ($ret['addrlist'][$ip_bin]))
615 {
616 $ret['addrlist'][$ip_bin] = array();
617 $ret['addrlist'][$ip_bin]['ip'] = $row['ip'];
618 $ret['addrlist'][$ip_bin]['name'] = '';
619 $ret['addrlist'][$ip_bin]['reserved'] = 'no';
620 $ret['addrlist'][$ip_bin]['references'] = array();
621 $ret['addrlist'][$ip_bin]['lbrefs'] = array();
622 $ret['addrlist'][$ip_bin]['rsrefs'] = array();
623 }
624 $tmp = array();
625 foreach (array ('rspool_id', 'rsport', 'rspool_name') as $cname)
626 $tmp[$cname] = $row[$cname];
627 $ret['addrlist'][$ip_bin]['rsrefs'][] = $tmp;
628 }
5c43a978 629
e673ee24
DO
630 return $ret;
631}
632
dda31a62
DO
633// Don't require any records in IPAddress, but if there is one,
634// merge the data between getting allocation list. Collect enough data
635// to call displayedName() ourselves.
e673ee24
DO
636function getIPAddress ($ip=0)
637{
638 $ret = array();
e673ee24 639 $ret['bonds'] = array();
59bebe2b 640 // FIXME: below two aren't neither filled in with data nor rendered (ticket:23)
e673ee24
DO
641 $ret['outpf'] = array();
642 $ret['inpf'] = array();
59bebe2b
DO
643 $ret['vslist'] = array();
644 $ret['rslist'] = array();
e673ee24 645 $ret['exists'] = 0;
dda31a62 646 $ret['name'] = '';
e673ee24
DO
647 $ret['reserved'] = 'no';
648 global $dbxlink;
59bebe2b 649 $query =
e673ee24 650 "select ".
dda31a62 651 "name, reserved ".
e673ee24 652 "from IPAddress ".
dda31a62 653 "where ip = INET_ATON('$ip') and (reserved = 'yes' or name != '')";
59bebe2b
DO
654 $result = $dbxlink->query ($query);
655 if ($result == NULL)
656 {
657 showError ('Query #1 failed', __FUNCTION__);
e673ee24 658 return NULL;
59bebe2b
DO
659 }
660 if ($row = $result->fetch (PDO::FETCH_ASSOC))
e673ee24 661 {
959c4f4c 662 $ret['exists'] = 1;
959c4f4c
DO
663 $ret['name'] = $row['name'];
664 $ret['reserved'] = $row['reserved'];
e673ee24 665 }
59bebe2b
DO
666 $result->closeCursor();
667 unset ($result);
e673ee24 668
59bebe2b 669 $query =
dda31a62
DO
670 "select ".
671 "IPBonds.object_id as object_id, ".
672 "IPBonds.name as name, ".
673 "IPBonds.type as type, ".
674 "objtype_id, dict_value as objtype_name, " .
675 "RackObject.name as object_name ".
676 "from IPBonds join RackObject on IPBonds.object_id=RackObject.id ".
677 "left join Dictionary on objtype_id=dict_key natural join Chapter " .
678 "where IPBonds.ip=INET_ATON('$ip') ".
679 "and chapter_name = 'RackObjectType' " .
680 "order by RackObject.id, IPBonds.name";
59bebe2b 681 $result = $dbxlink->query ($query);
dda31a62 682 $count=0;
59bebe2b 683 while ($row = $result->fetch (PDO::FETCH_ASSOC))
e673ee24 684 {
dda31a62
DO
685 $ret['bonds'][$count]['object_id'] = $row['object_id'];
686 $ret['bonds'][$count]['name'] = $row['name'];
687 $ret['bonds'][$count]['type'] = $row['type'];
688 $qo = array();
689 $qo['name'] = $row['object_name'];
690 $qo['objtype_id'] = $row['objtype_id'];
691 $qo['objtype_name'] = $row['objtype_name'];
692 $ret['bonds'][$count]['object_name'] = displayedName ($qo);
693 $count++;
694 $ret['exists'] = 1;
e673ee24 695 }
59bebe2b
DO
696 $result->closeCursor();
697 unset ($result);
698
5fe1ed76 699 $query = "select id, vport, proto, name from IPVirtualService where vip = inet_aton('${ip}')";
59bebe2b
DO
700 $result = $dbxlink->query ($query);
701 while ($row = $result->fetch (PDO::FETCH_ASSOC))
5fe1ed76
DO
702 {
703 $new = $row;
704 $new['vip'] = $ip;
705 $ret['vslist'][] = $new;
706 }
707 $result->closeCursor();
708 unset ($result);
709
710 $query = "select inservice, rsport, IPRSPool.id as pool_id, IPRSPool.name as poolname from " .
711 "IPRealServer inner join IPRSPool on rspool_id = IPRSPool.id " .
712 "where rsip = inet_aton('${ip}')";
713 $result = $dbxlink->query ($query);
714 while ($row = $result->fetch (PDO::FETCH_ASSOC))
715 {
716 $new = $row;
717 $new['rsip'] = $ip;
718 $ret['rslist'][] = $new;
719 }
59bebe2b
DO
720 $result->closeCursor();
721 unset ($result);
e673ee24
DO
722
723 return $ret;
724}
725
726function bindIpToObject ($ip='', $object_id=0, $name='', $type='')
727{
728 global $dbxlink;
729
730 $range = getRangeByIp($ip);
e673ee24
DO
731 if (!$range)
732 return 'Non-existant ip address. Try adding IP range first';
733
20a92aa2
DO
734 $result = useInsertBlade
735 (
736 'IPBonds',
737 array
738 (
dda31a62 739 'ip' => "INET_ATON('$ip')",
20a92aa2
DO
740 'object_id' => "'${object_id}'",
741 'name' => "'${name}'",
742 'type' => "'${type}'"
743 )
744 );
745 return $result ? '' : 'useInsertBlade() failed in bindIpToObject()';
e673ee24
DO
746}
747
748// This function looks up 'has_problems' flag for 'T' atoms
749// and modifies 'hl' key. May be, this should be better done
750// in getRackData(). We don't honour 'skipped' key, because
751// the function is also used for thumb creation.
752function markupObjectProblems (&$rackData)
753{
754 for ($i = $rackData['height']; $i > 0; $i--)
755 for ($locidx = 0; $locidx < 3; $locidx++)
756 if ($rackData[$i][$locidx]['state'] == 'T')
757 {
758 $object = getObjectInfo ($rackData[$i][$locidx]['object_id']);
759 if ($object['has_problems'] == 'yes')
760 {
761 // Object can be already highlighted.
762 if (isset ($rackData[$i][$locidx]['hl']))
763 $rackData[$i][$locidx]['hl'] = $rackData[$i][$locidx]['hl'] . 'w';
764 else
765 $rackData[$i][$locidx]['hl'] = 'w';
766 }
767 }
768}
769
770function search_cmpObj ($a, $b)
771{
772 return ($a['score'] > $b['score'] ? -1 : 1);
773}
774
775// This function performs search and then calculates score for each result.
776// Given previous search results in $objects argument, it adds new results
777// to the array and updates score for existing results, if it is greater than
778// existing score.
779function mergeSearchResults (&$objects, $terms, $fieldname)
780{
781 global $dbxlink;
782 $query =
783 "select name, label, asset_no, barcode, ro.id, dict_key as objtype_id, " .
784 "dict_value as objtype_name, asset_no from RackObject as ro inner join Dictionary " .
785 "on objtype_id = dict_key natural join Chapter where chapter_name = 'RackObjectType' and ";
786 $count = 0;
787 foreach (explode (' ', $terms) as $term)
788 {
789 if ($count) $query .= ' or ';
790 $query .= "${fieldname} like '%$term%'";
791 $count++;
792 }
ea5fc465 793 $result = useSelectBlade ($query);
e673ee24
DO
794// FIXME: this dead call was executed 4 times per 1 object search!
795// $typeList = getObjectTypeList();
796 $clist = array ('id', 'name', 'label', 'asset_no', 'barcode', 'objtype_id', 'objtype_name');
797 while ($row = $result->fetch(PDO::FETCH_ASSOC))
798 {
799 foreach ($clist as $cname)
800 $object[$cname] = $row[$cname];
801 $object['score'] = 0;
802 $object['dname'] = displayedName ($object);
803 unset ($object['objtype_id']);
804 foreach (explode (' ', $terms) as $term)
805 if (strstr ($object['name'], $term))
806 $object['score'] += 1;
807 unset ($object['name']);
808 if (!isset ($objects[$row['id']]))
809 $objects[$row['id']] = $object;
810 elseif ($objects[$row['id']]['score'] < $object['score'])
811 $objects[$row['id']]['score'] = $object['score'];
812 }
813 return $objects;
814}
815
ea5fc465 816function getObjectSearchResults ($terms)
e673ee24
DO
817{
818 $objects = array();
819 mergeSearchResults ($objects, $terms, 'name');
820 mergeSearchResults ($objects, $terms, 'label');
821 mergeSearchResults ($objects, $terms, 'asset_no');
822 mergeSearchResults ($objects, $terms, 'barcode');
823 if (count ($objects) == 1)
824 usort ($objects, 'search_cmpObj');
825 return $objects;
826}
827
828// This function removes all colons and dots from a string.
829function l2addressForDatabase ($string)
830{
831 if (empty ($string))
832 return 'NULL';
833 $pieces = explode (':', $string);
834 // This workaround is for SunOS ifconfig.
835 foreach ($pieces as &$byte)
836 if (strlen ($byte) == 1)
837 $byte = '0' . $byte;
838 // And this workaround is for PHP.
839 unset ($byte);
840 $string = implode ('', $pieces);
841 $pieces = explode ('.', $string);
842 $string = implode ('', $pieces);
843 $string = strtoupper ($string);
844 return "'$string'";
845}
846
847function l2addressFromDatabase ($string)
848{
849 switch (strlen ($string))
850 {
851 case 12: // Ethernet
852 case 16: // FireWire
853 $ret = implode (':', str_split ($string, 2));
854 break;
855 default:
856 $ret = $string;
857 break;
858 }
859 return $ret;
860}
861
862// The following 2 functions return previous and next rack IDs for
863// a given rack ID. The order of racks is the same as in renderRackspace()
864// or renderRow().
865function getPrevIDforRack ($row_id = 0, $rack_id = 0)
866{
867 if ($row_id <= 0 or $rack_id <= 0)
868 {
61e269b5 869 showError ('Invalid arguments passed', __FUNCTION__);
e673ee24
DO
870 return NULL;
871 }
872 $rackList = getRacksForRow ($row_id);
873 doubleLink ($rackList);
874 if (isset ($rackList[$rack_id]['prev_key']))
875 return $rackList[$rack_id]['prev_key'];
876 return NULL;
877}
878
879function getNextIDforRack ($row_id = 0, $rack_id = 0)
880{
881 if ($row_id <= 0 or $rack_id <= 0)
882 {
61e269b5 883 showError ('Invalid arguments passed', __FUNCTION__);
e673ee24
DO
884 return NULL;
885 }
886 $rackList = getRacksForRow ($row_id);
887 doubleLink ($rackList);
888 if (isset ($rackList[$rack_id]['next_key']))
889 return $rackList[$rack_id]['next_key'];
890 return NULL;
891}
892
893// This function finds previous and next array keys for each array key and
894// modifies its argument accordingly.
895function doubleLink (&$array)
896{
897 $prev_key = NULL;
898 foreach (array_keys ($array) as $key)
899 {
900 if ($prev_key)
901 {
902 $array[$key]['prev_key'] = $prev_key;
903 $array[$prev_key]['next_key'] = $key;
904 }
905 $prev_key = $key;
906 }
907}
908
909// After applying usort() to a rack list we will lose original array keys.
910// This function restores the keys so they are equal to rack IDs.
911function restoreRackIDs ($racks)
912{
913 $ret = array();
914 foreach ($racks as $rack)
915 $ret[$rack['id']] = $rack;
916 return $ret;
917}
918
919function sortTokenize ($a, $b)
920{
921 $aold='';
922 while ($a != $aold)
923 {
924 $aold=$a;
925 $a = ereg_replace('[^a-zA-Z0-9]',' ',$a);
926 $a = ereg_replace('([0-9])([a-zA-Z])','\\1 \\2',$a);
927 $a = ereg_replace('([a-zA-Z])([0-9])','\\1 \\2',$a);
928 }
929
930 $bold='';
931 while ($b != $bold)
932 {
933 $bold=$b;
934 $b = ereg_replace('[^a-zA-Z0-9]',' ',$b);
935 $b = ereg_replace('([0-9])([a-zA-Z])','\\1 \\2',$b);
936 $b = ereg_replace('([a-zA-Z])([0-9])','\\1 \\2',$b);
937 }
938
939
940
941 $ar = explode(' ', $a);
942 $br = explode(' ', $b);
943 for ($i=0; $i<count($ar) && $i<count($br); $i++)
944 {
945 $ret = 0;
946 if (is_numeric($ar[$i]) and is_numeric($br[$i]))
947 $ret = ($ar[$i]==$br[$i])?0:($ar[$i]<$br[$i]?-1:1);
948 else
949 $ret = strcasecmp($ar[$i], $br[$i]);
950 if ($ret != 0)
951 return $ret;
952 }
953 if ($i<count($ar))
954 return 1;
955 if ($i<count($br))
956 return -1;
957 return 0;
958}
959
960function sortByName ($a, $b)
961{
962 return sortTokenize($a['name'], $b['name']);
963}
964
6a3a37b2
DO
965function sortRacks ($a, $b)
966{
967 return sortTokenize($a['row_name'] . ': ' . $a['name'], $b['row_name'] . ': ' . $b['name']);
968}
969
e673ee24
DO
970function eq ($a, $b)
971{
972 return $a==$b;
973}
974
975function neq ($a, $b)
976{
977 return $a!=$b;
978}
979
980function countRefsOfType ($refs, $type, $eq)
981{
982 $count=0;
983 foreach ($refs as $ref)
984 {
985 if ($eq($ref['type'], $type))
986 $count++;
987 }
988 return $count;
989}
990
991function sortEmptyPorts ($a, $b)
992{
993 $objname_cmp = sortTokenize($a['Object_name'], $b['Object_name']);
994 if ($objname_cmp == 0)
995 {
996 return sortTokenize($a['Port_name'], $b['Port_name']);
997 }
998 return $objname_cmp;
999}
1000
1001function sortObjectAddressesAndNames ($a, $b)
1002{
1003 $objname_cmp = sortTokenize($a['object_name'], $b['object_name']);
1004 if ($objname_cmp == 0)
1005 {
79f22c3d
DO
1006 $name_a = (isset ($a['port_name'])) ? $a['port_name'] : '';
1007 $name_b = (isset ($b['port_name'])) ? $b['port_name'] : '';
1008 $objname_cmp = sortTokenize($name_a, $name_b);
e673ee24 1009 if ($objname_cmp == 0)
e673ee24 1010 sortTokenize($a['ip'], $b['ip']);
e673ee24
DO
1011 return $objname_cmp;
1012 }
1013 return $objname_cmp;
1014}
1015
1016
1017
1018function sortAddresses ($a, $b)
1019{
1020 $name_cmp = sortTokenize($a['name'], $b['name']);
1021 if ($name_cmp == 0)
1022 {
1023 return sortTokenize($a['ip'], $b['ip']);
1024 }
1025 return $name_cmp;
1026}
1027
1028// This function expands port compat list into a matrix.
1029function buildPortCompatMatrixFromList ($portTypeList, $portCompatList)
1030{
1031 $matrix = array();
1032 // Create type matrix and markup compatible types.
1033 foreach (array_keys ($portTypeList) as $type1)
1034 foreach (array_keys ($portTypeList) as $type2)
1035 $matrix[$type1][$type2] = FALSE;
1036 foreach ($portCompatList as $pair)
1037 $matrix[$pair['type1']][$pair['type2']] = TRUE;
1038 return $matrix;
1039}
1040
1041function newPortForwarding($object_id, $localip, $localport, $remoteip, $remoteport, $proto, $description)
1042{
1043 global $dbxlink;
1044
1045 $range = getRangeByIp($localip);
1046 if (!$range)
1047 return "$localip: Non existant ip";
1048
1049 $range = getRangeByIp($remoteip);
1050 if (!$range)
1051 return "$remoteip: Non existant ip";
1052
1053 if ( ($localport <= 0) or ($localport >= 65536) )
1054 return "$localport: invaild port";
1055
1056 if ( ($remoteport <= 0) or ($remoteport >= 65536) )
1057 return "$remoteport: invaild port";
1058
1059 $query =
1060 "insert into PortForwarding set object_id='$object_id', localip=INET_ATON('$localip'), remoteip=INET_ATON('$remoteip'), localport='$localport', remoteport='$remoteport', proto='$proto', description='$description'";
1061 $result = $dbxlink->exec ($query);
1062
1063 return '';
1064}
1065
1066function deletePortForwarding($object_id, $localip, $localport, $remoteip, $remoteport, $proto)
1067{
1068 global $dbxlink;
1069
1070 $query =
1071 "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'";
1072 $result = $dbxlink->exec ($query);
1073 return '';
1074}
1075
1076function updatePortForwarding($object_id, $localip, $localport, $remoteip, $remoteport, $proto, $description)
1077{
1078 global $dbxlink;
1079
1080 $query =
1081 "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'";
1082 $result = $dbxlink->exec ($query);
1083 return '';
1084}
1085
eeb4a5d8 1086function getNATv4ForObject ($object_id)
e673ee24 1087{
e673ee24
DO
1088 $ret = array();
1089 $ret['out'] = array();
1090 $ret['in'] = array();
1091 $query =
1092 "select ".
eeb4a5d8 1093 "proto, ".
e673ee24
DO
1094 "INET_NTOA(localip) as localip, ".
1095 "localport, ".
1096 "INET_NTOA(remoteip) as remoteip, ".
1097 "remoteport, ".
f28fbe8b
DO
1098 "ipa1.name as local_addr_name, " .
1099 "ipa2.name as remote_addr_name, " .
e673ee24 1100 "description ".
eeb4a5d8 1101 "from PortForwarding ".
f28fbe8b
DO
1102 "left join IPAddress as ipa1 on PortForwarding.localip = ipa1.ip " .
1103 "left join IPAddress as ipa2 on PortForwarding.remoteip = ipa2.ip " .
eeb4a5d8 1104 "where object_id='$object_id' ".
e673ee24 1105 "order by localip, localport, proto, remoteip, remoteport";
eeb4a5d8 1106 $result = useSelectBlade ($query);
e673ee24 1107 $count=0;
eeb4a5d8 1108 while ($row = $result->fetch (PDO::FETCH_ASSOC))
e673ee24 1109 {
eeb4a5d8 1110 foreach (array ('proto', 'localport', 'localip', 'remoteport', 'remoteip', 'description', 'local_addr_name', 'remote_addr_name') as $cname)
f28fbe8b 1111 $ret['out'][$count][$cname] = $row[$cname];
e673ee24
DO
1112 $count++;
1113 }
eeb4a5d8
DO
1114 $result->closeCursor();
1115 unset ($result);
e673ee24
DO
1116
1117 $query =
1118 "select ".
eeb4a5d8 1119 "proto, ".
e673ee24
DO
1120 "INET_NTOA(localip) as localip, ".
1121 "localport, ".
1122 "INET_NTOA(remoteip) as remoteip, ".
1123 "remoteport, ".
1124 "PortForwarding.object_id as object_id, ".
1125 "RackObject.name as object_name, ".
1126 "description ".
eeb4a5d8
DO
1127 "from ((PortForwarding join IPBonds on remoteip=IPBonds.ip) join RackObject on PortForwarding.object_id=RackObject.id) ".
1128 "where IPBonds.object_id='$object_id' ".
e673ee24 1129 "order by remoteip, remoteport, proto, localip, localport";
eeb4a5d8 1130 $result = useSelectBlade ($query);
e673ee24 1131 $count=0;
eeb4a5d8 1132 while ($row = $result->fetch (PDO::FETCH_ASSOC))
e673ee24 1133 {
eeb4a5d8 1134 foreach (array ('proto', 'localport', 'localip', 'remoteport', 'remoteip', 'object_id', 'object_name', 'description') as $cname)
f28fbe8b 1135 $ret['in'][$count][$cname] = $row[$cname];
e673ee24
DO
1136 $count++;
1137 }
eeb4a5d8 1138 $result->closeCursor();
e673ee24
DO
1139
1140 return $ret;
1141}
1142
c31cd72c 1143// This function returns an array of single element of object's FQDN attribute,
f321b50a
DO
1144// if FQDN is set. The next choice is object's common name, if it looks like a
1145// hostname. Otherwise an array of all 'regular' IP addresses of the
c31cd72c 1146// object is returned (which may appear 0 and more elements long).
f321b50a 1147function findAllEndpoints ($object_id, $fallback = '')
c31cd72c
DO
1148{
1149 $values = getAttrValues ($object_id);
1150 foreach ($values as $record)
f321b50a 1151 if ($record['name'] == 'FQDN' && !empty ($record['value']))
c31cd72c
DO
1152 return array ($record['value']);
1153 $addresses = getObjectAddresses ($object_id);
1154 $regular = array();
1155 foreach ($addresses as $idx => $address)
1156 if ($address['type'] == 'regular')
fa8112d1 1157 $regular[] = $address['ip'];
f321b50a
DO
1158 if (!count ($regular) && !empty ($fallback))
1159 return array ($fallback);
c31cd72c
DO
1160 return $regular;
1161}
1162
83ba6670
DO
1163// Some records in the dictionary may be written as plain text or as Wiki
1164// link in the following syntax:
1165// 1. word
1166// 2. [[word URL]] // FIXME: this isn't working
1167// 3. [[word word word | URL]]
1168// This function parses the line and returns text suitable for either A
1169// (rendering <A HREF>) or O (for <OPTION>).
24cbe8af 1170function parseWikiLink ($line, $which, $strip_optgroup = FALSE)
83ba6670 1171{
010231c2 1172 if (preg_match ('/^\[\[.+\]\]$/', $line) == 0)
24cbe8af
DO
1173 {
1174 if ($strip_optgroup)
2613bc1c 1175 return ereg_replace ('^.+\^', '', ereg_replace ('^(.+)&', '\\1 ', $line));
24cbe8af
DO
1176 else
1177 return $line;
1178 }
010231c2
DO
1179 $line = preg_replace ('/^\[\[(.+)\]\]$/', '$1', $line);
1180 $s = explode ('|', $line);
83ba6670 1181 $o_value = trim ($s[0]);
24cbe8af 1182 if ($strip_optgroup)
2613bc1c 1183 $o_value = ereg_replace ('^.+\^', '', ereg_replace ('^(.+)&', '\\1 ', $o_value));
83ba6670
DO
1184 $a_value = trim ($s[1]);
1185 if ($which == 'a')
010231c2 1186 return "<a href='${a_value}'>${o_value}</a>";
83ba6670
DO
1187 if ($which == 'o')
1188 return $o_value;
1189}
1190
2de490b7
DO
1191function buildVServiceName ($vsinfo = NULL)
1192{
1193 if ($vsinfo == NULL)
1194 {
1195 showError ('NULL argument', __FUNCTION__);
1196 return NULL;
1197 }
8d790216 1198 return $vsinfo['vip'] . ':' . $vsinfo['vport'] . '/' . $vsinfo['proto'];
2de490b7
DO
1199}
1200
177b1e9b
DO
1201// rackspace usage for a single rack
1202// (T + W + U) / (height * 3 - A)
11df133a 1203function getRSUforRack ($data = NULL)
177b1e9b 1204{
11df133a 1205 if ($data == NULL)
177b1e9b
DO
1206 {
1207 showError ('Invalid argument', __FUNCTION__);
1208 return NULL;
1209 }
6ffba290 1210 $counter = array ('A' => 0, 'U' => 0, 'T' => 0, 'W' => 0, 'F' => 0);
9e60f7df
DO
1211 for ($unit_no = $data['height']; $unit_no > 0; $unit_no--)
1212 for ($locidx = 0; $locidx < 3; $locidx++)
1213 $counter[$data[$unit_no][$locidx]['state']]++;
dfa3c075 1214 return ($counter['T'] + $counter['W'] + $counter['U']) / ($counter['T'] + $counter['W'] + $counter['U'] + $counter['F']);
177b1e9b
DO
1215}
1216
11df133a
DO
1217// Same for row.
1218function getRSUforRackRow ($rowData = NULL)
1219{
bb26a59e 1220 if ($rowData === NULL)
11df133a
DO
1221 {
1222 showError ('Invalid argument', __FUNCTION__);
1223 return NULL;
1224 }
bb26a59e
DO
1225 if (!count ($rowData))
1226 return 0;
11df133a 1227 $counter = array ('A' => 0, 'U' => 0, 'T' => 0, 'W' => 0, 'F' => 0);
f81a2012 1228 $total_height = 0;
dfa3c075
DO
1229 foreach (array_keys ($rowData) as $rack_id)
1230 {
1231 $data = getRackData ($rack_id);
1232 $total_height += $data['height'];
11df133a
DO
1233 for ($unit_no = $data['height']; $unit_no > 0; $unit_no--)
1234 for ($locidx = 0; $locidx < 3; $locidx++)
1235 $counter[$data[$unit_no][$locidx]['state']]++;
dfa3c075
DO
1236 }
1237 return ($counter['T'] + $counter['W'] + $counter['U']) / ($counter['T'] + $counter['W'] + $counter['U'] + $counter['F']);
11df133a
DO
1238}
1239
f1a0477d
DO
1240function getObjectCount ($rackData)
1241{
1242 $objects = array();
1243 for ($i = $rackData['height']; $i > 0; $i--)
1244 for ($locidx = 0; $locidx < 3; $locidx++)
1245 if
1246 (
1247 $rackData[$i][$locidx]['state'] == 'T' and
1248 !in_array ($rackData[$i][$locidx]['object_id'], $objects)
1249 )
1250 $objects[] = $rackData[$i][$locidx]['object_id'];
1251 return count ($objects);
1252}
1253
9af110b4
DO
1254// Perform substitutions and return resulting string
1255function apply_macros ($macros, $subject)
1256{
1257 $ret = $subject;
1258 foreach ($macros as $search => $replace)
1259 $ret = str_replace ($search, $replace, $ret);
1260 return $ret;
1261}
1262
1263// Make sure the string is always wrapped with LF characters
1264function lf_wrap ($str)
1265{
1266 $ret = trim ($str, "\r\n");
1267 if (!empty ($ret))
1268 $ret .= "\n";
1269 return $ret;
1270}
1271
e6e7d8b3
DO
1272// Adopted from Mantis BTS code.
1273function string_insert_hrefs ($s)
1274{
1275 if (getConfigVar ('DETECT_URLS') != 'yes')
1276 return $s;
1277 # Find any URL in a string and replace it by a clickable link
1278 $s = preg_replace( '/(([[:alpha:]][-+.[:alnum:]]*):\/\/(%[[:digit:]A-Fa-f]{2}|[-_.!~*\';\/?%^\\\\:@&={\|}+$#\(\),\[\][:alnum:]])+)/se',
1279 "'<a href=\"'.rtrim('\\1','.').'\">\\1</a> [<a href=\"'.rtrim('\\1','.').'\" target=\"_blank\">^</a>]'",
1280 $s);
1281 $s = preg_replace( '/\b' . email_regex_simple() . '\b/i',
1282 '<a href="mailto:\0">\0</a>',
1283 $s);
1284 return $s;
1285}
1286
1287// Idem.
1288function email_regex_simple ()
1289{
1290 return "(([a-z0-9!#*+\/=?^_{|}~-]+(?:\.[a-z0-9!#*+\/=?^_{|}~-]+)*)" . # recipient
1291 "\@((?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?))"; # @domain
1292}
1293
118e4c38
DO
1294// Parse AUTOPORTS_CONFIG and return a list of generated pairs (port_type, port_name)
1295// for the requested object_type_id.
1296function getAutoPorts ($type_id)
1297{
1298 $ret = array();
1299 $typemap = explode (';', str_replace (' ', '', getConfigVar ('AUTOPORTS_CONFIG')));
1300 foreach ($typemap as $equation)
1301 {
1302 $tmp = explode ('=', $equation);
1303 if (count ($tmp) != 2)
1304 continue;
1305 $objtype_id = $tmp[0];
1306 if ($objtype_id != $type_id)
1307 continue;
1308 $portlist = $tmp[1];
1309 foreach (explode ('+', $portlist) as $product)
1310 {
1311 $tmp = explode ('*', $product);
1312 if (count ($tmp) != 3)
1313 continue;
1314 $nports = $tmp[0];
1315 $port_type = $tmp[1];
1316 $format = $tmp[2];
1317 for ($i = 0; $i < $nports; $i++)
1318 $ret[] = array ('type' => $port_type, 'name' => @sprintf ($format, $i));
1319 }
1320 }
1321 return $ret;
1322}
1323
9dfd4cc9
DO
1324// Find if a particular tag id exists on the tree, then attach the
1325// given child tag to it. If the parent tag doesn't exist, return FALSE.
1326function attachChildTag (&$tree, $parent_id, $child_id, $child_info)
1327{
1328 foreach ($tree as $tagid => $taginfo)
1329 {
1330 if ($tagid == $parent_id)
1331 {
1332 $tree[$tagid]['kids'][$child_id] = $child_info;
1333 return TRUE;
1334 }
1335 elseif (attachChildTag ($tree[$tagid]['kids'], $parent_id, $child_id, $child_info))
1336 return TRUE;
1337 }
1338 return FALSE;
1339}
1340
1341// Build a tree from the tag list and return it.
1342function getTagTree ()
1343{
20c901a7
DO
1344 global $taglist;
1345 $mytaglist = $taglist;
1346 $ret = array();
1347 while (count ($mytaglist) > 0)
9dfd4cc9
DO
1348 {
1349 $picked = FALSE;
20c901a7 1350 foreach ($mytaglist as $tagid => $taginfo)
9dfd4cc9
DO
1351 {
1352 $taginfo['kids'] = array();
1353 if ($taginfo['parent_id'] == NULL)
1354 {
20c901a7 1355 $ret[$tagid] = $taginfo;
9dfd4cc9 1356 $picked = TRUE;
20c901a7 1357 unset ($mytaglist[$tagid]);
9dfd4cc9 1358 }
20c901a7 1359 elseif (attachChildTag ($ret, $taginfo['parent_id'], $tagid, $taginfo))
9dfd4cc9
DO
1360 {
1361 $picked = TRUE;
20c901a7 1362 unset ($mytaglist[$tagid]);
9dfd4cc9
DO
1363 }
1364 }
1365 if (!$picked) // Only orphaned items on the list.
1366 break;
1367 }
20c901a7 1368 return $ret;
9dfd4cc9
DO
1369}
1370
ba93bd98
DO
1371function serializeTags ($trail)
1372{
1373 $comma = '';
1374 $ret = '';
1375 foreach ($trail as $taginfo)
1376 {
1377 $ret .= $comma . $taginfo['tag'];
1378 $comma = ', ';
1379 }
1380 return $ret;
1381}
1382
ab379543 1383// a helper for getTrailExpansion()
ba93bd98
DO
1384function traceTrail ($tree, $trail)
1385{
1386 // For each tag find its path from the root, then combine items
1387 // of all paths and add them to the trail, if they aren't there yet.
1388 $ret = array();
1389 foreach ($tree as $taginfo1)
1390 {
1391 $hit = FALSE;
1392 foreach ($trail as $taginfo2)
1393 if ($taginfo1['id'] == $taginfo2['id'])
1394 {
1395 $hit = TRUE;
1396 break;
1397 }
1398 if (count ($taginfo1['kids']) > 0)
1399 {
1400 $subsearch = traceTrail ($taginfo1['kids'], $trail);
1401 if (count ($subsearch))
1402 {
1403 $hit = TRUE;
1404 $ret = array_merge ($ret, $subsearch);
1405 }
1406 }
1407 if ($hit)
1408 $ret[] = $taginfo1;
1409 }
1410 return $ret;
1411}
1412
1413// For each tag add all its parent tags onto the list. Don't expect anything
1414// except user's tags on the trail.
1415function getTrailExpansion ($trail)
1416{
20c901a7
DO
1417 global $tagtree;
1418 return traceTrail ($tagtree, $trail);
ba93bd98
DO
1419}
1420
1421// Return the list of missing implicit tags.
1422function getImplicitTags ($oldtags)
1423{
1424 $ret = array();
1425 $newtags = getTrailExpansion ($oldtags);
1426 foreach ($newtags as $newtag)
1427 {
1428 $already_exists = FALSE;
1429 foreach ($oldtags as $oldtag)
1430 if ($newtag['id'] == $oldtag['id'])
1431 {
1432 $already_exists = TRUE;
1433 break;
1434 }
1435 if ($already_exists)
1436 continue;
d6089951 1437 $ret[] = array ('id' => $newtag['id'], 'tag' => $newtag['tag'], 'parent_id' => $newtag['parent_id']);
ba93bd98
DO
1438 }
1439 return $ret;
1440}
1441
ab379543
DO
1442// Minimize the trail: exclude all implicit tags and return the resulting trail.
1443function getExplicitTagsOnly ($trail, $tree = NULL)
1444{
20c901a7 1445 global $tagtree;
ab379543 1446 if ($tree === NULL)
20c901a7 1447 $tree = $tagtree;
ab379543
DO
1448 $ret = array();
1449 foreach ($tree as $taginfo)
1450 {
1451 if (isset ($taginfo['kids']))
1452 {
1453 $harvest = getExplicitTagsOnly ($trail, $taginfo['kids']);
1454 if (count ($harvest) > 0)
1455 {
1456 $ret = array_merge ($ret, $harvest);
1457 continue;
1458 }
1459 }
1460 // This tag isn't implicit, test is for being explicit.
1461 foreach ($trail as $testtag)
1462 if ($taginfo['id'] == $testtag['id'])
1463 {
1464 $ret[] = $testtag;
1465 break;
1466 }
1467 }
1468 return $ret;
1469}
1470
74ccacff
DO
1471// Maximize the trail: for each tag add all tags, for which it is direct or indirect parent.
1472// Unlike other functions, this one accepts and returns a list of integer tag IDs, not
1473// a list of tag structures.
1474function complementByKids ($idlist, $tree = NULL, $getall = FALSE)
1475{
20c901a7 1476 global $tagtree;
74ccacff 1477 if ($tree === NULL)
20c901a7 1478 $tree = $tagtree;
74ccacff
DO
1479 $getallkids = $getall;
1480 $ret = array();
1481 foreach ($tree as $taginfo)
1482 {
1483 foreach ($idlist as $test_id)
1484 if ($getall or $taginfo['id'] == $test_id)
1485 {
1486 $ret[] = $taginfo['id'];
1487 // Once matched node makes all sub-nodes match, but don't make
1488 // a mistake of matching every other node at the current level.
1489 $getallkids = TRUE;
1490 break;
1491 }
1492 if (isset ($taginfo['kids']))
1493 $ret = array_merge ($ret, complementByKids ($idlist, $taginfo['kids'], $getallkids));
1494 $getallkids = FALSE;
1495 }
1496 return $ret;
1497}
1498
2fb24351 1499function loadRackObjectAutoTags()
ba93bd98 1500{
2c6c7645 1501 assertUIntArg ('object_id', __FUNCTION__);
2fb24351
DO
1502 $object_id = $_REQUEST['object_id'];
1503 $oinfo = getObjectInfo ($object_id);
1504 $ret = array();
1505 $ret[] = array ('tag' => '$id_' . $_REQUEST['object_id']);
d817ba03 1506 $ret[] = array ('tag' => '$any_object');
2fb24351
DO
1507 return $ret;
1508}
1509
f9bc186f
DO
1510function loadIPv4PrefixAutoTags()
1511{
2c6c7645 1512 assertUIntArg ('id', __FUNCTION__);
41bd8c87 1513 $subnet = getIPRange ($_REQUEST['id']);
f9bc186f
DO
1514 $ret = array();
1515 $ret[] = array ('tag' => '$id_' . $_REQUEST['id']);
41bd8c87 1516 $ret[] = array ('tag' => '$ipv4net-' . str_replace ('.', '-', $subnet['ip']) . '-' . $subnet['mask']);
d817ba03
DO
1517 // FIXME: find and list tags for all parent networks
1518 $ret[] = array ('tag' => '$any_ipv4net');
1519 $ret[] = array ('tag' => '$any_net');
41bd8c87
DO
1520 return $ret;
1521}
1522
1523function loadRackAutoTags()
1524{
2c6c7645 1525 assertUIntArg ('rack_id', __FUNCTION__);
41bd8c87
DO
1526 $ret = array();
1527 $ret[] = array ('tag' => '$id_' . $_REQUEST['rack_id']);
d817ba03
DO
1528 $ret[] = array ('tag' => '$any_rack');
1529 return $ret;
1530}
1531
1532function loadIPv4AddressAutoTags()
1533{
2c6c7645 1534 assertIPv4Arg ('ip', __FUNCTION__);
d817ba03
DO
1535 $ret = array();
1536 $ret[] = array ('tag' => '$ipv4net-' . str_replace ('.', '-', $subnet['ip']) . '-32');
1537 // FIXME: find and list tags for all parent networks
1538 $ret[] = array ('tag' => '$any_ipv4net');
1539 $ret[] = array ('tag' => '$any_net');
1540 return $ret;
1541}
1542
1543function loadIPv4VSAutoTags()
1544{
2c6c7645 1545 assertUIntArg ('id', __FUNCTION__);
d817ba03
DO
1546 $ret = array();
1547 $ret[] = array ('tag' => '$id_' . $_REQUEST['id']);
1548 $ret[] = array ('tag' => '$any_ipv4vs');
1549 $ret[] = array ('tag' => '$any_vs');
1550 return $ret;
1551}
1552
1553function loadIPv4RSPoolAutoTags()
1554{
2c6c7645 1555 assertUIntArg ('pool_id', __FUNCTION__);
d817ba03 1556 $ret = array();
6aff02cf 1557 $ret[] = array ('tag' => '$id_' . $_REQUEST['pool_id']);
d817ba03
DO
1558 $ret[] = array ('tag' => '$any_ipv4rspool');
1559 $ret[] = array ('tag' => '$any_rspool');
f9bc186f
DO
1560 return $ret;
1561}
1562
2fb24351
DO
1563function getGlobalAutoTags()
1564{
1565 global $remote_username, $accounts;
1566 $ret = array();
1567 $user_id = 0;
1568 foreach ($accounts as $a)
1569 if ($a['user_name'] == $remote_username)
1570 {
1571 $user_id = $a['user_id'];
1572 break;
1573 }
1574 $ret[] = array ('tag' => '$username_' . $remote_username);
1575 $ret[] = array ('tag' => '$userid_' . $user_id);
1576 return $ret;
ba93bd98
DO
1577}
1578
ab379543
DO
1579// Build a tag trail from supplied tag id list and return it.
1580function buildTrailFromIds ($tagidlist)
1581{
20c901a7 1582 global $taglist;
ab379543
DO
1583 $ret = array();
1584 foreach ($tagidlist as $tag_id)
1585 if (isset ($taglist[$tag_id]))
1586 $ret[] = $taglist[$tag_id];
1587 return $ret;
1588}
1589
7cc02fc1
DO
1590// Process a given tag tree and return only meaningful branches. The resulting
1591// (sub)tree will have refcnt leaves on every last branch.
1592function getObjectiveTagTree ($tree, $realm)
1593{
1594 $ret = array();
1595 foreach ($tree as $taginfo)
1596 {
1597 $subsearch = array();
1598 $pick = FALSE;
1599 if (count ($taginfo['kids']))
1600 {
1601 $subsearch = getObjectiveTagTree ($taginfo['kids'], $realm);
1602 $pick = count ($subsearch) > 0;
1603 }
5bdc04ec 1604 if (isset ($taginfo['refcnt'][$realm]))
7cc02fc1
DO
1605 $pick = TRUE;
1606 if (!$pick)
1607 continue;
1608 $ret[] = array
1609 (
1610 'id' => $taginfo['id'],
1611 'tag' => $taginfo['tag'],
1612 'parent_id' => $taginfo['parent_id'],
1613 'refcnt' => $taginfo['refcnt'],
1614 'kids' => $subsearch
1615 );
1616 }
1617 return $ret;
1618}
1619
1620function getStringFromTrail ($trail)
1621{
1622 $str = '';
1623 $comma = '';
1624 foreach ($trail as $tag)
1625 {
1626 $str .= $comma . $tag['id'];
1627 $comma = ',';
1628 }
1629 return $str;
1630}
1631
e673ee24 1632?>