1 /*
2 * domain_capabilities.c: domain capabilities XML processing
3 *
4 * Copyright (C) 2014 Red Hat, Inc.
5 *
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.
10 *
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.
15 *
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/>.
19 */
20
21 #include <config.h>
22
23 #include "device_conf.h"
24 #include "domain_capabilities.h"
25 #include "domain_conf.h"
26 #include "viralloc.h"
27 #include "virstring.h"
28
29 #define VIR_FROM_THIS VIR_FROM_CAPABILITIES
30
31 VIR_ENUM_IMPL(virDomainCapsCPUUsable,
32 VIR_DOMCAPS_CPU_USABLE_LAST,
33 "unknown", "yes", "no",
34 );
35
36
37 VIR_ENUM_DECL(virDomainCapsFeature);
38 VIR_ENUM_IMPL(virDomainCapsFeature,
39 VIR_DOMAIN_CAPS_FEATURE_LAST,
40 "iothreads",
41 "vmcoreinfo",
42 "genid",
43 "backingStoreInput",
44 "backup",
45 "s390-pv",
46 );
47
48 static virClass *virDomainCapsClass;
49 static virClass *virDomainCapsCPUModelsClass;
50
51 static void virDomainCapsDispose(void *obj);
52 static void virDomainCapsCPUModelsDispose(void *obj);
53
virDomainCapsOnceInit(void)54 static int virDomainCapsOnceInit(void)
55 {
56 if (!VIR_CLASS_NEW(virDomainCaps, virClassForObjectLockable()))
57 return -1;
58
59 if (!VIR_CLASS_NEW(virDomainCapsCPUModels, virClassForObject()))
60 return -1;
61
62 return 0;
63 }
64
65
66 VIR_ONCE_GLOBAL_INIT(virDomainCaps);
67
68
69 void
virSEVCapabilitiesFree(virSEVCapability * cap)70 virSEVCapabilitiesFree(virSEVCapability *cap)
71 {
72 if (!cap)
73 return;
74
75 g_free(cap->pdh);
76 g_free(cap->cert_chain);
77 g_free(cap);
78 }
79
80
81 static void
virDomainCapsDispose(void * obj)82 virDomainCapsDispose(void *obj)
83 {
84 virDomainCaps *caps = obj;
85 virDomainCapsStringValues *values;
86 size_t i;
87
88 g_free(caps->path);
89 g_free(caps->machine);
90 virObjectUnref(caps->cpu.custom);
91 virCPUDefFree(caps->cpu.hostModel);
92 virSEVCapabilitiesFree(caps->sev);
93
94 values = &caps->os.loader.values;
95 for (i = 0; i < values->nvalues; i++)
96 g_free(values->values[i]);
97 g_free(values->values);
98 }
99
100
101 static void
virDomainCapsCPUModelsDispose(void * obj)102 virDomainCapsCPUModelsDispose(void *obj)
103 {
104 virDomainCapsCPUModels *cpuModels = obj;
105 size_t i;
106
107 for (i = 0; i < cpuModels->nmodels; i++) {
108 g_free(cpuModels->models[i].name);
109 g_strfreev(cpuModels->models[i].blockers);
110 }
111
112 g_free(cpuModels->models);
113 }
114
115
116 virDomainCaps *
virDomainCapsNew(const char * path,const char * machine,virArch arch,virDomainVirtType virttype)117 virDomainCapsNew(const char *path,
118 const char *machine,
119 virArch arch,
120 virDomainVirtType virttype)
121 {
122 virDomainCaps *caps = NULL;
123
124 if (virDomainCapsInitialize() < 0)
125 return NULL;
126
127 if (!(caps = virObjectLockableNew(virDomainCapsClass)))
128 return NULL;
129
130 caps->path = g_strdup(path);
131 caps->machine = g_strdup(machine);
132 caps->arch = arch;
133 caps->virttype = virttype;
134
135 return caps;
136 }
137
138
139 virDomainCapsCPUModels *
virDomainCapsCPUModelsNew(size_t nmodels)140 virDomainCapsCPUModelsNew(size_t nmodels)
141 {
142 virDomainCapsCPUModels *cpuModels = NULL;
143
144 if (virDomainCapsInitialize() < 0)
145 return NULL;
146
147 if (!(cpuModels = virObjectNew(virDomainCapsCPUModelsClass)))
148 return NULL;
149
150 cpuModels->models = g_new0(virDomainCapsCPUModel, nmodels);
151 cpuModels->nmodels_max = nmodels;
152
153 return cpuModels;
154 }
155
156
157 virDomainCapsCPUModels *
virDomainCapsCPUModelsCopy(virDomainCapsCPUModels * old)158 virDomainCapsCPUModelsCopy(virDomainCapsCPUModels *old)
159 {
160 g_autoptr(virDomainCapsCPUModels) cpuModels = NULL;
161 size_t i;
162
163 if (!(cpuModels = virDomainCapsCPUModelsNew(old->nmodels)))
164 return NULL;
165
166 for (i = 0; i < old->nmodels; i++) {
167 if (virDomainCapsCPUModelsAdd(cpuModels,
168 old->models[i].name,
169 old->models[i].usable,
170 old->models[i].blockers,
171 old->models[i].deprecated) < 0)
172 return NULL;
173 }
174
175 return g_steal_pointer(&cpuModels);
176 }
177
178
179 int
virDomainCapsCPUModelsAdd(virDomainCapsCPUModels * cpuModels,const char * name,virDomainCapsCPUUsable usable,char ** blockers,bool deprecated)180 virDomainCapsCPUModelsAdd(virDomainCapsCPUModels *cpuModels,
181 const char *name,
182 virDomainCapsCPUUsable usable,
183 char **blockers,
184 bool deprecated)
185 {
186 g_autofree char * nameCopy = NULL;
187 virDomainCapsCPUModel *cpu;
188
189 nameCopy = g_strdup(name);
190
191 VIR_RESIZE_N(cpuModels->models, cpuModels->nmodels_max,
192 cpuModels->nmodels, 1);
193
194 cpu = cpuModels->models + cpuModels->nmodels;
195 cpuModels->nmodels++;
196
197 cpu->usable = usable;
198 cpu->name = g_steal_pointer(&nameCopy);
199 cpu->blockers = g_strdupv(blockers);
200 cpu->deprecated = deprecated;
201
202 return 0;
203 }
204
205
206 virDomainCapsCPUModel *
virDomainCapsCPUModelsGet(virDomainCapsCPUModels * cpuModels,const char * name)207 virDomainCapsCPUModelsGet(virDomainCapsCPUModels *cpuModels,
208 const char *name)
209 {
210 size_t i;
211
212 if (!cpuModels)
213 return NULL;
214
215 for (i = 0; i < cpuModels->nmodels; i++) {
216 if (STREQ(cpuModels->models[i].name, name))
217 return cpuModels->models + i;
218 }
219
220 return NULL;
221 }
222
223
224 int
virDomainCapsEnumSet(virDomainCapsEnum * capsEnum,const char * capsEnumName,size_t nvalues,unsigned int * values)225 virDomainCapsEnumSet(virDomainCapsEnum *capsEnum,
226 const char *capsEnumName,
227 size_t nvalues,
228 unsigned int *values)
229 {
230 size_t i;
231
232 for (i = 0; i < nvalues; i++) {
233 unsigned int val = 1 << values[i];
234
235 if (!val) {
236 /* Integer overflow */
237 virReportError(VIR_ERR_INTERNAL_ERROR,
238 _("integer overflow on %s. Please contact the "
239 "libvirt development team at libvir-list@redhat.com"),
240 capsEnumName);
241 return -1;
242 }
243
244 capsEnum->values |= val;
245 }
246
247 return 0;
248 }
249
250
251 void
virDomainCapsEnumClear(virDomainCapsEnum * capsEnum)252 virDomainCapsEnumClear(virDomainCapsEnum *capsEnum)
253 {
254 capsEnum->values = 0;
255 }
256
257
258 static int
virDomainCapsEnumFormat(virBuffer * buf,const virDomainCapsEnum * capsEnum,const char * capsEnumName,virDomainCapsValToStr valToStr)259 virDomainCapsEnumFormat(virBuffer *buf,
260 const virDomainCapsEnum *capsEnum,
261 const char *capsEnumName,
262 virDomainCapsValToStr valToStr)
263 {
264 size_t i;
265
266 if (!capsEnum->report)
267 return 0;
268
269 virBufferAsprintf(buf, "<enum name='%s'", capsEnumName);
270 if (!capsEnum->values) {
271 virBufferAddLit(buf, "/>\n");
272 return 0;
273 }
274 virBufferAddLit(buf, ">\n");
275 virBufferAdjustIndent(buf, 2);
276
277 for (i = 0; i < sizeof(capsEnum->values) * CHAR_BIT; i++) {
278 const char *val;
279
280 if (!VIR_DOMAIN_CAPS_ENUM_IS_SET(*capsEnum, i))
281 continue;
282
283 if ((val = (valToStr)(i)))
284 virBufferAsprintf(buf, "<value>%s</value>\n", val);
285 }
286 virBufferAdjustIndent(buf, -2);
287 virBufferAddLit(buf, "</enum>\n");
288
289 return 0;
290 }
291
292
293 static void
virDomainCapsStringValuesFormat(virBuffer * buf,const virDomainCapsStringValues * values)294 virDomainCapsStringValuesFormat(virBuffer *buf,
295 const virDomainCapsStringValues *values)
296 {
297 size_t i;
298
299 for (i = 0; i < values->nvalues; i++)
300 virBufferEscapeString(buf, "<value>%s</value>\n", values->values[i]);
301 }
302
303
304 #define FORMAT_PROLOGUE(item) \
305 do { \
306 if (item->supported == VIR_TRISTATE_BOOL_ABSENT) \
307 return; \
308 virBufferAsprintf(buf, "<" #item " supported='%s'%s\n", \
309 (item->supported == VIR_TRISTATE_BOOL_YES) ? "yes" : "no", \
310 (item->supported == VIR_TRISTATE_BOOL_YES) ? ">" : "/>"); \
311 if (item->supported == VIR_TRISTATE_BOOL_NO) \
312 return; \
313 virBufferAdjustIndent(buf, 2); \
314 } while (0)
315
316 #define FORMAT_EPILOGUE(item) \
317 do { \
318 virBufferAdjustIndent(buf, -2); \
319 virBufferAddLit(buf, "</" #item ">\n"); \
320 } while (0)
321
322 #define ENUM_PROCESS(master, capsEnum, valToStr) \
323 do { \
324 virDomainCapsEnumFormat(buf, &master->capsEnum, \
325 #capsEnum, valToStr); \
326 } while (0)
327
328
329 static void
qemuDomainCapsFeatureFormatSimple(virBuffer * buf,const char * featurename,virTristateBool supported)330 qemuDomainCapsFeatureFormatSimple(virBuffer *buf,
331 const char *featurename,
332 virTristateBool supported)
333 {
334 if (supported == VIR_TRISTATE_BOOL_ABSENT)
335 return;
336
337 virBufferAsprintf(buf, "<%s supported='%s'/>\n", featurename,
338 virTristateBoolTypeToString(supported));
339 }
340
341
342 static void
virDomainCapsLoaderFormat(virBuffer * buf,const virDomainCapsLoader * loader)343 virDomainCapsLoaderFormat(virBuffer *buf,
344 const virDomainCapsLoader *loader)
345 {
346 FORMAT_PROLOGUE(loader);
347
348 virDomainCapsStringValuesFormat(buf, &loader->values);
349 ENUM_PROCESS(loader, type, virDomainLoaderTypeToString);
350 ENUM_PROCESS(loader, readonly, virTristateBoolTypeToString);
351 ENUM_PROCESS(loader, secure, virTristateBoolTypeToString);
352
353 FORMAT_EPILOGUE(loader);
354 }
355
356 static void
virDomainCapsOSFormat(virBuffer * buf,const virDomainCapsOS * os)357 virDomainCapsOSFormat(virBuffer *buf,
358 const virDomainCapsOS *os)
359 {
360 const virDomainCapsLoader *loader = &os->loader;
361
362 FORMAT_PROLOGUE(os);
363
364 ENUM_PROCESS(os, firmware, virDomainOsDefFirmwareTypeToString);
365
366 virDomainCapsLoaderFormat(buf, loader);
367
368 FORMAT_EPILOGUE(os);
369 }
370
371 static void
virDomainCapsCPUCustomFormat(virBuffer * buf,virDomainCapsCPUModels * custom)372 virDomainCapsCPUCustomFormat(virBuffer *buf,
373 virDomainCapsCPUModels *custom)
374 {
375 size_t i;
376
377 virBufferAdjustIndent(buf, 2);
378
379 for (i = 0; i < custom->nmodels; i++) {
380 virDomainCapsCPUModel *model = custom->models + i;
381 virBufferAsprintf(buf, "<model usable='%s'",
382 virDomainCapsCPUUsableTypeToString(model->usable));
383 if (model->deprecated)
384 virBufferAddLit(buf, " deprecated='yes'");
385 virBufferAsprintf(buf, ">%s</model>\n",
386 model->name);
387 }
388
389 virBufferAdjustIndent(buf, -2);
390 }
391
392 static void
virDomainCapsCPUFormat(virBuffer * buf,const virDomainCapsCPU * cpu)393 virDomainCapsCPUFormat(virBuffer *buf,
394 const virDomainCapsCPU *cpu)
395 {
396 virBufferAddLit(buf, "<cpu>\n");
397 virBufferAdjustIndent(buf, 2);
398
399 virBufferAsprintf(buf, "<mode name='%s' supported='%s'",
400 virCPUModeTypeToString(VIR_CPU_MODE_HOST_PASSTHROUGH),
401 cpu->hostPassthrough ? "yes" : "no");
402
403 if (cpu->hostPassthrough && cpu->hostPassthroughMigratable.report) {
404 virBufferAddLit(buf, ">\n");
405 virBufferAdjustIndent(buf, 2);
406 ENUM_PROCESS(cpu, hostPassthroughMigratable,
407 virTristateSwitchTypeToString);
408 virBufferAdjustIndent(buf, -2);
409 virBufferAddLit(buf, "</mode>\n");
410 } else {
411 virBufferAddLit(buf, "/>\n");
412 }
413
414 virBufferAsprintf(buf, "<mode name='%s' supported='%s'",
415 virCPUModeTypeToString(VIR_CPU_MODE_MAXIMUM),
416 cpu->maximum ? "yes" : "no");
417
418 if (cpu->maximum && cpu->maximumMigratable.report) {
419 virBufferAddLit(buf, ">\n");
420 virBufferAdjustIndent(buf, 2);
421 ENUM_PROCESS(cpu, maximumMigratable,
422 virTristateSwitchTypeToString);
423 virBufferAdjustIndent(buf, -2);
424 virBufferAddLit(buf, "</mode>\n");
425 } else {
426 virBufferAddLit(buf, "/>\n");
427 }
428
429 virBufferAsprintf(buf, "<mode name='%s' ",
430 virCPUModeTypeToString(VIR_CPU_MODE_HOST_MODEL));
431 if (cpu->hostModel) {
432 virBufferAddLit(buf, "supported='yes'>\n");
433 virBufferAdjustIndent(buf, 2);
434
435 virCPUDefFormatBuf(buf, cpu->hostModel);
436
437 virBufferAdjustIndent(buf, -2);
438 virBufferAddLit(buf, "</mode>\n");
439 } else {
440 virBufferAddLit(buf, "supported='no'/>\n");
441 }
442
443 virBufferAsprintf(buf, "<mode name='%s' ",
444 virCPUModeTypeToString(VIR_CPU_MODE_CUSTOM));
445 if (cpu->custom && cpu->custom->nmodels) {
446 virBufferAddLit(buf, "supported='yes'>\n");
447 virDomainCapsCPUCustomFormat(buf, cpu->custom);
448 virBufferAddLit(buf, "</mode>\n");
449 } else {
450 virBufferAddLit(buf, "supported='no'/>\n");
451 }
452
453 virBufferAdjustIndent(buf, -2);
454 virBufferAddLit(buf, "</cpu>\n");
455 }
456
457 static void
virDomainCapsMemoryBackingFormat(virBuffer * buf,const virDomainCapsMemoryBacking * memoryBacking)458 virDomainCapsMemoryBackingFormat(virBuffer *buf,
459 const virDomainCapsMemoryBacking *memoryBacking)
460 {
461 FORMAT_PROLOGUE(memoryBacking);
462
463 ENUM_PROCESS(memoryBacking, sourceType, virDomainMemorySourceTypeToString);
464
465 FORMAT_EPILOGUE(memoryBacking);
466 }
467
468
469 static void
virDomainCapsDeviceDiskFormat(virBuffer * buf,const virDomainCapsDeviceDisk * disk)470 virDomainCapsDeviceDiskFormat(virBuffer *buf,
471 const virDomainCapsDeviceDisk *disk)
472 {
473 FORMAT_PROLOGUE(disk);
474
475 ENUM_PROCESS(disk, diskDevice, virDomainDiskDeviceTypeToString);
476 ENUM_PROCESS(disk, bus, virDomainDiskBusTypeToString);
477 ENUM_PROCESS(disk, model, virDomainDiskModelTypeToString);
478
479 FORMAT_EPILOGUE(disk);
480 }
481
482
483 static void
virDomainCapsDeviceGraphicsFormat(virBuffer * buf,const virDomainCapsDeviceGraphics * graphics)484 virDomainCapsDeviceGraphicsFormat(virBuffer *buf,
485 const virDomainCapsDeviceGraphics *graphics)
486 {
487 FORMAT_PROLOGUE(graphics);
488
489 ENUM_PROCESS(graphics, type, virDomainGraphicsTypeToString);
490
491 FORMAT_EPILOGUE(graphics);
492 }
493
494
495 static void
virDomainCapsDeviceVideoFormat(virBuffer * buf,const virDomainCapsDeviceVideo * video)496 virDomainCapsDeviceVideoFormat(virBuffer *buf,
497 const virDomainCapsDeviceVideo *video)
498 {
499 FORMAT_PROLOGUE(video);
500
501 ENUM_PROCESS(video, modelType, virDomainVideoTypeToString);
502
503 FORMAT_EPILOGUE(video);
504 }
505
506
507 static void
virDomainCapsDeviceHostdevFormat(virBuffer * buf,const virDomainCapsDeviceHostdev * hostdev)508 virDomainCapsDeviceHostdevFormat(virBuffer *buf,
509 const virDomainCapsDeviceHostdev *hostdev)
510 {
511 FORMAT_PROLOGUE(hostdev);
512
513 ENUM_PROCESS(hostdev, mode, virDomainHostdevModeTypeToString);
514 ENUM_PROCESS(hostdev, startupPolicy, virDomainStartupPolicyTypeToString);
515 ENUM_PROCESS(hostdev, subsysType, virDomainHostdevSubsysTypeToString);
516 ENUM_PROCESS(hostdev, capsType, virDomainHostdevCapsTypeToString);
517 ENUM_PROCESS(hostdev, pciBackend, virDomainHostdevSubsysPCIBackendTypeToString);
518
519 FORMAT_EPILOGUE(hostdev);
520 }
521
522
523 static void
virDomainCapsDeviceRNGFormat(virBuffer * buf,const virDomainCapsDeviceRNG * rng)524 virDomainCapsDeviceRNGFormat(virBuffer *buf,
525 const virDomainCapsDeviceRNG *rng)
526 {
527 FORMAT_PROLOGUE(rng);
528
529 ENUM_PROCESS(rng, model, virDomainRNGModelTypeToString);
530 ENUM_PROCESS(rng, backendModel, virDomainRNGBackendTypeToString);
531
532 FORMAT_EPILOGUE(rng);
533 }
534
535
536 static void
virDomainCapsDeviceFilesystemFormat(virBuffer * buf,const virDomainCapsDeviceFilesystem * filesystem)537 virDomainCapsDeviceFilesystemFormat(virBuffer *buf,
538 const virDomainCapsDeviceFilesystem *filesystem)
539 {
540 FORMAT_PROLOGUE(filesystem);
541
542 ENUM_PROCESS(filesystem, driverType, virDomainFSDriverTypeToString);
543
544 FORMAT_EPILOGUE(filesystem);
545 }
546
547
548 /**
549 * virDomainCapsFeatureGICFormat:
550 * @buf: target buffer
551 * @gic: GIC features
552 *
553 * Format GIC features for inclusion in the domcapabilities XML.
554 *
555 * The resulting XML will look like
556 *
557 * <gic supported='yes'>
558 * <enum name='version>
559 * <value>2</value>
560 * <value>3</value>
561 * </enum>
562 * </gic>
563 */
564 static void
virDomainCapsFeatureGICFormat(virBuffer * buf,const virDomainCapsFeatureGIC * gic)565 virDomainCapsFeatureGICFormat(virBuffer *buf,
566 const virDomainCapsFeatureGIC *gic)
567 {
568 FORMAT_PROLOGUE(gic);
569
570 ENUM_PROCESS(gic, version, virGICVersionTypeToString);
571
572 FORMAT_EPILOGUE(gic);
573 }
574
575 static void
virDomainCapsFeatureSEVFormat(virBuffer * buf,const virSEVCapability * sev)576 virDomainCapsFeatureSEVFormat(virBuffer *buf,
577 const virSEVCapability *sev)
578 {
579 if (!sev) {
580 virBufferAddLit(buf, "<sev supported='no'/>\n");
581 } else {
582 virBufferAddLit(buf, "<sev supported='yes'>\n");
583 virBufferAdjustIndent(buf, 2);
584 virBufferAsprintf(buf, "<cbitpos>%d</cbitpos>\n", sev->cbitpos);
585 virBufferAsprintf(buf, "<reducedPhysBits>%d</reducedPhysBits>\n",
586 sev->reduced_phys_bits);
587 virBufferAdjustIndent(buf, -2);
588 virBufferAddLit(buf, "</sev>\n");
589 }
590
591 return;
592 }
593
594
595 static void
virDomainCapsFormatFeatures(const virDomainCaps * caps,virBuffer * buf)596 virDomainCapsFormatFeatures(const virDomainCaps *caps,
597 virBuffer *buf)
598 {
599 g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
600 size_t i;
601
602 virDomainCapsFeatureGICFormat(&childBuf, &caps->gic);
603
604 for (i = 0; i < VIR_DOMAIN_CAPS_FEATURE_LAST; i++) {
605 if (i == VIR_DOMAIN_CAPS_FEATURE_IOTHREADS)
606 continue;
607
608 qemuDomainCapsFeatureFormatSimple(&childBuf,
609 virDomainCapsFeatureTypeToString(i),
610 caps->features[i]);
611 }
612
613 virDomainCapsFeatureSEVFormat(&childBuf, caps->sev);
614
615 virXMLFormatElement(buf, "features", NULL, &childBuf);
616 }
617
618
619 char *
virDomainCapsFormat(const virDomainCaps * caps)620 virDomainCapsFormat(const virDomainCaps *caps)
621 {
622 g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
623 const char *virttype_str = virDomainVirtTypeToString(caps->virttype);
624 const char *arch_str = virArchToString(caps->arch);
625
626 virBufferAddLit(&buf, "<domainCapabilities>\n");
627 virBufferAdjustIndent(&buf, 2);
628
629 virBufferEscapeString(&buf, "<path>%s</path>\n", caps->path);
630 virBufferAsprintf(&buf, "<domain>%s</domain>\n", virttype_str);
631 if (caps->machine)
632 virBufferAsprintf(&buf, "<machine>%s</machine>\n", caps->machine);
633 virBufferAsprintf(&buf, "<arch>%s</arch>\n", arch_str);
634
635 if (caps->maxvcpus)
636 virBufferAsprintf(&buf, "<vcpu max='%d'/>\n", caps->maxvcpus);
637
638 qemuDomainCapsFeatureFormatSimple(&buf, "iothreads",
639 caps->features[VIR_DOMAIN_CAPS_FEATURE_IOTHREADS]);
640
641 virDomainCapsOSFormat(&buf, &caps->os);
642 virDomainCapsCPUFormat(&buf, &caps->cpu);
643
644 virDomainCapsMemoryBackingFormat(&buf, &caps->memoryBacking);
645
646 virBufferAddLit(&buf, "<devices>\n");
647 virBufferAdjustIndent(&buf, 2);
648
649 virDomainCapsDeviceDiskFormat(&buf, &caps->disk);
650 virDomainCapsDeviceGraphicsFormat(&buf, &caps->graphics);
651 virDomainCapsDeviceVideoFormat(&buf, &caps->video);
652 virDomainCapsDeviceHostdevFormat(&buf, &caps->hostdev);
653 virDomainCapsDeviceRNGFormat(&buf, &caps->rng);
654 virDomainCapsDeviceFilesystemFormat(&buf, &caps->filesystem);
655
656 virBufferAdjustIndent(&buf, -2);
657 virBufferAddLit(&buf, "</devices>\n");
658
659 virDomainCapsFormatFeatures(caps, &buf);
660
661 virBufferAdjustIndent(&buf, -2);
662 virBufferAddLit(&buf, "</domainCapabilities>\n");
663
664 return virBufferContentAndReset(&buf);
665 }
666