[Libguestfs] [PATCH 4/4] v2v: Add a new tool virt-v2v-copy-to-local.

Richard W.M. Jones rjones at redhat.com
Thu Nov 19 19:37:35 UTC 2015


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 at 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




More information about the Libguestfs mailing list