type state = { greeting : bool;
remote_host : string option; remote_port : string option;
- transport : transport option;
remote_directory : string option;
network : network option;
devices_to_send : string list option;
memory : int option; vcpus : int option;
mac_address : string option;
}
-and transport = Server | SSH
and network = Auto | Shell
and partition = Part of string * string (* eg. "hda", "1" *)
| LV of string * string (* eg. "VolGroup00", "LogVol00" *)
*)
greeting = true;
- (* Transport: Set to 'Some Server' or 'Some SSH' to assume Server or SSH
- * transports respectively.
- *)
- transport = None;
-
(* Remote host and port. Set to 'Some "host"' and 'Some "port"',
* else ask the user.
*)
remote_host = None;
remote_port = None;
- (* Remote directory (only for SSH transport). Set to 'Some "path"'
- * to set up a directory path, else ask the user.
+ (* Remote directory. Set to 'Some "path"' to set up a
+ * directory path, else ask the user.
*)
remote_directory = None;
Next state
in
- let ask_transport state =
- match
- radiolist "Connection type" ~backbutton:false
- "Connection type."
- 11 50 2 [
- "ssh", "SSH (secure shell)", state.transport = Some SSH;
- "server", "P2V server on remote host", state.transport = Some Server
- ]
- with
- | Yes ("ssh"::_) -> Next { state with transport = Some SSH }
- | Yes ("server"::_) -> Next { state with transport = Some Server }
- | Yes _ | No | Help | Error -> Ask_again
- | Back -> Prev
- in
-
let ask_hostname state =
match
inputbox "Remote host" "Remote host" 10 50
inputbox "Remote port" "Remote port" 10 50
(Option.default "" state.remote_port)
with
- | Yes [] ->
- (match state.transport with
- | Some SSH -> Next { state with remote_port = Some "22" }
- | _ -> Next { state with remote_port = Some "16211" }
- )
+ | Yes ([]|""::_) -> Next { state with remote_port = Some "22" }
| Yes (port::_) -> Next { state with remote_port = Some port }
| No | Help | Error -> Ask_again
| Back -> Prev
yesno "Verify and proceed"
(sprintf "\nPlease verify the settings below and click [OK] to proceed, or the [Back] button to return to a previous step.
-Connection: %s
Host:port: %s : %s
Directory: %s
Network: %s
Memory: %s
VCPUs: %s
MAC address: %s"
- (match state.transport with
- | Some Server -> "Server" | Some SSH -> "SSH"
- | None -> "")
(Option.default "" state.remote_host)
(Option.default "" state.remote_port)
(Option.default "" state.remote_directory)
let dlgs = [|
ask_greeting, (* Initial greeting. *)
defaults.greeting;
- ask_transport, (* Transport (ssh, tcp) *)
- defaults.transport = None;
ask_hostname, (* Hostname. *)
defaults.remote_host = None;
ask_port, (* Port number. *)
let remote_host = Option.get state.remote_host in
let remote_port = Option.get state.remote_port in
let remote_directory = Option.get state.remote_directory in
- let transport = Option.get state.transport in
-
- (* Connect and disconnect from the remote system. *)
- let do_connect, do_disconnect =
- match transport with
- | Server ->
- let do_connect remote_name size =
- let addrs =
- getaddrinfo remote_host remote_port [AI_SOCKTYPE SOCK_STREAM] in
- let rec loop = function
- | [] ->
- fail_dialog
- (sprintf "Unable to connect to %s:%s" remote_host remote_port)
- | addr :: addrs ->
- try
- let sock =
- socket addr.ai_family addr.ai_socktype addr.ai_protocol in
- connect sock addr.ai_addr;
- let header = sprintf "p2v2 %s %Ld\n%!" remote_name size in
- let len = String.length header in
- assert (len = write sock header 0 len);
- sock
- with Unix_error (err, syscall, extra) ->
- (* Log the error message, but continue around the loop. *)
- eprintf "%s:%s: %s\n%!" syscall extra (error_message err);
- loop addrs
- in
- loop addrs
- in
- let do_disconnect sock = close sock in
- do_connect, do_disconnect
- | SSH ->
- (* Cheat by keeping a private variable around containing the original
- * channel, so we can close it easily. (XXX)
- *)
- let chan = ref None in
- let do_connect remote_name _ =
- let cmd = sprintf "ssh -C -p %s %s \"cat > %s/%s\""
- (quote remote_port) (quote remote_host)
- (quote remote_directory) (quote remote_name) in
- let c = open_process_out cmd in
- chan := Some c;
- descr_of_out_channel c
- in
- let do_disconnect _ =
- (match close_process_out (Option.get !chan) with
- | WEXITED 0 -> () (* OK *)
- | WEXITED i -> failwith (sprintf "ssh: exited with error code %d" i)
- | WSIGNALED i -> failwith (sprintf "ssh: killed by signal %d" i)
- | WSTOPPED i -> failwith (sprintf "ssh: stopped by signal %d" i)
- );
- chan := None
- in
- do_connect, do_disconnect in
+
+ (* Functions to connect and disconnect from the remote system. *)
+ let do_connect remote_name _ =
+ let cmd = sprintf "ssh -C -p %s %s \"cat > %s/%s\""
+ (quote remote_port) (quote remote_host)
+ (quote remote_directory) (quote remote_name) in
+ eprintf "connect: %s\n%!" cmd;
+ let chan = open_process_out cmd in
+ descr_of_out_channel chan, chan
+ in
+ let do_disconnect (_, chan) =
+ match close_process_out chan with
+ | WEXITED 0 -> () (* OK *)
+ | WEXITED i -> failwith (sprintf "ssh: exited with error code %d" i)
+ | WSIGNALED i -> failwith (sprintf "ssh: killed by signal %d" i)
+ | WSTOPPED i -> failwith (sprintf "ssh: stopped by signal %d" i)
+ in
(* XXX This is using the hostname derived from network configuration
* above. We might want to ask the user to choose.
let xml_len = String.length xml in
eprintf "length of configuration file is %d bytes\n%!" xml_len;
- let sock = do_connect conf_filename (Int64.of_int xml_len) in
+ let (sock,_) as conn = do_connect conf_filename (Int64.of_int xml_len) in
(* In OCaml this actually loops calling write(2) *)
ignore (write sock xml 0 xml_len);
- do_disconnect sock;
+ do_disconnect conn;
(* Send the device snapshots to the remote host. *)
(* XXX This code should be made more robust against both network
let fd = openfile ("/dev/mapper/" ^ snapshot_dev) [O_RDONLY] 0 in
(* Now connect. *)
- let sock = do_connect remote_name size in
+ let (sock,_) as conn = do_connect remote_name size in
(* Copy the data. *)
let bufsize = 1024 * 1024 in
copy 0L start;
(* Disconnect. *)
- do_disconnect sock
+ do_disconnect conn
) devices_to_send;
(* Clean up and reboot. *)