ckp before redoing forms
[plewww.git] / planetlab / nodes / node_actions.php
1 <?php
2
3   // $Id$
4   // This file was created when we decided to make the node page consistent with the other pages
5   // That is to say, we wanted the 'Update' and 'Delete' functions to appear in a drop-down
6   // menu like for the other objects
7   // It appeared after analysis that it was easier to have the following 
8   // functions gathered in a single php file 
9   // (*) node_update.php
10   //    (GET) id=node_id
11   //          displays current settings and prompt for changes before going to node_actions.php
12   // (*) node_actions.php 
13   //    (GET) 'del=node_id'
14   //    (GET) 'update=node_id' -> actual UpdateNode
15   //    (POST) node_id=node_id ng_add=nodegroup_id
16   //           AddNodeToNodeGroup
17   //    (POST) boot_state=boot_state node_id=node_id
18   //    (POST) ng_add=nodegroup_id
19   // (*) downloadconf.php
20   //     (GET) id=node_id [download=1]
21   //           either showed current status, or performed actual config download 
22   
23   // in this new version we support only POST behaviour with the following interface
24   // REQUIRED : node_id=node_id
25   // (*) action='prompt-update' : former node_update.php
26   // (*) action='update'
27   //     hostname=
28   //     model=                 : actually updated the node
29   // (*) action='boot-state'
30   //     boot_state=            : changes bootstate
31   // (*) action='delete'        : deletes node
32   // (*) action='download-node-floppy' : 
33   // (*) action='download-node-iso' : 
34   // (*) action='download-node-usb' : 
35   //                            : same as former downloadconf.php with download unset
36   //     if in addition POST contains a non-empty field 'download' :
37   //                            : performs actual node-dep download
38   // (*) action='download-generic-iso':
39   // (*) action='download-generic-usb':
40   //                            : performs actual generic download
41   // (*) action='add-in-nodegroup': should include this for compatibility, replaces ng_add, 
42   //                                though I could not figure where it's called
43
44
45   // TODO : manage a post field for displaying results of previous page
46   // see the special 'flash' in rails
47
48 // delivering node-dependant images requires larger memory limit
49 // trial and error, based on the current sizes
50 // generic-ISO 43980800 
51 // generic-usb 44720128 
52 // 256M OK
53 // 128M OK
54 // 96M OK
55 // 88M KO
56 // 80M KO
57 // 64M KO
58 // Bottom line is, looks like we need in the order of twice the file size
59 // so let's play it safe
60 // Thierry - for 4.2, we need a larger area, was 100 for 4.1
61 ini_set("memory_limit","150M");
62
63 // Require login
64 require_once 'plc_login.php';
65
66 // Get session and API handles
67 require_once 'plc_session.php';
68 global $plc, $api;
69
70 // Common functions
71 require_once 'plc_functions.php';
72 require_once 'plc_sorts.php';
73
74 // NOTE: this function exits() after it completes its job, 
75 // simply returning leads to html decorations being added around the contents
76 function deliver_and_unlink ($filename) {
77   
78   // for security reasons we want to be able to unlink the resulting file once downloaded
79   // so simply redirecting through a header("Location:") is not good enough
80
81   $size= filesize($filename);
82
83   // headers
84   header ("Content-Type: application/octet-stream");
85   header ("Content-Transfer-Encoding: binary");
86   header ("Content-Disposition: attachment; filename=" . basename($filename) );
87   header ("Content-Length: " . $size );
88   // for getting IE to work properly
89   // from princeton cvs new_plc_www/planetlab/nodes/downloadconf.php 1.2->1.3
90   header ("Pragma: hack");
91   header ("Cache-Control: public, must-revalidate");
92
93   // outputs the whole file contents
94   print (file_get_contents($filename));
95   
96   // unlink the file
97   if (! unlink ($filename) ) {
98     // cannot unlink, but how can we notify this ?
99     // certainly not by printing
100   }
101   exit();
102 }
103
104 function show_download_confirm_button ($api, $node_id, $action, $can_gen_config, $show_details) {
105
106   if( $can_gen_config ) {
107     if ($show_details) {
108       $preview=$api->GetBootMedium($node_id,"node-preview","");
109       print ("<hr /> <h3>Current node configuration contents</h3>");
110       print ("<pre>\n$preview</pre>\n");
111       print ("<hr />");
112     }
113     $action_labels = array ('download-node-floppy' => 'textual node config (for floppy)' ,
114                             'download-node-iso' => 'ISO image',
115                             'download-node-usb' => 'USB image' );
116     
117     $format = $action_labels [ $action ] ;
118     print( "<p><form method='post' action='node_actions.php'>\n");
119     print ("<input type='hidden' name='node_id' value='$node_id'>\n");
120     print ("<input type='hidden' name='action' value='$action'>\n");
121     print ("<input type='hidden' name='download' value='1'>\n");
122     print( "<input type='submit' value='Download $format'>\n" );
123     print( "</form>\n" );
124   } else {
125     echo "<p><font color=red>Configuration file cannot be created until missing values above are updated.</font>";
126   }
127 }
128
129 // check arguments
130
131 if (empty($_POST['node_id'])) {
132   plc_redirect (l_nodes());
133  } else {
134   $node_id = intval($_POST['node_id']);
135 }
136
137 $action=$_POST['action'];
138
139 switch ($action) {
140
141   // ACTION: prompt-update
142   // from former node_update.php
143  case "prompt-update":
144    
145    require_once('plc_drupal.php');
146    $node_info= $api->GetNodes( array( $node_id ), array( "hostname", "model" ) );
147    drupal_set_title("Updating " . $node_info[0]['hostname']);
148    include 'plc_header.php';
149    
150    // start form
151    echo "<form method=post action='node_actions.php'>\n";
152    echo "<input type=hidden name='node_id' value='$node_id'></input>\n";
153    echo "<input type=hidden name='action' value='update'></input>\n";
154    if( $_POST['error'] ) plc_error(unserialize( $_POST['error']));
155    echo "<p><table cellpadding=2><tbody>\n
156        <tr><th>Hostname: </th><td> <input type=text size=35 name='hostname' value='". $node_info[0]['hostname'] ."'></td></tr>\n
157        <tr><th>Model: </th><td> <input type=text size=35 name='model' value='". $node_info[0]['model'] ."'></td></tr>\n
158        </tbody></table>\n
159        <p><input type=submit value='Update Node'>\n
160        </form>\n";
161    
162    echo "<p><a href='index.php?id=$node_id'>Back to Node</a>\n";
163    
164    break;
165
166    // ACTION: update
167    // from former node_actions.php
168  case "update":
169
170    $hostname= $_POST['hostname'];
171    $model= $_POST['model'];
172
173    $fields= array( "hostname"=>$hostname, "model"=>$model );
174    $api->UpdateNode( intval( $node_id ), $fields );
175    $error= $api->error();
176
177    if( empty( $error ) ) {
178      plc_redirect(l_node($node_id));
179    } else {
180      echo "<font color=red>". $error . "</font>\n" ;
181      echo "<p> Press back to retry</p>";
182    }
183
184    break;
185
186    // ACTION: delete
187    // from former node_actions.php
188  case "delete":
189    $api->DeleteNode( intval( $node_id ) );
190    plc_redirect(l_nodes());
191    break;
192
193    // ACTION: boot-state
194    // from former node_actions.php
195  case "boot-state":
196    switch( $_POST['boot_state'] ) {
197    case 'boot':
198    case 'dbg':
199    case 'inst':
200    case 'rins':
201    case 'rcnf':
202    case 'new':
203      $api->UpdateNode( intval( $node_id ), array( "boot_state" => $_POST['boot_state'] ) );
204      plc_redirect (l_node($node_id));
205      break;
206    default:
207      print "<div class='plc-error'> no such boot state " . $_POST['boot_state'] . "</div>";
208      break;
209    }
210    break;
211
212   // ACTION: download-generic
213  case "download-generic-iso":
214  case "download-generic-usb":
215    
216    if ($action=="download-generic-iso") {
217      $boot_action="generic-iso";
218    } else {
219      $boot_action="generic-usb";
220    }
221
222    // place the result in a random-named sub dir for clear filenames
223    $filename = $api->GetBootMedium ($node_id, $boot_action, "%d/%n-%p-%a-%v%s");
224    $error=$api->error();
225    // NOTE. for some reason, GetBootMedium sometimes does not report an error but the
226    // file is not created - this happens e.g. when directory owmer/modes are wrong 
227    // in this case we get an empty filename
228    // see /etc/httpd/logs/error_log in this case
229    if (empty($error) && empty($filename)) {
230      $error="Unexpected error from GetBootMedium - probably wrong directory modes";
231    }    
232    if (! empty($error)) {
233      print ("<div class='plc-error'> $error </div>\n");
234      print ("<p><a href='/db/nodes/index.php?id=$node_id'>Back to node </a>\n");
235      return ;
236    } else {
237      deliver_and_unlink ($filename);
238      exit();
239    }
240    break;
241
242    // ACTION: download-node
243    // from former downloadconf.php
244    
245  case "download-node-floppy":
246  case "download-node-iso":
247  case "download-node-usb":
248    
249    $return= $api->GetNodes( array( $node_id ) );
250    $node_detail= $return[0];
251
252    // non-admin people need to be affiliated with the right site
253    if( ! plc_is_admin() ) {
254      $node_site_id = $node_detail['site_id'];
255      $in_site = plc_in_site($node_site_id);
256      if( ! $in_site) {
257        $error= "Insufficient permission. You cannot create configuration files for this node.";
258      }
259    }
260
261    $hostname= $node_detail['hostname'];
262    $return= $api->GetInterfaces( array( "node_id" => $node_id ), NULL );
263    
264    $can_gen_config= 1;
265    $has_primary= 0;
266    
267    if( count($return) > 0 ) {
268      foreach( $return as $interface_detail ) {
269        if( $interface_detail['is_primary'] == true ) {
270          $has_primary= 1;
271          break;
272        }
273      }
274    }
275
276    if( !$has_primary ) {
277      $can_gen_config= 0;
278    } else {
279      if( $node_detail['hostname'] == "" ) {
280        $can_gen_config= 0;
281        $node_detail['hostname']= "<i>Missing</i>";
282      }
283      
284      $fields= array("method","ip");
285      foreach( $fields as $field ) {
286        if( $interface_detail[$field] == "" ) {
287          $can_gen_config= 0;
288          $interface_detail[$field]= "<i>Missing</i>";
289        }
290      }
291
292      if( $interface_detail['method'] == "static" ) {
293        $fields= array("gateway","netmask","network","broadcast","dns1");
294        foreach( $fields as $field ) {
295          if( $interface_detail[$field] == "" ) {
296            $can_gen_config= 0;
297            $interface_detail[$field]= "<i>Missing</i>";
298          }
299        }
300      }
301
302      if(    $interface_detail['method'] != "static" 
303          && $interface_detail['method'] != "dhcp" ) {
304        $can_gen_config= 0;
305        $interface_detail['method']= "<i>Unknown method</i>";
306      }
307    }
308
309    $download= $_POST['download'];
310    
311    if( $can_gen_config && !empty($download) ) {
312      switch ($action) {
313      case 'download-node-floppy':
314        $boot_action='node-floppy'; 
315        $location = "%d/%n-%v-rename-into-plnode%s";
316        break;
317      case 'download-node-iso':
318        $boot_action='node-iso';
319        $location = "%d/%n-%a-%v%s";
320        break;
321      case 'download-node-usb':
322        $boot_action='node-usb';
323        $location = "%d/%n-%a-%v%s";
324        break;
325      }   
326
327      $filename=$api->GetBootMedium($node_id,$boot_action,$location);
328      $error=$api->error();
329      if (empty($error) && empty($filename)) {
330        $error="Unexpected error from GetBootMedium - probably wrong directory modes";
331      }    
332      if (! empty($error)) {
333        print ("<div class='plc-error'> $error </div>\n");
334        print ("<p><a href='/db/nodes/index.php?id=$node_id'>Back to node </a>\n");
335        return ;
336      } else {
337        deliver_and_unlink ($filename);
338        exit();
339      }
340    }
341
342    drupal_set_title("Download boot material for $hostname");
343
344    $header= <<<EOF
345
346 WARNING: Creating a new configuration file for this node will generate
347 a new node key, and any existing configuration file will be unusable and
348  must be updated before the node can successfully boot, install, or
349 go into debug mode.
350
351 <p>In order to create a configuration file for this node using this page,
352 all the interface settings must be up to date. Below is summary of these
353 values. Any missing values must be entered before this can be used.
354
355 EOF;
356
357    echo $header;
358
359    show_download_confirm_button($api, $node_id, $action, $can_gen_config, false);
360    print ("<p>");
361    print ("<h3>Current interface settings</h3>\n");
362    
363 if( $has_primary ) {
364   print( "<table border=\"0\" cellspacing=\"4\">\n" );
365   
366   print( "<tr><th colspan=2><a href='index.php?id=$node_id'>Node Details</a></th></tr>" );
367   print( "<tr><th>node_id:</th>" );
368   print( "<td>$node_id</td></tr>\n" );
369   print( "<tr><th>Hostname:</th>" );
370   print( "<td>" . $node_detail['hostname'] . "</td></tr>\n" );
371
372   $nn_id = $interface_detail['interface_id'];
373   print( "<tr><th colspan=2><a href='interfaces.php?id=$nn_id'>Interface Details</a></th></tr>" );
374
375   print( "<tr><th>Method:</th>" );
376   print( "<td>" . $interface_detail['method'] . "</td></tr>\n" );
377   print( "<tr><th>IP:</th>" );
378   print( "<td>" . $interface_detail['ip'] . "</td></tr>\n" );
379
380   if( $interface_detail['method'] == "static" ) {
381       print( "<tr><th>Gateway:</th>" );
382       print( "<td>" . $interface_detail['gateway'] . "</td></tr>\n" );
383       print( "<tr><th>Network mask:</th>" );
384       print( "<td>" . $interface_detail['netmask'] . "</td></tr>\n" );
385       print( "<tr><th>Network address:</th>" );
386       print( "<td>" . $interface_detail['network'] . "</td></tr>\n" );
387       print( "<tr><th>Broadcast address:</th>" );
388       print( "<td>" . $interface_detail['broadcast'] . "</td></tr>\n" );
389       print( "<tr><th>DNS 1:</th>" );
390       print( "<td>" . $interface_detail['dns1'] . "</td></tr>\n" );
391       print( "<tr><th>DNS 2:</th>" );
392       if( $interface_detail['dns2'] == "" ) {
393         print( "<td><i>Optional, missing</i></td></tr>\n" );
394       } else {
395         print( "<td>" . $interface_detail['dns2'] . "</td></tr>\n" );
396       }
397     }
398
399   print ("<tr><th colspan=2><a href='interfaces.php?id=$nn_id'>Additional Settings</a></th></tr>\n");
400   $nn_id = $interface_detail['interface_id'];
401   $settings=$api->GetInterfaceTags(array("interface_id" => array($nn_id)));
402   foreach ($settings as $setting) {
403     $category=$setting['category'];
404     $name=$setting['tagname'];
405     $value=$setting['value'];
406     print (" <tr><th> $category $name </th><td> $value </td></tr>\n");
407   }
408
409   print( "</table>\n" );
410 } else {
411   print( "<p class='plc-warning'>This node has no configured primary interface.</p>\n" );
412 }
413
414  show_download_confirm_button($api, $node_id, $action, $can_gen_config, true);
415  break;
416  
417  default:
418    echo "Unkown action <$action>.";
419    plc_redirect (l_node($node_id));
420    break;
421  }
422
423 ?>