diff --git a/ChangeLog b/ChangeLog index 8de1e3b554..2d402893b1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Tue May 12 16:16:09 EDT 2009 Cole Robinson + + * src/virsh.c: Virsh commands vol-clone and vol-create-from + Tue May 12 16:14:43 EDT 2009 Cole Robinson * src/test.c: Test driver implementation of diff --git a/src/virsh.c b/src/virsh.c index cb32ede266..34beeb5e3f 100644 --- a/src/virsh.c +++ b/src/virsh.c @@ -3970,8 +3970,6 @@ cmdPoolUuid(vshControl *ctl, const vshCmd *cmd) } - - /* * "vol-create" command */ @@ -4030,6 +4028,179 @@ cmdVolCreate(vshControl *ctl, const vshCmd *cmd) return ret; } +/* + * "vol-create-from" command + */ +static const vshCmdInfo info_vol_create_from[] = { + {"help", gettext_noop("create a vol, using another volume as input")}, + {"desc", gettext_noop("Create a vol from an existing volume.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_vol_create_from[] = { + {"pool", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("pool name")}, + {"file", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("file containing an XML vol description")}, + {"inputpool", VSH_OT_STRING, 0, gettext_noop("pool name or uuid of the input volume's pool")}, + {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("input vol name or key")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdVolCreateFrom(vshControl *ctl, const vshCmd *cmd) +{ + virStoragePoolPtr pool = NULL; + virStorageVolPtr newvol = NULL, inputvol = NULL; + char *from; + int found; + int ret = FALSE; + char *buffer = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + goto cleanup; + + if (!(pool = vshCommandOptPoolBy(ctl, cmd, "pool", NULL, VSH_BYNAME))) + goto cleanup; + + from = vshCommandOptString(cmd, "file", &found); + if (!found) { + goto cleanup; + } + + if (!(inputvol = vshCommandOptVol(ctl, cmd, "vol", "inputpool", NULL))) + goto cleanup; + + if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) { + goto cleanup; + } + + newvol = virStorageVolCreateXMLFrom(pool, buffer, inputvol, 0); + + if (newvol != NULL) { + vshPrint(ctl, _("Vol %s created from input vol %s\n"), + virStorageVolGetName(newvol), virStorageVolGetName(inputvol)); + } else { + vshError(ctl, FALSE, _("Failed to create vol from %s"), from); + goto cleanup; + } + + ret = TRUE; +cleanup: + free (buffer); + if (pool) + virStoragePoolFree(pool); + if (inputvol) + virStorageVolFree(inputvol); + return ret; +} + +static xmlChar * +makeCloneXML(char *origxml, char *newname) { + + xmlDocPtr doc; + xmlXPathContextPtr ctxt; + xmlXPathObjectPtr obj; + xmlChar *newxml = NULL; + int size; + + doc = xmlReadDoc((const xmlChar *) origxml, "domain.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | XML_PARSE_NOWARNING); + if (!doc) + goto cleanup; + ctxt = xmlXPathNewContext(doc); + if (!ctxt) + goto cleanup; + + obj = xmlXPathEval(BAD_CAST "/volume/name", ctxt); + if ((obj == NULL) || (obj->nodesetval == NULL) || + (obj->nodesetval->nodeTab == NULL)) + goto cleanup; + + xmlNodeSetContent(obj->nodesetval->nodeTab[0], (const xmlChar *)newname); + xmlDocDumpMemory(doc, &newxml, &size); + +cleanup: + xmlXPathFreeObject(obj); + xmlXPathFreeContext(ctxt); + xmlFreeDoc(doc); + return newxml; +} + +/* + * "vol-clone" command + */ +static const vshCmdInfo info_vol_clone[] = { + {"help", gettext_noop("clone a volume.")}, + {"desc", gettext_noop("Clone an existing volume.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_vol_clone[] = { + {"pool", VSH_OT_STRING, 0, gettext_noop("pool name or uuid")}, + {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("orig vol name or key")}, + {"newname", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("clone name")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdVolClone(vshControl *ctl, const vshCmd *cmd) +{ + virStoragePoolPtr origpool = NULL; + virStorageVolPtr origvol = NULL, newvol = NULL; + char *name, *origxml = NULL; + xmlChar *newxml = NULL; + int found; + int ret = FALSE; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + goto cleanup; + + if (!(origvol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL))) + goto cleanup; + + origpool = virStoragePoolLookupByVolume(origvol); + if (!origpool) { + vshError(ctl, FALSE, _("failed to get parent pool")); + goto cleanup; + } + + name = vshCommandOptString(cmd, "newname", &found); + if (!found) + goto cleanup; + + origxml = virStorageVolGetXMLDesc(origvol, 0); + if (!origxml) + goto cleanup; + + newxml = makeCloneXML(origxml, name); + if (!newxml) { + vshPrint(ctl, "%s", _("Failed to allocate XML buffer")); + goto cleanup; + } + + newvol = virStorageVolCreateXMLFrom(origpool, (char *) newxml, origvol, 0); + + if (newvol != NULL) { + vshPrint(ctl, _("Vol %s cloned from %s\n"), + virStorageVolGetName(newvol), virStorageVolGetName(origvol)); + virStorageVolFree(newvol); + } else { + vshError(ctl, FALSE, _("Failed to clone vol from %s"), + virStorageVolGetName(origvol)); + goto cleanup; + } + + ret = TRUE; + +cleanup: + free(origxml); + xmlFree(newxml); + if (origvol) + virStorageVolFree(origvol); + if (origpool) + virStoragePoolFree(origpool); + return ret; +} + /* * "vol-delete" command */ @@ -5931,7 +6102,9 @@ static const vshCmdDef commands[] = { {"uri", cmdURI, NULL, info_uri}, {"vol-create", cmdVolCreate, opts_vol_create, info_vol_create}, + {"vol-create-from", cmdVolCreateFrom, opts_vol_create_from, info_vol_create_from}, {"vol-create-as", cmdVolCreateAs, opts_vol_create_as, info_vol_create_as}, + {"vol-clone", cmdVolClone, opts_vol_clone, info_vol_clone}, {"vol-delete", cmdVolDelete, opts_vol_delete, info_vol_delete}, {"vol-dumpxml", cmdVolDumpXML, opts_vol_dumpxml, info_vol_dumpxml}, {"vol-info", cmdVolInfo, opts_vol_info, info_vol_info},