diff options
author | Yehuda Sadeh <yehuda@inktank.com> | 2013-02-26 14:53:49 -0800 |
---|---|---|
committer | Yehuda Sadeh <yehuda@inktank.com> | 2013-02-26 14:53:49 -0800 |
commit | f4eb144b4839ed1241468008d5fc1fef3c1f23f3 (patch) | |
tree | 7a6c38a2620f66809a648444bd5415aebca07b4f | |
parent | 35ce9d6374f5a6c61ddfa11c2a7a8cf1161c8df2 (diff) | |
download | ceph-f4eb144b4839ed1241468008d5fc1fef3c1f23f3.tar.gz |
cls_version: create a new objclass
New objclass to track and modify objects versions.
Signed-off-by: Yehuda Sadeh <yehuda@inktank.com>
-rw-r--r-- | src/Makefile.am | 18 | ||||
-rw-r--r-- | src/cls/version/cls_version.cc | 234 | ||||
-rw-r--r-- | src/cls/version/cls_version_client.cc | 77 | ||||
-rw-r--r-- | src/cls/version/cls_version_client.h | 21 | ||||
-rw-r--r-- | src/cls/version/cls_version_ops.h | 95 | ||||
-rw-r--r-- | src/cls/version/cls_version_types.h | 81 | ||||
-rw-r--r-- | src/objclass/class_api.cc | 33 | ||||
-rw-r--r-- | src/objclass/objclass.h | 4 |
8 files changed, 562 insertions, 1 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 9c453df1f53..4d0c11bfc1a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -380,7 +380,7 @@ noinst_LIBRARIES += librgw.a my_radosgw_ldadd = \ libglobal.la librgw.a librados.la libcls_rgw_client.a \ - libcls_lock_client.a libcls_refcount_client.a -lcurl -lexpat \ + libcls_lock_client.a libcls_refcount_client.a libcls_version_client.a -lcurl -lexpat \ $(PTHREAD_LIBS) -lm $(CRYPTO_LIBS) $(EXTRALIBS) radosgw_SOURCES = \ @@ -522,6 +522,15 @@ libcls_refcount_la_LDFLAGS = ${AM_LDFLAGS} -version-info 1:0:0 -export-symbols-r radoslib_LTLIBRARIES += libcls_refcount.la +# version class +libcls_version_la_SOURCES = cls/version/cls_version.cc +libcls_version_la_CFLAGS = ${AM_CFLAGS} +libcls_version_la_CXXFLAGS= ${AM_CXXFLAGS} +libcls_version_la_LIBADD = $(PTHREAD_LIBS) $(EXTRALIBS) +libcls_version_la_LDFLAGS = ${AM_LDFLAGS} -version-info 1:0:0 -export-symbols-regex '.*__cls_.*' + +radoslib_LTLIBRARIES += libcls_version.la + if WITH_RADOSGW # rgw: rados gateway @@ -544,6 +553,10 @@ libcls_refcount_client_a_SOURCES = \ cls/refcount/cls_refcount_client.cc noinst_LIBRARIES += libcls_refcount_client.a +libcls_version_client_a_SOURCES = \ + cls/version/cls_version_client.cc +noinst_LIBRARIES += libcls_version_client.a + libcls_rgw_client_a_SOURCES = \ cls/rgw/cls_rgw_client.cc \ cls/rgw/cls_rgw_types.cc \ @@ -1508,6 +1521,9 @@ noinst_HEADERS = \ cls/rbd/cls_rbd_client.h\ cls/refcount/cls_refcount_ops.h\ cls/refcount/cls_refcount_client.h\ + cls/version/cls_version_types.h\ + cls/version/cls_version_ops.h\ + cls/version/cls_version_client.h\ cls/rgw/cls_rgw_client.h\ cls/rgw/cls_rgw_ops.h\ cls/rgw/cls_rgw_types.h\ diff --git a/src/cls/version/cls_version.cc b/src/cls/version/cls_version.cc new file mode 100644 index 00000000000..bb79caa1767 --- /dev/null +++ b/src/cls/version/cls_version.cc @@ -0,0 +1,234 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include <iostream> + +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +#include "include/types.h" +#include "include/utime.h" +#include "objclass/objclass.h" +#include "cls/version/cls_version_types.h" +#include "cls/version/cls_version_ops.h" +#include "common/Clock.h" + +#include "global/global_context.h" + +CLS_VER(1,0) +CLS_NAME(version) + +cls_handle_t h_class; +cls_method_handle_t h_version_set; +cls_method_handle_t h_version_inc; +cls_method_handle_t h_version_inc_conds; +cls_method_handle_t h_version_read; +cls_method_handle_t h_version_check_conds; + + +#define VERSION_ATTR "version" + +static int set_version(cls_method_context_t hctx, struct obj_version *objv) +{ + bufferlist bl; + + ::encode(*objv, bl); + + int ret = cls_cxx_setxattr(hctx, VERSION_ATTR, &bl); + if (ret < 0) + return ret; + + return 0; +} + +static int init_version(cls_method_context_t hctx, struct obj_version *objv) +{ +#define TAG_LEN 24 + char buf[TAG_LEN + 1]; + + int ret = cls_gen_rand_base64(buf, sizeof(buf)); + if (ret < 0) + return ret; + + objv->ver = 1; + objv->tag = buf; + + return set_version(hctx, objv); +} + +/* implicit create should be true only if called from a write operation (set, inc), never from a read operation (read, check) */ +static int read_version(cls_method_context_t hctx, obj_version *objv, bool implicit_create) +{ + bufferlist bl; + int ret = cls_cxx_getxattr(hctx, VERSION_ATTR, &bl); + if (ret == -ENOENT || ret == -ENODATA) { + objv->ver = 0; + + if (implicit_create) { + return init_version(hctx, objv); + } + return 0; + } + if (ret < 0) + return ret; + + try { + bufferlist::iterator iter = bl.begin(); + ::decode(*objv, iter); + } catch (buffer::error& err) { + CLS_LOG(0, "ERROR: read_version(): failed to decode version entry\n"); + return -EIO; + } + + return 0; +} + +static int cls_version_set(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist::iterator in_iter = in->begin(); + + cls_version_set_op op; + try { + ::decode(op, in_iter); + } catch (buffer::error& err) { + CLS_LOG(1, "ERROR: cls_version_get(): failed to decode entry\n"); + return -EINVAL; + } + + int ret = set_version(hctx, &op.objv); + if (ret < 0) + return ret; + + return 0; +} + +static bool check_conds(list<obj_version_cond>& conds, obj_version& objv) +{ + if (conds.empty()) + return true; + + for (list<obj_version_cond>::iterator iter = conds.begin(); iter != conds.end(); ++iter) { + obj_version_cond& cond = *iter; + obj_version& v = cond.ver; + + switch (cond.cond) { + case VER_COND_NONE: + break; + case VER_COND_EQ: + if (!objv.compare(&v)) + return false; + break; + case VER_COND_GT: + if (!(objv.ver > v.ver)) + return false; + break; + case VER_COND_GE: + if (!(objv.ver >= v.ver)) + return false; + break; + case VER_COND_LT: + if (!(objv.ver < v.ver)) + return false; + break; + case VER_COND_LE: + if (!(objv.ver <= v.ver)) + return false; + break; + case VER_COND_TAG_EQ: + if (objv.tag.compare(v.tag) != 0) + return false; + break; + case VER_COND_TAG_NE: + if (objv.tag.compare(v.tag) == 0) + return false; + break; + } + } + + return true; +} + +static int cls_version_inc(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist::iterator in_iter = in->begin(); + + cls_version_inc_op op; + try { + ::decode(op, in_iter); + } catch (buffer::error& err) { + CLS_LOG(1, "ERROR: cls_version_get(): failed to decode entry\n"); + return -EINVAL; + } + + obj_version objv; + int ret = read_version(hctx, &objv, true); + if (ret < 0) + return ret; + + if (!check_conds(op.conds, objv)) { + return -EAGAIN; + } + objv.inc(); + + ret = set_version(hctx, &objv); + if (ret < 0) + return ret; + + return 0; +} + +static int cls_version_check(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist::iterator in_iter = in->begin(); + + cls_version_check_op op; + try { + ::decode(op, in_iter); + } catch (buffer::error& err) { + CLS_LOG(1, "ERROR: cls_version_get(): failed to decode entry\n"); + return -EINVAL; + } + + obj_version objv; + int ret = read_version(hctx, &objv, false); + if (ret < 0) + return ret; + + if (!check_conds(op.conds, objv)) { + return -EAGAIN; + } + + return 0; +} + +static int cls_version_read(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + obj_version objv; + + cls_version_read_ret read_ret; + int ret = read_version(hctx, &read_ret.objv, false); + if (ret < 0) + return ret; + + ::encode(read_ret, *out); + + return 0; +} + +void __cls_init() +{ + CLS_LOG(1, "Loaded version class!"); + + cls_register("version", &h_class); + + /* version */ + cls_register_cxx_method(h_class, "set", CLS_METHOD_RD | CLS_METHOD_WR, cls_version_set, &h_version_set); + cls_register_cxx_method(h_class, "inc", CLS_METHOD_RD | CLS_METHOD_WR, cls_version_inc, &h_version_inc); + cls_register_cxx_method(h_class, "inc_conds", CLS_METHOD_RD | CLS_METHOD_WR, cls_version_inc, &h_version_inc_conds); + cls_register_cxx_method(h_class, "read", CLS_METHOD_RD, cls_version_read, &h_version_read); + cls_register_cxx_method(h_class, "check_conds", CLS_METHOD_RD, cls_version_check, &h_version_check_conds); + + return; +} + diff --git a/src/cls/version/cls_version_client.cc b/src/cls/version/cls_version_client.cc new file mode 100644 index 00000000000..0aaa7a76b10 --- /dev/null +++ b/src/cls/version/cls_version_client.cc @@ -0,0 +1,77 @@ +#include <errno.h> + +#include "include/types.h" +#include "cls/version/cls_version_ops.h" +#include "include/rados/librados.hpp" + +using namespace librados; + + +void cls_version_set(librados::ObjectWriteOperation& op, obj_version& objv) +{ + bufferlist in; + cls_version_set_op call; + call.objv = objv; + ::encode(call, in); + op.exec("version", "set", in); +} + +void cls_version_inc(librados::ObjectWriteOperation& op) +{ + bufferlist in; + cls_version_inc_op call; + ::encode(call, in); + op.exec("version", "inc", in); +} + +void cls_version_inc(librados::ObjectWriteOperation& op, obj_version& objv, VersionCond cond) +{ + bufferlist in; + cls_version_inc_op call; + call.objv = objv; + + obj_version_cond c; + c.cond = cond; + c.ver = objv; + + call.conds.push_back(c); + + ::encode(call, in); + op.exec("version", "inc_cond", in); +} + +void cls_version_check(librados::ObjectOperation& op, obj_version& objv, VersionCond cond) +{ + bufferlist in; + cls_version_inc_op call; + call.objv = objv; + + obj_version_cond c; + c.cond = cond; + c.ver = objv; + + call.conds.push_back(c); + + ::encode(call, in); + op.exec("version", "check_conds", in); +} + +int cls_version_read(librados::IoCtx& io_ctx, string& oid, obj_version *ver) +{ + bufferlist in, out; + int r = io_ctx.exec(oid, "version", "read", in, out); + if (r < 0) + return r; + + cls_version_read_ret ret; + try { + bufferlist::iterator iter = out.begin(); + ::decode(ret, iter); + } catch (buffer::error& err) { + return -EIO; + } + + *ver = ret.objv; + + return r; +} diff --git a/src/cls/version/cls_version_client.h b/src/cls/version/cls_version_client.h new file mode 100644 index 00000000000..648254d912e --- /dev/null +++ b/src/cls/version/cls_version_client.h @@ -0,0 +1,21 @@ +#ifndef CEPH_CLS_VERSION_CLIENT_H +#define CEPH_CLS_VERSION_CLIENT_H + +#include "include/types.h" +#include "include/rados/librados.hpp" + +/* + * version objclass + */ + +void cls_version_set(librados::ObjectWriteOperation& op, obj_version& ver); + +/* increase anyway */ +void cls_version_inc(librados::ObjectWriteOperation& op); + +/* inc only if ver matches (if not empty), otherwise return -EAGAIN */ +void cls_version_inc_conditional(librados::ObjectWriteOperation& op, obj_version& ver); + +int cls_refcount_read(librados::IoCtx& io_ctx, obj_version *ver); + +#endif diff --git a/src/cls/version/cls_version_ops.h b/src/cls/version/cls_version_ops.h new file mode 100644 index 00000000000..86036bc79d1 --- /dev/null +++ b/src/cls/version/cls_version_ops.h @@ -0,0 +1,95 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_CLS_VERSION_OPS_H +#define CEPH_CLS_VERSION_OPS_H + +#include <map> + +#include "include/types.h" +#include "cls_version_types.h" + +struct cls_version_set_op { + obj_version objv; + + cls_version_set_op() {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(objv, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(objv, bl); + DECODE_FINISH(bl); + } +}; +WRITE_CLASS_ENCODER(cls_version_set_op) + +struct cls_version_inc_op { + obj_version objv; + list<obj_version_cond> conds; + + cls_version_inc_op() {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(objv, bl); + ::encode(conds, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(objv, bl); + ::decode(conds, bl); + DECODE_FINISH(bl); + } +}; +WRITE_CLASS_ENCODER(cls_version_inc_op) + +struct cls_version_check_op { + obj_version objv; + list<obj_version_cond> conds; + + cls_version_check_op() {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(objv, bl); + ::encode(conds, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(objv, bl); + ::decode(conds, bl); + DECODE_FINISH(bl); + } +}; +WRITE_CLASS_ENCODER(cls_version_check_op) + +struct cls_version_read_ret { + obj_version objv; + + cls_version_read_ret() {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(objv, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(objv, bl); + DECODE_FINISH(bl); + } +}; +WRITE_CLASS_ENCODER(cls_version_read_ret) + + +#endif diff --git a/src/cls/version/cls_version_types.h b/src/cls/version/cls_version_types.h new file mode 100644 index 00000000000..759ac25e298 --- /dev/null +++ b/src/cls/version/cls_version_types.h @@ -0,0 +1,81 @@ +#ifndef CEPH_CLS_VERSION_TYPES_H +#define CEPH_CLS_VERSION_TYPES_H + +#include "include/encoding.h" +#include "include/types.h" + + +struct obj_version { + uint64_t ver; + string tag; + + obj_version() : ver(0) {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(ver, bl); + ::encode(tag, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(ver, bl); + ::decode(tag, bl); + DECODE_FINISH(bl); + } + + void inc() { + ver++; + } + + bool empty() { + return tag.empty(); + } + + bool compare(struct obj_version *v) { + return (ver == v->ver && + tag.compare(v->tag) == 0); + } +}; +WRITE_CLASS_ENCODER(obj_version) + +enum VersionCond { + VER_COND_NONE = 0, + VER_COND_EQ, /* equal */ + VER_COND_GT, /* greater than */ + VER_COND_GE, /* greater or equal */ + VER_COND_LT, /* less than */ + VER_COND_LE, /* less or equal */ + VER_COND_TAG_EQ, + VER_COND_TAG_NE, +}; + +struct obj_version_cond { + struct obj_version ver; + VersionCond cond; + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(ver, bl); + uint32_t c = (uint32_t)cond; + ::encode(c, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(ver, bl); + uint32_t c; + ::decode(c, bl); + cond = (VersionCond)c; + DECODE_FINISH(bl); + } + +}; +WRITE_CLASS_ENCODER(obj_version_cond) + + +#endif + + diff --git a/src/objclass/class_api.cc b/src/objclass/class_api.cc index 79f5af70e98..d49890d2d4a 100644 --- a/src/objclass/class_api.cc +++ b/src/objclass/class_api.cc @@ -8,6 +8,9 @@ #include "osd/ClassHandler.h" +#include "auth/Crypto.h" +#include "common/armor.h" + static ClassHandler *ch; void cls_initialize(ClassHandler *h) @@ -542,3 +545,33 @@ int cls_cxx_map_remove_key(cls_method_context_t hctx, const string &key) return (*pctx)->pg->do_osd_ops(*pctx, ops); } +int cls_gen_random_bytes(char *buf, int size) +{ + return get_random_bytes(buf, size); +} + +int cls_gen_rand_base64(char *dest, int size) /* size should be the required string size + 1 */ +{ + char buf[size]; + char tmp_dest[size + 4]; /* so that there's space for the extra '=' characters, and some */ + int ret; + + ret = cls_gen_random_bytes(buf, sizeof(buf)); + if (ret < 0) { + generic_derr << "cannot get random bytes: " << ret << dendl; + return -1; + } + + ret = ceph_armor(tmp_dest, &tmp_dest[sizeof(tmp_dest)], + (const char *)buf, ((const char *)buf) + ((size - 1) * 3 + 4 - 1) / 4); + if (ret < 0) { + generic_derr << "ceph_armor failed" << dendl; + return -1; + } + tmp_dest[ret] = '\0'; + memcpy(dest, tmp_dest, size); + dest[size] = '\0'; + + return 0; +} + diff --git a/src/objclass/objclass.h b/src/objclass/objclass.h index a9ecd2e9d98..1acbcfd9be3 100644 --- a/src/objclass/objclass.h +++ b/src/objclass/objclass.h @@ -133,6 +133,10 @@ extern int cls_cxx_map_write_header(cls_method_context_t hctx, bufferlist *inbl) extern int cls_cxx_map_remove_key(cls_method_context_t hctx, const string &key); extern int cls_cxx_map_update(cls_method_context_t hctx, bufferlist *inbl); +/* utility functions */ +extern int cls_gen_random_bytes(char *buf, int size); +extern int cls_gen_rand_base64(char *dest, int size); /* size should be the required string size + 1 */ + /* These are also defined in rados.h and librados.h. Keep them in sync! */ #define CEPH_OSD_TMAP_HDR 'h' #define CEPH_OSD_TMAP_SET 's' |