Revert "- rebase against master branch"
[racktables] / wwwroot / inc / popup.php
1 <?php
2
3 # This file is a part of RackTables, a datacenter and server room management
4 # framework. See accompanying file "COPYING" for the full copyright and
5 # licensing information.
6
7 // Return a list of rack IDs, which are P or less positions
8 // far from the given rack in its row.
9 function getProximateRacks ($rack_id, $proximity = 0)
10 {
11 $ret = array ($rack_id);
12 if ($proximity > 0)
13 {
14 $rack = spotEntity ('rack', $rack_id);
15 $rackList = listCells ('rack', $rack['row_id']);
16 doubleLink ($rackList);
17 $todo = $proximity;
18 $cur_item = $rackList[$rack_id];
19 while ($todo and array_key_exists ('prev_key', $cur_item))
20 {
21 $cur_item = $rackList[$cur_item['prev_key']];
22 $ret[] = $cur_item['id'];
23 $todo--;
24 }
25 $todo = $proximity;
26 $cur_item = $rackList[$rack_id];
27 while ($todo and array_key_exists ('next_key', $cur_item))
28 {
29 $cur_item = $rackList[$cur_item['next_key']];
30 $ret[] = $cur_item['id'];
31 $todo--;
32 }
33 }
34 return $ret;
35 }
36
37 function findSparePorts ($port_info, $filter)
38 {
39 $qparams = array ();
40 $query = "
41 SELECT
42 p.id,
43 p.name,
44 p.reservation_comment,
45 p.iif_id,
46 p.type as oif_id,
47 pii.iif_name,
48 d.dict_value as oif_name,
49 p.object_id,
50 o.name as object_name
51 FROM Port p
52 INNER JOIN Object o ON o.id = p.object_id
53 INNER JOIN PortInnerInterface pii ON p.iif_id = pii.id
54 INNER JOIN Dictionary d ON d.dict_key = p.type
55 ";
56 // porttype filter (non-strict match)
57 $query .= "
58 INNER JOIN (
59 SELECT Port.id FROM Port
60 INNER JOIN
61 (
62 SELECT DISTINCT pic2.iif_id
63 FROM PortInterfaceCompat pic2
64 INNER JOIN PortCompat pc ON pc.type2 = pic2.oif_id
65 ";
66 if ($port_info['iif_id'] != 1)
67 {
68 $query .= " INNER JOIN PortInterfaceCompat pic ON pic.oif_id = pc.type1 WHERE pic.iif_id = ? AND ";
69 $qparams[] = $port_info['iif_id'];
70 }
71 else
72 {
73 $query .= " WHERE pc.type1 = ? AND ";
74 $qparams[] = $port_info['oif_id'];
75 }
76 $query .= "
77 pic2.iif_id <> 1
78 ) AS sub1 USING (iif_id)
79 UNION
80 SELECT Port.id
81 FROM Port
82 INNER JOIN PortCompat ON type1 = type
83 WHERE
84 iif_id = 1 and type2 = ?
85 ) AS sub2 ON sub2.id = p.id
86 ";
87 $qparams[] = $port_info['oif_id'];
88 $qparams[] = $port_info['id'];
89
90 // don't allow a port to be linked to itself
91 $query .= " WHERE p.id <> ? ";
92
93 // don't allow already linked ports
94 if (!$filter['linked'])
95 $query .= "AND p.id NOT IN (SELECT porta FROM Link) AND p.id NOT IN (SELECT portb FROM Link) ";
96
97 // rack filter
98 if (! empty ($filter['racks']))
99 {
100 // objects directly mounted in the racks
101 $query .= sprintf
102 (
103 'AND p.object_id IN (SELECT DISTINCT object_id FROM RackSpace WHERE rack_id IN (%s) ',
104 questionMarks (count ($filter['racks']))
105 );
106 // children of objects directly mounted in the racks
107 $query .= sprintf
108 (
109 "UNION SELECT child_entity_id FROM EntityLink WHERE parent_entity_type='object' AND child_entity_type = 'object' AND parent_entity_id IN (SELECT DISTINCT object_id FROM RackSpace WHERE rack_id IN (%s)) ",
110 questionMarks (count ($filter['racks']))
111 );
112 // zero-U objects mounted to the racks
113 $query .= sprintf
114 (
115 "UNION SELECT child_entity_id FROM EntityLink WHERE parent_entity_type='rack' AND child_entity_type='object' AND parent_entity_id IN (%s)) ",
116 questionMarks (count ($filter['racks']))
117 );
118 $qparams = array_merge ($qparams, $filter['racks']);
119 $qparams = array_merge ($qparams, $filter['racks']);
120 $qparams = array_merge ($qparams, $filter['racks']);
121 }
122 // objectname filter
123 if (! empty ($filter['objects']))
124 {
125 $query .= 'AND o.name like ? ';
126 $qparams[] = '%' . $filter['objects'] . '%';
127 }
128 // asset_no filter
129 if (! empty ($filter['asset_no']))
130 {
131 $query .= 'AND o.asset_no like ? ';
132 $qparams[] = '%' . $filter['asset_no'] . '%';
133 }
134 // portname filter
135 if (! empty ($filter['ports']))
136 {
137 $query .= 'AND p.name LIKE ? ';
138 $qparams[] = '%' . $filter['ports'] . '%';
139 }
140 // ordering
141 $query .= ' ORDER BY o.name';
142
143 $ret = array();
144 $result = usePreparedSelectBlade ($query, $qparams);
145
146 $rows_by_pn = array();
147 $prev_object_id = NULL;
148
149 // fetch port rows from the DB
150 while (TRUE)
151 {
152 $row = $result->fetch (PDO::FETCH_ASSOC);
153 if (isset ($prev_object_id) and (! $row or $row['object_id'] != $prev_object_id))
154 {
155 // handle sorted object's portlist
156 foreach (sortPortList ($rows_by_pn) as $ports_subarray)
157 foreach ($ports_subarray as $port_row)
158 {
159 $port_description = $port_row['object_name'] . ' -- ' . $port_row['name'];
160 if (count ($ports_subarray) > 1)
161 {
162 $if_type = $port_row['iif_id'] == 1 ? $port_row['oif_name'] : $port_row['iif_name'];
163 $port_description .= " ($if_type)";
164 }
165 if (! empty ($port_row['reservation_comment']))
166 $port_description .= ' -- ' . $port_row['reservation_comment'];
167 $ret[$port_row['id']] = $port_description;
168 }
169 $rows_by_pn = array();
170 }
171 $prev_object_id = $row['object_id'];
172 if ($row)
173 $rows_by_pn[$row['name']][] = $row;
174 else
175 break;
176 }
177
178 return $ret;
179 }
180
181 // Return a list of all compatible patch panels
182 function findPatchPanelCandidates ($panel_info, $filter)
183 {
184 // return patch panels which have the same number of ports
185 // exclude those with ports which are linked more than once
186 // TODO: add port compatibility checks
187 $query = "
188 SELECT Object.id,
189 IF(ISNULL(Object.name),CONCAT('[PatchPanel] - object_id: ',Object.id),Object.name) AS name
190 FROM Object
191 WHERE Object.objtype_id = 9
192 AND Object.id != ?
193 AND (SELECT COUNT(Port.id) FROM Port WHERE Port.object_id = Object.id) = (SELECT COUNT(Port.id) FROM Port WHERE Port.object_id = ?) ";
194 $qparams = array ($panel_info['id'], $panel_info['id']);
195
196 // name filter
197 if (! empty ($filter['name']))
198 {
199 $query .= 'AND Object.name LIKE ? ';
200 $qparams[] = '%' . $filter['name'] . '%';
201 }
202
203 // exclude panels which contain one or more ports which are already linked to two or more other ports
204 if (!$filter['linked'])
205 $query .= "AND Object.id NOT IN (SELECT DISTINCT(Object.id)
206 FROM Object
207 LEFT JOIN Port ON Object.id = Port.object_id
208 WHERE Object.objtype_id = 9
209 AND Port.id IN (SELECT Port.id FROM Port WHERE (SELECT COUNT(*) FROM Link WHERE Link.porta = Port.id OR Link.portb = Port.id) > 1)) ";
210
211 // ordering
212 $query .= ' ORDER BY Object.name';
213
214 $ret = array();
215 $result = usePreparedSelectBlade ($query, $qparams);
216 while ($row = $result->fetch (PDO::FETCH_ASSOC))
217 $ret[$row['id']] = $row['name'];
218 return $ret;
219 }
220
221 // Return a list of all objects which are possible parents
222 // Special case for VMs and VM Virtual Switches
223 // - only select Servers with the Hypervisor attribute set to Yes
224 function findObjectParentCandidates ($object_id)
225 {
226 $object = spotEntity ('object', $object_id);
227 $args = array ($object['objtype_id'], $object_id, $object_id);
228
229 $query = "SELECT O.id, O.name, O.objtype_id FROM Object O ";
230 $query .= "LEFT JOIN ObjectParentCompat OPC ON O.objtype_id = OPC.parent_objtype_id ";
231 $query .= "WHERE OPC.child_objtype_id = ? ";
232 $query .= "AND O.id != ? ";
233 // exclude existing parents
234 $query .= "AND O.id NOT IN (SELECT parent_entity_id FROM EntityLink WHERE parent_entity_type = 'object' AND child_entity_type = 'object' AND child_entity_id = ?) ";
235 if ($object['objtype_id'] == 1504 || $object['objtype_id'] == 1507)
236 {
237 array_push($args, $object['objtype_id'], $object_id, $object_id);
238 $query .= "AND OPC.parent_objtype_id != 4 ";
239 $query .= "UNION ";
240 $query .= "SELECT O.id, O.name, O.objtype_id FROM Object O ";
241 $query .= "LEFT JOIN ObjectParentCompat OPC ON O.objtype_id = OPC.parent_objtype_id ";
242 $query .= "LEFT JOIN AttributeValue AV ON O.id = AV.object_id ";
243 $query .= "WHERE OPC.child_objtype_id = ? ";
244 $query .= "AND (O.objtype_id = 4 AND AV.attr_id = 26 AND AV.uint_value = 1501) ";
245 $query .= "AND O.id != ? ";
246 // exclude existing parents
247 $query .= "AND O.id NOT IN (SELECT parent_entity_id FROM EntityLink WHERE parent_entity_type = 'object' AND child_entity_type = 'object' AND child_entity_id = ?) ";
248 }
249 $query .= "ORDER BY 2";
250
251 $result = usePreparedSelectBlade ($query, $args);
252 $ret = array();
253 while ($row = $result->fetch (PDO::FETCH_ASSOC))
254 $ret[$row['id']] = empty ($row['name']) ? sprintf("[%s] - object %d", decodeObjectType ($row['objtype_id'], 'o'), $row['id']) : $row['name'];
255 return $ret;
256 }
257
258 function sortObjectAddressesAndNames ($a, $b)
259 {
260 $objname_cmp = sortTokenize($a['object_name'], $b['object_name']);
261 if ($objname_cmp == 0)
262 {
263 $name_a = (isset ($a['port_name'])) ? $a['port_name'] : '';
264 $name_b = (isset ($b['port_name'])) ? $b['port_name'] : '';
265 $objname_cmp = sortTokenize($name_a, $name_b);
266 if ($objname_cmp == 0)
267 sortTokenize($a['ip'], $b['ip']);
268 return $objname_cmp;
269 }
270 return $objname_cmp;
271 }
272
273 function renderPopupObjectSelector()
274 {
275 $object_id = getBypassValue();
276 echo '<div style="background-color: #f0f0f0; border: 1px solid #3c78b5; padding: 10px; height: 100%; text-align: center; margin: 5px;">';
277 echo '<h2>Choose a container:</h2>';
278 echo '<form action="javascript:;">';
279 $parents = findObjectParentCandidates($object_id);
280 printSelect ($parents, array ('name' => 'parents', 'size' => getConfigVar ('MAXSELSIZE')));
281 echo '<br>';
282 echo "<input type=submit value='Proceed' onclick='".
283 "if (getElementById(\"parents\").value != \"\") {".
284 " opener.location=\"?module=redirect&page=object&tab=edit&op=linkEntities&object_id=${object_id}&child_entity_type=object&child_entity_id=${object_id}&parent_entity_type=object&parent_entity_id=\"+getElementById(\"parents\").value; ".
285 " window.close();}'>";
286 echo '</form></div>';
287 }
288
289 function handlePopupPortLink()
290 {
291 assertUIntArg ('port');
292 assertUIntArg ('remote_port');
293 assertStringArg ('cable', TRUE);
294 $port_info = getPortInfo ($_REQUEST['port']);
295 $remote_port_info = getPortInfo ($_REQUEST['remote_port']);
296 $POIFC = getPortOIFCompat();
297 if (isset ($_REQUEST['port_type']) and isset ($_REQUEST['remote_port_type']))
298 {
299 $type_local = $_REQUEST['port_type'];
300 $type_remote = $_REQUEST['remote_port_type'];
301 }
302 else
303 {
304 $type_local = $port_info['oif_id'];
305 $type_remote = $remote_port_info['oif_id'];
306 }
307 $matches = FALSE;
308 $js_table = '';
309 foreach ($POIFC as $pair)
310 if ($pair['type1'] == $type_local && $pair['type2'] == $type_remote)
311 {
312 $matches = TRUE;
313 break;
314 }
315 else
316 $js_table .= "POIFC['${pair['type1']}-${pair['type2']}'] = 1;\n";
317
318 if ($matches)
319 {
320 if ($port_info['oif_id'] != $type_local)
321 commitUpdatePortOIF ($port_info['id'], $type_local);
322 if ($remote_port_info['oif_id'] != $type_remote)
323 commitUpdatePortOIF ($remote_port_info['id'], $type_remote);
324 linkPorts ($port_info['id'], $remote_port_info['id'], $_REQUEST['cable']);
325 showOneLiner
326 (
327 8,
328 array
329 (
330 formatPortLink ($port_info['id'], $port_info['name'], NULL, NULL),
331 formatPort ($remote_port_info),
332 )
333 );
334 addJS (<<<END
335 window.opener.location.reload(true);
336 window.close();
337 END
338 , TRUE);
339 backupLogMessages();
340 }
341 else
342 {
343 // JS code to display port compatibility hint
344 addJS (<<<END
345 POIFC = {};
346 $js_table
347 $(document).ready(function () {
348 $('select.porttype').change(onPortTypeChange);
349 onPortTypeChange();
350 });
351 function onPortTypeChange() {
352 var key = $('*[name=port_type]')[0].value + '-' + $('*[name=remote_port_type]')[0].value;
353 if (POIFC[key] == 1)
354 {
355 $('#hint-not-compat').hide();
356 $('#hint-compat').show();
357 }
358 else
359 {
360 $('#hint-compat').hide();
361 $('#hint-not-compat').show();
362 }
363 }
364 END
365 , TRUE);
366 addCSS (<<<END
367 .compat-hint {
368 display: none;
369 font-size: 125%;
370 }
371 .compat-hint#hint-compat {
372 color: green;
373 }
374 .compat-hint#hint-not-compat {
375 color: #804040;
376 }
377 END
378 , TRUE);
379 // render port type editor form
380 echo '<form method=GET>';
381 echo '<input type=hidden name="module" value="popup">';
382 echo '<input type=hidden name="helper" value="portlist">';
383 echo '<input type=hidden name="port" value="' . $port_info['id'] . '">';
384 echo '<input type=hidden name="remote_port" value="' . $remote_port_info['id'] . '">';
385 echo '<input type=hidden name="cable" value="' . htmlspecialchars ($_REQUEST['cable'], ENT_QUOTES) . '">';
386 echo '<p>The ports you have selected are not compatible. Please select a compatible transceiver pair.';
387 echo '<p>';
388 echo formatPort ($port_info) . ' ';
389 if ($port_info['iif_id'] == 1)
390 {
391 echo formatPortIIFOIF ($port_info);
392 echo '<input type=hidden name="port_type" value="' . $port_info['oif_id'] . '">';
393 }
394 else
395 {
396 echo '<label>' . $port_info['iif_name'] . ' ';
397 printSelect (getExistingPortTypeOptions ($port_info['id']), array ('class' => 'porttype', 'name' => 'port_type'), $type_local);
398 echo '</label>';
399 }
400 echo ' &mdash; ';
401 if ($remote_port_info['iif_id'] == 1)
402 {
403 echo formatPortIIFOIF ($remote_port_info);
404 echo '<input type=hidden name="remote_port_type" value="' . $remote_port_info['oif_id'] . '">';
405 }
406 else
407 {
408 echo '<label>' . $remote_port_info['iif_name'] . ' ';
409 printSelect (getExistingPortTypeOptions ($remote_port_info['id']), array ('class' => 'porttype', 'name' => 'remote_port_type'), $type_remote);
410 echo '</label>';
411 }
412 echo ' ' . formatPort ($remote_port_info);
413 echo '<p class="compat-hint" id="hint-not-compat">&#10005; Not compatible port types</p>';
414 echo '<p class="compat-hint" id="hint-compat">&#10004; Compatible port types</p>';
415 echo '<p><input type=submit name="do_link" value="Link">';
416 }
417 }
418
419 function renderPopupPortSelector()
420 {
421 assertUIntArg ('port');
422 $port_id = $_REQUEST['port'];
423 $port_info = getPortInfo ($port_id);
424 $in_rack = isCheckSet ('in_rack');
425 $linked = isCheckSet ('linked');
426
427 // fill port filter structure
428 $filter = array
429 (
430 'racks' => array(),
431 'objects' => '',
432 'ports' => '',
433 'asset_no' => '',
434 'linked' => $linked
435 );
436 if (isset ($_REQUEST['filter-obj']))
437 $filter['objects'] = trim($_REQUEST['filter-obj']);
438 if (isset ($_REQUEST['filter-port']))
439 $filter['ports'] = trim($_REQUEST['filter-port']);
440 if (isset ($_REQUEST['filter-asset_no']))
441 $filter['asset_no'] = trim($_REQUEST['filter-asset_no']);
442 if ($in_rack)
443 {
444 $object = spotEntity ('object', $port_info['object_id']);
445 if ($object['rack_id']) // the object itself is mounted in a rack
446 $filter['racks'] = getProximateRacks ($object['rack_id'], getConfigVar ('PROXIMITY_RANGE'));
447 elseif ($object['container_id']) // the object is not mounted in a rack, but it's container may be
448 {
449 $container = spotEntity ('object', $object['container_id']);
450 if ($container['rack_id'])
451 $filter['racks'] = getProximateRacks ($container['rack_id'], getConfigVar ('PROXIMITY_RANGE'));
452 }
453 }
454 $spare_ports = array();
455 if
456 (
457 ! empty ($filter['racks']) ||
458 ! empty ($filter['objects']) ||
459 ! empty ($filter['ports']) ||
460 ! empty ($filter['asset_no'])
461 )
462 $spare_ports = findSparePorts ($port_info, $filter);
463
464 // display search form
465 echo 'Link ' . formatPort ($port_info) . ' to...';
466 echo '<form method=GET>';
467 startPortlet ('Port list filter');
468 echo '<input type=hidden name="module" value="popup">';
469 echo '<input type=hidden name="helper" value="portlist">';
470 echo '<input type=hidden name="port" value="' . $port_id . '">';
471 echo '<table align="center" valign="bottom"><tr>';
472 echo '<td class="tdleft"><label>Object name:<br><input type=text size=8 name="filter-obj" value="' . htmlspecialchars ($filter['objects'], ENT_QUOTES) . '"></label></td>';
473 echo '<td class="tdleft"><label>Asset tag:<br><input type=text size=8 name="filter-asset_no" value="' . htmlspecialchars ($filter['asset_no'], ENT_QUOTES) . '"></label></td>';
474 echo '<td class="tdleft"><label>Port name:<br><input type=text size=6 name="filter-port" value="' . htmlspecialchars ($filter['ports'], ENT_QUOTES) . '"></label></td></tr>';
475 echo '<td class="tdleft" valign="bottom"><label><input type=checkbox name="in_rack"' . ($in_rack ? ' checked' : '') . '>Nearest racks</label></td>';
476 echo '<td class="tdleft" vlaign="bottom"><label><input type=checkbox name="linked"'. ($linked ? ' checked' : '') .'>Include linked ports</label></td></tr>';
477 echo '<tr><td colspan=2 valign="bottom"><input type=submit value="show ports"></td></tr>';
478 echo '</table>';
479 finishPortlet();
480
481 // display results
482 startPortlet ('Compatible spare ports');
483 if (empty ($spare_ports))
484 echo '(nothing found)';
485 else
486 {
487 echo getSelect ($spare_ports, array ('name' => 'remote_port', 'size' => getConfigVar ('MAXSELSIZE')), NULL, FALSE);
488 echo "<p>Cable ID: <input type=text id=cable name=cable>";
489 echo "<p><input type='submit' value='Link' name='do_link'>";
490 }
491 finishPortlet();
492 echo '</form>';
493 }
494
495 function handlePopupPatchPanelLink()
496 {
497 global $dbxlink;
498 assertUIntArg ('object_id');
499 assertUIntArg ('remote_object_id');
500 $object_id = $_REQUEST['object_id'];
501 $object = spotEntity ('object', $object_id);
502 amplifyCell ($object);
503 $remote_object_id = $_REQUEST['remote_object_id'];
504 $remote_object = spotEntity ('object', $remote_object_id);
505 amplifyCell ($remote_object);
506
507 // reindex numerically instead of by port_id
508 $ports = array_values ($object['ports']);
509 $remote_ports = array_values ($remote_object['ports']);
510
511 $POIFC = getPortOIFCompat();
512 $dbxlink->beginTransaction();
513 $error = FALSE;
514 for ($i=0; $i<count($ports); $i++)
515 {
516 $matches = FALSE;
517 foreach ($POIFC as $pair)
518 {
519 if ($pair['type1'] == $ports[$i]['oif_id'] && $pair['type2'] == $remote_ports[$i]['oif_id'])
520 {
521 $matches = TRUE;
522 break;
523 }
524 }
525 if ($matches)
526 linkPorts ($ports[$i]['id'], $remote_ports[$i]['id']);
527 else
528 {
529 $error = TRUE;
530 break;
531 }
532 }
533 if ($error)
534 {
535 $dbxlink->rollBack();
536 showError ('Not all ports are compatible');
537 }
538 else
539 {
540 $dbxlink->commit();
541 showSuccess ('Patch panels linked successfully');
542 }
543 addJS (<<<END
544 window.opener.location.reload(true);
545 window.close();
546 END
547 , TRUE);
548 }
549
550 function renderPopupPatchPanelSelector ()
551 {
552 assertUIntArg ('object_id');
553 $object_id = $_REQUEST['object_id'];
554 $object = spotEntity ('object', $object_id);
555 amplifyCell ($object);
556 $linked = isset ($_REQUEST['linked']);
557
558 $filter = array
559 (
560 'name' => '',
561 'linked' => $linked
562 );
563 if (isset ($_REQUEST['filter-name']))
564 $filter['name'] = $_REQUEST['filter-name'];
565 $found_panels = findPatchPanelCandidates ($object, $filter);
566
567 // display search form
568 echo "Link <a href='index.php?page=object&object_id=${object_id}'>${object['name']}</a> to...";
569 echo '<form method=GET>';
570 startPortlet ('Patch panel list filter');
571 echo '<input type=hidden name="module" value="popup">';
572 echo '<input type=hidden name="helper" value="patchpanellist">';
573 echo '<input type=hidden name="object_id" value="' . $object_id . '">';
574 echo '<table border=0 align="center" valign="bottom"><tr>';
575 echo '<td class="tdleft"><label>Panel name:<br><input type=text size=10 name="filter-name" value="' . htmlspecialchars ($filter['name'], ENT_QUOTES) . '"></label></td></tr>';
576 echo '<tr><td class="tdleft" vlaign="bottom"><label><input type=checkbox name="linked"'. ($linked ? ' checked' : '') .'>Include panels already linked</label></td></tr>';
577 echo '<tr><td valign="bottom"><input type=submit value="Show panels"></td></tr>';
578 echo '</table>';
579 finishPortlet();
580
581 // display results
582 startPortlet ('Compatible patch panels');
583 if (empty ($found_panels))
584 echo '(nothing found)';
585 else
586 {
587 echo getSelect ($found_panels, array ('name' => 'remote_object_id', 'size' => getConfigVar ('MAXSELSIZE')), NULL, FALSE);
588 echo "<p><input type='submit' value='Link' name='do_link'>";
589 }
590 finishPortlet();
591 echo '</form>';
592 }
593
594 function renderPopupTraceRoute ()
595 {
596 // disable strict error reporting (GraphViz generates several)
597 error_reporting(E_ERROR | E_WARNING | E_PARSE);
598 @include_once 'Image/GraphViz.php';
599 if (!class_exists ('Image_GraphViz'))
600 {
601 echo ('<p>The GraphViz PEAR module could not be found.</p>');
602 return;
603 }
604 // determine if an object or port is being traced
605 if (isset ($_REQUEST['object_id']))
606 {
607 assertUIntArg ('object_id');
608 $object = spotEntity ('object', $_REQUEST['object_id']);
609 amplifyCell ($object);
610 $title = 'Tracing all ports of ' . $object['dname'];
611 $port_data = array ();
612 foreach ($object['ports'] as $port_id => $port_details)
613 $port_data = $port_data + getNeighborPorts ($port_id);
614 }
615 else
616 {
617 assertUIntArg ('port');
618 $port_id = intval ($_REQUEST['port']);
619 $port_info = getPortInfo ($port_id);
620 $title = 'Tracing ' . $port_info['object_name'] . ' / ' . $port_info['name'];
621 $port_data = getNeighborPorts ($port_id);
622 }
623
624 $graph = new Image_GraphViz(NULL, NULL, $title);
625 $graph->addAttributes(array ('label' => $title, 'labelloc' => 't'));
626
627 // add a cluster to the graph for each unique object
628 $objects = array ();
629 foreach ($port_data as $port_id => $port_details)
630 {
631 $object_id = $port_details['object_id'];
632 if (!array_key_exists ($object_id, $objects))
633 {
634 $objects[$object_id] = $port_details['object_name'];
635 $graph->addCluster("${object_id}Cluster", $port_details['object_name'], array ('URL' => "index.php?page=object&object_id=${object_id}"));
636 }
637 }
638
639 // add ports to the graph
640 foreach ($port_data as $port_id => $port_details)
641 {
642 $object_id = $port_details['object_id'];
643 $graph->addNode("${port_id}Node", array ('fontsize' => 8, 'label' => $port_details['port_name'], 'tooltip' => $port_details['port_name']), "${object_id}Cluster");
644 }
645
646 // identify the links
647 $links = $link_count = array ();
648 foreach ($port_data as $port_id => $port_details)
649 {
650 $remote_port_id = $port_details['remote_port_id'];
651 // skip this if the link has already been recorded
652 if (in_array (array ($port_id, $remote_port_id), $links) || in_array (array ($remote_port_id, $port_id), $links))
653 continue;
654
655 // record the link, also increment the endpoint counters
656 $links[] = array ($port_id, $remote_port_id);
657 $link_count[$port_id] = (!isset ($link_count[$port_id])) ? 1 : $link_count[$port_id]+1;
658 $link_count[$remote_port_id] = (!isset ($link_count[$remote_port_id])) ? 1 : $link_count[$remote_port_id]+1;
659 }
660
661 // if there are only two endpoints, flatten the graph
662 $endpoints = array_keys ($link_count, 1);
663 if (count ($endpoints) == 2)
664 {
665 sort ($endpoints);
666 $links = sortLinks ($endpoints[0], $links);
667 }
668
669 // add links to the graph
670 foreach ($links as $link)
671 $graph->addEdge(array ("${link[0]}Node" => "${link[1]}Node"), array ('arrowhead' => 'none', 'tooltip' => 'link'));
672
673 // display the graph
674 $graph->image();
675 }
676
677 function renderPopupIPv4Selector()
678 {
679 echo '<div style="background-color: #f0f0f0; border: 1px solid #3c78b5; padding: 10px; height: 100%; text-align: center; margin: 5px;">';
680 echo '<h2>Choose a port:</h2><br><br>';
681 echo '<form action="javascript:;">';
682 echo '<input type=hidden id=ip>';
683 echo '<select size=' . getConfigVar ('MAXSELSIZE') . ' id=addresses>';
684 $addresses = getAllIPv4Allocations();
685 usort ($addresses, 'sortObjectAddressesAndNames');
686 foreach ($addresses as $address)
687 echo "<option value='${address['ip']}' onclick='getElementById(\"ip\").value=\"${address['ip']}\";'>" .
688 "${address['object_name']} ${address['name']} ${address['ip']}</option>\n";
689 echo '</select><br><br>';
690 echo "<input type=submit value='Proceed' onclick='".
691 "if (getElementById(\"ip\")!=\"\") {".
692 " opener.document.getElementById(\"remoteip\").value=getElementById(\"ip\").value;".
693 " window.close();}'>";
694 echo '</form></div>';
695 }
696
697 function renderPopupHTML()
698 {
699 global $pageno, $tabno;
700 assertStringArg ('helper');
701 $text = '';
702 switch ($_REQUEST['helper'])
703 {
704 case 'objlist':
705 $pageno = 'object';
706 $tabno = 'default';
707 fixContext();
708 assertPermission();
709 $text .= getOutputOf ('renderPopupObjectSelector');
710 break;
711 case 'portlist':
712 case 'patchpanellist':
713 $target = ($_REQUEST['helper'] == 'portlist') ? 'Port' : 'PatchPanel';
714 $pageno = 'depot';
715 $tabno = 'default';
716 fixContext();
717 assertPermission();
718 $text .= '<div style="background-color: #f0f0f0; border: 1px solid #3c78b5; padding: 10px; height: 100%; text-align: center; margin: 5px;">';
719 if (isset ($_REQUEST['do_link']))
720 $text .= getOutputOf ('callHook', "handlePopup${target}Link");
721 else
722 $text .= getOutputOf ('callHook' , "renderPopup${target}Selector");
723 $text .= '</div>';
724 break;
725 case 'traceroute':
726 $pageno = 'depot';
727 $tabno = 'default';
728 fixContext();
729 assertPermission();
730 renderPopupTraceroute ();
731 exit;
732 break;
733 case 'inet4list':
734 $pageno = 'ipv4space';
735 $tabno = 'default';
736 fixContext();
737 assertPermission();
738 $text .= getOutputOf ('renderPopupIPv4Selector');
739 break;
740 default:
741 throw new InvalidRequestArgException ('helper', $_REQUEST['helper']);
742 }
743 header ('Content-Type: text/html; charset=UTF-8');
744 ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
745 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" style="height: 100%;">
746 <?php
747 echo '<head><title>RackTables pop-up</title>';
748 printPageHeaders();
749 echo '</head>';
750 echo '<body style="height: 100%;">' . $text . '</body>';
751 ?>
752 </html>
753 <?php
754 }
755 ?>