[Libguestfs] [PATCH v3 3/7] v2v: switch to ocaml-libvirt

Pino Toscano ptoscano at redhat.com
Wed Jan 30 13:36:03 UTC 2019


Currently virt-v2v has few custom C-based functions for libvirt
operations, which are limited in what they do, and there is a lot of
duplicated code.

Instead, switch to ocaml-libvirt for all the libvirt interaction
currently done by the Libvirt_utils module.  This has few advantages:
- each input & output module now opens a libvirt connection only once,
  only when needed
- no need to pass URIs and passwords around, if not needed
- a wider range of libvirt APIs can now be used, with no need to create
  bindings manually

The hierarchy of input_libvirt* classes is changed to take a Lazy object
with the libvirt connection, accessing it through a "proctected" method:
this way, the connection is opened only at the first access.
Also, the Libvirt_utils module now is just helpers around the Libvirt
module, to centralize error handling, and few common operations.
---
 docs/C_SOURCE_FILES                 |   1 -
 po/POTFILES                         |   1 -
 v2v/Makefile.am                     |   6 +-
 v2v/copy_to_local.ml                |   7 +-
 v2v/dummy.c                         |   2 +
 v2v/input_libvirt.ml                |  20 +-
 v2v/input_libvirt_other.ml          |  27 +-
 v2v/input_libvirt_other.mli         |   5 +-
 v2v/input_libvirt_vcenter_https.ml  |  13 +-
 v2v/input_libvirt_vcenter_https.mli |   2 +-
 v2v/input_libvirt_vddk.ml           |  15 +-
 v2v/input_libvirt_vddk.mli          |   4 +-
 v2v/input_libvirt_xen_ssh.ml        |  13 +-
 v2v/input_libvirt_xen_ssh.mli       |   2 +-
 v2v/input_libvirtxml.ml             |   3 +-
 v2v/libvirt_utils-c.c               | 517 ----------------------------
 v2v/libvirt_utils.ml                |  95 ++++-
 v2v/libvirt_utils.mli               |  51 ++-
 v2v/output_libvirt.ml               |  17 +-
 v2v/parse_libvirt_xml.ml            |  14 +-
 v2v/parse_libvirt_xml.mli           |  11 +-
 21 files changed, 201 insertions(+), 625 deletions(-)
 create mode 100644 v2v/dummy.c
 delete mode 100644 v2v/libvirt_utils-c.c

diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES
index 7f1c60b30..519acedd5 100644
--- a/docs/C_SOURCE_FILES
+++ b/docs/C_SOURCE_FILES
@@ -414,6 +414,5 @@ utils/boot-analysis/boot-analysis.h
 utils/boot-benchmark/boot-benchmark.c
 utils/qemu-boot/qemu-boot.c
 utils/qemu-speed-test/qemu-speed-test.c
-v2v/libvirt_utils-c.c
 v2v/qemuopts-c.c
 v2v/test-harness/dummy.c
diff --git a/po/POTFILES b/po/POTFILES
index 79f4b8c56..8d603162f 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -471,6 +471,5 @@ utils/boot-benchmark/boot-benchmark.c
 utils/max-disks/max-disks.pl
 utils/qemu-boot/qemu-boot.c
 utils/qemu-speed-test/qemu-speed-test.c
-v2v/libvirt_utils-c.c
 v2v/qemuopts-c.c
 v2v/test-harness/dummy.c
diff --git a/v2v/Makefile.am b/v2v/Makefile.am
index 6dcc6908b..b95e96fb7 100644
--- a/v2v/Makefile.am
+++ b/v2v/Makefile.am
@@ -157,7 +157,6 @@ SOURCES_ML = \
 	v2v.ml
 
 SOURCES_C = \
-	libvirt_utils-c.c \
 	qemuopts-c.c
 
 # These files are generated and contain *.py embedded as an OCaml string.
@@ -206,6 +205,7 @@ OCAMLPACKAGES = \
 	-I $(top_builddir)/common/mlpcre \
 	-I $(top_builddir)/common/mlxml \
 	-I $(top_builddir)/common/mltools \
+	-I $(top_builddir)/common/mllibvirt \
 	-I $(top_builddir)/customize
 if HAVE_OCAML_PKG_GETTEXT
 OCAMLPACKAGES += -package gettext-stub
@@ -236,6 +236,7 @@ OCAMLLINKFLAGS = \
 	mlxml.$(MLARCHIVE) \
 	mlcutils.$(MLARCHIVE) \
 	mltools.$(MLARCHIVE) \
+	mllibvirt.$(MLARCHIVE) \
 	$(LINK_CUSTOM_OCAMLC_ONLY)
 
 virt_v2v_DEPENDENCIES = $(OBJECTS) $(top_srcdir)/ocaml-link.sh
@@ -245,7 +246,7 @@ virt_v2v_LINK = \
 	  $(OBJECTS) -o $@
 
 virt_v2v_copy_to_local_SOURCES = \
-	libvirt_utils-c.c
+	dummy.c
 virt_v2v_copy_to_local_CPPFLAGS = \
 	-I. \
 	-I$(top_builddir) \
@@ -278,6 +279,7 @@ virt_v2v_copy_to_local_DEPENDENCIES = \
 	../common/mlpcre/mlpcre.$(MLARCHIVE) \
 	../common/mlutils/mlcutils.$(MLARCHIVE) \
 	../common/mltools/mltools.$(MLARCHIVE) \
+	../common/mllibvirt/mllibvirt.$(MLARCHIVE) \
 	$(top_srcdir)/ocaml-link.sh
 virt_v2v_copy_to_local_LINK = \
 	$(top_srcdir)/ocaml-link.sh -cclib '$(OCAMLCLIBS)' -- \
diff --git a/v2v/copy_to_local.ml b/v2v/copy_to_local.ml
index 408cbdebc..e841b44c5 100644
--- a/v2v/copy_to_local.ml
+++ b/v2v/copy_to_local.ml
@@ -125,7 +125,12 @@ read the man page virt-v2v-copy-to-local(1).
 
   (* Get the remote libvirt XML. *)
   message (f_"Fetching the remote libvirt XML metadata ...");
