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