From 638c7772a6ec67f3dea26d9bd2cd4fa2c3e479c6 Mon Sep 17 00:00:00 2001
From: Thierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Date: Sat, 28 Aug 2010 22:13:49 +0200
Subject: [PATCH] ongoing tweaks in the reservation frontend

---
 planetlab/common/actions.php | 41 +++++++++++++++++
 planetlab/slices/leases.js   | 86 +++++++++++++++++++++++++++---------
 planetlab/slices/slice.php   | 26 ++++++-----
 3 files changed, 121 insertions(+), 32 deletions(-)

diff --git a/planetlab/common/actions.php b/planetlab/common/actions.php
index ffb6bc1..5f3994d 100644
--- a/planetlab/common/actions.php
+++ b/planetlab/common/actions.php
@@ -116,6 +116,12 @@ $known_actions []= "add-nodegroup";
 $known_actions []= 'delete-nodegroups';
 //	expects nodegroup_ids
 
+//////////////////////////////////////// leases
+$known_actions []= "manage-leases";
+//	expects as 'actions' a list of 'action' of the form
+//      either [ 'add-leases', [nodenames], slicename, t_from, t_until ]
+//      or     [ 'delete-leases', lease_id ]
+
 ////////////////////////////////////////////////////////////
 $interface_details= array ('method','type', 'ip', 'gateway', 'network', 
 			   'broadcast', 'netmask', 'dns1', 'dns2', 
@@ -944,6 +950,41 @@ Our support team will be glad to answer any question that you might have.
    break;
  }
 
