QAPI patches patches for 2021-10-27

-----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAmF5bpISHGFybWJydUBy
 ZWRoYXQuY29tAAoJEDhwtADrkYZThBsP/jgLAry80cEd+xZUTQTfQDfWEB9ScE0R
 V/yV385klxl/yXf7D7pzel89fflu8V1sjSHn6x29HbOTHWctG8u8LtRO4lBqooRq
 yG4EZ7xWChchrjyBzeEtL/eHBJWktPVqvK7U2Z+2jCqM5gy1xgCsMzqPpPraOHoi
 Z3g50JKYQE+JVkoqzzry+yRckHtQsOF2CR+QQGpf+e3xvUrOnOyukGftr3Cfppcg
 fMkb70mPknHMDepHSYQop7n7LPIbZuX8nbV0uFfGShMyiJa0zSzfWEfWhPxRQgwv
 mMuV0yzxnXE0EP515rQ7HVRaYUopmMJaTVa7gBRMu5kh16/7eFXBjs/vSUlV1reH
 ZkNefKIsLpFlgFMOLbEVAPF3/VIlFkO1IQI+UvNz5FDKCR65YLUTZ0D89+XCQqlp
 zi8NXDQ+IhHIBRSMsCFgLClzmDrXeDW6g3IEJUvjP+vV6IqmbiU6TfvOkMurF+KI
 0QEH3LfKIWU/vkvecWd2EjDWoGatsqtQm5GMBEUHLnrQyekYc0k1f8W230EG2mKJ
 19Eh/R6aYc9K4Vgx0OgfZjxBh3GARrOO7TEnaLQNeClBR8UKGqP0iVtYdVkroyIB
 ui2U/hiRCi115Wj4y4F7M6er7MbkxR5iBy/8H7H96hOG5vFC9UyagUw1L6bsaRDt
 EMAnG+4daNgX
 =Ff93
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2021-10-27' into staging

QAPI patches patches for 2021-10-27

# gpg: Signature made Wed 27 Oct 2021 08:21:54 AM PDT
# gpg:                using RSA key 354BC8B3D7EB2A6B68674E5F3870B400EB918653
# gpg:                issuer "armbru@redhat.com"
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" [full]
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>" [full]

* remotes/armbru/tags/pull-qapi-2021-10-27:
  qapi: Implement deprecated-input={reject,crash} for enum values
  qapi: Move compat policy from QObject to generic visitor
  qapi: Add feature flags to enum members
  qapi: Enable enum member introspection to show more than name
  qapi: Improve input_type_enum()'s error message

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2021-10-27 09:42:40 -07:00
commit 5c49c6c241
31 changed files with 182 additions and 73 deletions

View File

@ -228,6 +228,12 @@ Use the more generic commands ``block-export-add`` and ``block-export-del``
instead. As part of this deprecation, where ``nbd-server-add`` used a instead. As part of this deprecation, where ``nbd-server-add`` used a
single ``bitmap``, the new ``block-export-add`` uses a list of ``bitmaps``. single ``bitmap``, the new ``block-export-add`` uses a list of ``bitmaps``.
``query-qmp-schema`` return value member ``values`` (since 6.2)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Member ``values`` in return value elements with meta-type ``enum`` is
deprecated. Use ``members`` instead.
System accelerators System accelerators
------------------- -------------------

View File

