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

This is a corrected version of the domain metadata modification API. I dropped the limit on length of the domain title and added skipping of newlines added at the end of file by some editors, which made --edit unusable with --title for the new desc command. 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 | 24 ++ include/libvirt/virterror.h | 1 + src/conf/domain_conf.c | 11 + src/conf/domain_conf.h | 1 + src/driver.h | 16 ++ src/libvirt.c | 181 +++++++++++++ src/libvirt_public.syms | 2 + src/qemu/qemu_driver.c | 174 ++++++++++++ src/remote/remote_driver.c | 2 + src/remote/remote_protocol.x | 24 ++- src/util/virterror.c | 6 + .../qemu-simple-description-title.xml | 27 ++ tests/qemuxml2argvdata/qemuxml2argv-minimal.xml | 5 + tools/virsh.c | 283 ++++++++++++++++++-- tools/virsh.pod | 34 +++- 17 files changed, 787 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 contain newlines and should be reasonably short. *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 --- Diff to v1: - Dropped length limit of <title> docs/formatdomain.html.in | 6 ++++ docs/schemas/domaincommon.rng | 13 +++++++++- src/conf/domain_conf.c | 11 ++++++++ src/conf/domain_conf.h | 1 + .../qemu-simple-description-title.xml | 27 ++++++++++++++++++++ tests/qemuxml2argvdata/qemuxml2argv-minimal.xml | 5 +++ 6 files changed, 62 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..3e1f8ff 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]*</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/src/conf/domain_conf.c b/src/conf/domain_conf.c index 6551a79..9e1a628 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,14 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, VIR_FREE(tmp); } + /* Extract short description of domain (title) */ + def->title = virXPathString("string(./title[1])", ctxt); + if (def->title && strchr(def->title, '\n')) { + virDomainReportError(VIR_ERR_XML_ERROR, "%s", + _("Domain title can't contain newlines")); + goto error; + } + /* Extract documentation if present */ def->description = virXPathString("string(./description[1])", ctxt); @@ -11455,6 +11464,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 01/30/2012 08:09 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 contain newlines and should be reasonably short.
s/title contain/title may not contain/
+++ 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,
Drop the 'capped at 40 bytes'
@@ -29,6 +37,9 @@ <ref name="metadata"/> </optional> <optional> + <ref name="title"/> + </optional>
Personally, I'd stick title before description, rather than after metadata (you want the most important stuff to come first, and as a title is intended to convey more information than name and in less space than description, that argues it should be first, not buried behind several lines of XML).
@@ -11455,6 +11464,8 @@ virDomainDefFormatInternal(virDomainDefPtr def, xmlIndentTreeOutput = oldIndentTreeOutput; }
+ virBufferEscapeString(buf, " <title>%s</title>\n", def->title);
Likewise, on output, I'd format <title> before <description>.
+++ 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;
Here's a case where you already did it the way I'm talking about :)
+++ 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>
Hmm, you aren't testing this in any path that reformats the parsed input, or you would have caught the ordering issue. Actually, we haven't stuck very many files in domainschemadata; most of the time we have called a file in qemuxml2argvdata good enough (since the domainschema test really does test both directories for well-formedness; but domainscehmadata is input only, while the qemuxml2argvdata directory also does further testing on the XML such as command line conversion and re-formatting the output).
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>
If you go with my ordering, this test needs a bit of a tweak. ACK with the nits fixed. -- 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 --- Diff to v1: - lifted the length restriction and tweaked commands according to that - moved argument checks from driver to main library implementation - sorted entries in symbol list include/libvirt/libvirt.h.in | 24 ++++++ include/libvirt/virterror.h | 1 + src/driver.h | 16 ++++ src/libvirt.c | 181 ++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 2 + src/remote/remote_driver.c | 2 + src/remote/remote_protocol.x | 24 ++++++- src/util/virterror.c | 6 ++ 8 files changed, 255 insertions(+), 1 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 0a7b324..da46b24 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 <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; + +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 9844cbe..9dbadfe 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 ba7dbc4..71b49e0 100644 --- a/src/driver.h +++ b/src/driver.h @@ -810,6 +810,20 @@ typedef int unsigned int ncpus, 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: * @@ -981,6 +995,8 @@ struct _virDriver { virDrvDomainSetBlockIoTune domainSetBlockIoTune; virDrvDomainGetBlockIoTune domainGetBlockIoTune; virDrvDomainGetCPUStats domainGetCPUStats; + virDrvDomainSetMetadata domainSetMetadata; + virDrvDomainGetMetadata domainGetMetadata; }; typedef int diff --git a/src/libvirt.c b/src/libvirt.c index e702a34..5d4669c 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -8887,6 +8887,187 @@ 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 no + * newlines are permitted, and should be short (although the length is + * not enforced). For these two options @key and @uri are irrelevant 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; + } + + 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: + virLibDomainError(VIR_ERR_INVALID_ARG, "%s", + _("Invalid metadata type")); + goto error; + 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. + * + * 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; + } + + if ((flags & VIR_DOMAIN_AFFECT_LIVE) && + (flags & VIR_DOMAIN_AFFECT_CONFIG)) { + virLibDomainError(VIR_ERR_INVALID_ARG, "%s", + _("Can't specify both LIVE and CONFIG flags")); + 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: + virLibDomainError(VIR_ERR_INVALID_ARG, "%s", + _("Invalid metadata type")); + goto error; + 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 * @secmodel: pointer to a virSecurityModel structure diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 1c4e0a3..dbbc189 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -519,7 +519,9 @@ LIBVIRT_0.9.9 { LIBVIRT_0.9.10 { global: virDomainGetCPUStats; + virDomainGetMetadata; virDomainPMSuspendForDuration; + virDomainSetMetadata; virDomainShutdownFlags; virStorageVolResize; virStorageVolWipePattern; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 61b96e9..210f59f 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -4849,6 +4849,8 @@ static virDriver remote_driver = { .domainSetNumaParameters = remoteDomainSetNumaParameters, /* 0.9.9 */ .domainGetNumaParameters = remoteDomainGetNumaParameters, /* 0.9.9 */ .domainGetCPUStats = remoteDomainGetCPUStats, /* 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 b58925a..2c46309 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1114,6 +1114,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; @@ -2708,7 +2728,9 @@ enum remote_procedure { REMOTE_PROC_STORAGE_VOL_RESIZE = 260, /* autogen autogen */ REMOTE_PROC_DOMAIN_PM_SUSPEND_FOR_DURATION = 261, /* autogen autogen */ - REMOTE_PROC_DOMAIN_GET_CPU_STATS = 262 /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_GET_CPU_STATS = 262, /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_SET_METADATA = 263, /* autogen autogen */ + REMOTE_PROC_DOMAIN_GET_METADATA = 264 /* 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 31ddd9d..640f5d8 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -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"); + break; } return (errmsg); } -- 1.7.3.4

On 01/30/2012 08:09 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 --- Diff to v1: - lifted the length restriction and tweaked commands according to that - moved argument checks from driver to main library implementation - sorted entries in symbol list
include/libvirt/libvirt.h.in | 24 ++++++ include/libvirt/virterror.h | 1 + src/driver.h | 16 ++++ src/libvirt.c | 181 ++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 2 + src/remote/remote_driver.c | 2 + src/remote/remote_protocol.x | 24 ++++++-
'yum install dwarves' then 'make check' fails, because you didn't update src/remote_protocol-structs.
/** + * 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 + * can be set to NULL.
s/can be/must be/ (since you error out if they are non-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)
I think we've settled on a good API.
+ + 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;
These are okay,
+ default: + virLibDomainError(VIR_ERR_INVALID_ARG, "%s", + _("Invalid metadata type")); + goto error; + break;
but I don't like this one. If you have an older client trying to talk to a newer server, where the newer server understands a fourth type of metadata, then we should not be getting in the way of the client passing that data over RPC.
+/** + * 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.
Also mention: Otherwise, uri 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=%p, flags=%x", + type, uri, flags);
Here (and in the setter), I'd use "uri=%s"/NULLSTR(uri), rather "uri=%p"/uri, as that makes debugging a bit nicer when we know we have a validly-null string.
+ + 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, "%s", + _("Can't specify both LIVE and CONFIG flags")); + goto error;
Elsewhere, we just used the terser: 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;
Again, fine with these, (we have a defined semantic for these flags)
+ default: + virLibDomainError(VIR_ERR_INVALID_ARG, "%s", + _("Invalid metadata type")); + goto error; + break;
but not this (this prevents expansion).
--- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1114,6 +1114,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;
This should be remote_nonnull_string (errors are returned automatically; the _ret struct is only used on success, which means it will never be used for NULL).
@@ -2708,7 +2728,9 @@ enum remote_procedure { REMOTE_PROC_STORAGE_VOL_RESIZE = 260, /* autogen autogen */
REMOTE_PROC_DOMAIN_PM_SUSPEND_FOR_DURATION = 261, /* autogen autogen */ - REMOTE_PROC_DOMAIN_GET_CPU_STATS = 262 /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_GET_CPU_STATS = 262, /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_SET_METADATA = 263, /* autogen autogen */ + REMOTE_PROC_DOMAIN_GET_METADATA = 264 /* autogen autogen */
I'm surprised our generator didn't complain about things - it should be requiring nonnull_string in *_ret (but that can be a patch for a different day). I'm interested in this API making it into 0.9.10, and I think you're close enough that you could push this after fixing the issues I pointed out above. ACK with above issues fixed. -- 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 --- Diff to v1: - tweaked wording in man page (forgot to change old names) - added removal of newline at the end of domain's title for convinience (and workaround of editors that add the newline automaticaly) tools/virsh.c | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++---- tools/virsh.pod | 34 +++++++- 2 files changed, 296 insertions(+), 21 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 3a59746..7dc2deb 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,179 @@ 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; + char *tmpstr; + 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; + } + + /* strip a possible newline at the end of file */ + /* some editors enforce a newline, this makes editing the title + * more convinient */ + if (title && + (tmpstr = strrchr(desc, '\n')) && + *(tmpstr+1) == '\0') + *tmpstr = '\0'; + + 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; } /* @@ -16168,6 +16351,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}, @@ -17933,6 +18117,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 4bc25bf..0dfde56 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 title message] + +Show or modify description and title of a domain. These values are user +fields that allow to store arbitrary textual data to allow easy identifiaction +of domains. Note should be short, although it's not enforced. + +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

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. --- Diff to v1: - move some checks to api function - change error message to "argument unsupported" src/qemu/qemu_driver.c | 174 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 174 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 196fd23..8897c0d 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -11740,6 +11740,178 @@ 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; + } + + 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" + "<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 (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_ARGUMENT_UNSUPPORTED, "%s", + _("QEMU driver does not support" + "<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 +12065,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
participants (2)
-
Eric Blake
-
Peter Krempa