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