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