[libvirt] [PATCH 0/7] virStorageVolCreateXMLFrom API

Hi all, The following patches implement a new API call: virStorageVolCreateXMLFrom. virStorageVolPtr virStorageVolCreateXMLFrom(virStoragePoolPtr pool, const char *xmldesc, unsigned int flags, virStorageVolPtr clonevol) Arguments function similar to the regular volume create API, except the parameter 'clonevol' is used as the input source. The API allows for volume cloning, possibly across pools, or within the same pool but converting volume formats. Tests I performed (and ensured that a VM booted without complaints from the new image): Raw -> Raw (with successful sparse detection) Raw -> QCOW2 QCOW2 -> Raw QCOW2 -> QCOW2 QCOW2 -> VMDK Raw partition ('disk' volume) -> Raw file Thanks, Cole Cole Robinson (7): Add public API stubs for virStorageVolCreateXMLFrom Remote driver plumbing for virStorageVolCreateXMLFrom Test driver implementation of virStorageVolCreateXMLFrom Virsh commands vol-clone and vol-create-from Storage driver implementation for CreateXMLFrom Break out FS volume build routines to their own functions. VolumeCreateXMLFrom FS storage backend implementation. include/libvirt/libvirt.h | 4 + include/libvirt/libvirt.h.in | 4 + qemud/remote.c | 34 +++ qemud/remote_dispatch_args.h | 1 + qemud/remote_dispatch_prototypes.h | 7 + qemud/remote_dispatch_ret.h | 1 + qemud/remote_dispatch_table.h | 5 + qemud/remote_protocol.c | 24 ++ qemud/remote_protocol.h | 18 ++ qemud/remote_protocol.x | 15 +- src/driver.h | 6 + src/libvirt.c | 61 +++++- src/libvirt_public.syms | 5 + src/remote_internal.c | 33 +++ src/storage_backend.h | 2 + src/storage_backend_fs.c | 491 +++++++++++++++++++++++++----------- src/storage_driver.c | 153 +++++++++++- src/test.c | 95 +++++++ src/virsh.c | 177 +++++++++++++- 19 files changed, 985 insertions(+), 151 deletions(-)

