[libvirt] [PATCH 0/4] API for modification of domain metadata

This patchset is based on my previous work to add a domain title. I chose a more general aproach to allow working with the <metadata> element as suggested by Eric and Daniel, although I did not implement the <metadata> operations code. I only provided a patch for the qemu driver for review, but I'll follow up with a patch for LXC as well as I'll have a final version of this. Peter Krempa (4): xml: Add element <title> to allow short description of domains API: Add api to set and get domain metadata virsh: Add support for modifying domain description and titles qemu: Add support for virDomainGetMetadata and virDomainSetMetadata docs/formatdomain.html.in | 6 + docs/schemas/domaincommon.rng | 13 +- include/libvirt/libvirt.h.in | 31 +++ include/libvirt/virterror.h | 1 + src/conf/domain_conf.c | 19 ++ src/conf/domain_conf.h | 1 + src/driver.h | 15 + src/libvirt.c | 125 +++++++++ src/libvirt_public.syms | 2 + src/qemu/qemu_driver.c | 195 ++++++++++++++ src/remote/remote_driver.c | 2 + src/remote/remote_protocol.x | 25 ++- src/util/virterror.c | 6 + .../qemu-simple-description-title.xml | 27 ++ tests/qemuxml2argvdata/qemuxml2argv-minimal.xml | 5 + tools/virsh.c | 274 ++++++++++++++++++-- tools/virsh.pod | 34 +++- 17 files changed, 758 insertions(+), 23 deletions(-) create mode 100644 tests/domainschemadata/qemu-simple-description-title.xml -- 1.7.3.4

This patch adds a new element <title> to the domain XML. This attribute can hold a short title defined by the user to ease the identification of domains. The title is limited to 40 bytes and can't contain newlines. *include/libvirt/libvirt.h.in - add macro specifying maximal note length *docs/formatdomain.html.in *docs/schemas/domaincommon.rng - add schema grammar for the new element and documentation *src/conf/domain_conf.c *src/conf/domain_conf.h - add field to hold the new attribute - add code to parse and create XML with the new attribute --- This patch is basicaly identical with those in previous versions. I just added a macro to set the maximal length of the <title> element to ease future possible changes. docs/formatdomain.html.in | 6 ++++ docs/schemas/domaincommon.rng | 13 +++++++++- include/libvirt/libvirt.h.in | 6 ++++ src/conf/domain_conf.c | 19 ++++++++++++++ src/conf/domain_conf.h | 1 + .../qemu-simple-description-title.xml | 27 ++++++++++++++++++++ tests/qemuxml2argvdata/qemuxml2argv-minimal.xml | 5 +++ 7 files changed, 76 insertions(+), 1 deletions(-) create mode 100644 tests/domainschemadata/qemu-simple-description-title.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 464c4a3..6ccff06 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -37,6 +37,7 @@ <app1:foo xmlns:app1="http://app1.org/app1/">..</app1:foo> <app2:bar xmlns:app2="http://app1.org/app2/">..</app2:bar> </metadata> + <title>A short description - title - of the domain</title> ...</pre> <dl> @@ -72,6 +73,11 @@ (if the application needs structure, they should have sub-elements to their namespace element). <span class="since">Since 0.9.10</span></dd> + + <dt><code>title</code></dt> + <dd>The optional element <code>title</code> provides space for a + shorter description, capped at 40 bytes and with no newline, + <span class="since">since 0.9.10</span>.</dd> </dl> <h3><a name="elementsOS">Operating system booting</a></h3> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 2e53e14..17e7b65 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -6,7 +6,7 @@ <include href='networkcommon.rng'/> <!-- - description element, may be placed anywhere under the root + description and title element, may be placed anywhere under the root --> <define name="description"> <element name="description"> @@ -14,6 +14,14 @@ </element> </define> + <define name="title"> + <element name="title"> + <data type="string"> + <param name='pattern'>[^\n]{0,40}</param> + </data> + </element> + </define> + <!-- We handle only document defining a domain --> @@ -29,6 +37,9 @@ <ref name="metadata"/> </optional> <optional> + <ref name="title"/> + </optional> + <optional> <ref name="cpu"/> </optional> <optional> diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index e99cd00..9a51c4f 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1474,6 +1474,12 @@ int virDomainGetMaxVcpus (virDomainPtr domain); int virDomainGetSecurityLabel (virDomainPtr domain, virSecurityLabelPtr seclabel); +/** + * VIR_DOMAIN_TITLE_LENGTH: + * + * Maximum length of the string in the title field. + */ +#define VIR_DOMAIN_TITLE_LENGTH 40 /* * XML domain description */ diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 978e91c..51a45ff 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1479,6 +1479,7 @@ void virDomainDefFree(virDomainDefPtr def) VIR_FREE(def->cpumask); VIR_FREE(def->emulator); VIR_FREE(def->description); + VIR_FREE(def->title); virBlkioDeviceWeightArrayClear(def->blkio.devices, def->blkio.ndevices); @@ -7093,6 +7094,22 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, VIR_FREE(tmp); } + /* Extract short description of domain (title) */ + def->title = virXPathString("string(./title[1])", ctxt); + if (def->title) { + if (strchr(def->title, '\n')) { + virDomainReportError(VIR_ERR_XML_ERROR, + "%s", _("Domain title can't contain newlines")); + goto error; + } + + if (strlen(def->title) > VIR_DOMAIN_TITLE_LENGTH) { + virDomainReportError(VIR_ERR_XML_ERROR, + "%s", _("Domain title too long")); + goto error; + } + } + /* Extract documentation if present */ def->description = virXPathString("string(./description[1])", ctxt); @@ -11455,6 +11472,8 @@ virDomainDefFormatInternal(virDomainDefPtr def, xmlIndentTreeOutput = oldIndentTreeOutput; } + virBufferEscapeString(buf, " <title>%s</title>\n", def->title); + virBufferAsprintf(buf, " <memory>%lu</memory>\n", def->mem.max_balloon); virBufferAsprintf(buf, " <currentMemory>%lu</currentMemory>\n", def->mem.cur_balloon); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 1d2fb81..46b392e 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1422,6 +1422,7 @@ struct _virDomainDef { int id; unsigned char uuid[VIR_UUID_BUFLEN]; char *name; + char *title; char *description; struct { diff --git a/tests/domainschemadata/qemu-simple-description-title.xml b/tests/domainschemadata/qemu-simple-description-title.xml new file mode 100644 index 0000000..a8a9cac --- /dev/null +++ b/tests/domainschemadata/qemu-simple-description-title.xml @@ -0,0 +1,27 @@ +<domain type='qemu'> + <name>qemu-demo</name> + <uuid>603cc28c-9841-864e-0949-8cc7d3bae9f8</uuid> + <memory>65536</memory> + <currentMemory>65536</currentMemory> + <title>A short description of this domain</title> + <description> + A longer explanation that this domain is a test domain + for validating domain schemas. + </description> + <vcpu>1</vcpu> + <os> + <type arch='x86_64' machine='pc-0.14'>hvm</type> + </os> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>restart</on_crash> + <devices> + <emulator>/usr/bin/qemu-kvm</emulator> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-minimal.xml b/tests/qemuxml2argvdata/qemuxml2argv-minimal.xml index 2f13d46..6cb0b31 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-minimal.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-minimal.xml @@ -1,6 +1,11 @@ <domain type='qemu'> <name>QEMUGuest1</name> <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <description> + A test of qemu's minimal configuration. + This test also tests the description and title elements. + </description> + <title>A description of the test machine.</title> <memory>219100</memory> <currentMemory>219100</currentMemory> <vcpu cpuset='1-4,8-20,525'>1</vcpu> -- 1.7.3.4

