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 (vshCommandOptStringReq(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 vshCmdOpt
*opt
= NULL
;
231 if (vshCommandOptBool(cmd
, "quiesce"))
232 flags
|= VIR_DOMAIN_CHECKPOINT_CREATE_QUIESCE
;
234 if (!(dom
= virshCommandOptDomain(ctl
, cmd
, NULL
)))
237 if (vshCommandOptStringReq(ctl
, cmd
, "name", &name
) < 0 ||
238 vshCommandOptStringReq(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 (vshCommandOptBool(cmd
, "diskspec")) {
247 virBufferAddLit(&buf
, "<disks>\n");
248 virBufferAdjustIndent(&buf
, 2);
249 while ((opt
= vshCommandOptArgv(ctl
, cmd
, opt
))) {
250 if (virshParseCheckpointDiskspec(ctl
, &buf
, opt
->data
) < 0)
253 virBufferAdjustIndent(&buf
, -2);
254 virBufferAddLit(&buf
, "</disks>\n");
256 virBufferAdjustIndent(&buf
, -2);
257 virBufferAddLit(&buf
, "</domaincheckpoint>\n");
259 buffer
= virBufferContentAndReset(&buf
);
261 if (vshCommandOptBool(cmd
, "print-xml")) {
262 vshPrint(ctl
, "%s\n", buffer
);
266 return virshCheckpointCreate(ctl
, dom
, buffer
, flags
, NULL
);
270 /* Helper for resolving --ARG name into a checkpoint
271 * belonging to DOM. On success, populate *CHK and *NAME, before
272 * returning 0. On failure, return -1 after issuing an error
275 virshLookupCheckpoint(vshControl
*ctl
,
279 virDomainCheckpointPtr
*chk
,
282 const char *chkname
= NULL
;
284 if (vshCommandOptStringReq(ctl
, cmd
, arg
, &chkname
) < 0)
287 if (!(*chk
= virDomainCheckpointLookupByName(dom
, chkname
, 0)))
290 *name
= virDomainCheckpointGetName(*chk
);
296 * "checkpoint-edit" command
298 static const vshCmdInfo info_checkpoint_edit
= {
299 .help
= N_("edit XML for a checkpoint"),
300 .desc
= N_("Edit the domain checkpoint XML for a named checkpoint"),
303 static const vshCmdOptDef opts_checkpoint_edit
[] = {
304 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT
),
305 {.name
= "checkpointname",
306 .type
= VSH_OT_STRING
,
309 .help
= N_("checkpoint name"),
310 .completer
= virshCheckpointNameCompleter
,
316 cmdCheckpointEdit(vshControl
*ctl
,
319 g_autoptr(virshDomain
) dom
= NULL
;
320 g_autoptr(virshDomainCheckpoint
) checkpoint
= NULL
;
321 g_autoptr(virshDomainCheckpoint
) edited
= NULL
;
322 const char *name
= NULL
;
323 const char *edited_name
;
325 unsigned int getxml_flags
= VIR_DOMAIN_CHECKPOINT_XML_SECURE
;
326 unsigned int define_flags
= VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE
;
328 if (!(dom
= virshCommandOptDomain(ctl
, cmd
, NULL
)))
331 if (virshLookupCheckpoint(ctl
, cmd
, "checkpointname", dom
,
332 &checkpoint
, &name
) < 0)
335 #define EDIT_GET_XML \
336 virDomainCheckpointGetXMLDesc(checkpoint, getxml_flags)
337 #define EDIT_NOT_CHANGED \
340 _("Checkpoint %1$s XML configuration not changed.\n"), \
345 #define EDIT_DEFINE \
346 edited = virDomainCheckpointCreateXML(dom, doc_edited, define_flags)
347 #include "virsh-edit.c"
349 edited_name
= virDomainCheckpointGetName(edited
);
350 if (STREQ(name
, edited_name
)) {
351 vshPrintExtra(ctl
, _("Checkpoint %1$s edited.\n"), name
);
353 unsigned int delete_flags
= VIR_DOMAIN_CHECKPOINT_DELETE_METADATA_ONLY
;
355 if (virDomainCheckpointDelete(edited
, delete_flags
) < 0) {
357 vshError(ctl
, _("Failed to clean up %1$s"), edited_name
);
360 vshError(ctl
, _("Cannot rename checkpoint %1$s to %2$s"),
369 vshError(ctl
, _("Failed to update %1$s"), name
);
374 /* Helper function to get the name of a checkpoint's parent. Caller
375 * must free the result. Returns 0 on success (including when it was
376 * proven no parent exists), and -1 on failure with error reported
377 * (such as no checkpoint support or domain deleted in meantime). */
379 virshGetCheckpointParent(vshControl
*ctl
,
380 virDomainCheckpointPtr checkpoint
,
383 g_autoptr(virshDomainCheckpoint
) parent
= NULL
;
388 parent
= virDomainCheckpointGetParent(checkpoint
, 0);
390 /* API works, and virDomainCheckpointGetName will succeed */
391 *parent_name
= g_strdup(virDomainCheckpointGetName(parent
));
393 } else if (last_error
->code
== VIR_ERR_NO_DOMAIN_CHECKPOINT
) {
394 /* API works, and we found a root with no parent */
400 vshError(ctl
, "%s", _("unable to determine if checkpoint has parent"));
402 vshResetLibvirtError();
409 * "checkpoint-info" command
411 static const vshCmdInfo info_checkpoint_info
= {
412 .help
= N_("checkpoint information"),
413 .desc
= N_("Returns basic information about a checkpoint."),
416 static const vshCmdOptDef opts_checkpoint_info
[] = {
417 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT
),
418 {.name
= "checkpointname",
419 .type
= VSH_OT_STRING
,
422 .help
= N_("checkpoint name"),
423 .completer
= virshCheckpointNameCompleter
,
430 cmdCheckpointInfo(vshControl
*ctl
,
433 g_autoptr(virshDomain
) dom
= NULL
;
434 g_autoptr(virshDomainCheckpoint
) checkpoint
= NULL
;
436 g_autofree
char *parent
= NULL
;
440 dom
= virshCommandOptDomain(ctl
, cmd
, NULL
);
444 if (virshLookupCheckpoint(ctl
, cmd
, "checkpointname", dom
,
445 &checkpoint
, &name
) < 0)
448 vshPrint(ctl
, "%-15s %s\n", _("Name:"), name
);
449 vshPrint(ctl
, "%-15s %s\n", _("Domain:"), virDomainGetName(dom
));
451 if (virshGetCheckpointParent(ctl
, checkpoint
, &parent
) < 0) {
453 _("unexpected problem querying checkpoint state"));
456 vshPrint(ctl
, "%-15s %s\n", _("Parent:"), parent
? parent
: "-");
458 /* Children, Descendants. */
460 count
= virDomainCheckpointListAllChildren(checkpoint
, NULL
, flags
);
462 if (last_error
->code
== VIR_ERR_NO_SUPPORT
) {
463 vshResetLibvirtError();
468 vshPrint(ctl
, "%-15s %d\n", _("Children:"), count
);
469 flags
= VIR_DOMAIN_CHECKPOINT_LIST_DESCENDANTS
;
470 count
= virDomainCheckpointListAllChildren(checkpoint
, NULL
, flags
);
473 vshPrint(ctl
, "%-15s %d\n", _("Descendants:"), count
);
479 /* Helpers for collecting a list of checkpoints. */
481 virDomainCheckpointPtr chk
;
484 struct virshCheckpointList
{
485 struct virshChk
*chks
;
490 virshCheckpointListFree(struct virshCheckpointList
*checkpointlist
)
496 if (checkpointlist
->chks
) {
497 for (i
= 0; i
< checkpointlist
->nchks
; i
++) {
498 virshDomainCheckpointFree(checkpointlist
->chks
[i
].chk
);
499 g_free(checkpointlist
->chks
[i
].parent
);
501 g_free(checkpointlist
->chks
);
503 g_free(checkpointlist
);
508 virshChkSorter(const void *a
,
510 void *opaque G_GNUC_UNUSED
)
512 const struct virshChk
*sa
= a
;
513 const struct virshChk
*sb
= b
;
515 if (sa
->chk
&& !sb
->chk
)
518 return sb
->chk
!= NULL
;
520 return vshStrcasecmp(virDomainCheckpointGetName(sa
->chk
),
521 virDomainCheckpointGetName(sb
->chk
));
525 /* Compute a list of checkpoints from DOM. If FROM is provided, the
526 * list is limited to descendants of the given checkpoint. If FLAGS is
527 * given, the list is filtered. If TREE is specified, then all but
528 * FROM or the roots will also have parent information. */
529 static struct virshCheckpointList
*
530 virshCheckpointListCollect(vshControl
*ctl
,
532 virDomainCheckpointPtr from
,
533 unsigned int orig_flags
,
538 virDomainCheckpointPtr
*chks
;
539 struct virshCheckpointList
*checkpointlist
= NULL
;
540 struct virshCheckpointList
*ret
= NULL
;
541 unsigned int flags
= orig_flags
;
543 checkpointlist
= g_new0(struct virshCheckpointList
, 1);
546 count
= virDomainCheckpointListAllChildren(from
, &chks
, flags
);
548 count
= virDomainListAllCheckpoints(dom
, &chks
, flags
);
551 _("unexpected problem querying checkpoints"));
555 /* When mixing --from and --tree, we also want a copy of from
556 * in the list, but with no parent for that one entry. */
558 checkpointlist
->chks
= g_new0(struct virshChk
, count
+ 1);
560 checkpointlist
->chks
= g_new0(struct virshChk
, count
);
561 checkpointlist
->nchks
= count
;
562 for (i
= 0; i
< count
; i
++)
563 checkpointlist
->chks
[i
].chk
= chks
[i
];
566 for (i
= 0; i
< count
; i
++) {
567 if (virshGetCheckpointParent(ctl
, checkpointlist
->chks
[i
].chk
,
568 &checkpointlist
->chks
[i
].parent
) < 0)
572 checkpointlist
->chks
[checkpointlist
->nchks
++].chk
= from
;
573 virDomainCheckpointRef(from
);
577 if (!(orig_flags
& VIR_DOMAIN_CHECKPOINT_LIST_TOPOLOGICAL
) &&
578 checkpointlist
->chks
) {
579 g_qsort_with_data(checkpointlist
->chks
, checkpointlist
->nchks
,
580 sizeof(*checkpointlist
->chks
), virshChkSorter
, NULL
);
583 ret
= g_steal_pointer(&checkpointlist
);
586 virshCheckpointListFree(checkpointlist
);
592 virshCheckpointListLookup(int id
,
596 struct virshCheckpointList
*checkpointlist
= opaque
;
598 return checkpointlist
->chks
[id
].parent
;
599 return virDomainCheckpointGetName(checkpointlist
->chks
[id
].chk
);
604 * "checkpoint-list" command
606 static const vshCmdInfo info_checkpoint_list
= {
607 .help
= N_("List checkpoints for a domain"),
608 .desc
= N_("Checkpoint List"),
611 static const vshCmdOptDef opts_checkpoint_list
[] = {
612 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT
),
615 .help
= N_("add a column showing parent checkpoint")
619 .help
= N_("list only checkpoints without parents")
623 .help
= N_("list only checkpoints without children")
625 {.name
= "no-leaves",
627 .help
= N_("list only checkpoints that are not leaves (with children)")
631 .help
= N_("list checkpoints in a tree")
634 .type
= VSH_OT_STRING
,
635 .unwanted_positional
= true,
636 .help
= N_("limit list to children of given checkpoint"),
637 .completer
= virshCheckpointNameCompleter
,
639 {.name
= "descendants",
641 .help
= N_("with --from, list all descendants")
645 .help
= N_("list checkpoint names only")
647 {.name
= "topological",
649 .help
= N_("sort list topologically rather than by name"),
655 cmdCheckpointList(vshControl
*ctl
,
658 g_autoptr(virshDomain
) dom
= NULL
;
660 unsigned int flags
= 0;
662 virDomainCheckpointPtr checkpoint
= NULL
;
663 long long creation_longlong
;
664 g_autoptr(GDateTime
) then
= NULL
;
665 bool tree
= vshCommandOptBool(cmd
, "tree");
666 bool name
= vshCommandOptBool(cmd
, "name");
667 bool from
= vshCommandOptBool(cmd
, "from");
668 bool parent
= vshCommandOptBool(cmd
, "parent");
669 bool roots
= vshCommandOptBool(cmd
, "roots");
670 const char *from_chk
= NULL
;
671 g_autoptr(virshDomainCheckpoint
) start
= NULL
;
672 struct virshCheckpointList
*checkpointlist
= NULL
;
673 g_autoptr(vshTable
) table
= NULL
;
675 VSH_EXCLUSIVE_OPTIONS_VAR(tree
, name
);
676 VSH_EXCLUSIVE_OPTIONS_VAR(parent
, roots
);
677 VSH_EXCLUSIVE_OPTIONS_VAR(parent
, tree
);
678 VSH_EXCLUSIVE_OPTIONS_VAR(roots
, tree
);
679 VSH_EXCLUSIVE_OPTIONS_VAR(roots
, from
);
681 #define FILTER(option, flag) \
683 if (vshCommandOptBool(cmd, option)) { \
686 _("--%1$s and --tree are mutually exclusive"), \
690 flags |= VIR_DOMAIN_CHECKPOINT_LIST_ ## flag; \
694 FILTER("leaves", LEAVES
);
695 FILTER("no-leaves", NO_LEAVES
);
698 if (vshCommandOptBool(cmd
, "topological"))
699 flags
|= VIR_DOMAIN_CHECKPOINT_LIST_TOPOLOGICAL
;
702 flags
|= VIR_DOMAIN_CHECKPOINT_LIST_ROOTS
;
704 if (vshCommandOptBool(cmd
, "descendants")) {
707 _("--descendants requires --from"));
710 flags
|= VIR_DOMAIN_CHECKPOINT_LIST_DESCENDANTS
;
713 if (!(dom
= virshCommandOptDomain(ctl
, cmd
, NULL
)))
717 virshLookupCheckpoint(ctl
, cmd
, "from", dom
, &start
, &from_chk
) < 0)
720 if (!(checkpointlist
= virshCheckpointListCollect(ctl
, dom
, start
, flags
,
724 if (!tree
&& !name
) {
726 table
= vshTableNew(_("Name"), _("Creation Time"), _("Parent"),
729 table
= vshTableNew(_("Name"), _("Creation Time"), NULL
);
735 for (i
= 0; i
< checkpointlist
->nchks
; i
++) {
736 if (!checkpointlist
->chks
[i
].parent
&&
737 vshTreePrint(ctl
, virshCheckpointListLookup
, checkpointlist
,
738 checkpointlist
->nchks
, i
) < 0)
745 for (i
= 0; i
< checkpointlist
->nchks
; i
++) {
746 g_autofree gchar
*thenstr
= NULL
;
747 g_autoptr(xmlDoc
) xml
= NULL
;
748 g_autoptr(xmlXPathContext
) ctxt
= NULL
;
749 g_autofree
char *parent_chk
= NULL
;
750 g_autofree
char *doc
= NULL
;
751 const char *chk_name
;
753 checkpoint
= checkpointlist
->chks
[i
].chk
;
754 chk_name
= virDomainCheckpointGetName(checkpoint
);
757 if (!(doc
= virDomainCheckpointGetXMLDesc(checkpoint
, 0)))
760 if (!(xml
= virXMLParseStringCtxt(doc
, _("(domain_checkpoint)"), &ctxt
)))
764 parent_chk
= virXPathString("string(/domaincheckpoint/parent/name)",
768 vshPrint(ctl
, "%s", chk_name
);
771 vshPrint(ctl
, "\t%s", parent_chk
);
775 /* just print the checkpoint name */
779 if (virXPathLongLong("string(/domaincheckpoint/creationTime)", ctxt
,
780 &creation_longlong
) < 0)
783 then
= g_date_time_new_from_unix_local(creation_longlong
);
784 thenstr
= g_date_time_format(then
, "%Y-%m-%d %H:%M:%S %z");
787 if (vshTableRowAppend(table
, chk_name
, thenstr
,
788 NULLSTR_EMPTY(parent_chk
), NULL
) < 0)
791 if (vshTableRowAppend(table
, chk_name
, thenstr
, NULL
) < 0)
797 vshTablePrintToStdout(table
, ctl
);
802 virshCheckpointListFree(checkpointlist
);
808 * "checkpoint-dumpxml" command
810 static const vshCmdInfo info_checkpoint_dumpxml
= {
811 .help
= N_("Dump XML for a domain checkpoint"),
812 .desc
= N_("Checkpoint Dump XML"),
815 static const vshCmdOptDef opts_checkpoint_dumpxml
[] = {
816 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT
),
817 {.name
= "checkpointname",
818 .type
= VSH_OT_STRING
,
821 .help
= N_("checkpoint name"),
822 .completer
= virshCheckpointNameCompleter
,
824 {.name
= "security-info",
826 .help
= N_("include security sensitive information in XML dump")
828 {.name
= "no-domain",
830 .help
= N_("exclude <domain> from XML")
834 .help
= N_("include backup size estimate in XML dump")
837 .type
= VSH_OT_STRING
,
838 .completer
= virshCompleteEmpty
,
839 .help
= N_("xpath expression to filter the XML document")
843 .help
= N_("wrap xpath results in an common root element"),
849 cmdCheckpointDumpXML(vshControl
*ctl
,
852 g_autoptr(virshDomain
) dom
= NULL
;
853 const char *name
= NULL
;
854 g_autoptr(virshDomainCheckpoint
) checkpoint
= NULL
;
855 g_autofree
char *xml
= NULL
;
856 unsigned int flags
= 0;
857 bool wrap
= vshCommandOptBool(cmd
, "wrap");
858 const char *xpath
= NULL
;
860 if (vshCommandOptBool(cmd
, "security-info"))
861 flags
|= VIR_DOMAIN_CHECKPOINT_XML_SECURE
;
862 if (vshCommandOptBool(cmd
, "no-domain"))
863 flags
|= VIR_DOMAIN_CHECKPOINT_XML_NO_DOMAIN
;
864 if (vshCommandOptBool(cmd
, "size"))
865 flags
|= VIR_DOMAIN_CHECKPOINT_XML_SIZE
;
867 if (!(dom
= virshCommandOptDomain(ctl
, cmd
, NULL
)))
870 if (vshCommandOptStringQuiet(ctl
, cmd
, "xpath", &xpath
) < 0)
873 if (virshLookupCheckpoint(ctl
, cmd
, "checkpointname", dom
,
874 &checkpoint
, &name
) < 0)
877 if (!(xml
= virDomainCheckpointGetXMLDesc(checkpoint
, flags
)))
880 return virshDumpXML(ctl
, xml
, "domain-checkpoint", xpath
, wrap
);
885 * "checkpoint-parent" command
887 static const vshCmdInfo info_checkpoint_parent
= {
888 .help
= N_("Get the name of the parent of a checkpoint"),
889 .desc
= N_("Extract the checkpoint's parent, if any"),
892 static const vshCmdOptDef opts_checkpoint_parent
[] = {
893 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT
),
894 {.name
= "checkpointname",
895 .type
= VSH_OT_STRING
,
898 .help
= N_("find parent of checkpoint name"),
899 .completer
= virshCheckpointNameCompleter
,
905 cmdCheckpointParent(vshControl
*ctl
,
908 g_autoptr(virshDomain
) dom
= NULL
;
909 const char *name
= NULL
;
910 g_autoptr(virshDomainCheckpoint
) checkpoint
= NULL
;
911 g_autofree
char *parent
= NULL
;
913 dom
= virshCommandOptDomain(ctl
, cmd
, NULL
);
917 if (virshLookupCheckpoint(ctl
, cmd
, "checkpointname", dom
,
918 &checkpoint
, &name
) < 0)
921 if (virshGetCheckpointParent(ctl
, checkpoint
, &parent
) < 0)
924 vshError(ctl
, _("checkpoint '%1$s' has no parent"), name
);
928 vshPrint(ctl
, "%s", parent
);
935 * "checkpoint-delete" command
937 static const vshCmdInfo info_checkpoint_delete
= {
938 .help
= N_("Delete a domain checkpoint"),
939 .desc
= N_("Checkpoint Delete"),
942 static const vshCmdOptDef opts_checkpoint_delete
[] = {
943 VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT
|
944 VIR_CONNECT_LIST_DOMAINS_ACTIVE
),
945 {.name
= "checkpointname",
946 .type
= VSH_OT_STRING
,
949 .help
= N_("checkpoint name"),
950 .completer
= virshCheckpointNameCompleter
,
954 .help
= N_("delete checkpoint and all children")
956 {.name
= "children-only",
958 .help
= N_("delete children but not checkpoint")
962 .help
= N_("delete only libvirt metadata, leaving checkpoint contents behind")
968 cmdCheckpointDelete(vshControl
*ctl
,
971 g_autoptr(virshDomain
) dom
= NULL
;
972 const char *name
= NULL
;
973 g_autoptr(virshDomainCheckpoint
) checkpoint
= NULL
;
974 unsigned int flags
= 0;
976 dom
= virshCommandOptDomain(ctl
, cmd
, NULL
);
980 if (virshLookupCheckpoint(ctl
, cmd
, "checkpointname", dom
,
981 &checkpoint
, &name
) < 0)
984 if (vshCommandOptBool(cmd
, "children"))
985 flags
|= VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN
;
986 if (vshCommandOptBool(cmd
, "children-only"))
987 flags
|= VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY
;
988 if (vshCommandOptBool(cmd
, "metadata"))
989 flags
|= VIR_DOMAIN_CHECKPOINT_DELETE_METADATA_ONLY
;
991 if (virDomainCheckpointDelete(checkpoint
, flags
) == 0) {
992 if (flags
& VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY
)
993 vshPrintExtra(ctl
, _("Domain checkpoint %1$s children deleted\n"), name
);
995 vshPrintExtra(ctl
, _("Domain checkpoint %1$s deleted\n"), name
);
997 vshError(ctl
, _("Failed to delete checkpoint %1$s"), name
);
1005 const vshCmdDef checkpointCmds
[] = {
1006 {.name
= "checkpoint-create",
1007 .handler
= cmdCheckpointCreate
,
1008 .opts
= opts_checkpoint_create
,
1009 .info
= &info_checkpoint_create
,
1012 {.name
= "checkpoint-create-as",
1013 .handler
= cmdCheckpointCreateAs
,
1014 .opts
= opts_checkpoint_create_as
,
1015 .info
= &info_checkpoint_create_as
,
1018 {.name
= "checkpoint-delete",
1019 .handler
= cmdCheckpointDelete
,
1020 .opts
= opts_checkpoint_delete
,
1021 .info
= &info_checkpoint_delete
,
1024 {.name
= "checkpoint-dumpxml",
1025 .handler
= cmdCheckpointDumpXML
,
1026 .opts
= opts_checkpoint_dumpxml
,
1027 .info
= &info_checkpoint_dumpxml
,
1030 {.name
= "checkpoint-edit",
1031 .handler
= cmdCheckpointEdit
,
1032 .opts
= opts_checkpoint_edit
,
1033 .info
= &info_checkpoint_edit
,
1036 {.name
= "checkpoint-info",
1037 .handler
= cmdCheckpointInfo
,
1038 .opts
= opts_checkpoint_info
,
1039 .info
= &info_checkpoint_info
,
1042 {.name
= "checkpoint-list",
1043 .handler
= cmdCheckpointList
,
1044 .opts
= opts_checkpoint_list
,
1045 .info
= &info_checkpoint_list
,
1048 {.name
= "checkpoint-parent",
1049 .handler
= cmdCheckpointParent
,
1050 .opts
= opts_checkpoint_parent
,
1051 .info
= &info_checkpoint_parent
,