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)