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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list