From c471e55e1030ad133da734a91427c82d10dd6a86 Mon Sep 17 00:00:00 2001 From: Peter Krempa Date: Wed, 1 Feb 2012 14:03:50 +0100 Subject: [PATCH] API: Add api to set and get domain metadata This patch adds API to modify domain metadata for running and stopped domains. The api supports changing description, title as well as the newly added element. The API has support for storing data in the metadata element using xml namespaces. * include/libvirt/libvirt.h.in * src/libvirt_public.syms - add function headers - add enum to select metadata to operate on - export functions * src/libvirt.c - add public api implementation * src/driver.h - add driver support * src/remote/remote_driver.c * src/remote/remote_protocol.x - wire up the remote protocol * include/libvirt/virterror.h * src/util/virterror.c - add a new error message note that metadata for domain are missing --- include/libvirt/libvirt.h.in | 24 +++++ include/libvirt/virterror.h | 1 + src/driver.h | 16 ++++ src/libvirt.c | 178 +++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 2 + src/remote/remote_driver.c | 2 + src/remote/remote_protocol.x | 24 ++++- src/remote_protocol-structs | 19 ++++ src/util/virterror.c | 8 +- 9 files changed, 272 insertions(+), 2 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index cf53cf260d..07d03fa160 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1496,6 +1496,30 @@ int virDomainGetMaxVcpus (virDomainPtr domain); int virDomainGetSecurityLabel (virDomainPtr domain, virSecurityLabelPtr seclabel); +typedef enum { + VIR_DOMAIN_METADATA_DESCRIPTION = 0, /* Operate on */ + VIR_DOMAIN_METADATA_TITLE = 1, /* Operate on */ + VIR_DOMAIN_METADATA_ELEMENT = 2, /* Operate on <metadata> */ + +#ifdef VIR_ENUM_SENTINELS + VIR_DOMAIN_METADATA_LAST +#endif +} virDomainMetadataType; + +int +virDomainSetMetadata(virDomainPtr domain, + int type, + const char *metadata, + const char *key, + const char *uri, + unsigned int flags); + +char * +virDomainGetMetadata(virDomainPtr domain, + int type, + const char *uri, + unsigned int flags); + /* * XML domain description */ diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 9844cbe23f..9dbadfe63f 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -244,6 +244,7 @@ typedef enum { VIR_ERR_OPERATION_ABORTED = 78, /* operation on a domain was canceled/aborted by user */ VIR_ERR_AUTH_CANCELLED = 79, /* authentication cancelled */ + VIR_ERR_NO_DOMAIN_METADATA = 80, /* The metadata is not present */ } virErrorNumber; /** diff --git a/src/driver.h b/src/driver.h index 9ff5edf18f..d6ee60f498 100644 --- a/src/driver.h +++ b/src/driver.h @@ -816,6 +816,20 @@ typedef int unsigned int maxerrors, unsigned int flags); +typedef int + (*virDrvDomainSetMetadata)(virDomainPtr dom, + int type, + const char *metadata, + const char *key, + const char *uri, + unsigned int flags); + +typedef char * + (*virDrvDomainGetMetadata)(virDomainPtr dom, + int type, + const char *uri, + unsigned int flags); + /** * _virDriver: * @@ -988,6 +1002,8 @@ struct _virDriver { virDrvDomainGetBlockIoTune domainGetBlockIoTune; virDrvDomainGetCPUStats domainGetCPUStats; virDrvDomainGetDiskErrors domainGetDiskErrors; + virDrvDomainSetMetadata domainSetMetadata; + virDrvDomainGetMetadata domainGetMetadata; }; typedef int diff --git a/src/libvirt.c b/src/libvirt.c index fc41a3fad2..659e5db4e4 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -8886,6 +8886,184 @@ error: return -1; } +/** + * virDomainSetMetadata: + * @domain: a domain object + * @type: type of description, from virDomainMetadataType + * @metadata: new metadata text + * @key: XML namespace key, or NULL + * @uri: XML namespace URI, or NULL + * @flags: bitwise-OR of virDomainModificationImpact + * + * Sets the appropriate domain element given by @type to the + * value of @description. A @type of VIR_DOMAIN_METADATA_DESCRIPTION + * is free-form text; VIR_DOMAIN_METADATA_TITLE is free-form, but no + * newlines are permitted, and should be short (although the length is + * not enforced). For these two options @key and @uri are irrelevant and + * must be set to NULL. + * + * For type VIR_DOMAIN_METADATA_ELEMENT @metadata must be well-formed + * XML belonging to namespace defined by @uri with local name @key. + * + * Passing NULL for @metadata says to remove that element from the + * domain XML (passing the empty string leaves the element present). + * + * The resulting metadata will be present in virDomainGetXMLDesc(), + * as well as quick access through virDomainGetMetadata(). + * + * @flags controls whether the live domain, persistent configuration, + * or both will be modified. + * + * Returns 0 on success, -1 in case of failure. + */ +int +virDomainSetMetadata(virDomainPtr domain, + int type, + const char *metadata, + const char *key, + const char *uri, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, + "type=%d, metadata='%s', key='%s', uri='%s', flags=%x", + type, NULLSTR(metadata), NULLSTR(key), NULLSTR(uri), + flags); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + if (conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + switch (type) { + case VIR_DOMAIN_METADATA_TITLE: + if (metadata && strchr(metadata, '\n')) { + virLibDomainError(VIR_ERR_INVALID_ARG, "%s", + _("Domain title can't contain newlines")); + goto error; + } + /* fallthrough */ + case VIR_DOMAIN_METADATA_DESCRIPTION: + if (uri || key) { + virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + break; + case VIR_DOMAIN_METADATA_ELEMENT: + if (!uri || !key) { + virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + break; + default: + /* For future expansion */ + break; + } + + if (conn->driver->domainSetMetadata) { + int ret; + ret = conn->driver->domainSetMetadata(domain, type, metadata, key, uri, + flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return -1; +} + +/** + * virDomainGetMetadata: + * @domain: a domain object + * @type: type of description, from virDomainMetadataType + * @uri: XML namespace identifier + * @flags: bitwise-OR of virDomainModificationImpact + * + * Retrieves the appropriate domain element given by @type. + * If VIR_DOMAIN_METADATA_ELEMENT is requested parameter @uri + * must be set to the name of the namespace the requested elements + * belong to, otherwise must be NULL. + * + * If an element of the domain XML is not present, the resulting + * error will be VIR_ERR_NO_DOMAIN_METADATA. This method forms + * a shortcut for seeing information from virDomainSetMetadata() + * without having to go through virDomainGetXMLDesc(). + * + * @flags controls whether the live domain or persistent + * configuration will be queried. + * + * Returns the metadata string on success (caller must free), + * or NULL in case of failure. + */ +char * +virDomainGetMetadata(virDomainPtr domain, + int type, + const char *uri, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "type=%d, uri='%s', flags=%x", + type, NULLSTR(uri), flags); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + goto error; + } + + if ((flags & VIR_DOMAIN_AFFECT_LIVE) && + (flags & VIR_DOMAIN_AFFECT_CONFIG)) { + virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + switch (type) { + case VIR_DOMAIN_METADATA_TITLE: + case VIR_DOMAIN_METADATA_DESCRIPTION: + if (uri) { + virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + break; + case VIR_DOMAIN_METADATA_ELEMENT: + if (!uri) { + virLibDomainError(VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + break; + default: + /* For future expansion */ + break; + } + + conn = domain->conn; + + if (conn->driver->domainGetMetadata) { + char *ret; + if (!(ret = conn->driver->domainGetMetadata(domain, type, uri, flags))) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return NULL; +} + /** * virNodeGetSecurityModel: * @conn: a connection object diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index ced9fb3969..9bbd3082f6 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -520,7 +520,9 @@ LIBVIRT_0.9.10 { global: virDomainGetCPUStats; virDomainGetDiskErrors; + virDomainGetMetadata; virDomainPMSuspendForDuration; + virDomainSetMetadata; virDomainShutdownFlags; virStorageVolResize; virStorageVolWipePattern; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 09b5ace666..34816ad440 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -4917,6 +4917,8 @@ static virDriver remote_driver = { .domainGetNumaParameters = remoteDomainGetNumaParameters, /* 0.9.9 */ .domainGetCPUStats = remoteDomainGetCPUStats, /* 0.9.10 */ .domainGetDiskErrors = remoteDomainGetDiskErrors, /* 0.9.10 */ + .domainSetMetadata = remoteDomainSetMetadata, /* 0.9.10 */ + .domainGetMetadata = remoteDomainGetMetadata, /* 0.9.10 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 10fd2940a8..efb209a23e 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1123,6 +1123,26 @@ struct remote_domain_set_autostart_args { int autostart; }; +struct remote_domain_set_metadata_args { + remote_nonnull_domain dom; + int type; + remote_string metadata; + remote_string key; + remote_string uri; + unsigned int flags; +}; + +struct remote_domain_get_metadata_args { + remote_nonnull_domain dom; + int type; + remote_string uri; + unsigned int flags; +}; + +struct remote_domain_get_metadata_ret { + remote_nonnull_string metadata; +}; + struct remote_domain_block_job_abort_args { remote_nonnull_domain dom; remote_nonnull_string path; @@ -2729,7 +2749,9 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_PM_SUSPEND_FOR_DURATION = 261, /* autogen autogen */ REMOTE_PROC_DOMAIN_GET_CPU_STATS = 262, /* skipgen skipgen */ - REMOTE_PROC_DOMAIN_GET_DISK_ERRORS = 263 /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_GET_DISK_ERRORS = 263, /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_SET_METADATA = 264, /* autogen autogen */ + REMOTE_PROC_DOMAIN_GET_METADATA = 265 /* autogen autogen */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index ee2207ca52..8b66a5fbea 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -788,6 +788,23 @@ struct remote_domain_set_autostart_args { remote_nonnull_domain dom; int autostart; }; +struct remote_domain_set_metadata_args { + remote_nonnull_domain dom; + int type; + remote_string metadata; + remote_string key; + remote_string uri; + u_int flags; +}; +struct remote_domain_get_metadata_args { + remote_nonnull_domain dom; + int type; + remote_string uri; + u_int flags; +}; +struct remote_domain_get_metadata_ret { + remote_nonnull_string metadata; +}; struct remote_domain_block_job_abort_args { remote_nonnull_domain dom; remote_nonnull_string path; @@ -2146,4 +2163,6 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_PM_SUSPEND_FOR_DURATION = 261, REMOTE_PROC_DOMAIN_GET_CPU_STATS = 262, REMOTE_PROC_DOMAIN_GET_DISK_ERRORS = 263, + REMOTE_PROC_DOMAIN_SET_METADATA = 264, + REMOTE_PROC_DOMAIN_GET_METADATA = 265, }; diff --git a/src/util/virterror.c b/src/util/virterror.c index 31ddd9d2d3..edd5198640 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -1,7 +1,7 @@ /* * virterror.c: implements error handling and reporting code for libvirt * - * Copy: Copyright (C) 2006, 2008-2011 Red Hat, Inc. + * Copy: Copyright (C) 2006, 2008-2012 Red Hat, Inc. * * See COPYING.LIB for the License of this software * @@ -1226,6 +1226,12 @@ virErrorMsg(virErrorNumber error, const char *info) else errmsg = _("operation aborted: %s"); break; + case VIR_ERR_NO_DOMAIN_METADATA: + if (info == NULL) + errmsg = _("metadata not found"); + else + errmsg = _("metadata not found: %s"); + break; } return (errmsg); }