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/input_libvirt.ml | 22 +-
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/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 | 23 +-
v2v/parse_libvirt_xml.mli | 11 +-
19 files changed, 208 insertions(+), 624 deletions(-)
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 52b61bbe3..b5761ef46 100644
--- a/v2v/Makefile.am
+++ b/v2v/Makefile.am
@@ -163,7 +163,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.
@@ -212,6 +211,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
@@ -242,6 +242,7 @@ OCAMLLINKFLAGS = \
mlxml.$(MLARCHIVE) \
mlcutils.$(MLARCHIVE) \
mltools.$(MLARCHIVE) \
+ mllibvirt.$(MLARCHIVE) \
$(LINK_CUSTOM_OCAMLC_ONLY)
virt_v2v_DEPENDENCIES = $(OBJECTS) $(top_srcdir)/ocaml-link.sh
@@ -251,7 +252,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) \
@@ -284,6 +285,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/input_libvirt.ml b/v2v/input_libvirt.ml
index 0e4afa157..a7d1706de 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 libvirt_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 libvirt_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 libvirt_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
+ libvirt_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
- vddk_options parsed_uri guest
+ Input_libvirt_vddk.input_libvirt_vddk
+ libvirt_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
+ libvirt_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 libvirt_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..5ff3cfc43 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 libvirt_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 libvirt_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 libvirt_conn guest =
+object (self)
+ inherit input_libvirt libvirt_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..c7a00210e 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
+ libvirt_conn input_password parsed_uri server guest =
+object (self)
+ inherit input_libvirt libvirt_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 e2efef842..d35148be7 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 libvirt_conn input_conn input_password vddk_options
+ parsed_uri guest =
(* The VDDK path. *)
let libdir =
try Some (List.assoc "libdir" vddk_options)
@@ -194,8 +194,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 libvirt_conn guest as super
method precheck () =
error_unless_vddk_libdir ();
@@ -215,12 +215,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..2fc6e9cf8 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 libvirt_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..ab6d933bc 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 libvirt_conn parsed_uri server guest =
+object (self)
+ inherit input_libvirt libvirt_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/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 14cd82afd..95273c89c 100644
--- a/v2v/parse_libvirt_xml.ml
+++ b/v2v/parse_libvirt_xml.ml
@@ -74,6 +74,17 @@ let create_curl_qemu_uri driver host port path =
let parse_libvirt_xml ?conn xml =
debug "libvirt xml is:\n%s" xml;
+ (* Create a default libvirt connection on request, to not open one
+ * in case there is no need to fetch more data (for example inspect
+ * the storage pools).
+ *)
+ let libvirt_conn =
+ lazy (Libvirt.Connect.connect ()) in
+ let get_conn () =
+ match conn with
+ | None -> Lazy.force libvirt_conn
+ | Some conn -> conn in
+
let doc = Xml.parse_memory xml in
let xpathctx = Xml.xpath_new_context doc in
let xpath_string = xpath_string xpathctx
@@ -333,7 +344,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 (get_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
@@ -536,3 +550,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..2d81e0d99 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 : ?conn: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.21.0