diff mbox

[qemu,v2] qmp: Add qom-list-properties to list QOM object properties

Message ID 20180226082259.40293-1-aik@ozlabs.ru (mailing list archive)
State New, archived
Headers show

Commit Message

Alexey Kardashevskiy Feb. 26, 2018, 8:22 a.m. UTC
There is already 'device-list-properties' which does most of the job,
however it does not handle everything returned by qom-list-types such
as machines as they inherit directly from TYPE_OBJECT and not TYPE_DEVICE.
It does not handle abstract classes either.

This adds a new qom-list-properties command which prints properties
of a specific class and its instance. It is pretty much a simplified copy
of the device-list-properties handler.

Since it creates an object instance, device properties should appear
in the output as they are copied to QOM properties at the instance_init
hook.

This adds a object_class_property_iter_init() helper to allow class
properties enumeration uses it in the new QMP command to allow properties
listing for abstract classes.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
Changes:
v2:
* added abstract classes support, now things like "pci-device" or
"spapr-machine" show properties, previously these would produce
an "abstract class" error
---
 qapi-schema.json     | 29 +++++++++++++++++++++++++++++
 include/qom/object.h | 16 ++++++++++++++++
 qmp.c                | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
 qom/object.c         |  7 +++++++
 4 files changed, 101 insertions(+)

Comments

Andrea Bolognani Feb. 28, 2018, 2:42 p.m. UTC | #1
On Mon, 2018-02-26 at 19:22 +1100, Alexey Kardashevskiy wrote:
> There is already 'device-list-properties' which does most of the job,
> however it does not handle everything returned by qom-list-types such
> as machines as they inherit directly from TYPE_OBJECT and not TYPE_DEVICE.
> It does not handle abstract classes either.
> 
> This adds a new qom-list-properties command which prints properties
> of a specific class and its instance. It is pretty much a simplified copy
> of the device-list-properties handler.
> 
> Since it creates an object instance, device properties should appear
> in the output as they are copied to QOM properties at the instance_init
> hook.
> 
> This adds a object_class_property_iter_init() helper to allow class
> properties enumeration uses it in the new QMP command to allow properties
> listing for abstract classes.
> 
> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
> ---
> Changes:
> v2:
> * added abstract classes support, now things like "pci-device" or
> "spapr-machine" show properties, previously these would produce
> an "abstract class" error

This is really nice, since it means libvirt won't have to figure
out the class name based on the default machine type but will be
able to simply list properties for the base class.

I'm going to implement support for this new command in libvirt
and report back if I run into any issue with the current design,
but it looks very good so far.
David Gibson March 1, 2018, 3:59 a.m. UTC | #2
On Mon, Feb 26, 2018 at 07:22:59PM +1100, Alexey Kardashevskiy wrote:
> There is already 'device-list-properties' which does most of the job,
> however it does not handle everything returned by qom-list-types such
> as machines as they inherit directly from TYPE_OBJECT and not TYPE_DEVICE.
> It does not handle abstract classes either.
> 
> This adds a new qom-list-properties command which prints properties
> of a specific class and its instance. It is pretty much a simplified copy
> of the device-list-properties handler.
> 
> Since it creates an object instance, device properties should appear
> in the output as they are copied to QOM properties at the instance_init
> hook.
> 
> This adds a object_class_property_iter_init() helper to allow class
> properties enumeration uses it in the new QMP command to allow properties
> listing for abstract classes.
> 
> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
> ---
> Changes:
> v2:
> * added abstract classes support, now things like "pci-device" or
> "spapr-machine" show properties, previously these would produce
> an "abstract class" error
> ---
>  qapi-schema.json     | 29 +++++++++++++++++++++++++++++
>  include/qom/object.h | 16 ++++++++++++++++
>  qmp.c                | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
>  qom/object.c         |  7 +++++++
>  4 files changed, 101 insertions(+)
> 
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 0262b9f..fa5f189 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -1455,6 +1455,35 @@
>    'returns': [ 'DevicePropertyInfo' ] }
>  
>  ##
> +# @QOMPropertyInfo:
> +#
> +# Information about object properties.
> +#
> +# @name: the name of the property
> +# @type: the typename of the property
> +# @description: if specified, the description of the property.
> +#
> +# Since: 2.12
> +##
> +{ 'struct': 'QOMPropertyInfo',
> +  'data': { 'name': 'str', 'type': 'str', '*description': 'str' } }

So, this has identical contents to DevicePropertyInfo, and is very
similar to ObjectPropertyInfo.  Is there any way we could consolidate
those types?

