Richard W.M. Jones
2017-Dec-08 16:02 UTC
[Libguestfs] [PATCH v2 0/2] v2v: Add -it vddk and -it ssh flags.
The first patch was previously posted here: https://www.redhat.com/archives/libguestfs/2017-December/msg00018.html That patch hasn't changed except that I made the ‘input_transport’ variable type-safe. The second patch adds a significant new mode for liberating data from VMware: the ability to copy VMs over SSH directly from ESXi hypervisors. Although this requires enabling SSH access (a check-box in the vSphere UI), it is fast, very convenient and doesn't require any proprietary drivers. Rich.
Richard W.M. Jones
2017-Dec-08 16:02 UTC
[Libguestfs] [PATCH v2 1/2] v2v: vddk: Switch to using ‘-it vddk’ to specify VDDK as input transport.
Previously the presence of the ‘--vddk <libdir>’ option magically
enabled VDDK mode. However we want to introduce other transports for
VMware conversions so this wasn't a very clean choice.
With this commit you must use ‘-it vddk’ to specify that you want VDDK
as a disk transport. The previous ‘--vddk <libdir>’ option has been
renamed to ‘--vddk-libdir <libdir>’ to be consistent with the other
passthrough options, and it is no longer required.
A new command line looks like:
$ export PATH=/path/to/nbdkit:$PATH
$ virt-v2v \
-ic 'vpx://root@vcenter.example.com/Datacenter/esxi?no_verify=1' \
| -it vddk \
| --vddk-libdir /path/to/vmware-vix-disklib-distrib \
--vddk-thumbprint xx:xx:xx:... \
"Windows 2003" \
-o local -os /var/tmp
where only the two lines marked with ‘|’ have changed.
---
v2v/cmdline.ml | 69 +++++++++++++++++++++++++++++------------------
v2v/input_libvirt.ml | 37 ++++++++++++-------------
v2v/input_libvirt.mli | 8 +++---
v2v/input_libvirt_vddk.ml | 57 +++++++++++++++++++++++++--------------
v2v/test-v2v-docs.sh | 2 +-
v2v/types.ml | 2 +-
v2v/types.mli | 2 +-
v2v/virt-v2v.pod | 30 +++++++++++++++------
8 files changed, 126 insertions(+), 81 deletions(-)
diff --git a/v2v/cmdline.ml b/v2v/cmdline.ml
index a452458a1..719e6f057 100644
--- a/v2v/cmdline.ml
+++ b/v2v/cmdline.ml
@@ -57,15 +57,16 @@ let parse_cmdline ()
let input_conn = ref None in
let input_format = ref None in
+ let input_transport = ref None in
let in_place = ref false in
let output_conn = ref None in
let output_format = ref None in
let output_name = ref None in
let output_storage = ref None in
let password_file = ref None in
- let vddk = ref None in
let vddk_config = ref None in
let vddk_cookie = ref None in
+ let vddk_libdir = ref None in
let vddk_nfchostport = ref None in
let vddk_port = ref None in
let vddk_snapshot = ref None in
@@ -191,6 +192,8 @@ let parse_cmdline ()
s_"Libvirt URI";
[ M"if" ], Getopt.String ("format",
set_string_option_once "-if" input_format),
s_"Input format (for -i disk)";
+ [ M"it" ], Getopt.String ("transport",
set_string_option_once "-it" input_transport),
+ s_"Input transport";
[ L"in-place" ], Getopt.Set in_place,
s_"Only tune the guest in the input
VM";
[ L"machine-readable" ], Getopt.Set machine_readable,
@@ -220,12 +223,12 @@ let parse_cmdline () [ L"qemu-boot" ],
Getopt.Set qemu_boot, s_"Boot in qemu (-o qemu only)";
[ L"root" ], Getopt.String ("ask|... ",
set_root_choice),
s_"How to choose root
filesystem";
- [ L"vddk" ], Getopt.String ("libpath",
set_string_option_once "--vddk" vddk),
- s_"Use nbdkit VDDK plugin";
[ L"vddk-config" ], Getopt.String ("filename",
set_string_option_once "--vddk-config" vddk_config),
s_"Set VDDK config file";
[ L"vddk-cookie" ], Getopt.String ("cookie",
set_string_option_once "--vddk-cookie" vddk_cookie),
s_"Set VDDK cookie";
+ [ L"vddk-libdir" ], Getopt.String ("libdir",
set_string_option_once "--vddk-libdir" vddk_libdir),
+ s_"Set VDDK library parent
directory";
[ L"vddk-nfchostport" ], Getopt.String ("nfchostport",
set_string_option_once "--vddk-nfchostport" vddk_nfchostport),
s_"Set VDDK nfchostport";
[ L"vddk-port" ], Getopt.String ("port",
set_string_option_once "--vddk-port" vddk_port),
@@ -286,6 +289,12 @@ read the man page virt-v2v(1).
let input_conn = !input_conn in
let input_format = !input_format in
let input_mode = !input_mode in
+ let input_transport + match !input_transport with
+ | None -> None
+ | Some "vddk" -> Some `VDDK
+ | Some transport ->
+ error (f_"unknown input transport ‘-it %s’") transport in
let in_place = !in_place in
let machine_readable = !machine_readable in
let network_map = !network_map in
@@ -303,28 +312,15 @@ read the man page virt-v2v(1).
let qemu_boot = !qemu_boot in
let root_choice = !root_choice in
let vddk_options - match !vddk with
- | Some libdir ->
- Some { vddk_libdir = libdir;
- vddk_config = !vddk_config;
- vddk_cookie = !vddk_cookie;
- vddk_nfchostport = !vddk_nfchostport;
- vddk_port = !vddk_port;
- vddk_snapshot = !vddk_snapshot;
- vddk_thumbprint = !vddk_thumbprint;
- vddk_transports = !vddk_transports;
- vddk_vimapiver = !vddk_vimapiver }
- | None ->
- if !vddk_config <> None ||
- !vddk_cookie <> None ||
- !vddk_nfchostport <> None ||
- !vddk_port <> None ||
- !vddk_snapshot <> None ||
- !vddk_thumbprint <> None ||
- !vddk_transports <> None ||
- !vddk_vimapiver <> None then
- error (f_"‘--vddk-*’ options should only be used when conversion
via the nbdkit VDDK plugin has been enabled, ie. using ‘--vddk’.");
- None in
+ { vddk_config = !vddk_config;
+ vddk_cookie = !vddk_cookie;
+ vddk_libdir = !vddk_libdir;
+ vddk_nfchostport = !vddk_nfchostport;
+ vddk_port = !vddk_port;
+ vddk_snapshot = !vddk_snapshot;
+ vddk_thumbprint = !vddk_thumbprint;
+ vddk_transports = !vddk_transports;
+ vddk_vimapiver = !vddk_vimapiver } in
let vdsm_compat = !vdsm_compat in
let vdsm_image_uuids = List.rev !vdsm_image_uuids in
let vdsm_vol_uuids = List.rev !vdsm_vol_uuids in
@@ -357,6 +353,26 @@ read the man page virt-v2v(1).
let password = read_first_line_from_file filename in
Some password in
+ (* Input transport affects whether some parameters should or
+ * should not be used.
+ *)
+ (match input_transport with
+ | None ->
+ if !vddk_config <> None ||
+ !vddk_cookie <> None ||
+ !vddk_libdir <> None ||
+ !vddk_nfchostport <> None ||
+ !vddk_port <> None ||
+ !vddk_snapshot <> None ||
+ !vddk_thumbprint <> None ||
+ !vddk_transports <> None ||
+ !vddk_vimapiver <> None then
+ error (f_"‘--vddk-*’ options should only be used when conversion
via the nbdkit VDDK plugin has been enabled, ie. using ‘-it vddk’.")
+ | Some `VDDK ->
+ if !vddk_thumbprint = None then
+ error (f_"‘--vddk-thumbprint’ is required when using ‘-it
vddk’.")
+ );
+
(* Parsing of the argument(s) depends on the input mode. *)
let input match input_mode with
@@ -379,7 +395,8 @@ read the man page virt-v2v(1).
| [guest] -> guest
| _ ->
error (f_"expecting a libvirt guest name on the command
line") in
- Input_libvirt.input_libvirt vddk_options password input_conn guest
+ Input_libvirt.input_libvirt vddk_options password
+ input_conn input_transport guest
| `LibvirtXML ->
(* -i libvirtxml: Expecting a filename (XML file). *)
diff --git a/v2v/input_libvirt.ml b/v2v/input_libvirt.ml
index 59845c9b2..66af6371c 100644
--- a/v2v/input_libvirt.ml
+++ b/v2v/input_libvirt.ml
@@ -27,7 +27,7 @@ open Types
open Utils
(* Choose the right subclass based on the URI. *)
-let input_libvirt vddk_options password libvirt_uri guest +let input_libvirt
vddk_options password libvirt_uri input_transport guest match libvirt_uri
with
| None ->
Input_libvirt_other.input_libvirt_other password libvirt_uri guest
@@ -39,29 +39,26 @@ let input_libvirt vddk_options password libvirt_uri guest
error (f_"could not parse '-ic %s'. Original error message was:
%s")
orig_uri msg in
- match server, scheme with
- | None, _
- | Some "", _ (* Not a remote URI. *)
+ match server, scheme, input_transport with
+ | None, _, _
+ | Some "", _, _ (* Not a remote URI. *)
- | Some _, None (* No scheme? *)
- | Some _, Some "" ->
+ | Some _, None, _ (* No scheme? *)
+ | Some _, Some "", _ ->
Input_libvirt_other.input_libvirt_other password libvirt_uri guest
- (* vCenter over https, or
- * vCenter or ESXi using nbdkit vddk plugin
- *)
- | Some server, Some ("esx"|"gsx"|"vpx") ->
- (match vddk_options with
- | None ->
- Input_libvirt_vcenter_https.input_libvirt_vcenter_https
- password libvirt_uri parsed_uri server guest
- | Some vddk_options ->
- Input_libvirt_vddk.input_libvirt_vddk vddk_options password
- libvirt_uri parsed_uri guest
- )
+ (* vCenter over https. *)
+ | Some server, Some ("esx"|"gsx"|"vpx"), None
->
+ Input_libvirt_vcenter_https.input_libvirt_vcenter_https
+ password libvirt_uri parsed_uri server guest
+
+ (* vCenter or ESXi using nbdkit vddk plugin *)
+ | Some server, Some ("esx"|"gsx"|"vpx"), Some
`VDDK ->
+ Input_libvirt_vddk.input_libvirt_vddk vddk_options password
+ libvirt_uri parsed_uri guest
(* Xen over SSH *)
- | Some server, Some "xen+ssh" ->
+ | Some server, Some "xen+ssh", _ ->
Input_libvirt_xen_ssh.input_libvirt_xen_ssh
password libvirt_uri parsed_uri server guest
@@ -71,7 +68,7 @@ let input_libvirt vddk_options password libvirt_uri guest
*)
(* Unknown remote scheme. *)
- | Some _, Some _ ->
+ | Some _, Some _, _ ->
warning (f_"no support for remote libvirt connections to '-ic
%s'. The conversion may fail when it tries to read the source disks.")
orig_uri;
Input_libvirt_other.input_libvirt_other password libvirt_uri guest
diff --git a/v2v/input_libvirt.mli b/v2v/input_libvirt.mli
index acf2ca417..eb3e48397 100644
--- a/v2v/input_libvirt.mli
+++ b/v2v/input_libvirt.mli
@@ -18,7 +18,7 @@
(** [-i libvirt] source. *)
-val input_libvirt : Types.vddk_options option -> string option -> string
option -> string -> Types.input
-(** [input_libvirt vddk_options password libvirt_uri guest] creates
- and returns a new {!Types.input} object specialized for reading input
- from libvirt sources. *)
+val input_libvirt : Types.vddk_options -> string option -> string option
-> [`VDDK] option -> string -> Types.input
+(** [input_libvirt vddk_options password libvirt_uri input_transport guest]
+ creates and returns a new {!Types.input} object specialized for reading
+ input from libvirt sources. *)
diff --git a/v2v/input_libvirt_vddk.ml b/v2v/input_libvirt_vddk.ml
index f3cc6eb9b..1e673f4c7 100644
--- a/v2v/input_libvirt_vddk.ml
+++ b/v2v/input_libvirt_vddk.ml
@@ -35,11 +35,16 @@ open Printf
(* Subclass specialized for handling VMware via nbdkit vddk plugin. *)
class input_libvirt_vddk vddk_options password libvirt_uri parsed_uri guest -
(* The VDDK path. *)
let libdir = vddk_options.vddk_libdir in
- (* Compute the LD_LIBRARY_PATH that we must pass to nbdkit. *)
- let library_path = libdir // sprintf "lib%d" Sys.word_size in
+
+ (* VDDK libraries are located under lib32/ or lib64/ relative to the
+ * libdir. Note this is unrelated to Linux multilib or multiarch.
+ *)
+ let libNN = sprintf "lib%d" Sys.word_size in
+
+ (* Compute the LD_LIBRARY_PATH that we may have to pass to nbdkit. *)
+ let library_path = Option.map (fun libdir -> libdir // libNN) libdir in
(* Is SELinux enabled and enforcing on the host? *)
let have_selinux @@ -47,18 +52,25 @@ class input_libvirt_vddk vddk_options
password libvirt_uri parsed_uri guest
(* Check that the VDDK path looks reasonable. *)
let error_unless_vddk_libdir () - if not (is_directory libdir) then
- error (f_"‘--vddk %s’ does not point to a directory. See
\"INPUT FROM VDDK\" in the virt-v2v(1) manual.") libdir;
+ (match libdir with
+ | None -> ()
+ | Some libdir ->
+ if not (is_directory libdir) then
+ error (f_"‘--vddk-libdir %s’ does not point to a directory. See
\"INPUT FROM VDDK\" in the virt-v2v(1) manual.") libdir
+ );
- if not (is_directory library_path) then
- error (f_"VDDK library path %s not found or not a directory. See
\"INPUT FROM VDDK\" in the virt-v2v(1) manual.")
- library_path
+ (match library_path with
+ | None -> ()
+ | Some library_path ->
+ if not (is_directory library_path) then
+ error (f_"VDDK library path %s not found or not a directory.
See \"INPUT FROM VDDK\" in the virt-v2v(1) manual.") library_path
+ )
in
(* Check that nbdkit is available and new enough. *)
let error_unless_nbdkit_working () if 0 <> Sys.command
"nbdkit --version >/dev/null" then
- error (f_"nbdkit is not installed or not working. It is required to
use ‘--vddk’. See \"INPUT FROM VDDK\" in the virt-v2v(1)
manual.");
+ error (f_"nbdkit is not installed or not working. It is required to
use ‘-it vddk’. See \"INPUT FROM VDDK\" in the virt-v2v(1)
manual.");
(* Check it's a new enough version. The latest features we
* require are ‘--exit-with-parent’ and ‘--selinux-label’, both
@@ -74,14 +86,20 @@ class input_libvirt_vddk vddk_options password libvirt_uri
parsed_uri guest
(* Check that the VDDK plugin is installed and working *)
let error_unless_nbdkit_vddk_working () + let set_ld_library_path +
match library_path with
+ | None -> ""
+ | Some library_path ->
+ sprintf "LD_LIBRARY_PATH=%s " (quote library_path) in
+
let cmd - sprintf "LD_LIBRARY_PATH=%s nbdkit vddk --dump-plugin
>/dev/null"
- (quote library_path) in
+ sprintf "%snbdkit vddk --dump-plugin >/dev/null"
+ set_ld_library_path in
if Sys.command cmd <> 0 then (
(* See if we can diagnose why ... *)
let cmd - sprintf "LD_LIBRARY_PATH=%s LANG=C nbdkit vddk
--dump-plugin 2>&1 | grep -sq libvixDiskLib.so"
- (quote library_path) in
+ sprintf "LANG=C %snbdkit vddk --dump-plugin 2>&1 | grep -sq
libvixDiskLib.so"
+ set_ld_library_path in
let needs_library = Sys.command cmd = 0 in
if not needs_library then
error (f_"nbdkit VDDK plugin is not installed or not working. It
is required if you want to use VDDK.
@@ -92,9 +110,9 @@ See also \"INPUT FROM VDDK\" in the virt-v2v(1)
manual.")
else
error (f_"nbdkit VDDK plugin is not installed or not working. It
is required if you want to use VDDK.
-It looks like you did not set the right path in the ‘--vddk’ option, or your
copy of the VDDK directory is incomplete. There should be a library called
’%s/libvixDiskLib.so.?’.
+It looks like you did not set the right path in the ‘--vddk-libdir’ option, or
your copy of the VDDK directory is incomplete. There should be a library called
’<libdir>/%s/libvixDiskLib.so.?’.
-See also \"INPUT FROM VDDK\" in the virt-v2v(1) manual.")
library_path
+See also \"INPUT FROM VDDK\" in the virt-v2v(1) manual.") libNN
)
in
@@ -121,6 +139,7 @@ See also \"INPUT FROM VDDK\" in the virt-v2v(1)
manual.") library_path
let vddk_passthrus [ "config", (fun { vddk_config }
-> vddk_config);
"cookie", (fun { vddk_cookie } -> vddk_cookie);
+ "libdir", (fun { vddk_libdir } -> vddk_libdir);
"nfchostport", (fun { vddk_nfchostport } ->
vddk_nfchostport);
"port", (fun { vddk_port } -> vddk_port);
"snapshot", (fun { vddk_snapshot } -> vddk_snapshot);
@@ -149,9 +168,8 @@ object
| Some field -> sprintf " --vddk-%s %s" name field
) vddk_passthrus
) in
- sprintf "%s --vddk %s%s"
+ sprintf "%s -it vddk %s"
super#as_options (* superclass prints "-i libvirt etc" *)
- vddk_options.vddk_libdir
pt_options
method source () @@ -252,7 +270,6 @@ object
add_arg (sprintf "user=%s" user);
add_arg password_param;
add_arg (sprintf "vm=moref=%s" moref);
- add_arg (sprintf "libdir=%s" libdir);
(* The passthrough parameters. *)
List.iter (
@@ -297,7 +314,7 @@ object
(* Print the full command we are about to run when debugging. *)
if verbose () then (
eprintf "running nbdkit:\n";
- eprintf "LD_LIBRARY_PATH=%s" library_path;
+ Option.may (eprintf "LD_LIBRARY_PATH=%s") library_path;
List.iter (fun arg -> eprintf " %s" (quote arg)) args;
prerr_newline ()
);
@@ -310,7 +327,7 @@ object
let pid = fork () in
if pid = 0 then (
(* Child process (nbdkit). *)
- putenv "LD_LIBRARY_PATH" library_path;
+ Option.may (putenv "LD_LIBRARY_PATH") library_path;
execvp "nbdkit" args
);
diff --git a/v2v/test-v2v-docs.sh b/v2v/test-v2v-docs.sh
index 8bd03f68e..5d034c465 100755
--- a/v2v/test-v2v-docs.sh
+++ b/v2v/test-v2v-docs.sh
@@ -22,4 +22,4 @@ $TEST_FUNCTIONS
skip_if_skipped
$top_srcdir/podcheck.pl virt-v2v.pod virt-v2v \
-
--ignore=--debug-overlay,--ic,--if,--no-trim,--oa,--oc,--of,--on,--os,--vmtype
+
--ignore=--debug-overlay,--ic,--if,--it,--no-trim,--oa,--oc,--of,--on,--os,--vmtype
diff --git a/v2v/types.ml b/v2v/types.ml
index ee6b642bf..be86f1b3b 100644
--- a/v2v/types.ml
+++ b/v2v/types.ml
@@ -480,9 +480,9 @@ type root_choice = AskRoot | SingleRoot | FirstRoot |
RootDev of string
type output_allocation = Sparse | Preallocated
type vddk_options = {
- vddk_libdir : string;
vddk_config : string option;
vddk_cookie : string option;
+ vddk_libdir : string option;
vddk_nfchostport : string option;
vddk_port : string option;
vddk_snapshot : string option;
diff --git a/v2v/types.mli b/v2v/types.mli
index a426bd696..95d890638 100644
--- a/v2v/types.mli
+++ b/v2v/types.mli
@@ -340,9 +340,9 @@ type output_allocation = Sparse | Preallocated
(** Type of [-oa] (output allocation) option. *)
type vddk_options = {
- vddk_libdir : string;
vddk_config : string option;
vddk_cookie : string option;
+ vddk_libdir : string option;
vddk_nfchostport : string option;
vddk_port : string option;
vddk_snapshot : string option;
diff --git a/v2v/virt-v2v.pod b/v2v/virt-v2v.pod
index 4ac44988e..503830c9d 100644
--- a/v2v/virt-v2v.pod
+++ b/v2v/virt-v2v.pod
@@ -379,6 +379,13 @@ See L</IN PLACE CONVERSION> below.
Conflicts with all I<-o *> options.
+=item B<-it> B<vddk>
+
+Use VMware VDDK as a transport to copy the input disks. See
+L</INPUT FROM VDDK> below. If you use this parameter then you may
+need to use other I<--vddk*> options to specify how to connect through
+VDDK.
+
=item B<--keys-from-stdin>
Read key or passphrase parameters from stdin. The default is
@@ -633,13 +640,20 @@ boot an operating system from the first virtio disk.
Specifically,
F</boot> must be on the first virtio disk, and it cannot chainload an
OS which is not in the first virtio disk.
-=item B<--vddk> LIBDIR
+=item B<--vddk-libdir> LIBDIR
-Enable VDDK input from VMware vCenter or ESXi. C<LIBDIR> is the top
-directory of the VDDK library. This directory should I<contain>
+Set the VDDK library directory. This directory should I<contain>
subdirectories called F<include>, F<lib64> etc., but do not include
F<lib64> actually in the parameter.
+In most cases this parameter is required when using the I<-it vddk>
+(VDDK) transport. See L</INPUT FROM VDDK> below for details.
+
+=item B<--vddk-thumbprint> xx:xx:xx:...
+
+Set the thumbprint of the remote VMware server.
+
+This parameter is required when using the I<-it vddk> (VDDK) transport.
See L</INPUT FROM VDDK> below for details.
=item B<--vddk-config> FILENAME
@@ -652,16 +666,13 @@ See L</INPUT FROM VDDK> below for details.
=item B<--vddk-snapshot> SNAPSHOT-MOREF
-=item B<--vddk-thumbprint> xx:xx:xx:...
-
=item B<--vddk-transports> MODE:MODE:...
=item B<--vddk-vimapiver> APIVER
When using VDDK mode, these options are passed unmodified to the
L<nbdkit(1)> VDDK plugin. Please refer to
L<nbdkit-vddk-plugin(1)>.
-If I<--vddk> is present, I<--vddk-thumbprint> is also required,
-the rest are optional.
+These are all optional.
=item B<--vdsm-compat=0.10>
@@ -1543,6 +1554,8 @@ continuing.
=head2 VDDK: IMPORTING A GUEST
+The I<-it vddk> parameter selects VDDK as the input transport for disks.
+
To import a particular guest from vCenter server or ESXi hypervisor,
use a command like the following, substituting the URI, guest name and
SSL thumbprint:
@@ -1550,7 +1563,8 @@ SSL thumbprint:
$ export PATH=/path/to/nbdkit:$PATH
$ virt-v2v \
-ic 'vpx://root@vcenter.example.com/Datacenter/esxi?no_verify=1' \
- --vddk /path/to/vmware-vix-disklib-distrib \
+ -it vddk \
+ --vddk-libdir /path/to/vmware-vix-disklib-distrib \
--vddk-thumbprint xx:xx:xx:... \
"Windows 2003" \
-o local -os /var/tmp
--
2.13.2
Richard W.M. Jones
2017-Dec-08 16:02 UTC
[Libguestfs] [PATCH v2 2/2] v2v: -i vmx: Enhance VMX support with ability to use ‘-it ssh’ transport.
This enhances the existing VMX input support allowing it to be
used over SSH to the ESXi server.
The original command (for local .vmx files) was:
$ virt-v2v -i vmx guest.vmx -o local -os /var/tmp
Adding ‘-it ssh’ and using an SSH remote path gives the new syntax:
$ virt-v2v \
-i vmx -it ssh \
"root@esxi.example.com:/vmfs/volumes/datastore1/guest/guest.vmx"
\
-o local -os /var/tmp
I anticipate that this input method will be widely used enough that it
deserves its own example at the top of the man page.
---
v2v/cmdline.ml | 26 +++++--
v2v/input_libvirt_other.ml | 9 ---
v2v/input_libvirt_other.mli | 1 -
v2v/input_vmx.ml | 171 +++++++++++++++++++++++++++++++++++++-------
v2v/input_vmx.mli | 5 +-
v2v/utils.ml | 9 +++
v2v/utils.mli | 2 +
v2v/virt-v2v.pod | 91 ++++++++++++++++++-----
8 files changed, 254 insertions(+), 60 deletions(-)
diff --git a/v2v/cmdline.ml b/v2v/cmdline.ml
index 719e6f057..0c6af3ed8 100644
--- a/v2v/cmdline.ml
+++ b/v2v/cmdline.ml
@@ -292,6 +292,7 @@ read the man page virt-v2v(1).
let input_transport match !input_transport with
| None -> None
+ | Some "ssh" -> Some `SSH
| Some "vddk" -> Some `VDDK
| Some transport ->
error (f_"unknown input transport ‘-it %s’") transport in
@@ -357,7 +358,8 @@ read the man page virt-v2v(1).
* should not be used.
*)
(match input_transport with
- | None ->
+ | None
+ | Some `SSH ->
if !vddk_config <> None ||
!vddk_cookie <> None ||
!vddk_libdir <> None ||
@@ -395,6 +397,12 @@ read the man page virt-v2v(1).
| [guest] -> guest
| _ ->
error (f_"expecting a libvirt guest name on the command
line") in
+ let input_transport + match input_transport with
+ | None -> None
+ | Some `VDDK -> Some `VDDK
+ | Some `SSH ->
+ error (f_"only ‘-it vddk’ can be used here") in
Input_libvirt.input_libvirt vddk_options password
input_conn input_transport guest
@@ -417,13 +425,19 @@ read the man page virt-v2v(1).
Input_ova.input_ova filename
| `VMX ->
- (* -i vmx: Expecting an vmx filename. *)
- let filename + (* -i vmx: Expecting a vmx filename or SSH remote
path. *)
+ let arg match args with
- | [filename] -> filename
+ | [arg] -> arg
| _ ->
- error (f_"expecting a VMX file name on the command line")
in
- Input_vmx.input_vmx filename in
+ error (f_"expecting a single VMX file name or SSH remote path on
the command line") in
+ let input_transport + match input_transport with
+ | None -> None
+ | Some `SSH -> Some `SSH
+ | Some `VDDK ->
+ error (f_"only ‘-it ssh’ can be used here") in
+ Input_vmx.input_vmx input_transport arg in
(* Common error message. *)
let error_option_cannot_be_used_in_output_mode mode opt diff --git
a/v2v/input_libvirt_other.ml b/v2v/input_libvirt_other.ml
index e08d79cc9..42d8c7df6 100644
--- a/v2v/input_libvirt_other.ml
+++ b/v2v/input_libvirt_other.ml
@@ -39,15 +39,6 @@ let error_if_libvirt_does_not_support_json_backingfile ()
Libvirt_utils.libvirt_get_version () < (2, 1, 0) then
error (f_"because of libvirt bug https://bugzilla.redhat.com/1134878
you must EITHER upgrade to libvirt >= 2.1.0 OR set this environment
variable:\n\nexport LIBGUESTFS_BACKEND=direct\n\nand then rerun the virt-v2v
command.")
-(* xen+ssh URLs use the SSH driver in CURL. Currently this requires
- * ssh-agent authentication. Give a clear error if this hasn't been
- * set up (RHBZ#1139973).
- *)
-let error_if_no_ssh_agent () - try ignore (Sys.getenv
"SSH_AUTH_SOCK")
- with Not_found ->
- error (f_"ssh-agent authentication has not been set up ($SSH_AUTH_SOCK
is not set). Please read \"INPUT FROM RHEL 5 XEN\" in the virt-v2v(1)
man page.")
-
(* Superclass. *)
class virtual input_libvirt (password : string option) libvirt_uri guest
object
diff --git a/v2v/input_libvirt_other.mli b/v2v/input_libvirt_other.mli
index 494ca908a..8b1e8aa1d 100644
--- a/v2v/input_libvirt_other.mli
+++ b/v2v/input_libvirt_other.mli
@@ -19,7 +19,6 @@
(** [-i libvirt] source. *)
val error_if_libvirt_does_not_support_json_backingfile : unit -> unit
-val error_if_no_ssh_agent : unit -> unit
class virtual input_libvirt : string option -> string option -> string
-> object
method precheck : unit -> unit
diff --git a/v2v/input_vmx.ml b/v2v/input_vmx.ml
index c50217b9e..3032eba96 100644
--- a/v2v/input_vmx.ml
+++ b/v2v/input_vmx.ml
@@ -21,14 +21,82 @@ open Scanf
open Std_utils
open Tools_utils
+open Unix_utils
open Common_gettext.Gettext
open Types
open Utils
open Name_from_disk
-let rec find_disks vmx vmx_filename - find_scsi_disks vmx vmx_filename @
find_ide_disks vmx vmx_filename
+type vmx_source + | File of string (* local file or
NFS *)
+ | SSH of string option * string * string (* SSH username, server, path *)
+
+(* The single filename on the command line is intepreted either as
+ * a local file or a remote SSH path (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)
+
+(* '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 localfile = tmpdir // filename in
+
+ (* XXX Assumes default port number. *)
+ let cmd + sprintf "scp%s %s%s:%s %s"
+ (if verbose () then "" else " -q")
+ (match user with None -> "" | Some user -> quote
user ^ "@")
+ (quote server)
+ (* 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 localfile) in
+ if verbose () then
+ eprintf "%s\n%!" cmd;
+ if Sys.command cmd <> 0 then
+ error (f_"could not copy the VMX file from the remote server, see
earlier error messages");
+ localfile
+
+(* Test if [path] exists on the remote server. *)
+let remote_file_exists user server path + (* XXX Assumes default port number.
*)
+ let cmd + sprintf "ssh %s%s test -f %s"
+ (match user with None -> "" | Some user -> quote
user ^ "@")
+ (quote server)
+ (* Double quoting is necessary here, see above. *)
+ (quote (quote path)) in
+ if verbose () then
+ eprintf "%s\n%!" cmd;
+ Sys.command cmd = 0
+
+let rec find_disks vmx vmx_source + find_scsi_disks vmx vmx_source @
find_ide_disks vmx vmx_source
(* Find all SCSI hard disks.
*
@@ -38,7 +106,7 @@ let rec find_disks vmx vmx_filename *
| omitted
* scsi0:0.fileName = "guest.vmdk"
*)
-and find_scsi_disks vmx vmx_filename +and find_scsi_disks vmx vmx_source let
get_scsi_controller_target ns sscanf ns "scsi%d:%d" (fun c t
-> c, t)
in
@@ -50,7 +118,7 @@ and find_scsi_disks vmx vmx_filename
Some "scsi-harddisk"; None ] in
let scsi_controller = Source_SCSI in
- find_hdds vmx vmx_filename
+ find_hdds vmx vmx_source
get_scsi_controller_target is_scsi_controller_target
scsi_device_types scsi_controller
@@ -60,7 +128,7 @@ and find_scsi_disks vmx vmx_filename * ide0:0.deviceType
= "ata-hardDisk"
* ide0:0.fileName = "guest.vmdk"
*)
-and find_ide_disks vmx vmx_filename +and find_ide_disks vmx vmx_source let
get_ide_controller_target ns sscanf ns "ide%d:%d" (fun c t ->
c, t)
in
@@ -71,11 +139,11 @@ and find_ide_disks vmx vmx_filename let ide_device_types
= [ Some "ata-harddisk" ] in
let ide_controller = Source_IDE in
- find_hdds vmx vmx_filename
+ find_hdds vmx vmx_source
get_ide_controller_target is_ide_controller_target
ide_device_types ide_controller
-and find_hdds vmx vmx_filename
+and find_hdds vmx vmx_source
get_controller_target is_controller_target
device_types controller (* Find namespaces matching
'(ide|scsi)X:Y' with suitable deviceType. *)
@@ -101,9 +169,9 @@ and find_hdds vmx vmx_filename
match path, v with
| [ns; "filename"], Some filename ->
let c, t = get_controller_target ns in
+ let uri, format = qemu_uri_of_filename vmx_source filename in
let s = { s_disk_id = (-1);
- s_qemu_uri = qemu_uri_of_filename vmx_filename filename;
- s_format = Some "vmdk";
+ s_qemu_uri = uri; s_format = Some format;
s_controller = Some controller } in
Some (c, t, s)
| _ -> None
@@ -125,17 +193,48 @@ and find_hdds vmx vmx_filename
(* The filename can be an absolute path, but is more often a
* path relative to the location of the vmx file.
*
- * Note that we always end up with an absolute path, which is
- * also useful because it means we won't have any paths that
- * could be misinterpreted by qemu.
+ * This constructs a QEMU URI of the filename relative to the
+ * vmx file (which might be remote over SSH).
*)
-and qemu_uri_of_filename vmx_filename filename - if not (Filename.is_relative
filename) then
- filename
- else (
- let dir = Filename.dirname (absolute_path vmx_filename) in
- dir // filename
- )
+and qemu_uri_of_filename vmx_source filename + match vmx_source with
+ | File vmx_filename ->
+ (* Always ensure this returns an absolute path to avoid
+ * any confusion with filenames containing colons.
+ *)
+ absolute_path_from_other_file vmx_filename filename, "vmdk"
+
+ | SSH (user, server, vmx_path) ->
+ let abs_path = absolute_path_from_other_file vmx_path filename in
+ let format = "vmdk" in
+
+ (* XXX This is a hack to work around qemu / VMDK limitation
+ * "Cannot use relative extent paths with VMDK descriptor
file"
+ * We can remove this if the above is fixed.
+ *)
+ 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")
+ 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_key_check", JSON.String "no";
+ ] in
+ let json_params + match user with
+ | None -> json_params
+ | Some user ->
+ ("file.user", JSON.String user) :: json_params in
+
+ "json:" ^ JSON.string_of_doc json_params, format
+
+and absolute_path_from_other_file other_filename filename + if not
(Filename.is_relative filename) then filename
+ else (Filename.dirname (absolute_path other_filename)) // filename
(* Find all removable disks.
*
@@ -268,21 +367,41 @@ and find_nics vmx let nics = List.map (fun (_, source)
-> source) nics in
nics
-class input_vmx vmx_filename = object
+class input_vmx input_transport arg = object
inherit input
- method as_options = "-i vmx " ^ vmx_filename
+ method as_options = "-i vmx " ^ arg
+
+ method precheck () + match input_transport with
+ | None -> ()
+ | Some `SSH ->
+ if backend_is_libvirt () then
+ error (f_"because libvirtd doesn't pass the SSH_AUTH_SOCK
environment variable to qemu you must set this environment variable:\n\nexport
LIBGUESTFS_BACKEND=direct\n\nand then rerun the virt-v2v command.");
+ error_if_no_ssh_agent ()
method source () - (* Parse the VMX file. *)
- let vmx = Parse_vmx.parse_file vmx_filename in
+ let vmx_source = vmx_source_of_arg input_transport arg in
+
+ (* If the transport is SSH, fetch the file from remote, else
+ * parse it from local.
+ *)
+ 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
+ Parse_vmx.parse_file filename in
let name match Parse_vmx.get_string vmx ["displayName"]
with
+ | Some s -> s
| None ->
warning (f_"no displayName key found in VMX file");
- name_from_disk vmx_filename
- | Some s -> s in
+ match vmx_source with
+ | File filename -> name_from_disk filename
+ | SSH (_, _, path) -> name_from_disk path in
let memory_mb match Parse_vmx.get_int64 vmx ["memSize"]
with
@@ -333,7 +452,7 @@ class input_vmx vmx_filename = object
None
| None -> None in
- let disks = find_disks vmx vmx_filename in
+ let disks = find_disks vmx vmx_source in
let removables = find_removables vmx in
let nics = find_nics vmx in
diff --git a/v2v/input_vmx.mli b/v2v/input_vmx.mli
index f236f8716..34ec2a5c6 100644
--- a/v2v/input_vmx.mli
+++ b/v2v/input_vmx.mli
@@ -18,5 +18,6 @@
(** [-i vmx] source. *)
-val input_vmx : string -> Types.input
-(** [input_vmx filename] sets up an input from vmware vmx file. *)
+val input_vmx : [`SSH] option -> string -> Types.input
+(** [input_vmx input_transport arg] sets up an input
+ from vmware vmx file. *)
diff --git a/v2v/utils.ml b/v2v/utils.ml
index 91c0ed1c8..d5d177e42 100644
--- a/v2v/utils.ml
+++ b/v2v/utils.ml
@@ -138,6 +138,15 @@ let backend_is_libvirt () let backend = fst
(String.split ":" backend) in
backend = "libvirt"
+(* When using the SSH driver in qemu (currently) this requires
+ * ssh-agent authentication. Give a clear error if this hasn't been
+ * set up (RHBZ#1139973). This might improve if we switch to libssh1.
+ *)
+let error_if_no_ssh_agent () + try ignore (Sys.getenv
"SSH_AUTH_SOCK")
+ with Not_found ->
+ error (f_"ssh-agent authentication has not been set up ($SSH_AUTH_SOCK
is not set). This is required by qemu to do passwordless ssh access. See the
virt-v2v(1) man page for more information.")
+
let ws = PCRE.compile "\\s+"
let find_file_in_tar tar filename diff --git a/v2v/utils.mli b/v2v/utils.mli
index 8d902a53a..422fde298 100644
--- a/v2v/utils.mli
+++ b/v2v/utils.mli
@@ -53,6 +53,8 @@ val qemu_img_supports_offset_and_size : unit -> bool
val backend_is_libvirt : unit -> bool
(** Return true iff the current backend is libvirt. *)
+val error_if_no_ssh_agent : unit -> unit
+
val find_file_in_tar : string -> string -> int64 * int64
(** [find_file_in_tar tar filename] looks up file in [tar] archive and returns
a tuple containing at which byte it starts and how long the file is.
diff --git a/v2v/virt-v2v.pod b/v2v/virt-v2v.pod
index 503830c9d..79269a9da 100644
--- a/v2v/virt-v2v.pod
+++ b/v2v/virt-v2v.pod
@@ -118,6 +118,23 @@ Note that after conversion, the guest will appear in the
RHV-M Export
Storage Domain, from where you will need to import it using the RHV-M
user interface. (See L</OUTPUT TO RHV>).
+=head2 Convert from ESXi hypervisor over SSH to local libvirt
+
+You have an ESXi hypervisor called C<esxi.example.com> with SSH access
+enabled. You want to convert from VMFS storage on that server to
+a local file.
+
+ virt-v2v \
+ -i vmx -it ssh \
+ "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<not> need to be run
+as root in this case.
+
+For more information about converting from VMX files see
+L</INPUT FROM VMWARE VMX> below.
+
=head2 Convert disk image to OpenStack glance
Given a disk image from another hypervisor that you want to convert to
@@ -343,9 +360,10 @@ L</INPUT FROM VMWARE OVA> below
Set the input method to I<vmx>.
-In this mode you can read a VMware vmx file directly. This is useful
-when VMware VMs are stored on an NFS server which you can mount
-directly. See L</INPUT FROM VMWARE VMX> below
+In this mode you can read a VMware vmx file directly or over SSH.
+This is useful when VMware VMs are stored on an NFS server which you
+can mount directly, or where you have access by SSH to an ESXi
+hypervisor. See L</INPUT FROM VMWARE VMX> below
=item B<-ic> libvirtURI
@@ -379,6 +397,11 @@ See L</IN PLACE CONVERSION> below.
Conflicts with all I<-o *> options.
+=item B<-it> B<ssh>
+
+When using I<-i vmx>, this enables the ssh transport.
+See L</INPUT FROM VMWARE VMX> below.
+
=item B<-it> B<vddk>
Use VMware VDDK as a transport to copy the input disks. See
@@ -1347,9 +1370,23 @@ directory containing the files:
=head1 INPUT FROM VMWARE VMX
-Virt-v2v is able to import guests from VMware’s vmx files. This is
-useful where VMware virtual machines are stored on a separate NFS
-server and you are able to mount the NFS storage directly.
+Virt-v2v is able to import guests from VMware’s vmx files.
+
+This is useful in two cases:
+
+=over 4
+
+=item 1.
+
+VMware virtual machines are stored on a separate NFS server and you
+are able to mount the NFS storage directly.
+
+=item 2.
+
+You have enabled SSH access to the VMware ESXi hypervisor and there is
+a C</vmfs/volumes> folder containing the virtual machines.
+
+=back
If you find a folder of files called F<I<guest>.vmx>,
F<I<guest>.vmxf>, F<I<guest>.nvram> and one or more
F<.vmdk> disk
@@ -1375,28 +1412,50 @@ With other methods, virt-v2v tries to prevent concurrent
access, but
because the I<-i vmx> method works directly against the storage,
checking for concurrent access is not possible.
-=head2 VMX: MOUNT THE NFS STORAGE ON THE CONVERSION SERVER
+=head2 VMX: ACCESS TO THE STORAGE CONTAINING THE VMX AND VMDK FILES
-Virt-v2v must be able to access the F<.vmx> file and any local
-F<.vmdk> disks. Normally this means you must mount the NFS storage
-containing these files.
+If the vmx and vmdk files aren't available locally then you must
+I<either> mount the NFS storage on the conversion server I<or>
enable
+passwordless SSH on the ESXi hypervisor.
+
+=head3 VMX: Passwordless SSH using ssh-agent
+
+You must also use ssh-agent, and add your ssh public key to
+F</etc/ssh/keys-root/authorized_keys> (on the ESXi hypervisor).
+
+After doing this, you should check that passwordless access works from
+the virt-v2v server to the ESXi hypervisor. For example:
+
+ $ ssh root@esxi.example.com
+ [ logs straight into the shell, no password is requested ]
+
+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.
=head2 VMX: IMPORTING A GUEST
-To import a vmx file, do:
+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:
+
+ $ virt-v2v \
+ -i vmx -it 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
any vmdk disks.
=head1 INPUT FROM VMWARE ESXi HYPERVISOR
-Virt-v2v cannot access an ESXi hypervisor directly. You should use
-the OVA or VMX methods above (see L</INPUT FROM VMWARE OVA> and/or
-L</INPUT FROM VMWARE VMX>) if possible, as it is much faster and
-requires much less disk space than the method described in this
-section.
+You should use the OVA or VMX methods above (see L</INPUT FROM VMWARE
+OVA> and/or L</INPUT FROM VMWARE VMX>) if possible, as it is much
+faster and requires much less disk space than the method described in
+this section.
You can use the L<virt-v2v-copy-to-local(1)> tool to copy the guest
off the hypervisor into a local file, and then convert it.
--
2.13.2
Pino Toscano
2017-Dec-08 16:55 UTC
Re: [Libguestfs] [PATCH v2 1/2] v2v: vddk: Switch to using ‘-it vddk’ to specify VDDK as input transport.
On Friday, 8 December 2017 17:02:29 CET Richard W.M. Jones wrote:> Previously the presence of the ‘--vddk <libdir>’ option magically > enabled VDDK mode. However we want to introduce other transports for > VMware conversions so this wasn't a very clean choice. > > With this commit you must use ‘-it vddk’ to specify that you want VDDK > as a disk transport. The previous ‘--vddk <libdir>’ option has been > renamed to ‘--vddk-libdir <libdir>’ to be consistent with the other > passthrough options, and it is no longer required. > > A new command line looks like: > > $ export PATH=/path/to/nbdkit:$PATH > $ virt-v2v \ > -ic 'vpx://root@vcenter.example.com/Datacenter/esxi?no_verify=1' \ > | -it vddk \ > | --vddk-libdir /path/to/vmware-vix-disklib-distrib \ > --vddk-thumbprint xx:xx:xx:... \ > "Windows 2003" \ > -o local -os /var/tmp > > where only the two lines marked with ‘|’ have changed. > --- > [...] > @@ -286,6 +289,12 @@ read the man page virt-v2v(1). > let input_conn = !input_conn in > let input_format = !input_format in > let input_mode = !input_mode in > + let input_transport > + match !input_transport with > + | None -> None > + | Some "vddk" -> Some `VDDK > + | Some transport -> > + error (f_"unknown input transport ‘-it %s’") transport inAnother option could be to switch from a polymorphic variant to a simple type, e.g.: type input_transport | None | VDDK of vddk_options | SSH So it will remove the extra vddk_options parameters in most functions where input_transport is passed, and easily support more options for future transports. The rest of the changes seem fine. -- Pino Toscano
Pino Toscano
2017-Dec-08 17:04 UTC
Re: [Libguestfs] [PATCH v2 2/2] v2v: -i vmx: Enhance VMX support with ability to use ‘-it ssh’ transport.
On Friday, 8 December 2017 17:02:30 CET Richard W.M. Jones wrote:> This enhances the existing VMX input support allowing it to be > used over SSH to the ESXi server. > > The original command (for local .vmx files) was: > > $ virt-v2v -i vmx guest.vmx -o local -os /var/tmp > > Adding ‘-it ssh’ and using an SSH remote path gives the new syntax: > > $ virt-v2v \ > -i vmx -it ssh \ > "root@esxi.example.com:/vmfs/volumes/datastore1/guest/guest.vmx" \ > -o local -os /var/tmp > > I anticipate that this input method will be widely used enough that it > deserves its own example at the top of the man page. > --- > [...] > +(* The single filename on the command line is intepreted either as > + * a local file or a remote SSH path (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)IMHO this new transport could use the standard URI syntax, so root@esxi.example.com/vmfs/volumes/datastore1/guest/guest.vmx instead of root@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). The rest seems fine, at a quick glance. -- Pino Toscano
Apparently Analagous Threads
- [PATCH v2 1/2] v2v: vddk: Switch to using ‘-it vddk’ to specify VDDK as input transport.
- v2v: vddk: Switch to using ‘-it vddk’ to specify VDDK as input transport.
- [PATCH v2 0/2] v2v: Add -it vddk and -it ssh flags.
- Re: [PATCH v7 4/6] v2v: Add general mechanism for input and output options (-io/-oo).
- [PATCH v7 4/6] v2v: Add general mechanism for input and output options (-io/-oo).