-  let xml = Libvirt_utils.dumpxml ?password_file ~conn:input_conn guest_name in
+  let xml =
+    let auth = Libvirt_utils.auth_for_password_file ?password_file () in
+    let conn = Libvirt.Connect.connect_auth ~name:input_conn auth in
+    let dom = Libvirt_utils.get_domain conn guest_name in
+    (* Use XmlSecure to get passwords (RHBZ#1174123). *)
+    Libvirt.Domain.get_xml_desc_flags dom [Libvirt.Domain.XmlSecure] in
 
   debug "libvirt XML from remote server:\n%s" xml;
 
diff --git a/v2v/dummy.c b/v2v/dummy.c
new file mode 100644
index 000000000..ebab6198c
--- /dev/null
+++ b/v2v/dummy.c
@@ -0,0 +1,2 @@
+/* Dummy source, to be used for OCaml-based tools with no C sources. */
+enum { foo = 1 };
diff --git a/v2v/input_libvirt.ml b/v2v/input_libvirt.ml
index 0e4afa157..3418e9f28 100644
--- a/v2v/input_libvirt.ml
+++ b/v2v/input_libvirt.ml
@@ -28,9 +28,17 @@ open Utils
 
 (* Choose the right subclass based on the URI. *)
 let input_libvirt input_conn input_password input_transport guest =
+  (* Create a lazy object to open the connection to libvirt only when
+   * needed.
+   *)
+  let lazy_conn =
+    lazy (
+      let auth = Libvirt_utils.auth_for_password_file ?password_file:input_password () in
+      Libvirt.Connect.connect_auth ?name:input_conn auth
+    ) in
   match input_conn with
   | None ->
-    Input_libvirt_other.input_libvirt_other input_conn input_password guest
+    Input_libvirt_other.input_libvirt_other lazy_conn guest
 
   | Some orig_uri ->
     let { Xml.uri_server = server; uri_scheme = scheme } as parsed_uri =
@@ -45,22 +53,22 @@ let input_libvirt input_conn input_password input_transport guest =
 
     | Some _, None, _                   (* No scheme? *)
     | Some _, Some "", _ ->
-      Input_libvirt_other.input_libvirt_other input_conn input_password guest
+      Input_libvirt_other.input_libvirt_other lazy_conn guest
 
     (* vCenter over https. *)
     | Some server, Some ("esx"|"gsx"|"vpx"), None ->
        Input_libvirt_vcenter_https.input_libvirt_vcenter_https
-         input_conn input_password parsed_uri server guest
+         lazy_conn input_password parsed_uri server guest
 
     (* vCenter or ESXi using nbdkit vddk plugin *)
     | Some server, Some ("esx"|"gsx"|"vpx"), Some (`VDDK vddk_options) ->
-       Input_libvirt_vddk.input_libvirt_vddk input_conn input_password
+       Input_libvirt_vddk.input_libvirt_vddk lazy_conn input_conn input_password
                                              vddk_options parsed_uri guest
 
     (* Xen over SSH *)
     | Some server, Some "xen+ssh", _ ->
       Input_libvirt_xen_ssh.input_libvirt_xen_ssh
-        input_conn input_password parsed_uri server guest
+        lazy_conn parsed_uri server guest
 
     (* Old virt-v2v also supported qemu+ssh://.  However I am
      * deliberately not supporting this in new virt-v2v.  Don't
@@ -71,6 +79,6 @@ let input_libvirt input_conn input_password input_transport guest =
     | 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 input_conn input_password guest
+      Input_libvirt_other.input_libvirt_other lazy_conn guest
 
 let () = Modules_list.register_input_module "libvirt"
diff --git a/v2v/input_libvirt_other.ml b/v2v/input_libvirt_other.ml
index dc69d64e5..aff1a872b 100644
--- a/v2v/input_libvirt_other.ml
+++ b/v2v/input_libvirt_other.ml
@@ -40,35 +40,28 @@ let error_if_libvirt_does_not_support_json_backingfile () =
     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.")
 
 (* Superclass. *)
-class virtual input_libvirt input_conn (input_password : string option) guest =
-object
+class virtual input_libvirt lazy_conn guest =
+object (self)
   inherit input
 
   method as_options =
-    sprintf "-i libvirt%s %s"
-      (match input_conn with
-      | None -> ""
-      | Some uri -> " -ic " ^ uri)
-      guest
+    sprintf "-i libvirt -ic %s %s" (Libvirt.Connect.get_uri self#conn) guest
+
+  method private conn : Libvirt.rw Libvirt.Connect.t =
+    Lazy.force lazy_conn
 end
 
 (* Subclass specialized for handling anything that's *not* VMware vCenter
  * or Xen.
  *)
-class input_libvirt_other input_conn input_password guest =
-object
-  inherit input_libvirt input_conn input_password guest
+class input_libvirt_other lazy_conn guest =
+object (self)
+  inherit input_libvirt lazy_conn guest
 
   method source () =
     debug "input_libvirt_other: source ()";
 
-    (* Get the libvirt XML.  This also checks (as a side-effect)
-     * that the domain is not running.  (RHBZ#1138586)
-     *)
-    let xml = Libvirt_utils.dumpxml ?password_file:input_password
-                                    ?conn:input_conn guest in
-
-    let source, disks = parse_libvirt_xml ?conn:input_conn xml in
+    let source, disks, _ = parse_libvirt_domain self#conn guest in
     let disks = List.map (fun { p_source_disk = disk } -> disk) disks in
     { source with s_disks = disks }
 end
diff --git a/v2v/input_libvirt_other.mli b/v2v/input_libvirt_other.mli
index e6671a76b..10f574281 100644
--- a/v2v/input_libvirt_other.mli
+++ b/v2v/input_libvirt_other.mli
@@ -20,11 +20,12 @@
 
 val error_if_libvirt_does_not_support_json_backingfile : unit -> unit
 
-class virtual input_libvirt : string option -> string option -> string -> object
+class virtual input_libvirt : Libvirt.rw Libvirt.Connect.t Lazy.t -> string -> object
   method precheck : unit -> unit
   method as_options : string
   method virtual source : unit -> Types.source
   method adjust_overlay_parameters : Types.overlay -> unit
+  method private conn : Libvirt.rw Libvirt.Connect.t
 end
 
-val input_libvirt_other : string option -> string option -> string -> Types.input
+val input_libvirt_other : Libvirt.rw Libvirt.Connect.t Lazy.t -> string -> Types.input
diff --git a/v2v/input_libvirt_vcenter_https.ml b/v2v/input_libvirt_vcenter_https.ml
index 96f009771..adc05f8c1 100644
--- a/v2v/input_libvirt_vcenter_https.ml
+++ b/v2v/input_libvirt_vcenter_https.ml
@@ -36,9 +36,9 @@ let readahead_for_copying = Some (64 * 1024 * 1024)
 
 (* Subclass specialized for handling VMware vCenter over https. *)
 class input_libvirt_vcenter_https
-        input_conn input_password parsed_uri server guest =
-object
-  inherit input_libvirt input_conn input_password guest
+        lazy_conn input_password parsed_uri server guest =
+object (self)
+  inherit input_libvirt lazy_conn guest
 
   val saved_source_paths = Hashtbl.create 13
   val mutable dcPath = ""
@@ -61,12 +61,7 @@ object
     unsetenv "ALL_PROXY";
     unsetenv "NO_PROXY";
 
-    (* Get the libvirt XML.  This also checks (as a side-effect)
-     * that the domain is not running.  (RHBZ#1138586)
-     *)
-    let xml = Libvirt_utils.dumpxml ?password_file:input_password
-                                    ?conn:input_conn guest in
-    let source, disks = parse_libvirt_xml ?conn:input_conn xml in
+    let source, disks, xml = parse_libvirt_domain self#conn guest in
 
     (* Find the <vmware:datacenterpath> element from the XML.  This
      * was added in libvirt >= 1.2.20.
diff --git a/v2v/input_libvirt_vcenter_https.mli b/v2v/input_libvirt_vcenter_https.mli
index 966cc674c..c59a34a71 100644
--- a/v2v/input_libvirt_vcenter_https.mli
+++ b/v2v/input_libvirt_vcenter_https.mli
@@ -18,4 +18,4 @@
 
 (** [-i libvirt] when the source is VMware vCenter *)
 
-val input_libvirt_vcenter_https : string option -> string option -> Xml.uri -> string -> string -> Types.input
+val input_libvirt_vcenter_https : Libvirt.rw Libvirt.Connect.t Lazy.t -> string option -> Xml.uri -> string -> string -> Types.input
diff --git a/v2v/input_libvirt_vddk.ml b/v2v/input_libvirt_vddk.ml
index 97c7cb532..827c4dbf4 100644
--- a/v2v/input_libvirt_vddk.ml
+++ b/v2v/input_libvirt_vddk.ml
@@ -93,8 +93,8 @@ let parse_input_options options =
   options
 
 (* Subclass specialized for handling VMware via nbdkit vddk plugin. *)
-class input_libvirt_vddk input_conn input_password vddk_options parsed_uri
-                         guest =
+class input_libvirt_vddk lazy_conn input_conn  input_password vddk_options
+                         parsed_uri guest =
   (* The VDDK path. *)
   let libdir =
     try Some (List.assoc "libdir" vddk_options)
@@ -198,8 +198,8 @@ See also the virt-v2v-input-vmware(1) manual.") libNN
       error (f_"nbdkit was compiled without SELinux support.  You will have to recompile nbdkit with libselinux-devel installed, or else set SELinux to Permissive mode while doing the conversion.")
   in
 
-object
-  inherit input_libvirt input_conn input_password guest as super
+object (self)
+  inherit input_libvirt lazy_conn guest as super
 
   method precheck () =
     error_unless_vddk_libdir ();
@@ -219,12 +219,7 @@ object
             pt_options
 
   method source () =
-    (* Get the libvirt XML.  This also checks (as a side-effect)
-     * that the domain is not running.  (RHBZ#1138586)
-     *)
-    let xml = Libvirt_utils.dumpxml ?password_file:input_password
-                                    ?conn:input_conn guest in
-    let source, disks = parse_libvirt_xml ?conn:input_conn xml in
+    let source, disks, xml = parse_libvirt_domain self#conn guest in
 
     (* Find the <vmware:moref> element from the XML.  This was added
      * in libvirt >= 3.7 and is required.
diff --git a/v2v/input_libvirt_vddk.mli b/v2v/input_libvirt_vddk.mli
index d321677d8..66d5f6f15 100644
--- a/v2v/input_libvirt_vddk.mli
+++ b/v2v/input_libvirt_vddk.mli
@@ -25,7 +25,7 @@ val print_input_options : unit -> unit
 val parse_input_options : (string * string) list -> vddk_options
 (** Print and parse vddk -io options. *)
 
-val input_libvirt_vddk : string option -> string option -> vddk_options -> Xml.uri -> string -> Types.input
-(** [input_libvirt_vddk input_conn input_password vddk_options parsed_uri guest]
+val input_libvirt_vddk : Libvirt.rw Libvirt.Connect.t Lazy.t -> string option -> string option -> vddk_options -> Xml.uri -> string -> Types.input
+(** [input_libvirt_vddk lazy_conn vddk_options parsed_uri guest]
     creates and returns a {!Types.input} object specialized for reading
     the guest disks using the nbdkit vddk plugin. *)
diff --git a/v2v/input_libvirt_xen_ssh.ml b/v2v/input_libvirt_xen_ssh.ml
index 6b77db029..f1d7d3bed 100644
--- a/v2v/input_libvirt_xen_ssh.ml
+++ b/v2v/input_libvirt_xen_ssh.ml
@@ -30,9 +30,9 @@ open Input_libvirt_other
 open Printf
 
 (* Subclass specialized for handling Xen over SSH. *)
-class input_libvirt_xen_ssh input_conn input_password parsed_uri server guest =
-object
-  inherit input_libvirt input_conn input_password guest
+class input_libvirt_xen_ssh lazy_conn parsed_uri server guest =
+object (self)
+  inherit input_libvirt lazy_conn guest
 
   method precheck () =
     if backend_is_libvirt () then
@@ -43,12 +43,7 @@ object
   method source () =
     debug "input_libvirt_xen_ssh: source: server %s" server;
 
-    (* Get the libvirt XML.  This also checks (as a side-effect)
-     * that the domain is not running.  (RHBZ#1138586)
-     *)
-    let xml = Libvirt_utils.dumpxml ?password_file:input_password
-                                    ?conn:input_conn guest in
-    let source, disks = parse_libvirt_xml ?conn:input_conn xml in
+    let source, disks, _ = parse_libvirt_domain self#conn guest in
 
     (* Map the <source/> filename (which is relative to the remote
      * Xen server) to an ssh URI.  This is a JSON URI looking something
diff --git a/v2v/input_libvirt_xen_ssh.mli b/v2v/input_libvirt_xen_ssh.mli
index 73c2645a1..6649b9883 100644
--- a/v2v/input_libvirt_xen_ssh.mli
+++ b/v2v/input_libvirt_xen_ssh.mli
@@ -18,4 +18,4 @@
 
 (** [-i libvirt] when the source is Xen *)
 
-val input_libvirt_xen_ssh : string option -> string option -> Xml.uri -> string -> string -> Types.input
+val input_libvirt_xen_ssh : Libvirt.rw Libvirt.Connect.t Lazy.t -> Xml.uri -> string -> string -> Types.input
diff --git a/v2v/input_libvirtxml.ml b/v2v/input_libvirtxml.ml
index a44b41fce..369d13bcd 100644
--- a/v2v/input_libvirtxml.ml
+++ b/v2v/input_libvirtxml.ml
@@ -34,7 +34,8 @@ object
   method source () =
     let xml = read_whole_file file in
 
-    let source, disks = parse_libvirt_xml xml in
+    let conn = Libvirt.Connect.connect () in
+    let source, disks = parse_libvirt_xml conn xml in
 
     (* When reading libvirt XML from a file (-i libvirtxml) we allow
      * paths to disk images in the libvirt XML to be relative (to the XML
diff --git a/v2v/libvirt_utils-c.c b/v2v/libvirt_utils-c.c
deleted file mode 100644
index 5ca0d5363..000000000
--- a/v2v/libvirt_utils-c.c
+++ /dev/null
@@ -1,517 +0,0 @@
-/* virt-v2v
- * Copyright (C) 2009-2019 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.
- */
-
-/**
- * This module implements various C<virsh>-like commands, but with
- * non-broken authentication handling.
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <errno.h>
-#include <libintl.h>
-
-#include <caml/alloc.h>
-#include <caml/fail.h>
-#include <caml/memory.h>
-#include <caml/mlvalues.h>
-
-#include <libvirt/libvirt.h>
-#include <libvirt/virterror.h>
-
-#include "guestfs.h"
-#include "guestfs-utils.h"
-
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
-#define ERROR_MESSAGE_LEN 512
-
-static void
-ignore_errors (void *ignore, virErrorPtr ignore2)
-{
-  /* empty */
-}
-
-/* Get the remote domain state (running, etc.).  Use virDomainGetState
- * which is most efficient, but if it's not implemented, fall back to
- * virDomainGetInfo.  See equivalent code in virsh.
- */
-static int
-get_dom_state (virDomainPtr dom)
-{
-  int state, reason;
-  virErrorPtr err;
-  virDomainInfo info;
-
-  if (virDomainGetState (dom, &state, &reason, 0) == 0)
-    return state;
-
-  err = virGetLastError ();
-  if (!err || err->code != VIR_ERR_NO_SUPPORT)
-    return -1;
-
-  if (virDomainGetInfo (dom, &info) == 0)
-    return info.state;
-
-  return -1;
-}
-
-/* See lib/libvirt-auth.c for why we need this. */
-static int
-libvirt_auth_default_wrapper (virConnectCredentialPtr cred,
-                              unsigned int ncred,
-                              void *passwordvp)
-{
-  const char *password = passwordvp;
-  unsigned int i;
-
-  if (password) {
-    /* If --password-file was specified on the command line, and the
-     * libvirt handler is asking for a password, return that.
-     */
-    for (i = 0; i < ncred; ++i) {
-      if (cred[i].type == VIR_CRED_PASSPHRASE) {
-        cred[i].result = strdup (password);
-        cred[i].resultlen = strlen (password);
-      }
-      else {
-        cred[i].result = NULL;
-        cred[i].resultlen = 0;
-      }
-    }
-    return 0;
-  }
-  else {
-    /* No --password-file so call the default handler. */
-    return virConnectAuthPtrDefault->cb (cred, ncred,
-                                         virConnectAuthPtrDefault->cbdata);
-  }
-}
-
-virStoragePoolPtr
-connect_and_load_pool (value connv, value poolnamev)
-{
-  CAMLparam2 (connv, poolnamev);
-  const char *conn_uri = NULL;
-  const char *poolname;
-  /* We have to assemble the error on the stack because a dynamic
-   * string couldn't be freed.
-   */
-  char errmsg[ERROR_MESSAGE_LEN];
-  virErrorPtr err;
-  virConnectPtr conn;
-  virStoragePoolPtr pool;
-
-  if (connv != Val_int (0))
-    conn_uri = String_val (Field (connv, 0)); /* Some conn */
-
-  /* We have to call the default authentication handler, not least
-   * since it handles all the PolicyKit crap.  However it also makes
-   * coding this simpler.
-   */
-  conn = virConnectOpenAuth (conn_uri, virConnectAuthPtrDefault,
-                             VIR_CONNECT_RO);
-  if (conn == NULL) {
-    if (conn_uri)
-      snprintf (errmsg, sizeof errmsg,
-                _("cannot open libvirt connection ‘%s’"), conn_uri);
-    else
-      snprintf (errmsg, sizeof errmsg, _("cannot open libvirt connection"));
-    caml_invalid_argument (errmsg);
-  }
-
-  /* Suppress default behaviour of printing errors to stderr.  Note
-   * you can't set this to NULL to ignore errors; setting it to NULL
-   * restores the default error handler ...
-   */
-  virConnSetErrorFunc (conn, NULL, ignore_errors);
-
-  /* Look up the pool. */
-  poolname = String_val (poolnamev);
-
-  pool = virStoragePoolLookupByUUIDString (conn, poolname);
-
-  if (!pool)
-    pool = virStoragePoolLookupByName (conn, poolname);
-
-  if (!pool) {
-    err = virGetLastError ();
-    snprintf (errmsg, sizeof errmsg,
-              _("cannot find libvirt pool ‘%s’: %s\n\nUse ‘virsh pool-list --all’ to list all available pools, and ‘virsh pool-dumpxml <pool>’ to display details about a particular pool.\n\nTo set the pool which virt-v2v uses, add the ‘-os <pool>’ option."),
-              poolname, err->message);
-    virConnectClose (conn);
-    caml_invalid_argument (errmsg);
-  }
-
-  CAMLreturnT (virStoragePoolPtr, pool);
-}
-
-value
-v2v_dumpxml (value passwordv, value connv, value domnamev)
-{
-  CAMLparam3 (passwordv, connv, domnamev);
-  CAMLlocal1 (retv);
-  const char *password = NULL;
-  const char *conn_uri = NULL;
-  const char *domname;
-  virConnectAuth authdata;
-  /* We have to assemble the error on the stack because a dynamic
-   * string couldn't be freed.
-   */
-  char errmsg[ERROR_MESSAGE_LEN];
-  virErrorPtr err;
-  virConnectPtr conn;
-  virDomainPtr dom;
-  int is_test_uri = 0;
-  char *xml;
-
-  if (passwordv != Val_int (0))
-    password = String_val (Field (passwordv, 0)); /* Some password */
-
-  if (connv != Val_int (0)) {
-    conn_uri = String_val (Field (connv, 0)); /* Some conn */
-    is_test_uri = STRPREFIX (conn_uri, "test:");
-  }
-
-  /* Set up authentication wrapper. */
-  authdata = *virConnectAuthPtrDefault;
-  authdata.cb = libvirt_auth_default_wrapper;
-  authdata.cbdata = (void *) password;
-
-  /* Note this cannot be a read-only connection since we need to use
-   * the VIR_DOMAIN_XML_SECURE flag below.
-   */
-  conn = virConnectOpenAuth (conn_uri, &authdata, 0);
-  if (conn == NULL) {
-    if (conn_uri)
-      snprintf (errmsg, sizeof errmsg,
-                _("cannot open libvirt connection ‘%s’"), conn_uri);
-    else
-      snprintf (errmsg, sizeof errmsg, _("cannot open libvirt connection"));
-    caml_invalid_argument (errmsg);
-  }
-
-  /* Suppress default behaviour of printing errors to stderr.  Note
-   * you can't set this to NULL to ignore errors; setting it to NULL
-   * restores the default error handler ...
-   */
-  virConnSetErrorFunc (conn, NULL, ignore_errors);
-
-  /* Look up the domain. */
-  domname = String_val (domnamev);
-
-  dom = virDomainLookupByUUIDString (conn, domname);
-
-  if (!dom)
-    dom = virDomainLookupByName (conn, domname);
-
-  if (!dom) {
-    err = virGetLastError ();
-    snprintf (errmsg, sizeof errmsg,
-              _("cannot find libvirt domain ‘%s’: %s"), domname, err->message);
-    virConnectClose (conn);
-    caml_invalid_argument (errmsg);
-  }
-
-  /* As a side-effect we check that the domain is shut down.  Of course
-   * this is only appropriate for virt-v2v.  (RHBZ#1138586)
-   */
-  if (!is_test_uri) {
-    const int state = get_dom_state (dom);
-
-    if (state == VIR_DOMAIN_RUNNING ||
-        state == VIR_DOMAIN_BLOCKED ||
-        state == VIR_DOMAIN_PAUSED) {
-      snprintf (errmsg, sizeof errmsg,
-                _("libvirt domain ‘%s’ is running or paused.  It must be shut down in order to perform virt-v2v conversion"),
-                domname);
-      virDomainFree (dom);
-      virConnectClose (conn);
-      caml_invalid_argument (errmsg);
-    }
-  }
-
-  /* Use VIR_DOMAIN_XML_SECURE to get passwords (RHBZ#1174123). */
-  xml = virDomainGetXMLDesc (dom, VIR_DOMAIN_XML_SECURE);
-  if (xml == NULL) {
-    err = virGetLastError ();
-    snprintf (errmsg, sizeof errmsg,
-              _("cannot fetch XML description of guest ‘%s’: %s"),
-              domname, err->message);
-    virDomainFree (dom);
-    virConnectClose (conn);
-    caml_invalid_argument (errmsg);
-  }
-  virDomainFree (dom);
-  virConnectClose (conn);
-
-  retv = caml_copy_string (xml);
-  free (xml);
-
-  CAMLreturn (retv);
-}
-
-value
-v2v_pool_dumpxml (value connv, value poolnamev)
-{
-  CAMLparam2 (connv, poolnamev);
-  CAMLlocal1 (retv);
-  /* We have to assemble the error on the stack because a dynamic
-   * string couldn't be freed.
-   */
-  char errmsg[ERROR_MESSAGE_LEN];
-  virErrorPtr err;
-  virConnectPtr conn;
-  virStoragePoolPtr pool;
-  char *xml;
-
-  /* Look up the pool. */
-  pool = connect_and_load_pool (connv, poolnamev);
-  conn = virStoragePoolGetConnect (pool);
-
-  xml = virStoragePoolGetXMLDesc (pool, 0);
-  if (xml == NULL) {
-    err = virGetLastError ();
-    snprintf (errmsg, sizeof errmsg,
-              _("cannot fetch XML description of pool ‘%s’: %s"),
-              String_val (poolnamev), err->message);
-    virStoragePoolFree (pool);
-    virConnectClose (conn);
-    caml_invalid_argument (errmsg);
-  }
-  virStoragePoolFree (pool);
-  virConnectClose (conn);
-
-  retv = caml_copy_string (xml);
-  free (xml);
-
-  CAMLreturn (retv);
-}
-
-value
-v2v_vol_dumpxml (value connv, value poolnamev, value volnamev)
-{
-  CAMLparam3 (connv, poolnamev, volnamev);
-  CAMLlocal1 (retv);
-  const char *volname;
-  /* We have to assemble the error on the stack because a dynamic
-   * string couldn't be freed.
-   */
-  char errmsg[ERROR_MESSAGE_LEN];
-  virErrorPtr err;
-  virConnectPtr conn;
-  virStoragePoolPtr pool;
-  virStorageVolPtr vol;
-  char *xml;
-
-  /* Look up the pool. */
-  pool = connect_and_load_pool (connv, poolnamev);
-  conn = virStoragePoolGetConnect (pool);
-
-  /* Look up the volume. */
-  volname = String_val (volnamev);
-
-  vol = virStorageVolLookupByName (pool, volname);
-
-  if (!vol) {
-    err = virGetLastError ();
-    snprintf (errmsg, sizeof errmsg,
-              _("cannot find libvirt volume ‘%s’: %s"), volname, err->message);
-    virStoragePoolFree (pool);
-    virConnectClose (conn);
-    caml_invalid_argument (errmsg);
-  }
-
-  xml = virStorageVolGetXMLDesc (vol, 0);
-  if (xml == NULL) {
-    err = virGetLastError ();
-    snprintf (errmsg, sizeof errmsg,
-              _("cannot fetch XML description of volume ‘%s’: %s"),
-              volname, err->message);
-    virStorageVolFree (vol);
-    virStoragePoolFree (pool);
-    virConnectClose (conn);
-    caml_invalid_argument (errmsg);
-  }
-  virStorageVolFree (vol);
-  virStoragePoolFree (pool);
-  virConnectClose (conn);
-
-  retv = caml_copy_string (xml);
-  free (xml);
-
-  CAMLreturn (retv);
-}
-
-value
-v2v_capabilities (value connv, value unitv)
-{
-  CAMLparam2 (connv, unitv);
-  CAMLlocal1 (capabilitiesv);
-  const char *conn_uri = NULL;
-  char *capabilities;
-  /* We have to assemble the error on the stack because a dynamic
-   * string couldn't be freed.
-   */
-  char errmsg[ERROR_MESSAGE_LEN];
-  virErrorPtr err;
-  virConnectPtr conn;
-
-  if (connv != Val_int (0))
-    conn_uri = String_val (Field (connv, 0)); /* Some conn */
-
-  /* We have to call the default authentication handler, not least
-   * since it handles all the PolicyKit crap.  However it also makes
-   * coding this simpler.
-   */
-  conn = virConnectOpenAuth (conn_uri, virConnectAuthPtrDefault,
-                             VIR_CONNECT_RO);
-  if (conn == NULL) {
-    if (conn_uri)
-      snprintf (errmsg, sizeof errmsg,
-                _("cannot open libvirt connection ‘%s’"), conn_uri);
-    else
-      snprintf (errmsg, sizeof errmsg, _("cannot open libvirt connection"));
-    caml_invalid_argument (errmsg);
-  }
-
-  /* Suppress default behaviour of printing errors to stderr.  Note
-   * you can't set this to NULL to ignore errors; setting it to NULL
-   * restores the default error handler ...
-   */
-  virConnSetErrorFunc (conn, NULL, ignore_errors);
-
-  capabilities = virConnectGetCapabilities (conn);
-  if (!capabilities) {
-    err = virGetLastError ();
-    snprintf (errmsg, sizeof errmsg,
-              _("cannot get libvirt hypervisor capabilities: %s"),
-              err->message);
-    virConnectClose (conn);
-    caml_invalid_argument (errmsg);
-  }
-
-  capabilitiesv = caml_copy_string (capabilities);
-  free (capabilities);
-
-  virConnectClose (conn);
-
-  CAMLreturn (capabilitiesv);
-}
-
-value
-v2v_domain_exists (value connv, value domnamev)
-{
-  CAMLparam2 (connv, domnamev);
-  const char *conn_uri = NULL;
-  const char *domname;
-  /* We have to assemble the error on the stack because a dynamic
-   * string couldn't be freed.
-   */
-  char errmsg[ERROR_MESSAGE_LEN];
-  virErrorPtr err;
-  virConnectPtr conn;
-  virDomainPtr dom;
-  int domain_exists;
-
-  if (connv != Val_int (0))
-    conn_uri = String_val (Field (connv, 0)); /* Some conn */
-
-  /* We have to call the default authentication handler, not least
-   * since it handles all the PolicyKit crap.  However it also makes
-   * coding this simpler.
-   */
-  conn = virConnectOpenAuth (conn_uri, virConnectAuthPtrDefault,
-                             VIR_CONNECT_RO);
-  if (conn == NULL) {
-    if (conn_uri)
-      snprintf (errmsg, sizeof errmsg,
-                _("cannot open libvirt connection ‘%s’"), conn_uri);
-    else
-      snprintf (errmsg, sizeof errmsg, _("cannot open libvirt connection"));
-    caml_invalid_argument (errmsg);
-  }
-
-  /* Suppress default behaviour of printing errors to stderr.  Note
-   * you can't set this to NULL to ignore errors; setting it to NULL
-   * restores the default error handler ...
-   */
-  virConnSetErrorFunc (conn, NULL, ignore_errors);
-
-  /* Look up the domain. */
-  domname = String_val (domnamev);
-  dom = virDomainLookupByName (conn, domname);
-
-  if (dom) {
-    domain_exists = 1;
-    virDomainFree (dom);
-  }
-  else {
-    err = virGetLastError ();
-    if (err->code == VIR_ERR_NO_DOMAIN)
-      domain_exists = 0;
-    else {
-      snprintf (errmsg, sizeof errmsg,
-                _("cannot find libvirt domain ‘%s’: %s"),
-                domname, err->message);
-      virConnectClose (conn);
-      caml_invalid_argument (errmsg);
-    }
-  }
-
-  virConnectClose (conn);
-
-  CAMLreturn (Val_bool (domain_exists));
-}
-
-value
-v2v_libvirt_get_version (value unitv)
-{
-  CAMLparam1 (unitv);
-  CAMLlocal1 (rv);
-  int major, minor, release;
-  /* We have to assemble the error on the stack because a dynamic
-   * string couldn't be freed.
-   */
-  char errmsg[ERROR_MESSAGE_LEN];
-  unsigned long ver;
-  virErrorPtr err;
-
-  if (virGetVersion (&ver, NULL, NULL) == -1) {
-    err = virGetLastError ();
-    snprintf (errmsg, sizeof errmsg,
-              _("cannot get libvirt library version: %s"),
-              err->message);
-    caml_invalid_argument (errmsg);
-  }
-
-  major = ver / 1000000UL;
-  minor = ver / 1000UL % 1000UL;
-  release = ver % 1000UL;
-
-  rv = caml_alloc (3, 0);
-  Store_field (rv, 0, Val_int (major));
-  Store_field (rv, 1, Val_int (minor));
-  Store_field (rv, 2, Val_int (release));
-
-  CAMLreturn (rv);
-}
diff --git a/v2v/libvirt_utils.ml b/v2v/libvirt_utils.ml
index efb186a2c..e6d315403 100644
--- a/v2v/libvirt_utils.ml
+++ b/v2v/libvirt_utils.ml
@@ -17,21 +17,92 @@
  *)
 
 open Std_utils
+open Tools_utils
+open Common_gettext.Gettext
 
-(* This module implements various [virsh]-like commands, but with
-    non-broken authentication handling. *)
+(* This module provides helper methods on top of the Libvirt
+    module. *)
 
-external dumpxml : ?password:string -> ?conn:string -> string -> string = "v2v_dumpxml"
-let dumpxml ?password_file =
-  let password = Option.map read_first_line_from_file password_file in
-  dumpxml ?password
+let auth_for_password_file ?password_file () =
+  let auth_fn creds =
+    let password = Option.map read_first_line_from_file password_file in
+    List.map (
+      function
+      | { Libvirt.Connect.typ = Libvirt.Connect.CredentialPassphrase } -> password
+      | _ -> None
+    ) creds
+  in
 
-external pool_dumpxml : ?conn:string -> string -> string = "v2v_pool_dumpxml"
-external vol_dumpxml : ?conn:string -> string -> string -> string = "v2v_vol_dumpxml"
+  {
+    Libvirt.Connect.credtype = [ Libvirt.Connect.CredentialPassphrase ];
+    cb = auth_fn;
+  }
 
-external capabilities : ?conn:string -> unit -> string = "v2v_capabilities"
+let get_domain conn name =
+  let dom =
+    try
+      Libvirt.Domain.lookup_by_uuid_string conn name
+    with
+    (* No such domain. *)
+    | Libvirt.Virterror { code = VIR_ERR_NO_DOMAIN }
+    (* Invalid UUID string. *)
+    | Libvirt.Virterror { code = VIR_ERR_INVALID_ARG; domain = VIR_FROM_DOMAIN } ->
+      (try
+        Libvirt.Domain.lookup_by_name conn name
+      with
+        Libvirt.Virterror { code = VIR_ERR_NO_DOMAIN; message } ->
+          error (f_"cannot find libvirt domain ‘%s’: %s")
+            name (Option.default "" message)
+      ) in
+  let uri = Libvirt.Connect.get_uri conn in
+  (* As a side-effect we check that the domain is shut down.  Of course
+   * this is only appropriate for virt-v2v.  (RHBZ#1138586)
+   *)
+  if not (String.is_prefix uri "test:") then (
+    (match (Libvirt.Domain.get_info dom).Libvirt.Domain.state with
+    | InfoRunning | InfoBlocked | InfoPaused ->
+      error (f_"libvirt domain ‘%s’ is running or paused.  It must be shut down in order to perform virt-v2v conversion")
+        (Libvirt.Domain.get_name dom)
+    | InfoNoState | InfoShutdown | InfoShutoff | InfoCrashed | InfoPMSuspended ->
+      ()
+    )
+  );
+  dom
 
-external domain_exists : ?conn:string -> string -> bool = "v2v_domain_exists"
+let get_pool conn name =
+  try
+    Libvirt.Pool.lookup_by_uuid_string conn name
+  with
+  (* No such pool. *)
+  | Libvirt.Virterror { code = VIR_ERR_NO_STORAGE_POOL }
+  (* Invalid UUID string. *)
+  | Libvirt.Virterror { code = VIR_ERR_INVALID_ARG; domain = VIR_FROM_STORAGE } ->
+    (try
+      Libvirt.Pool.lookup_by_name conn name
+    with Libvirt.Virterror { code = VIR_ERR_NO_STORAGE_POOL; message } ->
+      error (f_"cannot find libvirt pool ‘%s’: %s\n\nUse ‘virsh pool-list --all’ to list all available pools, and ‘virsh pool-dumpxml <pool>’ to display details about a particular pool.\n\nTo set the pool which virt-v2v uses, add the ‘-os <pool>’ option.")
+        name (Option.default "" message)
+    )
 
-external libvirt_get_version : unit -> int * int * int
-  = "v2v_libvirt_get_version"
+let get_volume pool name =
+  try
+    Libvirt.Volume.lookup_by_name pool name
+  with
+  (* No such volume. *)
+  | Libvirt.Virterror { code = VIR_ERR_NO_STORAGE_VOL; message } ->
+    error (f_"cannot find libvirt volume ‘%s’: %s")
+      name (Option.default "" message)
+
+let domain_exists conn dom =
+  try
+    ignore (Libvirt.Domain.lookup_by_name conn dom);
+    true
+  with
+    Libvirt.Virterror { code = VIR_ERR_NO_DOMAIN } -> false
+
+let libvirt_get_version () =
+  let v, _ = Libvirt.get_version () in
+  let v_major = v / 1000000 in
+  let v_minor = (v / 1000) mod 1000 in
+  let v_micro = v mod 1000 in
+  (v_major, v_minor, v_micro)
diff --git a/v2v/libvirt_utils.mli b/v2v/libvirt_utils.mli
index e6ba47ae8..91b2997ee 100644
--- a/v2v/libvirt_utils.mli
+++ b/v2v/libvirt_utils.mli
@@ -16,38 +16,35 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *)
 
-(** This module implements various [virsh]-like commands, but with
-    non-broken authentication handling.
+(** This module provides helper methods on top of the [Libvirt]
+    module. *)
 
-    If you do [virsh dumpxml foo] and if the libvirt source (eg. ESX)
-    requires an interactive password, then virsh unhelpfully sends the
-    password prompt to stdout, which is the same place we would be
-    reading the XML from.  This file works around this brokenness. *)
+val auth_for_password_file : ?password_file:string -> unit -> Libvirt.Connect.auth
+(** [auth_for_password_file ?password_file ()] returns a
+    {!Libvirt.Connect.auth} record to use when opening a new libvirt
+    connection with {!Libvirt.Connect.connect_auth} or
+    {!Libvirt.Connect.connect_auth_readonly}.  The record will
+    authenticate using the password specified in the first line of
+    [?password_file], if specified. *)
 
-val dumpxml : ?password_file:string -> ?conn:string -> string -> string
-(** [dumpxml ?password_file ?conn dom] returns the libvirt XML of domain [dom].
-    The optional [?conn] parameter is the libvirt connection URI.
-    [dom] may be a guest name or UUID. *)
+val get_domain : Libvirt.rw Libvirt.Connect.t -> string -> Libvirt.rw Libvirt.Domain.t
+(** [get_domain conn dom] returns the libvirt domain with the
+    specified [dom] name or UUID.  [conn] is the libvirt
+    connection. *)
 
-val pool_dumpxml : ?conn:string -> string -> string
-(** [pool_dumpxml ?conn pool] returns the libvirt XML of pool [pool].
-    The optional [?conn] parameter is the libvirt connection URI.
-    [pool] may be a pool name or UUID. *)
+val get_pool : Libvirt.rw Libvirt.Connect.t -> string -> Libvirt.rw Libvirt.Pool.t
+(** [get_pool conn pool] returns the libvirt pool with the
+    specified [pool] name or UUID.  [conn] is the libvirt
+    connection. *)
 
-val vol_dumpxml : ?conn:string -> string -> string -> string
-(** [vol_dumpxml ?conn pool vol] returns the libvirt XML of volume [vol],
-    which is part of the pool [pool].
-    The optional [?conn] parameter is the libvirt connection URI.
-    [pool] may be a pool name or UUID. *)
+val get_volume : Libvirt.rw Libvirt.Pool.t -> string -> Libvirt.rw Libvirt.Volume.t
+(** [get_volume pool vol] returns the libvirt volume with the
+    specified [vol] name or UUID, as part of the pool [pool]. *)
 
-val capabilities : ?conn:string -> unit -> string
-(** [capabilities ?conn ()] returns the libvirt capabilities XML.
-    The optional [?conn] parameter is the libvirt connection URI. *)
-
-val domain_exists : ?conn:string -> string -> bool
-(** [domain_exists ?conn dom] returns a boolean indicating if the
-    the libvirt XML domain [dom] exists.
-    The optional [?conn] parameter is the libvirt connection URI.
+val domain_exists : Libvirt.rw Libvirt.Connect.t -> string -> bool
+(** [domain_exists conn dom] returns a boolean indicating if the
+    the libvirt XML domain [dom] exists.  [conn] is the libvirt
+    connection.
     [dom] may be a guest name, but not a UUID. *)
 
 val libvirt_get_version : unit -> int * int * int
diff --git a/v2v/output_libvirt.ml b/v2v/output_libvirt.ml
index e110897c2..30857c572 100644
--- a/v2v/output_libvirt.ml
+++ b/v2v/output_libvirt.ml
@@ -71,8 +71,17 @@ class output_libvirt oc output_pool = object
     | Some uri -> sprintf "-o libvirt -oc %s -os %s" uri output_pool
 
   method prepare_targets source overlays _ _ _ _ =
+    (* Open the connection to libvirt. *)
+    let conn = Libvirt.Connect.connect ?name:oc () in
+
     (* Get the capabilities from libvirt. *)
-    let xml = Libvirt_utils.capabilities ?conn:oc () in
+    let xml =
+      try
+        Libvirt.Connect.get_capabilities conn
+      with
+        Libvirt.Virterror { message } ->
+          error (f_"cannot get libvirt hypervisor capabilities: %s")
+            (Option.default "" message) in
     debug "libvirt capabilities XML:\n%s" xml;
 
     (* This just checks that the capabilities XML is well-formed,
@@ -87,7 +96,7 @@ class output_libvirt oc output_pool = object
     capabilities_doc <- Some doc;
 
     (* Does the domain already exist on the target?  (RHBZ#889082) *)
-    if Libvirt_utils.domain_exists ?conn:oc source.s_name then (
+    if Libvirt_utils.domain_exists conn source.s_name then (
       if source.s_hypervisor = Physical then (* virt-p2v user *)
         error (f_"a libvirt domain called ‘%s’ already exists on the target.\n\nIf using virt-p2v, select a different ‘Name’ in the ‘Target properties’. Or delete the existing domain on the target using the ‘virsh undefine’ command.")
               source.s_name
@@ -99,7 +108,9 @@ class output_libvirt oc output_pool = object
     (* Connect to output libvirt instance and check that the pool exists
      * and dump out its XML.
      *)
-    let xml = Libvirt_utils.pool_dumpxml ?conn:oc output_pool in
+    let xml =
+      let pool = Libvirt_utils.get_pool conn output_pool in
+      Libvirt.Pool.get_xml_desc (Libvirt.Pool.const pool) in
     let doc = Xml.parse_memory xml in
     let xpathctx = Xml.xpath_new_context doc in
     let xpath_string = xpath_string xpathctx in
diff --git a/v2v/parse_libvirt_xml.ml b/v2v/parse_libvirt_xml.ml
index d5d78d367..23ab034f7 100644
--- a/v2v/parse_libvirt_xml.ml
+++ b/v2v/parse_libvirt_xml.ml
@@ -71,7 +71,7 @@ let create_curl_qemu_uri driver host port path =
   (* Turn the JSON parameters into a 'json:' protocol string. *)
   "json: " ^ JSON.string_of_doc json_params
 
-let parse_libvirt_xml ?conn xml =
+let parse_libvirt_xml conn xml =
   debug "libvirt xml is:\n%s" xml;
 
   let doc = Xml.parse_memory xml in
@@ -333,7 +333,10 @@ let parse_libvirt_xml ?conn xml =
         (match xpath_string "source/@pool", xpath_string "source/@volume" with
         | None, None | Some _, None | None, Some _ -> ()
         | Some pool, Some vol ->
-          let xml = Libvirt_utils.vol_dumpxml ?conn pool vol in
+          let xml =
+            let pool = Libvirt_utils.get_pool conn pool in
+            let vol = Libvirt_utils.get_volume pool vol in
+            Libvirt.Volume.get_xml_desc (Libvirt.Volume.const vol) in
           let doc = Xml.parse_memory xml in
           let xpathctx = Xml.xpath_new_context doc in
           let xpath_string = Xpath_helpers.xpath_string xpathctx in
@@ -511,3 +514,10 @@ let parse_libvirt_xml ?conn xml =
     s_nics = nics;
    },
    disks)
+
+let parse_libvirt_domain conn guest =
+  let dom = Libvirt_utils.get_domain conn guest in
+  (* Use XmlSecure to get passwords (RHBZ#1174123). *)
+  let xml = Libvirt.Domain.get_xml_desc_flags dom [Libvirt.Domain.XmlSecure] in
+  let source, disks = parse_libvirt_xml conn xml in
+  source, disks, xml
diff --git a/v2v/parse_libvirt_xml.mli b/v2v/parse_libvirt_xml.mli
index 67a8ea96e..dfd904ea7 100644
--- a/v2v/parse_libvirt_xml.mli
+++ b/v2v/parse_libvirt_xml.mli
@@ -27,7 +27,16 @@ and parsed_source =
 | P_source_file of string            (** <source file> *)
 | P_dont_rewrite                     (** s_qemu_uri is already set. *)
 
-val parse_libvirt_xml : ?conn:string -> string -> Types.source * parsed_disk list
+val parse_libvirt_domain : Libvirt.rw Libvirt.Connect.t -> string -> Types.source * parsed_disk list * string
+(** [parse_libvirt_domain conn dom] loads the XML of the domain [dom]
+    from the libvirt connection [conn].
+    The result is a tuple with a {!Types.source} structure, a list of
+    source disks, and the XML of the guest.
+
+    {b Note} the [source.s_disks] field is an empty list.  The caller
+    must map over the parsed disks and update the [source.s_disks] field. *)
+
+val parse_libvirt_xml : Libvirt.rw Libvirt.Connect.t -> string -> Types.source * parsed_disk list
 (** Take libvirt XML and parse it into a {!Types.source} structure and a
     list of source disks.
 
-- 
2.20.1




More information about the Libguestfs mailing list