add a non-primary interface : user to select for virtual or physical interface (set...
authorThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Fri, 20 Nov 2009 09:58:49 +0000 (09:58 +0000)
committerThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Fri, 20 Nov 2009 09:58:49 +0000 (09:58 +0000)
thoroughly rewrote the add-interface add-node javascript helpers
form interface has changed, method now passed as part of an options hash, new options now supported
more focused redirections after actions (prrof-of-concept, needs more work)
node view mentions the number of tags for each interface for faster checks

planetlab/common/actions.php
planetlab/css/plc_style.css
planetlab/events/events_choser.php
planetlab/includes/plc_functions.php
planetlab/nodes/interface.js
planetlab/nodes/interface.php
planetlab/nodes/node.php
planetlab/nodes/node_add.php
planetlab/slices/slice_add.php
plekit/php/details.php
plekit/php/form.php

index b3a34ba..c93a326 100644 (file)
@@ -421,8 +421,22 @@ switch ($action) {
    } else {
      $ip=$interface['ip'];
      drupal_set_message ("Interface $ip added into node $node_id");
+     if ($_POST['is-virtual']) {
+       $ifname=$_POST['ifname'];
+       if ($api->AddInterfaceTag($interface_id,"ifname",$ifname) <= 0) 
+        drupal_set_error ("Could not set tag 'ifname'=$ifname");
+       else 
+        drupal_set_message ("Set tag 'ifname'=$ifname");
+       $alias=$_POST['alias'];
+       // deafult to interface_id
+       if ( ! $alias ) $alias=strval($interface_id);
+       if ($api->AddInterfaceTag($interface_id,"alias",$alias) <= 0) 
+        drupal_set_error ("Could not set tag 'alias'=$alias");
+       else 
+        drupal_set_message ("Set tag 'alias'=$alias");
+     }
    }
-   plc_redirect (l_node($node_id));
+   plc_redirect (l_node_interfaces($node_id));
  }
    
  case 'update-interface': {
@@ -825,9 +839,9 @@ Our support team will be glad to answer any question that you might have.
    }
    
    if ($node_mode)
-     plc_redirect (l_node($node_id));
+     plc_redirect (l_node_tags($node_id));
    else
-     plc_redirect (l_interface($interface_id));
+     plc_redirect (l_interface_tags($interface_id));
  }
 
  case 'delete-node-tags' : 
@@ -862,9 +876,9 @@ Our support team will be glad to answer any question that you might have.
    else
      drupal_set_error ("Could not delete all selected tags, only $counter were removed");
    if ($node_mode)
-     plc_redirect(l_node($_POST['node_id']));
+     plc_redirect(l_node_tags($_POST['node_id']));
    else
-     plc_redirect(l_interface($_POST['interface_id']));
+     plc_redirect(l_interface_tags($_POST['interface_id']));
  }
 
 //////////////////////////////////////// nodegroups
index 4715bea..7e5472e 100644 (file)
@@ -70,7 +70,7 @@ p.node_add {
     font-style: italic;
     padding: 20px 40px;
     text-align: center;
-    font-size: larger;
+/*    font-size: smaller; */
 }
 
 p.node_download {
@@ -149,3 +149,7 @@ div#toggle-area-create-slice-persons {
 div#toggle-area-create-slice-details>table.plc_details {
     margin:0 ;
 }
+div#toggle-container-add-node,
+div#toggle-container-add-interface {
+    background-color: #e0d0ff;
+}
index da53366..875f757 100644 (file)
@@ -43,7 +43,7 @@ $from_picker->today();
 $until_picker = new PlekitDatepicker ('until_date','Until (inclusive)',array('inline'=>true));
 $until_picker->today();
 
-$form=new PlekitForm(l_events(),array(),'GET');
+$form=new PlekitForm(l_events(),array(),array('method'=>'get'));
 $form->start();
 
 $details = new PlekitDetails (true);
