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