qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Steven Sistare <steven.sistare@oracle.com>
To: Markus Armbruster <armbru@redhat.com>
Cc: qemu-devel@nongnu.org, John Snow <jsnow@redhat.com>,
	Cleber Rosa <crosa@redhat.com>, Eric Blake <eblake@redhat.com>,
	Paolo Bonzini <pbonzini@redhat.com>,
	"Daniel P. Berrange" <berrange@redhat.com>,
	Eduardo Habkost <eduardo@habkost.net>,
	Fabiano Rosas <farosas@suse.de>,
	Laurent Vivier <lvivier@redhat.com>,
	Peter Krempa <pkrempa@redhat.com>,
	devel@lists.libvirt.org
Subject: Re: [PATCH V2 1/5] qom: qom-tree-get
Date: Mon, 7 Jul 2025 10:44:17 -0400	[thread overview]
Message-ID: <76271add-d9b3-4b45-a272-3cbe336c2103@oracle.com> (raw)
In-Reply-To: <877c0ono29.fsf@pond.sub.org>

On 7/4/2025 8:22 AM, Markus Armbruster wrote:
> Steve Sistare <steven.sistare@oracle.com> writes:
> 
>> Define the qom-tree-get QAPI command, which fetches an entire tree of
>> properties and values with a single QAPI call.  This is much faster
>> than using qom-list plus qom-get for every node and property of the
>> tree.  See qom.json for details.
>>
>> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
>> ---
>>   qapi/qom.json      | 56 ++++++++++++++++++++++++++++++++++++++++++
>>   qom/qom-qmp-cmds.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   2 files changed, 128 insertions(+)
>>
>> diff --git a/qapi/qom.json b/qapi/qom.json
>> index 28ce24c..94662ad 100644
>> --- a/qapi/qom.json
>> +++ b/qapi/qom.json
>> @@ -46,6 +46,38 @@
>>               '*default-value': 'any' } }
>>   
>>   ##
>> +# @ObjectPropertyValue:
>> +#
>> +# @name: the name of the property
>> +#
>> +# @type: the type of the property, as described in @ObjectPropertyInfo
> 
> That description is crap.  In part because what it tries to describe is
> crap.  Neither is this patch's problem.
> 
>> +#
>> +# @value: the value of the property.  Omitted if cannot be read.
> 
> Suggest "Absent when the property cannot be read."

OK.

>> +#
>> +# Since 10.1
>> +##
>> +{ 'struct': 'ObjectPropertyValue',
>> +  'data': { 'name': 'str',
>> +            'type': 'str',
>> +            '*value': 'any' } }
> 
> ObjectPropertyValue suggests this describes a property's value.

I would agree with you if the name included "info" or "desc", but it
does not.  To me, "ObjectPropertyValue" says this is an object's
property and value.  But it's subjective.

Perhaps: ObjectPropertyWithValue

>  It does
> not.  It includes the name, i.e. it describes the *property*.
> 
> So does ObjectPropertyInfo.
> 
> The two overlap: both habe name and type.  Only ObjectPropertyValue has
> the current value.  Only ObjectPropertyInfo has the default value and
> description (I suspect the latter is useless in practice).
> 
> ObjectPropertyInfo is used with qom-list and qom-list-properties.
> 
> qom-list takes a QOM path, like your qom-tree-get and qom-list-getv.
> I'd expect your commands to supersede qom-list in practice.
> 
> qom-list-properties is unlike your qom-tree-get and qom-list-getv: it
> takes a type name.  It's unreliable for non-abstract types: it can miss
> dynamically created properties.
> 
> Let's ignore all this for now.
> 
>> +
>> +##
>> +# @ObjectNode:
>> +#
>> +# @name: the name of the node
>> +#
>> +# @children: child nodes
>> +#
>> +# @properties: properties of the node
>> +#
>> +# Since 10.1
>> +##
>> +{ 'struct': 'ObjectNode',
>> +  'data': { 'name': 'str',
>> +            'children': [ 'ObjectNode' ],
>> +            'properties': [ 'ObjectPropertyValue' ] }}
>> +
>> +##
>>   # @qom-list:
>>   #
>>   # This command will list any properties of a object given a path in
>> @@ -126,6 +158,30 @@
>>     'allow-preconfig': true }
>>   
>>   ##
>> +# @qom-tree-get:
>> +#
>> +# This command returns a tree of objects and their properties,
>> +# rooted at the specified path.
>> +#
>> +# @path: The absolute or partial path within the object model, as
>> +#     described in @qom-get
>> +#
>> +# Errors:
>> +#     - If path is not valid or is ambiguous, returns an error.
> 
> By convention, we use "If <condition>, <error>, where <error> is a
> member of QapiErrorClass.

