diff options
author | Yehuda Sadeh <yehuda@inktank.com> | 2013-03-08 15:30:31 -0800 |
---|---|---|
committer | Yehuda Sadeh <yehuda@inktank.com> | 2013-04-15 14:23:12 -0700 |
commit | 8db9a4bf167fc78430df9ce6000535ab83ddfba9 (patch) | |
tree | d14ca7b367526f306b9599780c3ce61bbd1b4920 | |
parent | 5415bd0892b4cf83a3f896d7de1c04207971628e (diff) | |
download | ceph-8db9a4bf167fc78430df9ce6000535ab83ddfba9.tar.gz |
cls_log: a class to handle info sorted by timestamp
Signed-off-by: Yehuda Sadeh <yehuda@inktank.com>
-rw-r--r-- | src/Makefile.am | 15 | ||||
-rw-r--r-- | src/cls/log/cls_log.cc | 192 | ||||
-rw-r--r-- | src/cls/log/cls_log_client.cc | 62 | ||||
-rw-r--r-- | src/cls/log/cls_log_client.h | 17 | ||||
-rw-r--r-- | src/cls/log/cls_log_ops.h | 95 | ||||
-rw-r--r-- | src/cls/log/cls_log_types.h | 46 |
6 files changed, 427 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 8ad79767060..11756f698fd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -540,6 +540,15 @@ libcls_version_la_LDFLAGS = ${AM_LDFLAGS} -version-info 1:0:0 -export-symbols-re radoslib_LTLIBRARIES += libcls_version.la +# log class +libcls_log_la_SOURCES = cls/log/cls_log.cc +libcls_log_la_CFLAGS = ${AM_CFLAGS} +libcls_log_la_CXXFLAGS= ${AM_CXXFLAGS} +libcls_log_la_LIBADD = $(PTHREAD_LIBS) $(EXTRALIBS) +libcls_log_la_LDFLAGS = ${AM_LDFLAGS} -version-info 1:0:0 -export-symbols-regex '.*__cls_.*' + +radoslib_LTLIBRARIES += libcls_log.la + if WITH_RADOSGW # rgw: rados gateway @@ -567,6 +576,10 @@ libcls_version_client_a_SOURCES = \ cls/version/cls_version_types.cc noinst_LIBRARIES += libcls_version_client.a +libcls_log_client_a_SOURCES = \ + cls/log/cls_log_client.cc +noinst_LIBRARIES += libcls_log_client.a + libcls_rgw_client_a_SOURCES = \ cls/rgw/cls_rgw_client.cc \ cls/rgw/cls_rgw_types.cc \ @@ -1586,6 +1599,8 @@ noinst_HEADERS = \ cls/version/cls_version_types.h\ cls/version/cls_version_ops.h\ cls/version/cls_version_client.h\ + cls/log/cls_log_types.h\ + cls/log/cls_log_ops.h\ cls/rgw/cls_rgw_client.h\ cls/rgw/cls_rgw_ops.h\ cls/rgw/cls_rgw_types.h\ diff --git a/src/cls/log/cls_log.cc b/src/cls/log/cls_log.cc new file mode 100644 index 00000000000..fad61eb3442 --- /dev/null +++ b/src/cls/log/cls_log.cc @@ -0,0 +1,192 @@ +// -*- 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_log_types.h" +#include "cls_log_ops.h" + +#include "global/global_context.h" + +CLS_VER(1,0) +CLS_NAME(log) + +cls_handle_t h_class; +cls_method_handle_t h_log_add; +cls_method_handle_t h_log_list; +cls_method_handle_t h_log_trim; + +static string log_index_prefix = "1_"; + + +static int write_log_entry(cls_method_context_t hctx, string& index, cls_log_entry& entry) +{ + bufferlist bl; + ::encode(entry, bl); + + int ret = cls_cxx_map_set_val(hctx, index, &bl); + if (ret < 0) + return ret; + + return 0; +} + +static void get_index_time_prefix(utime_t& ts, string& index) +{ + char buf[32]; + snprintf(buf, sizeof(buf), "%10ld.%10ld_", (long)ts.sec(), (long)ts.usec()); + + index = log_index_prefix + buf; +} + +static void get_index(utime_t& ts, string& section, string& name, string& index) +{ + get_index_time_prefix(ts, index); + + index += section + ":" + name; +} + +static int cls_log_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist::iterator in_iter = in->begin(); + + cls_log_add_op op; + try { + ::decode(op, in_iter); + } catch (buffer::error& err) { + CLS_LOG(1, "ERROR: cls_log_add_op(): failed to decode entry\n"); + return -EINVAL; + } + + cls_log_entry& entry = op.entry; + + string index; + + get_index(entry.timestamp, entry.section, entry.name, index); + + int ret = write_log_entry(hctx, index, entry); + if (ret < 0) + return ret; + + return 0; +} + +static int cls_log_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist::iterator in_iter = in->begin(); + + cls_log_list_op op; + try { + ::decode(op, in_iter); + } catch (buffer::error& err) { + CLS_LOG(1, "ERROR: cls_log_list_op(): failed to decode entry\n"); + return -EINVAL; + } + + map<string, bufferlist> keys; + + string index; + + get_index_time_prefix(op.from_time, index); + +#define MAX_ENTRIES 1000 + size_t max_entries = min(MAX_ENTRIES, op.num_entries); + + int rc = cls_cxx_map_get_vals(hctx, index, log_index_prefix, max_entries + 1, &keys); + if (rc < 0) + return rc; + + cls_log_list_ret ret; + + list<cls_log_entry>& entries = ret.entries; + map<string, bufferlist>::iterator iter = keys.begin(); + + size_t i; + for (i = 0; i < max_entries && iter != keys.end(); ++i, ++iter) { + bufferlist& bl = iter->second; + bufferlist::iterator biter = bl.begin(); + try { + cls_log_entry e; + ::decode(e, biter); + entries.push_back(e); + } catch (buffer::error& err) { + CLS_LOG(0, "ERROR: cls_log_list: could not decode entry, index=%s\n", index.c_str()); + } + } + + ret.truncated = (i < keys.size()); + + ::encode(ret, *out); + + return 0; +} + + +static int cls_log_trim(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist::iterator in_iter = in->begin(); + + cls_log_trim_op op; + try { + ::decode(op, in_iter); + } catch (buffer::error& err) { + CLS_LOG(1, "ERROR: cls_log_list_op(): failed to decode entry\n"); + return -EINVAL; + } + + map<string, bufferlist> keys; + + string from_index; + string to_index; + + get_index_time_prefix(op.from_time, from_index); + get_index_time_prefix(op.from_time, to_index); + +#define MAX_TRIM_ENTRIES 1000 + size_t max_entries = MAX_TRIM_ENTRIES; + + int rc = cls_cxx_map_get_vals(hctx, index, log_index_prefix, max_entries, &keys); + if (rc < 0) + return rc; + + map<string, bufferlist>::iterator iter = keys.begin(); + + size_t i; + for (i = 0; i < max_entries && iter != keys.end(); ++i, ++iter) { + const string& index = iter->first; + + if (index >= to_index) + break; + + int rc = cls_cxx_map_remove_key(hctx, index); + if (rc < 0) { + CLS_LOG(1, "ERROR: cls_log_trim_op(): failed to decode entry\n"); + return -EINVAL; + } + } + + return 0; +} + +void __cls_init() +{ + CLS_LOG(1, "Loaded log class!"); + + cls_register("log", &h_class); + + /* log */ + cls_register_cxx_method(h_class, "log_add", CLS_METHOD_RD | CLS_METHOD_WR, cls_log_add, &h_log_add); + cls_register_cxx_method(h_class, "log_list", CLS_METHOD_RD, cls_log_list, &h_log_list); + cls_register_cxx_method(h_class, "log_trim", CLS_METHOD_RD | CLS_METHOD_WR, cls_log_trim, &h_log_trim); + + return; +} + diff --git a/src/cls/log/cls_log_client.cc b/src/cls/log/cls_log_client.cc new file mode 100644 index 00000000000..0f1854f58bf --- /dev/null +++ b/src/cls/log/cls_log_client.cc @@ -0,0 +1,62 @@ +#include <errno.h> + +#include "include/types.h" +#include "cls/log/cls_log_ops.h" +#include "include/rados/librados.hpp" + + +using namespace librados; + + + + +void cls_log_add(librados::ObjectWriteOperation& op, cls_log_entry& entry) +{ + bufferlist in; + cls_log_add_op call; + call.entry = entry; + ::encode(call, in); + op.exec("log", "add", in); +} + +void cls_log_trim(librados::ObjectWriteOperation& op, utime_t& from, utime_t& to) +{ + bufferlist in; + cls_log_trim_op call; + call.from_time = from; + call.to_time = to; + ::encode(call, in); + op.exec("log", "trim", in); +} + +class LogListCtx : public ObjectOperationCompletion { + list<cls_log_entry> *entries; + bool *truncated; +public: + LogListCtx(list<cls_log_entry> *_entries, bool *_truncated) : + entries(_entries), truncated(_truncated) {} + void handle_completion(int r, bufferlist& outbl) { + if (r >= 0) { + cls_log_list_ret ret; + try { + bufferlist::iterator iter = outbl.begin(); + ::decode(ret, iter); + *entries = ret.entries; + *truncated = ret.truncated; + } catch (buffer::error& err) { + // nothing we can do about it atm + } + } + } +}; + +void cls_log_list(librados::ObjectReadOperation& op, utime_t& from, int max, + list<cls_log_entry>& entries, bool *truncated) +{ + bufferlist inbl; + cls_log_list_op call; + call.from_time = from; + call.num_entries = max; + op.exec("log", "list", inbl, new LogListCtx(&entries, truncated)); +} + diff --git a/src/cls/log/cls_log_client.h b/src/cls/log/cls_log_client.h new file mode 100644 index 00000000000..b2543c09c13 --- /dev/null +++ b/src/cls/log/cls_log_client.h @@ -0,0 +1,17 @@ +#ifndef CEPH_CLS_LOG_CLIENT_H +#define CEPH_CLS_LOG_CLIENT_H + +#include "include/types.h" +#include "include/rados/librados.hpp" + +/* + * log objclass + */ + +void cls_log_add(librados::ObjectWriteOperation& op, cls_log_entry& entry); + +void cls_log_list(librados::ObjectReadOperation& op, utime_t& from, int max, list<cls_log_entry>& entries); + +void cls_log_trim(librados::ObjectWriteOperation& op, utime_t& from, utime_t& to); + +#endif diff --git a/src/cls/log/cls_log_ops.h b/src/cls/log/cls_log_ops.h new file mode 100644 index 00000000000..28a7dc8184e --- /dev/null +++ b/src/cls/log/cls_log_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_LOG_OPS_H +#define CEPH_CLS_LOG_OPS_H + +#include "include/types.h" +#include "cls_log_types.h" + +struct cls_log_add_op { + cls_log_entry entry; + + cls_log_add_op() {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(entry, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(entry, bl); + DECODE_FINISH(bl); + } +}; +WRITE_CLASS_ENCODER(cls_log_add_op) + +struct cls_log_list_op { + utime_t from_time; + int num_entries; + + cls_log_list_op() {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(from_time, bl); + ::encode(num_entries, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(from_time, bl); + ::decode(num_entries, bl); + DECODE_FINISH(bl); + } +}; +WRITE_CLASS_ENCODER(cls_log_list_op) + +struct cls_log_list_ret { + list<cls_log_entry> entries; + bool truncated; + + cls_log_list_ret() : truncated(false) {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(entries, bl); + ::encode(truncated, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(entries, bl); + ::decode(truncated, bl); + DECODE_FINISH(bl); + } +}; +WRITE_CLASS_ENCODER(cls_log_list_ret) + +struct cls_log_trim_op { + utime_t from_time; + utime_t to_time; + + cls_log_trim_op() {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(from_time, bl); + ::encode(to_time, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(from_time, bl); + ::decode(to_time, bl); + DECODE_FINISH(bl); + } +}; +WRITE_CLASS_ENCODER(cls_log_trim_op) + +#endif diff --git a/src/cls/log/cls_log_types.h b/src/cls/log/cls_log_types.h new file mode 100644 index 00000000000..06684d9e5d4 --- /dev/null +++ b/src/cls/log/cls_log_types.h @@ -0,0 +1,46 @@ +#ifndef CEPH_CLS_LOG_TYPES_H +#define CEPH_CLS_LOG_TYPES_H + +#include "include/encoding.h" +#include "include/types.h" + +#include "include/utime.h" + +class JSONObj; + + +struct cls_log_entry { + string section; + string name; + utime_t timestamp; + bufferlist data; + + cls_log_entry() {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(section, bl); + ::encode(name, bl); + ::encode(timestamp, bl); + ::encode(data, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(section, bl); + ::decode(name, bl); + ::decode(timestamp, bl); + ::decode(data, bl); + DECODE_FINISH(bl); + } + + void dump(Formatter *f) const; + void decode_json(JSONObj *obj); +}; +WRITE_CLASS_ENCODER(cls_log_entry) + + +#endif + + |