button to fix non-canonical port names (#827)
[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 // highlight port name with yellow if it's name is not canonical
1315 $canon_pn = shortenPortName ($port['name'], $port['object_id']);
1316 $name_class = $canon_pn == $port['name'] ? '' : 'trwarning';
1317
1318 echo '<tr';
1319 if ($is_highlighted)
1320 echo ' class=highlight';
1321 $a_class = isEthernetPort ($port) ? 'port-menu' : '';
1322 echo "><td class='tdleft $name_class' NOWRAP><a name='port-${port['id']}' class='interactive-portname nolink $a_class'>${port['name']}</a></td>";
1323 echo "<td class=tdleft>${port['label']}</td>";
1324 echo "<td class=tdleft>" . formatPortIIFOIF ($port) . "</td><td class=tdleft><tt>${port['l2address']}</tt></td>";
1325 if ($port['remote_object_id'])
1326 {
1327 echo "<td class=tdleft>" .
1328 formatPortLink ($port['remote_object_id'], $port['remote_object_name'], $port['remote_id'], NULL) .
1329 "</td>";
1330 echo "<td class=tdleft>" . formatLoggedSpan ($port['last_log'], $port['remote_name'], 'underline') . "</td>";
1331 $editable = permitted ('object', 'ports', 'editPort')
1332 ? 'editable'
1333 : '';
1334 echo "<td class=tdleft><span class='rsvtext $editable id-${port['id']} op-upd-reservation-cable'>${port['cableid']}</span></td>";
1335 }
1336 else
1337 echo implode ('', formatPortReservation ($port)) . '<td></td>';
1338 echo "</tr>";
1339 }
1340
1341 function renderObject ($object_id)
1342 {
1343 global $nextorder, $virtual_obj_types;
1344 $info = spotEntity ('object', $object_id);
1345 amplifyCell ($info);
1346 // Main layout starts.
1347 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
1348 echo "<tr><td colspan=2 align=center><h1>${info['dname']}</h1></td></tr>\n";
1349 // left column with uknown number of portlets
1350 echo "<tr><td class=pcleft>";
1351
1352 // display summary portlet
1353 $summary = array();
1354 if (strlen ($info['name']))
1355 $summary['Common name'] = $info['name'];
1356 elseif (considerConfiguredConstraint ($info, 'NAMEWARN_LISTSRC'))
1357 $summary[] = array ('<tr><td colspan=2 class=msg_error>Common name is missing.</td></tr>');
1358 $summary['Object type'] = '<a href="' . makeHref (array (
1359 'page' => 'depot',
1360 'tab' => 'default',
1361 'cfe' => '{$typeid_' . $info['objtype_id'] . '}'
1362 )) . '">' . decodeObjectType ($info['objtype_id'], 'o') . '</a>';
1363 if (strlen ($info['label']))
1364 $summary['Visible label'] = $info['label'];
1365 if (strlen ($info['asset_no']))
1366 $summary['Asset tag'] = $info['asset_no'];
1367 elseif (considerConfiguredConstraint ($info, 'ASSETWARN_LISTSRC'))
1368 $summary[] = array ('<tr><td colspan=2 class=msg_error>Asset tag is missing.</td></tr>');
1369 $parents = getEntityRelatives ('parents', 'object', $object_id);
1370 if (count ($parents))
1371 {
1372 $fmt_parents = array();
1373 foreach ($parents as $parent)
1374 $fmt_parents[] = "<a href='".makeHref(array('page'=>$parent['page'], $parent['id_name'] => $parent['entity_id']))."'>${parent['name']}</a>";
1375 $summary[count($parents) > 1 ? 'Containers' : 'Container'] = implode ('<br>', $fmt_parents);
1376 }
1377 $children = getEntityRelatives ('children', 'object', $object_id);
1378 if (count ($children))
1379 {
1380 $fmt_children = array();
1381 foreach ($children as $child)
1382 $fmt_children[] = "<a href='".makeHref(array('page'=>$child['page'], $child['id_name']=>$child['entity_id']))."'>${child['name']}</a>";
1383 $summary['Contains'] = implode ('<br>', $fmt_children);
1384 }
1385 if ($info['has_problems'] == 'yes')
1386 $summary[] = array ('<tr><td colspan=2 class=msg_error>Has problems</td></tr>');
1387 foreach (getAttrValues ($object_id) as $record)
1388 if
1389 (
1390 strlen ($record['value']) and
1391 permitted (NULL, NULL, NULL, array (array ('tag' => '$attr_' . $record['id'])))
1392 )
1393 $summary['{sticker}' . $record['name']] = formatAttributeValue ($record);
1394 $summary[] = array (getOutputOf ('printTagTRs',
1395 $info,
1396 makeHref
1397 (
1398 array
1399 (
1400 'page'=>'depot',
1401 'tab'=>'default',
1402 'andor' => 'and',
1403 'cfe' => '{$typeid_' . $info['objtype_id'] . '}',
1404 )
1405 )."&"
1406 ));
1407 renderEntitySummary ($info, 'summary', $summary);
1408
1409 if (strlen ($info['comment']))
1410 {
1411 startPortlet ('Comment');
1412 echo '<div class=commentblock>' . string_insert_hrefs ($info['comment']) . '</div>';
1413 finishPortlet ();
1414 }
1415
1416 $logrecords = getLogRecordsForObject ($_REQUEST['object_id']);
1417 if (count ($logrecords))
1418 {
1419 startPortlet ('log records');
1420 echo "<table cellspacing=0 cellpadding=5 align=center class=widetable width='100%'>";
1421 $order = 'odd';
1422 foreach ($logrecords as $row)
1423 {
1424 echo "<tr class=row_${order} valign=top>";
1425 echo '<td class=tdleft>' . $row['date'] . '<br>' . $row['user'] . '</td>';
1426 echo '<td class="logentry">' . string_insert_hrefs (htmlspecialchars ($row['content'], ENT_NOQUOTES)) . '</td>';
1427 echo '</tr>';
1428 $order = $nextorder[$order];
1429 }
1430 echo '</table>';
1431 finishPortlet();
1432 }
1433
1434 switchportInfoJS ($object_id); // load JS code to make portnames interactive
1435 renderFilesPortlet ('object', $object_id);
1436
1437 if (count ($info['ports']))
1438 {
1439 startPortlet ('ports and links');
1440 $hl_port_id = 0;
1441 if (isset ($_REQUEST['hl_port_id']))
1442 {
1443 assertUIntArg ('hl_port_id');
1444 $hl_port_id = $_REQUEST['hl_port_id'];
1445 addAutoScrollScript ("port-$hl_port_id");
1446 }
1447 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>";
1448 echo '<tr><th class=tdleft>Local name</th><th class=tdleft>Visible label</th>';
1449 echo '<th class=tdleft>Interface</th><th class=tdleft>L2 address</th>';
1450 echo '<th class=tdcenter colspan=2>Remote object and port</th>';
1451 echo '<th class=tdleft>Cable ID</th></tr>';
1452 foreach ($info['ports'] as $port)
1453 callHook ('renderObjectPortRow', $port, ($hl_port_id == $port['id']));
1454 if (permitted (NULL, 'ports', 'set_reserve_comment'))
1455 addJS ('js/inplace-edit.js');
1456 echo "</table><br>";
1457 finishPortlet();
1458 }
1459
1460 if (count ($info['ipv4']) + count ($info['ipv6']))
1461 {
1462 startPortlet ('IP addresses');
1463 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
1464 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
1465 echo "<tr class=tdleft><th>OS interface</th><th>IP address</th><th>network</th><th>routed by</th><th>peers</th></tr>\n";
1466 else
1467 echo "<tr class=tdleft><th>OS interface</th><th>IP address</th><th>peers</th></tr>\n";
1468
1469 // group IP allocations by interface name instead of address family
1470 $allocs_by_iface = array();
1471 foreach (array ('ipv4', 'ipv6') as $ip_v)
1472 foreach ($info[$ip_v] as $ip_bin => $alloc)
1473 $allocs_by_iface[$alloc['osif']][$ip_bin] = $alloc;
1474
1475 // sort allocs array by portnames
1476 foreach (sortPortList ($allocs_by_iface) as $iface_name => $alloclist)
1477 {
1478 $is_first_row = TRUE;
1479 foreach ($alloclist as $alloc)
1480 {
1481 $rendered_alloc = callHook ('getRenderedAlloc', $object_id, $alloc);
1482 echo "<tr class='${rendered_alloc['tr_class']}' valign=top>";
1483
1484 // display iface name, same values are grouped into single cell
1485 if ($is_first_row)
1486 {
1487 $rowspan = count ($alloclist) > 1 ? 'rowspan="' . count ($alloclist) . '"' : '';
1488 echo "<td class=tdleft $rowspan>" . $iface_name . $rendered_alloc['td_name_suffix'] . "</td>";
1489 $is_first_row = FALSE;
1490 }
1491 echo $rendered_alloc['td_ip'];
1492 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
1493 {
1494 echo $rendered_alloc['td_network'];
1495 echo $rendered_alloc['td_routed_by'];
1496 }
1497 echo $rendered_alloc['td_peers'];
1498
1499 echo "</tr>\n";
1500 }
1501 }
1502 echo "</table><br>\n";
1503 finishPortlet();
1504 }
1505
1506 $forwards = $info['nat4'];
1507 if (count($forwards['in']) or count($forwards['out']))
1508 {
1509 startPortlet('NATv4');
1510
1511 if (count($forwards['out']))
1512 {
1513
1514 echo "<h3>locally performed NAT</h3>";
1515
1516 echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center'>\n";
1517 echo "<tr><th>Proto</th><th>Match endpoint</th><th>Translate to</th><th>Target object</th><th>Rule comment</th></tr>\n";
1518
1519 foreach ($forwards['out'] as $pf)
1520 {
1521 $class = 'trerror';
1522 $osif = '';
1523 if (isset ($alloclist [$pf['localip']]))
1524 {
1525 $class = $alloclist [$pf['localip']]['addrinfo']['class'];
1526 $osif = $alloclist [$pf['localip']]['osif'] . ': ';
1527 }
1528 echo "<tr class='$class'>";
1529 echo "<td>${pf['proto']}</td><td class=tdleft>${osif}" . getRenderedIPPortPair ($pf['localip'], $pf['localport']) . "</td>";
1530 echo "<td class=tdleft>" . getRenderedIPPortPair ($pf['remoteip'], $pf['remoteport']) . "</td>";
1531 $address = getIPAddress (ip4_parse ($pf['remoteip']));
1532 echo "<td class='description'>";
1533 if (count ($address['allocs']))
1534 foreach($address['allocs'] as $bond)
1535 echo mkA ("${bond['object_name']}(${bond['name']})", 'object', $bond['object_id']) . ' ';
1536 elseif (strlen ($pf['remote_addr_name']))
1537 echo '(' . $pf['remote_addr_name'] . ')';
1538 echo "</td><td class='description'>${pf['description']}</td></tr>";
1539 }
1540 echo "</table><br><br>";
1541 }
1542 if (count($forwards['in']))
1543 {
1544 echo "<h3>arriving NAT connections</h3>";
1545 echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center'>\n";
1546 echo "<tr><th>Matched endpoint</th><th>Source object</th><th>Translated to</th><th>Rule comment</th></tr>\n";
1547 foreach ($forwards['in'] as $pf)
1548 {
1549 echo "<tr>";
1550 echo "<td>${pf['proto']}/" . getRenderedIPPortPair ($pf['localip'], $pf['localport']) . "</td>";
1551 echo '<td class="description">' . mkA ($pf['object_name'], 'object', $pf['object_id']);
1552 echo "</td><td>" . getRenderedIPPortPair ($pf['remoteip'], $pf['remoteport']) . "</td>";
1553 echo "<td class='description'>${pf['description']}</td></tr>";
1554 }
1555 echo "</table><br><br>";
1556 }
1557 finishPortlet();
1558 }
1559
1560 renderSLBTriplets2 ($info);
1561 renderSLBTriplets ($info);
1562 echo "</td>\n";
1563
1564 // After left column we have (surprise!) right column with rackspace portlet only.
1565 echo "<td class=pcright>";
1566 if (!in_array($info['objtype_id'], $virtual_obj_types))
1567 {
1568 // rackspace portlet
1569 startPortlet ('rackspace allocation');
1570 foreach (getResidentRacksData ($object_id, FALSE) as $rack_id)
1571 renderRack ($rack_id, $object_id);
1572 echo '<br>';
1573 finishPortlet();
1574 }
1575 echo "</td></tr>";
1576 echo "</table>\n";
1577 }
1578
1579 function renderRackMultiSelect ($sname, $racks, $selected)
1580 {
1581 // Transform the given flat list into a list of groups, each representing a rack row.
1582 $rdata = array();
1583 foreach ($racks as $rack)
1584 {
1585 $trail = getLocationTrail ($rack['location_id'], FALSE);
1586 if(!empty ($trail))
1587 $row_name = $trail . ' : ' . $rack['row_name'];
1588 else
1589 $row_name = $rack['row_name'];
1590 $rdata[$row_name][$rack['id']] = $rack['name'];
1591 }
1592 echo "<select name=${sname} multiple size=" . getConfigVar ('MAXSELSIZE') . " onchange='getElementsByName(\"updateObjectAllocation\")[0].submit()'>\n";
1593 $row_names = array_keys ($rdata);
1594 natsort ($row_names);
1595 foreach ($row_names as $optgroup)
1596 {
1597 echo "<optgroup label='${optgroup}'>";
1598 foreach ($rdata[$optgroup] as $rack_id => $rack_name)
1599 {
1600 echo "<option value=${rack_id}";
1601 if (!(array_search ($rack_id, $selected) === FALSE))
1602 echo ' selected';
1603 echo">${rack_name}</option>\n";
1604 }
1605 }
1606 echo "</select>\n";
1607 }
1608
1609 // This function renders a form for port edition.
1610 function renderPortsForObject ($object_id)
1611 {
1612 $prefs = getPortListPrefs();
1613 function printNewItemTR ($prefs)
1614 {
1615 printOpFormIntro ('addPort');
1616 echo "<tr><td>";
1617 printImageHREF ('add', 'add a port', TRUE);
1618 echo "</td><td class='tdleft'><input type=text size=8 name=port_name tabindex=100></td>\n";
1619 echo "<td><input type=text name=port_label tabindex=101></td><td>";
1620 printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type_id', 'tabindex' => 102), $prefs['selected']);
1621 echo "<td><input type=text name=port_l2address tabindex=103 size=18 maxlength=24></td>\n";
1622 echo "<td colspan=4>&nbsp;</td><td>";
1623 printImageHREF ('add', 'add a port', TRUE, 104);
1624 echo "</td></tr></form>";
1625 }
1626 if (getConfigVar('ENABLE_MULTIPORT_FORM') == 'yes' || getConfigVar('ENABLE_BULKPORT_FORM') == 'yes' )
1627 startPortlet ('Ports and interfaces');
1628 else
1629 echo '<br>';
1630 $object = spotEntity ('object', $object_id);
1631 amplifyCell ($object);
1632 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes' && getConfigVar('ENABLE_BULKPORT_FORM') == 'yes'){
1633 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
1634 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>";
1635 echo "<th class=tdleft>Count</th><th>&nbsp;</th></tr>\n";
1636 printOpFormIntro ('addBulkPorts');
1637 echo "<tr><td>";
1638 printImageHREF ('add', 'add ports', TRUE);
1639 echo "</td><td><input type=text size=8 name=port_name tabindex=105></td>\n";
1640 echo "<td><input type=text name=port_label tabindex=106></td><td>";
1641 printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type_id', 'tabindex' => 107), $prefs['selected']);
1642 echo "<td><input type=text name=port_numbering_start tabindex=108 size=3 maxlength=3></td>\n";
1643 echo "<td><input type=text name=port_numbering_count tabindex=109 size=3 maxlength=3></td>\n";
1644 echo "<td>&nbsp;</td><td>";
1645 printImageHREF ('add', 'add ports', TRUE, 110);
1646 echo "</td></tr></form>";
1647 echo "</table><br>\n";
1648 }
1649
1650 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
1651 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>";
1652 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";
1653 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
1654 printNewItemTR ($prefs);
1655
1656 // clear ports link
1657 echo getOpLink (array ('op'=>'deleteAll'), 'Clear port list', 'clear', '', 'need-confirmation');
1658
1659 // rename ports link
1660 $n_ports_to_rename = 0;
1661 foreach ($object['ports'] as $port)
1662 if ($port['name'] != shortenPortName ($port['name'], $object['id']))
1663 $n_ports_to_rename++;
1664 if ($n_ports_to_rename)
1665 echo '<p>' . getOpLink (array ('op'=>'renameAll'), "Auto-rename $n_ports_to_rename ports", 'recalc', 'Use RackTables naming convention for this device type') . '</p>';
1666
1667 if (isset ($_REQUEST['hl_port_id']))
1668 {
1669 assertUIntArg ('hl_port_id');
1670 $hl_port_id = intval ($_REQUEST['hl_port_id']);
1671 addAutoScrollScript ("port-$hl_port_id");
1672 }
1673 switchportInfoJS ($object_id); // load JS code to make portnames interactive
1674 foreach ($object['ports'] as $port)
1675 {
1676 // highlight port name with yellow if it's name is not canonical
1677 $canon_pn = shortenPortName ($port['name'], $port['object_id']);
1678 $name_class = $canon_pn == $port['name'] ? '' : 'trwarning';
1679
1680 $tr_class = isset ($hl_port_id) && $hl_port_id == $port['id'] ? 'class="highlight"' : '';
1681 printOpFormIntro ('editPort', array ('port_id' => $port['id']));
1682 echo "<tr $tr_class><td><a name='port-${port['id']}' href='".makeHrefProcess(array('op'=>'delPort', 'port_id'=>$port['id']))."'>";
1683 printImageHREF ('delete', 'Unlink and Delete this port');
1684 echo "</a></td>\n";
1685 $a_class = isEthernetPort ($port) ? 'port-menu' : '';
1686 echo "<td class='tdleft $name_class' NOWRAP><input type=text name=name class='interactive-portname $a_class' value='${port['name']}' size=8></td>";
1687 echo "<td><input type=text name=label value='${port['label']}'></td>";
1688 echo '<td>';
1689 if ($port['iif_id'] != 1)
1690 echo '<label>' . $port['iif_name'] . ' ';
1691 printSelect (getExistingPortTypeOptions ($port['id']), array ('name' => 'port_type_id'), $port['oif_id']);
1692 if ($port['iif_id'] != 1)
1693 echo '</label>';
1694 echo '</td>';
1695
1696 // 18 is enough to fit 6-byte MAC address in its longest form,
1697 // while 24 should be Ok for WWN
1698 echo "<td><input type=text name=l2address value='${port['l2address']}' size=18 maxlength=24></td>\n";
1699 if ($port['remote_object_id'])
1700 {
1701 echo "<td>" .
1702 formatLoggedSpan ($port['last_log'], formatPortLink ($port['remote_object_id'], $port['remote_object_name'], $port['remote_id'], NULL)) .
1703 "</td>";
1704 echo "<td> " . formatLoggedSpan ($port['last_log'], $port['remote_name'], 'underline') .
1705 "<input type=hidden name=reservation_comment value=''></td>";
1706 echo "<td><input type=text name=cable value='${port['cableid']}'></td>";
1707 echo "<td class=tdcenter>";
1708 echo getOpLink (array('op'=>'unlinkPort', 'port_id'=>$port['id'], ), '', 'cut', 'Unlink this port');
1709 echo "</td>";
1710 }
1711 elseif (strlen ($port['reservation_comment']))
1712 {
1713 echo "<td>" . formatLoggedSpan ($port['last_log'], 'Reserved:', 'strong underline') . "</td>";
1714 echo "<td><input type=text name=reservation_comment value='${port['reservation_comment']}'></td>";
1715 echo "<td></td>";
1716 echo "<td class=tdcenter>";
1717 echo getOpLink (array('op'=>'useup', 'port_id'=>$port['id']), '', 'clear', 'Use up this port');
1718 echo "</td>";
1719 }
1720 else
1721 {
1722 $in_rack = getConfigVar ('NEAREST_RACKS_CHECKBOX');
1723 echo "<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td class=tdcenter><span";
1724 $helper_args = array
1725 (
1726 'port' => $port['id'],
1727 'in_rack' => ($in_rack == "yes" ? "on" : "")
1728 );
1729 $popup_args = 'height=700, width=400, location=no, menubar=no, '.
1730 'resizable=yes, scrollbars=yes, status=no, titlebar=no, toolbar=no';
1731 echo " ondblclick='window.open(\"" . makeHrefForHelper ('portlist', $helper_args);
1732 echo "\",\"findlink\",\"${popup_args}\");'";
1733 // end of onclick=
1734 echo " onclick='window.open(\"" . makeHrefForHelper ('portlist', $helper_args);
1735 echo "\",\"findlink\",\"${popup_args}\");'";
1736 // end of onclick=
1737 echo '>';
1738 // end of <a>
1739 printImageHREF ('plug', 'Link this port');
1740 echo "</span>";
1741 echo " <input type=text name=reservation_comment></td>\n";
1742 }
1743 echo "<td>";
1744 printImageHREF ('save', 'Save changes', TRUE);
1745 echo "</td></form></tr>\n";
1746 }
1747 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
1748 printNewItemTR ($prefs);
1749 echo "</table><br>\n";
1750 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes' && getConfigVar('ENABLE_BULKPORT_FORM') == 'yes'){
1751 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
1752 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>";
1753 echo "<th class=tdleft>Count</th><th>&nbsp;</th></tr>\n";
1754 printOpFormIntro ('addBulkPorts');
1755 echo "<tr><td>";
1756 printImageHREF ('add', 'add ports', TRUE);
1757 echo "</td><td><input type=text size=8 name=port_name tabindex=105></td>\n";
1758 echo "<td><input type=text name=port_label tabindex=106></td><td>";
1759 printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type_id', 'tabindex' => 107), $prefs['selected']);
1760 echo "<td><input type=text name=port_numbering_start tabindex=108 size=3 maxlength=3></td>\n";
1761 echo "<td><input type=text name=port_numbering_count tabindex=109 size=3 maxlength=3></td>\n";
1762 echo "<td>&nbsp;</td><td>";
1763 printImageHREF ('add', 'add ports', TRUE, 110);
1764 echo "</td></tr></form>";
1765 echo "</table><br>\n";
1766 }
1767 if (getConfigVar('ENABLE_MULTIPORT_FORM') == 'yes')
1768 finishPortlet();
1769 if (getConfigVar('ENABLE_MULTIPORT_FORM') != 'yes')
1770 return;
1771
1772 startPortlet ('Add/update multiple ports');
1773 printOpFormIntro ('addMultiPorts');
1774 echo 'Format: <select name=format tabindex=201>';
1775 echo '<option value=c3600asy>Cisco 3600 async: sh line | inc TTY</option>';
1776 echo '<option value=fiwg selected>Foundry ServerIron/FastIron WorkGroup/Edge: sh int br</option>';
1777 echo '<option value=fisxii>Foundry FastIron SuperX/II4000: sh int br</option>';
1778 echo '<option value=ssv1>SSV:&lt;interface name&gt; &lt;MAC address&gt;</option>';
1779 echo "</select>";
1780 echo 'Default port type: ';
1781 printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type', 'tabindex' => 202), $prefs['selected']);
1782 echo "<input type=submit value='Parse output' tabindex=204><br>\n";
1783 echo "<textarea name=input cols=100 rows=50 tabindex=203></textarea><br>\n";
1784 echo '</form>';
1785 finishPortlet();
1786 }
1787
1788 function renderIPForObject ($object_id)
1789 {
1790 function printNewItemTR ($default_type)
1791 {
1792 global $aat;
1793 printOpFormIntro ('add');
1794 echo "<tr><td>"; // left btn
1795 printImageHREF ('add', 'allocate', TRUE);
1796 echo "</td>";
1797 echo "<td class=tdleft><input type='text' size='10' name='bond_name' tabindex=100></td>\n"; // if-name
1798 echo "<td class=tdleft><input type=text name='ip' tabindex=101></td>\n"; // IP
1799 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
1800 echo "<td colspan=2>&nbsp;</td>"; // network, routed by
1801 echo '<td>';
1802 printSelect ($aat, array ('name' => 'bond_type', 'tabindex' => 102), $default_type); // type
1803 echo "</td><td>&nbsp;</td><td>"; // misc
1804 printImageHREF ('add', 'allocate', TRUE, 103); // right btn
1805 echo "</td></tr></form>";
1806 }
1807 global $aat;
1808 startPortlet ('Allocations');
1809 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'><tr>\n";
1810 echo '<th>&nbsp;</th>';
1811 echo '<th>OS interface</th>';
1812 echo '<th>IP address</th>';
1813 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
1814 {
1815 echo '<th>network</th>';
1816 echo '<th>routed by</th>';
1817 }
1818 echo '<th>type</th>';
1819 echo '<th>misc</th>';
1820 echo '<th>&nbsp;</th>';
1821 echo '</tr>';
1822
1823 $alloc_list = ''; // most of the output is stored here
1824 $used_alloc_types = array();
1825 foreach (getObjectIPAllocations ($object_id) as $alloc)
1826 {
1827 if (! isset ($used_alloc_types[$alloc['type']]))
1828 $used_alloc_types[$alloc['type']] = 0;
1829 $used_alloc_types[$alloc['type']]++;
1830
1831 $rendered_alloc = callHook ('getRenderedAlloc', $object_id, $alloc);
1832 $alloc_list .= getOutputOf ('printOpFormIntro', 'upd', array ('ip' => $alloc['addrinfo']['ip']));
1833 $alloc_list .= "<tr class='${rendered_alloc['tr_class']}' valign=top>";
1834
1835 $alloc_list .= "<td>" . getOpLink (array ('op' => 'del', 'ip' => $alloc['addrinfo']['ip']), '', 'delete', 'Delete this IP address') . "</td>";
1836 $alloc_list .= "<td class=tdleft><input type='text' name='bond_name' value='${alloc['osif']}' size=10>" . $rendered_alloc['td_name_suffix'] . "</td>";
1837 $alloc_list .= $rendered_alloc['td_ip'];
1838 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
1839 {
1840 $alloc_list .= $rendered_alloc['td_network'];
1841 $alloc_list .= $rendered_alloc['td_routed_by'];
1842 }
1843 $alloc_list .= '<td>' . getSelect ($aat, array ('name' => 'bond_type'), $alloc['type']) . "</td>";
1844 $alloc_list .= $rendered_alloc['td_peers'];
1845 $alloc_list .= "<td>" .getImageHREF ('save', 'Save changes', TRUE) . "</td>";
1846
1847 $alloc_list .= "</form></tr>\n";
1848 }
1849 asort ($used_alloc_types, SORT_NUMERIC);
1850 $most_popular_type = empty ($used_alloc_types) ? 'regular' : array_last (array_keys ($used_alloc_types));
1851
1852 if ($list_on_top = (getConfigVar ('ADDNEW_AT_TOP') != 'yes'))
1853 echo $alloc_list;
1854 printNewItemTR ($most_popular_type);
1855 if (! $list_on_top)
1856 echo $alloc_list;
1857
1858 echo "</table><br>\n";
1859 finishPortlet();
1860 }
1861
1862 // This function is deprecated. Do not rely on its internals,
1863 // it will probably be removed in the next major relese.
1864 // Use new showError, showWarning, showSuccess functions.
1865 // Log array is stored in global $log_messages. Its format is simple: plain ordered array
1866 // with values having keys 'c' (both message code and severity) and 'a' (sprintf arguments array)
1867 function showMessageOrError ()
1868 {
1869 global $log_messages;
1870
1871 @session_start();
1872 if (isset ($_SESSION['log']))
1873 {
1874 $log_messages = array_merge ($_SESSION['log'], $log_messages);
1875 unset ($_SESSION['log']);
1876 }
1877 session_commit();
1878
1879 if (empty ($log_messages))
1880 return;
1881 $msginfo = array
1882 (
1883 // records 0~99 with success messages
1884 0 => array ('code' => 'success', 'format' => '%s'),
1885 5 => array ('code' => 'success', 'format' => 'added record "%s" successfully'),
1886 6 => array ('code' => 'success', 'format' => 'updated record "%s" successfully'),
1887 7 => array ('code' => 'success', 'format' => 'deleted record "%s" successfully'),
1888 8 => array ('code' => 'success', 'format' => 'Port %s successfully linked with %s'),
1889 10 => array ('code' => 'success', 'format' => 'Added %u ports, updated %u ports, encountered %u errors.'),
1890 21 => array ('code' => 'success', 'format' => 'Generation complete'),
1891 26 => array ('code' => 'success', 'format' => 'updated %u records successfully'),
1892 37 => array ('code' => 'success', 'format' => 'added %u records successfully'),
1893 38 => array ('code' => 'success', 'format' => 'removed %u records successfully'),
1894 43 => array ('code' => 'success', 'format' => 'Saved successfully.'),
1895 44 => array ('code' => 'success', 'format' => '%s failures and %s successfull changes.'),
1896 48 => array ('code' => 'success', 'format' => 'added a record successfully'),
1897 49 => array ('code' => 'success', 'format' => 'deleted a record successfully'),
1898 51 => array ('code' => 'success', 'format' => 'updated a record successfully'),
1899 57 => array ('code' => 'success', 'format' => 'Reset complete'),
1900 63 => array ('code' => 'success', 'format' => '%u change request(s) have been processed'),
1901 67 => array ('code' => 'success', 'format' => "Tag rolling done, %u objects involved"),
1902 71 => array ('code' => 'success', 'format' => 'File "%s" was linked successfully'),
1903 72 => array ('code' => 'success', 'format' => 'File was unlinked successfully'),
1904 82 => array ('code' => 'success', 'format' => "Bulk port creation was successful. %u ports created, %u failed"),
1905 87 => array ('code' => 'success', 'format' => '802.1Q recalculate: %d ports changed on %d switches'),
1906 // records 100~199 with fatal error messages
1907 100 => array ('code' => 'error', 'format' => '%s'),
1908 109 => array ('code' => 'error', 'format' => 'failed updating a record'),
1909 131 => array ('code' => 'error', 'format' => 'invalid format requested'),
1910 141 => array ('code' => 'error', 'format' => 'Encountered %u errors, updated %u record(s)'),
1911 149 => array ('code' => 'error', 'format' => 'Turing test failed'),
1912 150 => array ('code' => 'error', 'format' => 'Can only change password under DB authentication.'),
1913 151 => array ('code' => 'error', 'format' => 'Old password doesn\'t match.'),
1914 152 => array ('code' => 'error', 'format' => 'New passwords don\'t match.'),
1915 154 => array ('code' => 'error', 'format' => "Verification error: %s"),
1916 155 => array ('code' => 'error', 'format' => 'Save failed.'),
1917 159 => array ('code' => 'error', 'format' => 'Permission denied moving port %s from VLAN%u to VLAN%u'),
1918 161 => array ('code' => 'error', 'format' => 'Endpoint not found. Please either set FQDN attribute or assign an IP address to the object.'),
1919 162 => array ('code' => 'error', 'format' => 'More than one IP address is assigned to this object, please configure FQDN attribute.'),
1920 170 => array ('code' => 'error', 'format' => 'There is no network for IP address "%s"'),
1921 172 => array ('code' => 'error', 'format' => 'Malformed request'),
1922 179 => array ('code' => 'error', 'format' => 'Expired form has been declined.'),
1923 188 => array ('code' => 'error', 'format' => "Fatal SNMP failure"),
1924 189 => array ('code' => 'error', 'format' => "Unknown OID '%s'"),
1925 191 => array ('code' => 'error', 'format' => "deploy was blocked due to conflicting configuration versions"),
1926
1927 // records 200~299 with warnings
1928 200 => array ('code' => 'warning', 'format' => '%s'),
1929 201 => array ('code' => 'warning', 'format' => 'nothing happened...'),
1930 206 => array ('code' => 'warning', 'format' => '%s is not empty'),
1931 207 => array ('code' => 'warning', 'format' => 'File upload failed, error: %s'),
1932
1933 // records 300~399 with notices
1934 300 => array ('code' => 'neutral', 'format' => '%s'),
1935
1936 );
1937 // Handle the arguments. Is there any better way to do it?
1938 foreach ($log_messages as $record)
1939 {
1940 if (!isset ($record['c']) or !isset ($msginfo[$record['c']]))
1941 {
1942 $prefix = isset ($record['c']) ? $record['c'] . ': ' : '';
1943 echo "<div class=msg_neutral>(${prefix}this message was lost)</div>";
1944 continue;
1945 }
1946 if (isset ($record['a']))
1947 switch (count ($record['a']))
1948 {
1949 case 1:
1950 $msgtext = sprintf
1951 (
1952 $msginfo[$record['c']]['format'],
1953 $record['a'][0]
1954 );
1955 break;
1956 case 2:
1957 $msgtext = sprintf
1958 (
1959 $msginfo[$record['c']]['format'],
1960 $record['a'][0],
1961 $record['a'][1]
1962 );
1963 break;
1964 case 3:
1965 $msgtext = sprintf
1966 (
1967 $msginfo[$record['c']]['format'],
1968 $record['a'][0],
1969 $record['a'][1],
1970 $record['a'][2]
1971 );
1972 break;
1973 case 4:
1974 default:
1975 $msgtext = sprintf
1976 (
1977 $msginfo[$record['c']]['format'],
1978 $record['a'][0],
1979 $record['a'][1],
1980 $record['a'][2],
1981 $record['a'][3]
1982 );
1983 break;
1984 }
1985 else
1986 $msgtext = $msginfo[$record['c']]['format'];
1987 echo '<div class=msg_' . $msginfo[$record['c']]['code'] . ">${msgtext}</div>";
1988 }
1989 $log_messages = array();
1990 }
1991
1992 // renders two tables: port link status and learned MAC list
1993 function renderPortsInfo($object_id)
1994 {
1995 try
1996 {
1997 if (permitted (NULL, NULL, 'get_link_status'))
1998 $linkStatus = queryDevice ($object_id, 'getportstatus');
1999 else
2000 showWarning ("You don't have permission to view ports link status");
2001
2002 if (permitted (NULL, NULL, 'get_mac_list'))
2003 $macList = sortPortList (queryDevice ($object_id, 'getmaclist'));
2004 else
2005 showWarning ("You don't have permission to view learned MAC list");
2006 }
2007 catch (RTGatewayError $e)
2008 {
2009 showError ($e->getMessage());
2010 return;
2011 }
2012
2013 global $nextorder;
2014 echo "<table width='100%'><tr>";
2015 if (! empty ($linkStatus))
2016 {
2017 echo "<td valign='top' width='50%'>";
2018 startPortlet('Link status');
2019 echo "<table width='80%' class='widetable' cellspacing=0 cellpadding='5px' align='center'><tr><th>Port<th><th>Link status<th>Link info</tr>";
2020 $order = 'even';
2021 foreach ($linkStatus as $pn => $link)
2022 {
2023 switch ($link['status'])
2024 {
2025 case 'up':
2026 $img_filename = 'link-up.png';
2027 break;
2028 case 'down':
2029 $img_filename = 'link-down.png';
2030 break;
2031 case 'disabled':
2032 $img_filename = 'link-disabled.png';
2033 break;
2034 default:
2035 $img_filename = '1x1t.gif';
2036 }
2037
2038 echo "<tr class='row_$order'>";
2039 $order = $nextorder[$order];
2040 echo '<td>' . $pn;
2041 echo '<td>' . '<img width=16 height=16 src="?module=chrome&uri=pix/' . $img_filename . '">';
2042 echo '<td>' . $link['status'];
2043 $info = '';
2044 if (isset ($link['speed']))
2045 $info .= $link['speed'];
2046 if (isset ($link['duplex']))
2047 {
2048 if (! empty ($info))
2049 $info .= ', ';
2050 $info .= $link['duplex'];
2051 }
2052 echo '<td>' . $info;
2053 echo '</tr>';
2054 }
2055 echo "</table></td>";
2056 finishPortlet();
2057 }
2058
2059 if (! empty ($macList))
2060 {
2061 echo "<td valign='top' width='50%'>";
2062 $rendered_macs = '';
2063 $mac_count = 0;
2064 $rendered_macs .= "<table width='80%' class='widetable' cellspacing=0 cellpadding='5px' align='center'><tr><th>MAC<th>Vlan<th>Port</tr>";
2065 $order = 'even';
2066 foreach ($macList as $pn => $list)
2067 {
2068 $order = $nextorder[$order];
2069 foreach ($list as $item)
2070 {
2071 ++$mac_count;
2072 $rendered_macs .= "<tr class='row_$order'>";
2073 $rendered_macs .= '<td style="font-family: monospace">' . $item['mac'];
2074 $rendered_macs .= '<td>' . $item['vid'];
2075 $rendered_macs .= '<td>' . $pn;
2076 $rendered_macs .= '</tr>';
2077 }
2078 }
2079 $rendered_macs .= "</table></td>";
2080
2081 startPortlet("Learned MACs ($mac_count)");
2082 echo $rendered_macs;
2083 finishPortlet();
2084 }
2085
2086 echo "</td></tr></table>";
2087 }
2088
2089 /*
2090 The following conditions must be met:
2091 1. We can mount onto free atoms only. This means: if any record for an atom
2092 already exists in RackSpace, it can't be used for mounting.
2093 2. We can't unmount from 'W' atoms. Operator should review appropriate comments
2094 and either delete them before unmounting or refuse to unmount the object.
2095 */
2096 function renderRackSpaceForObject ($object_id)
2097 {
2098 // Always process occupied racks plus racks chosen by user. First get racks with
2099 // already allocated rackspace...
2100 $workingRacksData = getResidentRacksData ($object_id);
2101 // ...and then add those chosen by user (if any).
2102 if (isset($_REQUEST['rackmulti']))
2103 foreach ($_REQUEST['rackmulti'] as $cand_id)
2104 if (!isset ($workingRacksData[$cand_id]))
2105 {
2106 $rackData = spotEntity ('rack', $cand_id);
2107 amplifyCell ($rackData);
2108 $workingRacksData[$cand_id] = $rackData;
2109 }
2110
2111 // Get a list of all of this object's parents,
2112 // then trim the list to only include parents that are racks
2113 $objectParents = getEntityRelatives('parents', 'object', $object_id);
2114 $parentRacks = array();
2115 foreach ($objectParents as $parentData)
2116 if ($parentData['entity_type'] == 'rack')
2117 $parentRacks[] = $parentData['entity_id'];
2118
2119 // Main layout starts.
2120 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0><tr>";
2121
2122 // Left portlet with rack list.
2123 echo "<td class=pcleft height='1%'>";
2124 startPortlet ('Racks');
2125 $allRacksData = listCells ('rack');
2126
2127 // filter rack list to match only racks having common tags with the object (reducing $allRacksData)
2128 if (! isset ($_REQUEST['show_all_racks']) and getConfigVar ('FILTER_RACKLIST_BY_TAGS') == 'yes')
2129 {
2130 $matching_racks = array();
2131 $object = spotEntity ('object', $object_id);
2132 $matched_tags = array();
2133 foreach ($allRacksData as $rack)
2134 foreach ($object['etags'] as $tag)
2135 if (tagOnChain ($tag, $rack['etags']) or tagOnChain ($tag, $rack['itags']))
2136 {
2137 $matching_racks[$rack['id']] = $rack;
2138 $matched_tags[$tag['id']] = $tag;
2139 break;
2140 }
2141 // add current object's racks even if they dont match filter
2142 foreach ($workingRacksData as $rack_id => $rack)
2143 if (! isset ($matching_racks[$rack_id]))
2144 $matching_racks[$rack_id] = $rack;
2145 // if matching racks found, and rack list is reduced, show 'show all' link
2146 if (count ($matching_racks) and count ($matching_racks) != count ($allRacksData))
2147 {
2148 $filter_text = '';
2149 foreach ($matched_tags as $tag)
2150 $filter_text .= (empty ($filter_text) ? '' : ' or ') . '{' . $tag['tag'] . '}';
2151 $href_show_all = trim($_SERVER['REQUEST_URI'], '&');
2152 $href_show_all .= htmlspecialchars('&show_all_racks=1');
2153 echo "(filtered by <span class='filter-text'>$filter_text</span>, <a href='$href_show_all'>show all</a>)<p>";
2154 $allRacksData = $matching_racks;
2155 }
2156 }
2157
2158 if (count ($allRacksData) <= getConfigVar ('RACK_PRESELECT_THRESHOLD'))
2159 foreach ($allRacksData as $rack)
2160 if (!array_key_exists ($rack['id'], $workingRacksData))
2161 {
2162 amplifyCell ($rack);
2163 $workingRacksData[$rack['id']] = $rack;
2164 }
2165 foreach (array_keys ($workingRacksData) as $rackId)
2166 applyObjectMountMask ($workingRacksData[$rackId], $object_id);
2167 printOpFormIntro ('updateObjectAllocation');
2168 renderRackMultiSelect ('rackmulti[]', $allRacksData, array_keys ($workingRacksData));
2169 echo "<br><br>";
2170 finishPortlet();
2171 echo "</td>";
2172
2173 // Middle portlet with comment and submit.
2174 echo "<td class=pcleft>";
2175 startPortlet ('Comment (for Rackspace History)');
2176 echo "<textarea name=comment rows=10 cols=40></textarea><br>\n";
2177 echo "<input type=submit value='Save' name=got_atoms>\n";
2178 echo "<br><br>";
2179 finishPortlet();
2180 echo "</td>";
2181
2182 // Right portlet with rendered racks. If this form submit is not final, we have to
2183 // reflect the former state of the grid in current form.
2184 echo "<td class=pcright rowspan=2 height='1%'>";
2185 startPortlet ('Working copy');
2186 includeJQueryUI (false);
2187 addJS ('js/racktables.js');
2188 addJS ('js/bulkselector.js');
2189 echo '<table border=0 cellspacing=10 align=center><tr>';
2190 foreach ($workingRacksData as $rack_id => $rackData)
2191 {
2192 // Order is important here: only original allocation is highlighted.
2193 highlightObject ($rackData, $object_id);
2194 markupAtomGrid ($rackData, 'T');
2195 // If we have a form processed, discard user input and show new database
2196 // contents.
2197 if (isset ($_REQUEST['rackmulti'][0])) // is an update
2198 mergeGridFormToRack ($rackData);
2199 echo "<td valign=top>";
2200 echo "<center>\n<h2>${rackData['name']}</h2>\n";
2201 echo "<table class=rack id=selectableRack border=0 cellspacing=0 cellpadding=1>\n";
2202 echo "<tr><th width='10%'>&nbsp;</th>";
2203 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
2204 echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
2205 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
2206 renderAtomGrid ($rackData);
2207 echo "<tr><th width='10%'>&nbsp;</th>";
2208 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
2209 echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
2210 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
2211 echo "</table>\n<br>\n";
2212 // Determine zero-u checkbox status.
2213 // If form has been submitted, use form data, otherwise use DB data.
2214 if (isset($_REQUEST['op']))
2215 $checked = isset($_REQUEST['zerou_'.$rack_id]) ? 'checked' : '';
2216 else
2217 $checked = in_array($rack_id, $parentRacks) ? 'checked' : '';
2218 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";
2219 echo "<input type='button' onclick='uncheckAll();' value='Uncheck all'>\n";
2220 echo '</center></td>';
2221 }
2222 echo "</tr></table>";
2223 finishPortlet();
2224 echo "</td>\n";
2225
2226 echo "</form>\n";
2227 echo "</tr></table>\n";
2228 }
2229
2230 function renderMolecule ($mdata, $object_id)
2231 {
2232 // sort data out
2233 $rackpack = array();
2234 global $loclist;
2235 foreach ($mdata as $rua)
2236 {
2237 $rack_id = $rua['rack_id'];
2238 $unit_no = $rua['unit_no'];
2239 $atom = $rua['atom'];
2240 if (!isset ($rackpack[$rack_id]))
2241 {
2242 $rackData = spotEntity ('rack', $rack_id);
2243 amplifyCell ($rackData);
2244 for ($i = $rackData['height']; $i > 0; $i--)
2245 for ($locidx = 0; $locidx < 3; $locidx++)
2246 $rackData[$i][$locidx]['state'] = 'F';
2247 $rackpack[$rack_id] = $rackData;
2248 }
2249 $rackpack[$rack_id][$unit_no][$loclist[$atom]]['state'] = 'T';
2250 $rackpack[$rack_id][$unit_no][$loclist[$atom]]['object_id'] = $object_id;
2251 }
2252 // now we have some racks to render
2253 foreach ($rackpack as $rackData)
2254 {
2255 markAllSpans ($rackData);
2256 echo "<table class=molecule cellspacing=0>\n";
2257 echo "<caption>${rackData['name']}</caption>\n";
2258 echo "<tr><th width='10%'>&nbsp;</th><th width='20%'>Front</th><th width='50%'>Interior</th><th width='20%'>Back</th></tr>\n";
2259 for ($i = $rackData['height']; $i > 0; $i--)
2260 {
2261 echo "<tr><th>" . inverseRackUnit ($i, $rackData) . "</th>";
2262 for ($locidx = 0; $locidx < 3; $locidx++)
2263 {
2264 $state = $rackData[$i][$locidx]['state'];
2265 echo "<td class='atom state_${state}'>&nbsp;</td>\n";
2266 }
2267 echo "</tr>\n";
2268 }
2269 echo "</table>\n";
2270 }
2271 }
2272
2273 function renderDepot ()
2274 {
2275 global $pageno, $nextorder;
2276 $cellfilter = getCellFilter();
2277 $objects = array();
2278 $objects_count = getEntitiesCount ('object');
2279
2280 echo "<table border=0 class=objectview>\n";
2281 echo "<tr><td class=pcleft>";
2282
2283 if ($objects_count == 0)
2284 echo '<h2>No objects exist</h2>';
2285 // 1st attempt: do not fetch all objects if cellfilter is empty and rendering empty result is enabled
2286 elseif (! ($cellfilter['is_empty'] && renderEmptyResults ($cellfilter, 'objects', $objects_count)))
2287 {
2288 $objects = applyCellFilter ('object', $cellfilter);
2289 // 2nd attempt: do not render all fetched objects if rendering empty result is enabled
2290 if (! renderEmptyResults ($cellfilter, 'objects', count($objects)))
2291 {
2292 startPortlet ('Objects (' . count ($objects) . ')');
2293 echo '<br><br><table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
2294 echo '<tr><th>Common name</th><th>Visible label</th><th>Asset tag</th><th>Row/Rack or Container</th></tr>';
2295 $order = 'odd';
2296 # gather IDs of all objects and fetch rackspace info in one pass
2297 $idlist = array();
2298 foreach ($objects as $obj)
2299 $idlist[] = $obj['id'];
2300 $mountinfo = getMountInfo ($idlist);
2301 $containerinfo = getContainerInfo ($idlist);
2302 foreach ($objects as $obj)
2303 {
2304 $problem = ($obj['has_problems'] == 'yes') ? 'has_problems' : '';
2305 echo "<tr class='row_${order} tdleft ${problem}' valign=top><td>" . mkA ("<strong>${obj['dname']}</strong>", 'object', $obj['id']);
2306 if (count ($obj['etags']))
2307 echo '<br><small>' . serializeTags ($obj['etags'], makeHref(array('page'=>$pageno, 'tab'=>'default')) . '&') . '</small>';
2308 echo "</td><td>${obj['label']}</td>";
2309 echo "<td>${obj['asset_no']}</td>";
2310 $places = array();
2311 if (array_key_exists ($obj['id'], $containerinfo))
2312 foreach ($containerinfo[$obj['id']] as $ci)
2313 $places[] = mkA ($ci['container_dname'], 'object', $ci['container_id']);
2314 if (array_key_exists ($obj['id'], $mountinfo))
2315 foreach ($mountinfo[$obj['id']] as $mi)
2316 $places[] = mkA ($mi['row_name'], 'row', $mi['row_id']) . '/' . mkA ($mi['rack_name'], 'rack', $mi['rack_id']);
2317 if (! count ($places))
2318 $places[] = 'Unmounted';
2319 echo "<td>" . implode (', ', $places) . '</td>';
2320 echo '</tr>';
2321 $order = $nextorder[$order];
2322 }
2323 echo '</table>';
2324 finishPortlet();
2325 }
2326 }
2327
2328 echo "</td><td class=pcright width='25%'>";
2329
2330 renderCellFilterPortlet ($cellfilter, 'object', $objects);
2331 echo "</td></tr></table>\n";
2332 }
2333
2334 // This function returns TRUE if the result set is too big to be rendered, and no filter is set.
2335 // In this case it renders the describing message instead.
2336 function renderEmptyResults($cellfilter, $entities_name, $count = NULL)
2337 {
2338 if (!$cellfilter['is_empty'])
2339 return FALSE;
2340 if (isset ($_REQUEST['show_all_objects']))
2341 return FALSE;
2342 $max = intval(getConfigVar('MAX_UNFILTERED_ENTITIES'));
2343 if (0 == $max || $count <= $max)
2344 return FALSE;
2345
2346 $href_show_all = trim($_SERVER['REQUEST_URI'], '&');
2347 $href_show_all .= htmlspecialchars('&show_all_objects=1');
2348 $suffix = isset ($count) ? " ($count)" : '';
2349 echo <<<END
2350 <p>Please set a filter to display the corresponging $entities_name.
2351 <br><a href="$href_show_all">Show all $entities_name$suffix</a>
2352 END;
2353 return TRUE;
2354 }
2355
2356 // History viewer for history-enabled simple dictionaries.
2357 function renderObjectHistory ($object_id)
2358 {
2359 $order = 'odd';
2360 global $nextorder;
2361 echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
2362 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>';
2363 $result = usePreparedSelectBlade
2364 (
2365 'SELECT ctime, user_name, name, label, asset_no, has_problems, comment FROM ObjectHistory WHERE id=? ORDER BY ctime',
2366 array ($object_id)
2367 );
2368 while ($row = $result->fetch (PDO::FETCH_NUM))
2369 {
2370 echo "<tr class=row_${order}><td>${row[0]}</td>";
2371 for ($i = 1; $i <= 6; $i++)
2372 echo "<td>" . $row[$i] . "</td>";
2373 echo "</tr>\n";
2374 $order = $nextorder[$order];
2375 }
2376 echo "</table><br>\n";
2377 }
2378
2379 function renderRackspaceHistory ()
2380 {
2381 global $nextorder, $pageno, $tabno;
2382 $order = 'odd';
2383 $history = getRackspaceHistory();
2384 // Show the last operation by default.
2385 if (isset ($_REQUEST['op_id']))
2386 $op_id = $_REQUEST['op_id'];
2387 elseif (isset ($history[0]['mo_id']))
2388 $op_id = $history[0]['mo_id'];
2389 else $op_id = NULL;
2390
2391 $omid = NULL;
2392 $nmid = NULL;
2393 $object_id = 1;
2394 if ($op_id)
2395 list ($omid, $nmid) = getOperationMolecules ($op_id);
2396
2397 // Main layout starts.
2398 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
2399
2400 // Left top portlet with old allocation.
2401 echo "<tr><td class=pcleft>";
2402 startPortlet ('Old allocation');
2403 if ($omid)
2404 {
2405 $oldMolecule = getMolecule ($omid);
2406 renderMolecule ($oldMolecule, $object_id);
2407 }
2408 else
2409 echo "nothing";
2410 finishPortlet();
2411
2412 echo '</td><td class=pcright>';
2413
2414 // Right top portlet with new allocation
2415 startPortlet ('New allocation');
2416 if ($nmid)
2417 {
2418 $newMolecule = getMolecule ($nmid);
2419 renderMolecule ($newMolecule, $object_id);
2420 }
2421 else
2422 echo "nothing";
2423 finishPortlet();
2424
2425 echo '</td></tr><tr><td colspan=2>';
2426
2427 // Bottom portlet with list
2428
2429 startPortlet ('Rackspace allocation history');
2430 echo "<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>\n";
2431 echo "<tr><th>timestamp</th><th>author</th><th>object</th><th>comment</th></tr>\n";
2432 foreach ($history as $row)
2433 {
2434 if ($row['mo_id'] == $op_id)
2435 $class = 'hl';
2436 else
2437 $class = "row_${order}";
2438 echo "<tr class=${class}><td><a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'op_id'=>$row['mo_id']))."'>${row['ctime']}</a></td>";
2439 echo "<td>${row['user_name']}</td><td>";
2440 renderCell (spotEntity ('object', $row['ro_id']));
2441 echo '</td><td>' . niftyString ($row['comment'], 0) . '</td></tr>';
2442 $order = $nextorder[$order];
2443 }
2444 echo "</table>\n";
2445 finishPortlet();
2446
2447 echo '</td></tr></table>';
2448 }
2449
2450 function renderIPSpaceRecords ($tree, $baseurl, $target = 0, $level = 1)
2451 {
2452 $self = __FUNCTION__;
2453 $knight = (getConfigVar ('IPV4_ENABLE_KNIGHT') == 'yes');
2454
2455 // scroll page to the highlighted item
2456 if ($target && isset ($_REQUEST['hl_net']))
2457 addAutoScrollScript ("net-$target");
2458
2459 foreach ($tree as $item)
2460 {
2461 if ($display_routers = (getConfigVar ('IPV4_TREE_RTR_AS_CELL') != 'none'))
2462 loadIPAddrList ($item); // necessary to compute router list and address counter
2463
2464 if (isset ($item['id']))
2465 {
2466 $decor = array ('indent' => $level);
2467 if ($item['symbol'] == 'node-collapsed')
2468 $decor['symbolurl'] = "${baseurl}&eid=${item['id']}&hl_net=1";
2469 elseif ($item['symbol'] == 'node-expanded')
2470 $decor['symbolurl'] = $baseurl . ($item['parent_id'] ? "&eid=${item['parent_id']}&hl_net=1" : '');
2471 $tr_class = '';
2472 if ($target == $item['id'] && isset ($_REQUEST['hl_net']))
2473 {
2474 $decor['tdclass'] = ' highlight';
2475 $tr_class = $decor['tdclass'];
2476 }
2477 echo "<tr valign=top class=\"$tr_class\">";
2478 printIPNetInfoTDs ($item, $decor);
2479
2480 // capacity and usage
2481 echo "<td class=tdcenter>";
2482 echo getRenderedIPNetCapacity ($item);
2483 echo "</td>";
2484
2485 if ($display_routers)
2486 printRoutersTD (findRouters ($item['own_addrlist']), getConfigVar ('IPV4_TREE_RTR_AS_CELL'));
2487 echo "</tr>";
2488 if ($item['symbol'] == 'node-expanded' or $item['symbol'] == 'node-expanded-static')
2489 $self ($item['kids'], $baseurl, $target, $level + 1);
2490 }
2491 else
2492 {
2493 // non-allocated (spare) IP range
2494 echo "<tr valign=top>";
2495 printIPNetInfoTDs ($item, array ('indent' => $level, 'knight' => $knight, 'tdclass' => 'sparenetwork'));
2496
2497 // capacity and usage
2498 echo "<td class=tdcenter>";
2499 echo getRenderedIPNetCapacity ($item);
2500 echo "</td>";
2501 if ($display_routers)
2502 echo "<td></td>";
2503 echo "</tr>";
2504 }
2505 }
2506 }
2507
2508 function renderIPSpace()
2509 {
2510 global $pageno, $tabno;
2511 $realm = ($pageno == 'ipv4space' ? 'ipv4net' : 'ipv6net');
2512 $cellfilter = getCellFilter();
2513
2514 // expand request can take either natural values or "ALL". Zero means no expanding.
2515 $eid = isset ($_REQUEST['eid']) ? $_REQUEST['eid'] : 0;
2516
2517 echo "<table border=0 class=objectview>\n";
2518 echo "<tr><td class=pcleft>";
2519
2520 $netlist = array();
2521 if (! ($cellfilter['is_empty'] && ! $eid && renderEmptyResults($cellfilter, 'IP nets', getEntitiesCount ($realm))))
2522 {
2523 $top = NULL;
2524 foreach (listCells ($realm) as $net)
2525 {
2526 if (isset ($top) and IPNetContains ($top, $net))
2527 ;
2528 elseif (! count ($cellfilter['expression']) or judgeCell ($net, $cellfilter['expression']))
2529 $top = $net;
2530 else
2531 continue;
2532 $netlist[$net['id']] = $net;
2533 }
2534 $netcount = count ($netlist);
2535 $tree = prepareIPTree ($netlist, $eid);
2536
2537 if (! renderEmptyResults($cellfilter, 'IP nets', count($tree)))
2538 {
2539 startPortlet ("networks (${netcount})");
2540 echo '<h4>';
2541 $all = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'ALL')) .
2542 $cellfilter['urlextra'] . "'>expand&nbsp;all</a>";
2543 $none = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'NONE')) .
2544 $cellfilter['urlextra'] . "'>collapse&nbsp;all</a>";
2545 $auto = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno)) .
2546 $cellfilter['urlextra'] . "'>auto-collapse</a>";
2547
2548 if ($eid === 0)
2549 echo 'auto-collapsing at threshold ' . getConfigVar ('TREE_THRESHOLD') . " ($all / $none)";
2550 elseif ($eid === 'ALL')
2551 echo "expanding all ($auto / $none)";
2552 elseif ($eid === 'NONE')
2553 echo "collapsing all ($all / $auto)";
2554 else
2555 {
2556 $netinfo = spotEntity ($realm, $eid);
2557 echo "expanding ${netinfo['ip']}/${netinfo['mask']} ($auto / $all / $none)";
2558 }
2559 echo "</h4><table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
2560 echo "<tr><th>prefix</th><th>name/tags</th><th>capacity</th>";
2561 if (getConfigVar ('IPV4_TREE_RTR_AS_CELL') != 'none')
2562 echo "<th>routed by</th>";
2563 echo "</tr>\n";
2564 $baseurl = makeHref(array('page'=>$pageno, 'tab'=>$tabno)) . $cellfilter['urlextra'];
2565 renderIPSpaceRecords ($tree, $baseurl, $eid);
2566 echo "</table>\n";
2567 finishPortlet();
2568 }
2569 }
2570
2571 echo '</td><td class=pcright>';
2572 renderCellFilterPortlet ($cellfilter, $realm, $netlist);
2573 echo "</td></tr></table>\n";
2574 }
2575
2576 function renderIPSpaceEditor()
2577 {
2578 global $pageno;
2579 $realm = ($pageno == 'ipv4space' ? 'ipv4net' : 'ipv6net');
2580 $net_page = $realm; // 'ipv4net', 'ipv6net'
2581 $addrspaceList = listCells ($realm);
2582 startPortlet ('Manage existing (' . count ($addrspaceList) . ')');
2583 if (count ($addrspaceList))
2584 {
2585 echo "<table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
2586 echo "<tr><th>&nbsp;</th><th>prefix</th><th>name</th><th>capacity</th></tr>";
2587 foreach ($addrspaceList as $netinfo)
2588 {
2589 echo "<tr valign=top><td>";
2590 if (! isIPNetworkEmpty ($netinfo))
2591 printImageHREF ('nodestroy', 'There are ' . count ($netinfo['addrlist']) . ' allocations inside');
2592 else
2593 echo getOpLink (array ('op' => 'del', 'id' => $netinfo['id']), '', 'destroy', 'Delete this prefix');
2594 echo '</td><td class=tdleft>' . mkA ("${netinfo['ip']}/${netinfo['mask']}", $net_page, $netinfo['id']) . '</td>';
2595 echo '<td class=tdleft>' . niftyString ($netinfo['name']);
2596 if (count ($netinfo['etags']))
2597 echo '<br><small>' . serializeTags ($netinfo['etags']) . '</small>';
2598 echo '</td><td>';
2599 echo getRenderedIPNetCapacity ($netinfo);
2600 echo '</tr>';
2601 }
2602 echo "</table>";
2603 finishPortlet();
2604 }
2605 }
2606
2607 function renderIPNewNetForm ()
2608 {
2609 global $pageno;
2610 if ($pageno == 'ipv6space')
2611 {
2612 $realm = 'ipv6net';
2613 $regexp = '^[a-fA-F0-9:]*:[a-fA-F0-9:\.]*/\d{1,3}$';
2614 }
2615 else
2616 {
2617 $realm = 'ipv4net';
2618 $regexp = '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$';
2619 }
2620
2621 // IP prefix validator
2622 addJS ('js/live_validation.js');
2623 $regexp = addslashes ($regexp);
2624 addJS (<<<END
2625 $(document).ready(function () {
2626 $('form#add input[name="range"]').attr('match', '$regexp');
2627 Validate.init();
2628 });
2629 END
2630 , TRUE);
2631
2632 startPortlet ('Add new');
2633 printOpFormIntro ('add');
2634 echo '<table border=0 cellpadding=5 cellspacing=0 align=center>';
2635
2636 // inputs column
2637 $prefix_value = empty ($_REQUEST['set-prefix']) ? '' : $_REQUEST['set-prefix'];
2638 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>";
2639 echo '<tr><th class=tdright>VLAN:</th><td class=tdleft>';
2640 echo getOptionTree ('vlan_ck', getAllVLANOptions(), array ('select_class' => 'vertical', 'tabindex' => 2)) . '</td></tr>';
2641 echo "<tr><th class=tdright>Name:</th><td class=tdleft><input type=text name='name' size='20' tabindex=3></td></tr>";
2642 echo '<tr><th class=tdright>Tags:</th><td class="tdleft">';
2643 printTagsPicker ();
2644 echo '</td></tr>';
2645 echo '<tr><td class=tdright><input type=checkbox name="is_connected" tabindex=4></td><th class=tdleft>reserve subnet-router anycast address</th></tr>';
2646 echo "<tr><td colspan=2>";
2647 printImageHREF ('CREATE', 'Add a new network', TRUE, 5);
2648 echo '</td></tr>';
2649 echo "</table></form><br><br>\n";
2650 finishPortlet();
2651 }
2652
2653 function getRenderedIPNetBacktrace ($range)
2654 {
2655 if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
2656 return array();
2657
2658 $v = ($range['realm'] == 'ipv4net') ? 4 : 6;
2659 $space = "ipv${v}space"; // ipv4space, ipv6space
2660 $tag = "\$ip${v}netid_"; // $ip4netid_, $ip6netid_
2661
2662 $ret = array();
2663 // Build a backtrace from all parent networks.
2664 $clen = $range['mask'];
2665 $backtrace = array();
2666 $backtrace['&rarr;'] = $range;
2667 $key = '';
2668 while (NULL !== ($upperid = getIPAddressNetworkId ($range['ip_bin'], $clen)))
2669 {
2670 $upperinfo = spotEntity ($range['realm'], $upperid);
2671 $clen = $upperinfo['mask'];
2672 $key .= '&uarr;';
2673 $backtrace[$key] = $upperinfo;
2674 }
2675 foreach (array_reverse ($backtrace) as $arrow => $ainfo)
2676 {
2677 $link = '<a href="' . makeHref (array (
2678 'page' => $space,
2679 'tab' => 'default',
2680 'clear-cf' => '',
2681 'cfe' => '{' . $tag . $ainfo['id'] . '}',
2682 'hl_net' => 1,
2683 'eid' => $range['id'],
2684 )) . '" title="View IP tree with this net as root">' . $arrow . '</a>';
2685 $ret[] = array ($link, getOutputOf ('renderCell', $ainfo));
2686 }
2687 return $ret;
2688 }
2689
2690 function renderIPNetwork ($id)
2691 {
2692 global $pageno;
2693 $realm = $pageno; // 'ipv4net', 'ipv6net'
2694 $range = spotEntity ($realm, $id);
2695 loadIPAddrList ($range);
2696 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
2697 echo "<tr><td colspan=2 align=center><h1>${range['ip']}/${range['mask']}</h1><h2>";
2698 echo htmlspecialchars ($range['name'], ENT_QUOTES, 'UTF-8') . "</h2></td></tr>\n";
2699
2700 echo "<tr><td class=pcleft width='50%'>";
2701
2702 // render summary portlet
2703 $summary = array();
2704 $summary['%% used'] = getRenderedIPNetCapacity ($range);
2705 $summary = getRenderedIPNetBacktrace ($range) + $summary;
2706 if ($realm == 'ipv4net')
2707 {
2708 $summary[] = array ('Netmask:', ip4_format ($range['mask_bin']));
2709 $summary[] = array ('Netmask:', "0x" . strtoupper (implode ('', unpack ('H*', $range['mask_bin']))));
2710 $summary['Wildcard bits'] = ip4_format ( ~ $range['mask_bin']);
2711 }
2712
2713 $reuse_domain = considerConfiguredConstraint ($range, '8021Q_MULTILINK_LISTSRC');
2714 $domainclass = array();
2715 foreach (array_count_values (reduceSubarraysToColumn ($range['8021q'], 'domain_id')) as $domain_id => $vlan_count)
2716 $domainclass[$domain_id] = $vlan_count == 1 ? '' : ($reuse_domain ? '{trwarning}' : '{trerror}');
2717 foreach ($range['8021q'] as $item)
2718 $summary[] = array ($domainclass[$item['domain_id']] . 'VLAN:', formatVLANAsHyperlink (getVLANInfo ($item['domain_id'] . '-' . $item['vlan_id'])));
2719 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes' and count ($routers = findRouters ($range['addrlist'])))
2720 {
2721 $summary['Routed by'] = '';
2722 foreach ($routers as $rtr)
2723 $summary['Routed by'] .= getOutputOf ('renderRouterCell', $rtr['ip_bin'], $rtr['iface'], spotEntity ('object', $rtr['id']));
2724 }
2725 $summary['tags'] = '';
2726 renderEntitySummary ($range, 'summary', $summary);
2727
2728 if (strlen ($range['comment']))
2729 {
2730 startPortlet ('Comment');
2731 echo '<div class=commentblock>' . string_insert_hrefs (htmlspecialchars ($range['comment'], ENT_QUOTES, 'UTF-8')) . '</div>';
2732 finishPortlet ();
2733 }
2734
2735 renderFilesPortlet ($realm, $id);
2736 echo "</td>\n";
2737
2738 echo "<td class=pcright>";
2739 startPortlet ('details');
2740 renderIPNetworkAddresses ($range);
2741 finishPortlet();
2742 echo "</td></tr></table>\n";
2743 }
2744
2745 // Used solely by renderSeparator
2746 function renderEmptyIPv6 ($ip_bin, $hl_ip)
2747 {
2748 $class = 'tdleft';
2749 if ($ip_bin === $hl_ip)
2750 $class .= ' highlight';
2751 $fmt = ip6_format ($ip_bin);
2752 echo "<tr class='$class'><td><a name='ip-$fmt' href='" . makeHref (array ('page' => 'ipaddress', 'ip' => $fmt)) . "'>" . $fmt;
2753 $editable = permitted ('ipaddress', 'properties', 'editAddress')
2754 ? 'editable'
2755 : '';
2756 echo "</a></td><td><span class='rsvtext $editable id-$fmt op-upd-ip-name'></span></td>";
2757 echo "<td><span class='rsvtext $editable id-$fmt op-upd-ip-comment'></span></td><td>&nbsp;</td></tr>\n";
2758 }
2759
2760 // Renders empty table line to shrink empty IPv6 address ranges.
2761 // If the range consists of single address, renders the address instead of empty line.
2762 // Renders address $hl_ip inside the range.
2763 // Used solely by renderIPv6NetworkAddresses
2764 function renderSeparator ($first, $last, $hl_ip)
2765 {
2766 $self = __FUNCTION__;
2767 if (strcmp ($first, $last) > 0)
2768 return;
2769 if ($first == $last)
2770 renderEmptyIPv6 ($first, $hl_ip);
2771 elseif (isset ($hl_ip) && strcmp ($hl_ip, $first) >= 0 && strcmp ($hl_ip, $last) <= 0)
2772 { // $hl_ip is inside the range $first - $last
2773 $self ($first, ip_prev ($hl_ip), $hl_ip);
2774 renderEmptyIPv6 ($hl_ip, $hl_ip);
2775 $self (ip_next ($hl_ip), $last, $hl_ip);
2776 }
2777 else
2778 echo "<tr><td colspan=4 class=tdleft></td></tr>\n";
2779 }
2780
2781 // calculates page number that contains given $ip (used by renderIPv6NetworkAddresses)
2782 function getPageNumOfIPv6 ($list, $ip_bin, $maxperpage)
2783 {
2784 if (intval ($maxperpage) <= 0 || count ($list) <= $maxperpage)
2785 return 0;
2786 $keys = array_keys ($list);
2787 for ($i = 1; $i <= count ($keys); $i++)
2788 if (strcmp ($keys[$i-1], $ip_bin) >= 0)
2789 return intval ($i / $maxperpage);
2790 return intval (count ($list) / $maxperpage);
2791 }
2792
2793 function renderIPNetworkAddresses ($range)
2794 {
2795 switch (strlen ($range['ip_bin']))
2796 {
2797 case 4: return renderIPv4NetworkAddresses ($range);
2798 case 16: return renderIPv6NetworkAddresses ($range);
2799 default: throw new InvalidArgException ("range['ip_bin']", $range['ip_bin']);
2800 }
2801 }
2802
2803 function renderIPv4NetworkAddresses ($range)
2804 {
2805 global $pageno, $tabno, $aac_left;
2806 $startip = ip4_bin2int ($range['ip_bin']);
2807 $endip