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