]>
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 "include/types.h" |
5 | #include "include/utime.h" | |
6 | #include "objclass/objclass.h" | |
7 | ||
8 | #include "cls_log_types.h" | |
9 | #include "cls_log_ops.h" | |
10 | ||
11 | #include "global/global_context.h" | |
12 | #include "include/compat.h" | |
13 | ||
14 | CLS_VER(1,0) | |
15 | CLS_NAME(log) | |
16 | ||
17 | static string log_index_prefix = "1_"; | |
18 | ||
19 | ||
20 | static int write_log_entry(cls_method_context_t hctx, string& index, cls_log_entry& entry) | |
21 | { | |
22 | bufferlist bl; | |
23 | ::encode(entry, bl); | |
24 | ||
25 | int ret = cls_cxx_map_set_val(hctx, index, &bl); | |
26 | if (ret < 0) | |
27 | return ret; | |
28 | ||
29 | return 0; | |
30 | } | |
31 | ||
32 | static void get_index_time_prefix(utime_t& ts, string& index) | |
33 | { | |
34 | char buf[32]; | |
35 | snprintf(buf, sizeof(buf), "%010ld.%06ld_", (long)ts.sec(), (long)ts.usec()); | |
36 | ||
37 | index = log_index_prefix + buf; | |
38 | } | |
39 | ||
40 | static int read_header(cls_method_context_t hctx, cls_log_header& header) | |
41 | { | |
42 | bufferlist header_bl; | |
43 | ||
44 | int ret = cls_cxx_map_read_header(hctx, &header_bl); | |
45 | if (ret < 0) | |
46 | return ret; | |
47 | ||
48 | if (header_bl.length() == 0) { | |
49 | header = cls_log_header(); | |
50 | return 0; | |
51 | } | |
52 | ||
53 | bufferlist::iterator iter = header_bl.begin(); | |
54 | try { | |
55 | ::decode(header, iter); | |
56 | } catch (buffer::error& err) { | |
57 | CLS_LOG(0, "ERROR: read_header(): failed to decode header"); | |
58 | } | |
59 | ||
60 | return 0; | |
61 | } | |
62 | ||
63 | static int write_header(cls_method_context_t hctx, cls_log_header& header) | |
64 | { | |
65 | bufferlist header_bl; | |
66 | ::encode(header, header_bl); | |
67 | ||
68 | int ret = cls_cxx_map_write_header(hctx, &header_bl); | |
69 | if (ret < 0) | |
70 | return ret; | |
71 | ||
72 | return 0; | |
73 | } | |
74 | ||
75 | static void get_index(cls_method_context_t hctx, utime_t& ts, string& index) | |
76 | { | |
77 | get_index_time_prefix(ts, index); | |
78 | ||
79 | string unique_id; | |
80 | ||
81 | cls_cxx_subop_version(hctx, &unique_id); | |
82 | ||
83 | index.append(unique_id); | |
84 | } | |
85 | ||
86 | static int cls_log_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out) | |
87 | { | |
88 | bufferlist::iterator in_iter = in->begin(); | |
89 | ||
90 | cls_log_add_op op; | |
91 | try { | |
92 | ::decode(op, in_iter); | |
93 | } catch (buffer::error& err) { | |
94 | CLS_LOG(1, "ERROR: cls_log_add_op(): failed to decode op"); | |
95 | return -EINVAL; | |
96 | } | |
97 | ||
98 | cls_log_header header; | |
99 | ||
100 | int ret = read_header(hctx, header); | |
101 | if (ret < 0) | |
102 | return ret; | |
103 | ||
104 | for (list<cls_log_entry>::iterator iter = op.entries.begin(); | |
105 | iter != op.entries.end(); ++iter) { | |
106 | cls_log_entry& entry = *iter; | |
107 | ||
108 | string index; | |
109 | ||
110 | utime_t timestamp = entry.timestamp; | |
111 | if (op.monotonic_inc && timestamp < header.max_time) | |
112 | timestamp = header.max_time; | |
113 | else if (timestamp > header.max_time) | |
114 | header.max_time = timestamp; | |
115 | ||
116 | if (entry.id.empty()) { | |
117 | get_index(hctx, timestamp, index); | |
118 | entry.id = index; | |
119 | } else { | |
120 | index = entry.id; | |
121 | } | |
122 | ||
123 | CLS_LOG(20, "storing entry at %s", index.c_str()); | |
124 | ||
125 | ||
126 | if (index > header.max_marker) | |
127 | header.max_marker = index; | |
128 | ||
129 | ret = write_log_entry(hctx, index, entry); | |
130 | if (ret < 0) | |
131 | return ret; | |
132 | } | |
133 | ||
134 | ret = write_header(hctx, header); | |
135 | if (ret < 0) | |
136 | return ret; | |
137 | ||
138 | return 0; | |
139 | } | |
140 | ||
141 | static int cls_log_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out) | |
142 | { | |
143 | bufferlist::iterator in_iter = in->begin(); | |
144 | ||
145 | cls_log_list_op op; | |
146 | try { | |
147 | ::decode(op, in_iter); | |
148 | } catch (buffer::error& err) { | |
149 | CLS_LOG(1, "ERROR: cls_log_list_op(): failed to decode op"); | |
150 | return -EINVAL; | |
151 | } | |
152 | ||
153 | map<string, bufferlist> keys; | |
154 | ||
155 | string from_index; | |
156 | string to_index; | |
157 | ||
158 | if (op.marker.empty()) { | |
159 | get_index_time_prefix(op.from_time, from_index); | |
160 | } else { | |
161 | from_index = op.marker; | |
162 | } | |
163 | bool use_time_boundary = (!op.from_time.is_zero() && (op.to_time >= op.from_time)); | |
164 | ||
165 | if (use_time_boundary) | |
166 | get_index_time_prefix(op.to_time, to_index); | |
167 | ||
168 | #define MAX_ENTRIES 1000 | |
169 | size_t max_entries = op.max_entries; | |
170 | if (!max_entries || max_entries > MAX_ENTRIES) | |
171 | max_entries = MAX_ENTRIES; | |
172 | ||
173 | int rc = cls_cxx_map_get_vals(hctx, from_index, log_index_prefix, max_entries + 1, &keys); | |
174 | if (rc < 0) | |
175 | return rc; | |
176 | ||
177 | cls_log_list_ret ret; | |
178 | ||
179 | list<cls_log_entry>& entries = ret.entries; | |
180 | map<string, bufferlist>::iterator iter = keys.begin(); | |
181 | ||
182 | bool done = false; | |
183 | string marker; | |
184 | ||
185 | size_t i; | |
186 | for (i = 0; i < max_entries && iter != keys.end(); ++i, ++iter) { | |
187 | const string& index = iter->first; | |
188 | marker = index; | |
189 | if (use_time_boundary && index.compare(0, to_index.size(), to_index) >= 0) { | |
190 | done = true; | |
191 | break; | |
192 | } | |
193 | ||
194 | bufferlist& bl = iter->second; | |
195 | bufferlist::iterator biter = bl.begin(); | |
196 | try { | |
197 | cls_log_entry e; | |
198 | ::decode(e, biter); | |
199 | entries.push_back(e); | |
200 | } catch (buffer::error& err) { | |
201 | CLS_LOG(0, "ERROR: cls_log_list: could not decode entry, index=%s", index.c_str()); | |
202 | } | |
203 | } | |
204 | ||
205 | if (iter == keys.end()) | |
206 | done = true; | |
207 | ||
208 | ret.marker = marker; | |
209 | ret.truncated = !done; | |
210 | ||
211 | ::encode(ret, *out); | |
212 | ||
213 | return 0; | |
214 | } | |
215 | ||
216 | ||
217 | static int cls_log_trim(cls_method_context_t hctx, bufferlist *in, bufferlist *out) | |
218 | { | |
219 | bufferlist::iterator in_iter = in->begin(); | |
220 | ||
221 | cls_log_trim_op op; | |
222 | try { | |
223 | ::decode(op, in_iter); | |
224 | } catch (buffer::error& err) { | |
225 | CLS_LOG(0, "ERROR: cls_log_list_op(): failed to decode entry"); | |
226 | return -EINVAL; | |
227 | } | |
228 | ||
229 | map<string, bufferlist> keys; | |
230 | ||
231 | string from_index; | |
232 | string to_index; | |
233 | ||
234 | if (op.from_marker.empty()) { | |
235 | get_index_time_prefix(op.from_time, from_index); | |
236 | } else { | |
237 | from_index = op.from_marker; | |
238 | } | |
239 | if (op.to_marker.empty()) { | |
240 | get_index_time_prefix(op.to_time, to_index); | |
241 | } else { | |
242 | to_index = op.to_marker; | |
243 | } | |
244 | ||
245 | #define MAX_TRIM_ENTRIES 1000 | |
246 | size_t max_entries = MAX_TRIM_ENTRIES; | |
247 | ||
248 | int rc = cls_cxx_map_get_vals(hctx, from_index, log_index_prefix, max_entries, &keys); | |
249 | if (rc < 0) | |
250 | return rc; | |
251 | ||
252 | map<string, bufferlist>::iterator iter = keys.begin(); | |
253 | ||
254 | size_t i; | |
255 | bool removed = false; | |
256 | for (i = 0; i < max_entries && iter != keys.end(); ++i, ++iter) { | |
257 | const string& index = iter->first; | |
258 | ||
259 | CLS_LOG(20, "index=%s to_index=%s", index.c_str(), to_index.c_str()); | |
260 | ||
261 | if (index.compare(0, to_index.size(), to_index) > 0) | |
262 | break; | |
263 | ||
264 | CLS_LOG(20, "removing key: index=%s", index.c_str()); | |
265 | ||
266 | int rc = cls_cxx_map_remove_key(hctx, index); | |
267 | if (rc < 0) { | |
268 | CLS_LOG(1, "ERROR: cls_cxx_map_remove_key failed rc=%d", rc); | |
269 | return -EINVAL; | |
270 | } | |
271 | removed = true; | |
272 | } | |
273 | ||
274 | if (!removed) | |
275 | return -ENODATA; | |
276 | ||
277 | return 0; | |
278 | } | |
279 | ||
280 | static int cls_log_info(cls_method_context_t hctx, bufferlist *in, bufferlist *out) | |
281 | { | |
282 | bufferlist::iterator in_iter = in->begin(); | |
283 | ||
284 | cls_log_info_op op; | |
285 | try { | |
286 | ::decode(op, in_iter); | |
287 | } catch (buffer::error& err) { | |
288 | CLS_LOG(1, "ERROR: cls_log_add_op(): failed to decode op"); | |
289 | return -EINVAL; | |
290 | } | |
291 | ||
292 | cls_log_info_ret ret; | |
293 | ||
294 | int rc = read_header(hctx, ret.header); | |
295 | if (rc < 0) | |
296 | return rc; | |
297 | ||
298 | ::encode(ret, *out); | |
299 | ||
300 | return 0; | |
301 | } | |
302 | ||
303 | CLS_INIT(log) | |
304 | { | |
305 | CLS_LOG(1, "Loaded log class!"); | |
306 | ||
307 | cls_handle_t h_class; | |
308 | cls_method_handle_t h_log_add; | |
309 | cls_method_handle_t h_log_list; | |
310 | cls_method_handle_t h_log_trim; | |
311 | cls_method_handle_t h_log_info; | |
312 | ||
313 | cls_register("log", &h_class); | |
314 | ||
315 | /* log */ | |
316 | cls_register_cxx_method(h_class, "add", CLS_METHOD_RD | CLS_METHOD_WR, cls_log_add, &h_log_add); | |
317 | cls_register_cxx_method(h_class, "list", CLS_METHOD_RD, cls_log_list, &h_log_list); | |
318 | cls_register_cxx_method(h_class, "trim", CLS_METHOD_RD | CLS_METHOD_WR, cls_log_trim, &h_log_trim); | |
319 | cls_register_cxx_method(h_class, "info", CLS_METHOD_RD, cls_log_info, &h_log_info); | |
320 | ||
321 | return; | |
322 | } | |
323 |