MySlice columns to external servers added or not based on config variable
[plewww.git] / planetlab / slices / slice.php
1 <?php
2
3 // Require login
4 require_once 'plc_login.php';
5
6 // Get session and API handles
7 require_once 'plc_session.php';
8 global $plc, $api;
9
10 // Print header
11 require_once 'plc_drupal.php';
12 include 'plc_header.php';
13
14 // Common functions
15 require_once 'plc_functions.php';
16 require_once 'plc_peers.php';
17 require_once 'plc_objects.php';
18 require_once 'plc_visibletags2.php';
19 require_once 'linetabs.php';
20 require_once 'table2.php';
21 require_once 'details.php';
22 require_once 'toggle.php';
23 require_once 'form.php';
24 require_once 'raphael.php';
25 require_once 'columns.php';
26
27 // keep css separate for now
28 drupal_set_html_head('
29 <link href="/planetlab/css/my_slice.css" rel="stylesheet" type="text/css" />
30 <script src="/planetlab/slices/leases.js" type="text/javascript" charset="utf-8"></script>
31 ');
32
33 // -------------------- admins potentially need to get full list of users
34 ini_set('memory_limit','32M');
35 //error_reporting(0);
36
37 $profiling=false;
38 if ($_GET['profiling']) $profiling=true;
39
40 if ($profiling)  plc_debug_prof_start();
41
42 // -------------------- 
43 // recognized URL arguments
44 $slice_id=intval($_GET['id']);
45 if ( ! $slice_id ) { plc_error('Malformed URL - id not set'); return; }
46
47 ////////////////////
48 // have to name columns b/c we need the non-native 'omf_control' column
49 $slice_columns=array('slice_id','name','peer_id','site_id','person_ids','node_ids','expires',
50                      'url','description','instantiation','omf_control');
51 $slices= $api->GetSlices( array($slice_id), $slice_columns);
52
53 if (empty($slices)) {
54   drupal_set_message ("Slice " . $slice_id . " not found");
55   return;
56  }
57
58 $slice=$slices[0];
59
60 if ($profiling) plc_debug_prof('1: slice',count($slices));
61 // pull all node info to vars
62 $name= $slice['name'];
63 $expires = date( "d/m/Y", $slice['expires'] );
64 $site_id= $slice['site_id'];
65
66 $person_ids=$slice['person_ids'];
67
68 // get peers
69 $peer_id= $slice['peer_id'];
70 $peers=new Peers ($api);
71 $local_peer = ! $peer_id;
72
73 if ($profiling) plc_debug_prof('2: peers',count($peers));
74
75 // gets site info
76 $sites= $api->GetSites( array( $site_id ) );
77 $site=$sites[0];
78 $site_name= $site['name'];
79 $max_slices = $site['max_slices'];
80
81 if ($profiling) plc_debug_prof('3: sites',count($sites));
82 //////////////////////////////////////// building blocks for the renew area
83 // Constants
84 global $DAY;            $DAY = 24*60*60;
85 global $WEEK;           $WEEK = 7 * $DAY; 
86 global $MAX_WEEKS;      $MAX_WEEKS= 8;          // weeks from today
87 global $GRACE_DAYS;     $GRACE_DAYS=10;         // days for renewal promoted on top
88 global $NOW;            $NOW=mktime();
89
90 //////////////////////////////////////////////////////////// utility for the renew tab
91 // make the renew area on top and open if the expiration time is less than 10 days from now
92 function renew_needed ($slice) {
93   global $DAY, $NOW, $GRACE_DAYS;
94   $current_exp=$slice['expires'];
95
96   $time_left = $current_exp - $NOW;
97   $visible = $time_left/$DAY <= $GRACE_DAYS;
98   return $visible;
99 }
100
101 function renew_area ($slice,$site,$visible) {
102   global $DAY, $WEEK, $MAX_WEEKS, $GRACE_DAYS, $NOW;
103  
104   $current_exp=$slice['expires'];
105   $current_text = gmstrftime("%A %b-%d-%y %T %Z", $current_exp);
106   $max_exp= $NOW + ($MAX_WEEKS * $WEEK); // seconds since epoch
107   $max_text = gmstrftime("%A %b-%d-%y %T %Z", $max_exp);
108
109   // xxx some extra code needed to enable this area only if the slice description is OK:
110   // description and url must be non void
111   $toggle=
112     new PlekitToggle('renew',"Expires $current_text - Renew this slice",
113                      array("bubble"=>
114                            "Enter this zone if you wish to renew your slice",
115                            'visible'=>$visible));
116   $toggle->start();
117
118   // xxx message could take roles into account
119   if ($site['max_slices']<=0) {
120      $message= <<< EOF
121 <p class='my-slice-renewal'>Slice creation and renewal have been temporarily disabled for your
122 <site. This may have occurred because your site's nodes have been down
123 or unreachable for several weeks, and multiple attempts to contact
124 your site's PI(s) and Technical Contact(s) have all failed. If so,
125 contact your site's PI(s) and Technical Contact(s) and ask them to
126 bring up your site's nodes. Please visit your <a
127 href='/db/sites/index.php?id=$site_id'>site details</a> page to find
128 out more about your site's nodes, and how to contact your site's PI(s)
129 and Technical Contact(s).</p>
130 EOF;
131      echo $message;
132  
133   } else {
134     // xxx this is a rough cut and paste from the former UI
135     // showing a datepicker view could be considered as well with some extra work
136     // calculate possible extension lengths
137     $selectors = array();
138     foreach ( array ( 1 => "One more week", 
139                       2 => "Two more weeks", 
140                       3 => "Three more weeks", 
141                       4 => "One more month" ) as $weeks => $text ) {
142       $candidate_exp = $current_exp + $weeks*$WEEK;
143       if ( $candidate_exp < $max_exp) {
144         $selectors []= array('display'=>"$text (" . gmstrftime("%A %b-%d-%y %T %Z", $candidate_exp) . ")",
145                              'value'=>$candidate_exp);
146         $max_renewal_weeks=$weeks;
147         $max_renewal_date= gmstrftime("%A %b-%d-%y %T %Z", $candidate_exp);
148       }
149     }
150
151     if ( empty( $selectors ) ) {
152       print <<< EOF
153 <div class='my-slice-renewal'>
154 Slices cannot be renewed more than $MAX_WEEKS weeks from now, i.e. not beyond $max_text. 
155 For this reason, the current slice cannot be renewed any further into the future, try again closer to expiration date.
156 </div>
157 EOF;
158      } else {
159       print <<< EOF
160 <div class='my-slice-renewal'>
161 <span class='bold'>Important:</span> Please take this opportunity to review and update your slice information in the Details tab.
162 <p>
163 PlanetLab's security model requires that anyone who is concerned about a slice's activity be able to immediately learn about that slice. The details that you provide are your public explanation about why the slice behaves as it does. Be sure to describe the <span class='bold'>kind of traffic</span> that your slice generates, and how it handles material that is under <span class='bold'>copyright</span>, if relevant.
164 </p><p>
165 The PlanetLab Operations Centres regularly respond to concerns raised by third parties about site behaviour. Most incidents are resolved rapidly based upon the publicly posted slice details. However, when these details are not sufficiently clear or accurate, and we cannot immediately reach the slice owner, we must delete the slice. 
166 </p>
167 EOF;
168
169       $form = new PlekitForm (l_actions(),
170                               array('action'=>'renew-slice',
171                                     'slice_id'=>$slice['slice_id']));
172       $form->start();
173       print $form->label_html('expires','Duration:&nbsp;');
174       print $form->select_html('expires',$selectors,array('label'=>'Pick one'));
175       print $form->submit_html('renew-button','Renew');
176       $form->end();
177
178 print("<p><i>NOTE: Slices cannot be renewed beyond another $max_renewal_weeks week(s) ($max_renewal_date).</i>  </p>");
179 print ("</div>");
180     }
181   }
182  
183   $toggle->end();
184 }
185
186 ////////////////////////////////////////////////////////////
187
188 $am_in_slice = in_array(plc_my_person_id(),$person_ids);
189
190 if ($am_in_slice) {
191   drupal_set_title("My slice " . $name);
192  } else {
193   drupal_set_title("Slice " . $name);
194 }
195
196 $privileges = ( $local_peer && (plc_is_admin()  || plc_is_pi() || $am_in_slice));
197 $tags_privileges = $privileges || plc_is_admin();
198
199 $tabs=array();
200 $tabs [] = tab_nodes_slice($slice_id);
201 $tabs [] = tab_site($site);
202
203 // are these the right privileges for deletion ?
204 if ($privileges) {
205   $tabs ['Delete']= array('url'=>l_actions(),
206                           'method'=>'post',
207                           'values'=>array('action'=>'delete-slice','slice_id'=>$slice_id),
208                           'bubble'=>"Delete slice $name",
209                           'confirm'=>"Are you sure to delete slice $name");
210
211   $tabs["Events"]=array_merge(tablook_event(),
212                               array('url'=>l_event("Slice","slice",$slice_id),
213                                     'bubble'=>"Events for slice $name"));
214   $tabs["Comon"]=array_merge(tablook_comon(),
215                              array('url'=>l_comon("slice_id",$slice_id),
216                                    'bubble'=>"Comon page about slice $name"));
217 }
218
219 plekit_linetabs($tabs);
220
221 ////////////////////////////////////////
222 $peers->block_start($peer_id);
223
224 //////////////////////////////////////// renewal area 
225 // (1) close to expiration : show on top and open
226
227 if ($local_peer ) {
228   $renew_visible = renew_needed ($slice);
229   if ($renew_visible) renew_area ($slice,$site,true);
230  }
231
232
233 //////////////////////////////////////////////////////////// tab:details
234 // default for opening the details section or not ?
235 if ($local_peer) {
236   $default_show_details = true;
237  } else {
238   $default_show_details = ! $renew_visible;
239  }
240   
241 $toggle = 
242   new PlekitToggle ('my-slice-details',"Details",
243                     array('bubble'=>
244                           'Display and modify details for that slice',
245                           'visible'=>get_arg('show_details',$default_show_details)));
246 $toggle->start();
247
248 $details=new PlekitDetails($privileges);
249 $details->form_start(l_actions(),array('action'=>'update-slice',
250                                        'slice_id'=>$slice_id,
251                                        'name'=>$name));
252
253 $details->start();
254 if (! $local_peer) {
255   $details->th_td("Peer",$peers->peer_link($peer_id));
256   $details->space();
257  }
258
259
260 $details->th_td('Name',$slice['name']);
261 $details->th_td('Description',$slice['description'],'description',
262                 array('input_type'=>'textarea',
263                       'width'=>50,'height'=>5));
264 $details->th_td('URL',$slice['url'],'url',array('width'=>50));
265 $details->tr_submit("submit","Update Slice");
266 $details->th_td('Expires',$expires);
267 $details->th_td('Instantiation',$slice['instantiation']);
268 $details->th_td("OMF-friendly", ($slice['omf_control'] ? 'Yes' : 'No') . " [to change: see 'omf_control' in the tags section below]");
269 $details->th_td('Site',l_site_obj($site));
270 // xxx show the PIs here
271 //$details->th_td('PIs',...);
272 $details->end();
273
274 $details->form_end();
275 $toggle->end();
276
277 //////////////////////////////////////////////////////////// tab:persons
278 $person_columns = array('email','person_id','first_name','last_name','roles');
279 // get persons in slice
280 if (!empty($person_ids))
281   $persons=$api->GetPersons(array('person_id'=>$slice['person_ids']),$person_columns);
282 // just propose to add everyone else
283 // xxx this is maybe too much for admins as it slows stuff down 
284 // as regular persons can see only a fraction of the db anyway
285 $potential_persons=
286   $api->GetPersons(array('~person_id'=>$slice['person_ids'],
287                          'peer_id'=>NULL,
288                          'enabled'=>true),
289                    $person_columns);
290 $count=count($persons);
291
292 if ($profiling) plc_debug_prof('4: persons',count($persons));
293 $toggle=
294   new PlekitToggle ('my-slice-persons',"$count users",
295                     array('bubble'=>
296                           'Manage accounts attached to this slice',
297                           'visible'=>get_arg('show_persons',false)));
298 $toggle->start();
299
300 ////////// people currently in
301 // visible:
302 // hide if both current+add are included
303 // so user can chose which section is of interest
304 // show otherwise
305 $toggle_persons = new PlekitToggle ('my-slice-persons-current',
306                                     "$count people currently in $name",
307                                     array('visible'=>get_arg('show_persons_current',!$privileges)));
308 $toggle_persons->start();
309
310 $headers=array();
311 $headers['email']='string';
312 $headers['first']='string';
313 $headers['last']='string';
314 $headers['R']='string';
315 if ($privileges) $headers[plc_delete_icon()]="none";
316 $table=new PlekitTable('persons',$headers,'0',
317                        array('notes_area'=>false));
318 $form=new PlekitForm(l_actions(),array('slice_id'=>$slice['slice_id']));
319 $form->start();
320 $table->start();
321 if ($persons) foreach ($persons as $person) {
322   $table->row_start();
323   $table->cell(l_person_obj($person));
324   $table->cell($person['first_name']);
325   $table->cell($person['last_name']);
326   $table->cell(plc_vertical_table ($person['roles']));
327   if ($privileges) $table->cell ($form->checkbox_html('person_ids[]',$person['person_id']));
328   $table->row_end();
329 }
330 // actions area
331 if ($privileges) {
332
333   // remove persons
334   $table->tfoot_start();
335
336   $table->row_start();
337   $table->cell($form->submit_html ("remove-persons-from-slice","Remove selected"),
338                array('hfill'=>true,'align'=>'right'));
339   $table->row_end();
340  }
341 $table->end();
342 $toggle_persons->end();
343
344 ////////// people to add
345 if ($privileges) {
346   $count=count($potential_persons);
347   $toggle_persons = new PlekitToggle ('my-slice-persons-add',
348                                       "$count people may be added to $name",
349                                       array('visible'=>get_arg('show_persons_add',false)));
350   $toggle_persons->start();
351   if ( ! $potential_persons ) {
352     // xxx improve style
353     echo "<p class='not-relevant'>No person to add</p>";
354   } else {
355     $headers=array();
356     $headers['email']='string';
357     $headers['first']='string';
358     $headers['last']='string';
359     $headers['R']='string';
360     $headers['+']="none";
361     $options = array('notes_area'=>false,
362                      'search_width'=>15,
363                      'pagesize'=>8);
364     // show search for admins only as other people won't get that many names to add
365     if ( ! plc_is_admin() ) $options['search_area']=false;
366     
367     $table=new PlekitTable('add_persons',$headers,'0',$options);
368     $form=new PlekitForm(l_actions(),array('slice_id'=>$slice['slice_id']));
369     $form->start();
370     $table->start();
371     if ($potential_persons) foreach ($potential_persons as $person) {
372         $table->row_start();
373         $table->cell(l_person_obj($person));
374         $table->cell($person['first_name']);
375         $table->cell($person['last_name']);
376         $table->cell(plc_vertical_table ($person['roles']));
377         $table->cell ($form->checkbox_html('person_ids[]',$person['person_id']));
378         $table->row_end();
379       }
380     // add users
381     $table->tfoot_start();
382     $table->row_start();
383     $table->cell($form->submit_html ("add-persons-in-slice","Add selected"),
384                  array('hfill'=>true,'align'=>'right'));
385     $table->row_end();
386     $table->end();
387     $form->end();
388   }
389   $toggle_persons->end();
390 }
391 $toggle->end();
392
393 //////////////////////////////////////////////////////////// tab:nodes
394 // the nodes details to display here
395 // (1) we search for the tag types for which 'category' matches 'node*/ui*'
396 // all these tags will then be tentatively displayed in this area
397 // (2) further information can also be optionally specified in the category:
398 //     (.) we split the category with '/' and search for assignments of the form var=value
399 //     (.) header can be set to supersede the column header (default is tagname)
400 //     (.) rank can be used for ordering the columns (default is tagname)
401 //     (.) type is passed to the javascript table, for sorting (default is 'string')
402
403 // minimal list as a start
404 $node_fixed_columns = array('hostname','node_id','peer_id','slice_ids_whitelist', 'site_id',
405                             'run_level','boot_state','last_contact','node_type');
406 // create a VisibleTags object : basically the list of tag columns to show
407 //$visibletags = new VisibleTags ($api, 'node');
408 //$visiblecolumns = $visibletags->column_names();
409
410 // optimizing calls to GetNodes
411 //$all_nodes=$api->GetNodes(NULL,$node_columns);
412 //$slice_nodes=$api->GetNodes(array('node_id'=>$slice['node_ids']),$node_columns);
413 //$potential_nodes=$api->GetNodes(array('~node_id'=>$slice['node_ids']),$node_columns);
414
415
416 //NEW CODE FOR ENABLING COLUMN CONFIGURATION
417
418 //prepare fix and configurable columns
419
420 $fix_columns = array();
421 $fix_columns[]=array('tagname'=>'hostname', 'header'=>'hostname', 'type'=>'string', 'title'=>'The name of the node');
422 $fix_columns[]=array('tagname'=>'peer_id', 'header'=>'AU', 'type'=>'string', 'title'=>'Authority');
423 $fix_columns[]=array('tagname'=>'run_level', 'header'=>'ST', 'type'=>'string', 'title'=>'Status');
424 $fix_columns[]=array('tagname'=>'node_type', 'header'=>'RES', 'type'=>'string', 'title'=>'Reservable');
425
426 // columns that correspond to the visible tags for nodes (*node/ui*)
427 $visibletags = new VisibleTags ($api, 'node');
428 $visibletags->columns();
429 $tag_columns = $visibletags->headers();
430
431 //columns that are not defined as extra myslice tags
432 $extra_columns = array();
433 //MyPLC columns
434 $extra_columns[]=array('tagname'=>'sitename', 'header'=>'SN', 'type'=>'string', 'title'=>'Site name', 'fetched'=>true, 'source'=>'myplc');
435 $extra_columns[]=array('tagname'=>'domain', 'header'=>'DN', 'type'=>'string', 'title'=>'Toplevel domain name', 'fetched'=>true, 'source'=>'myplc');
436 $extra_columns[]=array('tagname'=>'ipaddress', 'header'=>'IP', 'type'=>'string', 'title'=>'IP Address', 'fetched'=>true, 'source'=>'myplc');
437 $extra_columns[]=array('tagname'=>'fcdistro', 'header'=>'OS', 'type'=>'string', 'title'=>'Operating system', 'fetched'=>false, 'source'=>'myplc');
438 $extra_columns[]=array('tagname'=>'date_created', 'header'=>'DA', 'source'=>'myplc', 'type'=>'date', 'title'=>'Date added', 'fetched'=>false);
439 $extra_columns[]=array('tagname'=>'arch', 'header'=>'A', 'source'=>'myplc', 'type'=>'string', 'title'=>'Architecture', 'fetched'=>false);
440 if (plc_is_admin()) {
441 $extra_columns[]=array('tagname'=>'deployment', 'header'=>'DL', 'source'=>'myplc', 'type'=>'string', 'title'=>'Deployment', 'fetched'=>false);
442 }
443
444 //CoMon Live data
445
446 if (MYSLICE_COMON_AVAILABLE)
447 {
448 $extra_columns[]=array('tagname'=>'bwlimit', 'header'=>'BW', 'source'=>'comon', 'type'=>'sortAlphaNumericTop', 'title'=>'Bandwidth limit', 'fetched'=>false);
449 $extra_columns[]=array('tagname'=>'numcores', 'header'=>'CC', 'source'=>'comon', 'type'=>'sortAlphaNumericTop', 'title'=>'Number of CPU Cores', 'fetched'=>false);
450 $extra_columns[]=array('tagname'=>'cpuspeed', 'header'=>'CR', 'source'=>'comon', 'type'=>'sortAlphaNumericTop', 'title'=>'CPU clock rate', 'fetched'=>false);
451 $extra_columns[]=array('tagname'=>'disksize', 'header'=>'DS', 'source'=>'comon', 'type'=>'sortAlphaNumericTop', 'title'=>'Disk size', 'fetched'=>false);
452 $extra_columns[]=array('tagname'=>'gbfree', 'header'=>'DF', 'source'=>'comon', 'type'=>'sortAlphaNumericTop', 'title'=>'Currently available disk space', 'fetched'=>false);
453 $extra_columns[]=array('tagname'=>'memsize', 'header'=>'MS', 'source'=>'comon', 'type'=>'sortAlphaNumericTop', 'title'=>'Memory size', 'fetched'=>false);
454 $extra_columns[]=array('tagname'=>'numslices', 'header'=>'SM', 'source'=>'comon', 'type'=>'sortAlphaNumericTop', 'title'=>'Number of slices in memory', 'fetched'=>false);
455 $extra_columns[]=array('tagname'=>'uptime', 'header'=>'UT', 'source'=>'comon', 'type'=>'sortAlphaNumericTop', 'title'=>'Continuous uptime until now', 'fetched'=>false);
456 }
457
458 //TopHat Live data
459
460 if (MYSLICE_TOPHAT_AVAILABLE)
461 {
462 $extra_columns[]=array('tagname'=>'asn', 'header'=>'AS', 'source'=>'tophat', 'type'=>'string', 'title'=>'AS Number', 'fetched'=>false);
463 $extra_columns[]=array('tagname'=>'city', 'header'=>'LCY', 'source'=>'tophat', 'type'=>'string', 'title'=>'City', 'fetched'=>false);
464 $extra_columns[]=array('tagname'=>'region', 'header'=>'LRN', 'source'=>'tophat', 'type'=>'string', 'title'=>'Region', 'fetched'=>false);
465 $extra_columns[]=array('tagname'=>'country', 'header'=>'LCN', 'source'=>'tophat', 'type'=>'string', 'title'=>'Country', 'fetched'=>false);
466 $extra_columns[]=array('tagname'=>'continent', 'header'=>'LCT', 'source'=>'tophat', 'type'=>'string', 'title'=>'Continent', 'fetched'=>false);
467 //$extra_columns[]=array('tagname'=>'hopcount', 'header'=>'HC', 'source'=>'tophat', 'type'=>'sortAlphaNumericTop', 'title'=>'Hop count from reference node', 'fetched'=>false);
468 ////$extra_columns[]=array('tagname'=>'rtt', 'header'=>'RTT', 'source'=>'tophat', 'type'=>'sortAlphaNumericTop', 'title'=>'Round trip time from reference node', 'fetched'=>false);
469 //////$extra_columns[]=array('tagname'=>'agents', 'header'=>'MA', 'source'=>'tophat', 'type'=>'sortAlphaNumericTop', 'title'=>'Co-located measurement agents', 'fetched'=>true);
470 ////$extra_columns[]=array('tagname'=>'agents_sonoma', 'header'=>'MAS', 'source'=>'tophat', 'type'=>'sortAlphaNumericTop', 'title'=>'Co-located SONoMA agents', 'fetched'=>true);
471 ////$extra_columns[]=array('tagname'=>'agents_etomic', 'header'=>'MAE', 'source'=>'tophat', 'type'=>'sortAlphaNumericTop', 'title'=>'Co-located ETOMIC agents', 'fetched'=>true);
472 ////$extra_columns[]=array('tagname'=>'agents_tdmi', 'header'=>'MAT', 'source'=>'tophat', 'type'=>'sortAlphaNumericTop', 'title'=>'Co-located TDMI agents', 'fetched'=>true);
473 ////$extra_columns[]=array('tagname'=>'agents_dimes', 'header'=>'MAD', 'source'=>'tophat', 'type'=>'sortAlphaNumericTop', 'title'=>'Co-located DIMES agents', 'fetched'=>true);
474 }
475
476
477
478 //Get user's column configuration
479
480 $first_time_configuration = false;
481 $default_configuration = "hostname:f|ST:f|AU:f|RES:f";
482 //$extra_default = "";
483 $column_configuration = "";
484 $slice_column_configuration = "";
485
486 $show_configuration = "";
487
488 $PersonTags=$api->GetPersonTags (array('person_id'=>$plc->person['person_id']));
489 //plc_debug('ptags',$PersonTags);
490 foreach ($PersonTags as $ptag) {
491   if ($ptag['tagname'] == 'columnconf') {
492     $column_configuration = $ptag['value'];
493     $conf_tag_id = $ptag['person_tag_id'];
494   } else if ($ptag['tagname'] == 'showconf') {
495     $show_configuration = $ptag['value'];
496     $show_tag_id = $ptag['person_tag_id'];
497   }
498 }
499
500 $sliceconf_exists = false;
501 if ($column_configuration == "") {
502   $first_time_configuration = true;
503   $column_configuration = $slice_id.";default";
504   $sliceconf_exists = true;
505 } else {
506   $slice_conf = explode(";",$column_configuration);
507   for ($i=0; $i<count($slice_conf); $i++ ) {
508     if ($slice_conf[$i] == $slice_id) {
509       $i++;
510       $slice_column_configuration = $slice_conf[$i];
511       $sliceconf_exists = true;
512       break;
513     } else {
514       $i++;
515       $slice_column_configuration = $slice_conf[$i];
516     }
517   }        
518 }
519
520 if ($sliceconf_exists == false)
521   $column_configuration = $column_configuration.";".$slice_id.";default";
522
523 if ($slice_column_configuration == "")
524   $full_configuration = $default_configuration;
525 else
526   $full_configuration = $default_configuration."|".$slice_column_configuration;
527
528
529 //instantiate the column configuration class, which prepares the headers array
530 $ConfigureColumns =new PlekitColumns($full_configuration, $fix_columns, $tag_columns, $extra_columns);
531
532 $visiblecolumns = $ConfigureColumns->node_tags();
533
534 $node_columns=array_merge($node_fixed_columns,$visiblecolumns);
535 $all_nodes=$api->GetNodes(NULL,$node_columns);
536
537 $ConfigureColumns->fetch_live_data($all_nodes);
538
539 $show_reservable_info = TRUE;
540 $show_layout_info = '1';
541 $show_conf = explode(";",$show_configuration);
542 foreach ($show_conf as $ss) {
543   if ($ss =="reservable")
544     $show_reservable_info = FALSE;
545   else if ($ss =="columns")
546     $show_layout_info = '0';
547 }        
548
549 $slice_nodes=array();
550 $potential_nodes=array();
551 $reservable_nodes=array();
552 foreach ($all_nodes as $node) {
553   if (in_array($node['node_id'],$slice['node_ids'])) {
554     $slice_nodes[]=$node;
555     if ($node['node_type']=='reservable') $reservable_nodes[]=$node;
556   } else {
557     $potential_nodes[]=$node;
558   }
559 }
560 if ($profiling) plc_debug_prof('5: nodes',count($slice_nodes));
561 ////////////////////
562 // outline the number of reservable nodes
563 $nodes_message=count_english($slice_nodes,"node");
564 if (count($reservable_nodes)) $nodes_message .= " (" . count($reservable_nodes) . " reservable)";
565 $toggle=new PlekitToggle ('my-slice-nodes',$nodes_message,
566                           array('bubble'=>
567                                 'Manage nodes attached to this slice',
568                                 'visible'=>get_arg('show_nodes',false)));
569 $toggle->start();
570
571
572 //////////////////// reservable nodes area
573 $leases_info="
574 You have attached one or more reservable nodes to your slice. 
575 Reservable nodes show up with the '$mark' mark. 
576 Your slivers will be available only during timeslots
577 where you have obtained leases. 
578 You can manage your leases in the tab below.
579 <br>
580 This feature is still experimental; feedback is appreciated at <a href='mailto:devel@planet-lab.org'>devel@planet-lab.org</a>
581 ";
582 $count=count($reservable_nodes);
583 if ($count && $privileges) {
584   // having reservable nodes in white lists looks a bit off scope for now...
585   $toggle_nodes=new PlekitToggle('my-slice-nodes-reserve',
586                                  "Leases - " . count($reservable_nodes) . " reservable node(s)",
587                                  array('visible'=>get_arg('show_nodes_resa',false), 
588                                        'info-text'=>$leases_info,
589                                        'info-visible'=>$show_reservable_info));
590   $toggle_nodes->start();
591
592   // get settings from environment, otherwise set to defaults
593   // when to start, in hours in the future from now
594   $leases_offset=$_GET['leases_offset'];
595   if ( ! $leases_offset ) $leases_offset=0;
596   // how many timeslots to show
597   $leases_slots=$_GET['leases_slots'];
598   if ( ! $leases_slots ) $leases_slots = 48;
599   // offset in hours (in the future) from now 
600   $leases_w = $_GET['leases_w'];
601   if ( ! $leases_w) $leases_w=14;
602   // number of timeslots to display
603
604   $granularity=$api->GetLeaseGranularity();
605
606   // these elements are for passing data to the javascript layer
607   echo "<span class='hidden' id='leases_slicename'>" . $slice['name'] . "</span>";
608   echo "<span class='hidden' id='leases_slice_id'>" . $slice['slice_id']. "</span>";
609   echo "<span class='hidden' id='leases_granularity'>" . $granularity . "</span>";
610   // ditto, and editable - very rough for now
611   echo "<div class='center' id='leases_settings'>";
612   echo "<label id='leases_offset_label' class='leases_label'>start, in hours from now</label>";
613   echo "<input type='text' class='leases_input' id='leases_offset_input' value='$leases_offset' />";
614   echo "<label id='leases_slots_label' class='leases_label'># of timeslots</label>";
615   echo "<input type='text' class='leases_input' id='leases_slots_input' value='$leases_slots' />";
616   echo "<label id='leases_w_label' class='leases_label'>slot width, in pixels</label>";
617   echo "<input type='text' class='leases_input' id='leases_w_input' value='$leases_w' />";
618   echo "</div>";
619
620   // leases_data is the name used by leases.js to locate this place
621   // first population will be triggered by init_scheduler from leases.js
622   echo "<table id='leases_data' class='hidden'></table>";
623
624   // the general layout for the scheduler
625   echo <<< EOF
626 <div id='leases_area'></div>
627
628 <div id='leases_buttons'>
629   <button id='leases_refresh' type='submit'>Refresh</button>
630   <button id='leases_submit' type='submit'>Submit</button>
631 </div>
632 EOF;
633
634   $toggle_nodes->end();
635  }
636
637
638 //////////////////// node configuration panel
639 if ($first_time_configuration) 
640 $column_conf_visible = '1';
641 else
642 $column_conf_visible = '0';
643
644 $layout_info='
645 This tab allows you to customize the columns in the node tables,
646 below. Information on the nodes comes from a variety of monitoring
647 sources. If you, as either a user or a provider of monitoring data,
648 would like to see additional columns made available, please send us
649 your request in mail to <a
650 href="mailto:support@myslice.info">support@myslice.info</a>. You can
651 find more information about the MySlice project at <a
652 href="http://trac.myslice.info">http://trac.myslice.info</a>.
653 ';
654 $toggle_nodes=new PlekitToggle('my-slice-nodes-configuration',
655                                "Node table layout",
656                                array('visible'=>$column_conf_visible, 
657                                      'info-text'=>$layout_info,
658                                      'info-visible'=>$show_layout_info));
659 $toggle_nodes->start();
660
661 //usort ($table_headers, create_function('$col1,$col2','return strcmp($col1["header"],$col2["header"]);'));
662 //print("<p>TABLE HEADERS<p>");
663 //print_r($table_headers);
664
665 print("<div id='debug'></div>");
666 print("<input type='hidden' id='slice_id' value='".$slice['slice_id']."' />");
667 print("<input type='hidden' id='person_id' value='".$plc->person['person_id']."' />");
668 print("<input type='hidden' id='conf_tag_id' value='".$conf_tag_id."' />");
669 print("<input type='hidden' id='show_tag_id' value='".$show_tag_id."' />");
670 print("<input type='hidden' id='show_configuration' value='".$show_configuration."' />");
671 print("<input type='hidden' id='column_configuration' value='".$slice_column_configuration."' />");
672 print("<br><input type='hidden' size=80 id='full_column_configuration' value='".$column_configuration."' />");
673 print("<input type='hidden' id='previousConf' value='".$slice_column_configuration."' />");
674 print("<input type='hidden' id='defaultConf' value='".$default_configuration."' />");
675
676 $ConfigureColumns->configuration_panel_html(true);
677
678 $ConfigureColumns->javascript_init();
679
680 $toggle_nodes->end();
681
682
683 $all_sites=$api->GetSites(NULL, array('site_id','login_base'));
684 $site_hash=array();
685 foreach ($all_sites as $tmp_site) $site_hash[$tmp_site['site_id']]=$tmp_site['login_base'];
686
687 $interface_columns=array('ip','node_id','interface_id');
688 $interface_filter=array('is_primary'=>TRUE);
689 $interfaces=$api->GetInterfaces($interface_filter,$interface_columns);
690
691 $interface_hash=array();
692 foreach ($interfaces as $interface) $interface_hash[$interface['node_id']]=$interface;
693
694
695
696
697
698 //////////////////// nodes currently in
699 $toggle_nodes=new PlekitToggle('my-slice-nodes-current',
700                                count_english($slice_nodes,"node") . " currently in $name",
701                                array('visible'=>get_arg('show_nodes_current',!$privileges)));
702 $toggle_nodes->start();
703
704 $headers=array();
705 $notes=array();
706 //$notes=array_merge($notes,$visibletags->notes());
707 $notes [] = "For information about the different columns please see the <b>node table layout</b> tab above or <b>mouse over</b> the column headers";
708
709 /*
710 $headers['peer']='string';
711 $headers['hostname']='string';
712 $short="-S-"; $long=Node::status_footnote(); $type='string'; 
713         $headers[$short]=array('type'=>$type,'title'=>$long); $notes []= "$short = $long";
714 $short=reservable_mark(); $long=reservable_legend(); $type='string';
715         $headers[$short]=array('type'=>$type,'title'=>$long); $notes []= "$short = $long";
716 // the extra tags, configured for the UI
717 $headers=array_merge($headers,$visibletags->headers());
718
719 if ($privileges) $headers[plc_delete_icon()]="none";
720 */
721
722 $edit_header = array();
723 if ($privileges) $edit_header[plc_delete_icon()]="none";
724 $headers = array_merge($ConfigureColumns->get_headers(),$edit_header);
725
726 //print("<p>HEADERS<p>");
727 //print_r($headers);
728
729 $table_options = array('notes'=>$notes,
730                        'search_width'=>15,
731                        'pagesize'=>20,
732                         'configurable'=>true);
733
734 $table=new PlekitTable('nodes',$headers,NULL,$table_options);
735
736 $form=new PlekitForm(l_actions(),array('slice_id'=>$slice['slice_id']));
737 $form->start();
738 $table->start();
739 if ($slice_nodes) foreach ($slice_nodes as $node) {
740   $table->row_start();
741
742 $table->cell($node['node_id'], array('display'=>'none'));
743
744   $table->cell(l_node_obj($node));
745   $peers->cell($table,$node['peer_id']);
746   $run_level=$node['run_level'];
747   list($label,$class) = Node::status_label_class_($node);
748   $table->cell ($label,array('class'=>$class));
749   $table->cell( ($node['node_type']=='reservable')?reservable_mark():"" );
750
751   $hostname=$node['hostname'];
752   $ip=$interface_hash[$node['node_id']]['ip'];
753   $interface_id=$interface_hash[$node['node_id']]['interface_id'];
754
755 //extra columns
756 $node['domain'] = topdomain($hostname);
757 $node['sitename'] = l_site_t($node['site_id'],$site_hash[$node['site_id']]);
758 if ($interface_id)
759         $node['ipaddress'] = l_interface_t($interface_id,$ip);
760   else
761         $node['ipaddress'] = "n/a";
762
763  //foreach ($visiblecolumns as $tagname) $table->cell($node[$tagname]);
764  $ConfigureColumns->cells($table, $node);
765
766   if ($privileges) $table->cell ($form->checkbox_html('node_ids[]',$node['node_id']));
767   $table->row_end();
768 }
769 // actions area
770 if ($privileges) {
771
772   // remove nodes
773   $table->tfoot_start();
774
775   $table->row_start();
776   $table->cell($form->submit_html ("remove-nodes-from-slice","Remove selected"),
777                array('hfill'=>true,'align'=>'right'));
778   $table->row_end();
779  }
780 $table->end();
781 $toggle_nodes->end();
782
783 //////////////////// nodes to add
784 if ($privileges) {
785   $new_potential_nodes = array();
786   if ($potential_nodes) foreach ($potential_nodes as $node) {
787       $emptywl=empty($node['slice_ids_whitelist']);
788       $inwl = (!emptywl) and in_array($slice['slice_id'],$node['slice_ids_whitelist']);
789       if ($emptywl or $inwl)
790         $new_potential_nodes[]=$node;
791   }
792   $potential_nodes=$new_potential_nodes;
793
794   $count=count($potential_nodes);
795   $toggle_nodes=new PlekitToggle('my-slice-nodes-add',
796                                  count_english($potential_nodes,"more node") . " available",
797                                  array('visible'=>get_arg('show_nodes_add',false)));
798   $toggle_nodes->start();
799
800   if ( $potential_nodes ) {
801     $headers=array();
802     $notes=array();
803
804
805 /*
806     $headers['peer']='string';
807     $headers['hostname']='string';
808     $short="-S-"; $long=Node::status_footnote(); $type='string'; 
809         $headers[$short]=array('type'=>$type,'title'=>$long); $notes []= "$short = $long";
810         $short=reservable_mark(); $long=reservable_legend(); $type='string';
811         $headers[$short]=array('type'=>$type,'title'=>$long); $notes []= "$short = $long";
812     // the extra tags, configured for the UI
813     $headers=array_merge($headers,$visibletags->headers());
814     $headers['+']="none";
815 */
816
817     $add_header = array();
818     $add_header['+']="none";
819     $headers = array_merge($ConfigureColumns->get_headers(),$add_header);
820
821     //$notes=array_merge($notes,$visibletags->notes());
822 $notes [] = "For information about the different columns please see the <b>node table layout</b> tab above or <b>mouse over</b> the column headers";
823     
824     $table=new PlekitTable('add_nodes',$headers,NULL, $table_options);
825     $form=new PlekitForm(l_actions(),
826                          array('slice_id'=>$slice['slice_id']));
827     $form->start();
828     $table->start();
829     if ($potential_nodes) foreach ($potential_nodes as $node) {
830         $table->row_start();
831
832 $table->cell($node['node_id'], array('display'=>'none'));
833
834         $table->cell(l_node_obj($node));
835         $peers->cell($table,$node['peer_id']);
836         list($label,$class) = Node::status_label_class_($node);
837         $table->cell ($label,array('class'=>$class));
838         $table->cell( ($node['node_type']=='reservable')?reservable_mark():"" );
839
840         //extra columns
841           $hostname=$node['hostname'];
842           $ip=$interface_hash[$node['node_id']]['ip'];
843           $interface_id=$interface_hash[$node['node_id']]['interface_id'];
844         $node['domain'] = topdomain($hostname);
845         $node['sitename'] = l_site_t($node['site_id'],$site_hash[$node['site_id']]);
846         $node['ipaddress'] = l_interface_t($interface_id,$ip);
847
848         //foreach ($visiblecolumns as $tagname) $table->cell($node[$tagname]);
849         $ConfigureColumns->cells($table, $node);
850
851         $table->cell ($form->checkbox_html('node_ids[]',$node['node_id']));
852         $table->row_end();
853       }
854     // add nodes
855     $table->tfoot_start();
856     $table->row_start();
857     $table->cell($form->submit_html ("add-nodes-in-slice","Add selected"),
858                  array('hfill'=>true,'align'=>'right'));
859     $table->row_end();
860     $table->end();
861     $form->end();
862   }
863   $toggle_nodes->end();
864 }
865
866 $toggle->end();
867
868 //////////////////////////////////////// retrieve all slice tags
869 $tags=$api->GetSliceTags (array('slice_id'=>$slice_id));
870 //////////////////////////////////////////////////////////// tab:initscripts
871 // xxx fixme
872 // * add a message on how to use this:
873 // * explain the 2 mechanisms (initscript_code, initscript)
874 // * explain the interface : initscript start|stop|restart slicename
875 // xxx fixme
876
877 $initscript_info="
878 There are two ways to attach an initscript to a slice:<ul>
879
880 <li> <span class='bold'> Shared initscripts </span> are global to the
881 MyPLC, and managed by the Operations Team. For that reason, regular
882 users cannot change these scripts, but can reference one of the
883 available names in the drop down below.  </li>
884
885 <li> You also have the option to provide <span class='bold'> your own
886 code </span>, with the following conventions: <ul>
887
888 <li> Like regular initscripts, your script must except to receive as a
889 first argument <span class='bold'> start </span>, <span class='bold'>
890 stop </span> or <span class='bold'> restart </span>. It is important
891 to honor this argument, as your slice may be stopped and restarted at
892 any time; also this is used whenever the installed code gets changed.
893 </li>
894
895 <li> As a second argument, you will receive the slicename; in most
896 cases this can be safely ignored.  </li>
897
898 </ul>
899 </li>
900  </ul>
901 The slice-specific setting has precedence on a shared initscript.
902 ";
903
904 $shared_initscripts=$api->GetInitScripts(array('-SORT'=>'name'),array('name'));
905 //$shared_initscripts=$api->GetInitScripts();
906 if ($profiling) plc_debug_prof('6 initscripts',count($initscripts));
907 // xxx expose this even on foreign slices for now
908 if ($local_peer) {
909   $initscript='';
910   $initscript_code='';
911   if ($tags) foreach ($tags as $tag) {
912       if ($tag['tagname']=='initscript') {
913         if ($initscript!='') drupal_set_error("multiple occurrences of 'initscript' tag");
914         $initscript=$tag['value'];
915       }
916       if ($tag['tagname']=='initscript_code') {
917         if ($initscript_code!='') drupal_set_error("multiple occurrences of 'initscript_code' tag");
918         $initscript_code=$tag['value'];
919         // plc_debug_txt('retrieved body',$initscript_code);
920       }
921     }
922   $label="No initscript";
923   $trimmed=trim($initscript_code);
924   if (!empty($trimmed)) $label="Initscript : slice-specific (" . substr($initscript_code,0,20) . " ...)";
925   else if (!empty($initscript)) $label="Initscript: shared " . $initscript;
926
927   $toggle = new PlekitToggle('slice-initscripts',$label,
928                              array('bubble'=>'Manage initscript on that slice',
929                                    'visible'=>get_arg('show_initscripts',false),
930                                    'info-text'=>$initscript_info
931                                    // not messing with persontags to guess whether this should be displayed or not
932                                    // hopefully some day toggle will know how to handle that using web storage
933                                    ));
934   $toggle->start();
935
936   $details=new PlekitDetails(TRUE);
937   // we expose the previous values so that actions.php can know if changes are really needed
938   // the code needs to be encoded as it may contain any character
939   // as far as the code, this does not work too well b/c what actions.php receives
940   // seems to have spurrious \r chars, and the comparison between old and new values 
941   // is not reliable, which results in changes being made although the code hasn't changed
942   // hve spent too much time on this, good enough for now...
943   $details->form_start(l_actions(),array('action'=>'update-initscripts',
944                                          'slice_id'=>$slice_id,
945                                          'name'=>$name,
946                                          'previous-initscript'=>$initscript,
947                                          'previous-initscript-code'=>htmlentities($initscript_code)));
948   $details->start();
949   // comppute a pulldown with available names
950   $selectors=array();
951   $is_found=FALSE;
952   if ($shared_initscripts) foreach ($shared_initscripts as $is) {
953       $is_selector=array('display'=>$is['name'],'value'=>$is['name']);
954       if ($is['name']==$initscript) {
955         $is_selector['selected']=TRUE;
956         $is_found=TRUE;
957       }
958       $selectors[]=$is_selector;
959     }
960   // display a warning when initscript references an unknown script
961   $details->tr_submit('unused','Update initscripts');
962   ////////// by name
963   $details->th_td("shared initscript name",
964                   $details->form()->select_html('initscript',$selectors,array('label'=>'none')),
965                   'initscript',
966                   array('input_type'=>'select'));
967   if ($initscript && ! $is_found) 
968     // xxx better rendering ?
969     $details->th_td('WARNING',plc_warning_html("Current name '" . $initscript . "' is not a known shared initscript name"));
970   ////////// by contents
971   $script_height=8;
972   $script_width=60;
973   if ($initscript_code) {
974     $text=explode("\n",$initscript_code);
975     $script_height=count($text);
976     $script_width=10;
977     foreach ($text as $line) $script_width=max($script_width,strlen($line));
978   }
979   $details->th_td('slice initscript',$initscript_code,'initscript-code',
980                   array('input_type'=>'textarea', 'width'=>$script_width,'height'=>$script_height));
981   $details->tr_submit('unused','Update initscripts');
982   $details->form_end();
983   $details->end();  
984   $toggle->end();
985 }
986
987 //////////////////////////////////////////////////////////// tab:tags
988 // very wide values get abbreviated
989 $tag_value_threshold=24;
990 // xxx fixme
991 // * this area could use a help message about some special tags:
992 // * initscript-related should be taken out
993 // * sliverauth-related (ssh_key & hmac) should have a toggle to hide or show
994 // xxx fixme
995
996 // xxx expose this even on foreign slices for now
997 //if ( $local_peer ) {
998   if ($profiling) plc_debug_prof('7 slice tags',count($tags));
999   function get_tagname ($tag) { return $tag['tagname'];}
1000   $tagnames = array_map ("get_tagname",$tags);
1001   
1002   $toggle = new PlekitToggle ('slice-tags',count_english_warning($tags,'tag'),
1003                               array('bubble'=>'Inspect and set tags on that slice',
1004                                     'visible'=>get_arg('show_tags',false)));
1005   $toggle->start();
1006   
1007   $headers=array(
1008     "Name"=>"string",
1009     "Value"=>"string",
1010     "Node"=>"string",
1011     "NodeGroup"=>"string");
1012   if ($tags_privileges) $headers[plc_delete_icon()]="none";
1013   
1014   $table_options=array("notes_area"=>false,"pagesize_area"=>false,"search_width"=>10);
1015   $table=new PlekitTable("slice_tags",$headers,'0',$table_options);
1016   $form=new PlekitForm(l_actions(),
1017                        array('slice_id'=>$slice['slice_id']));
1018   $form->start();
1019   $table->start();
1020   if ($tags) {
1021     // Get hostnames for nodes in a single pass
1022     $_node_ids = array();
1023     foreach ($tags as $tag) {
1024       if ($tag['node_id']) {
1025         array_push($_node_ids, $tag['node_id']);
1026       }
1027     }
1028     $_nodes = $api->GetNodes(array('node_id' => $_node_ids), array('node_id', 'hostname'));
1029     $_hostnames = array();
1030     foreach ($_nodes as $_node) {
1031       $_hostnames[$_node['node_id']] = $_node['hostname'];
1032     }
1033
1034     // Loop through tags again to display
1035     foreach ($tags as $tag) {
1036       $node_name = "ALL";
1037       if ($tag['node_id']) {
1038         $node_name = $_hostnames[$tag['node_id']];
1039       }
1040       $nodegroup_name="n/a";
1041       if ($tag['nodegroup_id']) { 
1042         $nodegroups=$api->GetNodeGroups(array('nodegroup_id'=>$tag['nodegroup_id']));
1043         if ($profiling) plc_debug_prof('8 nodegroup for slice tag',$nodegroup);
1044         if ($nodegroup) {
1045           $nodegroup = $nodegroups[0];
1046           $nodegroup_name = $nodegroup['groupname'];
1047         }
1048       }
1049       $table->row_start();
1050       $table->cell(l_tag_obj($tag));
1051       // very wide values get abbreviated
1052       $table->cell(truncate_and_popup($tag['value'],$tag_value_threshold));
1053       $table->cell($node_name);
1054       $table->cell($nodegroup_name);
1055       if ($tags_privileges) $table->cell ($form->checkbox_html('slice_tag_ids[]',$tag['slice_tag_id']));
1056       $table->row_end();
1057     }
1058   }
1059   if ($tags_privileges) {
1060     $table->tfoot_start();
1061     $table->row_start();
1062     $table->cell($form->submit_html ("delete-slice-tags","Remove selected"),
1063                  array('hfill'=>true,'align'=>'right'));
1064     $table->row_end();
1065     
1066     $table->row_start();
1067     function tag_selector ($tag) {
1068       return array("display"=>$tag['tagname'],"value"=>$tag['tag_type_id']);
1069     }
1070     $all_tags= $api->GetTagTypes( array ("category"=>"slice*","-SORT"=>"+tagname"), array("tagname","tag_type_id"));
1071     if ($profiling) plc_debug_prof('9 tagtypes',count($all_tags));
1072     $selector_tag=array_map("tag_selector",$all_tags);
1073     
1074     function node_selector($node) { 
1075       return array("display"=>$node["hostname"],"value"=>$node['node_id']);
1076     }
1077     $selector_node=array_map("node_selector",$slice_nodes);
1078     
1079     function nodegroup_selector($ng) {
1080       return array("display"=>$ng["groupname"],"value"=>$ng['nodegroup_id']);
1081     }
1082     $all_nodegroups = $api->GetNodeGroups( array("groupname"=>"*"), array("groupname","nodegroup_id"));
1083     if ($profiling) plc_debug_prof('10 nodegroups',count($all_nodegroups));
1084     $selector_nodegroup=array_map("nodegroup_selector",$all_nodegroups);
1085     
1086     $table->cell($form->select_html("tag_type_id",$selector_tag,array('label'=>"Choose Tag")));
1087     $table->cell($form->text_html("value","",array('width'=>8)));
1088     $table->cell($form->select_html("node_id",$selector_node,array('label'=>"All Nodes")));
1089     $table->cell($form->select_html("nodegroup_id",$selector_nodegroup,array('label'=>"No Nodegroup")));
1090     $table->cell($form->submit_html("add-slice-tag","Set Tag"),array('columns'=>2,'align'=>'left'));
1091     $table->row_end();
1092   }
1093     
1094   $table->end();
1095   $form->end();
1096   $toggle->end();
1097 //}
1098
1099
1100 //////////////////////////////////////////////////////////// tab:renew
1101 if ($local_peer ) {
1102   if ( ! $renew_visible) renew_area ($slice,$site,false);
1103  }
1104
1105 $peers->block_end($peer_id);
1106
1107 if ($profiling) plc_debug_prof_end();
1108
1109 // Print footer
1110 include 'plc_footer.php';
1111
1112 ?>