v2v: Work around buggy `virsh dumpxml'.

The `virsh dumpxml' command doesn't work properly when the libvirt
source requires authentication.  This is because the authentication
prompts are sent to stdout, but stdout is also the place where we are
reading the output XML from.

Add a mini-binding to libvirt virDomainGetXMLDesc which avoids this,
getting the XML directly using the libvirt API.  Change existing
external calls to `virsh dumpxml' to use this API instead.
This commit is contained in:
Richard W.M. Jones
2014-08-26 17:31:08 +01:00
parent 53999bddf4
commit 8dc45325a6
8 changed files with 184 additions and 9 deletions

View File

@@ -331,5 +331,6 @@ src/test-utils.c
src/tmpdirs.c
src/utils.c
test-tool/test-tool.c
v2v/domainxml-c.c
v2v/utils-c.c
v2v/xml-c.c

View File

@@ -86,6 +86,7 @@ v2v/DOM.ml
v2v/cmdline.ml
v2v/convert_linux.ml
v2v/convert_windows.ml
v2v/domainxml.ml
v2v/input_disk.ml
v2v/input_libvirt.ml
v2v/lib_esx.ml

View File

@@ -30,6 +30,7 @@ SOURCES_MLI = \
convert_linux.mli \
convert_windows.mli \
DOM.mli \
domainxml.mli \
lib_esx.mli \
lib_linux.mli \
input_disk.mli \
@@ -45,6 +46,7 @@ SOURCES_ML = \
types.ml \
utils.ml \
xml.ml \
domainxml.ml \
DOM.ml \
lib_esx.ml \
lib_linux.ml \
@@ -65,7 +67,8 @@ SOURCES_C = \
$(top_builddir)/mllib/mkdtemp-c.c \
$(top_builddir)/customize/crypt-c.c \
utils-c.c \
xml-c.c
xml-c.c \
domainxml-c.c
if HAVE_OCAML
@@ -80,7 +83,8 @@ virt_v2v_CPPFLAGS = \
-I$(top_srcdir)/fish
virt_v2v_CFLAGS = \
$(WARN_CFLAGS) $(WERROR_CFLAGS) \
$(LIBXML2_CFLAGS)
$(LIBXML2_CFLAGS) \
$(LIBVIRT_CFLAGS)
BOBJECTS = \
$(top_builddir)/mllib/common_gettext.cmo \

125
v2v/domainxml-c.c Normal file
View File

@@ -0,0 +1,125 @@
/* virt-v2v
* Copyright (C) 2009-2014 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.
*/
/* [virsh dumpxml] but with non-broken authentication handling. */
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <caml/alloc.h>
#include <caml/fail.h>
#include <caml/memory.h>
#include <caml/mlvalues.h>
#ifdef HAVE_LIBVIRT
#include <libvirt/libvirt.h>
#endif
#include "guestfs.h"
#include "guestfs-internal-frontend.h"
#pragma GCC diagnostic ignored "-Wmissing-prototypes"
#ifdef HAVE_LIBVIRT
static void raise_error (const char *fs, ...)
__attribute__((noreturn))
__attribute__((format (printf,1,2)));
/* Note we rely on libvirt printing errors to stderr, so the exception
* doesn't need to contain the actual error from libvirt.
*/
static void
raise_error (const char *fs, ...)
{
va_list args;
/* We have to assemble the error on the stack because a dynamic
* string couldn't be freed.
*/
char msg[256];
int len;
va_start (args, fs);
len = vsnprintf (msg, sizeof msg, fs , args);
va_end (args);
if (len < 0) caml_failwith (fs);
caml_invalid_argument (msg);
}
value
v2v_dumpxml (value connv, value domnamev)
{
CAMLparam2 (connv, domnamev);
CAMLlocal1 (retv);
const char *conn_uri = NULL;
const char *domname;
virConnectPtr conn;
virDomainPtr dom;
char *xml;
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)
raise_error ("cannot open libvirt connection '%s'", conn_uri);
else
raise_error ("cannot open libvirt connection");
}
/* Look up the domain. */
domname = String_val (domnamev);
dom = virDomainLookupByName (conn, domname);
if (!dom) {
virConnectClose (conn);
raise_error ("cannot find libvirt domain '%s'", domname);
}
xml = virDomainGetXMLDesc (dom, 0);
virDomainFree (dom);
virConnectClose (conn);
if (xml == NULL)
raise_error ("cannot fetch XML description of guest '%s'", domname);
retv = caml_copy_string (xml);
free (xml);
CAMLreturn (retv);
}
#else /* !HAVE_LIBVIRT */
value
v2v_dumpxml (value connv, value domv)
{
caml_invalid_argument ("virt-v2v was compiled without libvirt support");
}
#endif /* !HAVE_LIBVIRT */

21
v2v/domainxml.ml Normal file
View File

@@ -0,0 +1,21 @@
(* virt-v2v
* Copyright (C) 2009-2014 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.
*)
(* [virsh dumpxml] but with non-broken authentication handling. *)
external dumpxml : ?conn:string -> string -> string = "v2v_dumpxml"

28
v2v/domainxml.mli Normal file
View File

@@ -0,0 +1,28 @@
(* virt-v2v
* Copyright (C) 2009-2014 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.
*)
(** [virsh dumpxml] but with non-broken authentication handling.
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 dumpxml : ?conn:string -> string -> string
(** [dumpxml ?conn dom] returns the libvirt XML of domain [dom].
The optional [?conn] parameter is the libvirt connection URI. *)

View File

@@ -311,11 +311,6 @@ let input_libvirt verbose libvirt_uri guest =
None, None in
(* Get the libvirt XML. *)
let cmd =
match libvirt_uri with
| None -> sprintf "virsh dumpxml %s" (quote guest)
| Some uri -> sprintf "virsh -c %s dumpxml %s" (quote uri) (quote guest) in
let lines = external_command ~prog cmd in
let xml = String.concat "\n" lines in
let xml = Domainxml.dumpxml ?conn:libvirt_uri guest in
new input_libvirt verbose options ?map_source_file ?map_source_dev xml

View File

@@ -19,4 +19,4 @@
# Hack automake to link binary properly. There is no other way to add
# the -cclib parameter to the end of the command line.
exec "$@" -linkpkg -cclib '-lutils -lncurses -lcrypt @LIBXML2_LIBS@ -lgnu'
exec "$@" -linkpkg -cclib '-lutils -lncurses -lcrypt @LIBVIRT_LIBS@ @LIBXML2_LIBS@ -lgnu'