[Libguestfs] [PATCH 5/7] New APIs: add-domain and add-libvirt-dom.
Daniel P. Berrange
berrange at redhat.com
Wed Nov 10 13:17:54 UTC 2010
On Wed, Nov 10, 2010 at 11:46:31AM +0000, Richard W.M. Jones wrote:
> diff --git a/perl/typemap b/perl/typemap
> index d978e60..223aee1 100644
> --- a/perl/typemap
> +++ b/perl/typemap
> @@ -3,6 +3,7 @@ char * T_PV
> const char * T_PV
> guestfs_h * O_OBJECT_guestfs_h
> int64_t T_IV
> +virDomainPtr O_OBJECT_domain
>
> INPUT
> O_OBJECT_guestfs_h
> @@ -18,6 +19,16 @@ O_OBJECT_guestfs_h
> croak (\"${Package}::$func_name(): $var is not a blessed HV reference\");
> }
>
> +# This comes from the Sys::Virt bindings.
> +INPUT
> +O_OBJECT_domain
> + if (sv_isobject ($arg) && (SvTYPE (SvRV ($arg)) == SVt_PVMG))
> + $var = ($type)SvIV ((SV*) SvRV ($arg));
> + else {
> + warn(\"${Package}::$func_name() -- $var is not a blessed SV reference\");
> + XSRETURN_UNDEF;
> + }
I haven't been considering this Sys::Virt type mapping to be
part of the stable ABI/API, just an internal impl details. I'm
wondering how other Perl XS modules allow extension, without
exposing their internal typedef implementation detail.
> diff --git a/src/virt.c b/src/virt.c
> new file mode 100644
> index 0000000..5816f61
> --- /dev/null
> +++ b/src/virt.c
> +
> +int
> +guestfs__add_domain (guestfs_h *g, const char *domain_name,
> + const struct guestfs_add_domain_argv *optargs)
> +{
> + virErrorPtr err;
> + virConnectPtr conn = NULL;
> + virDomainPtr dom = NULL;
> + int r = -1;
> + const char *libvirturi;
> + int readonly;
> + const char *iface;
> + struct guestfs_add_libvirt_dom_argv optargs2 = { .bitmask = 0 };
> +
> + libvirturi = optargs->bitmask & GUESTFS_ADD_DOMAIN_LIBVIRTURI_BITMASK
> + ? optargs->libvirturi : NULL;
> + readonly = optargs->bitmask & GUESTFS_ADD_DOMAIN_READONLY_BITMASK
> + ? optargs->readonly : 0;
> + iface = optargs->bitmask & GUESTFS_ADD_DOMAIN_IFACE_BITMASK
> + ? optargs->iface : NULL;
> +
> + /* Connect to libvirt, find the domain. */
> + conn = virConnectOpenReadOnly (libvirturi);
> + if (!conn) {
> + err = virGetLastError ();
> + error (g, _("could not connect to libvirt (code %d, domain %d): %s"),
> + err->code, err->domain, err->message);
> + goto cleanup;
> + }
> +
> + dom = virDomainLookupByName (conn, domain_name);
> + if (!dom) {
> + err = virConnGetLastError (conn);
NB, virConnGetLastError() is deprecated because it isn't threadsafe.
Instead use virGetLastError() in all places.
> + error (g, _("no libvirt domain called '%s': %s"),
> + domain_name, err->message);
> + goto cleanup;
> + }
> +
> + if (readonly) {
> + optargs2.bitmask |= GUESTFS_ADD_LIBVIRT_DOM_READONLY_BITMASK;
> + optargs2.readonly = readonly;
> + }
> + if (iface) {
> + optargs2.bitmask |= GUESTFS_ADD_LIBVIRT_DOM_IFACE_BITMASK;
> + optargs2.iface = iface;
> + }
> +
> + r = guestfs__add_libvirt_dom (g, dom, &optargs2);
> +
> + cleanup:
> + if (dom) virDomainFree (dom);
> + if (conn) virConnectClose (conn);
> +
> + return r;
> +}
> +
> +int
> +guestfs__add_libvirt_dom (guestfs_h *g, virDomainPtr dom,
> + const struct guestfs_add_libvirt_dom_argv *optargs)
> +{
> + int r = -1, nr_added = 0, i;
> + virErrorPtr err;
> + virConnectPtr conn = virDomainGetConnect (dom);
> + xmlDocPtr doc = NULL;
> + xmlXPathContextPtr xpathCtx = NULL;
> + xmlXPathObjectPtr xpathObj = NULL;
> + char *xml = NULL;
> + int readonly;
> + const char *iface;
> + int cmdline_pos;
> +
> + cmdline_pos = guestfs___checkpoint_cmdline (g);
> +
> + readonly = optargs->bitmask & GUESTFS_ADD_LIBVIRT_DOM_READONLY_BITMASK
> + ? optargs->readonly : 0;
> + iface = optargs->bitmask & GUESTFS_ADD_LIBVIRT_DOM_IFACE_BITMASK
> + ? optargs->iface : NULL;
> +
> + if (!readonly) {
> + virDomainInfo info;
> + if (virDomainGetInfo (dom, &info) == -1) {
> + err = virConnGetLastError (conn);
> + error (g, _("error getting domain info: %s"), err->message);
> + goto cleanup;
> + }
> + if (info.state != VIR_DOMAIN_SHUTOFF) {
> + error (g, _("error: domain is a live virtual machine.\nYou must use readonly access because write access to a running virtual machine\ncan cause disk corruption."));
> + goto cleanup;
> + }
> + }
> +
> + /* Domain XML. */
> + xml = virDomainGetXMLDesc (dom, 0);
> +
> + if (!xml) {
> + err = virConnGetLastError (conn);
> + error (g, _("error reading libvirt XML information: %s"),
> + err->message);
> + goto cleanup;
> + }
> +
> + /* Now the horrible task of parsing out the fields we need from the XML.
> + * http://www.xmlsoft.org/examples/xpath1.c
> + */
> + doc = xmlParseMemory (xml, strlen (xml));
> + if (doc == NULL) {
> + error (g, _("unable to parse XML information returned by libvirt"));
> + goto cleanup;
> + }
> +
> + xpathCtx = xmlXPathNewContext (doc);
> + if (xpathCtx == NULL) {
> + error (g, _("unable to create new XPath context"));
> + goto cleanup;
> + }
> +
> + /* This gives us a set of all the <disk> nodes. */
> + xpathObj = xmlXPathEvalExpression (BAD_CAST "//devices/disk", xpathCtx);
> + if (xpathObj == NULL) {
> + error (g, _("unable to evaluate XPath expression"));
> + goto cleanup;
> + }
> +
> + xmlNodeSetPtr nodes = xpathObj->nodesetval;
> + for (i = 0; i < nodes->nodeNr; ++i) {
> + xmlXPathObjectPtr xpfilename;
> + xmlXPathObjectPtr xpformat;
> +
> + /* Change the context to the current <disk> node.
> + * DV advises to reset this before each search since older versions of
> + * libxml2 might overwrite it.
> + */
> + xpathCtx->node = nodes->nodeTab[i];
> +
> + /* Filename can be in <source dev=..> or <source file=..> attribute. */
> + xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@dev", xpathCtx);
> + if (xpfilename == NULL ||
> + xpfilename->nodesetval == NULL ||
> + xpfilename->nodesetval->nodeNr == 0) {
> + xmlXPathFreeObject (xpfilename);
> + xpathCtx->node = nodes->nodeTab[i];
> + xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@file", xpathCtx);
> + if (xpfilename == NULL ||
> + xpfilename->nodesetval == NULL ||
> + xpfilename->nodesetval->nodeNr == 0) {
> + xmlXPathFreeObject (xpfilename);
> + continue; /* disk filename not found, skip this */
> + }
> + }
Rather than checking for @dev, and then checking for @file, it is safer
to fetch @type, and then if type=='file' use @file and if type=='block'
use @dev. This protects against addition of future types, which may
libguestfs may not be able to treat as simple paths on a local disk.
> +
> + assert (xpfilename->nodesetval->nodeTab[0]);
> + assert (xpfilename->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
> + xmlAttrPtr attr = (xmlAttrPtr) xpfilename->nodesetval->nodeTab[0];
> + char *filename = (char *) xmlNodeListGetString (doc, attr->children, 1);
> +
> + /* Get the disk format (may not be set). */
> + xpathCtx->node = nodes->nodeTab[i];
> + xpformat = xmlXPathEvalExpression (BAD_CAST "./driver/@type", xpathCtx);
> + char *format = NULL;
> + if (xpformat != NULL &&
> + xpformat->nodesetval &&
> + xpformat->nodesetval->nodeNr > 0) {
> + assert (xpformat->nodesetval->nodeTab[0]);
> + assert (xpformat->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
> + attr = (xmlAttrPtr) xpformat->nodesetval->nodeTab[0];
> + format = (char *) xmlNodeListGetString (doc, attr->children, 1);
> + }
Regards,
Daniel
--
|: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :|
|: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
More information about the Libguestfs
mailing list