]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
7c673cae FG |
4 | #include <errno.h> |
5 | ||
7c673cae FG |
6 | #include "objclass/objclass.h" |
7 | ||
7c673cae FG |
8 | #include "cls_statelog_ops.h" |
9 | ||
7c673cae FG |
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 | ||
c07f9fc5 FG |
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); | |
7c673cae FG |
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()); | |
7c673cae FG |
178 | |
179 | list<cls_statelog_entry>& entries = ret.entries; | |
180 | map<string, bufferlist>::iterator iter = keys.begin(); | |
181 | ||
7c673cae FG |
182 | string marker; |
183 | ||
c07f9fc5 | 184 | for (; iter != keys.end(); ++iter) { |
7c673cae FG |
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 | ||
c07f9fc5 | 199 | if (ret.truncated) { |
7c673cae | 200 | ret.marker = marker; |
c07f9fc5 | 201 | } |
7c673cae FG |
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 |