On 02/11/2017 05:29 PM, John Ferlan wrote:
> Introduce a new generic virPoolObj[Ptr] and virPoolObjTable[Ptr] in order
> to generically manage driver lists of objects rather than the current
> hodgepodge of some drivers using a forward linked list and other drivers
> using a single UUID only based hash table to look up objects.
>
> The concept is to mimic the domain objects which keep objects in a UUID
> hash table and a name hash table for faster lookup. There's also generic
> API's in order to Lock, Reference, Unreference, and Unlock the objects to
> remove the need for a heavy handed driver lock.
>
> Since these are Hash Table objects rather than List Objects, the structures
> and API's have been named thusly. These are all based upon the existing
> generic object/class model so allocation/freeing is using existing API's.
>
> In addition to common structures and API's, as each driver is converted
> the driver specific ACL filter API's can be replaced by a generic pool
> ACL filter API.
>
> Each of the API's is designed to have a consistency with respect to returning
> a locked and referenced object, so that a virPoolObjEndAPI can be called in
> order to dereference and unlock the object.
>
> The ultimate goal is to have a single way to think about these objects
> management so that the various drivers don't have to have special or different
> code in order to manage based on the "style" of list/table the driver
needs
> to manage.
>
> A _virPoolObj uses a fairly generic structure:
>
> virObjectLockable parent;
> bool flags
> virPoolDefPtr pooldef;
> void *def;
> void *newDef;
> virFreeCallback defFreeFunc;
> void *privateData;
> void (*privateDataFreeFunc)(void *);
>
> Access to the fields of the object is handled via API's to fetch or set
> the various fields rather than allowing the calling driver to manage the
> object directly. This ensures a more consistent look and feel to the code
> for each driver.
>
> The "def" and "newDef" field along with the defFreeFunc will
point at
> the driver's specific vir*DefPtr data and the FreeFunc handles the free
> of the data when the object is finally removed. The vir*DefPtr API's will
> each need to be modified to take an "void *opaque" and assign that to the
> specific driver definition type.
>
> Most drivers have no need for privateData; however, a few drivers will
> need to manage driver specific object private data. For those drivers
> additional object management code is generated in order to manage the
> Get, Set, and test of the private data. The goal being to have one set
> of API's to manage the data rather than drivers directly touching it.
>
> A _virPoolObjTable is another generic structure:
>
> virObjectLockable parent;
> virPoolObjTableType type;
> bool nameOnly;
> int hashStart;
> virHashTable *objsUuid;
> virHashTable *objsName;
>
> Not every driver supports UUID's, so the UUID table isn't required, but
> it is the preferred lookup mechanism. The bool 'nameOnly' will help the
> table code make those decisions.
>
> The various Pool Object Table API's are taken from the current object list
> managements and converted to use tables.
>
> Objects are added to the table allowing for a callback mechanism to handle
> driver specific "assignment" options when the object already appears in
the
> table and the driver would be managing the replacement of the "existing"
> object "def".
>
> There are common API's to Add, Clear, Remove, Search, Iterate, Collect,
> List, Clone, and Prune objects. Each uses the same common methodology to
> lock the table list, use the virHash* APIs, and unlock the table list
> returning the requested data for the operation. Generally that means
> a locked and referenced object. The virPoolObjEndAPI will handle the
> dereference and unlocking.
>
> Add new error code for the new poolobj subsystem
>
> Signed-off-by: John Ferlan <jferlan(a)redhat.com>
> ---
> include/libvirt/virterror.h | 1 +
> po/POTFILES.in | 1 +
> src/Makefile.am | 5 +
> src/conf/virpoolobj.c | 1184 +++++++++++++++++++++++++++++++++++++++++++
> src/conf/virpoolobj.h | 222 ++++++++
> src/libvirt_private.syms | 33 ++
> src/util/virerror.c | 1 +
> 7 files changed, 1447 insertions(+)
> create mode 100644 src/conf/virpoolobj.c
> create mode 100644 src/conf/virpoolobj.h
This is rather huge change. But let me see if I can review it. One thing
though - I would prefer if this would introduce only the bare minimum
needed to transform one driver (say node_device). Any extended APIs can
be introduced then when you need them (e.g. I don't see
virPoolObjSetAutostart() used until 8/9. That way the initial size of
this patch could be brought down and thus easier for review.
>
> diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
> index 2efee8f..2174334 100644
> --- a/include/libvirt/virterror.h
> +++ b/include/libvirt/virterror.h
> @@ -132,6 +132,7 @@ typedef enum {
>
> VIR_FROM_PERF = 65, /* Error from perf */
> VIR_FROM_LIBSSH = 66, /* Error from libssh connection transport */
> + VIR_FROM_POOLOBJ = 67, /* Error from the poolobj code */
>
> # ifdef VIR_ENUM_SENTINELS
> VIR_ERR_DOMAIN_LAST
> diff --git a/po/POTFILES.in b/po/POTFILES.in
> index 365ea66..95fa9a6 100644
> --- a/po/POTFILES.in
> +++ b/po/POTFILES.in
> @@ -42,6 +42,7 @@ src/conf/snapshot_conf.c
> src/conf/storage_conf.c
> src/conf/virchrdev.c
> src/conf/virdomainobjlist.c
> +src/conf/virpoolobj.c
> src/conf/virsecretobj.c
> src/cpu/cpu.c
> src/cpu/cpu_arm.c
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 2f32d41..03a8cf3 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -373,6 +373,10 @@ NWFILTER_CONF_SOURCES = \
> $(NWFILTER_PARAM_CONF_SOURCES) \
> conf/nwfilter_conf.c conf/nwfilter_conf.h
>
> +# "Generic" poolobj sources
> +POOLOBJ_SOURCES = \
> + conf/virpoolobj.h conf/virpoolobj.c
> +
virPoolObj is not bad, but I might prefer virObjectList or something
that suggests this is a list of objects, not an list turned into
virObject. I mean, to me "Obj" suffix suggests that I'm dealing with an
object (which is true as well), but I think the "list-ness" (made up
word describing list quality) is more important here. You know what I mean?
> # Storage driver generic impl APIs
> STORAGE_CONF_SOURCES = \
> conf/storage_conf.h conf/storage_conf.c
> @@ -405,6 +409,7 @@ CONF_SOURCES = \
> $(NETDEV_CONF_SOURCES) \
> $(DOMAIN_CONF_SOURCES) \
> $(OBJECT_EVENT_SOURCES) \
> + $(POOLOBJ_SOURCES) \
> $(DOMAIN_EVENT_SOURCES) \
> $(NETWORK_EVENT_SOURCES) \
> $(STORAGE_EVENT_SOURCES) \
> diff --git a/src/conf/virpoolobj.c b/src/conf/virpoolobj.c
> new file mode 100644
> index 0000000..bca6524
> --- /dev/null
> +++ b/src/conf/virpoolobj.c
> @@ -0,0 +1,1184 @@
> +/*
> + * virpoolobj.c: internal pool objects handling
> + *
> + * Copyright (C) 2016 Red Hat, Inc.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library. If not, see
> + * <
http://www.gnu.org/licenses/>.
> + */
> +
> +#include <config.h>
> +
> +#include "datatypes.h"
> +#include "viralloc.h"
> +#include "virpoolobj.h"
> +#include "virerror.h"
> +#include "virlog.h"
> +#include "virhash.h"
> +#include "virstring.h"
> +
> +#define VIR_FROM_THIS VIR_FROM_POOLOBJ
> +
> +VIR_LOG_INIT("conf.virpoolobj");
> +
> +VIR_ENUM_DECL(virPoolObjTable);
> +VIR_ENUM_IMPL(virPoolObjTable, VIR_POOLOBJTABLE_LAST,
> + "nodedev", "interface", "nwfilter",
"volume", "block storage",
> + "secret", "network", "domain snapshot",
"domain")
> +
> +struct _virPoolDef {
> + char *uuid;
> + char *name;
> +};
> +
> +struct _virPoolObj {
> + virObjectLockable parent;
> +
> + /* copy of the def->name and def->uuid (if available) used for lookups
> + * without needing to know the object type */
> + virPoolDefPtr pooldef;
> +
> + /* Boolean states that can be managed by consumer */
> + bool active; /* object is active or not */
> + bool beingRemoved; /* object being prepared for removal */
> + bool autostart; /* object is autostarted */
> + bool persistent; /* object definition is persistent */
> + bool updated; /* object config has been updated in some way */
> +
> + /* Boolean states managed by virPoolObj */
> + bool removing; /* true when object has been removed from table(s) */
> +
> + /* 'def' is the current config definition.
> + * 'newDef' is the next boot configuration.
> + * The 'type' value will describe which _vir*Def struct describes
> + * The 'privateData' will be private object data for each pool obj
> + * and specific to the 'type' of object being managed. */
> + void *def;
> + void *newDef;
> + virFreeCallback defFreeFunc;
> +
> + /* Private data to be managed by the specific object using
> + * vir{$OBJ}ObjPrivate{Get|Set}{$FIELD} type API's. Each of those
> + * calling the virPoolObjGetPrivateData in order to access this field */
> + void *privateData;
> + void (*privateDataFreeFunc)(void *);
> +};
So we are creating a new object to be put into hash table. How does this
work with the actual object then? I mean, take patch 6/9 for instance.
virSecretObj is actual object. Now, when dealing with virPoolObjList,
it's just virPoolObj which is locked and not the actual virSecretObj.
I'm worried that this could cause some trouble.
Maybe virSecret is not the best example, because it doesn't usually get
unlocked & locked again throughout virSecret APIs. Maybe virDomain would
be a better example.
A-ha! after going through all the patches, this new virPoolObj is a
merge of all the vir*Obj (virSecretObj, virNetworkObj, virNodeDeviceObj,
...). So the issue I'm describing above will never occur.
I wonder whether we can change this code so that we don't have to change
all those objects to virPoolObj. I mean, our virObject supports morphism
therefore we should be able to have APIs that are able to work over
different classes derived from virObject. For instance:
virSecretObjPtr sec;
virNodeDeviceObjPtr node;
virObjectListEndAPI(&sec);
virObjectListEndAPI(&node);
The same way we can call:
virObjectLock(sec);
virObjectLock((node);
currently. This would help us to ensure type safeness when passing args
to functions.
One approach that comes to my mind right now as I'm writing these lines
(read as "I haven't thought it through properly yet") is to create a new
type of virObject. Currently we have virObject and virObjectLockable.
What if we would have virObjectListable which would be derived from
virObjectLockable. All those driver objects (virSecretObj for instance)
would be derived from virObjectListable then. And in virObjectListable
you could hold all those booleans you need. This would have the benefit
of not dropping virSecretObj and thus rewriting half of libvirt.
I agree that the virPoolObj is not the best way to go. This is similar to
what I had in mind while reading this series. We should definitely preserve
all those object as they are.
I had a similar approach in my mind, it would basically follow how the
virDomainObjList is done, however the name would be generic (virObjectList,
virObjectListable or virObjectTable) with general APIs for searching by using
a function pointer, inserting, removing, etc. And each object type would
implement specific stuff. This would nicely follow how the real
object-inheritance is done in class oriented languages.
I definitely like the idea of having unified approach how to handle all
lists that we use in our code and this would give you the tool how to do it.
The virPoolObj kind of force you to use it.
Good job with starting the effort to make it unified, we definitely need it.
Pavel