+//////////////////////////////////////// leases
+ case 'manage-leases': {
+   $actions=json_decode($_POST['actions']);
+   foreach ($actions as $action) {
+     $todo=$action[0];
+     switch ($todo) {
+     case 'add-leases': {
+       plc_debug('(add) action',$action);
+       break;
+     }
+     case 'delete-leases': {
+       plc_debug('(delete) action',$action);
+       break;
+     }
+     default: {
+       plc_debug('wrong entry',$action);
+     }
+     }
+   }
+   
+   /*
+   plc_debug('POST',$_POST);
+   $slicename=$_POST['slicename'];
+   $nodenames=$_POST['nodenames'];
+   $t_from=intval($_POST['t_from']);
+   $t_until=intval($_POST['t_until']);
+   foreach ($nodenames as $nodename) plc_debug('nodename',$nodename);
+   $json=json_decode($_POST['nodenames_json']);
+   plc_debug('json_decode',$json);
+   $api->AddLeases();
+   */
+
+   break;
+ }
+
 ////////////////////////////////////////
 
  case 'debug': {
diff --git a/planetlab/slices/leases.js b/planetlab/slices/leases.js
index 96b695a..439ec0e 100644
--- a/planetlab/slices/leases.js
+++ b/planetlab/slices/leases.js
@@ -1,7 +1,7 @@
 /* need to put some place else in CSS ? */
 
 // space for the nodenames
-var x_nodelabel = 200;
+var x_nodelabel = 120;
 // right space after the nodename - removed from the above
 var x_sep=20;
 // height for the (two) rows of timelabels
@@ -26,6 +26,8 @@ var txt_nodelabel = {"font": '"Trebuchet MS", Verdana, Arial, Helvetica, sans-se
 
 var attr_timebutton = {'fill':'#bbf', 'stroke': '#338','stroke-width':2, 
 		       'stroke-linecap':'round', 'stroke-linejoin':'miter', 'stroke-miterlimit':3};
+// keep consistent with sizes above - need for something nicer
+var timebutton_path = "M1,0L23,0L12,13L1,0";
 
 /* lease dimensions and colors */
 /* refrain from using gradient color, seems to not be animated properly */
@@ -40,14 +42,15 @@ var attr_lease_mine_free={'fill':"white", 'stroke-width':1, 'stroke-dasharray':'
 var attr_lease_other={'fill':"#f88"};
 
 /* other slices name */
-var txt_slice = {"font": '"Trebuchet MS", Verdana, Arial, Helvetica, sans-serif', stroke: "none", fill: "#444",
-		 "font-size": 15 };
+var txt_otherslice = {"font": '"Trebuchet MS", Verdana, Arial, Helvetica, sans-serif', stroke: "none", fill: "#444",
+		      "font-size": 12 };
 
 ////////////////////////////////////////////////////////////
 // the scheduler object
-function Scheduler (slicename, axisx, axisy, data) {
+function Scheduler (sliceid, slicename, axisx, axisy, data) {
 
-    // the data only contain slice names, we need this to find our own leases (mine)
+    // the data contains slice names, and lease_id, we need this to find our own leases (mine)
+    this.sliceid=sliceid;
     this.slicename=slicename;
     this.axisx=axisx;
     this.axisy=axisy;
@@ -106,11 +109,7 @@ function Scheduler (slicename, axisx, axisy, data) {
 	allnodes.click(allnodes_methods.click);
 	// timeslot buttons
 	for (var i=0, len=axisx.length; i < len; ++i) {
-	    var pathspec="M"+left+","+top;
-	    pathspec+="L"+(left+x_grain)+","+top;
-	    pathspec+="L"+(left+x_grain/2)+","+(top+y_node);
-	    pathspec+="L"+left+","+top;
-	    var timebutton=paper.path(pathspec).attr(attr_timebutton);
+	    var timebutton=paper.path(timebutton_path).attr({'translation':left+','+top}).attr(attr_timebutton);
 	    timebutton.from_time=axisx[i][0];
 	    timebutton.scheduler=this;
 	    timebutton.click(timebutton_methods.click);
@@ -132,9 +131,11 @@ function Scheduler (slicename, axisx, axisy, data) {
 	    left += x_nodelabel;
 	    var grain=0;
 	    while (grain < this.nb_grains()) {
-		slicename=data[data_index][0];
-		duration=data[data_index][1];
+		lease_id=data[data_index][0];
+		slicename=data[data_index][1];
+		duration=data[data_index][2];
 		var lease=paper.rect (left,top,x_grain*duration,y_node,radius);
+		lease.lease_id=lease_id;
 		lease.nodename=nodename;
 		lease.nodelabel=nodelabel;
 		if (slicename == "") {
@@ -163,6 +164,8 @@ function Scheduler (slicename, axisx, axisy, data) {
     }
 
     this.submit = function () {
+	document.body.style.cursor = "wait";
+	var actions=new Array();
 	for (var i=0, len=this.leases.length; i<len; ++i) {
 	    var lease=this.leases[i];
 	    if (lease.current != lease.initial) {
@@ -175,14 +178,40 @@ function Scheduler (slicename, axisx, axisy, data) {
 		    until_time=this.leases[j].until_time;
 		    ++j; ++i;
 		}
-		var method=(lease.current=='free') ? 'DeleteLeases' : 'AddLeases';
-		window.console.log(method + "(" + 
-				   "[" + lease.nodename + "]," + 
-				   this.slicename + "," +
-				   from_time + "," +
-				   until_time + ')');
+		if (lease.current!='free') { // lease to add
+		    actions.push(new Array('add-leases',
+					   new Array(lease.nodename),
+					   this.slicename,
+					   from_time,
+					   until_time));
+		} else { // lease to delete
+		    actions.push(new Array ('delete-leases',
+					    lease.lease_id));
+		}
 	    }
 	}
+	sliceid=this.sliceid;
+	// once we're done with the side-effect performed in actions.php, we need to refresh this view
+	redirect = function (sliceid) {
+	    window.location = '/db/slices/slice.php?id=' + sliceid + '&show_details=0&show_nodes=1&show_nodes_resa=1';
+	}
+	// Ajax.Request comes with prototype
+	var ajax=new Ajax.Request('/planetlab/common/actions.php', 
+				  {method:'post',
+				   parameters:{'action':'manage-leases',
+					       'actions':actions.toJSON()},
+				   onSuccess: function(transport) {
+				       var response = transport.responseText || "no response text";
+				       document.body.style.cursor = "default";
+				       alert("Success (sliceid=" + sliceid + ")\n\n" + response);
+				       redirect(sliceid);
+				   },
+				   onFailure: function(){ 
+				       document.body.style.cursor = "default";
+				       alert('Something went wrong...') 
+				       redirect(sliceid);
+				   },
+				  });
     }
 
     this.clear = function () {
@@ -318,7 +347,7 @@ var lease_methods = {
 	/* a text obj to display the name of the slice that owns that lease */
 	var otherslicelabel = paper.text (lease.attr("x")+lease.attr("width")/2,
 					  // xxx
-					  lease.attr("y")+lease.attr("height")/2,slicename).attr(txt_slice);
+					  lease.attr("y")+lease.attr("height")/2,slicename).attr(txt_otherslice);
 	/* hide it right away */
 	otherslicelabel.hide();
 	/* record it */
@@ -344,11 +373,24 @@ function init_scheduler () {
         axisx.push(getInnerText(cell).split("&"));
     });
     // leases - expect colspan to describe length in grains
+    // the text contents is expected to be lease_id & slicename
     table.getElementsBySelector("tbody>tr>td").each(function (cell) {
-        data.push(new Array (getInnerText(cell),cell.colSpan));
+	var cell_data;
+	slice_attributes=getInnerText(cell).split('&');
+	// booked leases come with lease id and slice name
+	if (slice_attributes.length == 2) {
+	    // leases is booked : slice_id, slice_name, duration in grains
+	    cell_data=new Array (slice_attributes[0], slice_attributes[1], cell.colSpan);
+	} else {
+	    cell_data = new Array ('','',cell.colSpan);
+	}
+        data.push(cell_data);
     });
-    // slicename : the upper-left cell
-    var scheduler = new Scheduler (getInnerText(table.getElementsBySelector("thead>tr>td")[0]), axisx, axisy, data);
+    // sliceid & slicename : the upper-left cell
+    var slice_attributes = getInnerText(table.getElementsBySelector("thead>tr>td")[0]).split('&');
+    var sliceid=slice_attributes[0];
+    var slicename=slice_attributes[1];
+    var scheduler = new Scheduler (sliceid,slicename, axisx, axisy, data);
     table.hide();
     // leases_area is a <div> created by slice.php as a placeholder
     scheduler.init ("leases_area");
diff --git a/planetlab/slices/slice.php b/planetlab/slices/slice.php
index 74edc83..fbcc6d7 100644
--- a/planetlab/slices/slice.php
+++ b/planetlab/slices/slice.php
@@ -387,7 +387,8 @@ $toggle->end();
 //     (.) type is passed to the javascript table, for sorting (default is 'string')
 
 // minimal list as a start
-$node_fixed_columns = array('hostname','node_id','peer_id','slice_ids_whitelist','run_level','boot_state','last_contact','node_type');
+$node_fixed_columns = array('hostname','node_id','peer_id','slice_ids_whitelist',
+			    'run_level','boot_state','last_contact','node_type');
 // create a VisibleTags object : basically the list of tag columns to show
 $visibletags = new VisibleTags ($api, 'node');
 $visiblecolumns = $visibletags->column_names();
@@ -398,9 +399,14 @@ $potential_nodes=$api->GetNodes(array('~node_id'=>$slice['node_ids']),$node_colu
 $reservable_nodes=array();
 foreach ($nodes as $node) { if ($node['node_type']=='reservable') $reservable_nodes[]=$node; }
 
+$reservable_mark="-R-";
+$reservable_legend="reservable nodes are marked with " . $reservable_mark;
+
 ////////////////////
-$count=count($nodes);
-$toggle=new PlekitToggle ('my-slice-nodes',"$count Nodes",
+// outline the number of reservable nodes
+$nodes_message=count_english($nodes,"node");
+if (count($reservable_nodes)) $nodes_message .= " (" . count($reservable_nodes) . " reservable)";
+$toggle=new PlekitToggle ('my-slice-nodes',$nodes_message,
 			  array('bubble'=>
 				'Manage nodes attached to this slice',
 				'visible'=>get_arg('show_nodes',false)));
@@ -418,7 +424,7 @@ $headers['peer']='string';
 $headers['hostname']='string';
 $short="ST"; $long=Node::status_footnote(); $type='string'; 
 	$headers[$short]=array('type'=>$type,'title'=>$long); $notes []= "$short = $long";
-$short="R"; $long="reservable nodes"; $type='string';
+$short="R"; $long=$reservable_legend; $type='string';
 	$headers[$short]=array('type'=>$type,'title'=>$long); $notes []= "$short = $long";
 // the extra tags, configured for the UI
 $headers=array_merge($headers,$visibletags->headers());
@@ -441,7 +447,7 @@ if ($nodes) foreach ($nodes as $node) {
   $run_level=$node['run_level'];
   list($label,$class) = Node::status_label_class_($node);
   $table->cell ($label,array('class'=>$class));
-  $table->cell( ($node['node_type']=='reservable')?"-R-":"" );
+  $table->cell( ($node['node_type']=='reservable')?$reservable_mark:"" );
   foreach ($visiblecolumns as $tagname) $table->cell($node[$tagname]);
 
   if ($privileges) $table->cell ($form->checkbox_html('node_ids[]',$node['node_id']));
@@ -485,7 +491,7 @@ if ($privileges) {
     $headers['hostname']='string';
     $short="ST"; $long=Node::status_footnote(); $type='string'; 
 	$headers[$short]=array('type'=>$type,'title'=>$long); $notes []= "$short = $long";
-    $short="R"; $long="reservable nodes"; $type='string';
+    $short="R"; $long=$reservable_legend; $type='string';
 	$headers[$short]=array('type'=>$type,'title'=>$long); $notes []= "$short = $long";
     // the extra tags, configured for the UI
     $headers=array_merge($headers,$visibletags->headers());
@@ -503,7 +509,7 @@ if ($privileges) {
 	$table->cell(l_node_obj($node));
 	list($label,$class) = Node::status_label_class_($node);
 	$table->cell ($label,array('class'=>$class));
-	$table->cell( ($node['node_type']=='reservable')?"-R-":"" );
+	$table->cell( ($node['node_type']=='reservable')?$reservable_mark:"" );
 	foreach ($visiblecolumns as $tagname) $table->cell($node[$tagname]);
 	$table->cell ($form->checkbox_html('node_ids[]',$node['node_id']));
 	$table->row_end();
@@ -536,7 +542,7 @@ if ($count && $privileges) {
   $steps=$duration/$grain;
   $start=intval($now/$grain)*$grain;
   $end=$now+$duration;
-  $lease_columns=array('name','t_from','t_until','hostname','name');
+  $lease_columns=array('lease_id','name','t_from','t_until','hostname','name');
   $leases=$api->GetLeases(array(']t_until'=>$now,'[t_from'=>$end,'-SORT'=>'t_from'),$lease_columns);
   // hash nodes -> leases
   $host_hash=array();
@@ -553,7 +559,7 @@ if ($count && $privileges) {
   # leases_data is the name used by leases.js to locate this table
   echo "<table id='leases_data'>";
   # pass the slicename as the [0,0] coordinate as thead>tr>td
-  echo "<thead><tr><td>" . $slice['name'] . "</td>";
+  echo "<thead><tr><td>" . $slice['slice_id'] . '&' . $slice['name'] . "</td>";
   for ($i=0; $i<$steps; $i++) 
     // expose in each header cell the full timestamp, and how to display it - use & as a separator*/
     echo "<th>" . ($start+$i*$grain) . "&" . strftime("%H:%M",$start+$i*$grain). "</th>";
@@ -576,7 +582,7 @@ if ($count && $privileges) {
 	  array_shift($leases);
 	  }*/
 	$duration=$lease['nuntil']-$counter;
-	echo "<td colspan='$duration'>" . $lease['name'] . "</td>";
+	echo "<td colspan='$duration'>" . $lease['lease_id'] . '&' . $lease['name'] . "</td>";
 	$counter=$lease['nuntil']; 
       } else {
 	echo "<td></td>";
-- 
2.47.0