fixup formatting nits after commit 2e912e8
[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 'ssv1' => 'SSV:<interface name> [<MAC address>]',
1852 );
1853 echo 'Format: ' . getSelect ($formats, array ('name' => 'format'), 'ssv1') . ' ';
1854 echo 'Default port type: ';
1855 printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type'), $prefs['selected']);
1856 echo "<input type=submit value='Parse output'><br>\n";
1857 echo "<textarea name=input cols=100 rows=50></textarea><br>\n";
1858 echo '</form>';
1859 finishPortlet();
1860 }
1861
1862 function renderIPForObject ($object_id)
1863 {
1864 function printNewItemTR ($default_type, $object_id)
1865 {
1866 global $aat;
1867
1868 includeJQueryUI (TRUE);
1869
1870 addJS (<<<JSEND
1871 $(document).ready( function() {
1872 $('[name="bond_name"]').autocomplete({
1873 source: "?module=ajax&ac=autocomplete&realm=bond_name&object_id=$object_id",
1874 //minLength: 3,
1875 focus: function(event, ui) {
1876 if( ui.item.value == '' )
1877 event.preventDefault();
1878 },
1879 select: function(event, ui) {
1880 if( ui.item.value == '' )
1881 event.preventDefault();
1882 }
1883 });
1884 });
1885 JSEND
1886 , TRUE);
1887 printOpFormIntro ('add');
1888 echo "<tr><td>"; // left btn
1889 printImageHREF ('add', 'allocate', TRUE);
1890 echo "</td>";
1891 echo "<td class=tdleft><input type='text' size='10' name='bond_name'></td>\n"; // if-name
1892 echo "<td class=tdleft><input type=text name='ip'></td>\n"; // IP
1893 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
1894 echo "<td colspan=2>&nbsp;</td>"; // network, routed by
1895 echo '<td>';
1896 printSelect ($aat, array ('name' => 'bond_type'), $default_type); // type
1897 echo "</td><td>&nbsp;</td><td>"; // misc
1898 printImageHREF ('add', 'allocate', TRUE); // right btn
1899 echo "</td></tr></form>";
1900 }
1901 global $aat;
1902
1903 startPortlet ('Allocations');
1904 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'><tr>\n";
1905 echo '<th>&nbsp;</th>';
1906 echo '<th>OS interface</th>';
1907 echo '<th>IP address</th>';
1908 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
1909 {
1910 echo '<th>network</th>';
1911 echo '<th>routed by</th>';
1912 }
1913 echo '<th>type</th>';
1914 echo '<th>misc</th>';
1915 echo '<th>&nbsp;</th>';
1916 echo '</tr>';
1917
1918 $alloc_list = ''; // most of the output is stored here
1919 $used_alloc_types = array();
1920 foreach (getObjectIPAllocations ($object_id) as $alloc)
1921 {
1922 if (! isset ($used_alloc_types[$alloc['type']]))
1923 $used_alloc_types[$alloc['type']] = 0;
1924 $used_alloc_types[$alloc['type']]++;
1925
1926 $rendered_alloc = callHook ('getRenderedAlloc', $object_id, $alloc);
1927 $alloc_list .= getOutputOf ('printOpFormIntro', 'upd', array ('ip' => $alloc['addrinfo']['ip']));
1928 $alloc_list .= "<tr class='${rendered_alloc['tr_class']}' valign=top>";
1929
1930 $alloc_list .= "<td>" . getOpLink (array ('op' => 'del', 'ip' => $alloc['addrinfo']['ip']), '', 'delete', 'Delete this IP address') . "</td>";
1931 $alloc_list .= "<td class=tdleft><input type='text' name='bond_name' value='${alloc['osif']}' size=10>" . $rendered_alloc['td_name_suffix'] . "</td>";
1932 $alloc_list .= $rendered_alloc['td_ip'];
1933 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
1934 {
1935 $alloc_list .= $rendered_alloc['td_network'];
1936 $alloc_list .= $rendered_alloc['td_routed_by'];
1937 }
1938 $alloc_list .= '<td>' . getSelect ($aat, array ('name' => 'bond_type'), $alloc['type']) . "</td>";
1939 $alloc_list .= $rendered_alloc['td_peers'];
1940 $alloc_list .= "<td>" .getImageHREF ('save', 'Save changes', TRUE) . "</td>";
1941
1942 $alloc_list .= "</form></tr>\n";
1943 }
1944 asort ($used_alloc_types, SORT_NUMERIC);
1945 $most_popular_type = ! count ($used_alloc_types) ? 'regular' : array_last (array_keys ($used_alloc_types));
1946
1947 if ($list_on_top = (getConfigVar ('ADDNEW_AT_TOP') != 'yes'))
1948 echo $alloc_list;
1949 printNewItemTR ($most_popular_type, $object_id);
1950 if (! $list_on_top)
1951 echo $alloc_list;
1952
1953 echo "</table><br>\n";
1954 finishPortlet();
1955 }
1956
1957 // This function is deprecated. Do not rely on its internals,
1958 // it will probably be removed in the next major relese.
1959 // Use new showError, showWarning, showSuccess functions.
1960 // Log array is stored in global $log_messages. Its format is simple: plain ordered array
1961 // with values having keys 'c' (both message code and severity) and 'a' (sprintf arguments array)
1962 function showMessageOrError ()
1963 {
1964 global $log_messages;
1965
1966 startSession();
1967 if (isset ($_SESSION['log']))
1968 {
1969 $log_messages = array_merge ($_SESSION['log'], $log_messages);
1970 unset ($_SESSION['log']);
1971 }
1972 session_commit();
1973
1974 if (empty ($log_messages))
1975 return;
1976 $msginfo = array
1977 (
1978 // records 0~99 with success messages
1979 0 => array ('code' => 'success', 'format' => '%s'),
1980 5 => array ('code' => 'success', 'format' => 'added record "%s" successfully'),
1981 6 => array ('code' => 'success', 'format' => 'updated record "%s" successfully'),
1982 7 => array ('code' => 'success', 'format' => 'deleted record "%s" successfully'),
1983 8 => array ('code' => 'success', 'format' => 'Port %s successfully linked with %s'),
1984 10 => array ('code' => 'success', 'format' => 'Added %u port(s), updated %u port(s), encountered %u error(s).'),
1985 21 => array ('code' => 'success', 'format' => 'Generation complete'),
1986 26 => array ('code' => 'success', 'format' => 'updated %u record(s) successfully'),
1987 37 => array ('code' => 'success', 'format' => 'added %u record(s) successfully'),
1988 38 => array ('code' => 'success', 'format' => 'removed %u record(s) successfully'),
1989 43 => array ('code' => 'success', 'format' => 'Saved successfully.'),
1990 44 => array ('code' => 'success', 'format' => '%s failure(s) and %s successfull change(s).'),
1991 48 => array ('code' => 'success', 'format' => 'added a record successfully'),
1992 49 => array ('code' => 'success', 'format' => 'deleted a record successfully'),
1993 51 => array ('code' => 'success', 'format' => 'updated a record successfully'),
1994 57 => array ('code' => 'success', 'format' => 'Reset complete'),
1995 58 => array ('code' => 'success', 'format' => '%u device(s) unmounted successfully'),
1996 63 => array ('code' => 'success', 'format' => '%u change request(s) have been processed'),
1997 67 => array ('code' => 'success', 'format' => "Tag rolling done, %u object(s) involved"),
1998 71 => array ('code' => 'success', 'format' => 'File "%s" was linked successfully'),
1999 72 => array ('code' => 'success', 'format' => 'File was unlinked successfully'),
2000 82 => array ('code' => 'success', 'format' => "Bulk port creation was successful. %u port(s) created, %u failed"),
2001 87 => array ('code' => 'success', 'format' => '802.1Q recalculate: %d port(s) changed on %d switch(es)'),
2002 // records 100~199 with fatal error messages
2003 100 => array ('code' => 'error', 'format' => '%s'),
2004 109 => array ('code' => 'error', 'format' => 'failed updating a record'),
2005 131 => array ('code' => 'error', 'format' => 'invalid format requested'),
2006 141 => array ('code' => 'error', 'format' => 'Encountered %u error(s), updated %u record(s)'),
2007 149 => array ('code' => 'error', 'format' => 'Turing test failed'),
2008 150 => array ('code' => 'error', 'format' => 'Can only change password under DB authentication.'),
2009 151 => array ('code' => 'error', 'format' => 'Old password doesn\'t match.'),
2010 152 => array ('code' => 'error', 'format' => 'New passwords don\'t match.'),
2011 154 => array ('code' => 'error', 'format' => "Verification error: %s"),
2012 155 => array ('code' => 'error', 'format' => 'Save failed.'),
2013 159 => array ('code' => 'error', 'format' => 'Permission denied moving port %s from VLAN%u to VLAN%u'),
2014 170 => array ('code' => 'error', 'format' => 'There is no network for IP address "%s"'),
2015 172 => array ('code' => 'error', 'format' => 'Malformed request'),
2016 179 => array ('code' => 'error', 'format' => 'Expired form has been declined.'),
2017 191 => array ('code' => 'error', 'format' => "deploy was blocked due to conflicting configuration versions"),
2018
2019 // records 200~299 with warnings
2020 200 => array ('code' => 'warning', 'format' => '%s'),
2021 201 => array ('code' => 'warning', 'format' => 'nothing happened...'),
2022 206 => array ('code' => 'warning', 'format' => '%s is not empty'),
2023 207 => array ('code' => 'warning', 'format' => 'File upload failed, error: %s'),
2024
2025 // records 300~399 with notices
2026 300 => array ('code' => 'neutral', 'format' => '%s'),
2027
2028 );
2029 // Handle the arguments. Is there any better way to do it?
2030 foreach ($log_messages as $record)
2031 {
2032 if (! isset ($record['c']) || ! isset ($msginfo[$record['c']]))
2033 {
2034 $prefix = isset ($record['c']) ? $record['c'] . ': ' : '';
2035 echo "<div class=msg_neutral>(${prefix}this message was lost)</div>";
2036 continue;
2037 }
2038 if (isset ($record['a']))
2039 switch (count ($record['a']))
2040 {
2041 case 1:
2042 $msgtext = sprintf
2043 (
2044 $msginfo[$record['c']]['format'],
2045 $record['a'][0]
2046 );
2047 break;
2048 case 2:
2049 $msgtext = sprintf
2050 (
2051 $msginfo[$record['c']]['format'],
2052 $record['a'][0],
2053 $record['a'][1]
2054 );
2055 break;
2056 case 3:
2057 $msgtext = sprintf
2058 (
2059 $msginfo[$record['c']]['format'],
2060 $record['a'][0],
2061 $record['a'][1],
2062 $record['a'][2]
2063 );
2064 break;
2065 case 4:
2066 default:
2067 $msgtext = sprintf
2068 (
2069 $msginfo[$record['c']]['format'],
2070 $record['a'][0],
2071 $record['a'][1],
2072 $record['a'][2],
2073 $record['a'][3]
2074 );
2075 break;
2076 }
2077 else
2078 $msgtext = $msginfo[$record['c']]['format'];
2079 echo '<div class=msg_' . $msginfo[$record['c']]['code'] . ">${msgtext}</div>";
2080 }
2081 $log_messages = array();
2082 }
2083
2084 // renders two tables: port link status and learned MAC list
2085 function renderPortsInfo($object_id)
2086 {
2087 $breed = detectDeviceBreed ($object_id);
2088 try
2089 {
2090 if (permitted (NULL, NULL, 'get_link_status'))
2091 {
2092 if (validBreedFunction ($breed, 'getportstatus'))
2093 $linkStatus = queryDevice ($object_id, 'getportstatus');
2094 }
2095 else
2096 showWarning ("You don't have permission to view ports link status");
2097
2098 if (permitted (NULL, NULL, 'get_mac_list'))
2099 {
2100 if (validBreedFunction ($breed, 'getmaclist'))
2101 $macList = sortPortList (queryDevice ($object_id, 'getmaclist'));
2102 }
2103 else
2104 showWarning ("You don't have permission to view learned MAC list");
2105 }
2106 catch (RTGatewayError $e)
2107 {
2108 showError ($e->getMessage());
2109 return;
2110 }
2111
2112 global $nextorder;
2113 echo "<table width='100%'><tr>";
2114 if (! empty ($linkStatus))
2115 {
2116 echo "<td valign='top' width='50%'>";
2117 startPortlet('Link status');
2118 echo "<table width='80%' class='widetable' cellspacing=0 cellpadding='5px' align='center'>";
2119 echo '<tr><th>Port</th><th>&nbsp;</th><th>Link status</th><th>Link info</th></tr>';
2120 $statusmap = array
2121 (
2122 'up' => 'link up',
2123 'down' => 'link down',
2124 'disabled' => 'link disabled',
2125 );
2126 $order = 'even';
2127 foreach ($linkStatus as $pn => $link)
2128 {
2129 echo "<tr class='row_$order'>";
2130 $order = $nextorder[$order];
2131 echo '<td>' . $pn . '</td>';
2132 echo '<td>' . getImageHREF (array_fetch ($statusmap, $link['status'], '16x16t')) . '</td>';
2133 echo '<td>' . $link['status'] . '</td>';
2134 $info = '';
2135 if (isset ($link['speed']))
2136 $info .= $link['speed'];
2137 if (isset ($link['duplex']))
2138 {
2139 if ($info != '')
2140 $info .= ', ';
2141 $info .= $link['duplex'];
2142 }
2143 echo '<td>' . $info . '</td>';
2144 echo '</tr>';
2145 }
2146 echo "</table></td>";
2147 finishPortlet();
2148 }
2149
2150 if (! empty ($macList))
2151 {
2152 echo "<td valign='top' width='50%'>";
2153 $rendered_macs = '';
2154 $mac_count = 0;
2155 $rendered_macs .= "<table width='80%' class='widetable' cellspacing=0 cellpadding='5px' align='center'>";
2156 $rendered_macs .= '<tr><th>MAC address</th><th>VLAN</th><th>Port</th></tr>';
2157 $order = 'even';
2158 foreach ($macList as $pn => $list)
2159 {
2160 $order = $nextorder[$order];
2161 foreach ($list as $item)
2162 {
2163 ++$mac_count;
2164 $rendered_macs .= "<tr class='row_$order'>";
2165 $rendered_macs .= '<td style="font-family: monospace">' . $item['mac'] . '</td>';
2166 $rendered_macs .= '<td>' . $item['vid'] . '</td>';
2167 $rendered_macs .= '<td>' . $pn . '</td>';
2168 $rendered_macs .= '</tr>';
2169 }
2170 }
2171 $rendered_macs .= "</table></td>";
2172
2173 startPortlet("Learned MACs ($mac_count)");
2174 echo $rendered_macs;
2175 finishPortlet();
2176 }
2177
2178 echo "</td></tr></table>";
2179 }
2180
2181 /*
2182 The following conditions must be met:
2183 1. We can mount onto free atoms only. This means: if any record for an atom
2184 already exists in RackSpace, it can't be used for mounting.
2185 2. We can't unmount from 'W' atoms. Operator should review appropriate comments
2186 and either delete them before unmounting or refuse to unmount the object.
2187 */
2188 function renderRackSpaceForObject ($object_id)
2189 {
2190 // Always process occupied racks plus racks chosen by user. First get racks with
2191 // already allocated rackspace...
2192 $workingRacksData = getResidentRacksData ($object_id);
2193 // ...and then add those chosen by user (if any).
2194 if (isset($_REQUEST['rackmulti']))
2195 foreach ($_REQUEST['rackmulti'] as $cand_id)
2196 if (!isset ($workingRacksData[$cand_id]))
2197 {
2198 $rackData = spotEntity ('rack', $cand_id);
2199 amplifyCell ($rackData);
2200 $workingRacksData[$cand_id] = $rackData;
2201 }
2202
2203 // Get a list of rack ids that are parents of this object.
2204 $object = spotEntity ('object', $object_id);
2205 $parentRacks = reduceSubarraysToColumn (getParents ($object, 'rack'), 'id');
2206
2207 // Main layout starts.
2208 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0><tr>";
2209
2210 // Left portlet with rack list.
2211 echo "<td class=pcleft height='1%'>";
2212 startPortlet ('Racks');
2213 $allRacksData = listCells ('rack');
2214
2215 // filter rack list to match only racks having common tags with the object (reducing $allRacksData)
2216 if (! isset ($_REQUEST['show_all_racks']) && getConfigVar ('FILTER_RACKLIST_BY_TAGS') == 'yes')
2217 {
2218 $matching_racks = array();
2219 $matched_tags = array();
2220 foreach ($allRacksData as $rack)
2221 {
2222 $tag_chain = array_merge ($rack['etags'], $rack['itags']);
2223 foreach ($object['etags'] as $tag)
2224 if (tagOnChain ($tag, $tag_chain))
2225 {
2226 $matching_racks[$rack['id']] = $rack;
2227 $matched_tags[$tag['id']] = $tag;
2228 break;
2229 }
2230 }
2231 // add current object's racks even if they dont match filter
2232 foreach ($workingRacksData as $rack_id => $rack)
2233 if (! isset ($matching_racks[$rack_id]))
2234 $matching_racks[$rack_id] = $rack;
2235 // if matching racks found, and rack list is reduced, show 'show all' link
2236 if (count ($matching_racks) && count ($matching_racks) != count ($allRacksData))
2237 {
2238 $tmp = array();
2239 foreach ($matched_tags as $tag)
2240 $tmp[] = '{' . $tag['tag'] . '}';
2241 $filter_text = implode (' or ', $tmp);
2242 $href_show_all = trim($_SERVER['REQUEST_URI'], '&');
2243 $href_show_all .= htmlspecialchars('&show_all_racks=1');
2244 echo "(filtered by <span class='filter-text'>$filter_text</span>, <a href='$href_show_all'>show all</a>)<p>";
2245 $allRacksData = $matching_racks;
2246 }
2247 }
2248
2249 if (count ($allRacksData) <= getConfigVar ('RACK_PRESELECT_THRESHOLD'))
2250 foreach ($allRacksData as $rack)
2251 if (!array_key_exists ($rack['id'], $workingRacksData))
2252 {
2253 amplifyCell ($rack);
2254 $workingRacksData[$rack['id']] = $rack;
2255 }
2256 foreach (array_keys ($workingRacksData) as $rackId)
2257 applyObjectMountMask ($workingRacksData[$rackId], $object_id);
2258 printOpFormIntro ('updateObjectAllocation');
2259 renderRackMultiSelect ('rackmulti[]', $allRacksData, array_keys ($workingRacksData));
2260 echo "<br><br>";
2261 finishPortlet();
2262 echo "</td>";
2263
2264 // Middle portlet with comment and submit.
2265 echo "<td class=pcleft>";
2266 startPortlet ('Comment (for Rackspace History)');
2267 echo "<textarea name=comment rows=10 cols=40></textarea><br>\n";
2268 echo "<input type=submit value='Save' name=got_atoms>\n";
2269 echo "<br><br>";
2270 finishPortlet();
2271 echo "</td>";
2272
2273 // Right portlet with rendered racks. If this form submit is not final, we have to
2274 // reflect the former state of the grid in current form.
2275 echo "<td class=pcright rowspan=2 height='1%'>";
2276 startPortlet ('Working copy');
2277 includeJQueryUI (false);
2278 addJS ('js/racktables.js');
2279 addJS ('js/bulkselector.js');
2280 echo '<table border=0 cellspacing=10 align=center><tr>';
2281 foreach ($workingRacksData as $rack_id => $rackData)
2282 {
2283 $is_ro = !rackModificationPermitted ($rackData, 'updateObjectAllocation');
2284 // Order is important here: only original allocation is highlighted.
2285 highlightObject ($rackData, $object_id);
2286 markupAtomGrid ($rackData, 'T');
2287 // If we have a form processed, discard user input and show new database
2288 // contents.
2289 if (!$is_ro && isset ($_REQUEST['rackmulti'][0])) // is an update
2290 mergeGridFormToRack ($rackData);
2291 echo "<td valign=top>";
2292 echo "<center>\n<h2 style='margin:0px'>${rackData['name']}</h2>\n";
2293 $read_only_text = $is_ro ? '(read-only)' : '&nbsp;';
2294 echo "<p style='color: red; margin-top:0px'>${read_only_text}</p>\n";
2295 echo "<table class=rack id=selectableRack border=0 cellspacing=0 cellpadding=1>\n";
2296 echo "<tr><th width='10%'>&nbsp;</th>";
2297 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
2298 echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
2299 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
2300 renderAtomGrid ($rackData, $is_ro);
2301 echo "<tr><th width='10%'>&nbsp;</th>";
2302 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
2303 echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
2304 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
2305 echo "</table>\n<br>\n";
2306 // Determine zero-u checkbox status.
2307 // If form has been submitted, use form data, otherwise use DB data.
2308 if (!$is_ro && isset($_REQUEST['op']))
2309 $checked = isset($_REQUEST['zerou_'.$rack_id]) ? 'checked' : '';
2310 else
2311 $checked = in_array($rack_id, $parentRacks) ? 'checked' : '';
2312 $disabled_text = $is_ro ? ' disabled' : '';
2313 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";
2314 echo "<input type='button' onclick='uncheckAll();' value='Uncheck all'>\n";
2315 echo '</center></td>';
2316 }
2317 echo "</tr></table>";
2318 finishPortlet();
2319 echo "</td>\n";
2320
2321 echo "</form>\n";
2322 echo "</tr></table>\n";
2323 }
2324
2325 function renderMolecule ($mdata, $object_id)
2326 {
2327 // sort data out
2328 $rackpack = array();
2329 global $loclist;
2330 foreach ($mdata as $rua)
2331 {
2332 $rack_id = $rua['rack_id'];
2333 $unit_no = $rua['unit_no'];
2334 $atom = $rua['atom'];
2335 if (!isset ($rackpack[$rack_id]))
2336 {
2337 $rackData = spotEntity ('rack', $rack_id);
2338 amplifyCell ($rackData);
2339 for ($i = $rackData['height']; $i > 0; $i--)
2340 for ($locidx = 0; $locidx < 3; $locidx++)
2341 $rackData[$i][$locidx]['state'] = 'F';
2342 $rackpack[$rack_id] = $rackData;
2343 }
2344 $rackpack[$rack_id][$unit_no][$loclist[$atom]]['state'] = 'T';
2345 $rackpack[$rack_id][$unit_no][$loclist[$atom]]['object_id'] = $object_id;
2346 }
2347 // now we have some racks to render
2348 foreach ($rackpack as $rackData)
2349 {
2350 markAllSpans ($rackData);
2351 echo "<table class=molecule cellspacing=0>\n";
2352 echo "<caption>${rackData['name']}</caption>\n";
2353 echo "<tr><th width='10%'>&nbsp;</th><th width='20%'>Front</th><th width='50%'>Interior</th><th width='20%'>Back</th></tr>\n";
2354 for ($i = $rackData['height']; $i > 0; $i--)
2355 {
2356 echo "<tr><th>" . inverseRackUnit ($i, $rackData) . "</th>";
2357 for ($locidx = 0; $locidx < 3; $locidx++)
2358 {
2359 $state = $rackData[$i][$locidx]['state'];
2360 echo "<td class='atom state_${state}'>&nbsp;</td>\n";
2361 }
2362 echo "</tr>\n";
2363 }
2364 echo "</table>\n";
2365 }
2366 }
2367
2368 function renderDepot ()
2369 {
2370 global $pageno, $nextorder;
2371 $cellfilter = getCellFilter();
2372 $objects = array();
2373 $objects_count = getEntitiesCount ('object');
2374 $showobjecttype = (getConfigVar ('SHOW_OBJECTTYPE') == 'yes');
2375
2376 echo "<table border=0 class=objectview>\n";
2377 echo "<tr><td class=pcleft>";
2378
2379 if ($objects_count == 0)
2380 echo '<h2>No objects exist</h2>';
2381 // 1st attempt: do not fetch all objects if cellfilter is empty and rendering empty result is enabled
2382 elseif (! ($cellfilter['is_empty'] && renderEmptyResults ($cellfilter, 'objects', $objects_count)))
2383 {
2384 $objects = applyCellFilter ('object', $cellfilter);
2385 // 2nd attempt: do not render all fetched objects if rendering empty result is enabled
2386 if (! renderEmptyResults ($cellfilter, 'objects', count($objects)))
2387 {
2388 startPortlet ('Objects (' . count ($objects) . ')');
2389 echo '<br><br><table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
2390 echo '<tr><th>Common name</th>';
2391 if ($showobjecttype)
2392 echo '<th>Type</th>';
2393 echo '<th>Visible label</th><th>Asset tag</th><th>Row/Rack or Container</th></tr>';
2394 $order = 'odd';
2395 # gather IDs of all objects and fetch rackspace info in one pass
2396 $idlist = array();
2397 foreach ($objects as $obj)
2398 $idlist[] = $obj['id'];
2399 $mountinfo = getMountInfo ($idlist);
2400 $containerinfo = getContainerInfo ($idlist);
2401 foreach ($objects as $obj)
2402 {
2403 $problem = ($obj['has_problems'] == 'yes') ? 'has_problems' : '';
2404 echo "<tr class='row_${order} tdleft ${problem}' valign=top><td>" . mkA ("<strong>${obj['dname']}</strong>", 'object', $obj['id']);
2405 if (count ($obj['etags']))
2406 echo '<br><small>' . serializeTags ($obj['etags'], makeHref(array('page'=>$pageno, 'tab'=>'default')) . '&') . '</small>';
2407 echo '</td>';
2408 if ($showobjecttype)
2409 echo "<td>" . decodeObjectType ($obj['objtype_id']) . "</td>";
2410 echo "<td>${obj['label']}</td>";
2411 echo "<td>${obj['asset_no']}</td>";
2412 $places = array();
2413 if (array_key_exists ($obj['id'], $containerinfo))
2414 foreach ($containerinfo[$obj['id']] as $ci)
2415 $places[] = mkA ($ci['container_dname'], 'object', $ci['container_id']);
2416 if (array_key_exists ($obj['id'], $mountinfo))
2417 foreach ($mountinfo[$obj['id']] as $mi)
2418 $places[] = mkA ($mi['row_name'], 'row', $mi['row_id']) . '/' . mkA ($mi['rack_name'], 'rack', $mi['rack_id']);
2419 if (! count ($places))
2420 $places[] = 'Unmounted';
2421 echo "<td>" . implode (', ', $places) . '</td>';
2422 echo '</tr>';
2423 $order = $nextorder[$order];
2424 }
2425 echo '</table>';
2426 finishPortlet();
2427 }
2428 }
2429
2430 echo "</td><td class=pcright width='25%'>";
2431
2432 renderCellFilterPortlet ($cellfilter, 'object', $objects);
2433 echo "</td></tr></table>\n";
2434 }
2435
2436 // This function returns TRUE if the result set is too big to be rendered, and no filter is set.
2437 // In this case it renders the describing message instead.
2438 function renderEmptyResults($cellfilter, $entities_name, $count = NULL)
2439 {
2440 if (!$cellfilter['is_empty'])
2441 return FALSE;
2442 if (isset ($_REQUEST['show_all_objects']))
2443 return FALSE;
2444 $max = intval(getConfigVar('MAX_UNFILTERED_ENTITIES'));
2445 if (0 == $max || $count <= $max)
2446 return FALSE;
2447
2448 $href_show_all = trim($_SERVER['REQUEST_URI'], '&');
2449 $href_show_all .= htmlspecialchars('&show_all_objects=1');
2450 $suffix = isset ($count) ? " ($count)" : '';
2451 echo <<<END
2452 <p>Please set a filter to display the corresponging $entities_name.
2453 <br><a href="$href_show_all">Show all $entities_name$suffix</a>
2454 END;
2455 return TRUE;
2456 }
2457
2458 // History viewer for history-enabled simple dictionaries.
2459 function renderObjectHistory ($object_id)
2460 {
2461 $order = 'odd';
2462 global $nextorder;
2463 echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
2464 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>';
2465 $result = usePreparedSelectBlade
2466 (
2467 'SELECT ctime, user_name, name, label, asset_no, has_problems, comment FROM ObjectHistory WHERE id=? ORDER BY ctime',
2468 array ($object_id)
2469 );
2470 while ($row = $result->fetch (PDO::FETCH_NUM))
2471 {
2472 echo "<tr class=row_${order}><td>${row[0]}</td>";
2473 for ($i = 1; $i <= 6; $i++)
2474 echo "<td>" . $row[$i] . "</td>";
2475 echo "</tr>\n";
2476 $order = $nextorder[$order];
2477 }
2478 echo "</table><br>\n";
2479 }
2480
2481 function renderRackspaceHistory ()
2482 {
2483 global $nextorder, $pageno, $tabno;
2484 $order = 'odd';
2485 $history = getRackspaceHistory();
2486 // Show the last operation by default.
2487 if (isset ($_REQUEST['op_id']))
2488 $op_id = $_REQUEST['op_id'];
2489 elseif (isset ($history[0]['mo_id']))
2490 $op_id = $history[0]['mo_id'];
2491 else $op_id = NULL;
2492
2493 $omid = NULL;
2494 $nmid = NULL;
2495 $object_id = 1;
2496 if ($op_id)
2497 list ($omid, $nmid) = getOperationMolecules ($op_id);
2498
2499 // Main layout starts.
2500 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
2501
2502 // Left top portlet with old allocation.
2503 echo "<tr><td class=pcleft>";
2504 startPortlet ('Old allocation');
2505 if ($omid)
2506 {
2507 $oldMolecule = getMolecule ($omid);
2508 renderMolecule ($oldMolecule, $object_id);
2509 }
2510 else
2511 echo "nothing";
2512 finishPortlet();
2513
2514 echo '</td><td class=pcright>';
2515
2516 // Right top portlet with new allocation
2517 startPortlet ('New allocation');
2518 if ($nmid)
2519 {
2520 $newMolecule = getMolecule ($nmid);
2521 renderMolecule ($newMolecule, $object_id);
2522 }
2523 else
2524 echo "nothing";
2525 finishPortlet();
2526
2527 echo '</td></tr><tr><td colspan=2>';
2528
2529 // Bottom portlet with list
2530
2531 startPortlet ('Rackspace allocation history');
2532 echo "<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>\n";
2533 echo "<tr><th>timestamp</th><th>author</th><th>object</th><th>comment</th></tr>\n";
2534 foreach ($history as $row)
2535 {
2536 if ($row['mo_id'] == $op_id)
2537 $class = 'hl';
2538 else
2539 $class = "row_${order}";
2540 echo "<tr class=${class}><td><a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'op_id'=>$row['mo_id']))."'>${row['ctime']}</a></td>";
2541 echo "<td>${row['user_name']}</td><td>";
2542 renderCell (spotEntity ('object', $row['ro_id']));
2543 echo '</td><td>' . stringForTD ($row['comment'], 0) . '</td></tr>';
2544 $order = $nextorder[$order];
2545 }
2546 echo "</table>\n";
2547 finishPortlet();
2548
2549 echo '</td></tr></table>';
2550 }
2551
2552 function renderIPSpaceRecords ($tree, $baseurl, $target = 0, $level = 1)
2553 {
2554 $self = __FUNCTION__;
2555 $knight = (getConfigVar ('IPV4_ENABLE_KNIGHT') == 'yes');
2556 $display_routers = (getConfigVar ('IPV4_TREE_RTR_AS_CELL') != 'none');
2557
2558 // scroll page to the highlighted item
2559 if ($target && isset ($_REQUEST['hl_net']))
2560 addAutoScrollScript ("net-$target");
2561
2562 foreach ($tree as $item)
2563 {
2564 if (isset ($item['id']))
2565 {
2566 $decor = array ('indent' => $level);
2567 if ($item['symbol'] == 'node-collapsed')
2568 $decor['symbolurl'] = "${baseurl}&eid=${item['id']}&hl_net=1";
2569 elseif ($item['symbol'] == 'node-expanded')
2570 $decor['symbolurl'] = $baseurl . ($item['parent_id'] ? "&eid=${item['parent_id']}&hl_net=1" : '');
2571 $tr_class = '';
2572 if ($target == $item['id'] && isset ($_REQUEST['hl_net']))
2573 {
2574 $decor['tdclass'] = ' highlight';
2575 $tr_class = $decor['tdclass'];
2576 }
2577 echo "<tr valign=top class=\"$tr_class\">";
2578 printIPNetInfoTDs ($item, $decor);
2579
2580 // capacity and usage
2581 echo "<td class=tdcenter>";
2582 echo getRenderedIPNetCapacity ($item);
2583 echo "</td>";
2584
2585 if ($display_routers)
2586 printRoutersTD (findNetRouters ($item), getConfigVar ('IPV4_TREE_RTR_AS_CELL'));
2587 echo "</tr>";
2588 if ($item['symbol'] == 'node-expanded' || $item['symbol'] == 'node-expanded-static')
2589 $self ($item['kids'], $baseurl, $target, $level + 1);
2590 }
2591 elseif (getConfigVar ('IPV4_TREE_SHOW_UNALLOCATED') == 'yes')
2592 {
2593 // non-allocated (spare) IP range
2594 echo "<tr valign=top>";
2595 printIPNetInfoTDs ($item, array ('indent' => $level, 'knight' => $knight, 'tdclass' => 'sparenetwork'));
2596
2597 // capacity and usage
2598 echo "<td class=tdcenter>";
2599 echo getRenderedIPNetCapacity ($item);
2600 echo "</td>";
2601 if ($display_routers)
2602 echo "<td></td>";
2603 echo "</tr>";
2604 }
2605 }
2606 }
2607
2608 function renderIPSpace()
2609 {
2610 global $pageno, $tabno;
2611 $realm = ($pageno == 'ipv4space' ? 'ipv4net' : 'ipv6net');
2612 $cellfilter = getCellFilter();
2613
2614 // expand request can take either natural values or "ALL". Zero means no expanding.
2615 $eid = isset ($_REQUEST['eid']) ? $_REQUEST['eid'] : 0;
2616
2617 echo "<table border=0 class=objectview>\n";
2618 echo "<tr><td class=pcleft>";
2619
2620 $netlist = array();
2621 if (! ($cellfilter['is_empty'] && ! $eid && renderEmptyResults($cellfilter, 'IP nets', getEntitiesCount ($realm))))
2622 {
2623 $top = NULL;
2624 foreach (listCells ($realm) as $net)
2625 {
2626 if (isset ($top) && IPNetContains ($top, $net))
2627 ;
2628 elseif (! count ($cellfilter['expression']) || judgeCell ($net, $cellfilter['expression']))
2629 $top = $net;
2630 else
2631 continue;
2632 $netlist[$net['id']] = $net;
2633 }
2634 $netcount = count ($netlist);
2635 $tree = prepareIPTree ($netlist, $eid);
2636
2637 if (! renderEmptyResults($cellfilter, 'IP nets', count($tree)))
2638 {
2639 startPortlet ("networks (${netcount})");
2640 echo '<h4>';
2641 $all = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'ALL')) .
2642 $cellfilter['urlextra'] . "'>expand&nbsp;all</a>";
2643 $none = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'NONE')) .
2644 $cellfilter['urlextra'] . "'>collapse&nbsp;all</a>";
2645 $auto = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno)) .
2646 $cellfilter['urlextra'] . "'>auto-collapse</a>";
2647
2648 if ($eid === 0)
2649 echo 'auto-collapsing at threshold ' . getConfigVar ('TREE_THRESHOLD') . " ($all / $none)";
2650 elseif ($eid === 'ALL')
2651 echo "expanding all ($auto / $none)";
2652 elseif ($eid === 'NONE')
2653 echo "collapsing all ($all / $auto)";
2654 else
2655 {
2656 try
2657 {
2658 $netinfo = spotEntity ($realm, $eid);
2659 echo "expanding ${netinfo['ip']}/${netinfo['mask']} ($auto / $all / $none)";
2660 }
2661 catch (EntityNotFoundException $e)
2662 {
2663 // ignore invalid eid error
2664 }
2665 }
2666 echo "</h4><table class='widetable zebra' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
2667 echo "<tr><th>prefix</th><th>name/tags</th><th>capacity</th>";
2668 if (getConfigVar ('IPV4_TREE_RTR_AS_CELL') != 'none')
2669 echo "<th>routed by</th>";
2670 echo "</tr>\n";
2671 $baseurl = makeHref(array('page'=>$pageno, 'tab'=>$tabno)) . $cellfilter['urlextra'];
2672 renderIPSpaceRecords ($tree, $baseurl, $eid);
2673 echo "</table>\n";
2674 finishPortlet();
2675 }
2676 }
2677
2678 echo '</td><td class=pcright>';
2679 renderCellFilterPortlet ($cellfilter, $realm, $netlist);
2680 echo "</td></tr></table>\n";
2681 }
2682
2683 function renderIPSpaceEditor()
2684 {
2685 global $pageno;
2686 $realm = ($pageno == 'ipv4space' ? 'ipv4net' : 'ipv6net');
2687 $addrspaceList = listCells ($realm);
2688 startPortlet ('Manage existing (' . count ($addrspaceList) . ')');
2689 if (count ($addrspaceList))
2690 {
2691 echo "<table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
2692 echo "<tr><th>&nbsp;</th><th>prefix</th><th>name</th><th>capacity</th></tr>";
2693 foreach ($addrspaceList as $netinfo)
2694 {
2695 echo "<tr valign=top><td>";
2696 if (! isIPNetworkEmpty ($netinfo))
2697 printImageHREF ('nodestroy', 'There are ' . count ($netinfo['addrlist']) . ' allocations inside');
2698 else
2699 echo getOpLink (array ('op' => 'del', 'id' => $netinfo['id']), '', 'destroy', 'Delete this prefix');
2700 echo '</td><td class=tdleft>' . mkCellA ($netinfo) . '</td>';
2701 echo '<td class=tdleft>' . stringForTD ($netinfo['name']);
2702 if (count ($netinfo['etags']))
2703 echo '<br><small>' . serializeTags ($netinfo['etags']) . '</small>';
2704 echo '</td><td>';
2705 echo getRenderedIPNetCapacity ($netinfo);
2706 echo '</tr>';
2707 }
2708 echo "</table>";
2709 finishPortlet();
2710 }
2711 }
2712
2713 function renderIPNewNetForm ()
2714 {
2715 global $pageno;
2716 if ($pageno == 'ipv6space')
2717 {
2718 $realm = 'ipv6net';
2719 $regexp = '^[a-fA-F0-9:]*:[a-fA-F0-9:\.]*/\d{1,3}$';
2720 }
2721 else
2722 {
2723 $realm = 'ipv4net';
2724 $regexp = '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$';
2725 }
2726
2727 // IP prefix validator
2728 addJS ('js/live_validation.js');
2729 $regexp = addslashes ($regexp);
2730 addJS (<<<END
2731 $(document).ready(function () {
2732 $('form#add input[name="range"]').attr('match', '$regexp');
2733 Validate.init();
2734 });
2735 END
2736 , TRUE);
2737
2738 startPortlet ('Add new');
2739 printOpFormIntro ('add');
2740 echo '<table border=0 cellpadding=5 cellspacing=0 align=center>';
2741
2742 // inputs column
2743 $prefix_value = empty ($_REQUEST['set-prefix']) ? '' : $_REQUEST['set-prefix'];
2744 echo "<th class=tdright>Prefix:</th><td class=tdleft><input type=text name='range' size=36 class='live-validate' value='${prefix_value}'></td>";
2745 echo '<tr><th class=tdright>VLAN:</th><td class=tdleft>';
2746 echo getOptionTree ('vlan_ck', getAllVLANOptions(), array ('select_class' => 'vertical')) . '</td></tr>';
2747 echo "<tr><th class=tdright>Name:</th><td class=tdleft><input type=text name='name' size='20'></td></tr>";
2748 echo '<tr><th class=tdright>Tags:</th><td class="tdleft">';
2749 printTagsPicker ();
2750 echo '</td></tr>';
2751 echo '<tr><td class=tdright><input type=checkbox name="is_connected" id="is_connected"></td>';
2752 echo '<th class=tdleft><label for="is_connected">reserve subnet-router anycast address</label></th></tr>';
2753 echo "<tr><td colspan=2>";
2754 printImageHREF ('CREATE', 'Add a new network', TRUE);
2755 echo '</td></tr>';
2756 echo "</table></form><br><br>\n";
2757 finishPortlet();
2758 }
2759
2760 function getRenderedIPNetBacktrace ($range)
2761 {
2762 if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
2763 return array();
2764
2765 $v = ($range['realm'] == 'ipv4net') ? 4 : 6;
2766 $space = "ipv${v}space"; // ipv4space, ipv6space
2767 $tag = "\$ip${v}netid_"; // $ip4netid_, $ip6netid_
2768
2769 $ret = array();
2770 // Build a backtrace from all parent networks.
2771 $clen = $range['mask'];
2772 $backtrace = array();
2773 $backtrace['&rarr;'] = $range;
2774 $key = '';
2775 while (NULL !== ($upperid = getIPAddressNetworkId ($range['ip_bin'], $clen)))
2776 {
2777 $upperinfo = spotEntity ($range['realm'], $upperid);
2778 $clen = $upperinfo['mask'];
2779 $key .= '&uarr;';
2780 $backtrace[$key] = $upperinfo;
2781 }
2782 foreach (array_reverse ($backtrace) as $arrow => $ainfo)
2783 {
2784 $link = '<a href="' . makeHref (array (
2785 'page' => $space,
2786 'tab' => 'default',
2787 'clear-cf' => '',
2788 'cfe' => '{' . $tag . $ainfo['id'] . '}',
2789 'hl_net' => 1,
2790 'eid' => $range['id'],
2791 )) . '" title="View IP tree with this net as root">' . $arrow . '</a>';
2792 $ret[] = array ($link, getOutputOf ('renderCell', $ainfo));
2793 }
2794 return $ret;
2795 }
2796
2797 function renderIPNetwork ($id)
2798 {
2799 global $pageno;
2800 $realm = $pageno; // 'ipv4net', 'ipv6net'
2801 $range = spotEntity ($realm, $id);
2802 loadIPAddrList ($range);
2803 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
2804 echo "<tr><td colspan=2 align=center><h1>${range['ip']}/${range['mask']}</h1><h2>";
2805 echo htmlspecialchars ($range['name'], ENT_QUOTES, 'UTF-8') . "</h2></td></tr>\n";
2806
2807 echo "<tr><td class=pcleft width='50%'>";
2808
2809 // render summary portlet
2810 $summary = array();
2811 $summary['%% used'] = getRenderedIPNetCapacity ($range);
2812 $summary = getRenderedIPNetBacktrace ($range) + $summary;
2813 if ($realm == 'ipv4net')
2814 {
2815 $summary[] = array ('Netmask:', ip4_format ($range['mask_bin']));
2816 $summary[] = array ('Netmask:', "0x" . strtoupper (implode ('', unpack ('H*', $range['mask_bin']))));
2817 $summary['Wildcard bits'] = ip4_format ( ~ $range['mask_bin']);
2818 }
2819
2820 $reuse_domain = considerConfiguredConstraint ($range, '8021Q_MULTILINK_LISTSRC');
2821 $domainclass = array();
2822 foreach (array_count_values (reduceSubarraysToColumn ($range['8021q'], 'domain_id')) as $domain_id => $vlan_count)
2823 $domainclass[$domain_id] = $vlan_count == 1 ? '' : ($reuse_domain ? '{trwarning}' : '{trerror}');
2824 foreach ($range['8021q'] as $item)
2825 $summary[] = array ($domainclass[$item['domain_id']] . 'VLAN:', formatVLANAsHyperlink (getVlanRow ($item['domain_id'] . '-' . $item['vlan_id'])));
2826 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes' && count ($routers = findNetRouters ($range)))
2827 {
2828 $summary['Routed by'] = '';
2829 foreach ($routers as $rtr)
2830 $summary['Routed by'] .= getOutputOf ('renderRouterCell', $rtr['ip_bin'], $rtr['iface'], spotEntity ('object', $rtr['id']));
2831 }
2832 $summary['tags'] = '';
2833 renderEntitySummary ($range, 'summary', $summary);
2834
2835 if ($range['comment'] != '')
2836 {
2837 startPortlet ('Comment');
2838 echo '<div class=commentblock>' . string_insert_hrefs (htmlspecialchars ($range['comment'], ENT_QUOTES, 'UTF-8')) . '</div>';
2839 finishPortlet ();
2840 }
2841
2842 renderFilesPortlet ($realm, $id);
2843 echo "</td>\n";
2844
2845 echo "<td class=pcright>";
2846 startPortlet ('details');
2847 renderIPNetworkAddresses ($range);
2848 finishPortlet();
2849 echo "</td></tr></table>\n";
2850 }
2851
2852 // Used solely by renderSeparator
2853 function renderEmptyIPv6 ($ip_bin, $hl_ip)
2854 {
2855 $class = 'tdleft';
2856 if ($ip_bin === $hl_ip)
2857 $class .= ' highlight';
2858 $fmt = ip6_format ($ip_bin);
2859 echo "<tr class='$class'><td><a name='ip-$fmt' href='" . makeHref (array ('page' => 'ipaddress', 'ip' => $fmt)) . "'>" . $fmt;
2860 $editable = permitted ('ipaddress', 'properties', 'editAddress')
2861 ? 'editable'
2862 : '';
2863 echo "</a></td><td><span class='rsvtext $editable id-$fmt op-upd-ip-name'></span></td>";
2864 echo "<td><span class='rsvtext $editable id-$fmt op-upd-ip-comment'></span></td><td>&nbsp;</td></tr>\n";
2865 }
2866
2867 // Renders empty table line to shrink empty IPv6 address ranges.
2868 // If the range consists of single address, renders the address instead of empty line.
2869 // Renders address $hl_ip inside the range.
2870 // Used solely by renderIPv6NetworkAddresses
2871 function renderSeparator ($first, $last, $hl_ip)
2872 {
2873 $self = __FUNCTION__;
2874 if (strcmp ($first, $last) > 0)
2875 return;
2876 if ($first == $last)
2877 renderEmptyIPv6 ($first, $hl_ip);
2878 elseif (isset ($hl_ip) && strcmp ($hl_ip, $first) >= 0 && strcmp ($hl_ip, $last) <= 0)
2879 { // $hl_ip is inside the range $first - $last
2880 $self ($first, ip_prev ($hl_ip), $hl_ip);
2881 renderEmptyIPv6 ($hl_ip, $hl_ip);
2882 $self (ip_next ($hl_ip), $last, $hl_ip);
2883 }
2884 else
2885 echo "<tr><td colspan=4 class=tdleft></td></tr>\n";
2886 }
2887
2888 // calculates page number that contains given $ip (used by renderIPv6NetworkAddresses)
2889 function getPageNumOfIPv6 ($list, $ip_bin, $maxperpage)
2890 {
2891 if (intval ($maxperpage) <= 0 || count ($list) <= $maxperpage)
2892 return 0;
2893 $keys = array_keys ($list);
2894 for ($i = 1; $i <= count ($keys); $i++)
2895 if (strcmp ($keys[$i-1], $ip_bin) >= 0)
2896 return intval ($i / $maxperpage);
2897 return intval (count ($list) / $maxperpage);
2898 }
2899
2900 function renderIPNetworkAddresses ($range)
2901 {
2902 switch (strlen ($range['ip_bin']))
2903 {
2904 case 4: return renderIPv4NetworkAddresses ($range);
2905 case 16: return renderIPv6NetworkAddresses ($range);
2906 default: throw new InvalidArgException ("range['ip_bin']", $range['ip_bin']);
2907 }
2908 }
2909
2910 function renderIPv4NetworkPageLink ($rangeid, $page, $title)
2911 {
2912 global $pageno, $tabno;
2913 return "<a href='".makeHref (array ('page' => $pageno, 'tab' => $tabno, 'id' => $rangeid, 'pg' => $page)) . "' title='".$title."'>".$page."</a> ";
2914 }
2915
2916 function renderIPv4NetworkPagination ($range, $page, $numpages)
2917 {
2918 $rendered_pager = '';
2919 $startip = ip4_bin2int ($range['ip_bin']);
2920 $endip = ip4_bin2int (ip_last ($range));
2921 $rangeid = $range['id'];
2922 // Should make this configurable perhaps
2923 // How many pages before/after current page to show
2924 $prepostpagecount = 8;
2925 // Minimum pages where pagination does not happen
2926 $paginationat = 16; // 16 pages is a /20, 32 is a /19
2927 $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
2928 if ($numpages <= $paginationat)
2929 {
2930 // create original pagination
2931 for ($i = 0; $i < $numpages; $i++)
2932 if ($i == $page)
2933 $rendered_pager .= "<b>".$i."</b> ";
2934 else
2935 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2936 }
2937 else // number of pages > page range, create ranged pagination
2938 {
2939 // page is within first subset
2940 if ($page - $prepostpagecount <= 1)
2941 {
2942 for ($i = 0; $i < $page; $i++)
2943 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2944 }
2945 // render 0 ... [page - prepostpagecount] [page - prepostpagecount + 1] ... [page - 1]
2946 else
2947 {
2948 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, 0, ip4_format(ip4_int2bin($startip)));
2949 $rendered_pager .= "... ";
2950 for ($i = $page - $prepostpagecount; $i < $page; $i++)
2951 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i