[libvirt] [PATCH 11/14] Support for JSON mode monitor

Daniel Veillard veillard at redhat.com
Thu Dec 3 15:05:54 UTC 2009


On Thu, Nov 26, 2009 at 06:27:29PM +0000, Daniel P. Berrange wrote:
> Initial support for the new QEMU monitor protocol  using JSON
> as the data encoding format instead of plain text
[...]
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -188,6 +188,8 @@ QEMU_DRIVER_SOURCES =						\
>  		qemu/qemu_monitor.c qemu/qemu_monitor.h		\
>  		qemu/qemu_monitor_text.c			\
>  		qemu/qemu_monitor_text.h			\
> +		qemu/qemu_monitor_json.c			\
> +		qemu/qemu_monitor_json.h			\
>  		qemu/qemu_driver.c qemu/qemu_driver.h        	\
>  		qemu/qemu_bridge_filter.c 			\

  Hum could you take the opportunity to cleanup the tab/space mix
in the 2 following lines, above ?

[...]
> @@ -283,9 +285,14 @@ qemuMonitorIOProcess(qemuMonitorPtr mon)
>          msg = mon->msg;
>  
>      VIR_DEBUG("Process %d", (int)mon->bufferOffset);
> -    len = qemuMonitorTextIOProcess(mon,
> -                                   mon->buffer, mon->bufferOffset,
> -                                   msg);
> +    if (mon->json)
> +        len = qemuMonitorJSONIOProcess(mon,
> +                                       mon->buffer, mon->bufferOffset,
> +                                       msg);
> +    else
> +        len = qemuMonitorTextIOProcess(mon,
> +                                       mon->buffer, mon->bufferOffset,
> +                                       msg);

  I have just one doubt here, assuming we have a json handle, is the
text monitor still available. In which case should we try to fallback
assuming the json interface get into troubles ? I assume if one fail
then the other should fail too but ...

[...]
> +#define LINE_ENDING "\r\n"
> +
> +static int
> +qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
> +                             const char *line,
> +                             qemuMonitorMessagePtr msg)
> +{
> +    virJSONValuePtr obj = NULL;
> +    int ret = -1;
> +
> +    VIR_DEBUG("Line [%s]", line);
> +
> +    if (!(obj = virJSONValueFromString(line))) {
> +        VIR_DEBUG0("Parsing JSON string failed");
> +        errno = EINVAL;
> +        goto cleanup;
> +    }
> +
> +    if (obj->type != VIR_JSON_TYPE_OBJECT) {
> +        VIR_DEBUG0("Parsed JSON string isn't an object");
> +        errno = EINVAL;
> +    }
> +
> +    if (virJSONValueObjectHasKey(obj, "QMP") == 1) {
> +        VIR_DEBUG0("Got QMP capabilities data");
> +        ret = 0;
> +        goto cleanup;
> +    }
> +
> +    if (virJSONValueObjectHasKey(obj, "event") == 1) {
> +        VIR_DEBUG0("Got an event");
> +        ret = 0;
> +        goto cleanup;
> +    }
> +
> +    if (msg) {
> +        msg->rxBuffer = strdup(line);
> +        msg->rxLength = strlen(line);
> +        msg->finished = 1;
> +    } else {
> +        VIR_DEBUG("Ignoring unexpected JSON message [%s]", line);
> +    }
> +
> +    ret = 0;
> +
> +cleanup:
> +    virJSONValueFree(obj);
> +    return ret;
> +}

  I must be missing something in that routine, we parse the json blob,
get an object, check some of the object content and discard it, saving
the raw text .... seems to me we dropped the actual parsed content
instead of handling it, no ?

> +int qemuMonitorJSONIOProcess(qemuMonitorPtr mon,
> +                             const char *data,
> +                             size_t len,
> +                             qemuMonitorMessagePtr msg)
> +{
> +    int used = 0;
> +    /*VIR_DEBUG("Data %d bytes [%s]", len, data);*/
> +
> +    while (used < len) {
> +        char *nl = strstr(data + used, LINE_ENDING);
> +
> +        if (nl) {
> +            int got = nl - (data + used);
> +            char *line = strndup(data + used, got);
> +            used += got + strlen(LINE_ENDING);
> +            line[got] = '\0'; /* kill \n */
> +            if (qemuMonitorJSONIOProcessLine(mon, line, msg) < 0) {
> +                VIR_FREE(line);
> +                return -1;
> +            }
> +
> +            VIR_FREE(line);
> +        } else {
> +            break;
> +        }
> +    }

  Okay, I understand the problem of progressive parsing now... if
  somehow they extend the json blob with some (error) string containing
  LINE_ENDING we're stuck I'm afraid.

> +    VIR_DEBUG("Total used %d bytes out of %d available in buffer", used, len);
> +    return used;
> +}

