[Libguestfs] [PATCH v2 2/2] v2v: -i vmx: Enhance VMX support with ability to use ‘-it ssh’ transport.
Richard W.M. Jones
rjones at redhat.com
Fri Dec 8 18:24:10 UTC 2017
On Fri, Dec 08, 2017 at 06:04:51PM +0100, Pino Toscano wrote:
> IMHO this new transport could use the standard URI syntax, so
> root at esxi.example.com/vmfs/volumes/datastore1/guest/guest.vmx
> instead of
> root at esxi.example.com:/vmfs/volumes/datastore1/guest/guest.vmx
>
> This way, Xml.parse_uri can be used to parse it, giving a better code
> for extracting all the parts (including the port, for example).
I tried this, see attached incremental patch, but it has a few
problems:
(1) You must use ssh://... else XmlParseURI cannot parse the URI.
(2) You must replace all spaces with %20 in the path. This is a bit
of a pain since VMware VM names frequently contain spaces, and the
path will contains x 2 the number of spaces.
On the flip side, adding support for port numbers was possible.
Rich.
--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
Fedora Windows cross-compiler. Compile Windows programs, test, and
build Windows installers. Over 100 libraries supported.
http://fedoraproject.org/wiki/MinGW
-------------- next part --------------
>From 16ce1ed1e63e07a284621a318b820366273b5a4f Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones at redhat.com>
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 at esxi.example.com:/vmfs/volumes/datastore1/guest/guest.vmx" \
+ "root at esxi.example.com/vmfs/volumes/datastore1/guest/guest.vmx" \
-o local -os /var/tmp
The guest must not be running. Virt-v2v would I<not> need to be run
@@ -1433,6 +1433,21 @@ Note that password-interactive and Kerberos access are B<not>
supported. You B<have> 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<ssh://...> 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<server:/path> with optional username:
+transport and supply a remote SSH URI:
$ virt-v2v \
-i vmx -it ssh \
- "root at 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
More information about the Libguestfs
mailing list