> +##
> +# @qom-list-properties:
> +#
> +# List properties associated with a QOM object.
> +#
> +# @typename: the type name of an object
> +#
> +# Returns: a list of QOMPropertyInfo describing object properties
> +#
> +# Since: 2.12
> +##
> +{ 'command': 'qom-list-properties',
> +  'data': { 'typename': 'str'},
> +  'returns': [ 'QOMPropertyInfo' ] }
> +
> +##
>  # @xen-set-global-dirty-log:
>  #
>  # Enable or disable the global dirty log mode.
> diff --git a/include/qom/object.h b/include/qom/object.h
> index dc73d59..ef07d78 100644
> --- a/include/qom/object.h
> +++ b/include/qom/object.h
> @@ -1017,6 +1017,22 @@ void object_property_iter_init(ObjectPropertyIterator *iter,
>                                 Object *obj);
>  
>  /**
> + * object_class_property_iter_init:
> + * @klass: the class
> + *
> + * Initializes an iterator for traversing all properties
> + * registered against an object class and all parent classes.
> + *
> + * It is forbidden to modify the property list while iterating,
> + * whether removing or adding properties.
> + *
> + * This can be used on abstract classes as it does not create a temporary
> + * instance.
> + */
> +void object_class_property_iter_init(ObjectPropertyIterator *iter,
> +                                     ObjectClass *klass);
> +
> +/**
>   * object_property_iter_next:
>   * @iter: the iterator instance
>   *
> diff --git a/qmp.c b/qmp.c
> index 793f6f3..151d3d7 100644
> --- a/qmp.c
> +++ b/qmp.c
> @@ -576,6 +576,55 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
>      return prop_list;
>  }
>  
> +QOMPropertyInfoList *qmp_qom_list_properties(const char *typename,
> +                                             Error **errp)
> +{
> +    ObjectClass *klass;
> +    Object *obj = NULL;
> +    ObjectProperty *prop;
> +    ObjectPropertyIterator iter;
> +    QOMPropertyInfoList *prop_list = NULL;
> +
> +    klass = object_class_by_name(typename);
> +    if (klass == NULL) {
> +        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
> +                  "Class '%s' not found", typename);
> +        return NULL;
> +    }
> +
> +    klass = object_class_dynamic_cast(klass, TYPE_OBJECT);
> +    if (klass == NULL) {
> +        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "typename", TYPE_OBJECT);
> +        return NULL;
> +    }
> +
> +    if (object_class_is_abstract(klass)) {
> +        object_class_property_iter_init(&iter, klass);

I like the idea of adding abstract classes in principle, but I'm a
little concerned about the effect of this in practice, because it only
lists class properties.

Although nearly all properties in qemu *should* be class properties,
they're nearly all defined as instance properties in practice.  AFAICT
this is just because most people didn't get the memo on class
properties and how to use them.

Just listing class properties in general might be ok - it would
encourage people to move things to class properties if they should
be.  However listing class properties in some cases and all properties
in other sounds like a recipe for confusion.

> +    } else {
> +        obj = object_new(typename);
> +        object_property_iter_init(&iter, obj);
> +    }
> +    while ((prop = object_property_iter_next(&iter))) {
> +        QOMPropertyInfo *info;
> +        QOMPropertyInfoList *entry;
> +
> +        info = g_malloc0(sizeof(*info));
> +        info->name = g_strdup(prop->name);
> +        info->type = g_strdup(prop->type);
> +        info->has_description = !!prop->description;
> +        info->description = g_strdup(prop->description);
> +
> +        entry = g_malloc0(sizeof(*entry));
> +        entry->value = info;
> +        entry->next = prop_list;
> +        prop_list = entry;
> +    }
> +
> +    object_unref(obj);
> +
> +    return prop_list;
> +}
> +
>  CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
>  {
>      return arch_query_cpu_definitions(errp);
> diff --git a/qom/object.c b/qom/object.c
> index 5dcee46..e7978bd 100644
> --- a/qom/object.c
> +++ b/qom/object.c
> @@ -1037,6 +1037,13 @@ ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter)
>      return val;
>  }
>  
> +void object_class_property_iter_init(ObjectPropertyIterator *iter,
> +                                     ObjectClass *klass)
> +{
> +    g_hash_table_iter_init(&iter->iter, klass->properties);
> +    iter->nextclass = klass;
> +}
> +
>  ObjectProperty *object_class_property_find(ObjectClass *klass, const char *name,
>                                             Error **errp)
>  {
Alexey Kardashevskiy March 1, 2018, 11:47 a.m. UTC | #3
On 01/03/18 14:59, David Gibson wrote:
> On Mon, Feb 26, 2018 at 07:22:59PM +1100, Alexey Kardashevskiy wrote:
>> There is already 'device-list-properties' which does most of the job,
>> however it does not handle everything returned by qom-list-types such
>> as machines as they inherit directly from TYPE_OBJECT and not TYPE_DEVICE.
>> It does not handle abstract classes either.
>>
>> This adds a new qom-list-properties command which prints properties
>> of a specific class and its instance. It is pretty much a simplified copy
>> of the device-list-properties handler.
>>
>> Since it creates an object instance, device properties should appear
>> in the output as they are copied to QOM properties at the instance_init
>> hook.
>>
>> This adds a object_class_property_iter_init() helper to allow class
>> properties enumeration uses it in the new QMP command to allow properties
>> listing for abstract classes.
>>
>> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
>> ---
>> Changes:
>> v2:
>> * added abstract classes support, now things like "pci-device" or
>> "spapr-machine" show properties, previously these would produce
>> an "abstract class" error
>> ---
>>  qapi-schema.json     | 29 +++++++++++++++++++++++++++++
>>  include/qom/object.h | 16 ++++++++++++++++
>>  qmp.c                | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
>>  qom/object.c         |  7 +++++++
>>  4 files changed, 101 insertions(+)
>>
>> diff --git a/qapi-schema.json b/qapi-schema.json
>> index 0262b9f..fa5f189 100644
>> --- a/qapi-schema.json
>> +++ b/qapi-schema.json
>> @@ -1455,6 +1455,35 @@
>>    'returns': [ 'DevicePropertyInfo' ] }
>>  
>>  ##
>> +# @QOMPropertyInfo:
>> +#
>> +# Information about object properties.
>> +#
>> +# @name: the name of the property
>> +# @type: the typename of the property
>> +# @description: if specified, the description of the property.
>> +#
>> +# Since: 2.12
>> +##
>> +{ 'struct': 'QOMPropertyInfo',
>> +  'data': { 'name': 'str', 'type': 'str', '*description': 'str' } }
> 
> So, this has identical contents to DevicePropertyInfo, and is very
> similar to ObjectPropertyInfo.  Is there any way we could consolidate
> those types?

Sure, I can get rid of DevicePropertyInfo in favour of QOMPropertyInfo, I
am just not sure if this typename is visible anywhere in the QMP protocol.
I'll post v3 in minutes.


> 
>> +##
>> +# @qom-list-properties:
>> +#
>> +# List properties associated with a QOM object.
>> +#
>> +# @typename: the type name of an object
>> +#
>> +# Returns: a list of QOMPropertyInfo describing object properties
>> +#
>> +# Since: 2.12
>> +##
>> +{ 'command': 'qom-list-properties',
>> +  'data': { 'typename': 'str'},
>> +  'returns': [ 'QOMPropertyInfo' ] }
>> +
>> +##
>>  # @xen-set-global-dirty-log:
>>  #
>>  # Enable or disable the global dirty log mode.
>> diff --git a/include/qom/object.h b/include/qom/object.h
>> index dc73d59..ef07d78 100644
>> --- a/include/qom/object.h
>> +++ b/include/qom/object.h
>> @@ -1017,6 +1017,22 @@ void object_property_iter_init(ObjectPropertyIterator *iter,
>>                                 Object *obj);
>>  
>>  /**
>> + * object_class_property_iter_init:
>> + * @klass: the class
>> + *
>> + * Initializes an iterator for traversing all properties
>> + * registered against an object class and all parent classes.
>> + *
>> + * It is forbidden to modify the property list while iterating,
>> + * whether removing or adding properties.
>> + *
>> + * This can be used on abstract classes as it does not create a temporary
>> + * instance.
>> + */
>> +void object_class_property_iter_init(ObjectPropertyIterator *iter,
>> +                                     ObjectClass *klass);
>> +
>> +/**
>>   * object_property_iter_next:
>>   * @iter: the iterator instance
>>   *
>> diff --git a/qmp.c b/qmp.c
>> index 793f6f3..151d3d7 100644
>> --- a/qmp.c
>> +++ b/qmp.c
>> @@ -576,6 +576,55 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
>>      return prop_list;
>>  }
>>  
>> +QOMPropertyInfoList *qmp_qom_list_properties(const char *typename,
>> +                                             Error **errp)
>> +{
>> +    ObjectClass *klass;
>> +    Object *obj = NULL;
>> +    ObjectProperty *prop;
>> +    ObjectPropertyIterator iter;
>> +    QOMPropertyInfoList *prop_list = NULL;
>> +
>> +    klass = object_class_by_name(typename);
>> +    if (klass == NULL) {
>> +        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
>> +                  "Class '%s' not found", typename);
>> +        return NULL;
>> +    }
>> +
>> +    klass = object_class_dynamic_cast(klass, TYPE_OBJECT);
>> +    if (klass == NULL) {
>> +        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "typename", TYPE_OBJECT);
>> +        return NULL;
>> +    }
>> +
>> +    if (object_class_is_abstract(klass)) {
>> +        object_class_property_iter_init(&iter, klass);
> 
> I like the idea of adding abstract classes in principle, but I'm a
> little concerned about the effect of this in practice, because it only
> lists class properties.
>> Although nearly all properties in qemu *should* be class properties,
> they're nearly all defined as instance properties in practice.  AFAICT
> this is just because most people didn't get the memo on class
> properties and how to use them.
> 
> Just listing class properties in general might be ok - it would
> encourage people to move things to class properties if they should
> be.  However listing class properties in some cases and all properties
> in other sounds like a recipe for confusion.

This lists all properties one can get for a specific type. For an abstract
class, an instance cannot be created, hence only class properties are
listed. Quite straight forward imho.




>> +    } else {
>> +        obj = object_new(typename);
>> +        object_property_iter_init(&iter, obj);
>> +    }
>> +    while ((prop = object_property_iter_next(&iter))) {
>> +        QOMPropertyInfo *info;
>> +        QOMPropertyInfoList *entry;
>> +
>> +        info = g_malloc0(sizeof(*info));
>> +        info->name = g_strdup(prop->name);
>> +        info->type = g_strdup(prop->type);
>> +        info->has_description = !!prop->description;
>> +        info->description = g_strdup(prop->description);
>> +
>> +        entry = g_malloc0(sizeof(*entry));
>> +        entry->value = info;
>> +        entry->next = prop_list;
>> +        prop_list = entry;
>> +    }
>> +
>> +    object_unref(obj);
>> +
>> +    return prop_list;
>> +}
>> +
>>  CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
>>  {
>>      return arch_query_cpu_definitions(errp);
>> diff --git a/qom/object.c b/qom/object.c
>> index 5dcee46..e7978bd 100644
>> --- a/qom/object.c
>> +++ b/qom/object.c
>> @@ -1037,6 +1037,13 @@ ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter)
>>      return val;
>>  }
>>  
>> +void object_class_property_iter_init(ObjectPropertyIterator *iter,
>> +                                     ObjectClass *klass)
>> +{
>> +    g_hash_table_iter_init(&iter->iter, klass->properties);
>> +    iter->nextclass = klass;
>> +}
>> +
>>  ObjectProperty *object_class_property_find(ObjectClass *klass, const char *name,
>>                                             Error **errp)
>>  {
>
Paolo Bonzini March 1, 2018, 11:49 a.m. UTC | #4
On 01/03/2018 12:47, Alexey Kardashevskiy wrote:
>>> +{ 'struct': 'QOMPropertyInfo',
>>> +  'data': { 'name': 'str', 'type': 'str', '*description': 'str' } }
>> So, this has identical contents to DevicePropertyInfo, and is very
>> similar to ObjectPropertyInfo.  Is there any way we could consolidate
>> those types?
> Sure, I can get rid of DevicePropertyInfo in favour of QOMPropertyInfo, I
> am just not sure if this typename is visible anywhere in the QMP protocol.
> I'll post v3 in minutes.

You can just extend ObjectPropertyInfo with the optional description.
It's the one with the best documentation.

The type names are (intentionally) hidden from the schema and are only
visible in the documentation.

Paolo
Eric Blake March 1, 2018, 8:18 p.m. UTC | #5
On 03/01/2018 05:47 AM, Alexey Kardashevskiy wrote:

>>> +##
>>> +{ 'struct': 'QOMPropertyInfo',
>>> +  'data': { 'name': 'str', 'type': 'str', '*description': 'str' } }
>>
>> So, this has identical contents to DevicePropertyInfo, and is very
>> similar to ObjectPropertyInfo.  Is there any way we could consolidate
>> those types?
> 
> Sure, I can get rid of DevicePropertyInfo in favour of QOMPropertyInfo, I
> am just not sure if this typename is visible anywhere in the QMP protocol.

Introspection doesn't expose type names; you are free to change those at 
will (clients like libvirt that parse introspection merely get a list of 
members and types, regardless of what typename those members belonged to).
Andrea Bolognani March 2, 2018, 10:09 a.m. UTC | #6
On Wed, 2018-02-28 at 15:42 +0100, Andrea Bolognani wrote:
> I'm going to implement support for this new command in libvirt
> and report back if I run into any issue with the current design,
> but it looks very good so far.

I've posted the RFC implementation to libvir-list:

  https://www.redhat.com/archives/libvir-list/2018-March/msg00042.html

Everything looks solid to me, so FWIW

  Tested-by: Andrea Bolognani <abologna@redhat.com>
diff mbox

Patch

diff --git a/qapi-schema.json b/qapi-schema.json
index 0262b9f..fa5f189 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1455,6 +1455,35 @@ 
   'returns': [ 'DevicePropertyInfo' ] }
 
 ##
+# @QOMPropertyInfo:
+#
+# Information about object properties.
+#
+# @name: the name of the property
+# @type: the typename of the property
+# @description: if specified, the description of the property.
+#
+# Since: 2.12
+##
+{ 'struct': 'QOMPropertyInfo',
+  'data': { 'name': 'str', 'type': 'str', '*description': 'str' } }
+
+##
+# @qom-list-properties:
+#
+# List properties associated with a QOM object.
+#
+# @typename: the type name of an object
+#
+# Returns: a list of QOMPropertyInfo describing object properties
+#
+# Since: 2.12
+##
+{ 'command': 'qom-list-properties',
+  'data': { 'typename': 'str'},
+  'returns': [ 'QOMPropertyInfo' ] }
+
+##
 # @xen-set-global-dirty-log:
 #
 # Enable or disable the global dirty log mode.
diff --git a/include/qom/object.h b/include/qom/object.h
index dc73d59..ef07d78 100644
--- a/include/qom/object.h
+++ b/include/qom/object.h
@@ -1017,6 +1017,22 @@  void object_property_iter_init(ObjectPropertyIterator *iter,
                                Object *obj);
 
 /**
+ * object_class_property_iter_init:
+ * @klass: the class
+ *
+ * Initializes an iterator for traversing all properties
+ * registered against an object class and all parent classes.
+ *
+ * It is forbidden to modify the property list while iterating,
+ * whether removing or adding properties.
+ *
+ * This can be used on abstract classes as it does not create a temporary
+ * instance.
+ */
+void object_class_property_iter_init(ObjectPropertyIterator *iter,
+                                     ObjectClass *klass);
+
+/**
  * object_property_iter_next:
  * @iter: the iterator instance
  *
diff --git a/qmp.c b/qmp.c
index 793f6f3..151d3d7 100644
--- a/qmp.c
+++ b/qmp.c
@@ -576,6 +576,55 @@  DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
     return prop_list;
 }
 
+QOMPropertyInfoList *qmp_qom_list_properties(const char *typename,
+                                             Error **errp)
+{
+    ObjectClass *klass;
+    Object *obj = NULL;
+    ObjectProperty *prop;
+    ObjectPropertyIterator iter;
+    QOMPropertyInfoList *prop_list = NULL;
+
+    klass = object_class_by_name(typename);
+    if (klass == NULL) {
+        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+                  "Class '%s' not found", typename);
+        return NULL;
+    }
+
+    klass = object_class_dynamic_cast(klass, TYPE_OBJECT);
+    if (klass == NULL) {
+        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "typename", TYPE_OBJECT);
+        return NULL;
+    }
+
+    if (object_class_is_abstract(klass)) {
+        object_class_property_iter_init(&iter, klass);
+    } else {
+        obj = object_new(typename);
+        object_property_iter_init(&iter, obj);
+    }
+    while ((prop = object_property_iter_next(&iter))) {
+        QOMPropertyInfo *info;
+        QOMPropertyInfoList *entry;
+
+        info = g_malloc0(sizeof(*info));
+        info->name = g_strdup(prop->name);
+        info->type = g_strdup(prop->type);
+        info->has_description = !!prop->description;
+        info->description = g_strdup(prop->description);
+
+        entry = g_malloc0(sizeof(*entry));
+        entry->value = info;
+        entry->next = prop_list;
+        prop_list = entry;
+    }
+
+    object_unref(obj);
+
+    return prop_list;
+}
+
 CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
 {
     return arch_query_cpu_definitions(errp);
diff --git a/qom/object.c b/qom/object.c
index 5dcee46..e7978bd 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1037,6 +1037,13 @@  ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter)
     return val;
 }
 
+void object_class_property_iter_init(ObjectPropertyIterator *iter,
+                                     ObjectClass *klass)
+{
+    g_hash_table_iter_init(&iter->iter, klass->properties);
+    iter->nextclass = klass;
+}
+
 ObjectProperty *object_class_property_find(ObjectClass *klass, const char *name,
                                            Error **errp)
 {