[libvirt] [PATCH V4 0/3] Testing libvirt XML -> libxl_domain_config conversion

This is version 4 of the work started by danpb: https://www.redhat.com/archives/libvir-list/2014-May/msg01102.html This series tests the conversion of libvirt XML to libxl_domain_config objects by the libvirt libxl driver. Changed in V4: - V3 patches 2-4 have been pushed - Patch 1 is unchanged from V3 - Patch 2 is new and adds tests for virJSONStringCompare - Cleanup of ignored context paths definition in patch 3 (was #5 in V3) Daniel P. Berrange (2): util: Introduce virJSONStringCompare for JSON doc comparisons libxl: Add a test suite for libxl option generator Jim Fehlig (1): tests: add tests for virJSONStringCompare configure.ac | 2 + src/libvirt_private.syms | 1 + src/util/virjson.c | 242 +++++++++++++++++++++++++++++++++ src/util/virjson.h | 16 +++ tests/Makefile.am | 25 +++- tests/jsontest.c | 63 ++++++++- tests/libxlxml2jsondata/basic-hvm.json | 217 +++++++++++++++++++++++++++++ tests/libxlxml2jsondata/basic-hvm.xml | 36 +++++ tests/libxlxml2jsondata/basic-pv.json | 163 ++++++++++++++++++++++ tests/libxlxml2jsondata/basic-pv.xml | 28 ++++ tests/libxlxml2jsontest.c | 228 +++++++++++++++++++++++++++++++ tests/virmocklibxl.c | 87 ++++++++++++ 12 files changed, 1102 insertions(+), 6 deletions(-) create mode 100644 tests/libxlxml2jsondata/basic-hvm.json create mode 100644 tests/libxlxml2jsondata/basic-hvm.xml create mode 100644 tests/libxlxml2jsondata/basic-pv.json create mode 100644 tests/libxlxml2jsondata/basic-pv.xml create mode 100644 tests/libxlxml2jsontest.c create mode 100644 tests/virmocklibxl.c -- 1.8.4.5

