Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / 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::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 }