Add static network configuration option and make <Back> work.
authorRichard W.M. Jones <rjones@redhat.com>
Tue, 5 Feb 2008 17:14:17 +0000 (17:14 +0000)
committerRichard W.M. Jones <rjones@redhat.com>
Tue, 5 Feb 2008 17:14:17 +0000 (17:14 +0000)
 - Static network configuration.
 - Documentation.
 - Fix dialog back button / skipping.

virt-p2v
virt-p2v.pod

index 98febf1..20baf62 100755 (executable)
--- a/virt-p2v
+++ b/virt-p2v
@@ -39,6 +39,7 @@ type state = { greeting : bool;
               remote_directory : string option;
               remote_username : string option;
               network : network option;
+              static_network_config : static_network_config option;
               devices_to_send : string list option;
               root_filesystem : partition option;
               hypervisor : hypervisor option;
@@ -46,12 +47,17 @@ type state = { greeting : bool;
               memory : int option; vcpus : int option;
               mac_address : string option;
             }
-and network = Auto | Shell
+and network = Auto
+           | Shell
+           | QEMUUserNet
+           | Static
 and partition = Part of string * string (* eg. "hda", "1" *)
               | LV of string * string  (* eg. "VolGroup00", "LogVol00" *)
 and hypervisor = Xen | QEMU | KVM
 and architecture = I386 | X86_64 | IA64 | PPC | PPC64 | SPARC | SPARC64
                 | OtherArch of string | UnknownArch
+and static_network_config = string * string * string * string * string
+    (* interface, address, netmask, gateway, nameserver *)
 
 (*----------------------------------------------------------------------*)
 (* TO MAKE A CUSTOM virt-p2v SCRIPT, adjust the defaults in this section.
@@ -81,6 +87,7 @@ let defaults = {
   devices_to_send = None;
   root_filesystem = None;
   network = None;
+  static_network_config = None;
   hypervisor = None;
   architecture = None;
   memory = None;
@@ -159,7 +166,7 @@ and string_of_linux_distro = function
  *
  * Returns the exit status (Yes lines | No | Help | Back | Error).
  *)
-let msgbox, yesno, inputbox, radiolist, checklist =
+let msgbox, yesno, inputbox, radiolist, checklist, form =
   (* Internal function to actually run the "dialog" shell command. *)
   let run_dialog cparams params =
     let params = cparams @ params in
@@ -255,8 +262,25 @@ let msgbox, yesno, inputbox, radiolist, checklist =
          string_of_int listheight :: items in
        run_dialog cparams items
     )
+
+  (* Form. *)
+  and form =
+    with_common (
+      fun cparams text height width formheight items ->
+       let items = List.map (
+         fun (label, y, x, item, y', x', flen, ilen) ->
+           [ label; string_of_int y; string_of_int x; item;
+             string_of_int y'; string_of_int x';
+             string_of_int flen; string_of_int ilen ]
+       ) items in
+       let items = List.concat items in
+       let items = "--form" :: text ::
+         string_of_int height :: string_of_int width ::
+         string_of_int formheight :: items in
+       run_dialog cparams items
+    )
   in
-  msgbox, yesno, inputbox, radiolist, checklist
+  msgbox, yesno, inputbox, radiolist, checklist, form
 
 (* Print failure dialog and exit. *)
 let fail_dialog text =
@@ -474,6 +498,29 @@ let auto_network state =
     (* Non-interactive: return the status of /etc/init.d/network start. *)
     status = 0
 
+(* Configure the network statically. *)
+let static_network state =
+  match state.static_network_config with
+  | None -> false                      (* failed *)
+  | Some (interface, address, netmask, gateway, nameserver) ->
+      let do_cmd_or_exit cmd = if shwithstatus cmd <> 0 then raise Exit in
+      try
+       do_cmd_or_exit (sprintf "ifconfig %s %s netmask %s"
+                         (quote interface) (quote address) (quote netmask));
+       do_cmd_or_exit (sprintf "route add default gw %s %s"
+                         (quote gateway) (quote interface));
+       if nameserver <> "" then
+         do_cmd_or_exit (sprintf "echo nameserver %s > /etc/resolv.conf"
+                           (quote nameserver));
+       true                            (* succeeded *)
+      with
+       Exit -> false                   (* failed *)
+
+let qemu_network () =
+  sh "ifconfig eth0 10.0.2.10 netmask 255.255.255.0";
+  sh "route add default gw 10.0.2.2 eth0";
+  sh "echo nameserver 10.0.2.3 > /etc/resolv.conf"
+
 (* Map local device names to remote devices names.  At the moment we
  * just change sd* to hd* (as device names appear under fullvirt).  In
  * future, lots of complex possibilities.
@@ -812,13 +859,42 @@ let rec main ttyname =
 
   let ask_network state =
     match
-    radiolist "Network configuration" "Network configuration" 10 50 2 [
+    radiolist "Network configuration" "Network configuration" 12 50 4 [
       "auto", "Automatic configuration", state.network = Some Auto;
+      "ask", "Ask for fixed IP address and gateway",
+        state.network = Some Static;
       "sh", "Configure from the shell", state.network = Some Shell;
+      "qemu", "QEMU user network (for developers only)",
+        state.network = Some QEMUUserNet
     ]
     with
     | Yes ("auto"::_) -> Next { state with network = Some Auto }
+    | Yes ("ask"::_) -> Next { state with network = Some Static }
     | Yes ("sh"::_) -> Next { state with network = Some Shell }
+    | Yes ("qemu"::_) -> Next { state with network = Some QEMUUserNet }
+    | Yes _ | No | Help | Error -> Ask_again
+    | Back -> Prev
+  in
+
+  let ask_static_network_config state =
+    let interface, address, netmask, gateway, nameserver =
+      match state.static_network_config with
+      | Some (a,b,c,d,e) -> a,b,c,d,e
+      | None -> "eth0","","","","" in
+    match
+    form "Static network configuration" "Static network configuration"
+      13 50 5 [
+       "Interface",  1, 0, interface,  1, 12, 8,  0;
+       "Address",    2, 0, address,    2, 12, 16, 0;
+       "Netmask",    3, 0, netmask,    3, 12, 16, 0;
+       "Gateway",    4, 0, gateway,    4, 12, 16, 0;
+       "Nameserver", 5, 0, nameserver, 5, 12, 16, 0;
+      ]
+    with
+    | Yes (interface::address::netmask::gateway::nameserver::_) ->
+       Next { state with
+                static_network_config = Some (interface, address, netmask,
+                                              gateway, nameserver) }
     | Yes _ | No | Help | Error -> Ask_again
     | Back -> Prev
   in
@@ -978,6 +1054,7 @@ MAC address:  %s"
          (Option.default "" state.remote_directory)
          (match state.network with
          | Some Auto -> "Auto-configure" | Some Shell -> "Shell"
+         | Some Static -> "Static" | Some QEMUUserNet -> "QEMU user net"
          | None -> "")
          (String.concat "," (Option.default [] state.devices_to_send))
          (Option.map_default dev_of_partition "" state.root_filesystem)
@@ -1004,57 +1081,52 @@ MAC address:  %s"
   in
 
   (* This is the list of dialogs, in order.  The user can go forwards or
-   * backwards through them.  The second parameter in each pair is
-   * false if we need to skip this dialog (info already supplied in
-   * 'defaults' above).
+   * backwards through them.
+   *
+   * The second parameter in each tuple is true if we need to skip
+   * this dialog statically (info already supplied in 'defaults' above).
+   *
+   * The third parameter in each tuple is a function that tests whether
+   * this dialog should be skipped, given other parts of the current state.
    *)
-  let dlgs = [|
-    ask_greeting,                      (* Initial greeting. *)
-      defaults.greeting;
-    ask_hostname,                      (* Hostname. *)
-      defaults.remote_host = None;
-    ask_port,                          (* Port number. *)
-      defaults.remote_port = None;
-    ask_directory,                     (* Remote directory. *)
-      defaults.remote_directory = None;
-    ask_username,                      (* Remote username. *)
-      defaults.remote_username = None;
-    ask_network,                       (* Network configuration. *)
-      defaults.network = None;
-    ask_devices,                       (* Block devices to send. *)
-      defaults.devices_to_send = None;
-    ask_root,                          (* Root filesystem. *)
-      defaults.root_filesystem = None;
-    ask_hypervisor,                    (* Hypervisor. *)
-      defaults.hypervisor = None;
-    ask_architecture,                  (* Architecture. *)
-      defaults.architecture = None;
-    ask_memory,                                (* Memory. *)
-      defaults.memory = None;
-    ask_vcpus,                         (* VCPUs. *)
-      defaults.vcpus = None;
-    ask_mac_address,                   (* MAC address. *)
-      defaults.mac_address = None;
-    ask_verify,                                (* Verify settings. *)
-      defaults.greeting
+  let dlgs =
+    let dont_skip _ = false in
+    [|
+    ask_greeting,      not defaults.greeting,             dont_skip;
+    ask_hostname,      defaults.remote_host <> None,      dont_skip;
+    ask_port,          defaults.remote_port <> None,      dont_skip;
+    ask_directory,     defaults.remote_directory <> None, dont_skip;
+    ask_username,      defaults.remote_username <> None,  dont_skip;
+    ask_network,       defaults.network <> None,          dont_skip;
+    ask_static_network_config,
+      defaults.static_network_config <> None,
+      (function { network = Some Static } -> false | _ -> true);
+    ask_devices,       defaults.devices_to_send <> None,  dont_skip;
+    ask_root,          defaults.root_filesystem <> None,  dont_skip;
+    ask_hypervisor,    defaults.hypervisor <> None,       dont_skip;
+    ask_architecture,  defaults.architecture <> None,     dont_skip;
+    ask_memory,        defaults.memory <> None,           dont_skip;
+    ask_vcpus,         defaults.vcpus <> None,            dont_skip;
+    ask_mac_address,   defaults.mac_address <> None,      dont_skip;
+    ask_verify,        not defaults.greeting,             dont_skip;
   |] in
 
   (* Loop through the dialogs until we reach the end. *)
-  let rec loop posn state =
-    eprintf "dialog loop: posn = %d\n%!" posn;
+  let rec loop ?(back=false) posn state =
+    eprintf "dialog loop: posn = %d, back = %b\n%!" posn back;
     if posn >= Array.length dlgs then state (* Finished all dialogs. *)
+    else if posn < 0 then loop 0 state
     else (
-      let dlg, no_skip = dlgs.(posn) in
-      let skip = not no_skip in
-      if skip then
-       (* Skip this dialog and move straight to the next one. *)
-       loop (posn+1) state
+      let dlg, skip_static, skip_dynamic = dlgs.(posn) in
+      if skip_static || skip_dynamic state then
+       (* Skip this dialog. *)
+       loop ~back (if back then posn-1 else posn+1) state
       else (
        (* Run dialog. *)
        match dlg state with
        | Next new_state -> loop (posn+1) new_state (* Forwards. *)
-       | Prev -> loop (posn-1) state       (* Backwards / back button. *)
-       | Ask_again -> loop posn state      (* Repeat the question. *)
+       | Ask_again -> loop posn state  (* Repeat the question. *)
+       | Prev -> loop ~back:true (posn-1) state (* Backwards / back button. *)
       )
     )
   in
@@ -1112,6 +1184,15 @@ MAC address:  %s"
        printf "When you have finished, exit the shell with ^D or exit.\n\n%!";
        shell ()
 
+   | Static ->
+       printf "Trying static network configuration.\n\n%!";
+       if not (static_network state) then (
+        printf "\nAuto-configuration failed.  Starting a shell.\n\n";
+        printf "Please configure the network from this shell.\n\n";
+        printf "When you have finished, exit the shell with ^D or exit.\n\n";
+        shell ()
+       )
+
    | Auto ->
        printf
         "Trying network auto-configuration from root filesystem ...\n\n%!";
@@ -1121,6 +1202,9 @@ MAC address:  %s"
         printf "When you have finished, exit the shell with ^D or exit.\n\n";
         shell ()
        )
+   | QEMUUserNet ->
+       printf "Trying QEMU network configuration.\n\n%!";
+       qemu_network ()
   );
 
   (* Work out what devices will be called at the remote end. *)
index cf9cc0a..3018389 100644 (file)
@@ -84,6 +84,11 @@ In this mode, the live CD attempts to reuse the network configuration
 from the physical machine's root filesystem.  You should probably try
 this method even though occasionally it does not work.
 
+=item Ask for fixed IP address and gateway
+
+In this mode the live CD will ask you for a fixed IP address and
+gateway address, and will configure your chosen interface with these.
+
 =item Configure from the shell
 
 In this mode you will be dropped into a command shell and you will
@@ -99,6 +104,12 @@ interface would be:
 where C<AA.BB.CC.DD> is the IP address and C<GG.HH.II.JJ> is the
 gateway.
 
+=item QEMU user network
+
+This option configures the network for use inside a QEMU user network
+(L<http://fabrice.bellard.free.fr/qemu/qemu-doc.html#SEC30>).  It
+should only be used by developers.
+
 =back
 
 =item Devices
@@ -356,9 +367,35 @@ For a logical volume (eg. C</dev/VolGroup00/LogVol00>), use:
 
 =item C<network>
 
-Set this to the choice for network setup.  Use either C<Some Auto> or
-C<Some Shell> for auto-configuration or shell (manual) configuration
-respectively.
+Set this to the choice for network setup.  Use one of:
+
+=over 4
+
+=item C<Some Auto>
+
+for auto-configuration
+
+=item C<Some Static>
+
+to specify the interface, address, netmask and gateway statically
+
+=item C<Some Shell>
+
+to launch a shell
+
+=item C<Some QEMUUserNet>
+
+to use a QEMU user network (developers only)
+
+=back
+
+=item C<static_network_config>
+
+This setting only applies if C<network> is set to C<Some Static>,
+in which case you should set this to the static network settings,
+a tuple of (interface, address, netmask, gateway, nameserver):
+
+ Some ("eth0", "192.168.2.5", "255.255.255.0", "192.168.2.1", "192.168.2.1")
 
 =item C<hypervisor>