2 * snapshot_conf.c: domain snapshot XML processing
4 * Copyright (C) 2006-2019 Red Hat, Inc.
5 * Copyright (C) 2006-2008 Daniel P. Berrange
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see
19 * <http://www.gnu.org/licenses/>.
28 #include "configmake.h"
30 #include "virbitmap.h"
31 #include "virbuffer.h"
32 #include "count-one-bits.h"
33 #include "datatypes.h"
34 #include "domain_conf.h"
37 #include "netdev_bandwidth_conf.h"
38 #include "netdev_vport_profile_conf.h"
39 #include "nwfilter_conf.h"
40 #include "secret_conf.h"
41 #include "snapshot_conf.h"
42 #include "virstoragefile.h"
47 #include "virstring.h"
48 #include "virdomainsnapshotobjlist.h"
50 #define VIR_FROM_THIS VIR_FROM_DOMAIN_SNAPSHOT
52 VIR_LOG_INIT("conf.snapshot_conf");
54 static virClassPtr virDomainSnapshotDefClass
;
55 static void virDomainSnapshotDefDispose(void *obj
);
58 virDomainSnapshotOnceInit(void)
60 if (!VIR_CLASS_NEW(virDomainSnapshotDef
, virClassForDomainMomentDef()))
66 VIR_ONCE_GLOBAL_INIT(virDomainSnapshot
);
68 VIR_ENUM_IMPL(virDomainSnapshotLocation
,
69 VIR_DOMAIN_SNAPSHOT_LOCATION_LAST
,
76 /* virDomainSnapshotState is really virDomainState plus one extra state */
77 VIR_ENUM_IMPL(virDomainSnapshotState
,
78 VIR_DOMAIN_SNAPSHOT_LAST
,
90 /* Snapshot Def functions */
92 virDomainSnapshotDiskDefClear(virDomainSnapshotDiskDefPtr disk
)
95 virObjectUnref(disk
->src
);
99 /* Allocate a new virDomainSnapshotDef; free with virObjectUnref() */
100 virDomainSnapshotDefPtr
101 virDomainSnapshotDefNew(void)
103 virDomainSnapshotDefPtr def
;
105 if (virDomainSnapshotInitialize() < 0)
108 def
= virObjectNew(virDomainSnapshotDefClass
);
113 virDomainSnapshotDefDispose(void *obj
)
115 virDomainSnapshotDefPtr def
= obj
;
119 for (i
= 0; i
< def
->ndisks
; i
++)
120 virDomainSnapshotDiskDefClear(&def
->disks
[i
]);
121 VIR_FREE(def
->disks
);
122 virObjectUnref(def
->cookie
);
126 virDomainSnapshotDiskDefParseXML(xmlNodePtr node
,
127 xmlXPathContextPtr ctxt
,
128 virDomainSnapshotDiskDefPtr def
,
130 virDomainXMLOptionPtr xmlopt
)
133 char *snapshot
= NULL
;
137 xmlNodePtr saved
= ctxt
->node
;
141 if (!(def
->src
= virStorageSourceNew()))
144 def
->name
= virXMLPropString(node
, "name");
146 virReportError(VIR_ERR_INTERNAL_ERROR
, "%s",
147 _("missing name from disk snapshot element"));
151 snapshot
= virXMLPropString(node
, "snapshot");
153 def
->snapshot
= virDomainSnapshotLocationTypeFromString(snapshot
);
154 if (def
->snapshot
<= 0) {
155 virReportError(VIR_ERR_CONFIG_UNSUPPORTED
,
156 _("unknown disk snapshot setting '%s'"),
162 if ((type
= virXMLPropString(node
, "type"))) {
163 if ((def
->src
->type
= virStorageTypeFromString(type
)) <= 0 ||
164 def
->src
->type
== VIR_STORAGE_TYPE_VOLUME
||
165 def
->src
->type
== VIR_STORAGE_TYPE_DIR
) {
166 virReportError(VIR_ERR_XML_ERROR
,
167 _("unknown disk snapshot type '%s'"), type
);
171 def
->src
->type
= VIR_STORAGE_TYPE_FILE
;
174 if ((cur
= virXPathNode("./source", ctxt
)) &&
175 virDomainStorageSourceParse(cur
, ctxt
, def
->src
, flags
, xmlopt
) < 0)
178 if ((driver
= virXPathString("string(./driver/@type)", ctxt
)) &&
179 (def
->src
->format
= virStorageFileFormatTypeFromString(driver
)) <= 0) {
180 virReportError(VIR_ERR_CONFIG_UNSUPPORTED
,
181 _("unknown disk snapshot driver '%s'"), driver
);
185 /* validate that the passed path is absolute */
186 if (virStorageSourceIsRelative(def
->src
)) {
187 virReportError(VIR_ERR_XML_ERROR
,
188 _("disk snapshot image path '%s' must be absolute"),
193 if (!def
->snapshot
&& (def
->src
->path
|| def
->src
->format
))
194 def
->snapshot
= VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL
;
204 virDomainSnapshotDiskDefClear(def
);
208 /* flags is bitwise-or of virDomainSnapshotParseFlags.
209 * If flags does not include VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE, then
210 * caps are ignored. If flags does not include
211 * VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL, then current is ignored.
213 static virDomainSnapshotDefPtr
214 virDomainSnapshotDefParse(xmlXPathContextPtr ctxt
,
216 virDomainXMLOptionPtr xmlopt
,
220 virDomainSnapshotDefPtr def
= NULL
;
221 virDomainSnapshotDefPtr ret
= NULL
;
222 xmlNodePtr
*nodes
= NULL
;
225 char *creation
= NULL
, *state
= NULL
;
228 char *memorySnapshot
= NULL
;
229 char *memoryFile
= NULL
;
230 bool offline
= !!(flags
& VIR_DOMAIN_SNAPSHOT_PARSE_OFFLINE
);
231 virSaveCookieCallbacksPtr saveCookie
= virDomainXMLOptionGetSaveCookie(xmlopt
);
233 if (!(def
= virDomainSnapshotDefNew()))
236 def
->parent
.name
= virXPathString("string(./name)", ctxt
);
237 if (def
->parent
.name
== NULL
) {
238 if (flags
& VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE
) {
239 virReportError(VIR_ERR_XML_ERROR
, "%s",
240 _("a redefined snapshot must have a name"));
245 def
->parent
.description
= virXPathString("string(./description)", ctxt
);
247 if (flags
& VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE
) {
248 if (virXPathLongLong("string(./creationTime)", ctxt
,
249 &def
->parent
.creationTime
) < 0) {
250 virReportError(VIR_ERR_INTERNAL_ERROR
, "%s",
251 _("missing creationTime from existing snapshot"));
255 def
->parent
.parent_name
= virXPathString("string(./parent/name)", ctxt
);
257 state
= virXPathString("string(./state)", ctxt
);
259 /* there was no state in an existing snapshot; this
260 * should never happen
262 virReportError(VIR_ERR_XML_ERROR
, "%s",
263 _("missing state from existing snapshot"));
266 def
->state
= virDomainSnapshotStateTypeFromString(state
);
267 if (def
->state
<= 0) {
268 virReportError(VIR_ERR_CONFIG_UNSUPPORTED
,
269 _("Invalid state '%s' in domain snapshot XML"),
273 offline
= (def
->state
== VIR_DOMAIN_SNAPSHOT_SHUTOFF
||
274 def
->state
== VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT
);
276 /* Older snapshots were created with just <domain>/<uuid>, and
277 * lack domain/@type. In that case, leave dom NULL, and
278 * clients will have to decide between best effort
279 * initialization or outright failure. */
280 if ((tmp
= virXPathString("string(./domain/@type)", ctxt
))) {
281 int domainflags
= VIR_DOMAIN_DEF_PARSE_INACTIVE
|
282 VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE
;
283 xmlNodePtr domainNode
= virXPathNode("./domain", ctxt
);
287 virReportError(VIR_ERR_INTERNAL_ERROR
, "%s",
288 _("missing domain in snapshot"));
291 def
->parent
.dom
= virDomainDefParseNode(ctxt
->node
->doc
, domainNode
,
292 caps
, xmlopt
, NULL
, domainflags
);
293 if (!def
->parent
.dom
)
296 VIR_WARN("parsing older snapshot that lacks domain");
298 } else if (virDomainXMLOptionRunMomentPostParse(xmlopt
, &def
->parent
) < 0) {
302 memorySnapshot
= virXPathString("string(./memory/@snapshot)", ctxt
);
303 memoryFile
= virXPathString("string(./memory/@file)", ctxt
);
304 if (memorySnapshot
) {
305 def
->memory
= virDomainSnapshotLocationTypeFromString(memorySnapshot
);
306 if (def
->memory
<= 0) {
307 virReportError(VIR_ERR_CONFIG_UNSUPPORTED
,
308 _("unknown memory snapshot setting '%s'"),
313 def
->memory
!= VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL
) {
314 virReportError(VIR_ERR_XML_ERROR
,
315 _("memory filename '%s' requires external snapshot"),
320 def
->memory
== VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL
) {
321 virReportError(VIR_ERR_XML_ERROR
, "%s",
322 _("external memory snapshots require a filename"));
325 } else if (memoryFile
) {
326 def
->memory
= VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL
;
327 } else if (flags
& VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE
) {
328 def
->memory
= (offline
?
329 VIR_DOMAIN_SNAPSHOT_LOCATION_NONE
:
330 VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL
);
332 if (offline
&& def
->memory
&&
333 def
->memory
!= VIR_DOMAIN_SNAPSHOT_LOCATION_NONE
) {
334 virReportError(VIR_ERR_XML_ERROR
, "%s",
335 _("memory state cannot be saved with offline or "
336 "disk-only snapshot"));
339 VIR_STEAL_PTR(def
->file
, memoryFile
);
341 /* verify that memory path is absolute */
342 if (def
->file
&& def
->file
[0] != '/') {
343 virReportError(VIR_ERR_XML_ERROR
,
344 _("memory snapshot file path (%s) must be absolute"),
349 if ((n
= virXPathNodeSet("./disks/*", ctxt
, &nodes
)) < 0)
351 if (flags
& VIR_DOMAIN_SNAPSHOT_PARSE_DISKS
) {
352 if (n
&& VIR_ALLOC_N(def
->disks
, n
) < 0)
355 for (i
= 0; i
< def
->ndisks
; i
++) {
356 if (virDomainSnapshotDiskDefParseXML(nodes
[i
], ctxt
, &def
->disks
[i
],
362 virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED
, "%s",
363 _("unable to handle disk requests in snapshot"));
367 if (flags
& VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL
) {
369 virReportError(VIR_ERR_INTERNAL_ERROR
, "%s",
370 _("internal parse requested with NULL current"));
373 if (virXPathInt("string(./active)", ctxt
, &active
) < 0) {
374 virReportError(VIR_ERR_INTERNAL_ERROR
, "%s",
375 _("Could not find 'active' element"));
378 *current
= active
!= 0;
381 if (!offline
&& virSaveCookieParse(ctxt
, &def
->cookie
, saveCookie
) < 0)
384 VIR_STEAL_PTR(ret
, def
);
390 VIR_FREE(memorySnapshot
);
391 VIR_FREE(memoryFile
);
397 virDomainSnapshotDefPtr
398 virDomainSnapshotDefParseNode(xmlDocPtr xml
,
401 virDomainXMLOptionPtr xmlopt
,
405 xmlXPathContextPtr ctxt
= NULL
;
406 virDomainSnapshotDefPtr def
= NULL
;
408 if (!virXMLNodeNameEqual(root
, "domainsnapshot")) {
409 virReportError(VIR_ERR_XML_ERROR
, "%s", _("domainsnapshot"));
413 if (flags
& VIR_DOMAIN_SNAPSHOT_PARSE_VALIDATE
) {
414 VIR_AUTOFREE(char *) schema
= NULL
;
416 schema
= virFileFindResource("domainsnapshot.rng",
417 abs_top_srcdir
"/docs/schemas",
418 PKGDATADIR
"/schemas");
421 if (virXMLValidateAgainstSchema(schema
, xml
) < 0)
425 ctxt
= xmlXPathNewContext(xml
);
432 def
= virDomainSnapshotDefParse(ctxt
, caps
, xmlopt
, current
, flags
);
434 xmlXPathFreeContext(ctxt
);
438 virDomainSnapshotDefPtr
439 virDomainSnapshotDefParseString(const char *xmlStr
,
441 virDomainXMLOptionPtr xmlopt
,
445 virDomainSnapshotDefPtr ret
= NULL
;
447 int keepBlanksDefault
= xmlKeepBlanksDefault(0);
449 if ((xml
= virXMLParse(NULL
, xmlStr
, _("(domain_snapshot)")))) {
450 xmlKeepBlanksDefault(keepBlanksDefault
);
451 ret
= virDomainSnapshotDefParseNode(xml
, xmlDocGetRootElement(xml
),
452 caps
, xmlopt
, current
, flags
);
455 xmlKeepBlanksDefault(keepBlanksDefault
);
461 /* Perform sanity checking on a redefined snapshot definition. If
462 * @other is non-NULL, this may include swapping def->parent.dom from other
465 virDomainSnapshotRedefineValidate(virDomainSnapshotDefPtr def
,
466 const unsigned char *domain_uuid
,
467 virDomainMomentObjPtr other
,
468 virDomainXMLOptionPtr xmlopt
,
471 int align_location
= VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL
;
472 bool align_match
= true;
473 bool external
= def
->state
== VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT
||
474 virDomainSnapshotDefIsExternal(def
);
476 if ((flags
& VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY
) && !external
) {
477 virReportError(VIR_ERR_INVALID_ARG
,
478 _("disk-only flag for snapshot %s requires "
479 "disk-snapshot state"),
483 if (def
->parent
.dom
&& memcmp(def
->parent
.dom
->uuid
, domain_uuid
,
485 char uuidstr
[VIR_UUID_STRING_BUFLEN
];
487 virUUIDFormat(domain_uuid
, uuidstr
);
488 virReportError(VIR_ERR_INVALID_ARG
,
489 _("definition for snapshot %s must use uuid %s"),
490 def
->parent
.name
, uuidstr
);
495 virDomainSnapshotDefPtr otherdef
= virDomainSnapshotObjGetDef(other
);
497 if ((otherdef
->state
== VIR_DOMAIN_SNAPSHOT_RUNNING
||
498 otherdef
->state
== VIR_DOMAIN_SNAPSHOT_PAUSED
) !=
499 (def
->state
== VIR_DOMAIN_SNAPSHOT_RUNNING
||
500 def
->state
== VIR_DOMAIN_SNAPSHOT_PAUSED
)) {
501 virReportError(VIR_ERR_INVALID_ARG
,
502 _("cannot change between online and offline "
503 "snapshot state in snapshot %s"),
508 if ((otherdef
->state
== VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT
) !=
509 (def
->state
== VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT
)) {
510 virReportError(VIR_ERR_INVALID_ARG
,
511 _("cannot change between disk only and "
512 "full system in snapshot %s"),
517 if (otherdef
->parent
.dom
) {
518 if (def
->parent
.dom
) {
519 if (!virDomainDefCheckABIStability(otherdef
->parent
.dom
,
520 def
->parent
.dom
, xmlopt
))
523 /* Transfer the domain def */
524 VIR_STEAL_PTR(def
->parent
.dom
, otherdef
->parent
.dom
);
529 if (def
->parent
.dom
) {
531 align_location
= VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL
;
534 if (virDomainSnapshotAlignDisks(def
, align_location
,
545 * virDomainSnapshotDefAssignExternalNames:
546 * @def: snapshot def object
548 * Generate default external file names for snapshot targets. Returns 0 on
549 * success, -1 on error.
552 virDomainSnapshotDefAssignExternalNames(virDomainSnapshotDefPtr def
)
554 const char *origpath
;
561 for (i
= 0; i
< def
->ndisks
; i
++) {
562 virDomainSnapshotDiskDefPtr disk
= &def
->disks
[i
];
564 if (disk
->snapshot
!= VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL
||
568 if (disk
->src
->type
!= VIR_STORAGE_TYPE_FILE
) {
569 virReportError(VIR_ERR_CONFIG_UNSUPPORTED
,
570 _("cannot generate external snapshot name "
571 "for disk '%s' on a '%s' device"),
572 disk
->name
, virStorageTypeToString(disk
->src
->type
));
576 if (!(origpath
= virDomainDiskGetSource(def
->parent
.dom
->disks
[i
]))) {
577 virReportError(VIR_ERR_CONFIG_UNSUPPORTED
,
578 _("cannot generate external snapshot name "
579 "for disk '%s' without source"),
584 if (stat(origpath
, &sb
) < 0 || !S_ISREG(sb
.st_mode
)) {
585 virReportError(VIR_ERR_CONFIG_UNSUPPORTED
,
586 _("source for disk '%s' is not a regular "
587 "file; refusing to generate external "
593 if (VIR_STRDUP(tmppath
, origpath
) < 0)
596 /* drop suffix of the file name */
597 if ((tmp
= strrchr(tmppath
, '.')) && !strchr(tmp
, '/'))
600 if (virAsprintf(&disk
->src
->path
, "%s.%s", tmppath
, def
->parent
.name
) < 0) {
607 /* verify that we didn't generate a duplicate name */
608 for (j
= 0; j
< i
; j
++) {
609 if (STREQ_NULLABLE(disk
->src
->path
, def
->disks
[j
].src
->path
)) {
610 virReportError(VIR_ERR_CONFIG_UNSUPPORTED
,
611 _("cannot generate external snapshot name for "
612 "disk '%s': collision with disk '%s'"),
613 disk
->name
, def
->disks
[j
].name
);
624 virDomainSnapshotCompareDiskIndex(const void *a
, const void *b
)
626 const virDomainSnapshotDiskDef
*diska
= a
;
627 const virDomainSnapshotDiskDef
*diskb
= b
;
629 /* Integer overflow shouldn't be a problem here. */
630 return diska
->idx
- diskb
->idx
;
633 /* Align def->disks to def->parent.dom. Sort the list of def->disks,
634 * filling in any missing disks or snapshot state defaults given by
635 * the domain, with a fallback to a passed in default. Convert paths
636 * to disk targets for uniformity. Issue an error and return -1 if
637 * any def->disks[n]->name appears more than once or does not map to
638 * dom->disks. If require_match, also ensure that there is no
639 * conflicting requests for both internal and external snapshots. */
641 virDomainSnapshotAlignDisks(virDomainSnapshotDefPtr def
,
642 int default_snapshot
,
646 virBitmapPtr map
= NULL
;
650 if (!def
->parent
.dom
) {
651 virReportError(VIR_ERR_INTERNAL_ERROR
, "%s",
652 _("missing domain in snapshot"));
656 if (def
->ndisks
> def
->parent
.dom
->ndisks
) {
657 virReportError(VIR_ERR_CONFIG_UNSUPPORTED
, "%s",
658 _("too many disk snapshot requests for domain"));
662 /* Unlikely to have a guest without disks but technically possible. */
663 if (!def
->parent
.dom
->ndisks
) {
668 if (!(map
= virBitmapNew(def
->parent
.dom
->ndisks
)))
671 /* Double check requested disks. */
672 for (i
= 0; i
< def
->ndisks
; i
++) {
673 virDomainSnapshotDiskDefPtr disk
= &def
->disks
[i
];
674 int idx
= virDomainDiskIndexByName(def
->parent
.dom
, disk
->name
, false);
678 virReportError(VIR_ERR_CONFIG_UNSUPPORTED
,
679 _("no disk named '%s'"), disk
->name
);
683 if (virBitmapIsBitSet(map
, idx
)) {
684 virReportError(VIR_ERR_CONFIG_UNSUPPORTED
,
685 _("disk '%s' specified twice"),
689 ignore_value(virBitmapSetBit(map
, idx
));
692 disk_snapshot
= def
->parent
.dom
->disks
[idx
]->snapshot
;
693 if (!disk
->snapshot
) {
696 disk_snapshot
== VIR_DOMAIN_SNAPSHOT_LOCATION_NONE
))
697 disk
->snapshot
= disk_snapshot
;
699 disk
->snapshot
= default_snapshot
;
700 } else if (require_match
&&
701 disk
->snapshot
!= default_snapshot
&&
702 !(disk
->snapshot
== VIR_DOMAIN_SNAPSHOT_LOCATION_NONE
&&
703 disk_snapshot
== VIR_DOMAIN_SNAPSHOT_LOCATION_NONE
)) {
706 tmp
= virDomainSnapshotLocationTypeToString(default_snapshot
);
707 virReportError(VIR_ERR_CONFIG_UNSUPPORTED
,
708 _("disk '%s' must use snapshot mode '%s'"),
712 if (disk
->src
->path
&&
713 disk
->snapshot
!= VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL
) {
714 virReportError(VIR_ERR_CONFIG_UNSUPPORTED
,
715 _("file '%s' for disk '%s' requires "
716 "use of external snapshot mode"),
717 disk
->src
->path
, disk
->name
);
720 if (STRNEQ(disk
->name
, def
->parent
.dom
->disks
[idx
]->dst
)) {
721 VIR_FREE(disk
->name
);
722 if (VIR_STRDUP(disk
->name
, def
->parent
.dom
->disks
[idx
]->dst
) < 0)
727 /* Provide defaults for all remaining disks. */
728 ndisks
= def
->ndisks
;
729 if (VIR_EXPAND_N(def
->disks
, def
->ndisks
,
730 def
->parent
.dom
->ndisks
- def
->ndisks
) < 0)
733 for (i
= 0; i
< def
->parent
.dom
->ndisks
; i
++) {
734 virDomainSnapshotDiskDefPtr disk
;
736 if (virBitmapIsBitSet(map
, i
))
738 disk
= &def
->disks
[ndisks
++];
739 if (!(disk
->src
= virStorageSourceNew()))
741 if (VIR_STRDUP(disk
->name
, def
->parent
.dom
->disks
[i
]->dst
) < 0)
745 /* Don't snapshot empty drives */
746 if (virStorageSourceIsEmpty(def
->parent
.dom
->disks
[i
]->src
))
747 disk
->snapshot
= VIR_DOMAIN_SNAPSHOT_LOCATION_NONE
;
749 disk
->snapshot
= def
->parent
.dom
->disks
[i
]->snapshot
;
751 disk
->src
->type
= VIR_STORAGE_TYPE_FILE
;
753 disk
->snapshot
= default_snapshot
;
756 qsort(&def
->disks
[0], def
->ndisks
, sizeof(def
->disks
[0]),
757 virDomainSnapshotCompareDiskIndex
);
759 /* Generate default external file names for external snapshot locations */
760 if (virDomainSnapshotDefAssignExternalNames(def
) < 0)
771 /* Converts public VIR_DOMAIN_SNAPSHOT_XML_* into
772 * VIR_DOMAIN_SNAPSHOT_FORMAT_* flags, and silently ignores any other
775 virDomainSnapshotFormatConvertXMLFlags(unsigned int flags
)
777 unsigned int formatFlags
= 0;
779 if (flags
& VIR_DOMAIN_SNAPSHOT_XML_SECURE
)
780 formatFlags
|= VIR_DOMAIN_SNAPSHOT_FORMAT_SECURE
;
787 virDomainSnapshotDiskDefFormat(virBufferPtr buf
,
788 virDomainSnapshotDiskDefPtr disk
,
789 virDomainXMLOptionPtr xmlopt
)
791 int type
= disk
->src
->type
;
796 virBufferEscapeString(buf
, "<disk name='%s'", disk
->name
);
797 if (disk
->snapshot
> 0)
798 virBufferAsprintf(buf
, " snapshot='%s'",
799 virDomainSnapshotLocationTypeToString(disk
->snapshot
));
801 if (!disk
->src
->path
&& disk
->src
->format
== 0) {
802 virBufferAddLit(buf
, "/>\n");
806 virBufferAsprintf(buf
, " type='%s'>\n", virStorageTypeToString(type
));
807 virBufferAdjustIndent(buf
, 2);
809 if (disk
->src
->format
> 0)
810 virBufferEscapeString(buf
, "<driver type='%s'/>\n",
811 virStorageFileFormatTypeToString(disk
->src
->format
));
812 if (virDomainDiskSourceFormat(buf
, disk
->src
, "source", 0, false, 0,
816 virBufferAdjustIndent(buf
, -2);
817 virBufferAddLit(buf
, "</disk>\n");
822 /* Append XML describing def into buf. Return 0 on success, or -1 on
823 * failure with buf cleared. */
825 virDomainSnapshotDefFormatInternal(virBufferPtr buf
,
827 virDomainSnapshotDefPtr def
,
829 virDomainXMLOptionPtr xmlopt
,
833 int domainflags
= VIR_DOMAIN_DEF_FORMAT_INACTIVE
;
835 if (flags
& VIR_DOMAIN_SNAPSHOT_FORMAT_SECURE
)
836 domainflags
|= VIR_DOMAIN_DEF_FORMAT_SECURE
;
838 virBufferAddLit(buf
, "<domainsnapshot>\n");
839 virBufferAdjustIndent(buf
, 2);
841 virBufferEscapeString(buf
, "<name>%s</name>\n", def
->parent
.name
);
842 if (def
->parent
.description
)
843 virBufferEscapeString(buf
, "<description>%s</description>\n",
844 def
->parent
.description
);
846 virBufferAsprintf(buf
, "<state>%s</state>\n",
847 virDomainSnapshotStateTypeToString(def
->state
));
849 if (def
->parent
.parent_name
) {
850 virBufferAddLit(buf
, "<parent>\n");
851 virBufferAdjustIndent(buf
, 2);
852 virBufferEscapeString(buf
, "<name>%s</name>\n",
853 def
->parent
.parent_name
);
854 virBufferAdjustIndent(buf
, -2);
855 virBufferAddLit(buf
, "</parent>\n");
858 if (def
->parent
.creationTime
)
859 virBufferAsprintf(buf
, "<creationTime>%lld</creationTime>\n",
860 def
->parent
.creationTime
);
863 virBufferAsprintf(buf
, "<memory snapshot='%s'",
864 virDomainSnapshotLocationTypeToString(def
->memory
));
865 virBufferEscapeString(buf
, " file='%s'", def
->file
);
866 virBufferAddLit(buf
, "/>\n");
870 virBufferAddLit(buf
, "<disks>\n");
871 virBufferAdjustIndent(buf
, 2);
872 for (i
= 0; i
< def
->ndisks
; i
++) {
873 if (virDomainSnapshotDiskDefFormat(buf
, &def
->disks
[i
], xmlopt
) < 0)
876 virBufferAdjustIndent(buf
, -2);
877 virBufferAddLit(buf
, "</disks>\n");
880 if (def
->parent
.dom
) {
881 if (virDomainDefFormatInternal(def
->parent
.dom
, caps
, domainflags
, buf
,
884 } else if (uuidstr
) {
885 virBufferAddLit(buf
, "<domain>\n");
886 virBufferAdjustIndent(buf
, 2);
887 virBufferAsprintf(buf
, "<uuid>%s</uuid>\n", uuidstr
);
888 virBufferAdjustIndent(buf
, -2);
889 virBufferAddLit(buf
, "</domain>\n");
892 if (virSaveCookieFormatBuf(buf
, def
->cookie
,
893 virDomainXMLOptionGetSaveCookie(xmlopt
)) < 0)
896 if (flags
& VIR_DOMAIN_SNAPSHOT_FORMAT_INTERNAL
)
897 virBufferAsprintf(buf
, "<active>%d</active>\n",
898 !!(flags
& VIR_DOMAIN_SNAPSHOT_FORMAT_CURRENT
));
900 virBufferAdjustIndent(buf
, -2);
901 virBufferAddLit(buf
, "</domainsnapshot>\n");
903 if (virBufferCheckError(buf
) < 0)
909 virBufferFreeAndReset(buf
);
915 virDomainSnapshotDefFormat(const char *uuidstr
,
916 virDomainSnapshotDefPtr def
,
918 virDomainXMLOptionPtr xmlopt
,
921 virBuffer buf
= VIR_BUFFER_INITIALIZER
;
923 virCheckFlags(VIR_DOMAIN_SNAPSHOT_FORMAT_SECURE
|
924 VIR_DOMAIN_SNAPSHOT_FORMAT_INTERNAL
|
925 VIR_DOMAIN_SNAPSHOT_FORMAT_CURRENT
, NULL
);
926 if (virDomainSnapshotDefFormatInternal(&buf
, uuidstr
, def
, caps
,
930 return virBufferContentAndReset(&buf
);
935 virDomainSnapshotDefIsExternal(virDomainSnapshotDefPtr def
)
939 if (def
->memory
== VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL
)
942 for (i
= 0; i
< def
->ndisks
; i
++) {
943 if (def
->disks
[i
].snapshot
== VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL
)
951 virDomainSnapshotIsExternal(virDomainMomentObjPtr snap
)
953 virDomainSnapshotDefPtr def
= virDomainSnapshotObjGetDef(snap
);
955 return virDomainSnapshotDefIsExternal(def
);
959 virDomainSnapshotRedefinePrep(virDomainPtr domain
,
961 virDomainSnapshotDefPtr
*defptr
,
962 virDomainMomentObjPtr
*snap
,
963 virDomainXMLOptionPtr xmlopt
,
964 bool *update_current
,
967 virDomainSnapshotDefPtr def
= *defptr
;
968 virDomainMomentObjPtr other
;
969 virDomainSnapshotDefPtr otherdef
= NULL
;
970 bool check_if_stolen
;
972 if (virDomainSnapshotCheckCycles(vm
->snapshots
, def
, vm
->def
->name
) < 0)
975 other
= virDomainSnapshotFindByName(vm
->snapshots
, def
->parent
.name
);
977 otherdef
= virDomainSnapshotObjGetDef(other
);
978 check_if_stolen
= other
&& otherdef
->parent
.dom
;
979 if (virDomainSnapshotRedefineValidate(def
, domain
->uuid
, other
, xmlopt
,
981 /* revert any stealing of the snapshot domain definition */
982 if (check_if_stolen
&& def
->parent
.dom
&& !otherdef
->parent
.dom
)
983 VIR_STEAL_PTR(otherdef
->parent
.dom
, def
->parent
.dom
);
987 if (other
== virDomainSnapshotGetCurrent(vm
->snapshots
)) {
988 *update_current
= true;
989 virDomainSnapshotSetCurrent(vm
->snapshots
, NULL
);
992 /* Drop and rebuild the parent relationship, but keep all
993 * child relations by reusing snap. */
994 virDomainMomentDropParent(other
);
995 virObjectUnref(otherdef
);
996 other
->def
= &(*defptr
)->parent
;