OK.  I was following the minimal Errors examples from this same file.

> What are the possible error classes?  As far as I can tell:
> 
>           - If path is ambiguous, GenericError
>           - If path cannot be resolved, DeviceNotFound
> 
> However, use of error classes other than GenericError is strongly
> discouraged (see error_set() in qapi/error.h).
> 
> Is the ability to distinguish between these two errors useful?
> 
> Existing related commands such as qom-get also use DeviceNotFound.
> Entirely undocumented, exact error conditions unclear.  Awesome.
> 
> Libvirt seems to rely on this undocumented behavior: I can see code
> checking for DeviceNotFound.  Hyrum's law strikes.
> 
> qom-get fails with DeviceNotFound in both of the above cases.  It fails
> with GenericError when @property doesn't exist or cannot be read.  Your
> qom-tree-get fails differently.  Awesome again.
> 
> Choices:
> 
> 1. Leave errors undocumented and inconsistent.
> 
> 2. Document errors for all related commands.  Make the new ones as
> consistent as we can.

Ignoring qom-tree-get since we are dropping it.

Do you prefer that qom-list-getv be consistent with qom-list (GenericError
and DeviceNotFound, as created by the common subroutine qom_resolve_path),
or only return GenericError with a customized message per best practices?

(Regardless, it will still succeed when @property cannot be read).

>> +#     - If a property cannot be read, the value field is omitted in
>> +#       the corresponding @ObjectPropertyValue.
> 
> This is not an error, and therefore doesn't belong here.
> ObjectPropertyValue's documentation also mentions it.  Good enough?

OK.

