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