This allows certain guests which virt-v2v cannot access to be copied
off the remote hypervisor and converted. Essentially this just
automates the process of copying the guest's disks and adjusting the
libvirt XML.
---
.gitignore | 3 +
po/POTFILES-ml | 1 +
v2v/Makefile.am | 65 ++++++++-
v2v/copy_to_local.ml | 311 +++++++++++++++++++++++++++++++++++++++++
v2v/virt-v2v-copy-to-local.pod | 206 +++++++++++++++++++++++++++
v2v/virt-v2v.pod | 72 ++++------
6 files changed, 606 insertions(+), 52 deletions(-)
create mode 100644 v2v/copy_to_local.ml
create mode 100644 v2v/virt-v2v-copy-to-local.pod
diff --git a/.gitignore b/.gitignore
index a43c243..11557b6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -562,6 +562,7 @@ Makefile.in
/v2v/rhel-6.5.img
/v2v/rhel-7.0.img
/v2v/stamp-virt-v2v.pod
+/v2v/stamp-virt-v2v-copy-to-local.pod
/v2v/test-harness/.depend
/v2v/test-harness/META
/v2v/test-harness/dllv2v_test_harness.so
@@ -570,6 +571,8 @@ Makefile.in
/v2v/v2v_unit_tests
/v2v/virt-v2v
/v2v/virt-v2v.1
+/v2v/virt-v2v-copy-to-local
+/v2v/virt-v2v-copy-to-local.1
/website/*.html
/website/README.txt
/website/TODO.txt
diff --git a/po/POTFILES-ml b/po/POTFILES-ml
index c02ffc0..00a9d63 100644
--- a/po/POTFILES-ml
+++ b/po/POTFILES-ml
@@ -101,6 +101,7 @@ v2v/changeuid.ml
v2v/cmdline.ml
v2v/convert_linux.ml
v2v/convert_windows.ml
+v2v/copy_to_local.ml
v2v/curl.ml
v2v/domainxml.ml
v2v/input_disk.ml
diff --git a/v2v/Makefile.am b/v2v/Makefile.am
index 5dfef6e..4b6cbcc 100644
--- a/v2v/Makefile.am
+++ b/v2v/Makefile.am
@@ -35,7 +35,8 @@ EXTRA_DIST = \
test-v2v-networks-and-bridges-expected.xml \
test-v2v-networks-and-bridges.xml \
test-v2v-sound.xml \
- virt-v2v.pod
+ virt-v2v.pod \
+ virt-v2v-copy-to-local.pod
CLEANFILES = *~ *.annot *.cmi *.cmo *.cmx *.cmxa *.o virt-v2v
@@ -113,7 +114,7 @@ SOURCES_C = \
if HAVE_OCAML
-bin_PROGRAMS = virt-v2v
+bin_PROGRAMS = virt-v2v virt-v2v-copy-to-local
virt_v2v_SOURCES = $(SOURCES_C)
virt_v2v_CPPFLAGS = \
@@ -177,6 +178,46 @@ virt_v2v_LINK = \
$(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLPACKAGES) $(OCAMLLINKFLAGS) \
$(OBJECTS) -o $@
+virt_v2v_copy_to_local_SOURCES = \
+ domainxml-c.c \
+ utils-c.c \
+ xml-c.c
+virt_v2v_copy_to_local_CPPFLAGS = \
+ -I. \
+ -I$(top_builddir) \
+ -I$(shell $(OCAMLC) -where) \
+ -I$(top_srcdir)/src
+virt_v2v_copy_to_local_CFLAGS = \
+ $(WARN_CFLAGS) $(WERROR_CFLAGS) \
+ $(LIBXML2_CFLAGS) \
+ $(LIBVIRT_CFLAGS)
+
+COPY_TO_LOCAL_BOBJECTS = \
+ $(top_builddir)/mllib/guestfs_config.cmo \
+ $(top_builddir)/mllib/common_gettext.cmo \
+ $(top_builddir)/mllib/common_utils.cmo \
+ $(top_builddir)/mllib/JSON.cmo \
+ xml.cmo \
+ utils.cmo \
+ curl.cmo \
+ vCenter.cmo \
+ domainxml.cmo \
+ copy_to_local.cmo
+COPY_TO_LOCAL_XOBJECTS = $(COPY_TO_LOCAL_BOBJECTS:.cmo=.cmx)
+
+if !HAVE_OCAMLOPT
+COPY_TO_LOCAL_OBJECTS = $(COPY_TO_LOCAL_BOBJECTS)
+else
+COPY_TO_LOCAL_OBJECTS = $(COPY_TO_LOCAL_XOBJECTS)
+endif
+
+virt_v2v_copy_to_local_DEPENDENCIES = \
+ $(COPY_TO_LOCAL_OBJECTS) $(top_srcdir)/ocaml-link.sh
+virt_v2v_copy_to_local_LINK = \
+ $(top_srcdir)/ocaml-link.sh -cclib '$(OCAMLCLIBS)' -- \
+ $(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLPACKAGES) $(OCAMLLINKFLAGS) \
+ $(COPY_TO_LOCAL_OBJECTS) -o $@
+
.mli.cmi:
$(OCAMLFIND) ocamlc $(OCAMLFLAGS) $(OCAMLPACKAGES) -c $< -o $@
.ml.cmo:
@@ -192,9 +233,11 @@ virttoolsdatadir = $(datadir)/virt-tools
# Manual pages and HTML files for the website.
-man_MANS = virt-v2v.1
+man_MANS = virt-v2v.1 virt-v2v-copy-to-local.1
-noinst_DATA = $(top_builddir)/website/virt-v2v.1.html
+noinst_DATA = \
+ $(top_builddir)/website/virt-v2v.1.html \
+ $(top_builddir)/website/virt-v2v-copy-to-local.1.html
virt-v2v.1 $(top_builddir)/website/virt-v2v.1.html: stamp-virt-v2v.pod
@@ -206,9 +249,21 @@ stamp-virt-v2v.pod: virt-v2v.pod
$<
touch $@
+virt-v2v-copy-to-local.1 $(top_builddir)/website/virt-v2v-copy-to-local.1.html:
stamp-virt-v2v-copy-to-local.pod
+
+stamp-virt-v2v-copy-to-local.pod: virt-v2v-copy-to-local.pod
+ $(PODWRAPPER) \
+ --man virt-v2v-copy-to-local.1 \
+ --html $(top_builddir)/website/virt-v2v-copy-to-local.1.html \
+ --license GPLv2+ \
+ $<
+ touch $@
+
CLEANFILES += \
stamp-virt-v2v.pod \
- virt-v2v.1
+ stamp-virt-v2v-copy-to-local.pod \
+ virt-v2v.1 \
+ virt-v2v-copy-to-local.1
# Tests.
diff --git a/v2v/copy_to_local.ml b/v2v/copy_to_local.ml
new file mode 100644
index 0000000..ed967be
--- /dev/null
+++ b/v2v/copy_to_local.ml
@@ -0,0 +1,311 @@
+(* virt-v2v
+ * Copyright (C) 2009-2015 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+(* The separate virt-v2v-copy-to-local tool. *)
+
+open Printf
+
+open Common_gettext.Gettext
+open Common_utils
+
+open Utils
+
+type source_t = Xen_ssh | ESXi
+
+let rec main () =
+ let input_conn = ref None in
+
+ let set_string_option_once optname optref arg =
+ match !optref with
+ | Some _ ->
+ error (f_"%s option used more than once on the command line") optname
+ | None ->
+ optref := Some arg
+ in
+
+ (* Handle the command line. *)
+ let argspec = [
+ "-ic", Arg.String (set_string_option_once "-ic"
input_conn),
+ "uri " ^ s_"Libvirt
URI";
+ ] in
+ let argspec = set_standard_options argspec in
+ let args = ref [] in
+ let anon_fun s = args := s :: !args in
+ let usage_msg =
+ sprintf (f_"\
+%s: copy a remote guest to the local machine
+
+Copy the remote guest:
+
+ virt-v2v-copy-to-local -ic xen+ssh://root@xen.example.com guest
+
+ virt-v2v-copy-to-local -ic
esx://esxi.example.com guest
+
+Then perform the conversion step:
+
+ virt-v2v -i libvirtxml guest.xml -o local -os /var/tmp
+
+To clean up:
+
+ rm guest.xml guest-disk*
+
+A short summary of the options is given below. For detailed help please
+read the man page virt-v2v-copy-to-local(1).
+")
+ prog in
+ Arg.parse argspec anon_fun usage_msg;
+
+ let args = !args in
+ let input_conn = !input_conn in
+
+ let input_conn =
+ match input_conn with
+ | None ->
+ error (f_"the -ic parameter is required") (* at the moment *)
+ | Some ic -> ic in
+
+ (* Check this is a libvirt URI we can understand. *)
+ let parsed_uri =
+ try Xml.parse_uri input_conn
+ with Invalid_argument msg ->
+ error (f_"could not parse '-ic %s'. Original error message was:
%s")
+ input_conn msg in
+ let source, server =
+ match parsed_uri.Xml.uri_server, parsed_uri.Xml.uri_scheme with
+ | Some server, Some "xen+ssh" -> (* Xen over SSH *)
+ Xen_ssh, server
+ | Some server, Some "esx" -> (* esxi over https *)
+ ESXi, server
+
+ (* We can probably extend this list in future. *)
+ | _ ->
+ error (f_"only copies from VMware ESXi or Xen over SSH are supported. See
the virt-v2v-copy-to-local(1) manual page.") in
+
+ (* We expect a single extra argument, which is the guest name. *)
+ let guest_name =
+ match args with
+ | [] ->
+ error (f_"missing guest name. See the virt-v2v-copy-to-local(1) manual
page.")
+ | [arg] -> arg
+ | _ ->
+ error (f_"too many command line parameters. See the
virt-v2v-copy-to-local(1) manual page.") in
+
+ (* Print the version, easier than asking users to tell us. *)
+ if verbose () then
+ printf "%s: %s %s (%s)\n%!"
+ prog Guestfs_config.package_name Guestfs_config.package_version
Guestfs_config.host_cpu;
+
+ (* Get the remote libvirt XML. *)
+ message (f_"Fetching the remote libvirt XML metadata ...");
+ let xml = Domainxml.dumpxml ~conn:input_conn guest_name in
+
+ if verbose () then
+ printf "libvirt XML from remote server:\n%s\n" xml;
+
+ (* Get the disk remote paths from the XML. *)
+ message (f_"Parsing the remote libvirt XML metadata ...");
+ let disks, dcpath, xml = parse_libvirt_xml guest_name xml in
+
+ if verbose () then
+ printf "libvirt XML after modifying for local disks:\n%s\n" xml;
+
+ (* For VMware ESXi source, we have to massage the disk path. *)
+ let disks =
+ match source with
+ | ESXi ->
+ List.map (
+ fun (remote_disk, local_disk) ->
+ let url, sslverify =
+ VCenter.map_source_to_https dcpath parsed_uri
+ server remote_disk in
+ if verbose () then
+ printf "esxi: source disk %s (sslverify=%b)\n" url sslverify;
+ let cookie =
+ VCenter.get_session_cookie None "esx" parsed_uri sslverify url in
+ (url, local_disk, sslverify, cookie)
+ ) disks
+ | Xen_ssh ->
+ List.map (fun (remote_disk, local_disk) ->
+ (remote_disk, local_disk, false, None)) disks in
+
+ (* Delete the disks on exit, unless we finish everything OK. *)
+ let delete_on_exit = ref true in
+ at_exit (
+ fun () ->
+ if !delete_on_exit then (
+ List.iter (
+ fun (_, local_disk, _, _) ->
+ try Unix.unlink local_disk with _ -> ()
+ ) disks
+ )
+ );
+
+ (* Copy the disks. *)
+ let n = List.length disks in
+ iteri (
+ fun i (remote_disk, local_disk, sslverify, cookie) ->
+ message (f_"Copying remote disk %d/%d to %s")
+ (i+1) n local_disk;
+
+ (* How we copy it depends on the source. *)
+ match source with
+ | Xen_ssh ->
+ let { Xml.uri_server = server; uri_user = user; uri_port = port } =
+ parsed_uri in
+
+ let cmd =
+ sprintf "set -o pipefail; ssh%s %s%s dd bs=1M if=%s | dd%s conv=sparse
bs=1M of=%s"
+ (match port with
+ | n when n >= 1 -> sprintf " -p %d" n
+ | _ -> "")
+ (match user with
+ | None -> ""
+ | Some u -> sprintf "%s@" (quote u))
+ (match server with
+ | None -> assert false
+ | Some server -> server)
+ (quote remote_disk)
+ (if quiet () then ""
+ else " status=progress")
+ (quote local_disk) in
+ if verbose () then
+ printf "%s\n%!" cmd;
+ if Sys.command cmd <> 0 then
+ error (f_"ssh copy command failed, see earlier errors");
+
+ | ESXi ->
+ let curl_args = [
+ "url", Some remote_disk;
+ "output", Some local_disk;
+ ] in
+ let curl_args =
+ if sslverify then curl_args
+ else ("insecure", None) :: curl_args in
+ let curl_args =
+ match cookie with
+ | None -> curl_args
+ | Some cookie -> ("cookie", Some cookie) :: curl_args in
+ let curl_args =
+ if quiet () then ("silent", None) :: curl_args
+ else curl_args in
+
+ if verbose () then
+ Curl.print_curl_command stdout curl_args;
+ ignore (Curl.run curl_args)
+ ) disks;
+
+ let guest_xml = guest_name ^ ".xml" in
+ message (f_"Writing libvirt XML metadata to %s.xml ...") guest_xml;
+ let chan = open_out guest_xml in
+ output_string chan xml;
+ close_out chan;
+
+ (* Finished, so don't delete the disks on exit. *)
+ message (f_"Finishing off");
+ delete_on_exit := false
+
+(* This is a greatly simplified version of the parsing function
+ * in virt-v2v input_libvirtxml.ml:parse_libvirt_xml
+ * It also modifies the XML <disk> elements to point to local disks.
+ *)
+and parse_libvirt_xml guest_name xml =
+ (* Parse the XML. *)
+ let doc = Xml.parse_memory xml in
+ let xpathctx = Xml.xpath_new_context doc in
+ Xml.xpath_register_ns xpathctx
+ "vmware"
"http://libvirt.org/schemas/domain/vmware/1.0";
+ let xpath_string = xpath_string xpathctx
+ and xpath_string_default = xpath_string_default xpathctx in
+
+ (* Get the dcpath, only present for libvirt >= 1.2.20 so use a
+ * sensible default for older versions.
+ *)
+ let dcpath =
+ xpath_string_default "/domain/vmware:datacenterpath"
"ha-datacenter" in
+
+ (* Parse the disks. *)
+ let get_disks, add_disk =
+ let disks = ref [] and i = ref 0 in
+ let get_disks () = List.rev !disks in
+ let add_disk remote_disk =
+ (* Generate a unique name for each output disk. *)
+ incr i;
+ let local_disk = sprintf "%s-disk%d" guest_name !i in
+
+ disks := (remote_disk, local_disk) :: !disks;
+ local_disk
+ in
+ get_disks, add_disk
+ in
+
+ (* node is a <disk> node, containing a <source> element. Update the
+ * node to point to a local file.
+ *)
+ let update_disk_node node local_disk =
+ Xml.set_prop node "type" "file";
+ let obj = Xml.xpath_eval_expression xpathctx "source" in
+ let nr_nodes = Xml.xpathobj_nr_nodes obj in
+ assert (nr_nodes >= 1);
+ for i = 0 to nr_nodes-1 do
+ let source_node = Xml.xpathobj_node obj i in
+ ignore (Xml.unset_prop source_node "dev");
+ Xml.set_prop source_node "file" local_disk
+ done
+ in
+
+ let obj =
+ Xml.xpath_eval_expression xpathctx
+ "/domain/devices/disk[@device='disk']"
in
+ let nr_nodes = Xml.xpathobj_nr_nodes obj in
+ if nr_nodes < 1 then
+ error (f_"this guest has no non-removable disks");
+
+ for i = 0 to nr_nodes-1 do
+ let node = Xml.xpathobj_node obj i in
+ Xml.xpathctx_set_current_context xpathctx node;
+
+ (* The <disk type='...'> attribute may be 'block' or
'file'.
+ * We ignore any other types.
+ *)
+ match xpath_string "@type" with
+ | None ->
+ warning (f_"<disk> element with no type attribute ignored")
+
+ | Some "block" ->
+ (match xpath_string "source/@dev" with
+ | Some path ->
+ let local_disk = add_disk path in
+ update_disk_node node local_disk
+ | None -> ()
+ );
+ | Some "file" ->
+ (match xpath_string "source/@file" with
+ | Some path ->
+ let local_disk = add_disk path in
+ update_disk_node node local_disk
+ | None -> ()
+ );
+
+ | Some disk_type ->
+ warning (f_"<disk type='%s'> was ignored") disk_type
+ done;
+
+ let xml = Xml.to_string doc ~format:true in
+ get_disks (), dcpath, xml
+
+let () = run_main_and_handle_errors main
diff --git a/v2v/virt-v2v-copy-to-local.pod b/v2v/virt-v2v-copy-to-local.pod
new file mode 100644
index 0000000..85e6d4e
--- /dev/null
+++ b/v2v/virt-v2v-copy-to-local.pod
@@ -0,0 +1,206 @@
+=head1 NAME
+
+virt-v2v-copy-to-local - Copy a remote guest to the local machine
+
+=head1 SYNOPSIS
+
+ virt-v2v-copy-to-local -ic LIBVIRT_URI GUEST
+
+ virt-v2v-copy-to-local -ic xen+ssh://root@xen.example.com xen_guest
+
+ virt-v2v-copy-to-local -ic esx://root@esxi.example.com vmware_guest
+
+=head1 DESCRIPTION
+
+C<virt-v2v-copy-to-local> copies a guest from a remote hypervisor to
+the local machine, in preparation for conversion by L<virt-v2v(1)>.
+Note this tool alone B<does not> do the virt-v2v conversion.
+
+=head2 When to use this tool
+
+This tool is not usually necessary, but there are a few special cases
+(see list below) where you might need it.
+
+If your case does not fit one of these special cases, then ignore this
+tool and read L<virt-v2v(1)> instead. The virt-v2v-copy-to-local
+process is slower than using virt-v2v directly, because it has to copy
+unused parts of the guest disk.
+
+=over 4
+
+=item *
+
+You have a Xen guest using host block devices. Virt-v2v cannot
+convert such guests directly.
+
+See L<virt-v2v(1)/XEN OR SSH CONVERSIONS FROM BLOCK DEVICES>.
+
+=item *
+
+You have VMware ESXi hypervisors, and are not using VMware vCenter to
+manage them. Virt-v2v cannot directly access ESXi hypervisor, so you
+either have to export the guest as an OVA (eg. using VMware's
+C<ovftool>); or you can use this tool to copy the guest to a local
+file on the conversion server, from where virt-v2v will be able to
+access it.
+
+=back
+
+=head2 How this tool works
+
+This tool uses libvirt to get the libvirt XML (metadata) of the remote
+guest, essentially equivalent to running C<virsh dumpxml guest>.
+
+It then uses the XML to locate the remote guest disks, which are
+copied over using a hypervisor-specific method. It uses ssh for
+remote Xen hypervisors, and HTTPS (curl) for remote ESXi hypervisors.
+
+It then modifies the libvirt XML so that it points at the local copies
+of the guest disks.
+
+The libvirt XML is output to a file called F<guest.xml> (where
+I<guest> is the name of the guest). The disk(s) are output to file(s)
+called F<guest-disk1>, F<guest-disk2> and so on.
+
+After copying the guest locally, you can convert it using:
+
+ virt-v2v -i libvirtxml guest.xml [-o options ...]
+
+Virt-v2v finds the local copies of the disks by looking in the XML.
+
+=head1 EXAMPLES
+
+=head2 Copy and convert from Xen hypervisor that uses host block devices
+
+ virt-v2v-copy-to-local -ic xen+ssh://root@xen.example.com xen_guest
+ virt-v2v -i libvirtxml xen_guest.xml -o local -os /var/tmp
+ rm xen_guest.xml xen_guest-disk*
+
+=head2 Copy and convert from ESXi hypervisor
+
+ virt-v2v-copy-to-local -ic esx://root@esxi.example.com?no_verify=1 vmware_guest
+ virt-v2v -i libvirtxml vmware_guest.xml -o local -os /var/tmp
+ rm vmware_guest.xml vmware_guest-disk*
+
+=head1 COPYING FROM XEN HYPERVISOR
+
+=head2 XEN: LIBVIRT URI
+
+The libvirt URI for remote Xen hosts will look something like this:
+
+ xen+ssh://root@xen.example.com
+
+The remote Xen server must allow root logins over ssh.
+
+To test it and list the remote guests available, use L<virsh(1)>:
+
+ $ virsh -c xen+ssh://root@xen.example.com list --all
+ Id Name State
+ ----------------------------------------------------
+ 0 Domain-0 running
+ - guest shut off
+
+=head2 XEN: COPY THE GUEST
+
+Using the libvirt URI as the I<-ic> option, copy one of the guests to
+the local machine:
+
+ $ virt-v2v-copy-to-local -ic xen+ssh://root@xen.example.com guest
+
+This creates F<guest.xml>, F<guest-disk1>, ...
+
+=head2 XEN: DO THE CONVERSION
+
+Perform the conversion of the guest using virt-v2v:
+
+ $ virt-v2v -i libvirtxml guest.xml -o local -os /var/tmp
+
+=head2 XEN: CLEAN UP
+
+Remove the F<guest.xml> and F<guest-disk*> files.
+
+=head1 COPYING FROM VMware ESXi HYPERVISOR
+
+=head2 ESXi: LIBVIRT URI
+
+The libvirt URI for VMware ESXi hypervisors will look something like this:
+
+ esx://root@esxi.example.com?no_verify=1
+
+The C<?no_verify=1> parameter disables TLS certificate checking.
+
+To test it and list the remote guests available, use L<virsh(1)>:
+
+ $ virsh -c esx://root@esxi.example.com?no_verify=1 list --all
+ Enter root's password for
esxi.example.com: ***
+ Id Name State
+ ----------------------------------------------------
+ - guest shut off
+
+=head2 ESXi: COPY THE GUEST
+
+Using the libvirt URI as the I<-ic> option, copy one of the guests to
+the local machine:
+
+ $ virt-v2v-copy-to-local -ic esx://root@esxi.example.com?no_verify=1 guest
+
+This creates F<guest.xml>, F<guest-disk1>, ...
+
+=head2 ESXi: DO THE CONVERSION
+
+Perform the conversion of the guest using virt-v2v:
+
+ $ virt-v2v -i libvirtxml guest.xml -o local -os /var/tmp
+
+=head2 ESXi: CLEAN UP
+
+Remove the F<guest.xml> and F<guest-disk*> files.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--help>
+
+Display help.
+
+=item B<-ic> libvirtURI
+
+Specify a libvirt connection URI
+
+=item B<-q>
+
+=item B<--quiet>
+
+This disables progress bars and other unnecessary output.
+
+=item B<-v>
+
+=item B<--verbose>
+
+Enable verbose messages for debugging.
+
+=item B<-V>
+
+=item B<--version>
+
+Display version number and exit.
+
+=back
+
+=head1 SEE ALSO
+
+L<virt-v2v(1)>,
+L<virsh(1)>,
+L<http://libguestfs.org/>,
+L<https://libvirt.org/uri.html>,
+L<https://libvirt.org/remote.html>,
+L<https://libvirt.org/drvesx.html>.
+
+=head1 AUTHORS
+
+Richard W.M. Jones
L<http://people.redhat.com/~rjones/>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2009-2015 Red Hat Inc.
diff --git a/v2v/virt-v2v.pod b/v2v/virt-v2v.pod
index 0b7be7d..0aa0c56 100644
--- a/v2v/virt-v2v.pod
+++ b/v2v/virt-v2v.pod
@@ -1177,6 +1177,19 @@ directory containing the files:
$ virt-v2v -i ova /path/to/files -o local -os /var/tmp
+=head1 INPUT FROM VMWARE ESXi HYPERVISOR
+
+Virt-v2v cannot access an ESXi hypervisor directly.
+
+However 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:
+
+ virt-v2v-copy-to-local -ic
esx://esxi.example.com vmware_guest
+ virt-v2v -i libvirtxml vmware_guest.xml [-o options ...]
+ rm vmware_guest.xml vmware_guest-disk*
+
+See L<virt-v2v-copy-to-local(1)> for further information.
+
=head1 INPUT FROM RHEL 5 XEN
Virt-v2v is able to import Xen guests from RHEL 5 Xen hosts.
@@ -1269,53 +1282,9 @@ The workaround is to copy the guest over to the conversion server.
You will need sufficient space on the conversion server to store a
full copy of the guest.
-=over 4
-
-=item 1.
-
-From the conversion host, dump the guest XML to a local file:
-
- virsh -c xen+ssh://root@xen.example.com dumpxml guest > guest.xml
-
-=item 2.
-
-From the conversion host, copy the guest disk(s) over. You may need
-to read the C<E<lt>diskE<gt>> sections from the guest XML to find the
-names of the guest disks.
-
- ssh root(a)xen.example.com 'dd if=/dev/VG/guest' | dd conv=sparse of=guest-disk1
-
-Notice C<conv=sparse> which adds sparseness, so the copied disk will
-use as little space as possible.
-
-=item 3.
-
-Edit the guest XML file, replacing C<E<lt>diskE<gt>> section(s) that
-refer to the remote disks with references to the local copies.
-
-Three changes have to be made. Firstly in:
-
- <disk type='block' device='disk'>
-
-the C<type> must be changed to C<file>:
-
- <disk type='file' device='disk'>
-
-The last two changes are in the C<E<lt>sourceE<gt>> line where:
-
- <source dev='/dev/VG/guest'/>
-
-C<source dev=> becomes C<source file=> pointing at the local file:
-
- <source file='guest-disk1'/>
-
-=item 4.
-
-Convert the guest using C<virt-v2v -i libvirtxml> mode like this:
-
- virt-v2v -i libvirtxml guest.xml [-o options as usual ...]
-
-=back
+ virt-v2v-copy-to-local -ic xen+ssh://root@xen.example.com guest
+ virt-v2v -i libvirtxml guest.xml [-o options ...]
+ rm guest.xml guest-disk*
=head1 OUTPUT TO LIBVIRT
@@ -1786,6 +1755,14 @@ For other environment variables, see L<guestfs(3)/ENVIRONMENT
VARIABLES>.
=over 4
+=item L<virt-v2v-copy-to-local(1)>
+
+There are some special cases where virt-v2v cannot directly access the
+remote hypervisor. In that case you have to use
+L<virt-v2v-copy-to-local(1)> to make a local copy of the guest first,
+followed by running C<virt-v2v -i libvirtxml> to perform the
+conversion.
+
=item L<engine-image-uploader(8)>
Variously called C<engine-image-uploader>, C<ovirt-image-uploader> or
@@ -1816,6 +1793,7 @@ L<guestfs(3)>,
L<guestfish(1)>,
L<qemu-img(1)>,
L<fstrim(8)>,
+L<virt-v2v-copy-to-local(1)>,
L<virt-v2v-test-harness(1)>,
L<engine-image-uploader(8)>,
L<import-to-ovirt.pl|http://git.annexia.org/?p=import-to-ovirt.git>,
--
2.5.0