>> +#
>> +# Returns: A tree of @ObjectNode.  Each node contains its name, list
>> +#     of properties, and list of child nodes.
> 
> Hmm.
> 
> A struct Object has no name.  Only properties have a name.
> 
> An ObjectNode has a name, and an ObjectPropertyValue has a name.
> 
> I may get back to this in a later message.
> 
>> +#
>> +# Since 10.1
>> +##
>> +{ 'command': 'qom-tree-get',
>> +  'data': { 'path': 'str' },
>> +  'returns': 'ObjectNode',
>> +  'allow-preconfig': true }
>> +
>> +##
>>   # @qom-set:
>>   #
>>   # This command will set a property from a object model path.
>> diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c
>> index 293755f..b876681 100644
>> --- a/qom/qom-qmp-cmds.c
>> +++ b/qom/qom-qmp-cmds.c
>> @@ -69,6 +69,78 @@ ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp)
>>       return props;
>>   }
>>   
>> +static void qom_list_add_property_value(Object *obj, ObjectProperty *prop,
>> +                                        ObjectPropertyValueList **props)
>> +{
>> +    ObjectPropertyValue *item = g_new0(ObjectPropertyValue, 1);
>> +    Error *err = NULL;
>> +
>> +    QAPI_LIST_PREPEND(*props, item);
> 
> List elements are in reverse iteration order.  Not wrong.  I would've
> reached for QAPI_LIST_APPEND(), though.
> 
> Wait!  Existing command code uses QAPI_LIST_PREPEND().  Nevermind, carry
> on!

Exactly so.

>> +
>> +    item->name = g_strdup(prop->name);
>> +    item->type = g_strdup(prop->type);
>> +    item->value = object_property_get_qobject(obj, prop->name, &err);
>> +
>> +    if (!item->value) {
>> +        /*
>> +         * For bulk get, the error message is dropped, but the value field
>> +         * is omitted so the caller knows this property could not be read.
>> +         */
>> +        error_free(err);
> 
> Simpler: pass NULL to object_property_get_qobject().

Yes, thanks.

- Steve

>> +    }
>> +}
>> +
>> +static ObjectNode *qom_tree_get(const char *path, Error **errp)
>> +{
>> +    Object *obj;
>> +    ObjectProperty *prop;
>> +    ObjectNode *result, *child;
>> +    ObjectPropertyIterator iter;
>> +
>> +    obj = qom_resolve_path(path, errp);
>> +    if (obj == NULL) {
>> +        return NULL;
>> +    }
>> +
>> +    result = g_new0(ObjectNode, 1);
>> +
>> +    object_property_iter_init(&iter, obj);
>> +    while ((prop = object_property_iter_next(&iter))) {
>> +        if (strstart(prop->type, "child<", NULL)) {
>> +            g_autofree char *child_path = g_strdup_printf("%s/%s",
>> +                                                          path, prop->name);
>> +            child = qom_tree_get(child_path, errp);
>> +            if (!child) {
>> +                qapi_free_ObjectNode(result);
>> +                return NULL;
>> +            }
>> +            child->name = g_strdup(prop->name);
> 
> WAT?
> 
>> +            QAPI_LIST_PREPEND(result->children, child);
>> +        } else {
>> +            qom_list_add_property_value(obj, prop, &result->properties);
>> +        }
>> +    }
>> +
> 
> Oh, result->name remains unset, and the caller is expected to fill it
> in.  Two callers, "WAT" above, and ...
> 
>> +    return result;
>> +}
>> +
>> +ObjectNode *qmp_qom_tree_get(const char *path, Error **errp)
>> +{
>> +    ObjectNode *result = qom_tree_get(path, errp);
>> +
>> +    if (result) {
>> +        /* Strip the path prefix if any */
>> +        const char *basename = strrchr(path, '/');
>> +
>> +        if (!basename || !basename[1]) {
>> +            result->name = g_strdup(path);
>> +        } else {
>> +            result->name = g_strdup(basename + 1);
>> +        }
>> +    }
> 
> ... this one.
> 
> Not a fan.  But it works.
> 
>> +    return result;
>> +}
>> +
>>   void qmp_qom_set(const char *path, const char *property, QObject *value,
>>                    Error **errp)
>>   {
> 



  reply	other threads:[~2025-07-07 15:05 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-05-12 13:47 [PATCH V2 0/5] fast qom tree get Steve Sistare
2025-05-12 13:47 ` [PATCH V2 1/5] qom: qom-tree-get Steve Sistare
2025-07-04 12:22   ` Markus Armbruster
2025-07-07 14:44     ` Steven Sistare [this message]
2025-07-08  5:06       ` Markus Armbruster
2025-07-08  6:53         ` Markus Armbruster
2025-07-08  7:14   ` Philippe Mathieu-Daudé
2025-07-08 11:50     ` Steven Sistare
2025-07-08 15:17       ` Philippe Mathieu-Daudé
2025-05-12 13:47 ` [PATCH V2 2/5] python: use qom-tree-get Steve Sistare
2025-05-12 13:47 ` [PATCH V2 3/5] tests/qtest/qom-test: unit test for qom-tree-get Steve Sistare
2025-07-08  7:15   ` Philippe Mathieu-Daudé
2025-05-12 13:47 ` [PATCH V2 4/5] qom: qom-list-getv Steve Sistare
2025-07-04 12:22   ` Markus Armbruster
2025-07-07 14:40     ` Steven Sistare
2025-07-08  4:41       ` Markus Armbruster
2025-05-12 13:47 ` [PATCH V2 5/5] tests/qtest/qom-test: unit test for qom-list-getv Steve Sistare
2025-05-19 21:19 ` [PATCH V2 0/5] fast qom tree get Fabiano Rosas
2025-07-04 12:26 ` Markus Armbruster
2025-07-07 14:39   ` Steven Sistare
2025-07-04 12:33 ` Markus Armbruster
2025-07-07 14:39   ` Steven Sistare

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=76271add-d9b3-4b45-a272-3cbe336c2103@oracle.com \
    --to=steven.sistare@oracle.com \
    --cc=armbru@redhat.com \
    --cc=berrange@redhat.com \
    --cc=crosa@redhat.com \
    --cc=devel@lists.libvirt.org \
    --cc=eblake@redhat.com \
    --cc=eduardo@habkost.net \
    --cc=farosas@suse.de \
    --cc=jsnow@redhat.com \
    --cc=lvivier@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=pkrempa@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).