diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 100deed1857182708c6472c3db03709966714342..6f298ac57de6cd2f970b80e18002c6d4ccffe729 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -416,6 +416,117 @@ qemuDomainSupportsNetdev(virDomainDefPtr def, return virQEMUCapsGet(qemuCaps, QEMU_CAPS_NETDEV); } + +static int +qemuBuildObjectCommandLinePropsInternal(const char *key, + const virJSONValue *value, + virBufferPtr buf, + bool nested) +{ + virJSONValuePtr elem; + virBitmapPtr bitmap = NULL; + ssize_t pos = -1; + ssize_t end; + size_t i; + + switch ((virJSONType) value->type) { + case VIR_JSON_TYPE_STRING: + virBufferAsprintf(buf, ",%s=%s", key, value->data.string); + break; + + case VIR_JSON_TYPE_NUMBER: + virBufferAsprintf(buf, ",%s=%s", key, value->data.number); + break; + + case VIR_JSON_TYPE_BOOLEAN: + if (value->data.boolean) + virBufferAsprintf(buf, ",%s=yes", key); + else + virBufferAsprintf(buf, ",%s=no", key); + + break; + + case VIR_JSON_TYPE_ARRAY: + if (nested) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("nested -object property arrays are not supported")); + return -1; + } + + if (virJSONValueGetArrayAsBitmap(value, &bitmap) == 0) { + while ((pos = virBitmapNextSetBit(bitmap, pos)) > -1) { + if ((end = virBitmapNextClearBit(bitmap, pos)) < 0) + end = virBitmapLastSetBit(bitmap) + 1; + + if (end - 1 > pos) { + virBufferAsprintf(buf, ",%s=%zd-%zd", key, pos, end - 1); + pos = end; + } else { + virBufferAsprintf(buf, ",%s=%zd", key, pos); + } + } + } else { + /* fallback, treat the array as a non-bitmap, adding the key + * for each member */ + for (i = 0; i < virJSONValueArraySize(value); i++) { + elem = virJSONValueArrayGet((virJSONValuePtr)value, i); + + /* recurse to avoid duplicating code */ + if (qemuBuildObjectCommandLinePropsInternal(key, elem, buf, + true) < 0) + return -1; + } + } + break; + + case VIR_JSON_TYPE_OBJECT: + case VIR_JSON_TYPE_NULL: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("NULL and OBJECT JSON types can't be converted to " + "commandline string")); + return -1; + } + + virBitmapFree(bitmap); + return 0; +} + + +static int +qemuBuildObjectCommandLineProps(const char *key, + const virJSONValue *value, + void *opaque) +{ + return qemuBuildObjectCommandLinePropsInternal(key, value, opaque, false); +} + + +char * +qemuBuildObjectCommandlineFromJSON(const char *type, + const char *alias, + virJSONValuePtr props) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *ret = NULL; + + virBufferAsprintf(&buf, "%s,id=%s", type, alias); + + if (virJSONValueObjectForeachKeyValue(props, + qemuBuildObjectCommandLineProps, + &buf) < 0) + goto cleanup; + + if (virBufferCheckError(&buf) < 0) + goto cleanup; + + ret = virBufferContentAndReset(&buf); + + cleanup: + virBufferFreeAndReset(&buf); + return ret; +} + + /** * qemuOpenVhostNet: * @def: domain definition diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 8eaf1e44f43f765670f8a356cfb7a638b436ca80..ae36bd8d2cecd322051ed98568e05a4f72a5335c 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -69,6 +69,10 @@ struct _qemuBuildCommandLineCallbacks { extern qemuBuildCommandLineCallbacks buildCommandLineCallbacks; +char *qemuBuildObjectCommandlineFromJSON(const char *type, + const char *alias, + virJSONValuePtr props); + virCommandPtr qemuBuildCommandLine(virConnectPtr conn, virQEMUDriverPtr driver, virDomainDefPtr def, diff --git a/tests/Makefile.am b/tests/Makefile.am index 1d838a5fe5d3d292b5e108a28352ca9032854fbc..938270ccbf95ff44a96bead94f106e310907c48a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -237,7 +237,8 @@ if WITH_QEMU test_programs += qemuxml2argvtest qemuxml2xmltest qemuxmlnstest \ qemuargv2xmltest qemuhelptest domainsnapshotxml2xmltest \ qemumonitortest qemumonitorjsontest qemuhotplugtest \ - qemuagenttest qemucapabilitiestest qemucaps2xmltest + qemuagenttest qemucapabilitiestest qemucaps2xmltest \ + qemucommandutiltest endif WITH_QEMU if WITH_LXC @@ -586,6 +587,14 @@ qemucapabilitiestest_SOURCES = \ qemucapabilitiestest_LDADD = libqemumonitortestutils.la \ $(qemu_LDADDS) $(LDADDS) +qemucommandutiltest_SOURCES = \ + qemucommandutiltest.c \ + testutils.c testutils.h \ + testutilsqemu.c testutilsqemu.h \ + $(NULL) +qemucommandutiltest_LDADD = libqemumonitortestutils.la \ + $(qemu_LDADDS) $(LDADDS) + qemucaps2xmltest_SOURCES = \ qemucaps2xmltest.c \ testutils.c testutils.h \ @@ -616,7 +625,7 @@ EXTRA_DIST += qemuxml2argvtest.c qemuxml2xmltest.c qemuargv2xmltest.c \ qemumonitortest.c testutilsqemu.c testutilsqemu.h \ qemumonitorjsontest.c qemuhotplugtest.c \ qemuagenttest.c qemucapabilitiestest.c \ - qemucaps2xmltest.c \ + qemucaps2xmltest.c qemucommandutiltest.c \ $(QEMUMONITORTESTUTILS_SOURCES) endif ! WITH_QEMU diff --git a/tests/qemucommandutiltest.c b/tests/qemucommandutiltest.c new file mode 100644 index 0000000000000000000000000000000000000000..8c52f02d18ac85cfb650ee62090dc77ae4ab301e --- /dev/null +++ b/tests/qemucommandutiltest.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + */ + +#include + +#include "qemu/qemu_command.h" +#include "util/virjson.h" +#include "testutils.h" +#include "testutilsqemu.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +typedef struct +{ + const char *props; + const char *expectprops; +} testQemuCommandBuildObjectFromJSONData; + +static int +testQemuCommandBuildObjectFromJSON(const void *opaque) +{ + const testQemuCommandBuildObjectFromJSONData *data = opaque; + virJSONValuePtr val = NULL; + char *expect = NULL; + char *result = NULL; + int ret = -1; + + if (!(val = virJSONValueFromString(data->props))) { + fprintf(stderr, "Failed to parse JSON string '%s'", data->props); + return -1; + } + + if (virAsprintf(&expect, "testobject,id=testalias%s%s", + data->expectprops ? "," : "", + data->expectprops ? data->expectprops : "") < 0) + return -1; + + result = qemuBuildObjectCommandlineFromJSON("testobject", + "testalias", + val); + + if (STRNEQ_NULLABLE(expect, result)) { + fprintf(stderr, "\nFailed to create object string. " + "\nExpected:\n'%s'\nGot:\n'%s'", + NULLSTR(expect), NULLSTR(result)); + goto cleanup; + } + + ret = 0; + cleanup: + virJSONValueFree(val); + VIR_FREE(result); + return ret; +} + +static int +mymain(void) +{ + int ret = 0; + testQemuCommandBuildObjectFromJSONData data1; + +#if !WITH_YAJL + fputs("libvirt not compiled with yajl, skipping this test\n", stderr); + return EXIT_AM_SKIP; +#endif + + virtTestCounterReset("testQemuCommandBuildObjectFromJSON"); + +#define DO_TEST_COMMAND_OBJECT_FROM_JSON(PROPS, EXPECT) \ + do { \ + data1.props = PROPS; \ + data1.expectprops = EXPECT; \ + if (virtTestRun(virtTestCounterNext(), \ + testQemuCommandBuildObjectFromJSON, \ + &data1) < 0) \ + ret = -1; \ + } while (0) + + DO_TEST_COMMAND_OBJECT_FROM_JSON("{}", NULL); + DO_TEST_COMMAND_OBJECT_FROM_JSON("{\"string\":\"qwer\"}", "string=qwer"); + DO_TEST_COMMAND_OBJECT_FROM_JSON("{\"number\":1234}", "number=1234"); + DO_TEST_COMMAND_OBJECT_FROM_JSON("{\"boolean\":true}", "boolean=yes"); + DO_TEST_COMMAND_OBJECT_FROM_JSON("{\"boolean\":false}", "boolean=no"); + DO_TEST_COMMAND_OBJECT_FROM_JSON("{\"bitmap\":[]}", NULL); + DO_TEST_COMMAND_OBJECT_FROM_JSON("{\"bitmap\":[0]}", "bitmap=0"); + DO_TEST_COMMAND_OBJECT_FROM_JSON("{\"bitmap\":[1,3,5]}", + "bitmap=1,bitmap=3,bitmap=5"); + DO_TEST_COMMAND_OBJECT_FROM_JSON("{\"bitmap\":[0,1,2,3]}", "bitmap=0-3"); + DO_TEST_COMMAND_OBJECT_FROM_JSON("{\"bitmap\":[1,2,3,5]}", + "bitmap=1-3,bitmap=5"); + DO_TEST_COMMAND_OBJECT_FROM_JSON("{\"bitmap\":[1,2,3,5,7,8,9]}", + "bitmap=1-3,bitmap=5,bitmap=7-9"); + DO_TEST_COMMAND_OBJECT_FROM_JSON("{\"array\":[\"bleah\",\"qwerty\",1]}", + "array=bleah,array=qwerty,array=1"); + DO_TEST_COMMAND_OBJECT_FROM_JSON("{\"boolean\":true,\"hyphen-name\":1234,\"some_string\":\"bleah\"}", + "boolean=yes,hyphen-name=1234,some_string=bleah"); + + return ret; + +} + +VIRT_TEST_MAIN(mymain)