From: "Daniel P. Berrange" <berrange@redhat.com> Comparing JSON docs using strcmp is simple, but is not flexible as it is sensitive to whitespace used in the doc generation. When comparing objects it may also be desirable to treat the existance of keys in the actual object but not expected object as non-fatal. Introduce a virJSONStringCompare function which takes two strings representing expected and actual JSON docs and then does a DOM comparison. Comparison is controled with the ignore_contexts and flags parameters. No comparison is done on context paths specified in ignore_contexts. The VIR_JSON_COMPARE_IGNORE_EXPECTED_NULL flag can be used to ignore actual values that have changed from an expected value of null. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Signed-off-by: Jim Fehlig <jfehlig@suse.com> --- Beyond rebasing, unchanged from V3. src/libvirt_private.syms | 1 + src/util/virjson.c | 242 +++++++++++++++++++++++++++++++++++++++++++++++ src/util/virjson.h | 16 ++++ 3 files changed, 259 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index fdf4548..b0c0625 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1446,6 +1446,7 @@ virISCSIScanTargets; # util/virjson.h +virJSONStringCompare; virJSONValueArrayAppend; virJSONValueArrayGet; virJSONValueArraySize; diff --git a/src/util/virjson.c b/src/util/virjson.c index ec83b2f..73b71f4 100644 --- a/src/util/virjson.c +++ b/src/util/virjson.c @@ -47,6 +47,11 @@ VIR_LOG_INIT("util.json"); +VIR_ENUM_DECL(virJSONType) +VIR_ENUM_IMPL(virJSONType, VIR_JSON_TYPE_LAST, + "object", "array", "string", + "number", "boolean", "null") + typedef struct _virJSONParserState virJSONParserState; typedef virJSONParserState *virJSONParserStatePtr; struct _virJSONParserState { @@ -91,6 +96,7 @@ virJSONValueFree(virJSONValuePtr value) break; case VIR_JSON_TYPE_BOOLEAN: case VIR_JSON_TYPE_NULL: + case VIR_JSON_TYPE_LAST: break; } @@ -1107,6 +1113,204 @@ virJSONParserHandleEndArray(void *ctx) } +static bool +virJSONValueCompare(virJSONValuePtr expect, + virJSONValuePtr actual, + const char *context, + const char **ignore_contexts, + unsigned int flags) +{ + size_t i, j; + + if (expect->type != actual->type) { + if (expect->type == VIR_JSON_TYPE_NULL && + (flags & VIR_JSON_COMPARE_IGNORE_EXPECTED_NULL)) + return true; + + const char *expectType = virJSONTypeTypeToString(expect->type); + const char *actualType = virJSONTypeTypeToString(actual->type); + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Expected value type '%s' but got value type '%s' at '%s'"), + expectType, actualType, context); + return false; + } + + switch (expect->type) { + case VIR_JSON_TYPE_OBJECT: + /* Ensure actual data contains all expected data */ + for (i = 0; i < expect->data.object.npairs; i++) { + bool found = false; + char *childcontext; + + if (virAsprintf(&childcontext, "%s%s%s", + context, + STREQ(context, "/") ? "" : "/", + expect->data.object.pairs[i].key) < 0) + return false; + + /* Bypass paths we've been asked to ignore */ + if (ignore_contexts) { + bool ignored = false; + + j = 0; + while (ignore_contexts[j]) { + if (STREQ(childcontext, ignore_contexts[j])) { + ignored = true; + break; + } + j++; + } + + if (ignored) + continue; + } + + for (j = 0; j < actual->data.object.npairs; j++) { + if (STREQ(expect->data.object.pairs[i].key, + actual->data.object.pairs[j].key)) { + found = true; + break; + } + } + + if (!found) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Expected object key '%s' not found in actual object at '%s'"), + expect->data.object.pairs[i].key, context); + VIR_FREE(childcontext); + return false; + } + + if (!virJSONValueCompare(expect->data.object.pairs[i].value, + actual->data.object.pairs[j].value, + childcontext, + ignore_contexts, + flags)) { + VIR_FREE(childcontext); + return false; + } + VIR_FREE(childcontext); + } + + /* Ensure expected data contains all actual data */ + for (i = 0; i < actual->data.object.npairs; i++) { + bool found = false; + char *childcontext; + + if (virAsprintf(&childcontext, "%s%s%s", + context, + STREQ(context, "/") ? "" : "/", + actual->data.object.pairs[i].key) < 0) + return false; + + /* Bypass paths we've been asked to ignore */ + if (ignore_contexts) { + bool ignored = false; + + j = 0; + while (ignore_contexts[j]) { + if (STREQ(childcontext, ignore_contexts[j])) { + ignored = true; + break; + } + j++; + } + + if (ignored) + continue; + } + + for (j = 0; j < expect->data.object.npairs; j++) { + if (STREQ(actual->data.object.pairs[i].key, + expect->data.object.pairs[j].key)) { + found = true; + break; + } + } + + if (!found) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Actual object key '%s' not found in expected object at '%s'"), + actual->data.object.pairs[i].key, context); + VIR_FREE(childcontext); + return false; + } + + if (!virJSONValueCompare(actual->data.object.pairs[i].value, + expect->data.object.pairs[j].value, + childcontext, + ignore_contexts, + flags)) { + VIR_FREE(childcontext); + } + } + break; + + case VIR_JSON_TYPE_ARRAY: + if (expect->data.array.nvalues != + actual->data.array.nvalues) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Expected array size '%zu' but got size '%zu' at '%s'"), + expect->data.array.nvalues, + actual->data.array.nvalues, + context); + return false; + } + + for (i = 0; i < expect->data.array.nvalues; i++) { + if (!virJSONValueCompare(expect->data.array.values[i], + actual->data.array.values[i], + context, + ignore_contexts, + flags)) + return false; + } + break; + + case VIR_JSON_TYPE_STRING: + if (STRNEQ(expect->data.string, + actual->data.string)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Expected string value '%s' but got '%s' at '%s'"), + expect->data.string, actual->data.string, context); + return false; + } + break; + + case VIR_JSON_TYPE_NUMBER: + if (STRNEQ(expect->data.number, + actual->data.number)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Expected number value '%s' but got '%s' at '%s'"), + expect->data.number, actual->data.number, context); + return false; + } + break; + + case VIR_JSON_TYPE_BOOLEAN: + if (expect->data.boolean != + actual->data.boolean) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Expected bool value '%d' but got '%d' at '%s'"), + expect->data.boolean, actual->data.boolean, context); + return false; + } + break; + + case VIR_JSON_TYPE_NULL: + /* trivially equal */ + break; + + case VIR_JSON_TYPE_LAST: + /* nothing */ + break; + } + + return true; +} + + static const yajl_callbacks parserCallbacks = { virJSONParserHandleNull, virJSONParserHandleBoolean, @@ -1306,6 +1510,30 @@ virJSONValueToString(virJSONValuePtr object, } +bool +virJSONStringCompare(const char *expect, + const char *actual, + const char **ignore_contexts, + unsigned int flags) +{ + virJSONValuePtr expectVal = NULL; + virJSONValuePtr actualVal = NULL; + int ret = false; + + if (!(expectVal = virJSONValueFromString(expect))) + goto cleanup; + if (!(actualVal = virJSONValueFromString(actual))) + goto cleanup; + + ret = virJSONValueCompare(expectVal, actualVal, "/", ignore_contexts, flags); + + cleanup: + virJSONValueFree(expectVal); + virJSONValueFree(actualVal); + return ret; +} + + #else virJSONValuePtr virJSONValueFromString(const char *jsonstring ATTRIBUTE_UNUSED) @@ -1324,4 +1552,18 @@ virJSONValueToString(virJSONValuePtr object ATTRIBUTE_UNUSED, _("No JSON parser implementation is available")); return NULL; } + + +bool +virJSONStringCompare(const char *expect ATTRIBUTE_UNUSED, + const char *actual ATTRIBUTE_UNUSED, + const char **ignore_contexts ATTRIBUTE_UNUSED, + unsigned int flags) +{ + virCheckFlags(VIR_JSON_COMPARE_IGNORE_EXPECTED_NULL, false); + + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("No JSON parser implementation is available")); + return false; +} #endif diff --git a/src/util/virjson.h b/src/util/virjson.h index 9487729..5dc948f 100644 --- a/src/util/virjson.h +++ b/src/util/virjson.h @@ -34,6 +34,8 @@ typedef enum { VIR_JSON_TYPE_NUMBER, VIR_JSON_TYPE_BOOLEAN, VIR_JSON_TYPE_NULL, + + VIR_JSON_TYPE_LAST, } virJSONType; typedef struct _virJSONValue virJSONValue; @@ -141,4 +143,18 @@ virJSONValuePtr virJSONValueFromString(const char *jsonstring); char *virJSONValueToString(virJSONValuePtr object, bool pretty); +typedef enum { + /* + * when comparing two values, if their types are different, + * and the 'expected' value type is 'null', then this will + * be considered non-fatal. + */ + VIR_JSON_COMPARE_IGNORE_EXPECTED_NULL = (1 << 0), +} virJSONCompareFlags; + +bool virJSONStringCompare(const char *expect, + const char *actual, + const char **ignore_contexts, + unsigned int flags); + #endif /* __VIR_JSON_H_ */ -- 1.8.4.5

