summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYehuda Sadeh <yehuda@inktank.com>2013-03-08 15:30:31 -0800
committerYehuda Sadeh <yehuda@inktank.com>2013-04-15 14:23:12 -0700
commit8db9a4bf167fc78430df9ce6000535ab83ddfba9 (patch)
treed14ca7b367526f306b9599780c3ce61bbd1b4920
parent5415bd0892b4cf83a3f896d7de1c04207971628e (diff)
downloadceph-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.am15
-rw-r--r--src/cls/log/cls_log.cc192
-rw-r--r--src/cls/log/cls_log_client.cc62
-rw-r--r--src/cls/log/cls_log_client.h17
-rw-r--r--src/cls/log/cls_log_ops.h95
-rw-r--r--src/cls/log/cls_log_types.h46
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
+
+