Pino Toscano
2016-Sep-20 15:07 UTC
[Libguestfs] [PATCH] libvirt: read disk paths from pools (RHBZ#1366049)
A disk of type 'volume' is stored as
<source pool='pool_name' volume='volume_name'/>
and its real location is inside the 'volume_name', as
'pool_name': in
this case, query libvirt for the actual path of the specified volume in
the specified pool.
Adjust the code so that:
- for_each_disk gets the virConnectPtr, needed to do operations with
libvirt
- when extracting the disk filename depending on the type, the code
snippet doing it can directly set 'filename', without setting an XPath
result variable
Only file-based volumes are supported for now; more types can be added
(with proper testing) later on.
---
src/libvirt-domain.c | 131 +++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 117 insertions(+), 14 deletions(-)
diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c
index d54814f..50759e3 100644
--- a/src/libvirt-domain.c
+++ b/src/libvirt-domain.c
@@ -40,8 +40,9 @@
#if defined(HAVE_LIBVIRT)
static xmlDocPtr get_domain_xml (guestfs_h *g, virDomainPtr dom);
-static ssize_t for_each_disk (guestfs_h *g, xmlDocPtr doc, int (*f) (guestfs_h
*g, const char *filename, const char *format, int readonly, const char
*protocol, char *const *server, const char *username, void *data), void *data);
+static ssize_t for_each_disk (guestfs_h *g, virConnectPtr conn, xmlDocPtr doc,
int (*f) (guestfs_h *g, const char *filename, const char *format, int readonly,
const char *protocol, char *const *server, const char *username, void *data),
void *data);
static int libvirt_selinux_label (guestfs_h *g, xmlDocPtr doc, char
**label_rtn, char **imagelabel_rtn);
+static char *filename_from_pool (guestfs_h *g, virConnectPtr conn, const char
*pool_nane, const char *volume_name);
static void
ignore_errors (void *ignore, virErrorPtr ignore2)
@@ -311,7 +312,7 @@ guestfs_impl_add_libvirt_dom (guestfs_h *g, void *domvp,
* all disks are added or none are added.
*/
ckp = guestfs_int_checkpoint_drives (g);
- r = for_each_disk (g, doc, add_disk, &data);
+ r = for_each_disk (g, virDomainGetConnect (dom), doc, add_disk, &data);
if (r == -1)
guestfs_int_rollback_drives (g, ckp);
@@ -466,6 +467,7 @@ libvirt_selinux_label (guestfs_h *g, xmlDocPtr doc,
*/
static ssize_t
for_each_disk (guestfs_h *g,
+ virConnectPtr conn,
xmlDocPtr doc,
int (*f) (guestfs_h *g,
const char *filename, const char *format,
@@ -509,6 +511,8 @@ for_each_disk (guestfs_h *g,
CLEANUP_XMLXPATHFREEOBJECT xmlXPathObjectPtr xpprotocol = NULL;
CLEANUP_XMLXPATHFREEOBJECT xmlXPathObjectPtr xphost = NULL;
CLEANUP_XMLXPATHFREEOBJECT xmlXPathObjectPtr xpusername = NULL;
+ CLEANUP_XMLXPATHFREEOBJECT xmlXPathObjectPtr xppool = NULL;
+ CLEANUP_XMLXPATHFREEOBJECT xmlXPathObjectPtr xpvolume = NULL;
xmlAttrPtr attr;
int readonly;
int t;
@@ -628,22 +632,65 @@ for_each_disk (guestfs_h *g,
* TODO: secrets: ./auth/secret/@type,
* ./auth/secret/@usage || ./auth/secret/@uuid
*/
+ } else if (STREQ (type, "volume")) { /* type =
"volume", use source/@volume */
+ CLEANUP_FREE char *pool = NULL;
+ CLEANUP_FREE char *volume = NULL;
+
+ xpathCtx->node = nodes->nodeTab[i];
+
+ /* Get the source pool. Required. */
+ xppool = xmlXPathEvalExpression (BAD_CAST "./source/@pool",
+ xpathCtx);
+ if (xppool == NULL ||
+ xppool->nodesetval == NULL ||
+ xppool->nodesetval->nodeNr == 0)
+ continue;
+ assert (xppool->nodesetval->nodeTab[0]);
+ assert (xppool->nodesetval->nodeTab[0]->type =+
XML_ATTRIBUTE_NODE);
+ attr = (xmlAttrPtr) xppool->nodesetval->nodeTab[0];
+ pool = (char *) xmlNodeListGetString (doc, attr->children, 1);
+
+ /* Get the source volume. Required. */
+ xpvolume = xmlXPathEvalExpression (BAD_CAST
"./source/@volume",
+ xpathCtx);
+ if (xpvolume == NULL ||
+ xpvolume->nodesetval == NULL ||
+ xpvolume->nodesetval->nodeNr == 0)
+ continue;
+ assert (xpvolume->nodesetval->nodeTab[0]);
+ assert (xpvolume->nodesetval->nodeTab[0]->type =+
XML_ATTRIBUTE_NODE);
+ attr = (xmlAttrPtr) xpvolume->nodesetval->nodeTab[0];
+ volume = (char *) xmlNodeListGetString (doc, attr->children, 1);
+
+ debug (g, "disk[%zu]: pool: %s; volume: %s", i, pool,
volume);
+
+ filename = filename_from_pool (g, conn, pool, volume);
+ if (filename == NULL)
+ continue; /* filename_from_pool already called error() */
} else
continue; /* type <> "file", "block", or
"network", skip it */
- assert (xpfilename);
- assert (xpfilename->nodesetval);
- if (xpfilename->nodesetval->nodeNr > 0) {
- assert (xpfilename->nodesetval->nodeTab[0]);
- assert (xpfilename->nodesetval->nodeTab[0]->type =-
XML_ATTRIBUTE_NODE);
- attr = (xmlAttrPtr) xpfilename->nodesetval->nodeTab[0];
- filename = (char *) xmlNodeListGetString (doc, attr->children, 1);
- debug (g, "disk[%zu]: filename: %s", i, filename);
+ /* Allow a disk type to directly get the filename, with no need
+ * for an XPath evaluation.
+ */
+ if (filename == NULL) {
+ assert (xpfilename);
+ assert (xpfilename->nodesetval);
+ if (xpfilename->nodesetval->nodeNr > 0) {
+ assert (xpfilename->nodesetval->nodeTab[0]);
+ assert (xpfilename->nodesetval->nodeTab[0]->type =+
XML_ATTRIBUTE_NODE);
+ attr = (xmlAttrPtr) xpfilename->nodesetval->nodeTab[0];
+ filename = (char *) xmlNodeListGetString (doc, attr->children, 1);
+ }
+ else
+ /* For network protocols (eg. nbd), name may be omitted. */
+ filename = safe_strdup (g, "");
}
- else
- /* For network protocols (eg. nbd), name may be omitted. */
- filename = safe_strdup (g, "");
+
+ debug (g, "disk[%zu]: filename: %s", i, filename);
/* Get the disk format (may not be set). */
xpathCtx->node = nodes->nodeTab[i];
@@ -784,6 +831,62 @@ get_domain_xml (guestfs_h *g, virDomainPtr dom)
return doc;
}
+static char *
+filename_from_pool (guestfs_h *g, virConnectPtr conn,
+ const char *pool_name, const char *volume_name)
+{
+ char *filename = NULL;
+ virErrorPtr err;
+ virStoragePoolPtr pool = NULL;
+ virStorageVolPtr vol = NULL;
+ virStorageVolInfo info;
+ int ret;
+
+ pool = virStoragePoolLookupByName (conn, pool_name);
+ if (pool == NULL) {
+ err = virGetLastError ();
+ error (g, _("no libvirt pool called '%s': %s"),
+ pool_name, err->message);
+ goto cleanup;
+ }
+
+ vol = virStorageVolLookupByName (pool, volume_name);
+ if (vol == NULL) {
+ err = virGetLastError ();
+ error (g, _("no volume called '%s' in the libvirt pool
'%s': %s"),
+ volume_name, pool_name, err->message);
+ goto cleanup;
+ }
+
+ ret = virStorageVolGetInfo (vol, &info);
+ if (ret < 0) {
+ err = virGetLastError ();
+ error (g, _("cannot get information of the libvirt volume
'%s': %s"),
+ volume_name, err->message);
+ goto cleanup;
+ }
+
+ debug (g, "type of libvirt volume %s: %d", volume_name, info.type);
+
+ /* Support only file-based volumes for now. */
+ if (info.type != VIR_STORAGE_VOL_FILE)
+ goto cleanup;
+
+ filename = virStorageVolGetPath (vol);
+ if (filename == NULL) {
+ err = virGetLastError ();
+ error (g, _("cannot get the filename of the libvirt volume
'%s': %s"),
+ volume_name, err->message);
+ goto cleanup;
+ }
+
+ cleanup:
+ if (vol) virStorageVolFree (vol);
+ if (pool) virStoragePoolFree (pool);
+
+ return filename;
+}
+
#else /* no libvirt at compile time */
#define NOT_IMPL(r) \
--
2.7.4
Richard W.M. Jones
2016-Sep-21 13:14 UTC
Re: [Libguestfs] [PATCH] libvirt: read disk paths from pools (RHBZ#1366049)
On Tue, Sep 20, 2016 at 05:07:23PM +0200, Pino Toscano wrote:> A disk of type 'volume' is stored as > <source pool='pool_name' volume='volume_name'/> > and its real location is inside the 'volume_name', as 'pool_name': in > this case, query libvirt for the actual path of the specified volume in > the specified pool. > > Adjust the code so that: > - for_each_disk gets the virConnectPtr, needed to do operations with > libvirt > - when extracting the disk filename depending on the type, the code > snippet doing it can directly set 'filename', without setting an XPath > result variable > > Only file-based volumes are supported for now; more types can be added > (with proper testing) later on. > --- > src/libvirt-domain.c | 131 +++++++++++++++++++++++++++++++++++++++++++++------ > 1 file changed, 117 insertions(+), 14 deletions(-) > > diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c > index d54814f..50759e3 100644 > --- a/src/libvirt-domain.c > +++ b/src/libvirt-domain.c > @@ -40,8 +40,9 @@ > #if defined(HAVE_LIBVIRT) > > static xmlDocPtr get_domain_xml (guestfs_h *g, virDomainPtr dom); > -static ssize_t for_each_disk (guestfs_h *g, xmlDocPtr doc, int (*f) (guestfs_h *g, const char *filename, const char *format, int readonly, const char *protocol, char *const *server, const char *username, void *data), void *data); > +static ssize_t for_each_disk (guestfs_h *g, virConnectPtr conn, xmlDocPtr doc, int (*f) (guestfs_h *g, const char *filename, const char *format, int readonly, const char *protocol, char *const *server, const char *username, void *data), void *data); > static int libvirt_selinux_label (guestfs_h *g, xmlDocPtr doc, char **label_rtn, char **imagelabel_rtn); > +static char *filename_from_pool (guestfs_h *g, virConnectPtr conn, const char *pool_nane, const char *volume_name); > > static void > ignore_errors (void *ignore, virErrorPtr ignore2) > @@ -311,7 +312,7 @@ guestfs_impl_add_libvirt_dom (guestfs_h *g, void *domvp, > * all disks are added or none are added. > */ > ckp = guestfs_int_checkpoint_drives (g); > - r = for_each_disk (g, doc, add_disk, &data); > + r = for_each_disk (g, virDomainGetConnect (dom), doc, add_disk, &data); > if (r == -1) > guestfs_int_rollback_drives (g, ckp); > > @@ -466,6 +467,7 @@ libvirt_selinux_label (guestfs_h *g, xmlDocPtr doc, > */ > static ssize_t > for_each_disk (guestfs_h *g, > + virConnectPtr conn, > xmlDocPtr doc, > int (*f) (guestfs_h *g, > const char *filename, const char *format, > @@ -509,6 +511,8 @@ for_each_disk (guestfs_h *g, > CLEANUP_XMLXPATHFREEOBJECT xmlXPathObjectPtr xpprotocol = NULL; > CLEANUP_XMLXPATHFREEOBJECT xmlXPathObjectPtr xphost = NULL; > CLEANUP_XMLXPATHFREEOBJECT xmlXPathObjectPtr xpusername = NULL; > + CLEANUP_XMLXPATHFREEOBJECT xmlXPathObjectPtr xppool = NULL; > + CLEANUP_XMLXPATHFREEOBJECT xmlXPathObjectPtr xpvolume = NULL; > xmlAttrPtr attr; > int readonly; > int t; > @@ -628,22 +632,65 @@ for_each_disk (guestfs_h *g, > * TODO: secrets: ./auth/secret/@type, > * ./auth/secret/@usage || ./auth/secret/@uuid > */ > + } else if (STREQ (type, "volume")) { /* type = "volume", use source/@volume */ > + CLEANUP_FREE char *pool = NULL; > + CLEANUP_FREE char *volume = NULL; > + > + xpathCtx->node = nodes->nodeTab[i]; > + > + /* Get the source pool. Required. */ > + xppool = xmlXPathEvalExpression (BAD_CAST "./source/@pool", > + xpathCtx); > + if (xppool == NULL || > + xppool->nodesetval == NULL || > + xppool->nodesetval->nodeNr == 0) > + continue; > + assert (xppool->nodesetval->nodeTab[0]); > + assert (xppool->nodesetval->nodeTab[0]->type => + XML_ATTRIBUTE_NODE); > + attr = (xmlAttrPtr) xppool->nodesetval->nodeTab[0]; > + pool = (char *) xmlNodeListGetString (doc, attr->children, 1); > + > + /* Get the source volume. Required. */ > + xpvolume = xmlXPathEvalExpression (BAD_CAST "./source/@volume", > + xpathCtx); > + if (xpvolume == NULL || > + xpvolume->nodesetval == NULL || > + xpvolume->nodesetval->nodeNr == 0) > + continue; > + assert (xpvolume->nodesetval->nodeTab[0]); > + assert (xpvolume->nodesetval->nodeTab[0]->type => + XML_ATTRIBUTE_NODE); > + attr = (xmlAttrPtr) xpvolume->nodesetval->nodeTab[0]; > + volume = (char *) xmlNodeListGetString (doc, attr->children, 1); > + > + debug (g, "disk[%zu]: pool: %s; volume: %s", i, pool, volume); > + > + filename = filename_from_pool (g, conn, pool, volume); > + if (filename == NULL) > + continue; /* filename_from_pool already called error() */ > } else > continue; /* type <> "file", "block", or "network", skip it */This comment should be amended. Probably best to say "type is not handled above, skip it" rather than enumerating every type here.> - assert (xpfilename); > - assert (xpfilename->nodesetval); > - if (xpfilename->nodesetval->nodeNr > 0) { > - assert (xpfilename->nodesetval->nodeTab[0]); > - assert (xpfilename->nodesetval->nodeTab[0]->type => - XML_ATTRIBUTE_NODE); > - attr = (xmlAttrPtr) xpfilename->nodesetval->nodeTab[0]; > - filename = (char *) xmlNodeListGetString (doc, attr->children, 1); > - debug (g, "disk[%zu]: filename: %s", i, filename); > + /* Allow a disk type to directly get the filename, with no need > + * for an XPath evaluation. > + */I didn't understand this hunk until I parsed the comment to mean that the previous code (for type == "volume") was setting the filename. Perhaps the comment should include the magic words "in the code above"?> + if (filename == NULL) { > + assert (xpfilename); > + assert (xpfilename->nodesetval); > + if (xpfilename->nodesetval->nodeNr > 0) { > + assert (xpfilename->nodesetval->nodeTab[0]); > + assert (xpfilename->nodesetval->nodeTab[0]->type => + XML_ATTRIBUTE_NODE); > + attr = (xmlAttrPtr) xpfilename->nodesetval->nodeTab[0]; > + filename = (char *) xmlNodeListGetString (doc, attr->children, 1); > + } > + else > + /* For network protocols (eg. nbd), name may be omitted. */ > + filename = safe_strdup (g, ""); > } > - else > - /* For network protocols (eg. nbd), name may be omitted. */ > - filename = safe_strdup (g, ""); > + > + debug (g, "disk[%zu]: filename: %s", i, filename); > /* Get the disk format (may not be set). */ > xpathCtx->node = nodes->nodeTab[i]; > @@ -784,6 +831,62 @@ get_domain_xml (guestfs_h *g, virDomainPtr dom) > return doc; > } > > +static char * > +filename_from_pool (guestfs_h *g, virConnectPtr conn, > + const char *pool_name, const char *volume_name) > +{ > + char *filename = NULL; > + virErrorPtr err; > + virStoragePoolPtr pool = NULL; > + virStorageVolPtr vol = NULL; > + virStorageVolInfo info; > + int ret; > + > + pool = virStoragePoolLookupByName (conn, pool_name); > + if (pool == NULL) { > + err = virGetLastError (); > + error (g, _("no libvirt pool called '%s': %s"), > + pool_name, err->message); > + goto cleanup; > + } > + > + vol = virStorageVolLookupByName (pool, volume_name); > + if (vol == NULL) { > + err = virGetLastError (); > + error (g, _("no volume called '%s' in the libvirt pool '%s': %s"), > + volume_name, pool_name, err->message); > + goto cleanup; > + } > + > + ret = virStorageVolGetInfo (vol, &info); > + if (ret < 0) { > + err = virGetLastError (); > + error (g, _("cannot get information of the libvirt volume '%s': %s"), > + volume_name, err->message); > + goto cleanup; > + } > + > + debug (g, "type of libvirt volume %s: %d", volume_name, info.type); > + > + /* Support only file-based volumes for now. */ > + if (info.type != VIR_STORAGE_VOL_FILE) > + goto cleanup; > + > + filename = virStorageVolGetPath (vol); > + if (filename == NULL) { > + err = virGetLastError (); > + error (g, _("cannot get the filename of the libvirt volume '%s': %s"), > + volume_name, err->message); > + goto cleanup; > + } > + > + cleanup: > + if (vol) virStorageVolFree (vol); > + if (pool) virStoragePoolFree (pool); > + > + return filename; > +} > + > #else /* no libvirt at compile time */It looks OK although I didn't test it. Can we add a test? There is already a test for the corresponding virt-v2v functionality (v2v/test-v2v-o-libvirt.sh). Just make sure that the pool name is chosen randomly (see commit 2efd8d0b1da). Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com Fedora Windows cross-compiler. Compile Windows programs, test, and build Windows installers. Over 100 libraries supported. http://fedoraproject.org/wiki/MinGW
Apparently Analagous Threads
- [PATCH v2] libvirt: read disk paths from pools (RHBZ#1366049)
- [PATCH 1/2] libvirt: un-duplicate XPath code
- [PATCH 0/7] Various fixes for Ceph drives and parsing libvirt XML.
- [PATCH 0/5] rbd improvements
- [PATCH 0/3] lib: Don't assert fail if port is missing in XML (RHBZ#1370424).