[libvirt] [PATCH 0/4] New command for changing media of changeable disk

This is an improved version of : https://www.redhat.com/archives/libvir-list/2011-December/msg00976.html The changes are: * New helper functions to find and change the disk XML * Use the helper functions in cmdDetachDisk * Merge insert-media and eject-media as change-media * Support --update option. [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. NB, as I explained previously, the new QEMU commands to separate tray and media management shouldn't block us. [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 e4b812e..6b2de19 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -13649,6 +13649,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

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 6b2de19..d98a037 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -13875,18 +13875,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; @@ -13901,60 +13897,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) { @@ -13965,10 +13922,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

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 d98a037..89eb18d 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -13931,6 +13931,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[] = { @@ -15934,6 +16067,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

--- tools/virsh.pod | 25 +++++++++++++++++++++++++ 1 files changed, 25 insertions(+), 0 deletions(-) diff --git a/tools/virsh.pod b/tools/virsh.pod index 138f886..bd127c6 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1408,6 +1408,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
participants (1)
-
Osier Yang