index 76c81e7..658cc86 100644 (file)
@@ -78,6 +78,8 @@ function l_pcu ($pcu_id)              { return "/db/sites/pcu.php?id=$pcu_id"; }
 function l_nodes ()                    { return "/db/nodes/index.php"; }
 function l_nodes_peer ($peer_id)       { return "/db/nodes/index.php?peerscope=$peer_id"; }
 function l_node ($node_id)             { return "/db/nodes/node.php?id=$node_id"; }
+function l_node_interfaces ($node_id)  { return "/db/nodes/node.php?id=$node_id%show_details=0&show_interfaces=1"; }
+function l_node_tags ($node_id)                { return "/db/nodes/node.php?id=$node_id%show_details=0&show_tags=1"; }
 function l_node_t ($node_id,$text)     { return href (l_node($node_id),$text); }
 function l_node_obj($node)             { return href(l_node($node['node_id']),$node['hostname']); }
 function l_node_add ()                 { return "/db/nodes/node_add.php"; }
index 434333e..9e5c496 100644 (file)
@@ -2,25 +2,8 @@
 
 /* using prototype.js */
 
-/* disable/enable input fields according to the selected method */
-function updateMethodFields() {
-  var method=$('method');
-  var index = method.selectedIndex;
-  var selectedText = method[index].text;
-
-  var is_static = selectedText == 'Static';
-  var is_tap = selectedText == 'TUN/TAP';
-
-  $('netmask').disabled= !is_static;
-  $('network').disabled= !is_static;
-  $('gateway').disabled= !is_static && !is_tap;
-  $('broadcast').disabled= !is_static;
-  $('dns1').disabled= !is_static;
-  $('dns2').disabled= !is_static;
-}
-
-/* updates broadcast & network from IP and netmask, as long as they are reasonably set */
-
+////////////////////
+// basic IP arithmetic
 /* a is assumed to be a 4-items array text.split('.') */
 function arr_to_int (as) {
   /*a=as.map(parseInt);*/
@@ -51,9 +34,9 @@ function mask (masklen) {
 
 // input is the user-typed text
 // return the number of bits in the mask (like 24 for a /24) or -1 if the mask is wrong
-function get_masklen (nm) {
-  var a = nm.split('.');
-  if ( 4 != a.length ) return -1;
+function get_masklen (netmask) {
+  var a = netmask.split('.');
+  if ( IPCheckerAtom (netmask,'netmask')) return -1;
   var n = arr_to_int (a);
   var bits = int_to_bits (n);
   var masklen=0;
@@ -63,7 +46,6 @@ function get_masklen (nm) {
   return (n == n_mask) ? masklen : -1;
 }
 
-
 // returns network and broadcast from ip and masklen
 function get_derived (n,masklen) {
   var n_mask = mask(masklen);
@@ -79,29 +61,52 @@ function same_subnet (ip1,ip2,masklen) {
   return (n1&mask(masklen)) == (n2 & mask(masklen));
 }
 
-function networkHelper () {
-  window.console.log('networkHelper');
-  var ip=$('ip').value;
-  var nm=$('netmask').value;
-
-  var ip_a = ip.split('.');
-  var nm_a = ip.split('.');
-  
-  /* don't trigger if the input does not make sense */
-  if (ip_a.length != 4) return; 
-  if (ip_a[3] == "") return;
-  if (nm_a.length != 4) return; 
-  if (nm_a[3] == "") return;
+//////////////////// basic chackers
+function IPCheckerAtom (ip,id) {
+  if ( ! ip ) return "Empty field " + id;
+  ip_a = ip.split('.');
+  if ( ip_a.length != 4) return "Invalid IP (" + id + ") "+ ip;
+  for (var i=0; i<4; i++) if (ip_a[i]<0 || ip_a[i]>256) return "Invalid IP (" + id + ") "+ ip;
+  return "";
+}
   
-  /*check netmask*/
-  var masklen=get_masklen (nm);
-  if (masklen < 0) return;
+function IPCheckerSilent (id) { return IPCheckerAtom ( $(id).value, id); }
+
+function netmaskCheckerSilent (id) {
+  var netmask=$(id).value;
+  var check_ip = IPCheckerAtom (netmask,'netmask');
+  if (check_ip) return check_ip;
+  var masklen = get_masklen (netmask);
+  if (masklen <= 0) return "Invalid netmask " + netmask;
+  return "";
+}
 
-  var ip_n=arr_to_int(ip_a);
-  var derived = get_derived(ip_n,masklen);
+// focus on the field to check, other ones checked already
+function subnetChecker(id, optional) {
+  var error= subnetCheckerSilent($(id).value);
+  if (error) {
+    Form.Element.focus($(id));
+    alert(error);
+  }
+}
 
-  $('network').value=derived[0];
-  $('broadcast').value=derived[1];
+function subnetCheckerSilent (id, optional) {
+
+  var subnet=$(id).value;
+  // skip this field if optional
+  if (optional && (subnet=="")) return "";
+  var check_ip = IPCheckerAtom (subnet,id);
+  if (check_ip) return check_ip;
+
+  var masklen = get_masklen ($('netmask').value);
+  if (masklen < 0) return "Could not check " + id;
+
+  var ip=$('ip').value;
+
+  if ( ! same_subnet (ip,subnet,masklen) ) 
+    return id + ' ' + subnet + ' is not in the /' + masklen + ' subnet range';
+  
+  return "";
 }
 
 function macChecker(id, optional) {
@@ -113,53 +118,79 @@ function macChecker(id, optional) {
 }
 
 function macCheckerSilent(macAdd) {
-       var RegExPattern = /^[0-9a-fA-F:]+$/;
-       if (!(macAdd.match(RegExPattern)) || macAdd.length != 17) 
-       {
-               return "Invalid MAC Address";
-       } else {
-               return "";
-       }
-}
-
-/* check one */
-function subnetChecker (id, optional) {
-  var error= subnetCheckerSilent([id,optional]);
-  if (error) {
-    Form.Element.focus($(id));
-    alert(error);
+  var RegExPattern = /^[0-9a-fA-F:]+$/;
+  
+  if (!(macAdd.match(RegExPattern)) || macAdd.length != 17) {
+    return "Invalid MAC Address";
+  } else {
+    return "";
   }
 }
 
-function subnetCheckerSilent (args) {
+////////////////////
+// updates broadcast & network from IP and netmask, as long as they are reasonably set 
+function networkHelper () {
+  var ip=$('ip').value;
+  var netmask=$('netmask').value;
+
+  /* don't trigger if the input does not make sense */
+  if (IPCheckerAtom (ip,'ip')) return;
+  if (IPCheckerAtom (netmask,'netmask')) return;
   
-  id=args[0];
-  optional=args[1];
+  /*check netmask*/
+  var masklen=get_masklen (netmask);
+  if (masklen <= 0) return;
 
-  var ip2=$(id).value;
-  if (optional && (ip2=="")) return "";
-  if ( ip2.split(".").length != 4) return "Inconsistent value for " + id;
+  var ip_a = ip.split('.');
+  var ip_n=arr_to_int(ip_a);
+  var derived = get_derived(ip_n,masklen);
 
-  var masklen = get_masklen ($('netmask').value);
-  if (masklen < 0) return "Inconsistent netmask";
+  $('network').value=derived[0];
+  $('broadcast').value=derived[1];
+}
 
-  var ip=$('ip').value;
-  if ( ip.split(".").length != 4) return "Inconsistent IP";
+// disable/enable input fields according to the selected method
+function updateMethodFields() {
+  var method=$('method');
+  var index = method.selectedIndex;
+  var selectedText = method[index].text;
+  var is_static = selectedText == 'Static';
+  var is_tap = selectedText == 'TUN/TAP';
 
-  if ( ! same_subnet (ip,ip2,masklen) ) 
-    return id + ' ' + ip2 + ' is not in the /' + masklen + ' subnet range';
-  
-  return "";
+  $('netmask').disabled= !is_static;
+  $('network').disabled= !is_static;
+  $('gateway').disabled= !is_static && !is_tap;
+  $('broadcast').disabled= !is_static;
+  $('dns1').disabled= !is_static;
+  $('dns2').disabled= !is_static;
 }
 
+// check inputs and prevent submit in case s/t is wrong
 function interfaceSubmit () {
-  alert ('submitting');
-  // get error strings, and remove the empty ones
-  // dns2 is optional
-  var errors=['gateway','dns1','dns2'].zip ([true,true,false],subnetCheckerSilent).reject( function (s) {return s.length==0;} );
-  if ( ! errors.length)
-    $('ip').up('form').submit();
-  else
-    alert(errors.join("\n"));
+
+  var method=$('method');
+  var index = method.selectedIndex;
+  var selectedText = method[index].text;
+  var is_static = selectedText == 'Static';
+  var is_tap = selectedText == 'TUN/TAP';
+
+  var errors="";
+  var counter=0;
+  var error;
+  error = IPCheckerSilent ('ip'); if (error) errors += error + "\n" ;
+  if ( ! $('netmask').disabled ) { error = netmaskCheckerSilent ('netmask'); if (error) errors += error + "\n" ; }
+  if ( ! $('network').disabled ) { error = IPCheckerSilent ('network'); if (error) errors += error + "\n" ; }
+  if ( ! $('gateway').disabled ) { error = subnetCheckerSilent ('gateway',false); if (error) errors += error + "\n" ; }
+  if ( ! $('broadcast').disabled ) { error = subnetCheckerSilent ('broadcast',false); if (error) errors += error + "\n" ; }
+  if ( ! $('dns1').disabled ) { error = subnetCheckerSilent ('dns1',false); if (error) errors += error + "\n" ; }
+  if ( ! $('dns2').disabled ) { error = subnetCheckerSilent ('dns2',true); if (error) errors += error + "\n" ; }
+
+  if ( ! errors.length) {
+    return true;
+  } else {
+    alert("-- Cannot create interface --\n" + errors);
+    return false;
+  }
 }
+
+
index f9676ac..338b9b8 100644 (file)
@@ -57,7 +57,8 @@ $fields=array( 'method', 'type', 'ip', 'gateway', 'network', 'broadcast', 'netma
               'dns1', 'dns2', 'hostname', 'mac', 'bwlimit', 'node_id' );
 
 //////////////////////////////
-$nodes= $api->GetNodes( array( intval($node_id) ), array( 'node_id', 'hostname', 'site_id' ) );
+$node_columns = array( 'node_id', 'hostname', 'site_id', 'interface_ids' );
+$nodes= $api->GetNodes( array( intval($node_id) ), $node_columns);
 $node= $nodes[0];
 $site_id=$node['site_id'];
 
@@ -71,7 +72,8 @@ drupal_set_html_head ('
 <script type="text/javascript" src="/planetlab/nodes/interface.js"></script>
 ');
 
-$toggle = new PlekitToggle ('interface',"Details",
+$nifty_id = ($mode == 'add' ) ? 'add-interface' : 'interface';
+$toggle = new PlekitToggle ($nifty_id,"Details",
                            array('bubble'=>'Display and modify details for that interface',
                                  'visible'=>get_arg('show_details',true)));
 $toggle->start();
@@ -81,12 +83,18 @@ $details=new PlekitDetails($can_update);
 // xxx hardwire network type for now
 $form_variables = array('node_id'=>$node_id,'type'=>"ipv4");
 if ($mode == "update") $form_variables['interface_id']=$interface_id;
-$form=$details->form_start(l_actions(),$form_variables);
+$form=$details->form_start(l_actions(),$form_variables,
+                          array('onSubmit'=>'return interfaceSubmit()'));
 
 $details->start();
 
+if ($mode == 'add') 
+  // would have preferred 'dhcp' as a default but could not figure how to trigger updateMethodFields on startup
+  $method_default = 'static';
+else
+  $method_default = $interface['method'];
 $method_select = $form->select_html ("method",
-                                    interface_method_selectors($api,$interface['method'],false),
+                                    interface_method_selectors($api,$method_default,false),
                                     array('id'=>'method','onChange'=>'updateMethodFields()'));
 $details->th_td("Method",$method_select,"method",array('input_type'=>'select','value'=>$interface['method']));
 
@@ -113,17 +121,21 @@ $details->th_td("BW limit (bps)",$interface['bwlimit'],"bwlimit",array('width'=>
 $details->th_td("Hostname",$interface['hostname'],"hostname");
 $details->th_td("Mac address",$interface['mac'],"mac", array('onChange'=>'macChecker("mac", true)'));
 
-# xxx should the user be allowed to change this ?
-//$mac=$interface['mac'];
-//if ($mac) $details->th_td("MAC address",$mac);
-
 // the buttons
-$update_button = $form->submit_html ("update-interface","Update",
-                                    array('onSubmit'=>'interfaceSubmit()'));
+$update_button = $form->submit_html ("update-interface","Update");
 $add_button = $form->submit_html ("add-interface","Add as new",
                                  array('onSubmit'=>'interfaceSubmit()'));
 switch ($mode) {
  case 'add':
+   // primary interfaces can't be virtual
+   $is_primary = (count($node['interface_ids']) == 0);
+   if ( ! $is_primary) {
+     // default is to create virtual interfaces
+     $details->th_th($form->checkbox_html('is-virtual','yes',array('checked'=>'checked')),
+                    "Virtual Interface");
+     $details->th_td("Interface name","eth0",'ifname');
+     $details->th_td("alias (leave empty if unsure)","",'alias');
+   }
    $details->tr($add_button,"right");
    break;
  case 'update':
@@ -142,7 +154,7 @@ if ($mode == 'add') return;
 //////////////////////////////////////// tags
 $tags=$api->GetInterfaceTags (array('interface_id'=>$interface_id));
 $toggle=new PlekitToggle ('tags',count_english($tags,'tag'),
-                         array('visible'=>get_arg('show_tags',false)));
+                         array('visible'=>get_arg('show_tags',true)));
 $toggle->start();
 
 $form = new PlekitForm (l_actions(),array('interface_id'=>$interface_id));
index 11df5b8..7a863f9 100644 (file)
@@ -349,7 +349,7 @@ if ( $local_peer ) {
   $tagnames = array_map ("get_tagname",$tags);
   $nodegroups_hash=plc_nodegroup_global_hash($api,$tagnames);
   
-  $toggle = new PlekitToggle ('tags',count_english_warning($tags,'tag'),
+  $toggle = new PlekitToggle ('tags',count_english($tags,'tag'),
                              array('bubble'=>'Inspect and set tags on that node',
                                    'visible'=>get_arg('show_tags',false)));
   $toggle->start();
@@ -432,6 +432,8 @@ if ( $local_peer ) {
     $headers["Type"]="string";
     $headers["MAC"]="string";
     $headers["bw limit"]="sortBandwidth";
+    $headers["tags"]=array('type'=>'int',
+                          'title'=>"number of tags set on interface");
     // a single symbol, marking 'p' for primary and a delete button for non-primary
     if ( $privileges ) $headers[plc_delete_icon()]='string';
 
@@ -451,6 +453,8 @@ if ( $local_peer ) {
       $table->cell($interface['type']);
       $table->cell($interface['mac']);
       $table->cell(pretty_bandwidth($interface['bwlimit']));
+      $table->cell(href(l_interface_tags($interface_id),
+                       count($interface['interface_tag_ids'])));
       if ( $privileges ) {
        if ($interface['is_primary']) {
          $table->cell(plc_bubble("p","Cannot delete a primary interface"));
index 78b5e3b..63b9fd5 100644 (file)
@@ -11,6 +11,7 @@ global $plc, $api;
 
 // Common functions
 require_once 'plc_functions.php';
+require_once 'toggle.php';
 require_once 'details.php';
 require_once 'form.php';
   
@@ -152,18 +153,28 @@ if( ! $model ) $model= "Custom";
 print <<< EOF
 <p class='node_add'>
 This page lets you declare a new machine in your site. 
-This must be done before the machine is turned on, as it will allow you to download a boot image when complete for this node.
 <br/>
-You must enter an IP address even if you use DHCP.
+This must be done before the machine is turned on, 
+as it will allow you to download a boot image when complete for this node.
+<br/>
+  It is now reserved to admins, as regular users are supposed to use the register wizard, that among other things enforces PCU deployment.
+<br/>
+An IP address is required even if you use DHCP.
 </p>
 EOF;
 
+$toggle = new PlekitToggle ('add-node',"Add Node",
+                           array('bubble'=>'Add a node - does not enforce PCU - for admins only !',
+                                 'visible'=>get_arg('show_details',true)));
+$toggle->start();
+
 $details=new PlekitDetails($has_privileges);
 
 // xxx hardwire network type for now
 $form_variables = array('type'=>"ipv4");
 //$form=$details->form_start(l_actions(),$form_variables);
-$form=$details->form_start('/db/nodes/node_add.php',$form_variables);
+$form=$details->form_start('/db/nodes/node_add.php',$form_variables,
+                          array('onSubmit'=>'return interfaceSubmit()'));
 
 $details->start();
 
@@ -198,12 +209,12 @@ $details->space();
 $details->th_td("BW limit (bps)",$bwlimit,"bwlimit",array('width'=>11));
 
 // the buttons
-$add_button = $form->submit_html ("add-node","Add New Node",
-                                 array('onSubmit'=>'interfaceSubmit()'));
+$add_button = $form->submit_html ("add-node","Add New Node");
 $details->tr($add_button,"right");
 
-$details->end();
 $form->end();
+$details->end();
+$toggle->end();
 
 // Print footer
 include 'plc_footer.php';
index 4558e84..053f5cb 100644 (file)
@@ -172,7 +172,7 @@ if ($multiple_sites) {
     $selectors []= $selector;
   }
 
-  $site_form = new  PleKitForm (l_slice_add(),array(),'get');
+  $site_form = new  PleKitForm (l_slice_add(),array(),array('method'=>'get'));
   $site_form->start();
   print $site_form->label_html('site_id','Or choose some other site');
   print $site_form->select_html('site_id',$selectors,array('autosubmit'=>true,
index 91dc7b7..bee9b5c 100644 (file)
@@ -60,9 +60,9 @@ class PlekitDetails {
 
   // starts an inner form if the details are editable
   // accpets same args as PlekitForm
-  function form_start ($url,$values,$method="POST") { print $this->form_start_html($url,$values,$method); return $this->form; }
-  function form_start_html ($url,$values,$method="POST") {
-    $this->form = new PlekitForm ($url,$values,$method);
+  function form_start ($url,$values,$options=NULL) { print $this->form_start_html($url,$values,$options); return $this->form; }
+  function form_start_html ($url,$values,$options=NULL) {
+    $this->form = new PlekitForm ($url,$values,$options);
     return $this->form->start_html();
   }
 
index 4d5e9fd..f12d53e 100644 (file)
@@ -12,9 +12,11 @@ class PlekitForm {
   // mandatory
   var $url;
   var $values; // a hash var=>value - default is empty array
-  var $method; // default is POST
+  var $method; // default is POST, can be changed with options
+  var $onSubmit; // can be set with options
+  var $onReset; // can be set with options
 
-  function PlekitForm ($full_url, $values, $method="POST") {
+  function PlekitForm ($full_url, $values, $options=NULL) {
     // so we can use the various l_* functions:
     // we parse the url to extract var-values pairs, 
     // and add them to the 'values' argument if any
@@ -29,12 +31,17 @@ class PlekitForm {
     $this->values=$values;
 
     // make strict xhtml happy
-    $this->method=strtolower($method);
+    $this->method="post";      if ($options['method']) $this->method=strtolower($options['method']);
+    $this->onSubmit=NULL;      if ($options['onSubmit']) $this->onSubmit=$options['onSubmit'];
+    $this->onReset=NULL;       if ($options['onReset']) $this->onReset=$options['onReset'];
   }
 
   function start () { print $this->start_html(); }
   function start_html () {
-    $html="<form method='$this->method' action='$this->url' enctype='multipart/form-data'>";
+    $html="<form method='$this->method' action='$this->url' enctype='multipart/form-data'";
+    if ($this->onSubmit) $html .= " onSubmit='$this->onSubmit'";
+    if ($this->onReset) $html .= " onReset='$this->onReset'";
+    $html .= ">";
     if ($this->values) 
       foreach ($this->values as $key=>$value) 
        $html .= $this->hidden_html($key,$value);