]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/librgw_file_marker.cc
2475423fd468322b68a0b95eee0dcc340f255c59
[ceph.git] / ceph / src / test / librgw_file_marker.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2015 Red Hat, Inc.
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15 #include <stdint.h>
16 #include <tuple>
17 #include <iostream>
18 #include <fstream>
19 #include <stack>
20
21 #include "include/rados/librgw.h"
22 #include "include/rados/rgw_file.h"
23 #include "rgw/rgw_file.h"
24 #include "rgw/rgw_lib_frontend.h" // direct requests
25
26 #include "gtest/gtest.h"
27 #include "common/ceph_argparse.h"
28 #include "common/debug.h"
29 #include "global/global_init.h"
30 #include "include/ceph_assert.h"
31
32 #define dout_subsys ceph_subsys_rgw
33
34 namespace {
35
36 using namespace rgw;
37 using std::get;
38 using std::string;
39
40 librgw_t rgw_h = nullptr;
41 string userid("testuser");
42 string access_key("");
43 string secret_key("");
44 struct rgw_fs *fs = nullptr;
45 CephContext* cct = nullptr;
46
47 uint32_t owner_uid = 867;
48 uint32_t owner_gid = 5309;
49
50 uint32_t create_mask = RGW_SETATTR_UID | RGW_SETATTR_GID | RGW_SETATTR_MODE;
51
52 string bucket_name("dmarker");
53
54 class obj_rec
55 {
56 public:
57 string name;
58 struct rgw_file_handle* fh;
59 struct rgw_file_handle* parent_fh;
60 RGWFileHandle* rgw_fh; // alias into fh
61
62 struct state {
63 bool readdir;
64 state() : readdir(false) {}
65 } state;
66
67 obj_rec(string _name, struct rgw_file_handle* _fh,
68 struct rgw_file_handle* _parent_fh, RGWFileHandle* _rgw_fh)
69 : name(std::move(_name)), fh(_fh), parent_fh(_parent_fh),
70 rgw_fh(_rgw_fh) {}
71
72 void clear() {
73 fh = nullptr;
74 rgw_fh = nullptr;
75 }
76
77 void sync() {
78 if (fh)
79 rgw_fh = get_rgwfh(fh);
80 }
81
82 friend ostream& operator<<(ostream& os, const obj_rec& rec);
83 };
84
85 /* Unused
86 ostream& operator<<(ostream& os, const obj_rec& rec)
87 {
88 RGWFileHandle* rgw_fh = rec.rgw_fh;
89 if (rgw_fh) {
90 const char* type = rgw_fh->is_dir() ? "DIR " : "FILE ";
91 os << rec.rgw_fh->full_object_name()
92 << " (" << rec.rgw_fh->object_name() << "): "
93 << type;
94 }
95 return os;
96 }
97 */
98
99 std::stack<obj_rec> obj_stack;
100 std::deque<obj_rec> cleanup_queue;
101
102 typedef std::vector<obj_rec> obj_vec;
103 typedef std::tuple<obj_rec, obj_vec> dirs1_rec;
104 typedef std::vector<dirs1_rec> dirs1_vec;
105
106 dirs1_vec dirs_vec;
107
108 struct obj_rec_st
109 {
110 const obj_rec& obj;
111 const struct stat& st;
112
113 obj_rec_st(const obj_rec& _obj, const struct stat& _st)
114 : obj(_obj), st(_st) {}
115 };
116
117 /* Unused
118 ostream& operator<<(ostream& os, const obj_rec_st& rec)
119 {
120 RGWFileHandle* rgw_fh = rec.obj.rgw_fh;
121 if (rgw_fh) {
122 const char* type = rgw_fh->is_dir() ? "DIR " : "FILE ";
123 os << rgw_fh->full_object_name()
124 << " (" << rgw_fh->object_name() << "): "
125 << type;
126 const struct stat& st = rec.st;
127 switch(uint8_t(rgw_fh->is_dir())) {
128 case 1:
129 os << " mode: " << st.st_mode;
130 os << " nlinks: " << st.st_nlink;
131 break;
132 case 0:
133 default:
134 os << " mode: " << st.st_mode;
135 os << " size: " << st.st_size;
136 // xxx
137 break;
138 }
139 }
140 return os;
141 }
142 */
143
144 bool do_marker1 = false;
145 bool do_marker2 = true;
146 bool do_create = false;
147 bool do_delete = false;
148 bool verbose = false;
149
150 string marker_dir("nfs_marker");
151 struct rgw_file_handle *bucket_fh = nullptr;
152 struct rgw_file_handle *marker_fh;
153 uint32_t marker_nobjs = 2*1024;
154 std::deque<obj_rec> marker_objs;
155
156 using dirent_t = std::tuple<std::string, uint64_t>;
157 struct dirent_vec
158 {
159 std::vector<dirent_t> obj_names;
160 uint32_t count;
161 dirent_vec() : count(0) {}
162 };
163
164 struct {
165 int argc;
166 char **argv;
167 } saved_args;
168 }
169
170 TEST(LibRGW, TVAR) {
171 typedef boost::variant<uint64_t*, const char*> readdir_offset;
172
173 uint64_t i1{64001};
174 std::string s1{"blunderbuss"};
175
176 readdir_offset v1{&i1};
177 readdir_offset v2{s1.c_str()};
178 readdir_offset v3{static_cast<const char*>(nullptr)};
179
180 uint64_t* pi1 = get<uint64_t*>(v1);
181 ASSERT_NE(pi1, nullptr);
182 std::cout << "read i1: " << *pi1 << std::endl;
183
184 const char* ps1 = get<const char*>(v2);
185 ASSERT_NE(ps1, nullptr);
186 std::cout << "read s1: " << ps1 << std::endl;
187
188 const char* ps3 = get<const char*>(v3);
189 ASSERT_EQ(ps3, nullptr);
190 std::cout << "read s3: " << ps3 << std::endl;
191 }
192
193 TEST(LibRGW, INIT) {
194 int ret = librgw_create(&rgw_h, saved_args.argc, saved_args.argv);
195 ASSERT_EQ(ret, 0);
196 ASSERT_NE(rgw_h, nullptr);
197 }
198
199 TEST(LibRGW, MOUNT) {
200 int ret = rgw_mount2(rgw_h, userid.c_str(), access_key.c_str(),
201 secret_key.c_str(), "/", &fs, RGW_MOUNT_FLAG_NONE);
202 ASSERT_EQ(ret, 0);
203 ASSERT_NE(fs, nullptr);
204
205 cct = static_cast<RGWLibFS*>(fs->fs_private)->get_context();
206 }
207
208 TEST(LibRGW, MARKER1_SETUP_BUCKET) {
209 /* "large" directory enumeration test. this one deals only with
210 * file objects */
211 struct stat st;
212 int ret;
213
214 st.st_uid = owner_uid;
215 st.st_gid = owner_gid;
216 st.st_mode = 755;
217
218 (void) rgw_lookup(fs, fs->root_fh, bucket_name.c_str(), &bucket_fh,
219 nullptr, 0, RGW_LOOKUP_FLAG_NONE);
220 if (! bucket_fh) {
221 if (do_create) {
222 struct stat st;
223
224 st.st_uid = owner_uid;
225 st.st_gid = owner_gid;
226 st.st_mode = 755;
227
228 ret = rgw_mkdir(fs, fs->root_fh, bucket_name.c_str(), &st, create_mask,
229 &bucket_fh, RGW_MKDIR_FLAG_NONE);
230 ASSERT_EQ(ret, 0);
231 }
232 }
233
234 ASSERT_NE(bucket_fh, nullptr);
235
236 (void) rgw_lookup(fs, bucket_fh, marker_dir.c_str(), &marker_fh,
237 nullptr, 0, RGW_LOOKUP_FLAG_NONE);
238 if (! marker_fh) {
239 if (do_create) {
240 ret = rgw_mkdir(fs, bucket_fh, marker_dir.c_str(), &st, create_mask,
241 &marker_fh, RGW_MKDIR_FLAG_NONE);
242 ASSERT_EQ(ret, 0);
243 }
244 }
245
246 ASSERT_NE(marker_fh, nullptr);
247 }
248
249 TEST(LibRGW, MARKER1_SETUP_OBJECTS)
250 {
251 /* "large" directory enumeration test. this one deals only with
252 * file objects */
253
254 if (do_create) {
255 int ret;
256
257 for (uint32_t ix = 0; ix < marker_nobjs; ++ix) {
258 std::string object_name("f_");
259 object_name += to_string(ix);
260 obj_rec obj{object_name, nullptr, marker_fh, nullptr};
261 // lookup object--all operations are by handle
262 ret = rgw_lookup(fs, marker_fh, obj.name.c_str(), &obj.fh,
263 nullptr, 0, RGW_LOOKUP_FLAG_CREATE);
264 ASSERT_EQ(ret, 0);
265 obj.rgw_fh = get_rgwfh(obj.fh);
266 // open object--open transaction
267 ret = rgw_open(fs, obj.fh, 0 /* posix flags */, RGW_OPEN_FLAG_NONE);
268 ASSERT_EQ(ret, 0);
269 ASSERT_TRUE(obj.rgw_fh->is_open());
270 // unstable write data
271 size_t nbytes;
272 string data("data for ");
273 data += object_name;
274 int ret = rgw_write(fs, obj.fh, 0, data.length(), &nbytes,
275 (void*) data.c_str(), RGW_WRITE_FLAG_NONE);
276 ASSERT_EQ(ret, 0);
277 ASSERT_EQ(nbytes, data.length());
278 // commit transaction (write on close)
279 ret = rgw_close(fs, obj.fh, 0 /* flags */);
280 ASSERT_EQ(ret, 0);
281 if (verbose) {
282 /* XXX std:cout fragged...did it get /0 in the stream
283 * somewhere? */
284 printf("created: %s:%s\n", bucket_name.c_str(), obj.name.c_str());
285 }
286 // save for cleanup
287 marker_objs.push_back(obj);
288 }
289 }
290 }
291
292 extern "C" {
293 static bool r2_cb(const char* name, void *arg, uint64_t offset,
294 struct stat* st, uint32_t st_mask,
295 uint32_t flags) {
296 dirent_vec& dvec =
297 *(static_cast<dirent_vec*>(arg));
298
299 printf("%s bucket=%s dir=%s iv count=%d called back name=%s flags=%d\n",
300 __func__,
301 bucket_name.c_str(),
302 marker_dir.c_str(),
303 dvec.count,
304 name,
305 flags);
306
307 string name_str{name};
308 if (! ((name_str == ".") ||
309 (name_str == ".."))) {
310 dvec.obj_names.push_back(dirent_t{std::move(name_str), offset});
311 }
312 return true; /* XXX */
313 }
314 }
315
316 TEST(LibRGW, MARKER1_READDIR)
317 {
318 if (do_marker1) {
319 using std::get;
320
321 dirent_vec dvec;
322 uint64_t offset = 0;
323 bool eof = false;
324
325 /* because RGWReaddirRequest::default_max is 1000 (XXX make
326 * configurable?) and marker_nobjs is 5*1024, the number
327 * of required rgw_readdir operations N should be
328 * marker_nobjs/1000 < N < marker_nobjs/1000+1, i.e., 6 when
329 * marker_nobjs==5*1024 */
330 uint32_t max_iterations = marker_nobjs/1000+1;
331
332 do {
333 ASSERT_TRUE(dvec.count <= max_iterations);
334 int ret = rgw_readdir(fs, marker_fh, &offset, r2_cb, &dvec, &eof,
335 RGW_READDIR_FLAG_DOTDOT);
336 ASSERT_EQ(ret, 0);
337 ASSERT_GE(dvec.obj_names.size(), 0);
338 ASSERT_EQ(offset, get<1>(dvec.obj_names.back())); // cookie check
339 ++dvec.count;
340 } while(!eof);
341 std::cout << "Read " << dvec.obj_names.size() << " objects in "
342 << marker_dir.c_str() << std::endl;
343 }
344 }
345
346 TEST(LibRGW, MARKER2_READDIR)
347 {
348 if (do_marker2) {
349 using std::get;
350
351 dirent_vec dvec;
352 std::string marker{""};
353 bool eof = false;
354
355 /* because RGWReaddirRequest::default_max is 1000 (XXX make
356 * configurable?) and marker_nobjs is 5*1024, the number
357 * of required rgw_readdir operations N should be
358 * marker_nobjs/1000 < N < marker_nobjs/1000+1, i.e., 6 when
359 * marker_nobjs==5*1024 */
360 uint32_t max_iterations = marker_nobjs/1000+1;
361
362 do {
363 ASSERT_TRUE(dvec.count <= max_iterations);
364 int ret = rgw_readdir2(fs, marker_fh,
365 (marker.length() > 0) ? marker.c_str() : nullptr,
366 r2_cb, &dvec, &eof,
367 RGW_READDIR_FLAG_NONE);
368 ASSERT_EQ(ret, 0);
369 ASSERT_GE(dvec.obj_names.size(), 0);
370 marker = get<0>(dvec.obj_names.back());
371 ++dvec.count;
372 } while((!eof) && dvec.count < 4);
373 std::cout << "Read " << dvec.obj_names.size() << " objects in "
374 << marker_dir.c_str() << std::endl;
375 }
376 }
377
378 TEST(LibRGW, MARKER1_OBJ_CLEANUP)
379 {
380 int rc;
381 for (auto& obj : marker_objs) {
382 if (obj.fh) {
383 if (do_delete) {
384 if (verbose) {
385 std::cout << "unlinking: " << bucket_name << ":" << obj.name
386 << std::endl;
387 }
388 rc = rgw_unlink(fs, marker_fh, obj.name.c_str(), RGW_UNLINK_FLAG_NONE);
389 }
390 rc = rgw_fh_rele(fs, obj.fh, 0 /* flags */);
391 ASSERT_EQ(rc, 0);
392 }
393 }
394 marker_objs.clear();
395 }
396
397 TEST(LibRGW, CLEANUP) {
398 int rc;
399
400 if (do_marker1) {
401 cleanup_queue.push_back(
402 obj_rec{bucket_name, bucket_fh, fs->root_fh, get_rgwfh(fs->root_fh)});
403 }
404
405 for (auto& elt : cleanup_queue) {
406 if (elt.fh) {
407 rc = rgw_fh_rele(fs, elt.fh, 0 /* flags */);
408 ASSERT_EQ(rc, 0);
409 }
410 }
411 cleanup_queue.clear();
412 }
413
414 TEST(LibRGW, UMOUNT) {
415 if (! fs)
416 return;
417
418 int ret = rgw_umount(fs, RGW_UMOUNT_FLAG_NONE);
419 ASSERT_EQ(ret, 0);
420 }
421
422 TEST(LibRGW, SHUTDOWN) {
423 librgw_shutdown(rgw_h);
424 }
425
426 int main(int argc, char *argv[])
427 {
428 char *v{nullptr};
429 string val;
430 vector<const char*> args;
431
432 argv_to_vec(argc, const_cast<const char**>(argv), args);
433 env_to_vec(args);
434
435 v = getenv("AWS_ACCESS_KEY_ID");
436 if (v) {
437 access_key = v;
438 }
439
440 v = getenv("AWS_SECRET_ACCESS_KEY");
441 if (v) {
442 secret_key = v;
443 }
444
445 for (auto arg_iter = args.begin(); arg_iter != args.end();) {
446 if (ceph_argparse_witharg(args, arg_iter, &val, "--access",
447 (char*) nullptr)) {
448 access_key = val;
449 } else if (ceph_argparse_witharg(args, arg_iter, &val, "--secret",
450 (char*) nullptr)) {
451 secret_key = val;
452 } else if (ceph_argparse_witharg(args, arg_iter, &val, "--userid",
453 (char*) nullptr)) {
454 userid = val;
455 } else if (ceph_argparse_witharg(args, arg_iter, &val, "--bn",
456 (char*) nullptr)) {
457 bucket_name = val;
458 } else if (ceph_argparse_witharg(args, arg_iter, &val, "--uid",
459 (char*) nullptr)) {
460 owner_uid = std::stoi(val);
461 } else if (ceph_argparse_witharg(args, arg_iter, &val, "--gid",
462 (char*) nullptr)) {
463 owner_gid = std::stoi(val);
464 } else if (ceph_argparse_witharg(args, arg_iter, &val, "--nobjs",
465 (char*) nullptr)) {
466 marker_nobjs = std::stoi(val);
467 } else if (ceph_argparse_flag(args, arg_iter, "--marker1",
468 (char*) nullptr)) {
469 do_marker1 = true;
470 } else if (ceph_argparse_flag(args, arg_iter, "--create",
471 (char*) nullptr)) {
472 do_create = true;
473 } else if (ceph_argparse_flag(args, arg_iter, "--delete",
474 (char*) nullptr)) {
475 do_delete = true;
476 } else if (ceph_argparse_flag(args, arg_iter, "--verbose",
477 (char*) nullptr)) {
478 verbose = true;
479 } else {
480 ++arg_iter;
481 }
482 }
483
484 /* don't accidentally run as anonymous */
485 if ((access_key == "") ||
486 (secret_key == "")) {
487 std::cout << argv[0] << " no AWS credentials, exiting" << std::endl;
488 return EPERM;
489 }
490
491 saved_args.argc = argc;
492 saved_args.argv = argv;
493
494 ::testing::InitGoogleTest(&argc, argv);
495 return RUN_ALL_TESTS();
496 }