Signed-off-by: Cole Robinson <crobinso@redhat.com> --- include/libvirt/libvirt.h | 4 +++ include/libvirt/libvirt.h.in | 4 +++ src/driver.h | 6 ++++ src/libvirt.c | 61 ++++++++++++++++++++++++++++++++++++++++- src/libvirt_public.syms | 5 +++ 5 files changed, 78 insertions(+), 2 deletions(-) diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h index 30f559d..b1e45e4 100644 --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -1047,6 +1047,10 @@ const char* virStorageVolGetKey (virStorageVolPtr vol); virStorageVolPtr virStorageVolCreateXML (virStoragePoolPtr pool, const char *xmldesc, unsigned int flags); +virStorageVolPtr virStorageVolCreateXMLFrom (virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags, + virStorageVolPtr clonevol); int virStorageVolDelete (virStorageVolPtr vol, unsigned int flags); int virStorageVolRef (virStorageVolPtr vol); diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 2f7076f..f5cadb4 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1047,6 +1047,10 @@ const char* virStorageVolGetKey (virStorageVolPtr vol); virStorageVolPtr virStorageVolCreateXML (virStoragePoolPtr pool, const char *xmldesc, unsigned int flags); +virStorageVolPtr virStorageVolCreateXMLFrom (virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags, + virStorageVolPtr clonevol); int virStorageVolDelete (virStorageVolPtr vol, unsigned int flags); int virStorageVolRef (virStorageVolPtr vol); diff --git a/src/driver.h b/src/driver.h index c357b76..ff12ada 100644 --- a/src/driver.h +++ b/src/driver.h @@ -586,6 +586,11 @@ typedef char * typedef char * (*virDrvStorageVolGetPath) (virStorageVolPtr vol); +typedef virStorageVolPtr + (*virDrvStorageVolCreateXMLFrom) (virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags, + virStorageVolPtr clone); typedef struct _virStorageDriver virStorageDriver; @@ -633,6 +638,7 @@ struct _virStorageDriver { virDrvStorageVolLookupByKey volLookupByKey; virDrvStorageVolLookupByPath volLookupByPath; virDrvStorageVolCreateXML volCreateXML; + virDrvStorageVolCreateXMLFrom volCreateXMLFrom; virDrvStorageVolDelete volDelete; virDrvStorageVolGetInfo volGetInfo; virDrvStorageVolGetXMLDesc volGetXMLDesc; diff --git a/src/libvirt.c b/src/libvirt.c index ded18a7..5a51c45 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -6767,6 +6767,65 @@ error: /** + * virStorageVolCreateXMLFrom: + * @pool: pointer to parent pool for the new volume + * @xmldesc: description of volume to create + * @flags: flags for creation (unused, pass 0) + * @clonevol: storage volume to use as input + * + * Create a storage volume in the parent pool, using the + * 'clonevol' volume as input. Information for the new + * volume (name, perms) are passed via a typical volume + * XML description. + * + * return the storage volume, or NULL on error + */ +virStorageVolPtr +virStorageVolCreateXMLFrom(virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags, + virStorageVolPtr clonevol) +{ + DEBUG("pool=%p, flags=%u, clonevol=%p", pool, flags, clonevol); + + virResetLastError(); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibConnError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (NULL); + } + + if (!VIR_IS_STORAGE_VOL(clonevol)) { + virLibConnError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return (NULL); + } + + if (pool->conn->flags & VIR_CONNECT_RO || + clonevol->conn->flags & VIR_CONNECT_RO) { + virLibConnError(pool->conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (pool->conn->storageDriver && + pool->conn->storageDriver->volCreateXMLFrom) { + virStorageVolPtr ret; + ret = pool->conn->storageDriver->volCreateXMLFrom (pool, xmldesc, + flags, clonevol); + if (!ret) + goto error; + return ret; + } + + virLibConnError (pool->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(pool->conn); + return NULL; +} + + +/** * virStorageVolDelete: * @vol: pointer to storage volume * @flags: future flags, use 0 for now @@ -7009,8 +7068,6 @@ error: } - - /** * virNodeNumOfDevices: * @conn: pointer to the hypervisor connection diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index b8f9128..0ea130f 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -265,3 +265,8 @@ LIBVIRT_0.6.3 { } LIBVIRT_0.6.1; # .... define new API here using predicted next version number .... + +LIBVIRT_0.6.4 { + global: + virStorageVolCreateXMLFrom; +} LIBVIRT_0.6.3; -- 1.6.0.6

On Mon, May 04, 2009 at 01:42:56PM -0400, Cole Robinson wrote:
Signed-off-by: Cole Robinson <crobinso@redhat.com> --- include/libvirt/libvirt.h | 4 +++ include/libvirt/libvirt.h.in | 4 +++ src/driver.h | 6 ++++ src/libvirt.c | 61 ++++++++++++++++++++++++++++++++++++++++- src/libvirt_public.syms | 5 +++ 5 files changed, 78 insertions(+), 2 deletions(-)
ACK, this looks good to me. Daniel
diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h index 30f559d..b1e45e4 100644 --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -1047,6 +1047,10 @@ const char* virStorageVolGetKey (virStorageVolPtr vol); virStorageVolPtr virStorageVolCreateXML (virStoragePoolPtr pool, const char *xmldesc, unsigned int flags); +virStorageVolPtr virStorageVolCreateXMLFrom (virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags, + virStorageVolPtr clonevol); int virStorageVolDelete (virStorageVolPtr vol, unsigned int flags); int virStorageVolRef (virStorageVolPtr vol); diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 2f7076f..f5cadb4 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1047,6 +1047,10 @@ const char* virStorageVolGetKey (virStorageVolPtr vol); virStorageVolPtr virStorageVolCreateXML (virStoragePoolPtr pool, const char *xmldesc, unsigned int flags); +virStorageVolPtr virStorageVolCreateXMLFrom (virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags, + virStorageVolPtr clonevol); int virStorageVolDelete (virStorageVolPtr vol, unsigned int flags); int virStorageVolRef (virStorageVolPtr vol); diff --git a/src/driver.h b/src/driver.h index c357b76..ff12ada 100644 --- a/src/driver.h +++ b/src/driver.h @@ -586,6 +586,11 @@ typedef char * typedef char * (*virDrvStorageVolGetPath) (virStorageVolPtr vol);
+typedef virStorageVolPtr + (*virDrvStorageVolCreateXMLFrom) (virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags, + virStorageVolPtr clone);
typedef struct _virStorageDriver virStorageDriver; @@ -633,6 +638,7 @@ struct _virStorageDriver { virDrvStorageVolLookupByKey volLookupByKey; virDrvStorageVolLookupByPath volLookupByPath; virDrvStorageVolCreateXML volCreateXML; + virDrvStorageVolCreateXMLFrom volCreateXMLFrom; virDrvStorageVolDelete volDelete; virDrvStorageVolGetInfo volGetInfo; virDrvStorageVolGetXMLDesc volGetXMLDesc; diff --git a/src/libvirt.c b/src/libvirt.c index ded18a7..5a51c45 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -6767,6 +6767,65 @@ error:
/** + * virStorageVolCreateXMLFrom: + * @pool: pointer to parent pool for the new volume + * @xmldesc: description of volume to create + * @flags: flags for creation (unused, pass 0) + * @clonevol: storage volume to use as input + * + * Create a storage volume in the parent pool, using the + * 'clonevol' volume as input. Information for the new + * volume (name, perms) are passed via a typical volume + * XML description. + * + * return the storage volume, or NULL on error + */ +virStorageVolPtr +virStorageVolCreateXMLFrom(virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags, + virStorageVolPtr clonevol) +{ + DEBUG("pool=%p, flags=%u, clonevol=%p", pool, flags, clonevol); + + virResetLastError(); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibConnError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + return (NULL); + } + + if (!VIR_IS_STORAGE_VOL(clonevol)) { + virLibConnError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + return (NULL); + } + + if (pool->conn->flags & VIR_CONNECT_RO || + clonevol->conn->flags & VIR_CONNECT_RO) { + virLibConnError(pool->conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (pool->conn->storageDriver && + pool->conn->storageDriver->volCreateXMLFrom) { + virStorageVolPtr ret; + ret = pool->conn->storageDriver->volCreateXMLFrom (pool, xmldesc, + flags, clonevol); + if (!ret) + goto error; + return ret; + } + + virLibConnError (pool->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(pool->conn); + return NULL; +} + + +/** * virStorageVolDelete: * @vol: pointer to storage volume * @flags: future flags, use 0 for now @@ -7009,8 +7068,6 @@ error: }
- - /** * virNodeNumOfDevices: * @conn: pointer to the hypervisor connection diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index b8f9128..0ea130f 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -265,3 +265,8 @@ LIBVIRT_0.6.3 { } LIBVIRT_0.6.1;
# .... define new API here using predicted next version number .... + +LIBVIRT_0.6.4 { + global: + virStorageVolCreateXMLFrom; +} LIBVIRT_0.6.3; -- 1.6.0.6
-- Libvir-list mailing list Libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
-- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Mon, May 04, 2009 at 01:42:56PM -0400, Cole Robinson wrote:
Signed-off-by: Cole Robinson <crobinso@redhat.com> --- include/libvirt/libvirt.h | 4 +++ include/libvirt/libvirt.h.in | 4 +++ src/driver.h | 6 ++++ src/libvirt.c | 61 ++++++++++++++++++++++++++++++++++++++++- src/libvirt_public.syms | 5 +++ 5 files changed, 78 insertions(+), 2 deletions(-)
diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h index 30f559d..b1e45e4 100644 --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -1047,6 +1047,10 @@ const char* virStorageVolGetKey (virStorageVolPtr vol); virStorageVolPtr virStorageVolCreateXML (virStoragePoolPtr pool, const char *xmldesc, unsigned int flags); +virStorageVolPtr virStorageVolCreateXMLFrom (virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags, + virStorageVolPtr clonevol);
just one nitpick, we usually keep flags at the end of the list of arguments, so I would just swap flags and clonevol
diff --git a/src/driver.h b/src/driver.h index c357b76..ff12ada 100644 --- a/src/driver.h +++ b/src/driver.h @@ -586,6 +586,11 @@ typedef char * typedef char * (*virDrvStorageVolGetPath) (virStorageVolPtr vol);
+typedef virStorageVolPtr + (*virDrvStorageVolCreateXMLFrom) (virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags, + virStorageVolPtr clone);
same here, even if it's the internal one and less of a problem. Except for this ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

Signed-off-by: Cole Robinson <crobinso@redhat.com> --- qemud/remote.c | 34 ++++++++++++++++++++++++++++++++++ qemud/remote_dispatch_args.h | 1 + qemud/remote_dispatch_prototypes.h | 7 +++++++ qemud/remote_dispatch_ret.h | 1 + qemud/remote_dispatch_table.h | 5 +++++ qemud/remote_protocol.c | 24 ++++++++++++++++++++++++ qemud/remote_protocol.h | 18 ++++++++++++++++++ qemud/remote_protocol.x | 15 ++++++++++++++- src/remote_internal.c | 33 +++++++++++++++++++++++++++++++++ 9 files changed, 137 insertions(+), 1 deletions(-) diff --git a/qemud/remote.c b/qemud/remote.c index 8d24a3a..5ba9430 100644 --- a/qemud/remote.c +++ b/qemud/remote.c @@ -3831,6 +3831,40 @@ remoteDispatchStorageVolCreateXml (struct qemud_server *server ATTRIBUTE_UNUSED, return 0; } +static int +remoteDispatchStorageVolCreateXmlFrom (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_error *rerr, + remote_storage_vol_create_xml_from_args *args, + remote_storage_vol_create_xml_from_ret *ret) +{ + virStoragePoolPtr pool; + virStorageVolPtr clonevol, newvol; + + pool = get_nonnull_storage_pool (conn, args->pool); + if (pool == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + clonevol = get_nonnull_storage_vol (conn, args->clonevol); + if (clonevol == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + newvol = virStorageVolCreateXMLFrom (pool, args->xml, args->flags, + clonevol); + if (newvol == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + make_nonnull_storage_vol (&ret->vol, newvol); + virStorageVolFree(newvol); + return 0; +} static int remoteDispatchStorageVolDelete (struct qemud_server *server ATTRIBUTE_UNUSED, diff --git a/qemud/remote_dispatch_args.h b/qemud/remote_dispatch_args.h index 58eccf5..8f8b05b 100644 --- a/qemud/remote_dispatch_args.h +++ b/qemud/remote_dispatch_args.h @@ -105,3 +105,4 @@ remote_domain_get_security_label_args val_remote_domain_get_security_label_args; remote_node_device_create_xml_args val_remote_node_device_create_xml_args; remote_node_device_destroy_args val_remote_node_device_destroy_args; + remote_storage_vol_create_xml_from_args val_remote_storage_vol_create_xml_from_args; diff --git a/qemud/remote_dispatch_prototypes.h b/qemud/remote_dispatch_prototypes.h index 96dcb2a..1a2d98b 100644 --- a/qemud/remote_dispatch_prototypes.h +++ b/qemud/remote_dispatch_prototypes.h @@ -814,6 +814,13 @@ static int remoteDispatchStorageVolCreateXml( remote_error *err, remote_storage_vol_create_xml_args *args, remote_storage_vol_create_xml_ret *ret); +static int remoteDispatchStorageVolCreateXmlFrom( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_error *err, + remote_storage_vol_create_xml_from_args *args, + remote_storage_vol_create_xml_from_ret *ret); static int remoteDispatchStorageVolDelete( struct qemud_server *server, struct qemud_client *client, diff --git a/qemud/remote_dispatch_ret.h b/qemud/remote_dispatch_ret.h index 3325c8b..75e2ca6 100644 --- a/qemud/remote_dispatch_ret.h +++ b/qemud/remote_dispatch_ret.h @@ -89,3 +89,4 @@ remote_domain_get_security_label_ret val_remote_domain_get_security_label_ret; remote_node_get_security_model_ret val_remote_node_get_security_model_ret; remote_node_device_create_xml_ret val_remote_node_device_create_xml_ret; + remote_storage_vol_create_xml_from_ret val_remote_storage_vol_create_xml_from_ret; diff --git a/qemud/remote_dispatch_table.h b/qemud/remote_dispatch_table.h index ac7f9b9..e601a6c 100644 --- a/qemud/remote_dispatch_table.h +++ b/qemud/remote_dispatch_table.h @@ -627,3 +627,8 @@ .args_filter = (xdrproc_t) xdr_remote_node_device_destroy_args, .ret_filter = (xdrproc_t) xdr_void, }, +{ /* StorageVolCreateXmlFrom => 125 */ + .fn = (dispatch_fn) remoteDispatchStorageVolCreateXmlFrom, + .args_filter = (xdrproc_t) xdr_remote_storage_vol_create_xml_from_args, + .ret_filter = (xdrproc_t) xdr_remote_storage_vol_create_xml_from_ret, +}, diff --git a/qemud/remote_protocol.c b/qemud/remote_protocol.c index af3c792..738f95c 100644 --- a/qemud/remote_protocol.c +++ b/qemud/remote_protocol.c @@ -1992,6 +1992,30 @@ xdr_remote_storage_vol_create_xml_ret (XDR *xdrs, remote_storage_vol_create_xml_ } bool_t +xdr_remote_storage_vol_create_xml_from_args (XDR *xdrs, remote_storage_vol_create_xml_from_args *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->xml)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->flags)) + return FALSE; + if (!xdr_remote_nonnull_storage_vol (xdrs, &objp->clonevol)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_vol_create_xml_from_ret (XDR *xdrs, remote_storage_vol_create_xml_from_ret *objp) +{ + + if (!xdr_remote_nonnull_storage_vol (xdrs, &objp->vol)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_storage_vol_delete_args (XDR *xdrs, remote_storage_vol_delete_args *objp) { diff --git a/qemud/remote_protocol.h b/qemud/remote_protocol.h index 9d67e58..9480dce 100644 --- a/qemud/remote_protocol.h +++ b/qemud/remote_protocol.h @@ -1120,6 +1120,19 @@ struct remote_storage_vol_create_xml_ret { }; typedef struct remote_storage_vol_create_xml_ret remote_storage_vol_create_xml_ret; +struct remote_storage_vol_create_xml_from_args { + remote_nonnull_storage_pool pool; + remote_nonnull_string xml; + u_int flags; + remote_nonnull_storage_vol clonevol; +}; +typedef struct remote_storage_vol_create_xml_from_args remote_storage_vol_create_xml_from_args; + +struct remote_storage_vol_create_xml_from_ret { + remote_nonnull_storage_vol vol; +}; +typedef struct remote_storage_vol_create_xml_from_ret remote_storage_vol_create_xml_from_ret; + struct remote_storage_vol_delete_args { remote_nonnull_storage_vol vol; u_int flags; @@ -1415,6 +1428,7 @@ enum remote_procedure { REMOTE_PROC_NODE_GET_SECURITY_MODEL = 122, REMOTE_PROC_NODE_DEVICE_CREATE_XML = 123, REMOTE_PROC_NODE_DEVICE_DESTROY = 124, + REMOTE_PROC_STORAGE_VOL_CREATE_XML_FROM = 125, }; typedef enum remote_procedure remote_procedure; @@ -1623,6 +1637,8 @@ extern bool_t xdr_remote_storage_vol_lookup_by_path_args (XDR *, remote_storage extern bool_t xdr_remote_storage_vol_lookup_by_path_ret (XDR *, remote_storage_vol_lookup_by_path_ret*); extern bool_t xdr_remote_storage_vol_create_xml_args (XDR *, remote_storage_vol_create_xml_args*); extern bool_t xdr_remote_storage_vol_create_xml_ret (XDR *, remote_storage_vol_create_xml_ret*); +extern bool_t xdr_remote_storage_vol_create_xml_from_args (XDR *, remote_storage_vol_create_xml_from_args*); +extern bool_t xdr_remote_storage_vol_create_xml_from_ret (XDR *, remote_storage_vol_create_xml_from_ret*); extern bool_t xdr_remote_storage_vol_delete_args (XDR *, remote_storage_vol_delete_args*); extern bool_t xdr_remote_storage_vol_dump_xml_args (XDR *, remote_storage_vol_dump_xml_args*); extern bool_t xdr_remote_storage_vol_dump_xml_ret (XDR *, remote_storage_vol_dump_xml_ret*); @@ -1837,6 +1853,8 @@ extern bool_t xdr_remote_storage_vol_lookup_by_path_args (); extern bool_t xdr_remote_storage_vol_lookup_by_path_ret (); extern bool_t xdr_remote_storage_vol_create_xml_args (); extern bool_t xdr_remote_storage_vol_create_xml_ret (); +extern bool_t xdr_remote_storage_vol_create_xml_from_args (); +extern bool_t xdr_remote_storage_vol_create_xml_from_ret (); extern bool_t xdr_remote_storage_vol_delete_args (); extern bool_t xdr_remote_storage_vol_dump_xml_args (); extern bool_t xdr_remote_storage_vol_dump_xml_ret (); diff --git a/qemud/remote_protocol.x b/qemud/remote_protocol.x index 2c79949..123f6ae 100644 --- a/qemud/remote_protocol.x +++ b/qemud/remote_protocol.x @@ -1002,6 +1002,17 @@ struct remote_storage_vol_create_xml_ret { remote_nonnull_storage_vol vol; }; +struct remote_storage_vol_create_xml_from_args { + remote_nonnull_storage_pool pool; + remote_nonnull_string xml; + unsigned flags; + remote_nonnull_storage_vol clonevol; +}; + +struct remote_storage_vol_create_xml_from_ret { + remote_nonnull_storage_vol vol; +}; + struct remote_storage_vol_delete_args { remote_nonnull_storage_vol vol; unsigned flags; @@ -1286,7 +1297,9 @@ enum remote_procedure { REMOTE_PROC_NODE_GET_SECURITY_MODEL = 122, REMOTE_PROC_NODE_DEVICE_CREATE_XML = 123, - REMOTE_PROC_NODE_DEVICE_DESTROY = 124 + REMOTE_PROC_NODE_DEVICE_DESTROY = 124, + + REMOTE_PROC_STORAGE_VOL_CREATE_XML_FROM = 125 }; /* Custom RPC structure. */ diff --git a/src/remote_internal.c b/src/remote_internal.c index 24226e5..e100767 100644 --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -4508,6 +4508,38 @@ done: return vol; } +static virStorageVolPtr +remoteStorageVolCreateXMLFrom (virStoragePoolPtr pool, + const char *xmlDesc, + unsigned int flags, + virStorageVolPtr clonevol) +{ + virStorageVolPtr newvol = NULL; + remote_storage_vol_create_xml_from_args args; + remote_storage_vol_create_xml_from_ret ret; + struct private_data *priv = pool->conn->storagePrivateData; + + remoteDriverLock(priv); + + make_nonnull_storage_pool (&args.pool, pool); + make_nonnull_storage_vol (&args.clonevol, clonevol); + args.xml = (char *) xmlDesc; + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_CREATE_XML_FROM, + (xdrproc_t) xdr_remote_storage_vol_create_xml_from_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_vol_create_xml_from_ret, (char *) &ret) == -1) + goto done; + + newvol = get_nonnull_storage_vol (pool->conn, ret.vol); + xdr_free ((xdrproc_t) &xdr_remote_storage_vol_create_xml_from_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return newvol; +} + static int remoteStorageVolDelete (virStorageVolPtr vol, unsigned int flags) @@ -7027,6 +7059,7 @@ static virStorageDriver storage_driver = { .volLookupByKey = remoteStorageVolLookupByKey, .volLookupByPath = remoteStorageVolLookupByPath, .volCreateXML = remoteStorageVolCreateXML, + .volCreateXMLFrom = remoteStorageVolCreateXMLFrom, .volDelete = remoteStorageVolDelete, .volGetInfo = remoteStorageVolGetInfo, .volGetXMLDesc = remoteStorageVolDumpXML, -- 1.6.0.6

On Mon, May 04, 2009 at 01:42:57PM -0400, Cole Robinson wrote:
Signed-off-by: Cole Robinson <crobinso@redhat.com> --- qemud/remote.c | 34 ++++++++++++++++++++++++++++++++++ qemud/remote_dispatch_args.h | 1 + qemud/remote_dispatch_prototypes.h | 7 +++++++ qemud/remote_dispatch_ret.h | 1 + qemud/remote_dispatch_table.h | 5 +++++ qemud/remote_protocol.c | 24 ++++++++++++++++++++++++ qemud/remote_protocol.h | 18 ++++++++++++++++++ qemud/remote_protocol.x | 15 ++++++++++++++- src/remote_internal.c | 33 +++++++++++++++++++++++++++++++++ 9 files changed, 137 insertions(+), 1 deletions(-)
ACK, all fine, boring stuff. Daniel
diff --git a/qemud/remote.c b/qemud/remote.c index 8d24a3a..5ba9430 100644 --- a/qemud/remote.c +++ b/qemud/remote.c @@ -3831,6 +3831,40 @@ remoteDispatchStorageVolCreateXml (struct qemud_server *server ATTRIBUTE_UNUSED, return 0; }
+static int +remoteDispatchStorageVolCreateXmlFrom (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_error *rerr, + remote_storage_vol_create_xml_from_args *args, + remote_storage_vol_create_xml_from_ret *ret) +{ + virStoragePoolPtr pool; + virStorageVolPtr clonevol, newvol; + + pool = get_nonnull_storage_pool (conn, args->pool); + if (pool == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + clonevol = get_nonnull_storage_vol (conn, args->clonevol); + if (clonevol == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + newvol = virStorageVolCreateXMLFrom (pool, args->xml, args->flags, + clonevol); + if (newvol == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + make_nonnull_storage_vol (&ret->vol, newvol); + virStorageVolFree(newvol); + return 0; +}
static int remoteDispatchStorageVolDelete (struct qemud_server *server ATTRIBUTE_UNUSED, diff --git a/qemud/remote_dispatch_args.h b/qemud/remote_dispatch_args.h index 58eccf5..8f8b05b 100644 --- a/qemud/remote_dispatch_args.h +++ b/qemud/remote_dispatch_args.h @@ -105,3 +105,4 @@ remote_domain_get_security_label_args val_remote_domain_get_security_label_args; remote_node_device_create_xml_args val_remote_node_device_create_xml_args; remote_node_device_destroy_args val_remote_node_device_destroy_args; + remote_storage_vol_create_xml_from_args val_remote_storage_vol_create_xml_from_args; diff --git a/qemud/remote_dispatch_prototypes.h b/qemud/remote_dispatch_prototypes.h index 96dcb2a..1a2d98b 100644 --- a/qemud/remote_dispatch_prototypes.h +++ b/qemud/remote_dispatch_prototypes.h @@ -814,6 +814,13 @@ static int remoteDispatchStorageVolCreateXml( remote_error *err, remote_storage_vol_create_xml_args *args, remote_storage_vol_create_xml_ret *ret); +static int remoteDispatchStorageVolCreateXmlFrom( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_error *err, + remote_storage_vol_create_xml_from_args *args, + remote_storage_vol_create_xml_from_ret *ret); static int remoteDispatchStorageVolDelete( struct qemud_server *server, struct qemud_client *client, diff --git a/qemud/remote_dispatch_ret.h b/qemud/remote_dispatch_ret.h index 3325c8b..75e2ca6 100644 --- a/qemud/remote_dispatch_ret.h +++ b/qemud/remote_dispatch_ret.h @@ -89,3 +89,4 @@ remote_domain_get_security_label_ret val_remote_domain_get_security_label_ret; remote_node_get_security_model_ret val_remote_node_get_security_model_ret; remote_node_device_create_xml_ret val_remote_node_device_create_xml_ret; + remote_storage_vol_create_xml_from_ret val_remote_storage_vol_create_xml_from_ret; diff --git a/qemud/remote_dispatch_table.h b/qemud/remote_dispatch_table.h index ac7f9b9..e601a6c 100644 --- a/qemud/remote_dispatch_table.h +++ b/qemud/remote_dispatch_table.h @@ -627,3 +627,8 @@ .args_filter = (xdrproc_t) xdr_remote_node_device_destroy_args, .ret_filter = (xdrproc_t) xdr_void, }, +{ /* StorageVolCreateXmlFrom => 125 */ + .fn = (dispatch_fn) remoteDispatchStorageVolCreateXmlFrom, + .args_filter = (xdrproc_t) xdr_remote_storage_vol_create_xml_from_args, + .ret_filter = (xdrproc_t) xdr_remote_storage_vol_create_xml_from_ret, +}, diff --git a/qemud/remote_protocol.c b/qemud/remote_protocol.c index af3c792..738f95c 100644 --- a/qemud/remote_protocol.c +++ b/qemud/remote_protocol.c @@ -1992,6 +1992,30 @@ xdr_remote_storage_vol_create_xml_ret (XDR *xdrs, remote_storage_vol_create_xml_ }
bool_t +xdr_remote_storage_vol_create_xml_from_args (XDR *xdrs, remote_storage_vol_create_xml_from_args *objp) +{ + + if (!xdr_remote_nonnull_storage_pool (xdrs, &objp->pool)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->xml)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->flags)) + return FALSE; + if (!xdr_remote_nonnull_storage_vol (xdrs, &objp->clonevol)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_storage_vol_create_xml_from_ret (XDR *xdrs, remote_storage_vol_create_xml_from_ret *objp) +{ + + if (!xdr_remote_nonnull_storage_vol (xdrs, &objp->vol)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_storage_vol_delete_args (XDR *xdrs, remote_storage_vol_delete_args *objp) {
diff --git a/qemud/remote_protocol.h b/qemud/remote_protocol.h index 9d67e58..9480dce 100644 --- a/qemud/remote_protocol.h +++ b/qemud/remote_protocol.h @@ -1120,6 +1120,19 @@ struct remote_storage_vol_create_xml_ret { }; typedef struct remote_storage_vol_create_xml_ret remote_storage_vol_create_xml_ret;
+struct remote_storage_vol_create_xml_from_args { + remote_nonnull_storage_pool pool; + remote_nonnull_string xml; + u_int flags; + remote_nonnull_storage_vol clonevol; +}; +typedef struct remote_storage_vol_create_xml_from_args remote_storage_vol_create_xml_from_args; + +struct remote_storage_vol_create_xml_from_ret { + remote_nonnull_storage_vol vol; +}; +typedef struct remote_storage_vol_create_xml_from_ret remote_storage_vol_create_xml_from_ret; + struct remote_storage_vol_delete_args { remote_nonnull_storage_vol vol; u_int flags; @@ -1415,6 +1428,7 @@ enum remote_procedure { REMOTE_PROC_NODE_GET_SECURITY_MODEL = 122, REMOTE_PROC_NODE_DEVICE_CREATE_XML = 123, REMOTE_PROC_NODE_DEVICE_DESTROY = 124, + REMOTE_PROC_STORAGE_VOL_CREATE_XML_FROM = 125, }; typedef enum remote_procedure remote_procedure;
@@ -1623,6 +1637,8 @@ extern bool_t xdr_remote_storage_vol_lookup_by_path_args (XDR *, remote_storage extern bool_t xdr_remote_storage_vol_lookup_by_path_ret (XDR *, remote_storage_vol_lookup_by_path_ret*); extern bool_t xdr_remote_storage_vol_create_xml_args (XDR *, remote_storage_vol_create_xml_args*); extern bool_t xdr_remote_storage_vol_create_xml_ret (XDR *, remote_storage_vol_create_xml_ret*); +extern bool_t xdr_remote_storage_vol_create_xml_from_args (XDR *, remote_storage_vol_create_xml_from_args*); +extern bool_t xdr_remote_storage_vol_create_xml_from_ret (XDR *, remote_storage_vol_create_xml_from_ret*); extern bool_t xdr_remote_storage_vol_delete_args (XDR *, remote_storage_vol_delete_args*); extern bool_t xdr_remote_storage_vol_dump_xml_args (XDR *, remote_storage_vol_dump_xml_args*); extern bool_t xdr_remote_storage_vol_dump_xml_ret (XDR *, remote_storage_vol_dump_xml_ret*); @@ -1837,6 +1853,8 @@ extern bool_t xdr_remote_storage_vol_lookup_by_path_args (); extern bool_t xdr_remote_storage_vol_lookup_by_path_ret (); extern bool_t xdr_remote_storage_vol_create_xml_args (); extern bool_t xdr_remote_storage_vol_create_xml_ret (); +extern bool_t xdr_remote_storage_vol_create_xml_from_args (); +extern bool_t xdr_remote_storage_vol_create_xml_from_ret (); extern bool_t xdr_remote_storage_vol_delete_args (); extern bool_t xdr_remote_storage_vol_dump_xml_args (); extern bool_t xdr_remote_storage_vol_dump_xml_ret (); diff --git a/qemud/remote_protocol.x b/qemud/remote_protocol.x index 2c79949..123f6ae 100644 --- a/qemud/remote_protocol.x +++ b/qemud/remote_protocol.x @@ -1002,6 +1002,17 @@ struct remote_storage_vol_create_xml_ret { remote_nonnull_storage_vol vol; };
+struct remote_storage_vol_create_xml_from_args { + remote_nonnull_storage_pool pool; + remote_nonnull_string xml; + unsigned flags; + remote_nonnull_storage_vol clonevol; +}; + +struct remote_storage_vol_create_xml_from_ret { + remote_nonnull_storage_vol vol; +}; + struct remote_storage_vol_delete_args { remote_nonnull_storage_vol vol; unsigned flags; @@ -1286,7 +1297,9 @@ enum remote_procedure { REMOTE_PROC_NODE_GET_SECURITY_MODEL = 122,
REMOTE_PROC_NODE_DEVICE_CREATE_XML = 123, - REMOTE_PROC_NODE_DEVICE_DESTROY = 124 + REMOTE_PROC_NODE_DEVICE_DESTROY = 124, + + REMOTE_PROC_STORAGE_VOL_CREATE_XML_FROM = 125 };
/* Custom RPC structure. */ diff --git a/src/remote_internal.c b/src/remote_internal.c index 24226e5..e100767 100644 --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -4508,6 +4508,38 @@ done: return vol; }
+static virStorageVolPtr +remoteStorageVolCreateXMLFrom (virStoragePoolPtr pool, + const char *xmlDesc, + unsigned int flags, + virStorageVolPtr clonevol) +{ + virStorageVolPtr newvol = NULL; + remote_storage_vol_create_xml_from_args args; + remote_storage_vol_create_xml_from_ret ret; + struct private_data *priv = pool->conn->storagePrivateData; + + remoteDriverLock(priv); + + make_nonnull_storage_pool (&args.pool, pool); + make_nonnull_storage_vol (&args.clonevol, clonevol); + args.xml = (char *) xmlDesc; + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (pool->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_CREATE_XML_FROM, + (xdrproc_t) xdr_remote_storage_vol_create_xml_from_args, (char *) &args, + (xdrproc_t) xdr_remote_storage_vol_create_xml_from_ret, (char *) &ret) == -1) + goto done; + + newvol = get_nonnull_storage_vol (pool->conn, ret.vol); + xdr_free ((xdrproc_t) &xdr_remote_storage_vol_create_xml_from_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return newvol; +} + static int remoteStorageVolDelete (virStorageVolPtr vol, unsigned int flags) @@ -7027,6 +7059,7 @@ static virStorageDriver storage_driver = { .volLookupByKey = remoteStorageVolLookupByKey, .volLookupByPath = remoteStorageVolLookupByPath, .volCreateXML = remoteStorageVolCreateXML, + .volCreateXMLFrom = remoteStorageVolCreateXMLFrom, .volDelete = remoteStorageVolDelete, .volGetInfo = remoteStorageVolGetInfo, .volGetXMLDesc = remoteStorageVolDumpXML, -- 1.6.0.6
-- Libvir-list mailing list Libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
-- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Signed-off-by: Cole Robinson <crobinso@redhat.com> --- src/test.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 95 insertions(+), 0 deletions(-) diff --git a/src/test.c b/src/test.c index 1915b57..d7be18b 100644 --- a/src/test.c +++ b/src/test.c @@ -3140,6 +3140,100 @@ cleanup: return ret; } +static virStorageVolPtr +testStorageVolumeCreateXMLFrom(virStoragePoolPtr pool, + const char *xmldesc, + unsigned int flags ATTRIBUTE_UNUSED, + virStorageVolPtr clonevol) { + testConnPtr privconn = pool->conn->privateData; + virStoragePoolObjPtr privpool; + virStorageVolDefPtr privvol = NULL, origvol = NULL; + virStorageVolPtr ret = NULL; + + testDriverLock(privconn); + privpool = virStoragePoolObjFindByName(&privconn->pools, + pool->name); + testDriverUnlock(privconn); + + if (privpool == NULL) { + testError(pool->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto cleanup; + } + + if (!virStoragePoolObjIsActive(privpool)) { + testError(pool->conn, VIR_ERR_INTERNAL_ERROR, + _("storage pool '%s' is not active"), pool->name); + goto cleanup; + } + + privvol = virStorageVolDefParse(pool->conn, privpool->def, xmldesc, NULL); + if (privvol == NULL) + goto cleanup; + + if (virStorageVolDefFindByName(privpool, privvol->name)) { + testError(pool->conn, VIR_ERR_INVALID_STORAGE_VOL, + "%s", _("storage vol already exists")); + goto cleanup; + } + + origvol = virStorageVolDefFindByName(privpool, clonevol->name); + if (!origvol) { + testError(pool->conn, VIR_ERR_INVALID_STORAGE_VOL, + _("no storage vol with matching name '%s'"), + clonevol->name); + goto cleanup; + } + + /* Make sure enough space */ + if ((privpool->def->allocation + privvol->allocation) > + privpool->def->capacity) { + testError(pool->conn, VIR_ERR_INTERNAL_ERROR, + _("Not enough free space in pool for volume '%s'"), + privvol->name); + goto cleanup; + } + privpool->def->available = (privpool->def->capacity - + privpool->def->allocation); + + if (VIR_REALLOC_N(privpool->volumes.objs, + privpool->volumes.count+1) < 0) { + virReportOOMError(pool->conn); + goto cleanup; + } + + if (VIR_ALLOC_N(privvol->target.path, + strlen(privpool->def->target.path) + + 1 + strlen(privvol->name) + 1) < 0) { + virReportOOMError(pool->conn); + goto cleanup; + } + + strcpy(privvol->target.path, privpool->def->target.path); + strcat(privvol->target.path, "/"); + strcat(privvol->target.path, privvol->name); + privvol->key = strdup(privvol->target.path); + if (privvol->key == NULL) { + virReportOOMError(pool->conn); + goto cleanup; + } + + privpool->def->allocation += privvol->allocation; + privpool->def->available = (privpool->def->capacity - + privpool->def->allocation); + + privpool->volumes.objs[privpool->volumes.count++] = privvol; + + ret = virGetStorageVol(pool->conn, privpool->def->name, + privvol->name, privvol->key); + privvol = NULL; + +cleanup: + virStorageVolDefFree(privvol); + if (privpool) + virStoragePoolObjUnlock(privpool); + return ret; +} + static int testStorageVolumeDelete(virStorageVolPtr vol, unsigned int flags ATTRIBUTE_UNUSED) { @@ -3583,6 +3677,7 @@ static virStorageDriver testStorageDriver = { .volLookupByKey = testStorageVolumeLookupByKey, .volLookupByPath = testStorageVolumeLookupByPath, .volCreateXML = testStorageVolumeCreateXML, + .volCreateXMLFrom = testStorageVolumeCreateXMLFrom, .volDelete = testStorageVolumeDelete, .volGetInfo = testStorageVolumeGetInfo, .volGetXMLDesc = testStorageVolumeGetXMLDesc, -- 1.6.0.6

On Mon, May 04, 2009 at 01:42:58PM -0400, Cole Robinson wrote:
Signed-off-by: Cole Robinson <crobinso@redhat.com> --- src/test.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 95 insertions(+), 0 deletions(-)
+ + if (VIR_ALLOC_N(privvol->target.path, + strlen(privpool->def->target.path) + + 1 + strlen(privvol->name) + 1) < 0) { + virReportOOMError(pool->conn); + goto cleanup; + } + + strcpy(privvol->target.path, privpool->def->target.path); + strcat(privvol->target.path, "/"); + strcat(privvol->target.path, privvol->name); + privvol->key = strdup(privvol->target.path); + if (privvol->key == NULL) { + virReportOOMError(pool->conn); + goto cleanup; + }
Be a little shorter to just call virAsprintf() for target.path ACK aside from that Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Daniel P. Berrange wrote:
On Mon, May 04, 2009 at 01:42:58PM -0400, Cole Robinson wrote:
Signed-off-by: Cole Robinson <crobinso@redhat.com> --- src/test.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 95 insertions(+), 0 deletions(-)
+ + if (VIR_ALLOC_N(privvol->target.path, + strlen(privpool->def->target.path) + + 1 + strlen(privvol->name) + 1) < 0) { + virReportOOMError(pool->conn); + goto cleanup; + } + + strcpy(privvol->target.path, privpool->def->target.path); + strcat(privvol->target.path, "/"); + strcat(privvol->target.path, privvol->name); + privvol->key = strdup(privvol->target.path); + if (privvol->key == NULL) { + virReportOOMError(pool->conn); + goto cleanup; + }
Be a little shorter to just call virAsprintf() for target.path
ACK aside from that
Daniel
That's largely just duplication of code that was already in the storage driver, so there are other culprits as well. I'll send a cleanup patch after this series is applied (or before a v2) that fixes all the cases. Thanks, Cole

On Mon, May 11, 2009 at 08:54:23AM -0400, Cole Robinson wrote:
Daniel P. Berrange wrote:
On Mon, May 04, 2009 at 01:42:58PM -0400, Cole Robinson wrote:
Signed-off-by: Cole Robinson <crobinso@redhat.com> --- src/test.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 95 insertions(+), 0 deletions(-)
+ + if (VIR_ALLOC_N(privvol->target.path, + strlen(privpool->def->target.path) + + 1 + strlen(privvol->name) + 1) < 0) { + virReportOOMError(pool->conn); + goto cleanup; + } + + strcpy(privvol->target.path, privpool->def->target.path); + strcat(privvol->target.path, "/"); + strcat(privvol->target.path, privvol->name); + privvol->key = strdup(privvol->target.path); + if (privvol->key == NULL) { + virReportOOMError(pool->conn); + goto cleanup; + }
Be a little shorter to just call virAsprintf() for target.path
ACK aside from that
Daniel
That's largely just duplication of code that was already in the storage driver, so there are other culprits as well. I'll send a cleanup patch after this series is applied (or before a v2) that fixes all the cases.
Sure, that works for me Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

vol-clone is a convenience function, requiring only a volume to clone and a new name. vol-create-from is a direct mapping to the public API, which allows cloning across pools, converting between formats, etc, but requires an xml file to be passed Signed-off-by: Cole Robinson <crobinso@redhat.com> --- src/virsh.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 175 insertions(+), 2 deletions(-) diff --git a/src/virsh.c b/src/virsh.c index 2e41c02..5d46c0a 100644 --- a/src/virsh.c +++ b/src/virsh.c @@ -3970,8 +3970,6 @@ cmdPoolUuid(vshControl *ctl, const vshCmd *cmd) } - - /* * "vol-create" command */ @@ -4031,6 +4029,179 @@ cmdVolCreate(vshControl *ctl, const vshCmd *cmd) } /* + * "vol-create-from" command + */ +static const vshCmdInfo info_vol_create_from[] = { + {"help", gettext_noop("create a vol, using another volume as input")}, + {"desc", gettext_noop("Create a vol from an existing volume.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_vol_create_from[] = { + {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("pool name")}, + {"file", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("file containing an XML vol description")}, + {"inputpool", VSH_OT_STRING, 0, gettext_noop("pool name or uuid of the input volume's pool")}, + {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("input vol name or key")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdVolCreateFrom(vshControl *ctl, const vshCmd *cmd) +{ + virStoragePoolPtr pool = NULL; + virStorageVolPtr newvol = NULL, inputvol = NULL; + char *from; + int found; + int ret = FALSE; + char *buffer = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + goto cleanup; + + if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL, VSH_BYNAME))) + goto cleanup; + + from = vshCommandOptString(cmd, "file", &found); + if (!found) { + goto cleanup; + } + + if (!(inputvol = vshCommandOptVol(ctl, cmd, "vol", "inputpool", NULL))) + goto cleanup; + + if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) { + goto cleanup; + } + + newvol = virStorageVolCreateXMLFrom(pool, buffer, 0, inputvol); + + if (newvol != NULL) { + vshPrint(ctl, _("Vol %s created from input vol %s\n"), + virStorageVolGetName(newvol), virStorageVolGetName(inputvol)); + } else { + vshError(ctl, FALSE, _("Failed to create vol from %s"), from); + goto cleanup; + } + + ret = TRUE; +cleanup: + free (buffer); + if (pool) + virStoragePoolFree(pool); + if (inputvol) + virStorageVolFree(inputvol); + return ret; +} + +static xmlChar * +makeCloneXML(char *origxml, char *newname) { + + xmlDocPtr doc; + xmlXPathContextPtr ctxt; + xmlXPathObjectPtr obj; + xmlChar *newxml = NULL; + int size; + + doc = xmlReadDoc((const xmlChar *) origxml, "domain.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | XML_PARSE_NOWARNING); + if (!doc) + goto cleanup; + ctxt = xmlXPathNewContext(doc); + if (!ctxt) + goto cleanup; + + obj = xmlXPathEval(BAD_CAST "/volume/name", ctxt); + if ((obj == NULL) || (obj->nodesetval == NULL) || + (obj->nodesetval->nodeTab == NULL)) + goto cleanup; + + xmlNodeSetContent(obj->nodesetval->nodeTab[0], (const xmlChar *)newname); + xmlDocDumpMemory(doc, &newxml, &size); + +cleanup: + xmlXPathFreeObject(obj); + xmlXPathFreeContext(ctxt); + xmlFreeDoc(doc); + return newxml; +} + +/* + * "vol-clone" command + */ +static const vshCmdInfo info_vol_clone[] = { + {"help", gettext_noop("clone a volume.")}, + {"desc", gettext_noop("Clone an existing volume.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_vol_clone[] = { + {"pool", VSH_OT_STRING, 0, gettext_noop("pool name or uuid")}, + {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("orig vol name or key")}, + {"newname", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("clone name")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdVolClone(vshControl *ctl, const vshCmd *cmd) +{ + virStoragePoolPtr origpool = NULL; + virStorageVolPtr origvol = NULL, newvol = NULL; + char *name, *origxml = NULL; + xmlChar *newxml = NULL; + int found; + int ret = FALSE; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + goto cleanup; + + if (!(origvol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL))) + goto cleanup; + + origpool = virStoragePoolLookupByVolume(origvol); + if (!origpool) { + vshError(ctl, FALSE, _("failed to get parent pool")); + goto cleanup; + } + + name = vshCommandOptString(cmd, "newname", &found); + if (!found) + goto cleanup; + + origxml = virStorageVolGetXMLDesc(origvol, 0); + if (!origxml) + goto cleanup; + + newxml = makeCloneXML(origxml, name); + if (!newxml) { + vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); + goto cleanup; + } + + newvol = virStorageVolCreateXMLFrom(origpool, (char *) newxml, 0, origvol); + + if (newvol != NULL) { + vshPrint(ctl, _("Vol %s cloned from %s\n"), + virStorageVolGetName(newvol), virStorageVolGetName(origvol)); + virStorageVolFree(newvol); + } else { + vshError(ctl, FALSE, _("Failed to clone vol from %s"), + virStorageVolGetName(origvol)); + goto cleanup; + } + + ret = TRUE; + +cleanup: + free(origxml); + xmlFree(newxml); + if (origvol) + virStorageVolFree(origvol); + if (origpool) + virStoragePoolFree(origpool); + return ret; +} + +/* * "vol-delete" command */ static const vshCmdInfo info_vol_delete[] = { @@ -5931,7 +6102,9 @@ static const vshCmdDef commands[] = { {"uri", cmdURI, NULL, info_uri}, {"vol-create", cmdVolCreate, opts_vol_create, info_vol_create}, + {"vol-create-from", cmdVolCreateFrom, opts_vol_create_from, info_vol_create_from}, {"vol-create-as", cmdVolCreateAs, opts_vol_create_as, info_vol_create_as}, + {"vol-clone", cmdVolClone, opts_vol_clone, info_vol_clone}, {"vol-delete", cmdVolDelete, opts_vol_delete, info_vol_delete}, {"vol-dumpxml", cmdVolDumpXML, opts_vol_dumpxml, info_vol_dumpxml}, {"vol-info", cmdVolInfo, opts_vol_info, info_vol_info}, -- 1.6.0.6

On Mon, May 04, 2009 at 01:42:59PM -0400, Cole Robinson wrote:
vol-clone is a convenience function, requiring only a volume to clone and a new name.
vol-create-from is a direct mapping to the public API, which allows cloning across pools, converting between formats, etc, but requires an xml file to be passed
Signed-off-by: Cole Robinson <crobinso@redhat.com>
Looks good to me. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

There is some funkiness here, since we are either dealing with 2 different pools (which means validation x 2) or the same pool, where we have to be careful not to deadlock. Signed-off-by: Cole Robinson <crobinso@redhat.com> --- src/storage_backend.h | 2 + src/storage_driver.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 151 insertions(+), 4 deletions(-) diff --git a/src/storage_backend.h b/src/storage_backend.h index c9c1e35..7bf8814 100644 --- a/src/storage_backend.h +++ b/src/storage_backend.h @@ -38,6 +38,7 @@ typedef int (*virStorageBackendBuildVol)(virConnectPtr conn, virStorageVolDefPtr typedef int (*virStorageBackendCreateVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol); typedef int (*virStorageBackendRefreshVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol); typedef int (*virStorageBackendDeleteVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol, unsigned int flags); +typedef int (*virStorageBackendBuildVolFrom)(virConnectPtr conn, virStorageVolDefPtr origvol, virStorageVolDefPtr newvol, unsigned int flags); typedef struct _virStorageBackend virStorageBackend; @@ -54,6 +55,7 @@ struct _virStorageBackend { virStorageBackendDeletePool deletePool; virStorageBackendBuildVol buildVol; + virStorageBackendBuildVolFrom buildVolFrom; virStorageBackendCreateVol createVol; virStorageBackendRefreshVol refreshVol; virStorageBackendDeleteVol deleteVol; diff --git a/src/storage_driver.c b/src/storage_driver.c index b72f0a0..2d28306 100644 --- a/src/storage_driver.c +++ b/src/storage_driver.c @@ -1303,6 +1303,154 @@ cleanup: return ret; } +static virStorageVolPtr +storageVolumeCreateXMLFrom(virStoragePoolPtr obj, + const char *xmldesc, + unsigned int flags ATTRIBUTE_UNUSED, + virStorageVolPtr vobj) { + virStorageDriverStatePtr driver = obj->conn->storagePrivateData; + virStoragePoolObjPtr pool, origpool = NULL; + virStorageBackendPtr backend; + virStorageVolDefPtr origvol = NULL, newvol = NULL; + virStorageVolPtr ret = NULL, volobj = NULL; + int buildret, diffpool; + + diffpool = !STREQ(obj->name, vobj->pool); + + storageDriverLock(driver); + pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid); + if (diffpool) + origpool = virStoragePoolObjFindByName(&driver->pools, vobj->pool); + else + origpool = pool; + storageDriverUnlock(driver); + + if (!pool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "%s", _("no storage pool with matching uuid")); + goto cleanup; + } + + if (diffpool && !origpool) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "%s", _("no storage pool with matching name")); + goto cleanup; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("storage pool is not active")); + goto cleanup; + } + + if (diffpool && !virStoragePoolObjIsActive(origpool)) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("storage pool is not active")); + goto cleanup; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) + goto cleanup; + + origvol = virStorageVolDefFindByName(origpool, vobj->name); + if (!origvol) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + "%s", _("no storage vol with matching name")); + goto cleanup; + } + + newvol = virStorageVolDefParse(obj->conn, pool->def, xmldesc, NULL); + if (newvol == NULL) + goto cleanup; + + if (virStorageVolDefFindByName(pool, newvol->name)) { + virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, + _("storage volume name '%s' already in use."), + newvol->name); + goto cleanup; + } + + /* Is there ever a valid case for this? */ + if (newvol->capacity < origvol->capacity) + newvol->capacity = origvol->capacity; + + if (!backend->buildVolFrom) { + virStorageReportError(obj->conn, VIR_ERR_NO_SUPPORT, + "%s", _("storage pool does not support volume creation from an existing volume")); + goto cleanup; + } + + if (origvol->building) { + virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR, + _("volume '%s' is still being allocated."), + origvol->name); + goto cleanup; + } + + if (backend->refreshVol && + backend->refreshVol(obj->conn, pool, origvol) < 0) + goto cleanup; + + if (VIR_REALLOC_N(pool->volumes.objs, + pool->volumes.count+1) < 0) { + virReportOOMError(obj->conn); + goto cleanup; + } + + /* 'Define' the new volume so we get async progress reporting */ + if (backend->createVol(obj->conn, pool, newvol) < 0) { + goto cleanup; + } + + pool->volumes.objs[pool->volumes.count++] = newvol; + volobj = virGetStorageVol(obj->conn, pool->def->name, newvol->name, + newvol->key); + + /* Drop the pool lock during volume allocation */ + pool->asyncjobs++; + virStoragePoolObjUnlock(pool); + + if (diffpool) { + origpool->asyncjobs++; + virStoragePoolObjUnlock(origpool); + } + + newvol->building = 1; + buildret = backend->buildVolFrom(obj->conn, newvol, origvol, flags); + newvol->building = 0; + newvol = NULL; + + virStoragePoolObjLock(pool); + pool->asyncjobs--; + + if (diffpool) { + virStoragePoolObjLock(origpool); + origpool->asyncjobs--; + virStoragePoolObjUnlock(origpool); + origpool = NULL; + } + + if (buildret < 0) { + virStoragePoolObjUnlock(pool); + storageVolumeDelete(volobj, 0); + pool = NULL; + goto cleanup; + } + + ret = volobj; + volobj = NULL; + +cleanup: + if (volobj) + virUnrefStorageVol(volobj); + virStorageVolDefFree(newvol); + if (pool) + virStoragePoolObjUnlock(pool); + if (diffpool && origpool) + virStoragePoolObjUnlock(origpool); + return ret; +} + static int storageVolumeDelete(virStorageVolPtr obj, unsigned int flags) { @@ -1523,10 +1671,6 @@ cleanup: return ret; } - - - - static virStorageDriver storageDriver = { .name = "storage", .open = storageOpen, @@ -1558,6 +1702,7 @@ static virStorageDriver storageDriver = { .volLookupByKey = storageVolumeLookupByKey, .volLookupByPath = storageVolumeLookupByPath, .volCreateXML = storageVolumeCreateXML, + .volCreateXMLFrom = storageVolumeCreateXMLFrom, .volDelete = storageVolumeDelete, .volGetInfo = storageVolumeGetInfo, .volGetXMLDesc = storageVolumeGetXMLDesc, -- 1.6.0.6

On Mon, May 04, 2009 at 01:43:00PM -0400, Cole Robinson wrote:
There is some funkiness here, since we are either dealing with 2 different pools (which means validation x 2) or the same pool, where we have to be careful not to deadlock.
Ahhh, tricky stuff.
+ + newvol->building = 1; + buildret = backend->buildVolFrom(obj->conn, newvol, origvol, flags); + newvol->building = 0; + newvol = NULL;
I'm wondering if we should also take care to mark origvol->building as non-zero, to incidate that it is in use too. Regardless though, we need to ensure buildVolFrom is safe against someone deleting the file from the filesystem behind our back Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Improves readability, particularly wrt the pending CreateFromXML changes. This will also help implementing File->Block volume creation in the future, since some of this code will need to be moved to a backend agnostic file. Signed-off-by: Cole Robinson <crobinso@redhat.com> --- src/storage_backend_fs.c | 327 +++++++++++++++++++++++++--------------------- 1 files changed, 179 insertions(+), 148 deletions(-) diff --git a/src/storage_backend_fs.c b/src/storage_backend_fs.c index 7a1bba8..241fb29 100644 --- a/src/storage_backend_fs.c +++ b/src/storage_backend_fs.c @@ -62,6 +62,8 @@ static int qcowXGetBackingStore(virConnectPtr, char **, static int vmdk4GetBackingStore(virConnectPtr, char **, const unsigned char *, size_t); +typedef int (*createFile)(virConnectPtr conn, virStorageVolDefPtr vol); + static int track_allocation_progress = 0; /* Either 'magic' or 'extension' *must* be provided */ @@ -1011,183 +1013,202 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn, return 0; } -/** - * Allocate a new file as a volume. This is either done directly - * for raw/sparse files, or by calling qemu-img/qcow-create for - * special kinds of files - */ -static int -virStorageBackendFileSystemVolBuild(virConnectPtr conn, - virStorageVolDefPtr vol) -{ +// XXX: Have these functions all use the same format? +static int createRaw(virConnectPtr conn, + virStorageVolDefPtr vol) { int fd; - if (vol->target.format == VIR_STORAGE_VOL_FILE_RAW) { - if ((fd = open(vol->target.path, O_RDWR | O_CREAT | O_EXCL, - vol->target.perms.mode)) < 0) { - virReportSystemError(conn, errno, - _("cannot create path '%s'"), - vol->target.path); - return -1; - } + if ((fd = open(vol->target.path, O_RDWR | O_CREAT | O_EXCL, + vol->target.perms.mode)) < 0) { + virReportSystemError(conn, errno, + _("cannot create path '%s'"), + vol->target.path); + return -1; + } - /* Seek to the final size, so the capacity is available upfront - * for progress reporting */ - if (ftruncate(fd, vol->capacity) < 0) { - virReportSystemError(conn, errno, - _("cannot extend file '%s'"), - vol->target.path); - close(fd); - return -1; - } + /* Seek to the final size, so the capacity is available upfront + * for progress reporting */ + if (ftruncate(fd, vol->capacity) < 0) { + virReportSystemError(conn, errno, + _("cannot extend file '%s'"), + vol->target.path); + close(fd); + return -1; + } - /* Pre-allocate any data if requested */ - /* XXX slooooooooooooooooow on non-extents-based file systems */ - /* FIXME: Add in progress bars & bg thread if progress bar requested */ - if (vol->allocation) { - if (track_allocation_progress) { - unsigned long long remain = vol->allocation; - - while (remain) { - /* Allocate in chunks of 512MiB: big-enough chunk - * size and takes approx. 9s on ext3. A progress - * update every 9s is a fair-enough trade-off - */ - unsigned long long bytes = 512 * 1024 * 1024; - int r; - - if (bytes > remain) - bytes = remain; - if ((r = safezero(fd, 0, vol->allocation - remain, - bytes)) != 0) { - virReportSystemError(conn, r, - _("cannot fill file '%s'"), - vol->target.path); - close(fd); - return -1; - } - remain -= bytes; - } - } else { /* No progress bars to be shown */ + /* Pre-allocate any data if requested */ + /* XXX slooooooooooooooooow on non-extents-based file systems */ + if (vol->allocation) { + if (track_allocation_progress) { + unsigned long long remain = vol->allocation; + + while (remain) { + /* Allocate in chunks of 512MiB: big-enough chunk + * size and takes approx. 9s on ext3. A progress + * update every 9s is a fair-enough trade-off + */ + unsigned long long bytes = 512 * 1024 * 1024; int r; - if ((r = safezero(fd, 0, 0, vol->allocation)) != 0) { + if (bytes > remain) + bytes = remain; + if ((r = safezero(fd, 0, vol->allocation - remain, + bytes)) != 0) { virReportSystemError(conn, r, _("cannot fill file '%s'"), vol->target.path); close(fd); return -1; } + remain -= bytes; + } + } else { /* No progress bars to be shown */ + int r; + + if ((r = safezero(fd, 0, 0, vol->allocation)) != 0) { + virReportSystemError(conn, r, + _("cannot fill file '%s'"), + vol->target.path); + close(fd); + return -1; } } + } - } else if (vol->target.format == VIR_STORAGE_VOL_FILE_DIR) { - if (mkdir(vol->target.path, vol->target.perms.mode) < 0) { - virReportSystemError(conn, errno, - _("cannot create path '%s'"), - vol->target.path); - return -1; - } + if (close(fd) < 0) { + virReportSystemError(conn, errno, + _("cannot close file '%s'"), + vol->target.path); + return -1; + } - if ((fd = open(vol->target.path, O_RDWR)) < 0) { - virReportSystemError(conn, errno, - _("cannot read path '%s'"), - vol->target.path); - return -1; - } - } else { -#if HAVE_QEMU_IMG - const char *type = virStorageVolFormatFileSystemTypeToString(vol->target.format); - const char *backingType = vol->backingStore.path ? - virStorageVolFormatFileSystemTypeToString(vol->backingStore.format) : NULL; - char size[100]; - const char **imgargv; - const char *imgargvnormal[] = { - QEMU_IMG, "create", "-f", type, vol->target.path, size, NULL, - }; - /* XXX including "backingType" here too, once QEMU accepts - * the patches to specify it. It'll probably be -F backingType */ - const char *imgargvbacking[] = { - QEMU_IMG, "create", "-f", type, "-b", vol->backingStore.path, vol->target.path, size, NULL, - }; - - if (type == NULL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("unknown storage vol type %d"), - vol->target.format); - return -1; - } - if (vol->backingStore.path == NULL) { - imgargv = imgargvnormal; - } else { - if (backingType == NULL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("unknown storage vol backing store type %d"), - vol->backingStore.format); - return -1; - } - if (access(vol->backingStore.path, R_OK) != 0) { - virReportSystemError(conn, errno, - _("inaccessible backing store volume %s"), - vol->backingStore.path); - return -1; - } + return 0; +} - imgargv = imgargvbacking; - } +static int createFileDir(virConnectPtr conn, + virStorageVolDefPtr vol) { + if (mkdir(vol->target.path, vol->target.perms.mode) < 0) { + virReportSystemError(conn, errno, + _("cannot create path '%s'"), + vol->target.path); + return -1; + } - /* Size in KB */ - snprintf(size, sizeof(size), "%llu", vol->capacity/1024); + return 0; +} - if (virRun(conn, imgargv, NULL) < 0) { - return -1; - } +#if HAVE_QEMU_IMG +static int createQemuImg(virConnectPtr conn, + virStorageVolDefPtr vol) { + const char *type = virStorageVolFormatFileSystemTypeToString(vol->target.format); + const char *backingType = vol->backingStore.path ? + virStorageVolFormatFileSystemTypeToString(vol->backingStore.format) : NULL; + char size[100]; + const char **imgargv; + const char *imgargvnormal[] = { + QEMU_IMG, "create", "-f", type, vol->target.path, size, NULL, + }; + /* XXX including "backingType" here too, once QEMU accepts + * the patches to specify it. It'll probably be -F backingType */ + const char *imgargvbacking[] = { + QEMU_IMG, "create", "-f", type, "-b", vol->backingStore.path, vol->target.path, size, NULL, + }; - if ((fd = open(vol->target.path, O_RDONLY)) < 0) { - virReportSystemError(conn, errno, - _("cannot read path '%s'"), - vol->target.path); - return -1; - } -#elif HAVE_QCOW_CREATE - /* - * Xen removed the fully-functional qemu-img, and replaced it - * with a partially functional qcow-create. Go figure ??!? - */ - char size[100]; - const char *imgargv[4]; - - if (vol->target.format != VIR_STORAGE_VOL_FILE_QCOW2) { + if (type == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown storage vol type %d"), + vol->target.format); + return -1; + } + if (vol->backingStore.path == NULL) { + imgargv = imgargvnormal; + } else { + if (backingType == NULL) { virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("unsupported storage vol type %d"), - vol->target.format); + _("unknown storage vol backing store type %d"), + vol->backingStore.format); return -1; } - if (vol->backingStore.path != NULL) { - virStorageReportError(conn, VIR_ERR_NO_SUPPORT, - _("copy-on-write image not supported with " - "qcow-create")); + if (access(vol->backingStore.path, R_OK) != 0) { + virReportSystemError(conn, errno, + _("inaccessible backing store volume %s"), + vol->backingStore.path); return -1; } - /* Size in MB - yes different units to qemu-img :-( */ - snprintf(size, sizeof(size), "%llu", vol->capacity/1024/1024); + imgargv = imgargvbacking; + } - imgargv[0] = QCOW_CREATE; - imgargv[1] = size; - imgargv[2] = vol->target.path; - imgargv[3] = NULL; + /* Size in KB */ + snprintf(size, sizeof(size), "%llu", vol->capacity/1024); - if (virRun(conn, imgargv, NULL) < 0) { - return -1; - } + if (virRun(conn, imgargv, NULL) < 0) { + return -1; + } - if ((fd = open(vol->target.path, O_RDONLY)) < 0) { - virReportSystemError(conn, errno, - _("cannot read path '%s'"), - vol->target.path); - return -1; - } + return 0; +} + +#elif HAVE_QCOW_CREATE +/* + * Xen removed the fully-functional qemu-img, and replaced it + * with a partially functional qcow-create. Go figure ??!? + */ +static int createQemuCreate(virConnectPtr conn, + virStorageVolDefPtr vol) { + char size[100]; + const char *imgargv[4]; + + if (vol->target.format != VIR_STORAGE_VOL_FILE_QCOW2) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported storage vol type %d"), + vol->target.format); + return -1; + } + if (vol->backingStore.path != NULL) { + virStorageReportError(conn, VIR_ERR_NO_SUPPORT, + _("copy-on-write image not supported with " + "qcow-create")); + return -1; + } + + /* Size in MB - yes different units to qemu-img :-( */ + snprintf(size, sizeof(size), "%llu", vol->capacity/1024/1024); + + imgargv[0] = QCOW_CREATE; + imgargv[1] = size; + imgargv[2] = vol->target.path; + imgargv[3] = NULL; + + if (virRun(conn, imgargv, NULL) < 0) { + return -1; + } + + return 0; +} +#endif /* HAVE_QEMU_IMG, elif HAVE_QCOW_CREATE */ + +/** + * Allocate a new file as a volume. This is either done directly + * for raw/sparse files, or by calling qemu-img/qcow-create for + * special kinds of files + */ +static int +virStorageBackendFileSystemVolBuild(virConnectPtr conn, + virStorageVolDefPtr vol) +{ + int fd; + createFile create_func; + + if (vol->target.format == VIR_STORAGE_VOL_FILE_RAW) { + create_func = createRaw; + } else if (vol->target.format == VIR_STORAGE_VOL_FILE_DIR) { + create_func = createFileDir; + } else { +#if HAVE_QEMU_IMG + create_func = createQemuImg; +#elif HAVE_QCOW_CREATE + create_func = createQemuCreate; #else virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", _("creation of non-raw images " @@ -1196,6 +1217,16 @@ virStorageBackendFileSystemVolBuild(virConnectPtr conn, #endif } + if (create_func(conn, vol) < 0) + return -1; + + if ((fd = open(vol->target.path, O_RDONLY)) < 0) { + virReportSystemError(conn, errno, + _("cannot read path '%s'"), + vol->target.path); + return -1; + } + /* We can only chown/grp if root */ if (getuid() == 0) { if (fchown(fd, vol->target.perms.uid, vol->target.perms.gid) < 0) { -- 1.6.0.6

On Mon, May 04, 2009 at 01:43:01PM -0400, Cole Robinson wrote:
Improves readability, particularly wrt the pending CreateFromXML changes. This will also help implementing File->Block volume creation in the future, since some of this code will need to be moved to a backend agnostic file.
This is a nice idea - it was getting rather unwieldly as it grew. ACK. Daniel
Signed-off-by: Cole Robinson <crobinso@redhat.com> --- src/storage_backend_fs.c | 327 +++++++++++++++++++++++++--------------------- 1 files changed, 179 insertions(+), 148 deletions(-)
diff --git a/src/storage_backend_fs.c b/src/storage_backend_fs.c index 7a1bba8..241fb29 100644 --- a/src/storage_backend_fs.c +++ b/src/storage_backend_fs.c @@ -62,6 +62,8 @@ static int qcowXGetBackingStore(virConnectPtr, char **, static int vmdk4GetBackingStore(virConnectPtr, char **, const unsigned char *, size_t);
+typedef int (*createFile)(virConnectPtr conn, virStorageVolDefPtr vol); + static int track_allocation_progress = 0;
/* Either 'magic' or 'extension' *must* be provided */ @@ -1011,183 +1013,202 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn, return 0; }
-/** - * Allocate a new file as a volume. This is either done directly - * for raw/sparse files, or by calling qemu-img/qcow-create for - * special kinds of files - */ -static int -virStorageBackendFileSystemVolBuild(virConnectPtr conn, - virStorageVolDefPtr vol) -{ +// XXX: Have these functions all use the same format? +static int createRaw(virConnectPtr conn, + virStorageVolDefPtr vol) { int fd;
- if (vol->target.format == VIR_STORAGE_VOL_FILE_RAW) { - if ((fd = open(vol->target.path, O_RDWR | O_CREAT | O_EXCL, - vol->target.perms.mode)) < 0) { - virReportSystemError(conn, errno, - _("cannot create path '%s'"), - vol->target.path); - return -1; - } + if ((fd = open(vol->target.path, O_RDWR | O_CREAT | O_EXCL, + vol->target.perms.mode)) < 0) { + virReportSystemError(conn, errno, + _("cannot create path '%s'"), + vol->target.path); + return -1; + }
- /* Seek to the final size, so the capacity is available upfront - * for progress reporting */ - if (ftruncate(fd, vol->capacity) < 0) { - virReportSystemError(conn, errno, - _("cannot extend file '%s'"), - vol->target.path); - close(fd); - return -1; - } + /* Seek to the final size, so the capacity is available upfront + * for progress reporting */ + if (ftruncate(fd, vol->capacity) < 0) { + virReportSystemError(conn, errno, + _("cannot extend file '%s'"), + vol->target.path); + close(fd); + return -1; + }
- /* Pre-allocate any data if requested */ - /* XXX slooooooooooooooooow on non-extents-based file systems */ - /* FIXME: Add in progress bars & bg thread if progress bar requested */ - if (vol->allocation) { - if (track_allocation_progress) { - unsigned long long remain = vol->allocation; - - while (remain) { - /* Allocate in chunks of 512MiB: big-enough chunk - * size and takes approx. 9s on ext3. A progress - * update every 9s is a fair-enough trade-off - */ - unsigned long long bytes = 512 * 1024 * 1024; - int r; - - if (bytes > remain) - bytes = remain; - if ((r = safezero(fd, 0, vol->allocation - remain, - bytes)) != 0) { - virReportSystemError(conn, r, - _("cannot fill file '%s'"), - vol->target.path); - close(fd); - return -1; - } - remain -= bytes; - } - } else { /* No progress bars to be shown */ + /* Pre-allocate any data if requested */ + /* XXX slooooooooooooooooow on non-extents-based file systems */ + if (vol->allocation) { + if (track_allocation_progress) { + unsigned long long remain = vol->allocation; + + while (remain) { + /* Allocate in chunks of 512MiB: big-enough chunk + * size and takes approx. 9s on ext3. A progress + * update every 9s is a fair-enough trade-off + */ + unsigned long long bytes = 512 * 1024 * 1024; int r;
- if ((r = safezero(fd, 0, 0, vol->allocation)) != 0) { + if (bytes > remain) + bytes = remain; + if ((r = safezero(fd, 0, vol->allocation - remain, + bytes)) != 0) { virReportSystemError(conn, r, _("cannot fill file '%s'"), vol->target.path); close(fd); return -1; } + remain -= bytes; + } + } else { /* No progress bars to be shown */ + int r; + + if ((r = safezero(fd, 0, 0, vol->allocation)) != 0) { + virReportSystemError(conn, r, + _("cannot fill file '%s'"), + vol->target.path); + close(fd); + return -1; } } + }
- } else if (vol->target.format == VIR_STORAGE_VOL_FILE_DIR) { - if (mkdir(vol->target.path, vol->target.perms.mode) < 0) { - virReportSystemError(conn, errno, - _("cannot create path '%s'"), - vol->target.path); - return -1; - } + if (close(fd) < 0) { + virReportSystemError(conn, errno, + _("cannot close file '%s'"), + vol->target.path); + return -1; + }
- if ((fd = open(vol->target.path, O_RDWR)) < 0) { - virReportSystemError(conn, errno, - _("cannot read path '%s'"), - vol->target.path); - return -1; - } - } else { -#if HAVE_QEMU_IMG - const char *type = virStorageVolFormatFileSystemTypeToString(vol->target.format); - const char *backingType = vol->backingStore.path ? - virStorageVolFormatFileSystemTypeToString(vol->backingStore.format) : NULL; - char size[100]; - const char **imgargv; - const char *imgargvnormal[] = { - QEMU_IMG, "create", "-f", type, vol->target.path, size, NULL, - }; - /* XXX including "backingType" here too, once QEMU accepts - * the patches to specify it. It'll probably be -F backingType */ - const char *imgargvbacking[] = { - QEMU_IMG, "create", "-f", type, "-b", vol->backingStore.path, vol->target.path, size, NULL, - }; - - if (type == NULL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("unknown storage vol type %d"), - vol->target.format); - return -1; - } - if (vol->backingStore.path == NULL) { - imgargv = imgargvnormal; - } else { - if (backingType == NULL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("unknown storage vol backing store type %d"), - vol->backingStore.format); - return -1; - } - if (access(vol->backingStore.path, R_OK) != 0) { - virReportSystemError(conn, errno, - _("inaccessible backing store volume %s"), - vol->backingStore.path); - return -1; - } + return 0; +}
- imgargv = imgargvbacking; - } +static int createFileDir(virConnectPtr conn, + virStorageVolDefPtr vol) { + if (mkdir(vol->target.path, vol->target.perms.mode) < 0) { + virReportSystemError(conn, errno, + _("cannot create path '%s'"), + vol->target.path); + return -1; + }
- /* Size in KB */ - snprintf(size, sizeof(size), "%llu", vol->capacity/1024); + return 0; +}
- if (virRun(conn, imgargv, NULL) < 0) { - return -1; - } +#if HAVE_QEMU_IMG +static int createQemuImg(virConnectPtr conn, + virStorageVolDefPtr vol) { + const char *type = virStorageVolFormatFileSystemTypeToString(vol->target.format); + const char *backingType = vol->backingStore.path ? + virStorageVolFormatFileSystemTypeToString(vol->backingStore.format) : NULL; + char size[100]; + const char **imgargv; + const char *imgargvnormal[] = { + QEMU_IMG, "create", "-f", type, vol->target.path, size, NULL, + }; + /* XXX including "backingType" here too, once QEMU accepts + * the patches to specify it. It'll probably be -F backingType */ + const char *imgargvbacking[] = { + QEMU_IMG, "create", "-f", type, "-b", vol->backingStore.path, vol->target.path, size, NULL, + };
- if ((fd = open(vol->target.path, O_RDONLY)) < 0) { - virReportSystemError(conn, errno, - _("cannot read path '%s'"), - vol->target.path); - return -1; - } -#elif HAVE_QCOW_CREATE - /* - * Xen removed the fully-functional qemu-img, and replaced it - * with a partially functional qcow-create. Go figure ??!? - */ - char size[100]; - const char *imgargv[4]; - - if (vol->target.format != VIR_STORAGE_VOL_FILE_QCOW2) { + if (type == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown storage vol type %d"), + vol->target.format); + return -1; + } + if (vol->backingStore.path == NULL) { + imgargv = imgargvnormal; + } else { + if (backingType == NULL) { virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("unsupported storage vol type %d"), - vol->target.format); + _("unknown storage vol backing store type %d"), + vol->backingStore.format); return -1; } - if (vol->backingStore.path != NULL) { - virStorageReportError(conn, VIR_ERR_NO_SUPPORT, - _("copy-on-write image not supported with " - "qcow-create")); + if (access(vol->backingStore.path, R_OK) != 0) { + virReportSystemError(conn, errno, + _("inaccessible backing store volume %s"), + vol->backingStore.path); return -1; }
- /* Size in MB - yes different units to qemu-img :-( */ - snprintf(size, sizeof(size), "%llu", vol->capacity/1024/1024); + imgargv = imgargvbacking; + }
- imgargv[0] = QCOW_CREATE; - imgargv[1] = size; - imgargv[2] = vol->target.path; - imgargv[3] = NULL; + /* Size in KB */ + snprintf(size, sizeof(size), "%llu", vol->capacity/1024);
- if (virRun(conn, imgargv, NULL) < 0) { - return -1; - } + if (virRun(conn, imgargv, NULL) < 0) { + return -1; + }
- if ((fd = open(vol->target.path, O_RDONLY)) < 0) { - virReportSystemError(conn, errno, - _("cannot read path '%s'"), - vol->target.path); - return -1; - } + return 0; +} + +#elif HAVE_QCOW_CREATE +/* + * Xen removed the fully-functional qemu-img, and replaced it + * with a partially functional qcow-create. Go figure ??!? + */ +static int createQemuCreate(virConnectPtr conn, + virStorageVolDefPtr vol) { + char size[100]; + const char *imgargv[4]; + + if (vol->target.format != VIR_STORAGE_VOL_FILE_QCOW2) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported storage vol type %d"), + vol->target.format); + return -1; + } + if (vol->backingStore.path != NULL) { + virStorageReportError(conn, VIR_ERR_NO_SUPPORT, + _("copy-on-write image not supported with " + "qcow-create")); + return -1; + } + + /* Size in MB - yes different units to qemu-img :-( */ + snprintf(size, sizeof(size), "%llu", vol->capacity/1024/1024); + + imgargv[0] = QCOW_CREATE; + imgargv[1] = size; + imgargv[2] = vol->target.path; + imgargv[3] = NULL; + + if (virRun(conn, imgargv, NULL) < 0) { + return -1; + } + + return 0; +} +#endif /* HAVE_QEMU_IMG, elif HAVE_QCOW_CREATE */ + +/** + * Allocate a new file as a volume. This is either done directly + * for raw/sparse files, or by calling qemu-img/qcow-create for + * special kinds of files + */ +static int +virStorageBackendFileSystemVolBuild(virConnectPtr conn, + virStorageVolDefPtr vol) +{ + int fd; + createFile create_func; + + if (vol->target.format == VIR_STORAGE_VOL_FILE_RAW) { + create_func = createRaw; + } else if (vol->target.format == VIR_STORAGE_VOL_FILE_DIR) { + create_func = createFileDir; + } else { +#if HAVE_QEMU_IMG + create_func = createQemuImg; +#elif HAVE_QCOW_CREATE + create_func = createQemuCreate; #else virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", _("creation of non-raw images " @@ -1196,6 +1217,16 @@ virStorageBackendFileSystemVolBuild(virConnectPtr conn, #endif }
+ if (create_func(conn, vol) < 0) + return -1; + + if ((fd = open(vol->target.path, O_RDONLY)) < 0) { + virReportSystemError(conn, errno, + _("cannot read path '%s'"), + vol->target.path); + return -1; + } + /* We can only chown/grp if root */ if (getuid() == 0) { if (fchown(fd, vol->target.perms.uid, vol->target.perms.gid) < 0) { -- 1.6.0.6
-- Libvir-list mailing list Libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
-- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On 05/11/2009 08:58 AM, Daniel P. Berrange wrote:
On Mon, May 04, 2009 at 01:43:01PM -0400, Cole Robinson wrote:
Improves readability, particularly wrt the pending CreateFromXML changes. This will also help implementing File->Block volume creation in the future, since some of this code will need to be moved to a backend agnostic file.
This is a nice idea - it was getting rather unwieldly as it grew.
ACK.
Daniel
Since this is a mechanical change separate from the VolCreateXMLFrom implementation, I've committed this. Thanks, Cole

Add an extra 'inputvol' parameter to the file volume building routines. This allows us to easily reuse the duplicate functionality. Signed-off-by: Cole Robinson <crobinso@redhat.com> --- src/storage_backend_fs.c | 248 +++++++++++++++++++++++++++++++++++++++------- 1 files changed, 212 insertions(+), 36 deletions(-) diff --git a/src/storage_backend_fs.c b/src/storage_backend_fs.c index 241fb29..22cb493 100644 --- a/src/storage_backend_fs.c +++ b/src/storage_backend_fs.c @@ -62,7 +62,9 @@ static int qcowXGetBackingStore(virConnectPtr, char **, static int vmdk4GetBackingStore(virConnectPtr, char **, const unsigned char *, size_t); -typedef int (*createFile)(virConnectPtr conn, virStorageVolDefPtr vol); +typedef int (*createFile)(virConnectPtr conn, + virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol); static int track_allocation_progress = 0; @@ -1013,17 +1015,29 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn, return 0; } -// XXX: Have these functions all use the same format? static int createRaw(virConnectPtr conn, - virStorageVolDefPtr vol) { - int fd; + virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol) { + int fd = -1; + int inputfd = -1; + int ret = -1; + unsigned long long remain; if ((fd = open(vol->target.path, O_RDWR | O_CREAT | O_EXCL, vol->target.perms.mode)) < 0) { virReportSystemError(conn, errno, _("cannot create path '%s'"), vol->target.path); - return -1; + goto cleanup; + } + + if (inputvol) { + if ((inputfd = open(inputvol->target.path, O_RDONLY)) < 0) { + virReportSystemError(conn, errno, + _("could not open input path '%s'"), + inputvol->target.path); + goto cleanup; + } } /* Seek to the final size, so the capacity is available upfront @@ -1032,15 +1046,68 @@ static int createRaw(virConnectPtr conn, virReportSystemError(conn, errno, _("cannot extend file '%s'"), vol->target.path); - close(fd); - return -1; + goto cleanup; + } + + remain = vol->capacity; + + if (inputfd != -1) { + size_t bytes = 1024 * 1024; + int sparse_interval = 512; + int amtread = -1; + + while (amtread != 0) { + char buf[bytes]; + char *tmp; + int newamt; + + if (remain < bytes) + bytes = remain; + + if ((amtread = saferead(inputfd, buf, bytes)) < 0) { + virReportSystemError(conn, errno, + _("failed reading from file '%s'"), + inputvol->target.path); + goto cleanup; + } + remain -= amtread; + + /* Loop over amt read in 512 byte increments, looking for sparse + * blocks */ + tmp = buf; + newamt = amtread; + do { + int interval = ((sparse_interval > newamt) ? newamt + : sparse_interval ); + int sparse_counter = interval; + + while (sparse_counter-- && *(tmp+sparse_counter) == '\0') + continue; + + if (sparse_counter < 0) { + if (lseek(fd, interval, SEEK_CUR) < 0) { + virReportSystemError(conn, errno, + _("cannot extend file '%s'"), + vol->target.path); + goto cleanup; + } + } else if (safewrite(fd, tmp, interval) < 0) { + virReportSystemError(conn, errno, + _("failed writing to file '%s'"), + vol->target.path); + goto cleanup; + + } + + tmp += interval; + } while ((newamt -= sparse_interval) > 0); + } } /* Pre-allocate any data if requested */ /* XXX slooooooooooooooooow on non-extents-based file systems */ - if (vol->allocation) { + if (remain) { if (track_allocation_progress) { - unsigned long long remain = vol->allocation; while (remain) { /* Allocate in chunks of 512MiB: big-enough chunk @@ -1057,20 +1124,18 @@ static int createRaw(virConnectPtr conn, virReportSystemError(conn, r, _("cannot fill file '%s'"), vol->target.path); - close(fd); - return -1; + goto cleanup; } remain -= bytes; } } else { /* No progress bars to be shown */ int r; - if ((r = safezero(fd, 0, 0, vol->allocation)) != 0) { + if ((r = safezero(fd, 0, 0, remain)) != 0) { virReportSystemError(conn, r, _("cannot fill file '%s'"), vol->target.path); - close(fd); - return -1; + goto cleanup; } } } @@ -1079,14 +1144,38 @@ static int createRaw(virConnectPtr conn, virReportSystemError(conn, errno, _("cannot close file '%s'"), vol->target.path); - return -1; + goto cleanup; } + fd = -1; - return 0; + if (inputfd != -1 && close(inputfd) < 0) { + virReportSystemError(conn, errno, + _("cannot close file '%s'"), + inputvol->target.path); + goto cleanup; + } + inputfd = -1; + + ret = 0; +cleanup: + if (fd != -1) + close(fd); + if (inputfd != -1) + close(inputfd); + + return ret; } static int createFileDir(virConnectPtr conn, - virStorageVolDefPtr vol) { + virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol) { + if (inputvol) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", + _("cannot copy from volume to a directory volume")); + return -1; + } + if (mkdir(vol->target.path, vol->target.perms.mode) < 0) { virReportSystemError(conn, errno, _("cannot create path '%s'"), @@ -1099,20 +1188,56 @@ static int createFileDir(virConnectPtr conn, #if HAVE_QEMU_IMG static int createQemuImg(virConnectPtr conn, - virStorageVolDefPtr vol) { + virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol) { + char size[100]; + const char *type = virStorageVolFormatFileSystemTypeToString(vol->target.format); const char *backingType = vol->backingStore.path ? virStorageVolFormatFileSystemTypeToString(vol->backingStore.format) : NULL; - char size[100]; + + const char *inputBackingPath = (inputvol ? inputvol->backingStore.path + : NULL); + const char *inputPath = inputvol ? inputvol->target.path : NULL; + /* Treat input block devices as 'raw' format */ + const char *inputType = inputPath ? + virStorageVolFormatFileSystemTypeToString(inputvol->type == VIR_STORAGE_VOL_BLOCK ? VIR_STORAGE_VOL_FILE_RAW : inputvol->target.format) : + NULL; + const char **imgargv; const char *imgargvnormal[] = { - QEMU_IMG, "create", "-f", type, vol->target.path, size, NULL, + QEMU_IMG, "create", + "-f", type, + vol->target.path, + size, + NULL, }; /* XXX including "backingType" here too, once QEMU accepts * the patches to specify it. It'll probably be -F backingType */ const char *imgargvbacking[] = { - QEMU_IMG, "create", "-f", type, "-b", vol->backingStore.path, vol->target.path, size, NULL, + QEMU_IMG, "create", + "-f", type, + "-b", vol->backingStore.path, + vol->target.path, + size, + NULL, }; + const char *convargv[] = { + QEMU_IMG, "convert", + "-f", inputType, + "-O", type, + inputPath, + vol->target.path, + NULL, + }; + + if (inputvol) { + imgargv = convargv; + } else if (vol->backingStore.path) { + imgargv = imgargvbacking; + } else { + imgargv = imgargvnormal; + } if (type == NULL) { virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, @@ -1120,9 +1245,27 @@ static int createQemuImg(virConnectPtr conn, vol->target.format); return -1; } - if (vol->backingStore.path == NULL) { - imgargv = imgargvnormal; - } else { + if (inputvol && inputType == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown storage vol type %d"), + inputvol->target.format); + return -1; + } + + if (vol->backingStore.path) { + + /* XXX: Not strictly required: qemu-img has an option a different + * backing store, not really sure what use it serves though, and it + * may cause issues with lvm. Untested essentially. + */ + if (!inputBackingPath || + !STREQ(inputBackingPath, vol->backingStore.path)) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("a different backing store can not " + "be specified.")); + return -1; + } + if (backingType == NULL) { virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, _("unknown storage vol backing store type %d"), @@ -1135,8 +1278,6 @@ static int createQemuImg(virConnectPtr conn, vol->backingStore.path); return -1; } - - imgargv = imgargvbacking; } /* Size in KB */ @@ -1155,10 +1296,18 @@ static int createQemuImg(virConnectPtr conn, * with a partially functional qcow-create. Go figure ??!? */ static int createQemuCreate(virConnectPtr conn, - virStorageVolDefPtr vol) { + virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol) { char size[100]; const char *imgargv[4]; + if (inputvol) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", + _("cannot copy from volume with qcow-create")); + return -1; + } + if (vol->target.format != VIR_STORAGE_VOL_FILE_QCOW2) { virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, _("unsupported storage vol type %d"), @@ -1188,19 +1337,22 @@ static int createQemuCreate(virConnectPtr conn, } #endif /* HAVE_QEMU_IMG, elif HAVE_QCOW_CREATE */ -/** - * Allocate a new file as a volume. This is either done directly - * for raw/sparse files, or by calling qemu-img/qcow-create for - * special kinds of files - */ static int -virStorageBackendFileSystemVolBuild(virConnectPtr conn, - virStorageVolDefPtr vol) +_virStorageBackendFileSystemVolBuild(virConnectPtr conn, + virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol) { int fd; createFile create_func; - if (vol->target.format == VIR_STORAGE_VOL_FILE_RAW) { + if (vol->target.format == VIR_STORAGE_VOL_FILE_RAW && + (!inputvol || + (inputvol->type == VIR_STORAGE_VOL_BLOCK || + inputvol->target.format == VIR_STORAGE_VOL_FILE_RAW))) { + /* Raw file creation + * Raw -> Raw copying + * Block dev -> Raw copying + */ create_func = createRaw; } else if (vol->target.format == VIR_STORAGE_VOL_FILE_DIR) { create_func = createFileDir; @@ -1217,7 +1369,7 @@ virStorageBackendFileSystemVolBuild(virConnectPtr conn, #endif } - if (create_func(conn, vol) < 0) + if (create_func(conn, vol, inputvol) < 0) return -1; if ((fd = open(vol->target.path, O_RDONLY)) < 0) { @@ -1263,6 +1415,27 @@ virStorageBackendFileSystemVolBuild(virConnectPtr conn, return 0; } +/** + * Allocate a new file as a volume. This is either done directly + * for raw/sparse files, or by calling qemu-img/qcow-create for + * special kinds of files + */ +static int +virStorageBackendFileSystemVolBuild(virConnectPtr conn, + virStorageVolDefPtr vol) { + return _virStorageBackendFileSystemVolBuild(conn, vol, NULL); +} + +/* + * Create a storage vol using 'inputvol' as input + */ +static int +virStorageBackendFileSystemVolBuildFrom(virConnectPtr conn, + virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol, + unsigned int flags ATTRIBUTE_UNUSED) { + return _virStorageBackendFileSystemVolBuild(conn, vol, inputvol); +} /** * Remove a volume - just unlinks for now @@ -1305,6 +1478,7 @@ virStorageBackend virStorageBackendDirectory = { .refreshPool = virStorageBackendFileSystemRefresh, .deletePool = virStorageBackendFileSystemDelete, .buildVol = virStorageBackendFileSystemVolBuild, + .buildVolFrom = virStorageBackendFileSystemVolBuildFrom, .createVol = virStorageBackendFileSystemVolCreate, .refreshVol = virStorageBackendFileSystemVolRefresh, .deleteVol = virStorageBackendFileSystemVolDelete, @@ -1320,6 +1494,7 @@ virStorageBackend virStorageBackendFileSystem = { .stopPool = virStorageBackendFileSystemStop, .deletePool = virStorageBackendFileSystemDelete, .buildVol = virStorageBackendFileSystemVolBuild, + .buildVolFrom = virStorageBackendFileSystemVolBuildFrom, .createVol = virStorageBackendFileSystemVolCreate, .refreshVol = virStorageBackendFileSystemVolRefresh, .deleteVol = virStorageBackendFileSystemVolDelete, @@ -1334,6 +1509,7 @@ virStorageBackend virStorageBackendNetFileSystem = { .stopPool = virStorageBackendFileSystemStop, .deletePool = virStorageBackendFileSystemDelete, .buildVol = virStorageBackendFileSystemVolBuild, + .buildVolFrom = virStorageBackendFileSystemVolBuildFrom, .createVol = virStorageBackendFileSystemVolCreate, .refreshVol = virStorageBackendFileSystemVolRefresh, .deleteVol = virStorageBackendFileSystemVolDelete, -- 1.6.0.6

On Mon, May 04, 2009 at 01:43:02PM -0400, Cole Robinson wrote:
Add an extra 'inputvol' parameter to the file volume building routines. This allows us to easily reuse the duplicate functionality.
@@ -1032,15 +1046,68 @@ static int createRaw(virConnectPtr conn, virReportSystemError(conn, errno, _("cannot extend file '%s'"), vol->target.path); - close(fd); - return -1; + goto cleanup; + } + + remain = vol->capacity; + + if (inputfd != -1) { + size_t bytes = 1024 * 1024; + int sparse_interval = 512; + int amtread = -1; + + while (amtread != 0) { + char buf[bytes];
Preferrable not to allocate 1 MB on the stack, since not all OS default to such large stacks as Linux does for threads.
+ char *tmp; + int newamt; + + if (remain < bytes) + bytes = remain; + + if ((amtread = saferead(inputfd, buf, bytes)) < 0) { + virReportSystemError(conn, errno, + _("failed reading from file '%s'"), + inputvol->target.path); + goto cleanup; + } + remain -= amtread; + + /* Loop over amt read in 512 byte increments, looking for sparse + * blocks */ + tmp = buf; + newamt = amtread; + do { + int interval = ((sparse_interval > newamt) ? newamt + : sparse_interval ); + int sparse_counter = interval; + + while (sparse_counter-- && *(tmp+sparse_counter) == '\0') + continue; + + if (sparse_counter < 0) { + if (lseek(fd, interval, SEEK_CUR) < 0) { + virReportSystemError(conn, errno, + _("cannot extend file '%s'"), + vol->target.path); + goto cleanup; + } + } else if (safewrite(fd, tmp, interval) < 0) { + virReportSystemError(conn, errno, + _("failed writing to file '%s'"), + vol->target.path); + goto cleanup; + + } + + tmp += interval; + } while ((newamt -= sparse_interval) > 0); + }
I'm finding this bit of code a little hard to follow. Since you're scanning in fixed 512 byte chunks, how about just doing char buf[512]; memset(buf, 0, sizeof buf); and then just doing a simple loop using memcmp(tmp, buf) over the 1 MB data. Sure, the current code allows for detection of chunks of zeros < 512 in size, but since data is allocated in underlying FS in at least that size chunks, its not worth trying to detect zeros < 512 in length Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On 05/04/2009 01:42 PM, Cole Robinson wrote:
Hi all,
The following patches implement a new API call: virStorageVolCreateXMLFrom.
virStorageVolPtr virStorageVolCreateXMLFrom(virStoragePoolPtr pool, const char *xmldesc, unsigned int flags, virStorageVolPtr clonevol)
Arguments function similar to the regular volume create API, except the parameter 'clonevol' is used as the input source. The API allows for volume cloning, possibly across pools, or within the same pool but converting volume formats.
Tests I performed (and ensured that a VM booted without complaints from the new image):
Raw -> Raw (with successful sparse detection) Raw -> QCOW2 QCOW2 -> Raw QCOW2 -> QCOW2 QCOW2 -> VMDK Raw partition ('disk' volume) -> Raw file
Thanks, Cole
I committed patches 1-4 (Public API w/ docs, remote driver, test driver, virsh cmds), with the API interface change recommended by DV (moving flags to the end of the parameter list). I'll post the updated storage driver implementation soon. Thanks, Cole
participants (3)
-
Cole Robinson
-
Daniel P. Berrange
-
Daniel Veillard