]> xenbits.xensource.com Git - people/iwj/ring3-xl-test.git/commitdiff
Initial commit
authorEuan Harris <euan.harris@citrix.com>
Wed, 3 Jun 2015 15:32:13 +0000 (15:32 +0000)
committerEuan Harris <euan.harris@citrix.com>
Wed, 3 Jun 2015 15:32:13 +0000 (16:32 +0100)
Signed-off-by: Euan Harris <euan.harris@citrix.com>
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
testcase.c [new file with mode: 0644]
testcase.h [new file with mode: 0644]
thread_test.c [new file with mode: 0644]
thread_test.h [new file with mode: 0644]
thread_test_test.c [new file with mode: 0644]
xl_event_test.c [new file with mode: 0644]
xl_eventloop.c [new file with mode: 0644]
xl_eventloop.h [new file with mode: 0644]
xlu_test.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..a4b0580
--- /dev/null
@@ -0,0 +1,3 @@
+*.o
+*.d
+xl_event_test
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..5a595f7
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,15 @@
+SRCS = thread_test.c xl_eventloop.c testcase.c xl_event_test.c
+CFLAGS = -Wall -Wextra -Werror -pedantic -g
+LDFLAGS = -pthread -lxenlight
+
+all: xl_event_test
+xl_event_test: $(SRCS:.c=.o)
+
+%.d: %.c
+       $(CC) -M $< > $@
+
+.PHONY: clean
+clean:
+       rm -f $(SRCS:.c=.o)  $(SRCS:.c=.d) xl_event_test
+
+-include $(SRCS:.c=.d)
diff --git a/testcase.c b/testcase.c
new file mode 100644 (file)
index 0000000..61f2085
--- /dev/null
@@ -0,0 +1,29 @@
+#include <libxl.h>
+#include "thread_test.h"
+#include "xl_eventloop.h"
+
+void *
+testcase(struct test *tc)
+{
+       uint32_t domid;
+       libxl_domain_config dc;
+
+       printf("thread started\n");
+
+       init_domain_config(&dc, "badger", "/root/vmlinuz-4.0.4-301.fc22.x86_64");
+       do_domain_create(tc, &dc, &domid);
+       wait_for(tc, EV_LIBXL_CALLBACK);  /* need to be able to distingish out callback from others */
+       libxl_domain_config_dispose(&dc);
+       printf("domain %d created\n", domid);
+
+       libxl_domain_unpause(tc->ctx, domid);
+       printf("domain %d unpaused\n", domid);
+
+       wait_for_n(tc, EV_EVENTLOOP, 10);
+       do_domain_suspend(tc, domid);
+       wait_for(tc, EV_LIBXL_CALLBACK);
+       printf("domain %d suspended\n", domid);
+
+       pthread_exit(NULL);
+}
+
diff --git a/testcase.h b/testcase.h
new file mode 100644 (file)
index 0000000..5d3ef8a
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __TESTCASE_H
+#define __TESTCASE_H
+
+void *testcase(struct test *tc);
+
+#endif /* __TESTCASE_H */
diff --git a/thread_test.c b/thread_test.c
new file mode 100644 (file)
index 0000000..b3b5970
--- /dev/null
@@ -0,0 +1,117 @@
+#include <assert.h>
+#include <pthread.h>
+#include <stdio.h>
+
+#include "thread_test.h"
+#include "xl_eventloop.h"
+
+
+int
+test_spawn(struct test *tc, xentoollog_logger *logger,
+           void *(*fn)(struct test *))
+{
+       /* Initialize the test structure */
+       libxl_ctx_alloc(&tc->ctx, LIBXL_VERSION, 0, logger);
+       register_callbacks(tc);
+
+       /* Initialize the mailbox */
+       pthread_mutex_init(&tc->mailbox_lock, NULL);
+       pthread_cond_init(&tc->producer_cv, NULL);
+       pthread_cond_init(&tc->consumer_cv, NULL);
+       tc->mailbox.type = EV_NONE;
+
+       /* Spawn a thread to run the test case */
+       return pthread_create(&tc->thread, NULL,
+                              (void *(*)(void *)) fn, tc);
+}
+
+
+void
+test_destroy(struct test *tc) {
+       pthread_mutex_destroy(&tc->mailbox_lock);
+       pthread_cond_destroy(&tc->producer_cv);
+       pthread_cond_destroy(&tc->consumer_cv);
+       libxl_ctx_free(tc->ctx);
+}
+
+
+void
+send_event(struct test *tc, struct event ev)
+{
+       pthread_mutex_lock(&tc->mailbox_lock);
+       while (tc->mailbox.type != EV_NONE) {
+               pthread_cond_wait(&tc->producer_cv, &tc->mailbox_lock);
+       }
+       tc->mailbox = ev;
+       pthread_cond_signal(&tc->consumer_cv);
+       pthread_mutex_unlock(&tc->mailbox_lock);
+}
+
+
+void
+recv_event(struct test *tc, struct event *ev)
+{
+       pthread_mutex_lock(&tc->mailbox_lock);
+       while (tc->mailbox.type == EV_NONE) {
+               pthread_cond_wait(&tc->consumer_cv, &tc->mailbox_lock);
+       }
+       *ev = tc->mailbox;
+       tc->mailbox.type = EV_NONE;
+       pthread_cond_signal(&tc->producer_cv);
+       pthread_mutex_unlock(&tc->mailbox_lock);
+}
+
+
+/* Wait until the an event matching the expected mask is posted.
+   Ignores and discards any non-matching events received in the meantime. */
+void
+wait_for(struct test *tc, enum event_type expected)
+{
+       struct event received;
+       do {
+               recv_event(tc, &received);
+       } while(!(received.type & expected));
+}
+
+
+/* Wait until the the specified number of the expected events are posted.
+   Ignores and discards any other events received in the meantime. */
+void
+wait_for_n(struct test *tc, enum event_type expected, int count)
+{
+       while(count--) {
+               wait_for(tc, expected);
+       }
+}
+
+
+void
+send_fd_event(struct test *tc, int fd)
+{
+       struct event ev;
+       ev.type = EV_FD_EVENT;
+       ev.u.fd = fd;
+       send_event(tc, ev);
+       printf("-> EV_FD_EVENT\n");
+}
+
+
+void
+send_libxl_callback_event(struct test *tc)
+{
+       struct event ev;
+       ev.type = EV_LIBXL_CALLBACK;
+       send_event(tc, ev);
+       printf("-> EV_LIBXL_CALLBACK\n");
+}
+
+
+void
+send_eventloop(struct test *tc)
+{
+       struct event ev;
+       ev.type = EV_EVENTLOOP;
+       send_event(tc, ev);
+       printf("-> EV_EVENTLOOP_TICK\n");
+}
+
diff --git a/thread_test.h b/thread_test.h
new file mode 100644 (file)
index 0000000..26247aa
--- /dev/null
@@ -0,0 +1,75 @@
+#ifndef __THREAD_TEST_H
+#define __THREAD_TEST_H
+
+#include <libxl.h>
+#include <pthread.h>
+
+enum event_type {
+       EV_NONE                 = 0x0000,
+       EV_TEST_START           = 0x0001,
+       EV_LIBXL_CALLBACK       = 0x0002,
+       EV_FD_EVENT             = 0x0004,
+       EV_EVENTLOOP            = 0x0008,
+       EV_ANY                  = 0xffff
+};
+
+
+struct event {
+       enum event_type type;
+       union {
+               int fd;
+       } u;
+};
+
+struct test {
+       /*
+         * Test task structure.
+         */
+
+       /*
+         * Pointer to the libxl context structure.
+         * The memory which this points to is allocated and
+         * owned by libxl.
+         */
+       libxl_ctx *ctx;
+
+       /*
+         * Structures containing the hooks which libxl
+         * uses to register its timers and its interest in
+         * file descriptors.
+         * These structures must outlive the libxl context
+         * in which they are used, so it makes sense to
+         * keep them here alongside the ctx pointer.
+         */
+       libxl_event_hooks xl_ev_hooks;
+       libxl_osevent_hooks xl_os_ev_hooks;
+
+       /*
+        * Structure defining per-function callbacks.
+        * Must outlive the asynchronous call which uses it.
+         */
+       libxl_asyncop_how ao_how;
+
+       /* The thread running the test */
+       pthread_t thread;
+
+       /* Test thread's mailbox and locks */
+       pthread_mutex_t mailbox_lock;
+       pthread_cond_t producer_cv;
+       pthread_cond_t consumer_cv;
+       struct event mailbox;
+};
+
+
+int test_spawn(struct test *tc, xentoollog_logger *logger,
+               void *(*fn)(struct test *));
+void test_destroy(struct test *tc);
+void send_event(struct test *tc, struct event ev);
+void recv_event(struct test *tc, struct event *ev);
+void wait_for(struct test *tc, enum event_type expected);
+void wait_for_n(struct test *tc, enum event_type expected, int count);
+void send_fd_event(struct test *tc, int fd);
+void send_libxl_callback_event(struct test *tc);
+void send_eventloop(struct test *tc);
+
+#endif /* __THREAD_TEST_H */
diff --git a/thread_test_test.c b/thread_test_test.c
new file mode 100644 (file)
index 0000000..2233bff
--- /dev/null
@@ -0,0 +1,24 @@
+int
+main(int argc __attribute__((unused)), 
+     char **argv __attribute__((unused)))
+{
+       struct test t;
+       test_init(&t);
+       test_spawn(&t, testcase);
+
+       send_fd_event(&t, 0);
+       send_fd_event(&t, 1);
+       send_fd_event(&t, 2);
+       send_fd_event(&t, 3);
+       send_fd_event(&t, 4);
+
+       send_libxl_callback_event(&t);
+       send_libxl_callback_event(&t);
+
+       send_eventloop(&t);
+
+       pthread_join(t.thread, NULL);
+       test_destroy(&t);
+       pthread_exit(NULL);
+}
+
diff --git a/xl_event_test.c b/xl_event_test.c
new file mode 100644 (file)
index 0000000..3b71d0e
--- /dev/null
@@ -0,0 +1,76 @@
+#include <libxl_event.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <unistd.h>
+
+#include <sys/select.h>
+
+#include "xl_eventloop.h"
+#include "thread_test.h"
+#include "testcase.h"
+
+int
+main(int argc __attribute__((unused)), char **argv __attribute__((unused)))
+{
+       struct test t;
+       xentoollog_logger *logger;
+
+       init_pollfds(pollfds, NUM_POLL_FDS);
+
+       logger = (xentoollog_logger*)
+               xtl_createlogger_stdiostream(stderr, XTL_PROGRESS,  0);
+
+       if (!!test_spawn(&t, logger, testcase)) {
+               perror("Failed to spawn test thread");
+               exit(EXIT_FAILURE);
+       }
+
+       while (1) {
+               int i, rc;
+
+               rc = poll(pollfds, NUM_POLL_FDS, 2000);
+               if (rc == 0) {
+                       send_eventloop(&t);
+                       continue;
+               }
+
+               for (i = 0; i < NUM_POLL_FDS; i++) {
+
+                       if (pollfds[i].revents & POLLIN) {
+                               fprintf(stderr, "fd %d readable\n", pollfds[i].fd);
+                       }
+                       if (pollfds[i].revents & POLLOUT) {
+                               fprintf(stderr, "fd %d writeable\n", pollfds[i].fd);
+                       }
+                       if (pollfds[i].revents & POLLPRI) {
+                               fprintf(stderr, "fd %d priority readable\n", pollfds[i].fd);
+                       }
+                       if (pollfds[i].revents & POLLERR) {
+                               fprintf(stderr, "fd %d output error\n", pollfds[i].fd);
+                       }
+                       if (pollfds[i].revents & POLLHUP) {
+                               fprintf(stderr, "fd %d hung up\n", pollfds[i].fd);
+                       }
+                       if (pollfds[i].revents & POLLNVAL) {
+                               fprintf(stderr, "fd %d not open\n", pollfds[i].fd);
+                       }
+
+                       if (pollfds[i].revents != 0) {
+                               struct libxl_task *lxt = &libxl_tasks[i];
+                               struct pollfd *pfd = &pollfds[i];
+
+                               libxl_osevent_occurred_fd(lxt->task->ctx, lxt->for_libxl,
+                                       pfd->fd, pfd->events, pfd->revents);
+                               send_fd_event(&t, pfd->fd);
+                       }
+               }
+        }
+
+       test_destroy(&t);
+       xtl_logger_destroy(logger);
+        exit(EXIT_SUCCESS);
+}
diff --git a/xl_eventloop.c b/xl_eventloop.c
new file mode 100644 (file)
index 0000000..06c5b4d
--- /dev/null
@@ -0,0 +1,280 @@
+#include <assert.h>
+#include <libxl.h>
+#include <libxl_event.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <unistd.h>
+
+#include <sys/select.h>
+#include <string.h>
+#include "xl_eventloop.h"
+#include "thread_test.h"
+
+/*
+ * These operations may need to be protected by a lock, since
+ * the worker thread will make calls which require file descriptors
+ * to be registered and the event loop will deregister them.
+ * Alternatively, the test case will need to ask for file descriptors
+ * to be registered and deregistered by sending messages to the main
+ * event loop.
+ */
+
+void
+init_pollfds(struct pollfd *pollfds, int numfds)
+{
+       int i;
+       for (i = 0; i < numfds; i++) {
+                       pollfds[i].fd = -1;
+                       pollfds[i].events = 0;
+                       pollfds[i].revents = 0;
+       }
+}
+
+int
+add_poll_fd(struct pollfd *pollfds, int numfds, int fd, short events)
+{
+       int i;
+       for (i = 0; i < numfds; i++) {
+               if (pollfds[i].fd == -1) {
+                       pollfds[i].fd = fd;
+                       pollfds[i].events = events;
+                       pollfds[i].revents = 0;
+                       return i;
+               }
+       }
+
+       return -1;
+}
+
+int
+modify_poll_fd(struct pollfd *pollfds, int numfds, int slot, int fd, short events)
+{
+       assert(slot < numfds);
+       assert(pollfds[slot].fd == fd);
+       pollfds[slot].events = events;
+       return 0;
+}
+
+int
+remove_poll_fd(struct pollfd *pollfds, int numfds, int slot, int fd)
+{
+       assert(slot < numfds);
+       assert (pollfds[slot].fd != -1);
+       assert (pollfds[slot].fd == fd);
+       pollfds[slot].fd = -1;
+       pollfds[slot].events = 0;
+       pollfds[slot].revents = 0;
+       return 0;
+}
+
+
+int
+fd_register(void *user, int fd, void **for_app_registration_out,
+            short events, void *for_libxl)
+{
+       int slot;
+       struct test *t;
+       struct libxl_task *lxt;
+       t = user;
+
+       slot = add_poll_fd(pollfds, NUM_POLL_FDS, fd, events);
+       lxt = &libxl_tasks[slot];
+       lxt->task = t;
+       lxt->slot = slot;
+       lxt->for_libxl = for_libxl;
+       *for_app_registration_out = lxt;
+
+       return 0;
+}
+
+int
+fd_modify(void *user, int fd, void **for_app_registration_update,
+          short events)
+{
+       struct test *t;
+       struct libxl_task *lxt;
+
+       assert(user);
+       assert(for_app_registration_update);
+       assert(*for_app_registration_update);
+
+       t = user;
+       lxt = *for_app_registration_update;
+
+       modify_poll_fd(pollfds, NUM_POLL_FDS, lxt->slot, fd, events);
+
+       return 0;
+}
+
+void
+fd_deregister(void *user, int fd, void *for_app_registration)
+{
+       struct test *t;
+       struct libxl_task *lxt;
+
+
+       t = user;
+       lxt = for_app_registration;
+
+       remove_poll_fd(pollfds, NUM_POLL_FDS, lxt->slot, fd);
+
+}
+
+int
+timeout_register(void *user __attribute__((unused)),
+                 void **for_app_registration_out __attribute__((unused)),
+                 struct timeval abs __attribute__((unused)),
+                 void *for_libxl __attribute__((unused)))
+{
+       return 0;
+}
+
+/* only ever called with abs={0,0}, meaning ASAP */
+int
+timeout_modify(void *user __attribute__((unused)), void **for_app_registration_update __attribute__((unused)),
+               struct timeval abs __attribute__((unused)))
+{
+       return 0;
+}
+
+/* will never be called */
+void
+timeout_deregister(void *user __attribute__((unused)), void *for_app_registration __attribute__((unused)))
+{
+}
+
+
+void
+print_domain_config(libxl_ctx *ctx, char *msg, libxl_domain_config *dc) {
+       char *json = libxl_domain_config_to_json(ctx, dc);
+       printf("%s: %s\n", msg, json);
+       free(json);
+}
+
+
+void
+libxlEventHandler(void *data __attribute__((unused)), /* const */ libxl_event *event __attribute__((unused)))
+{
+}
+
+void
+register_callbacks(struct test *t)
+{
+       /*
+         * Register the hook functions which libxl will call
+         * to register its timers and its interest in file
+         * descriptors used for operations such as suspend
+         * and resume.
+         * The structs containing these hooks must outlive
+         * the xl context.
+         * Each callback will be called with a pointer to the
+         * task structure.
+         */
+
+       /* Register ordinary async callbacks */
+       t->xl_ev_hooks.event_occurs_mask = LIBXL_EVENTMASK_ALL;
+       t->xl_ev_hooks.event_occurs      = libxlEventHandler;
+       t->xl_ev_hooks.disaster          = 0;
+       libxl_event_register_callbacks(t->ctx, &t->xl_ev_hooks, t);
+
+       /* Register eventloop integration callbacks */
+       t->xl_os_ev_hooks.fd_register   = fd_register;
+       t->xl_os_ev_hooks.fd_modify     = fd_modify;
+       t->xl_os_ev_hooks.fd_deregister = fd_deregister;
+       t->xl_os_ev_hooks.timeout_register   = timeout_register;
+       t->xl_os_ev_hooks.timeout_modify     = timeout_modify;
+       t->xl_os_ev_hooks.timeout_deregister = timeout_deregister;
+       libxl_osevent_register_hooks(t->ctx, &t->xl_os_ev_hooks, t);
+}
+
+void domain_create_cb(libxl_ctx *ctx, int rc, void *for_callback);
+
+void
+init_domain_config(libxl_domain_config *dc, char *name, char *kernel)
+{
+        libxl_device_disk *disk;
+
+       libxl_domain_config_init(dc);
+
+       /* should we be using xlu_cfg_replace_string? */
+       dc->c_info.name = strdup(name);
+       dc->c_info.type = LIBXL_DOMAIN_TYPE_PV;
+       dc->b_info.type = LIBXL_DOMAIN_TYPE_PV;
+       dc->b_info.max_memkb = 512*1024;
+       dc->b_info.u.pv.kernel = strdup(kernel);
+       dc->b_info.u.pv.ramdisk = strdup("/root/foobar.img");
+       dc->b_info.cmdline = strdup("root=/dev/xvda1 selinux=0 console=hvc0");
+
+        /* need to add devices here; create returns immediatly otherwise
+           use xlu_disk_parse? xlu_cfg_readfile? */
+        dc->num_disks = 0;
+        dc->disks = NULL;
+
+        dc->disks = malloc(sizeof(*dc->disks) * 2);
+        dc->num_disks = 2;
+
+        disk = &dc->disks[0];
+       libxl_device_disk_init(disk);
+        disk->pdev_path = strdup("/root/Fedora-Cloud-Base-22-20150521.x86_64.qcow2");
+        disk->vdev = strdup("xvda");
+        disk->format = LIBXL_DISK_FORMAT_QCOW2;
+       disk->readwrite = 1;
+
+        disk = &dc->disks[1];
+       libxl_device_disk_init(disk);
+        disk->pdev_path = strdup("/root/init.iso");
+        disk->vdev = strdup("xvdb");
+        disk->format = LIBXL_DISK_FORMAT_RAW;
+       disk->removable = 1;
+       disk->is_cdrom = 1;
+}
+
+int
+do_domain_create(struct test *t, libxl_domain_config *dc, uint32_t *domid_out)
+{
+       /* speak to andy cooper about valgrind runes to handle xen hypercalls */
+
+       t->ao_how.callback = domain_create_cb;
+       t->ao_how.u.for_callback = t; /* need to carry the other data to be freed */
+
+       return libxl_domain_create_new(t->ctx, dc, domid_out, &t->ao_how, 0);
+}
+
+void
+domain_create_cb(libxl_ctx *ctx __attribute__((unused)),
+                 int rc __attribute__((unused)), void *for_callback)
+{
+        struct test *tc = for_callback;
+       send_libxl_callback_event(tc);
+}
+
+
+void domain_suspend_cb(libxl_ctx *ctx, int rc, void *for_callback);
+
+int
+do_domain_suspend(struct test *t, uint32_t domid)
+{
+       /* need to issue a suspend in order to get an event channel wait
+        * which should ask to register an fd or something else for us above */
+       int fd = open("/tmp/suspend", O_RDWR|O_CREAT|O_TRUNC);
+
+       t->ao_how.callback = domain_suspend_cb;
+       /* t->ao_how.u.for_callback = (void*) fd;  could rely on the test case to provide and close the fd - it can track it in a local variable */
+
+       return libxl_domain_suspend(t->ctx, domid, fd, LIBXL_SUSPEND_LIVE, &t->ao_how);
+}
+
+void
+domain_suspend_cb(libxl_ctx *ctx __attribute__((unused)),
+                  int rc __attribute__((unused)),
+                  void *for_callback __attribute__((unused)))
+{
+       /* struct test *t = for_callback; */
+       /* close(t->in_args.domain_suspend.fd); */
+       printf("< domain_suspend_cb()\n");
+}
+
diff --git a/xl_eventloop.h b/xl_eventloop.h
new file mode 100644 (file)
index 0000000..271bc20
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef __XL_EVENTLOOP_H
+#define __XL_EVENTLOOP_H
+
+#include <libxl.h>
+#include <libxl_event.h>
+
+struct test;
+struct libxl_task {
+       struct test *task;
+       int slot;
+       void *for_libxl;
+};
+
+
+enum { NUM_POLL_FDS = 10 };
+struct pollfd pollfds[NUM_POLL_FDS];
+struct libxl_task libxl_tasks[NUM_POLL_FDS];
+
+void init_pollfds(struct pollfd *pollfds, int numfds);
+
+void init_domain_config(libxl_domain_config *dc, char *name, char *kernel);
+
+void register_callbacks(struct test *t);
+int do_domain_create(struct test *t, libxl_domain_config *dc, uint32_t *domid_out);
+int do_domain_suspend(struct test *t, uint32_t domid);
+
+#endif /* __XL_EVENTLOOP_H */
diff --git a/xlu_test.c b/xlu_test.c
new file mode 100644 (file)
index 0000000..f8eb9bd
--- /dev/null
@@ -0,0 +1,85 @@
+/* 
+gcc -Wall -Werror -Wextra -pedantic  -g -lxenlight -lxlutil xlu_test.c -o xlu_test
+*/
+
+#include <assert.h>
+#include <libxl.h>
+#include <libxlutil.h>
+#include <libxl_event.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <unistd.h>
+
+#include <sys/select.h>
+#include <string.h>
+
+void
+disk_test(XLU_Config *config, const char *spec)
+{
+   libxl_device_disk disk;
+   libxl_device_disk_init(&disk);
+   xlu_disk_parse(config, 1, &spec, &disk);
+}
+
+int
+main(int argc, char **argv)
+{
+   int err;
+   XLU_Config *config; 
+   XLU_ConfigList *config_list; 
+   int count;
+
+   FILE *report;
+   char *report_filename = "/tmp/xlu.out";
+   const char *response;
+
+   if (argc != 2) {
+      fprintf(stderr, "%s: no config file provided\n", argv[0]);
+      exit(-1);
+   }
+
+   report = fopen(report_filename, "w+");
+
+   config = xlu_cfg_init(report, report_filename);
+   if (!config) {
+      perror("xlu_cfg_init");
+      exit(-1);
+   }
+
+   err = xlu_cfg_readfile(config, argv[1]);
+   if (err != 0) {
+      perror("xlu_cfg_readfile");
+      exit(-1);
+   }
+
+   err = xlu_cfg_get_string(config, "name", &response, 1);
+   if (err != 0) {
+      perror("xlu_cfg_get_string");
+      exit(-1);
+   }
+   printf("name: %s\n", response);
+
+   err = xlu_cfg_get_string(config, "kernel", &response, 1);
+   if (err != 0) {
+      perror("xlu_cfg_get_string");
+      exit(-1);
+   }
+   printf("kernel: %s\n", response);
+
+   err = xlu_cfg_get_list(config, "disk", &config_list, &count, 1);
+   if (err != 0) {
+      perror("xlu_cfg_get_string");
+      exit(-1);
+   }
+   printf("disk: %d items\n", count);
+
+   disk_test(config, "qcow2:/root/Fedora-Cloud-Base-22-20150521.x86_64.qcow2,xvda,w");
+   disk_test(config, "file:/root/init.iso,xvdb:cdrom,r");
+   xlu_cfg_destroy(config);
+   exit(0);
+}
+