diff options
| author | Ram Raja <rraja@redhat.com> | 2013-04-30 00:38:04 +0530 | 
|---|---|---|
| committer | Anand Avati <avati@redhat.com> | 2013-05-10 14:15:08 -0700 | 
| commit | 1e62ff42dfcf84ded563f710cb8da92fa0f41936 (patch) | |
| tree | 5e0375d69af015878c7a96e643ddebe54af912eb | |
| parent | 40d026e10013f533c4fee33b87dabc4ca11c94b3 (diff) | |
glupy: Importing Jeff's glupy project into glusterfs
Change-Id: I3891ef6eaf6ede7c8cbedc3298ce2501a69b2b05
BUG: 961856
Original-author: Jeff Darcy <jdarcy@redhat.com>
Signed-off-by: Ram Raja <rraja@redhat.com>
Reviewed-on: http://review.gluster.org/4906
Reviewed-by: Justin Clift <jclift@redhat.com>
Tested-by: Justin Clift <jclift@redhat.com>
| -rw-r--r-- | configure.ac | 10 | ||||
| -rwxr-xr-x | tests/features/glupy.t | 29 | ||||
| -rw-r--r-- | xlators/features/Makefile.am | 2 | ||||
| -rw-r--r-- | xlators/features/glupy/Makefile.am | 3 | ||||
| -rw-r--r-- | xlators/features/glupy/doc/README.md | 44 | ||||
| -rw-r--r-- | xlators/features/glupy/src/Makefile.am | 20 | ||||
| -rw-r--r-- | xlators/features/glupy/src/glupy.c | 405 | ||||
| -rw-r--r-- | xlators/features/glupy/src/glupy.h | 47 | ||||
| -rw-r--r-- | xlators/features/glupy/src/gluster.py | 118 | ||||
| -rw-r--r-- | xlators/features/glupy/src/helloworld.py | 19 | ||||
| -rw-r--r-- | xlators/features/glupy/src/negative.py | 93 | 
11 files changed, 789 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac index 6357b19fa..48237bc40 100644 --- a/configure.ac +++ b/configure.ac @@ -95,6 +95,8 @@ AC_CONFIG_FILES([Makefile  		xlators/protocol/server/Makefile  		xlators/protocol/server/src/Makefile  		xlators/features/Makefile +		xlators/features/glupy/Makefile +		xlators/features/glupy/src/Makefile  		xlators/features/locks/Makefile  		xlators/features/locks/src/Makefile  		xlators/features/quota/Makefile @@ -632,6 +634,14 @@ if test "x$LIBAIO" != "x"; then  fi +BUILD_GLUPY=no +AC_CHECK_LIB([python2.6],[PyImport_Import],[BUILD_GLUPY=yes]) + +if test "x$BUILD_GLUPY" = "xyes"; then +   GLUPY_SUBDIR=glupy +fi +AC_SUBST(GLUPY_SUBDIR) +  AC_SUBST(GF_HOST_OS)  AC_SUBST([GF_GLUSTERFS_LIBS])  AC_SUBST(GF_GLUSTERFS_CFLAGS) diff --git a/tests/features/glupy.t b/tests/features/glupy.t new file mode 100755 index 000000000..49bf11df5 --- /dev/null +++ b/tests/features/glupy.t @@ -0,0 +1,29 @@ +#!/bin/bash + +. $(dirname $0)/../include.rc + +cleanup; + +TEST mkdir -p $B0/glupytest +cat > $B0/glupytest.vol <<EOF +volume vol-posix +    type storage/posix +    option directory $B0/glupytest +end-volume + +volume vol-glupy +    type features/glupy +    option module-name helloworld +    subvolumes vol-posix +end-volume +EOF + +TEST glusterfs -f $B0/glupytest.vol $M0; + +TEST touch $M0/filename; +EXPECT "filename" ls $M0 +TEST rm -f $M0/filename; + +TEST umount -l $M0; + +cleanup; diff --git a/xlators/features/Makefile.am b/xlators/features/Makefile.am index 09aab1293..6a73301d7 100644 --- a/xlators/features/Makefile.am +++ b/xlators/features/Makefile.am @@ -1,4 +1,4 @@  SUBDIRS = locks quota read-only mac-compat quiesce marker index \ -	  protect # trash path-converter # filter +	  protect $(GLUPY_SUBDIR) # trash path-converter # filter  CLEANFILES = diff --git a/xlators/features/glupy/Makefile.am b/xlators/features/glupy/Makefile.am new file mode 100644 index 000000000..a985f42a8 --- /dev/null +++ b/xlators/features/glupy/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = src + +CLEANFILES = diff --git a/xlators/features/glupy/doc/README.md b/xlators/features/glupy/doc/README.md new file mode 100644 index 000000000..2d7b30ef6 --- /dev/null +++ b/xlators/features/glupy/doc/README.md @@ -0,0 +1,44 @@ +This is just the very start for a GlusterFS[1] meta-translator that will +allow translator code to be written in Python.  It's based on the standard +Python embedding (not extending) techniques, plus a dash of the ctypes module. +The interface is a pretty minimal adaptation of the dispatches and callbacks +from the C API[2] to Python, as follows: + +* Dispatch functions and callbacks must be defined on an "xlator" class +  derived from gluster.Translator so that they'll be auto-registered with +  the C translator during initialization. + +* For each dispatch or callback function you want to intercept, you define a +  Python function using the xxx\_fop\_t or xxx\_cbk\_t decorator. + +* The arguments for each operation are different, so you'll need to refer to +  the C API.  GlusterFS-specific types are used (though only loc\_t is fully +  defined so far) and type correctness is enforced by ctypes. + +* If you do intercept a dispatch function, it is your responsibility to call +  xxx\_wind (like STACK\_WIND in the C API but operation-specific) to pass +  the request to the next translator.  If you do not intercept a function, it +  will default the same way as for C (pass through to the same operation with +  the same arguments on the first child translator). + +* If you intercept a callback function, it is your responsibility to call +  xxx\_unwind (like STACK\_UNWIND\_STRICT in the C API) to pass the request back +  to the caller. + +So far only the lookup and create operations are handled this way, to support +the "negative lookup" example.  Now that the basic infrastructure is in place, +adding more functions should be very quick, though with that much boilerplate I +might pause to write a code generator.  I also plan to add structure +definitions and interfaces for some of the utility functions in libglusterfs +(especially those having to do with inode and fd context) in the fairly near +future.  Note that you can also use ctypes to get at anything not explicitly +exposed to Python already. + +_If you're coming here because of the Linux Journal article, please note that +the code has evolved since that was written. The version that matches the +article is here:_ + +https://github.com/jdarcy/glupy/tree/4bbae91ba459ea46ef32f2966562492e4ca9187a + +[1] http://www.gluster.org +[2] http://hekafs.org/dist/xlator_api_2.html diff --git a/xlators/features/glupy/src/Makefile.am b/xlators/features/glupy/src/Makefile.am new file mode 100644 index 000000000..ccecc7a4f --- /dev/null +++ b/xlators/features/glupy/src/Makefile.am @@ -0,0 +1,20 @@ +xlator_LTLIBRARIES = glupy.la + +xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/features + +glupydir = $(xlatordir)/glupy + +glupy_PYTHON = gluster.py negative.py helloworld.py + +glupy_la_LDFLAGS = -module -avoid-version -shared -nostartfiles +glupy_la_SOURCES = glupy.c +glupy_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la \ +	 -lpthread -lpython2.6 + +noinst_HEADERS = glupy.h + +AM_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src + +AM_CFLAGS = -Wall -fno-strict-aliasing -DGLUSTER_PYTHON_PATH=\"$(glupydir)\" $(GF_CFLAGS) + +CLEANFILES = diff --git a/xlators/features/glupy/src/glupy.c b/xlators/features/glupy/src/glupy.c new file mode 100644 index 000000000..9c06085b9 --- /dev/null +++ b/xlators/features/glupy/src/glupy.c @@ -0,0 +1,405 @@ +/* +  Copyright (c) 2012 Red Hat, Inc. <http://www.redhat.com> +  This file is part of GlusterFS. + +  GlusterFS is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published +  by the Free Software Foundation; either version 3 of the License, +  or (at your option) any later version. + +  GlusterFS 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 +  General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program.  If not, see +  <http://www.gnu.org/licenses/>. +*/ + +#include <ctype.h> +#include <sys/uio.h> +#include <python2.6/Python.h> + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "glusterfs.h" +#include "xlator.h" +#include "logging.h" +#include "defaults.h" + +#include "glupy.h" + +/* UTILITY FUNCTIONS FOR FOP-SPECIFIC CODE */ + +pthread_key_t gil_init_key; + +PyGILState_STATE +glupy_enter (void) +{ +#if 0 +        if (!pthread_getspecific(gil_init_key)) { +                PyEval_ReleaseLock(); +                (void)pthread_setspecific(gil_init_key,(void *)1); +        } +#endif + +        return PyGILState_Ensure(); +} + +void +glupy_leave (PyGILState_STATE gstate) +{ +        PyGILState_Release(gstate); +} + +/* FOP: LOOKUP */ + +int32_t +glupy_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                  int32_t op_ret, int32_t op_errno, inode_t *inode, +                  struct iatt *buf, dict_t *xdata, struct iatt *postparent) +{ +        glupy_private_t *priv = this->private; +        PyGILState_STATE gstate; +        int32_t ret; + +        if (!priv->cbks[GLUPY_LOOKUP]) { +                goto unwind; +        } + +        gstate = glupy_enter(); +        ret = ((fop_lookup_cbk_t)(priv->cbks[GLUPY_LOOKUP]))( +                frame, cookie, this, op_ret, op_errno, +                inode, buf, xdata, postparent); +        glupy_leave(gstate); + +        return ret; +         +unwind: +        frame->local = NULL; +        STACK_UNWIND_STRICT (lookup, frame, op_ret, op_errno, inode, buf, +                             xdata, postparent); +        return 0; +} + +int32_t +glupy_lookup (call_frame_t *frame, xlator_t *this, loc_t *loc, +              dict_t *xdata) +{ +        glupy_private_t *priv = this->private; +        PyGILState_STATE gstate; +        int32_t ret; +        static long next_id = 0; + +        if (!priv->fops[GLUPY_LOOKUP]) { +                goto wind; +        } + +        gstate = glupy_enter(); +        frame->local = (void *)++next_id; +        ret = ((fop_lookup_t)(priv->fops[GLUPY_LOOKUP]))( +                frame, this, loc, xdata); +        glupy_leave(gstate); + +        return ret; + +wind: +        STACK_WIND (frame, glupy_lookup_cbk, FIRST_CHILD(this), +                    FIRST_CHILD(this)->fops->lookup, loc, xdata); +        return 0; +} + +void +wind_lookup (call_frame_t *frame, xlator_t *xl, loc_t *loc, dict_t *xdata) +{ +         +        xlator_t        *this = THIS; + +        if (!xl || (xl == this)) { +                xl = FIRST_CHILD(this); +        } + +        STACK_WIND(frame,glupy_lookup_cbk,xl,xl->fops->lookup,loc,xdata); +} + +void +unwind_lookup (call_frame_t *frame, long cookie, xlator_t *this, +               int32_t op_ret, int32_t op_errno, inode_t *inode, +               struct iatt *buf, dict_t *xdata, struct iatt *postparent) +{ +        frame->local = NULL; +        STACK_UNWIND_STRICT(lookup,frame,op_ret,op_errno, +                            inode,buf,xdata,postparent); +} + +void +set_lookup_fop (long py_this, fop_lookup_t fop) +{ +        glupy_private_t *priv   = ((xlator_t *)py_this)->private; + +        priv->fops[GLUPY_LOOKUP] = (long)fop; +} + +void +set_lookup_cbk (long py_this, fop_lookup_cbk_t cbk) +{ +        glupy_private_t *priv   = ((xlator_t *)py_this)->private; + +        priv->cbks[GLUPY_LOOKUP] = (long)cbk; +} + +/* FOP: CREATE */ + +int32_t +glupy_create_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                  int32_t op_ret, int32_t op_errno, fd_t *fd, inode_t *inode, +                  struct iatt *buf, struct iatt *preparent, +                  struct iatt *postparent, dict_t *xdata) +{ +        glupy_private_t *priv = this->private; +        PyGILState_STATE gstate; +        int32_t ret; + +        if (!priv->cbks[GLUPY_CREATE]) { +                goto unwind; +        } + +        gstate = glupy_enter(); +        ret = ((fop_create_cbk_t)(priv->cbks[GLUPY_CREATE]))( +                frame, cookie, this, op_ret, op_errno, +                fd, inode, buf, preparent, postparent, xdata); +        glupy_leave(gstate); + +        return ret; +         +unwind: +        frame->local = NULL; +        STACK_UNWIND_STRICT (create, frame, op_ret, op_errno, fd, inode, buf, +                             preparent, postparent, xdata); +        return 0; +} + +int32_t +glupy_create (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags, +              mode_t mode, mode_t umask, fd_t *fd, dict_t *xdata) +{ +        glupy_private_t *priv = this->private; +        PyGILState_STATE gstate; +        int32_t ret; +        static long next_id = 0; + +        if (!priv->fops[GLUPY_CREATE]) { +                goto wind; +        } + +        gstate = glupy_enter(); +        frame->local = (void *)++next_id; +        ret = ((fop_create_t)(priv->fops[GLUPY_CREATE]))( +                frame, this, loc, flags, mode, umask, fd, xdata); +        glupy_leave(gstate); + +        return ret; + +wind: +        STACK_WIND (frame, glupy_create_cbk, FIRST_CHILD(this), +                    FIRST_CHILD(this)->fops->create, loc, flags, mode, umask, +                    fd, xdata); +        return 0; +} + +void +wind_create (call_frame_t *frame, xlator_t *xl, loc_t *loc, int32_t flags, +             mode_t mode, mode_t umask, fd_t *fd, dict_t *xdata) +{ +         +        xlator_t        *this = THIS; + +        if (!xl || (xl == this)) { +                xl = FIRST_CHILD(this); +        } + +        STACK_WIND (frame, glupy_create_cbk,xl, xl->fops->create, +                    loc, flags, mode, umask, fd, xdata); +} + +void +unwind_create (call_frame_t *frame, long cookie, xlator_t *this, +               int32_t op_ret, int32_t op_errno, fd_t *fd, inode_t *inode, +               struct iatt *buf, struct iatt *preparent, +               struct iatt *postparent, dict_t *xdata) +{ +        frame->local = NULL; +        STACK_UNWIND_STRICT (create, frame, op_ret, op_errno, fd, inode, buf, +                             preparent, postparent, xdata); +} + +void +set_create_fop (long py_this, fop_create_t fop) +{ +        glupy_private_t *priv   = ((xlator_t *)py_this)->private; + +        priv->fops[GLUPY_CREATE] = (long)fop; +} + +void +set_create_cbk (long py_this, fop_create_cbk_t cbk) +{ +        glupy_private_t *priv   = ((xlator_t *)py_this)->private; + +        priv->cbks[GLUPY_CREATE] = (long)cbk; +} + +/* NON-FOP-SPECIFIC CODE */ + +long +get_id (call_frame_t *frame) +{ +        return (long)(frame->local); +} + +int32_t +init (xlator_t *this) +{ +        glupy_private_t         *priv           = NULL; +        char                    *module_name    = NULL; +        PyObject                *py_mod_name    = NULL; +        PyObject                *py_init_func   = NULL; +        PyObject                *py_args        = NULL; +        PyObject                *syspath        = NULL; +	PyObject                *path           = NULL; +	static gf_boolean_t      py_inited      = _gf_false; +        void *                   err_cleanup    = &&err_return; + +        if (dict_get_str(this->options,"module-name",&module_name) != 0) { +                gf_log (this->name, GF_LOG_ERROR, "missing module-name"); +                return -1; +        } + +	priv = GF_CALLOC (1, sizeof (glupy_private_t), gf_glupy_mt_priv); +        if (!priv) { +                goto *err_cleanup; +        } +        this->private = priv; +        err_cleanup = &&err_free_priv; + +	if (!py_inited) { +                Py_Initialize(); +                PyEval_InitThreads(); +#if 0 +                (void)pthread_key_create(&gil_init_key,NULL); +                (void)pthread_setspecific(gil_init_key,(void *)1); +#endif +                /* PyEval_InitThreads takes this "for" us.  No thanks. */ +                PyEval_ReleaseLock(); +                py_inited = _gf_true; +        } + +	/* Adjust python's path */ +	syspath = PySys_GetObject("path"); +	path = PyString_FromString(GLUSTER_PYTHON_PATH); +	PyList_Append(syspath, path); +	Py_DECREF(path); + +	py_mod_name = PyString_FromString(module_name); +        if (!py_mod_name) { +                gf_log (this->name, GF_LOG_ERROR, "could not create name"); +                if (PyErr_Occurred()) { +                        PyErr_Print(); +                } +                goto *err_cleanup; +        } + +        priv->py_module = PyImport_Import(py_mod_name); +        Py_DECREF(py_mod_name); +        if (!priv->py_module) { +                gf_log (this->name, GF_LOG_ERROR, "Python import failed"); +                if (PyErr_Occurred()) { +                        PyErr_Print(); +                } +                goto *err_cleanup; +        } +        err_cleanup = &&err_deref_module; + +        py_init_func = PyObject_GetAttrString(priv->py_module, "xlator"); +        if (!py_init_func || !PyCallable_Check(py_init_func)) { +                gf_log (this->name, GF_LOG_ERROR, "missing init func"); +                if (PyErr_Occurred()) { +                        PyErr_Print(); +                } +                goto *err_cleanup; +        } +        err_cleanup = &&err_deref_init; + +        py_args = PyTuple_New(1); +        if (!py_args) { +                gf_log (this->name, GF_LOG_ERROR, "could not create args"); +                if (PyErr_Occurred()) { +                        PyErr_Print(); +                } +                goto *err_cleanup; +        } +        PyTuple_SetItem(py_args,0,PyLong_FromLong((long)this)); + +        /* TBD: pass in list of children */ +        priv->py_xlator = PyObject_CallObject(py_init_func, py_args); +        Py_DECREF(py_args); +        if (!priv->py_xlator) { +                gf_log (this->name, GF_LOG_ERROR, "Python init failed"); +                if (PyErr_Occurred()) { +                        PyErr_Print(); +                } +                goto *err_cleanup; +        } +        gf_log (this->name, GF_LOG_INFO, "init returned %p", priv->py_xlator); + +        return 0; + +err_deref_init: +        Py_DECREF(py_init_func); +err_deref_module: +        Py_DECREF(priv->py_module); +err_free_priv: +        GF_FREE(priv); +err_return: +        return -1; +} + +void +fini (xlator_t *this) +{ +        int              i = 0; +        glupy_private_t *priv = this->private; + +        if (!priv) +                return; +        for (i = 0; i < GLUPY_N_FUNCS; ++i) { +                if (priv->fops[i]) { +                        Py_DECREF(priv->fops[i]); +                } +                if (priv->cbks[i]) { +                        Py_DECREF(priv->fops[i]); +                } +        } +        Py_DECREF(priv->py_xlator); +        Py_DECREF(priv->py_module); +        this->private = NULL; +        GF_FREE (priv); + +        return; +} + +struct xlator_fops fops = { +        .lookup = glupy_lookup, +        .create = glupy_create, +}; + +struct xlator_cbks cbks = { +}; + +struct volume_options options[] = { +        { .key  = {NULL} }, +}; diff --git a/xlators/features/glupy/src/glupy.h b/xlators/features/glupy/src/glupy.h new file mode 100644 index 000000000..e6d2ff4f3 --- /dev/null +++ b/xlators/features/glupy/src/glupy.h @@ -0,0 +1,47 @@ +/* +   Copyright (c) 2006-2011 Gluster, Inc. <http://www.gluster.com> +   This file is part of GlusterFS. + +   GlusterFS is free software; you can redistribute it and/or modify +   it under the terms of the GNU General Public License as published +   by the Free Software Foundation; either version 3 of the License, +   or (at your option) any later version. + +   GlusterFS 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 +   General Public License for more details. + +   You should have received a copy of the GNU General Public License +   along with this program.  If not, see +   <http://www.gnu.org/licenses/>. +*/ + +#ifndef __GLUPY_H__ +#define __GLUPY_H__ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif +#include "mem-types.h" + +enum { +        GLUPY_LOOKUP = 0, +        GLUPY_CREATE, +        GLUPY_N_FUNCS +}; + +typedef struct { +        PyObject        *py_module; +        PyObject        *py_xlator; +        long            fops[GLUPY_N_FUNCS]; +        long            cbks[GLUPY_N_FUNCS]; +} glupy_private_t; + +enum gf_glupy_mem_types_ { +        gf_glupy_mt_priv = gf_common_mt_end + 1, +        gf_glupy_mt_end +}; + +#endif /* __GLUPY_H__ */ diff --git a/xlators/features/glupy/src/gluster.py b/xlators/features/glupy/src/gluster.py new file mode 100644 index 000000000..ac732fb17 --- /dev/null +++ b/xlators/features/glupy/src/gluster.py @@ -0,0 +1,118 @@ +import sys +from ctypes import * + +dl = CDLL("",RTLD_GLOBAL) + +class call_frame_t (Structure): +	pass + +class dict_t (Structure): +	pass + +class fd_t (Structure): +	pass + +class iatt_t (Structure): +	pass + +class inode_t (Structure): +	pass + +class loc_t (Structure): +	_fields_ = [ +		( "path",	c_char_p ), +		( "name",	c_char_p ), +		( "inode",	c_void_p ), +		( "parent",	c_void_p ), +		# Not quite correct, but easier to manipulate. +		( "gfid", c_uint * 4 ), +		( "pargfid", c_uint * 4 ), +	] + +class xlator_t (Structure): +	pass + +def _init_op (a_class, fop, cbk, wind, unwind): +	# Decorators, used by translators. We could pass the signatures as +	# parameters, but it's actually kind of nice to keep them around for +	# inspection. +	a_class.fop_type = apply(CFUNCTYPE,a_class.fop_sig) +	a_class.cbk_type = apply(CFUNCTYPE,a_class.cbk_sig) +	# Dispatch-function registration. +	fop.restype = None +	fop.argtypes = [ c_long, a_class.fop_type ] +	# Callback-function registration. +	cbk.restype = None +	cbk.argtypes = [ c_long, a_class.cbk_type ] +	# STACK_WIND function. +	wind.restype = None +	wind.argtypes = list(a_class.fop_sig[1:]) +	# STACK_UNWIND function. +	unwind.restype = None +	unwind.argtypes = list(a_class.cbk_sig[1:]) + +class OpLookup: +	fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), +		   POINTER(loc_t), POINTER(dict_t)) +	cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), +		   c_int, c_int, POINTER(inode_t), POINTER(iatt_t), +		   POINTER(dict_t), POINTER(iatt_t)) +_init_op (OpLookup, dl.set_lookup_fop, dl.set_lookup_cbk, +		    dl.wind_lookup,    dl.unwind_lookup) + +class OpCreate: +	fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), +		   POINTER(loc_t), c_int, c_uint, c_uint, POINTER(fd_t), +		   POINTER(dict_t)) +	cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), +		   c_int, c_int, POINTER(fd_t), POINTER(inode_t), +		   POINTER(iatt_t), POINTER(iatt_t), POINTER(iatt_t), +		   POINTER(dict_t)) +_init_op (OpCreate, dl.set_create_fop, dl.set_create_cbk, +		    dl.wind_create,    dl.unwind_create) + +class Translator: +	def __init__ (self, c_this): +		# This is only here to keep references to the stubs we create, +		# because ctypes doesn't and glupy.so can't because it doesn't +		# get a pointer to the actual Python object. It's a dictionary +		# instead of a list in case we ever allow changing fops/cbks +		# after initialization and need to look them up. +		self.stub_refs = {} +		funcs = dir(self.__class__) +		if "lookup_fop" in funcs: +			@OpLookup.fop_type +			def stub (frame, this, loc, xdata, s=self): +				return s.lookup_fop (frame, this, loc, xdata) +			self.stub_refs["lookup_fop"] = stub +			dl.set_lookup_fop(c_this,stub) +		if "lookup_cbk" in funcs: +			@OpLookup.cbk_type +			def stub (frame, cookie, this, op_ret, op_errno, inode, +				  buf, xdata, postparent, s=self): +				return s.lookup_cbk(frame, cookie, this, op_ret, +						    op_errno, inode, buf, xdata, +						    postparent) +			self.stub_refs["lookup_cbk"] = stub +			dl.set_lookup_cbk(c_this,stub) +		if "create_fop" in funcs: +			@OpCreate.fop_type +			def stub (frame, this, loc, flags, mode, umask, fd, +				  xdata, s=self): +				return s.create_fop (frame, this, loc, flags, +						     mode, umask, fd, xdata) +			self.stub_refs["create_fop"] = stub +			dl.set_create_fop(c_this,stub) +		if "create_cbk" in funcs: +			@OpCreate.cbk_type +			def stub (frame, cookie, this, op_ret, op_errno, fd, +				  inode, buf, preparent, postparent, xdata, +				  s=self): +				return s.create_cbk (frame, cookie, this, +						     op_ret, op_errno, fd, +						     inode, buf, preparent, +						     postparent, xdata) +				return 0 +			self.stub_refs["create_cbk"] = stub +			dl.set_create_cbk(c_this,stub) + diff --git a/xlators/features/glupy/src/helloworld.py b/xlators/features/glupy/src/helloworld.py new file mode 100644 index 000000000..8fe403711 --- /dev/null +++ b/xlators/features/glupy/src/helloworld.py @@ -0,0 +1,19 @@ +import sys +from gluster import * + +class xlator (Translator): + +    def __init__(self, c_this): +        Translator.__init__(self, c_this) + +    def lookup_fop(self, frame, this, loc, xdata): +        print "Python xlator: Hello!" +        dl.wind_lookup(frame, POINTER(xlator_t)(), loc, xdata) +        return 0 + +    def lookup_cbk(self, frame, cookie, this, op_ret, op_errno, inode, buf, +                   xdata, postparent): +        print "Python xlator: Hello again!" +        dl.unwind_lookup(frame, cookie, this, op_ret, op_errno, inode, buf, +                         xdata, postparent) +        return 0 diff --git a/xlators/features/glupy/src/negative.py b/xlators/features/glupy/src/negative.py new file mode 100644 index 000000000..6ca855f72 --- /dev/null +++ b/xlators/features/glupy/src/negative.py @@ -0,0 +1,93 @@ +import sys +from gluster import * + +# Negative-lookup-caching example.  If a file wasn't there the last time we +# looked, it's probably still not there.  This translator keeps track of +# those failed lookups for us, and returns ENOENT without needing to pass the +# call any further for repeated requests. + +# If we were doing this for real, we'd need separate caches for each xlator +# instance.  The easiest way to do this would be to have xlator.__init__ +# "register" each instance in a module-global dict, with the key as the C +# translator address and the value as the xlator object itself.  For testing +# and teaching, it's sufficient just to have one cache.  The keys are parent +# GFIDs, and the entries are lists of names within that parent that we know +# don't exist. +cache = {} + +# TBD: we need a better way of handling per-request data (frame->local in C). +dl.get_id.restype = c_long +dl.get_id.argtypes = [ POINTER(call_frame_t) ] + +def uuid2str (orig): +	return "%08x%08x%08x%08x" % (orig[0], orig[1], orig[2], orig[3]) + +class xlator (Translator): + +	def __init__ (self, c_this): +		self.requests = {} +		Translator.__init__(self,c_this) + +	def lookup_fop (self, frame, this, loc, xdata): +		pargfid = uuid2str(loc.contents.pargfid) +		print "lookup FOP: %s:%s" % (pargfid, loc.contents.name) +		# Check the cache. +		if cache.has_key(pargfid): +			if loc.contents.name in cache[pargfid]: +				print "short-circuiting for %s:%s" % ( +					pargfid, loc.contents.name) +				dl.unwind_lookup(frame,0,this,-1,2,None,None, +						 None,None) +				return 0 +		key = dl.get_id(frame) +		self.requests[key] = (pargfid, loc.contents.name[:]) +		# TBD: get real child xl from init, pass it here +		dl.wind_lookup(frame,POINTER(xlator_t)(),loc,xdata) +		return 0 + +	def lookup_cbk (self, frame, cookie, this, op_ret, op_errno, inode, buf, +			xdata, postparent): +		print "lookup CBK: %d (%d)" % (op_ret, op_errno) +		key = dl.get_id(frame) +		pargfid, name = self.requests[key] +		# Update the cache. +		if op_ret == 0: +			print "found %s, removing from cache" % name +			if cache.has_key(pargfid): +				cache[pargfid].discard(name) +		elif op_errno == 2:	# ENOENT +			print "failed to find %s, adding to cache" % name +			if cache.has_key(pargfid): +				cache[pargfid].add(name) +			else: +				cache[pargfid] = set([name]) +		del self.requests[key] +		dl.unwind_lookup(frame,cookie,this,op_ret,op_errno, +						 inode,buf,xdata,postparent) +		return 0 + +	def create_fop (self, frame, this, loc, flags, mode, umask, fd, xdata): +		pargfid = uuid2str(loc.contents.pargfid) +		print "create FOP: %s:%s" % (pargfid, loc.contents.name) +		key = dl.get_id(frame) +		self.requests[key] = (pargfid, loc.contents.name[:]) +		# TBD: get real child xl from init, pass it here +		dl.wind_create(frame,POINTER(xlator_t)(),loc,flags,mode,umask, +			       fd,xdata) +		return 0 + +	def create_cbk (self, frame, cookie, this, op_ret, op_errno, fd, inode, +			buf, preparent, postparent, xdata): +		print "create CBK: %d (%d)" % (op_ret, op_errno) +		key = dl.get_id(frame) +		pargfid, name = self.requests[key] +		# Update the cache. +		if op_ret == 0: +			print "created %s, removing from cache" % name +			if cache.has_key(pargfid): +				cache[pargfid].discard(name) +		del self.requests[key] +		dl.unwind_create(frame,cookie,this,op_ret,op_errno,fd,inode,buf, +				 preparent,postparent,xdata) +		return 0 +  | 
