2 * virsh-volume.c: Commands to manage storage volume
4 * Copyright (C) 2005, 2007-2016 Red Hat, Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see
18 * <http://www.gnu.org/licenses/>.
22 #include "virsh-volume.h"
23 #include "virsh-util.h"
27 #include <libxml/parser.h>
28 #include <libxml/tree.h>
29 #include <libxml/xpath.h>
30 #include <libxml/xmlsave.h>
33 #include "virbuffer.h"
37 #include "virsh-pool.h"
39 #include "virstring.h"
40 #include "vsh-table.h"
43 #define VIRSH_COMMON_OPT_POOL_FULL \
44 VIRSH_COMMON_OPT_POOL(N_("pool name or uuid"), \
45 VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE)
47 #define VIRSH_COMMON_OPT_POOL_NAME \
48 VIRSH_COMMON_OPT_POOL(N_("pool name"), \
49 VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE)
51 #define VIRSH_COMMON_OPT_POOL_OPTIONAL \
53 .type = VSH_OT_STRING, \
54 .help = N_("pool name or uuid"), \
55 .completer = virshStoragePoolNameCompleter, \
56 .completer_flags = VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE, \
59 #define VIRSH_COMMON_OPT_VOLUME_VOL \
61 .type = VSH_OT_DATA, \
62 .flags = VSH_OFLAG_REQ, \
63 .help = N_("vol name, key or path"), \
64 .completer = virshStorageVolNameCompleter, \
68 virshCommandOptVolBy(vshControl
*ctl
, const vshCmd
*cmd
,
70 const char *pooloptname
,
71 const char **name
, unsigned int flags
)
73 virStorageVolPtr vol
= NULL
;
74 virStoragePoolPtr pool
= NULL
;
75 const char *n
= NULL
, *p
= NULL
;
76 virshControlPtr priv
= ctl
->privData
;
78 virCheckFlags(VIRSH_BYUUID
| VIRSH_BYNAME
, NULL
);
80 if (vshCommandOptStringReq(ctl
, cmd
, optname
, &n
) < 0)
83 if (pooloptname
!= NULL
&&
84 vshCommandOptStringReq(ctl
, cmd
, pooloptname
, &p
) < 0)
88 if (!(pool
= virshCommandOptPoolBy(ctl
, cmd
, pooloptname
, name
, flags
)))
91 if (virStoragePoolIsActive(pool
) != 1) {
92 vshError(ctl
, _("pool '%s' is not active"), p
);
93 virStoragePoolFree(pool
);
98 vshDebug(ctl
, VSH_ERR_DEBUG
, "%s: found option <%s>: %s\n",
99 cmd
->def
->name
, optname
, n
);
105 if (pool
&& (flags
& VIRSH_BYNAME
)) {
106 vshDebug(ctl
, VSH_ERR_DEBUG
, "%s: <%s> trying as vol name\n",
107 cmd
->def
->name
, optname
);
108 vol
= virStorageVolLookupByName(pool
, n
);
111 if (!vol
&& (flags
& VIRSH_BYUUID
)) {
112 vshDebug(ctl
, VSH_ERR_DEBUG
, "%s: <%s> trying as vol key\n",
113 cmd
->def
->name
, optname
);
114 vol
= virStorageVolLookupByKey(priv
->conn
, n
);
117 if (!vol
&& (flags
& VIRSH_BYUUID
)) {
118 vshDebug(ctl
, VSH_ERR_DEBUG
, "%s: <%s> trying as vol path\n",
119 cmd
->def
->name
, optname
);
120 vol
= virStorageVolLookupByPath(priv
->conn
, n
);
124 if (pool
|| !pooloptname
)
125 vshError(ctl
, _("failed to get vol '%s'"), n
);
127 vshError(ctl
, _("failed to get vol '%s', specifying --%s "
128 "might help"), n
, pooloptname
);
130 vshResetLibvirtError();
133 /* If the pool was specified, then make sure that the returned
134 * volume is from the given pool */
136 virStoragePoolPtr volpool
= NULL
;
138 if ((volpool
= virStoragePoolLookupByVolume(vol
))) {
139 if (STRNEQ(virStoragePoolGetName(volpool
),
140 virStoragePoolGetName(pool
))) {
141 vshResetLibvirtError();
143 _("Requested volume '%s' is not in pool '%s'"),
144 n
, virStoragePoolGetName(pool
));
145 virStorageVolFree(vol
);
148 virStoragePoolFree(volpool
);
153 virStoragePoolFree(pool
);
159 * "vol-create-as" command
161 static const vshCmdInfo info_vol_create_as
[] = {
163 .data
= N_("create a volume from a set of args")
166 .data
= N_("Create a vol.")
171 static const vshCmdOptDef opts_vol_create_as
[] = {
172 VIRSH_COMMON_OPT_POOL_NAME
,
175 .flags
= VSH_OFLAG_REQ
,
176 .help
= N_("name of the volume")
180 .flags
= VSH_OFLAG_REQ
,
181 .help
= N_("size of the vol, as scaled integer (default bytes)")
183 {.name
= "allocation",
184 .type
= VSH_OT_STRING
,
185 .help
= N_("initial allocation size, as scaled integer (default bytes)")
188 .type
= VSH_OT_STRING
,
189 .help
= N_("file format type raw,bochs,qcow,qcow2,qed,vmdk")
191 {.name
= "backing-vol",
192 .type
= VSH_OT_STRING
,
193 .help
= N_("the backing volume if taking a snapshot")
195 {.name
= "backing-vol-format",
196 .type
= VSH_OT_STRING
,
197 .help
= N_("format of backing volume if taking a snapshot")
199 {.name
= "prealloc-metadata",
201 .help
= N_("preallocate metadata (for qcow2 instead of full allocation)")
203 {.name
= "print-xml",
205 .help
= N_("print XML document, but don't define/create")
211 virshVolSize(const char *data
, unsigned long long *val
)
214 if (virStrToLong_ullp(data
, &end
, 10, val
) < 0)
216 return virScaleInteger(val
, end
, 1, ULLONG_MAX
);
220 cmdVolCreateAs(vshControl
*ctl
, const vshCmd
*cmd
)
222 virStoragePoolPtr pool
;
223 virStorageVolPtr vol
= NULL
;
225 bool printXML
= vshCommandOptBool(cmd
, "print-xml");
226 const char *name
, *capacityStr
= NULL
, *allocationStr
= NULL
, *format
= NULL
;
227 const char *snapshotStrVol
= NULL
, *snapshotStrFormat
= NULL
;
228 unsigned long long capacity
, allocation
= 0;
229 virBuffer buf
= VIR_BUFFER_INITIALIZER
;
230 unsigned long flags
= 0;
231 virshControlPtr priv
= ctl
->privData
;
234 if (vshCommandOptBool(cmd
, "prealloc-metadata"))
235 flags
|= VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA
;
237 if (!(pool
= virshCommandOptPool(ctl
, cmd
, "pool", NULL
)))
240 if (vshCommandOptStringReq(ctl
, cmd
, "name", &name
) < 0)
243 if (vshCommandOptStringReq(ctl
, cmd
, "capacity", &capacityStr
) < 0)
246 if (virshVolSize(capacityStr
, &capacity
) < 0) {
247 vshError(ctl
, _("Malformed size %s"), capacityStr
);
251 if (vshCommandOptStringQuiet(ctl
, cmd
, "allocation", &allocationStr
) > 0 &&
252 virshVolSize(allocationStr
, &allocation
) < 0) {
253 vshError(ctl
, _("Malformed size %s"), allocationStr
);
257 if (vshCommandOptStringReq(ctl
, cmd
, "format", &format
) < 0 ||
258 vshCommandOptStringReq(ctl
, cmd
, "backing-vol", &snapshotStrVol
) < 0 ||
259 vshCommandOptStringReq(ctl
, cmd
, "backing-vol-format",
260 &snapshotStrFormat
) < 0)
263 virBufferAddLit(&buf
, "<volume>\n");
264 virBufferAdjustIndent(&buf
, 2);
265 virBufferAsprintf(&buf
, "<name>%s</name>\n", name
);
266 virBufferAsprintf(&buf
, "<capacity>%llu</capacity>\n", capacity
);
268 virBufferAsprintf(&buf
, "<allocation>%llu</allocation>\n", allocation
);
271 virBufferAddLit(&buf
, "<target>\n");
272 virBufferAdjustIndent(&buf
, 2);
273 virBufferAsprintf(&buf
, "<format type='%s'/>\n", format
);
274 virBufferAdjustIndent(&buf
, -2);
275 virBufferAddLit(&buf
, "</target>\n");
278 /* Convert the snapshot parameters into backingStore XML */
279 if (snapshotStrVol
) {
280 /* Lookup snapshot backing volume. Try the backing-vol
281 * parameter as a name */
282 vshDebug(ctl
, VSH_ERR_DEBUG
,
283 "%s: Look up backing store volume '%s' as name\n",
284 cmd
->def
->name
, snapshotStrVol
);
285 virStorageVolPtr snapVol
= virStorageVolLookupByName(pool
, snapshotStrVol
);
287 vshDebug(ctl
, VSH_ERR_DEBUG
,
288 "%s: Backing store volume found using '%s' as name\n",
289 cmd
->def
->name
, snapshotStrVol
);
291 if (snapVol
== NULL
) {
292 /* Snapshot backing volume not found by name. Try the
293 * backing-vol parameter as a key */
294 vshDebug(ctl
, VSH_ERR_DEBUG
,
295 "%s: Look up backing store volume '%s' as key\n",
296 cmd
->def
->name
, snapshotStrVol
);
297 snapVol
= virStorageVolLookupByKey(priv
->conn
, snapshotStrVol
);
299 vshDebug(ctl
, VSH_ERR_DEBUG
,
300 "%s: Backing store volume found using '%s' as key\n",
301 cmd
->def
->name
, snapshotStrVol
);
303 if (snapVol
== NULL
) {
304 /* Snapshot backing volume not found by key. Try the
305 * backing-vol parameter as a path */
306 vshDebug(ctl
, VSH_ERR_DEBUG
,
307 "%s: Look up backing store volume '%s' as path\n",
308 cmd
->def
->name
, snapshotStrVol
);
309 snapVol
= virStorageVolLookupByPath(priv
->conn
, snapshotStrVol
);
311 vshDebug(ctl
, VSH_ERR_DEBUG
,
312 "%s: Backing store volume found using '%s' as path\n",
313 cmd
->def
->name
, snapshotStrVol
);
315 if (snapVol
== NULL
) {
316 vshError(ctl
, _("failed to get vol '%s'"), snapshotStrVol
);
320 char *snapshotStrVolPath
;
321 if ((snapshotStrVolPath
= virStorageVolGetPath(snapVol
)) == NULL
) {
322 virStorageVolFree(snapVol
);
326 /* Create XML for the backing store */
327 virBufferAddLit(&buf
, "<backingStore>\n");
328 virBufferAdjustIndent(&buf
, 2);
329 virBufferAsprintf(&buf
, "<path>%s</path>\n", snapshotStrVolPath
);
330 if (snapshotStrFormat
)
331 virBufferAsprintf(&buf
, "<format type='%s'/>\n",
333 virBufferAdjustIndent(&buf
, -2);
334 virBufferAddLit(&buf
, "</backingStore>\n");
336 /* Cleanup snapshot allocations */
337 VIR_FREE(snapshotStrVolPath
);
338 virStorageVolFree(snapVol
);
341 virBufferAdjustIndent(&buf
, -2);
342 virBufferAddLit(&buf
, "</volume>\n");
344 if (virBufferError(&buf
)) {
345 vshError(ctl
, "%s", _("Failed to allocate XML buffer"));
348 xml
= virBufferContentAndReset(&buf
);
351 vshPrint(ctl
, "%s", xml
);
353 if (!(vol
= virStorageVolCreateXML(pool
, xml
, flags
))) {
354 vshError(ctl
, _("Failed to create vol %s"), name
);
357 vshPrintExtra(ctl
, _("Vol %s created\n"), name
);
363 virBufferFreeAndReset(&buf
);
365 virStorageVolFree(vol
);
366 virStoragePoolFree(pool
);
372 * "vol-create" command
374 static const vshCmdInfo info_vol_create
[] = {
376 .data
= N_("create a vol from an XML file")
379 .data
= N_("Create a vol.")
384 static const vshCmdOptDef opts_vol_create
[] = {
385 VIRSH_COMMON_OPT_POOL_NAME
,
386 VIRSH_COMMON_OPT_FILE(N_("file containing an XML vol description")),
387 {.name
= "prealloc-metadata",
389 .help
= N_("preallocate metadata (for qcow2 instead of full allocation)")
395 cmdVolCreate(vshControl
*ctl
, const vshCmd
*cmd
)
397 virStoragePoolPtr pool
;
398 virStorageVolPtr vol
;
399 const char *from
= NULL
;
401 unsigned int flags
= 0;
404 if (vshCommandOptBool(cmd
, "prealloc-metadata"))
405 flags
|= VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA
;
407 if (!(pool
= virshCommandOptPool(ctl
, cmd
, "pool", NULL
)))
410 if (vshCommandOptStringReq(ctl
, cmd
, "file", &from
) < 0)
413 if (virFileReadAll(from
, VSH_MAX_XML_FILE
, &buffer
) < 0) {
414 vshSaveLibvirtError();
418 if ((vol
= virStorageVolCreateXML(pool
, buffer
, flags
))) {
419 vshPrintExtra(ctl
, _("Vol %s created from %s\n"),
420 virStorageVolGetName(vol
), from
);
421 virStorageVolFree(vol
);
424 vshError(ctl
, _("Failed to create vol from %s"), from
);
429 virStoragePoolFree(pool
);
434 * "vol-create-from" command
436 static const vshCmdInfo info_vol_create_from
[] = {
438 .data
= N_("create a vol, using another volume as input")
441 .data
= N_("Create a vol from an existing volume.")
446 static const vshCmdOptDef opts_vol_create_from
[] = {
447 VIRSH_COMMON_OPT_POOL_FULL
,
448 VIRSH_COMMON_OPT_FILE(N_("file containing an XML vol description")),
449 VIRSH_COMMON_OPT_VOLUME_VOL
,
450 {.name
= "inputpool",
451 .type
= VSH_OT_STRING
,
452 .help
= N_("pool name or uuid of the input volume's pool")
454 {.name
= "prealloc-metadata",
456 .help
= N_("preallocate metadata (for qcow2 instead of full allocation)")
460 .help
= N_("use btrfs COW lightweight copy")
466 cmdVolCreateFrom(vshControl
*ctl
, const vshCmd
*cmd
)
468 virStoragePoolPtr pool
= NULL
;
469 virStorageVolPtr newvol
= NULL
, inputvol
= NULL
;
470 const char *from
= NULL
;
473 unsigned int flags
= 0;
475 if (!(pool
= virshCommandOptPool(ctl
, cmd
, "pool", NULL
)))
478 if (vshCommandOptBool(cmd
, "prealloc-metadata"))
479 flags
|= VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA
;
481 if (vshCommandOptBool(cmd
, "reflink"))
482 flags
|= VIR_STORAGE_VOL_CREATE_REFLINK
;
484 if (vshCommandOptStringReq(ctl
, cmd
, "file", &from
) < 0)
487 if (!(inputvol
= virshCommandOptVol(ctl
, cmd
, "vol", "inputpool", NULL
)))
490 if (virFileReadAll(from
, VSH_MAX_XML_FILE
, &buffer
) < 0) {
495 newvol
= virStorageVolCreateXMLFrom(pool
, buffer
, inputvol
, flags
);
497 if (newvol
!= NULL
) {
498 vshPrintExtra(ctl
, _("Vol %s created from input vol %s\n"),
499 virStorageVolGetName(newvol
), virStorageVolGetName(inputvol
));
501 vshError(ctl
, _("Failed to create vol from %s"), from
);
509 virStoragePoolFree(pool
);
511 virStorageVolFree(inputvol
);
513 virStorageVolFree(newvol
);
518 virshMakeCloneXML(const char *origxml
, const char *newname
)
520 xmlDocPtr doc
= NULL
;
521 xmlXPathContextPtr ctxt
= NULL
;
522 xmlXPathObjectPtr obj
= NULL
;
523 xmlChar
*newxml
= NULL
;
526 doc
= virXMLParseStringCtxt(origxml
, _("(volume_definition)"), &ctxt
);
530 obj
= xmlXPathEval(BAD_CAST
"/volume/name", ctxt
);
531 if (obj
== NULL
|| obj
->nodesetval
== NULL
||
532 obj
->nodesetval
->nodeTab
== NULL
)
535 xmlNodeSetContent(obj
->nodesetval
->nodeTab
[0], (const xmlChar
*)newname
);
536 xmlDocDumpMemory(doc
, &newxml
, &size
);
539 xmlXPathFreeObject(obj
);
540 xmlXPathFreeContext(ctxt
);
546 * "vol-clone" command
548 static const vshCmdInfo info_vol_clone
[] = {
550 .data
= N_("clone a volume.")
553 .data
= N_("Clone an existing volume within the parent pool.")
558 static const vshCmdOptDef opts_vol_clone
[] = {
559 VIRSH_COMMON_OPT_VOLUME_VOL
,
562 .flags
= VSH_OFLAG_REQ
,
563 .help
= N_("clone name")
565 VIRSH_COMMON_OPT_POOL_OPTIONAL
,
566 {.name
= "prealloc-metadata",
568 .help
= N_("preallocate metadata (for qcow2 instead of full allocation)")
572 .help
= N_("use btrfs COW lightweight copy")
578 cmdVolClone(vshControl
*ctl
, const vshCmd
*cmd
)
580 virStoragePoolPtr origpool
= NULL
;
581 virStorageVolPtr origvol
= NULL
, newvol
= NULL
;
582 const char *name
= NULL
;
583 char *origxml
= NULL
;
584 xmlChar
*newxml
= NULL
;
586 unsigned int flags
= 0;
588 if (!(origvol
= virshCommandOptVol(ctl
, cmd
, "vol", "pool", NULL
)))
591 if (vshCommandOptBool(cmd
, "prealloc-metadata"))
592 flags
|= VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA
;
594 if (vshCommandOptBool(cmd
, "reflink"))
595 flags
|= VIR_STORAGE_VOL_CREATE_REFLINK
;
597 origpool
= virStoragePoolLookupByVolume(origvol
);
599 vshError(ctl
, "%s", _("failed to get parent pool"));
603 if (vshCommandOptStringReq(ctl
, cmd
, "newname", &name
) < 0)
606 origxml
= virStorageVolGetXMLDesc(origvol
, 0);
610 newxml
= virshMakeCloneXML(origxml
, name
);
612 vshError(ctl
, "%s", _("Failed to allocate XML buffer"));
616 newvol
= virStorageVolCreateXMLFrom(origpool
, (char *) newxml
, origvol
, flags
);
618 if (newvol
!= NULL
) {
619 vshPrintExtra(ctl
, _("Vol %s cloned from %s\n"),
620 virStorageVolGetName(newvol
), virStorageVolGetName(origvol
));
622 vshError(ctl
, _("Failed to clone vol from %s"),
623 virStorageVolGetName(origvol
));
633 virStorageVolFree(origvol
);
635 virStorageVolFree(newvol
);
637 virStoragePoolFree(origpool
);
642 * "vol-upload" command
644 static const vshCmdInfo info_vol_upload
[] = {
646 .data
= N_("upload file contents to a volume")
649 .data
= N_("Upload file contents to a volume")
654 static const vshCmdOptDef opts_vol_upload
[] = {
655 VIRSH_COMMON_OPT_VOLUME_VOL
,
656 VIRSH_COMMON_OPT_FILE(N_("file")),
657 VIRSH_COMMON_OPT_POOL_OPTIONAL
,
660 .help
= N_("volume offset to upload to")
664 .help
= N_("amount of data to upload")
668 .help
= N_("preserve sparseness of volume")
674 cmdVolUpload(vshControl
*ctl
, const vshCmd
*cmd
)
676 const char *file
= NULL
;
677 virStorageVolPtr vol
= NULL
;
680 virStreamPtr st
= NULL
;
681 const char *name
= NULL
;
682 unsigned long long offset
= 0, length
= 0;
683 virshControlPtr priv
= ctl
->privData
;
684 unsigned int flags
= 0;
685 virshStreamCallbackData cbData
;
687 if (vshCommandOptULongLong(ctl
, cmd
, "offset", &offset
) < 0)
690 if (vshCommandOptULongLongWrap(ctl
, cmd
, "length", &length
) < 0)
693 if (!(vol
= virshCommandOptVol(ctl
, cmd
, "vol", "pool", &name
)))
696 if (vshCommandOptStringReq(ctl
, cmd
, "file", &file
) < 0)
699 if ((fd
= open(file
, O_RDONLY
)) < 0) {
700 vshError(ctl
, _("cannot read %s"), file
);
707 if (vshCommandOptBool(cmd
, "sparse"))
708 flags
|= VIR_STORAGE_VOL_UPLOAD_SPARSE_STREAM
;
710 if (!(st
= virStreamNew(priv
->conn
, 0))) {
711 vshError(ctl
, _("cannot create a new stream"));
715 if (virStorageVolUpload(vol
, st
, offset
, length
, flags
) < 0) {
716 vshError(ctl
, _("cannot upload to volume %s"), name
);
720 if (flags
& VIR_STORAGE_VOL_UPLOAD_SPARSE_STREAM
) {
721 if (virStreamSparseSendAll(st
, virshStreamSource
,
723 virshStreamSourceSkip
, &cbData
) < 0) {
724 vshError(ctl
, _("cannot send data to volume %s"), name
);
728 if (virStreamSendAll(st
, virshStreamSource
, &cbData
) < 0) {
729 vshError(ctl
, _("cannot send data to volume %s"), name
);
734 if (VIR_CLOSE(fd
) < 0) {
735 vshError(ctl
, _("cannot close file %s"), file
);
740 if (virStreamFinish(st
) < 0) {
741 vshError(ctl
, _("cannot close volume %s"), name
);
749 virStorageVolFree(vol
);
757 * "vol-download" command
759 static const vshCmdInfo info_vol_download
[] = {
761 .data
= N_("download volume contents to a file")
764 .data
= N_("Download volume contents to a file")
769 static const vshCmdOptDef opts_vol_download
[] = {
770 VIRSH_COMMON_OPT_VOLUME_VOL
,
771 VIRSH_COMMON_OPT_FILE(N_("file")),
772 VIRSH_COMMON_OPT_POOL_OPTIONAL
,
775 .help
= N_("volume offset to download from")
779 .help
= N_("amount of data to download")
783 .help
= N_("preserve sparseness of volume")
789 cmdVolDownload(vshControl
*ctl
, const vshCmd
*cmd
)
791 const char *file
= NULL
;
792 virStorageVolPtr vol
= NULL
;
795 virStreamPtr st
= NULL
;
796 const char *name
= NULL
;
797 unsigned long long offset
= 0, length
= 0;
798 bool created
= false;
799 virshControlPtr priv
= ctl
->privData
;
800 unsigned int flags
= 0;
802 if (vshCommandOptULongLong(ctl
, cmd
, "offset", &offset
) < 0)
805 if (vshCommandOptULongLongWrap(ctl
, cmd
, "length", &length
) < 0)
808 if (!(vol
= virshCommandOptVol(ctl
, cmd
, "vol", "pool", &name
)))
811 if (vshCommandOptStringReq(ctl
, cmd
, "file", &file
) < 0)
814 if (vshCommandOptBool(cmd
, "sparse"))
815 flags
|= VIR_STORAGE_VOL_DOWNLOAD_SPARSE_STREAM
;
817 if ((fd
= open(file
, O_WRONLY
|O_CREAT
|O_EXCL
, 0666)) < 0) {
818 if (errno
!= EEXIST
||
819 (fd
= open(file
, O_WRONLY
|O_TRUNC
, 0666)) < 0) {
820 vshError(ctl
, _("cannot create %s"), file
);
827 if (!(st
= virStreamNew(priv
->conn
, 0))) {
828 vshError(ctl
, _("cannot create a new stream"));
832 if (virStorageVolDownload(vol
, st
, offset
, length
, flags
) < 0) {
833 vshError(ctl
, _("cannot download from volume %s"), name
);
837 if (virStreamSparseRecvAll(st
, virshStreamSink
, virshStreamSkip
, &fd
) < 0) {
838 vshError(ctl
, _("cannot receive data from volume %s"), name
);
842 if (VIR_CLOSE(fd
) < 0) {
843 vshError(ctl
, _("cannot close file %s"), file
);
848 if (virStreamFinish(st
) < 0) {
849 vshError(ctl
, _("cannot close volume %s"), name
);
860 virStorageVolFree(vol
);
867 * "vol-delete" command
869 static const vshCmdInfo info_vol_delete
[] = {
871 .data
= N_("delete a vol")
874 .data
= N_("Delete a given vol.")
879 static const vshCmdOptDef opts_vol_delete
[] = {
880 VIRSH_COMMON_OPT_VOLUME_VOL
,
881 VIRSH_COMMON_OPT_POOL_OPTIONAL
,
882 {.name
= "delete-snapshots",
884 .help
= N_("delete snapshots associated with volume (must be "
885 "supported by storage driver)")
891 cmdVolDelete(vshControl
*ctl
, const vshCmd
*cmd
)
893 virStorageVolPtr vol
;
896 bool delete_snapshots
= vshCommandOptBool(cmd
, "delete-snapshots");
897 unsigned int flags
= 0;
899 if (!(vol
= virshCommandOptVol(ctl
, cmd
, "vol", "pool", &name
)))
902 if (delete_snapshots
)
903 flags
|= VIR_STORAGE_VOL_DELETE_WITH_SNAPSHOTS
;
905 if (virStorageVolDelete(vol
, flags
) == 0) {
906 vshPrintExtra(ctl
, _("Vol %s deleted\n"), name
);
908 vshError(ctl
, _("Failed to delete vol %s"), name
);
912 virStorageVolFree(vol
);
919 static const vshCmdInfo info_vol_wipe
[] = {
921 .data
= N_("wipe a vol")
924 .data
= N_("Ensure data previously on a volume is not accessible to future reads")
929 static const vshCmdOptDef opts_vol_wipe
[] = {
930 VIRSH_COMMON_OPT_VOLUME_VOL
,
931 VIRSH_COMMON_OPT_POOL_OPTIONAL
,
932 {.name
= "algorithm",
933 .type
= VSH_OT_STRING
,
934 .help
= N_("perform selected wiping algorithm")
939 VIR_ENUM_DECL(virStorageVolWipeAlgorithm
);
940 VIR_ENUM_IMPL(virStorageVolWipeAlgorithm
,
941 VIR_STORAGE_VOL_WIPE_ALG_LAST
,
942 "zero", "nnsa", "dod", "bsi", "gutmann", "schneier",
943 "pfitzner7", "pfitzner33", "random", "trim");
946 cmdVolWipe(vshControl
*ctl
, const vshCmd
*cmd
)
948 virStorageVolPtr vol
;
951 const char *algorithm_str
= NULL
;
952 int algorithm
= VIR_STORAGE_VOL_WIPE_ALG_ZERO
;
955 if (!(vol
= virshCommandOptVol(ctl
, cmd
, "vol", "pool", &name
)))
958 if (vshCommandOptStringReq(ctl
, cmd
, "algorithm", &algorithm_str
) < 0)
962 (algorithm
= virStorageVolWipeAlgorithmTypeFromString(algorithm_str
)) < 0) {
963 vshError(ctl
, _("Unsupported algorithm '%s'"), algorithm_str
);
967 if ((funcRet
= virStorageVolWipePattern(vol
, algorithm
, 0)) < 0) {
968 if (last_error
->code
== VIR_ERR_NO_SUPPORT
&&
969 algorithm
== VIR_STORAGE_VOL_WIPE_ALG_ZERO
)
970 funcRet
= virStorageVolWipe(vol
, 0);
974 vshError(ctl
, _("Failed to wipe vol %s"), name
);
978 vshPrintExtra(ctl
, _("Vol %s wiped\n"), name
);
981 virStorageVolFree(vol
);
986 VIR_ENUM_DECL(virshStorageVol
);
987 VIR_ENUM_IMPL(virshStorageVol
,
988 VIR_STORAGE_VOL_LAST
,
997 virshVolumeTypeToString(int type
)
999 const char *str
= virshStorageVolTypeToString(type
);
1000 return str
? _(str
) : _("unknown");
1005 * "vol-info" command
1007 static const vshCmdInfo info_vol_info
[] = {
1009 .data
= N_("storage vol information")
1012 .data
= N_("Returns basic information about the storage vol.")
1017 static const vshCmdOptDef opts_vol_info
[] = {
1018 VIRSH_COMMON_OPT_VOLUME_VOL
,
1019 VIRSH_COMMON_OPT_POOL_OPTIONAL
,
1021 .type
= VSH_OT_BOOL
,
1022 .help
= N_("sizes are represented in bytes rather than pretty units")
1024 {.name
= "physical",
1025 .type
= VSH_OT_BOOL
,
1026 .help
= N_("return the physical size of the volume in allocation field")
1032 cmdVolInfo(vshControl
*ctl
, const vshCmd
*cmd
)
1034 virStorageVolInfo info
;
1035 virStorageVolPtr vol
;
1036 bool bytes
= vshCommandOptBool(cmd
, "bytes");
1037 bool physical
= vshCommandOptBool(cmd
, "physical");
1040 unsigned int flags
= 0;
1042 if (!(vol
= virshCommandOptVol(ctl
, cmd
, "vol", "pool", NULL
)))
1045 vshPrint(ctl
, "%-15s %s\n", _("Name:"), virStorageVolGetName(vol
));
1048 flags
|= VIR_STORAGE_VOL_GET_PHYSICAL
;
1051 rc
= virStorageVolGetInfoFlags(vol
, &info
, flags
);
1053 rc
= virStorageVolGetInfo(vol
, &info
);
1059 vshPrint(ctl
, "%-15s %s\n", _("Type:"),
1060 virshVolumeTypeToString(info
.type
));
1063 vshPrint(ctl
, "%-15s %llu %s\n", _("Capacity:"),
1064 info
.capacity
, _("bytes"));
1066 val
= vshPrettyCapacity(info
.capacity
, &unit
);
1067 vshPrint(ctl
, "%-15s %2.2lf %s\n", _("Capacity:"), val
, unit
);
1072 vshPrint(ctl
, "%-15s %llu %s\n", _("Physical:"),
1073 info
.allocation
, _("bytes"));
1075 vshPrint(ctl
, "%-15s %llu %s\n", _("Allocation:"),
1076 info
.allocation
, _("bytes"));
1078 val
= vshPrettyCapacity(info
.allocation
, &unit
);
1080 vshPrint(ctl
, "%-15s %2.2lf %s\n", _("Physical:"), val
, unit
);
1082 vshPrint(ctl
, "%-15s %2.2lf %s\n", _("Allocation:"), val
, unit
);
1088 virStorageVolFree(vol
);
1093 * "vol-resize" command
1095 static const vshCmdInfo info_vol_resize
[] = {
1097 .data
= N_("resize a vol")
1100 .data
= N_("Resizes a storage volume. This is safe only for storage "
1101 "volumes not in use by an active guest.\n"
1102 "See blockresize for live resizing.")
1107 static const vshCmdOptDef opts_vol_resize
[] = {
1108 VIRSH_COMMON_OPT_VOLUME_VOL
,
1109 {.name
= "capacity",
1110 .type
= VSH_OT_DATA
,
1111 .flags
= VSH_OFLAG_REQ
,
1112 .help
= N_("new capacity for the vol, as scaled integer (default bytes)")
1114 VIRSH_COMMON_OPT_POOL_OPTIONAL
,
1115 {.name
= "allocate",
1116 .type
= VSH_OT_BOOL
,
1117 .help
= N_("allocate the new capacity, rather than leaving it sparse")
1120 .type
= VSH_OT_BOOL
,
1121 .help
= N_("use capacity as a delta to current size, rather than the new size")
1124 .type
= VSH_OT_BOOL
,
1125 .help
= N_("allow the resize to shrink the volume")
1131 cmdVolResize(vshControl
*ctl
, const vshCmd
*cmd
)
1133 virStorageVolPtr vol
;
1134 const char *capacityStr
= NULL
;
1135 unsigned long long capacity
= 0;
1136 unsigned int flags
= 0;
1138 bool delta
= vshCommandOptBool(cmd
, "delta");
1140 if (vshCommandOptBool(cmd
, "allocate"))
1141 flags
|= VIR_STORAGE_VOL_RESIZE_ALLOCATE
;
1142 if (vshCommandOptBool(cmd
, "shrink"))
1143 flags
|= VIR_STORAGE_VOL_RESIZE_SHRINK
;
1145 if (!(vol
= virshCommandOptVol(ctl
, cmd
, "vol", "pool", NULL
)))
1148 if (vshCommandOptStringReq(ctl
, cmd
, "capacity", &capacityStr
) < 0)
1150 virSkipSpaces(&capacityStr
);
1151 if (*capacityStr
== '-') {
1152 /* The API always requires a positive value; but we allow a
1153 * negative value for convenience. */
1154 if (vshCommandOptBool(cmd
, "shrink")) {
1159 _("negative size requires --shrink"));
1165 flags
|= VIR_STORAGE_VOL_RESIZE_DELTA
;
1167 if (virshVolSize(capacityStr
, &capacity
) < 0) {
1168 vshError(ctl
, _("Malformed size %s"), capacityStr
);
1172 if (virStorageVolResize(vol
, capacity
, flags
) == 0) {
1174 delta
? _("Size of volume '%s' successfully changed by %s\n")
1175 : _("Size of volume '%s' successfully changed to %s\n"),
1176 virStorageVolGetName(vol
), capacityStr
);
1180 delta
? _("Failed to change size of volume '%s' by %s")
1181 : _("Failed to change size of volume '%s' to %s"),
1182 virStorageVolGetName(vol
), capacityStr
);
1187 virStorageVolFree(vol
);
1192 * "vol-dumpxml" command
1194 static const vshCmdInfo info_vol_dumpxml
[] = {
1196 .data
= N_("vol information in XML")
1199 .data
= N_("Output the vol information as an XML dump to stdout.")
1204 static const vshCmdOptDef opts_vol_dumpxml
[] = {
1205 VIRSH_COMMON_OPT_VOLUME_VOL
,
1206 VIRSH_COMMON_OPT_POOL_OPTIONAL
,
1211 cmdVolDumpXML(vshControl
*ctl
, const vshCmd
*cmd
)
1213 virStorageVolPtr vol
;
1217 if (!(vol
= virshCommandOptVol(ctl
, cmd
, "vol", "pool", NULL
)))
1220 dump
= virStorageVolGetXMLDesc(vol
, 0);
1222 vshPrint(ctl
, "%s", dump
);
1228 virStorageVolFree(vol
);
1233 virshStorageVolSorter(const void *a
, const void *b
)
1235 virStorageVolPtr
*va
= (virStorageVolPtr
*) a
;
1236 virStorageVolPtr
*vb
= (virStorageVolPtr
*) b
;
1244 return vshStrcasecmp(virStorageVolGetName(*va
),
1245 virStorageVolGetName(*vb
));
1248 struct virshStorageVolList
{
1249 virStorageVolPtr
*vols
;
1252 typedef struct virshStorageVolList
*virshStorageVolListPtr
;
1255 virshStorageVolListFree(virshStorageVolListPtr list
)
1259 if (list
&& list
->vols
) {
1260 for (i
= 0; i
< list
->nvols
; i
++) {
1262 virStorageVolFree(list
->vols
[i
]);
1264 VIR_FREE(list
->vols
);
1269 static virshStorageVolListPtr
1270 virshStorageVolListCollect(vshControl
*ctl
,
1271 virStoragePoolPtr pool
,
1274 virshStorageVolListPtr list
= vshMalloc(ctl
, sizeof(*list
));
1276 char **names
= NULL
;
1277 virStorageVolPtr vol
= NULL
;
1278 bool success
= false;
1283 /* try the list with flags support (0.10.2 and later) */
1284 if ((ret
= virStoragePoolListAllVolumes(pool
,
1291 /* check if the command is actually supported */
1292 if (last_error
&& last_error
->code
== VIR_ERR_NO_SUPPORT
)
1295 /* there was an error during the call */
1296 vshError(ctl
, "%s", _("Failed to list volumes"));
1300 /* fall back to old method (0.10.1 and older) */
1301 vshResetLibvirtError();
1303 /* Determine the number of volumes in the pool */
1304 if ((nvols
= virStoragePoolNumOfVolumes(pool
)) < 0) {
1305 vshError(ctl
, "%s", _("Failed to list storage volumes"));
1312 /* Retrieve the list of volume names in the pool */
1313 names
= vshCalloc(ctl
, nvols
, sizeof(*names
));
1314 if ((nvols
= virStoragePoolListVolumes(pool
, names
, nvols
)) < 0) {
1315 vshError(ctl
, "%s", _("Failed to list storage volumes"));
1319 list
->vols
= vshMalloc(ctl
, sizeof(virStorageVolPtr
) * (nvols
));
1323 for (i
= 0; i
< nvols
; i
++) {
1324 if (!(vol
= virStorageVolLookupByName(pool
, names
[i
])))
1326 list
->vols
[list
->nvols
++] = vol
;
1329 /* truncate the list for not found vols */
1330 deleted
= nvols
- list
->nvols
;
1334 if (list
->vols
&& list
->nvols
)
1335 qsort(list
->vols
, list
->nvols
, sizeof(*list
->vols
), virshStorageVolSorter
);
1338 VIR_SHRINK_N(list
->vols
, list
->nvols
, deleted
);
1344 for (i
= 0; i
< nvols
; i
++)
1349 virshStorageVolListFree(list
);
1357 * "vol-list" command
1359 static const vshCmdInfo info_vol_list
[] = {
1361 .data
= N_("list vols")
1364 .data
= N_("Returns list of vols by pool.")
1369 static const vshCmdOptDef opts_vol_list
[] = {
1370 VIRSH_COMMON_OPT_POOL_FULL
,
1372 .type
= VSH_OT_BOOL
,
1373 .help
= N_("display extended details for volumes")
1379 cmdVolList(vshControl
*ctl
, const vshCmd
*cmd ATTRIBUTE_UNUSED
)
1381 virStorageVolInfo volumeInfo
;
1382 virStoragePoolPtr pool
;
1385 bool details
= vshCommandOptBool(cmd
, "details");
1388 struct volInfoText
{
1394 struct volInfoText
*volInfoTexts
= NULL
;
1395 virshStorageVolListPtr list
= NULL
;
1396 vshTablePtr table
= NULL
;
1398 /* Look up the pool information given to us by the user */
1399 if (!(pool
= virshCommandOptPool(ctl
, cmd
, "pool", NULL
)))
1402 if (!(list
= virshStorageVolListCollect(ctl
, pool
, 0)))
1405 if (list
->nvols
> 0)
1406 volInfoTexts
= vshCalloc(ctl
, list
->nvols
, sizeof(*volInfoTexts
));
1408 /* Collect the rest of the volume information for display */
1409 for (i
= 0; i
< list
->nvols
; i
++) {
1410 /* Retrieve volume info */
1411 virStorageVolPtr vol
= list
->vols
[i
];
1413 /* Retrieve the volume path */
1414 if ((volInfoTexts
[i
].path
= virStorageVolGetPath(vol
)) == NULL
) {
1415 /* Something went wrong retrieving a volume path, cope with it */
1416 volInfoTexts
[i
].path
= vshStrdup(ctl
, _("unknown"));
1419 /* If requested, retrieve volume type and sizing information */
1421 if (virStorageVolGetInfo(vol
, &volumeInfo
) != 0) {
1422 /* Something went wrong retrieving volume info, cope with it */
1423 volInfoTexts
[i
].allocation
= vshStrdup(ctl
, _("unknown"));
1424 volInfoTexts
[i
].capacity
= vshStrdup(ctl
, _("unknown"));
1425 volInfoTexts
[i
].type
= vshStrdup(ctl
, _("unknown"));
1427 /* Convert the returned volume info into output strings */
1430 volInfoTexts
[i
].type
= vshStrdup(ctl
,
1431 virshVolumeTypeToString(volumeInfo
.type
));
1433 val
= vshPrettyCapacity(volumeInfo
.capacity
, &unit
);
1434 if (virAsprintf(&volInfoTexts
[i
].capacity
,
1435 "%.2lf %s", val
, unit
) < 0)
1438 val
= vshPrettyCapacity(volumeInfo
.allocation
, &unit
);
1439 if (virAsprintf(&volInfoTexts
[i
].allocation
,
1440 "%.2lf %s", val
, unit
) < 0)
1446 /* If the --details option wasn't selected, we output the volume
1447 * info using the fixed string format from previous versions to
1448 * maintain backward compatibility.
1451 /* Output basic info then return if --details option not selected */
1453 /* The old output format */
1454 table
= vshTableNew(_("Name"), _("Path"), NULL
);
1458 for (i
= 0; i
< list
->nvols
; i
++) {
1459 if (vshTableRowAppend(table
,
1460 virStorageVolGetName(list
->vols
[i
]),
1461 volInfoTexts
[i
].path
,
1466 vshTablePrintToStdout(table
, ctl
);
1468 /* Cleanup and return */
1473 /* We only get here if the --details option was selected. */
1475 /* Insert the header into table */
1476 table
= vshTableNew(_("Name"), _("Path"), _("Type"), _("Capacity"), _("Allocation"), NULL
);
1480 /* Insert the volume info rows into table */
1481 for (i
= 0; i
< list
->nvols
; i
++) {
1482 if (vshTableRowAppend(table
,
1483 virStorageVolGetName(list
->vols
[i
]),
1484 volInfoTexts
[i
].path
,
1485 volInfoTexts
[i
].type
,
1486 volInfoTexts
[i
].capacity
,
1487 volInfoTexts
[i
].allocation
,
1492 vshTablePrintToStdout(table
, ctl
);
1494 /* Cleanup and return */
1498 vshTableFree(table
);
1500 /* Safely free the memory allocated in this function */
1501 if (list
&& list
->nvols
) {
1502 for (i
= 0; i
< list
->nvols
; i
++) {
1503 /* Cleanup the memory for one volume info structure per loop */
1504 VIR_FREE(volInfoTexts
[i
].path
);
1505 VIR_FREE(volInfoTexts
[i
].type
);
1506 VIR_FREE(volInfoTexts
[i
].capacity
);
1507 VIR_FREE(volInfoTexts
[i
].allocation
);
1511 /* Cleanup remaining memory */
1512 VIR_FREE(volInfoTexts
);
1513 virStoragePoolFree(pool
);
1514 virshStorageVolListFree(list
);
1516 /* Return the desired value */
1521 * "vol-name" command
1523 static const vshCmdInfo info_vol_name
[] = {
1525 .data
= N_("returns the volume name for a given volume key or path")
1533 static const vshCmdOptDef opts_vol_name
[] = {
1535 .type
= VSH_OT_DATA
,
1536 .flags
= VSH_OFLAG_REQ
,
1537 .help
= N_("volume key or path")
1543 cmdVolName(vshControl
*ctl
, const vshCmd
*cmd
)
1545 virStorageVolPtr vol
;
1547 if (!(vol
= virshCommandOptVolBy(ctl
, cmd
, "vol", NULL
, NULL
,
1551 vshPrint(ctl
, "%s\n", virStorageVolGetName(vol
));
1552 virStorageVolFree(vol
);
1557 * "vol-pool" command
1559 static const vshCmdInfo info_vol_pool
[] = {
1561 .data
= N_("returns the storage pool for a given volume key or path")
1569 static const vshCmdOptDef opts_vol_pool
[] = {
1571 .type
= VSH_OT_DATA
,
1572 .flags
= VSH_OFLAG_REQ
,
1573 .help
= N_("volume key or path")
1576 .type
= VSH_OT_BOOL
,
1577 .help
= N_("return the pool uuid rather than pool name")
1583 cmdVolPool(vshControl
*ctl
, const vshCmd
*cmd
)
1585 virStoragePoolPtr pool
;
1586 virStorageVolPtr vol
;
1587 char uuid
[VIR_UUID_STRING_BUFLEN
];
1589 /* Use the supplied string to locate the volume */
1590 if (!(vol
= virshCommandOptVolBy(ctl
, cmd
, "vol", NULL
, NULL
,
1595 /* Look up the parent storage pool for the volume */
1596 pool
= virStoragePoolLookupByVolume(vol
);
1598 vshError(ctl
, "%s", _("failed to get parent pool"));
1599 virStorageVolFree(vol
);
1603 /* Return the requested details of the parent storage pool */
1604 if (vshCommandOptBool(cmd
, "uuid")) {
1605 /* Retrieve and return pool UUID string */
1606 if (virStoragePoolGetUUIDString(pool
, &uuid
[0]) == 0)
1607 vshPrint(ctl
, "%s\n", uuid
);
1609 /* Return the storage pool name */
1610 vshPrint(ctl
, "%s\n", virStoragePoolGetName(pool
));
1614 virStorageVolFree(vol
);
1615 virStoragePoolFree(pool
);
1622 static const vshCmdInfo info_vol_key
[] = {
1624 .data
= N_("returns the volume key for a given volume name or path")
1632 static const vshCmdOptDef opts_vol_key
[] = {
1634 .type
= VSH_OT_DATA
,
1635 .flags
= VSH_OFLAG_REQ
,
1636 .help
= N_("volume name or path")
1638 VIRSH_COMMON_OPT_POOL_OPTIONAL
,
1643 cmdVolKey(vshControl
*ctl
, const vshCmd
*cmd
)
1645 virStorageVolPtr vol
;
1647 if (!(vol
= virshCommandOptVol(ctl
, cmd
, "vol", "pool", NULL
)))
1650 vshPrint(ctl
, "%s\n", virStorageVolGetKey(vol
));
1651 virStorageVolFree(vol
);
1656 * "vol-path" command
1658 static const vshCmdInfo info_vol_path
[] = {
1660 .data
= N_("returns the volume path for a given volume name or key")
1668 static const vshCmdOptDef opts_vol_path
[] = {
1670 .type
= VSH_OT_DATA
,
1671 .flags
= VSH_OFLAG_REQ
,
1672 .help
= N_("volume name or key")
1674 VIRSH_COMMON_OPT_POOL_OPTIONAL
,
1679 cmdVolPath(vshControl
*ctl
, const vshCmd
*cmd
)
1681 virStorageVolPtr vol
;
1682 char * StorageVolPath
;
1684 if (!(vol
= virshCommandOptVol(ctl
, cmd
, "vol", "pool", NULL
)))
1687 if ((StorageVolPath
= virStorageVolGetPath(vol
)) == NULL
) {
1688 virStorageVolFree(vol
);
1692 vshPrint(ctl
, "%s\n", StorageVolPath
);
1693 VIR_FREE(StorageVolPath
);
1694 virStorageVolFree(vol
);
1698 const vshCmdDef storageVolCmds
[] = {
1699 {.name
= "vol-clone",
1700 .handler
= cmdVolClone
,
1701 .opts
= opts_vol_clone
,
1702 .info
= info_vol_clone
,
1705 {.name
= "vol-create-as",
1706 .handler
= cmdVolCreateAs
,
1707 .opts
= opts_vol_create_as
,
1708 .info
= info_vol_create_as
,
1711 {.name
= "vol-create",
1712 .handler
= cmdVolCreate
,
1713 .opts
= opts_vol_create
,
1714 .info
= info_vol_create
,
1717 {.name
= "vol-create-from",
1718 .handler
= cmdVolCreateFrom
,
1719 .opts
= opts_vol_create_from
,
1720 .info
= info_vol_create_from
,
1723 {.name
= "vol-delete",
1724 .handler
= cmdVolDelete
,
1725 .opts
= opts_vol_delete
,
1726 .info
= info_vol_delete
,
1729 {.name
= "vol-download",
1730 .handler
= cmdVolDownload
,
1731 .opts
= opts_vol_download
,
1732 .info
= info_vol_download
,
1735 {.name
= "vol-dumpxml",
1736 .handler
= cmdVolDumpXML
,
1737 .opts
= opts_vol_dumpxml
,
1738 .info
= info_vol_dumpxml
,
1741 {.name
= "vol-info",
1742 .handler
= cmdVolInfo
,
1743 .opts
= opts_vol_info
,
1744 .info
= info_vol_info
,
1748 .handler
= cmdVolKey
,
1749 .opts
= opts_vol_key
,
1750 .info
= info_vol_key
,
1753 {.name
= "vol-list",
1754 .handler
= cmdVolList
,
1755 .opts
= opts_vol_list
,
1756 .info
= info_vol_list
,
1759 {.name
= "vol-name",
1760 .handler
= cmdVolName
,
1761 .opts
= opts_vol_name
,
1762 .info
= info_vol_name
,
1765 {.name
= "vol-path",
1766 .handler
= cmdVolPath
,
1767 .opts
= opts_vol_path
,
1768 .info
= info_vol_path
,
1771 {.name
= "vol-pool",
1772 .handler
= cmdVolPool
,
1773 .opts
= opts_vol_pool
,
1774 .info
= info_vol_pool
,
1777 {.name
= "vol-resize",
1778 .handler
= cmdVolResize
,
1779 .opts
= opts_vol_resize
,
1780 .info
= info_vol_resize
,
1783 {.name
= "vol-upload",
1784 .handler
= cmdVolUpload
,
1785 .opts
= opts_vol_upload
,
1786 .info
= info_vol_upload
,
1789 {.name
= "vol-wipe",
1790 .handler
= cmdVolWipe
,
1791 .opts
= opts_vol_wipe
,
1792 .info
= info_vol_wipe
,