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;
               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;
               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;
             }
               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 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.
 
 (*----------------------------------------------------------------------*)
 (* 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;
   devices_to_send = None;
   root_filesystem = None;
   network = None;
+  static_network_config = None;
   hypervisor = None;
   architecture = None;
   memory = 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).
  *)
  *
  * 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
   (* 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
     )
          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
   in
-  msgbox, yesno, inputbox, radiolist, checklist
+  msgbox, yesno, inputbox, radiolist, checklist, form
 
 (* Print failure dialog and exit. *)
 let fail_dialog text =
 
 (* 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
 
     (* 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.
 (* 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
 
   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;
       "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;
       "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 }
     ]
     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 ("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
     | 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"
          (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)
          | 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
   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. *)
   |] 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. *)
     if posn >= Array.length dlgs then state (* Finished all dialogs. *)
+    else if posn < 0 then loop 0 state
     else (
     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. *)
       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
       )
     )
   in
@@ -1112,6 +1184,15 @@ MAC address:  %s"
        printf "When you have finished, exit the shell with ^D or exit.\n\n%!";
        shell ()
 
        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%!";
    | 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 ()
        )
         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. *)
   );
 
   (* 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.
 
 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
 =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.
 
 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
 =back
 
 =item Devices
@@ -356,9 +367,35 @@ For a logical volume (eg. C</dev/VolGroup00/LogVol00>), use:
 
 =item C<network>
 
 
 =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>
 
 
 =item C<hypervisor>