Signed-off-by: Jim Fehlig <jfehlig@suse.com> --- This patch is new to V4. tests/jsontest.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/tests/jsontest.c b/tests/jsontest.c index a2a42e3..5d5f619 100644 --- a/tests/jsontest.c +++ b/tests/jsontest.c @@ -12,6 +12,7 @@ struct testInfo { const char *doc; const char *expect; + const char **ignore_contexts; bool pass; }; @@ -131,22 +132,45 @@ testJSONAddRemove(const void *data) static int +testJSONCompare(const void *data) +{ + const struct testInfo *info = data; + bool ret; + + ret = virJSONStringCompare(info->expect, + info->doc, + info->ignore_contexts, + 0); + + if (ret == info->pass) + return 0; + else + return -1; +} + + +static int mymain(void) { int ret = 0; + const char *ignore_paths[] = { + "/dict1/key3", + "/key4", + NULL + }; -#define DO_TEST_FULL(name, cmd, doc, expect, pass) \ +#define DO_TEST_FULL(name, cmd, doc, expect, ignore_ctx, pass) \ do { \ - struct testInfo info = { doc, expect, pass }; \ + struct testInfo info = { doc, expect, ignore_ctx, pass }; \ if (virtTestRun(name, testJSON ## cmd, &info) < 0) \ ret = -1; \ } while (0) #define DO_TEST_PARSE(name, doc) \ - DO_TEST_FULL(name, FromString, doc, NULL, true) + DO_TEST_FULL(name, FromString, doc, NULL, NULL, true) #define DO_TEST_PARSE_FAIL(name, doc) \ - DO_TEST_FULL(name, FromString, doc, NULL, false) + DO_TEST_FULL(name, FromString, doc, NULL, NULL, false) DO_TEST_PARSE("Simple", "{\"return\": {}, \"id\": \"libvirt-1\"}"); @@ -188,9 +212,10 @@ mymain(void) DO_TEST_FULL("add and remove", AddRemove, "{\"name\": \"sample\", \"value\": true}", "{\"value\":true,\"newname\":\"foo\"}", + NULL, true); DO_TEST_FULL("add and remove", AddRemove, - "[ 1 ]", NULL, false); + "[ 1 ]", NULL, NULL, false); DO_TEST_PARSE("almost nothing", "[]"); @@ -214,6 +239,34 @@ mymain(void) "[ {[\"key1\", \"key2\"]: \"value\"} ]"); DO_TEST_PARSE_FAIL("object with unterminated key", "{ \"key:7 }"); + DO_TEST_FULL("Compare identical docs", Compare, + "{ \"key1\" : \"value1\", \"key2\" : \"value2\"}", + "{ \"key1\" : \"value1\", \"key2\" : \"value2\"}", + NULL, true); + + DO_TEST_FULL("Compare different docs", Compare, + "{\"dict1\" : { \"key1\" : \"value1\", \"key2\" : \"value2\", " + "\"key3\" : [\"elem1\", \"elemfoo\"]}}", + "{\"dict1\" : { \"key1\" : \"value1\", \"key2\" : \"value2\", " + "\"key3\" : [\"elem1\", \"elem2\"]}}", + NULL, false); + + DO_TEST_FULL("Compare docs and ignore differing contexts", Compare, + "{\"dict1\" : { \"key1\" : \"value1\", \"key2\" : \"value2\", " + "\"key3\" : \"valuefoo\"}, \"key4\" : \"valuebar\"}", + "{\"dict1\" : { \"key1\" : \"value1\", \"key2\" : \"value2\", " + "\"key3\" : \"value3\"}, \"key4\" : \"value4\"}", + ignore_paths, true); + + DO_TEST_FULL("Compare docs with insufficient ignored contexts", Compare, + "{\"dict1\" : { \"key1\" : \"value1\", \"key2\" : \"value2\", " + "\"key3\" : \"valuefoo\"}, \"key4\" : \"valuebar\", " + "\"key5\" : \"valuebaz\"}", + "{\"dict1\" : { \"key1\" : \"value1\", \"key2\" : \"value2\", " + "\"key3\" : \"value3\"}, \"key4\" : \"value4\", " + "\"key5\" : \"value5\"}", + ignore_paths, false); + return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } -- 1.8.4.5

From: "Daniel P. Berrange" <berrange@redhat.com> The libxl library allows a libxl_domain_config object to be serialized to a JSON string. Use this to allow testing of the XML -> libxl_domain_config conversion process Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Signed-off-by: Jim Fehlig <jfehlig@suse.com> --- V4: Cleanup definition of ignored context paths configure.ac | 2 + tests/Makefile.am | 25 +++- tests/libxlxml2jsondata/basic-hvm.json | 217 +++++++++++++++++++++++++++++++ tests/libxlxml2jsondata/basic-hvm.xml | 36 ++++++ tests/libxlxml2jsondata/basic-pv.json | 163 +++++++++++++++++++++++ tests/libxlxml2jsondata/basic-pv.xml | 28 ++++ tests/libxlxml2jsontest.c | 228 +++++++++++++++++++++++++++++++++ tests/virmocklibxl.c | 87 +++++++++++++ 8 files changed, 785 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index b4fb99a..80c331f 100644 --- a/configure.ac +++ b/configure.ac @@ -875,6 +875,8 @@ if test "$with_libxl" != "no" ; then AC_CHECK_LIB([xenlight], [libxl_ctx_alloc], [ with_libxl=yes LIBXL_LIBS="$LIBXL_LIBS -lxenlight -lxenctrl" + LIBS="$LIBS -lxenlight -lxenctrl" + AC_CHECK_FUNCS([libxl_domain_config_to_json]) ],[ if test "$with_libxl" = "yes"; then fail=1 diff --git a/tests/Makefile.am b/tests/Makefile.am index d6c3cfb..a8e4e88 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -84,6 +84,7 @@ EXTRA_DIST = \ domainsnapshotxml2xmlout \ fchostdata \ interfaceschemadata \ + libxlxml2jsondata \ lxcconf2xmldata \ lxcxml2xmldata \ lxcxml2xmloutdata \ @@ -222,6 +223,9 @@ if WITH_XEN test_programs += xml2sexprtest sexpr2xmltest \ xmconfigtest xencapstest statstest reconnect endif WITH_XEN +if WITH_LIBXL +test_programs += libxlxml2jsontest +endif WITH_LIBXL if WITH_QEMU test_programs += qemuxml2argvtest qemuxml2xmltest qemuxmlnstest \ qemuargv2xmltest qemuhelptest domainsnapshotxml2xmltest \ @@ -393,7 +397,9 @@ test_libraries += libqemumonitortestutils.la \ qemuxml2argvmock.la \ $(NULL) endif WITH_QEMU - +if WITH_LIBXL +test_libraries += virmocklibxl.la +endif WITH_LIBXL if WITH_BHYVE test_libraries += bhyvexml2argvmock.la endif WITH_BHYVE @@ -593,6 +599,23 @@ EXTRA_DIST += qemuxml2argvtest.c qemuxml2xmltest.c qemuargv2xmltest.c \ $(QEMUMONITORTESTUTILS_SOURCES) endif ! WITH_QEMU +if WITH_LIBXL +libxl_LDADDS = ../src/libvirt_driver_libxl_impl.la +libxl_LDADDS += $(LDADDS) + +libxlxml2jsontest_SOURCES = \ + libxlxml2jsontest.c testutilsxen.c testutilsxen.h \ + testutils.c testutils.h +libxlxml2jsontest_LDADD = $(libxl_LDADDS) $(LIBXML_LIBS) + +virmocklibxl_la_SOURCES = \ + virmocklibxl.c +virmocklibxl_la_CFLAGS = $(AM_CFLAGS) +virmocklibxl_la_LDFLAGS = -module -avoid-version \ + -rpath /evil/libtool/hack/to/force/shared/lib/creation + +endif WITH_LIBXL + if WITH_LXC lxc_LDADDS = ../src/libvirt_driver_lxc_impl.la diff --git a/tests/libxlxml2jsondata/basic-hvm.json b/tests/libxlxml2jsondata/basic-hvm.json new file mode 100644 index 0000000..b02e299 --- /dev/null +++ b/tests/libxlxml2jsondata/basic-hvm.json @@ -0,0 +1,217 @@ +{ + "c_info": { + "type": "hvm", + "hap": "<default>", + "oos": "<default>", + "ssidref": 0, + "name": "sles-hvm", + "uuid": "2147d599-9cc6-c0dc-92ab-4064b5446e9b", + "xsdata": { + + }, + "platformdata": { + + }, + "poolid": 0, + "run_hotplug_scripts": "<default>", + "pvh": "<default>", + "driver_domain": "<default>" + }, + "b_info": { + "max_vcpus": 4, + "avail_vcpus": [ + 0, + 1, + 2, + 3 + ], + "cpumap": [ + + ], + "nodemap": [ + + ], + "numa_placement": "<default>", + "tsc_mode": "default", + "max_memkb": 1048576, + "target_memkb": 1048576, + "video_memkb": -1, + "shadow_memkb": 12288, + "rtc_timeoffset": 0, + "exec_ssidref": 0, + "localtime": "<default>", + "disable_migrate": "<default>", + "cpuid": [ + + ], + "blkdev_start": null, + "device_model_version": "unknown", + "device_model_stubdomain": "<default>", + "device_model": null, + "device_model_ssidref": 0, + "extra": [ + + ], + "extra_pv": [ + + ], + "extra_hvm": [ + + ], + "sched_params": { + "sched": "unknown", + "weight": 1000, + "cap": -1, + "period": -1, + "slice": -1, + "latency": -1, + "extratime": -1 + }, + "ioports": [ + + ], + "irqs": [ + + ], + "iomem": [ + + ], + "claim_mode": "<default>", + "event_channels": 0, + "u": { + "firmware": null, + "kernel": null, + "cmdline": null, + "ramdisk": null, + "bios": "unknown", + "pae": "True", + "apic": "True", + "acpi": "True", + "acpi_s3": "<default>", + "acpi_s4": "<default>", + "nx": "<default>", + "viridian": "<default>", + "timeoffset": null, + "hpet": "<default>", + "vpt_align": "<default>", + "timer_mode": "unknown", + "nested_hvm": "<default>", + "smbios_firmware": null, + "acpi_firmware": null, + "nographic": "<default>", + "vga": { + "kind": "cirrus" + }, + "vnc": { + "enable": "True", + "listen": "0.0.0.0", + "passwd": null, + "display": 0, + "findunused": "False" + }, + "keymap": null, + "sdl": { + "enable": "<default>", + "opengl": "<default>", + "display": null, + "xauthority": null + }, + "spice": { + "enable": "<default>", + "port": 0, + "tls_port": 0, + "host": null, + "disable_ticketing": "<default>", + "passwd": null, + "agent_mouse": "<default>", + "vdagent": "<default>", + "clipboard_sharing": "<default>", + "usbredirection": 0 + }, + "gfx_passthru": "<default>", + "serial": null, + "boot": "c", + "usb": "<default>", + "usbversion": 0, + "usbdevice": null, + "soundhw": null, + "xen_platform_pci": "<default>", + "usbdevice_list": [ + + ], + "vendor_device": "none", + "watchdog": null, + "watchdog_action": null + } + }, + "disks": [ + { + "backend_domid": 0, + "backend_domname": null, + "pdev_path": "/var/lib/xen/images/sles-hvm.img", + "vdev": "hda", + "backend": "qdisk", + "format": "raw", + "script": null, + "removable": 1, + "readwrite": 1, + "is_cdrom": 0 + } + ], + "nics": [ + { + "backend_domid": 0, + "backend_domname": null, + "devid": 0, + "mtu": 0, + "model": null, + "mac": "00:16:3e:66:12:b4", + "ip": null, + "bridge": "br0", + "ifname": null, + "script": "/etc/xen/scripts/vif-bridge", + "nictype": "vif_ioemu", + "rate_bytes_per_interval": 0, + "rate_interval_usecs": 0, + "gatewaydev": null + } + ], + "pcidevs": [ + + ], + "vfbs": [ + { + "backend_domid": 0, + "backend_domname": null, + "devid": -1, + "vnc": { + "enable": "True", + "listen": "0.0.0.0", + "passwd": null, + "display": 0, + "findunused": "False" + }, + "sdl": { + "enable": "<default>", + "opengl": "<default>", + "display": null, + "xauthority": null + }, + "keymap": null + } + ], + "vkbs": [ + { + "backend_domid": 0, + "backend_domname": null, + "devid": -1 + } + ], + "vtpms": [ + + ], + "on_poweroff": "destroy", + "on_reboot": "restart", + "on_watchdog": "destroy", + "on_crash": "destroy" +} diff --git a/tests/libxlxml2jsondata/basic-hvm.xml b/tests/libxlxml2jsondata/basic-hvm.xml new file mode 100644 index 0000000..693a715 --- /dev/null +++ b/tests/libxlxml2jsondata/basic-hvm.xml @@ -0,0 +1,36 @@ +<domain type='xen'> + <name>sles-hvm</name> + <description>None</description> + <uuid>2147d599-9cc6-c0dc-92ab-4064b5446e9b</uuid> + <memory>1048576</memory> + <currentMemory>1048576</currentMemory> + <vcpu>4</vcpu> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <clock sync='utc'/> + <os> + <type>hvm</type> + <loader>/usr/lib/xen/boot/hvmloader</loader> + <boot dev='hd'/> + </os> + <features> + <apic/> + <acpi/> + <pae/> + </features> + <devices> + <emulator>/usr/lib/xen/bin/qemu-system-i386</emulator> + <disk type='file' device='disk'> + <driver name='qemu'/> + <source file='/var/lib/xen/images/sles-hvm.img'/> + <target dev='hda'/> + </disk> + <interface type='bridge'> + <source bridge='br0'/> + <mac address='00:16:3e:66:12:b4'/> + <script path='/etc/xen/scripts/vif-bridge'/> + </interface> + <graphics type='vnc' port='-1' autoport='yes' listen='0.0.0.0'/> + </devices> +</domain> diff --git a/tests/libxlxml2jsondata/basic-pv.json b/tests/libxlxml2jsondata/basic-pv.json new file mode 100644 index 0000000..326b705 --- /dev/null +++ b/tests/libxlxml2jsondata/basic-pv.json @@ -0,0 +1,163 @@ +{ + "c_info": { + "type": "pv", + "hap": "<default>", + "oos": "<default>", + "ssidref": 0, + "name": "sles11-pv", + "uuid": "039e9ee6-4a84-3055-4c81-8ba426ae2656", + "xsdata": { + + }, + "platformdata": { + + }, + "poolid": 0, + "run_hotplug_scripts": "<default>", + "pvh": "<default>", + "driver_domain": "<default>" + }, + "b_info": { + "max_vcpus": 4, + "avail_vcpus": [ + 0, + 1, + 2, + 3 + ], + "cpumap": [ + + ], + "nodemap": [ + + ], + "numa_placement": "<default>", + "tsc_mode": "default", + "max_memkb": 524288, + "target_memkb": 524288, + "video_memkb": -1, + "shadow_memkb": -1, + "rtc_timeoffset": 0, + "exec_ssidref": 0, + "localtime": "<default>", + "disable_migrate": "<default>", + "cpuid": [ + + ], + "blkdev_start": null, + "device_model_version": "unknown", + "device_model_stubdomain": "<default>", + "device_model": null, + "device_model_ssidref": 0, + "extra": [ + + ], + "extra_pv": [ + + ], + "extra_hvm": [ + + ], + "sched_params": { + "sched": "unknown", + "weight": 1000, + "cap": -1, + "period": -1, + "slice": -1, + "latency": -1, + "extratime": -1 + }, + "ioports": [ + + ], + "irqs": [ + + ], + "iomem": [ + + ], + "claim_mode": "<default>", + "event_channels": 0, + "u": { + "kernel": null, + "slack_memkb": -1, + "bootloader": "pygrub", + "bootloader_args": [ + + ], + "cmdline": null, + "ramdisk": null, + "e820_host": "<default>" + } + }, + "disks": [ + { + "backend_domid": 0, + "backend_domname": null, + "pdev_path": "/var/lib/xen/images/sles11-pv.img", + "vdev": "xvda", + "backend": "qdisk", + "format": "raw", + "script": null, + "removable": 1, + "readwrite": 1, + "is_cdrom": 0 + } + ], + "nics": [ + { + "backend_domid": 0, + "backend_domname": null, + "devid": 0, + "mtu": 0, + "model": null, + "mac": "00:16:3e:3e:86:60", + "ip": null, + "bridge": "br0", + "ifname": null, + "script": "/etc/xen/scripts/vif-bridge", + "nictype": "vif", + "rate_bytes_per_interval": 0, + "rate_interval_usecs": 0, + "gatewaydev": null + } + ], + "pcidevs": [ + + ], + "vfbs": [ + { + "backend_domid": 0, + "backend_domname": null, + "devid": -1, + "vnc": { + "enable": "True", + "listen": "0.0.0.0", + "passwd": null, + "display": 0, + "findunused": "False" + }, + "sdl": { + "enable": "<default>", + "opengl": "<default>", + "display": null, + "xauthority": null + }, + "keymap": null + } + ], + "vkbs": [ + { + "backend_domid": 0, + "backend_domname": null, + "devid": -1 + } + ], + "vtpms": [ + + ], + "on_poweroff": "destroy", + "on_reboot": "restart", + "on_watchdog": "destroy", + "on_crash": "destroy" +} diff --git a/tests/libxlxml2jsondata/basic-pv.xml b/tests/libxlxml2jsondata/basic-pv.xml new file mode 100644 index 0000000..109e657 --- /dev/null +++ b/tests/libxlxml2jsondata/basic-pv.xml @@ -0,0 +1,28 @@ +<domain type='xen'> + <name>sles11-pv</name> + <uuid>039e9ee6-4a84-3055-4c81-8ba426ae2656</uuid> + <memory>524288</memory> + <currentMemory>524288</currentMemory> + <vcpu>4</vcpu> + <bootloader>pygrub</bootloader> + <os> + <type arch='i686' machine='xenpv'>linux</type> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <disk type='file' device='disk'> + <driver name='qemu'/> + <source file='/var/lib/xen/images/sles11-pv.img'/> + <target dev='xvda'/> + </disk> + <interface type='bridge'> + <source bridge='br0'/> + <mac address='00:16:3e:3e:86:60'/> + <script path='/etc/xen/scripts/vif-bridge'/> + </interface> + <graphics type='vnc' port='-1' autoport='yes' listen='0.0.0.0'/> + </devices> +</domain> diff --git a/tests/libxlxml2jsontest.c b/tests/libxlxml2jsontest.c new file mode 100644 index 0000000..f427d3e --- /dev/null +++ b/tests/libxlxml2jsontest.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2014 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 + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <sys/types.h> +#include <fcntl.h> + +#include "testutils.h" + +#if defined(WITH_LIBXL) && defined(WITH_YAJL) && defined(HAVE_LIBXL_DOMAIN_CONFIG_TO_JSON) + +# include "internal.h" +# include "viralloc.h" +# include "libxl/libxl_conf.h" +# include "datatypes.h" +# include "virstring.h" +# include "virmock.h" +# include "virjson.h" +# include "testutilsxen.h" + +# define VIR_FROM_THIS VIR_FROM_XEN + +# if defined LIBXL_HAVE_BUILDINFO_HVM_VENDOR_DEVICE +# define VIR_LIBXL_VERSION 4004 +# elif defined LIBXL_HAVE_DOMAIN_NODEAFFINITY +# define VIR_LIBXL_VERSION 4003 +# else +# define VIR_LIBXL_VERSION 4002 +# endif + +static const char *abs_top_srcdir; +static virCapsPtr xencaps; +static const char *ignore_paths[] = { +# if VIR_LIBXL_VERSION < 4004 + "/c_info/pvh", + "/c_info/driver_domain", + "/b_info/device_model_version", + "/b_info/event_channels", + "/b_info/u/kernel", + "/b_info/u/cmdline", + "/b_info/u/ramdisk", + "/b_info/u/bios", + "/b_info/u/timer_mode", + "/b_info/u/vga/kind", + "/b_info/u/spice/vdagent", + "/b_info/u/spice/clipboard_sharing", + "/b_info/u/spice/usbredirection", + "/b_info/u/usbversion", + "/b_info/u/vendor_device", + "/on_watchdog", +# endif +# if VIR_LIBXL_VERSION < 4003 + "/b_info/nodemap", + "/b_info/exec_ssidref", + "/b_info/iomem", + "/b_info/claim_mode", + "/b_info/u/usbdevice_list", + "/disks/backend_domname", + "/nics/backend_domname", + "/vfbs/backend_domname", + "/vkbs/backend_domname", + "/vtpms", +# endif + NULL +}; + + +static int testCompareXMLToJSONFiles(const char *xml, + const char *cmdline) +{ + char *expectargv = NULL; + int ret = -1; + virDomainDefPtr vmdef = NULL; + virPortAllocatorPtr gports = NULL; + libxl_ctx *ctx = NULL; + libxl_domain_config config; + xentoollog_logger *log = NULL; + virDomainXMLOptionPtr xmlopt = NULL; + char *actualargv; + + libxl_domain_config_init(&config); + + if (!(log = (xentoollog_logger *)xtl_createlogger_stdiostream(stderr, XTL_DEBUG, 0))) + goto cleanup; + + if (libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, log) < 0) + goto cleanup; + + if (!(gports = virPortAllocatorNew("vnc", 5900, 6000, + VIR_PORT_ALLOCATOR_SKIP_BIND_CHECK))) + goto cleanup; + + if (!(xmlopt = libxlCreateXMLConf())) + goto cleanup; + + if (!(vmdef = virDomainDefParseFile(xml, xencaps, xmlopt, + 1 << VIR_DOMAIN_VIRT_XEN, + VIR_DOMAIN_XML_INACTIVE))) + goto cleanup; + + if (libxlBuildDomainConfig(gports, vmdef, ctx, &config) < 0) + goto cleanup; + + if (!(actualargv = libxl_domain_config_to_json(ctx, &config))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + "Failed to create JSON doc for xl config"); + goto cleanup; + } + + virtTestLoadFile(cmdline, &expectargv); + + if (!virJSONStringCompare(expectargv, actualargv, ignore_paths, + VIR_JSON_COMPARE_IGNORE_EXPECTED_NULL)) + goto cleanup; + + ret = 0; + + cleanup: + VIR_FREE(expectargv); + VIR_FREE(actualargv); + virDomainDefFree(vmdef); + virObjectUnref(gports); + virObjectUnref(xmlopt); + libxl_ctx_free(ctx); + libxl_domain_config_dispose(&config); + xtl_logger_destroy(log); + return ret; +} + + +struct testInfo { + const char *name; +}; + +static int +testCompareXMLToJSONHelper(const void *data) +{ + int ret = -1; + const struct testInfo *info = data; + char *xml = NULL; + char *args = NULL; + + if (virAsprintf(&xml, "%s/libxlxml2jsondata/%s.xml", + abs_srcdir, info->name) < 0 || + virAsprintf(&args, "%s/libxlxml2jsondata/%s.json", + abs_srcdir, info->name) < 0) + goto cleanup; + + ret = testCompareXMLToJSONFiles(xml, args); + + cleanup: + VIR_FREE(xml); + VIR_FREE(args); + return ret; +} + + +static int +mymain(void) +{ + int ret = 0; + + abs_top_srcdir = getenv("abs_top_srcdir"); + if (!abs_top_srcdir) + abs_top_srcdir = abs_srcdir "/.."; + + /* 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 ((xencaps = testXenCapsInit()) == NULL) + return EXIT_FAILURE; + +# define DO_TEST(name) \ + do { \ + static struct testInfo info = { \ + name, \ + }; \ + if (virtTestRun("LibXL XML-2-JSON " name, \ + testCompareXMLToJSONHelper, &info) < 0) \ + ret = -1; \ + } while (0) + + DO_TEST("basic-pv"); + DO_TEST("basic-hvm"); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/virmocklibxl.so") + +#else + +int main(void) +{ + return EXIT_AM_SKIP; +} + +#endif /* WITH_XEN && WITH_YAJL && HAVE_LIBXL_DOMAIN_CONFIG_TO_JSON */ diff --git a/tests/virmocklibxl.c b/tests/virmocklibxl.c new file mode 100644 index 0000000..bc4b53d --- /dev/null +++ b/tests/virmocklibxl.c @@ -0,0 +1,87 @@ +/* + * virmocklibxl.c: mocking of xenstore/libxs for libxl + * + * Copyright (C) 2014 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 + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#if defined(WITH_LIBXL) && defined(WITH_YAJL) +# include "virmock.h" +# include <sys/stat.h> +# include <unistd.h> +# include <libxl.h> +# include <xenstore.h> +# include <xenctrl.h> + +VIR_MOCK_IMPL_RET_VOID(xs_daemon_open, + struct xs_handle *) +{ + VIR_MOCK_REAL_INIT(xs_daemon_open); + return (void*)0x1; +} + +VIR_MOCK_IMPL_RET_ARGS(xc_interface_open, + xc_interface *, + xentoollog_logger *, logger, + xentoollog_logger *, dombuild_logger, + unsigned, open_flags) +{ + VIR_MOCK_REAL_INIT(xc_interface_open); + return (void*)0x1; +} + + +VIR_MOCK_STUB_RET_ARGS(xc_interface_close, + int, 0, + xc_interface *, handle) + +VIR_MOCK_STUB_VOID_ARGS(xs_daemon_close, + struct xs_handle *, handle) + +VIR_MOCK_IMPL_RET_ARGS(__xstat, int, + int, ver, + const char *, path, + struct stat *, sb) +{ + VIR_MOCK_REAL_INIT(__xstat); + + if (strstr(path, "xenstored.pid")) { + memset(sb, 0, sizeof(*sb)); + return 0; + } + + return real___xstat(ver, path, sb); +} + +VIR_MOCK_IMPL_RET_ARGS(stat, int, + const char *, path, + struct stat *, sb) +{ + VIR_MOCK_REAL_INIT(stat); + + if (strstr(path, "xenstored.pid")) { + memset(sb, 0, sizeof(*sb)); + return 0; + } + + return real_stat(path, sb); +} + +#endif /* WITH_LIBXL && WITH_YAJL */ -- 1.8.4.5
participants (1)
-
Jim Fehlig