@ -200,7 +200,9 @@ Syntax::
'*if': COND, '*if': COND,
'*features': FEATURES } '*features': FEATURES }
ENUM-VALUE = STRING ENUM-VALUE = STRING
| { 'name': STRING, '*if': COND } | { 'name': STRING,
'*if': COND,
'*features': FEATURES }
Member 'enum' names the enum type. Member 'enum' names the enum type.
@ -706,8 +708,10 @@ QEMU shows a certain behaviour.
Special features Special features
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
Feature "deprecated" marks a command, event, or struct member as Feature "deprecated" marks a command, event, enum value, or struct
deprecated. It is not supported elsewhere so far. member as deprecated. It is not supported elsewhere so far.
Interfaces so marked may be withdrawn in future releases in accordance
with QEMU's deprecation policy.
Naming rules and reserved names Naming rules and reserved names
@ -1157,7 +1161,8 @@ and "variants".
"members" is a JSON array describing the object's common members, if "members" is a JSON array describing the object's common members, if
any. Each element is a JSON object with members "name" (the member's any. Each element is a JSON object with members "name" (the member's
name), "type" (the name of its type), and optionally "default". The name), "type" (the name of its type), "features" (a JSON array of
feature strings), and "default". The latter two are optional. The
member is optional if "default" is present. Currently, "default" can member is optional if "default" is present. Currently, "default" can
only have value null. Other values are reserved for future only have value null. Other values are reserved for future
extensions. The "members" array is in no particular order; clients extensions. The "members" array is in no particular order; clients
@ -1231,14 +1236,22 @@ Example: the SchemaInfo for ['str'] ::
"element-type": "str" } "element-type": "str" }
The SchemaInfo for an enumeration type has meta-type "enum" and The SchemaInfo for an enumeration type has meta-type "enum" and
variant member "values". The values are listed in no particular variant member "members".
order; clients must search the entire enum when learning whether a
particular value is supported. "members" is a JSON array describing the enumeration values. Each
element is a JSON object with member "name" (the member's name), and
optionally "features" (a JSON array of feature strings). The
"members" array is in no particular order; clients must search the
entire array when learning whether a particular value is supported.
Example: the SchemaInfo for MyEnum from section `Enumeration types`_ :: Example: the SchemaInfo for MyEnum from section `Enumeration types`_ ::
{ "name": "MyEnum", "meta-type": "enum", { "name": "MyEnum", "meta-type": "enum",
"values": [ "value1", "value2", "value3" ] } "members": [
{ "name": "value1" },
{ "name": "value2" },
{ "name": "value3" }
] }
The SchemaInfo for a built-in type has the same name as the type in The SchemaInfo for a built-in type has the same name as the type in
the QAPI schema (see section `Built-in Types`_), with one exception the QAPI schema (see section `Built-in Types`_), with one exception

View File

@ -15,7 +15,6 @@
#ifndef QOBJECT_INPUT_VISITOR_H #ifndef QOBJECT_INPUT_VISITOR_H
#define QOBJECT_INPUT_VISITOR_H #define QOBJECT_INPUT_VISITOR_H
#include "qapi/qapi-types-compat.h"
#include "qapi/visitor.h" #include "qapi/visitor.h"
typedef struct QObjectInputVisitor QObjectInputVisitor; typedef struct QObjectInputVisitor QObjectInputVisitor;
@ -59,9 +58,6 @@ typedef struct QObjectInputVisitor QObjectInputVisitor;
*/ */
Visitor *qobject_input_visitor_new(QObject *obj); Visitor *qobject_input_visitor_new(QObject *obj);
void qobject_input_visitor_set_policy(Visitor *v,
CompatPolicyInput deprecated);
/* /*
* Create a QObject input visitor for @obj for use with keyval_parse() * Create a QObject input visitor for @obj for use with keyval_parse()
* *

View File

@ -15,7 +15,6 @@
#define QOBJECT_OUTPUT_VISITOR_H #define QOBJECT_OUTPUT_VISITOR_H
#include "qapi/visitor.h" #include "qapi/visitor.h"
#include "qapi/qapi-types-compat.h"
typedef struct QObjectOutputVisitor QObjectOutputVisitor; typedef struct QObjectOutputVisitor QObjectOutputVisitor;
@ -54,7 +53,4 @@ typedef struct QObjectOutputVisitor QObjectOutputVisitor;
*/ */
Visitor *qobject_output_visitor_new(QObject **result); Visitor *qobject_output_visitor_new(QObject **result);
void qobject_output_visitor_set_policy(Visitor *v,
CompatPolicyOutput deprecated);
#endif #endif

View File

@ -11,9 +11,13 @@
#ifndef QAPI_UTIL_H #ifndef QAPI_UTIL_H
#define QAPI_UTIL_H #define QAPI_UTIL_H
/* QEnumLookup flags */
#define QAPI_ENUM_DEPRECATED 1
typedef struct QEnumLookup { typedef struct QEnumLookup {
const char *const *array; const char *const *array;
int size; const unsigned char *const flags;
const int size;
} QEnumLookup; } QEnumLookup;
const char *qapi_enum_lookup(const QEnumLookup *lookup, int val); const char *qapi_enum_lookup(const QEnumLookup *lookup, int val);

View File

@ -122,6 +122,9 @@ struct Visitor
/* Must be set */ /* Must be set */
VisitorType type; VisitorType type;
/* Optional */
struct CompatPolicy compat_policy;
/* Must be set for output visitors, optional otherwise. */ /* Must be set for output visitors, optional otherwise. */
void (*complete)(Visitor *v, void *opaque); void (*complete)(Visitor *v, void *opaque);

View File

@ -16,6 +16,7 @@
#define QAPI_VISITOR_H #define QAPI_VISITOR_H
#include "qapi/qapi-builtin-types.h" #include "qapi/qapi-builtin-types.h"
#include "qapi/qapi-types-compat.h"
/* /*
* The QAPI schema defines both a set of C data types, and a QMP wire * The QAPI schema defines both a set of C data types, and a QMP wire
@ -477,6 +478,14 @@ bool visit_deprecated_accept(Visitor *v, const char *name, Error **errp);
*/ */
bool visit_deprecated(Visitor *v, const char *name); bool visit_deprecated(Visitor *v, const char *name);
/*
* Set policy for handling deprecated management interfaces.
*
* Intended use: call visit_set_policy(v, &compat_policy) when
* visiting management interface input or output.
*/
void visit_set_policy(Visitor *v, CompatPolicy *policy);
/* /*
* Visit an enum value. * Visit an enum value.
* *

View File

@ -42,6 +42,9 @@
# with feature 'deprecated'. We may want to extend it to cover # with feature 'deprecated'. We may want to extend it to cover
# semantic aspects, CLI, and experimental features. # semantic aspects, CLI, and experimental features.
# #
# Limitation: deprecated-output policy @hide is not implemented for
# enumeration values. They behave the same as with policy @accept.
#
# @deprecated-input: how to handle deprecated input (default 'accept') # @deprecated-input: how to handle deprecated input (default 'accept')
# @deprecated-output: how to handle deprecated output (default 'accept') # @deprecated-output: how to handle deprecated output (default 'accept')
# #

View File

@ -142,14 +142,38 @@
# #
# Additional SchemaInfo members for meta-type 'enum'. # Additional SchemaInfo members for meta-type 'enum'.
# #
# @values: the enumeration type's values, in no particular order. # @members: the enum type's members, in no particular order
# (since 6.2).
#
# @values: the enumeration type's member names, in no particular order.
# Redundant with @members. Just for backward compatibility.
#
# Features:
# @deprecated: Member @values is deprecated. Use @members instead.
# #
# Values of this type are JSON string on the wire. # Values of this type are JSON string on the wire.
# #
# Since: 2.5 # Since: 2.5
## ##
{ 'struct': 'SchemaInfoEnum', { 'struct': 'SchemaInfoEnum',
'data': { 'values': ['str'] } } 'data': { 'members': [ 'SchemaInfoEnumMember' ],
'values': { 'type': [ 'str' ],
'features': [ 'deprecated' ] } } }
##
# @SchemaInfoEnumMember:
#
# An object member.
#
# @name: the member's name, as defined in the QAPI schema.
#
# @features: names of features associated with the member, in no
# particular order.
#
# Since: 6.2
##
{ 'struct': 'SchemaInfoEnumMember',
'data': { 'name': 'str', '*features': [ 'str' ] } }
## ##
# @SchemaInfoArray: # @SchemaInfoArray:

View File

@ -19,6 +19,10 @@
#include "qapi/visitor-impl.h" #include "qapi/visitor-impl.h"
#include "trace.h" #include "trace.h"
/* Zero-initialization must result in default policy */
QEMU_BUILD_BUG_ON(COMPAT_POLICY_INPUT_ACCEPT || COMPAT_POLICY_OUTPUT_ACCEPT);
void visit_complete(Visitor *v, void *opaque) void visit_complete(Visitor *v, void *opaque)
{ {
assert(v->type != VISITOR_OUTPUT || v->complete); assert(v->type != VISITOR_OUTPUT || v->complete);
@ -153,6 +157,11 @@ bool visit_deprecated(Visitor *v, const char *name)
return true; return true;
} }
void visit_set_policy(Visitor *v, CompatPolicy *policy)
{
v->compat_policy = *policy;
}
bool visit_is_input(Visitor *v) bool visit_is_input(Visitor *v)
{ {
return v->type == VISITOR_INPUT; return v->type == VISITOR_INPUT;
@ -384,7 +393,7 @@ static bool input_type_enum(Visitor *v, const char *name, int *obj,
const QEnumLookup *lookup, Error **errp) const QEnumLookup *lookup, Error **errp)
{ {
int64_t value; int64_t value;
char *enum_str; g_autofree char *enum_str = NULL;
if (!visit_type_str(v, name, &enum_str, errp)) { if (!visit_type_str(v, name, &enum_str, errp)) {
return false; return false;
@ -392,12 +401,25 @@ static bool input_type_enum(Visitor *v, const char *name, int *obj,
value = qapi_enum_parse(lookup, enum_str, -1, NULL); value = qapi_enum_parse(lookup, enum_str, -1, NULL);
if (value < 0) { if (value < 0) {
error_setg(errp, QERR_INVALID_PARAMETER, enum_str); error_setg(errp, "Parameter '%s' does not accept value '%s'",
g_free(enum_str); name ? name : "null", enum_str);
return false; return false;
} }
g_free(enum_str); if (lookup->flags && (lookup->flags[value] & QAPI_ENUM_DEPRECATED)) {
switch (v->compat_policy.deprecated_input) {
case COMPAT_POLICY_INPUT_ACCEPT:
break;
case COMPAT_POLICY_INPUT_REJECT:
error_setg(errp, "Deprecated value '%s' disabled by policy",
enum_str);
return false;
case COMPAT_POLICY_INPUT_CRASH:
default:
abort();
}
}
*obj = value; *obj = value;
return true; return true;
} }

View File

@ -32,7 +32,7 @@ Visitor *qobject_input_visitor_new_qmp(QObject *obj)
{ {
Visitor *v = qobject_input_visitor_new(obj); Visitor *v = qobject_input_visitor_new(obj);
qobject_input_visitor_set_policy(v, compat_policy.deprecated_input); visit_set_policy(v, &compat_policy);
return v; return v;
} }
@ -40,7 +40,7 @@ Visitor *qobject_output_visitor_new_qmp(QObject **result)
{ {
Visitor *v = qobject_output_visitor_new(result); Visitor *v = qobject_output_visitor_new(result);
qobject_output_visitor_set_policy(v, compat_policy.deprecated_output); visit_set_policy(v, &compat_policy);
return v; return v;
} }

View File

@ -14,7 +14,6 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include <math.h> #include <math.h>
#include "qapi/compat-policy.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/qobject-input-visitor.h" #include "qapi/qobject-input-visitor.h"
#include "qapi/visitor-impl.h" #include "qapi/visitor-impl.h"
@ -44,7 +43,6 @@ typedef struct StackObject {
struct QObjectInputVisitor { struct QObjectInputVisitor {
Visitor visitor; Visitor visitor;
CompatPolicyInput deprecated_policy;
/* Root of visit at visitor creation. */ /* Root of visit at visitor creation. */
QObject *root; QObject *root;
@ -667,9 +665,7 @@ static void qobject_input_optional(Visitor *v, const char *name, bool *present)
static bool qobject_input_deprecated_accept(Visitor *v, const char *name, static bool qobject_input_deprecated_accept(Visitor *v, const char *name,
Error **errp) Error **errp)
{ {
QObjectInputVisitor *qiv = to_qiv(v); switch (v->compat_policy.deprecated_input) {
switch (qiv->deprecated_policy) {
case COMPAT_POLICY_INPUT_ACCEPT: case COMPAT_POLICY_INPUT_ACCEPT:
return true; return true;
case COMPAT_POLICY_INPUT_REJECT: case COMPAT_POLICY_INPUT_REJECT:
@ -739,14 +735,6 @@ Visitor *qobject_input_visitor_new(QObject *obj)
return &v->visitor; return &v->visitor;
} }
void qobject_input_visitor_set_policy(Visitor *v,
CompatPolicyInput deprecated)
{
QObjectInputVisitor *qiv = to_qiv(v);
qiv->deprecated_policy = deprecated;
}
Visitor *qobject_input_visitor_new_keyval(QObject *obj) Visitor *qobject_input_visitor_new_keyval(QObject *obj)
{ {
QObjectInputVisitor *v = qobject_input_visitor_base_new(obj); QObjectInputVisitor *v = qobject_input_visitor_base_new(obj);

View File

@ -13,7 +13,6 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/compat-policy.h"
#include "qapi/qobject-output-visitor.h" #include "qapi/qobject-output-visitor.h"
#include "qapi/visitor-impl.h" #include "qapi/visitor-impl.h"
#include "qemu/queue.h" #include "qemu/queue.h"
@ -32,7 +31,6 @@ typedef struct QStackEntry {
struct QObjectOutputVisitor { struct QObjectOutputVisitor {
Visitor visitor; Visitor visitor;
CompatPolicyOutput deprecated_policy;
QSLIST_HEAD(, QStackEntry) stack; /* Stack of unfinished containers */ QSLIST_HEAD(, QStackEntry) stack; /* Stack of unfinished containers */
QObject *root; /* Root of the output visit */ QObject *root; /* Root of the output visit */
@ -212,9 +210,7 @@ static bool qobject_output_type_null(Visitor *v, const char *name,
static bool qobject_output_deprecated(Visitor *v, const char *name) static bool qobject_output_deprecated(Visitor *v, const char *name)
{ {
QObjectOutputVisitor *qov = to_qov(v); return v->compat_policy.deprecated_output != COMPAT_POLICY_OUTPUT_HIDE;
return qov->deprecated_policy != COMPAT_POLICY_OUTPUT_HIDE;
} }
/* Finish building, and return the root object. /* Finish building, and return the root object.
@ -275,11 +271,3 @@ Visitor *qobject_output_visitor_new(QObject **result)
return &v->visitor; return &v->visitor;
} }
void qobject_output_visitor_set_policy(Visitor *v,
CompatPolicyOutput deprecated)
{
QObjectOutputVisitor *qov = to_qov(v);
qov->deprecated_policy = deprecated;
}

View File

@ -472,7 +472,7 @@ def check_enum(expr: _JSONObject, info: QAPISourceInfo) -> None:
for m in members] for m in members]
for member in members: for member in members:
source = "'data' member" source = "'data' member"
check_keys(member, info, source, ['name'], ['if']) check_keys(member, info, source, ['name'], ['if', 'features'])
member_name = member['name'] member_name = member['name']
check_name_is_str(member_name, info, source) check_name_is_str(member_name, info, source)
source = "%s '%s'" % (source, member_name) source = "%s '%s'" % (source, member_name)
@ -483,6 +483,7 @@ def check_enum(expr: _JSONObject, info: QAPISourceInfo) -> None:
permit_upper=permissive, permit_upper=permissive,
permit_underscore=permissive) permit_underscore=permissive)
check_if(member, info, source) check_if(member, info, source)
check_features(member.get('features'), info)
def check_struct(expr: _JSONObject, info: QAPISourceInfo) -> None: def check_struct(expr: _JSONObject, info: QAPISourceInfo) -> None:

View File

@ -68,6 +68,7 @@ JSONValue = Union[_Value, 'Annotated[_Value]']
# TypedDict constructs, so they are broadly typed here as simple # TypedDict constructs, so they are broadly typed here as simple
# Python Dicts. # Python Dicts.
SchemaInfo = Dict[str, object] SchemaInfo = Dict[str, object]
SchemaInfoEnumMember = Dict[str, object]
SchemaInfoObject = Dict[str, object] SchemaInfoObject = Dict[str, object]
SchemaInfoObjectVariant = Dict[str, object] SchemaInfoObjectVariant = Dict[str, object]
SchemaInfoObjectMember = Dict[str, object] SchemaInfoObjectMember = Dict[str, object]
@ -274,7 +275,16 @@ const QLitObject %(c_name)s = %(c_string)s;
obj['features'] = self._gen_features(features) obj['features'] = self._gen_features(features)
self._trees.append(Annotated(obj, ifcond, comment)) self._trees.append(Annotated(obj, ifcond, comment))
def _gen_member(self, member: QAPISchemaObjectTypeMember def _gen_enum_member(self, member: QAPISchemaEnumMember
) -> Annotated[SchemaInfoEnumMember]:
obj: SchemaInfoEnumMember = {
'name': member.name,
}
if member.features:
obj['features'] = self._gen_features(member.features)
return Annotated(obj, member.ifcond)
def _gen_object_member(self, member: QAPISchemaObjectTypeMember
) -> Annotated[SchemaInfoObjectMember]: ) -> Annotated[SchemaInfoObjectMember]:
obj: SchemaInfoObjectMember = { obj: SchemaInfoObjectMember = {
'name': member.name, 'name': member.name,
@ -305,7 +315,8 @@ const QLitObject %(c_name)s = %(c_string)s;
prefix: Optional[str]) -> None: prefix: Optional[str]) -> None:
self._gen_tree( self._gen_tree(
name, 'enum', name, 'enum',
{'values': [Annotated(m.name, m.ifcond) for m in members]}, {'members': [self._gen_enum_member(m) for m in members],
'values': [Annotated(m.name, m.ifcond) for m in members]},
ifcond, features ifcond, features
) )
@ -322,7 +333,7 @@ const QLitObject %(c_name)s = %(c_string)s;
members: List[QAPISchemaObjectTypeMember], members: List[QAPISchemaObjectTypeMember],
variants: Optional[QAPISchemaVariants]) -> None: variants: Optional[QAPISchemaVariants]) -> None:
obj: SchemaInfoObject = { obj: SchemaInfoObject = {
'members': [self._gen_member(m) for m in members] 'members': [self._gen_object_member(m) for m in members]
} }
if variants: if variants:
obj['tag'] = variants.tag_member.name obj['tag'] = variants.tag_member.name

View File

@ -708,6 +708,19 @@ class QAPISchemaMember:
class QAPISchemaEnumMember(QAPISchemaMember): class QAPISchemaEnumMember(QAPISchemaMember):
role = 'value' role = 'value'
def __init__(self, name, info, ifcond=None, features=None):
super().__init__(name, info, ifcond)
for f in features or []:
assert isinstance(f, QAPISchemaFeature)
f.set_defined_in(name)
self.features = features or []
def connect_doc(self, doc):
super().connect_doc(doc)
if doc:
for f in self.features:
doc.connect_feature(f)
class QAPISchemaFeature(QAPISchemaMember): class QAPISchemaFeature(QAPISchemaMember):
role = 'feature' role = 'feature'
@ -980,9 +993,14 @@ class QAPISchema:
QAPISchemaIfCond(f.get('if'))) QAPISchemaIfCond(f.get('if')))
for f in features] for f in features]
def _make_enum_member(self, name, ifcond, features, info):
return QAPISchemaEnumMember(name, info,
QAPISchemaIfCond(ifcond),
self._make_features(features, info))
def _make_enum_members(self, values, info): def _make_enum_members(self, values, info):
return [QAPISchemaEnumMember(v['name'], info, return [self._make_enum_member(v['name'], v.get('if'),
QAPISchemaIfCond(v.get('if'))) v.get('features'), info)
for v in values] for v in values]
def _make_array_type(self, element_type, info): def _make_array_type(self, element_type, info):

View File

@ -38,6 +38,8 @@ objects_seen = set()
def gen_enum_lookup(name: str, def gen_enum_lookup(name: str,
members: List[QAPISchemaEnumMember], members: List[QAPISchemaEnumMember],
prefix: Optional[str] = None) -> str: prefix: Optional[str] = None) -> str:
max_index = c_enum_const(name, '_MAX', prefix)
flags = ''
ret = mcgen(''' ret = mcgen('''
const QEnumLookup %(c_name)s_lookup = { const QEnumLookup %(c_name)s_lookup = {
@ -52,13 +54,26 @@ const QEnumLookup %(c_name)s_lookup = {
''', ''',
index=index, name=memb.name) index=index, name=memb.name)
ret += memb.ifcond.gen_endif() ret += memb.ifcond.gen_endif()
if 'deprecated' in (f.name for f in memb.features):
flags += mcgen('''
[%(index)s] = QAPI_ENUM_DEPRECATED,
''',
index=index)
if flags:
ret += mcgen('''
},
.flags = (const unsigned char[%(max_index)s]) {
''',
max_index=max_index)
ret += flags
ret += mcgen(''' ret += mcgen('''
}, },
.size = %(max_index)s .size = %(max_index)s
}; };
''', ''',
max_index=c_enum_const(name, '_MAX', prefix)) max_index=max_index)
return ret return ret

View File

@ -58,11 +58,14 @@
# #
# Features: # Features:
# @enum-feat: Also _one_ {and only} # @enum-feat: Also _one_ {and only}
# @enum-member-feat: a member feature
# #
# @two is undocumented # @two is undocumented
## ##
{ 'enum': 'Enum', { 'enum': 'Enum',
'data': [ { 'name': 'one', 'if': 'IFONE' }, 'two' ], 'data': [ { 'name': 'one', 'if': 'IFONE',
'features': [ 'enum-member-feat' ] },
'two' ],
'features': [ 'enum-feat' ], 'features': [ 'enum-feat' ],
'if': 'IFCOND' } 'if': 'IFCOND' }

View File

@ -13,6 +13,7 @@ module doc-good.json
enum Enum enum Enum
member one member one
if IFONE if IFONE
feature enum-member-feat
member two member two
if IFCOND if IFCOND
feature enum-feat feature enum-feat
@ -108,6 +109,8 @@ The _one_ {and only}
feature=enum-feat feature=enum-feat
Also _one_ {and only} Also _one_ {and only}
feature=enum-member-feat
a member feature
section=None section=None
@two is undocumented @two is undocumented
doc symbol=Base doc symbol=Base

View File

@ -56,6 +56,9 @@ Features
"enum-feat" "enum-feat"
Also _one_ {and only} Also _one_ {and only}
"enum-member-feat"
a member feature
"two" is undocumented "two" is undocumented

View File

@ -1,3 +1,3 @@
enum-dict-member-unknown.json: In enum 'MyEnum': enum-dict-member-unknown.json: In enum 'MyEnum':
enum-dict-member-unknown.json:2: 'data' member has unknown key 'bad-key' enum-dict-member-unknown.json:2: 'data' member has unknown key 'bad-key'
Valid keys are 'if', 'name'. Valid keys are 'features', 'if', 'name'.

View File

@ -301,7 +301,8 @@
'TEST_IF_COND_2'] } } ] } 'TEST_IF_COND_2'] } } ] }
{ 'enum': 'FeatureEnum1', { 'enum': 'FeatureEnum1',
'data': [ 'eins', 'zwei', 'drei' ], 'data': [ 'eins', 'zwei',
{ 'name': 'drei', 'features': [ 'deprecated' ] } ],
'features': [ 'feature1' ] } 'features': [ 'feature1' ] }
{ 'union': 'FeatureUnion1', { 'union': 'FeatureUnion1',

View File

@ -341,6 +341,7 @@ enum FeatureEnum1
member eins member eins
member zwei member zwei
member drei member drei
feature deprecated
feature feature1 feature feature1
object q_obj_FeatureUnion1-base object q_obj_FeatureUnion1-base
member tag: FeatureEnum1 optional=False member tag: FeatureEnum1 optional=False

View File

@ -37,6 +37,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
for m in members: for m in members:
print(' member %s' % m.name) print(' member %s' % m.name)
self._print_if(m.ifcond, indent=8) self._print_if(m.ifcond, indent=8)
self._print_features(m.features, indent=8)
self._print_if(ifcond) self._print_if(ifcond)
self._print_features(features) self._print_features(features)

View File

@ -174,11 +174,11 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off comp
qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=0.42 lazy_refcounts=off refcount_bits=16 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=0.42 lazy_refcounts=off refcount_bits=16
qemu-img: TEST_DIR/t.qcow2: Invalid parameter '0.42' qemu-img: TEST_DIR/t.qcow2: Parameter 'version' does not accept value '0.42'
qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=foobar lazy_refcounts=off refcount_bits=16 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 compat=foobar lazy_refcounts=off refcount_bits=16
qemu-img: TEST_DIR/t.qcow2: Invalid parameter 'foobar' qemu-img: TEST_DIR/t.qcow2: Parameter 'version' does not accept value 'foobar'
== Check preallocation option == == Check preallocation option ==
@ -190,7 +190,7 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off prea
qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off preallocation=1234 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off preallocation=1234 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
qemu-img: TEST_DIR/t.qcow2: Invalid parameter '1234' qemu-img: TEST_DIR/t.qcow2: Parameter 'preallocation' does not accept value '1234'
== Check encryption option == == Check encryption option ==

View File

@ -192,7 +192,7 @@ Job failed: Could not resize image: Failed to grow the L1 table: File too large
=== Invalid version === === Invalid version ===
{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 67108864, "version": "v1"}}} {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 67108864, "version": "v1"}}}
{"error": {"class": "GenericError", "desc": "Invalid parameter 'v1'"}} {"error": {"class": "GenericError", "desc": "Parameter 'version' does not accept value 'v1'"}}
{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "lazy-refcounts": true, "size": 67108864, "version": "v2"}}} {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "lazy-refcounts": true, "size": 67108864, "version": "v2"}}}
{"return": {}} {"return": {}}

View File

@ -116,13 +116,13 @@ Job failed: Cannot find device='this doesn't exist' nor node-name='this doesn't
== Invalid adapter types == == Invalid adapter types ==
{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "foo", "driver": "vmdk", "file": "node0", "size": 33554432}}} {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "foo", "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"error": {"class": "GenericError", "desc": "Invalid parameter 'foo'"}} {"error": {"class": "GenericError", "desc": "Parameter 'adapter-type' does not accept value 'foo'"}}
{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "IDE", "driver": "vmdk", "file": "node0", "size": 33554432}}} {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "IDE", "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"error": {"class": "GenericError", "desc": "Invalid parameter 'IDE'"}} {"error": {"class": "GenericError", "desc": "Parameter 'adapter-type' does not accept value 'IDE'"}}
{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "legacyesx", "driver": "vmdk", "file": "node0", "size": 33554432}}} {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "legacyesx", "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"error": {"class": "GenericError", "desc": "Invalid parameter 'legacyesx'"}} {"error": {"class": "GenericError", "desc": "Parameter 'adapter-type' does not accept value 'legacyesx'"}}
{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": 1, "driver": "vmdk", "file": "node0", "size": 33554432}}} {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": 1, "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"error": {"class": "GenericError", "desc": "Invalid parameter type for 'options.adapter-type', expected: string"}} {"error": {"class": "GenericError", "desc": "Invalid parameter type for 'options.adapter-type', expected: string"}}

View File

@ -149,7 +149,7 @@ class TestBlockdevReopen(iotests.QMPTestCase):
self.reopen(opts, {'node-name': ''}, "Failed to find node with node-name=''") self.reopen(opts, {'node-name': ''}, "Failed to find node with node-name=''")
self.reopen(opts, {'node-name': None}, "Invalid parameter type for 'options[0].node-name', expected: string") self.reopen(opts, {'node-name': None}, "Invalid parameter type for 'options[0].node-name', expected: string")
self.reopen(opts, {'driver': 'raw'}, "Cannot change the option 'driver'") self.reopen(opts, {'driver': 'raw'}, "Cannot change the option 'driver'")
self.reopen(opts, {'driver': ''}, "Invalid parameter ''") self.reopen(opts, {'driver': ''}, "Parameter 'driver' does not accept value ''")
self.reopen(opts, {'driver': None}, "Invalid parameter type for 'options[0].driver', expected: string") self.reopen(opts, {'driver': None}, "Invalid parameter type for 'options[0].driver', expected: string")
self.reopen(opts, {'file': 'not-found'}, "Cannot find device='' nor node-name='not-found'") self.reopen(opts, {'file': 'not-found'}, "Cannot find device='' nor node-name='not-found'")
self.reopen(opts, {'file': ''}, "Cannot find device='' nor node-name=''") self.reopen(opts, {'file': ''}, "Cannot find device='' nor node-name=''")

View File

@ -53,7 +53,7 @@ CLUSTER_SIZE=65536
# Check if we can run this test. # Check if we can run this test.
output=$(_make_test_img -o 'compression_type=zstd' 64M; _cleanup_test_img) output=$(_make_test_img -o 'compression_type=zstd' 64M; _cleanup_test_img)
if echo "$output" | grep -q "Invalid parameter 'zstd'"; then if echo "$output" | grep -q "Parameter 'compression-type' does not accept value 'zstd'"; then
_notrun "ZSTD is disabled" _notrun "ZSTD is disabled"
fi fi

View File

@ -148,7 +148,7 @@ rmdir "$EXT_MP" 2>/dev/null
rm -f "$EXT_MP" rm -f "$EXT_MP"
output=$(fuse_export_add 'export-err' "'mountpoint': '$EXT_MP'" error) output=$(fuse_export_add 'export-err' "'mountpoint': '$EXT_MP'" error)
if echo "$output" | grep -q "Invalid parameter 'fuse'"; then if echo "$output" | grep -q "Parameter 'type' does not accept value 'fuse'"; then
_notrun 'No FUSE support' _notrun 'No FUSE support'
fi fi

View File

@ -488,7 +488,7 @@ static void test_dummy_badenum(void)
g_assert(dobj == NULL); g_assert(dobj == NULL);
g_assert(err != NULL); g_assert(err != NULL);
g_assert_cmpstr(error_get_pretty(err), ==, g_assert_cmpstr(error_get_pretty(err), ==,
"Invalid parameter 'yeti'"); "Parameter 'av' does not accept value 'yeti'");
g_assert(object_resolve_path_component(parent, "dummy0") g_assert(object_resolve_path_component(parent, "dummy0")
== NULL); == NULL);