mirror of
https://github.com/ceph/ceph
synced 2025-01-20 10:01:45 +00:00
cls_version: create a new objclass
New objclass to track and modify objects versions. Signed-off-by: Yehuda Sadeh <yehuda@inktank.com>
This commit is contained in:
parent
8bab4fe043
commit
6a5966c308
@ -400,7 +400,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 = \
|
||||
@ -545,6 +545,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
|
||||
@ -567,6 +576,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 \
|
||||
@ -1578,6 +1591,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\
|
||||
|
234
src/cls/version/cls_version.cc
Normal file
234
src/cls/version/cls_version.cc
Normal file
@ -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;
|
||||
}
|
||||
|
77
src/cls/version/cls_version_client.cc
Normal file
77
src/cls/version/cls_version_client.cc
Normal file
@ -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;
|
||||
}
|
21
src/cls/version/cls_version_client.h
Normal file
21
src/cls/version/cls_version_client.h
Normal file
@ -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
|
95
src/cls/version/cls_version_ops.h
Normal file
95
src/cls/version/cls_version_ops.h
Normal file
@ -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
|
81
src/cls/version/cls_version_types.h
Normal file
81
src/cls/version/cls_version_types.h
Normal file
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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'
|
||||
|
Loading…
Reference in New Issue
Block a user