> +static virJSONValuePtr
> +qemuMonitorJSONMakeCommand(const char *cmdname,
> +                           ...)


  Hum, let's add a SENTINEL attribute !

> +{
> +    virJSONValuePtr obj;
> +    virJSONValuePtr jargs = NULL;
> +    va_list args;
> +    char *key;
> +
> +    va_start(args, cmdname);
> +
> +    if (!(obj = virJSONValueNewObject()))
> +        goto no_memory;
> +
> +    if (virJSONValueObjectAppendString(obj, "execute", cmdname) < 0)
> +        goto no_memory;
> +
> +    if (qemuMonitorJSONCommandAddTimestamp(obj) < 0)
> +        goto error;
> +
> +    while ((key = va_arg(args, char *)) != NULL) {
> +        int ret;
> +        char type;
> +
> +        if (strlen(key) < 3) {
> +            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
> +                             _("argument key '%s' is too short, missing type prefix"),
> +                             key);
> +            goto error;
> +        }
> +
> +        /* Keys look like   s:name  the first letter is a type code */
> +        type = key[0];
> +        key += 2;

  Hum, we add a type info on top using prefixing ... weird but why not ...

> +        if (!jargs &&
> +            !(jargs = virJSONValueNewObject()))
> +            goto no_memory;
> +
> +        /* This doesn't supports maps/arrays.  This hasn't
> +         * proved to be a problem..... yet :-)  */
> +        switch (type) {
> +        case 's': {
> +            char *val = va_arg(args, char *);
> +            ret = virJSONValueObjectAppendString(jargs, key, val);
> +        }   break;
> +        case 'i': {
> +            int val = va_arg(args, int);
> +            ret = virJSONValueObjectAppendNumberInt(jargs, key, val);
> +        }   break;
> +        case 'u': {
> +            unsigned int val = va_arg(args, unsigned int);
> +            ret = virJSONValueObjectAppendNumberUint(jargs, key, val);
> +        }   break;
> +        case 'I': {
> +            long long val = va_arg(args, long long);
> +            ret = virJSONValueObjectAppendNumberLong(jargs, key, val);
> +        }   break;
> +        case 'U': {
> +            unsigned long long val = va_arg(args, unsigned long long);
> +            ret = virJSONValueObjectAppendNumberUlong(jargs, key, val);
> +        }   break;
> +        case 'd': {
> +            double val = va_arg(args, double);
> +            ret = virJSONValueObjectAppendNumberDouble(jargs, key, val);
> +        }   break;
> +        case 'b': {
> +            int val = va_arg(args, int);
> +            ret = virJSONValueObjectAppendBoolean(jargs, key, val);
> +        }   break;
> +        case 'n': {
> +            ret = virJSONValueObjectAppendNull(jargs, key);
> +        }   break;
> +        default:
> +            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
> +                             _("unsupported data type '%c' for arg '%s'"), type, key - 2);
> +            goto error;
> +        }
> +        if (ret < 0)
> +            goto no_memory;
> +    }
> +
> +    if (jargs &&
> +        virJSONValueObjectAppend(obj, "arguments", jargs) < 0)
> +        goto no_memory;
> +
> +    va_end(args);
> +
> +    return obj;
> +
> +no_memory:
> +    virReportOOMError(NULL);
> +error:
> +    virJSONValueFree(obj);
> +    virJSONValueFree(jargs);
> +    va_end(args);
> +    return NULL;
> +}
[...]
> +{
> +    int ret;
> +    virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("balloon",
> +                                                     "U:value", (unsigned long long)newmem,

  I would rather drop alognment of args and fit into 80 cols.

[...]

  good to see a more formal interface, now they just have to make it XML
and that would be perfect ! >:->

> diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
> new file mode 100644
> index 0000000..62a88c0
> --- /dev/null
> +++ b/src/qemu/qemu_monitor_json.h
> @@ -0,0 +1,156 @@
[...]
> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
> index 677c5b4..c39cbbc 100644
> --- a/tests/qemuxml2argvtest.c
> +++ b/tests/qemuxml2argvtest.c
> @@ -60,7 +60,7 @@ static int testCompareXMLToArgvFiles(const char *xml,
>          goto fail;
>  
>      if (qemudBuildCommandLine(NULL, &driver,
> -                              vmdef, &monitor_chr, flags,
> +                              vmdef, &monitor_chr, 0, flags,
>                                &argv, &qenv,
>                                NULL, NULL, migrateFrom) < 0)
>          goto fail;

  As pointed earlier by Matthias it would be good to have some kind
of regression testing for JSON code, maybe not down to QEmu actual
format but at least parsing and serializing the common stuff.

  ACK

Daniel

-- 
Daniel Veillard      | libxml Gnome XML XSLT toolkit  http://xmlsoft.org/
daniel at veillard.com  | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library  http://libvirt.org/




More information about the libvir-list mailing list