]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_sync_module_es_rest.cc
import ceph pacific 16.2.5
[ceph.git] / ceph / src / rgw / rgw_sync_module_es_rest.cc
CommitLineData
11fdf7f2 1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
9f95a23c 2// vim: ts=8 sw=2 smarttab ft=cpp
11fdf7f2 3
31f18b77
FG
4#include "rgw_sync_module_es.h"
5#include "rgw_sync_module_es_rest.h"
6#include "rgw_es_query.h"
7#include "rgw_op.h"
8#include "rgw_rest.h"
9#include "rgw_rest_s3.h"
f67539c2 10#include "rgw_sal_rados.h"
31f18b77
FG
11
12#define dout_context g_ceph_context
13#define dout_subsys ceph_subsys_rgw
14
15struct es_index_obj_response {
16 string bucket;
17 rgw_obj_key key;
18 uint64_t versioned_epoch{0};
19 ACLOwner owner;
20 set<string> read_permissions;
21
22 struct {
23 uint64_t size{0};
24 ceph::real_time mtime;
25 string etag;
26 string content_type;
a8e16298 27 string storage_class;
31f18b77
FG
28 map<string, string> custom_str;
29 map<string, int64_t> custom_int;
30 map<string, string> custom_date;
31
32 template <class T>
33 struct _custom_entry {
34 string name;
35 T value;
36 void decode_json(JSONObj *obj) {
37 JSONDecoder::decode_json("name", name, obj);
38 JSONDecoder::decode_json("value", value, obj);
39 }
40 };
41
42 void decode_json(JSONObj *obj) {
43 JSONDecoder::decode_json("size", size, obj);
44 string mtime_str;
45 JSONDecoder::decode_json("mtime", mtime_str, obj);
46 parse_time(mtime_str.c_str(), &mtime);
47 JSONDecoder::decode_json("etag", etag, obj);
48 JSONDecoder::decode_json("content_type", content_type, obj);
a8e16298 49 JSONDecoder::decode_json("storage_class", storage_class, obj);
31f18b77
FG
50 list<_custom_entry<string> > str_entries;
51 JSONDecoder::decode_json("custom-string", str_entries, obj);
52 for (auto& e : str_entries) {
53 custom_str[e.name] = e.value;
54 }
55 list<_custom_entry<int64_t> > int_entries;
56 JSONDecoder::decode_json("custom-int", int_entries, obj);
57 for (auto& e : int_entries) {
58 custom_int[e.name] = e.value;
59 }
60 list<_custom_entry<string> > date_entries;
61 JSONDecoder::decode_json("custom-date", date_entries, obj);
62 for (auto& e : date_entries) {
63 custom_date[e.name] = e.value;
64 }
65 }
66 } meta;
67
68 void decode_json(JSONObj *obj) {
69 JSONDecoder::decode_json("bucket", bucket, obj);
70 JSONDecoder::decode_json("name", key.name, obj);
71 JSONDecoder::decode_json("instance", key.instance, obj);
72 JSONDecoder::decode_json("versioned_epoch", versioned_epoch, obj);
73 JSONDecoder::decode_json("permissions", read_permissions, obj);
74 JSONDecoder::decode_json("owner", owner, obj);
75 JSONDecoder::decode_json("meta", meta, obj);
76 }
77};
78
79struct es_search_response {
80 uint32_t took;
81 bool timed_out;
82 struct {
83 uint32_t total;
84 uint32_t successful;
85 uint32_t failed;
86 void decode_json(JSONObj *obj) {
87 JSONDecoder::decode_json("total", total, obj);
88 JSONDecoder::decode_json("successful", successful, obj);
89 JSONDecoder::decode_json("failed", failed, obj);
90 }
91 } shards;
92 struct obj_hit {
93 string index;
94 string type;
95 string id;
96 // double score
97 es_index_obj_response source;
98 void decode_json(JSONObj *obj) {
99 JSONDecoder::decode_json("_index", index, obj);
100 JSONDecoder::decode_json("_type", type, obj);
101 JSONDecoder::decode_json("_id", id, obj);
102 JSONDecoder::decode_json("_source", source, obj);
103 }
104 };
105 struct {
106 uint32_t total;
107 // double max_score;
108 list<obj_hit> hits;
109 void decode_json(JSONObj *obj) {
110 JSONDecoder::decode_json("total", total, obj);
111 // JSONDecoder::decode_json("max_score", max_score, obj);
112 JSONDecoder::decode_json("hits", hits, obj);
113 }
114 } hits;
115 void decode_json(JSONObj *obj) {
116 JSONDecoder::decode_json("took", took, obj);
117 JSONDecoder::decode_json("timed_out", timed_out, obj);
118 JSONDecoder::decode_json("_shards", shards, obj);
119 JSONDecoder::decode_json("hits", hits, obj);
120 }
121};
122
123class RGWMetadataSearchOp : public RGWOp {
124 RGWSyncModuleInstanceRef sync_module_ref;
125 RGWElasticSyncModuleInstance *es_module;
126protected:
127 string expression;
128 string custom_prefix;
129#define MAX_KEYS_DEFAULT 100
130 uint64_t max_keys{MAX_KEYS_DEFAULT};
131 string marker_str;
132 uint64_t marker{0};
133 string next_marker;
134 bool is_truncated{false};
135 string err;
136
137 es_search_response response;
138
139public:
140 RGWMetadataSearchOp(const RGWSyncModuleInstanceRef& sync_module) : sync_module_ref(sync_module) {
141 es_module = static_cast<RGWElasticSyncModuleInstance *>(sync_module_ref.get());
142 }
143
f67539c2 144 int verify_permission(optional_yield) override {
31f18b77
FG
145 return 0;
146 }
147 virtual int get_params() = 0;
11fdf7f2 148 void pre_exec() override;
f67539c2 149 void execute(optional_yield y) override;
31f18b77 150
11fdf7f2
TL
151 const char* name() const override { return "metadata_search"; }
152 virtual RGWOpType get_type() override { return RGW_OP_METADATA_SEARCH; }
153 virtual uint32_t op_mask() override { return RGW_OP_TYPE_READ; }
31f18b77
FG
154};
155
156void RGWMetadataSearchOp::pre_exec()
157{
158 rgw_bucket_object_pre_exec(s);
159}
160
f67539c2 161void RGWMetadataSearchOp::execute(optional_yield y)
31f18b77
FG
162{
163 op_ret = get_params();
164 if (op_ret < 0)
165 return;
166
167 list<pair<string, string> > conds;
168
9f95a23c
TL
169 if (!s->user->get_info().system) {
170 conds.push_back(make_pair("permissions", s->user->get_id().to_str()));
31f18b77
FG
171 }
172
173 if (!s->bucket_name.empty()) {
174 conds.push_back(make_pair("bucket", s->bucket_name));
175 }
176
177 ESQueryCompiler es_query(expression, &conds, custom_prefix);
178
179 static map<string, string, ltstr_nocase> aliases = {
180 { "bucket", "bucket" }, /* forces lowercase */
181 { "name", "name" },
182 { "key", "name" },
183 { "instance", "instance" },
184 { "etag", "meta.etag" },
185 { "size", "meta.size" },
186 { "mtime", "meta.mtime" },
187 { "lastmodified", "meta.mtime" },
a8e16298
TL
188 { "last_modified", "meta.mtime" },
189 { "contenttype", "meta.content_type" },
190 { "content_type", "meta.content_type" },
191 { "storageclass", "meta.storage_class" },
192 { "storage_class", "meta.storage_class" },
31f18b77
FG
193 };
194 es_query.set_field_aliases(&aliases);
195
196 static map<string, ESEntityTypeMap::EntityType> generic_map = { {"bucket", ESEntityTypeMap::ES_ENTITY_STR},
197 {"name", ESEntityTypeMap::ES_ENTITY_STR},
198 {"instance", ESEntityTypeMap::ES_ENTITY_STR},
199 {"permissions", ESEntityTypeMap::ES_ENTITY_STR},
200 {"meta.etag", ESEntityTypeMap::ES_ENTITY_STR},
a8e16298 201 {"meta.content_type", ESEntityTypeMap::ES_ENTITY_STR},
31f18b77 202 {"meta.mtime", ESEntityTypeMap::ES_ENTITY_DATE},
a8e16298
TL
203 {"meta.size", ESEntityTypeMap::ES_ENTITY_INT},
204 {"meta.storage_class", ESEntityTypeMap::ES_ENTITY_STR} };
31f18b77
FG
205 ESEntityTypeMap gm(generic_map);
206 es_query.set_generic_type_map(&gm);
207
208 static set<string> restricted_fields = { {"permissions"} };
209 es_query.set_restricted_fields(&restricted_fields);
210
211 map<string, ESEntityTypeMap::EntityType> custom_map;
f67539c2 212 for (auto& i : s->bucket->get_info().mdsearch_config) {
31f18b77
FG
213 custom_map[i.first] = (ESEntityTypeMap::EntityType)i.second;
214 }
215
216 ESEntityTypeMap em(custom_map);
217 es_query.set_custom_type_map(&em);
218
219 bool valid = es_query.compile(&err);
220 if (!valid) {
b3b6e05e 221 ldpp_dout(this, 10) << "invalid query, failed generating request json" << dendl;
31f18b77
FG
222 op_ret = -EINVAL;
223 return;
224 }
225
226 JSONFormatter f;
227 encode_json("root", es_query, &f);
228
229 RGWRESTConn *conn = es_module->get_rest_conn();
230
231 bufferlist in;
232 bufferlist out;
233
234 stringstream ss;
235
236 f.flush(ss);
237 in.append(ss.str());
238
239 string resource = es_module->get_index_path() + "/_search";
240 param_vec_t params;
241 static constexpr int BUFSIZE = 32;
242 char buf[BUFSIZE];
243 snprintf(buf, sizeof(buf), "%lld", (long long)max_keys);
244 params.push_back(param_pair_t("size", buf));
245 if (marker > 0) {
246 params.push_back(param_pair_t("from", marker_str.c_str()));
247 }
b3b6e05e 248 ldpp_dout(this, 20) << "sending request to elasticsearch, payload=" << string(in.c_str(), in.length()) << dendl;
a8e16298 249 auto& extra_headers = es_module->get_request_headers();
b3b6e05e 250 op_ret = conn->get_resource(s, resource, &params, &extra_headers,
f67539c2 251 out, &in, nullptr, y);
31f18b77 252 if (op_ret < 0) {
b3b6e05e 253 ldpp_dout(this, 0) << "ERROR: failed to fetch resource (r=" << resource << ", ret=" << op_ret << ")" << dendl;
31f18b77
FG
254 return;
255 }
256
b3b6e05e 257 ldpp_dout(this, 20) << "response: " << string(out.c_str(), out.length()) << dendl;
31f18b77
FG
258
259 JSONParser jparser;
260 if (!jparser.parse(out.c_str(), out.length())) {
b3b6e05e 261 ldpp_dout(this, 0) << "ERROR: failed to parse elasticsearch response" << dendl;
31f18b77
FG
262 op_ret = -EINVAL;
263 return;
264 }
265
266 try {
267 decode_json_obj(response, &jparser);
9f95a23c 268 } catch (const JSONDecoder::err& e) {
b3b6e05e 269 ldpp_dout(this, 0) << "ERROR: failed to decode JSON input: " << e.what() << dendl;
31f18b77
FG
270 op_ret = -EINVAL;
271 return;
272 }
273
274}
275
276class RGWMetadataSearch_ObjStore_S3 : public RGWMetadataSearchOp {
277public:
11fdf7f2 278 explicit RGWMetadataSearch_ObjStore_S3(const RGWSyncModuleInstanceRef& _sync_module) : RGWMetadataSearchOp(_sync_module) {
31f18b77
FG
279 custom_prefix = "x-amz-meta-";
280 }
281
282 int get_params() override {
283 expression = s->info.args.get("query");
284 bool exists;
285 string max_keys_str = s->info.args.get("max-keys", &exists);
286#define MAX_KEYS_MAX 10000
287 if (exists) {
288 string err;
289 max_keys = strict_strtoll(max_keys_str.c_str(), 10, &err);
290 if (!err.empty()) {
291 return -EINVAL;
292 }
293 if (max_keys > MAX_KEYS_MAX) {
294 max_keys = MAX_KEYS_MAX;
295 }
296 }
297 marker_str = s->info.args.get("marker", &exists);
298 if (exists) {
299 string err;
300 marker = strict_strtoll(marker_str.c_str(), 10, &err);
301 if (!err.empty()) {
302 return -EINVAL;
303 }
304 }
305 uint64_t nm = marker + max_keys;
306 static constexpr int BUFSIZE = 32;
307 char buf[BUFSIZE];
308 snprintf(buf, sizeof(buf), "%lld", (long long)nm);
309 next_marker = buf;
310 return 0;
311 }
312 void send_response() override {
313 if (op_ret) {
314 s->err.message = err;
315 set_req_state_err(s, op_ret);
316 }
317 dump_errno(s);
318 end_header(s, this, "application/xml");
319
320 if (op_ret < 0) {
321 return;
322 }
323
324 is_truncated = (response.hits.hits.size() >= max_keys);
325
326 s->formatter->open_object_section("SearchMetadataResponse");
327 s->formatter->dump_string("Marker", marker_str);
328 s->formatter->dump_string("IsTruncated", (is_truncated ? "true" : "false"));
329 if (is_truncated) {
330 s->formatter->dump_string("NextMarker", next_marker);
331 }
332 if (s->format == RGW_FORMAT_JSON) {
333 s->formatter->open_array_section("Objects");
334 }
335 for (auto& i : response.hits.hits) {
336 s->formatter->open_object_section("Contents");
337 es_index_obj_response& e = i.source;
338 s->formatter->dump_string("Bucket", e.bucket);
339 s->formatter->dump_string("Key", e.key.name);
340 string instance = (!e.key.instance.empty() ? e.key.instance : "null");
341 s->formatter->dump_string("Instance", instance.c_str());
342 s->formatter->dump_int("VersionedEpoch", e.versioned_epoch);
343 dump_time(s, "LastModified", &e.meta.mtime);
344 s->formatter->dump_int("Size", e.meta.size);
345 s->formatter->dump_format("ETag", "\"%s\"", e.meta.etag.c_str());
346 s->formatter->dump_string("ContentType", e.meta.content_type.c_str());
a8e16298 347 s->formatter->dump_string("StorageClass", e.meta.storage_class.c_str());
31f18b77
FG
348 dump_owner(s, e.owner.get_id(), e.owner.get_display_name());
349 s->formatter->open_array_section("CustomMetadata");
350 for (auto& m : e.meta.custom_str) {
351 s->formatter->open_object_section("Entry");
352 s->formatter->dump_string("Name", m.first.c_str());
353 s->formatter->dump_string("Value", m.second);
354 s->formatter->close_section();
355 }
356 for (auto& m : e.meta.custom_int) {
357 s->formatter->open_object_section("Entry");
358 s->formatter->dump_string("Name", m.first.c_str());
359 s->formatter->dump_int("Value", m.second);
360 s->formatter->close_section();
361 }
362 for (auto& m : e.meta.custom_date) {
363 s->formatter->open_object_section("Entry");
364 s->formatter->dump_string("Name", m.first.c_str());
365 s->formatter->dump_string("Value", m.second);
366 s->formatter->close_section();
367 }
368 s->formatter->close_section();
369 rgw_flush_formatter(s, s->formatter);
370 s->formatter->close_section();
371 };
372 if (s->format == RGW_FORMAT_JSON) {
373 s->formatter->close_section();
374 }
375 s->formatter->close_section();
376 rgw_flush_formatter_and_reset(s, s->formatter);
377 }
378};
379
380class RGWHandler_REST_MDSearch_S3 : public RGWHandler_REST_S3 {
381protected:
11fdf7f2 382 RGWOp *op_get() override {
31f18b77 383 if (s->info.args.exists("query")) {
9f95a23c 384 return new RGWMetadataSearch_ObjStore_S3(store->getRados()->get_sync_module());
31f18b77
FG
385 }
386 if (!s->init_state.url_bucket.empty() &&
387 s->info.args.exists("mdsearch")) {
388 return new RGWGetBucketMetaSearch_ObjStore_S3;
389 }
390 return nullptr;
391 }
11fdf7f2 392 RGWOp *op_head() override {
31f18b77
FG
393 return nullptr;
394 }
11fdf7f2 395 RGWOp *op_post() override {
31f18b77
FG
396 return nullptr;
397 }
398public:
11fdf7f2 399 explicit RGWHandler_REST_MDSearch_S3(const rgw::auth::StrategyRegistry& auth_registry) : RGWHandler_REST_S3(auth_registry) {}
31f18b77
FG
400 virtual ~RGWHandler_REST_MDSearch_S3() {}
401};
402
403
f67539c2
TL
404RGWHandler_REST* RGWRESTMgr_MDSearch_S3::get_handler(rgw::sal::RGWRadosStore *store,
405 struct req_state* const s,
31f18b77
FG
406 const rgw::auth::StrategyRegistry& auth_registry,
407 const std::string& frontend_prefix)
408{
409 int ret =
f67539c2 410 RGWHandler_REST_S3::init_from_header(store, s,
31f18b77
FG
411 RGW_FORMAT_XML, true);
412 if (ret < 0) {
413 return nullptr;
414 }
415
f67539c2 416 if (!s->object->empty()) {
31f18b77
FG
417 return nullptr;
418 }
419
420 RGWHandler_REST *handler = new RGWHandler_REST_MDSearch_S3(auth_registry);
421
b3b6e05e 422 ldpp_dout(s, 20) << __func__ << " handler=" << typeid(*handler).name()
31f18b77
FG
423 << dendl;
424 return handler;
425}
426