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