diff mbox series

[9/9] qapi: Extend -compat to set policy for unstable interfaces

Message ID 20211025052532.3859634-10-armbru@redhat.com
State New
Headers show
Series Configurable policy for handling unstable interfaces | expand

Commit Message

Markus Armbruster Oct. 25, 2021, 5:25 a.m. UTC
New option parameters unstable-input and unstable-output set policy
for unstable interfaces just like deprecated-input and
deprecated-output set policy for deprecated interfaces (see commit
6dd75472d5 "qemu-options: New -compat to set policy for deprecated
interfaces").  This is intended for testing users of the management
interfaces.  It is experimental.

For now, this covers only syntactic aspects of QMP, i.e. stuff tagged
with feature 'unstable'.  We may want to extend it to cover semantic
aspects, or the command line.

Note that there is no good way for management application to detect
presence of these new option parameters: they are not visible output
of query-qmp-schema or query-command-line-options.  Tolerable, because
it's meant for testing.  If running with -compat fails, skip the test.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 qapi/compat.json              |  6 +++++-
 include/qapi/util.h           |  1 +
 qapi/qmp-dispatch.c           |  6 ++++++
 qapi/qobject-output-visitor.c |  8 ++++++--
 qemu-options.hx               | 20 +++++++++++++++++++-
 scripts/qapi/events.py        | 10 ++++++----
 scripts/qapi/schema.py        | 10 ++++++----
 7 files changed, 49 insertions(+), 12 deletions(-)

Comments

John Snow Oct. 25, 2021, 7:40 p.m. UTC | #1
On Mon, Oct 25, 2021 at 1:26 AM Markus Armbruster <armbru@redhat.com> wrote:

> New option parameters unstable-input and unstable-output set policy
> for unstable interfaces just like deprecated-input and
> deprecated-output set policy for deprecated interfaces (see commit
> 6dd75472d5 "qemu-options: New -compat to set policy for deprecated
> interfaces").  This is intended for testing users of the management
> interfaces.  It is experimental.
>
> For now, this covers only syntactic aspects of QMP, i.e. stuff tagged
> with feature 'unstable'.  We may want to extend it to cover semantic
> aspects, or the command line.
>
> Note that there is no good way for management application to detect
> presence of these new option parameters: they are not visible output
> of query-qmp-schema or query-command-line-options.  Tolerable, because
> it's meant for testing.  If running with -compat fails, skip the test.
>
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  qapi/compat.json              |  6 +++++-
>  include/qapi/util.h           |  1 +
>  qapi/qmp-dispatch.c           |  6 ++++++
>  qapi/qobject-output-visitor.c |  8 ++++++--
>  qemu-options.hx               | 20 +++++++++++++++++++-
>  scripts/qapi/events.py        | 10 ++++++----
>  scripts/qapi/schema.py        | 10 ++++++----
>  7 files changed, 49 insertions(+), 12 deletions(-)
>
> diff --git a/qapi/compat.json b/qapi/compat.json
> index 74a8493d3d..9bc9804abb 100644
> --- a/qapi/compat.json
> +++ b/qapi/compat.json
> @@ -47,9 +47,13 @@
>  #
>  # @deprecated-input: how to handle deprecated input (default 'accept')
>  # @deprecated-output: how to handle deprecated output (default 'accept')
> +# @unstable-input: how to handle unstable input (default 'accept')
> +# @unstable-output: how to handle unstable output (default 'accept')
>  #
>  # Since: 6.0
>  ##
>  { 'struct': 'CompatPolicy',
>    'data': { '*deprecated-input': 'CompatPolicyInput',
> -            '*deprecated-output': 'CompatPolicyOutput' } }
> +            '*deprecated-output': 'CompatPolicyOutput',
> +            '*unstable-input': 'CompatPolicyInput',
> +            '*unstable-output': 'CompatPolicyOutput' } }
> diff --git a/include/qapi/util.h b/include/qapi/util.h
> index 0cc98db9f9..81a2b13a33 100644
> --- a/include/qapi/util.h
> +++ b/include/qapi/util.h
> @@ -13,6 +13,7 @@
>
>  typedef enum {
>      QAPI_DEPRECATED,
> +    QAPI_UNSTABLE,
>  } QapiSpecialFeature;
>
>  typedef struct QEnumLookup {
> diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
> index e29ade134c..c5c6e521a2 100644
> --- a/qapi/qmp-dispatch.c
> +++ b/qapi/qmp-dispatch.c
> @@ -59,6 +59,12 @@ bool compat_policy_input_ok(unsigned special_features,
>                                      error_class, kind, name, errp)) {
>          return false;
>      }
> +    if ((special_features & (1u << QAPI_UNSTABLE))
> +        && !compat_policy_input_ok1("Unstable",
> +                                    policy->unstable_input,
> +                                    error_class, kind, name, errp)) {
> +        return false;
> +    }
>      return true;
>  }
>
> diff --git a/qapi/qobject-output-visitor.c b/qapi/qobject-output-visitor.c
> index b5c6564cbb..74770edd73 100644
> --- a/qapi/qobject-output-visitor.c
> +++ b/qapi/qobject-output-visitor.c
> @@ -212,8 +212,12 @@ static bool qobject_output_type_null(Visitor *v,
> const char *name,
>  static bool qobject_output_policy_skip(Visitor *v, const char *name,
>                                         unsigned special_features)
>  {
> -    return !(special_features && 1u << QAPI_DEPRECATED)
> -        || v->compat_policy.deprecated_output ==
> COMPAT_POLICY_OUTPUT_HIDE;
> +    CompatPolicy *pol = &v->compat_policy;
> +
> +    return ((special_features & 1u << QAPI_DEPRECATED)
> +            && pol->deprecated_output == COMPAT_POLICY_OUTPUT_HIDE)
> +        || ((special_features & 1u << QAPI_UNSTABLE)
> +            && pol->unstable_output == COMPAT_POLICY_OUTPUT_HIDE);
>  }
>
>  /* Finish building, and return the root object.
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 5f375bbfa6..f051536b63 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -3641,7 +3641,9 @@ DEFHEADING(Debug/Expert options:)
>
>  DEF("compat", HAS_ARG, QEMU_OPTION_compat,
>      "-compat
> [deprecated-input=accept|reject|crash][,deprecated-output=accept|hide]\n"
> -    "                Policy for handling deprecated management
> interfaces\n",
> +    "                Policy for handling deprecated management
> interfaces\n"
> +    "-compat
> [unstable-input=accept|reject|crash][,unstable-output=accept|hide]\n"
> +    "                Policy for handling unstable management
> interfaces\n",
>      QEMU_ARCH_ALL)
>  SRST
>  ``-compat
> [deprecated-input=@var{input-policy}][,deprecated-output=@var{output-policy}]``
> @@ -3659,6 +3661,22 @@ SRST
>          Suppress deprecated command results and events
>
>      Limitation: covers only syntactic aspects of QMP.
> +
> +``-compat
> [unstable-input=@var{input-policy}][,unstable-output=@var{output-policy}]``
> +    Set policy for handling unstable management interfaces (experimental):
> +
> +    ``unstable-input=accept`` (default)
> +        Accept unstable commands and arguments
> +    ``unstable-input=reject``
> +        Reject unstable commands and arguments
> +    ``unstable-input=crash``
> +        Crash on unstable commands and arguments
> +    ``unstable-output=accept`` (default)
> +        Emit unstable command results and events
> +    ``unstable-output=hide``
> +        Suppress unstable command results and events
> +
> +    Limitation: covers only syntactic aspects of QMP.
>  ERST
>
>  DEF("fw_cfg", HAS_ARG, QEMU_OPTION_fwcfg,
> diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py
> index 82475e84ec..27b44c49f5 100644
> --- a/scripts/qapi/events.py
> +++ b/scripts/qapi/events.py
> @@ -109,13 +109,15 @@ def gen_event_send(name: str,
>          if not boxed:
>              ret += gen_param_var(arg_type)
>
> -    if 'deprecated' in [f.name for f in features]:
> -        ret += mcgen('''
> +    for f in features:
> +        if f.is_special():
> +            ret += mcgen('''
>
> -    if (compat_policy.deprecated_output == COMPAT_POLICY_OUTPUT_HIDE) {
> +    if (compat_policy.%(feat)s_output == COMPAT_POLICY_OUTPUT_HIDE) {
>          return;
>      }
> -''')
> +''',
> +                         feat=f.name)
>
>      ret += mcgen('''
>
> diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
> index 55f82d7389..b7b3fc0ce4 100644
> --- a/scripts/qapi/schema.py
> +++ b/scripts/qapi/schema.py
> @@ -254,9 +254,11 @@ def doc_type(self):
>
>      def check(self, schema):
>          QAPISchemaEntity.check(self, schema)
> -        if 'deprecated' in [f.name for f in self.features]:
> -            raise QAPISemError(
> -                self.info, "feature 'deprecated' is not supported for
> types")
> +        for feat in self.features:
> +            if feat.is_special():
> +                raise QAPISemError(
> +                    self.info,
> +                    f"feature '{feat.name}' is not supported for types")
>
>      def describe(self):
>          assert self.meta
> @@ -726,7 +728,7 @@ class QAPISchemaFeature(QAPISchemaMember):
>      role = 'feature'
>
>      def is_special(self):
> -        return self.name in ('deprecated')
> +        return self.name in ('deprecated', 'unstable')
>
>
>  class QAPISchemaObjectTypeMember(QAPISchemaMember):
> --
> 2.31.1
>
>
Python bits: Acked-by: John Snow <jsnow@redhat.com>

Looks good overall from what I can see, minor style quibbles that I'd
probably fold on if you frowned at me.

--js
Dr. David Alan Gilbert Oct. 26, 2021, 6:45 p.m. UTC | #2
* Markus Armbruster (armbru@redhat.com) wrote:
> New option parameters unstable-input and unstable-output set policy
> for unstable interfaces just like deprecated-input and
> deprecated-output set policy for deprecated interfaces (see commit
> 6dd75472d5 "qemu-options: New -compat to set policy for deprecated
> interfaces").  This is intended for testing users of the management
> interfaces.  It is experimental.

So is there no way to mark the option as unstable?

Dave

> For now, this covers only syntactic aspects of QMP, i.e. stuff tagged
> with feature 'unstable'.  We may want to extend it to cover semantic
> aspects, or the command line.
> 
> Note that there is no good way for management application to detect
> presence of these new option parameters: they are not visible output
> of query-qmp-schema or query-command-line-options.  Tolerable, because
> it's meant for testing.  If running with -compat fails, skip the test.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  qapi/compat.json              |  6 +++++-
>  include/qapi/util.h           |  1 +
>  qapi/qmp-dispatch.c           |  6 ++++++
>  qapi/qobject-output-visitor.c |  8 ++++++--
>  qemu-options.hx               | 20 +++++++++++++++++++-
>  scripts/qapi/events.py        | 10 ++++++----
>  scripts/qapi/schema.py        | 10 ++++++----
>  7 files changed, 49 insertions(+), 12 deletions(-)
> 
> diff --git a/qapi/compat.json b/qapi/compat.json
> index 74a8493d3d..9bc9804abb 100644
> --- a/qapi/compat.json
> +++ b/qapi/compat.json
> @@ -47,9 +47,13 @@
>  #
>  # @deprecated-input: how to handle deprecated input (default 'accept')
>  # @deprecated-output: how to handle deprecated output (default 'accept')
> +# @unstable-input: how to handle unstable input (default 'accept')
> +# @unstable-output: how to handle unstable output (default 'accept')
>  #
>  # Since: 6.0
>  ##
>  { 'struct': 'CompatPolicy',
>    'data': { '*deprecated-input': 'CompatPolicyInput',
> -            '*deprecated-output': 'CompatPolicyOutput' } }
> +            '*deprecated-output': 'CompatPolicyOutput',
> +            '*unstable-input': 'CompatPolicyInput',
> +            '*unstable-output': 'CompatPolicyOutput' } }
> diff --git a/include/qapi/util.h b/include/qapi/util.h
> index 0cc98db9f9..81a2b13a33 100644
> --- a/include/qapi/util.h
> +++ b/include/qapi/util.h
> @@ -13,6 +13,7 @@
>  
>  typedef enum {
>      QAPI_DEPRECATED,
> +    QAPI_UNSTABLE,
>  } QapiSpecialFeature;
>  
>  typedef struct QEnumLookup {
> diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
> index e29ade134c..c5c6e521a2 100644
> --- a/qapi/qmp-dispatch.c
> +++ b/qapi/qmp-dispatch.c
> @@ -59,6 +59,12 @@ bool compat_policy_input_ok(unsigned special_features,
>                                      error_class, kind, name, errp)) {
>          return false;
>      }
> +    if ((special_features & (1u << QAPI_UNSTABLE))
> +        && !compat_policy_input_ok1("Unstable",
> +                                    policy->unstable_input,
> +                                    error_class, kind, name, errp)) {
> +        return false;
> +    }
>      return true;
>  }
>  
> diff --git a/qapi/qobject-output-visitor.c b/qapi/qobject-output-visitor.c
> index b5c6564cbb..74770edd73 100644
> --- a/qapi/qobject-output-visitor.c
> +++ b/qapi/qobject-output-visitor.c
> @@ -212,8 +212,12 @@ static bool qobject_output_type_null(Visitor *v, const char *name,
>  static bool qobject_output_policy_skip(Visitor *v, const char *name,
>                                         unsigned special_features)
>  {
> -    return !(special_features && 1u << QAPI_DEPRECATED)
> -        || v->compat_policy.deprecated_output == COMPAT_POLICY_OUTPUT_HIDE;
> +    CompatPolicy *pol = &v->compat_policy;
> +
> +    return ((special_features & 1u << QAPI_DEPRECATED)
> +            && pol->deprecated_output == COMPAT_POLICY_OUTPUT_HIDE)
> +        || ((special_features & 1u << QAPI_UNSTABLE)
> +            && pol->unstable_output == COMPAT_POLICY_OUTPUT_HIDE);
>  }
>  
>  /* Finish building, and return the root object.
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 5f375bbfa6..f051536b63 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -3641,7 +3641,9 @@ DEFHEADING(Debug/Expert options:)
>  
>  DEF("compat", HAS_ARG, QEMU_OPTION_compat,
>      "-compat [deprecated-input=accept|reject|crash][,deprecated-output=accept|hide]\n"
> -    "                Policy for handling deprecated management interfaces\n",
> +    "                Policy for handling deprecated management interfaces\n"
> +    "-compat [unstable-input=accept|reject|crash][,unstable-output=accept|hide]\n"
> +    "                Policy for handling unstable management interfaces\n",
>      QEMU_ARCH_ALL)
>  SRST
>  ``-compat [deprecated-input=@var{input-policy}][,deprecated-output=@var{output-policy}]``
> @@ -3659,6 +3661,22 @@ SRST
>          Suppress deprecated command results and events
>  
>      Limitation: covers only syntactic aspects of QMP.
> +
> +``-compat [unstable-input=@var{input-policy}][,unstable-output=@var{output-policy}]``
> +    Set policy for handling unstable management interfaces (experimental):
> +
> +    ``unstable-input=accept`` (default)
> +        Accept unstable commands and arguments
> +    ``unstable-input=reject``
> +        Reject unstable commands and arguments
> +    ``unstable-input=crash``
> +        Crash on unstable commands and arguments
> +    ``unstable-output=accept`` (default)
> +        Emit unstable command results and events
> +    ``unstable-output=hide``
> +        Suppress unstable command results and events
> +
> +    Limitation: covers only syntactic aspects of QMP.
>  ERST
>  
>  DEF("fw_cfg", HAS_ARG, QEMU_OPTION_fwcfg,
> diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py
> index 82475e84ec..27b44c49f5 100644
> --- a/scripts/qapi/events.py
> +++ b/scripts/qapi/events.py
> @@ -109,13 +109,15 @@ def gen_event_send(name: str,
>          if not boxed:
>              ret += gen_param_var(arg_type)
>  
> -    if 'deprecated' in [f.name for f in features]:
> -        ret += mcgen('''
> +    for f in features:
> +        if f.is_special():
> +            ret += mcgen('''
>  
> -    if (compat_policy.deprecated_output == COMPAT_POLICY_OUTPUT_HIDE) {
> +    if (compat_policy.%(feat)s_output == COMPAT_POLICY_OUTPUT_HIDE) {
>          return;
>      }
> -''')
> +''',
> +                         feat=f.name)
>  
>      ret += mcgen('''
>  
> diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
> index 55f82d7389..b7b3fc0ce4 100644
> --- a/scripts/qapi/schema.py
> +++ b/scripts/qapi/schema.py
> @@ -254,9 +254,11 @@ def doc_type(self):
>  
>      def check(self, schema):
>          QAPISchemaEntity.check(self, schema)
> -        if 'deprecated' in [f.name for f in self.features]:
> -            raise QAPISemError(
> -                self.info, "feature 'deprecated' is not supported for types")
> +        for feat in self.features:
> +            if feat.is_special():
> +                raise QAPISemError(
> +                    self.info,
> +                    f"feature '{feat.name}' is not supported for types")
>  
>      def describe(self):
>          assert self.meta
> @@ -726,7 +728,7 @@ class QAPISchemaFeature(QAPISchemaMember):
>      role = 'feature'
>  
>      def is_special(self):
> -        return self.name in ('deprecated')
> +        return self.name in ('deprecated', 'unstable')
>  
>  
>  class QAPISchemaObjectTypeMember(QAPISchemaMember):
> -- 
> 2.31.1
>
Markus Armbruster Oct. 27, 2021, 5:59 a.m. UTC | #3
"Dr. David Alan Gilbert" <dgilbert@redhat.com> writes:

> * Markus Armbruster (armbru@redhat.com) wrote:
>> New option parameters unstable-input and unstable-output set policy
>> for unstable interfaces just like deprecated-input and
>> deprecated-output set policy for deprecated interfaces (see commit
>> 6dd75472d5 "qemu-options: New -compat to set policy for deprecated
>> interfaces").  This is intended for testing users of the management
>> interfaces.  It is experimental.
>
> So is there no way to mark the option as unstable?

The closest we have to marking command line options as unstable is
putting them under "Debug/Expert options" in -help.

For option *parameters*, we sometimes use the x- convention.

QAPIfication will give us better tools.
diff mbox series

Patch

diff --git a/qapi/compat.json b/qapi/compat.json
index 74a8493d3d..9bc9804abb 100644
--- a/qapi/compat.json
+++ b/qapi/compat.json
@@ -47,9 +47,13 @@ 
 #
 # @deprecated-input: how to handle deprecated input (default 'accept')
 # @deprecated-output: how to handle deprecated output (default 'accept')
+# @unstable-input: how to handle unstable input (default 'accept')
+# @unstable-output: how to handle unstable output (default 'accept')
 #
 # Since: 6.0
 ##
 { 'struct': 'CompatPolicy',
   'data': { '*deprecated-input': 'CompatPolicyInput',
-            '*deprecated-output': 'CompatPolicyOutput' } }
+            '*deprecated-output': 'CompatPolicyOutput',
+            '*unstable-input': 'CompatPolicyInput',
+            '*unstable-output': 'CompatPolicyOutput' } }
diff --git a/include/qapi/util.h b/include/qapi/util.h
index 0cc98db9f9..81a2b13a33 100644
--- a/include/qapi/util.h
+++ b/include/qapi/util.h
@@ -13,6 +13,7 @@ 
 
 typedef enum {
     QAPI_DEPRECATED,
+    QAPI_UNSTABLE,
 } QapiSpecialFeature;
 
 typedef struct QEnumLookup {
diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
index e29ade134c..c5c6e521a2 100644
--- a/qapi/qmp-dispatch.c
+++ b/qapi/qmp-dispatch.c
@@ -59,6 +59,12 @@  bool compat_policy_input_ok(unsigned special_features,
                                     error_class, kind, name, errp)) {
         return false;
     }
+    if ((special_features & (1u << QAPI_UNSTABLE))
+        && !compat_policy_input_ok1("Unstable",
+                                    policy->unstable_input,
+                                    error_class, kind, name, errp)) {
+        return false;
+    }
     return true;
 }
 
diff --git a/qapi/qobject-output-visitor.c b/qapi/qobject-output-visitor.c
index b5c6564cbb..74770edd73 100644
--- a/qapi/qobject-output-visitor.c
+++ b/qapi/qobject-output-visitor.c
@@ -212,8 +212,12 @@  static bool qobject_output_type_null(Visitor *v, const char *name,
 static bool qobject_output_policy_skip(Visitor *v, const char *name,
                                        unsigned special_features)
 {
-    return !(special_features && 1u << QAPI_DEPRECATED)
-        || v->compat_policy.deprecated_output == COMPAT_POLICY_OUTPUT_HIDE;
+    CompatPolicy *pol = &v->compat_policy;
+
+    return ((special_features & 1u << QAPI_DEPRECATED)
+            && pol->deprecated_output == COMPAT_POLICY_OUTPUT_HIDE)
+        || ((special_features & 1u << QAPI_UNSTABLE)
+            && pol->unstable_output == COMPAT_POLICY_OUTPUT_HIDE);
 }
 
 /* Finish building, and return the root object.
diff --git a/qemu-options.hx b/qemu-options.hx
index 5f375bbfa6..f051536b63 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3641,7 +3641,9 @@  DEFHEADING(Debug/Expert options:)
 
 DEF("compat", HAS_ARG, QEMU_OPTION_compat,
     "-compat [deprecated-input=accept|reject|crash][,deprecated-output=accept|hide]\n"
-    "                Policy for handling deprecated management interfaces\n",
+    "                Policy for handling deprecated management interfaces\n"
+    "-compat [unstable-input=accept|reject|crash][,unstable-output=accept|hide]\n"
+    "                Policy for handling unstable management interfaces\n",
     QEMU_ARCH_ALL)
 SRST
 ``-compat [deprecated-input=@var{input-policy}][,deprecated-output=@var{output-policy}]``
@@ -3659,6 +3661,22 @@  SRST
         Suppress deprecated command results and events
 
     Limitation: covers only syntactic aspects of QMP.
+
+``-compat [unstable-input=@var{input-policy}][,unstable-output=@var{output-policy}]``
+    Set policy for handling unstable management interfaces (experimental):
+
+    ``unstable-input=accept`` (default)
+        Accept unstable commands and arguments
+    ``unstable-input=reject``
+        Reject unstable commands and arguments
+    ``unstable-input=crash``
+        Crash on unstable commands and arguments
+    ``unstable-output=accept`` (default)
+        Emit unstable command results and events
+    ``unstable-output=hide``
+        Suppress unstable command results and events
+
+    Limitation: covers only syntactic aspects of QMP.
 ERST
 
 DEF("fw_cfg", HAS_ARG, QEMU_OPTION_fwcfg,
diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py
index 82475e84ec..27b44c49f5 100644
--- a/scripts/qapi/events.py
+++ b/scripts/qapi/events.py
@@ -109,13 +109,15 @@  def gen_event_send(name: str,
         if not boxed:
             ret += gen_param_var(arg_type)
 
-    if 'deprecated' in [f.name for f in features]:
-        ret += mcgen('''
+    for f in features:
+        if f.is_special():
+            ret += mcgen('''
 
-    if (compat_policy.deprecated_output == COMPAT_POLICY_OUTPUT_HIDE) {
+    if (compat_policy.%(feat)s_output == COMPAT_POLICY_OUTPUT_HIDE) {
         return;
     }
-''')
+''',
+                         feat=f.name)
 
     ret += mcgen('''
 
diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 55f82d7389..b7b3fc0ce4 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -254,9 +254,11 @@  def doc_type(self):
 
     def check(self, schema):
         QAPISchemaEntity.check(self, schema)
-        if 'deprecated' in [f.name for f in self.features]:
-            raise QAPISemError(
-                self.info, "feature 'deprecated' is not supported for types")
+        for feat in self.features:
+            if feat.is_special():
+                raise QAPISemError(
+                    self.info,
+                    f"feature '{feat.name}' is not supported for types")
 
     def describe(self):
         assert self.meta
@@ -726,7 +728,7 @@  class QAPISchemaFeature(QAPISchemaMember):
     role = 'feature'
 
     def is_special(self):
-        return self.name in ('deprecated')
+        return self.name in ('deprecated', 'unstable')
 
 
 class QAPISchemaObjectTypeMember(QAPISchemaMember):