From 8dc45325a6b7790896a43f41db5aca376aee2bfc Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 26 Aug 2014 17:31:08 +0100 Subject: [PATCH] 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. --- po/POTFILES | 1 + po/POTFILES-ml | 1 + v2v/Makefile.am | 8 ++- v2v/domainxml-c.c | 125 +++++++++++++++++++++++++++++++++++++++++++ v2v/domainxml.ml | 21 ++++++++ v2v/domainxml.mli | 28 ++++++++++ v2v/input_libvirt.ml | 7 +-- v2v/link.sh.in | 2 +- 8 files changed, 184 insertions(+), 9 deletions(-) create mode 100644 v2v/domainxml-c.c create mode 100644 v2v/domainxml.ml create mode 100644 v2v/domainxml.mli diff --git a/po/POTFILES b/po/POTFILES index 254f41923..3d208d503 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -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 diff --git a/po/POTFILES-ml b/po/POTFILES-ml index 2001f4784..7786569a7 100644 --- a/po/POTFILES-ml +++ b/po/POTFILES-ml @@ -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 diff --git a/v2v/Makefile.am b/v2v/Makefile.am index a336ee892..049d9df7d 100644 --- a/v2v/Makefile.am +++ b/v2v/Makefile.am @@ -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 \ diff --git a/v2v/domainxml-c.c b/v2v/domainxml-c.c new file mode 100644 index 000000000..fc2993d3f --- /dev/null +++ b/v2v/domainxml-c.c @@ -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 + +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef HAVE_LIBVIRT +#include +#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 */ diff --git a/v2v/domainxml.ml b/v2v/domainxml.ml new file mode 100644 index 000000000..03994f930 --- /dev/null +++ b/v2v/domainxml.ml @@ -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" diff --git a/v2v/domainxml.mli b/v2v/domainxml.mli new file mode 100644 index 000000000..6d58981e9 --- /dev/null +++ b/v2v/domainxml.mli @@ -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. *) diff --git a/v2v/input_libvirt.ml b/v2v/input_libvirt.ml index ffe0c7c09..d409cd958 100644 --- a/v2v/input_libvirt.ml +++ b/v2v/input_libvirt.ml @@ -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 diff --git a/v2v/link.sh.in b/v2v/link.sh.in index f90901551..a226947b5 100644 --- a/v2v/link.sh.in +++ b/v2v/link.sh.in @@ -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'