add more profiling points
[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 //error_reporting(0);
34
35 $profiling=false;
36 if ($_GET['profiling']) $profiling=true;
37
38 if ($profiling)  plc_debug_prof_start();
39
40 // -------------------- 
41 // recognized URL arguments
42 $slice_id=intval($_GET['id']);
43 if ( ! $slice_id ) { plc_error('Malformed URL - id not set'); return; }
44
45 ////////////////////
46 // have to name columns b/c we need the non-native 'omf_control' column
47 $slice_columns=array('slice_id','name','peer_id','site_id','person_ids','node_ids','expires',
48                      'url','description','instantiation','omf_control');
49 $slices= $api->GetSlices( array($slice_id), $slice_columns);
50
51 if (empty($slices)) {
52   drupal_set_message ("Slice " . $slice_id . " not found");
53   return;
54  }
55
56 $slice=$slices[0];
57
58 if ($profiling) plc_debug_prof('01: slice',count($slices));
59 // pull all node info to vars
60 $name= $slice['name'];
61 $expires = date( "d/m/Y", $slice['expires'] );
62 $site_id= $slice['site_id'];
63
64 $person_ids=$slice['person_ids'];
65
66 // get peers
67 $peer_id= $slice['peer_id'];
68 $peers=new Peers ($api);
69 $local_peer = ! $peer_id;
70
71 if ($profiling) plc_debug_prof('02: peers',count($peers));
72
73 // gets site info
74 $sites= $api->GetSites( array( $site_id ) );
75 $site=$sites[0];
76 $site_name= $site['name'];
77 $max_slices = $site['max_slices'];
78
79 if ($profiling) plc_debug_prof('03: sites',count($sites));
80 //////////////////////////////////////// building blocks for the renew area
81 // Constants
82 global $DAY;            $DAY = 24*60*60;
83 global $WEEK;           $WEEK = 7 * $DAY; 
84 global $MAX_WEEKS;      $MAX_WEEKS= 8;          // weeks from today
85 global $GRACE_DAYS;     $GRACE_DAYS=10;         // days for renewal promoted on top
86 global $NOW;            $NOW=mktime();
87
88 //////////////////////////////////////////////////////////// utility for the renew tab
89 // make the renew area on top and open if the expiration time is less than 10 days from now
90 function renew_needed ($slice) {
91   global $DAY, $NOW, $GRACE_DAYS;
92   $current_exp=$slice['expires'];
93
94   $time_left = $current_exp - $NOW;
95   $visible = $time_left/$DAY <= $GRACE_DAYS;
96   return $visible;
97 }
98
99 function renew_area ($slice,$site,$visible) {
100   global $DAY, $WEEK, $MAX_WEEKS, $GRACE_DAYS, $NOW;
101  
102   $current_exp=$slice['expires'];
103   $current_text = gmstrftime("%A %b-%d-%y %T %Z", $current_exp);
104   $max_exp= $NOW + ($MAX_WEEKS * $WEEK); // seconds since epoch
105   $max_text = gmstrftime("%A %b-%d-%y %T %Z", $max_exp);
106
107   // xxx some extra code needed to enable this area only if the slice description is OK:
108   // description and url must be non void
109   $toggle=
110     new PlekitToggle('renew',"Expires $current_text - Renew this slice",
111                      array("bubble"=>
112                            "Enter this zone if you wish to renew your slice",
113                            'visible'=>$visible));
114   $toggle->start();
115
116   // xxx message could take roles into account
117   if ($site['max_slices']<=0) {
118      $message= <<< EOF
119 <p class='my-slice-renewal'>Slice creation and renewal have been temporarily disabled for your
120 <site. This may have occurred because your site's nodes have been down
121 or unreachable for several weeks, and multiple attempts to contact
122 your site's PI(s) and Technical Contact(s) have all failed. If so,
123 contact your site's PI(s) and Technical Contact(s) and ask them to
124 bring up your site's nodes. Please visit your <a
125 href='/db/sites/index.php?id=$site_id'>site details</a> page to find
126 out more about your site's nodes, and how to contact your site's PI(s)
127 and Technical Contact(s).</p>
128 EOF;
129      echo $message;
130  
131   } else {
132     // xxx this is a rough cut and paste from the former UI
133     // showing a datepicker view could be considered as well with some extra work
134     // calculate possible extension lengths
135     $selectors = array();
136     foreach ( array ( 1 => "One more week", 
137                       2 => "Two more weeks", 
138                       3 => "Three more weeks", 
139                       4 => "One more month" ) as $weeks => $text ) {
140       $candidate_exp = $current_exp + $weeks*$WEEK;
141       if ( $candidate_exp < $max_exp) {
142         $selectors []= array('display'=>"$text (" . gmstrftime("%A %b-%d-%y %T %Z", $candidate_exp) . ")",
143                              'value'=>$candidate_exp);
144         $max_renewal_weeks=$weeks;
145         $max_renewal_date= gmstrftime("%A %b-%d-%y %T %Z", $candidate_exp);
146       }
147     }
148
149     if ( empty( $selectors ) ) {
150       print <<< EOF
151 <div class='my-slice-renewal'>
152 Slices cannot be renewed more than $MAX_WEEKS weeks from now, i.e. not beyond $max_text. 
153 For this reason, the current slice cannot be renewed any further into the future, try again closer to expiration date.
154 </div>
155 EOF;
156      } else {
157       print <<< EOF
158 <div class='my-slice-renewal'>
159 <span class='bold'>Important:</span> Please take this opportunity to review and update your slice information in the Details tab.
160 <p>
161 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.
162 </p><p>
163 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. 
164 </p>
165 EOF;
166
167       $form = new PlekitForm (l_actions(),
168                               array('action'=>'renew-slice',
169                                     'slice_id'=>$slice['slice_id']));
170       $form->start();
171       print $form->label_html('expires','Duration:&nbsp;');
172       print $form->select_html('expires',$selectors,array('label'=>'Pick one'));
173       print $form->submit_html('renew-button','Renew');
174       $form->end();
175
176 print("<p><i>NOTE: Slices cannot be renewed beyond another $max_renewal_weeks week(s) ($max_renewal_date).</i>  </p>");
177 print ("</div>");
178     }
179   }
180  
181   $toggle->end();
182 }
183
184 ////////////////////////////////////////////////////////////
185
186 $am_in_slice = in_array(plc_my_person_id(),$person_ids);
187
188 if ($am_in_slice) {
189   drupal_set_title("My slice " . $name);
190  } else {
191   drupal_set_title("Slice " . $name);
192 }
193
194 $privileges = ( $local_peer && (plc_is_admin()  || plc_is_pi() || $am_in_slice));
195 $tags_privileges = $privileges || plc_is_admin();
196
197 $tabs=array();
198 $tabs [] = tab_nodes_slice($slice_id);
199 $tabs [] = tab_site($site);
200
201 // are these the right privileges for deletion ?
202 if ($privileges) {
203   $tabs ['Delete']= array('url'=>l_actions(),
204                           'method'=>'post',
205                           'values'=>array('action'=>'delete-slice','slice_id'=>$slice_id),
206                           'bubble'=>"Delete slice $name",
207                           'confirm'=>"Are you sure to delete slice $name");
208
209   $tabs["Events"]=array_merge(tablook_event(),
210                               array('url'=>l_event("Slice","slice",$slice_id),
211                                     'bubble'=>"Events for slice $name"));
212   $tabs["Comon"]=array_merge(tablook_comon(),
213                              array('url'=>l_comon("slice_id",$slice_id),
214                                    'bubble'=>"Comon page about slice $name"));
215 }
216
217 plekit_linetabs($tabs);
218
219 ////////////////////////////////////////
220 $peers->block_start($peer_id);
221
222 //////////////////////////////////////// renewal area 
223 // (1) close to expiration : show on top and open
224
225 if ($local_peer ) {
226   $renew_visible = renew_needed ($slice);
227   if ($renew_visible) renew_area ($slice,$site,true);
228  }
229
230
231 //////////////////////////////////////////////////////////// tab:details
232 $toggle = 
233   new PlekitToggle ('my-slice-details',"Details",
234                     array('bubble'=>
235                           'Display and modify details for that slice',
236                           'visible'=>get_arg('show_details')));
237 $toggle->start();
238
239 $details=new PlekitDetails($privileges);
240 $details->form_start(l_actions(),array('action'=>'update-slice',
241                                        'slice_id'=>$slice_id,
242                                        'name'=>$name));
243
244 $details->start();
245 if (! $local_peer) {
246   $details->th_td("Peer",$peers->peer_link($peer_id));
247   $details->space();
248  }
249
250
251 $details->th_td('Name',$slice['name']);
252 $details->th_td('Description',$slice['description'],'description',
253                 array('input_type'=>'textarea',
254                       'width'=>50,'height'=>5));
255 $details->th_td('URL',$slice['url'],'url',array('width'=>50));
256 $details->tr_submit("submit","Update Slice");
257 $details->th_td('Expires',$expires);
258 $details->th_td('Instantiation',$slice['instantiation']);
259 $details->th_td("OMF-friendly", ($slice['omf_control'] ? 'Yes' : 'No') . " [to change: see 'omf_control' in the tags section below]");
260 $details->th_td('Site',l_site_obj($site));
261 // xxx show the PIs here
262 //$details->th_td('PIs',...);
263 $details->end();
264
265 $details->form_end();
266 $toggle->end();
267
268 //////////////////////////////////////////////////////////// tab:persons
269 $person_columns = array('email','person_id','first_name','last_name','roles');
270 // get persons in slice
271 if (!empty($person_ids))
272   $persons=$api->GetPersons(array('person_id'=>$slice['person_ids']),$person_columns);
273 // just propose to add everyone else
274 // xxx this is maybe too much for admins as it slows stuff down 
275 // as regular persons can see only a fraction of the db anyway
276 $potential_persons=
277   $api->GetPersons(array('~person_id'=>$slice['person_ids'],
278                          'peer_id'=>NULL,
279                          'enabled'=>true),
280                    $person_columns);
281 $count=count($persons);
282
283 if ($profiling) plc_debug_prof('04: persons',count($persons));
284 $toggle=
285   new PlekitToggle ('my-slice-persons',"$count users",
286                     array('bubble'=>
287                           'Manage accounts attached to this slice',
288                           'visible'=>get_arg('show_persons')));
289 $toggle->start();
290
291 ////////// people currently in
292 // visible:
293 // hide if both current+add are included
294 // so user can chose which section is of interest
295 // show otherwise
296 $toggle_persons = new PlekitToggle ('my-slice-persons-current',
297                                     "$count people currently in $name",
298                                     array('visible'=>get_arg('show_persons_current')));
299 $toggle_persons->start();
300
301 $headers=array();
302 $headers['email']='string';
303 $headers['first']='string';
304 $headers['last']='string';
305 $headers['R']='string';
306 if ($privileges) $headers[plc_delete_icon()]="none";
307 $table=new PlekitTable('persons',$headers,'0',
308                        array('notes_area'=>false));
309 $form=new PlekitForm(l_actions(),array('slice_id'=>$slice['slice_id']));
310 $form->start();
311 $table->start();
312 if ($persons) foreach ($persons as $person) {
313   $table->row_start();
314   $table->cell(l_person_obj($person));
315   $table->cell($person['first_name']);
316   $table->cell($person['last_name']);
317   $table->cell(plc_vertical_table ($person['roles']));
318   if ($privileges) $table->cell ($form->checkbox_html('person_ids[]',$person['person_id']));
319   $table->row_end();
320 }
321 // actions area
322 if ($privileges) {
323
324   // remove persons
325   $table->tfoot_start();
326
327   $table->row_start();
328   $table->cell($form->submit_html ("remove-persons-from-slice","Remove selected"),
329                array('hfill'=>true,'align'=>'right'));
330   $table->row_end();
331  }
332 $table->end();
333 $toggle_persons->end();
334
335 ////////// people to add
336 if ($privileges) {
337   $count=count($potential_persons);
338   $toggle_persons = new PlekitToggle ('my-slice-persons-add',
339                                       "$count people may be added to $name",
340                                       array('visible'=>get_arg('show_persons_add')));
341   $toggle_persons->start();
342   if ( ! $potential_persons ) {
343     // xxx improve style
344     echo "<p class='not-relevant'>No person to add</p>";
345   } else {
346     $headers=array();
347     $headers['email']='string';
348     $headers['first']='string';
349     $headers['last']='string';
350     $headers['R']='string';
351     $headers['+']="none";
352     $options = array('notes_area'=>false,
353                      'search_width'=>15,
354                      'pagesize'=>8);
355     // show search for admins only as other people won't get that many names to add
356     if ( ! plc_is_admin() ) $options['search_area']=false;
357     
358     $table=new PlekitTable('add_persons',$headers,'0',$options);
359     $form=new PlekitForm(l_actions(),array('slice_id'=>$slice['slice_id']));
360     $form->start();
361     $table->start();
362     if ($potential_persons) foreach ($potential_persons as $person) {
363         $table->row_start();
364         $table->cell(l_person_obj($person));
365         $table->cell($person['first_name']);
366         $table->cell($person['last_name']);
367         $table->cell(plc_vertical_table ($person['roles']));
368         $table->cell ($form->checkbox_html('person_ids[]',$person['person_id']));
369         $table->row_end();
370       }
371     // add users
372     $table->tfoot_start();
373     $table->row_start();
374     $table->cell($form->submit_html ("add-persons-in-slice","Add selected"),
375                  array('hfill'=>true,'align'=>'right'));
376     $table->row_end();
377     $table->end();
378     $form->end();
379   }
380   $toggle_persons->end();
381 }
382 $toggle->end();
383
384 //////////////////////////////////////////////////////////// tab:nodes
385 // the nodes details to display here
386 // (1) we search for the tag types for which 'category' matches 'node*/ui*'
387 // all these tags will then be tentatively displayed in this area
388 // (2) further information can also be optionally specified in the category:
389 //     (.) we split the category with '/' and search for assignments of the form var=value
390 //     (.) header can be set to supersede the column header (default is tagname)
391 //     (.) rank can be used for ordering the columns (default is tagname)
392 //     (.) type is passed to the javascript table, for sorting (default is 'string')
393
394 // minimal list as a start
395 $node_fixed_columns = array('hostname','node_id','peer_id','slice_ids_whitelist', 'site_id',
396                             'run_level','boot_state','last_contact','node_type');
397 // create a VisibleTags object : basically the list of tag columns to show
398 //$visibletags = new VisibleTags ($api, 'node');
399 //$visiblecolumns = $visibletags->column_names();
400
401 // optimizing calls to GetNodes
402 //$all_nodes=$api->GetNodes(NULL,$node_columns);
403 //$slice_nodes=$api->GetNodes(array('node_id'=>$slice['node_ids']),$node_columns);
404 //$potential_nodes=$api->GetNodes(array('~node_id'=>$slice['node_ids']),$node_columns);
405
406
407 //NEW CODE FOR ENABLING COLUMN CONFIGURATION
408
409 //prepare fix and configurable columns
410
411 $fix_columns = array();
412 $fix_columns[]=array('tagname'=>'hostname', 'header'=>'hostname', 'type'=>'string', 'title'=>'The name of the node');
413 $fix_columns[]=array('tagname'=>'peer_id', 'header'=>'AU', 'type'=>'string', 'title'=>'Authority');
414 $fix_columns[]=array('tagname'=>'run_level', 'header'=>'ST', 'type'=>'string', 'title'=>'Status');
415 $fix_columns[]=array('tagname'=>'node_type', 'header'=>'RES', 'type'=>'string', 'title'=>'Reservable');
416
417 // columns that correspond to the visible tags for nodes (*node/ui*)
418 $visibletags = new VisibleTags ($api, 'node');
419 $visibletags->columns();
420 $tag_columns = $visibletags->headers();
421
422 //columns that are not defined as extra myslice tags
423 $extra_columns = array();
424 //MyPLC columns
425 $extra_columns[]=array('tagname'=>'sitename', 'header'=>'SN', 'type'=>'string', 'title'=>'Site name', 'fetched'=>true, 'source'=>'myplc');
426 $extra_columns[]=array('tagname'=>'domain', 'header'=>'DN', 'type'=>'string', 'title'=>'Toplevel domain name', 'fetched'=>true, 'source'=>'myplc');
427 $extra_columns[]=array('tagname'=>'ipaddress', 'header'=>'IP', 'type'=>'string', 'title'=>'IP Address', 'fetched'=>true, 'source'=>'myplc');
428 $extra_columns[]=array('tagname'=>'fcdistro', 'header'=>'OS', 'type'=>'string', 'title'=>'Operating system', 'fetched'=>false, 'source'=>'myplc');
429 $extra_columns[]=array('tagname'=>'date_created', 'header'=>'DA', 'source'=>'myplc', 'type'=>'date', 'title'=>'Date added', 'fetched'=>false);
430 $extra_columns[]=array('tagname'=>'arch', 'header'=>'A', 'source'=>'myplc', 'type'=>'string', 'title'=>'Architecture', 'fetched'=>false);
431 if (plc_is_admin()) {
432 $extra_columns[]=array('tagname'=>'deployment', 'header'=>'DL', 'source'=>'myplc', 'type'=>'string', 'title'=>'Deployment', 'fetched'=>false);
433 }
434
435 //CoMon Live data
436
437 if (MYSLICE_COMON_AVAILABLE)
438 {
439 $extra_columns[]=array('tagname'=>'bwlimit', 'header'=>'BW', 'source'=>'comon', 'type'=>'sortAlphaNumericTop', 'title'=>'Bandwidth limit', 'fetched'=>false);
440 $extra_columns[]=array('tagname'=>'numcores', 'header'=>'CC', 'source'=>'comon', 'type'=>'sortAlphaNumericTop', 'title'=>'Number of CPU Cores', 'fetched'=>false);
441 $extra_columns[]=array('tagname'=>'cpuspeed', 'header'=>'CR', 'source'=>'comon', 'type'=>'sortAlphaNumericTop', 'title'=>'CPU clock rate', 'fetched'=>false);
442 $extra_columns[]=array('tagname'=>'disksize', 'header'=>'DS', 'source'=>'comon', 'type'=>'sortAlphaNumericTop', 'title'=>'Disk size', 'fetched'=>false);
443 $extra_columns[]=array('tagname'=>'gbfree', 'header'=>'DF', 'source'=>'comon', 'type'=>'sortAlphaNumericTop', 'title'=>'Currently available disk space', 'fetched'=>false);
444 $extra_columns[]=array('tagname'=>'memsize', 'header'=>'MS', 'source'=>'comon', 'type'=>'sortAlphaNumericTop', 'title'=>'Memory size', 'fetched'=>false);
445 $extra_columns[]=array('tagname'=>'numslices', 'header'=>'SM', 'source'=>'comon', 'type'=>'sortAlphaNumericTop', 'title'=>'Number of slices in memory', 'fetched'=>false);
446 $extra_columns[]=array('tagname'=>'uptime', 'header'=>'UT', 'source'=>'comon', 'type'=>'sortAlphaNumericTop', 'title'=>'Continuous uptime until now', 'fetched'=>false);
447 }
448
449 //TopHat Live data
450
451 if (MYSLICE_TOPHAT_AVAILABLE)
452 {
453 $extra_columns[]=array('tagname'=>'asn', 'header'=>'AS', 'source'=>'tophat', 'type'=>'string', 'title'=>'AS Number', 'fetched'=>false);
454 $extra_columns[]=array('tagname'=>'city', 'header'=>'LCY', 'source'=>'tophat', 'type'=>'string', 'title'=>'City', 'fetched'=>false);
455 $extra_columns[]=array('tagname'=>'region', 'header'=>'LRN', 'source'=>'tophat', 'type'=>'string', 'title'=>'Region', 'fetched'=>false);
456 $extra_columns[]=array('tagname'=>'country', 'header'=>'LCN', 'source'=>'tophat', 'type'=>'string', 'title'=>'Country', 'fetched'=>false);
457 $extra_columns[]=array('tagname'=>'continent', 'header'=>'LCT', 'source'=>'tophat', 'type'=>'string', 'title'=>'Continent', 'fetched'=>false);
458 //$extra_columns[]=array('tagname'=>'hopcount', 'header'=>'HC', 'source'=>'tophat', 'type'=>'sortAlphaNumericTop', 'title'=>'Hop count from reference node', 'fetched'=>false);
459 ////$extra_columns[]=array('tagname'=>'rtt', 'header'=>'RTT', 'source'=>'tophat', 'type'=>'sortAlphaNumericTop', 'title'=>'Round trip time from reference node', 'fetched'=>false);
460 //////$extra_columns[]=array('tagname'=>'agents', 'header'=>'MA', 'source'=>'tophat', 'type'=>'sortAlphaNumericTop', 'title'=>'Co-located measurement agents', 'fetched'=>true);
461 ////$extra_columns[]=array('tagname'=>'agents_sonoma', 'header'=>'MAS', 'source'=>'tophat', 'type'=>'sortAlphaNumericTop', 'title'=>'Co-located SONoMA agents', 'fetched'=>true);
462 ////$extra_columns[]=array('tagname'=>'agents_etomic', 'header'=>'MAE', 'source'=>'tophat', 'type'=>'sortAlphaNumericTop', 'title'=>'Co-located ETOMIC agents', 'fetched'=>true);
463 ////$extra_columns[]=array('tagname'=>'agents_tdmi', 'header'=>'MAT', 'source'=>'tophat', 'type'=>'sortAlphaNumericTop', 'title'=>'Co-located TDMI agents', 'fetched'=>true);
464 ////$extra_columns[]=array('tagname'=>'agents_dimes', 'header'=>'MAD', 'source'=>'tophat', 'type'=>'sortAlphaNumericTop', 'title'=>'Co-located DIMES agents', 'fetched'=>true);
465 }
466
467
468
469 //Get user's column configuration
470
471 $first_time_configuration = false;
472 $default_configuration = "hostname:f|ST:f|AU:f|RES:f";
473 //$extra_default = "";
474 $column_configuration = "";
475 $slice_column_configuration = "";
476
477 $show_configuration = "";
478
479 $PersonTags=$api->GetPersonTags (array('person_id'=>$plc->person['person_id']));
480 //plc_debug('ptags',$PersonTags);
481 foreach ($PersonTags as $ptag) {
482   if ($ptag['tagname'] == 'columnconf') {
483     $column_configuration = $ptag['value'];
484     $conf_tag_id = $ptag['person_tag_id'];
485   } else if ($ptag['tagname'] == 'showconf') {
486     $show_configuration = $ptag['value'];
487     $show_tag_id = $ptag['person_tag_id'];
488   }
489 }
490
491 $sliceconf_exists = false;
492 if ($column_configuration == "") {
493   $first_time_configuration = true;
494   $column_configuration = $slice_id.";default";
495   $sliceconf_exists = true;
496 } else {
497   $slice_conf = explode(";",$column_configuration);
498   for ($i=0; $i<count($slice_conf); $i++ ) {
499     if ($slice_conf[$i] == $slice_id) {
500       $i++;
501       $slice_column_configuration = $slice_conf[$i];
502       $sliceconf_exists = true;
503       break;
504     } else {
505       $i++;
506       $slice_column_configuration = $slice_conf[$i];
507     }
508   }        
509 }
510
511 if ($sliceconf_exists == false)
512   $column_configuration = $column_configuration.";".$slice_id.";default";
513
514 if ($slice_column_configuration == "")
515   $full_configuration = $default_configuration;
516 else
517   $full_configuration = $default_configuration."|".$slice_column_configuration;
518
519
520 //instantiate the column configuration class, which prepares the headers array
521 $ConfigureColumns =new PlekitColumns($full_configuration, $fix_columns, $tag_columns, $extra_columns);
522
523 $visiblecolumns = $ConfigureColumns->node_tags();
524
525 $node_columns=array_merge($node_fixed_columns,$visiblecolumns);
526 $all_nodes=$api->GetNodes(NULL,$node_columns);
527
528 $ConfigureColumns->fetch_live_data($all_nodes);
529
530 $show_reservable_info = TRUE;
531 $show_layout_info = '1';
532 $show_conf = explode(";",$show_configuration);
533 foreach ($show_conf as $ss) {
534   if ($ss =="reservable")
535     $show_reservable_info = FALSE;
536   else if ($ss =="columns")
537     $show_layout_info = '0';
538 }        
539
540 $slice_nodes=array();
541 $potential_nodes=array();
542 $reservable_nodes=array();
543 foreach ($all_nodes as $node) {
544   if (in_array($node['node_id'],$slice['node_ids'])) {
545     $slice_nodes[]=$node;
546     if ($node['node_type']=='reservable') $reservable_nodes[]=$node;
547   } else {
548     $potential_nodes[]=$node;
549   }
550 }
551 if ($profiling) plc_debug_prof('05: nodes',count($slice_nodes));
552 ////////////////////
553 // outline the number of reservable nodes
554 $nodes_message=count_english($slice_nodes,"node");
555 if (count($reservable_nodes)) $nodes_message .= " (" . count($reservable_nodes) . " reservable)";
556 $toggle=new PlekitToggle ('my-slice-nodes',$nodes_message,
557                           array('bubble'=>
558                                 'Manage nodes attached to this slice',
559                                 'visible'=>get_arg('show_nodes')));
560 $toggle->start();
561
562
563 //////////////////// reservable nodes area
564 $leases_info="
565 You have attached one or more reservable nodes to your slice. 
566 Reservable nodes show up with the '$mark' mark. 
567 Your slivers will be available only during timeslots
568 where you have obtained leases. 
569 You can manage your leases in the tab below.
570 <br>
571 This feature is still experimental; feedback is appreciated at <a href='mailto:devel@planet-lab.org'>devel@planet-lab.org</a>
572 ";
573 $count=count($reservable_nodes);
574 if ($count && $privileges) {
575   // include leases.js only if needed
576   drupal_set_html_head('<script src="/planetlab/slices/leases.js" type="text/javascript" charset="utf-8"></script>');
577
578   // having reservable nodes in white lists looks a bit off scope for now...
579   $toggle_nodes=new PlekitToggle('my-slice-nodes-reserve',
580                                  "Leases - " . count($reservable_nodes) . " reservable node(s)",
581                                  array('visible'=>get_arg('show_nodes_resa'), 
582                                        'info-text'=>$leases_info,
583                                        'info-visible'=>$show_reservable_info));
584   $toggle_nodes->start();
585
586   // get settings from environment, otherwise set to defaults
587   // when to start, in hours in the future from now
588   $leases_offset=$_GET['leases_offset'];
589   if ( ! $leases_offset ) $leases_offset=0;
590   // how many timeslots to show
591   $leases_slots=$_GET['leases_slots'];
592   if ( ! $leases_slots ) $leases_slots = 36;
593   // offset in hours (in the future) from now 
594   $leases_w = $_GET['leases_w'];
595   if ( ! $leases_w) $leases_w=18;
596   // number of timeslots to display
597
598   $granularity=$api->GetLeaseGranularity();
599
600   // these elements are for passing data to the javascript layer
601   echo "<span class='hidden' id='leases_slicename'>" . $slice['name'] . "</span>";
602   echo "<span class='hidden' id='leases_slice_id'>" . $slice['slice_id']. "</span>";
603   echo "<span class='hidden' id='leases_granularity'>" . $granularity . "</span>";
604   // ditto, and editable - very rough for now
605   echo "<div class='center' id='leases_settings'>";
606   echo "<label id='leases_offset_label' class='leases_label'>start, in hours from now</label>";
607   echo "<input type='text' class='leases_input' id='leases_offset_input' value='$leases_offset' />";
608   echo "<label id='leases_slots_label' class='leases_label'># of timeslots</label>";
609   echo "<input type='text' class='leases_input' id='leases_slots_input' value='$leases_slots' />";
610   echo "<label id='leases_w_label' class='leases_label'>slot width, in pixels</label>";
611   echo "<input type='text' class='leases_input' id='leases_w_input' value='$leases_w' />";
612   echo "</div>";
613
614   // leases_data is the name used by leases.js to locate this place
615   // first population will be triggered by init_scheduler from leases.js
616   echo "<table id='leases_data' class='hidden'></table>";
617
618   // the general layout for the scheduler
619   echo <<< EOF
620 <div id='leases_area'></div>
621
622 <div id='leases_buttons'>
623     <button id='leases_refresh' type='submit'>Refresh (Pull)</button>
624     <button id='leases_submit' type='submit'>Submit (Push)</button>
625 </div>
626 EOF;
627
628   $toggle_nodes->end();
629  }
630
631 if ($profiling) plc_debug_prof('06: leases',0);
632
633 //////////////////// node configuration panel
634 if ($first_time_configuration) 
635 $column_conf_visible = '1';
636 else
637 $column_conf_visible = '0';
638
639 $layout_info='
640 This tab allows you to customize the columns in the node tables,
641 below. Information on the nodes comes from a variety of monitoring
642 sources. If you, as either a user or a provider of monitoring data,
643 would like to see additional columns made available, please send us
644 your request in mail to <a
645 href="mailto:support@myslice.info">support@myslice.info</a>. You can
646 find more information about the MySlice project at <a
647 href="http://trac.myslice.info">http://trac.myslice.info</a>.
648 ';
649 $toggle_nodes=new PlekitToggle('my-slice-nodes-configuration',
650                                "Node table layout",
651                                array('info-text'=>$layout_info,
652                                      'info-visible'=>$show_layout_info));
653 $toggle_nodes->start();
654
655 //usort ($table_headers, create_function('$col1,$col2','return strcmp($col1["header"],$col2["header"]);'));
656 //print("<p>TABLE HEADERS<p>");
657 //print_r($table_headers);
658
659 print("<div id='debug'></div>");
660 print("<input type='hidden' id='slice_id' value='".$slice['slice_id']."' />");
661 print("<input type='hidden' id='person_id' value='".$plc->person['person_id']."' />");
662 print("<input type='hidden' id='conf_tag_id' value='".$conf_tag_id."' />");
663 print("<input type='hidden' id='show_tag_id' value='".$show_tag_id."' />");
664 print("<input type='hidden' id='show_configuration' value='".$show_configuration."' />");
665 print("<input type='hidden' id='column_configuration' value='".$slice_column_configuration."' />");
666 print("<br><input type='hidden' size=80 id='full_column_configuration' value='".$column_configuration."' />");
667 print("<input type='hidden' id='previousConf' value='".$slice_column_configuration."' />");
668 print("<input type='hidden' id='defaultConf' value='".$default_configuration."' />");
669
670 if ($profiling) plc_debug_prof('07: before configuration_panel',0);
671 $ConfigureColumns->configuration_panel_html(true);
672
673 if ($profiling) plc_debug_prof('08: before javascript_init',0);
674 $ConfigureColumns->javascript_init();
675
676 $toggle_nodes->end();
677
678 if ($profiling) plc_debug_prof('09: layout',0);
679
680 $all_sites=$api->GetSites(NULL, array('site_id','login_base'));
681 $site_hash=array();
682 foreach ($all_sites as $tmp_site) $site_hash[$tmp_site['site_id']]=$tmp_site['login_base'];
683
684 $interface_columns=array('ip','node_id','interface_id');
685 $interface_filter=array('is_primary'=>TRUE);
686 $interfaces=$api->GetInterfaces($interface_filter,$interface_columns);
687
688 $interface_hash=array();
689 foreach ($interfaces as $interface) $interface_hash[$interface['node_id']]=$interface;
690
691
692 if ($profiling) plc_debug_prof('10: interfaces',count($interfaces));
693
694 //////////////////// nodes currently in
695 $toggle_nodes=new PlekitToggle('my-slice-nodes-current',
696                                count_english($slice_nodes,"node") . " currently in $name",
697                                array('visible'=>get_arg('show_nodes_current')));
698 $toggle_nodes->start();
699
700 $headers=array();
701 $notes=array();
702 //$notes=array_merge($notes,$visibletags->notes());
703 $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";
704
705 /*
706 $headers['peer']='string';
707 $headers['hostname']='string';
708 $short="-S-"; $long=Node::status_footnote(); $type='string'; 
709         $headers[$short]=array('type'=>$type,'title'=>$long); $notes []= "$short = $long";
710 $short=reservable_mark(); $long=reservable_legend(); $type='string';
711         $headers[$short]=array('type'=>$type,'title'=>$long); $notes []= "$short = $long";
712 // the extra tags, configured for the UI
713 $headers=array_merge($headers,$visibletags->headers());
714
715 if ($privileges) $headers[plc_delete_icon()]="none";
716 */
717
718 $edit_header = array();
719 if ($privileges) $edit_header[plc_delete_icon()]="none";
720 $headers = array_merge($ConfigureColumns->get_headers(),$edit_header);
721
722 //print("<p>HEADERS<p>");
723 //print_r($headers);
724
725 $table_options = array('notes'=>$notes,
726                        'search_width'=>15,
727                        'pagesize'=>20,
728                         'configurable'=>true);
729
730 $table=new PlekitTable('nodes',$headers,NULL,$table_options);
731
732 $form=new PlekitForm(l_actions(),array('slice_id'=>$slice['slice_id']));
733 $form->start();
734 $table->start();
735 if ($slice_nodes) foreach ($slice_nodes as $node) {
736   $table->row_start();
737
738   $table->cell($node['node_id'], array('display'=>'none'));
739
740   $table->cell(l_node_obj($node));
741   $peers->cell($table,$node['peer_id']);
742   $run_level=$node['run_level'];
743   list($label,$class) = Node::status_label_class_($node);
744   $table->cell ($label,array('class'=>$class));
745   $table->cell( ($node['node_type']=='reservable')?reservable_mark():"" );
746
747   $hostname=$node['hostname'];
748   $ip=$interface_hash[$node['node_id']]['ip'];
749   $interface_id=$interface_hash[$node['node_id']]['interface_id'];
750
751 //extra columns
752 $node['domain'] = topdomain($hostname);
753 $node['sitename'] = l_site_t($node['site_id'],$site_hash[$node['site_id']]);
754 if ($interface_id)
755         $node['ipaddress'] = l_interface_t($interface_id,$ip);
756   else
757         $node['ipaddress'] = "n/a";
758
759  //foreach ($visiblecolumns as $tagname) $table->cell($node[$tagname]);
760  $ConfigureColumns->cells($table, $node);
761
762   if ($privileges) $table->cell ($form->checkbox_html('node_ids[]',$node['node_id']));
763   $table->row_end();
764 }
765 // actions area
766 if ($privileges) {
767
768   // remove nodes
769   $table->tfoot_start();
770
771   $table->row_start();
772   $table->cell($form->submit_html ("remove-nodes-from-slice","Remove selected"),
773                array('hfill'=>true,'align'=>'right'));
774   $table->row_end();
775  }
776 $table->end();
777 $toggle_nodes->end();
778
779 if ($profiling) plc_debug_prof('11: nodes in',count($slice_nodes));
780
781 //////////////////// nodes to add
782 if ($privileges) {
783   $new_potential_nodes = array();
784   if ($potential_nodes) foreach ($potential_nodes as $node) {
785       $emptywl=empty($node['slice_ids_whitelist']);
786       $inwl = (!emptywl) and in_array($slice['slice_id'],$node['slice_ids_whitelist']);
787       if ($emptywl or $inwl)
788         $new_potential_nodes[]=$node;
789   }
790   $potential_nodes=$new_potential_nodes;
791
792   $count=count($potential_nodes);
793   $toggle_nodes=new PlekitToggle('my-slice-nodes-add',
794                                  count_english($potential_nodes,"more node") . " available",
795                                  array('visible'=>get_arg('show_nodes_add')));
796   $toggle_nodes->start();
797
798   if ( $potential_nodes ) {
799     $headers=array();
800     $notes=array();
801
802
803 /*
804     $headers['peer']='string';
805     $headers['hostname']='string';
806     $short="-S-"; $long=Node::status_footnote(); $type='string'; 
807         $headers[$short]=array('type'=>$type,'title'=>$long); $notes []= "$short = $long";
808         $short=reservable_mark(); $long=reservable_legend(); $type='string';
809         $headers[$short]=array('type'=>$type,'title'=>$long); $notes []= "$short = $long";
810     // the extra tags, configured for the UI
811     $headers=array_merge($headers,$visibletags->headers());
812     $headers['+']="none";
813 */
814
815     $add_header = array();
816     $add_header['+']="none";
817     $headers = array_merge($ConfigureColumns->get_headers(),$add_header);
818
819     //$notes=array_merge($notes,$visibletags->notes());
820 $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";
821     
822     $table=new PlekitTable('add_nodes',$headers,NULL, $table_options);
823     $form=new PlekitForm(l_actions(),
824                          array('slice_id'=>$slice['slice_id']));
825     $form->start();
826     $table->start();
827     if ($potential_nodes) foreach ($potential_nodes as $node) {
828         $table->row_start();
829
830         $table->cell($node['node_id'], array('display'=>'none'));
831
832         $table->cell(l_node_obj($node));
833         $peers->cell($table,$node['peer_id']);
834         list($label,$class) = Node::status_label_class_($node);
835         $table->cell ($label,array('class'=>$class));
836         $table->cell( ($node['node_type']=='reservable')?reservable_mark():"" );
837
838         //extra columns
839           $hostname=$node['hostname'];
840           $ip=$interface_hash[$node['node_id']]['ip'];
841           $interface_id=$interface_hash[$node['node_id']]['interface_id'];
842         $node['domain'] = topdomain($hostname);
843         $node['sitename'] = l_site_t($node['site_id'],$site_hash[$node['site_id']]);
844         $node['ipaddress'] = l_interface_t($interface_id,$ip);
845
846         //foreach ($visiblecolumns as $tagname) $table->cell($node[$tagname]);
847         $ConfigureColumns->cells($table, $node);
848
849         $table->cell ($form->checkbox_html('node_ids[]',$node['node_id']));
850         $table->row_end();
851       }
852     // add nodes
853     $table->tfoot_start();
854     $table->row_start();
855     $table->cell($form->submit_html ("add-nodes-in-slice","Add selected"),
856                  array('hfill'=>true,'align'=>'right'));
857     $table->row_end();
858     $table->end();
859     $form->end();
860   }
861   $toggle_nodes->end();
862 }
863
864 $toggle->end();
865
866 if ($profiling) plc_debug_prof('12: nodes to add',count($potential_nodes));
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('13: 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'),
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('14: 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')));
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('15: 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('16: 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('17: 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,NULL);
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 ?>