fixed: php warnings
[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=time();
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 if ($interfaces) {
690         foreach ($interfaces as $interface) $interface_hash[$interface['node_id']]=$interface;
691 }
692
693 if ($profiling) plc_debug_prof('10: interfaces',count($interfaces));
694
695 //////////////////// nodes currently in
696 $toggle_nodes=new PlekitToggle('my-slice-nodes-current',
697                                count_english($slice_nodes,"node") . " currently in $name",
698                                array('visible'=>get_arg('show_nodes_current')));
699 $toggle_nodes->start();
700
701 $headers=array();
702 $notes=array();
703 //$notes=array_merge($notes,$visibletags->notes());
704 $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";
705
706 /*
707 $headers['peer']='string';
708 $headers['hostname']='string';
709 $short="-S-"; $long=Node::status_footnote(); $type='string'; 
710         $headers[$short]=array('type'=>$type,'title'=>$long); $notes []= "$short = $long";
711 $short=reservable_mark(); $long=reservable_legend(); $type='string';
712         $headers[$short]=array('type'=>$type,'title'=>$long); $notes []= "$short = $long";
713 // the extra tags, configured for the UI
714 $headers=array_merge($headers,$visibletags->headers());
715
716 if ($privileges) $headers[plc_delete_icon()]="none";
717 */
718
719 $edit_header = array();
720 if ($privileges) $edit_header[plc_delete_icon()]="none";
721 $headers = array_merge($ConfigureColumns->get_headers(),$edit_header);
722
723 //print("<p>HEADERS<p>");
724 //print_r($headers);
725
726 $table_options = array('notes'=>$notes,
727                        'search_width'=>15,
728                        'pagesize'=>20,
729                         'configurable'=>true);
730
731 $table=new PlekitTable('nodes',$headers,NULL,$table_options);
732
733 $form=new PlekitForm(l_actions(),array('slice_id'=>$slice['slice_id']));
734 $form->start();
735 $table->start();
736 if ($slice_nodes) foreach ($slice_nodes as $node) {
737   $table->row_start();
738
739   $table->cell($node['node_id'], array('display'=>'none'));
740
741   $table->cell(l_node_obj($node));
742   $peers->cell($table,$node['peer_id']);
743   $run_level=$node['run_level'];
744   list($label,$class) = Node::status_label_class_($node);
745   $table->cell ($label,array('class'=>$class));
746   $table->cell( ($node['node_type']=='reservable')?reservable_mark():"" );
747
748   $hostname=$node['hostname'];
749   $ip=$interface_hash[$node['node_id']]['ip'];
750   $interface_id=$interface_hash[$node['node_id']]['interface_id'];
751
752 //extra columns
753 $node['domain'] = topdomain($hostname);
754 $node['sitename'] = l_site_t($node['site_id'],$site_hash[$node['site_id']]);
755 if ($interface_id)
756         $node['ipaddress'] = l_interface_t($interface_id,$ip);
757   else
758         $node['ipaddress'] = "n/a";
759
760  //foreach ($visiblecolumns as $tagname) $table->cell($node[$tagname]);
761  $ConfigureColumns->cells($table, $node);
762
763   if ($privileges) $table->cell ($form->checkbox_html('node_ids[]',$node['node_id']));
764   $table->row_end();
765 }
766 // actions area
767 if ($privileges) {
768
769   // remove nodes
770   $table->tfoot_start();
771
772   $table->row_start();
773   $table->cell($form->submit_html ("remove-nodes-from-slice","Remove selected"),
774                array('hfill'=>true,'align'=>'right'));
775   $table->row_end();
776  }
777 $table->end();
778 $toggle_nodes->end();
779
780 if ($profiling) plc_debug_prof('11: nodes in',count($slice_nodes));
781
782 //////////////////// nodes to add
783 if ($privileges) {
784   $new_potential_nodes = array();
785   if ($potential_nodes) foreach ($potential_nodes as $node) {
786       $emptywl=empty($node['slice_ids_whitelist']);
787       $inwl = (!emptywl) and in_array($slice['slice_id'],$node['slice_ids_whitelist']);
788       if ($emptywl or $inwl)
789         $new_potential_nodes[]=$node;
790   }
791   $potential_nodes=$new_potential_nodes;
792
793   $count=count($potential_nodes);
794   $toggle_nodes=new PlekitToggle('my-slice-nodes-add',
795                                  count_english($potential_nodes,"more node") . " available",
796                                  array('visible'=>get_arg('show_nodes_add')));
797   $toggle_nodes->start();
798
799   if ( $potential_nodes ) {
800     $headers=array();
801     $notes=array();
802
803
804 /*
805     $headers['peer']='string';
806     $headers['hostname']='string';
807     $short="-S-"; $long=Node::status_footnote(); $type='string'; 
808         $headers[$short]=array('type'=>$type,'title'=>$long); $notes []= "$short = $long";
809         $short=reservable_mark(); $long=reservable_legend(); $type='string';
810         $headers[$short]=array('type'=>$type,'title'=>$long); $notes []= "$short = $long";
811     // the extra tags, configured for the UI
812     $headers=array_merge($headers,$visibletags->headers());
813     $headers['+']="none";
814 */
815
816     $add_header = array();
817     $add_header['+']="none";
818     $headers = array_merge($ConfigureColumns->get_headers(),$add_header);
819
820     //$notes=array_merge($notes,$visibletags->notes());
821 $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";
822     
823     $table=new PlekitTable('add_nodes',$headers,NULL, $table_options);
824     $form=new PlekitForm(l_actions(),
825                          array('slice_id'=>$slice['slice_id']));
826     $form->start();
827     $table->start();
828     if ($potential_nodes) foreach ($potential_nodes as $node) {
829         $table->row_start();
830
831         $table->cell($node['node_id'], array('display'=>'none'));
832
833         $table->cell(l_node_obj($node));
834         $peers->cell($table,$node['peer_id']);
835         list($label,$class) = Node::status_label_class_($node);
836         $table->cell ($label,array('class'=>$class));
837         $table->cell( ($node['node_type']=='reservable')?reservable_mark():"" );
838
839         //extra columns
840           $hostname=$node['hostname'];
841           $ip=$interface_hash[$node['node_id']]['ip'];
842           $interface_id=$interface_hash[$node['node_id']]['interface_id'];
843         $node['domain'] = topdomain($hostname);
844         $node['sitename'] = l_site_t($node['site_id'],$site_hash[$node['site_id']]);
845         $node['ipaddress'] = l_interface_t($interface_id,$ip);
846
847         //foreach ($visiblecolumns as $tagname) $table->cell($node[$tagname]);
848         $ConfigureColumns->cells($table, $node);
849
850         $table->cell ($form->checkbox_html('node_ids[]',$node['node_id']));
851         $table->row_end();
852       }
853     // add nodes
854     $table->tfoot_start();
855     $table->row_start();
856     $table->cell($form->submit_html ("add-nodes-in-slice","Add selected"),
857                  array('hfill'=>true,'align'=>'right'));
858     $table->row_end();
859     $table->end();
860     $form->end();
861   }
862   $toggle_nodes->end();
863 }
864
865 $toggle->end();
866
867 if ($profiling) plc_debug_prof('12: nodes to add',count($potential_nodes));
868
869 //////////////////////////////////////// retrieve all slice tags
870 $tags=$api->GetSliceTags (array('slice_id'=>$slice_id));
871 //////////////////////////////////////////////////////////// tab:initscripts
872 // xxx fixme
873 // * add a message on how to use this:
874 // * explain the 2 mechanisms (initscript_code, initscript)
875 // * explain the interface : initscript start|stop|restart slicename
876 // xxx fixme
877
878 $initscript_info="
879 There are two ways to attach an initscript to a slice:<ul>
880
881 <li> <span class='bold'> Shared initscripts </span> are global to the
882 MyPLC, and managed by the Operations Team. For that reason, regular
883 users cannot change these scripts, but can reference one of the
884 available names in the drop down below.  </li>
885
886 <li> You also have the option to provide <span class='bold'> your own
887 code </span>, with the following conventions: <ul>
888
889 <li> Like regular initscripts, your script must expect to receive as a
890 first argument <span class='bold'> start </span>, <span class='bold'>
891 stop </span> or <span class='bold'> restart </span>. It is important
892 to honor this argument, as your slice may be stopped and restarted at
893 any time; also this is used whenever the installed code gets changed.
894 </li>
895
896 <li> As a second argument, you will receive the slicename; in most
897 cases this can be safely ignored.  </li>
898
899 </ul>
900 </li>
901  </ul>
902 The slice-specific setting has precedence on a shared initscript.
903 ";
904
905 $shared_initscripts=$api->GetInitScripts(array('-SORT'=>'name'),array('name'));
906 //$shared_initscripts=$api->GetInitScripts();
907 if ($profiling) plc_debug_prof('13: initscripts',count($initscripts));
908 // xxx expose this even on foreign slices for now
909 if ($local_peer) {
910   $initscript='';
911   $initscript_code='';
912   if ($tags) foreach ($tags as $tag) {
913       if ($tag['tagname']=='initscript') {
914         if ($initscript!='') drupal_set_error("multiple occurrences of 'initscript' tag");
915         $initscript=$tag['value'];
916       }
917       if ($tag['tagname']=='initscript_code') {
918         if ($initscript_code!='') drupal_set_error("multiple occurrences of 'initscript_code' tag");
919         $initscript_code=$tag['value'];
920         // plc_debug_txt('retrieved body',$initscript_code);
921       }
922     }
923   $label="No initscript";
924   $trimmed=trim($initscript_code);
925   if (!empty($trimmed)) $label="Initscript : slice-specific (" . substr($initscript_code,0,20) . " ...)";
926   else if (!empty($initscript)) $label="Initscript: shared " . $initscript;
927
928   $toggle = new PlekitToggle('slice-initscripts',$label,
929                              array('bubble'=>'Manage initscript on that slice',
930                                    'visible'=>get_arg('show_initscripts'),
931                                    'info-text'=>$initscript_info
932                                    // not messing with persontags to guess whether this should be displayed or not
933                                    // hopefully some day toggle will know how to handle that using web storage
934                                    ));
935   $toggle->start();
936
937   $details=new PlekitDetails(TRUE);
938   // we expose the previous values so that actions.php can know if changes are really needed
939   // the code needs to be encoded as it may contain any character
940   // as far as the code, this does not work too well b/c what actions.php receives
941   // seems to have spurrious \r chars, and the comparison between old and new values 
942   // is not reliable, which results in changes being made although the code hasn't changed
943   // hve spent too much time on this, good enough for now...
944   $details->form_start(l_actions(),array('action'=>'update-initscripts',
945                                          'slice_id'=>$slice_id,
946                                          'name'=>$name,
947                                          'previous-initscript'=>$initscript,
948                                          'previous-initscript-code'=>htmlentities($initscript_code)));
949   $details->start();
950   // comppute a pulldown with available names
951   $selectors=array();
952   $is_found=FALSE;
953   if ($shared_initscripts) foreach ($shared_initscripts as $is) {
954       $is_selector=array('display'=>$is['name'],'value'=>$is['name']);
955       if ($is['name']==$initscript) {
956         $is_selector['selected']=TRUE;
957         $is_found=TRUE;
958       }
959       $selectors[]=$is_selector;
960     }
961   // display a warning when initscript references an unknown script
962   $details->tr_submit('unused','Update initscripts');
963   ////////// by name
964   $details->th_td("shared initscript name",
965                   $details->form()->select_html('initscript',$selectors,array('label'=>'none')),
966                   'initscript',
967                   array('input_type'=>'select'));
968   if ($initscript && ! $is_found) 
969     // xxx better rendering ?
970     $details->th_td('WARNING',plc_warning_html("Current name '" . $initscript . "' is not a known shared initscript name"));
971   ////////// by contents
972   $script_height=8;
973   $script_width=60;
974   if ($initscript_code) {
975     $text=explode("\n",$initscript_code);
976     $script_height=count($text);
977     $script_width=10;
978     foreach ($text as $line) $script_width=max($script_width,strlen($line));
979   }
980   $details->th_td('slice initscript',$initscript_code,'initscript-code',
981                   array('input_type'=>'textarea', 'width'=>$script_width,'height'=>$script_height));
982   $details->tr_submit('unused','Update initscripts');
983   $details->form_end();
984   $details->end();  
985   $toggle->end();
986 }
987
988 //////////////////////////////////////////////////////////// tab:tags
989 // very wide values get abbreviated
990 $tag_value_threshold=24;
991 // xxx fixme
992 // * this area could use a help message about some special tags:
993 // * initscript-related should be taken out
994 // * sliverauth-related (ssh_key & hmac) should have a toggle to hide or show
995 // xxx fixme
996
997 // xxx expose this even on foreign slices for now
998 //if ( $local_peer ) {
999   if ($profiling) plc_debug_prof('14: slice tags',count($tags));
1000   function get_tagname ($tag) { return $tag['tagname'];}
1001   $tagnames = array_map ("get_tagname",$tags);
1002   
1003   $toggle = new PlekitToggle ('slice-tags',count_english_warning($tags,'tag'),
1004                               array('bubble'=>'Inspect and set tags on that slice',
1005                                     'visible'=>get_arg('show_tags')));
1006   $toggle->start();
1007   
1008   $headers=array(
1009     "Name"=>"string",
1010     "Value"=>"string",
1011     "Node"=>"string",
1012     "NodeGroup"=>"string");
1013   if ($tags_privileges) $headers[plc_delete_icon()]="none";
1014   
1015   $table_options=array("notes_area"=>false,"pagesize_area"=>false,"search_width"=>10);
1016   $table=new PlekitTable("slice_tags",$headers,'0',$table_options);
1017   $form=new PlekitForm(l_actions(),
1018                        array('slice_id'=>$slice['slice_id']));
1019   $form->start();
1020   $table->start();
1021   if ($tags) {
1022     // Get hostnames for nodes in a single pass
1023     $_node_ids = array();
1024     foreach ($tags as $tag) {
1025       if ($tag['node_id']) {
1026         array_push($_node_ids, $tag['node_id']);
1027       }
1028     }
1029     $_nodes = $api->GetNodes(array('node_id' => $_node_ids), array('node_id', 'hostname'));
1030     $_hostnames = array();
1031     if ($_nodes) {
1032         foreach ($_nodes as $_node) {
1033                 $_hostnames[$_node['node_id']] = $_node['hostname'];
1034         }
1035     }
1036
1037     // Loop through tags again to display
1038     foreach ($tags as $tag) {
1039       $node_name = "ALL";
1040       if ($tag['node_id']) {
1041         $node_name = $_hostnames[$tag['node_id']];
1042       }
1043       $nodegroup_name="n/a";
1044       if ($tag['nodegroup_id']) { 
1045         $nodegroups=$api->GetNodeGroups(array('nodegroup_id'=>$tag['nodegroup_id']));
1046         if ($profiling) plc_debug_prof('15: nodegroup for slice tag',$nodegroup);
1047         if ($nodegroup) {
1048           $nodegroup = $nodegroups[0];
1049           $nodegroup_name = $nodegroup['groupname'];
1050         }
1051       }
1052       $table->row_start();
1053       $table->cell(l_tag_obj($tag));
1054       // very wide values get abbreviated
1055       $table->cell(truncate_and_popup($tag['value'],$tag_value_threshold));
1056       $table->cell($node_name);
1057       $table->cell($nodegroup_name);
1058       if ($tags_privileges) $table->cell ($form->checkbox_html('slice_tag_ids[]',$tag['slice_tag_id']));
1059       $table->row_end();
1060     }
1061   }
1062   if ($tags_privileges) {
1063     $table->tfoot_start();
1064     $table->row_start();
1065     $table->cell($form->submit_html ("delete-slice-tags","Remove selected"),
1066                  array('hfill'=>true,'align'=>'right'));
1067     $table->row_end();
1068     
1069     $table->row_start();
1070     function tag_selector ($tag) {
1071       return array("display"=>$tag['tagname'],"value"=>$tag['tag_type_id']);
1072     }
1073     $all_tags= $api->GetTagTypes( array ("category"=>"*slice*","-SORT"=>"+tagname"), array("tagname","tag_type_id"));
1074     if ($profiling) plc_debug_prof('16: tagtypes',count($all_tags));
1075     $selector_tag=array_map("tag_selector",$all_tags);
1076     
1077     function node_selector($node) { 
1078       return array("display"=>$node["hostname"],"value"=>$node['node_id']);
1079     }
1080     $selector_node=array_map("node_selector",$slice_nodes);
1081     
1082     function nodegroup_selector($ng) {
1083       return array("display"=>$ng["groupname"],"value"=>$ng['nodegroup_id']);
1084     }
1085     $all_nodegroups = $api->GetNodeGroups( array("groupname"=>"*"), array("groupname","nodegroup_id"));
1086     if ($profiling) plc_debug_prof('17: nodegroups',count($all_nodegroups));
1087     $selector_nodegroup=array_map("nodegroup_selector",$all_nodegroups);
1088     
1089     $table->cell($form->select_html("tag_type_id",$selector_tag,array('label'=>"Choose Tag")));
1090     $table->cell($form->text_html("value","",array('width'=>8)));
1091     $table->cell($form->select_html("node_id",$selector_node,array('label'=>"All Nodes")));
1092     $table->cell($form->select_html("nodegroup_id",$selector_nodegroup,array('label'=>"No Nodegroup")));
1093     $table->cell($form->submit_html("add-slice-tag","Set Tag"),array('columns'=>2,'align'=>'left'));
1094     $table->row_end();
1095   }
1096     
1097   $table->end();
1098   $form->end();
1099   $toggle->end();
1100 //}
1101
1102
1103 //////////////////////////////////////////////////////////// tab:renew
1104 if ($local_peer ) {
1105   if ( ! $renew_visible) renew_area ($slice,$site,NULL);
1106  }
1107
1108 $peers->block_end($peer_id);
1109
1110 if ($profiling) plc_debug_prof_end();
1111
1112 // Print footer
1113 include 'plc_footer.php';
1114
1115 ?>