]> git.proxmox.com Git - ceph.git/blob - ceph/src/cls/hello/cls_hello.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / cls / hello / cls_hello.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 /*
5 * This is a simple example RADOS class, designed to be usable as a
6 * template for implementing new methods.
7 *
8 * Our goal here is to illustrate the interface between the OSD and
9 * the class and demonstrate what kinds of things a class can do.
10 *
11 * Note that any *real* class will probably have a much more
12 * sophisticated protocol dealing with the in and out data buffers.
13 * For an example of the model that we've settled on for handling that
14 * in a clean way, please refer to cls_lock or cls_version for
15 * relatively simple examples of how the parameter encoding can be
16 * encoded in a way that allows for forward and backward compatibility
17 * between client vs class revisions.
18 */
19
20 /*
21 * A quick note about bufferlists:
22 *
23 * The bufferlist class allows memory buffers to be concatenated,
24 * truncated, spliced, "copied," encoded/embedded, and decoded. For
25 * most operations no actual data is ever copied, making bufferlists
26 * very convenient for efficiently passing data around.
27 *
28 * bufferlist is actually a typedef of buffer::list, and is defined in
29 * include/buffer.h (and implemented in common/buffer.cc).
30 */
31
32 #include <algorithm>
33 #include <string>
34 #include <sstream>
35 #include <errno.h>
36
37 #include "objclass/objclass.h"
38
39 CLS_VER(1,0)
40 CLS_NAME(hello)
41
42 /**
43 * say hello - a "read" method that does not depend on the object
44 *
45 * This is an example of a method that does some computation and
46 * returns data to the caller, without depending on the local object
47 * content.
48 */
49 static int say_hello(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
50 {
51 // see if the input data from the client matches what this method
52 // expects to receive. your class can fill this buffer with what it
53 // wants.
54 if (in->length() > 100)
55 return -EINVAL;
56
57 // we generate our reply
58 out->append("Hello, ");
59 if (in->length() == 0)
60 out->append("world");
61 else
62 out->append(*in);
63 out->append("!");
64
65 // this return value will be returned back to the librados caller
66 return 0;
67 }
68
69 /**
70 * record hello - a "write" method that creates an object
71 *
72 * This method modifies a local object (in this case, by creating it
73 * if it doesn't exist). We make multiple write calls (write,
74 * setxattr) which are accumulated and applied as an atomic
75 * transaction.
76 */
77 static int record_hello(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
78 {
79 // we can write arbitrary stuff to the ceph-osd debug log. each log
80 // message is accompanied by an integer log level. smaller is
81 // "louder". how much of this makes it into the log is controlled
82 // by the debug_cls option on the ceph-osd, similar to how other log
83 // levels are controlled. this message, at level 20, will generally
84 // not be seen by anyone unless debug_cls is set at 20 or higher.
85 CLS_LOG(20, "in record_hello");
86
87 // see if the input data from the client matches what this method
88 // expects to receive. your class can fill this buffer with what it
89 // wants.
90 if (in->length() > 100)
91 return -EINVAL;
92
93 // only say hello to non-existent objects
94 if (cls_cxx_stat(hctx, NULL, NULL) == 0)
95 return -EEXIST;
96
97 bufferlist content;
98 content.append("Hello, ");
99 if (in->length() == 0)
100 content.append("world");
101 else
102 content.append(*in);
103 content.append("!");
104
105 // create/write the object
106 int r = cls_cxx_write_full(hctx, &content);
107 if (r < 0)
108 return r;
109
110 // also make note of who said it
111 entity_inst_t origin;
112 cls_get_request_origin(hctx, &origin);
113 ostringstream ss;
114 ss << origin;
115 bufferlist attrbl;
116 attrbl.append(ss.str());
117 r = cls_cxx_setxattr(hctx, "said_by", &attrbl);
118 if (r < 0)
119 return r;
120
121 // For write operations, there are two possible outcomes:
122 //
123 // * For a failure, we return a negative error code. The out
124 // buffer can contain any data that we want, and that data will
125 // be returned to the caller. No change is made to the object.
126 //
127 // * For a success, we must return 0 and *no* data in the out
128 // buffer. This is becaues the OSD does not log write result
129 // codes or output buffers and we need a replayed/resent
130 // operation (e.g., after a TCP disconnect) to be idempotent.
131 //
132 // If a class returns a positive value or puts data in the out
133 // buffer, the OSD code will ignore it and return 0 to the
134 // client.
135 return 0;
136 }
137
138 static int writes_dont_return_data(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
139 {
140 // make some change to the object
141 bufferlist attrbl;
142 attrbl.append("bar");
143 int r = cls_cxx_setxattr(hctx, "foo", &attrbl);
144 if (r < 0)
145 return r;
146
147 if (in->length() > 0) {
148 // note that if we return anything < 0 (an error), this
149 // operation/transaction will abort, and the setattr above will
150 // never happen. however, we *can* return data on error.
151 out->append("too much input data!");
152 return -EINVAL;
153 }
154
155 // try to return some data. note that this *won't* reach the
156 // client! see the matching test case in test_cls_hello.cc.
157 out->append("you will never see this");
158
159 // if we try to return anything > 0 here the client will see 0.
160 return 42;
161 }
162
163
164 /**
165 * replay - a "read" method to get a previously recorded hello
166 *
167 * This is a read method that will retrieve a previously recorded
168 * hello statement.
169 */
170 static int replay(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
171 {
172 // read contents out of the on-disk object. our behavior can be a
173 // function of either the request alone, or the request and the
174 // on-disk state, depending on whether the RD flag is specified when
175 // registering the method (see the __cls__init function below).
176 int r = cls_cxx_read(hctx, 0, 1100, out);
177 if (r < 0)
178 return r;
179
180 // note that our return value need not be the length of the returned
181 // data; it can be whatever value we want: positive, zero or
182 // negative (this is a read).
183 return 0;
184 }
185
186 /**
187 * turn_it_to_11 - a "write" method that mutates existing object data
188 *
189 * A write method can depend on previous object content (i.e., perform
190 * a read/modify/write operation). This atomically transitions the
191 * object state from the old content to the new content.
192 */
193 static int turn_it_to_11(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
194 {
195 // see if the input data from the client matches what this method
196 // expects to receive. your class can fill this buffer with what it
197 // wants.
198 if (in->length() != 0)
199 return -EINVAL;
200
201 bufferlist previous;
202 int r = cls_cxx_read(hctx, 0, 1100, &previous);
203 if (r < 0)
204 return r;
205
206 std::string str(previous.c_str(), previous.length());
207 std::transform(str.begin(), str.end(), str.begin(), ::toupper);
208 previous.clear();
209 previous.append(str);
210
211 // replace previous byte data content (write_full == truncate(0) + write)
212 r = cls_cxx_write_full(hctx, &previous);
213 if (r < 0)
214 return r;
215
216 // record who did it
217 entity_inst_t origin;
218 cls_get_request_origin(hctx, &origin);
219 ostringstream ss;
220 ss << origin;
221 bufferlist attrbl;
222 attrbl.append(ss.str());
223 r = cls_cxx_setxattr(hctx, "amplified_by", &attrbl);
224 if (r < 0)
225 return r;
226
227 // return value is 0 for success; out buffer is empty.
228 return 0;
229 }
230
231 /**
232 * example method that does not behave
233 *
234 * This method is registered as WR but tries to read
235 */
236 static int bad_reader(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
237 {
238 return cls_cxx_read(hctx, 0, 100, out);
239 }
240
241 /**
242 * example method that does not behave
243 *
244 * This method is registered as RD but tries to write
245 */
246 static int bad_writer(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
247 {
248 return cls_cxx_write_full(hctx, in);
249 }
250
251
252 class PGLSHelloFilter : public PGLSFilter {
253 string val;
254 public:
255 int init(bufferlist::const_iterator& params) override {
256 try {
257 decode(xattr, params);
258 decode(val, params);
259 } catch (buffer::error &e) {
260 return -EINVAL;
261 }
262 return 0;
263 }
264
265 ~PGLSHelloFilter() override {}
266 bool filter(const hobject_t &obj, bufferlist& xattr_data,
267 bufferlist& outdata) override
268 {
269 if (val.size() != xattr_data.length())
270 return false;
271
272 if (memcmp(val.c_str(), xattr_data.c_str(), val.size()))
273 return false;
274
275 return true;
276 }
277 };
278
279
280 PGLSFilter *hello_filter()
281 {
282 return new PGLSHelloFilter();
283 }
284
285
286 /**
287 * initialize class
288 *
289 * We do two things here: we register the new class, and then register
290 * all of the class's methods.
291 */
292 CLS_INIT(hello)
293 {
294 // this log message, at level 0, will always appear in the ceph-osd
295 // log file.
296 CLS_LOG(0, "loading cls_hello");
297
298 cls_handle_t h_class;
299 cls_method_handle_t h_say_hello;
300 cls_method_handle_t h_record_hello;
301 cls_method_handle_t h_replay;
302 cls_method_handle_t h_writes_dont_return_data;
303 cls_method_handle_t h_turn_it_to_11;
304 cls_method_handle_t h_bad_reader;
305 cls_method_handle_t h_bad_writer;
306
307 cls_register("hello", &h_class);
308
309 // There are two flags we specify for methods:
310 //
311 // RD : whether this method (may) read prior object state
312 // WR : whether this method (may) write or update the object
313 //
314 // A method can be RD, WR, neither, or both. If a method does
315 // neither, the data it returns to the caller is a function of the
316 // request and not the object contents.
317
318 cls_register_cxx_method(h_class, "say_hello",
319 CLS_METHOD_RD,
320 say_hello, &h_say_hello);
321 cls_register_cxx_method(h_class, "record_hello",
322 CLS_METHOD_WR | CLS_METHOD_PROMOTE,
323 record_hello, &h_record_hello);
324 cls_register_cxx_method(h_class, "writes_dont_return_data",
325 CLS_METHOD_WR,
326 writes_dont_return_data, &h_writes_dont_return_data);
327 cls_register_cxx_method(h_class, "replay",
328 CLS_METHOD_RD,
329 replay, &h_replay);
330
331 // RD | WR is a read-modify-write method.
332 cls_register_cxx_method(h_class, "turn_it_to_11",
333 CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PROMOTE,
334 turn_it_to_11, &h_turn_it_to_11);
335
336 // counter-examples
337 cls_register_cxx_method(h_class, "bad_reader", CLS_METHOD_WR,
338 bad_reader, &h_bad_reader);
339 cls_register_cxx_method(h_class, "bad_writer", CLS_METHOD_RD,
340 bad_writer, &h_bad_writer);
341
342 // A PGLS filter
343 cls_register_cxx_filter(h_class, "hello", hello_filter);
344 }