2 * virsh-checkpoint.c: Commands to manage domain checkpoints
4 * Copyright (C) 2005-2019 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-checkpoint.h"
26 #include <libxml/parser.h>
27 #include <libxml/tree.h>
28 #include <libxml/xpath.h>
29 #include <libxml/xmlsave.h>
32 #include "virbuffer.h"
35 #include "virsh-util.h"
37 #include "vsh-table.h"
39 /* Helper for checkpoint-create and checkpoint-create-as */
41 virshCheckpointCreate(vshControl
*ctl
,
47 g_autoptr(virshDomainCheckpoint
) checkpoint
= NULL
;
48 const char *name
= NULL
;
50 checkpoint
= virDomainCheckpointCreateXML(dom
, buffer
, flags
);
52 if (checkpoint
== NULL
)
55 name
= virDomainCheckpointGetName(checkpoint
);
57 vshError(ctl
, "%s", _("Could not get checkpoint name"));
62 vshPrintExtra(ctl
, _("Domain checkpoint %1$s created from '%2$s'"),
65 vshPrintExtra(ctl
, _("Domain checkpoint %1$s created"), name
);
72 * "checkpoint-create" command
74 static const vshCmdInfo info_checkpoint_create
= {
75 .help
= N_("Create a checkpoint from XML"),
76 .desc
= N_("Create a checkpoint from XML for use in "
77 "future incremental backups"),
80 static const vshCmdOptDef opts_checkpoint_create
[] = {
81 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE
),
83 .type
= VSH_OT_STRING
,
85 .completer
= virshCompletePathLocalExisting
,
86 .help
= N_("domain checkpoint XML")
90 .help
= N_("redefine metadata for existing checkpoint")
92 {.name
= "redefine-validate",
94 .help
= N_("validate the redefined checkpoint")
98 .help
= N_("quiesce guest's file systems")
104 cmdCheckpointCreate(vshControl
*ctl
,
107 g_autoptr(virshDomain
) dom
= NULL
;
108 const char *from
= NULL
;
109 g_autofree
char *buffer
= NULL
;
110 unsigned int flags
= 0;
112 VSH_REQUIRE_OPTION("redefine-validate", "redefine");
114 if (vshCommandOptBool(cmd
, "redefine"))
115 flags
|= VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE
;
116 if (vshCommandOptBool(cmd
, "redefine-validate"))
117 flags
|= VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE_VALIDATE
;
118 if (vshCommandOptBool(cmd
, "quiesce"))
119 flags
|= VIR_DOMAIN_CHECKPOINT_CREATE_QUIESCE
;
121 if (!(dom
= virshCommandOptDomain(ctl
, cmd
, NULL
)))
124 if (vshCommandOptString(ctl
, cmd
, "xmlfile", &from
) < 0)
127 buffer
= g_strdup("<domaincheckpoint/>");
129 if (virFileReadAll(from
, VSH_MAX_XML_FILE
, &buffer
) < 0) {
130 vshSaveLibvirtError();
135 return virshCheckpointCreate(ctl
, dom
, buffer
, flags
, from
);
140 * "checkpoint-create-as" command
143 virshParseCheckpointDiskspec(vshControl
*ctl
,
148 const char *name
= NULL
;
149 const char *checkpoint
= NULL
;
150 const char *bitmap
= NULL
;
151 g_auto(GStrv
) array
= NULL
;
155 narray
= vshStringToArray(str
, &array
);
160 for (i
= 1; i
< narray
; i
++) {
161 if (!checkpoint
&& STRPREFIX(array
[i
], "checkpoint="))
162 checkpoint
= array
[i
] + strlen("checkpoint=");
163 else if (!bitmap
&& STRPREFIX(array
[i
], "bitmap="))
164 bitmap
= array
[i
] + strlen("bitmap=");
169 virBufferEscapeString(buf
, "<disk name='%s'", name
);
171 virBufferAsprintf(buf
, " checkpoint='%s'", checkpoint
);
173 virBufferAsprintf(buf
, " bitmap='%s'", bitmap
);
174 virBufferAddLit(buf
, "/>\n");
178 vshError(ctl
, _("unable to parse diskspec: %1$s"), str
);
182 static const vshCmdInfo info_checkpoint_create_as
= {
183 .help
= N_("Create a checkpoint from a set of args"),
184 .desc
= N_("Create a checkpoint from arguments for use in "
185 "future incremental backups"),
188 static const vshCmdOptDef opts_checkpoint_create_as
[] = {
189 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE
),
191 .type
= VSH_OT_STRING
,
192 .unwanted_positional
= true,
193 .completer
= virshCompleteEmpty
,
194 .help
= N_("name of checkpoint")
196 {.name
= "description",
197 .type
= VSH_OT_STRING
,
198 .unwanted_positional
= true,
199 .completer
= virshCompleteEmpty
,
200 .help
= N_("description of checkpoint")
202 {.name
= "print-xml",
204 .help
= N_("print XML document rather than create")
208 .help
= N_("quiesce guest's file systems")
212 .unwanted_positional
= true,
213 .help
= N_("disk attributes: disk[,checkpoint=type][,bitmap=name]")
220 cmdCheckpointCreateAs(vshControl
*ctl
,
223 g_autoptr(virshDomain
) dom
= NULL
;
224 g_autofree
char *buffer
= NULL
;
225 const char *name
= NULL
;
226 const char *desc
= NULL
;
227 g_auto(virBuffer
) buf
= VIR_BUFFER_INITIALIZER
;
228 unsigned int flags
= 0;
229 const char **diskspec
= NULL
;
231 if (vshCommandOptBool(cmd
, "quiesce"))
232 flags
|= VIR_DOMAIN_CHECKPOINT_CREATE_QUIESCE
;
234 if (!(dom
= virshCommandOptDomain(ctl
, cmd
, NULL
)))
237 if (vshCommandOptString(ctl
, cmd
, "name", &name
) < 0 ||
238 vshCommandOptString(ctl
, cmd
, "description", &desc
) < 0)
241 virBufferAddLit(&buf
, "<domaincheckpoint>\n");
242 virBufferAdjustIndent(&buf
, 2);
243 virBufferEscapeString(&buf
, "<name>%s</name>\n", name
);
244 virBufferEscapeString(&buf
, "<description>%s</description>\n", desc
);
246 if ((diskspec
= vshCommandOptArgv(cmd
, "diskspec"))) {
247 virBufferAddLit(&buf
, "<disks>\n");
248 virBufferAdjustIndent(&buf
, 2);
250 for (; *diskspec
; diskspec
++) {
251 if (virshParseCheckpointDiskspec(ctl
, &buf
, *diskspec
) < 0)
255 virBufferAdjustIndent(&buf
, -2);
256 virBufferAddLit(&buf
, "</disks>\n");
258 virBufferAdjustIndent(&buf
, -2);
259 virBufferAddLit(&buf
, "</domaincheckpoint>\n");
261 buffer
= virBufferContentAndReset(&buf
);
263 if (vshCommandOptBool(cmd
, "print-xml")) {
264 vshPrint(ctl
, "%s\n", buffer
);
268 return virshCheckpointCreate(ctl
, dom
, buffer
, flags
, NULL
);
272 /* Helper for resolving --ARG name into a checkpoint
273 * belonging to DOM. On success, populate *CHK and *NAME, before
274 * returning 0. On failure, return -1 after issuing an error
277 virshLookupCheckpoint(vshControl
*ctl
,
281 virDomainCheckpointPtr
*chk
,
284 const char *chkname
= NULL
;
286 if (vshCommandOptString(ctl
, cmd
, arg
, &chkname
) < 0)
289 if (!(*chk
= virDomainCheckpointLookupByName(dom
, chkname
, 0)))
292 *name
= virDomainCheckpointGetName(*chk
);
298 * "checkpoint-edit" command
300 static const vshCmdInfo info_checkpoint_edit
= {
301 .help
= N_("edit XML for a checkpoint"),
302 .desc
= N_("Edit the domain checkpoint XML for a named checkpoint"),
305 static const vshCmdOptDef opts_checkpoint_edit
[] = {
306 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT
),
307 {.name
= "checkpointname",
308 .type
= VSH_OT_STRING
,
311 .help
= N_("checkpoint name"),
312 .completer
= virshCheckpointNameCompleter
,
318 cmdCheckpointEdit(vshControl
*ctl
,
321 g_autoptr(virshDomain
) dom
= NULL
;
322 g_autoptr(virshDomainCheckpoint
) checkpoint
= NULL
;
323 g_autoptr(virshDomainCheckpoint
) edited
= NULL
;
324 const char *name
= NULL
;
325 const char *edited_name
;
327 unsigned int getxml_flags
= VIR_DOMAIN_CHECKPOINT_XML_SECURE
;
328 unsigned int define_flags
= VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE
;
330 if (!(dom
= virshCommandOptDomain(ctl
, cmd
, NULL
)))
333 if (virshLookupCheckpoint(ctl
, cmd
, "checkpointname", dom
,
334 &checkpoint
, &name
) < 0)
337 #define EDIT_GET_XML \
338 virDomainCheckpointGetXMLDesc(checkpoint, getxml_flags)
339 #define EDIT_NOT_CHANGED \
342 _("Checkpoint %1$s XML configuration not changed.\n"), \
347 #define EDIT_DEFINE \
348 edited = virDomainCheckpointCreateXML(dom, doc_edited, define_flags)
349 #include "virsh-edit.c"
351 edited_name
= virDomainCheckpointGetName(edited
);
352 if (STREQ(name
, edited_name
)) {
353 vshPrintExtra(ctl
, _("Checkpoint %1$s edited.\n"), name
);
355 unsigned int delete_flags
= VIR_DOMAIN_CHECKPOINT_DELETE_METADATA_ONLY
;
357 if (virDomainCheckpointDelete(edited
, delete_flags
) < 0) {
359 vshError(ctl
, _("Failed to clean up %1$s"), edited_name
);
362 vshError(ctl
, _("Cannot rename checkpoint %1$s to %2$s"),
371 vshError(ctl
, _("Failed to update %1$s"), name
);
376 /* Helper function to get the name of a checkpoint's parent. Caller
377 * must free the result. Returns 0 on success (including when it was
378 * proven no parent exists), and -1 on failure with error reported
379 * (such as no checkpoint support or domain deleted in meantime). */
381 virshGetCheckpointParent(vshControl
*ctl
,
382 virDomainCheckpointPtr checkpoint
,
385 g_autoptr(virshDomainCheckpoint
) parent
= NULL
;
390 parent
= virDomainCheckpointGetParent(checkpoint
, 0);
392 /* API works, and virDomainCheckpointGetName will succeed */
393 *parent_name
= g_strdup(virDomainCheckpointGetName(parent
));
395 } else if (last_error
->code
== VIR_ERR_NO_DOMAIN_CHECKPOINT
) {
396 /* API works, and we found a root with no parent */
402 vshError(ctl
, "%s", _("unable to determine if checkpoint has parent"));
404 vshResetLibvirtError();
411 * "checkpoint-info" command
413 static const vshCmdInfo info_checkpoint_info
= {
414 .help
= N_("checkpoint information"),
415 .desc
= N_("Returns basic information about a checkpoint."),
418 static const vshCmdOptDef opts_checkpoint_info
[] = {
419 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT
),
420 {.name
= "checkpointname",
421 .type
= VSH_OT_STRING
,
424 .help
= N_("checkpoint name"),
425 .completer
= virshCheckpointNameCompleter
,
432 cmdCheckpointInfo(vshControl
*ctl
,
435 g_autoptr(virshDomain
) dom
= NULL
;
436 g_autoptr(virshDomainCheckpoint
) checkpoint
= NULL
;
438 g_autofree
char *parent
= NULL
;
442 dom
= virshCommandOptDomain(ctl
, cmd
, NULL
);
446 if (virshLookupCheckpoint(ctl
, cmd
, "checkpointname", dom
,
447 &checkpoint
, &name
) < 0)
450 vshPrint(ctl
, "%-15s %s\n", _("Name:"), name
);
451 vshPrint(ctl
, "%-15s %s\n", _("Domain:"), virDomainGetName(dom
));
453 if (virshGetCheckpointParent(ctl
, checkpoint
, &parent
) < 0) {
455 _("unexpected problem querying checkpoint state"));
458 vshPrint(ctl
, "%-15s %s\n", _("Parent:"), NULLSTR_MINUS(parent
));
460 /* Children, Descendants. */
462 count
= virDomainCheckpointListAllChildren(checkpoint
, NULL
, flags
);
464 if (last_error
->code
== VIR_ERR_NO_SUPPORT
) {
465 vshResetLibvirtError();
470 vshPrint(ctl
, "%-15s %d\n", _("Children:"), count
);
471 flags
= VIR_DOMAIN_CHECKPOINT_LIST_DESCENDANTS
;
472 count
= virDomainCheckpointListAllChildren(checkpoint
, NULL
, flags
);
475 vshPrint(ctl
, "%-15s %d\n", _("Descendants:"), count
);
481 /* Helpers for collecting a list of checkpoints. */
483 virDomainCheckpointPtr chk
;
486 struct virshCheckpointList
{
487 struct virshChk
*chks
;
492 virshCheckpointListFree(struct virshCheckpointList
*checkpointlist
)
498 if (checkpointlist
->chks
) {
499 for (i
= 0; i
< checkpointlist
->nchks
; i
++) {
500 virshDomainCheckpointFree(checkpointlist
->chks
[i
].chk
);
501 g_free(checkpointlist
->chks
[i
].parent
);
503 g_free(checkpointlist
->chks
);
505 g_free(checkpointlist
);
510 virshChkSorter(const void *a
,
512 void *opaque G_GNUC_UNUSED
)
514 const struct virshChk
*sa
= a
;
515 const struct virshChk
*sb
= b
;
517 if (sa
->chk
&& !sb
->chk
)
520 return sb
->chk
!= NULL
;
522 return vshStrcasecmp(virDomainCheckpointGetName(sa
->chk
),
523 virDomainCheckpointGetName(sb
->chk
));
527 /* Compute a list of checkpoints from DOM. If FROM is provided, the
528 * list is limited to descendants of the given checkpoint. If FLAGS is
529 * given, the list is filtered. If TREE is specified, then all but
530 * FROM or the roots will also have parent information. */
531 static struct virshCheckpointList
*
532 virshCheckpointListCollect(vshControl
*ctl
,
534 virDomainCheckpointPtr from
,
535 unsigned int orig_flags
,
540 virDomainCheckpointPtr
*chks
;
541 struct virshCheckpointList
*checkpointlist
= NULL
;
542 struct virshCheckpointList
*ret
= NULL
;
543 unsigned int flags
= orig_flags
;
545 checkpointlist
= g_new0(struct virshCheckpointList
, 1);
548 count
= virDomainCheckpointListAllChildren(from
, &chks
, flags
);
550 count
= virDomainListAllCheckpoints(dom
, &chks
, flags
);
553 _("unexpected problem querying checkpoints"));
557 /* When mixing --from and --tree, we also want a copy of from
558 * in the list, but with no parent for that one entry. */
560 checkpointlist
->chks
= g_new0(struct virshChk
, count
+ 1);
562 checkpointlist
->chks
= g_new0(struct virshChk
, count
);
563 checkpointlist
->nchks
= count
;
564 for (i
= 0; i
< count
; i
++)
565 checkpointlist
->chks
[i
].chk
= chks
[i
];
568 for (i
= 0; i
< count
; i
++) {
569 if (virshGetCheckpointParent(ctl
, checkpointlist
->chks
[i
].chk
,
570 &checkpointlist
->chks
[i
].parent
) < 0)
574 checkpointlist
->chks
[checkpointlist
->nchks
++].chk
= from
;
575 virDomainCheckpointRef(from
);
579 if (!(orig_flags
& VIR_DOMAIN_CHECKPOINT_LIST_TOPOLOGICAL
) &&
580 checkpointlist
->chks
) {
581 g_qsort_with_data(checkpointlist
->chks
, checkpointlist
->nchks
,
582 sizeof(*checkpointlist
->chks
), virshChkSorter
, NULL
);
585 ret
= g_steal_pointer(&checkpointlist
);
588 virshCheckpointListFree(checkpointlist
);
594 virshCheckpointListLookup(int id
,
598 struct virshCheckpointList
*checkpointlist
= opaque
;
600 return checkpointlist
->chks
[id
].parent
;
601 return virDomainCheckpointGetName(checkpointlist
->chks
[id
].chk
);
606 * "checkpoint-list" command
608 static const vshCmdInfo info_checkpoint_list
= {
609 .help
= N_("List checkpoints for a domain"),
610 .desc
= N_("Checkpoint List"),
613 static const vshCmdOptDef opts_checkpoint_list
[] = {
614 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT
),
617 .help
= N_("add a column showing parent checkpoint")
621 .help
= N_("list only checkpoints without parents")
625 .help
= N_("list only checkpoints without children")
627 {.name
= "no-leaves",
629 .help
= N_("list only checkpoints that are not leaves (with children)")
633 .help
= N_("list checkpoints in a tree")
636 .type
= VSH_OT_STRING
,
637 .unwanted_positional
= true,
638 .help
= N_("limit list to children of given checkpoint"),
639 .completer
= virshCheckpointNameCompleter
,
641 {.name
= "descendants",
643 .help
= N_("with --from, list all descendants")
647 .help
= N_("list checkpoint names only")
649 {.name
= "topological",
651 .help
= N_("sort list topologically rather than by name"),
657 cmdCheckpointList(vshControl
*ctl
,
660 g_autoptr(virshDomain
) dom
= NULL
;
662 unsigned int flags
= 0;
664 virDomainCheckpointPtr checkpoint
= NULL
;
665 long long creation_longlong
;
666 g_autoptr(GDateTime
) then
= NULL
;
667 bool tree
= vshCommandOptBool(cmd
, "tree");
668 bool name
= vshCommandOptBool(cmd
, "name");
669 bool from
= vshCommandOptBool(cmd
, "from");
670 bool parent
= vshCommandOptBool(cmd
, "parent");
671 bool roots
= vshCommandOptBool(cmd
, "roots");
672 const char *from_chk
= NULL
;
673 g_autoptr(virshDomainCheckpoint
) start
= NULL
;
674 struct virshCheckpointList
*checkpointlist
= NULL
;
675 g_autoptr(vshTable
) table
= NULL
;
677 VSH_EXCLUSIVE_OPTIONS_VAR(tree
, name
);
678 VSH_EXCLUSIVE_OPTIONS_VAR(parent
, roots
);
679 VSH_EXCLUSIVE_OPTIONS_VAR(parent
, tree
);
680 VSH_EXCLUSIVE_OPTIONS_VAR(roots
, tree
);
681 VSH_EXCLUSIVE_OPTIONS_VAR(roots
, from
);
683 #define FILTER(option, flag) \
685 if (vshCommandOptBool(cmd, option)) { \
688 _("--%1$s and --tree are mutually exclusive"), \
692 flags |= VIR_DOMAIN_CHECKPOINT_LIST_ ## flag; \
696 FILTER("leaves", LEAVES
);
697 FILTER("no-leaves", NO_LEAVES
);
700 if (vshCommandOptBool(cmd
, "topological"))
701 flags
|= VIR_DOMAIN_CHECKPOINT_LIST_TOPOLOGICAL
;
704 flags
|= VIR_DOMAIN_CHECKPOINT_LIST_ROOTS
;
706 if (vshCommandOptBool(cmd
, "descendants")) {
709 _("--descendants requires --from"));
712 flags
|= VIR_DOMAIN_CHECKPOINT_LIST_DESCENDANTS
;
715 if (!(dom
= virshCommandOptDomain(ctl
, cmd
, NULL
)))
719 virshLookupCheckpoint(ctl
, cmd
, "from", dom
, &start
, &from_chk
) < 0)
722 if (!(checkpointlist
= virshCheckpointListCollect(ctl
, dom
, start
, flags
,
726 if (!tree
&& !name
) {
728 table
= vshTableNew(_("Name"), _("Creation Time"), _("Parent"),
731 table
= vshTableNew(_("Name"), _("Creation Time"), NULL
);
737 for (i
= 0; i
< checkpointlist
->nchks
; i
++) {
738 if (!checkpointlist
->chks
[i
].parent
&&
739 vshTreePrint(ctl
, virshCheckpointListLookup
, checkpointlist
,
740 checkpointlist
->nchks
, i
) < 0)
747 for (i
= 0; i
< checkpointlist
->nchks
; i
++) {
748 g_autofree gchar
*thenstr
= NULL
;
749 g_autoptr(xmlDoc
) xml
= NULL
;
750 g_autoptr(xmlXPathContext
) ctxt
= NULL
;
751 g_autofree
char *parent_chk
= NULL
;
752 g_autofree
char *doc
= NULL
;
753 const char *chk_name
;
755 checkpoint
= checkpointlist
->chks
[i
].chk
;
756 chk_name
= virDomainCheckpointGetName(checkpoint
);
759 if (!(doc
= virDomainCheckpointGetXMLDesc(checkpoint
, 0)))
762 if (!(xml
= virXMLParseStringCtxt(doc
, _("(domain_checkpoint)"), &ctxt
)))
766 parent_chk
= virXPathString("string(/domaincheckpoint/parent/name)",
770 vshPrint(ctl
, "%s", chk_name
);
773 vshPrint(ctl
, "\t%s", parent_chk
);
777 /* just print the checkpoint name */
781 if (virXPathLongLong("string(/domaincheckpoint/creationTime)", ctxt
,
782 &creation_longlong
) < 0)
785 then
= g_date_time_new_from_unix_local(creation_longlong
);
786 thenstr
= g_date_time_format(then
, "%Y-%m-%d %H:%M:%S %z");
789 if (vshTableRowAppend(table
, chk_name
, thenstr
,
790 NULLSTR_EMPTY(parent_chk
), NULL
) < 0)
793 if (vshTableRowAppend(table
, chk_name
, thenstr
, NULL
) < 0)
799 vshTablePrintToStdout(table
, ctl
);
804 virshCheckpointListFree(checkpointlist
);
810 * "checkpoint-dumpxml" command
812 static const vshCmdInfo info_checkpoint_dumpxml
= {
813 .help
= N_("Dump XML for a domain checkpoint"),
814 .desc
= N_("Checkpoint Dump XML"),
817 static const vshCmdOptDef opts_checkpoint_dumpxml
[] = {
818 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT
),
819 {.name
= "checkpointname",
820 .type
= VSH_OT_STRING
,
823 .help
= N_("checkpoint name"),
824 .completer
= virshCheckpointNameCompleter
,
826 {.name
= "security-info",
828 .help
= N_("include security sensitive information in XML dump")
830 {.name
= "no-domain",
832 .help
= N_("exclude <domain> from XML")
836 .help
= N_("include backup size estimate in XML dump")
839 .type
= VSH_OT_STRING
,
840 .completer
= virshCompleteEmpty
,
841 .help
= N_("xpath expression to filter the XML document")
845 .help
= N_("wrap xpath results in an common root element"),
851 cmdCheckpointDumpXML(vshControl
*ctl
,
854 g_autoptr(virshDomain
) dom
= NULL
;
855 const char *name
= NULL
;
856 g_autoptr(virshDomainCheckpoint
) checkpoint
= NULL
;
857 g_autofree
char *xml
= NULL
;
858 unsigned int flags
= 0;
859 bool wrap
= vshCommandOptBool(cmd
, "wrap");
860 const char *xpath
= NULL
;
862 if (vshCommandOptBool(cmd
, "security-info"))
863 flags
|= VIR_DOMAIN_CHECKPOINT_XML_SECURE
;
864 if (vshCommandOptBool(cmd
, "no-domain"))
865 flags
|= VIR_DOMAIN_CHECKPOINT_XML_NO_DOMAIN
;
866 if (vshCommandOptBool(cmd
, "size"))
867 flags
|= VIR_DOMAIN_CHECKPOINT_XML_SIZE
;
869 if (!(dom
= virshCommandOptDomain(ctl
, cmd
, NULL
)))
872 if (vshCommandOptStringQuiet(ctl
, cmd
, "xpath", &xpath
) < 0)
875 if (virshLookupCheckpoint(ctl
, cmd
, "checkpointname", dom
,
876 &checkpoint
, &name
) < 0)
879 if (!(xml
= virDomainCheckpointGetXMLDesc(checkpoint
, flags
)))
882 return virshDumpXML(ctl
, xml
, "domain-checkpoint", xpath
, wrap
);
887 * "checkpoint-parent" command
889 static const vshCmdInfo info_checkpoint_parent
= {
890 .help
= N_("Get the name of the parent of a checkpoint"),
891 .desc
= N_("Extract the checkpoint's parent, if any"),
894 static const vshCmdOptDef opts_checkpoint_parent
[] = {
895 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT
),
896 {.name
= "checkpointname",
897 .type
= VSH_OT_STRING
,
900 .help
= N_("find parent of checkpoint name"),
901 .completer
= virshCheckpointNameCompleter
,
907 cmdCheckpointParent(vshControl
*ctl
,
910 g_autoptr(virshDomain
) dom
= NULL
;
911 const char *name
= NULL
;
912 g_autoptr(virshDomainCheckpoint
) checkpoint
= NULL
;
913 g_autofree
char *parent
= NULL
;
915 dom
= virshCommandOptDomain(ctl
, cmd
, NULL
);
919 if (virshLookupCheckpoint(ctl
, cmd
, "checkpointname", dom
,
920 &checkpoint
, &name
) < 0)
923 if (virshGetCheckpointParent(ctl
, checkpoint
, &parent
) < 0)
926 vshError(ctl
, _("checkpoint '%1$s' has no parent"), name
);
930 vshPrint(ctl
, "%s", parent
);
937 * "checkpoint-delete" command
939 static const vshCmdInfo info_checkpoint_delete
= {
940 .help
= N_("Delete a domain checkpoint"),
941 .desc
= N_("Checkpoint Delete"),
944 static const vshCmdOptDef opts_checkpoint_delete
[] = {
945 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT
|
946 VIR_CONNECT_LIST_DOMAINS_ACTIVE
),
947 {.name
= "checkpointname",
948 .type
= VSH_OT_STRING
,
951 .help
= N_("checkpoint name"),
952 .completer
= virshCheckpointNameCompleter
,
956 .help
= N_("delete checkpoint and all children")
958 {.name
= "children-only",
960 .help
= N_("delete children but not checkpoint")
964 .help
= N_("delete only libvirt metadata, leaving checkpoint contents behind")
970 cmdCheckpointDelete(vshControl
*ctl
,
973 g_autoptr(virshDomain
) dom
= NULL
;
974 const char *name
= NULL
;
975 g_autoptr(virshDomainCheckpoint
) checkpoint
= NULL
;
976 unsigned int flags
= 0;
978 dom
= virshCommandOptDomain(ctl
, cmd
, NULL
);
982 if (virshLookupCheckpoint(ctl
, cmd
, "checkpointname", dom
,
983 &checkpoint
, &name
) < 0)
986 if (vshCommandOptBool(cmd
, "children"))
987 flags
|= VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN
;
988 if (vshCommandOptBool(cmd
, "children-only"))
989 flags
|= VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY
;
990 if (vshCommandOptBool(cmd
, "metadata"))
991 flags
|= VIR_DOMAIN_CHECKPOINT_DELETE_METADATA_ONLY
;
993 if (virDomainCheckpointDelete(checkpoint
, flags
) == 0) {
994 if (flags
& VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY
)
995 vshPrintExtra(ctl
, _("Domain checkpoint %1$s children deleted\n"), name
);
997 vshPrintExtra(ctl
, _("Domain checkpoint %1$s deleted\n"), name
);
999 vshError(ctl
, _("Failed to delete checkpoint %1$s"), name
);
1007 const vshCmdDef checkpointCmds
[] = {
1008 {.name
= "checkpoint-create",
1009 .handler
= cmdCheckpointCreate
,
1010 .opts
= opts_checkpoint_create
,
1011 .info
= &info_checkpoint_create
,
1014 {.name
= "checkpoint-create-as",
1015 .handler
= cmdCheckpointCreateAs
,
1016 .opts
= opts_checkpoint_create_as
,
1017 .info
= &info_checkpoint_create_as
,
1020 {.name
= "checkpoint-delete",
1021 .handler
= cmdCheckpointDelete
,
1022 .opts
= opts_checkpoint_delete
,
1023 .info
= &info_checkpoint_delete
,
1026 {.name
= "checkpoint-dumpxml",
1027 .handler
= cmdCheckpointDumpXML
,
1028 .opts
= opts_checkpoint_dumpxml
,
1029 .info
= &info_checkpoint_dumpxml
,
1032 {.name
= "checkpoint-edit",
1033 .handler
= cmdCheckpointEdit
,
1034 .opts
= opts_checkpoint_edit
,
1035 .info
= &info_checkpoint_edit
,
1038 {.name
= "checkpoint-info",
1039 .handler
= cmdCheckpointInfo
,
1040 .opts
= opts_checkpoint_info
,
1041 .info
= &info_checkpoint_info
,
1044 {.name
= "checkpoint-list",
1045 .handler
= cmdCheckpointList
,
1046 .opts
= opts_checkpoint_list
,
1047 .info
= &info_checkpoint_list
,
1050 {.name
= "checkpoint-parent",
1051 .handler
= cmdCheckpointParent
,
1052 .opts
= opts_checkpoint_parent
,
1053 .info
= &info_checkpoint_parent
,