From nobody Sun Apr 28 14:37:19 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1554122386711568.358124140467; Mon, 1 Apr 2019 05:39:46 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 0CDFB356F5; Mon, 1 Apr 2019 12:39:45 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id D5BE319C70; Mon, 1 Apr 2019 12:39:44 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id B43D21803389; Mon, 1 Apr 2019 12:39:43 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id x31Cdfnp007321 for ; Mon, 1 Apr 2019 08:39:41 -0400 Received: by smtp.corp.redhat.com (Postfix) id CC51C60BE5; Mon, 1 Apr 2019 12:39:41 +0000 (UTC) Received: from kinshicho.brq.redhat.com (unknown [10.43.2.212]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 26F1460260 for ; Mon, 1 Apr 2019 12:39:37 +0000 (UTC) From: Andrea Bolognani To: libvir-list@redhat.com Date: Mon, 1 Apr 2019 14:39:23 +0200 Message-Id: <20190401123930.21777-2-abologna@redhat.com> In-Reply-To: <20190401123930.21777-1-abologna@redhat.com> References: <20190401123930.21777-1-abologna@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-loop: libvir-list@redhat.com Subject: [libvirt] [RFC PATCH 1/8] conf: Introduce virDomainDefParseJSONString() X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.30]); Mon, 01 Apr 2019 12:39:45 +0000 (UTC) Content-Type: text/plain; charset="utf-8" We parse just enough JSON to be able to define a domain using the new API at the moment: clearly more work is needed before it can be considered a replacement for the existing XML-based APIs, but you gotta start somewhere :) Signed-off-by: Andrea Bolognani --- include/libvirt/virterror.h | 1 + src/conf/domain_conf.c | 241 ++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 5 + src/libvirt_private.syms | 1 + src/util/virerror.c | 3 + 5 files changed, 251 insertions(+) diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 6dc83a17cc..90406d595a 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -326,6 +326,7 @@ typedef enum { VIR_ERR_INVALID_DOMAIN_CHECKPOINT =3D 102, /* invalid domain checkpoin= t */ VIR_ERR_NO_DOMAIN_CHECKPOINT =3D 103, /* domain checkpoint not found */ VIR_ERR_NO_DOMAIN_BACKUP =3D 104, /* domain backup job id not foun= d */ + VIR_ERR_JSON_ERROR =3D 105, /* JSON format error */ =20 # ifdef VIR_ENUM_SENTINELS VIR_ERR_NUMBER_LAST diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 5f2b1f68b5..896dd75082 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -58,6 +58,7 @@ #include "virhostdev.h" #include "virmdev.h" #include "virdomainsnapshotobjlist.h" +#include "virjson.h" =20 #define VIR_FROM_THIS VIR_FROM_DOMAIN =20 @@ -21206,6 +21207,246 @@ virDomainDefParseFile(const char *filename, } =20 =20 +static int +virDomainDefParseJSONDomainOSType(virDomainDefPtr def, + virJSONValuePtr osType) +{ + virJSONValuePtr attributes =3D virJSONValueObjectGetObject(osType, "at= tributes"); + const char *type =3D NULL; + const char *arch =3D NULL; + const char *machine =3D NULL; + int ret =3D -1; + + if (!attributes) { + virReportError(VIR_ERR_JSON_ERROR, "%s", + _("No attributes for 'type' object")); + goto cleanup; + } + + if (!(type =3D virJSONValueObjectGetString(osType, "value"))) { + virReportError(VIR_ERR_JSON_ERROR, "%s", + _("Missing OS type")); + goto cleanup; + } + + if ((def->os.type =3D virDomainOSTypeFromString(type)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Invalid OS type '%s'"), + type); + return -1; + } + + if ((machine =3D virJSONValueObjectGetString(attributes, "machine")) && + VIR_STRDUP(def->os.machine, machine) < 0) { + goto cleanup; + } + + if ((arch =3D virJSONValueObjectGetString(attributes, "arch")) && + !(def->os.arch =3D virArchFromString(arch))) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Invalid architecture '%s'"), + arch); + } + + ret =3D 0; + + cleanup: + return ret; +} + + +static int +virDomainDefParseJSONDomainOS(virDomainDefPtr def, + virJSONValuePtr os) +{ + virJSONValuePtr children =3D virJSONValueObjectGetObject(os, "children= "); + virJSONValuePtr osType =3D NULL; + int ret =3D -1; + + if (!children) { + virReportError(VIR_ERR_JSON_ERROR, "%s", + _("No children for 'os' object")); + goto cleanup; + } + + if (!(osType =3D virJSONValueObjectGetObject(children, "type"))) { + virReportError(VIR_ERR_JSON_ERROR, "%s", + _("Missing 'type' children for 'os' object")); + goto cleanup; + } + + if (virDomainDefParseJSONDomainOSType(def, osType) < 0) + goto cleanup; + + ret =3D 0; + + cleanup: + return ret; +} + + +static int +virDomainDefParseJSONDomain(virDomainDefPtr def, + virJSONValuePtr domain) +{ + virJSONValuePtr attributes =3D virJSONValueObjectGetObject(domain, "at= tributes"); + virJSONValuePtr children =3D virJSONValueObjectGetObject(domain, "chil= dren"); + virJSONValuePtr tmp =3D NULL; + const char *virtType =3D NULL; + const char *name =3D NULL; + int ret =3D -1; + + if (!attributes) { + virReportError(VIR_ERR_JSON_ERROR, "%s", + _("No attributes for 'domain' object")); + goto cleanup; + } + + if (!(virtType =3D virJSONValueObjectGetString(attributes, "type"))) { + virReportError(VIR_ERR_JSON_ERROR, "%s", + _("Missing 'type' attribute for 'domain' object")); + goto cleanup; + } + + if ((def->virtType =3D virDomainVirtTypeFromString(virtType)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Invalid virtualization type '%s'"), + virtType); + goto cleanup; + } + + if (!children) { + virReportError(VIR_ERR_JSON_ERROR, "%s", + _("No children for 'domain' object")); + goto cleanup; + } + + if (!(tmp =3D virJSONValueObjectGetObject(children, "name")) || + !(name =3D virJSONValueObjectGetString(tmp, "value"))) { + virReportError(VIR_ERR_NO_NAME, NULL); + goto cleanup; + } + + if (!VIR_STRDUP(def->name, name)) + goto cleanup; + + if ((tmp =3D virJSONValueObjectGetObject(children, "uuid"))) { + + const char *uuid; + + if (!(uuid =3D virJSONValueObjectGetString(tmp, "value")) || + virUUIDParse(uuid, def->uuid) < 0) { + virReportError(VIR_ERR_JSON_ERROR, + _("Invalid UUID '%s'"), + uuid); + goto cleanup; + } + } else { + if (virUUIDGenerate(def->uuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to generate UUID")); + goto cleanup; + } + } + + if ((tmp =3D virJSONValueObjectGetObject(children, "memory"))) { + + unsigned long long max =3D virMemoryMaxValue(true); + unsigned long long memory; + + if (!virJSONValueObjectHasKey(tmp, "value")) { + virReportError(VIR_ERR_JSON_ERROR, "%s", + _("Missing 'value' attribute for 'memory' objec= t")); + goto cleanup; + } + + if (virJSONValueObjectGetNumberUlong(tmp, "value", &memory) < 0 || + virScaleInteger(&memory, NULL, 1024, max) < 0) { + virReportError(VIR_ERR_JSON_ERROR, "%s", + _("Invalid memory size")); + goto cleanup; + } + + /* Yes, we really do use kibibytes for our internal sizing. */ + def->mem.total_memory =3D VIR_DIV_UP(memory, 1024); + + if (def->mem.total_memory >=3D VIR_DIV_UP(max, 1024)) { + virReportError(VIR_ERR_OVERFLOW, "%s", + _("Memory size is too large")); + goto cleanup; + } + } + + if (virDomainDefSetVcpusMax(def, 1, NULL) < 0 || + virDomainDefSetVcpus(def, 1) < 0) { + goto cleanup; + } + + if (!(tmp =3D virJSONValueObjectGetObject(children, "os"))) { + virReportError(VIR_ERR_JSON_ERROR, "%s", + _("No 'os' object")); + goto cleanup; + } + + if (virDomainDefParseJSONDomainOS(def, tmp) < 0) + goto cleanup; + + ret =3D 0; + + cleanup: + return ret; +} + + +static virDomainDefPtr +virDomainDefParseJSON(virJSONValuePtr json, + virCapsPtr caps, + virDomainXMLOptionPtr xmlopt, + void *parseOpaque, + unsigned int flags) +{ + virDomainDefPtr def =3D NULL; + virJSONValuePtr domain =3D NULL; + + if (!(def =3D virDomainDefNew())) + goto error; + + if (!(domain =3D virJSONValueObjectGetObject(json, "domain"))) { + virReportError(VIR_ERR_JSON_ERROR, "%s", + _("No 'domain' object")); + goto error; + } + + if (virDomainDefParseJSONDomain(def, domain) < 0 || + virDomainDefPostParse(def, caps, flags, xmlopt, parseOpaque) < 0 || + virDomainDefValidate(def, caps, flags, xmlopt) < 0) { + goto error; + } + + return def; + + error: + virDomainDefFree(def); + return NULL; +} + + +virDomainDefPtr +virDomainDefParseJSONString(const char *buf, + virCapsPtr caps, + virDomainXMLOptionPtr xmlopt, + void *parseOpaque, + unsigned int flags) +{ + VIR_AUTOPTR(virJSONValue) json =3D NULL; + + if (!(json =3D virJSONValueFromString(buf))) + return NULL; + + return virDomainDefParseJSON(json, caps, xmlopt, parseOpaque, flags); +} + + virDomainDefPtr virDomainDefParseNode(xmlDocPtr xml, xmlNodePtr root, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 4a25480662..ebeabd8dbe 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2954,6 +2954,11 @@ virDomainDefPtr virDomainDefParseFile(const char *fi= lename, virDomainXMLOptionPtr xmlopt, void *parseOpaque, unsigned int flags); +virDomainDefPtr virDomainDefParseJSONString(const char *buf, + virCapsPtr caps, + virDomainXMLOptionPtr xmlopt, + void *parseOpaque, + unsigned int flags); virDomainDefPtr virDomainDefParseNode(xmlDocPtr doc, xmlNodePtr root, virCapsPtr caps, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 73ef24d66f..8c60a01d8c 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -291,6 +291,7 @@ virDomainDefMaybeAddInput; virDomainDefNeedsPlacementAdvice; virDomainDefNew; virDomainDefParseFile; +virDomainDefParseJSONString; virDomainDefParseNode; virDomainDefParseString; virDomainDefPostParse; diff --git a/src/util/virerror.c b/src/util/virerror.c index 05e535d859..4e843efe99 100644 --- a/src/util/virerror.c +++ b/src/util/virerror.c @@ -1224,6 +1224,9 @@ const virErrorMsgTuple virErrorMsgStrings[VIR_ERR_NUM= BER_LAST] =3D { [VIR_ERR_NO_DOMAIN_BACKUP] =3D { N_("Domain backup job id not found"), N_("Domain backup job id not found: %s") }, + [VIR_ERR_JSON_ERROR] =3D { + N_("JSON description is invalid or not well formed"), + N_("JSON error: %s") }, }; =20 =20 --=20 2.20.1 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list From nobody Sun Apr 28 14:37:19 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1554122391371936.4870311663495; Mon, 1 Apr 2019 05:39:51 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 34B705945E; Mon, 1 Apr 2019 12:39:50 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 0D5D0648AF; Mon, 1 Apr 2019 12:39:50 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id C670A3FB11; Mon, 1 Apr 2019 12:39:49 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id x31Cdh64007327 for ; Mon, 1 Apr 2019 08:39:43 -0400 Received: by smtp.corp.redhat.com (Postfix) id 08FF860BE5; Mon, 1 Apr 2019 12:39:43 +0000 (UTC) Received: from kinshicho.brq.redhat.com (unknown [10.43.2.212]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 2B47160260 for ; Mon, 1 Apr 2019 12:39:42 +0000 (UTC) From: Andrea Bolognani To: libvir-list@redhat.com Date: Mon, 1 Apr 2019 14:39:24 +0200 Message-Id: <20190401123930.21777-3-abologna@redhat.com> In-Reply-To: <20190401123930.21777-1-abologna@redhat.com> References: <20190401123930.21777-1-abologna@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-loop: libvir-list@redhat.com Subject: [libvirt] [RFC PATCH 2/8] tests: Add qemujson2argvtest X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Mon, 01 Apr 2019 12:39:50 +0000 (UTC) Content-Type: text/plain; charset="utf-8" This is basically a copy of the existing qemuxml2argvtest, with a couple of obvious changes. We're not using most of the features at the moment, but as the JSON-based APIs evolve and get closer to feature-parity with the XML-based ones we're definitely going to need more and more of them, so we might as well start with the full package. Signed-off-by: Andrea Bolognani --- tests/Makefile.am | 15 + tests/qemujson2argvdata/tiny.json | 29 + .../qemujson2argvdata/tiny.x86_64-latest.args | 33 + tests/qemujson2argvtest.c | 1001 +++++++++++++++++ 4 files changed, 1078 insertions(+) create mode 100644 tests/qemujson2argvdata/tiny.json create mode 100644 tests/qemujson2argvdata/tiny.x86_64-latest.args create mode 100644 tests/qemujson2argvtest.c diff --git a/tests/Makefile.am b/tests/Makefile.am index d3cdbff8bb..df82c7dc22 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -162,6 +162,7 @@ EXTRA_DIST =3D \ virstorageutildata \ virfilecachedata \ virresctrldata \ + qemujson2argvdata \ $(NULL) =20 test_helpers =3D commandhelper ssh @@ -283,6 +284,7 @@ test_programs +=3D qemuxml2argvtest qemuxml2xmltest \ qemumigparamstest \ qemusecuritytest \ qemufirmwaretest \ + qemujson2argvtest \ $(NULL) test_helpers +=3D qemucapsprobe test_libraries +=3D libqemumonitortestutils.la \ @@ -692,6 +694,18 @@ qemufirmwaretest_SOURCES =3D \ $(NULL) qemufirmwaretest_LDADD =3D $(qemu_LDADDS) $(LDADDS) =20 +qemujson2argvtest_SOURCES =3D \ + virfilewrapper.c virfilewrapper.h \ + testutilsqemu.c testutilsqemu.h \ + testutils.c testutils.h \ + qemujson2argvtest.c \ + $(NULL) +qemujson2argvtest_LDADD =3D \ + libqemutestdriver.la \ + $(LIBXML_LIBS) \ + $(LDADDS) \ + $(NULL) + else ! WITH_QEMU EXTRA_DIST +=3D qemuxml2argvtest.c qemuxml2xmltest.c qemuargv2xmltest.c \ domainsnapshotxml2xmltest.c \ @@ -706,6 +720,7 @@ EXTRA_DIST +=3D qemuxml2argvtest.c qemuxml2xmltest.c qe= muargv2xmltest.c \ qemusecuritytest.c qemusecuritytest.h \ qemusecuritymock.c \ qemufirmwaretest.c \ + qemujson2argvtest.c \ $(QEMUMONITORTESTUTILS_SOURCES) endif ! WITH_QEMU =20 diff --git a/tests/qemujson2argvdata/tiny.json b/tests/qemujson2argvdata/ti= ny.json new file mode 100644 index 0000000000..99071c5ec0 --- /dev/null +++ b/tests/qemujson2argvdata/tiny.json @@ -0,0 +1,29 @@ +{ + "domain": { + "attributes": { + "type": "qemu" + }, + "children": { + "name": { + "value": "guest" + }, + "uuid": { + "value": "4f49aff1-4f74-45c3-87a7-51a6ad052a54" + }, + "memory": { + "value": 4194304 + }, + "os": { + "children": { + "type": { + "attributes": { + "arch": "x86_64", + "machine": "pc" + }, + "value": "hvm" + } + } + } + } + } +} diff --git a/tests/qemujson2argvdata/tiny.x86_64-latest.args b/tests/qemujs= on2argvdata/tiny.x86_64-latest.args new file mode 100644 index 0000000000..e8e67d7727 --- /dev/null +++ b/tests/qemujson2argvdata/tiny.x86_64-latest.args @@ -0,0 +1,33 @@ +LC_ALL=3DC \ +PATH=3D/bin \ +HOME=3D/tmp/lib/domain--1-guest \ +USER=3Dtest \ +LOGNAME=3Dtest \ +XDG_DATA_HOME=3D/tmp/lib/domain--1-guest/.local/share \ +XDG_CACHE_HOME=3D/tmp/lib/domain--1-guest/.cache \ +XDG_CONFIG_HOME=3D/tmp/lib/domain--1-guest/.config \ +QEMU_AUDIO_DRV=3Dnone \ +/usr/bin/qemu-system-x86_64 \ +-name guest=3Dguest,debug-threads=3Don \ +-S \ +-object secret,id=3DmasterKey0,format=3Draw,\ +file=3D/tmp/lib/domain--1-guest/master-key.aes \ +-machine pc,accel=3Dtcg,usb=3Doff,dump-guest-core=3Doff \ +-m 4096 \ +-realtime mlock=3Doff \ +-smp 1,sockets=3D1,cores=3D1,threads=3D1 \ +-uuid 4f49aff1-4f74-45c3-87a7-51a6ad052a54 \ +-display none \ +-no-user-config \ +-nodefaults \ +-chardev socket,id=3Dcharmonitor,fd=3D1729,server,nowait \ +-mon chardev=3Dcharmonitor,id=3Dmonitor,mode=3Dcontrol \ +-rtc base=3Dutc \ +-no-reboot \ +-no-acpi \ +-boot strict=3Don \ +-device piix3-usb-uhci,id=3Dusb,bus=3Dpci.0,addr=3D0x1.0x2 \ +-device virtio-balloon-pci,id=3Dballoon0,bus=3Dpci.0,addr=3D0x2 \ +-sandbox on,obsolete=3Ddeny,elevateprivileges=3Ddeny,spawn=3Ddeny,\ +resourcecontrol=3Ddeny \ +-msg timestamp=3Don diff --git a/tests/qemujson2argvtest.c b/tests/qemujson2argvtest.c new file mode 100644 index 0000000000..e754813646 --- /dev/null +++ b/tests/qemujson2argvtest.c @@ -0,0 +1,1001 @@ +#include + +#include + +#include +#include + +#include "testutils.h" + +#ifdef WITH_QEMU + +# include "internal.h" +# include "viralloc.h" +# include "qemu/qemu_alias.h" +# include "qemu/qemu_capabilities.h" +# include "qemu/qemu_command.h" +# include "qemu/qemu_domain.h" +# include "qemu/qemu_migration.h" +# include "qemu/qemu_process.h" +# include "datatypes.h" +# include "conf/storage_conf.h" +# include "cpu/cpu_map.h" +# include "virstring.h" +# include "storage/storage_driver.h" +# include "virmock.h" +# include "virfilewrapper.h" +# include "configmake.h" + +# define LIBVIRT_QEMU_CAPSPRIV_H_ALLOW +# include "qemu/qemu_capspriv.h" + +# include "testutilsqemu.h" + +# define VIR_FROM_THIS VIR_FROM_QEMU + +static virQEMUDriver driver; + +static unsigned char * +fakeSecretGetValue(virSecretPtr obj ATTRIBUTE_UNUSED, + size_t *value_size, + unsigned int fakeflags ATTRIBUTE_UNUSED, + unsigned int internalFlags ATTRIBUTE_UNUSED) +{ + char *secret; + if (VIR_STRDUP(secret, "AQCVn5hO6HzFAhAAq0NCv8jtJcIcE+HOBlMQ1A") < 0) + return NULL; + *value_size =3D strlen(secret); + return (unsigned char *) secret; +} + +static virSecretPtr +fakeSecretLookupByUsage(virConnectPtr conn, + int usageType, + const char *usageID) +{ + unsigned char uuid[VIR_UUID_BUFLEN]; + if (usageType =3D=3D VIR_SECRET_USAGE_TYPE_VOLUME) { + if (!STRPREFIX(usageID, "/storage/guest_disks/")) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "test provided invalid volume storage prefix '%= s'", + usageID); + return NULL; + } + } else if (STRNEQ(usageID, "mycluster_myname")) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "test provided incorrect usage '%s'", usageID); + return NULL; + } + + if (virUUIDGenerate(uuid) < 0) + return NULL; + + return virGetSecret(conn, uuid, usageType, usageID); +} + +static virSecretPtr +fakeSecretLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) +{ + /* NB: This mocked value could be "tls" or "volume" depending on + * which test is being run, we'll leave at NONE (or 0) */ + return virGetSecret(conn, uuid, VIR_SECRET_USAGE_TYPE_NONE, ""); +} + +static virSecretDriver fakeSecretDriver =3D { + .connectNumOfSecrets =3D NULL, + .connectListSecrets =3D NULL, + .secretLookupByUUID =3D fakeSecretLookupByUUID, + .secretLookupByUsage =3D fakeSecretLookupByUsage, + .secretDefineXML =3D NULL, + .secretGetXMLDesc =3D NULL, + .secretSetValue =3D NULL, + .secretGetValue =3D fakeSecretGetValue, + .secretUndefine =3D NULL, +}; + + +# define STORAGE_POOL_XML_PATH "storagepoolxml2xmlout/" +static const unsigned char fakeUUID[VIR_UUID_BUFLEN] =3D "fakeuuid"; + +static virStoragePoolPtr +fakeStoragePoolLookupByName(virConnectPtr conn, + const char *name) +{ + char *xmlpath =3D NULL; + virStoragePoolPtr ret =3D NULL; + + if (STRNEQ(name, "inactive")) { + if (virAsprintf(&xmlpath, "%s/%s%s.xml", + abs_srcdir, + STORAGE_POOL_XML_PATH, + name) < 0) + return NULL; + + if (!virFileExists(xmlpath)) { + virReportError(VIR_ERR_NO_STORAGE_POOL, + "File '%s' not found", xmlpath); + goto cleanup; + } + } + + ret =3D virGetStoragePool(conn, name, fakeUUID, NULL, NULL); + + cleanup: + VIR_FREE(xmlpath); + return ret; +} + + +static virStorageVolPtr +fakeStorageVolLookupByName(virStoragePoolPtr pool, + const char *name) +{ + char **volinfo =3D NULL; + virStorageVolPtr ret =3D NULL; + + if (STREQ(pool->name, "inactive")) { + virReportError(VIR_ERR_OPERATION_INVALID, + "storage pool '%s' is not active", pool->name); + return NULL; + } + + if (STREQ(name, "nonexistent")) { + virReportError(VIR_ERR_NO_STORAGE_VOL, + "no storage vol with matching name '%s'", name); + return NULL; + } + + if (!strchr(name, '+')) + goto fallback; + + if (!(volinfo =3D virStringSplit(name, "+", 2))) + return NULL; + + if (!volinfo[1]) + goto fallback; + + ret =3D virGetStorageVol(pool->conn, pool->name, volinfo[1], volinfo[0= ], + NULL, NULL); + + cleanup: + virStringListFree(volinfo); + return ret; + + fallback: + ret =3D virGetStorageVol(pool->conn, pool->name, name, "block", NULL, = NULL); + goto cleanup; +} + +static int +fakeStorageVolGetInfo(virStorageVolPtr vol, + virStorageVolInfoPtr info) +{ + memset(info, 0, sizeof(*info)); + + info->type =3D virStorageVolTypeFromString(vol->key); + + if (info->type < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "Invalid volume type '%s'", vol->key); + return -1; + } + + return 0; +} + + +static char * +fakeStorageVolGetPath(virStorageVolPtr vol) +{ + char *ret =3D NULL; + + ignore_value(virAsprintf(&ret, "/some/%s/device/%s", vol->key, vol->na= me)); + + return ret; +} + + +static char * +fakeStoragePoolGetXMLDesc(virStoragePoolPtr pool, + unsigned int flags_unused ATTRIBUTE_UNUSED) +{ + char *xmlpath =3D NULL; + char *xmlbuf =3D NULL; + + if (STREQ(pool->name, "inactive")) { + virReportError(VIR_ERR_NO_STORAGE_POOL, NULL); + return NULL; + } + + if (virAsprintf(&xmlpath, "%s/%s%s.xml", + abs_srcdir, + STORAGE_POOL_XML_PATH, + pool->name) < 0) + return NULL; + + if (virTestLoadFile(xmlpath, &xmlbuf) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "failed to load XML file '%s'", + xmlpath); + goto cleanup; + } + + cleanup: + VIR_FREE(xmlpath); + + return xmlbuf; +} + +static int +fakeStoragePoolIsActive(virStoragePoolPtr pool) +{ + if (STREQ(pool->name, "inactive")) + return 0; + + return 1; +} + +/* Test storage pool implementation + * + * These functions aid testing of storage pool related stuff when creating= a + * qemu command line. + * + * There are a few "magic" values to pass to these functions: + * + * 1) "inactive" as a pool name to create an inactive pool. All other name= s are + * interpreted as file names in storagepoolxml2xmlout/ and are used as the + * definition for the pool. If the file doesn't exist the pool doesn't exi= st. + * + * 2) "nonexistent" returns an error while looking up a volume. Otherwise + * pattern VOLUME_TYPE+VOLUME_PATH can be used to simulate a volume in a p= ool. + * This creates a fake path for this volume. If the '+' sign is omitted, b= lock + * type is assumed. + */ +static virStorageDriver fakeStorageDriver =3D { + .storagePoolLookupByName =3D fakeStoragePoolLookupByName, + .storageVolLookupByName =3D fakeStorageVolLookupByName, + .storagePoolGetXMLDesc =3D fakeStoragePoolGetXMLDesc, + .storageVolGetPath =3D fakeStorageVolGetPath, + .storageVolGetInfo =3D fakeStorageVolGetInfo, + .storagePoolIsActive =3D fakeStoragePoolIsActive, +}; + + +/* virNetDevOpenvswitchGetVhostuserIfname mocks a portdev name - handle th= at */ +static virNWFilterBindingPtr +fakeNWFilterBindingLookupByPortDev(virConnectPtr conn, + const char *portdev) +{ + if (STREQ(portdev, "vhost-user0")) + return virGetNWFilterBinding(conn, "fake_vnet0", "fakeFilterName"); + + virReportError(VIR_ERR_NO_NWFILTER_BINDING, + "no nwfilter binding for port dev '%s'", portdev); + return NULL; +} + + +static int +fakeNWFilterBindingDelete(virNWFilterBindingPtr binding ATTRIBUTE_UNUSED) +{ + return 0; +} + + +static virNWFilterDriver fakeNWFilterDriver =3D { + .nwfilterBindingLookupByPortDev =3D fakeNWFilterBindingLookupByPortDev, + .nwfilterBindingDelete =3D fakeNWFilterBindingDelete, +}; + +typedef enum { + FLAG_EXPECT_FAILURE =3D 1 << 0, + FLAG_EXPECT_PARSE_ERROR =3D 1 << 1, + FLAG_FIPS =3D 1 << 2, + FLAG_REAL_CAPS =3D 1 << 3, + FLAG_SKIP_LEGACY_CPUS =3D 1 << 4, +} virQemuJSON2ArgvTestFlags; + +struct testInfo { + const char *name; + const char *suffix; + virQEMUCapsPtr qemuCaps; + const char *migrateFrom; + int migrateFd; + unsigned int flags; + unsigned int parseFlags; +}; + + +static int +testAddCPUModels(virQEMUCapsPtr caps, bool skipLegacy) +{ + virArch arch =3D virQEMUCapsGetArch(caps); + const char *x86Models[] =3D { + "Opteron_G3", "Opteron_G2", "Opteron_G1", + "Nehalem", "Penryn", "Conroe", + "Haswell-noTSX", "Haswell", + }; + const char *x86LegacyModels[] =3D { + "n270", "athlon", "pentium3", "pentium2", "pentium", + "486", "coreduo", "kvm32", "qemu32", "kvm64", + "core2duo", "phenom", "qemu64", + }; + const char *armModels[] =3D { + "cortex-a9", "cortex-a8", "cortex-a57", "cortex-a53", + }; + const char *ppc64Models[] =3D { + "POWER8", "POWER7", + }; + const char *s390xModels[] =3D { + "z990", "zEC12", "z13", + }; + + if (ARCH_IS_X86(arch)) { + if (virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_KVM, x86Mod= els, + ARRAY_CARDINALITY(x86Models), + VIR_DOMCAPS_CPU_USABLE_UNKNOWN) <= 0 || + virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_QEMU, x86Mo= dels, + ARRAY_CARDINALITY(x86Models), + VIR_DOMCAPS_CPU_USABLE_UNKNOWN) <= 0) + return -1; + + if (!skipLegacy) { + if (virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_KVM, + x86LegacyModels, + ARRAY_CARDINALITY(x86LegacyMo= dels), + VIR_DOMCAPS_CPU_USABLE_UNKNOW= N) < 0 || + virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_QEMU, + x86LegacyModels, + ARRAY_CARDINALITY(x86LegacyMo= dels), + VIR_DOMCAPS_CPU_USABLE_UNKNOW= N) < 0) + return -1; + } + } else if (ARCH_IS_ARM(arch)) { + if (virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_KVM, armMod= els, + ARRAY_CARDINALITY(armModels), + VIR_DOMCAPS_CPU_USABLE_UNKNOWN) <= 0 || + virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_QEMU, armMo= dels, + ARRAY_CARDINALITY(armModels), + VIR_DOMCAPS_CPU_USABLE_UNKNOWN) <= 0) + return -1; + } else if (ARCH_IS_PPC64(arch)) { + if (virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_KVM, ppc64M= odels, + ARRAY_CARDINALITY(ppc64Models), + VIR_DOMCAPS_CPU_USABLE_UNKNOWN) <= 0 || + virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_QEMU, ppc64= Models, + ARRAY_CARDINALITY(ppc64Models), + VIR_DOMCAPS_CPU_USABLE_UNKNOWN) <= 0) + return -1; + } else if (ARCH_IS_S390(arch)) { + if (virQEMUCapsAddCPUDefinitions(caps, VIR_DOMAIN_VIRT_KVM, s390xM= odels, + ARRAY_CARDINALITY(s390xModels), + VIR_DOMCAPS_CPU_USABLE_UNKNOWN) <= 0) + return -1; + } + + return 0; +} + + +static int +testUpdateQEMUCaps(const struct testInfo *info, + virDomainObjPtr vm, + virCapsPtr caps) +{ + int ret =3D -1; + + if (!caps) + goto cleanup; + + virQEMUCapsSetArch(info->qemuCaps, vm->def->os.arch); + + virQEMUCapsInitQMPBasicArch(info->qemuCaps); + + if (testAddCPUModels(info->qemuCaps, + !!(info->flags & FLAG_SKIP_LEGACY_CPUS)) < 0) + goto cleanup; + + virQEMUCapsInitHostCPUModel(info->qemuCaps, caps->host.arch, + VIR_DOMAIN_VIRT_KVM); + virQEMUCapsInitHostCPUModel(info->qemuCaps, caps->host.arch, + VIR_DOMAIN_VIRT_QEMU); + + ret =3D 0; + + cleanup: + return ret; +} + + +static int +testCheckExclusiveFlags(int flags) +{ + virCheckFlags(FLAG_EXPECT_FAILURE | + FLAG_EXPECT_PARSE_ERROR | + FLAG_FIPS | + FLAG_REAL_CAPS | + FLAG_SKIP_LEGACY_CPUS | + 0, -1); + + VIR_EXCLUSIVE_FLAGS_RET(FLAG_REAL_CAPS, FLAG_SKIP_LEGACY_CPUS, -1); + return 0; +} + + +# define JSON_BUFSIZE (10*1024*1024) + + +static int +testCompareJSONToArgv(const void *data) +{ + struct testInfo *info =3D (void *) data; + char *json =3D NULL; + char *args =3D NULL; + char *migrateURI =3D NULL; + char *actualargv =3D NULL; + const char *suffix =3D info->suffix; + unsigned int flags =3D info->flags; + unsigned int parseFlags =3D info->parseFlags; + int ret =3D -1; + virDomainObjPtr vm =3D NULL; + virDomainChrSourceDef monitor_chr; + virConnectPtr conn; + char *log =3D NULL; + virCommandPtr cmd =3D NULL; + size_t i; + qemuDomainObjPrivatePtr priv =3D NULL; + VIR_AUTOFREE(char *) buf =3D NULL; + + memset(&monitor_chr, 0, sizeof(monitor_chr)); + + if (!(conn =3D virGetConnect())) + goto cleanup; + + if (!suffix) + suffix =3D ""; + + conn->secretDriver =3D &fakeSecretDriver; + conn->storageDriver =3D &fakeStorageDriver; + conn->nwfilterDriver =3D &fakeNWFilterDriver; + + virSetConnectInterface(conn); + virSetConnectNetwork(conn); + virSetConnectNWFilter(conn); + virSetConnectNodeDev(conn); + virSetConnectSecret(conn); + virSetConnectStorage(conn); + + if (virQEMUCapsGet(info->qemuCaps, QEMU_CAPS_ENABLE_FIPS)) + flags |=3D FLAG_FIPS; + + if (testCheckExclusiveFlags(info->flags) < 0) + goto cleanup; + + if (qemuTestCapsCacheInsert(driver.qemuCapsCache, info->qemuCaps) < 0) + goto cleanup; + + if (virAsprintf(&json, "%s/qemujson2argvdata/%s.json", + abs_srcdir, info->name) < 0 || + virAsprintf(&args, "%s/qemujson2argvdata/%s%s.args", + abs_srcdir, info->name, suffix) < 0) + goto cleanup; + + if (info->migrateFrom && + !(migrateURI =3D qemuMigrationDstGetURI(info->migrateFrom, + info->migrateFd))) + goto cleanup; + + if (!(vm =3D virDomainObjNew(driver.xmlopt))) + goto cleanup; + + if (virFileReadAll(json, JSON_BUFSIZE, &buf) < 0) + goto cleanup; + + parseFlags |=3D VIR_DOMAIN_DEF_PARSE_INACTIVE; + if (!(vm->def =3D virDomainDefParseJSONString(buf, driver.caps, driver= .xmlopt, + NULL, parseFlags))) { + if (flags & FLAG_EXPECT_PARSE_ERROR) + goto ok; + goto cleanup; + } + if (flags & FLAG_EXPECT_PARSE_ERROR) { + VIR_TEST_DEBUG("passed instead of expected parse error"); + goto cleanup; + } + priv =3D vm->privateData; + + if (virBitmapParse("0-3", &priv->autoNodeset, 4) < 0) + goto cleanup; + + if (!virDomainDefCheckABIStability(vm->def, vm->def, driver.xmlopt)) { + VIR_TEST_DEBUG("ABI stability check failed on %s", json); + goto cleanup; + } + + vm->def->id =3D -1; + + if (qemuProcessPrepareMonitorChr(&monitor_chr, priv->libDir) < 0) + goto cleanup; + + if (!(info->flags & FLAG_REAL_CAPS) && + testUpdateQEMUCaps(info, vm, driver.caps) < 0) + goto cleanup; + + log =3D virTestLogContentAndReset(); + VIR_FREE(log); + virResetLastError(); + + for (i =3D 0; i < vm->def->nhostdevs; i++) { + virDomainHostdevDefPtr hostdev =3D vm->def->hostdevs[i]; + + if (hostdev->mode =3D=3D VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + hostdev->source.subsys.type =3D=3D VIR_DOMAIN_HOSTDEV_SUBSYS_T= YPE_PCI && + hostdev->source.subsys.u.pci.backend =3D=3D VIR_DOMAIN_HOSTDEV= _PCI_BACKEND_DEFAULT) { + hostdev->source.subsys.u.pci.backend =3D VIR_DOMAIN_HOSTDEV_PC= I_BACKEND_KVM; + } + } + + if (vm->def->vsock) { + virDomainVsockDefPtr vsock =3D vm->def->vsock; + qemuDomainVsockPrivatePtr vsockPriv =3D + (qemuDomainVsockPrivatePtr)vsock->privateData; + + if (vsock->auto_cid =3D=3D VIR_TRISTATE_BOOL_YES) + vsock->guest_cid =3D 42; + + vsockPriv->vhostfd =3D 6789; + } + + if (vm->def->tpm) { + switch (vm->def->tpm->type) { + case VIR_DOMAIN_TPM_TYPE_EMULATOR: + VIR_FREE(vm->def->tpm->data.emulator.source.data.file.path); + if (VIR_STRDUP(vm->def->tpm->data.emulator.source.data.file.pa= th, + "/dev/test") < 0) + goto cleanup; + vm->def->tpm->data.emulator.source.type =3D VIR_DOMAIN_CHR_TYP= E_FILE; + break; + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + case VIR_DOMAIN_TPM_TYPE_LAST: + break; + } + } + + if (!(cmd =3D qemuProcessCreatePretendCmd(&driver, vm, migrateURI, + (flags & FLAG_FIPS), false, + VIR_QEMU_PROCESS_START_COLD)))= { + if (flags & FLAG_EXPECT_FAILURE) + goto ok; + goto cleanup; + } + if (flags & FLAG_EXPECT_FAILURE) { + VIR_TEST_DEBUG("passed instead of expected failure"); + goto cleanup; + } + + if (!(actualargv =3D virCommandToString(cmd, false))) + goto cleanup; + + if (virTestCompareToFile(actualargv, args) < 0) + goto cleanup; + + ret =3D 0; + + ok: + if (ret =3D=3D 0 && flags & FLAG_EXPECT_FAILURE) { + ret =3D -1; + VIR_TEST_DEBUG("Error expected but there wasn't any.\n"); + goto cleanup; + } + if (!virTestOOMActive()) { + if (flags & FLAG_EXPECT_FAILURE) { + if ((log =3D virTestLogContentAndReset())) + VIR_TEST_DEBUG("Got expected error: \n%s", log); + } + virResetLastError(); + ret =3D 0; + } + + cleanup: + VIR_FREE(log); + VIR_FREE(actualargv); + virDomainChrSourceDefClear(&monitor_chr); + virCommandFree(cmd); + virObjectUnref(vm); + virSetConnectSecret(NULL); + virSetConnectStorage(NULL); + virObjectUnref(conn); + VIR_FREE(migrateURI); + VIR_FREE(json); + VIR_FREE(args); + return ret; +} + +# define TEST_CAPS_PATH abs_srcdir "/qemucapabilitiesdata" + +typedef enum { + ARG_QEMU_CAPS, + ARG_GIC, + ARG_MIGRATE_FROM, + ARG_MIGRATE_FD, + ARG_FLAGS, + ARG_PARSEFLAGS, + ARG_CAPS_ARCH, + ARG_CAPS_VER, + ARG_END, +} testInfoArgName; + +static int +testInfoSetArgs(struct testInfo *info, + virHashTablePtr capslatest, ...) +{ + va_list argptr; + testInfoArgName argname; + virQEMUCapsPtr qemuCaps =3D NULL; + int gic =3D GIC_NONE; + char *capsarch =3D NULL; + char *capsver =3D NULL; + VIR_AUTOFREE(char *) capsfile =3D NULL; + int flag; + int ret =3D -1; + + va_start(argptr, capslatest); + argname =3D va_arg(argptr, testInfoArgName); + while (argname !=3D ARG_END) { + switch (argname) { + case ARG_QEMU_CAPS: + if (qemuCaps || !(qemuCaps =3D virQEMUCapsNew())) + goto cleanup; + + while ((flag =3D va_arg(argptr, int)) < QEMU_CAPS_LAST) + virQEMUCapsSet(qemuCaps, flag); + + /* Some tests are run with NONE capabilities, which is just + * another name for QEMU_CAPS_LAST. If that is the case the + * arguments look like this : + * + * ARG_QEMU_CAPS, NONE, QEMU_CAPS_LAST, ARG_END + * + * Fetch one argument more and if it is QEMU_CAPS_LAST then + * break from the switch() to force getting next argument + * in the line. If it is not QEMU_CAPS_LAST then we've + * fetched real ARG_* and we must process it. + */ + if ((flag =3D va_arg(argptr, int)) !=3D QEMU_CAPS_LAST) { + argname =3D flag; + continue; + } + + break; + + case ARG_GIC: + gic =3D va_arg(argptr, int); + break; + + case ARG_MIGRATE_FROM: + info->migrateFrom =3D va_arg(argptr, char *); + break; + + case ARG_MIGRATE_FD: + info->migrateFd =3D va_arg(argptr, int); + break; + + case ARG_FLAGS: + info->flags =3D va_arg(argptr, int); + break; + + case ARG_PARSEFLAGS: + info->parseFlags =3D va_arg(argptr, int); + break; + + case ARG_CAPS_ARCH: + capsarch =3D va_arg(argptr, char *); + break; + + case ARG_CAPS_VER: + capsver =3D va_arg(argptr, char *); + break; + + case ARG_END: + default: + fprintf(stderr, "Unexpected test info argument"); + goto cleanup; + } + + argname =3D va_arg(argptr, testInfoArgName); + } + + if (!!capsarch ^ !!capsver) { + fprintf(stderr, "ARG_CAPS_ARCH and ARG_CAPS_VER " + "must be specified together.\n"); + goto cleanup; + } + + if (qemuCaps && (capsarch || capsver)) { + fprintf(stderr, "ARG_QEMU_CAPS can not be combined with ARG_CAPS_A= RCH " + "or ARG_CAPS_VER\n"); + goto cleanup; + } + + if (!qemuCaps && capsarch && capsver) { + bool stripmachinealiases =3D false; + + if (STREQ(capsver, "latest")) { + if (VIR_STRDUP(capsfile, virHashLookup(capslatest, capsarch)) = < 0) + goto cleanup; + stripmachinealiases =3D true; + } else if (virAsprintf(&capsfile, "%s/caps_%s.%s.xml", + TEST_CAPS_PATH, capsver, capsarch) < 0) { + goto cleanup; + } + + if (!(qemuCaps =3D qemuTestParseCapabilitiesArch(virArchFromString= (capsarch), + capsfile))) { + goto cleanup; + } + + if (stripmachinealiases) + virQEMUCapsStripMachineAliases(qemuCaps); + info->flags |=3D FLAG_REAL_CAPS; + } + + if (!qemuCaps) { + fprintf(stderr, "No qemuCaps generated\n"); + goto cleanup; + } + VIR_STEAL_PTR(info->qemuCaps, qemuCaps); + + if (gic !=3D GIC_NONE && testQemuCapsSetGIC(info->qemuCaps, gic) < 0) + goto cleanup; + + ret =3D 0; + + cleanup: + virObjectUnref(qemuCaps); + va_end(argptr); + + return ret; +} + +static void +testInfoClear(struct testInfo *info) +{ + virObjectUnref(info->qemuCaps); +} + +# define FAKEROOTDIRTEMPLATE abs_builddir "/fakerootdir-XXXXXX" + +static int +mymain(void) +{ + int ret =3D 0, i; + char *fakerootdir; + const char *archs[] =3D { + "aarch64", + "ppc64", + "riscv64", + "s390x", + "x86_64", + }; + virHashTablePtr capslatest =3D NULL; + + if (VIR_STRDUP_QUIET(fakerootdir, FAKEROOTDIRTEMPLATE) < 0) { + fprintf(stderr, "Out of memory\n"); + abort(); + } + + if (!mkdtemp(fakerootdir)) { + fprintf(stderr, "Cannot create fakerootdir"); + abort(); + } + + setenv("LIBVIRT_FAKE_ROOT_DIR", fakerootdir, 1); + + /* Set the timezone because we are mocking the time() function. + * If we don't do that, then localtime() may return unpredictable + * results. In order to detect things that just work by a blind + * chance, we need to set an virtual timezone that no libvirt + * developer resides in. */ + if (setenv("TZ", "VIR00:30", 1) < 0) { + perror("setenv"); + return EXIT_FAILURE; + } + + if (qemuTestDriverInit(&driver) < 0) + return EXIT_FAILURE; + + driver.privileged =3D true; + + VIR_FREE(driver.config->defaultTLSx509certdir); + if (VIR_STRDUP_QUIET(driver.config->defaultTLSx509certdir, "/etc/pki/q= emu") < 0) + return EXIT_FAILURE; + VIR_FREE(driver.config->vncTLSx509certdir); + if (VIR_STRDUP_QUIET(driver.config->vncTLSx509certdir, "/etc/pki/libvi= rt-vnc") < 0) + return EXIT_FAILURE; + VIR_FREE(driver.config->spiceTLSx509certdir); + if (VIR_STRDUP_QUIET(driver.config->spiceTLSx509certdir, "/etc/pki/lib= virt-spice") < 0) + return EXIT_FAILURE; + VIR_FREE(driver.config->chardevTLSx509certdir); + if (VIR_STRDUP_QUIET(driver.config->chardevTLSx509certdir, "/etc/pki/l= ibvirt-chardev") < 0) + return EXIT_FAILURE; + VIR_FREE(driver.config->vxhsTLSx509certdir); + if (VIR_STRDUP_QUIET(driver.config->vxhsTLSx509certdir, "/etc/pki/libv= irt-vxhs/dummy,path") < 0) + return EXIT_FAILURE; + VIR_FREE(driver.config->nbdTLSx509certdir); + if (VIR_STRDUP_QUIET(driver.config->nbdTLSx509certdir, "/etc/pki/libvi= rt-nbd/dummy,path") < 0) + return EXIT_FAILURE; + + VIR_FREE(driver.config->hugetlbfs); + if (VIR_ALLOC_N(driver.config->hugetlbfs, 2) < 0) + return EXIT_FAILURE; + driver.config->nhugetlbfs =3D 2; + if (VIR_STRDUP(driver.config->hugetlbfs[0].mnt_dir, "/dev/hugepages2M"= ) < 0 || + VIR_STRDUP(driver.config->hugetlbfs[1].mnt_dir, "/dev/hugepages1G"= ) < 0) + return EXIT_FAILURE; + driver.config->hugetlbfs[0].size =3D 2048; + driver.config->hugetlbfs[0].deflt =3D true; + driver.config->hugetlbfs[1].size =3D 1048576; + driver.config->spiceTLS =3D 1; + if (VIR_STRDUP_QUIET(driver.config->spicePassword, "123456") < 0) + return EXIT_FAILURE; + VIR_FREE(driver.config->memoryBackingDir); + if (VIR_STRDUP_QUIET(driver.config->memoryBackingDir, "/var/lib/libvir= t/qemu/ram") < 0) + return EXIT_FAILURE; + VIR_FREE(driver.config->nvramDir); + if (VIR_STRDUP(driver.config->nvramDir, "/var/lib/libvirt/qemu/nvram")= < 0) + return EXIT_FAILURE; + + capslatest =3D virHashCreate(4, virHashValueFree); + if (!capslatest) + return EXIT_FAILURE; + + VIR_TEST_VERBOSE("\n"); + + for (i =3D 0; i < ARRAY_CARDINALITY(archs); ++i) { + char *cap =3D testQemuGetLatestCapsForArch(abs_srcdir "/qemucapabi= litiesdata", + archs[i], "xml"); + + if (!cap || virHashAddEntry(capslatest, archs[i], cap) < 0) + return EXIT_FAILURE; + + VIR_TEST_VERBOSE("latest caps for %s: %s\n", archs[i], cap); + } + + VIR_TEST_VERBOSE("\n"); + + virFileWrapperAddPrefix(SYSCONFDIR "/qemu/firmware", + abs_srcdir "/qemufirmwaredata/etc/qemu/firmwar= e"); + virFileWrapperAddPrefix(PREFIX "/share/qemu/firmware", + abs_srcdir "/qemufirmwaredata/usr/share/qemu/f= irmware"); + virFileWrapperAddPrefix("/home/user/.config/qemu/firmware", + abs_srcdir "/qemufirmwaredata/home/user/.confi= g/qemu/firmware"); + +/** + * The following set of macros allows testing of JSON -> argv conversion w= ith a + * real set of capabilities gathered from a real qemu copy. It is desired = to use + * these for positive test cases as it provides combinations of flags which + * can be met in real life. + * + * The capabilities are taken from the real capabilities stored in + * tests/qemucapabilitiesdata. + * + * It is suggested to use the DO_TEST_CAPS_LATEST macro which always takes= the + * most recent capability set. In cases when the new code would change beh= aviour + * the test cases should be forked using DO_TEST_CAPS_VER with the appropr= iate + * version. + */ +# define DO_TEST_INTERNAL(_name, _suffix, ...) \ + do { \ + static struct testInfo info =3D { \ + .name =3D _name, \ + .suffix =3D _suffix, \ + }; \ + if (testInfoSetArgs(&info, capslatest, \ + __VA_ARGS__, ARG_END) < 0) \ + return EXIT_FAILURE; \ + if (virTestRun("QEMU JSON-2-ARGV " _name _suffix, \ + testCompareJSONToArgv, &info) < 0) \ + ret =3D -1; \ + testInfoClear(&info); \ + } while (0) + +# define DO_TEST_CAPS_INTERNAL(name, arch, ver, ...) \ + DO_TEST_INTERNAL(name, "." arch "-" ver, \ + ARG_CAPS_ARCH, arch, \ + ARG_CAPS_VER, ver, \ + __VA_ARGS__) + +# define DO_TEST_CAPS_ARCH_VER(name, arch, ver) \ + DO_TEST_CAPS_INTERNAL(name, arch, ver, ARG_END) + +# define DO_TEST_CAPS_VER(name, ver) \ + DO_TEST_CAPS_ARCH_VER(name, "x86_64", ver) + +# define DO_TEST_CAPS_ARCH_LATEST_FULL(name, arch, ...) \ + DO_TEST_CAPS_INTERNAL(name, arch, "latest", __VA_ARGS__) + +# define DO_TEST_CAPS_ARCH_LATEST(name, arch) \ + DO_TEST_CAPS_ARCH_LATEST_FULL(name, arch, ARG_END) + +# define DO_TEST_CAPS_LATEST(name) \ + DO_TEST_CAPS_ARCH_LATEST(name, "x86_64") + +# define DO_TEST_CAPS_LATEST_FAILURE(name) \ + DO_TEST_CAPS_ARCH_LATEST_FULL(name, "x86_64", \ + ARG_FLAGS, FLAG_EXPECT_FAILURE) + +# define DO_TEST_CAPS_LATEST_PARSE_ERROR(name) \ + DO_TEST_CAPS_ARCH_LATEST_FULL(name, "x86_64", \ + ARG_FLAGS, FLAG_EXPECT_PARSE_ERROR) + + +# define DO_TEST_FULL(name, ...) \ + DO_TEST_INTERNAL(name, "", \ + __VA_ARGS__, QEMU_CAPS_LAST) + +/* All the following macros require an explicit QEMU_CAPS_* list + * at the end of the argument list, or the NONE placeholder. + * */ +# define DO_TEST(name, ...) \ + DO_TEST_FULL(name, \ + ARG_QEMU_CAPS, __VA_ARGS__) + +# define DO_TEST_GIC(name, gic, ...) \ + DO_TEST_FULL(name, \ + ARG_GIC, gic, \ + ARG_QEMU_CAPS, __VA_ARGS__) + +# define DO_TEST_FAILURE(name, ...) \ + DO_TEST_FULL(name, \ + ARG_FLAGS, FLAG_EXPECT_FAILURE, \ + ARG_QEMU_CAPS, __VA_ARGS__) + +# define DO_TEST_PARSE_ERROR(name, ...) \ + DO_TEST_FULL(name, \ + ARG_FLAGS, FLAG_EXPECT_PARSE_ERROR | FLAG_EXPECT_FAILURE,= \ + ARG_QEMU_CAPS, __VA_ARGS__) + +# define NONE QEMU_CAPS_LAST + + /* Unset or set all envvars here that are copied in qemudBuildCommandL= ine + * using ADD_ENV_COPY, otherwise these tests may fail due to unexpected + * values for these envvars */ + setenv("PATH", "/bin", 1); + setenv("USER", "test", 1); + setenv("LOGNAME", "test", 1); + setenv("HOME", "/home/test", 1); + unsetenv("TMPDIR"); + unsetenv("LD_PRELOAD"); + unsetenv("LD_LIBRARY_PATH"); + unsetenv("QEMU_AUDIO_DRV"); + unsetenv("SDL_AUDIODRIVER"); + + DO_TEST_CAPS_LATEST("tiny"); + + if (getenv("LIBVIRT_SKIP_CLEANUP") =3D=3D NULL) + virFileDeleteTree(fakerootdir); + + VIR_FREE(driver.config->nbdTLSx509certdir); + qemuTestDriverFree(&driver); + VIR_FREE(fakerootdir); + virHashFree(capslatest); + virFileWrapperClearPrefixes(); + + return ret =3D=3D 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIR_TEST_MAIN_PRELOAD(mymain, + abs_builddir "/.libs/qemuxml2argvmock.so", + abs_builddir "/.libs/virrandommock.so", + abs_builddir "/.libs/qemucpumock.so", + abs_builddir "/.libs/virpcimock.so") + +#else + +int main(void) +{ + return EXIT_AM_SKIP; +} + +#endif /* WITH_QEMU */ --=20 2.20.1 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list From nobody Sun Apr 28 14:37:19 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1554122388271479.354938150551; Mon, 1 Apr 2019 05:39:48 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 045FD3087941; Mon, 1 Apr 2019 12:39:47 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id CE0BD60141; Mon, 1 Apr 2019 12:39:46 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 8EFE6180338D; Mon, 1 Apr 2019 12:39:46 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id x31Cdjn2007341 for ; Mon, 1 Apr 2019 08:39:45 -0400 Received: by smtp.corp.redhat.com (Postfix) id 4597460FD9; Mon, 1 Apr 2019 12:39:45 +0000 (UTC) Received: from kinshicho.brq.redhat.com (unknown [10.43.2.212]) by smtp.corp.redhat.com (Postfix) with ESMTPS id BB87E60BE5 for ; Mon, 1 Apr 2019 12:39:43 +0000 (UTC) From: Andrea Bolognani To: libvir-list@redhat.com Date: Mon, 1 Apr 2019 14:39:25 +0200 Message-Id: <20190401123930.21777-4-abologna@redhat.com> In-Reply-To: <20190401123930.21777-1-abologna@redhat.com> References: <20190401123930.21777-1-abologna@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-loop: libvir-list@redhat.com Subject: [libvirt] [RFC PATCH 3/8] rpc: Add JSON to the list of fixups in gendispatch X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.45]); Mon, 01 Apr 2019 12:39:47 +0000 (UTC) Content-Type: text/plain; charset="utf-8" Without this tweak, the script will generate functions and code that look like virCromulentJsonFrobnicate() instead of the expected virCromulentJSONFrobnicate(), thus making it impossible to integrate the automatically generated functions with the hand-crafted ones. Signed-off-by: Andrea Bolognani --- src/rpc/gendispatch.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rpc/gendispatch.pl b/src/rpc/gendispatch.pl index ae3a42c4c1..3708d56810 100755 --- a/src/rpc/gendispatch.pl +++ b/src/rpc/gendispatch.pl @@ -74,6 +74,7 @@ sub fixup_name { $name =3D~ s/Scsi/SCSI/; $name =3D~ s/Wwn$/WWN/; $name =3D~ s/Dhcp$/DHCP/; + $name =3D~ s/Json/JSON/; =20 return $name; } --=20 2.20.1 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list From nobody Sun Apr 28 14:37:19 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 155412239568387.94007976307876; Mon, 1 Apr 2019 05:39:55 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 2B39530BC65E; Mon, 1 Apr 2019 12:39:54 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id E8E02103BAAD; Mon, 1 Apr 2019 12:39:53 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id AA25F3FB15; Mon, 1 Apr 2019 12:39:53 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id x31CdkD1007346 for ; Mon, 1 Apr 2019 08:39:46 -0400 Received: by smtp.corp.redhat.com (Postfix) id 1EC2B60262; Mon, 1 Apr 2019 12:39:46 +0000 (UTC) Received: from kinshicho.brq.redhat.com (unknown [10.43.2.212]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 9601560BE5 for ; Mon, 1 Apr 2019 12:39:45 +0000 (UTC) From: Andrea Bolognani To: libvir-list@redhat.com Date: Mon, 1 Apr 2019 14:39:26 +0200 Message-Id: <20190401123930.21777-5-abologna@redhat.com> In-Reply-To: <20190401123930.21777-1-abologna@redhat.com> References: <20190401123930.21777-1-abologna@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-loop: libvir-list@redhat.com Subject: [libvirt] [RFC PATCH 4/8] Introduce the virDomainDefineJSONFlags() public API X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.46]); Mon, 01 Apr 2019 12:39:54 +0000 (UTC) Content-Type: text/plain; charset="utf-8" This will be the entry point to JSON-based APIs for all non-internal users. Signed-off-by: Andrea Bolognani --- include/libvirt/libvirt-domain.h | 3 +++ src/driver-hypervisor.h | 6 +++++ src/libvirt-domain.c | 45 ++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 4 files changed, 55 insertions(+) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-dom= ain.h index 7d36820b5a..193c9ef24f 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -1777,6 +1777,9 @@ virDomainPtr virDomainDefineXML (virC= onnectPtr conn, virDomainPtr virDomainDefineXMLFlags (virConnectPtr conn, const char *xml, unsigned int flags); +virDomainPtr virDomainDefineJSONFlags (virConnectPtr conn, + const char *json, + unsigned int flags); int virDomainUndefine (virDomainPtr domain); =20 typedef enum { diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index 5315e33dde..4b2a0ca443 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -1328,6 +1328,11 @@ typedef int int *nparams, unsigned int flags); =20 +typedef virDomainPtr +(*virDrvDomainDefineJSONFlags)(virConnectPtr conn, + const char *json, + unsigned int flags); + =20 typedef struct _virHypervisorDriver virHypervisorDriver; typedef virHypervisorDriver *virHypervisorDriverPtr; @@ -1580,6 +1585,7 @@ struct _virHypervisorDriver { virDrvConnectBaselineHypervisorCPU connectBaselineHypervisorCPU; virDrvNodeGetSEVInfo nodeGetSEVInfo; virDrvDomainGetLaunchSecurityInfo domainGetLaunchSecurityInfo; + virDrvDomainDefineJSONFlags domainDefineJSONFlags; }; =20 =20 diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index be5b1f6740..7c2fcd9f09 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -6217,6 +6217,51 @@ virDomainDefineXMLFlags(virConnectPtr conn, const ch= ar *xml, unsigned int flags) } =20 =20 +/** + * virDomainDefineJSONFlags: + * @conn: pointer to the hypervisor connection + * @json: the JSON description for the domain, preferably in UTF-8 + * @flags: bitwise OR of the virDomainDefineFlags constants + * + * Defines a domain, but does not start it. + * This definition is persistent, until explicitly undefined with + * virDomainUndefine(). A previous definition for this domain would be + * overridden if it already exists. + * + * virDomainFree should be used to free the resources after the + * domain object is no longer needed. + * + * Returns NULL in case of error, a pointer to the domain otherwise + */ +virDomainPtr +virDomainDefineJSONFlags(virConnectPtr conn, + const char *json, + unsigned int flags) +{ + VIR_DEBUG("conn=3D%p, json=3D%s flags=3D0x%x", conn, NULLSTR(json), fl= ags); + + virResetLastError(); + + virCheckConnectReturn(conn, NULL); + virCheckReadOnlyGoto(conn->flags, error); + virCheckNonNullArgGoto(json, error); + + if (conn->driver->domainDefineJSONFlags) { + virDomainPtr ret; + ret =3D conn->driver->domainDefineJSONFlags(conn, json, flags); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(conn); + return NULL; +} + + /** * virDomainUndefine: * @domain: pointer to a defined domain diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index dbce3336d5..fe63b51cdb 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -817,6 +817,7 @@ LIBVIRT_4.10.0 { LIBVIRT_5.2.0 { global: virConnectGetStoragePoolCapabilities; + virDomainDefineJSONFlags; } LIBVIRT_4.10.0; =20 # .... define new API here using predicted next version number .... --=20 2.20.1 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list From nobody Sun Apr 28 14:37:19 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1554122428565507.88023904136924; Mon, 1 Apr 2019 05:40:28 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 4E16A3086233; Mon, 1 Apr 2019 12:40:27 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 1DE765C225; Mon, 1 Apr 2019 12:40:27 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id CDBEB3FB16; Mon, 1 Apr 2019 12:40:26 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id x31Cdmwd007362 for ; Mon, 1 Apr 2019 08:39:48 -0400 Received: by smtp.corp.redhat.com (Postfix) id CC0352B1BB; Mon, 1 Apr 2019 12:39:48 +0000 (UTC) Received: from kinshicho.brq.redhat.com (unknown [10.43.2.212]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 4F20C60BE5 for ; Mon, 1 Apr 2019 12:39:46 +0000 (UTC) From: Andrea Bolognani To: libvir-list@redhat.com Date: Mon, 1 Apr 2019 14:39:27 +0200 Message-Id: <20190401123930.21777-6-abologna@redhat.com> In-Reply-To: <20190401123930.21777-1-abologna@redhat.com> References: <20190401123930.21777-1-abologna@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-loop: libvir-list@redhat.com Subject: [libvirt] [RFC PATCH 5/8] remote: Implement virDomainDefineJSONFlags() support X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.42]); Mon, 01 Apr 2019 12:40:27 +0000 (UTC) Content-Type: text/plain; charset="utf-8" This allows us to use virDomainDefineJSONFlags() when connected to a remote hypervisor. Signed-off-by: Andrea Bolognani --- src/remote/remote_driver.c | 3 ++- src/remote/remote_protocol.x | 19 ++++++++++++++++++- src/remote_protocol-structs | 8 ++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 5c4dd41227..5c22d139d7 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8516,7 +8516,8 @@ static virHypervisorDriver hypervisor_driver =3D { .connectCompareHypervisorCPU =3D remoteConnectCompareHypervisorCPU, /*= 4.4.0 */ .connectBaselineHypervisorCPU =3D remoteConnectBaselineHypervisorCPU, = /* 4.4.0 */ .nodeGetSEVInfo =3D remoteNodeGetSEVInfo, /* 4.5.0 */ - .domainGetLaunchSecurityInfo =3D remoteDomainGetLaunchSecurityInfo /* = 4.5.0 */ + .domainGetLaunchSecurityInfo =3D remoteDomainGetLaunchSecurityInfo, /*= 4.5.0 */ + .domainDefineJSONFlags =3D remoteDomainDefineJSONFlags, /* 5.2.0 */ }; =20 static virNetworkDriver network_driver =3D { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 74be4b37d0..93bd916a7d 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1106,6 +1106,15 @@ struct remote_domain_define_xml_flags_ret { remote_nonnull_domain dom; }; =20 +struct remote_domain_define_json_flags_args { + remote_nonnull_string json; + unsigned int flags; +}; + +struct remote_domain_define_json_flags_ret { + remote_nonnull_domain dom; +}; + struct remote_domain_undefine_args { remote_nonnull_domain dom; }; @@ -6342,5 +6351,13 @@ enum remote_procedure { * @generate: both * @acl: connect:read */ - REMOTE_PROC_CONNECT_GET_STORAGE_POOL_CAPABILITIES =3D 403 + REMOTE_PROC_CONNECT_GET_STORAGE_POOL_CAPABILITIES =3D 403, + + /** + * @priority: high + * @generate: both + * @acl: domain:write + * @acl: domain:save + */ + REMOTE_PROC_DOMAIN_DEFINE_JSON_FLAGS =3D 404 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 768189c573..083e3f1e0d 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -715,6 +715,13 @@ struct remote_domain_define_xml_flags_args { struct remote_domain_define_xml_flags_ret { remote_nonnull_domain dom; }; +struct remote_domain_define_json_flags_args { + remote_nonnull_string json; + u_int flags; +}; +struct remote_domain_define_json_flags_ret { + remote_nonnull_domain dom; +}; struct remote_domain_undefine_args { remote_nonnull_domain dom; }; @@ -3385,4 +3392,5 @@ enum remote_procedure { REMOTE_PROC_CONNECT_LIST_ALL_NWFILTER_BINDINGS =3D 401, REMOTE_PROC_DOMAIN_SET_IOTHREAD_PARAMS =3D 402, REMOTE_PROC_CONNECT_GET_STORAGE_POOL_CAPABILITIES =3D 403, + REMOTE_PROC_DOMAIN_DEFINE_JSON_FLAGS =3D 404, }; --=20 2.20.1 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list From nobody Sun Apr 28 14:37:19 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1554122433415725.5452493697146; Mon, 1 Apr 2019 05:40:33 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id D1886307D979; Mon, 1 Apr 2019 12:40:31 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id A961460DB7; Mon, 1 Apr 2019 12:40:31 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 69017180338A; Mon, 1 Apr 2019 12:40:31 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id x31Cdnvr007368 for ; Mon, 1 Apr 2019 08:39:49 -0400 Received: by smtp.corp.redhat.com (Postfix) id A7A9A60BE5; Mon, 1 Apr 2019 12:39:49 +0000 (UTC) Received: from kinshicho.brq.redhat.com (unknown [10.43.2.212]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 298DB2AFBA for ; Mon, 1 Apr 2019 12:39:48 +0000 (UTC) From: Andrea Bolognani To: libvir-list@redhat.com Date: Mon, 1 Apr 2019 14:39:28 +0200 Message-Id: <20190401123930.21777-7-abologna@redhat.com> In-Reply-To: <20190401123930.21777-1-abologna@redhat.com> References: <20190401123930.21777-1-abologna@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-loop: libvir-list@redhat.com Subject: [libvirt] [RFC PATCH 6/8] qemu: Add virDomainDefineJSONFlags() support X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.48]); Mon, 01 Apr 2019 12:40:32 +0000 (UTC) Content-Type: text/plain; charset="utf-8" This allows us to use virDomainDefineJSONFlags() when connected to a QEMU-based hypervisor. Signed-off-by: Andrea Bolognani --- src/qemu/qemu_driver.c | 84 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 62d8d977c5..d973574290 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7720,6 +7720,89 @@ qemuDomainDefineXML(virConnectPtr conn, const char *= xml) return qemuDomainDefineXMLFlags(conn, xml, 0); } =20 +static virDomainPtr +qemuDomainDefineJSONFlags(virConnectPtr conn, + const char *json, + unsigned int flags) +{ + virQEMUDriverPtr driver =3D conn->privateData; + virDomainDefPtr def =3D NULL; + virDomainDefPtr oldDef =3D NULL; + virDomainObjPtr vm =3D NULL; + virDomainPtr dom =3D NULL; + virObjectEventPtr event =3D NULL; + virQEMUDriverConfigPtr cfg; + virCapsPtr caps =3D NULL; + unsigned int parse_flags =3D VIR_DOMAIN_DEF_PARSE_INACTIVE | + VIR_DOMAIN_DEF_PARSE_ABI_UPDATE; + + virCheckFlags(VIR_DOMAIN_DEFINE_VALIDATE, NULL); + + if (flags & VIR_DOMAIN_DEFINE_VALIDATE) + parse_flags |=3D VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA; + + cfg =3D virQEMUDriverGetConfig(driver); + + if (!(caps =3D virQEMUDriverGetCapabilities(driver, false))) + goto cleanup; + + if (!(def =3D virDomainDefParseJSONString(json, caps, driver->xmlopt, + NULL, parse_flags))) + goto cleanup; + + if (virXMLCheckIllegalChars("name", def->name, "\n") < 0) + goto cleanup; + + if (virDomainDefineJSONFlagsEnsureACL(conn, def) < 0) + goto cleanup; + + if (!(vm =3D virDomainObjListAdd(driver->domains, def, + driver->xmlopt, + 0, &oldDef))) + goto cleanup; + def =3D NULL; + + vm->persistent =3D 1; + + if (virDomainSaveConfig(cfg->configDir, driver->caps, + vm->newDef ? vm->newDef : vm->def) < 0) { + if (oldDef) { + /* There is backup so this VM was defined before. + * Just restore the backup. */ + VIR_INFO("Restoring domain '%s' definition", vm->def->name); + if (virDomainObjIsActive(vm)) + vm->newDef =3D oldDef; + else + vm->def =3D oldDef; + oldDef =3D NULL; + } else { + /* Brand new domain. Remove it */ + VIR_INFO("Deleting domain '%s'", vm->def->name); + vm->persistent =3D 0; + qemuDomainRemoveInactiveJob(driver, vm); + } + goto cleanup; + } + + event =3D virDomainEventLifecycleNewFromObj(vm, + VIR_DOMAIN_EVENT_DEFINED, + !oldDef ? + VIR_DOMAIN_EVENT_DEFINED_ADDED : + VIR_DOMAIN_EVENT_DEFINED_UPDATED); + + VIR_INFO("Creating domain '%s'", vm->def->name); + dom =3D virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id); + + cleanup: + virDomainDefFree(oldDef); + virDomainDefFree(def); + virDomainObjEndAPI(&vm); + virObjectEventStateQueue(driver->domainEventState, event); + virObjectUnref(caps); + virObjectUnref(cfg); + return dom; +} + static int qemuDomainUndefineFlags(virDomainPtr dom, unsigned int flags) @@ -22559,6 +22642,7 @@ static virHypervisorDriver qemuHypervisorDriver =3D= { .connectBaselineHypervisorCPU =3D qemuConnectBaselineHypervisorCPU, /*= 4.4.0 */ .nodeGetSEVInfo =3D qemuNodeGetSEVInfo, /* 4.5.0 */ .domainGetLaunchSecurityInfo =3D qemuDomainGetLaunchSecurityInfo, /* 4= .5.0 */ + .domainDefineJSONFlags =3D qemuDomainDefineJSONFlags, /* 5.2.0 */ }; =20 =20 --=20 2.20.1 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list From nobody Sun Apr 28 14:37:19 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1554122395470669.9058608467791; Mon, 1 Apr 2019 05:39:55 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 253AB3086236; Mon, 1 Apr 2019 12:39:54 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id E59585D9D4; Mon, 1 Apr 2019 12:39:53 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id A414218033A2; Mon, 1 Apr 2019 12:39:53 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id x31CdqlQ007383 for ; Mon, 1 Apr 2019 08:39:52 -0400 Received: by smtp.corp.redhat.com (Postfix) id 8CAAA60FD9; Mon, 1 Apr 2019 12:39:52 +0000 (UTC) Received: from kinshicho.brq.redhat.com (unknown [10.43.2.212]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 0FF6060BE5 for ; Mon, 1 Apr 2019 12:39:49 +0000 (UTC) From: Andrea Bolognani To: libvir-list@redhat.com Date: Mon, 1 Apr 2019 14:39:29 +0200 Message-Id: <20190401123930.21777-8-abologna@redhat.com> In-Reply-To: <20190401123930.21777-1-abologna@redhat.com> References: <20190401123930.21777-1-abologna@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-loop: libvir-list@redhat.com Subject: [libvirt] [RFC PATCH 7/8] virsh: Add JSON support to the 'define' subcommand X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.42]); Mon, 01 Apr 2019 12:39:54 +0000 (UTC) Content-Type: text/plain; charset="utf-8" In order not to break user expectations (and scripts!) we need to keep parsing input files as XML by default, so JSON support is implemented as opt-in through the new --json option; a brand new --xml option is also added for consistency. That said, if the name of the input file ends in .json it's fair to assume the contents are not going to be in XML format: we can use this heuristic to make virsh more user-friendly by not requiring the user to specify --json in this single case. Signed-off-by: Andrea Bolognani --- tools/virsh-domain.c | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index afcd0a5f8d..0ebea3fe13 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -8208,6 +8208,14 @@ static const vshCmdOptDef opts_define[] =3D { .type =3D VSH_OT_BOOL, .help =3D N_("validate the XML against the schema") }, + {.name =3D "xml", + .type =3D VSH_OT_BOOL, + .help =3D N_("Input file is in XML format") + }, + {.name =3D "json", + .type =3D VSH_OT_BOOL, + .help =3D N_("Input file is in JSON format") + }, {.name =3D NULL} }; =20 @@ -8220,6 +8228,9 @@ cmdDefine(vshControl *ctl, const vshCmd *cmd) char *buffer; unsigned int flags =3D 0; virshControlPtr priv =3D ctl->privData; + bool xmlInput; + + VSH_EXCLUSIVE_OPTIONS("xml", "json"); =20 if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0) return false; @@ -8227,13 +8238,30 @@ cmdDefine(vshControl *ctl, const vshCmd *cmd) if (vshCommandOptBool(cmd, "validate")) flags |=3D VIR_DOMAIN_DEFINE_VALIDATE; =20 + /* If the input filename ends with .json, it's fair to assume that + * the format is going to be JSON. We default to XML otherwise */ + xmlInput =3D true; + if (virStringHasCaseSuffix(from, ".json")) + xmlInput =3D false; + + /* User asked for a specific input format: override both defaults + * and automatic detection */ + if (vshCommandOptBool(cmd, "xml")) + xmlInput =3D true; + else if (vshCommandOptBool(cmd, "json")) + xmlInput =3D false; + if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) return false; =20 - if (flags) - dom =3D virDomainDefineXMLFlags(priv->conn, buffer, flags); - else - dom =3D virDomainDefineXML(priv->conn, buffer); + if (xmlInput) { + if (flags) + dom =3D virDomainDefineXMLFlags(priv->conn, buffer, flags); + else + dom =3D virDomainDefineXML(priv->conn, buffer); + } else { + dom =3D virDomainDefineJSONFlags(priv->conn, buffer, flags); + } VIR_FREE(buffer); =20 if (dom !=3D NULL) { --=20 2.20.1 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list From nobody Sun Apr 28 14:37:19 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1554122437610421.18544609705634; Mon, 1 Apr 2019 05:40:37 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id E78DD3082B6D; Mon, 1 Apr 2019 12:40:35 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id B2E0F5D73F; Mon, 1 Apr 2019 12:40:35 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 6AF9C3FB16; Mon, 1 Apr 2019 12:40:35 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id x31CdrPt007388 for ; Mon, 1 Apr 2019 08:39:53 -0400 Received: by smtp.corp.redhat.com (Postfix) id 6620460FD9; Mon, 1 Apr 2019 12:39:53 +0000 (UTC) Received: from kinshicho.brq.redhat.com (unknown [10.43.2.212]) by smtp.corp.redhat.com (Postfix) with ESMTPS id DDA7660BE5 for ; Mon, 1 Apr 2019 12:39:52 +0000 (UTC) From: Andrea Bolognani To: libvir-list@redhat.com Date: Mon, 1 Apr 2019 14:39:30 +0200 Message-Id: <20190401123930.21777-9-abologna@redhat.com> In-Reply-To: <20190401123930.21777-1-abologna@redhat.com> References: <20190401123930.21777-1-abologna@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-loop: libvir-list@redhat.com Subject: [libvirt] [RFC PATCH 8/8] news: Update for virDomainDefineJSONFlags() X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.45]); Mon, 01 Apr 2019 12:40:37 +0000 (UTC) Content-Type: text/plain; charset="utf-8" Signed-off-by: Andrea Bolognani --- docs/news.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/news.xml b/docs/news.xml index fcf8520132..76e8c90bf4 100644 --- a/docs/news.xml +++ b/docs/news.xml @@ -116,6 +116,17 @@ unable to saturate the network link. + + + Introduce the virDomainDefineJSONFlags() public API + + + This new public API is very similar in scope and use to the exis= ting + virDomainDefineXMLFlags() API, but it expects its i= nput + to be in JSON format rather than XML. Note that support for many + features of the XML-based API is still missing at the moment. + +
--=20 2.20.1 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list