>From 16ce1ed1e63e07a284621a318b820366273b5a4f Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Fri, 8 Dec 2017 18:10:10 +0000 Subject: [PATCH] V2V: USE URI FOR REMOTE VMX PATH --- v2v/input_vmx.ml | 104 +++++++++++++++++++++++++++++++++---------------------- v2v/virt-v2v.pod | 21 +++++++++-- 2 files changed, 81 insertions(+), 44 deletions(-) diff --git a/v2v/input_vmx.ml b/v2v/input_vmx.ml index 3032eba96..a8b33f66f 100644 --- a/v2v/input_vmx.ml +++ b/v2v/input_vmx.ml @@ -29,52 +29,59 @@ open Utils open Name_from_disk type vmx_source = - | File of string (* local file or NFS *) - | SSH of string option * string * string (* SSH username, server, path *) + | File of string (* local file or NFS *) + | SSH of Xml.uri (* SSH URI *) (* The single filename on the command line is intepreted either as - * a local file or a remote SSH path (only if ‘-it ssh’). + * a local file or a remote SSH URI (only if ‘-it ssh’). *) let vmx_source_of_arg input_transport arg = match input_transport, arg with | None, arg -> File arg | Some `SSH, arg -> - let arg1, path = String.split ":" arg in - if path = "" then - error (f_"expecting [user@]server:path with ‘-it ssh’"); - let user, server = match String.split "@" arg1 with - | server, "" -> None, server - | user, server -> Some user, server in - SSH (user, server, path) + let uri = + try Xml.parse_uri arg + with Invalid_argument _ -> + error (f_"remote vmx ‘%s’ could not be parsed as a URI") arg in + if uri.Xml.uri_scheme <> None && uri.Xml.uri_scheme <> Some "ssh" then + error (f_"vmx URI start with ‘ssh://...’"); + if uri.Xml.uri_server = None then + error (f_"vmx URI remote server name omitted"); + if uri.Xml.uri_path = None || uri.Xml.uri_path = Some "/" then + error (f_"vmx URI path component looks incorrect"); + SSH uri + +(* Return various fields from the URI. The checks in vmx_source_of_arg + * should ensure that none of these assertions fail. + *) +let port_of_uri { Xml.uri_port } = + match uri_port with i when i <= 0 -> None | i -> Some i +let server_of_uri { Xml.uri_server } = + match uri_server with None -> assert false | Some s -> s +let path_of_uri { Xml.uri_path } = + match uri_path with None -> assert false | Some p -> p (* 'scp' a remote file into a temporary local file, returning the path * of the temporary local file. *) -let memo_tmpdir = ref None -let scp_from_remote_to_temporary user server path filename = - let tmpdir = - match !memo_tmpdir with - | None -> - let base_dir = (open_guestfs ())#get_cachedir () in - let t = Mkdtemp.temp_dir ~base_dir "vmx." in - rmdir_on_exit t; - memo_tmpdir := Some t; - t - | Some tmpdir -> tmpdir in - +let scp_from_remote_to_temporary uri tmpdir filename = let localfile = tmpdir // filename in - (* XXX Assumes default port number. *) let cmd = - sprintf "scp%s %s%s:%s %s" + sprintf "scp%s%s %s%s:%s %s" (if verbose () then "" else " -q") - (match user with None -> "" | Some user -> quote user ^ "@") - (quote server) + (match port_of_uri uri with + | None -> "" + | Some port -> sprintf " -P %d" port) + (match uri.Xml.uri_user with + | None -> "" + | Some user -> quote user ^ "@") + (quote (server_of_uri uri)) (* The double quoting of the path is counter-intuitive * but correct, see: * https://stackoverflow.com/questions/19858176/how-to-escape-spaces-in-path-during-scp-copy-in-linux *) - (quote (quote path)) + (quote (quote (path_of_uri uri))) (quote localfile) in if verbose () then eprintf "%s\n%!" cmd; @@ -83,12 +90,16 @@ let scp_from_remote_to_temporary user server path filename = localfile (* Test if [path] exists on the remote server. *) -let remote_file_exists user server path = - (* XXX Assumes default port number. *) +let remote_file_exists uri path = let cmd = - sprintf "ssh %s%s test -f %s" - (match user with None -> "" | Some user -> quote user ^ "@") - (quote server) + sprintf "ssh%s %s%s test -f %s" + (match port_of_uri uri with + | None -> "" + | Some port -> sprintf " -p %d" port) + (match uri.Xml.uri_user with + | None -> "" + | Some user -> quote user ^ "@") + (quote (server_of_uri uri)) (* Double quoting is necessary here, see above. *) (quote (quote path)) in if verbose () then @@ -204,7 +215,8 @@ and qemu_uri_of_filename vmx_source filename = *) absolute_path_from_other_file vmx_filename filename, "vmdk" - | SSH (user, server, vmx_path) -> + | SSH uri -> + let vmx_path = path_of_uri uri in let abs_path = absolute_path_from_other_file vmx_path filename in let format = "vmdk" in @@ -215,20 +227,25 @@ and qemu_uri_of_filename vmx_source filename = let abs_path, format = let flat_vmdk = PCRE.replace (PCRE.compile "\\.vmdk$") "-flat.vmdk" abs_path in - if remote_file_exists user server flat_vmdk then (flat_vmdk, "raw") + if remote_file_exists uri flat_vmdk then (flat_vmdk, "raw") else (abs_path, format) in let json_params = [ "file.driver", JSON.String "ssh"; "file.path", JSON.String abs_path; - "file.host", JSON.String server; + "file.host", JSON.String (server_of_uri uri); "file.host_key_check", JSON.String "no"; ] in let json_params = - match user with + match uri.Xml.uri_user with | None -> json_params | Some user -> ("file.user", JSON.String user) :: json_params in + let json_params = + match port_of_uri uri with + | None -> json_params + | Some port -> + ("file.port", JSON.Int port) :: json_params in "json:" ^ JSON.string_of_doc json_params, format @@ -367,7 +384,13 @@ and find_nics vmx = let nics = List.map (fun (_, source) -> source) nics in nics -class input_vmx input_transport arg = object +class input_vmx input_transport arg = + let tmpdir = + let base_dir = (open_guestfs ())#get_cachedir () in + let t = Mkdtemp.temp_dir ~base_dir "vmx." in + rmdir_on_exit t; + t in +object inherit input method as_options = "-i vmx " ^ arg @@ -389,9 +412,8 @@ class input_vmx input_transport arg = object let vmx = match vmx_source with | File filename -> Parse_vmx.parse_file filename - | SSH (user, server, path) -> - let filename = - scp_from_remote_to_temporary user server path "source.vmx" in + | SSH uri -> + let filename = scp_from_remote_to_temporary uri tmpdir "source.vmx" in Parse_vmx.parse_file filename in let name = @@ -401,7 +423,7 @@ class input_vmx input_transport arg = object warning (f_"no displayName key found in VMX file"); match vmx_source with | File filename -> name_from_disk filename - | SSH (_, _, path) -> name_from_disk path in + | SSH uri -> name_from_disk (path_of_uri uri) in let memory_mb = match Parse_vmx.get_int64 vmx ["memSize"] with diff --git a/v2v/virt-v2v.pod b/v2v/virt-v2v.pod index 79269a9da..cc4e90dff 100644 --- a/v2v/virt-v2v.pod +++ b/v2v/virt-v2v.pod @@ -126,7 +126,7 @@ a local file. virt-v2v \ -i vmx -it ssh \ - "root@esxi.example.com:/vmfs/volumes/datastore1/guest/guest.vmx" \ + "root@esxi.example.com/vmfs/volumes/datastore1/guest/guest.vmx" \ -o local -os /var/tmp The guest must not be running. Virt-v2v would I need to be run @@ -1433,6 +1433,21 @@ Note that password-interactive and Kerberos access are B supported. You B to set up ssh access using ssh-agent and authorized_keys. +=head3 VMX: Construct the SSH URI + +When using the SSH input transport you must specify a remote +C URI pointing to the VMX file. A typical URI looks like: + + ssh://root@esxi.example.com/vmfs/volumes/datastore1/my%20guest/my%20guest.vmx + +Any space must be escaped with C<%20> and other non-ASCII characters +may also need to be URI-escaped. + +The username is not required if it is the same as your local username. + +You may optionally supply a port number after the hostname if the SSH +server is not listening on the default port (22). + =head2 VMX: IMPORTING A GUEST To import a vmx file from a local file or NFS, do: @@ -1440,11 +1455,11 @@ To import a vmx file from a local file or NFS, do: $ virt-v2v -i vmx guest.vmx -o local -os /var/tmp To import a vmx file over SSH, add I<-it ssh> to select the SSH -transport and supply a remote C with optional username: +transport and supply a remote SSH URI: $ virt-v2v \ -i vmx -it ssh \ - "root@esxi.example.com:/vmfs/volumes/datastore1/guest/guest.vmx" \ + "ssh://root@esxi.example.com/vmfs/volumes/datastore1/guest/guest.vmx" \ -o local -os /var/tmp Virt-v2v processes the vmx file and uses it to find the location of -- 2.13.2