[libvirt] [PATCH 0/4 v2] New command for changing media

[PATCH 0/4] introduces two new helper functions: vshFindDisk is to find the disk XML node in xml doc with the flags (indicates it's normal disk or changeable disk). VshPrepareDiskXML is to prepare the disk XML for disk changing commands' use, such as detach-disk. The purpose of this patch is to prepare for upcoming new command "change-disk", which will use the two helper functions too. Please see [PATCH 4/4] for a overall description of the new command. v1 - v2 * No changes, only rebasing [PATCH 1/4] virsh: Two new helper functions for disk device changes [PATCH 2/4] virsh: Use vshFindDisk and vshPrepareDiskXML in [PATCH 3/4] virsh: New command cmdChangeMedia [PATCH 4/4] virsh: Add document for cmdChangeMedia Regards, Osier

vshFindDisk is to find the disk node in xml doc with given target and flags (indicates disk type, normal disk or changeable disk). vshPrepareDiskXML is to make changes on the disk node (e.g. create and insert the new <source> node for inserting media of CDROM drive). --- tools/virsh.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 207 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index af78102..ca69f30 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -14210,6 +14210,213 @@ cmdAttachDisk(vshControl *ctl, const vshCmd *cmd) return functionReturn; } +typedef enum { + VSH_FIND_DISK_NORMAL, + VSH_FIND_DISK_CHANGEABLE, +} vshFindDiskFlags; + + +/* Helper function to find disk device in XML doc. Returns the disk + * node on success, or NULL on failure. Caller must free the result + * @type can be either VSH_FIND_DISK_NORMAL or VSH_FIND_DISK_CHANGEABLE. + */ +static xmlNodePtr +vshFindDisk(const char *doc, + const char *target, + unsigned int flags) +{ + xmlDocPtr xml = NULL; + xmlXPathObjectPtr obj= NULL; + xmlXPathContextPtr ctxt = NULL; + xmlNodePtr cur = NULL; + xmlNodePtr ret = NULL; + int i = 0; + + xml = xmlReadDoc((const xmlChar *) doc, "domain.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOWARNING); + + if (!xml) { + vshError(NULL, "%s", _("Failed to get disk information")); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (!ctxt) { + vshError(NULL, "%s", _("Failed to get disk information")); + goto cleanup; + } + + obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt); + if ((obj == NULL) || + (obj->type != XPATH_NODESET) || + (obj->nodesetval == NULL) || + (obj->nodesetval->nodeNr == 0)) { + vshError(NULL, "%s", _("Failed to get disk information")); + goto cleanup; + } + + /* search target */ + for (; i < obj->nodesetval->nodeNr; i++) { + bool is_supported = true; + + if (flags & VSH_FIND_DISK_CHANGEABLE) { + xmlNodePtr n = obj->nodesetval->nodeTab[i]; + is_supported = false; + + /* Check if the disk is CDROM or floppy disk */ + if (xmlStrEqual(n->name, BAD_CAST "disk")) { + char *device_value = virXMLPropString(n, "device"); + + if (STREQ(device_value, "cdrom") || + STREQ(device_value, "floppy")) + is_supported = true; + + VIR_FREE(device_value); + } + } + + cur = obj->nodesetval->nodeTab[i]->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE && + xmlStrEqual(cur->name, BAD_CAST "target")) { + char *tmp = virXMLPropString(cur, "dev"); + + if (is_supported && + STREQ_NULLABLE(tmp, target)) { + ret = xmlCopyNode(obj->nodesetval->nodeTab[i], 1); + VIR_FREE(tmp); + goto cleanup; + } + VIR_FREE(tmp); + } + cur = cur->next; + } + } + + vshError(NULL, _("No found disk whose target is %s"), target); + +cleanup: + xmlXPathFreeObject(obj); + xmlXPathFreeContext(ctxt); + xmlFreeDoc(xml); + return ret; +} + +typedef enum { + VSH_PREPARE_DISK_XML_NONE = 0, + VSH_PREPARE_DISK_XML_EJECT = (1 << 0), + VSH_PREPARE_DISK_XML_INSERT = (1 << 1), + VSH_PREPARE_DISK_XML_UPDATE = (1 << 2), +} vshPrepareDiskXMLFlags; + +/* Helper function to prepare disk XML. Could be used for disk + * detaching, media changing(ejecting, inserting, updating) + * for changedable disk. Returns the processed XML as string on + * success, or NULL on failure. Caller must free the result. + */ +static char * +vshPrepareDiskXML(xmlNodePtr disk_node, + const char *source, + const char *target, + unsigned int flags) +{ + xmlNodePtr cur = NULL; + xmlBufferPtr xml_buf = NULL; + const char *disk_type = NULL; + const char *device_type = NULL; + xmlNodePtr new_node = NULL; + char *ret = NULL; + + if (!disk_node) + return NULL; + + xml_buf = xmlBufferCreate(); + if (!xml_buf) { + vshError(NULL, "%s", _("Failed to allocate memory")); + return NULL; + } + + device_type = virXMLPropString(disk_node, "device"); + + if (STREQ_NULLABLE(device_type, "cdrom") || + STREQ_NULLABLE(device_type, "floppy")) { + bool has_source = false; + disk_type = virXMLPropString(disk_node, "type"); + + cur = disk_node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE && + xmlStrEqual(cur->name, BAD_CAST "source")) { + has_source = true; + break; + } + cur = cur->next; + } + + if (!has_source) { + if (flags & VSH_PREPARE_DISK_XML_EJECT) { + vshError(NULL, _("The disk device whose target is '%s' doesn't " + "have media"), target); + goto error; + } + + if (source) { + new_node = xmlNewNode(NULL, BAD_CAST "source"); + xmlNewProp(new_node, (const xmlChar *)disk_type, + (const xmlChar *)source); + xmlAddChild(disk_node, new_node); + } else if (flags & VSH_PREPARE_DISK_XML_INSERT) { + vshError(NULL, _("No source is specified for inserting media")); + goto error; + } else if (flags & VSH_PREPARE_DISK_XML_UPDATE) { + vshError(NULL, _("No source is specified for updating media")); + goto error; + } + } + + if (has_source) { + if (flags & VSH_PREPARE_DISK_XML_INSERT) { + vshError(NULL, _("The disk device whose target is '%s' already" + "has media"), target); + goto error; + } + + /* Remove the source if it tends to eject/update media. */ + xmlUnlinkNode(cur); + xmlFreeNode(cur); + + if (source && (flags & VSH_PREPARE_DISK_XML_UPDATE)) { + new_node = xmlNewNode(NULL, BAD_CAST "source"); + xmlNewProp(new_node, (const xmlChar *)disk_type, + (const xmlChar *)source); + xmlAddChild(disk_node, new_node); + } + } + } + + if (xmlNodeDump(xml_buf, NULL, disk_node, 0, 0) < 0) { + vshError(NULL, "%s", _("Failed to create XML")); + goto error; + } + + goto cleanup; + +error: + xmlBufferFree(xml_buf); + xml_buf = NULL; + +cleanup: + VIR_FREE(device_type); + VIR_FREE(disk_type); + if (xml_buf) { + ret = vshCalloc(NULL, xmlBufferLength(xml_buf), sizeof(char)); + memcpy(ret, (char *)xmlBufferContent(xml_buf), xmlBufferLength(xml_buf)); + xmlBufferFree(xml_buf); + } + return ret; +} + /* * "detach-disk" command */ -- 1.7.7.3

On 02/03/2012 02:23 AM, Osier Yang wrote:
vshFindDisk is to find the disk node in xml doc with given target and flags (indicates disk type, normal disk or changeable disk).
vshPrepareDiskXML is to make changes on the disk node (e.g. create and insert the new <source> node for inserting media of CDROM drive). --- tools/virsh.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 207 insertions(+), 0 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index af78102..ca69f30 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -14210,6 +14210,213 @@ cmdAttachDisk(vshControl *ctl, const vshCmd *cmd) return functionReturn; }
+typedef enum { + VSH_FIND_DISK_NORMAL, + VSH_FIND_DISK_CHANGEABLE, +} vshFindDiskFlags;
If these were really flags, they would be (1 << 0) and (1 << 1), not 0 and 1.
+ + +/* Helper function to find disk device in XML doc. Returns the disk + * node on success, or NULL on failure. Caller must free the result + * @type can be either VSH_FIND_DISK_NORMAL or VSH_FIND_DISK_CHANGEABLE. + */ +static xmlNodePtr +vshFindDisk(const char *doc, + const char *target, + unsigned int flags)
I think it may be simpler to just treat this parameter as bool changeable; and pass false for normal disks and true for cdrom, unless you really do plan to add more flags later.
+{ + xmlDocPtr xml = NULL; + xmlXPathObjectPtr obj= NULL; + xmlXPathContextPtr ctxt = NULL; + xmlNodePtr cur = NULL; + xmlNodePtr ret = NULL; + int i = 0; + + xml = xmlReadDoc((const xmlChar *) doc, "domain.xml", NULL,
Shouldn't we be using something like virXMLParseString(), and not raw xmlReadDoc(), for better error reporting purposes? No one else in this file uses xmlReadDoc, and we _aren't_ reading from a file named 'domain.xml', but from an in-memory string (where using something like _("(domain_definition)") will give a better error message on a parse error).
+ XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOWARNING); + + if (!xml) { + vshError(NULL, "%s", _("Failed to get disk information")); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (!ctxt) { + vshError(NULL, "%s", _("Failed to get disk information")); + goto cleanup; + } + + obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt); + if ((obj == NULL) || + (obj->type != XPATH_NODESET) || + (obj->nodesetval == NULL) || + (obj->nodesetval->nodeNr == 0)) { + vshError(NULL, "%s", _("Failed to get disk information")); + goto cleanup; + } + + /* search target */ + for (; i < obj->nodesetval->nodeNr; i++) { + bool is_supported = true; + + if (flags & VSH_FIND_DISK_CHANGEABLE) { + xmlNodePtr n = obj->nodesetval->nodeTab[i]; + is_supported = false; + + /* Check if the disk is CDROM or floppy disk */ + if (xmlStrEqual(n->name, BAD_CAST "disk")) { + char *device_value = virXMLPropString(n, "device"); + + if (STREQ(device_value, "cdrom") || + STREQ(device_value, "floppy")) + is_supported = true; + + VIR_FREE(device_value); + } + }
Right here, couldn't you just 'continue' the outer loop if is_supported is false, rather than spinning your wheels...
+ + cur = obj->nodesetval->nodeTab[i]->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE && + xmlStrEqual(cur->name, BAD_CAST "target")) { + char *tmp = virXMLPropString(cur, "dev"); + + if (is_supported && + STREQ_NULLABLE(tmp, target)) {
...on an inner loop that will never resolve?
+ ret = xmlCopyNode(obj->nodesetval->nodeTab[i], 1); + VIR_FREE(tmp); + goto cleanup; + } + VIR_FREE(tmp); + } + cur = cur->next; + } + } + + vshError(NULL, _("No found disk whose target is %s"), target); + +cleanup: + xmlXPathFreeObject(obj); + xmlXPathFreeContext(ctxt); + xmlFreeDoc(xml); + return ret; +} + +typedef enum { + VSH_PREPARE_DISK_XML_NONE = 0, + VSH_PREPARE_DISK_XML_EJECT = (1 << 0), + VSH_PREPARE_DISK_XML_INSERT = (1 << 1), + VSH_PREPARE_DISK_XML_UPDATE = (1 << 2), +} vshPrepareDiskXMLFlags;
Can you really eject and update at the same time? That is, should this be a linear enum (1, 2, 3) rather than bits (1, 2, 4)?
+ +/* Helper function to prepare disk XML. Could be used for disk + * detaching, media changing(ejecting, inserting, updating) + * for changedable disk. Returns the processed XML as string on
s/changedable/changeable/
+ * success, or NULL on failure. Caller must free the result. + */ +static char * +vshPrepareDiskXML(xmlNodePtr disk_node, + const char *source, + const char *target, + unsigned int flags) +{ + xmlNodePtr cur = NULL; + xmlBufferPtr xml_buf = NULL; + const char *disk_type = NULL; + const char *device_type = NULL; + xmlNodePtr new_node = NULL; + char *ret = NULL; + + if (!disk_node) + return NULL; + + xml_buf = xmlBufferCreate(); + if (!xml_buf) { + vshError(NULL, "%s", _("Failed to allocate memory")); + return NULL; + } + + device_type = virXMLPropString(disk_node, "device"); +
+ + if (xmlNodeDump(xml_buf, NULL, disk_node, 0, 0) < 0) { + vshError(NULL, "%s", _("Failed to create XML")); + goto error; + } + + goto cleanup; + +error: + xmlBufferFree(xml_buf); + xml_buf = NULL;
I've generally seen the style: ret = ... cleanup: ... return ret; error: ... goto cleanup: more than your style of: ret = ... goto cleanup; error: ... cleanup: ... return ret;
+ +cleanup: + VIR_FREE(device_type); + VIR_FREE(disk_type); + if (xml_buf) { + ret = vshCalloc(NULL, xmlBufferLength(xml_buf), sizeof(char));
sizeof(char) is always 1; it always looks fishy to me to see it without good reason. vshCalloc generally wants a non-NULL first parameter, for better logging of OOM messages. Is it any easier to just use: if (VIR_ALLOC_N(ret, xmlBufferLength(xml_buf)) < 0) error reporting -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 02/14/2012 05:59 AM, Eric Blake wrote:
On 02/03/2012 02:23 AM, Osier Yang wrote:
vshFindDisk is to find the disk node in xml doc with given target and flags (indicates disk type, normal disk or changeable disk).
vshPrepareDiskXML is to make changes on the disk node (e.g. create and insert the new<source> node for inserting media of CDROM drive). --- tools/virsh.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 207 insertions(+), 0 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index af78102..ca69f30 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -14210,6 +14210,213 @@ cmdAttachDisk(vshControl *ctl, const vshCmd *cmd) return functionReturn; }
+typedef enum { + VSH_FIND_DISK_NORMAL, + VSH_FIND_DISK_CHANGEABLE, +} vshFindDiskFlags;
If these were really flags, they would be (1<< 0) and (1<< 1), not 0 and 1.
+ + +/* Helper function to find disk device in XML doc. Returns the disk + * node on success, or NULL on failure. Caller must free the result + * @type can be either VSH_FIND_DISK_NORMAL or VSH_FIND_DISK_CHANGEABLE. + */ +static xmlNodePtr +vshFindDisk(const char *doc, + const char *target, + unsigned int flags)
I think it may be simpler to just treat this parameter as bool changeable; and pass false for normal disks and true for cdrom, unless you really do plan to add more flags later.
That was the intention, the function could be used to find disk in other special way in future. Such as find a disk with device == lun.
+{ + xmlDocPtr xml = NULL; + xmlXPathObjectPtr obj= NULL; + xmlXPathContextPtr ctxt = NULL; + xmlNodePtr cur = NULL; + xmlNodePtr ret = NULL; + int i = 0; + + xml = xmlReadDoc((const xmlChar *) doc, "domain.xml", NULL,
Shouldn't we be using something like virXMLParseString(), and not raw xmlReadDoc(), for better error reporting purposes? No one else in this file uses xmlReadDoc, and we _aren't_ reading from a file named 'domain.xml', but from an in-memory string (where using something like _("(domain_definition)") will give a better error message on a parse error).
Agreed, virXMLParseString() is better. :)
+ XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOWARNING); + + if (!xml) { + vshError(NULL, "%s", _("Failed to get disk information")); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (!ctxt) { + vshError(NULL, "%s", _("Failed to get disk information")); + goto cleanup; + } + + obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt); + if ((obj == NULL) || + (obj->type != XPATH_NODESET) || + (obj->nodesetval == NULL) || + (obj->nodesetval->nodeNr == 0)) { + vshError(NULL, "%s", _("Failed to get disk information")); + goto cleanup; + } + + /* search target */ + for (; i< obj->nodesetval->nodeNr; i++) { + bool is_supported = true; + + if (flags& VSH_FIND_DISK_CHANGEABLE) { + xmlNodePtr n = obj->nodesetval->nodeTab[i]; + is_supported = false; + + /* Check if the disk is CDROM or floppy disk */ + if (xmlStrEqual(n->name, BAD_CAST "disk")) { + char *device_value = virXMLPropString(n, "device"); + + if (STREQ(device_value, "cdrom") || + STREQ(device_value, "floppy")) + is_supported = true; + + VIR_FREE(device_value); + } + }
Right here, couldn't you just 'continue' the outer loop if is_supported is false, rather than spinning your wheels...
+ + cur = obj->nodesetval->nodeTab[i]->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE&& + xmlStrEqual(cur->name, BAD_CAST "target")) { + char *tmp = virXMLPropString(cur, "dev"); + + if (is_supported&& + STREQ_NULLABLE(tmp, target)) {
...on an inner loop that will never resolve?
Oops, yes.
+ ret = xmlCopyNode(obj->nodesetval->nodeTab[i], 1); + VIR_FREE(tmp); + goto cleanup; + } + VIR_FREE(tmp); + } + cur = cur->next; + } + } + + vshError(NULL, _("No found disk whose target is %s"), target); + +cleanup: + xmlXPathFreeObject(obj); + xmlXPathFreeContext(ctxt); + xmlFreeDoc(xml); + return ret; +} + +typedef enum { + VSH_PREPARE_DISK_XML_NONE = 0, + VSH_PREPARE_DISK_XML_EJECT = (1<< 0), + VSH_PREPARE_DISK_XML_INSERT = (1<< 1), + VSH_PREPARE_DISK_XML_UPDATE = (1<< 2), +} vshPrepareDiskXMLFlags;
Can you really eject and update at the same time? That is, should this be a linear enum (1, 2, 3) rather than bits (1, 2, 4)?
It should be enum, will update.
+ +/* Helper function to prepare disk XML. Could be used for disk + * detaching, media changing(ejecting, inserting, updating) + * for changedable disk. Returns the processed XML as string on
s/changedable/changeable/
+ * success, or NULL on failure. Caller must free the result. + */ +static char * +vshPrepareDiskXML(xmlNodePtr disk_node, + const char *source, + const char *target, + unsigned int flags) +{ + xmlNodePtr cur = NULL; + xmlBufferPtr xml_buf = NULL; + const char *disk_type = NULL; + const char *device_type = NULL; + xmlNodePtr new_node = NULL; + char *ret = NULL; + + if (!disk_node) + return NULL; + + xml_buf = xmlBufferCreate(); + if (!xml_buf) { + vshError(NULL, "%s", _("Failed to allocate memory")); + return NULL; + } + + device_type = virXMLPropString(disk_node, "device"); +
+ + if (xmlNodeDump(xml_buf, NULL, disk_node, 0, 0)< 0) { + vshError(NULL, "%s", _("Failed to create XML")); + goto error; + } + + goto cleanup; + +error: + xmlBufferFree(xml_buf); + xml_buf = NULL;
I've generally seen the style:
ret = ... cleanup: ... return ret; error: ... goto cleanup:
more than your style of:
ret = ... goto cleanup; error: ... cleanup: ... return ret;
Agreed.
+ +cleanup: + VIR_FREE(device_type); + VIR_FREE(disk_type); + if (xml_buf) { + ret = vshCalloc(NULL, xmlBufferLength(xml_buf), sizeof(char));
sizeof(char) is always 1; it always looks fishy to me to see it without good reason. vshCalloc generally wants a non-NULL first parameter, for better logging of OOM messages. Is it any easier to just use:
if (VIR_ALLOC_N(ret, xmlBufferLength(xml_buf))< 0) error reporting
Agreed, nicer way. Osier

The first use of the two new helper functions. --- tools/virsh.c | 62 ++++++++------------------------------------------------ 1 files changed, 9 insertions(+), 53 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index ca69f30..5dad1d6 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -14436,18 +14436,14 @@ static const vshCmdOptDef opts_detach_disk[] = { static bool cmdDetachDisk(vshControl *ctl, const vshCmd *cmd) { - xmlDocPtr xml = NULL; - xmlXPathObjectPtr obj=NULL; - xmlXPathContextPtr ctxt = NULL; - xmlNodePtr cur = NULL; - xmlBufferPtr xml_buf = NULL; + char *disk_xml = NULL; virDomainPtr dom = NULL; const char *target = NULL; char *doc; - int i = 0, diff_tgt; int ret; bool functionReturn = false; unsigned int flags; + xmlNodePtr disk_node = NULL; if (!vshConnectionUsability(ctl, ctl->conn)) goto cleanup; @@ -14462,60 +14458,21 @@ cmdDetachDisk(vshControl *ctl, const vshCmd *cmd) if (!doc) goto cleanup; - xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt); - VIR_FREE(doc); - if (!xml) { - vshError(ctl, "%s", _("Failed to get disk information")); - goto cleanup; - } - - obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt); - if ((obj == NULL) || (obj->type != XPATH_NODESET) || - (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) { - vshError(ctl, "%s", _("Failed to get disk information")); - goto cleanup; - } - - /* search target */ - for (; i < obj->nodesetval->nodeNr; i++) { - cur = obj->nodesetval->nodeTab[i]->children; - while (cur != NULL) { - if (cur->type == XML_ELEMENT_NODE && - xmlStrEqual(cur->name, BAD_CAST "target")) { - char *tmp_tgt = virXMLPropString(cur, "dev"); - diff_tgt = STREQ(tmp_tgt, target); - VIR_FREE(tmp_tgt); - if (diff_tgt) { - goto hit; - } - } - cur = cur->next; - } - } - vshError(ctl, _("No found disk whose target is %s"), target); - goto cleanup; - - hit: - xml_buf = xmlBufferCreate(); - if (!xml_buf) { - vshError(ctl, "%s", _("Failed to allocate memory")); + if (!(disk_node = vshFindDisk(doc, target, 0))) goto cleanup; - } - if (xmlNodeDump(xml_buf, xml, obj->nodesetval->nodeTab[i], 0, 0) < 0) { - vshError(ctl, "%s", _("Failed to create XML")); + if (!(disk_xml = vshPrepareDiskXML(disk_node, NULL, NULL, 0))) goto cleanup; - } if (vshCommandOptBool(cmd, "persistent")) { flags = VIR_DOMAIN_AFFECT_CONFIG; if (virDomainIsActive(dom) == 1) flags |= VIR_DOMAIN_AFFECT_LIVE; ret = virDomainDetachDeviceFlags(dom, - (char *)xmlBufferContent(xml_buf), + disk_xml, flags); } else { - ret = virDomainDetachDevice(dom, (char *)xmlBufferContent(xml_buf)); + ret = virDomainDetachDevice(dom, disk_xml); } if (ret != 0) { @@ -14526,10 +14483,9 @@ cmdDetachDisk(vshControl *ctl, const vshCmd *cmd) } cleanup: - xmlXPathFreeObject(obj); - xmlXPathFreeContext(ctxt); - xmlFreeDoc(xml); - xmlBufferFree(xml_buf); + xmlFreeNode(disk_node); + VIR_FREE(disk_xml); + VIR_FREE(doc); if (dom) virDomainFree(dom); return functionReturn; -- 1.7.7.3

On 02/03/2012 02:23 AM, Osier Yang wrote:
The first use of the two new helper functions. --- tools/virsh.c | 62 ++++++++------------------------------------------------ 1 files changed, 9 insertions(+), 53 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index ca69f30..5dad1d6 100644 --- a/tools/virsh.c
+ if (!(disk_node = vshFindDisk(doc, target, 0)))
More evidence that VSH_FIND_DISK_NORMAL is not needed, since you passed 0 instead of a name. But overall, looks like a nice re-factorization. ACK. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

One could use it to eject, insert, or update media of the CDROM or floppy drive. See the documents for more details. --- tools/virsh.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 134 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 5dad1d6..95bda67 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -14492,6 +14492,139 @@ cmdDetachDisk(vshControl *ctl, const vshCmd *cmd) } /* + * "change-media" command + */ +static const vshCmdInfo info_change_media[] = { + {"help", N_("insert media into CD or floppy drive")}, + {"desc", N_("Insert media into CD or floppy drive.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_change_media[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"target", VSH_OT_DATA, VSH_OFLAG_REQ, N_("target of disk device")}, + {"source", VSH_OT_DATA, 0, N_("source of the media")}, + {"eject", VSH_OT_BOOL, 0, N_("Eject the media")}, + {"insert", VSH_OT_BOOL, 0, N_("Insert the media")}, + {"update", VSH_OT_BOOL, 0, N_("Update the media")}, + {"current", VSH_OT_BOOL, 0, N_("can be either or both of --live and --config, " + "depends on implementation of hypervisor driver")}, + {"live", VSH_OT_BOOL, 0, N_("alter live configuration of running domain")}, + {"config", VSH_OT_BOOL, 0, N_("alter persistent configuration, effect observed on next boot")}, + {"force", VSH_OT_BOOL, 0, N_("force media insertion")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdChangeMedia(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + const char *source = NULL; + const char *target = NULL; + const char *doc = NULL; + xmlNodePtr disk_node = NULL; + const char *disk_xml = NULL; + int flags = 0; + int config, live, current, force = 0; + int eject, insert, update = 0; + bool ret = false; + unsigned int prepare_flags = 0; + + config = vshCommandOptBool(cmd, "config"); + live = vshCommandOptBool(cmd, "live"); + current = vshCommandOptBool(cmd, "current"); + force = vshCommandOptBool(cmd, "force"); + eject = vshCommandOptBool(cmd, "eject"); + insert = vshCommandOptBool(cmd, "insert"); + update = vshCommandOptBool(cmd, "update"); + + if ((eject && insert) || + (insert && update) || + (eject && update)) { + vshError(ctl, "%s", _("--eject, --insert, and --update must be specified " + "exclusively.")); + return false; + } + + if (!eject && !insert && !update) { + vshError(ctl, "%s", _("One of --eject, --insert, and --update should be " + "specified")); + return false; + } + + if (eject) + prepare_flags |= VSH_PREPARE_DISK_XML_EJECT; + + if (insert) + prepare_flags |= VSH_PREPARE_DISK_XML_INSERT; + + if (update) + prepare_flags |= VSH_PREPARE_DISK_XML_UPDATE; + + if (current) { + if (live || config) { + vshError(ctl, "%s", _("--current must be specified exclusively")); + return false; + } + flags = VIR_DOMAIN_AFFECT_CURRENT; + } else { + if (config) + flags |= VIR_DOMAIN_AFFECT_CONFIG; + if (live) + flags |= VIR_DOMAIN_AFFECT_LIVE; + } + + if (force) + flags |= VIR_DOMAIN_DEVICE_MODIFY_FORCE; + + if (!vshConnectionUsability(ctl, ctl->conn)) + goto cleanup; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + goto cleanup; + + if (vshCommandOptString(cmd, "target", &target) <= 0) + goto cleanup; + + if (vshCommandOptString(cmd, "source", &source) < 0) + goto cleanup; + + if (insert && !source) { + vshError(ctl, "%s", _("No disk source specified for inserting")); + goto cleanup; + } + + if (flags & VIR_DOMAIN_AFFECT_CONFIG) + doc = virDomainGetXMLDesc(dom, VIR_DOMAIN_XML_INACTIVE); + else + doc = virDomainGetXMLDesc(dom, 0); + if (!doc) + goto cleanup; + + if (!(disk_node = vshFindDisk(doc, target, VSH_FIND_DISK_CHANGEABLE))) + goto cleanup; + + if (!(disk_xml = vshPrepareDiskXML(disk_node, source, target, prepare_flags))) + goto cleanup; + + if (virDomainUpdateDeviceFlags(dom, disk_xml, flags) != 0) { + vshError(ctl, "%s", _("Failed to insert media")); + goto cleanup; + } + + vshPrint(ctl, "%s", _("Media inserted successfully\n")); + ret = true; + +cleanup: + VIR_FREE(doc); + xmlFreeNode(disk_node); + VIR_FREE(disk_xml); + if (dom) + virDomainFree(dom); + return ret; +} + +/* * "cpu-compare" command */ static const vshCmdInfo info_cpu_compare[] = { @@ -16584,6 +16717,7 @@ static const vshCmdDef domManagementCmds[] = { {"blockpull", cmdBlockPull, opts_block_pull, info_block_pull, 0}, {"blockjob", cmdBlockJob, opts_block_job, info_block_job, 0}, {"blockresize", cmdBlockResize, opts_block_resize, info_block_resize, 0}, + {"change-media", cmdChangeMedia, opts_change_media, info_change_media, 0}, #ifndef WIN32 {"console", cmdConsole, opts_console, info_console, 0}, #endif -- 1.7.7.3

On 02/03/2012 02:23 AM, Osier Yang wrote:
One could use it to eject, insert, or update media of the CDROM or floppy drive. See the documents for more details. --- tools/virsh.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 134 insertions(+), 0 deletions(-)
I would squash 3/4 and 4/4 into one patch (new commands should be committed with documentation).
+ + if ((eject && insert) || + (insert && update) || + (eject && update)) { + vshError(ctl, "%s", _("--eject, --insert, and --update must be specified " + "exclusively.")); + return false; + } + + if (!eject && !insert && !update) { + vshError(ctl, "%s", _("One of --eject, --insert, and --update should be " + "specified")); + return false; + }
Seems reasonable.
+ + if (eject) + prepare_flags |= VSH_PREPARE_DISK_XML_EJECT; + + if (insert) + prepare_flags |= VSH_PREPARE_DISK_XML_INSERT; + + if (update) + prepare_flags |= VSH_PREPARE_DISK_XML_UPDATE;
But that means that you are using these as an enum, not as a bitmask to be OR'd together.
+ + if (insert && !source) { + vshError(ctl, "%s", _("No disk source specified for inserting")); + goto cleanup; + }
Shouldn't you also error out if (eject && source)?
+ if (virDomainUpdateDeviceFlags(dom, disk_xml, flags) != 0) { + vshError(ctl, "%s", _("Failed to insert media"));
The error message is a bit misleading in the --eject case, since we didn't insert media, but removed it. But overall, I think this is a long over-due command. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 02/14/2012 06:18 AM, Eric Blake wrote:
On 02/03/2012 02:23 AM, Osier Yang wrote:
One could use it to eject, insert, or update media of the CDROM or floppy drive. See the documents for more details. --- tools/virsh.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 134 insertions(+), 0 deletions(-)
I would squash 3/4 and 4/4 into one patch (new commands should be committed with documentation).
+ + if ((eject&& insert) || + (insert&& update) || + (eject&& update)) { + vshError(ctl, "%s", _("--eject, --insert, and --update must be specified " + "exclusively.")); + return false; + } + + if (!eject&& !insert&& !update) { + vshError(ctl, "%s", _("One of --eject, --insert, and --update should be " + "specified")); + return false; + }
Seems reasonable.
+ + if (eject) + prepare_flags |= VSH_PREPARE_DISK_XML_EJECT; + + if (insert) + prepare_flags |= VSH_PREPARE_DISK_XML_INSERT; + + if (update) + prepare_flags |= VSH_PREPARE_DISK_XML_UPDATE;
But that means that you are using these as an enum, not as a bitmask to be OR'd together.
Yeah, it should be enum.
+ + if (insert&& !source) { + vshError(ctl, "%s", _("No disk source specified for inserting")); + goto cleanup; + }
Shouldn't you also error out if (eject&& source)?
"source" is just ignored if (eject).
+ if (virDomainUpdateDeviceFlags(dom, disk_xml, flags) != 0) { + vshError(ctl, "%s", _("Failed to insert media"));
The error message is a bit misleading in the --eject case, since we didn't insert media, but removed it.
Ah, yes, will update.
But overall, I think this is a long over-due command.

--- tools/virsh.pod | 25 +++++++++++++++++++++++++ 1 files changed, 25 insertions(+), 0 deletions(-) diff --git a/tools/virsh.pod b/tools/virsh.pod index 28ad422..6ac7d15 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1475,6 +1475,31 @@ option can be used to force device update, e.g., to eject a CD-ROM even if it is locked/mounted in the domain. See the documentation to learn about libvirt XML format for a device. +=item B<change-media> I<domain-id> I<target> [I<source>] [I<--eject>] +[I<--insert>] [I<--update>] [I<--current>] [I<--live>] [I<--config>] +[I<--force>] + +Change media of CDROM or floppy drive. I<target> is the unique target name +(<target dev='name'/>) of the device. I<source> specifies the path of the media +to be inserted or updated. + +I<--eject> indicates the media will be ejected. +I<--insert> indicates the media will be inserted. I<source> must be specified. +If the device has source (e.g. <source file='media'>), and I<source> is not +specified, I<--update> is equal to I<--eject>. If the device has no source, +and I<source> is specified, I<--update> is equal to I<--insert>. If the device +has source, and I<source> is specified, I<--update> behaves like combination +of I<--eject> and I<--insert>. +If I<--live> is specified, alter live configuration of running guest. +If I<--config> is specified, alter persistent configuration, effect observed +on next boot. +I<--current> can be either or both of I<live> and I<config>, depends on +the hypervisor's implementation. +Both I<--live> and I<--config> flags may be given, but I<--current> is +exclusive. If no flag is specified, behavior is different depending +on hypervisor. +The I<--force> option can be used to force media changing. + =back =head1 NODEDEV COMMANDS -- 1.7.7.3

On 02/03/2012 02:23 AM, Osier Yang wrote:
--- tools/virsh.pod | 25 +++++++++++++++++++++++++ 1 files changed, 25 insertions(+), 0 deletions(-)
diff --git a/tools/virsh.pod b/tools/virsh.pod index 28ad422..6ac7d15 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1475,6 +1475,31 @@ option can be used to force device update, e.g., to eject a CD-ROM even if it is locked/mounted in the domain. See the documentation to learn about libvirt XML format for a device.
+=item B<change-media> I<domain-id> I<target> [I<source>] [I<--eject>] +[I<--insert>] [I<--update>] [I<--current>] [I<--live>] [I<--config>] +[I<--force>]
Given the source code, I think this should be: =item B<change-media> I<domain-id> I<target> {I<--insert> | I<--update> | I<--eject>} [I<source>] [I<--force>] [[I<--live>] [I<--config>] | [I<--current>]]
+ +Change media of CDROM or floppy drive. I<target> is the unique target name +(<target dev='name'/>) of the device.
It would be nice if we could also recognize existing source file name. That is, if 'vda' has /path/to/disk1, and I am about to change it to /path/to/disk2, I think that both of these commands should work: virsh change-media dom --update /path/to/disk1 /path/to/disk2 virsh change-media dom --update vda /path/to/disk2
I<source> specifies the path of the media +to be inserted or updated. + +I<--eject> indicates the media will be ejected. +I<--insert> indicates the media will be inserted. I<source> must be specified. +If the device has source (e.g. <source file='media'>), and I<source> is not +specified, I<--update> is equal to I<--eject>. If the device has no source, +and I<source> is specified, I<--update> is equal to I<--insert>. If the device +has source, and I<source> is specified, I<--update> behaves like combination +of I<--eject> and I<--insert>.
Is there any reason why we require exactly one of --insert/--update/--eject, or could we make the command automatically assume --update if none of the three flags were given? Less typing is nicer.
+If I<--live> is specified, alter live configuration of running guest. +If I<--config> is specified, alter persistent configuration, effect observed +on next boot. +I<--current> can be either or both of I<live> and I<config>, depends on +the hypervisor's implementation. +Both I<--live> and I<--config> flags may be given, but I<--current> is +exclusive. If no flag is specified, behavior is different depending +on hypervisor. +The I<--force> option can be used to force media changing. + =back
=head1 NODEDEV COMMANDS
-- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 02/14/2012 06:27 AM, Eric Blake wrote:
On 02/03/2012 02:23 AM, Osier Yang wrote:
--- tools/virsh.pod | 25 +++++++++++++++++++++++++ 1 files changed, 25 insertions(+), 0 deletions(-)
diff --git a/tools/virsh.pod b/tools/virsh.pod index 28ad422..6ac7d15 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1475,6 +1475,31 @@ option can be used to force device update, e.g., to eject a CD-ROM even if it is locked/mounted in the domain. See the documentation to learn about libvirt XML format for a device.
+=item B<change-media> I<domain-id> I<target> [I<source>] [I<--eject>] +[I<--insert>] [I<--update>] [I<--current>] [I<--live>] [I<--config>] +[I<--force>]
Given the source code, I think this should be:
=item B<change-media> I<domain-id> I<target> {I<--insert> | I<--update> | I<--eject>} [I<source>] [I<--force>] [[I<--live>] [I<--config>] | [I<--current>]]
Agreed, will update.
+ +Change media of CDROM or floppy drive. I<target> is the unique target name +(<target dev='name'/>) of the device.
It would be nice if we could also recognize existing source file name. That is, if 'vda' has /path/to/disk1, and I am about to change it to /path/to/disk2, I think that both of these commands should work:
virsh change-media dom --update /path/to/disk1 /path/to/disk2 virsh change-media dom --update vda /path/to/disk2
Good idea, will update vshFindDisk to do that.
I<source> specifies the path of the media +to be inserted or updated. + +I<--eject> indicates the media will be ejected. +I<--insert> indicates the media will be inserted. I<source> must be specified. +If the device has source (e.g.<source file='media'>), and I<source> is not +specified, I<--update> is equal to I<--eject>. If the device has no source, +and I<source> is specified, I<--update> is equal to I<--insert>. If the device +has source, and I<source> is specified, I<--update> behaves like combination +of I<--eject> and I<--insert>.
Is there any reason why we require exactly one of --insert/--update/--eject, or could we make the command automatically assume --update if none of the three flags were given? Less typing is nicer.
Yeah, should be a right way to go.
+If I<--live> is specified, alter live configuration of running guest. +If I<--config> is specified, alter persistent configuration, effect observed +on next boot. +I<--current> can be either or both of I<live> and I<config>, depends on +the hypervisor's implementation. +Both I<--live> and I<--config> flags may be given, but I<--current> is +exclusive. If no flag is specified, behavior is different depending +on hypervisor. +The I<--force> option can be used to force media changing. + =back
=head1 NODEDEV COMMANDS

ping? On 2012年02月03日 17:23, Osier Yang wrote:
[PATCH 0/4] introduces two new helper functions: vshFindDisk is to find the disk XML node in xml doc with the flags (indicates it's normal disk or changeable disk). VshPrepareDiskXML is to prepare the disk XML for disk changing commands' use, such as detach-disk. The purpose of this patch is to prepare for upcoming new command "change-disk", which will use the two helper functions too.
Please see [PATCH 4/4] for a overall description of the new command.
v1 - v2 * No changes, only rebasing
[PATCH 1/4] virsh: Two new helper functions for disk device changes [PATCH 2/4] virsh: Use vshFindDisk and vshPrepareDiskXML in [PATCH 3/4] virsh: New command cmdChangeMedia [PATCH 4/4] virsh: Add document for cmdChangeMedia
Regards, Osier
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On 02/10/2012 05:35 AM, Osier Yang wrote:
ping?
On 2012年02月03日 17:23, Osier Yang wrote:
[PATCH 0/4] introduces two new helper functions: vshFindDisk is to find the disk XML node in xml doc with the flags (indicates it's normal disk or changeable disk). VshPrepareDiskXML is to prepare the disk XML for disk changing commands' use, such as detach-disk. The purpose of this patch is to prepare for upcoming new command "change-disk", which will use the two helper functions too.
Please see [PATCH 4/4] for a overall description of the new command.
Hi Osier, have you tested this on a guest that (like Fedora 16) always keeps the tray locked? What is the behavior like in that case? Paolo

On 02/14/2012 05:00 PM, Paolo Bonzini wrote:
On 02/10/2012 05:35 AM, Osier Yang wrote:
ping?
On 2012年02月03日 17:23, Osier Yang wrote:
[PATCH 0/4] introduces two new helper functions: vshFindDisk is to find the disk XML node in xml doc with the flags (indicates it's normal disk or changeable disk). VshPrepareDiskXML is to prepare the disk XML for disk changing commands' use, such as detach-disk. The purpose of this patch is to prepare for upcoming new command "change-disk", which will use the two helper functions too.
Please see [PATCH 4/4] for a overall description of the new command.
Hi Osier,
have you tested this on a guest that (like Fedora 16) always keeps the tray locked? What is the behavior like in that case?
Paolo
Hi, Paolo, I didn't do this testing yet, would like to do later. Osier
participants (3)
-
Eric Blake
-
Osier Yang
-
Paolo Bonzini