1 /*
2 * backup_conf.c: domain backup XML processing
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see
16 * <http://www.gnu.org/licenses/>.
17 */
18
19 #include <config.h>
20
21 #include "configmake.h"
22 #include "internal.h"
23 #include "virbuffer.h"
24 #include "datatypes.h"
25 #include "domain_conf.h"
26 #include "virlog.h"
27 #include "viralloc.h"
28 #include "backup_conf.h"
29 #include "storage_source_conf.h"
30 #include "virfile.h"
31 #include "virerror.h"
32 #include "virxml.h"
33 #include "virstring.h"
34 #include "virhash.h"
35 #include "virenum.h"
36
37 #define VIR_FROM_THIS VIR_FROM_DOMAIN
38
39 VIR_LOG_INIT("conf.backup_conf");
40
41 VIR_ENUM_DECL(virDomainBackup);
42 VIR_ENUM_IMPL(virDomainBackup,
43 VIR_DOMAIN_BACKUP_TYPE_LAST,
44 "default",
45 "push",
46 "pull");
47
48 /* following values appear in the status XML */
49 VIR_ENUM_DECL(virDomainBackupDiskState);
50 VIR_ENUM_IMPL(virDomainBackupDiskState,
51 VIR_DOMAIN_BACKUP_DISK_STATE_LAST,
52 "",
53 "running",
54 "complete",
55 "failed",
56 "cancelling",
57 "cancelled");
58
59 VIR_ENUM_DECL(virDomainBackupDiskBackupMode);
60 VIR_ENUM_IMPL(virDomainBackupDiskBackupMode,
61 VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_LAST,
62 "",
63 "full",
64 "incremental");
65
66 void
virDomainBackupDefFree(virDomainBackupDef * def)67 virDomainBackupDefFree(virDomainBackupDef *def)
68 {
69 size_t i;
70
71 if (!def)
72 return;
73
74 g_free(def->incremental);
75 g_free(def->errmsg);
76 virStorageNetHostDefFree(1, def->server);
77
78 for (i = 0; i < def->ndisks; i++) {
79 virDomainBackupDiskDef *disk = def->disks + i;
80
81 g_free(disk->name);
82 g_free(disk->incremental);
83 g_free(disk->exportname);
84 g_free(disk->exportbitmap);
85 virObjectUnref(disk->store);
86 }
87
88 g_free(def->disks);
89
90 g_free(def->tlsAlias);
91 g_free(def->tlsSecretAlias);
92
93 g_free(def);
94 }
95
96
97 static int
virDomainBackupDiskDefParseXML(xmlNodePtr node,xmlXPathContextPtr ctxt,virDomainBackupDiskDef * def,bool push,unsigned int flags,virDomainXMLOption * xmlopt)98 virDomainBackupDiskDefParseXML(xmlNodePtr node,
99 xmlXPathContextPtr ctxt,
100 virDomainBackupDiskDef *def,
101 bool push,
102 unsigned int flags,
103 virDomainXMLOption *xmlopt)
104 {
105 VIR_XPATH_NODE_AUTORESTORE(ctxt)
106 g_autofree char *type = NULL;
107 g_autofree char *format = NULL;
108 g_autofree char *idx = NULL;
109 xmlNodePtr srcNode;
110 unsigned int storageSourceParseFlags = 0;
111 bool internal = flags & VIR_DOMAIN_BACKUP_PARSE_INTERNAL;
112
113 if (internal)
114 storageSourceParseFlags = VIR_DOMAIN_DEF_PARSE_STATUS;
115
116 ctxt->node = node;
117
118 if (!(def->name = virXMLPropString(node, "name"))) {
119 virReportError(VIR_ERR_XML_ERROR, "%s",
120 _("missing name from disk backup element"));
121 return -1;
122 }
123
124 if (virXMLPropTristateBool(node, "backup", VIR_XML_PROP_NONE,
125 &def->backup) < 0)
126 return -1;
127
128 if (def->backup == VIR_TRISTATE_BOOL_ABSENT)
129 def->backup = VIR_TRISTATE_BOOL_YES;
130
131 /* don't parse anything else if backup is disabled */
132 if (def->backup == VIR_TRISTATE_BOOL_NO)
133 return 0;
134
135 if (!push) {
136 def->exportname = virXMLPropString(node, "exportname");
137 def->exportbitmap = virXMLPropString(node, "exportbitmap");
138 }
139
140 if (virXMLPropEnum(node, "backupmode",
141 virDomainBackupDiskBackupModeTypeFromString,
142 VIR_XML_PROP_NONE, &def->backupmode) < 0)
143 return -1;
144
145 def->incremental = virXMLPropString(node, "incremental");
146
147 if (internal) {
148 if (virXMLPropEnum(node, "state",
149 virDomainBackupDiskStateTypeFromString,
150 VIR_XML_PROP_REQUIRED, &def->state) < 0)
151 return -1;
152 }
153
154 type = virXMLPropString(node, "type");
155 format = virXPathString("string(./driver/@type)", ctxt);
156 if (internal)
157 idx = virXMLPropString(node, "index");
158
159 if (!(def->store = virDomainStorageSourceParseBase(type, format, idx)))
160 return -1;
161
162 if (def->store->type != VIR_STORAGE_TYPE_FILE &&
163 def->store->type != VIR_STORAGE_TYPE_BLOCK) {
164 virReportError(VIR_ERR_XML_ERROR,
165 _("unsupported disk backup type '%s'"), type);
166 return -1;
167 }
168
169 if (push)
170 srcNode = virXPathNode("./target", ctxt);
171 else
172 srcNode = virXPathNode("./scratch", ctxt);
173
174 if (srcNode &&
175 virDomainStorageSourceParse(srcNode, ctxt, def->store,
176 storageSourceParseFlags, xmlopt) < 0)
177 return -1;
178
179 return 0;
180 }
181
182
183 static void
virDomainBackupDefParsePrivate(virDomainBackupDef * def,xmlXPathContextPtr ctxt,unsigned int flags)184 virDomainBackupDefParsePrivate(virDomainBackupDef *def,
185 xmlXPathContextPtr ctxt,
186 unsigned int flags)
187 {
188 if (!(flags & VIR_DOMAIN_BACKUP_PARSE_INTERNAL))
189 return;
190
191 def->tlsSecretAlias = virXPathString("string(./privateData/objects/secret[@type='tlskey']/@alias)", ctxt);
192 def->tlsAlias = virXPathString("string(./privateData/objects/TLSx509/@alias)", ctxt);
193 }
194
195
196 static virDomainBackupDef *
virDomainBackupDefParse(xmlXPathContextPtr ctxt,virDomainXMLOption * xmlopt,unsigned int flags)197 virDomainBackupDefParse(xmlXPathContextPtr ctxt,
198 virDomainXMLOption *xmlopt,
199 unsigned int flags)
200 {
201 g_autoptr(virDomainBackupDef) def = NULL;
202 g_autofree xmlNodePtr *nodes = NULL;
203 xmlNodePtr node = NULL;
204 g_autofree char *mode = NULL;
205 bool push;
206 size_t i;
207 int n;
208
209 def = g_new0(virDomainBackupDef, 1);
210
211 def->type = VIR_DOMAIN_BACKUP_TYPE_PUSH;
212
213 if ((mode = virXMLPropString(ctxt->node, "mode"))) {
214 if ((def->type = virDomainBackupTypeFromString(mode)) <= 0) {
215 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
216 _("unknown backup mode '%s'"), mode);
217 return NULL;
218 }
219 }
220
221 push = def->type == VIR_DOMAIN_BACKUP_TYPE_PUSH;
222
223 def->incremental = virXPathString("string(./incremental)", ctxt);
224
225 if ((node = virXPathNode("./server", ctxt))) {
226 if (def->type != VIR_DOMAIN_BACKUP_TYPE_PULL) {
227 virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
228 _("use of <server> requires pull mode backup"));
229 return NULL;
230 }
231
232 def->server = g_new0(virStorageNetHostDef, 1);
233
234 if (virDomainStorageNetworkParseHost(node, def->server) < 0)
235 return NULL;
236
237 if (def->server->transport == VIR_STORAGE_NET_HOST_TRANS_RDMA) {
238 virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
239 _("transport rdma is not supported for <server>"));
240 return NULL;
241 }
242
243 if (def->server->transport == VIR_STORAGE_NET_HOST_TRANS_UNIX &&
244 !g_path_is_absolute(def->server->socket)) {
245 virReportError(VIR_ERR_XML_ERROR,
246 _("backup socket path '%s' must be absolute"),
247 def->server->socket);
248 return NULL;
249 }
250
251 if (virXMLPropTristateBool(node, "tls", VIR_XML_PROP_NONE,
252 &def->tls) < 0)
253 return NULL;
254 }
255
256 if ((n = virXPathNodeSet("./disks/*", ctxt, &nodes)) < 0)
257 return NULL;
258
259 def->disks = g_new0(virDomainBackupDiskDef, n);
260
261 def->ndisks = n;
262 for (i = 0; i < def->ndisks; i++) {
263 if (virDomainBackupDiskDefParseXML(nodes[i], ctxt,
264 &def->disks[i], push,
265 flags, xmlopt) < 0)
266 return NULL;
267 }
268
269 virDomainBackupDefParsePrivate(def, ctxt, flags);
270
271 return g_steal_pointer(&def);
272 }
273
274
275 virDomainBackupDef *
virDomainBackupDefParseString(const char * xmlStr,virDomainXMLOption * xmlopt,unsigned int flags)276 virDomainBackupDefParseString(const char *xmlStr,
277 virDomainXMLOption *xmlopt,
278 unsigned int flags)
279 {
280 virDomainBackupDef *ret = NULL;
281 g_autoptr(xmlDoc) xml = NULL;
282 int keepBlanksDefault = xmlKeepBlanksDefault(0);
283
284 if ((xml = virXMLParse(NULL, xmlStr, _("(domain_backup)"), "domainbackup.rng",
285 !(flags & VIR_DOMAIN_BACKUP_PARSE_INTERNAL)))) {
286 xmlKeepBlanksDefault(keepBlanksDefault);
287 ret = virDomainBackupDefParseNode(xml, xmlDocGetRootElement(xml),
288 xmlopt, flags);
289 }
290 xmlKeepBlanksDefault(keepBlanksDefault);
291
292 return ret;
293 }
294
295
296 virDomainBackupDef *
virDomainBackupDefParseNode(xmlDocPtr xml,xmlNodePtr root,virDomainXMLOption * xmlopt,unsigned int flags)297 virDomainBackupDefParseNode(xmlDocPtr xml,
298 xmlNodePtr root,
299 virDomainXMLOption *xmlopt,
300 unsigned int flags)
301 {
302 g_autoptr(xmlXPathContext) ctxt = NULL;
303
304 if (!virXMLNodeNameEqual(root, "domainbackup")) {
305 virReportError(VIR_ERR_XML_ERROR, "%s", _("domainbackup"));
306 return NULL;
307 }
308
309 if (!(ctxt = virXMLXPathContextNew(xml)))
310 return NULL;
311
312 ctxt->node = root;
313 return virDomainBackupDefParse(ctxt, xmlopt, flags);
314 }
315
316
317 static int
virDomainBackupDiskDefFormat(virBuffer * buf,virDomainBackupDiskDef * disk,bool push,bool internal,virDomainXMLOption * xmlopt)318 virDomainBackupDiskDefFormat(virBuffer *buf,
319 virDomainBackupDiskDef *disk,
320 bool push,
321 bool internal,
322 virDomainXMLOption *xmlopt)
323 {
324 g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
325 g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
326 const char *sourcename = "scratch";
327 unsigned int storageSourceFormatFlags = 0;
328
329 if (push)
330 sourcename = "target";
331
332 if (internal)
333 storageSourceFormatFlags |= VIR_DOMAIN_DEF_FORMAT_STATUS;
334
335 virBufferEscapeString(&attrBuf, " name='%s'", disk->name);
336 virBufferAsprintf(&attrBuf, " backup='%s'", virTristateBoolTypeToString(disk->backup));
337 if (internal && disk->state != VIR_DOMAIN_BACKUP_DISK_STATE_NONE)
338 virBufferAsprintf(&attrBuf, " state='%s'", virDomainBackupDiskStateTypeToString(disk->state));
339
340 if (disk->backup == VIR_TRISTATE_BOOL_YES) {
341 virBufferAsprintf(&attrBuf, " type='%s'", virStorageTypeToString(disk->store->type));
342
343 if (disk->backupmode != VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_DEFAULT) {
344 virBufferAsprintf(&attrBuf, " backupmode='%s'",
345 virDomainBackupDiskBackupModeTypeToString(disk->backupmode));
346 }
347
348 virBufferEscapeString(&attrBuf, " incremental='%s'", disk->incremental);
349
350 virBufferEscapeString(&attrBuf, " exportname='%s'", disk->exportname);
351 virBufferEscapeString(&attrBuf, " exportbitmap='%s'", disk->exportbitmap);
352
353 if (disk->store->id != 0)
354 virBufferAsprintf(&attrBuf, " index='%u'", disk->store->id);
355
356 if (disk->store->format > 0)
357 virBufferEscapeString(&childBuf, "<driver type='%s'/>\n",
358 virStorageFileFormatTypeToString(disk->store->format));
359
360 if (virDomainDiskSourceFormat(&childBuf, disk->store, sourcename,
361 0, false, storageSourceFormatFlags,
362 false, false, xmlopt) < 0)
363 return -1;
364 }
365
366 virXMLFormatElement(buf, "disk", &attrBuf, &childBuf);
367 return 0;
368 }
369
370
371 static void
virDomainBackupDefFormatPrivate(virBuffer * buf,virDomainBackupDef * def,bool internal)372 virDomainBackupDefFormatPrivate(virBuffer *buf,
373 virDomainBackupDef *def,
374 bool internal)
375 {
376 g_auto(virBuffer) privChildBuf = VIR_BUFFER_INIT_CHILD(buf);
377 g_auto(virBuffer) objectsChildBuf = VIR_BUFFER_INIT_CHILD(&privChildBuf);
378
379 if (!internal)
380 return;
381
382 virBufferEscapeString(&objectsChildBuf, "<secret type='tlskey' alias='%s'/>\n",
383 def->tlsSecretAlias);
384 virBufferEscapeString(&objectsChildBuf, "<TLSx509 alias='%s'/>\n", def->tlsAlias);
385
386 virXMLFormatElement(&privChildBuf, "objects", NULL, &objectsChildBuf);
387 virXMLFormatElement(buf, "privateData", NULL, &privChildBuf);
388 }
389
390
391 int
virDomainBackupDefFormat(virBuffer * buf,virDomainBackupDef * def,bool internal,virDomainXMLOption * xmlopt)392 virDomainBackupDefFormat(virBuffer *buf,
393 virDomainBackupDef *def,
394 bool internal,
395 virDomainXMLOption *xmlopt)
396 {
397 g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
398 g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
399 g_auto(virBuffer) serverAttrBuf = VIR_BUFFER_INITIALIZER;
400 g_auto(virBuffer) disksChildBuf = VIR_BUFFER_INIT_CHILD(&childBuf);
401 size_t i;
402
403 virBufferAsprintf(&attrBuf, " mode='%s'", virDomainBackupTypeToString(def->type));
404
405 virBufferEscapeString(&childBuf, "<incremental>%s</incremental>\n", def->incremental);
406
407 if (def->server) {
408 virBufferAsprintf(&serverAttrBuf, " transport='%s'",
409 virStorageNetHostTransportTypeToString(def->server->transport));
410 if (def->tls != VIR_TRISTATE_BOOL_ABSENT)
411 virBufferAsprintf(&serverAttrBuf, " tls='%s'", virTristateBoolTypeToString(def->tls));
412 virBufferEscapeString(&serverAttrBuf, " name='%s'", def->server->name);
413 if (def->server->port)
414 virBufferAsprintf(&serverAttrBuf, " port='%u'", def->server->port);
415 virBufferEscapeString(&serverAttrBuf, " socket='%s'", def->server->socket);
416 }
417
418 virXMLFormatElement(&childBuf, "server", &serverAttrBuf, NULL);
419
420 for (i = 0; i < def->ndisks; i++) {
421 if (virDomainBackupDiskDefFormat(&disksChildBuf, &def->disks[i],
422 def->type == VIR_DOMAIN_BACKUP_TYPE_PUSH,
423 internal, xmlopt) < 0)
424 return -1;
425 }
426
427 virXMLFormatElement(&childBuf, "disks", NULL, &disksChildBuf);
428
429 virDomainBackupDefFormatPrivate(&childBuf, def, internal);
430
431 virXMLFormatElement(buf, "domainbackup", &attrBuf, &childBuf);
432
433 return 0;
434 }
435
436
437 static int
virDomainBackupDefAssignStore(virDomainBackupDiskDef * disk,virStorageSource * src,const char * suffix)438 virDomainBackupDefAssignStore(virDomainBackupDiskDef *disk,
439 virStorageSource *src,
440 const char *suffix)
441 {
442 if (virStorageSourceIsEmpty(src)) {
443 if (disk->store) {
444 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
445 _("disk '%s' has no media"), disk->name);
446 return -1;
447 }
448 } else if (!disk->store) {
449 if (virStorageSourceGetActualType(src) == VIR_STORAGE_TYPE_FILE) {
450 disk->store = virStorageSourceNew();
451 disk->store->type = VIR_STORAGE_TYPE_FILE;
452 disk->store->path = g_strdup_printf("%s.%s", src->path, suffix);
453 } else {
454 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
455 _("refusing to generate file name for disk '%s'"),
456 disk->name);
457 return -1;
458 }
459 }
460
461 return 0;
462 }
463
464
465 int
virDomainBackupAlignDisks(virDomainBackupDef * def,virDomainDef * dom,const char * suffix)466 virDomainBackupAlignDisks(virDomainBackupDef *def,
467 virDomainDef *dom,
468 const char *suffix)
469 {
470 g_autoptr(GHashTable) disks = virHashNew(NULL);
471 size_t i;
472 int ndisks;
473 bool backup_all = false;
474
475 /* Unlikely to have a guest without disks but technically possible. */
476 if (!dom->ndisks) {
477 virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
478 _("domain must have at least one disk to perform backup"));
479 return -1;
480 }
481
482 /* Double check requested disks. */
483 for (i = 0; i < def->ndisks; i++) {
484 virDomainBackupDiskDef *backupdisk = &def->disks[i];
485 virDomainDiskDef *domdisk;
486
487 if (!(domdisk = virDomainDiskByTarget(dom, backupdisk->name))) {
488 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
489 _("no disk named '%s'"), backupdisk->name);
490 return -1;
491 }
492
493 if (virHashAddEntry(disks, backupdisk->name, NULL) < 0) {
494 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
495 _("disk '%s' specified twice"),
496 backupdisk->name);
497 return -1;
498 }
499
500 if (backupdisk->backupmode == VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_INCREMENTAL &&
501 !backupdisk->incremental &&
502 !def->incremental) {
503 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
504 _("'incremental' backup mode of disk '%s' requires setting 'incremental' field for disk or backup"),
505 backupdisk->name);
506 return -1;
507 }
508
509
510 if (backupdisk->backup == VIR_TRISTATE_BOOL_YES &&
511 virDomainBackupDefAssignStore(backupdisk, domdisk->src, suffix) < 0)
512 return -1;
513 }
514
515 if (def->ndisks == 0)
516 backup_all = true;
517
518 ndisks = def->ndisks;
519 VIR_EXPAND_N(def->disks, def->ndisks, dom->ndisks - def->ndisks);
520
521 for (i = 0; i < dom->ndisks; i++) {
522 virDomainBackupDiskDef *backupdisk = NULL;
523 virDomainDiskDef *domdisk = dom->disks[i];
524
525 if (virHashHasEntry(disks, domdisk->dst))
526 continue;
527
528 backupdisk = &def->disks[ndisks++];
529 backupdisk->name = g_strdup(domdisk->dst);
530
531 if (backup_all &&
532 !virStorageSourceIsEmpty(domdisk->src) &&
533 !domdisk->src->readonly) {
534 backupdisk->backup = VIR_TRISTATE_BOOL_YES;
535
536 if (virDomainBackupDefAssignStore(backupdisk, domdisk->src, suffix) < 0)
537 return -1;
538 } else {
539 backupdisk->backup = VIR_TRISTATE_BOOL_NO;
540 }
541 }
542
543 for (i = 0; i < def->ndisks; i++) {
544 virDomainBackupDiskDef *backupdisk = &def->disks[i];
545
546 if (backupdisk->backupmode == VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_DEFAULT) {
547 if (def->incremental || backupdisk->incremental) {
548 backupdisk->backupmode = VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_INCREMENTAL;
549 } else {
550 backupdisk->backupmode = VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_FULL;
551 }
552 }
553
554 if (!backupdisk->incremental &&
555 backupdisk->backupmode == VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_INCREMENTAL)
556 backupdisk->incremental = g_strdup(def->incremental);
557 }
558
559 return 0;
560 }
561