[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v8 3/7] qapi: add a QmpInputVisitor that does string
From: |
Daniel P. Berrange |
Subject: |
[Qemu-devel] [PATCH v8 3/7] qapi: add a QmpInputVisitor that does string conversion |
Date: |
Thu, 14 Jul 2016 15:03:35 +0100 |
Currently the QmpInputVisitor assumes that all scalar
values are directly represented as their final types.
ie it assumes an 'int' is using QInt, and a 'bool' is
using QBool.
This adds an alternative constructor for QmpInputVisitor
that will set it up such that it expects a QString for
all scalar types instead.
This makes it possible to use QmpInputVisitor with a
QDict produced from QemuOpts, where everything is in
string format.
Reviewed-by: Marc-André Lureau <address@hidden>
Signed-off-by: Daniel P. Berrange <address@hidden>
---
include/qapi/qmp-input-visitor.h | 42 +++++++++--
qapi/qmp-input-visitor.c | 106 ++++++++++++++++++++++++++++
tests/test-qmp-input-visitor.c | 149 ++++++++++++++++++++++++++++++++++++++-
3 files changed, 290 insertions(+), 7 deletions(-)
diff --git a/include/qapi/qmp-input-visitor.h b/include/qapi/qmp-input-visitor.h
index f3ff5f3..33439b7 100644
--- a/include/qapi/qmp-input-visitor.h
+++ b/include/qapi/qmp-input-visitor.h
@@ -19,12 +19,46 @@
typedef struct QmpInputVisitor QmpInputVisitor;
-/*
- * Return a new input visitor that converts QMP to QAPI.
+/**
+ * qmp_input_visitor_new:
+ * @obj: the input object to visit
+ * @strict: whether to require that all input keys are consumed
+ *
+ * Create a new input visitor that converts QMP to QAPI.
+ *
+ * Any scalar values in the @obj input data structure should be in the
+ * required type already. ie if visiting a bool, the value should
+ * already be a QBool instance.
*
- * Set @strict to reject a parse that doesn't consume all keys of a
- * dictionary; otherwise excess input is ignored.
+ * If @strict is set to true, then an error will be reported if any
+ * dict keys are not consumed during visitation.
+ *
+ * The returned input visitor should be released by calling
+ * qmp_input_visitor_cleanup when no longer required.
+ *
+ * Returns: a new input visitor
*/
Visitor *qmp_input_visitor_new(QObject *obj, bool strict);
+/**
+ * qmp_string_input_visitor_new:
+ * @obj: the input object to visit
+ * @strict: whether to require that all input keys are consumed
+ *
+ * Create a new input visitor that converts QMP to QAPI.
+ *
+ * Any scalar values in the @obj input data structure should always be
+ * represented as strings. ie if visiting a boolean, the value should
+ * be a QString whose contents represent a valid boolean.
+ *
+ * If @strict is set to true, then an error will be reported if any
+ * dict keys are not consumed during visitation.
+ *
+ * The returned input visitor should be released by calling
+ * qmp_input_visitor_cleanup when no longer required.
+ *
+ * Returns: a new input visitor
+ */
+Visitor *qmp_string_input_visitor_new(QObject *obj, bool strict);
+
#endif
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 21edb39..307155f 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -20,6 +20,7 @@
#include "qemu-common.h"
#include "qapi/qmp/types.h"
#include "qapi/qmp/qerror.h"
+#include "qemu/cutils.h"
#define QIV_STACK_SIZE 1024
@@ -268,6 +269,17 @@ static void qmp_input_type_int64(Visitor *v, const char
*name, int64_t *obj,
*obj = qint_get_int(qint);
}
+static void qmp_input_type_int64_str(Visitor *v, const char *name, int64_t
*obj,
+ Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ QString *qstr = qobject_to_qstring(qmp_input_get_object(qiv, name, true));
+ uint64_t ret;
+
+ parse_option_number(name, qstr ? qstr->string : NULL, &ret, errp);
+ *obj = ret;
+}
+
static void qmp_input_type_uint64(Visitor *v, const char *name, uint64_t *obj,
Error **errp)
{
@@ -284,6 +296,15 @@ static void qmp_input_type_uint64(Visitor *v, const char
*name, uint64_t *obj,
*obj = qint_get_int(qint);
}
+static void qmp_input_type_uint64_str(Visitor *v, const char *name,
+ uint64_t *obj, Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ QString *qstr = qobject_to_qstring(qmp_input_get_object(qiv, name, true));
+
+ parse_option_number(name, qstr ? qstr->string : NULL, obj, errp);
+}
+
static void qmp_input_type_bool(Visitor *v, const char *name, bool *obj,
Error **errp)
{
@@ -299,6 +320,15 @@ static void qmp_input_type_bool(Visitor *v, const char
*name, bool *obj,
*obj = qbool_get_bool(qbool);
}
+static void qmp_input_type_bool_str(Visitor *v, const char *name, bool *obj,
+ Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ QString *qstr = qobject_to_qstring(qmp_input_get_object(qiv, name, true));
+
+ parse_option_bool(name, qstr ? qstr->string : NULL, obj, errp);
+}
+
static void qmp_input_type_str(Visitor *v, const char *name, char **obj,
Error **errp)
{
@@ -339,6 +369,25 @@ static void qmp_input_type_number(Visitor *v, const char
*name, double *obj,
"number");
}
+static void qmp_input_type_number_str(Visitor *v, const char *name, double
*obj,
+ Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ QString *qstr = qobject_to_qstring(qmp_input_get_object(qiv, name, true));
+ char *endp;
+
+ if (qstr && qstr->string) {
+ errno = 0;
+ *obj = strtod(qstr->string, &endp);
+ if (errno == 0 && endp != qstr->string && *endp == '\0') {
+ return;
+ }
+ }
+
+ error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+ "number");
+}
+
static void qmp_input_type_any(Visitor *v, const char *name, QObject **obj,
Error **errp)
{
@@ -360,6 +409,31 @@ static void qmp_input_type_null(Visitor *v, const char
*name, Error **errp)
}
}
+static void qmp_input_type_size_str(Visitor *v, const char *name, uint64_t
*obj,
+ Error **errp)
+{
+ QmpInputVisitor *qiv = to_qiv(v);
+ QString *qstr = qobject_to_qstring(qmp_input_get_object(qiv, name, true));
+ int64_t val;
+ char *endptr;
+
+ if (qstr && qstr->string) {
+ val = qemu_strtosz_suffix(qstr->string, &endptr,
+ QEMU_STRTOSZ_DEFSUFFIX_B);
+ if (val < 0 || *endptr) {
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name,
+ "a size value representible as a non-negative int64");
+ return;
+ }
+
+ *obj = val;
+ return;
+ }
+
+ error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+ "size");
+}
+
static void qmp_input_optional(Visitor *v, const char *name, bool *present)
{
QmpInputVisitor *qiv = to_qiv(v);
@@ -411,3 +485,35 @@ Visitor *qmp_input_visitor_new(QObject *obj, bool strict)
return &v->visitor;
}
+
+Visitor *qmp_string_input_visitor_new(QObject *obj, bool strict)
+{
+ QmpInputVisitor *v;
+
+ v = g_malloc0(sizeof(*v));
+
+ v->visitor.type = VISITOR_INPUT;
+ v->visitor.start_struct = qmp_input_start_struct;
+ v->visitor.check_struct = qmp_input_check_struct;
+ v->visitor.end_struct = qmp_input_pop;
+ v->visitor.start_list = qmp_input_start_list;
+ v->visitor.next_list = qmp_input_next_list;
+ v->visitor.end_list = qmp_input_pop;
+ v->visitor.start_alternate = qmp_input_start_alternate;
+ v->visitor.type_int64 = qmp_input_type_int64_str;
+ v->visitor.type_uint64 = qmp_input_type_uint64_str;
+ v->visitor.type_bool = qmp_input_type_bool_str;
+ v->visitor.type_str = qmp_input_type_str;
+ v->visitor.type_number = qmp_input_type_number_str;
+ v->visitor.type_any = qmp_input_type_any;
+ v->visitor.type_null = qmp_input_type_null;
+ v->visitor.type_size = qmp_input_type_size_str;
+ v->visitor.optional = qmp_input_optional;
+ v->visitor.free = qmp_input_free;
+ v->strict = strict;
+
+ v->root = obj;
+ qobject_incref(obj);
+
+ return &v->visitor;
+}
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index f583dce..2677226 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -41,6 +41,7 @@ static void visitor_input_teardown(TestInputVisitorData *data,
function so that the JSON string used by the tests are kept in the test
functions (and not in main()). */
static Visitor *visitor_input_test_init_internal(TestInputVisitorData *data,
+ bool strict, bool autocast,
const char *json_string,
va_list *ap)
{
@@ -49,11 +50,30 @@ static Visitor
*visitor_input_test_init_internal(TestInputVisitorData *data,
data->obj = qobject_from_jsonv(json_string, ap);
g_assert(data->obj);
- data->qiv = qmp_input_visitor_new(data->obj, false);
+ if (autocast) {
+ data->qiv = qmp_string_input_visitor_new(data->obj, strict);
+ } else {
+ data->qiv = qmp_input_visitor_new(data->obj, strict);
+ }
g_assert(data->qiv);
return data->qiv;
}
+static GCC_FMT_ATTR(4, 5)
+Visitor *visitor_input_test_init_full(TestInputVisitorData *data,
+ bool strict, bool autocast,
+ const char *json_string, ...)
+{
+ Visitor *v;
+ va_list ap;
+
+ va_start(ap, json_string);
+ v = visitor_input_test_init_internal(data, strict, autocast,
+ json_string, &ap);
+ va_end(ap);
+ return v;
+}
+
static GCC_FMT_ATTR(2, 3)
Visitor *visitor_input_test_init(TestInputVisitorData *data,
const char *json_string, ...)
@@ -62,7 +82,8 @@ Visitor *visitor_input_test_init(TestInputVisitorData *data,
va_list ap;
va_start(ap, json_string);
- v = visitor_input_test_init_internal(data, json_string, &ap);
+ v = visitor_input_test_init_internal(data, false, false,
+ json_string, &ap);
va_end(ap);
return v;
}
@@ -77,7 +98,8 @@ Visitor *visitor_input_test_init(TestInputVisitorData *data,
static Visitor *visitor_input_test_init_raw(TestInputVisitorData *data,
const char *json_string)
{
- return visitor_input_test_init_internal(data, json_string, NULL);
+ return visitor_input_test_init_internal(data, false, false,
+ json_string, NULL);
}
static void test_visitor_in_int(TestInputVisitorData *data,
@@ -109,6 +131,33 @@ static void
test_visitor_in_int_overflow(TestInputVisitorData *data,
error_free_or_abort(&err);
}
+static void test_visitor_in_int_autocast(TestInputVisitorData *data,
+ const void *unused)
+{
+ int64_t res = 0, value = -42;
+ Visitor *v;
+
+ v = visitor_input_test_init_full(data, false, true,
+ "\"-42\"");
+
+ visit_type_int(v, NULL, &res, &error_abort);
+ g_assert_cmpint(res, ==, value);
+}
+
+static void test_visitor_in_int_noautocast(TestInputVisitorData *data,
+ const void *unused)
+{
+ int64_t res = 0;
+ Visitor *v;
+ Error *err = NULL;
+
+ v = visitor_input_test_init(data, "\"-42\"");
+
+ visit_type_int(v, NULL, &res, &err);
+ g_assert(err != NULL);
+ error_free(err);
+}
+
static void test_visitor_in_bool(TestInputVisitorData *data,
const void *unused)
{
@@ -121,6 +170,32 @@ static void test_visitor_in_bool(TestInputVisitorData
*data,
g_assert_cmpint(res, ==, true);
}
+static void test_visitor_in_bool_autocast(TestInputVisitorData *data,
+ const void *unused)
+{
+ bool res = false;
+ Visitor *v;
+
+ v = visitor_input_test_init_full(data, false, true, "\"yes\"");
+
+ visit_type_bool(v, NULL, &res, &error_abort);
+ g_assert_cmpint(res, ==, true);
+}
+
+static void test_visitor_in_bool_noautocast(TestInputVisitorData *data,
+ const void *unused)
+{
+ bool res = false;
+ Visitor *v;
+ Error *err = NULL;
+
+ v = visitor_input_test_init(data, "\"true\"");
+
+ visit_type_bool(v, NULL, &res, &err);
+ g_assert(err != NULL);
+ error_free(err);
+}
+
static void test_visitor_in_number(TestInputVisitorData *data,
const void *unused)
{
@@ -133,6 +208,58 @@ static void test_visitor_in_number(TestInputVisitorData
*data,
g_assert_cmpfloat(res, ==, value);
}
+static void test_visitor_in_number_autocast(TestInputVisitorData *data,
+ const void *unused)
+{
+ double res = 0, value = 3.14;
+ Visitor *v;
+
+ v = visitor_input_test_init_full(data, false, true, "\"3.14\"");
+
+ visit_type_number(v, NULL, &res, &error_abort);
+ g_assert_cmpfloat(res, ==, value);
+}
+
+static void test_visitor_in_number_noautocast(TestInputVisitorData *data,
+ const void *unused)
+{
+ double res = 0;
+ Visitor *v;
+ Error *err = NULL;
+
+ v = visitor_input_test_init(data, "\"3.14\"");
+
+ visit_type_number(v, NULL, &res, &err);
+ g_assert(err != NULL);
+ error_free(err);
+}
+
+static void test_visitor_in_size_autocast(TestInputVisitorData *data,
+ const void *unused)
+{
+ uint64_t res, value = 500 * 1024 * 1024;
+ Visitor *v;
+
+ v = visitor_input_test_init_full(data, false, true, "\"500M\"");
+
+ visit_type_size(v, NULL, &res, &error_abort);
+ g_assert_cmpfloat(res, ==, value);
+}
+
+static void test_visitor_in_size_noautocast(TestInputVisitorData *data,
+ const void *unused)
+{
+ uint64_t res = 0;
+ Visitor *v;
+ Error *err = NULL;
+
+ v = visitor_input_test_init(data, "\"500M\"");
+
+ visit_type_size(v, NULL, &res, &err);
+ g_assert(err != NULL);
+ error_free(err);
+}
+
static void test_visitor_in_string(TestInputVisitorData *data,
const void *unused)
{
@@ -841,10 +968,26 @@ int main(int argc, char **argv)
&in_visitor_data, test_visitor_in_int);
input_visitor_test_add("/visitor/input/int_overflow",
&in_visitor_data, test_visitor_in_int_overflow);
+ input_visitor_test_add("/visitor/input/int_autocast",
+ &in_visitor_data, test_visitor_in_int_autocast);
+ input_visitor_test_add("/visitor/input/int_noautocast",
+ &in_visitor_data, test_visitor_in_int_noautocast);
input_visitor_test_add("/visitor/input/bool",
&in_visitor_data, test_visitor_in_bool);
+ input_visitor_test_add("/visitor/input/bool_autocast",
+ &in_visitor_data, test_visitor_in_bool_autocast);
+ input_visitor_test_add("/visitor/input/bool_noautocast",
+ &in_visitor_data, test_visitor_in_bool_noautocast);
input_visitor_test_add("/visitor/input/number",
&in_visitor_data, test_visitor_in_number);
+ input_visitor_test_add("/visitor/input/number_autocast",
+ &in_visitor_data, test_visitor_in_number_autocast);
+ input_visitor_test_add("/visitor/input/number_noautocast",
+ &in_visitor_data,
test_visitor_in_number_noautocast);
+ input_visitor_test_add("/visitor/input/size_autocast",
+ &in_visitor_data, test_visitor_in_size_autocast);
+ input_visitor_test_add("/visitor/input/size_noautocast",
+ &in_visitor_data, test_visitor_in_size_noautocast);
input_visitor_test_add("/visitor/input/string",
&in_visitor_data, test_visitor_in_string);
input_visitor_test_add("/visitor/input/enum",
--
2.7.4
- [Qemu-devel] [PATCH v8 0/7] Provide a QOM-based authorization API, Daniel P. Berrange, 2016/07/14
- [Qemu-devel] [PATCH v8 3/7] qapi: add a QmpInputVisitor that does string conversion,
Daniel P. Berrange <=
- [Qemu-devel] [PATCH v8 2/7] option: make parse_option_bool/number non-static, Daniel P. Berrange, 2016/07/14
- [Qemu-devel] [PATCH v8 1/7] qdict: implement a qdict_crumple method for un-flattening a dict, Daniel P. Berrange, 2016/07/14
- [Qemu-devel] [PATCH v8 4/7] qom: support arbitrary non-scalar properties with -object, Daniel P. Berrange, 2016/07/14
- [Qemu-devel] [PATCH v8 5/7] util: add QAuthZ object as an authorization base class, Daniel P. Berrange, 2016/07/14
- [Qemu-devel] [PATCH v8 7/7] acl: delete existing ACL implementation, Daniel P. Berrange, 2016/07/14
- [Qemu-devel] [PATCH v8 6/7] util: add QAuthZSimple object type for a simple access control list, Daniel P. Berrange, 2016/07/14