qemu: tpm: do not update profile name for transient domains
[libvirt.git] / tools / virsh-checkpoint.c
bloba0ac0d7f7116e529a3d58bccb19a752cd89e7ea9
1 /*
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/>.
21 #include <config.h>
22 #include "virsh-checkpoint.h"
24 #include <assert.h>
26 #include <libxml/parser.h>
27 #include <libxml/tree.h>
28 #include <libxml/xpath.h>
29 #include <libxml/xmlsave.h>
31 #include "internal.h"
32 #include "virbuffer.h"
33 #include "viralloc.h"
34 #include "virfile.h"
35 #include "virsh-util.h"
36 #include "virxml.h"
37 #include "vsh-table.h"
39 /* Helper for checkpoint-create and checkpoint-create-as */
40 static bool
41 virshCheckpointCreate(vshControl *ctl,
42 virDomainPtr dom,
43 const char *buffer,
44 unsigned int flags,
45 const char *from)
47 g_autoptr(virshDomainCheckpoint) checkpoint = NULL;
48 const char *name = NULL;
50 checkpoint = virDomainCheckpointCreateXML(dom, buffer, flags);
52 if (checkpoint == NULL)
53 return false;
55 name = virDomainCheckpointGetName(checkpoint);
56 if (!name) {
57 vshError(ctl, "%s", _("Could not get checkpoint name"));
58 return false;
61 if (from)
62 vshPrintExtra(ctl, _("Domain checkpoint %1$s created from '%2$s'"),
63 name, from);
64 else
65 vshPrintExtra(ctl, _("Domain checkpoint %1$s created"), name);
67 return true;
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),
82 {.name = "xmlfile",
83 .type = VSH_OT_STRING,
84 .positional = true,
85 .completer = virshCompletePathLocalExisting,
86 .help = N_("domain checkpoint XML")
88 {.name = "redefine",
89 .type = VSH_OT_BOOL,
90 .help = N_("redefine metadata for existing checkpoint")
92 {.name = "redefine-validate",
93 .type = VSH_OT_BOOL,
94 .help = N_("validate the redefined checkpoint")
96 {.name = "quiesce",
97 .type = VSH_OT_BOOL,
98 .help = N_("quiesce guest's file systems")
100 {.name = NULL}
103 static bool
104 cmdCheckpointCreate(vshControl *ctl,
105 const vshCmd *cmd)
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)))
122 return false;
124 if (vshCommandOptString(ctl, cmd, "xmlfile", &from) < 0)
125 return false;
126 if (!from) {
127 buffer = g_strdup("<domaincheckpoint/>");
128 } else {
129 if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) {
130 vshSaveLibvirtError();
131 return false;
135 return virshCheckpointCreate(ctl, dom, buffer, flags, from);
140 * "checkpoint-create-as" command
142 static int
143 virshParseCheckpointDiskspec(vshControl *ctl,
144 virBuffer *buf,
145 const char *str)
147 int ret = -1;
148 const char *name = NULL;
149 const char *checkpoint = NULL;
150 const char *bitmap = NULL;
151 g_auto(GStrv) array = NULL;
152 int narray;
153 size_t i;
155 narray = vshStringToArray(str, &array);
156 if (narray <= 0)
157 goto cleanup;
159 name = array[0];
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=");
165 else
166 goto cleanup;
169 virBufferEscapeString(buf, "<disk name='%s'", name);
170 if (checkpoint)
171 virBufferAsprintf(buf, " checkpoint='%s'", checkpoint);
172 if (bitmap)
173 virBufferAsprintf(buf, " bitmap='%s'", bitmap);
174 virBufferAddLit(buf, "/>\n");
175 ret = 0;
176 cleanup:
177 if (ret < 0)
178 vshError(ctl, _("unable to parse diskspec: %1$s"), str);
179 return ret;
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),
190 {.name = "name",
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",
203 .type = VSH_OT_BOOL,
204 .help = N_("print XML document rather than create")
206 {.name = "quiesce",
207 .type = VSH_OT_BOOL,
208 .help = N_("quiesce guest's file systems")
210 {.name = "diskspec",
211 .type = VSH_OT_ARGV,
212 .unwanted_positional = true,
213 .help = N_("disk attributes: disk[,checkpoint=type][,bitmap=name]")
215 {.name = NULL}
219 static bool
220 cmdCheckpointCreateAs(vshControl *ctl,
221 const vshCmd *cmd)
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)))
235 return false;
237 if (vshCommandOptString(ctl, cmd, "name", &name) < 0 ||
238 vshCommandOptString(ctl, cmd, "description", &desc) < 0)
239 return false;
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)
252 return false;
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);
265 return true;
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
275 * message. */
276 static int
277 virshLookupCheckpoint(vshControl *ctl,
278 const vshCmd *cmd,
279 const char *arg,
280 virDomainPtr dom,
281 virDomainCheckpointPtr *chk,
282 const char **name)
284 const char *chkname = NULL;
286 if (vshCommandOptString(ctl, cmd, arg, &chkname) < 0)
287 return -1;
289 if (!(*chk = virDomainCheckpointLookupByName(dom, chkname, 0)))
290 return -1;
292 *name = virDomainCheckpointGetName(*chk);
293 return 0;
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,
309 .positional = true,
310 .required = true,
311 .help = N_("checkpoint name"),
312 .completer = virshCheckpointNameCompleter,
314 {.name = NULL}
317 static bool
318 cmdCheckpointEdit(vshControl *ctl,
319 const vshCmd *cmd)
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;
326 bool ret = false;
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)))
331 return false;
333 if (virshLookupCheckpoint(ctl, cmd, "checkpointname", dom,
334 &checkpoint, &name) < 0)
335 goto cleanup;
337 #define EDIT_GET_XML \
338 virDomainCheckpointGetXMLDesc(checkpoint, getxml_flags)
339 #define EDIT_NOT_CHANGED \
340 do { \
341 vshPrintExtra(ctl, \
342 _("Checkpoint %1$s XML configuration not changed.\n"), \
343 name); \
344 ret = true; \
345 goto edit_cleanup; \
346 } while (0)
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);
354 } else {
355 unsigned int delete_flags = VIR_DOMAIN_CHECKPOINT_DELETE_METADATA_ONLY;
357 if (virDomainCheckpointDelete(edited, delete_flags) < 0) {
358 vshReportError(ctl);
359 vshError(ctl, _("Failed to clean up %1$s"), edited_name);
360 goto cleanup;
362 vshError(ctl, _("Cannot rename checkpoint %1$s to %2$s"),
363 name, edited_name);
364 goto cleanup;
367 ret = true;
369 cleanup:
370 if (!ret && name)
371 vshError(ctl, _("Failed to update %1$s"), name);
372 return ret;
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). */
380 static int
381 virshGetCheckpointParent(vshControl *ctl,
382 virDomainCheckpointPtr checkpoint,
383 char **parent_name)
385 g_autoptr(virshDomainCheckpoint) parent = NULL;
386 int ret = -1;
388 *parent_name = NULL;
390 parent = virDomainCheckpointGetParent(checkpoint, 0);
391 if (parent) {
392 /* API works, and virDomainCheckpointGetName will succeed */
393 *parent_name = g_strdup(virDomainCheckpointGetName(parent));
394 ret = 0;
395 } else if (last_error->code == VIR_ERR_NO_DOMAIN_CHECKPOINT) {
396 /* API works, and we found a root with no parent */
397 ret = 0;
400 if (ret < 0) {
401 vshReportError(ctl);
402 vshError(ctl, "%s", _("unable to determine if checkpoint has parent"));
403 } else {
404 vshResetLibvirtError();
406 return ret;
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,
422 .positional = true,
423 .required = true,
424 .help = N_("checkpoint name"),
425 .completer = virshCheckpointNameCompleter,
427 {.name = NULL}
431 static bool
432 cmdCheckpointInfo(vshControl *ctl,
433 const vshCmd *cmd)
435 g_autoptr(virshDomain) dom = NULL;
436 g_autoptr(virshDomainCheckpoint) checkpoint = NULL;
437 const char *name;
438 g_autofree char *parent = NULL;
439 int count;
440 unsigned int flags;
442 dom = virshCommandOptDomain(ctl, cmd, NULL);
443 if (dom == NULL)
444 return false;
446 if (virshLookupCheckpoint(ctl, cmd, "checkpointname", dom,
447 &checkpoint, &name) < 0)
448 return false;
450 vshPrint(ctl, "%-15s %s\n", _("Name:"), name);
451 vshPrint(ctl, "%-15s %s\n", _("Domain:"), virDomainGetName(dom));
453 if (virshGetCheckpointParent(ctl, checkpoint, &parent) < 0) {
454 vshError(ctl, "%s",
455 _("unexpected problem querying checkpoint state"));
456 return false;
458 vshPrint(ctl, "%-15s %s\n", _("Parent:"), NULLSTR_MINUS(parent));
460 /* Children, Descendants. */
461 flags = 0;
462 count = virDomainCheckpointListAllChildren(checkpoint, NULL, flags);
463 if (count < 0) {
464 if (last_error->code == VIR_ERR_NO_SUPPORT) {
465 vshResetLibvirtError();
466 return true;
468 return false;
470 vshPrint(ctl, "%-15s %d\n", _("Children:"), count);
471 flags = VIR_DOMAIN_CHECKPOINT_LIST_DESCENDANTS;
472 count = virDomainCheckpointListAllChildren(checkpoint, NULL, flags);
473 if (count < 0)
474 return false;
475 vshPrint(ctl, "%-15s %d\n", _("Descendants:"), count);
477 return true;
481 /* Helpers for collecting a list of checkpoints. */
482 struct virshChk {
483 virDomainCheckpointPtr chk;
484 char *parent;
486 struct virshCheckpointList {
487 struct virshChk *chks;
488 int nchks;
491 static void
492 virshCheckpointListFree(struct virshCheckpointList *checkpointlist)
494 size_t i;
496 if (!checkpointlist)
497 return;
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);
509 static int
510 virshChkSorter(const void *a,
511 const void *b,
512 void *opaque G_GNUC_UNUSED)
514 const struct virshChk *sa = a;
515 const struct virshChk *sb = b;
517 if (sa->chk && !sb->chk)
518 return -1;
519 if (!sa->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,
533 virDomainPtr dom,
534 virDomainCheckpointPtr from,
535 unsigned int orig_flags,
536 bool tree)
538 size_t i;
539 int count = -1;
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);
547 if (from)
548 count = virDomainCheckpointListAllChildren(from, &chks, flags);
549 else
550 count = virDomainListAllCheckpoints(dom, &chks, flags);
551 if (count < 0) {
552 vshError(ctl, "%s",
553 _("unexpected problem querying checkpoints"));
554 goto cleanup;
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. */
559 if (from && tree)
560 checkpointlist->chks = g_new0(struct virshChk, count + 1);
561 else
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];
566 VIR_FREE(chks);
567 if (tree) {
568 for (i = 0; i < count; i++) {
569 if (virshGetCheckpointParent(ctl, checkpointlist->chks[i].chk,
570 &checkpointlist->chks[i].parent) < 0)
571 goto cleanup;
573 if (from) {
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);
587 cleanup:
588 virshCheckpointListFree(checkpointlist);
589 return ret;
593 static const char *
594 virshCheckpointListLookup(int id,
595 bool parent,
596 void *opaque)
598 struct virshCheckpointList *checkpointlist = opaque;
599 if (parent)
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),
615 {.name = "parent",
616 .type = VSH_OT_BOOL,
617 .help = N_("add a column showing parent checkpoint")
619 {.name = "roots",
620 .type = VSH_OT_BOOL,
621 .help = N_("list only checkpoints without parents")
623 {.name = "leaves",
624 .type = VSH_OT_BOOL,
625 .help = N_("list only checkpoints without children")
627 {.name = "no-leaves",
628 .type = VSH_OT_BOOL,
629 .help = N_("list only checkpoints that are not leaves (with children)")
631 {.name = "tree",
632 .type = VSH_OT_BOOL,
633 .help = N_("list checkpoints in a tree")
635 {.name = "from",
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",
642 .type = VSH_OT_BOOL,
643 .help = N_("with --from, list all descendants")
645 {.name = "name",
646 .type = VSH_OT_BOOL,
647 .help = N_("list checkpoint names only")
649 {.name = "topological",
650 .type = VSH_OT_BOOL,
651 .help = N_("sort list topologically rather than by name"),
653 {.name = NULL}
656 static bool
657 cmdCheckpointList(vshControl *ctl,
658 const vshCmd *cmd)
660 g_autoptr(virshDomain) dom = NULL;
661 bool ret = false;
662 unsigned int flags = 0;
663 size_t i;
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) \
684 do { \
685 if (vshCommandOptBool(cmd, option)) { \
686 if (tree) { \
687 vshError(ctl, \
688 _("--%1$s and --tree are mutually exclusive"), \
689 option); \
690 return false; \
692 flags |= VIR_DOMAIN_CHECKPOINT_LIST_ ## flag; \
694 } while (0)
696 FILTER("leaves", LEAVES);
697 FILTER("no-leaves", NO_LEAVES);
698 #undef FILTER
700 if (vshCommandOptBool(cmd, "topological"))
701 flags |= VIR_DOMAIN_CHECKPOINT_LIST_TOPOLOGICAL;
703 if (roots)
704 flags |= VIR_DOMAIN_CHECKPOINT_LIST_ROOTS;
706 if (vshCommandOptBool(cmd, "descendants")) {
707 if (!from) {
708 vshError(ctl, "%s",
709 _("--descendants requires --from"));
710 return false;
712 flags |= VIR_DOMAIN_CHECKPOINT_LIST_DESCENDANTS;
715 if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
716 return false;
718 if (from &&
719 virshLookupCheckpoint(ctl, cmd, "from", dom, &start, &from_chk) < 0)
720 goto cleanup;
722 if (!(checkpointlist = virshCheckpointListCollect(ctl, dom, start, flags,
723 tree)))
724 goto cleanup;
726 if (!tree && !name) {
727 if (parent)
728 table = vshTableNew(_("Name"), _("Creation Time"), _("Parent"),
729 NULL);
730 else
731 table = vshTableNew(_("Name"), _("Creation Time"), NULL);
732 if (!table)
733 goto cleanup;
736 if (tree) {
737 for (i = 0; i < checkpointlist->nchks; i++) {
738 if (!checkpointlist->chks[i].parent &&
739 vshTreePrint(ctl, virshCheckpointListLookup, checkpointlist,
740 checkpointlist->nchks, i) < 0)
741 goto cleanup;
743 ret = true;
744 goto cleanup;
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);
757 assert(chk_name);
759 if (!(doc = virDomainCheckpointGetXMLDesc(checkpoint, 0)))
760 continue;
762 if (!(xml = virXMLParseStringCtxt(doc, _("(domain_checkpoint)"), &ctxt)))
763 continue;
765 if (parent)
766 parent_chk = virXPathString("string(/domaincheckpoint/parent/name)",
767 ctxt);
769 if (name) {
770 vshPrint(ctl, "%s", chk_name);
772 if (parent_chk)
773 vshPrint(ctl, "\t%s", parent_chk);
775 vshPrint(ctl, "\n");
777 /* just print the checkpoint name */
778 continue;
781 if (virXPathLongLong("string(/domaincheckpoint/creationTime)", ctxt,
782 &creation_longlong) < 0)
783 continue;
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");
788 if (parent) {
789 if (vshTableRowAppend(table, chk_name, thenstr,
790 NULLSTR_EMPTY(parent_chk), NULL) < 0)
791 goto cleanup;
792 } else {
793 if (vshTableRowAppend(table, chk_name, thenstr, NULL) < 0)
794 goto cleanup;
798 if (table)
799 vshTablePrintToStdout(table, ctl);
801 ret = true;
803 cleanup:
804 virshCheckpointListFree(checkpointlist);
805 return ret;
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,
821 .positional = true,
822 .required = true,
823 .help = N_("checkpoint name"),
824 .completer = virshCheckpointNameCompleter,
826 {.name = "security-info",
827 .type = VSH_OT_BOOL,
828 .help = N_("include security sensitive information in XML dump")
830 {.name = "no-domain",
831 .type = VSH_OT_BOOL,
832 .help = N_("exclude <domain> from XML")
834 {.name = "size",
835 .type = VSH_OT_BOOL,
836 .help = N_("include backup size estimate in XML dump")
838 {.name = "xpath",
839 .type = VSH_OT_STRING,
840 .completer = virshCompleteEmpty,
841 .help = N_("xpath expression to filter the XML document")
843 {.name = "wrap",
844 .type = VSH_OT_BOOL,
845 .help = N_("wrap xpath results in an common root element"),
847 {.name = NULL}
850 static bool
851 cmdCheckpointDumpXML(vshControl *ctl,
852 const vshCmd *cmd)
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)))
870 return false;
872 if (vshCommandOptStringQuiet(ctl, cmd, "xpath", &xpath) < 0)
873 return false;
875 if (virshLookupCheckpoint(ctl, cmd, "checkpointname", dom,
876 &checkpoint, &name) < 0)
877 return false;
879 if (!(xml = virDomainCheckpointGetXMLDesc(checkpoint, flags)))
880 return false;
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,
898 .positional = true,
899 .required = true,
900 .help = N_("find parent of checkpoint name"),
901 .completer = virshCheckpointNameCompleter,
903 {.name = NULL}
906 static bool
907 cmdCheckpointParent(vshControl *ctl,
908 const vshCmd *cmd)
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);
916 if (dom == NULL)
917 return false;
919 if (virshLookupCheckpoint(ctl, cmd, "checkpointname", dom,
920 &checkpoint, &name) < 0)
921 return false;
923 if (virshGetCheckpointParent(ctl, checkpoint, &parent) < 0)
924 return false;
925 if (!parent) {
926 vshError(ctl, _("checkpoint '%1$s' has no parent"), name);
927 return false;
930 vshPrint(ctl, "%s", parent);
932 return true;
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,
949 .positional = true,
950 .required = true,
951 .help = N_("checkpoint name"),
952 .completer = virshCheckpointNameCompleter,
954 {.name = "children",
955 .type = VSH_OT_BOOL,
956 .help = N_("delete checkpoint and all children")
958 {.name = "children-only",
959 .type = VSH_OT_BOOL,
960 .help = N_("delete children but not checkpoint")
962 {.name = "metadata",
963 .type = VSH_OT_BOOL,
964 .help = N_("delete only libvirt metadata, leaving checkpoint contents behind")
966 {.name = NULL}
969 static bool
970 cmdCheckpointDelete(vshControl *ctl,
971 const vshCmd *cmd)
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);
979 if (dom == NULL)
980 return false;
982 if (virshLookupCheckpoint(ctl, cmd, "checkpointname", dom,
983 &checkpoint, &name) < 0)
984 return false;
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);
996 else
997 vshPrintExtra(ctl, _("Domain checkpoint %1$s deleted\n"), name);
998 } else {
999 vshError(ctl, _("Failed to delete checkpoint %1$s"), name);
1000 return false;
1003 return true;
1007 const vshCmdDef checkpointCmds[] = {
1008 {.name = "checkpoint-create",
1009 .handler = cmdCheckpointCreate,
1010 .opts = opts_checkpoint_create,
1011 .info = &info_checkpoint_create,
1012 .flags = 0
1014 {.name = "checkpoint-create-as",
1015 .handler = cmdCheckpointCreateAs,
1016 .opts = opts_checkpoint_create_as,
1017 .info = &info_checkpoint_create_as,
1018 .flags = 0
1020 {.name = "checkpoint-delete",
1021 .handler = cmdCheckpointDelete,
1022 .opts = opts_checkpoint_delete,
1023 .info = &info_checkpoint_delete,
1024 .flags = 0
1026 {.name = "checkpoint-dumpxml",
1027 .handler = cmdCheckpointDumpXML,
1028 .opts = opts_checkpoint_dumpxml,
1029 .info = &info_checkpoint_dumpxml,
1030 .flags = 0
1032 {.name = "checkpoint-edit",
1033 .handler = cmdCheckpointEdit,
1034 .opts = opts_checkpoint_edit,
1035 .info = &info_checkpoint_edit,
1036 .flags = 0
1038 {.name = "checkpoint-info",
1039 .handler = cmdCheckpointInfo,
1040 .opts = opts_checkpoint_info,
1041 .info = &info_checkpoint_info,
1042 .flags = 0
1044 {.name = "checkpoint-list",
1045 .handler = cmdCheckpointList,
1046 .opts = opts_checkpoint_list,
1047 .info = &info_checkpoint_list,
1048 .flags = 0
1050 {.name = "checkpoint-parent",
1051 .handler = cmdCheckpointParent,
1052 .opts = opts_checkpoint_parent,
1053 .info = &info_checkpoint_parent,
1054 .flags = 0
1056 {.name = NULL}