On Fri, Jan 27, 2012 at 06:22:16PM +0100, Peter Krempa wrote:
This patch adds a new element <title> to the domain XML. This attribute can hold a short title defined by the user to ease the identification of domains. The title is limited to 40 bytes and can't contain newlines.
I'm not convinced that putting a hard limit on the title length is a good idea. That feels like a limitation just to suit the display used in virsh. If an app only has space to show 40 characters, then it is free to truncate and append '...' Also note that 40 bytes may be equivalent to as few as 12-15 printable characters in UTF-8 with non-western languages since single characters can take 3 bytes or even more Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Fri, Jan 27, 2012 at 05:31:20PM +0000, Daniel P. Berrange wrote:
On Fri, Jan 27, 2012 at 06:22:16PM +0100, Peter Krempa wrote:
This patch adds a new element <title> to the domain XML. This attribute can hold a short title defined by the user to ease the identification of domains. The title is limited to 40 bytes and can't contain newlines.
I'm not convinced that putting a hard limit on the title length is a good idea. That feels like a limitation just to suit the display used in virsh. If an app only has space to show 40 characters, then it is free to truncate and append '...'
Agreed; virsh can do what it wants with the data.
Also note that 40 bytes may be equivalent to as few as 12-15 printable characters in UTF-8 with non-western languages since single characters can take 3 bytes or even more
Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

