[libvirt] [PATCH 09/15] Add libvirt-admin library

Daniel P. Berrange berrange at redhat.com
Fri Apr 17 10:28:11 UTC 2015


On Thu, Apr 16, 2015 at 04:46:44PM +0200, Martin Kletzander wrote:
> Initial scratch of the admin library.  It has its own virAdmConnectPtr
> that inherits from virAbstractConnectPtr and thus trivially supports
> error reporting.

See my note earlier about error reporting on the connection being
a bad idea due to lack of thread safety.

> Configuration option --with-admin is added to control whether the admin
> library should be built or not (set to 'yes' by default).

Is there a compelling reason why we'd need/want to be able to
disable building of the admin library ?  As a comparison we
unconditionally build  libvirt-qemu.so & libvirt-lxc.so


> diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h
> new file mode 100644
> index 0000000..7fe03cf
> --- /dev/null
> +++ b/include/libvirt/libvirt-admin.h
> @@ -0,0 +1,62 @@
> +/*
> + * libvirt-admin.h: Admin interface for libvirt
> + * Summary: Interfaces for handling server-related tasks
> + * Description: Provides the interfaces of the libvirt library to operate
> + *              with the server itself, not any hypervisors.
> + *
> + * Copyright (C) 2014-2015 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/>.
> + *
> + * Author: Martin Kletzander <mkletzan at redhat.com>
> + */
> +
> +#ifndef __VIR_ADMIN_H__
> +# define __VIR_ADMIN_H__
> +
> +# include "internal.h"
> +
> +# ifdef __cplusplus
> +extern "C" {
> +# endif
> +
> +
> +/**
> + * virAdmConnect:
> + *
> + * a virAdmConnect is a private structure representing a connection to
> + * libvirt daemon.
> + */
> +typedef struct _virAdmConnect virAdmConnect;
> +
> +/**
> + * virAdmConnectPtr:
> + *
> + * a virAdmConnectPtr is pointer to a virAdmConnect private structure,
> + * this is the type used to reference a connection to the daemon
> + * in the API.
> + */
> +typedef virAdmConnect *virAdmConnectPtr;
> +
> +virAdmConnectPtr virAdmConnectOpen(unsigned int flags);

How does this figure out which libvirtd daemon to connect to ? Presumably
you've hardcoded it based on the UID you're running as ?  I think for
future proofing we should probably define a URI syntax for this.

eg

   admin:///system
   admin:///session

And allow an optional parameter for the socket path, for people who
have built their daemon with an unusual --prefix arg.


> +libvirt_admin_la_SOURCES += 			\
> +		datatypes.c			\
> +		util/viralloc.c			\
> +		util/viratomic.c		\
> +		util/virauth.c			\
> +		util/virauthconfig.c		\
> +		util/virbitmap.c		\
> +		util/virbuffer.c		\
> +		util/vircommand.c		\
> +		util/virerror.c			\
> +		util/virevent.c			\
> +		util/vireventpoll.c		\
> +		util/virfile.c			\
> +		util/virhash.c			\
> +		util/virhashcode.c		\
> +		util/virjson.c			\
> +		util/virkeyfile.c		\
> +		util/virlog.c			\
> +		util/virobject.c		\
> +		util/virpidfile.c		\
> +		util/virprocess.c		\
> +		util/virrandom.c		\
> +		util/virseclabel.c		\
> +		util/virsocketaddr.c		\
> +		util/virstorageencryption.c	\
> +		util/virstoragefile.c		\
> +		util/virstring.c		\
> +		util/virthread.c		\
> +		util/virtime.c			\
> +		util/virtypedparam.c		\
> +		util/viruri.c			\
> +		util/virutil.c			\
> +		util/viruuid.c			\
> +		util/virxml.c			\
> +		remote/remote_protocol.c	\
> +		rpc/virnetmessage.h		\
> +		rpc/virnetmessage.c		\
> +		rpc/virnetsocket.c		\
> +		rpc/virnetsshsession.c		\
> +		rpc/virkeepalive.c		\
> +		rpc/virnetclient.c		\
> +		rpc/virnetclientprogram.c	\
> +		rpc/virnetclientstream.c	\
> +		rpc/virnetprotocol.c		\
> +		rpc/virnettlscontext.c		\
> +		rpc/virnetsaslcontext.c



> +
> +libvirt_admin_la_LDFLAGS = \
> +		$(VERSION_SCRIPT_FLAGS)$(LIBVIRT_ADMIN_SYMBOL_FILE)	\
> +		-version-info $(LIBVIRT_VERSION_INFO)			\
> +		$(AM_LDFLAGS) 						\
> +		$(CYGWIN_EXTRA_LDFLAGS) 				\
> +		$(MINGW_EXTRA_LDFLAGS)
> +
> +libvirt_admin_la_LIBADD = \
> +		$(CYGWIN_EXTRA_LIBADD)
> +
> +libvirt_admin_la_CFLAGS = \
> +		$(AM_CFLAGS)		\
> +		-I$(srcdir)/remote	\
> +		-I$(srcdir)/rpc		\
> +		-I$(srcdir)/admin
> +
> +libvirt_admin_la_CFLAGS += \
> +		$(CAPNG_CFLAGS)			\
> +		$(YAJL_CFLAGS)			\
> +		$(SSH2_CFLAGS)			\
> +		$(SASL_CFLAGS)			\
> +		$(GNUTLS_CFLAGS)
> +
> +libvirt_admin_la_LIBADD += \
> +		$(CAPNG_LIBS)			\
> +		$(YAJL_LIBS)			\
> +		$(DEVMAPPER_LIBS)		\
> +		$(LIBXML_LIBS)			\
> +		$(SSH2_LIBS)			\
> +		$(SASL_LIBS)			\
> +		$(GNUTLS_LIBS)
> +
> +if WITH_DTRACE_PROBES
> +libvirt_admin_la_LIBADD += libvirt_probes.lo
> +endif WITH_DTRACE_PROBES
> +
> +endif WITH_ADMIN
> +
>  # Empty source list - it merely links a bunch of convenience libs together
>  libvirt_la_SOURCES =
>  libvirt_la_LDFLAGS = \
> diff --git a/src/datatypes.c b/src/datatypes.c
> index b21113e..83cee7e 100644
> --- a/src/datatypes.c
> +++ b/src/datatypes.c
> @@ -59,6 +59,10 @@ static void virStreamDispose(void *obj);
>  static void virStorageVolDispose(void *obj);
>  static void virStoragePoolDispose(void *obj);
> 
> +virClassPtr virAdmConnectClass;
> +
> +static void virAdmConnectDispose(void *obj);
> +
>  static int
>  virDataTypesOnceInit(void)
>  {
> @@ -88,6 +92,8 @@ virDataTypesOnceInit(void)
>      DECLARE_CLASS(virStorageVol);
>      DECLARE_CLASS(virStoragePool);
> 
> +    DECLARE_CLASS_CONNECT(virAdmConnect);
> +
>  #undef DECLARE_CLASS_COMMON
>  #undef DECLARE_CLASS_LOCKABLE
>  #undef DECLARE_CLASS_CONNECT
> @@ -804,3 +810,27 @@ virDomainSnapshotDispose(void *obj)
>      VIR_FREE(snapshot->name);
>      virObjectUnref(snapshot->domain);
>  }
> +
> +
> +virAdmConnectPtr
> +virAdmConnectNew(void)
> +{
> +    virAdmConnectPtr ret;
> +
> +    if (virDataTypesInitialize() < 0)
> +        return NULL;
> +
> +    if (!(ret = virGetAbstractConnect(virAdmConnectClass)))
> +        return NULL;
> +
> +    return ret;
> +}
> +
> +static void
> +virAdmConnectDispose(void *obj)
> +{
> +    virAdmConnectPtr conn = obj;
> +
> +    if (conn->privateDataFreeFunc)
> +        conn->privateDataFreeFunc(conn->privateData);
> +}
> diff --git a/src/datatypes.h b/src/datatypes.h
> index 9f95811..b240e4c 100644
> --- a/src/datatypes.h
> +++ b/src/datatypes.h
> @@ -42,6 +42,8 @@ extern virClassPtr virStreamClass;
>  extern virClassPtr virStorageVolClass;
>  extern virClassPtr virStoragePoolClass;
> 
> +extern virClassPtr virAdmConnectClass;
> +
>  # define virCheckConnectReturn(obj, retval)                             \
>      do {                                                                \
>          if (!virObjectIsClass(obj, virConnectClass)) {                  \
> @@ -296,6 +298,26 @@ extern virClassPtr virStoragePoolClass;
>                    dom, NULLSTR(_domname), _uuidstr, __VA_ARGS__);       \
>      } while (0)
> 
> +# define virCheckAdmConnectReturn(obj, retval)                          \
> +    do {                                                                \
> +        if (!virObjectIsClass(obj, virAdmConnectClass)) {               \
> +            virReportErrorHelper(VIR_FROM_THIS, VIR_ERR_INVALID_CONN,   \
> +                                 __FILE__, __FUNCTION__, __LINE__,      \
> +                                 __FUNCTION__);                         \
> +            virDispatchError(NULL);                                     \
> +            return retval;                                              \
> +        }                                                               \
> +    } while (0)
> +# define virCheckAdmConnectGoto(obj, label)                             \
> +    do {                                                                \
> +        if (!virObjectIsClass(obj, virAdmConnectClass)) {               \
> +            virReportErrorHelper(VIR_FROM_THIS, VIR_ERR_INVALID_CONN,   \
> +                                 __FILE__, __FUNCTION__, __LINE__,      \
> +                                 __FUNCTION__);                         \
> +            goto label;                                                 \
> +        }                                                               \
> +    } while (0)
> +
>  /**
>   * VIR_DOMAIN_DEBUG:
>   * @dom: domain
> @@ -365,6 +387,19 @@ struct _virConnect {
>  };
> 
>  /**
> + * _virAdmConnect:
> + *
> + * Internal structure associated to an admin connection
> + */
> +struct _virAdmConnect {
> +    virAbstractConnect object;
> +
> +    void *privateData;
> +    virFreeCallback privateDataFreeFunc;
> +};
> +
> +
> +/**
>  * _virDomain:
>  *
>  * Internal structure associated to a domain
> @@ -545,4 +580,6 @@ virNWFilterPtr virGetNWFilter(virConnectPtr conn,
>  virDomainSnapshotPtr virGetDomainSnapshot(virDomainPtr domain,
>                                            const char *name);
> 
> +virAdmConnectPtr virAdmConnectNew(void);
> +
>  #endif /* __VIR_DATATYPES_H__ */
> diff --git a/src/internal.h b/src/internal.h
> index 4d473af..1fbcfc2 100644
> --- a/src/internal.h
> +++ b/src/internal.h
> @@ -58,6 +58,7 @@
>  # include "libvirt/libvirt.h"
>  # include "libvirt/libvirt-lxc.h"
>  # include "libvirt/libvirt-qemu.h"
> +# include "libvirt/libvirt-admin.h"
>  # include "libvirt/virterror.h"
> 
>  # include "c-strcase.h"
> diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c
> new file mode 100644
> index 0000000..e47089d
> --- /dev/null
> +++ b/src/libvirt-admin.c
> @@ -0,0 +1,337 @@
> +/*
> + * libvirt-admin.c
> + *
> + * Copyright (C) 2014-2015 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/>.
> + *
> + * Author: Martin Kletzander <mkletzan at redhat.com>
> + */
> +
> +#include <config.h>
> +
> +#include <rpc/rpc.h>
> +
> +#include "internal.h"
> +#include "configmake.h"
> +#include "datatypes.h"
> +#include "viralloc.h"
> +#include "virlog.h"
> +#include "virstring.h"
> +#include "virutil.h"
> +#include "viruuid.h"
> +#include "virnetclient.h"
> +
> +#define VIR_FROM_THIS VIR_FROM_ADMIN
> +#define LIBVIRTD_ADMIN_UNIX_SOCKET LOCALSTATEDIR "/run/libvirt/libvirt-admin-sock"
> +
> +VIR_LOG_INIT("libvirt-admin");
> +
> +
> +typedef struct _remoteAdminPriv remoteAdminPriv;
> +typedef remoteAdminPriv *remoteAdminPrivPtr;
> +
> +struct _remoteAdminPriv {
> +    virMutex lock;
> +    int counter;
> +    int localUses;
> +    virNetClientPtr client;
> +    virNetClientProgramPtr program;
> +};
> +
> +static void
> +remoteAdminLock(remoteAdminPrivPtr priv)
> +{
> +    virMutexLock(&priv->lock);
> +}
> +
> +static void
> +remoteAdminUnlock(remoteAdminPrivPtr priv)
> +{
> +    virMutexUnlock(&priv->lock);
> +}
> +
> +static void
> +remoteAdminPrivFree(void *opaque)
> +{
> +    remoteAdminPrivPtr priv = opaque;
> +
> +    virObjectUnref(priv->program);
> +    virObjectUnref(priv->client);
> +
> +    virMutexDestroy(&priv->lock);
> +    VIR_FREE(priv);
> +}
> +
> +static int
> +callFull(virAdmConnectPtr conn ATTRIBUTE_UNUSED,
> +         remoteAdminPrivPtr priv,
> +         int *fdin,
> +         size_t fdinlen,
> +         int **fdout,
> +         size_t *fdoutlen,
> +         int proc_nr,
> +         xdrproc_t args_filter, char *args,
> +         xdrproc_t ret_filter, char *ret)
> +{
> +    int rv;
> +    virNetClientProgramPtr prog;
> +    int counter = priv->counter++;
> +    virNetClientPtr client = priv->client;
> +    priv->localUses++;
> +
> +    /* We hav nothing else right now, but we can still add more
> +     * programs in the future */
> +    prog = priv->program;
> +
> +    /* Unlock, so that if we get any async events/stream data
> +     * while processing the RPC, we don't deadlock when our
> +     * callbacks for those are invoked
> +     */
> +    remoteAdminUnlock(priv);
> +    rv = virNetClientProgramCall(prog,
> +                                 client,
> +                                 counter,
> +                                 proc_nr,
> +                                 fdinlen, fdin,
> +                                 fdoutlen, fdout,
> +                                 args_filter, args,
> +                                 ret_filter, ret);
> +    remoteAdminLock(priv);
> +    priv->localUses--;
> +
> +    return rv;
> +}
> +
> +static int
> +call(virAdmConnectPtr conn,
> +     unsigned int flags,
> +     int proc_nr,
> +     xdrproc_t args_filter, char *args,
> +     xdrproc_t ret_filter, char *ret)
> +{
> +    virCheckFlags(0, -1);
> +
> +    return callFull(conn, conn->privateData,
> +                    NULL, 0, NULL, NULL, proc_nr,
> +                    args_filter, args, ret_filter, ret);
> +}
> +
> +#include "admin_protocol.h"
> +#include "admin_client.h"
> +
> +static bool virAdmGlobalError;
> +static virOnceControl virAdmGlobalOnce = VIR_ONCE_CONTROL_INITIALIZER;
> +
> +static remoteAdminPrivPtr
> +remoteAdminPrivNew(void)
> +{
> +    remoteAdminPrivPtr priv = NULL;
> +
> +    /* initialize private data */
> +    if (VIR_ALLOC(priv) < 0)
> +        goto error;
> +
> +    if (virMutexInit(&priv->lock) < 0) {
> +        VIR_FREE(priv);
> +        goto error;
> +    }
> +
> +    priv->localUses = 1;
> +    if (!(priv->client = virNetClientNewUNIX(LIBVIRTD_ADMIN_UNIX_SOCKET,
> +                                             false, NULL)))
> +        goto error;
> +
> +    if (!(priv->program = virNetClientProgramNew(ADMIN_PROGRAM,
> +                                                 ADMIN_PROTOCOL_VERSION,
> +                                                 NULL, 0, NULL)))
> +        goto error;
> +
> +    if (virNetClientAddProgram(priv->client, priv->program) < 0)
> +        goto error;
> +
> +    return priv;
> + error:
> +    remoteAdminPrivFree(priv);
> +    return NULL;
> +}
> +
> +static void
> +virAdmGlobalInit(void)
> +{
> +    /* It would be nice if we could trace the use of this call, to
> +     * help diagnose in log files if a user calls something other than
> +     * virAdmConnectOpen first.  But we can't rely on VIR_DEBUG working
> +     * until after initialization is complete, and since this is
> +     * one-shot, we never get here again.  */
> +    if (virThreadInitialize() < 0 ||
> +        virErrorInitialize() < 0)
> +        goto error;
> +
> +    virLogSetFromEnv();
> +
> +    if (!bindtextdomain(PACKAGE, LOCALEDIR))
> +        goto error;
> +
> +    return;
> + error:
> +    virAdmGlobalError = true;
> +}
> +
> +/**
> + * virAdmInitialize:
> + *
> + * Initialize the library.
> + *
> + * Returns 0 in case of success, -1 in case of error
> + */
> +static int
> +virAdmInitialize(void)
> +{
> +    if (virOnce(&virAdmGlobalOnce, virAdmGlobalInit) < 0)
> +        return -1;
> +
> +    if (virAdmGlobalError)
> +        return -1;
> +
> +    return 0;
> +}
> +
> +/**
> + * virAdmConnectOpen:
> + * @flags: unused, must be 0
> + *
> + * Opens connection to admin interface of the daemon.
> + *
> + * Returns @virAdmConnectPtr object or NULL on error
> + */
> +virAdmConnectPtr
> +virAdmConnectOpen(unsigned int flags)
> +{
> +    virAdmConnectPtr conn = NULL;
> +    remoteAdminPrivPtr priv = NULL;
> +
> +    if (virAdmInitialize() < 0)
> +        goto error;
> +
> +    VIR_DEBUG("flags=%x", flags);
> +    virResetLastError();
> +
> +    if (!(conn = virAdmConnectNew()))
> +        goto error;
> +
> +    if (!(priv = remoteAdminPrivNew()))
> +        goto error;
> +
> +    remoteAdminLock(priv);
> +    conn->privateData = priv;
> +    conn->privateDataFreeFunc = remoteAdminPrivFree;
> +
> +    {
> +        admin_connect_open_args args = { flags };
> +
> +        VIR_DEBUG("Trying to open admin connection");
> +        if (call(conn, 0, ADMIN_PROC_CONNECT_OPEN,
> +                 (xdrproc_t) xdr_admin_connect_open_args, (char *) &args,
> +                 (xdrproc_t) xdr_void, (char *) NULL) == -1)
> +            goto error;
> +    }
> +
> +    remoteAdminUnlock(priv);
> +
> +    return conn;
> + error:
> +    virDispatchError(NULL);
> +    if (priv)
> +        remoteAdminUnlock(priv);
> +    virObjectUnref(conn);
> +    return NULL;
> +}
> +
> +/**
> + * virAdmConnectClose:
> + * @conn: pointer to admin connection to close
> + *
> + * This function closes the connection to the Hypervisor. This should
> + * not be called if further interaction with the Hypervisor are needed
> + * especially if there is running domain which need further monitoring by
> + * the application.
> + *
> + * Connections are reference counted; the count is explicitly
> + * increased by the initial open (virConnectOpen, virConnectOpenAuth,
> + * and the like) as well as virConnectRef; it is also temporarily
> + * increased by other API that depend on the connection remaining
> + * alive.  The open and every virConnectRef call should have a
> + * matching virConnectClose, and all other references will be released
> + * after the corresponding operation completes.
> + *
> + * Returns a positive number if at least 1 reference remains on
> + * success. The returned value should not be assumed to be the total
> + * reference count. A return of 0 implies no references remain and
> + * the connection is closed and memory has been freed. A return of -1
> + * implies a failure.
> + *
> + * It is possible for the last virConnectClose to return a positive
> + * value if some other object still has a temporary reference to the
> + * connection, but the application should not try to further use a
> + * connection after the virConnectClose that matches the initial open.
> + */
> +int
> +virAdmConnectClose(virAdmConnectPtr conn)
> +{
> +    VIR_DEBUG("conn=%p", conn);
> +
> +    virResetLastError();
> +    if (!conn)
> +        return 0;
> +
> +    virCheckAdmConnectReturn(conn, -1);
> +
> +    if (!virObjectUnref(conn))
> +        return 0;
> +    return 1;
> +}
> +
> +
> +/**
> + * virAdmConnectRef:
> + * @conn: the connection to hold a reference on
> + *
> + * Increment the reference count on the connection. For each
> + * additional call to this method, there shall be a corresponding
> + * call to virConnectClose to release the reference count, once
> + * the caller no longer needs the reference to this object.
> + *
> + * This method is typically useful for applications where multiple
> + * threads are using a connection, and it is required that the
> + * connection remain open until all threads have finished using
> + * it. I.e., each new thread using a connection would increment
> + * the reference count.
> + *
> + * Returns 0 in case of success, -1 in case of failure
> + */
> +int
> +virAdmConnectRef(virAdmConnectPtr conn)
> +{
> +    VIR_DEBUG("conn=%p refs=%d", conn,
> +              conn ? conn->object.parent.parent.u.s.refs : 0);
> +
> +    virResetLastError();
> +    virCheckAdmConnectReturn(conn, -1);
> +
> +    virObjectRef(conn);
> +
> +    return 0;
> +}
> diff --git a/src/libvirt_admin.syms b/src/libvirt_admin.syms
> new file mode 100644
> index 0000000..292433f
> --- /dev/null
> +++ b/src/libvirt_admin.syms
> @@ -0,0 +1,18 @@
> +#
> +# Officially exported symbols, for which header
> +# file definitions are installed in /usr/include/libvirt
> +# from libvirt-admin.h
> +#
> +# Versions here are *fixed* to match the libvirt version
> +# at which the symbol was introduced. This ensures that
> +# a new client app requiring symbol foo() can't accidentally
> +# run with old libvirt-admin.so not providing foo() - the global
> +# soname version info can't enforce this since we never
> +# change the soname
> +#
> +LIBVIRT_ADMIN_1.2.3 {

Either you've been working on this a really long time, or this
version was a typo.

Either way, when we are ready to merge the admin library, that
might be a nice reason to bump our version to 1.3.x, since
we've been on 1.2.x a long time.


Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|




More information about the libvir-list mailing list