4421eecc3ae6b003fd1bf941bf6697a9bd41c9e4
[racktables] / wwwroot / inc / interface.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 /*
8 *
9 * This file contains frontend functions for RackTables.
10 *
11 */
12
13 require_once 'ajax-interface.php';
14 require_once 'slb-interface.php';
15
16 // Interface function's special.
17 $nextorder['odd'] = 'even';
18 $nextorder['even'] = 'odd';
19
20 // address allocation type
21 $aat = array
22 (
23 'regular' => 'Connected',
24 'virtual' => 'Loopback',
25 'shared' => 'Shared',
26 'router' => 'Router',
27 'point2point' => 'Point-to-point',
28 );
29 // address allocation code, IPv4 addresses and objects view
30 $aac_right = array
31 (
32 'regular' => '',
33 'virtual' => '<span class="aac-right" title="' . $aat['virtual'] . '">L</span>',
34 'shared' => '<span class="aac-right" title="' . $aat['shared'] . '">S</span>',
35 'router' => '<span class="aac-right" title="' . $aat['router'] . '">R</span>',
36 'point2point' => '<span class="aac-right" title="' . $aat['point2point'] . '">P</span>',
37 );
38 // address allocation code, IPv4 networks view
39 $aac_left = array
40 (
41 'regular' => '',
42 'virtual' => '<span class="aac-left" title="' . $aat['virtual'] . '">L:</span>',
43 'shared' => '<span class="aac-left" title="' . $aat['shared'] . '">S:</span>',
44 'router' => '<span class="aac-left" title="' . $aat['router'] . '">R:</span>',
45 'point2point' => '<span class="aac-left" title="' . $aat['point2point'] . '">P:</span>',
46 );
47
48 $vtdecoder = array
49 (
50 'ondemand' => '',
51 'compulsory' => 'P',
52 # 'alien' => 'NT',
53 );
54
55 $vtoptions = array
56 (
57 'ondemand' => 'auto',
58 'compulsory' => 'permanent',
59 # 'alien' => 'never touch',
60 );
61
62 // This may be populated later onsite, report rendering function will use it.
63 // See the $systemreport for structure.
64 $localreports = array();
65
66 $CodePressMap = array
67 (
68 'sql' => 'sql',
69 'php' => 'php',
70 'html' => 'htmlmixed',
71 'css' => 'css',
72 'js' => 'javascript',
73 );
74
75 $attrtypes = array
76 (
77 'uint' => '[U] unsigned integer',
78 'float' => '[F] floating point',
79 'string' => '[S] string',
80 'dict' => '[D] dictionary record',
81 'date' => '[T] date'
82 );
83
84 function showLogoutURL ()
85 {
86 $https = (isset ($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 's' : '';
87 $port = (! in_array ($_SERVER['SERVER_PORT'], array (80, 443))) ? ':' . $_SERVER['SERVER_PORT'] : '';
88 $pathinfo = pathinfo ($_SERVER['REQUEST_URI']);
89 $dirname = $pathinfo['dirname'];
90 // add a trailing slash if the installation resides in a subdirectory
91 if ($dirname != '/')
92 $dirname .= '/';
93 printf ('http%s://logout@%s%s?logout', $https, $_SERVER['SERVER_NAME'], $dirname);
94 }
95
96 $quick_links = NULL; // you can override this in your local.php, but first initialize it with getConfiguredQuickLinks()
97
98 function renderQuickLinks()
99 {
100 global $quick_links;
101 if (! isset ($quick_links))
102 $quick_links = getConfiguredQuickLinks();
103 echo '<ul class="qlinks">';
104 foreach ($quick_links as $link)
105 echo '<li><a href="' . $link['href'] . '">' . str_replace (' ', '&nbsp;', $link['title']) . '</a></li>';
106 echo '</ul>';
107 }
108
109 function renderInterfaceHTML ($pageno, $tabno, $payload)
110 {
111 ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
112 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
113 <head><title><?php echo getTitle ($pageno); ?></title>
114 <?php printPageHeaders(); ?>
115 </head>
116 <body>
117 <div class="maintable">
118 <div class="mainheader">
119 <div style="float: right" class=greeting><?php global $remote_displayname; echo mkA ($remote_displayname, 'myaccount', NULL, 'default'); ?> [ <a href='<?php showLogoutURL(); ?>'>logout</a> ]</div>
120 <?php echo getConfigVar ('enterprise') ?> RackTables <a href="http://racktables.org" title="Visit RackTables site"><?php echo CODE_VERSION ?></a><?php renderQuickLinks() ?>
121 </div>
122 <div class="menubar"><?php showPathAndSearch ($pageno, $tabno); ?></div>
123 <div class="tabbar"><?php showTabs ($pageno, $tabno); ?></div>
124 <div class="msgbar"><?php showMessageOrError(); ?></div>
125 <div class="pagebar"><?php echo $payload; ?></div>
126 </div>
127 </body>
128 </html>
129 <?php
130 }
131
132 // Main menu.
133 function renderIndexItem ($ypageno)
134 {
135 echo (! permitted ($ypageno)) ? " <td>&nbsp;</td>\n" :
136 " <td>\n" .
137 " <h1><a href='" . makeHref (array ('page' => $ypageno)) . "'>" .
138 getPageName ($ypageno) . "<br>\n" . getImageHREF ($ypageno) .
139 "</a></h1>\n" .
140 " </td>\n";
141 }
142
143 function renderIndex ()
144 {
145 global $indexlayout;
146 ?>
147 <table border=0 cellpadding=0 cellspacing=0 width='100%'>
148 <tr>
149 <td>
150 <div style='text-align: center; margin: 10px; '>
151 <table width='100%' cellspacing=0 cellpadding=20 class=mainmenu border=0>
152 <?php
153 foreach ($indexlayout as $row)
154 {
155 echo '<tr>';
156 foreach ($row as $column)
157 if ($column === NULL)
158 echo '<td>&nbsp;</td>';
159 else
160 renderIndexItem ($column);
161 echo '</tr>';
162 }
163 ?>
164 </table>
165 </div>
166 </td>
167 </tr>
168 </table>
169 <?php
170 }
171
172 function getRenderedAlloc ($object_id, $alloc)
173 {
174 $ret = array
175 (
176 'tr_class' => '',
177 'td_name_suffix' => '',
178 'td_ip' => '',
179 'td_network' => '',
180 'td_routed_by' => '',
181 'td_peers' => '',
182 );
183 $dottedquad = $alloc['addrinfo']['ip'];
184 $ip_bin = $alloc['addrinfo']['ip_bin'];
185
186 $hl_ip_bin = NULL;
187 if (isset ($_REQUEST['hl_ip']))
188 {
189 $hl_ip_bin = ip_parse ($_REQUEST['hl_ip']);
190 addAutoScrollScript ("ip-" . $_REQUEST['hl_ip']);
191 }
192
193 $ret['tr_class'] = $alloc['addrinfo']['class'];
194 if ($hl_ip_bin === $ip_bin)
195 $ret['tr_class'] .= ' highlight';
196
197 // render IP change history
198 $ip_title = '';
199 $ip_class = '';
200 if (isset ($alloc['addrinfo']['last_log']))
201 {
202 $log = $alloc['addrinfo']['last_log'];
203 $ip_title = "title='" .
204 htmlspecialchars
205 (
206 $log['user'] . ', ' . formatAge ($log['time']),
207 ENT_QUOTES
208 ) . "'";
209 $ip_class = 'hover-history underline';
210 }
211
212 // render IP address td
213 global $aac_right;
214 $netinfo = spotNetworkByIP ($ip_bin);
215 $ret['td_ip'] = "<td class='tdleft'>";
216 if (isset ($netinfo))
217 {
218 $title = $dottedquad;
219 if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
220 $title .= '/' . $netinfo['mask'];
221 $ret['td_ip'] .= "<a name='ip-$dottedquad' class='$ip_class' $ip_title href='" .
222 makeHref (
223 array
224 (
225 'page' => 'ipaddress',
226 'hl_object_id' => $object_id,
227 'ip' => $dottedquad,
228 )
229 ) . "'>$title</a>";
230 }
231 else
232 $ret['td_ip'] .= "<span class='$ip_class' $ip_title>$dottedquad</span>";
233 $ret['td_ip'] .= $aac_right[$alloc['type']];
234 if ($alloc['addrinfo']['name'] != '')
235 $ret['td_ip'] .= ' (' . stringForLabel ($alloc['addrinfo']['name']) . ')';
236 $ret['td_ip'] .= '</td>';
237
238 // render network and routed_by tds
239 $td_class = 'tdleft';
240 if (! isset ($netinfo))
241 {
242 $ret['td_network'] = "<td class='$td_class sparenetwork'>N/A</td>";
243 $ret['td_routed_by'] = $ret['td_network'];
244 }
245 else
246 {
247 $ret['td_network'] = "<td class='$td_class'>" .
248 getOutputOf ('renderCell', $netinfo) . '</td>';
249
250 // render "routed by" td
251 if ($display_routers = (getConfigVar ('IPV4_TREE_RTR_AS_CELL') == 'none'))
252 $ret['td_routed_by'] = '<td>&nbsp;</td>';
253 else
254 {
255 $other_routers = array();
256 foreach (findNetRouters ($netinfo) as $router)
257 if ($router['id'] != $object_id)
258 $other_routers[] = $router;
259 if (count ($other_routers))
260 $ret['td_routed_by'] = getOutputOf ('printRoutersTD', $other_routers, $display_routers);
261 else
262 $ret['td_routed_by'] = "<td class='$td_class'>&nbsp;</td>";
263 }
264 }
265
266 // render peers td
267 $ret['td_peers'] = "<td class='$td_class'>";
268 $prefix = '';
269 $separator = '; ';
270 if ($alloc['addrinfo']['reserved'] == 'yes')
271 {
272 $ret['td_peers'] .= $prefix . '<strong>RESERVED</strong>';
273 $prefix = $separator;
274 }
275 foreach ($alloc['addrinfo']['allocs'] as $allocpeer)
276 if ($allocpeer['object_id'] != $object_id)
277 {
278 $ret['td_peers'] .= $prefix . makeIPAllocLink ($ip_bin, $allocpeer);
279 $prefix = $separator;
280 }
281 elseif ($allocpeer['type'] == 'point2point' && isset ($netinfo))
282 {
283 // show PtP peers in the IP network
284 $addrlist = isset ($netinfo['own_addrlist']) ? $netinfo['own_addrlist'] : getIPAddrList ($netinfo, IPSCAN_DO_ALLOCS);
285 foreach (getPtPNeighbors ($ip_bin, $addrlist) as $p_ip_bin => $p_alloc_list)
286 foreach ($p_alloc_list as $p_alloc)
287 {
288 $ret['td_peers'] .= $prefix . '&harr;&nbsp;' . makeIPAllocLink ($p_ip_bin, $p_alloc);
289 $prefix = $separator;
290 }
291 }
292
293 $ret['td_peers'] .= '</td>';
294
295 return $ret;
296 }
297
298 function renderLocationFilterPortlet ()
299 {
300 // Recursive function used to build the location tree
301 function renderLocationCheckbox ($subtree, $level = 0)
302 {
303 $self = __FUNCTION__;
304
305 foreach ($subtree as $location_id => $location)
306 {
307 echo "<div class=tagbox style='text-align:left; padding-left:" . ($level * 16) . "px;'>";
308 $checked = (! isset ($_SESSION['locationFilter']) || in_array ($location['id'], $_SESSION['locationFilter'])) ? 'checked' : '';
309 echo "<label><input type=checkbox name='location_id[]' class=${level} value='${location['id']}'${checked} onClick=checkAll(this)>${location['name']}";
310 echo '</label>';
311 if ($location['kidc'])
312 {
313 echo "<a id='lfa" . $location['id'] . "' onclick=\"expand('${location['id']}')\" href\"#\" > - </a>";
314 echo "<div id='lfd" . $location['id'] . "'>";
315 $self ($location['kids'], $level + 1);
316 echo '</div>';
317 }
318 echo '</div>';
319 }
320 }
321
322 addJS(<<<END
323 function checkAll(bx) {
324 for (var tbls=document.getElementsByTagName("table"), i=tbls.length; i--;)
325 if (tbls[i].id == "locationFilter") {
326 var bxs=tbls[i].getElementsByTagName("input");
327 var in_tree = false;
328 for (var j=0; j<bxs.length; j++) {
329 if(in_tree == false && bxs[j].value == bx.value)
330 in_tree = true;
331 else if(parseInt(bxs[j].className) <= parseInt(bx.className))
332 in_tree = false;
333 if (bxs[j].type=="checkbox" && in_tree == true)
334 bxs[j].checked = bx.checked;
335 }
336 }
337 }
338
339 function collapseAll(bx) {
340 for (var tbls=document.getElementsByTagName("table"), i=tbls.length; i--;)
341 if (tbls[i].id == "locationFilter") {
342 var bxs=tbls[i].getElementsByTagName("div");
343 //loop through divs to hide unchecked
344 for (var j=0; j<bxs.length; j++) {
345 var is_checked = -1;
346 var in_div=bxs[j].getElementsByTagName("input");
347 //loop through input to find if any is checked
348 for (var k=0; k<in_div.length; k++) {
349 if(in_div[k].type="checkbox") {
350 if (in_div[k].checked == true) {
351 is_checked = true;
352 break;
353 }
354 else
355 is_checked = false;
356 }
357 }
358 // nothing selected and element id is lfd, collapse it
359 if (is_checked == false && !bxs[j].id.indexOf("lfd"))
360 expand(bxs[j].id.substr(3));
361 }
362 }
363 }
364
365 function expand(id) {
366 var divid = document.getElementById("lfd" + id);
367 var iconid = document.getElementById("lfa" + id);
368 if (divid.style.display == 'none') {
369 divid.style.display = 'block';
370 iconid.innerHTML = ' - ';
371 } else {
372 divid.style.display = 'none';
373 iconid.innerHTML = ' + ';
374 }
375 }
376 END
377 ,TRUE);
378 startPortlet ('Location filter');
379 echo <<<END
380 <table border=0 align=center cellspacing=0 class="tagtree" id="locationFilter">
381 <form method=post>
382 <input type=hidden name=page value=rackspace>
383 <input type=hidden name=tab value=default>
384 <input type=hidden name=changeLocationFilter value=true>
385 END;
386
387 $locationlist = listCells ('location');
388 if (count ($locationlist))
389 {
390 echo "<tr><td class=tagbox style='padding-left: 0px'><label>";
391 echo "<input type=checkbox name='location' onClick=checkAll(this)> Toggle all";
392 echo "<img src=?module=chrome&uri=pix/1x1t.gif onLoad=collapseAll(this)>"; // dirty hack to collapse all when page is displayed
393 echo "</label></td></tr>\n";
394 echo "<tr><td class=tagbox><hr>\n";
395 renderLocationCheckbox (treeFromList (addTraceToNodes ($locationlist)));
396 echo "<hr></td></tr>\n";
397 echo '<tr><td>';
398 printImageHREF ('setfilter', 'set filter', TRUE);
399 echo "</td></tr>\n";
400 }
401 else
402 {
403 echo "<tr><td class='tagbox sparenetwork'>(no locations exist)</td></tr>\n";
404 echo "<tr><td>";
405 printImageHREF ('setfilter gray');
406 echo "</td></tr>\n";
407 }
408
409 echo "</form></table>\n";
410 finishPortlet ();
411 }
412
413 function rackspaceCmp ($a, $b)
414 {
415 $ret = strnatcasecmp ($a['location_tree'], $b['location_tree']);
416 if (!$ret)
417 $ret = strnatcasecmp ($a['row_name'], $b['row_name']);
418 return $ret;
419 }
420
421 function getRackThumbLink ($rack, $scale = 1, $object_id = NULL)
422 {
423 if (! is_int ($scale) || $scale <= 0)
424 throw new InvalidArgException ('scale', $scale, 'must be a natural number');
425 $width = getRackImageWidth() * $scale;
426 $height = getRackImageHeight ($rack['height']) * $scale;
427 $title = "${rack['height']} units";
428 $src = '?module=image' .
429 ($scale == 1 && $object_id === NULL ? '&img=minirack' : "&img=midirack&scale=${scale}") .
430 "&rack_id=${rack['id']}" .
431 ($object_id === NULL ? '' : "&object_id=${object_id}");
432 $img = "<img border=0 width=${width} height=${height} title='${title}' src='${src}'>";
433 return mkA ($img, 'rack', $rack['id']);
434 }
435
436 function renderRackspace ()
437 {
438 // Handle the location filter
439 startSession();
440 if (isset ($_REQUEST['changeLocationFilter']))
441 unset ($_SESSION['locationFilter']);
442 if (isset ($_REQUEST['location_id']))
443 $_SESSION['locationFilter'] = $_REQUEST['location_id'];
444 session_commit();
445
446 echo "<table class=objview border=0 width='100%'><tr><td class=pcleft>";
447
448 $found_racks = array();
449 $cellfilter = getCellFilter();
450 if (! ($cellfilter['is_empty'] && !isset ($_SESSION['locationFilter']) && renderEmptyResults ($cellfilter, 'racks', getEntitiesCount ('rack'))))
451 {
452 $rows = array();
453 $rackCount = 0;
454 foreach (listCells ('row') as $row_id => $rowInfo)
455 {
456 $rackList = applyCellFilter ('rack', $cellfilter, $row_id);
457 $found_racks = array_merge ($found_racks, $rackList);
458 $location_id = $rowInfo['location_id'];
459 $locationIdx = 0;
460 // contains location names in the form of 'grandparent parent child', used for sorting
461 $locationTree = '';
462 // contains location names as well as links
463 $hrefLocationTree = '';
464 while ($location_id)
465 {
466 if ($locationIdx == 20)
467 {
468 showWarning ("Warning: There is likely a circular reference in the location tree. Investigate location ${location_id}.");
469 break;
470 }
471 $parentLocation = spotEntity ('location', $location_id);
472 $locationTree = sprintf ('%s %s', $parentLocation['name'], $locationTree);
473 $hrefLocationTree = "&raquo; <a href='" .
474 makeHref(array('page'=>'location', 'location_id'=>$parentLocation['id'])) .
475 "${cellfilter['urlextra']}'>${parentLocation['name']}</a> " .
476 $hrefLocationTree;
477 $location_id = $parentLocation['parent_id'];
478 $locationIdx++;
479 }
480 $hrefLocationTree = substr ($hrefLocationTree, 8);
481 $rows[] = array (
482 'location_id' => $rowInfo['location_id'],
483 'location_tree' => $locationTree,
484 'href_location_tree' => $hrefLocationTree,
485 'row_id' => $row_id,
486 'row_name' => $rowInfo['name'],
487 'racks' => $rackList
488 );
489 $rackCount += count($rackList);
490 }
491
492 // sort by location, then by row
493 usort ($rows, 'rackspaceCmp');
494
495 if (! renderEmptyResults($cellfilter, 'racks', $rackCount))
496 {
497 // generate thumb gallery
498 global $nextorder;
499 // Zero value effectively disables the limit.
500 $maxPerRow = getConfigVar ('RACKS_PER_ROW');
501 $order = 'odd';
502 if (! count ($rows))
503 echo "<h2>No rows found</h2>\n";
504 else
505 {
506 echo '<table border=0 cellpadding=10 class=cooltable>';
507 echo '<tr><th class=tdleft>Location</th><th class=tdleft>Row</th><th class=tdleft>Racks</th></tr>';
508 foreach ($rows as $row)
509 {
510 $rackList = $row['racks'];
511 $location_id = $row['location_id'];
512
513 if (
514 $location_id != '' && isset ($_SESSION['locationFilter']) && ! in_array ($location_id, $_SESSION['locationFilter']) ||
515 empty ($rackList) && ! $cellfilter['is_empty']
516 )
517 continue;
518 $rackListIdx = 0;
519 echo "<tr class=row_${order}><th class=tdleft>${row['href_location_tree']}</th>";
520 echo "<th class=tdleft><a href='".makeHref(array('page'=>'row', 'row_id'=>$row['row_id']))."${cellfilter['urlextra']}'>${row['row_name']}</a></th>";
521 echo "<th class=tdleft><table border=0 cellspacing=5><tr>";
522 if (! count ($rackList))
523 echo '<td>(empty row)</td>';
524 else
525 foreach ($rackList as $rack)
526 {
527 if ($rackListIdx > 0 && $maxPerRow > 0 && $rackListIdx % $maxPerRow == 0)
528 {
529 echo '</tr></table></th></tr>';
530 echo "<tr class=row_${order}><th class=tdleft></th><th class=tdleft>${row['row_name']} (continued)";
531 echo "</th><th class=tdleft><table border=0 cellspacing=5><tr>";
532 }
533 echo '<td align=center valign=bottom>' . getRackThumbLink ($rack);
534 echo '<br>' . mkA (stringForLabel ($rack['name']), 'rack', $rack['id']) . '</td>';
535 $rackListIdx++;
536 }
537 $order = $nextorder[$order];
538 echo "</tr></table></th></tr>\n";
539 }
540 echo "</table>\n";
541 }
542 }
543 }
544 echo '</td><td class=pcright width="25%">';
545 renderCellFilterPortlet ($cellfilter, 'rack', $found_racks);
546 echo "<br>\n";
547 renderLocationFilterPortlet ();
548 echo "</td></tr></table>\n";
549 }
550
551 function renderLocationRowForEditor ($subtree, $level = 0)
552 {
553 $self = __FUNCTION__;
554 foreach ($subtree as $locationinfo)
555 {
556 echo "<tr><td align=left style='padding-left: " . ($locationinfo['kidc'] ? $level : ($level + 1) * 16) . "px;'>";
557 if ($locationinfo['kidc'])
558 printImageHREF ('node-expanded-static');
559 if ($locationinfo['refcnt'] > 0 || $locationinfo['kidc'] > 0)
560 printImageHREF ('nodestroy');
561 else
562 echo getOpLink (array ('op' => 'deleteLocation', 'location_id' => $locationinfo['id']), '', 'destroy', 'Delete location');
563 echo '</td><td class=tdleft>';
564 printOpFormIntro ('updateLocation', array ('location_id' => $locationinfo['id']));
565 $parent = isset ($locationinfo['parent_id']) ? $locationinfo['parent_id'] : 0;
566 echo getSelect
567 (
568 array ( $parent => $parent ? htmlspecialchars ($locationinfo['parent_name']) : '-- NONE --'),
569 array ('name' => 'parent_id', 'id' => 'locationid_' . $locationinfo['id'], 'class' => 'locationlist-popup'),
570 $parent,
571 FALSE
572 );
573 echo "</td><td class=tdleft>";
574 echo "<input type=text size=48 name=name value='${locationinfo['name']}'>";
575 echo '</td><td>' . getImageHREF ('save', 'Save changes', TRUE) . "</form></td></tr>\n";
576 if ($locationinfo['kidc'])
577 $self ($locationinfo['kids'], $level + 1);
578 }
579 }
580
581 function renderLocationSelectTree ($select_name, $selected_id = NULL)
582 {
583 echo "<select name='${select_name}'>";
584 echo '<option value=0>-- NONE --</option>';
585 $locationlist = listCells ('location');
586 foreach (treeFromList (addTraceToNodes ($locationlist)) as $location)
587 {
588 echo "<option value=${location['id']} style='font-weight: bold' ";
589 if ($location['id'] == $selected_id )
590 echo ' selected';
591 echo ">${location['name']}</option>";
592 printLocationChildrenSelectOptions ($location, $selected_id);
593 }
594 echo '</select>';
595 }
596
597 function renderRackspaceLocationEditor ()
598 {
599 $js = <<<JSTXT
600 function locationeditor_showselectbox(e) {
601 $(this).load('index.php', {module: 'ajax', ac: 'get-location-select', locationid: this.id});
602 $(this).unbind('mousedown', locationeditor_showselectbox);
603 }
604 $(document).ready(function () {
605 $('select.locationlist-popup').bind('mousedown', locationeditor_showselectbox);
606 });
607 JSTXT;
608
609 addJS($js, TRUE );
610 function printNewItemTR ()
611 {
612 printOpFormIntro ('addLocation');
613 echo '<tr><td>';
614 printImageHREF ('create', 'Add new location', TRUE);
615 echo '</td><td>';
616 renderLocationSelectTree ('parent_id');
617 echo '</td><td><input type=text size=48 name=name></td><td>';
618 printImageHREF ('create', 'Add new location', TRUE);
619 echo "</td></tr></form>\n";
620 }
621
622 startPortlet ('Locations');
623 echo "<table border=0 cellspacing=0 cellpadding=5 align=center class=widetable>\n";
624 echo "<tr><th>&nbsp;</th><th>Parent</th><th>Name</th><th>&nbsp;</th></tr>\n";
625 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
626 printNewItemTR();
627
628 $locations = listCells ('location');
629 renderLocationRowForEditor (treeFromList (addTraceToNodes ($locations)));
630
631 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
632 printNewItemTR();
633 echo "</table><br>\n";
634 finishPortlet();
635 }
636
637 function renderRackspaceRowEditor ()
638 {
639 function printNewItemTR ()
640 {
641 printOpFormIntro ('addRow');
642 echo '<tr><td>';
643 printImageHREF ('create', 'Add new row', TRUE);
644 echo '</td><td>&nbsp;';
645 echo '</td><td>&nbsp;';
646 echo '</td><td>';
647 renderLocationSelectTree ('location_id');
648 echo '</td><td><input type=text name=name></td><td>';
649 printImageHREF ('create', 'Add new row', TRUE);
650 echo '</td><td>&nbsp;';
651 echo '</td></tr></form>';
652 }
653 startPortlet ('Rows');
654 echo "<table border=0 cellspacing=0 cellpadding=5 align=center class=widetable>\n";
655 echo "<tr><th>&nbsp;</th><th># Racks</th><th># Devices</th><th>Location</th><th>Name</th><th>&nbsp;</th><th>Row link</th></tr>\n";
656 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
657 printNewItemTR ();
658 foreach (listCells ('row') as $row_id => $rowInfo)
659 {
660 echo '<tr><td>';
661 $rc = $rowInfo['rackc'];
662 $delete_racks_str = $rc ? " and $rc rack(s)" : '';
663 echo getOpLink (array ('op'=>'deleteRow', 'row_id'=>$row_id), '', 'destroy', 'Delete row'.$delete_racks_str, 'need-confirmation');
664 printOpFormIntro ('updateRow', array ('row_id' => $row_id));
665 echo '</td><td>';
666 echo $rc;
667 echo '</td><td>';
668 echo getRowMountsCount ($row_id);
669 echo '</td><td>';
670 renderLocationSelectTree ('location_id', $rowInfo['location_id']);
671 echo "</td><td><input type=text name=name value='${rowInfo['name']}'></td><td>";
672 printImageHREF ('save', 'Save changes', TRUE);
673 echo "</form></td>";
674 echo "<td>" . mkCellA ($rowInfo) . "</td>";
675 echo "</tr>\n";
676 }
677 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
678 printNewItemTR ();
679 echo "</table><br>\n";
680 finishPortlet();
681 }
682
683 function renderRow ($row_id)
684 {
685 $rowInfo = getRowInfo ($row_id);
686 $cellfilter = getCellFilter();
687 $rackList = applyCellFilter ('rack', $cellfilter, $row_id);
688
689 $summary = array ();
690 $summary['Name'] = $rowInfo['name'];
691 if ($rowInfo['location_id'])
692 $summary['Location'] = mkA ($rowInfo['location'], 'location', $rowInfo['location_id']);
693 $summary['Racks'] = $rowInfo['count'];
694 $summary['Units'] = $rowInfo['sum'];
695 $summary['% used'] = getProgressBar (getRSUforRow ($rackList));
696 foreach (getAttrValuesSorted ($row_id) as $record)
697 if
698 (
699 $record['value'] != '' &&
700 permitted (NULL, NULL, NULL, array (array ('tag' => '$attr_' . $record['id'])))
701 )
702 $summary['{sticker}' . $record['name']] = formatAttributeValue ($record);
703
704 // Main layout starts.
705 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
706
707 // Left portlet with row information.
708 echo "<tr><td class=pcleft>";
709 renderEntitySummary ($rowInfo, 'Summary', $summary);
710 renderCellFilterPortlet ($cellfilter, 'rack', $rackList, array ('row_id' => $row_id));
711 renderFilesPortlet ('row',$row_id);
712 echo "</td><td class=pcright>";
713
714 global $nextorder;
715 // Maximum number of racks per row is proportionally less, but at least 1.
716 $maxPerRow = max (floor (getConfigVar ('RACKS_PER_ROW') / getConfigVar ('ROW_SCALE')), 1);
717 $rackListIdx = 0;
718 $order = 'odd';
719 startPortlet ('Racks');
720 echo "<table border=0 cellspacing=5 align='center'><tr>";
721 foreach ($rackList as $rack)
722 {
723 if ($rackListIdx % $maxPerRow == 0)
724 {
725 if ($rackListIdx > 0)
726 echo '</tr>';
727 echo '<tr>';
728 }
729 $class = ($rack['has_problems'] == 'yes') ? 'error' : $order;
730 echo "<td align=center valign=bottom class=row_${class}>" .
731 getRackThumbLink ($rack, getConfigVar ('ROW_SCALE')) .
732 '<br>' . mkA (stringForLabel ($rack['name']), 'rack', $rack['id']) . '</td>';
733 $order = $nextorder[$order];
734 $rackListIdx++;
735 }
736 echo "</tr></table>\n";
737 finishPortlet();
738 echo "</td></tr></table>";
739 }
740
741 function renderEditRowForm ($row_id)
742 {
743 $row = getRowInfo ($row_id);
744
745 startPortlet ('Attributes');
746 printOpFormIntro ('updateRow');
747 echo '<table border=0 align=center>';
748 echo '<tr><td>&nbsp;</td><th class=tdright>Location:</th><td class=tdleft>';
749 $locations = array ();
750 $locations[0] = '-- NOT SET --';
751 foreach (listCells ('location') as $id => $locationInfo)
752 $locations[$id] = $locationInfo['name'];
753 natcasesort ($locations);
754 printSelect ($locations, array ('name' => 'location_id'), $row['location_id']);
755 echo "</td></tr>\n";
756 echo "<tr><td>&nbsp;</td><th class=tdright>Name (required):</th><td class=tdleft><input type=text name=name value='${row['name']}'></td></tr>\n";
757
758 // optional attributes
759 $values = getAttrValuesSorted ($row_id);
760 $num_attrs = count ($values);
761 echo "<input type=hidden name=num_attrs value=${num_attrs}>\n";
762 $i = 0;
763 foreach ($values as $record)
764 {
765 echo "<input type=hidden name=${i}_attr_id value=${record['id']}>";
766 echo '<tr><td>';
767 if ($record['value'] != '')
768 echo getOpLink (array('op'=>'clearSticker', 'attr_id'=>$record['id']), '', 'clear', 'Clear value', 'need-confirmation');
769 else
770 echo '&nbsp;';
771 echo '</td>';
772 echo "<th class=sticker>${record['name']}:</th><td class=tdleft>";
773 switch ($record['type'])
774 {
775 case 'uint':
776 case 'float':
777 case 'string':
778 echo "<input type=text name=${i}_value value='${record['value']}'>";
779 break;
780 case 'dict':
781 $chapter = readChapter ($record['chapter_id'], 'o');
782 $chapter[0] = '-- NOT SET --';
783 $chapter = cookOptgroups ($chapter, 1562, $record['key']);
784 printNiftySelect ($chapter, array ('name' => "${i}_value"), $record['key']);
785 break;
786 }
787 echo "</td></tr>\n";
788 $i++;
789 }
790 if ($row['count'] == 0)
791 {
792 echo '<tr><td>&nbsp;</td><th class=tdright>Actions:</th><td class=tdleft>';
793 echo getOpLink (array ('op'=>'deleteRow'), '', 'destroy', 'Delete row', 'need-confirmation');
794 echo "&nbsp;</td></tr>\n";
795 }
796 echo "<tr><td class=submit colspan=3>";
797 printImageHREF ('SAVE', 'Save changes', TRUE);
798 echo "</td></tr>\n";
799 echo '</form></table><br>';
800 finishPortlet();
801
802 startPortlet ('History');
803 renderObjectHistory ($row_id);
804 finishPortlet();
805 }
806
807 // Used by renderRack()
808 function printObjectDetailsForRenderRack ($object_id, $hl_obj_id = 0)
809 {
810 $objectData = spotEntity ('object', $object_id);
811 if ($objectData['asset_no'] != '')
812 $prefix = "<div title='${objectData['asset_no']}";
813 else
814 $prefix = "<div title='no asset tag";
815 // Don't tell about label, if it matches common name.
816 $body = '';
817 if ($objectData['name'] != $objectData['label'] && $objectData['label'] != '')
818 $body = ", visible label is \"${objectData['label']}\"";
819 // Display list of child objects, if any
820 $objectChildren = getChildren ($objectData, 'object');
821 $slotRows = $slotCols = $slotInfo = $slotData = $slotTitle = $slotClass = array ();
822 if (count($objectChildren) > 0)
823 {
824 foreach ($objectChildren as $childData)
825 {
826 $childNames[] = $childData['name'];
827 $attrData = getAttrValues ($childData['id']);
828 $numRows = $numCols = 1;
829 if (isset ($attrData[2])) // HW type
830 {
831 extractLayout ($attrData[2]);
832 if (isset ($attrData[2]['rows']))
833 {
834 $numRows = $attrData[2]['rows'];
835 $numCols = $attrData[2]['cols'];
836 }
837 }
838 if (isset ($attrData['28'])) // slot number
839 {
840 $slot = $attrData['28']['value'];
841 if (preg_match ('/\d+/', $slot, $matches))
842 $slot = $matches[0];
843 $slotRows[$slot] = $numRows;
844 $slotCols[$slot] = $numCols;
845 $slotInfo[$slot] = $childData['dname'];
846 $slotData[$slot] = $childData['id'];
847 if ($childData['asset_no'] != '')
848 $slotTitle[$slot] = "<div title='${childData['asset_no']}";
849 else
850 $slotTitle[$slot] = "<div title='no asset tag";
851 if ($childData['label'] != '' && $childData['label'] != $childData['dname'])
852 $slotTitle[$slot] .= ", visible label is \"${childData['label']}\"";
853 $slotTitle[$slot] .= "'>";
854 $slotClass[$slot] = 'state_T';
855 if ($childData['id'] == $hl_obj_id)
856 $slotClass[$slot] .= 'h';
857 if ($childData['has_problems'] == 'yes')
858 $slotClass[$slot] .= 'w';
859 }
860 }
861 natsort($childNames);
862 $suffix = sprintf(", contains %s'>", implode(', ', $childNames));
863 }
864 else
865 $suffix = "'>";
866 echo "${prefix}${body}${suffix}" . mkCellA ($objectData) . '</div>';
867 if (in_array ($objectData['objtype_id'], array (1502,1503))) // server chassis, network chassis
868 {
869 $objAttr = getAttrValues ($objectData['id']);
870 if (isset ($objAttr[2])) // HW type
871 {
872 extractLayout ($objAttr[2]);
873 if (isset ($objAttr[2]['rows']))
874 {
875 $rows = $objAttr[2]['rows'];
876 $cols = $objAttr[2]['cols'];
877 $layout = $objAttr[2]['layout'];
878 echo "<table width='100%' border='1'>";
879 for ($r = 0; $r < $rows; $r++)
880 {
881 echo '<tr>';
882 for ($c = 0; $c < $cols; $c++)
883 {
884 $s = ($r * $cols) + $c + 1;
885 if (isset ($slotData[$s]))
886 {
887 if ($slotData[$s] >= 0)
888 {
889 for ($lr = 0; $lr < $slotRows[$s]; $lr++)
890 for ($lc = 0; $lc < $slotCols[$s]; $lc++)
891 {
892 $skip = ($lr * $cols) + $lc;
893 if ($skip > 0)
894 $slotData[$s + $skip] = -1;
895 }
896 echo '<td';
897 if ($slotRows[$s] > 1)
898 echo " rowspan=$slotRows[$s]";
899 if ($slotCols[$s] > 1)
900 echo " colspan=$slotCols[$s]";
901 echo " class='${slotClass[$s]}'>${slotTitle[$s]}";
902 if ($layout == 'V')
903 {
904 $tmp = mb_substr($slotInfo[$s], 0, 1);
905 for($i = 1; $i < mb_strlen($slotInfo[$s]); $i++)
906 {
907 $tmp .= '<br>' . mb_substr($slotInfo[$s], $i, 1);
908 }
909 $slotInfo[$s] = $tmp;
910 }
911 echo mkA ($slotInfo[$s], 'object', $slotData[$s]);
912 echo '</div></td>';
913 }
914 }
915 else
916 echo "<td class='state_F'><div title=\"Free slot\">&nbsp;</div></td>";
917 }
918 echo '</tr>';
919 }
920 echo '</table>';
921 }
922 }
923 }
924 }
925
926 // This function renders rack as HTML table.
927 function renderRack ($rack_id, $hl_obj_id = 0)
928 {
929 $rackData = spotEntity ('rack', $rack_id);
930 amplifyCell ($rackData);
931 markAllSpans ($rackData);
932 if ($hl_obj_id > 0)
933 highlightObject ($rackData, $hl_obj_id);
934 $neighbors = getRackNeighbors ($rackData['row_id'], $rack_id);
935 $prev_id = $neighbors['prev'];
936 $next_id = $neighbors['next'];
937 echo "<center><table border=0><tr valign=middle>";
938 echo '<td><h2>' . mkA ($rackData['row_name'], 'row', $rackData['row_id']) . ' :</h2></td>';
939 if ($prev_id != NULL)
940 echo '<td>' . mkA (getImageHREF ('prev', 'previous rack'), 'rack', $prev_id) . '</td>';
941 echo '<td><h2>' . mkA ($rackData['name'], 'rack', $rackData['id']) . '</h2></td>';
942 if ($next_id != NULL)
943 echo '<td>' . mkA (getImageHREF ('next', 'next rack'), 'rack', $next_id) . '</td>';
944 echo "</h2></td></tr></table>\n";
945 echo "<table class=rack border=0 cellspacing=0 cellpadding=1>\n";
946 echo "<tr><th width='10%'>&nbsp;</th><th width='20%'>Front</th>";
947 echo "<th width='50%'>Interior</th><th width='20%'>Back</th></tr>\n";
948 for ($i = $rackData['height']; $i > 0; $i--)
949 {
950 echo "<tr><th>" . inverseRackUnit ($i, $rackData) . "</th>";
951 for ($locidx = 0; $locidx < 3; $locidx++)
952 {
953 if (isset ($rackData[$i][$locidx]['skipped']))
954 continue;
955 $state = $rackData[$i][$locidx]['state'];
956 echo "<td class='atom state_${state}";
957 if (isset ($rackData[$i][$locidx]['hl']))
958 echo $rackData[$i][$locidx]['hl'];
959 echo "'";
960 if (isset ($rackData[$i][$locidx]['colspan']))
961 echo ' colspan=' . $rackData[$i][$locidx]['colspan'];
962 if (isset ($rackData[$i][$locidx]['rowspan']))
963 echo ' rowspan=' . $rackData[$i][$locidx]['rowspan'];
964 echo ">";
965 switch ($state)
966 {
967 case 'T':
968 printObjectDetailsForRenderRack ($rackData[$i][$locidx]['object_id'], $hl_obj_id);
969 break;
970 case 'A':
971 echo '<div title="This rackspace does not exist">&nbsp;</div>';
972 break;
973 case 'F':
974 echo '<div title="Free rackspace">&nbsp;</div>';
975 break;
976 case 'U':
977 echo '<div title="Problematic rackspace, you CAN\'T mount here">&nbsp;</div>';
978 break;
979 default:
980 echo '<div title="No data">&nbsp;</div>';
981 break;
982 }
983 echo '</td>';
984 }
985 echo "</tr>\n";
986 }
987 echo "</table>\n";
988 // Get a list of all of objects Zero-U mounted to this rack
989 $zeroUObjects = getChildren ($rackData, 'object');
990 uasort ($zeroUObjects, 'compare_name');
991 if (count ($zeroUObjects) > 0)
992 {
993 echo "<br><table width='75%' class=rack border=0 cellspacing=0 cellpadding=1>\n";
994 echo "<tr><th>Zero-U:</th></tr>\n";
995 foreach ($zeroUObjects as $zeroUObject)
996 {
997 $state = ($zeroUObject['id'] == $hl_obj_id) ? 'Th' : 'T';
998 if ($zeroUObject['has_problems'] == 'yes')
999 $state .= 'w';
1000 echo "<tr><td class='atom state_${state}'>";
1001 printObjectDetailsForRenderRack($zeroUObject['id']);
1002 echo "</td></tr>\n";
1003 }
1004 echo "</table>\n";
1005 }
1006 echo "</center>\n";
1007 }
1008
1009 function renderRackSortForm ($row_id)
1010 {
1011 includeJQueryUI (false);
1012 $js = <<<JSTXT
1013 $(document).ready(
1014 function () {
1015 $("#sortRacks").sortable({
1016 update : function () {
1017 serial = $('#sortRacks').sortable('serialize');
1018 $.ajax({
1019 url: 'index.php?module=ajax&ac=upd-rack-sort-order&row_id=${row_id}',
1020 type: 'post',
1021 data: serial,
1022 });
1023 }
1024 });
1025 }
1026 );
1027 JSTXT;
1028 addJS($js, true);
1029
1030 startPortlet ('Racks');
1031 echo "<table border=0 cellspacing=0 cellpadding=5 align=center class=widetable>\n";
1032 echo "<tr><th>Drag to change order</th></tr>\n";
1033 echo "<tr><td><ul class='uflist' id='sortRacks'>\n";
1034 foreach (getRacks($row_id) as $rack_id => $rackInfo)
1035 echo "<li id=racks_${rack_id}>${rackInfo['name']}</li>\n";
1036 echo "</ul></td></tr></table>\n";
1037 finishPortlet();
1038 }
1039
1040 function renderNewRackForm()
1041 {
1042 $default_height = getConfigVar ('DEFAULT_RACK_HEIGHT');
1043 if ($default_height == 0)
1044 $default_height = '';
1045 startPortlet ('Add one');
1046 printOpFormIntro ('addRack', array ('mode' => 'one'));
1047 echo '<table border=0 align=center>';
1048 echo "<tr><th class=tdright>Name (required):</th><td class=tdleft><input type=text name=name></td>";
1049 echo "<tr><th class=tdright>Height in units (required):</th><td class=tdleft><input type=text name=height1 value='${default_height}'></td></tr>\n";
1050 echo "<tr><th class=tdright>Asset tag:</th><td class=tdleft><input type=text name=asset_no></td></tr>\n";
1051 echo "<tr><th class=tdright>Tags:</td><td class=tdleft>";
1052 printTagsPicker ();
1053 echo "</td></tr>\n";
1054 echo "<tr><td class=submit colspan=2>";
1055 printImageHREF ('CREATE', 'Add', TRUE);
1056 echo "</td></tr></table></form>";
1057 finishPortlet();
1058
1059 startPortlet ('Add many');
1060 printOpFormIntro ('addRack', array ('mode' => 'many'));
1061 echo '<table border=0 align=center>';
1062 echo "<tr><th class=tdright>Height in units (*):</th><td class=tdleft><input type=text name=height2 value='${default_height}'></td>";
1063 echo "<tr><th class=tdright>Assign tags:</td><td class=tdleft>";
1064 printTagsPicker ();
1065 echo "</td></tr>\n";
1066 echo "<tr><th class=tdright>Rack names (required):</th><td class=tdleft><textarea name=names cols=40 rows=25></textarea></td></tr>\n";
1067 echo "<tr><td class=submit colspan=2>";
1068 printImageHREF ('CREATE', 'Add', TRUE);
1069 echo '</form></table>';
1070 finishPortlet();
1071 }
1072
1073 function renderEditObjectForm()
1074 {
1075 global $pageno;
1076 $object_id = getBypassValue();
1077 $object = spotEntity ('object', $object_id);
1078 startPortlet ();
1079 printOpFormIntro ('update');
1080
1081 // static attributes
1082 echo '<table border=0 cellspacing=0 cellpadding=3 align=center>';
1083 echo "<tr><td>&nbsp;</td><th colspan=2><h2>Attributes</h2></th></tr>";
1084 echo '<tr><td>&nbsp;</td><th class=tdright>Type:</th><td class=tdleft>';
1085 printSelect (getObjectTypeChangeOptions ($object['id']), array ('name' => 'object_type_id'), $object['objtype_id']);
1086 echo '</td></tr>';
1087 // baseline info
1088 echo "<tr><td>&nbsp;</td><th class=tdright>Common name:</th><td class=tdleft><input type=text name=object_name value='${object['name']}'></td></tr>\n";
1089 echo "<tr><td>&nbsp;</td><th class=tdright>Visible label:</th><td class=tdleft><input type=text name=object_label value='${object['label']}'></td></tr>\n";
1090 echo "<tr><td>&nbsp;</td><th class=tdright>Asset tag:</th><td class=tdleft><input type=text name=object_asset_no value='${object['asset_no']}'></td></tr>\n";
1091 echo "<tr><td>&nbsp;</td><th class=tdright>Tags:</th><td class=tdleft>";
1092 printTagsPicker ();
1093 echo "</td></tr>\n";
1094 // parent selection
1095 if (objectTypeMayHaveParent ($object['objtype_id']))
1096 {
1097 $parents = getParents ($object, 'object');
1098 // lookup the human-readable object type, sort by it
1099 foreach ($parents as $parent_id => $parent)
1100 $parents[$parent_id]['object_type'] = decodeObjectType ($parent['objtype_id']);
1101 $grouped_parents = groupBy ($parents, 'object_type');
1102 ksort ($grouped_parents);
1103 foreach ($grouped_parents as $parents_group)
1104 {
1105 uasort ($parents_group, 'compare_name');
1106 $label = $parents_group[key ($parents_group)]['object_type'] . (count($parents_group) > 1 ? ' containers:' : ' container:');
1107 foreach ($parents_group as $link_id => $parent_cell)
1108 {
1109 echo "<tr><td>&nbsp;</td>";
1110 echo "<th class=tdright>${label}</th><td class=tdleft>";
1111 echo mkCellA ($parent_cell);
1112 echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
1113 echo getOpLink (array('op'=>'unlinkObjects', 'link_id'=>$link_id), '', 'cut', 'Unlink container');
1114 echo "</td></tr>\n";
1115 $label = '&nbsp;';
1116 }
1117 }
1118 echo "<tr><td>&nbsp;</td>";
1119 echo "<th class=tdright>Select container:</th><td class=tdleft>";
1120 echo getPopupLink ('objlist', array(), 'findlink', 'attach', 'Select a container');
1121 echo "</td></tr>\n";
1122 }
1123 // optional attributes
1124 $i = 0;
1125 $values = getAttrValuesSorted ($object_id);
1126 if (count($values) > 0)
1127 {
1128 foreach ($values as $record)
1129 {
1130 if (! permitted (NULL, NULL, NULL, array (
1131 array ('tag' => '$attr_' . $record['id']),
1132 array ('tag' => '$any_op'),
1133 )))
1134 continue;
1135 echo "<input type=hidden name=${i}_attr_id value=${record['id']}>";
1136 echo '<tr><td>';
1137 if ($record['value'] != '')
1138 echo getOpLink (array('op'=>'clearSticker', 'attr_id'=>$record['id']), '', 'clear', 'Clear value', 'need-confirmation');
1139 else
1140 echo '&nbsp;';
1141 echo '</td>';
1142 echo "<th class=sticker>${record['name']}";
1143 if ($record['type'] == 'date')
1144 echo ' (' . datetimeFormatHint (getConfigVar ('DATETIME_FORMAT')) . ')';
1145 echo ':</th><td class=tdleft>';
1146 switch ($record['type'])
1147 {
1148 case 'uint':
1149 case 'float':
1150 case 'string':
1151 echo "<input type=text name=${i}_value value='${record['value']}'>";
1152 break;
1153 case 'dict':
1154 $chapter = readChapter ($record['chapter_id'], 'o');
1155 $chapter[0] = '-- NOT SET --';
1156 $chapter = cookOptgroups ($chapter, $object['objtype_id'], $record['key']);
1157 printNiftySelect ($chapter, array ('name' => "${i}_value"), $record['key']);
1158 break;
1159 case 'date':
1160 $date_value = $record['value'] ? datetimestrFromTimestamp ($record['value']) : '';
1161 echo "<input type=text name=${i}_value value='${date_value}'>";
1162 break;
1163 }
1164 echo "</td></tr>\n";
1165 $i++;
1166 }
1167 }
1168 echo '<input type=hidden name=num_attrs value=' . $i . ">\n";
1169 echo "<tr><td>&nbsp;</td><th class=tdright>Has problems:</th><td class=tdleft><input type=checkbox name=object_has_problems";
1170 if ($object['has_problems'] == 'yes')
1171 echo ' checked';
1172 echo "></td></tr>\n";
1173 echo "<tr><td>&nbsp;</td><th class=tdright>Actions:</th><td class=tdleft>";
1174 echo getOpLink (array ('op'=>'deleteObject', 'page'=>'depot', 'tab'=>'addmore', 'object_id'=>$object_id), '' ,'destroy', 'Delete object', 'need-confirmation');
1175 echo "&nbsp;";
1176 echo getOpLink (array ('op'=>'resetObject'), '' ,'clear', 'Reset (cleanup) object', 'need-confirmation');
1177 echo "</td></tr>\n";
1178 echo "<tr><td colspan=3><b>Comment:</b><br><textarea name=object_comment rows=10 cols=80>${object['comment']}</textarea></td></tr>";
1179
1180 echo "<tr><th class=submit colspan=3>";
1181 printImageHREF ('SAVE', 'Save changes', TRUE);
1182 echo "</form></th></tr></table>\n";
1183 finishPortlet();
1184
1185 echo '<table border=0 width=100%><tr><td>';
1186 startPortlet ('history');
1187 renderObjectHistory ($object_id);
1188 finishPortlet();
1189 echo '</td></tr></table>';
1190 }
1191
1192 function renderEditRackForm ($rack_id)
1193 {
1194 global $pageno;
1195 $rack = spotEntity ('rack', $rack_id);
1196 amplifyCell ($rack);
1197
1198 startPortlet ('Attributes');
1199 printOpFormIntro ('updateRack');
1200 echo '<table border=0 align=center>';
1201 echo "<tr><td>&nbsp;</td><th class=tdright>Rack row:</th><td class=tdleft>";
1202 foreach (listCells ('row') as $row_id => $rowInfo)
1203 {
1204 $trail = getLocationTrail ($rowInfo['location_id'], FALSE);
1205 $rows[$row_id] = empty ($trail) ? $rowInfo['name'] : $rowInfo['name'] . ' [' . $trail . ']';
1206 }
1207 natcasesort ($rows);
1208 printSelect ($rows, array ('name' => 'row_id'), $rack['row_id']);
1209 echo "</td></tr>\n";
1210 echo "<tr><td>&nbsp;</td><th class=tdright>Name (required):</th><td class=tdleft><input type=text name=name value='${rack['name']}'></td></tr>\n";
1211 echo "<tr><td>&nbsp;</td><th class=tdright>Height (required):</th><td class=tdleft><input type=text name=height value='${rack['height']}'></td></tr>\n";
1212 echo "<tr><td>&nbsp;</td><th class=tdright>Asset tag:</th><td class=tdleft><input type=text name=asset_no value='${rack['asset_no']}'></td></tr>\n";
1213 echo "<tr><td>&nbsp;</td><th class=tdright>Tags:</th><td class=tdleft>";
1214 printTagsPicker ();
1215 echo "</td></tr>\n";
1216 // optional attributes
1217 $values = getAttrValuesSorted ($rack_id);
1218 $num_attrs = count($values);
1219 $num_attrs = $num_attrs-2; // subtract for the 'height' and 'sort_order' attributes
1220 echo "<input type=hidden name=num_attrs value=${num_attrs}>\n";
1221 $i = 0;
1222 foreach ($values as $record)
1223 {
1224 // Skip the 'height' attribute as it's already displayed as a required field
1225 // Also skip the 'sort_order' attribute
1226 if ($record['id'] == 27 || $record['id'] == 29)
1227 continue;
1228 echo "<input type=hidden name=${i}_attr_id value=${record['id']}>";
1229 echo '<tr><td>';
1230 if ($record['value'] != '')
1231 echo getOpLink (array('op'=>'clearSticker', 'attr_id'=>$record['id']), '', 'clear', 'Clear value', 'need-confirmation');
1232 else
1233 echo '&nbsp;';
1234 echo '</td>';
1235 echo "<th class=sticker>${record['name']}:</th><td class=tdleft>";
1236 switch ($record['type'])
1237 {
1238 case 'uint':
1239 case 'float':
1240 case 'string':
1241 echo "<input type=text name=${i}_value value='${record['value']}'>";
1242 break;
1243 case 'dict':
1244 $chapter = readChapter ($record['chapter_id'], 'o');
1245 $chapter[0] = '-- NOT SET --';
1246 $chapter = cookOptgroups ($chapter, 1560, $record['key']);
1247 printNiftySelect ($chapter, array ('name' => "${i}_value"), $record['key']);
1248 break;
1249 }
1250 echo "</td></tr>\n";
1251 $i++;
1252 }
1253 echo "<tr><td>&nbsp;</td><th class=tdright>Has problems:</th><td class=tdleft><input type=checkbox name=has_problems";
1254 if ($rack['has_problems'] == 'yes')
1255 echo ' checked';
1256 echo "></td></tr>\n";
1257 echo "<tr><td>&nbsp;</td><th class=tdright>Actions:</th><td class=tdleft>";
1258 if ($rack['isDeletable'])
1259 {
1260 echo getOpLink (array ('op'=>'deleteRack'), '', 'destroy', 'Delete rack', 'need-confirmation');
1261 echo "&nbsp;";
1262 }
1263 else
1264 {
1265 echo getOpLink (array ('op'=>'cleanRack'), '' ,'clear', 'Reset (cleanup) rack mounts', 'need-confirmation');
1266 echo "&nbsp;";
1267 }
1268 echo "</td></tr>\n";
1269 echo "<tr><td colspan=3><b>Comment:</b><br><textarea name=comment rows=10 cols=80>${rack['comment']}</textarea></td></tr>";
1270 echo "<tr><td class=submit colspan=3>";
1271 printImageHREF ('SAVE', 'Save changes', TRUE);
1272 echo "</td></tr>\n";
1273 echo '</form></table><br>';
1274 finishPortlet();
1275
1276 startPortlet ('History');
1277 renderObjectHistory ($rack_id);
1278 finishPortlet();
1279 }
1280
1281 // populates the $summary array with the sum of power attributes of the objects mounted into the rack
1282 function populateRackPower ($rackData, &$summary)
1283 {
1284 $power_attrs = array(
1285 7, // 'float','max. current, Ampers'
1286 13, // 'float','max power, Watts'
1287 );
1288 $sum = array();
1289 if (! isset ($rackData['mountedObjects']))
1290 amplifyCell ($rackData);
1291 foreach ($rackData['mountedObjects'] as $object_id)
1292 {
1293 $attrs = getAttrValues ($object_id);
1294 foreach ($power_attrs as $attr_id)
1295 if (isset ($attrs[$attr_id]) && $attrs[$attr_id]['type'] == 'float')
1296 {
1297 if (! isset ($sum[$attr_id]))
1298 {
1299 $sum[$attr_id]['sum'] = 0.0;
1300 $sum[$attr_id]['name'] = $attrs[$attr_id]['name'];
1301 }
1302 $sum[$attr_id]['sum'] += $attrs[$attr_id]['value'];
1303 }
1304 }
1305 foreach ($sum as $attr)
1306 if ($attr['sum'] > 0.0)
1307 $summary[$attr['name']] = $attr['sum'];
1308 }
1309
1310 // used by renderGridForm() and renderRackPage()
1311 function renderRackInfoPortlet ($rackData)
1312 {
1313 $summary = array();
1314 $summary['Rack row'] = mkA ($rackData['row_name'], 'row', $rackData['row_id']);
1315 $summary['Name'] = $rackData['name'];
1316 $summary['Height'] = $rackData['height'];
1317 if ($rackData['asset_no'] != '')
1318 $summary['Asset tag'] = $rackData['asset_no'];
1319 if ($rackData['has_problems'] == 'yes')
1320 $summary[] = array ('<tr><td colspan=2 class=msg_error>Has problems</td></tr>');
1321 populateRackPower ($rackData, $summary);
1322 // Display populated attributes, but skip 'height' since it's already displayed above
1323 // and skip 'sort_order' because it's modified using AJAX
1324 foreach (getAttrValuesSorted ($rackData['id']) as $record)
1325 if ($record['id'] != 27 && $record['id'] != 29 && $record['value'] != '')
1326 $summary['{sticker}' . $record['name']] = formatAttributeValue ($record);
1327 $summary['% used'] = getProgressBar (getRSUforRack ($rackData));
1328 $summary['Objects'] = count ($rackData['mountedObjects']);
1329 $summary['tags'] = '';
1330 renderEntitySummary ($rackData, 'summary', $summary);
1331 if ($rackData['comment'] != '')
1332 {
1333 startPortlet ('Comment');
1334 echo '<div class=commentblock>' . string_insert_hrefs ($rackData['comment']) . '</div>';
1335 finishPortlet ();
1336 }
1337 }
1338
1339 // This is a universal editor of rack design/waste.
1340 // FIXME: switch to using printOpFormIntro()
1341 function renderGridForm ($rack_id, $filter, $header, $submit, $state1, $state2)
1342 {
1343 $rackData = spotEntity ('rack', $rack_id);
1344 amplifyCell ($rackData);
1345 $filter ($rackData);
1346
1347 // Render the result whatever it is.
1348 // Main layout.
1349 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
1350 echo "<tr><td colspan=2 align=center><h1>${rackData['name']}</h1></td></tr>\n";
1351
1352 // Left column with information portlet.
1353 echo "<tr><td class=pcleft height='1%' width='50%'>";
1354 renderRackInfoPortlet ($rackData);
1355 echo "</td>\n";
1356 echo "<td class=pcright>";
1357
1358 // Grid form.
1359 $is_ro = !rackModificationPermitted ($rackData, 'updateRack');
1360 startPortlet ($header);
1361 addJS ('js/racktables.js');
1362 echo "<center>\n";
1363 $read_only_text = $is_ro ? '(read-only)' : '&nbsp;';
1364 echo "<p style='color: red; margin-top:0px'>${read_only_text}</p>\n";
1365 echo "<table class=rack border=0 cellspacing=0 cellpadding=1>\n";
1366 echo "<tr><th width='10%'>&nbsp;</th>";
1367 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
1368 echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
1369 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
1370 printOpFormIntro ('updateRack');
1371 markupAtomGrid ($rackData, $state2);
1372 renderAtomGrid ($rackData, $is_ro);
1373 echo "</table></center>\n";
1374 echo "<br><input type=submit name=do_update value='${submit}'></form><br><br>\n";
1375 finishPortlet();
1376 echo "</td></tr></table>\n";
1377 }
1378
1379 function renderRackDesign ($rack_id)
1380 {
1381 renderGridForm ($rack_id, 'applyRackDesignMask', 'Rack design', 'Set rack design', 'A', 'F');
1382 }
1383
1384 function renderRackProblems ($rack_id)
1385 {
1386 renderGridForm ($rack_id, 'applyRackProblemMask', 'Rack problems', 'Mark unusable atoms', 'F', 'U');
1387 }
1388
1389 function renderObjectPortRow ($port, $is_highlighted)
1390 {
1391 // highlight port name with yellow if it's name is not canonical
1392 $canon_pn = shortenPortName ($port['name'], $port['object_id']);
1393 $name_class = $canon_pn == $port['name'] ? '' : 'trwarning';
1394
1395 echo '<tr';
1396 if ($is_highlighted)
1397 echo ' class=highlight';
1398 $a_class = isEthernetPort ($port) ? 'port-menu' : '';
1399 echo "><td class='tdleft $name_class' NOWRAP><a name='port-${port['id']}' class='interactive-portname nolink $a_class'>${port['name']}</a></td>";
1400 echo "<td class=tdleft>${port['label']}</td>";
1401 echo "<td class=tdleft>" . formatPortIIFOIF ($port) . "</td><td class=tdleft><tt>${port['l2address']}</tt></td>";
1402 if ($port['remote_object_id'])
1403 {
1404 echo "<td class=tdleft>" .
1405 formatPortLink ($port['remote_object_id'], $port['remote_object_name'], $port['remote_id'], NULL) .
1406 "</td>";
1407 echo "<td class=tdleft>" . formatLoggedSpan ($port['last_log'], $port['remote_name'], 'underline') . "</td>";
1408 $editable = permitted ('object', 'ports', 'editPort')
1409 ? 'editable'
1410 : '';
1411 echo "<td class=tdleft><span class='rsvtext $editable id-${port['id']} op-upd-reservation-cable'>${port['cableid']}</span></td>";
1412 }
1413 else
1414 echo implode ('', formatPortReservation ($port)) . '<td></td>';
1415 echo "</tr>";
1416 }
1417
1418 function renderObject ($object_id)
1419 {
1420 global $nextorder, $virtual_obj_types;
1421 $info = spotEntity ('object', $object_id);
1422 amplifyCell ($info);
1423 // Main layout starts.
1424 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
1425 echo "<tr><td colspan=2 align=center><h1>${info['dname']}</h1></td></tr>\n";
1426 // left column with uknown number of portlets
1427 echo "<tr><td class=pcleft>";
1428
1429 // display summary portlet
1430 $summary = array();
1431 if ($info['name'] != '')
1432 $summary['Common name'] = $info['name'];
1433 elseif (considerConfiguredConstraint ($info, 'NAMEWARN_LISTSRC'))
1434 $summary[] = array ('<tr><td colspan=2 class=msg_error>Common name is missing.</td></tr>');
1435 $summary['Object type'] = '<a href="' . makeHref (array (
1436 'page' => 'depot',
1437 'tab' => 'default',
1438 'cfe' => '{$typeid_' . $info['objtype_id'] . '}'
1439 )) . '">' . decodeObjectType ($info['objtype_id']) . '</a>';
1440 if ($info['label'] != '')
1441 $summary['Visible label'] = $info['label'];
1442 if ($info['asset_no'] != '')
1443 $summary['Asset tag'] = $info['asset_no'];
1444 elseif (considerConfiguredConstraint ($info, 'ASSETWARN_LISTSRC'))
1445 $summary[] = array ('<tr><td colspan=2 class=msg_error>Asset tag is missing.</td></tr>');
1446 $parents = getParents ($info, 'object');
1447 // lookup the human-readable object type, sort by it
1448 foreach ($parents as $parent_id => $parent)
1449 $parents[$parent_id]['object_type'] = decodeObjectType ($parent['objtype_id']);
1450 $grouped_parents = groupBy ($parents, 'object_type');
1451 ksort ($grouped_parents);
1452 foreach ($grouped_parents as $parents_group)
1453 {
1454 uasort ($parents_group, 'compare_name');
1455 $label = $parents_group[key ($parents_group)]['object_type'] . (count($parents_group) > 1 ? ' containers' : ' container');
1456 $fmt_parents = array();
1457 foreach ($parents_group as $parent)
1458 $fmt_parents[] = mkCellA ($parent);
1459 $summary[$label] = implode ('<br>', $fmt_parents);
1460 }
1461 $children = getChildren ($info, 'object');
1462 foreach (groupBy ($children, 'objtype_id') as $objtype_id => $children_group)
1463 {
1464 uasort ($children_group, 'compare_name');
1465 $fmt_children = array();
1466 foreach ($children_group as $child)
1467 $fmt_children[] = mkCellA ($child);
1468 $summary["Contains " . strtolower(decodeObjectType ($objtype_id))] = implode ('<br>', $fmt_children);
1469 }
1470 if ($info['has_problems'] == 'yes')
1471 $summary[] = array ('<tr><td colspan=2 class=msg_error>Has problems</td></tr>');
1472 foreach (getAttrValuesSorted ($object_id) as $record)
1473 if
1474 (
1475 $record['value'] != '' &&
1476 permitted (NULL, NULL, NULL, array (array ('tag' => '$attr_' . $record['id'])))
1477 )
1478 $summary['{sticker}' . $record['name']] = formatAttributeValue ($record);
1479 $summary[] = array (getOutputOf ('printTagTRs',
1480 $info,
1481 makeHref
1482 (
1483 array
1484 (
1485 'page'=>'depot',
1486 'tab'=>'default',
1487 'andor' => 'and',
1488 'cfe' => '{$typeid_' . $info['objtype_id'] . '}',
1489 )
1490 )."&"
1491 ));
1492 renderEntitySummary ($info, 'summary', $summary);
1493
1494 if ($info['comment'] != '')
1495 {
1496 startPortlet ('Comment');
1497 echo '<div class=commentblock>' . string_insert_hrefs ($info['comment']) . '</div>';
1498 finishPortlet ();
1499 }
1500
1501 $logrecords = getLogRecordsForObject ($_REQUEST['object_id']);
1502 if (count ($logrecords))
1503 {
1504 startPortlet ('log records');
1505 echo "<table cellspacing=0 cellpadding=5 align=center class=widetable width='100%'>";
1506 $order = 'odd';
1507 foreach ($logrecords as $row)
1508 {
1509 echo "<tr class=row_${order} valign=top>";
1510 echo '<td class=tdleft>' . $row['date'] . '<br>' . $row['user'] . '</td>';
1511 echo '<td class="logentry">' . string_insert_hrefs (htmlspecialchars ($row['content'], ENT_NOQUOTES)) . '</td>';
1512 echo '</tr>';
1513 $order = $nextorder[$order];
1514 }
1515 echo '</table>';
1516 finishPortlet();
1517 }
1518
1519 switchportInfoJS ($object_id); // load JS code to make portnames interactive
1520 renderFilesPortlet ('object', $object_id);
1521
1522 if (count ($info['ports']))
1523 {
1524 startPortlet ('ports and links');
1525 $hl_port_id = 0;
1526 if (isset ($_REQUEST['hl_port_id']))
1527 {
1528 assertUIntArg ('hl_port_id');
1529 $hl_port_id = $_REQUEST['hl_port_id'];
1530 addAutoScrollScript ("port-$hl_port_id");
1531 }
1532 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>";
1533 echo '<tr><th class=tdleft>Local name</th><th class=tdleft>Visible label</th>';
1534 echo '<th class=tdleft>Interface</th><th class=tdleft>L2 address</th>';
1535 echo '<th class=tdcenter colspan=2>Remote object and port</th>';
1536 echo '<th class=tdleft>Cable ID</th></tr>';
1537 foreach ($info['ports'] as $port)
1538 callHook ('renderObjectPortRow', $port, ($hl_port_id == $port['id']));
1539 if (permitted (NULL, 'ports', 'set_reserve_comment'))
1540 addJS ('js/inplace-edit.js');
1541 echo "</table><br>";
1542 finishPortlet();
1543 }
1544
1545 if (count ($info['ipv4']) + count ($info['ipv6']))
1546 {
1547 startPortlet ('IP addresses');
1548 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
1549 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
1550 echo "<tr class=tdleft><th>OS interface</th><th>IP address</th><th>network</th><th>routed by</th><th>peers</th></tr>\n";
1551 else
1552 echo "<tr class=tdleft><th>OS interface</th><th>IP address</th><th>peers</th></tr>\n";
1553
1554 // group IP allocations by interface name instead of address family
1555 $allocs_by_iface = array();
1556 foreach (array ('ipv4', 'ipv6') as $ip_v)
1557 foreach ($info[$ip_v] as $ip_bin => $alloc)
1558 $allocs_by_iface[$alloc['osif']][$ip_bin] = $alloc;
1559
1560 // sort allocs array by portnames
1561 foreach (sortPortList ($allocs_by_iface) as $iface_name => $alloclist)
1562 {
1563 $is_first_row = TRUE;
1564 foreach ($alloclist as $alloc)
1565 {
1566 $rendered_alloc = callHook ('getRenderedAlloc', $object_id, $alloc);
1567 echo "<tr class='${rendered_alloc['tr_class']}' valign=top>";
1568
1569 // display iface name, same values are grouped into single cell
1570 if ($is_first_row)
1571 {
1572 $rowspan = count ($alloclist) > 1 ? 'rowspan="' . count ($alloclist) . '"' : '';
1573 echo "<td class=tdleft $rowspan>" . $iface_name . $rendered_alloc['td_name_suffix'] . "</td>";
1574 $is_first_row = FALSE;
1575 }
1576 echo $rendered_alloc['td_ip'];
1577 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
1578 {
1579 echo $rendered_alloc['td_network'];
1580 echo $rendered_alloc['td_routed_by'];
1581 }
1582 echo $rendered_alloc['td_peers'];
1583
1584 echo "</tr>\n";
1585 }
1586 }
1587 echo "</table><br>\n";
1588 finishPortlet();
1589 }
1590
1591 $forwards = $info['nat4'];
1592 if (count ($forwards['in']) || count ($forwards['out']))
1593 {
1594 startPortlet('NATv4');
1595
1596 if (count($forwards['out']))
1597 {
1598
1599 echo "<h3>locally performed NAT</h3>";
1600
1601 echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center'>\n";
1602 echo "<tr><th>Proto</th><th>Match endpoint</th><th>Translate to</th><th>Target object</th><th>Rule comment</th></tr>\n";
1603
1604 foreach ($forwards['out'] as $pf)
1605 {
1606 $class = 'trerror';
1607 $osif = '';
1608 $localip_bin = ip_parse ($pf['localip']);
1609 if (array_key_exists ($localip_bin, $info['ipv4']))
1610 {
1611 $class = $info['ipv4'][$localip_bin]['addrinfo']['class'];
1612 $osif = $info['ipv4'][$localip_bin]['osif'] . ': ';
1613 }
1614 echo "<tr class='$class'>";
1615 echo "<td>${pf['proto']}</td><td class=tdleft>${osif}" . getRenderedIPPortPair ($pf['localip'], $pf['localport']) . "</td>";
1616 echo "<td class=tdleft>" . getRenderedIPPortPair ($pf['remoteip'], $pf['remoteport']) . "</td>";
1617 $address = getIPAddress (ip4_parse ($pf['remoteip']));
1618 echo "<td class='description'>";
1619 if (count ($address['allocs']))
1620 foreach($address['allocs'] as $bond)
1621 echo mkA ("${bond['object_name']}(${bond['name']})", 'object', $bond['object_id']) . ' ';
1622 elseif ($pf['remote_addr_name'] != '')
1623 echo '(' . $pf['remote_addr_name'] . ')';
1624 echo "</td><td class='description'>${pf['description']}</td></tr>";
1625 }
1626 echo "</table><br><br>";
1627 }
1628 if (count($forwards['in']))
1629 {
1630 echo "<h3>arriving NAT connections</h3>";
1631 echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center'>\n";
1632 echo "<tr><th>Matched endpoint</th><th>Source object</th><th>Translated to</th><th>Rule comment</th></tr>\n";
1633 foreach ($forwards['in'] as $pf)
1634 {
1635 echo "<tr>";
1636 echo "<td>${pf['proto']}/" . getRenderedIPPortPair ($pf['localip'], $pf['localport']) . "</td>";
1637 echo '<td class="description">' . mkA ($pf['object_name'], 'object', $pf['object_id']);
1638 echo "</td><td>" . getRenderedIPPortPair ($pf['remoteip'], $pf['remoteport']) . "</td>";
1639 echo "<td class='description'>${pf['description']}</td></tr>";
1640 }
1641 echo "</table><br><br>";
1642 }
1643 finishPortlet();
1644 }
1645
1646 renderSLBTriplets2 ($info);
1647 renderSLBTriplets ($info);
1648 echo "</td>\n";
1649
1650 // After left column we have (surprise!) right column with rackspace portlet only.
1651 echo "<td class=pcright>";
1652 if (!in_array($info['objtype_id'], $virtual_obj_types))
1653 {
1654 // rackspace portlet
1655 startPortlet ('rackspace allocation');
1656 foreach (getResidentRacksData ($object_id, FALSE) as $rack_id)
1657 renderRack ($rack_id, $object_id);
1658 echo '<br>';
1659 finishPortlet();
1660 }
1661 echo "</td></tr>";
1662 echo "</table>\n";
1663 }
1664
1665 function renderRackMultiSelect ($sname, $racks, $selected)
1666 {
1667 // Transform the given flat list into a list of groups, each representing a rack row.
1668 $rdata = array();
1669 foreach ($racks as $rack)
1670 {
1671 if ('' != $trail = getLocationTrail ($rack['location_id'], FALSE))
1672 $row_name = $trail . ' : ' . $rack['row_name'];
1673 else
1674 $row_name = $rack['row_name'];
1675 $rdata[$row_name][$rack['id']] = $rack['name'];
1676 }
1677 echo "<select name=${sname} multiple size=" . getConfigVar ('MAXSELSIZE') . " onchange='getElementsByName(\"updateObjectAllocation\")[0].submit()'>\n";
1678 $row_names = array_keys ($rdata);
1679 natsort ($row_names);
1680 foreach ($row_names as $optgroup)
1681 {
1682 echo "<optgroup label='${optgroup}'>";
1683 foreach ($rdata[$optgroup] as $rack_id => $rack_name)
1684 {
1685 echo "<option value=${rack_id}";
1686 if (!(array_search ($rack_id, $selected) === FALSE))
1687 echo ' selected';
1688 echo">${rack_name}</option>\n";
1689 }
1690 }
1691 echo "</select>\n";
1692 }
1693
1694 // This function renders a form for port edition.
1695 function renderPortsForObject ($object_id)
1696 {
1697 $prefs = getPortListPrefs();
1698 function printNewItemTR ($prefs)
1699 {
1700 printOpFormIntro ('addPort');
1701 echo "<tr><td>";
1702 printImageHREF ('add', 'add a port', TRUE);
1703 echo "</td><td class='tdleft'><input type=text size=16 name=port_name></td>\n";
1704 echo "<td><input type=text name=port_label></td><td>";
1705 printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type_id'), $prefs['selected']);
1706 echo "<td><input type=text name=port_l2address size=18 maxlength=24></td>\n";
1707 echo "<td colspan=4>&nbsp;</td><td>";
1708 printImageHREF ('add', 'add a port', TRUE);
1709 echo "</td></tr></form>";
1710 }
1711 if (getConfigVar('ENABLE_MULTIPORT_FORM') == 'yes' || getConfigVar('ENABLE_BULKPORT_FORM') == 'yes' )
1712 startPortlet ('Ports and interfaces');
1713 else
1714 echo '<br>';
1715 $object = spotEntity ('object', $object_id);
1716 amplifyCell ($object);
1717 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes' && getConfigVar('ENABLE_BULKPORT_FORM') == 'yes'){
1718 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
1719 echo "<tr><th>&nbsp;</th><th class=tdleft>Local name</th><th class=tdleft>Visible label</th><th class=tdleft>Interface</th><th class=tdleft>Start Number</th>";
1720 echo "<th class=tdleft>Count</th><th>&nbsp;</th></tr>\n";
1721 printOpFormIntro ('addBulkPorts');
1722 echo "<tr><td>";
1723 printImageHREF ('add', 'add ports', TRUE);
1724 echo "</td><td><input type=text size=8 name=port_name></td>\n";
1725 echo "<td><input type=text name=port_label></td><td>";
1726 printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type_id'), $prefs['selected']);
1727 echo "<td><input type=text name=port_numbering_start size=3 maxlength=3></td>\n";
1728 echo "<td><input type=text name=port_numbering_count size=3 maxlength=3></td>\n";
1729 echo "<td>&nbsp;</td><td>";
1730 printImageHREF ('add', 'add ports', TRUE);
1731 echo "</td></tr></form>";
1732 echo "</table><br>\n";
1733 }
1734
1735 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
1736 echo "<tr><th>&nbsp;</th><th class=tdleft>Local name</th><th class=tdleft>Visible label</th><th class=tdleft>Interface</th><th class=tdleft>L2 address</th>";
1737 echo "<th class=tdcenter colspan=2>Remote object and port</th><th>Cable ID</th><th class=tdcenter>(Un)link or (un)reserve</th><th>&nbsp;</th></tr>\n";
1738 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
1739 printNewItemTR ($prefs);
1740
1741 // clear ports link
1742 echo getOpLink (array ('op'=>'deleteAll'), 'Clear port list', 'clear', '', 'need-confirmation');
1743
1744 // rename ports link
1745 $n_ports_to_rename = 0;
1746 foreach ($object['ports'] as $port)
1747 if ($port['name'] != shortenPortName ($port['name'], $object['id']))
1748 $n_ports_to_rename++;
1749 if ($n_ports_to_rename)
1750 echo '<p>' . getOpLink (array ('op'=>'renameAll'), "Auto-rename $n_ports_to_rename port(s)", 'recalc', 'Use RackTables naming convention for this device type') . '</p>';
1751
1752 if (isset ($_REQUEST['hl_port_id']))
1753 {
1754 assertUIntArg ('hl_port_id');
1755 $hl_port_id = intval ($_REQUEST['hl_port_id']);
1756 addAutoScrollScript ("port-$hl_port_id");
1757 }
1758 switchportInfoJS ($object_id); // load JS code to make portnames interactive
1759 foreach ($object['ports'] as $port)
1760 {
1761 // highlight port name with yellow if it's name is not canonical
1762 $canon_pn = shortenPortName ($port['name'], $port['object_id']);
1763 $name_class = $canon_pn == $port['name'] ? '' : 'trwarning';
1764
1765 $tr_class = isset ($hl_port_id) && $hl_port_id == $port['id'] ? 'class="highlight"' : '';
1766 printOpFormIntro ('editPort', array ('port_id' => $port['id']));
1767 echo "<tr $tr_class><td><a name='port-${port['id']}' href='".makeHrefProcess(array('op'=>'delPort', 'port_id'=>$port['id']))."'>";
1768 printImageHREF ('delete', 'Unlink and Delete this port');
1769 echo "</a></td>\n";
1770 $a_class = isEthernetPort ($port) ? 'port-menu' : '';
1771 echo "<td class='tdleft $name_class' NOWRAP><input type=text name=name class='interactive-portname $a_class' value='${port['name']}' size=16></td>";
1772 echo "<td><input type=text name=label value='${port['label']}'></td>";
1773 echo '<td>';
1774 if ($port['iif_id'] != 1)
1775 echo '<label>' . $port['iif_name'] . ' ';
1776 $port_type_opts = array();
1777 if (! $port['linked'])
1778 $port_type_opts = getUnlinkedPortTypeOptions ($port['iif_id']);
1779 else
1780 foreach (getExistingPortTypeOptions ($port) as $oif_id => $opt_str)
1781 $port_type_opts[$port['iif_name']][$port['iif_id'] . '-' . $oif_id] = $opt_str;
1782 printNiftySelect ($port_type_opts, array ('name' => 'port_type_id'), $port['iif_id'] . '-' . $port['oif_id']);
1783 if ($port['iif_id'] != 1)
1784 echo '</label>';
1785 echo '</td>';
1786
1787 // 18 is enough to fit 6-byte MAC address in its longest form,
1788 // while 24 should be Ok for WWN
1789 echo "<td><input type=text name=l2address value='${port['l2address']}' size=18 maxlength=24></td>\n";
1790 if ($port['remote_object_id'])
1791 {
1792 echo "<td>" .
1793 formatLoggedSpan ($port['last_log'], formatPortLink ($port['remote_object_id'], $port['remote_object_name'], $port['remote_id'], NULL)) .
1794 "</td>";
1795 echo "<td> " . formatLoggedSpan ($port['last_log'], $port['remote_name'], 'underline') .
1796 "<input type=hidden name=reservation_comment value=''></td>";
1797 echo "<td><input type=text name=cable value='${port['cableid']}'></td>";
1798 echo "<td class=tdcenter>";
1799 echo getOpLink (array('op'=>'unlinkPort', 'port_id'=>$port['id'], ), '', 'cut', 'Unlink this port');
1800 echo "</td>";
1801 }
1802 elseif ($port['reservation_comment'] != '')
1803 {
1804 echo "<td>" . formatLoggedSpan ($port['last_log'], 'Reserved:', 'strong underline') . "</td>";
1805 echo "<td><input type=text name=reservation_comment value='${port['reservation_comment']}'></td>";
1806 echo "<td></td>";
1807 echo "<td class=tdcenter>";
1808 echo getOpLink (array('op'=>'useup', 'port_id'=>$port['id']), '', 'clear', 'Use up this port');
1809 echo "</td>";
1810 }
1811 else
1812 {
1813 $in_rack = getConfigVar ('NEAREST_RACKS_CHECKBOX');
1814 echo "<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td class=tdcenter>";
1815 echo getPopupLink ('portlist', array ('port' => $port['id'], 'in_rack' => ($in_rack == "yes" ? "on" : "")), 'findlink', 'plug', '', 'Link this port');
1816 echo " <input type=text name=reservation_comment></td>\n";
1817 }
1818 echo "<td>";
1819 printImageHREF ('save', 'Save changes', TRUE);
1820 echo "</td></form></tr>\n";
1821 }
1822 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
1823 printNewItemTR ($prefs);
1824 echo "</table><br>\n";
1825 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes' && getConfigVar('ENABLE_BULKPORT_FORM') == 'yes'){
1826 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
1827 echo "<tr><th>&nbsp;</th><th class=tdleft>Local name</th><th class=tdleft>Visible label</th><th class=tdleft>Interface</th><th class=tdleft>Start Number</th>";
1828 echo "<th class=tdleft>Count</th><th>&nbsp;</th></tr>\n";
1829 printOpFormIntro ('addBulkPorts');
1830 echo "<tr><td>";
1831 printImageHREF ('add', 'add ports', TRUE);
1832 echo "</td><td><input type=text size=8 name=port_name></td>\n";
1833 echo "<td><input type=text name=port_label></td><td>";
1834 printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type_id'), $prefs['selected']);
1835 echo "<td><input type=text name=port_numbering_start size=3 maxlength=3></td>\n";
1836 echo "<td><input type=text name=port_numbering_count size=3 maxlength=3></td>\n";
1837 echo "<td>&nbsp;</td><td>";
1838 printImageHREF ('add', 'add ports', TRUE);
1839 echo "</td></tr></form>";
1840 echo "</table><br>\n";
1841 }
1842 if (getConfigVar('ENABLE_MULTIPORT_FORM') == 'yes')
1843 finishPortlet();
1844 if (getConfigVar('ENABLE_MULTIPORT_FORM') != 'yes')
1845 return;
1846
1847 startPortlet ('Add/update multiple ports');
1848 printOpFormIntro ('addMultiPorts');
1849 $formats = array
1850 (
1851 'c3600asy' => 'Cisco 3600 async: sh line | inc TTY',
1852 'fiwg' => 'Foundry ServerIron/FastIron WorkGroup/Edge: sh int br',
1853 'fisxii' => 'Foundry FastIron SuperX/II4000: sh int br',
1854 'ssv1' => 'SSV:<interface name> [<MAC address>]',
1855 );
1856 echo 'Format: ' . getSelect ($formats, array ('name' => 'format'), 'ssv1');
1857 echo 'Default port type: ';
1858 printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type'), $prefs['selected']);
1859 echo "<input type=submit value='Parse output'><br>\n";
1860 echo "<textarea name=input cols=100 rows=50></textarea><br>\n";
1861 echo '</form>';
1862 finishPortlet();
1863 }
1864
1865 function renderIPForObject ($object_id)
1866 {
1867 function printNewItemTR ($default_type, $object_id)
1868 {
1869 global $aat;
1870
1871 includeJQueryUI (TRUE);
1872
1873 addJS (<<<JSEND
1874 $(document).ready( function() {
1875 $('[name="bond_name"]').autocomplete({
1876 source: "?module=ajax&ac=autocomplete&realm=bond_name&object_id=$object_id",
1877 //minLength: 3,
1878 focus: function(event, ui) {
1879 if( ui.item.value == '' )
1880 event.preventDefault();
1881 },
1882 select: function(event, ui) {
1883 if( ui.item.value == '' )
1884 event.preventDefault();
1885 }
1886 });
1887 });
1888 JSEND
1889 , TRUE);
1890 printOpFormIntro ('add');
1891 echo "<tr><td>"; // left btn
1892 printImageHREF ('add', 'allocate', TRUE);
1893 echo "</td>";
1894 echo "<td class=tdleft><input type='text' size='10' name='bond_name'></td>\n"; // if-name
1895 echo "<td class=tdleft><input type=text name='ip'></td>\n"; // IP
1896 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
1897 echo "<td colspan=2>&nbsp;</td>"; // network, routed by
1898 echo '<td>';
1899 printSelect ($aat, array ('name' => 'bond_type'), $default_type); // type
1900 echo "</td><td>&nbsp;</td><td>"; // misc
1901 printImageHREF ('add', 'allocate', TRUE); // right btn
1902 echo "</td></tr></form>";
1903 }
1904 global $aat;
1905
1906 startPortlet ('Allocations');
1907 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'><tr>\n";
1908 echo '<th>&nbsp;</th>';
1909 echo '<th>OS interface</th>';
1910 echo '<th>IP address</th>';
1911 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
1912 {
1913 echo '<th>network</th>';
1914 echo '<th>routed by</th>';
1915 }
1916 echo '<th>type</th>';
1917 echo '<th>misc</th>';
1918 echo '<th>&nbsp;</th>';
1919 echo '</tr>';
1920
1921 $alloc_list = ''; // most of the output is stored here
1922 $used_alloc_types = array();
1923 foreach (getObjectIPAllocations ($object_id) as $alloc)
1924 {
1925 if (! isset ($used_alloc_types[$alloc['type']]))
1926 $used_alloc_types[$alloc['type']] = 0;
1927 $used_alloc_types[$alloc['type']]++;
1928
1929 $rendered_alloc = callHook ('getRenderedAlloc', $object_id, $alloc);
1930 $alloc_list .= getOutputOf ('printOpFormIntro', 'upd', array ('ip' => $alloc['addrinfo']['ip']));
1931 $alloc_list .= "<tr class='${rendered_alloc['tr_class']}' valign=top>";
1932
1933 $alloc_list .= "<td>" . getOpLink (array ('op' => 'del', 'ip' => $alloc['addrinfo']['ip']), '', 'delete', 'Delete this IP address') . "</td>";
1934 $alloc_list .= "<td class=tdleft><input type='text' name='bond_name' value='${alloc['osif']}' size=10>" . $rendered_alloc['td_name_suffix'] . "</td>";
1935 $alloc_list .= $rendered_alloc['td_ip'];
1936 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
1937 {
1938 $alloc_list .= $rendered_alloc['td_network'];
1939 $alloc_list .= $rendered_alloc['td_routed_by'];
1940 }
1941 $alloc_list .= '<td>' . getSelect ($aat, array ('name' => 'bond_type'), $alloc['type']) . "</td>";
1942 $alloc_list .= $rendered_alloc['td_peers'];
1943 $alloc_list .= "<td>" .getImageHREF ('save', 'Save changes', TRUE) . "</td>";
1944
1945 $alloc_list .= "</form></tr>\n";
1946 }
1947 asort ($used_alloc_types, SORT_NUMERIC);
1948 $most_popular_type = ! count ($used_alloc_types) ? 'regular' : array_last (array_keys ($used_alloc_types));
1949
1950 if ($list_on_top = (getConfigVar ('ADDNEW_AT_TOP') != 'yes'))
1951 echo $alloc_list;
1952 printNewItemTR ($most_popular_type, $object_id);
1953 if (! $list_on_top)
1954 echo $alloc_list;
1955
1956 echo "</table><br>\n";
1957 finishPortlet();
1958 }
1959
1960 // This function is deprecated. Do not rely on its internals,
1961 // it will probably be removed in the next major relese.
1962 // Use new showError, showWarning, showSuccess functions.
1963 // Log array is stored in global $log_messages. Its format is simple: plain ordered array
1964 // with values having keys 'c' (both message code and severity) and 'a' (sprintf arguments array)
1965 function showMessageOrError ()
1966 {
1967 global $log_messages;
1968
1969 startSession();
1970 if (isset ($_SESSION['log']))
1971 {
1972 $log_messages = array_merge ($_SESSION['log'], $log_messages);
1973 unset ($_SESSION['log']);
1974 }
1975 session_commit();
1976
1977 if (empty ($log_messages))
1978 return;
1979 $msginfo = array
1980 (
1981 // records 0~99 with success messages
1982 0 => array ('code' => 'success', 'format' => '%s'),
1983 5 => array ('code' => 'success', 'format' => 'added record "%s" successfully'),
1984 6 => array ('code' => 'success', 'format' => 'updated record "%s" successfully'),
1985 7 => array ('code' => 'success', 'format' => 'deleted record "%s" successfully'),
1986 8 => array ('code' => 'success', 'format' => 'Port %s successfully linked with %s'),
1987 10 => array ('code' => 'success', 'format' => 'Added %u port(s), updated %u port(s), encountered %u error(s).'),
1988 21 => array ('code' => 'success', 'format' => 'Generation complete'),
1989 26 => array ('code' => 'success', 'format' => 'updated %u record(s) successfully'),
1990 37 => array ('code' => 'success', 'format' => 'added %u record(s) successfully'),
1991 38 => array ('code' => 'success', 'format' => 'removed %u record(s) successfully'),
1992 43 => array ('code' => 'success', 'format' => 'Saved successfully.'),
1993 44 => array ('code' => 'success', 'format' => '%s failure(s) and %s successfull change(s).'),
1994 48 => array ('code' => 'success', 'format' => 'added a record successfully'),
1995 49 => array ('code' => 'success', 'format' => 'deleted a record successfully'),
1996 51 => array ('code' => 'success', 'format' => 'updated a record successfully'),
1997 57 => array ('code' => 'success', 'format' => 'Reset complete'),
1998 58 => array ('code' => 'success', 'format' => '%u device(s) unmounted successfully'),
1999 63 => array ('code' => 'success', 'format' => '%u change request(s) have been processed'),
2000 67 => array ('code' => 'success', 'format' => "Tag rolling done, %u object(s) involved"),
2001 71 => array ('code' => 'success', 'format' => 'File "%s" was linked successfully'),
2002 72 => array ('code' => 'success', 'format' => 'File was unlinked successfully'),
2003 82 => array ('code' => 'success', 'format' => "Bulk port creation was successful. %u port(s) created, %u failed"),
2004 87 => array ('code' => 'success', 'format' => '802.1Q recalculate: %d port(s) changed on %d switch(es)'),
2005 // records 100~199 with fatal error messages
2006 100 => array ('code' => 'error', 'format' => '%s'),
2007 109 => array ('code' => 'error', 'format' => 'failed updating a record'),
2008 131 => array ('code' => 'error', 'format' => 'invalid format requested'),
2009 141 => array ('code' => 'error', 'format' => 'Encountered %u error(s), updated %u record(s)'),
2010 149 => array ('code' => 'error', 'format' => 'Turing test failed'),
2011 150 => array ('code' => 'error', 'format' => 'Can only change password under DB authentication.'),
2012 151 => array ('code' => 'error', 'format' => 'Old password doesn\'t match.'),
2013 152 => array ('code' => 'error', 'format' => 'New passwords don\'t match.'),
2014 154 => array ('code' => 'error', 'format' => "Verification error: %s"),
2015 155 => array ('code' => 'error', 'format' => 'Save failed.'),
2016 159 => array ('code' => 'error', 'format' => 'Permission denied moving port %s from VLAN%u to VLAN%u'),
2017 170 => array ('code' => 'error', 'format' => 'There is no network for IP address "%s"'),
2018 172 => array ('code' => 'error', 'format' => 'Malformed request'),
2019 179 => array ('code' => 'error', 'format' => 'Expired form has been declined.'),
2020 191 => array ('code' => 'error', 'format' => "deploy was blocked due to conflicting configuration versions"),
2021
2022 // records 200~299 with warnings
2023 200 => array ('code' => 'warning', 'format' => '%s'),
2024 201 => array ('code' => 'warning', 'format' => 'nothing happened...'),
2025 206 => array ('code' => 'warning', 'format' => '%s is not empty'),
2026 207 => array ('code' => 'warning', 'format' => 'File upload failed, error: %s'),
2027
2028 // records 300~399 with notices
2029 300 => array ('code' => 'neutral', 'format' => '%s'),
2030
2031 );
2032 // Handle the arguments. Is there any better way to do it?
2033 foreach ($log_messages as $record)
2034 {
2035 if (! isset ($record['c']) || ! isset ($msginfo[$record['c']]))
2036 {
2037 $prefix = isset ($record['c']) ? $record['c'] . ': ' : '';
2038 echo "<div class=msg_neutral>(${prefix}this message was lost)</div>";
2039 continue;
2040 }
2041 if (isset ($record['a']))
2042 switch (count ($record['a']))
2043 {
2044 case 1:
2045 $msgtext = sprintf
2046 (
2047 $msginfo[$record['c']]['format'],
2048 $record['a'][0]
2049 );
2050 break;
2051 case 2:
2052 $msgtext = sprintf
2053 (
2054 $msginfo[$record['c']]['format'],
2055 $record['a'][0],
2056 $record['a'][1]
2057 );
2058 break;
2059 case 3:
2060 $msgtext = sprintf
2061 (
2062 $msginfo[$record['c']]['format'],
2063 $record['a'][0],
2064 $record['a'][1],
2065 $record['a'][2]
2066 );
2067 break;
2068 case 4:
2069 default:
2070 $msgtext = sprintf
2071 (
2072 $msginfo[$record['c']]['format'],
2073 $record['a'][0],
2074 $record['a'][1],
2075 $record['a'][2],
2076 $record['a'][3]
2077 );
2078 break;
2079 }
2080 else
2081 $msgtext = $msginfo[$record['c']]['format'];
2082 echo '<div class=msg_' . $msginfo[$record['c']]['code'] . ">${msgtext}</div>";
2083 }
2084 $log_messages = array();
2085 }
2086
2087 // renders two tables: port link status and learned MAC list
2088 function renderPortsInfo($object_id)
2089 {
2090 $breed = detectDeviceBreed ($object_id);
2091 try
2092 {
2093 if (permitted (NULL, NULL, 'get_link_status'))
2094 {
2095 if (validBreedFunction ($breed, 'getportstatus'))
2096 $linkStatus = queryDevice ($object_id, 'getportstatus');
2097 }
2098 else
2099 showWarning ("You don't have permission to view ports link status");
2100
2101 if (permitted (NULL, NULL, 'get_mac_list'))
2102 {
2103 if (validBreedFunction ($breed, 'getmaclist'))
2104 $macList = sortPortList (queryDevice ($object_id, 'getmaclist'));
2105 }
2106 else
2107 showWarning ("You don't have permission to view learned MAC list");
2108 }
2109 catch (RTGatewayError $e)
2110 {
2111 showError ($e->getMessage());
2112 return;
2113 }
2114
2115 global $nextorder;
2116 echo "<table width='100%'><tr>";
2117 if (! empty ($linkStatus))
2118 {
2119 echo "<td valign='top' width='50%'>";
2120 startPortlet('Link status');
2121 echo "<table width='80%' class='widetable' cellspacing=0 cellpadding='5px' align='center'>";
2122 echo '<tr><th>Port</th><th>&nbsp;</th><th>Link status</th><th>Link info</th></tr>';
2123 $statusmap = array
2124 (
2125 'up' => 'link up',
2126 'down' => 'link down',
2127 'disabled' => 'link disabled',
2128 );
2129 $order = 'even';
2130 foreach ($linkStatus as $pn => $link)
2131 {
2132 echo "<tr class='row_$order'>";
2133 $order = $nextorder[$order];
2134 echo '<td>' . $pn . '</td>';
2135 echo '<td>' . getImageHREF (array_fetch ($statusmap, $link['status'], '16x16t')) . '</td>';
2136 echo '<td>' . $link['status'] . '</td>';
2137 $info = '';
2138 if (isset ($link['speed']))
2139 $info .= $link['speed'];
2140 if (isset ($link['duplex']))
2141 {
2142 if ($info != '')
2143 $info .= ', ';
2144 $info .= $link['duplex'];
2145 }
2146 echo '<td>' . $info . '</td>';
2147 echo '</tr>';
2148 }
2149 echo "</table></td>";
2150 finishPortlet();
2151 }
2152
2153 if (! empty ($macList))
2154 {
2155 echo "<td valign='top' width='50%'>";
2156 $rendered_macs = '';
2157 $mac_count = 0;
2158 $rendered_macs .= "<table width='80%' class='widetable' cellspacing=0 cellpadding='5px' align='center'>";
2159 $rendered_macs .= '<tr><th>MAC address</th><th>VLAN</th><th>Port</th></tr>';
2160 $order = 'even';
2161 foreach ($macList as $pn => $list)
2162 {
2163 $order = $nextorder[$order];
2164 foreach ($list as $item)
2165 {
2166 ++$mac_count;
2167 $rendered_macs .= "<tr class='row_$order'>";
2168 $rendered_macs .= '<td style="font-family: monospace">' . $item['mac'] . '</td>';
2169 $rendered_macs .= '<td>' . $item['vid'] . '</td>';
2170 $rendered_macs .= '<td>' . $pn . '</td>';
2171 $rendered_macs .= '</tr>';
2172 }
2173 }
2174 $rendered_macs .= "</table></td>";
2175
2176 startPortlet("Learned MACs ($mac_count)");
2177 echo $rendered_macs;
2178 finishPortlet();
2179 }
2180
2181 echo "</td></tr></table>";
2182 }
2183
2184 /*
2185 The following conditions must be met:
2186 1. We can mount onto free atoms only. This means: if any record for an atom
2187 already exists in RackSpace, it can't be used for mounting.
2188 2. We can't unmount from 'W' atoms. Operator should review appropriate comments
2189 and either delete them before unmounting or refuse to unmount the object.
2190 */
2191 function renderRackSpaceForObject ($object_id)
2192 {
2193 // Always process occupied racks plus racks chosen by user. First get racks with
2194 // already allocated rackspace...
2195 $workingRacksData = getResidentRacksData ($object_id);
2196 // ...and then add those chosen by user (if any).
2197 if (isset($_REQUEST['rackmulti']))
2198 foreach ($_REQUEST['rackmulti'] as $cand_id)
2199 if (!isset ($workingRacksData[$cand_id]))
2200 {
2201 $rackData = spotEntity ('rack', $cand_id);
2202 amplifyCell ($rackData);
2203 $workingRacksData[$cand_id] = $rackData;
2204 }
2205
2206 // Get a list of rack ids that are parents of this object.
2207 $object = spotEntity ('object', $object_id);
2208 $parentRacks = reduceSubarraysToColumn (getParents ($object, 'rack'), 'id');
2209
2210 // Main layout starts.
2211 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0><tr>";
2212
2213 // Left portlet with rack list.
2214 echo "<td class=pcleft height='1%'>";
2215 startPortlet ('Racks');
2216 $allRacksData = listCells ('rack');
2217
2218 // filter rack list to match only racks having common tags with the object (reducing $allRacksData)
2219 if (! isset ($_REQUEST['show_all_racks']) && getConfigVar ('FILTER_RACKLIST_BY_TAGS') == 'yes')
2220 {
2221 $matching_racks = array();
2222 $matched_tags = array();
2223 foreach ($allRacksData as $rack)
2224 {
2225 $tag_chain = array_merge ($rack['etags'], $rack['itags']);
2226 foreach ($object['etags'] as $tag)
2227 if (tagOnChain ($tag, $tag_chain))
2228 {
2229 $matching_racks[$rack['id']] = $rack;
2230 $matched_tags[$tag['id']] = $tag;
2231 break;
2232 }
2233 }
2234 // add current object's racks even if they dont match filter
2235 foreach ($workingRacksData as $rack_id => $rack)
2236 if (! isset ($matching_racks[$rack_id]))
2237 $matching_racks[$rack_id] = $rack;
2238 // if matching racks found, and rack list is reduced, show 'show all' link
2239 if (count ($matching_racks) && count ($matching_racks) != count ($allRacksData))
2240 {
2241 $tmp = array();
2242 foreach ($matched_tags as $tag)
2243 $tmp[] = '{' . $tag['tag'] . '}';
2244 $filter_text = implode (' or ', $tmp);
2245 $href_show_all = trim($_SERVER['REQUEST_URI'], '&');
2246 $href_show_all .= htmlspecialchars('&show_all_racks=1');
2247 echo "(filtered by <span class='filter-text'>$filter_text</span>, <a href='$href_show_all'>show all</a>)<p>";
2248 $allRacksData = $matching_racks;
2249 }
2250 }
2251
2252 if (count ($allRacksData) <= getConfigVar ('RACK_PRESELECT_THRESHOLD'))
2253 foreach ($allRacksData as $rack)
2254 if (!array_key_exists ($rack['id'], $workingRacksData))
2255 {
2256 amplifyCell ($rack);
2257 $workingRacksData[$rack['id']] = $rack;
2258 }
2259 foreach (array_keys ($workingRacksData) as $rackId)
2260 applyObjectMountMask ($workingRacksData[$rackId], $object_id);
2261 printOpFormIntro ('updateObjectAllocation');
2262 renderRackMultiSelect ('rackmulti[]', $allRacksData, array_keys ($workingRacksData));
2263 echo "<br><br>";
2264 finishPortlet();
2265 echo "</td>";
2266
2267 // Middle portlet with comment and submit.
2268 echo "<td class=pcleft>";
2269 startPortlet ('Comment (for Rackspace History)');
2270 echo "<textarea name=comment rows=10 cols=40></textarea><br>\n";
2271 echo "<input type=submit value='Save' name=got_atoms>\n";
2272 echo "<br><br>";
2273 finishPortlet();
2274 echo "</td>";
2275
2276 // Right portlet with rendered racks. If this form submit is not final, we have to
2277 // reflect the former state of the grid in current form.
2278 echo "<td class=pcright rowspan=2 height='1%'>";
2279 startPortlet ('Working copy');
2280 includeJQueryUI (false);
2281 addJS ('js/racktables.js');
2282 addJS ('js/bulkselector.js');
2283 echo '<table border=0 cellspacing=10 align=center><tr>';
2284 foreach ($workingRacksData as $rack_id => $rackData)
2285 {
2286 $is_ro = !rackModificationPermitted ($rackData, 'updateObjectAllocation');
2287 // Order is important here: only original allocation is highlighted.
2288 highlightObject ($rackData, $object_id);
2289 markupAtomGrid ($rackData, 'T');
2290 // If we have a form processed, discard user input and show new database
2291 // contents.
2292 if (!$is_ro && isset ($_REQUEST['rackmulti'][0])) // is an update
2293 mergeGridFormToRack ($rackData);
2294 echo "<td valign=top>";
2295 echo "<center>\n<h2 style='margin:0px'>${rackData['name']}</h2>\n";
2296 $read_only_text = $is_ro ? '(read-only)' : '&nbsp;';
2297 echo "<p style='color: red; margin-top:0px'>${read_only_text}</p>\n";
2298 echo "<table class=rack id=selectableRack border=0 cellspacing=0 cellpadding=1>\n";
2299 echo "<tr><th width='10%'>&nbsp;</th>";
2300 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
2301 echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
2302 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
2303 renderAtomGrid ($rackData, $is_ro);
2304 echo "<tr><th width='10%'>&nbsp;</th>";
2305 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
2306 echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
2307 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
2308 echo "</table>\n<br>\n";
2309 // Determine zero-u checkbox status.
2310 // If form has been submitted, use form data, otherwise use DB data.
2311 if (!$is_ro && isset($_REQUEST['op']))
2312 $checked = isset($_REQUEST['zerou_'.$rack_id]) ? 'checked' : '';
2313 else
2314 $checked = in_array($rack_id, $parentRacks) ? 'checked' : '';
2315 $disabled_text = $is_ro ? ' disabled' : '';
2316 echo "<label for=zerou_${rack_id}>Zero-U:</label> <input type=checkbox ${checked} name=zerou_${rack_id} id=zerou_${rack_id}${disabled_text}>\n<br><br>\n";
2317 echo "<input type='button' onclick='uncheckAll();' value='Uncheck all'>\n";
2318 echo '</center></td>';
2319 }
2320 echo "</tr></table>";
2321 finishPortlet();
2322 echo "</td>\n";
2323
2324 echo "</form>\n";
2325 echo "</tr></table>\n";
2326 }
2327
2328 function renderMolecule ($mdata, $object_id)
2329 {
2330 // sort data out
2331 $rackpack = array();
2332 global $loclist;
2333 foreach ($mdata as $rua)
2334 {
2335 $rack_id = $rua['rack_id'];
2336 $unit_no = $rua['unit_no'];
2337 $atom = $rua['atom'];
2338 if (!isset ($rackpack[$rack_id]))
2339 {
2340 $rackData = spotEntity ('rack', $rack_id);
2341 amplifyCell ($rackData);
2342 for ($i = $rackData['height']; $i > 0; $i--)
2343 for ($locidx = 0; $locidx < 3; $locidx++)
2344 $rackData[$i][$locidx]['state'] = 'F';
2345 $rackpack[$rack_id] = $rackData;
2346 }
2347 $rackpack[$rack_id][$unit_no][$loclist[$atom]]['state'] = 'T';
2348 $rackpack[$rack_id][$unit_no][$loclist[$atom]]['object_id'] = $object_id;
2349 }
2350 // now we have some racks to render
2351 foreach ($rackpack as $rackData)
2352 {
2353 markAllSpans ($rackData);
2354 echo "<table class=molecule cellspacing=0>\n";
2355 echo "<caption>${rackData['name']}</caption>\n";
2356 echo "<tr><th width='10%'>&nbsp;</th><th width='20%'>Front</th><th width='50%'>Interior</th><th width='20%'>Back</th></tr>\n";
2357 for ($i = $rackData['height']; $i > 0; $i--)
2358 {
2359 echo "<tr><th>" . inverseRackUnit ($i, $rackData) . "</th>";
2360 for ($locidx = 0; $locidx < 3; $locidx++)
2361 {
2362 $state = $rackData[$i][$locidx]['state'];
2363 echo "<td class='atom state_${state}'>&nbsp;</td>\n";
2364 }
2365 echo "</tr>\n";
2366 }
2367 echo "</table>\n";
2368 }
2369 }
2370
2371 function renderDepot ()
2372 {
2373 global $pageno, $nextorder;
2374 $cellfilter = getCellFilter();
2375 $objects = array();
2376 $objects_count = getEntitiesCount ('object');
2377 $showobjecttype = (getConfigVar ('SHOW_OBJECTTYPE') == 'yes');
2378
2379 echo "<table border=0 class=objectview>\n";
2380 echo "<tr><td class=pcleft>";
2381
2382 if ($objects_count == 0)
2383 echo '<h2>No objects exist</h2>';
2384 // 1st attempt: do not fetch all objects if cellfilter is empty and rendering empty result is enabled
2385 elseif (! ($cellfilter['is_empty'] && renderEmptyResults ($cellfilter, 'objects', $objects_count)))
2386 {
2387 $objects = applyCellFilter ('object', $cellfilter);
2388 // 2nd attempt: do not render all fetched objects if rendering empty result is enabled
2389 if (! renderEmptyResults ($cellfilter, 'objects', count($objects)))
2390 {
2391 startPortlet ('Objects (' . count ($objects) . ')');
2392 echo '<br><br><table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
2393 echo '<tr><th>Common name</th>';
2394 if ($showobjecttype)
2395 echo '<th>Type</th>';
2396 echo '<th>Visible label</th><th>Asset tag</th><th>Row/Rack or Container</th></tr>';
2397 $order = 'odd';
2398 # gather IDs of all objects and fetch rackspace info in one pass
2399 $idlist = array();
2400 foreach ($objects as $obj)
2401 $idlist[] = $obj['id'];
2402 $mountinfo = getMountInfo ($idlist);
2403 $containerinfo = getContainerInfo ($idlist);
2404 foreach ($objects as $obj)
2405 {
2406 $problem = ($obj['has_problems'] == 'yes') ? 'has_problems' : '';
2407 echo "<tr class='row_${order} tdleft ${problem}' valign=top><td>" . mkA ("<strong>${obj['dname']}</strong>", 'object', $obj['id']);
2408 if (count ($obj['etags']))
2409 echo '<br><small>' . serializeTags ($obj['etags'], makeHref(array('page'=>$pageno, 'tab'=>'default')) . '&') . '</small>';
2410 echo '</td>';
2411 if ($showobjecttype)
2412 echo "<td>" . decodeObjectType ($obj['objtype_id']) . "</td>";
2413 echo "<td>${obj['label']}</td>";
2414 echo "<td>${obj['asset_no']}</td>";
2415 $places = array();
2416 if (array_key_exists ($obj['id'], $containerinfo))
2417 foreach ($containerinfo[$obj['id']] as $ci)
2418 $places[] = mkA ($ci['container_dname'], 'object', $ci['container_id']);
2419 if (array_key_exists ($obj['id'], $mountinfo))
2420 foreach ($mountinfo[$obj['id']] as $mi)
2421 $places[] = mkA ($mi['row_name'], 'row', $mi['row_id']) . '/' . mkA ($mi['rack_name'], 'rack', $mi['rack_id']);
2422 if (! count ($places))
2423 $places[] = 'Unmounted';
2424 echo "<td>" . implode (', ', $places) . '</td>';
2425 echo '</tr>';
2426 $order = $nextorder[$order];
2427 }
2428 echo '</table>';
2429 finishPortlet();
2430 }
2431 }
2432
2433 echo "</td><td class=pcright width='25%'>";
2434
2435 renderCellFilterPortlet ($cellfilter, 'object', $objects);
2436 echo "</td></tr></table>\n";
2437 }
2438
2439 // This function returns TRUE if the result set is too big to be rendered, and no filter is set.
2440 // In this case it renders the describing message instead.
2441 function renderEmptyResults($cellfilter, $entities_name, $count = NULL)
2442 {
2443 if (!$cellfilter['is_empty'])
2444 return FALSE;
2445 if (isset ($_REQUEST['show_all_objects']))
2446 return FALSE;
2447 $max = intval(getConfigVar('MAX_UNFILTERED_ENTITIES'));
2448 if (0 == $max || $count <= $max)
2449 return FALSE;
2450
2451 $href_show_all = trim($_SERVER['REQUEST_URI'], '&');
2452 $href_show_all .= htmlspecialchars('&show_all_objects=1');
2453 $suffix = isset ($count) ? " ($count)" : '';
2454 echo <<<END
2455 <p>Please set a filter to display the corresponging $entities_name.
2456 <br><a href="$href_show_all">Show all $entities_name$suffix</a>
2457 END;
2458 return TRUE;
2459 }
2460
2461 // History viewer for history-enabled simple dictionaries.
2462 function renderObjectHistory ($object_id)
2463 {
2464 $order = 'odd';
2465 global $nextorder;
2466 echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
2467 echo '<tr><th>change time</th><th>author</th><th>name</th><th>visible label</th><th>asset no</th><th>has problems?</th><th>comment</th></tr>';
2468 $result = usePreparedSelectBlade
2469 (
2470 'SELECT ctime, user_name, name, label, asset_no, has_problems, comment FROM ObjectHistory WHERE id=? ORDER BY ctime',
2471 array ($object_id)
2472 );
2473 while ($row = $result->fetch (PDO::FETCH_NUM))
2474 {
2475 echo "<tr class=row_${order}><td>${row[0]}</td>";
2476 for ($i = 1; $i <= 6; $i++)
2477 echo "<td>" . $row[$i] . "</td>";
2478 echo "</tr>\n";
2479 $order = $nextorder[$order];
2480 }
2481 echo "</table><br>\n";
2482 }
2483
2484 function renderRackspaceHistory ()
2485 {
2486 global $nextorder, $pageno, $tabno;
2487 $order = 'odd';
2488 $history = getRackspaceHistory();
2489 // Show the last operation by default.
2490 if (isset ($_REQUEST['op_id']))
2491 $op_id = $_REQUEST['op_id'];
2492 elseif (isset ($history[0]['mo_id']))
2493 $op_id = $history[0]['mo_id'];
2494 else $op_id = NULL;
2495
2496 $omid = NULL;
2497 $nmid = NULL;
2498 $object_id = 1;
2499 if ($op_id)
2500 list ($omid, $nmid) = getOperationMolecules ($op_id);
2501
2502 // Main layout starts.
2503 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
2504
2505 // Left top portlet with old allocation.
2506 echo "<tr><td class=pcleft>";
2507 startPortlet ('Old allocation');
2508 if ($omid)
2509 {
2510 $oldMolecule = getMolecule ($omid);
2511 renderMolecule ($oldMolecule, $object_id);
2512 }
2513 else
2514 echo "nothing";
2515 finishPortlet();
2516
2517 echo '</td><td class=pcright>';
2518
2519 // Right top portlet with new allocation
2520 startPortlet ('New allocation');
2521 if ($nmid)
2522 {
2523 $newMolecule = getMolecule ($nmid);
2524 renderMolecule ($newMolecule, $object_id);
2525 }
2526 else
2527 echo "nothing";
2528 finishPortlet();
2529
2530 echo '</td></tr><tr><td colspan=2>';
2531
2532 // Bottom portlet with list
2533
2534 startPortlet ('Rackspace allocation history');
2535 echo "<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>\n";
2536 echo "<tr><th>timestamp</th><th>author</th><th>object</th><th>comment</th></tr>\n";
2537 foreach ($history as $row)
2538 {
2539 if ($row['mo_id'] == $op_id)
2540 $class = 'hl';
2541 else
2542 $class = "row_${order}";
2543 echo "<tr class=${class}><td><a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'op_id'=>$row['mo_id']))."'>${row['ctime']}</a></td>";
2544 echo "<td>${row['user_name']}</td><td>";
2545 renderCell (spotEntity ('object', $row['ro_id']));
2546 echo '</td><td>' . stringForTD ($row['comment'], 0) . '</td></tr>';
2547 $order = $nextorder[$order];
2548 }
2549 echo "</table>\n";
2550 finishPortlet();
2551
2552 echo '</td></tr></table>';
2553 }
2554
2555 function renderIPSpaceRecords ($tree, $baseurl, $target = 0, $level = 1)
2556 {
2557 $self = __FUNCTION__;
2558 $knight = (getConfigVar ('IPV4_ENABLE_KNIGHT') == 'yes');
2559 $display_routers = (getConfigVar ('IPV4_TREE_RTR_AS_CELL') != 'none');
2560
2561 // scroll page to the highlighted item
2562 if ($target && isset ($_REQUEST['hl_net']))
2563 addAutoScrollScript ("net-$target");
2564
2565 foreach ($tree as $item)
2566 {
2567 if (isset ($item['id']))
2568 {
2569 $decor = array ('indent' => $level);
2570 if ($item['symbol'] == 'node-collapsed')
2571 $decor['symbolurl'] = "${baseurl}&eid=${item['id']}&hl_net=1";
2572 elseif ($item['symbol'] == 'node-expanded')
2573 $decor['symbolurl'] = $baseurl . ($item['parent_id'] ? "&eid=${item['parent_id']}&hl_net=1" : '');
2574 $tr_class = '';
2575 if ($target == $item['id'] && isset ($_REQUEST['hl_net']))
2576 {
2577 $decor['tdclass'] = ' highlight';
2578 $tr_class = $decor['tdclass'];
2579 }
2580 echo "<tr valign=top class=\"$tr_class\">";
2581 printIPNetInfoTDs ($item, $decor);
2582
2583 // capacity and usage
2584 echo "<td class=tdcenter>";
2585 echo getRenderedIPNetCapacity ($item);
2586 echo "</td>";
2587
2588 if ($display_routers)
2589 printRoutersTD (findNetRouters ($item), getConfigVar ('IPV4_TREE_RTR_AS_CELL'));
2590 echo "</tr>";
2591 if ($item['symbol'] == 'node-expanded' || $item['symbol'] == 'node-expanded-static')
2592 $self ($item['kids'], $baseurl, $target, $level + 1);
2593 }
2594 elseif (getConfigVar ('IPV4_TREE_SHOW_UNALLOCATED') == 'yes')
2595 {
2596 // non-allocated (spare) IP range
2597 echo "<tr valign=top>";
2598 printIPNetInfoTDs ($item, array ('indent' => $level, 'knight' => $knight, 'tdclass' => 'sparenetwork'));
2599
2600 // capacity and usage
2601 echo "<td class=tdcenter>";
2602 echo getRenderedIPNetCapacity ($item);
2603 echo "</td>";
2604 if ($display_routers)
2605 echo "<td></td>";
2606 echo "</tr>";
2607 }
2608 }
2609 }
2610
2611 function renderIPSpace()
2612 {
2613 global $pageno, $tabno;
2614 $realm = ($pageno == 'ipv4space' ? 'ipv4net' : 'ipv6net');
2615 $cellfilter = getCellFilter();
2616
2617 // expand request can take either natural values or "ALL". Zero means no expanding.
2618 $eid = isset ($_REQUEST['eid']) ? $_REQUEST['eid'] : 0;
2619
2620 echo "<table border=0 class=objectview>\n";
2621 echo "<tr><td class=pcleft>";
2622
2623 $netlist = array();
2624 if (! ($cellfilter['is_empty'] && ! $eid && renderEmptyResults($cellfilter, 'IP nets', getEntitiesCount ($realm))))
2625 {
2626 $top = NULL;
2627 foreach (listCells ($realm) as $net)
2628 {
2629 if (isset ($top) && IPNetContains ($top, $net))
2630 ;
2631 elseif (! count ($cellfilter['expression']) || judgeCell ($net, $cellfilter['expression']))
2632 $top = $net;
2633 else
2634 continue;
2635 $netlist[$net['id']] = $net;
2636 }
2637 $netcount = count ($netlist);
2638 $tree = prepareIPTree ($netlist, $eid);
2639
2640 if (! renderEmptyResults($cellfilter, 'IP nets', count($tree)))
2641 {
2642 startPortlet ("networks (${netcount})");
2643 echo '<h4>';
2644 $all = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'ALL')) .
2645 $cellfilter['urlextra'] . "'>expand&nbsp;all</a>";
2646 $none = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'NONE')) .
2647 $cellfilter['urlextra'] . "'>collapse&nbsp;all</a>";
2648 $auto = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno)) .
2649 $cellfilter['urlextra'] . "'>auto-collapse</a>";
2650
2651 if ($eid === 0)
2652 echo 'auto-collapsing at threshold ' . getConfigVar ('TREE_THRESHOLD') . " ($all / $none)";
2653 elseif ($eid === 'ALL')
2654 echo "expanding all ($auto / $none)";
2655 elseif ($eid === 'NONE')
2656 echo "collapsing all ($all / $auto)";
2657 else
2658 {
2659 try
2660 {
2661 $netinfo = spotEntity ($realm, $eid);
2662 echo "expanding ${netinfo['ip']}/${netinfo['mask']} ($auto / $all / $none)";
2663 }
2664 catch (EntityNotFoundException $e)
2665 {
2666 // ignore invalid eid error
2667 }
2668 }
2669 echo "</h4><table class='widetable zebra' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
2670 echo "<tr><th>prefix</th><th>name/tags</th><th>capacity</th>";
2671 if (getConfigVar ('IPV4_TREE_RTR_AS_CELL') != 'none')
2672 echo "<th>routed by</th>";
2673 echo "</tr>\n";
2674 $baseurl = makeHref(array('page'=>$pageno, 'tab'=>$tabno)) . $cellfilter['urlextra'];
2675 renderIPSpaceRecords ($tree, $baseurl, $eid);
2676 echo "</table>\n";
2677 finishPortlet();
2678 }
2679 }
2680
2681 echo '</td><td class=pcright>';
2682 renderCellFilterPortlet ($cellfilter, $realm, $netlist);
2683 echo "</td></tr></table>\n";
2684 }
2685
2686 function renderIPSpaceEditor()
2687 {
2688 global $pageno;
2689 $realm = ($pageno == 'ipv4space' ? 'ipv4net' : 'ipv6net');
2690 $addrspaceList = listCells ($realm);
2691 startPortlet ('Manage existing (' . count ($addrspaceList) . ')');
2692 if (count ($addrspaceList))
2693 {
2694 echo "<table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
2695 echo "<tr><th>&nbsp;</th><th>prefix</th><th>name</th><th>capacity</th></tr>";
2696 foreach ($addrspaceList as $netinfo)
2697 {
2698 echo "<tr valign=top><td>";
2699 if (! isIPNetworkEmpty ($netinfo))
2700 printImageHREF ('nodestroy', 'There are ' . count ($netinfo['addrlist']) . ' allocations inside');
2701 else
2702 echo getOpLink (array ('op' => 'del', 'id' => $netinfo['id']), '', 'destroy', 'Delete this prefix');
2703 echo '</td><td class=tdleft>' . mkCellA ($netinfo) . '</td>';
2704 echo '<td class=tdleft>' . stringForTD ($netinfo['name']);
2705 if (count ($netinfo['etags']))
2706 echo '<br><small>' . serializeTags ($netinfo['etags']) . '</small>';
2707 echo '</td><td>';
2708 echo getRenderedIPNetCapacity ($netinfo);
2709 echo '</tr>';
2710 }
2711 echo "</table>";
2712 finishPortlet();
2713 }
2714 }
2715
2716 function renderIPNewNetForm ()
2717 {
2718 global $pageno;
2719 if ($pageno == 'ipv6space')
2720 {
2721 $realm = 'ipv6net';
2722 $regexp = '^[a-fA-F0-9:]*:[a-fA-F0-9:\.]*/\d{1,3}$';
2723 }
2724 else
2725 {
2726 $realm = 'ipv4net';
2727 $regexp = '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$';
2728 }
2729
2730 // IP prefix validator
2731 addJS ('js/live_validation.js');
2732 $regexp = addslashes ($regexp);
2733 addJS (<<<END
2734 $(document).ready(function () {
2735 $('form#add input[name="range"]').attr('match', '$regexp');
2736 Validate.init();
2737 });
2738 END
2739 , TRUE);
2740
2741 startPortlet ('Add new');
2742 printOpFormIntro ('add');
2743 echo '<table border=0 cellpadding=5 cellspacing=0 align=center>';
2744
2745 // inputs column
2746 $prefix_value = empty ($_REQUEST['set-prefix']) ? '' : $_REQUEST['set-prefix'];
2747 echo "<th class=tdright>Prefix:</th><td class=tdleft><input type=text name='range' size=36 class='live-validate' value='${prefix_value}'></td>";
2748 echo '<tr><th class=tdright>VLAN:</th><td class=tdleft>';
2749 echo getOptionTree ('vlan_ck', getAllVLANOptions(), array ('select_class' => 'vertical')) . '</td></tr>';
2750 echo "<tr><th class=tdright>Name:</th><td class=tdleft><input type=text name='name' size='20'></td></tr>";
2751 echo '<tr><th class=tdright>Tags:</th><td class="tdleft">';
2752 printTagsPicker ();
2753 echo '</td></tr>';
2754 echo '<tr><td class=tdright><input type=checkbox name="is_connected" id="is_connected"></td>';
2755 echo '<th class=tdleft><label for="is_connected">reserve subnet-router anycast address</label></th></tr>';
2756 echo "<tr><td colspan=2>";
2757 printImageHREF ('CREATE', 'Add a new network', TRUE);
2758 echo '</td></tr>';
2759 echo "</table></form><br><br>\n";
2760 finishPortlet();
2761 }
2762
2763 function getRenderedIPNetBacktrace ($range)
2764 {
2765 if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
2766 return array();
2767
2768 $v = ($range['realm'] == 'ipv4net') ? 4 : 6;
2769 $space = "ipv${v}space"; // ipv4space, ipv6space
2770 $tag = "\$ip${v}netid_"; // $ip4netid_, $ip6netid_
2771
2772 $ret = array();
2773 // Build a backtrace from all parent networks.
2774 $clen = $range['mask'];
2775 $backtrace = array();
2776 $backtrace['&rarr;'] = $range;
2777 $key = '';
2778 while (NULL !== ($upperid = getIPAddressNetworkId ($range['ip_bin'], $clen)))
2779 {
2780 $upperinfo = spotEntity ($range['realm'], $upperid);
2781 $clen = $upperinfo['mask'];
2782 $key .= '&uarr;';
2783 $backtrace[$key] = $upperinfo;
2784 }
2785 foreach (array_reverse ($backtrace) as $arrow => $ainfo)
2786 {
2787 $link = '<a href="' . makeHref (array (
2788 'page' => $space,
2789 'tab' => 'default',
2790 'clear-cf' => '',
2791 'cfe' => '{' . $tag . $ainfo['id'] . '}',
2792 'hl_net' => 1,
2793 'eid' => $range['id'],
2794 )) . '" title="View IP tree with this net as root">' . $arrow . '</a>';
2795 $ret[] = array ($link, getOutputOf ('renderCell', $ainfo));
2796 }
2797 return $ret;
2798 }
2799
2800 function renderIPNetwork ($id)
2801 {
2802 global $pageno;
2803 $realm = $pageno; // 'ipv4net', 'ipv6net'
2804 $range = spotEntity ($realm, $id);
2805 loadIPAddrList ($range);
2806 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
2807 echo "<tr><td colspan=2 align=center><h1>${range['ip']}/${range['mask']}</h1><h2>";
2808 echo htmlspecialchars ($range['name'], ENT_QUOTES, 'UTF-8') . "</h2></td></tr>\n";
2809
2810 echo "<tr><td class=pcleft width='50%'>";
2811
2812 // render summary portlet
2813 $summary = array();
2814 $summary['%% used'] = getRenderedIPNetCapacity ($range);
2815 $summary = getRenderedIPNetBacktrace ($range) + $summary;
2816 if ($realm == 'ipv4net')
2817 {
2818 $summary[] = array ('Netmask:', ip4_format ($range['mask_bin']));
2819 $summary[] = array ('Netmask:', "0x" . strtoupper (implode ('', unpack ('H*', $range['mask_bin']))));
2820 $summary['Wildcard bits'] = ip4_format ( ~ $range['mask_bin']);
2821 }
2822
2823 $reuse_domain = considerConfiguredConstraint ($range, '8021Q_MULTILINK_LISTSRC');
2824 $domainclass = array();
2825 foreach (array_count_values (reduceSubarraysToColumn ($range['8021q'], 'domain_id')) as $domain_id => $vlan_count)
2826 $domainclass[$domain_id] = $vlan_count == 1 ? '' : ($reuse_domain ? '{trwarning}' : '{trerror}');
2827 foreach ($range['8021q'] as $item)
2828 $summary[] = array ($domainclass[$item['domain_id']] . 'VLAN:', formatVLANAsHyperlink (getVlanRow ($item['domain_id'] . '-' . $item['vlan_id'])));
2829 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes' && count ($routers = findNetRouters ($range)))
2830 {
2831 $summary['Routed by'] = '';
2832 foreach ($routers as $rtr)
2833 $summary['Routed by'] .= getOutputOf ('renderRouterCell', $rtr['ip_bin'], $rtr['iface'], spotEntity ('object', $rtr['id']));
2834 }
2835 $summary['tags'] = '';
2836 renderEntitySummary ($range, 'summary', $summary);
2837
2838 if ($range['comment'] != '')
2839 {
2840 startPortlet ('Comment');
2841 echo '<div class=commentblock>' . string_insert_hrefs (htmlspecialchars ($range['comment'], ENT_QUOTES, 'UTF-8')) . '</div>';
2842 finishPortlet ();
2843 }
2844
2845 renderFilesPortlet ($realm, $id);
2846 echo "</td>\n";
2847
2848 echo "<td class=pcright>";
2849 startPortlet ('details');
2850 renderIPNetworkAddresses ($range);
2851 finishPortlet();
2852 echo "</td></tr></table>\n";
2853 }
2854
2855 // Used solely by renderSeparator
2856 function renderEmptyIPv6 ($ip_bin, $hl_ip)
2857 {
2858 $class = 'tdleft';
2859 if ($ip_bin === $hl_ip)
2860 $class .= ' highlight';
2861 $fmt = ip6_format ($ip_bin);
2862 echo "<tr class='$class'><td><a name='ip-$fmt' href='" . makeHref (array ('page' => 'ipaddress', 'ip' => $fmt)) . "'>" . $fmt;
2863 $editable = permitted ('ipaddress', 'properties', 'editAddress')
2864 ? 'editable'
2865 : '';
2866 echo "</a></td><td><span class='rsvtext $editable id-$fmt op-upd-ip-name'></span></td>";
2867 echo "<td><span class='rsvtext $editable id-$fmt op-upd-ip-comment'></span></td><td>&nbsp;</td></tr>\n";
2868 }
2869
2870 // Renders empty table line to shrink empty IPv6 address ranges.
2871 // If the range consists of single address, renders the address instead of empty line.
2872 // Renders address $hl_ip inside the range.
2873 // Used solely by renderIPv6NetworkAddresses
2874 function renderSeparator ($first, $last, $hl_ip)
2875 {
2876 $self = __FUNCTION__;
2877 if (strcmp ($first, $last) > 0)
2878 return;
2879 if ($first == $last)
2880 renderEmptyIPv6 ($first, $hl_ip);
2881 elseif (isset ($hl_ip) && strcmp ($hl_ip, $first) >= 0 && strcmp ($hl_ip, $last) <= 0)
2882 { // $hl_ip is inside the range $first - $last
2883 $self ($first, ip_prev ($hl_ip), $hl_ip);
2884 renderEmptyIPv6 ($hl_ip, $hl_ip);
2885 $self (ip_next ($hl_ip), $last, $hl_ip);
2886 }
2887 else
2888 echo "<tr><td colspan=4 class=tdleft></td></tr>\n";
2889 }
2890
2891 // calculates page number that contains given $ip (used by renderIPv6NetworkAddresses)
2892 function getPageNumOfIPv6 ($list, $ip_bin, $maxperpage)
2893 {
2894 if (intval ($maxperpage) <= 0 || count ($list) <= $maxperpage)
2895 return 0;
2896 $keys = array_keys ($list);
2897 for ($i = 1; $i <= count ($keys); $i++)
2898 if (strcmp ($keys[$i-1], $ip_bin) >= 0)
2899 return intval ($i / $maxperpage);
2900 return intval (count ($list) / $maxperpage);
2901 }
2902
2903 function renderIPNetworkAddresses ($range)
2904 {
2905 switch (strlen ($range['ip_bin']))
2906 {
2907 case 4: return renderIPv4NetworkAddresses ($range);
2908 case 16: return renderIPv6NetworkAddresses ($range);
2909 default: throw new InvalidArgException ("range['ip_bin']", $range['ip_bin']);
2910 }
2911 }
2912
2913 function renderIPv4NetworkPageLink ($rangeid, $page, $title)
2914 {
2915 global $pageno, $tabno;
2916 return "<a href='".makeHref (array ('page' => $pageno, 'tab' => $tabno, 'id' => $rangeid, 'pg' => $page)) . "' title='".$title."'>".$page."</a> ";
2917 }
2918
2919 function renderIPv4NetworkPagination ($range, $page, $numpages)
2920 {
2921 $rendered_pager = '';
2922 $startip = ip4_bin2int ($range['ip_bin']);
2923 $endip = ip4_bin2int (ip_last ($range));
2924 $rangeid = $range['id'];
2925 // Should make this configurable perhaps
2926 // How many pages before/after current page to show
2927 $prepostpagecount = 8;
2928 // Minimum pages where pagination does not happen
2929 $paginationat = 16; // 16 pages is a /20, 32 is a /19
2930 $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
2931 if ($numpages <= $paginationat)
2932 {
2933 // create original pagination
2934 for ($i = 0; $i < $numpages; $i++)
2935 if ($i == $page)
2936 $rendered_pager .= "<b>".$i."</b> ";
2937 else
2938 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2939 }
2940 else // number of pages > page range, create ranged pagination
2941 {
2942 // page is within first subset
2943 if ($page - $prepostpagecount <= 1)
2944 {
2945 for ($i = 0; $i < $page; $i++)
2946 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2947 }