Dňa 27.1.2012 20:03, Dave Allan wrote / napísal(a):
On Fri, Jan 27, 2012 at 05:31:20PM +0000, Daniel P. Berrange wrote:
On Fri, Jan 27, 2012 at 06:22:16PM +0100, Peter Krempa wrote:
This patch adds a new element<title> to the domain XML. This attribute can hold a short title defined by the user to ease the identification of domains. The title is limited to 40 bytes and can't contain newlines.
I'm not convinced that putting a hard limit on the title length is a good idea. That feels like a limitation just to suit the display used in virsh. If an app only has space to show 40 characters, then it is free to truncate and append '...'
Agreed; virsh can do what it wants with the data.
Ok, there's no problem to lift that restriction. Peter
Also note that 40 bytes may be equivalent to as few as 12-15 printable characters in UTF-8 with non-western languages since single characters can take 3 bytes or even more
Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On 01/27/2012 10:22 AM, Peter Krempa wrote:
This patch adds a new element <title> to the domain XML. This attribute can hold a short title defined by the user to ease the identification of domains. The title is limited to 40 bytes and can't contain newlines.
*include/libvirt/libvirt.h.in - add macro specifying maximal note length *docs/formatdomain.html.in *docs/schemas/domaincommon.rng - add schema grammar for the new element and documentation
*src/conf/domain_conf.c *src/conf/domain_conf.h - add field to hold the new attribute - add code to parse and create XML with the new attribute --- This patch is basicaly identical with those in previous versions. I just added a macro to set the maximal length of the <title> element to ease future possible changes.
And yet length is the sticking point :)
+++ b/tests/qemuxml2argvdata/qemuxml2argv-minimal.xml @@ -1,6 +1,11 @@ <domain type='qemu'> <name>QEMUGuest1</name> <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <description> + A test of qemu's minimal configuration. + This test also tests the description and title elements. + </description> + <title>A description of the test machine.</title>
Thanks for adding the tests. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

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 <metadata> 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 --- This is a major rework based on Eric's and Daniels comments. include/libvirt/libvirt.h.in | 25 ++++++++ include/libvirt/virterror.h | 1 + src/driver.h | 15 +++++ src/libvirt.c | 125 ++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 2 + src/remote/remote_driver.c | 2 + src/remote/remote_protocol.x | 25 ++++++++- src/util/virterror.c | 6 ++ 8 files changed, 200 insertions(+), 1 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 9a51c4f..ab6b90d 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1474,12 +1474,37 @@ int virDomainGetMaxVcpus (virDomainPtr domain); int virDomainGetSecurityLabel (virDomainPtr domain, virSecurityLabelPtr seclabel); +typedef enum { + VIR_DOMAIN_METADATA_DESCRIPTION = 0, /* Operate on <description> */ + VIR_DOMAIN_METADATA_TITLE = 1, /* Operate on <title> */ + VIR_DOMAIN_METADATA_ELEMENT = 2, /* Operate on <metadata> */ + +#ifdef VIR_ENUM_SENTINELS + VIR_DOMAIN_METADATA_LAST +#endif +} virDomainMetadataType; + /** * VIR_DOMAIN_TITLE_LENGTH: * * Maximum length of the string in the title field. */ #define VIR_DOMAIN_TITLE_LENGTH 40 + +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 e896d67..4395637 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -243,6 +243,7 @@ typedef enum { risky domain snapshot revert */ VIR_ERR_OPERATION_ABORTED = 78, /* operation on a domain was canceled/aborted by user */ + VIR_ERR_NO_DOMAIN_METADATA = 79, /* The metadata is not present */ } virErrorNumber; /** diff --git a/src/driver.h b/src/driver.h index df2aa60..153a5eb 100644 --- a/src/driver.h +++ b/src/driver.h @@ -797,6 +797,19 @@ typedef int (*virDrvDomainShutdownFlags)(virDomainPtr domain, 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: @@ -967,6 +980,8 @@ struct _virDriver { virDrvNodeSuspendForDuration nodeSuspendForDuration; virDrvDomainSetBlockIoTune domainSetBlockIoTune; virDrvDomainGetBlockIoTune domainGetBlockIoTune; + virDrvDomainSetMetadata domainSetMetadata; + virDrvDomainGetMetadata domainGetMetadata; }; typedef int diff --git a/src/libvirt.c b/src/libvirt.c index e9d638b..d1a59de 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -8826,6 +8826,131 @@ error: } /** + * 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 + * length-limited to 40 bytes. For these two options @key and @uri are + * irelevant and can 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=%p, key=%p, uri=%p flags=%x", + type, metadata, key, 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; + } + + 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. + * + * 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=%p, flags=%x", + type, uri, flags); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + goto error; + } + + 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 * @secmodel: pointer to a virSecurityModel structure diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 1340b0c..f71ba22 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -520,6 +520,8 @@ LIBVIRT_0.9.10 { global: virDomainShutdownFlags; virStorageVolWipePattern; + virDomainSetMetadata; + virDomainGetMetadata; } LIBVIRT_0.9.9; # .... define new API here using predicted next version number .... diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index f79f53e..c70f5a6 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -4751,6 +4751,8 @@ static virDriver remote_driver = { .domainGetBlockIoTune = remoteDomainGetBlockIoTune, /* 0.9.8 */ .domainSetNumaParameters = remoteDomainSetNumaParameters, /* 0.9.9 */ .domainGetNumaParameters = remoteDomainGetNumaParameters, /* 0.9.9 */ + .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 0f354bb..b4d2035 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1096,6 +1096,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_string metadata; +}; + struct remote_domain_block_job_abort_args { remote_nonnull_domain dom; remote_nonnull_string path; @@ -2667,7 +2687,10 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SET_INTERFACE_PARAMETERS = 256, /* autogen autogen */ REMOTE_PROC_DOMAIN_GET_INTERFACE_PARAMETERS = 257, /* skipgen skipgen */ REMOTE_PROC_DOMAIN_SHUTDOWN_FLAGS = 258, /* autogen autogen */ - REMOTE_PROC_STORAGE_VOL_WIPE_PATTERN = 259 /* autogen autogen */ + REMOTE_PROC_STORAGE_VOL_WIPE_PATTERN = 259, /* autogen autogen */ + REMOTE_PROC_DOMAIN_SET_METADATA = 260, /* autogen autogen */ + + REMOTE_PROC_DOMAIN_GET_METADATA = 261 /* autogen autogen */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/util/virterror.c b/src/util/virterror.c index 85eec8d..5bcfe79 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -1220,6 +1220,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"); + break; } return (errmsg); } -- 1.7.3.4

