Richard W.M. Jones
2015-May-05 18:06 UTC
[Libguestfs] [PATCH 0/2] v2v: -o libvirt: Check if the domain exists on the target (RHBZ#889082).
https://bugzilla.redhat.com/show_bug.cgi?id=889082
Richard W.M. Jones
2015-May-05 18:06 UTC
[Libguestfs] [PATCH 1/2] v2v: Add a C function to fetch libvirt hypervisor capabilities.
Instead of having to run external 'virsh capabilities' command and parsing the output. --- v2v/domainxml-c.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++- v2v/domainxml.ml | 5 +++- v2v/domainxml.mli | 7 +++++- v2v/output_libvirt.ml | 9 +------- 4 files changed, 74 insertions(+), 11 deletions(-) diff --git a/v2v/domainxml-c.c b/v2v/domainxml-c.c index abf39f1..27b1389 100644 --- a/v2v/domainxml-c.c +++ b/v2v/domainxml-c.c @@ -16,7 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -/* [virsh dumpxml] but with non-broken authentication handling. */ +/* This module implements various [virsh]-like commands, but with + * non-broken authentication handling. + */ #include <config.h> @@ -360,6 +362,60 @@ v2v_vol_dumpxml (value connv, value poolnamev, value volnamev) 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[256]; + 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); + + CAMLreturn (capabilitiesv); +} + #else /* !HAVE_LIBVIRT */ value v2v_dumpxml (value connv, value domv) __attribute__((noreturn)); @@ -386,4 +442,10 @@ v2v_vol_dumpxml (value connv, value poolnamev, value volnamev) caml_invalid_argument ("virt-v2v was compiled without libvirt support"); } +value +v2v_capabilities (value connv, value unitv) +{ + caml_invalid_argument ("virt-v2v was compiled without libvirt support"); +} + #endif /* !HAVE_LIBVIRT */ diff --git a/v2v/domainxml.ml b/v2v/domainxml.ml index 7dbfcbd..2ac304b 100644 --- a/v2v/domainxml.ml +++ b/v2v/domainxml.ml @@ -16,8 +16,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -(* [virsh dumpxml] but with non-broken authentication handling. *) +(* This module implements various [virsh]-like commands, but with + non-broken authentication handling. *) external dumpxml : ?password:string -> ?conn:string -> string -> string = "v2v_dumpxml" external pool_dumpxml : ?conn:string -> string -> string = "v2v_pool_dumpxml" external vol_dumpxml : ?conn:string -> string -> string -> string = "v2v_vol_dumpxml" + +external capabilities : ?conn:string -> unit -> string = "v2v_capabilities" diff --git a/v2v/domainxml.mli b/v2v/domainxml.mli index 7008769..11cf48e 100644 --- a/v2v/domainxml.mli +++ b/v2v/domainxml.mli @@ -16,7 +16,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -(** [virsh dumpxml] but with non-broken authentication handling. +(** This module implements various [virsh]-like commands, 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 @@ -38,3 +39,7 @@ val vol_dumpxml : ?conn:string -> string -> string -> string 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 capabilities : ?conn:string -> unit -> string +(** [capabilities ?conn ()] returns the libvirt capabilities XML. + The optional [?conn] parameter is the libvirt connection URI. *) diff --git a/v2v/output_libvirt.ml b/v2v/output_libvirt.ml index 0ed7a61..8e2337d 100644 --- a/v2v/output_libvirt.ml +++ b/v2v/output_libvirt.ml @@ -319,14 +319,7 @@ class output_libvirt verbose oc output_pool = object method prepare_targets source targets (* Get the capabilities from libvirt. *) - let cmd - match oc with - | None -> "virsh capabilities" - | Some uri -> sprintf "virsh -c %s capabilities" (quote uri) in - if verbose then printf "%s\n%!" cmd; - let xml = external_command ~prog cmd in - let xml = String.concat "\n" xml in - + let xml = Domainxml.capabilities ?conn:oc () in if verbose then printf "libvirt capabilities XML:\n%s\n%!" xml; (* This just checks that the capabilities XML is well-formed, -- 2.3.1
Richard W.M. Jones
2015-May-05 18:06 UTC
[Libguestfs] [PATCH 2/2] v2v: -o libvirt: Check if the domain exists on the target (RHBZ#889082).
--- v2v/domainxml-c.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++ v2v/domainxml.ml | 2 ++ v2v/domainxml.mli | 6 +++++ v2v/output_libvirt.ml | 5 ++++ 4 files changed, 78 insertions(+) diff --git a/v2v/domainxml-c.c b/v2v/domainxml-c.c index 27b1389..865b18b 100644 --- a/v2v/domainxml-c.c +++ b/v2v/domainxml-c.c @@ -416,6 +416,71 @@ v2v_capabilities (value connv, value unitv) 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[256]; + 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)); +} + #else /* !HAVE_LIBVIRT */ value v2v_dumpxml (value connv, value domv) __attribute__((noreturn)); diff --git a/v2v/domainxml.ml b/v2v/domainxml.ml index 2ac304b..a12391f 100644 --- a/v2v/domainxml.ml +++ b/v2v/domainxml.ml @@ -24,3 +24,5 @@ external pool_dumpxml : ?conn:string -> string -> string = "v2v_pool_dumpxml" external vol_dumpxml : ?conn:string -> string -> string -> string = "v2v_vol_dumpxml" external capabilities : ?conn:string -> unit -> string = "v2v_capabilities" + +external domain_exists : ?conn:string -> string -> bool = "v2v_domain_exists" diff --git a/v2v/domainxml.mli b/v2v/domainxml.mli index 11cf48e..ccbb8c8 100644 --- a/v2v/domainxml.mli +++ b/v2v/domainxml.mli @@ -43,3 +43,9 @@ val vol_dumpxml : ?conn:string -> string -> string -> string 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. + [dom] may be a guest name, but not a UUID. *) diff --git a/v2v/output_libvirt.ml b/v2v/output_libvirt.ml index 8e2337d..9a294e4 100644 --- a/v2v/output_libvirt.ml +++ b/v2v/output_libvirt.ml @@ -318,6 +318,11 @@ class output_libvirt verbose oc output_pool = object method supported_firmware = [ TargetBIOS; TargetUEFI ] method prepare_targets source targets + (* Does the domain already exist on the target? (RHBZ#889082) *) + if Domainxml.domain_exists ?conn:oc source.s_name then + error (f_"a libvirt domain called '%s' already exists on the target.\n\nUse the '-on' option for virt-v2v to use a different name, or delete the existing domain on the target using the 'virsh undefine' command.") + source.s_name; + (* Get the capabilities from libvirt. *) let xml = Domainxml.capabilities ?conn:oc () in if verbose then printf "libvirt capabilities XML:\n%s\n%!" xml; -- 2.3.1