Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / cls / statelog / cls_statelog.cc
1 // -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include <errno.h>
5
6 #include "objclass/objclass.h"
7
8 #include "cls_statelog_ops.h"
9
10
11 CLS_VER(1,0)
12 CLS_NAME(statelog)
13
14 static string statelog_index_by_client_prefix = "1_";
15 static string statelog_index_by_object_prefix = "2_";
16
17
18 static int write_statelog_entry(cls_method_context_t hctx, const string& index, const cls_statelog_entry& entry)
19 {
20   bufferlist bl;
21   ::encode(entry, bl);
22
23   int ret = cls_cxx_map_set_val(hctx, index, &bl);
24   if (ret < 0)
25     return ret;
26
27   return 0;
28 }
29
30 static void get_index_by_client(const string& client_id, const string& op_id, string& index)
31 {
32   index = statelog_index_by_client_prefix;
33   index.append(client_id + "_" + op_id);
34 }
35
36 static void get_index_by_client(cls_statelog_entry& entry, string& index)
37 {
38   get_index_by_client(entry.client_id, entry.op_id, index);
39 }
40
41 static void get_index_by_object(const string& object, const string& op_id, string& index)
42 {
43   char buf[16];
44   snprintf(buf, sizeof(buf), "%d_", (int)object.size());
45
46   index = statelog_index_by_object_prefix + buf; /* append object length to ensure uniqueness */
47   index.append(object + "_" + op_id);
48 }
49
50 static void get_index_by_object(cls_statelog_entry& entry, string& index)
51 {
52   get_index_by_object(entry.object, entry.op_id, index);
53 }
54
55 static int get_existing_entry(cls_method_context_t hctx, const string& client_id,
56                                const string& op_id, const string& object,
57                                cls_statelog_entry& entry)
58 {
59   if ((object.empty() && client_id.empty()) || op_id.empty()) {
60     return -EINVAL;
61   }
62
63   string obj_index;
64   if (!object.empty()) {
65     get_index_by_object(object, op_id, obj_index);
66   } else {
67     get_index_by_client(client_id, op_id, obj_index);
68   }
69
70   bufferlist bl;
71   int rc = cls_cxx_map_get_val(hctx, obj_index, &bl);
72   if (rc < 0) {
73     CLS_LOG(0, "could not find entry %s", obj_index.c_str());
74     return rc;
75   }
76   try {
77     bufferlist::iterator iter = bl.begin();
78     ::decode(entry, iter);
79   } catch (buffer::error& err) {
80     CLS_LOG(0, "ERROR: failed to decode entry %s", obj_index.c_str());
81     return -EIO;
82   }
83
84   if ((!object.empty() && entry.object != object) ||
85       (!client_id.empty() && entry.client_id != client_id)){
86     /* ouch, we were passed inconsistent client_id / object */
87     CLS_LOG(0, "data mismatch: object=%s client_id=%s entry: object=%s client_id=%s",
88             object.c_str(), client_id.c_str(), entry.object.c_str(), entry.client_id.c_str());
89     return -EINVAL;
90   }
91
92   return 0;
93 }
94
95 static int cls_statelog_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
96 {
97   bufferlist::iterator in_iter = in->begin();
98
99   cls_statelog_add_op op;
100   try {
101     ::decode(op, in_iter);
102   } catch (buffer::error& err) {
103     CLS_LOG(1, "ERROR: cls_statelog_add_op(): failed to decode op");
104     return -EINVAL;
105   }
106
107   for (list<cls_statelog_entry>::iterator iter = op.entries.begin();
108        iter != op.entries.end(); ++iter) {
109     cls_statelog_entry& entry = *iter;
110
111     string index_by_client;
112
113     get_index_by_client(entry, index_by_client);
114
115     CLS_LOG(0, "storing entry by client/op at %s", index_by_client.c_str());
116
117     int ret = write_statelog_entry(hctx, index_by_client, entry);
118     if (ret < 0)
119       return ret;
120
121     string index_by_obj;
122
123     get_index_by_object(entry, index_by_obj);
124
125     CLS_LOG(0, "storing entry by object at %s", index_by_obj.c_str());
126     ret = write_statelog_entry(hctx, index_by_obj, entry);
127     if (ret < 0)
128       return ret;
129
130   }
131   
132   return 0;
133 }
134
135 static int cls_statelog_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
136 {
137   bufferlist::iterator in_iter = in->begin();
138
139   cls_statelog_list_op op;
140   try {
141     ::decode(op, in_iter);
142   } catch (buffer::error& err) {
143     CLS_LOG(1, "ERROR: cls_statelog_list_op(): failed to decode op");
144     return -EINVAL;
145   }
146
147   map<string, bufferlist> keys;
148
149   string from_index;
150   string match_prefix;
151
152   if (!op.client_id.empty()) {
153     get_index_by_client(op.client_id, op.op_id, match_prefix);
154   } else if (!op.object.empty()) {
155     get_index_by_object(op.object, op.op_id, match_prefix);
156   } else {
157     match_prefix = statelog_index_by_object_prefix;
158   }
159
160   if (op.marker.empty()) {
161     from_index = match_prefix;
162   } else {
163     from_index = op.marker;
164   }
165
166 #define MAX_ENTRIES 1000
167   size_t max_entries = op.max_entries;
168   if (!max_entries || max_entries > MAX_ENTRIES)
169     max_entries = MAX_ENTRIES;
170
171   cls_statelog_list_ret ret;
172
173   int rc = cls_cxx_map_get_vals(hctx, from_index, match_prefix, max_entries, &keys, &ret.truncated);
174   if (rc < 0)
175     return rc;
176
177   CLS_LOG(20, "from_index=%s match_prefix=%s", from_index.c_str(), match_prefix.c_str());
178
179   list<cls_statelog_entry>& entries = ret.entries;
180   map<string, bufferlist>::iterator iter = keys.begin();
181
182   string marker;
183
184   for (; iter != keys.end(); ++iter) {
185     const string& index = iter->first;
186     marker = index;
187
188     bufferlist& bl = iter->second;
189     bufferlist::iterator biter = bl.begin();
190     try {
191       cls_statelog_entry e;
192       ::decode(e, biter);
193       entries.push_back(e);
194     } catch (buffer::error& err) {
195       CLS_LOG(0, "ERROR: cls_statelog_list: could not decode entry, index=%s", index.c_str());
196     }
197   }
198
199   if (ret.truncated) {
200     ret.marker = marker;
201   }
202
203   ::encode(ret, *out);
204
205   return 0;
206 }
207
208 static int cls_statelog_remove(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
209 {
210   bufferlist::iterator in_iter = in->begin();
211
212   cls_statelog_remove_op op;
213   try {
214     ::decode(op, in_iter);
215   } catch (buffer::error& err) {
216     CLS_LOG(1, "ERROR: cls_statelog_remove_op(): failed to decode op");
217     return -EINVAL;
218   }
219
220   cls_statelog_entry entry;
221
222   int rc = get_existing_entry(hctx, op.client_id, op.op_id, op.object, entry);
223   if (rc < 0)
224     return rc;
225
226   string obj_index;
227   get_index_by_object(entry.object, entry.op_id, obj_index);
228
229   rc = cls_cxx_map_remove_key(hctx, obj_index);
230   if (rc < 0) {
231     CLS_LOG(0, "ERROR: failed to remove key");
232     return rc;
233   }
234
235   string client_index;
236   get_index_by_client(entry.client_id, entry.op_id, client_index);
237
238   rc = cls_cxx_map_remove_key(hctx, client_index);
239   if (rc < 0) {
240     CLS_LOG(0, "ERROR: failed to remove key");
241     return rc;
242   }
243
244   return 0;
245 }
246
247 static int cls_statelog_check_state(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
248 {
249   bufferlist::iterator in_iter = in->begin();
250
251   cls_statelog_check_state_op op;
252   try {
253     ::decode(op, in_iter);
254   } catch (buffer::error& err) {
255     CLS_LOG(1, "ERROR: cls_statelog_check_state_op(): failed to decode op");
256     return -EINVAL;
257   }
258
259   if (op.object.empty() || op.op_id.empty()) {
260     CLS_LOG(0, "object name or op id not specified");
261     return -EINVAL;
262   }
263
264
265   cls_statelog_entry entry;
266
267   int rc = get_existing_entry(hctx, op.client_id, op.op_id, op.object, entry);
268   if (rc < 0)
269     return rc;
270
271   if (entry.state != op.state)
272     return -ECANCELED;
273
274   return 0;
275 }
276
277 CLS_INIT(statelog)
278 {
279   CLS_LOG(1, "Loaded log class!");
280
281   cls_handle_t h_class;
282   cls_method_handle_t h_statelog_add;
283   cls_method_handle_t h_statelog_list;
284   cls_method_handle_t h_statelog_remove;
285   cls_method_handle_t h_statelog_check_state;
286
287   cls_register("statelog", &h_class);
288
289   /* log */
290   cls_register_cxx_method(h_class, "add", CLS_METHOD_RD | CLS_METHOD_WR, cls_statelog_add, &h_statelog_add);
291   cls_register_cxx_method(h_class, "list", CLS_METHOD_RD, cls_statelog_list, &h_statelog_list);
292   cls_register_cxx_method(h_class, "remove", CLS_METHOD_RD | CLS_METHOD_WR, cls_statelog_remove, &h_statelog_remove);
293   cls_register_cxx_method(h_class, "check_state", CLS_METHOD_RD, cls_statelog_check_state, &h_statelog_check_state);
294
295   return;
296 }
297