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