On 01/27/2012 10:22 AM, Peter Krempa wrote:
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 <metadata> 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 --- This is a major rework based on Eric's and Daniels comments.
I agree that we want to settle on the API, and get that in before the 0.9.10 freeze, even if you don't have <metadata> manipulation wired up by the freeze deadline. I'm still reluctant on the 40-byte limit; maybe a better formulation would be: <description> is free-form text, with no limitations on newlines or length <title> is free-form, but no newlines permitted, and should be short (although the length is not enforced)
/** + * 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 + * length-limited to 40 bytes. For these two options @key and @uri are + * irelevant and can be set to NULL.
s/irelevant/irrelevant/
+ * + * For type VIR_DOMAIN_METADATA_ELEMENT @metadata must be well-formed + * XML belonging to namespace defined by @uri with local name @key.
Our tests give this as an example of metadata: <metadata> <app1:foo xmlns:app1="http://foo.org/">fooish</app1:foo> <app2:bar xmlns:app2="http://bar.com/" maman="baz">barish</app2:bar> </metadata> So based on your description, it looks like I would call: virDomainSetMetadata(domain, VIR_DOMAIN_METADATA_ELEMENT, "<bar maman=\"baz\">barish</bar>", "app2", "http://bar.com/", 0); and that libvirt then renames the top-level element <bar> into <KEY:bar>, then adds the xmlns:KEY=URI attribute to that element, so that it ties the entire XML argument to that namespace? It's probably worth giving an example of this in the documentation, so that users know what their incoming @metadata argument must look like.
+ if (conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + }
It might be nice to guarantee that if type is DESCRIPTION or TITLE, then key and uri are NULL; while if type is ELEMENT, then key and uri are not NULL.
+/** + * 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. + * + * 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=%p, flags=%x", + type, uri, flags); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + goto error; + } +
Probably worth checking that uri is either NULL or not NULL, as required by type. Check that flags does not contain both AFFECT_LIVE and AFFECT_CONFIG at once.
+++ b/src/libvirt_public.syms @@ -520,6 +520,8 @@ LIBVIRT_0.9.10 { global: virDomainShutdownFlags; virStorageVolWipePattern; + virDomainSetMetadata; + virDomainGetMetadata;
I like to keep this sorted. And you'll have to rebase, since another API went in today.
diff --git a/src/util/virterror.c b/src/util/virterror.c index 85eec8d..5bcfe79 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -1220,6 +1220,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"); + break;
Do we use this new error yet? Maybe it's not worth introducing it until we have a use for it. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

This patch adds a new command "desc" to show and modify titles and description for the domains using the new API. This patch also adds a new flag for the "list" command to show titles in the domain list, to allow easy identification of VMs by storing a short description. Example: virsh # list --title Id Name State Title ----------------------------------------------- 0 Domain-0 running Mailserver 1 2 fedora paused --- This patch is almost identical to the previous versions, just tweaked to the changed API. tools/virsh.c | 274 +++++++++++++++++++++++++++++++++++++++++++++++++++---- tools/virsh.pod | 34 +++++++- 2 files changed, 287 insertions(+), 21 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 74655c2..0fce7e9 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -313,6 +313,9 @@ static int vshCommandOptULongLong(const vshCmd *cmd, const char *name, static bool vshCommandOptBool(const vshCmd *cmd, const char *name); static const vshCmdOpt *vshCommandOptArgv(const vshCmd *cmd, const vshCmdOpt *opt); +static char *vshGetDomainDescription(vshControl *ctl, virDomainPtr dom, + bool title, unsigned int flags) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; #define VSH_BYID (1 << 1) #define VSH_BYUUID (1 << 2) @@ -886,6 +889,7 @@ static const vshCmdOptDef opts_list[] = { {"all", VSH_OT_BOOL, 0, N_("list inactive & active domains")}, {"managed-save", VSH_OT_BOOL, 0, N_("mark domains with managed save state")}, + {"title", VSH_OT_BOOL, 0, N_("show short domain description")}, {NULL, 0, 0, NULL} }; @@ -900,7 +904,10 @@ cmdList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) char **names = NULL; int maxname = 0; bool managed = vshCommandOptBool(cmd, "managed-save"); + bool desc = vshCommandOptBool(cmd, "title"); + char *title; int state; + bool ret = false; inactive |= all; @@ -918,8 +925,7 @@ cmdList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) if ((maxid = virConnectListDomains(ctl->conn, &ids[0], maxid)) < 0) { vshError(ctl, "%s", _("Failed to list active domains")); - VIR_FREE(ids); - return false; + goto cleanup; } qsort(&ids[0], maxid, sizeof(int), idsorter); @@ -929,37 +935,52 @@ cmdList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) maxname = virConnectNumOfDefinedDomains(ctl->conn); if (maxname < 0) { vshError(ctl, "%s", _("Failed to list inactive domains")); - VIR_FREE(ids); - return false; + goto cleanup; } if (maxname) { names = vshMalloc(ctl, sizeof(char *) * maxname); if ((maxname = virConnectListDefinedDomains(ctl->conn, names, maxname)) < 0) { vshError(ctl, "%s", _("Failed to list inactive domains")); - VIR_FREE(ids); - VIR_FREE(names); - return false; + goto cleanup; } qsort(&names[0], maxname, sizeof(char*), namesorter); } } - vshPrintExtra(ctl, " %-5s %-30s %s\n", _("Id"), _("Name"), _("State")); - vshPrintExtra(ctl, "----------------------------------------------------\n"); + + if (desc) { + vshPrintExtra(ctl, "%-5s %-30s %-10s %s\n", _("Id"), _("Name"), _("State"), _("Title")); + vshPrintExtra(ctl, "-----------------------------------------------------------\n"); + } else { + vshPrintExtra(ctl, " %-5s %-30s %s\n", _("Id"), _("Name"), _("State")); + vshPrintExtra(ctl, "----------------------------------------------------\n"); + } for (i = 0; i < maxid; i++) { - virDomainPtr dom = virDomainLookupByID(ctl->conn, ids[i]); + virDomainPtr dom = virDomainLookupByID(ctl->conn, ids[i]); /* this kind of work with domains is not atomic operation */ if (!dom) continue; - vshPrint(ctl, " %-5d %-30s %s\n", - virDomainGetID(dom), - virDomainGetName(dom), - _(vshDomainStateToString(vshDomainState(ctl, dom, NULL)))); - virDomainFree(dom); + if (desc) { + if (!(title = vshGetDomainDescription(ctl, dom, true, 0))) + goto cleanup; + + vshPrint(ctl, "%-5d %-30s %-10s %s\n", + virDomainGetID(dom), + virDomainGetName(dom), + _(vshDomainStateToString(vshDomainState(ctl, dom, NULL))), + title); + VIR_FREE(title); + } else { + vshPrint(ctl, " %-5d %-30s %s\n", + virDomainGetID(dom), + virDomainGetName(dom), + _(vshDomainStateToString(vshDomainState(ctl, dom, NULL)))); + } + virDomainFree(dom); } for (i = 0; i < maxname; i++) { virDomainPtr dom = virDomainLookupByName(ctl->conn, names[i]); @@ -975,17 +996,170 @@ cmdList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) virDomainHasManagedSaveImage(dom, 0) > 0) state = -2; - vshPrint(ctl, " %-5s %-30s %s\n", - "-", - names[i], - state == -2 ? _("saved") : _(vshDomainStateToString(state))); + if (desc) { + if (!(title = vshGetDomainDescription(ctl, dom, true, 0))) + goto cleanup; + + vshPrint(ctl, "%-5s %-30s %-10s %s\n", + "-", + names[i], + state == -2 ? _("saved") : _(vshDomainStateToString(state)), + title); + VIR_FREE(title); + } else { + vshPrint(ctl, " %-5s %-30s %s\n", + "-", + names[i], + state == -2 ? _("saved") : _(vshDomainStateToString(state))); virDomainFree(dom); VIR_FREE(names[i]); + } } + + ret = true; +cleanup: VIR_FREE(ids); VIR_FREE(names); - return true; + return ret; +} + +/* + * "desc" command for managing domain description and title + */ +static const vshCmdInfo info_desc[] = { + {"help", N_("show or set domain's description or title")}, + {"desc", N_("Allows to show or modify description or title of a domain.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_desc[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"live", VSH_OT_BOOL, 0, N_("modify/get running state")}, + {"config", VSH_OT_BOOL, 0, N_("modify/get persistent configuration")}, + {"current", VSH_OT_BOOL, 0, N_("modify/get current state configuration")}, + {"title", VSH_OT_BOOL, 0, N_("modify the title instead of description")}, + {"edit", VSH_OT_BOOL, 0, N_("open an editor to modify the description")}, + {"new-desc", VSH_OT_ARGV, 0, N_("message")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdDesc(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + virDomainPtr dom; + bool config = vshCommandOptBool(cmd, "config"); + bool live = vshCommandOptBool(cmd, "live"); + /* current is ignored */ + + bool title = vshCommandOptBool(cmd, "title"); + bool edit = vshCommandOptBool(cmd, "edit"); + + int state; + int type; + char *desc = NULL; + char *desc_edited = NULL; + char *tmp = NULL; + const vshCmdOpt *opt = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; + bool pad = false; + bool ret = false; + unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if ((state = vshDomainState(ctl, dom, NULL)) < 0) { + ret = false; + goto cleanup; + } + + while ((opt = vshCommandOptArgv(cmd, opt))) { + if (pad) + virBufferAddChar(&buf, ' '); + pad = true; + virBufferAdd(&buf, opt->data, -1); + } + + if (live) + flags |= VIR_DOMAIN_AFFECT_LIVE; + if (config) + flags |= VIR_DOMAIN_AFFECT_CONFIG; + if (title) + type = VIR_DOMAIN_METADATA_TITLE; + else + type = VIR_DOMAIN_METADATA_DESCRIPTION; + + if (virBufferError(&buf)) { + vshPrint(ctl, "%s", _("Failed to collect new description/title")); + goto cleanup; + } + desc = virBufferContentAndReset(&buf); + + if (edit || desc) { + if (!desc) { + desc = vshGetDomainDescription(ctl, dom, title, + config?VIR_DOMAIN_XML_INACTIVE:0); + if (!desc) + goto cleanup; + } + + if (edit) { + /* Create and open the temporary file. */ + if (!(tmp = editWriteToTempFile(ctl, desc))) + goto cleanup; + + /* Start the editor. */ + if (editFile(ctl, tmp) == -1) + goto cleanup; + + /* Read back the edited file. */ + if (!(desc_edited = editReadBackFile(ctl, tmp))) + goto cleanup; + + /* Compare original XML with edited. Has it changed at all? */ + if (STREQ(desc, desc_edited)) { + vshPrint(ctl, _("Domain description not changed.\n")); + ret = true; + goto cleanup; + } + + VIR_FREE(desc); + desc = desc_edited; + desc_edited = NULL; + } + + if (virDomainSetMetadata(dom, type, desc, NULL, NULL, flags) < 0) { + vshError(ctl, "%s", + _("Failed to set new domain description")); + goto cleanup; + } + vshPrint(ctl, "%s", _("Domain description updated successfuly")); + } else { + desc = vshGetDomainDescription(ctl, dom, title, + config?VIR_DOMAIN_XML_INACTIVE:0); + if (!desc) + goto cleanup; + + if (strlen(desc) > 0) + vshPrint(ctl, "%s", desc); + else + vshPrint(ctl, _("No description for domain: %s"), + virDomainGetName(dom)); + } + + ret = true; +cleanup: + VIR_FREE(desc_edited); + VIR_FREE(desc); + if (tmp) { + unlink(tmp); + VIR_FREE(tmp); + } + return ret; } /* @@ -16016,6 +16190,7 @@ static const vshCmdDef domManagementCmds[] = { {"cpu-compare", cmdCPUCompare, opts_cpu_compare, info_cpu_compare, 0}, {"create", cmdCreate, opts_create, info_create, 0}, {"define", cmdDefine, opts_define, info_define, 0}, + {"desc", cmdDesc, opts_desc, info_desc, 0}, {"destroy", cmdDestroy, opts_destroy, info_destroy, 0}, {"detach-device", cmdDetachDevice, opts_detach_device, info_detach_device, 0}, @@ -17778,6 +17953,65 @@ vshDomainStateReasonToString(int state, int reason) return N_("unknown"); } +/* extract description or title from domain xml */ +static char * +vshGetDomainDescription(vshControl *ctl, virDomainPtr dom, bool title, + unsigned int flags) +{ + char *desc = NULL; + char *domxml = NULL; + virErrorPtr err = NULL; + xmlDocPtr doc = NULL; + xmlXPathContextPtr ctxt = NULL; + int type; + + if (title) + type = VIR_DOMAIN_METADATA_TITLE; + else + type = VIR_DOMAIN_METADATA_DESCRIPTION; + + if ((desc = virDomainGetMetadata(dom, type, NULL, flags))) { + return desc; + } else { + err = virGetLastError(); + + if (err && err->code == VIR_ERR_NO_DOMAIN_METADATA) { + desc = vshStrdup(ctl, ""); + virResetLastError(); + return desc; + } + + if (err && err->code != VIR_ERR_NO_SUPPORT) + return desc; + } + + /* fall back to xml */ + /* get domains xml description and extract the title/description */ + if (!(domxml = virDomainGetXMLDesc(dom, flags))) { + vshError(ctl, "%s", _("Failed to retrieve domain XML")); + goto cleanup; + } + doc = virXMLParseStringCtxt(domxml, _("(domain_definition)"), &ctxt); + if (!doc) { + vshError(ctl, "%s", _("Couldn't parse domain XML")); + goto cleanup; + } + if (title) + desc = virXPathString("string(./title[1])", ctxt); + else + desc = virXPathString("string(./description[1])", ctxt); + + if (!desc) + desc = vshStrdup(ctl, ""); + +cleanup: + VIR_FREE(domxml); + xmlXPathFreeContext(ctxt); + xmlFreeDoc(doc); + + return desc; +} + /* Return a non-NULL string representation of a typed parameter; exit * if we are out of memory. */ static char * diff --git a/tools/virsh.pod b/tools/virsh.pod index 8599f66..e74254f 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -279,7 +279,7 @@ The XML also show the NUMA topology information if available. Inject NMI to the guest. -=item B<list> [I<--inactive> | I<--all>] [I<--managed-save>] +=item B<list> [I<--inactive> | I<--all>] [I<--managed-save>] [I<--title>] Prints information about existing domains. If no options are specified it prints out information about running domains. @@ -350,6 +350,15 @@ If I<--managed-save> is specified, then domains that have managed save state (only possible if they are in the B<shut off> state) will instead show as B<saved> in the listing. +If I<--title> is specified, then the domain note is printed. The output then +the output looks as follows. + +B<virsh> list --note + Id Name State Title +----------------------------------------------- + 0 Domain-0 running Mailserver 1 + 2 fedora paused + =item B<freecell> [B<cellno> | I<--all>] Prints the available amount of memory on the machine or within a @@ -426,6 +435,29 @@ Define a domain from an XML <file>. The domain definition is registered but not started. If domain is already running, the changes will take effect on the next boot. +=item B<desc> [I<--live> | I<--config>] [I<--title>] [I<--edit>] + [I<--new-desc> New description or note message] + +Show or modify description and note for a domain. These values are user +fields that allow to store arbitrary textual data to allow easy identifiaction +of domains. Note is a short (maximum 40 characters) field. + +Flags I<--live> or I<--config> select wether this command works on live +or persistent definitions of the domain. By default both are infuenced, while +modifying and running definition is used while reading the note. + +If both I<--live> and I<--config> are specified, the I<--config> option takes +predcedence on getting the current description and both live configuration +and config are updated while setting the description. + +Flag I<--edit> specifies that an editor with the contents of current description +or note should be opened and the contents saved back afterwards. + +Flag I<--title> selects operation on the note field instead of description. + +If neither of I<--edit> and I<--new_desc> are specified the note or description +is displayed instead of being modified. + =item B<destroy> I<domain-id> Immediately terminate the domain domain-id. This doesn't give the domain -- 1.7.3.4

On 01/27/2012 10:22 AM, Peter Krempa wrote:
This patch adds a new command "desc" to show and modify titles and description for the domains using the new API.
Are your plans to have "desc" also modify metadata, or would that be better suited to a new command, probably named "metadata"?
This patch also adds a new flag for the "list" command to show titles in the domain list, to allow easy identification of VMs by storing a short description.
Example: virsh # list --title Id Name State Title ----------------------------------------------- 0 Domain-0 running Mailserver 1 2 fedora paused ---
+/* extract description or title from domain xml */ +static char * +vshGetDomainDescription(vshControl *ctl, virDomainPtr dom, bool title, + unsigned int flags) +{ + char *desc = NULL; + char *domxml = NULL; + virErrorPtr err = NULL; + xmlDocPtr doc = NULL; + xmlXPathContextPtr ctxt = NULL; + int type; + + if (title) + type = VIR_DOMAIN_METADATA_TITLE; + else + type = VIR_DOMAIN_METADATA_DESCRIPTION; + + if ((desc = virDomainGetMetadata(dom, type, NULL, flags))) { + return desc; + } else { + err = virGetLastError(); + + if (err && err->code == VIR_ERR_NO_DOMAIN_METADATA) {
Ah, so we are using the new error (hmm, I should re-read the description - we even documented it that way).
+ desc = vshStrdup(ctl, ""); + virResetLastError(); + return desc; + } + + if (err && err->code != VIR_ERR_NO_SUPPORT) + return desc; + } + + /* fall back to xml */ + /* get domains xml description and extract the title/description */ + if (!(domxml = virDomainGetXMLDesc(dom, flags))) { + vshError(ctl, "%s", _("Failed to retrieve domain XML")); + goto cleanup; + } + doc = virXMLParseStringCtxt(domxml, _("(domain_definition)"), &ctxt); + if (!doc) { + vshError(ctl, "%s", _("Couldn't parse domain XML")); + goto cleanup; + } + if (title) + desc = virXPathString("string(./title[1])", ctxt); + else + desc = virXPathString("string(./description[1])", ctxt); + + if (!desc) + desc = vshStrdup(ctl, "");
Looks like a reasonable use of the API.
Prints the available amount of memory on the machine or within a @@ -426,6 +435,29 @@ Define a domain from an XML <file>. The domain definition is registered but not started. If domain is already running, the changes will take effect on the next boot.
+=item B<desc> [I<--live> | I<--config>] [I<--title>] [I<--edit>] + [I<--new-desc> New description or note message] + +Show or modify description and note for a domain. These values are user +fields that allow to store arbitrary textual data to allow easy identifiaction +of domains. Note is a short (maximum 40 characters) field.
Again, careful with the wording. We may want to s/note/title/ to match the XML, and be careful in describing the differences between them. I think this patch is mostly there; but may have more fallout if we make any more tweaks to the public API or description of it. I'm okay if we save <metadata> manipulation from virsh for another day. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

This patch adds support for the new api into the qemu driver to support modification and retireval of domain description and title. This patch does not add support for modifying the <metadata> element. --- A major rework to support the change in API. src/qemu/qemu_driver.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 195 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index ab69dca..8aededa 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -11740,6 +11740,199 @@ cleanup: return ret; } +static int +qemuDomainSetMetadata(virDomainPtr dom, + int type, + const char *metadata, + const char *key ATTRIBUTE_UNUSED, + const char *uri ATTRIBUTE_UNUSED, + unsigned int flags) +{ + struct qemud_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + virDomainDefPtr persistentDef; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG, -1); + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + qemuDriverUnlock(driver); + + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + /* validate title */ + if (type == VIR_DOMAIN_METADATA_TITLE && metadata) { + if (strchr(metadata, '\n')) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("Domain title can't contain newlines")); + goto cleanup; + } + if (strlen(metadata) > VIR_DOMAIN_TITLE_LENGTH) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("Domain title too long")); + goto cleanup; + } + } + + if (virDomainLiveConfigHelperMethod(driver->caps, vm, &flags, + &persistentDef) < 0) + goto cleanup; + + if (flags & VIR_DOMAIN_AFFECT_LIVE) { + switch ((virDomainMetadataType) type) { + case VIR_DOMAIN_METADATA_DESCRIPTION: + VIR_FREE(vm->def->description); + if (metadata && + !(vm->def->description = strdup(metadata))) + goto no_memory; + break; + case VIR_DOMAIN_METADATA_TITLE: + VIR_FREE(vm->def->title); + if (metadata && + !(vm->def->title = strdup(metadata))) + goto no_memory; + break; + case VIR_DOMAIN_METADATA_ELEMENT: + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("QEmu driver does not support modifying" + "<metadata> element")); + goto cleanup; + break; + default: + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("unknown metadata type")); + goto cleanup; + break; + } + } + + if (flags & VIR_DOMAIN_AFFECT_CONFIG) { + switch ((virDomainMetadataType) type) { + case VIR_DOMAIN_METADATA_DESCRIPTION: + VIR_FREE(persistentDef->description); + if (metadata && + !(persistentDef->description = strdup(metadata))) + goto no_memory; + break; + case VIR_DOMAIN_METADATA_TITLE: + VIR_FREE(persistentDef->title); + if (metadata && + !(persistentDef->title = strdup(metadata))) + goto no_memory; + break; + case VIR_DOMAIN_METADATA_ELEMENT: + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("QEmu driver does not support modifying" + "<metadata> element")); + goto cleanup; + default: + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("unknown metadata type")); + goto cleanup; + break; + } + + if (virDomainSaveConfig(driver->configDir, persistentDef) < 0) + goto cleanup; + } + + ret = 0; + +cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +no_memory: + virReportOOMError(); + goto cleanup; +} + +static char * +qemuDomainGetMetadata(virDomainPtr dom, + int type, + const char *uri ATTRIBUTE_UNUSED, + unsigned int flags) +{ + struct qemud_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + virDomainDefPtr def; + char *ret = NULL; + char *field = NULL; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG, NULL); + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + qemuDriverUnlock(driver); + + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (flags & + (VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG)) { + qemuReportError(VIR_ERR_INVALID_ARG, + _("Can't specify both LIVE and CONFIG flags")); + goto cleanup; + } + + if (virDomainLiveConfigHelperMethod(driver->caps, vm, &flags, &def) < 0) + goto cleanup; + + /* use correct domain definition according to flags */ + if (flags & VIR_DOMAIN_AFFECT_LIVE) + def = vm->def; + + switch ((virDomainMetadataType) type) { + case VIR_DOMAIN_METADATA_DESCRIPTION: + field = def->description; + break; + case VIR_DOMAIN_METADATA_TITLE: + field = def->title; + break; + case VIR_DOMAIN_METADATA_ELEMENT: + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("QEmu driver does not support modifying" + "<metadata> element")); + goto cleanup; + break; + default: + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("unknown metadata type")); + goto cleanup; + break; + } + + if (!field) { + qemuReportError(VIR_ERR_NO_DOMAIN_METADATA, "%s", + _("Requested metadata element is not present")); + goto cleanup; + } + + if (!(ret = strdup(field))) { + virReportOOMError(); + goto cleanup; + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} + static virDriver qemuDriver = { .no = VIR_DRV_QEMU, .name = "QEMU", @@ -11893,6 +12086,8 @@ static virDriver qemuDriver = { .domainGetNumaParameters = qemuDomainGetNumaParameters, /* 0.9.9 */ .domainGetInterfaceParameters = qemuDomainGetInterfaceParameters, /* 0.9.9 */ .domainSetInterfaceParameters = qemuDomainSetInterfaceParameters, /* 0.9.9 */ + .domainSetMetadata = qemuDomainSetMetadata, /* 0.9.10 */ + .domainGetMetadata = qemuDomainGetMetadata, /* 0.9.10 */ }; -- 1.7.3.4

On 01/27/2012 10:22 AM, Peter Krempa wrote:
This patch adds support for the new api into the qemu driver to support modification and retireval of domain description and title. This patch does not add support for modifying the <metadata> element. ---
+ + /* validate title */ + if (type == VIR_DOMAIN_METADATA_TITLE && metadata) { + if (strchr(metadata, '\n')) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("Domain title can't contain newlines")); + goto cleanup; + } + if (strlen(metadata) > VIR_DOMAIN_TITLE_LENGTH) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("Domain title too long")); + goto cleanup; + } + }
Since title is relatively new, we may want to do this validation in both domain_conf (when parsing XML) and in libvirt.c (when calling virDomainSetMetadata), so that hypervisors don't have to repeat this code.
+ case VIR_DOMAIN_METADATA_ELEMENT: + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("QEmu driver does not support modifying" + "<metadata> element"));
I'd use VIR_ERR_ARGUMENT_UNSUPPORTED here. All of the arguments were valid, we just don't know how to act on them.
+static char * +qemuDomainGetMetadata(virDomainPtr dom, + int type, + const char *uri ATTRIBUTE_UNUSED, + unsigned int flags) +{ + struct qemud_driver *driver = dom->conn->privateData;
+ + if (flags & + (VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG)) { + qemuReportError(VIR_ERR_INVALID_ARG, + _("Can't specify both LIVE and CONFIG flags")); + goto cleanup; + }
Drop this check. It should live in libvirt.c. And as written, you didn't do the check you wanted - you are rejecting any use of LIVE or CONFIG, rather than the intended simultaneous use of both flags. Looks like a good implementation for 2 of the 3 fields, and again, I'm okay if we don't get <metadata> implemented until a later patch, as long as we're happy that the API will let us do it right. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org
participants (4)
-
Daniel P. Berrange
-
Dave Allan
-
Eric Blake
-
Peter Krempa