]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/librados/list.cc
update sources to 12.2.7
[ceph.git] / ceph / src / test / librados / list.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 #include "include/rados/librados.h"
4 #include "include/rados/librados.hpp"
5 #include "include/stringify.h"
6 #include "test/librados/test.h"
7 #include "test/librados/test_common.h"
8 #include "test/librados/TestCase.h"
9 #include "global/global_context.h"
10
11 #include "include/types.h"
12 #include "common/hobject.h"
13 #include "gtest/gtest.h"
14 #include <errno.h>
15 #include <string>
16 #include <stdexcept>
17
18 using namespace librados;
19
20 typedef RadosTestNSCleanup LibRadosList;
21 typedef RadosTestPPNSCleanup LibRadosListPP;
22 typedef RadosTestECNSCleanup LibRadosListEC;
23 typedef RadosTestECPPNSCleanup LibRadosListECPP;
24 typedef RadosTestNP LibRadosListNP;
25
26
27 TEST_F(LibRadosList, ListObjects) {
28 char buf[128];
29 memset(buf, 0xcc, sizeof(buf));
30 ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
31 rados_list_ctx_t ctx;
32 ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
33 const char *entry;
34 bool foundit = false;
35 while (rados_nobjects_list_next(ctx, &entry, NULL, NULL) != -ENOENT) {
36 foundit = true;
37 ASSERT_EQ(std::string(entry), "foo");
38 }
39 ASSERT_TRUE(foundit);
40 rados_nobjects_list_close(ctx);
41 }
42
43 TEST_F(LibRadosListPP, ListObjectsPP) {
44 char buf[128];
45 memset(buf, 0xcc, sizeof(buf));
46 bufferlist bl1;
47 bl1.append(buf, sizeof(buf));
48 ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
49 NObjectIterator iter(ioctx.nobjects_begin());
50 bool foundit = false;
51 while (iter != ioctx.nobjects_end()) {
52 foundit = true;
53 ASSERT_EQ((*iter).get_oid(), "foo");
54 ++iter;
55 }
56 ASSERT_TRUE(foundit);
57 }
58
59 TEST_F(LibRadosListPP, ListObjectsTwicePP) {
60 char buf[128];
61 memset(buf, 0xcc, sizeof(buf));
62 bufferlist bl1;
63 bl1.append(buf, sizeof(buf));
64 ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
65 NObjectIterator iter(ioctx.nobjects_begin());
66 bool foundit = false;
67 while (iter != ioctx.nobjects_end()) {
68 foundit = true;
69 ASSERT_EQ((*iter).get_oid(), "foo");
70 ++iter;
71 }
72 ASSERT_TRUE(foundit);
73 ++iter;
74 ASSERT_TRUE(iter == ioctx.nobjects_end());
75 foundit = false;
76 iter.seek(0);
77 while (iter != ioctx.nobjects_end()) {
78 foundit = true;
79 ASSERT_EQ((*iter).get_oid(), "foo");
80 ++iter;
81 }
82 ASSERT_TRUE(foundit);
83 }
84
85 TEST_F(LibRadosListPP, ListObjectsCopyIterPP) {
86 char buf[128];
87 memset(buf, 0xcc, sizeof(buf));
88 bufferlist bl1;
89 bl1.append(buf, sizeof(buf));
90 ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
91
92 // make sure this is still valid after the original iterators are gone
93 NObjectIterator iter3;
94 {
95 NObjectIterator iter(ioctx.nobjects_begin());
96 NObjectIterator iter2(iter);
97 iter3 = iter2;
98 ASSERT_EQ((*iter).get_oid(), "foo");
99 ++iter;
100 ASSERT_TRUE(iter == ioctx.nobjects_end());
101 ++iter;
102 ASSERT_TRUE(iter == ioctx.nobjects_end());
103
104 ASSERT_EQ(iter2->get_oid(), "foo");
105 ASSERT_EQ(iter3->get_oid(), "foo");
106 ++iter2;
107 ASSERT_TRUE(iter2 == ioctx.nobjects_end());
108 }
109
110 ASSERT_EQ(iter3->get_oid(), "foo");
111 iter3 = iter3;
112 ASSERT_EQ(iter3->get_oid(), "foo");
113 ++iter3;
114 ASSERT_TRUE(iter3 == ioctx.nobjects_end());
115 }
116
117 TEST_F(LibRadosListPP, ListObjectsEndIter) {
118 char buf[128];
119 memset(buf, 0xcc, sizeof(buf));
120 bufferlist bl1;
121 bl1.append(buf, sizeof(buf));
122 ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
123
124 NObjectIterator iter(ioctx.nobjects_begin());
125 NObjectIterator iter_end(ioctx.nobjects_end());
126 NObjectIterator iter_end2 = ioctx.nobjects_end();
127 ASSERT_TRUE(iter_end == iter_end2);
128 ASSERT_TRUE(iter_end == ioctx.nobjects_end());
129 ASSERT_TRUE(iter_end2 == ioctx.nobjects_end());
130
131 ASSERT_EQ(iter->get_oid(), "foo");
132 ++iter;
133 ASSERT_TRUE(iter == ioctx.nobjects_end());
134 ASSERT_TRUE(iter == iter_end);
135 ASSERT_TRUE(iter == iter_end2);
136 NObjectIterator iter2 = iter;
137 ASSERT_TRUE(iter2 == ioctx.nobjects_end());
138 ASSERT_TRUE(iter2 == iter_end);
139 ASSERT_TRUE(iter2 == iter_end2);
140 }
141
142 static void check_list(
143 std::set<std::string>& myset,
144 rados_list_ctx_t& ctx,
145 std::string check_nspace)
146 {
147 const char *entry, *nspace;
148 cout << "myset " << myset << std::endl;
149 // we should see every item exactly once.
150 int ret;
151 while ((ret = rados_nobjects_list_next(ctx, &entry, NULL, &nspace)) == 0) {
152 std::string test_name;
153 if (check_nspace == all_nspaces) {
154 test_name = std::string(nspace) + ":" + std::string(entry);
155 } else {
156 ASSERT_TRUE(std::string(nspace) == check_nspace);
157 test_name = std::string(entry);
158 }
159 cout << test_name << std::endl;
160
161 ASSERT_TRUE(myset.end() != myset.find(test_name));
162 myset.erase(test_name);
163 }
164 ASSERT_EQ(-ENOENT, ret);
165 ASSERT_TRUE(myset.empty());
166 }
167
168 TEST_F(LibRadosList, ListObjectsNS) {
169 char buf[128];
170 memset(buf, 0xcc, sizeof(buf));
171 // Create :foo1, :foo2, :foo3, n1:foo1, ns1:foo4, ns1:foo5, ns2:foo6, n2:foo7
172 rados_ioctx_set_namespace(ioctx, "");
173 ASSERT_EQ(0, rados_write(ioctx, "foo1", buf, sizeof(buf), 0));
174 rados_ioctx_set_namespace(ioctx, "ns1");
175 ASSERT_EQ(0, rados_write(ioctx, "foo1", buf, sizeof(buf), 0));
176 rados_ioctx_set_namespace(ioctx, "");
177 ASSERT_EQ(0, rados_write(ioctx, "foo2", buf, sizeof(buf), 0));
178 ASSERT_EQ(0, rados_write(ioctx, "foo3", buf, sizeof(buf), 0));
179 rados_ioctx_set_namespace(ioctx, "ns1");
180 ASSERT_EQ(0, rados_write(ioctx, "foo4", buf, sizeof(buf), 0));
181 ASSERT_EQ(0, rados_write(ioctx, "foo5", buf, sizeof(buf), 0));
182 rados_ioctx_set_namespace(ioctx, "ns2");
183 ASSERT_EQ(0, rados_write(ioctx, "foo6", buf, sizeof(buf), 0));
184 ASSERT_EQ(0, rados_write(ioctx, "foo7", buf, sizeof(buf), 0));
185
186 std::set<std::string> def, ns1, ns2, all;
187 def.insert(std::string("foo1"));
188 def.insert(std::string("foo2"));
189 def.insert(std::string("foo3"));
190 ns1.insert(std::string("foo1"));
191 ns1.insert(std::string("foo4"));
192 ns1.insert(std::string("foo5"));
193 ns2.insert(std::string("foo6"));
194 ns2.insert(std::string("foo7"));
195 all.insert(std::string(":foo1"));
196 all.insert(std::string(":foo2"));
197 all.insert(std::string(":foo3"));
198 all.insert(std::string("ns1:foo1"));
199 all.insert(std::string("ns1:foo4"));
200 all.insert(std::string("ns1:foo5"));
201 all.insert(std::string("ns2:foo6"));
202 all.insert(std::string("ns2:foo7"));
203
204 rados_list_ctx_t ctx;
205 // Check default namespace ""
206 rados_ioctx_set_namespace(ioctx, "");
207 ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
208 check_list(def, ctx, "");
209 rados_nobjects_list_close(ctx);
210
211 // Check namespace "ns1"
212 rados_ioctx_set_namespace(ioctx, "ns1");
213 ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
214 check_list(ns1, ctx, "ns1");
215 rados_nobjects_list_close(ctx);
216
217 // Check namespace "ns2"
218 rados_ioctx_set_namespace(ioctx, "ns2");
219 ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
220 check_list(ns2, ctx, "ns2");
221 rados_nobjects_list_close(ctx);
222
223 // Check ALL namespaces
224 rados_ioctx_set_namespace(ioctx, LIBRADOS_ALL_NSPACES);
225 ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
226 check_list(all, ctx, all_nspaces);
227 rados_nobjects_list_close(ctx);
228 }
229
230 static void check_listpp(std::set<std::string>& myset, IoCtx& ioctx, std::string check_nspace)
231 {
232 NObjectIterator iter(ioctx.nobjects_begin());
233 std::set<std::string> orig_set(myset);
234 /**
235 * During splitting, we might see duplicate items.
236 * We assert that every object returned is in myset and that
237 * we don't hit ENOENT until we have hit every item in myset
238 * at least once.
239 */
240 while (iter != ioctx.nobjects_end()) {
241 std::string test_name;
242 if (check_nspace == all_nspaces) {
243 test_name = iter->get_nspace() + ":" + iter->get_oid();
244 } else {
245 ASSERT_TRUE(iter->get_nspace() == check_nspace);
246 test_name = iter->get_oid();
247 }
248 ASSERT_TRUE(orig_set.end() != orig_set.find(test_name));
249 myset.erase(test_name);
250 ++iter;
251 }
252 ASSERT_TRUE(myset.empty());
253 }
254
255 TEST_F(LibRadosListPP, ListObjectsPPNS) {
256 char buf[128];
257 memset(buf, 0xcc, sizeof(buf));
258 bufferlist bl1;
259 bl1.append(buf, sizeof(buf));
260 // Create :foo1, :foo2, :foo3, n1:foo1, ns1:foo4, ns1:foo5, ns2:foo6, n2:foo7
261 ioctx.set_namespace("");
262 ASSERT_EQ(0, ioctx.write("foo1", bl1, sizeof(buf), 0));
263 ioctx.set_namespace("ns1");
264 ASSERT_EQ(0, ioctx.write("foo1", bl1, sizeof(buf), 0));
265 ioctx.set_namespace("");
266 ASSERT_EQ(0, ioctx.write("foo2", bl1, sizeof(buf), 0));
267 ASSERT_EQ(0, ioctx.write("foo3", bl1, sizeof(buf), 0));
268 ioctx.set_namespace("ns1");
269 ASSERT_EQ(0, ioctx.write("foo4", bl1, sizeof(buf), 0));
270 ASSERT_EQ(0, ioctx.write("foo5", bl1, sizeof(buf), 0));
271 ioctx.set_namespace("ns2");
272 ASSERT_EQ(0, ioctx.write("foo6", bl1, sizeof(buf), 0));
273 ASSERT_EQ(0, ioctx.write("foo7", bl1, sizeof(buf), 0));
274
275 std::set<std::string> def, ns1, ns2, all;
276 def.insert(std::string("foo1"));
277 def.insert(std::string("foo2"));
278 def.insert(std::string("foo3"));
279 ns1.insert(std::string("foo1"));
280 ns1.insert(std::string("foo4"));
281 ns1.insert(std::string("foo5"));
282 ns2.insert(std::string("foo6"));
283 ns2.insert(std::string("foo7"));
284 all.insert(std::string(":foo1"));
285 all.insert(std::string(":foo2"));
286 all.insert(std::string(":foo3"));
287 all.insert(std::string("ns1:foo1"));
288 all.insert(std::string("ns1:foo4"));
289 all.insert(std::string("ns1:foo5"));
290 all.insert(std::string("ns2:foo6"));
291 all.insert(std::string("ns2:foo7"));
292
293 ioctx.set_namespace("");
294 check_listpp(def, ioctx, "");
295
296 ioctx.set_namespace("ns1");
297 check_listpp(ns1, ioctx, "ns1");
298
299 ioctx.set_namespace("ns2");
300 check_listpp(ns2, ioctx, "ns2");
301
302 ioctx.set_namespace(all_nspaces);
303 check_listpp(all, ioctx, all_nspaces);
304 }
305
306 TEST_F(LibRadosListPP, ListObjectsManyPP) {
307 char buf[128];
308 memset(buf, 0xcc, sizeof(buf));
309 bufferlist bl;
310 bl.append(buf, sizeof(buf));
311
312 for (int i=0; i<256; ++i) {
313 ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
314 }
315
316 librados::NObjectIterator it = ioctx.nobjects_begin();
317 std::set<std::string> saw_obj;
318 std::set<int> saw_pg;
319 for (; it != ioctx.nobjects_end(); ++it) {
320 std::cout << it->get_oid()
321 << " " << it.get_pg_hash_position() << std::endl;
322 saw_obj.insert(it->get_oid());
323 saw_pg.insert(it.get_pg_hash_position());
324 }
325 std::cout << "saw " << saw_pg.size() << " pgs " << std::endl;
326
327 // make sure they are 0..n
328 for (unsigned i = 0; i < saw_pg.size(); ++i)
329 ASSERT_TRUE(saw_pg.count(i));
330 }
331
332 TEST_F(LibRadosList, ListObjectsStart) {
333 char buf[128];
334 memset(buf, 0xcc, sizeof(buf));
335
336 for (int i=0; i<16; ++i) {
337 string n = stringify(i);
338 ASSERT_EQ(0, rados_write(ioctx, n.c_str(), buf, sizeof(buf), 0));
339 }
340
341 rados_list_ctx_t ctx;
342 ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
343 std::map<int, std::set<std::string> > pg_to_obj;
344 const char *entry;
345 while (rados_nobjects_list_next(ctx, &entry, NULL, NULL) == 0) {
346 uint32_t pos = rados_nobjects_list_get_pg_hash_position(ctx);
347 std::cout << entry << " " << pos << std::endl;
348 pg_to_obj[pos].insert(entry);
349 }
350 rados_nobjects_list_close(ctx);
351
352 std::map<int, std::set<std::string> >::reverse_iterator p =
353 pg_to_obj.rbegin();
354 ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
355 while (p != pg_to_obj.rend()) {
356 ASSERT_EQ((uint32_t)p->first, rados_nobjects_list_seek(ctx, p->first));
357 ASSERT_EQ(0, rados_nobjects_list_next(ctx, &entry, NULL, NULL));
358 std::cout << "have " << entry << " expect one of " << p->second << std::endl;
359 ASSERT_TRUE(p->second.count(entry));
360 ++p;
361 }
362 rados_nobjects_list_close(ctx);
363 }
364
365 TEST_F(LibRadosListPP, ListObjectsStartPP) {
366 char buf[128];
367 memset(buf, 0xcc, sizeof(buf));
368 bufferlist bl;
369 bl.append(buf, sizeof(buf));
370
371 for (int i=0; i<16; ++i) {
372 ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
373 }
374
375 librados::NObjectIterator it = ioctx.nobjects_begin();
376 std::map<int, std::set<std::string> > pg_to_obj;
377 for (; it != ioctx.nobjects_end(); ++it) {
378 std::cout << it->get_oid() << " " << it.get_pg_hash_position() << std::endl;
379 pg_to_obj[it.get_pg_hash_position()].insert(it->get_oid());
380 }
381
382 std::map<int, std::set<std::string> >::reverse_iterator p =
383 pg_to_obj.rbegin();
384 it = ioctx.nobjects_begin(p->first);
385 while (p != pg_to_obj.rend()) {
386 ASSERT_EQ((uint32_t)p->first, it.seek(p->first));
387 std::cout << "have " << it->get_oid() << " expect one of " << p->second << std::endl;
388 ASSERT_TRUE(p->second.count(it->get_oid()));
389 ++p;
390 }
391 }
392
393 TEST_F(LibRadosListPP, ListObjectsCursorNSPP) {
394 char buf[128];
395 memset(buf, 0xcc, sizeof(buf));
396 bufferlist bl;
397 bl.append(buf, sizeof(buf));
398
399 const int max_objs = 16;
400
401 map<string, string> oid_to_ns;
402
403 for (int i=0; i<max_objs; ++i) {
404 stringstream ss;
405 ss << "ns" << i / 4;
406 ioctx.set_namespace(ss.str());
407 string oid = stringify(i);
408 ASSERT_EQ(0, ioctx.write(oid, bl, bl.length(), 0));
409
410 oid_to_ns[oid] = ss.str();
411 }
412
413 ioctx.set_namespace(all_nspaces);
414
415 librados::NObjectIterator it = ioctx.nobjects_begin();
416 std::map<librados::ObjectCursor, string> cursor_to_obj;
417
418 int count = 0;
419
420 librados::ObjectCursor seek_cursor;
421
422 map<string, list<librados::ObjectCursor> > ns_to_cursors;
423
424 for (it = ioctx.nobjects_begin(); it != ioctx.nobjects_end(); ++it) {
425 librados::ObjectCursor cursor = it.get_cursor();
426 string oid = it->get_oid();
427 cout << "> oid=" << oid << " cursor=" << it.get_cursor() << std::endl;
428 }
429
430 vector<string> objs_order;
431
432 for (it = ioctx.nobjects_begin(); it != ioctx.nobjects_end(); ++it, ++count) {
433 librados::ObjectCursor cursor = it.get_cursor();
434 string oid = it->get_oid();
435 std::cout << oid << " " << it.get_pg_hash_position() << std::endl;
436 cout << ": oid=" << oid << " cursor=" << it.get_cursor() << std::endl;
437 cursor_to_obj[cursor] = oid;
438
439 ASSERT_EQ(oid_to_ns[oid], it->get_nspace());
440
441 it.seek(cursor);
442 cout << ": seek to " << cursor << " it.cursor=" << it.get_cursor() << std::endl;
443 ASSERT_EQ(oid, it->get_oid());
444 ASSERT_LT(count, max_objs); /* avoid infinite loops due to bad seek */
445
446 ns_to_cursors[it->get_nspace()].push_back(cursor);
447
448 if (count == max_objs/2) {
449 seek_cursor = cursor;
450 }
451 objs_order.push_back(it->get_oid());
452 }
453
454 ASSERT_EQ(count, max_objs);
455
456 /* check that reading past seek also works */
457 cout << "seek_cursor=" << seek_cursor << std::endl;
458 it.seek(seek_cursor);
459 for (count = max_objs/2; count < max_objs; ++count, ++it) {
460 ASSERT_EQ(objs_order[count], it->get_oid());
461 }
462
463 /* seek to all cursors, check that we get expected obj */
464 for (auto& niter : ns_to_cursors) {
465 const string& ns = niter.first;
466 list<librados::ObjectCursor>& cursors = niter.second;
467
468 for (auto& cursor : cursors) {
469 cout << ": seek to " << cursor << std::endl;
470 it.seek(cursor);
471 ASSERT_EQ(cursor, it.get_cursor());
472 string& expected_oid = cursor_to_obj[cursor];
473 cout << ": it->get_cursor()=" << it.get_cursor() << " expected=" << cursor << std::endl;
474 cout << ": it->get_oid()=" << it->get_oid() << " expected=" << expected_oid << std::endl;
475 cout << ": it->get_nspace()=" << it->get_oid() << " expected=" << ns << std::endl;
476 ASSERT_EQ(expected_oid, it->get_oid());
477 ASSERT_EQ(it->get_nspace(), ns);
478 }
479 }
480 }
481
482 TEST_F(LibRadosListPP, ListObjectsCursorPP) {
483 char buf[128];
484 memset(buf, 0xcc, sizeof(buf));
485 bufferlist bl;
486 bl.append(buf, sizeof(buf));
487
488 const int max_objs = 16;
489
490 for (int i=0; i<max_objs; ++i) {
491 stringstream ss;
492 ss << "ns" << i / 4;
493 ioctx.set_namespace(ss.str());
494 ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
495 }
496
497 ioctx.set_namespace(all_nspaces);
498
499 librados::NObjectIterator it = ioctx.nobjects_begin();
500 std::map<librados::ObjectCursor, string> cursor_to_obj;
501
502 int count = 0;
503
504 for (; it != ioctx.nobjects_end(); ++it, ++count) {
505 librados::ObjectCursor cursor = it.get_cursor();
506 string oid = it->get_oid();
507 std::cout << oid << " " << it.get_pg_hash_position() << std::endl;
508 cout << ": oid=" << oid << " cursor=" << it.get_cursor() << std::endl;
509 cursor_to_obj[cursor] = oid;
510
511 it.seek(cursor);
512 cout << ": seek to " << cursor << std::endl;
513 ASSERT_EQ(oid, it->get_oid());
514 ASSERT_LT(count, max_objs); /* avoid infinite loops due to bad seek */
515 }
516
517 ASSERT_EQ(count, max_objs);
518
519 auto p = cursor_to_obj.rbegin();
520 it = ioctx.nobjects_begin();
521 while (p != cursor_to_obj.rend()) {
522 cout << ": seek to " << p->first << std::endl;
523 it.seek(p->first);
524 ASSERT_EQ(p->first, it.get_cursor());
525 cout << ": it->get_cursor()=" << it.get_cursor() << " expected=" << p->first << std::endl;
526 cout << ": it->get_oid()=" << it->get_oid() << " expected=" << p->second << std::endl;
527 ASSERT_EQ(p->second, it->get_oid());
528
529 librados::NObjectIterator it2 = ioctx.nobjects_begin(it.get_cursor());
530 ASSERT_EQ(it2->get_oid(), it->get_oid());
531
532 ++p;
533 }
534 }
535
536 TEST_F(LibRadosList, ListObjectsCursor) {
537 char buf[128];
538 memset(buf, 0xcc, sizeof(buf));
539
540 const int max_objs = 16;
541
542 for (int i=0; i<max_objs; ++i) {
543 string n = stringify(i);
544 ASSERT_EQ(0, rados_write(ioctx, n.c_str(), buf, sizeof(buf), 0));
545 }
546
547 {
548 rados_list_ctx_t ctx;
549 const char *entry;
550 rados_object_list_cursor cursor;
551 ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
552 ASSERT_EQ(rados_nobjects_list_get_cursor(ctx, &cursor), 0);
553 rados_object_list_cursor first_cursor = cursor;
554 cout << "x cursor=" << ObjectCursor(cursor) << std::endl;
555 while (rados_nobjects_list_next(ctx, &entry, NULL, NULL) == 0) {
556 string oid = entry;
557 ASSERT_EQ(rados_nobjects_list_get_cursor(ctx, &cursor), 0);
558 cout << "> oid=" << oid << " cursor=" << ObjectCursor(cursor) << std::endl;
559 }
560 rados_nobjects_list_seek_cursor(ctx, first_cursor);
561 ASSERT_EQ(rados_nobjects_list_next(ctx, &entry, NULL, NULL), 0);
562 cout << "FIRST> seek to " << ObjectCursor(first_cursor) << " oid=" << string(entry) << std::endl;
563 }
564 rados_list_ctx_t ctx;
565 ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
566
567 std::map<rados_object_list_cursor, string> cursor_to_obj;
568 int count = 0;
569
570 const char *entry;
571 while (rados_nobjects_list_next(ctx, &entry, NULL, NULL) == 0) {
572 rados_object_list_cursor cursor;
573 ASSERT_EQ(rados_nobjects_list_get_cursor(ctx, &cursor), 0);
574 string oid = entry;
575 cout << ": oid=" << oid << " cursor=" << ObjectCursor(cursor) << std::endl;
576 cursor_to_obj[cursor] = oid;
577
578 rados_nobjects_list_seek_cursor(ctx, cursor);
579 cout << ": seek to " << ObjectCursor(cursor) << std::endl;
580 ASSERT_EQ(rados_nobjects_list_next(ctx, &entry, NULL, NULL), 0);
581 cout << "> " << ObjectCursor(cursor) << " -> " << entry << std::endl;
582 ASSERT_EQ(string(entry), oid);
583 ASSERT_LT(count, max_objs); /* avoid infinite loops due to bad seek */
584
585 ++count;
586 }
587
588 ASSERT_EQ(count, max_objs);
589
590 auto p = cursor_to_obj.rbegin();
591 ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
592 while (p != cursor_to_obj.rend()) {
593 cout << ": seek to " << ObjectCursor(p->first) << std::endl;
594 rados_object_list_cursor cursor;
595 rados_object_list_cursor oid(p->first);
596 rados_nobjects_list_seek_cursor(ctx, oid);
597 ASSERT_EQ(rados_nobjects_list_get_cursor(ctx, &cursor), 0);
598 cout << ": cursor()=" << ObjectCursor(cursor) << " expected=" << oid << std::endl;
599 // ASSERT_EQ(ObjectCursor(oid), ObjectCursor(cursor));
600 ASSERT_EQ(rados_nobjects_list_next(ctx, &entry, NULL, NULL), 0);
601 cout << "> " << ObjectCursor(cursor) << " -> " << entry << std::endl;
602 cout << ": entry=" << entry << " expected=" << p->second << std::endl;
603 ASSERT_EQ(p->second, string(entry));
604
605 ++p;
606
607 rados_object_list_cursor_free(ctx, cursor);
608 }
609 }
610
611 TEST_F(LibRadosListEC, ListObjects) {
612 char buf[128];
613 memset(buf, 0xcc, sizeof(buf));
614 ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
615 rados_list_ctx_t ctx;
616 ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
617 const char *entry;
618 bool foundit = false;
619 while (rados_nobjects_list_next(ctx, &entry, NULL, NULL) != -ENOENT) {
620 foundit = true;
621 ASSERT_EQ(std::string(entry), "foo");
622 }
623 ASSERT_TRUE(foundit);
624 rados_nobjects_list_close(ctx);
625 }
626
627 TEST_F(LibRadosListECPP, ListObjectsPP) {
628 char buf[128];
629 memset(buf, 0xcc, sizeof(buf));
630 bufferlist bl1;
631 bl1.append(buf, sizeof(buf));
632 ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
633 NObjectIterator iter(ioctx.nobjects_begin());
634 bool foundit = false;
635 while (iter != ioctx.nobjects_end()) {
636 foundit = true;
637 ASSERT_EQ((*iter).get_oid(), "foo");
638 ++iter;
639 }
640 ASSERT_TRUE(foundit);
641 }
642
643 TEST_F(LibRadosListECPP, ListObjectsTwicePP) {
644 char buf[128];
645 memset(buf, 0xcc, sizeof(buf));
646 bufferlist bl1;
647 bl1.append(buf, sizeof(buf));
648 ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
649 NObjectIterator iter(ioctx.nobjects_begin());
650 bool foundit = false;
651 while (iter != ioctx.nobjects_end()) {
652 foundit = true;
653 ASSERT_EQ((*iter).get_oid(), "foo");
654 ++iter;
655 }
656 ASSERT_TRUE(foundit);
657 ++iter;
658 ASSERT_TRUE(iter == ioctx.nobjects_end());
659 foundit = false;
660 iter.seek(0);
661 while (iter != ioctx.nobjects_end()) {
662 foundit = true;
663 ASSERT_EQ((*iter).get_oid(), "foo");
664 ++iter;
665 }
666 ASSERT_TRUE(foundit);
667 }
668
669 TEST_F(LibRadosListECPP, ListObjectsCopyIterPP) {
670 char buf[128];
671 memset(buf, 0xcc, sizeof(buf));
672 bufferlist bl1;
673 bl1.append(buf, sizeof(buf));
674 ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
675
676 // make sure this is still valid after the original iterators are gone
677 NObjectIterator iter3;
678 {
679 NObjectIterator iter(ioctx.nobjects_begin());
680 NObjectIterator iter2(iter);
681 iter3 = iter2;
682 ASSERT_EQ((*iter).get_oid(), "foo");
683 ++iter;
684 ASSERT_TRUE(iter == ioctx.nobjects_end());
685 ++iter;
686 ASSERT_TRUE(iter == ioctx.nobjects_end());
687
688 ASSERT_EQ(iter2->get_oid(), "foo");
689 ASSERT_EQ(iter3->get_oid(), "foo");
690 ++iter2;
691 ASSERT_TRUE(iter2 == ioctx.nobjects_end());
692 }
693
694 ASSERT_EQ(iter3->get_oid(), "foo");
695 iter3 = iter3;
696 ASSERT_EQ(iter3->get_oid(), "foo");
697 ++iter3;
698 ASSERT_TRUE(iter3 == ioctx.nobjects_end());
699 }
700
701 TEST_F(LibRadosListECPP, ListObjectsEndIter) {
702 char buf[128];
703 memset(buf, 0xcc, sizeof(buf));
704 bufferlist bl1;
705 bl1.append(buf, sizeof(buf));
706 ASSERT_EQ(0, ioctx.write("foo", bl1, sizeof(buf), 0));
707
708 NObjectIterator iter(ioctx.nobjects_begin());
709 NObjectIterator iter_end(ioctx.nobjects_end());
710 NObjectIterator iter_end2 = ioctx.nobjects_end();
711 ASSERT_TRUE(iter_end == iter_end2);
712 ASSERT_TRUE(iter_end == ioctx.nobjects_end());
713 ASSERT_TRUE(iter_end2 == ioctx.nobjects_end());
714
715 ASSERT_EQ(iter->get_oid(), "foo");
716 ++iter;
717 ASSERT_TRUE(iter == ioctx.nobjects_end());
718 ASSERT_TRUE(iter == iter_end);
719 ASSERT_TRUE(iter == iter_end2);
720 NObjectIterator iter2 = iter;
721 ASSERT_TRUE(iter2 == ioctx.nobjects_end());
722 ASSERT_TRUE(iter2 == iter_end);
723 ASSERT_TRUE(iter2 == iter_end2);
724 }
725
726 TEST_F(LibRadosListEC, ListObjectsNS) {
727 char buf[128];
728 memset(buf, 0xcc, sizeof(buf));
729 // Create :foo1, :foo2, :foo3, n1:foo1, ns1:foo4, ns1:foo5, ns2:foo6, n2:foo7
730 rados_ioctx_set_namespace(ioctx, "");
731 ASSERT_EQ(0, rados_write(ioctx, "foo1", buf, sizeof(buf), 0));
732 rados_ioctx_set_namespace(ioctx, "ns1");
733 ASSERT_EQ(0, rados_write(ioctx, "foo1", buf, sizeof(buf), 0));
734 rados_ioctx_set_namespace(ioctx, "");
735 ASSERT_EQ(0, rados_write(ioctx, "foo2", buf, sizeof(buf), 0));
736 ASSERT_EQ(0, rados_write(ioctx, "foo3", buf, sizeof(buf), 0));
737 rados_ioctx_set_namespace(ioctx, "ns1");
738 ASSERT_EQ(0, rados_write(ioctx, "foo4", buf, sizeof(buf), 0));
739 ASSERT_EQ(0, rados_write(ioctx, "foo5", buf, sizeof(buf), 0));
740 rados_ioctx_set_namespace(ioctx, "ns2");
741 ASSERT_EQ(0, rados_write(ioctx, "foo6", buf, sizeof(buf), 0));
742 ASSERT_EQ(0, rados_write(ioctx, "foo7", buf, sizeof(buf), 0));
743
744 std::set<std::string> def, ns1, ns2, all;
745 def.insert(std::string("foo1"));
746 def.insert(std::string("foo2"));
747 def.insert(std::string("foo3"));
748 ns1.insert(std::string("foo1"));
749 ns1.insert(std::string("foo4"));
750 ns1.insert(std::string("foo5"));
751 ns2.insert(std::string("foo6"));
752 ns2.insert(std::string("foo7"));
753 all.insert(std::string(":foo1"));
754 all.insert(std::string(":foo2"));
755 all.insert(std::string(":foo3"));
756 all.insert(std::string("ns1:foo1"));
757 all.insert(std::string("ns1:foo4"));
758 all.insert(std::string("ns1:foo5"));
759 all.insert(std::string("ns2:foo6"));
760 all.insert(std::string("ns2:foo7"));
761
762 rados_list_ctx_t ctx;
763 // Check default namespace ""
764 rados_ioctx_set_namespace(ioctx, "");
765 ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
766 check_list(def, ctx, "");
767 rados_nobjects_list_close(ctx);
768
769 // Check default namespace "ns1"
770 rados_ioctx_set_namespace(ioctx, "ns1");
771 ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
772 check_list(ns1, ctx, "ns1");
773 rados_nobjects_list_close(ctx);
774
775 // Check default namespace "ns2"
776 rados_ioctx_set_namespace(ioctx, "ns2");
777 ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
778 check_list(ns2, ctx, "ns2");
779 rados_nobjects_list_close(ctx);
780
781 // Check all namespaces
782 rados_ioctx_set_namespace(ioctx, LIBRADOS_ALL_NSPACES);
783 ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
784 check_list(all, ctx, all_nspaces);
785 rados_nobjects_list_close(ctx);
786 }
787
788 TEST_F(LibRadosListECPP, ListObjectsPPNS) {
789 char buf[128];
790 memset(buf, 0xcc, sizeof(buf));
791 bufferlist bl1;
792 bl1.append(buf, sizeof(buf));
793 // Create :foo1, :foo2, :foo3, n1:foo1, ns1:foo4, ns1:foo5, ns2:foo6, n2:foo7
794 ioctx.set_namespace("");
795 ASSERT_EQ(0, ioctx.write("foo1", bl1, sizeof(buf), 0));
796 ioctx.set_namespace("ns1");
797 ASSERT_EQ(0, ioctx.write("foo1", bl1, sizeof(buf), 0));
798 ioctx.set_namespace("");
799 ASSERT_EQ(0, ioctx.write("foo2", bl1, sizeof(buf), 0));
800 ASSERT_EQ(0, ioctx.write("foo3", bl1, sizeof(buf), 0));
801 ioctx.set_namespace("ns1");
802 ASSERT_EQ(0, ioctx.write("foo4", bl1, sizeof(buf), 0));
803 ASSERT_EQ(0, ioctx.write("foo5", bl1, sizeof(buf), 0));
804 ioctx.set_namespace("ns2");
805 ASSERT_EQ(0, ioctx.write("foo6", bl1, sizeof(buf), 0));
806 ASSERT_EQ(0, ioctx.write("foo7", bl1, sizeof(buf), 0));
807
808 std::set<std::string> def, ns1, ns2;
809 def.insert(std::string("foo1"));
810 def.insert(std::string("foo2"));
811 def.insert(std::string("foo3"));
812 ns1.insert(std::string("foo1"));
813 ns1.insert(std::string("foo4"));
814 ns1.insert(std::string("foo5"));
815 ns2.insert(std::string("foo6"));
816 ns2.insert(std::string("foo7"));
817
818 ioctx.set_namespace("");
819 check_listpp(def, ioctx, "");
820
821 ioctx.set_namespace("ns1");
822 check_listpp(ns1, ioctx, "ns1");
823
824 ioctx.set_namespace("ns2");
825 check_listpp(ns2, ioctx, "ns2");
826 }
827
828 TEST_F(LibRadosListECPP, ListObjectsManyPP) {
829 char buf[128];
830 memset(buf, 0xcc, sizeof(buf));
831 bufferlist bl;
832 bl.append(buf, sizeof(buf));
833
834 for (int i=0; i<256; ++i) {
835 ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
836 }
837
838 librados::NObjectIterator it = ioctx.nobjects_begin();
839 std::set<std::string> saw_obj;
840 std::set<int> saw_pg;
841 for (; it != ioctx.nobjects_end(); ++it) {
842 std::cout << it->get_oid()
843 << " " << it.get_pg_hash_position() << std::endl;
844 saw_obj.insert(it->get_oid());
845 saw_pg.insert(it.get_pg_hash_position());
846 }
847 std::cout << "saw " << saw_pg.size() << " pgs " << std::endl;
848
849 // make sure they are 0..n
850 for (unsigned i = 0; i < saw_pg.size(); ++i)
851 ASSERT_TRUE(saw_pg.count(i));
852 }
853
854 TEST_F(LibRadosListEC, ListObjectsStart) {
855 char buf[128];
856 memset(buf, 0xcc, sizeof(buf));
857
858 for (int i=0; i<16; ++i) {
859 string n = stringify(i);
860 ASSERT_EQ(0, rados_write(ioctx, n.c_str(), buf, sizeof(buf), 0));
861 }
862
863 rados_list_ctx_t ctx;
864 ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
865 std::map<int, std::set<std::string> > pg_to_obj;
866 const char *entry;
867 while (rados_nobjects_list_next(ctx, &entry, NULL, NULL) == 0) {
868 uint32_t pos = rados_nobjects_list_get_pg_hash_position(ctx);
869 std::cout << entry << " " << pos << std::endl;
870 pg_to_obj[pos].insert(entry);
871 }
872 rados_nobjects_list_close(ctx);
873
874 std::map<int, std::set<std::string> >::reverse_iterator p =
875 pg_to_obj.rbegin();
876 ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
877 while (p != pg_to_obj.rend()) {
878 ASSERT_EQ((uint32_t)p->first, rados_nobjects_list_seek(ctx, p->first));
879 ASSERT_EQ(0, rados_nobjects_list_next(ctx, &entry, NULL, NULL));
880 std::cout << "have " << entry << " expect one of " << p->second << std::endl;
881 ASSERT_TRUE(p->second.count(entry));
882 ++p;
883 }
884 rados_nobjects_list_close(ctx);
885 }
886
887 TEST_F(LibRadosListECPP, ListObjectsStartPP) {
888 char buf[128];
889 memset(buf, 0xcc, sizeof(buf));
890 bufferlist bl;
891 bl.append(buf, sizeof(buf));
892
893 for (int i=0; i<16; ++i) {
894 ASSERT_EQ(0, ioctx.write(stringify(i), bl, bl.length(), 0));
895 }
896
897 librados::NObjectIterator it = ioctx.nobjects_begin();
898 std::map<int, std::set<std::string> > pg_to_obj;
899 for (; it != ioctx.nobjects_end(); ++it) {
900 std::cout << it->get_oid() << " " << it.get_pg_hash_position() << std::endl;
901 pg_to_obj[it.get_pg_hash_position()].insert(it->get_oid());
902 }
903
904 std::map<int, std::set<std::string> >::reverse_iterator p =
905 pg_to_obj.rbegin();
906 it = ioctx.nobjects_begin(p->first);
907 while (p != pg_to_obj.rend()) {
908 ASSERT_EQ((uint32_t)p->first, it.seek(p->first));
909 std::cout << "have " << it->get_oid() << " expect one of " << p->second << std::endl;
910 ASSERT_TRUE(p->second.count(it->get_oid()));
911 ++p;
912 }
913 }
914
915 TEST_F(LibRadosListPP, ListObjectsFilterPP) {
916 char buf[128];
917 memset(buf, 0xcc, sizeof(buf));
918 bufferlist obj_content;
919 obj_content.append(buf, sizeof(buf));
920
921 std::string target_str = "content";
922
923 // Write xattr bare, no ::encod'ing
924 bufferlist target_val;
925 target_val.append(target_str);
926 bufferlist nontarget_val;
927 nontarget_val.append("rhubarb");
928
929 ASSERT_EQ(0, ioctx.write("has_xattr", obj_content, obj_content.length(), 0));
930 ASSERT_EQ(0, ioctx.write("has_wrong_xattr", obj_content, obj_content.length(), 0));
931 ASSERT_EQ(0, ioctx.write("no_xattr", obj_content, obj_content.length(), 0));
932
933 ASSERT_EQ(0, ioctx.setxattr("has_xattr", "theattr", target_val));
934 ASSERT_EQ(0, ioctx.setxattr("has_wrong_xattr", "theattr", nontarget_val));
935
936 bufferlist filter_bl;
937 std::string filter_name = "plain";
938 ::encode(filter_name, filter_bl);
939 ::encode("_theattr", filter_bl);
940 ::encode(target_str, filter_bl);
941
942 NObjectIterator iter(ioctx.nobjects_begin(filter_bl));
943 bool foundit = false;
944 int k = 0;
945 while (iter != ioctx.nobjects_end()) {
946 foundit = true;
947 // We should only see the object that matches the filter
948 ASSERT_EQ((*iter).get_oid(), "has_xattr");
949 // We should only see it once
950 ASSERT_EQ(k, 0);
951 ++iter;
952 ++k;
953 }
954 ASSERT_TRUE(foundit);
955 }
956
957 TEST_F(LibRadosListNP, ListObjectsError) {
958 std::string pool_name;
959 rados_t cluster;
960 rados_ioctx_t ioctx;
961 pool_name = get_temp_pool_name();
962 ASSERT_EQ("", create_one_pool(pool_name, &cluster));
963 ASSERT_EQ(0, rados_ioctx_create(cluster, pool_name.c_str(), &ioctx));
964 char buf[128];
965 memset(buf, 0xcc, sizeof(buf));
966 rados_ioctx_set_namespace(ioctx, "");
967 ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
968
969 //ASSERT_EQ(0, rados_pool_delete(cluster, pool_name.c_str()));
970 {
971 char *buf, *st;
972 size_t buflen, stlen;
973 string c = "{\"prefix\":\"osd pool rm\",\"pool\": \"" + pool_name +
974 "\",\"pool2\":\"" + pool_name +
975 "\",\"sure\": \"--yes-i-really-really-mean-it-not-faking\"}";
976 const char *cmd[2] = { c.c_str(), 0 };
977 ASSERT_EQ(0, rados_mon_command(cluster, (const char **)cmd, 1, "", 0, &buf, &buflen, &st, &stlen));
978 ASSERT_EQ(0, rados_wait_for_latest_osdmap(cluster));
979 }
980
981 rados_list_ctx_t ctx;
982 ASSERT_EQ(0, rados_nobjects_list_open(ioctx, &ctx));
983 const char *entry;
984 ASSERT_EQ(-ENOENT, rados_nobjects_list_next(ctx, &entry, NULL, NULL));
985 rados_nobjects_list_close(ctx);
986 rados_ioctx_destroy(ioctx);
987 rados_shutdown(cluster);
988 }
989
990
991
992 // ---------------------------------------------
993
994 TEST_F(LibRadosList, EnumerateObjects) {
995 char buf[128];
996 memset(buf, 0xcc, sizeof(buf));
997
998 const uint32_t n_objects = 16;
999 for (unsigned i=0; i<n_objects; ++i) {
1000 ASSERT_EQ(0, rados_write(ioctx, stringify(i).c_str(), buf, sizeof(buf), 0));
1001 }
1002
1003 // Ensure a non-power-of-two PG count to avoid only
1004 // touching the easy path.
1005 ASSERT_TRUE(set_pg_num(&s_cluster, pool_name, 11).empty());
1006 ASSERT_TRUE(set_pgp_num(&s_cluster, pool_name, 11).empty());
1007
1008 std::set<std::string> saw_obj;
1009 rados_object_list_cursor c = rados_object_list_begin(ioctx);
1010 rados_object_list_cursor end = rados_object_list_end(ioctx);
1011 while(!rados_object_list_is_end(ioctx, c))
1012 {
1013 rados_object_list_item results[12];
1014 memset(results, 0, sizeof(rados_object_list_item) * 12);
1015 rados_object_list_cursor temp_end = rados_object_list_end(ioctx);
1016 int r = rados_object_list(ioctx, c, temp_end,
1017 12, NULL, 0, results, &c);
1018 rados_object_list_cursor_free(ioctx, temp_end);
1019 ASSERT_GE(r, 0);
1020 for (int i = 0; i < r; ++i) {
1021 std::string oid(results[i].oid, results[i].oid_length);
1022 if (saw_obj.count(oid)) {
1023 std::cerr << "duplicate obj " << oid << std::endl;
1024 }
1025 ASSERT_FALSE(saw_obj.count(oid));
1026 saw_obj.insert(oid);
1027 }
1028 rados_object_list_free(12, results);
1029 }
1030 rados_object_list_cursor_free(ioctx, c);
1031 rados_object_list_cursor_free(ioctx, end);
1032
1033 for (unsigned i=0; i<n_objects; ++i) {
1034 if (!saw_obj.count(stringify(i))) {
1035 std::cerr << "missing object " << i << std::endl;
1036 }
1037 ASSERT_TRUE(saw_obj.count(stringify(i)));
1038 }
1039 ASSERT_EQ(n_objects, saw_obj.size());
1040 }
1041
1042 TEST_F(LibRadosList, EnumerateObjectsSplit) {
1043 char buf[128];
1044 memset(buf, 0xcc, sizeof(buf));
1045
1046 const uint32_t n_objects = 16;
1047 for (unsigned i=0; i<n_objects; ++i) {
1048 ASSERT_EQ(0, rados_write(ioctx, stringify(i).c_str(), buf, sizeof(buf), 0));
1049 }
1050
1051 // Ensure a non-power-of-two PG count to avoid only
1052 // touching the easy path.
1053 ASSERT_TRUE(set_pg_num(&s_cluster, pool_name, 11).empty());
1054 ASSERT_TRUE(set_pgp_num(&s_cluster, pool_name, 11).empty());
1055
1056 rados_object_list_cursor begin = rados_object_list_begin(ioctx);
1057 rados_object_list_cursor end = rados_object_list_end(ioctx);
1058
1059 // Step through an odd number of shards
1060 unsigned m = 5;
1061 std::set<std::string> saw_obj;
1062 for (unsigned n = 0; n < m; ++n) {
1063 rados_object_list_cursor shard_start = rados_object_list_begin(ioctx);;
1064 rados_object_list_cursor shard_end = rados_object_list_end(ioctx);;
1065
1066 rados_object_list_slice(
1067 ioctx,
1068 begin,
1069 end,
1070 n,
1071 m,
1072 &shard_start,
1073 &shard_end);
1074 std::cout << "split " << n << "/" << m << " -> "
1075 << *(hobject_t*)shard_start << " "
1076 << *(hobject_t*)shard_end << std::endl;
1077
1078 rados_object_list_cursor c = shard_start;
1079 //while(c < shard_end)
1080 while(rados_object_list_cursor_cmp(ioctx, c, shard_end) == -1)
1081 {
1082 rados_object_list_item results[12];
1083 memset(results, 0, sizeof(rados_object_list_item) * 12);
1084 int r = rados_object_list(ioctx,
1085 c, shard_end,
1086 12, NULL, 0, results, &c);
1087 ASSERT_GE(r, 0);
1088 for (int i = 0; i < r; ++i) {
1089 std::string oid(results[i].oid, results[i].oid_length);
1090 if (saw_obj.count(oid)) {
1091 std::cerr << "duplicate obj " << oid << std::endl;
1092 }
1093 ASSERT_FALSE(saw_obj.count(oid));
1094 saw_obj.insert(oid);
1095 }
1096 rados_object_list_free(12, results);
1097 }
1098 rados_object_list_cursor_free(ioctx, shard_start);
1099 rados_object_list_cursor_free(ioctx, shard_end);
1100 }
1101
1102 rados_object_list_cursor_free(ioctx, begin);
1103 rados_object_list_cursor_free(ioctx, end);
1104
1105 for (unsigned i=0; i<n_objects; ++i) {
1106 if (!saw_obj.count(stringify(i))) {
1107 std::cerr << "missing object " << i << std::endl;
1108 }
1109 ASSERT_TRUE(saw_obj.count(stringify(i)));
1110 }
1111 ASSERT_EQ(n_objects, saw_obj.size());
1112 }
1113
1114 TEST_F(LibRadosListPP, EnumerateObjectsPP) {
1115 char buf[128];
1116 memset(buf, 0xcc, sizeof(buf));
1117 bufferlist bl;
1118 bl.append(buf, sizeof(buf));
1119
1120 const uint32_t n_objects = 16;
1121 for (unsigned i=0; i<n_objects; ++i) {
1122 ASSERT_EQ(0, ioctx.write(stringify(i), bl, sizeof(buf), 0));
1123 }
1124
1125 std::set<std::string> saw_obj;
1126 ObjectCursor c = ioctx.object_list_begin();
1127 ObjectCursor end = ioctx.object_list_end();
1128 while(!ioctx.object_list_is_end(c))
1129 {
1130 std::vector<ObjectItem> result;
1131 int r = ioctx.object_list(c, end, 12, {}, &result, &c);
1132 ASSERT_GE(r, 0);
1133 ASSERT_EQ(r, (int)result.size());
1134 for (int i = 0; i < r; ++i) {
1135 auto oid = result[i].oid;
1136 if (saw_obj.count(oid)) {
1137 std::cerr << "duplicate obj " << oid << std::endl;
1138 }
1139 ASSERT_FALSE(saw_obj.count(oid));
1140 saw_obj.insert(oid);
1141 }
1142 }
1143
1144 for (unsigned i=0; i<n_objects; ++i) {
1145 if (!saw_obj.count(stringify(i))) {
1146 std::cerr << "missing object " << i << std::endl;
1147 }
1148 ASSERT_TRUE(saw_obj.count(stringify(i)));
1149 }
1150 ASSERT_EQ(n_objects, saw_obj.size());
1151 }
1152
1153 TEST_F(LibRadosListPP, EnumerateObjectsSplitPP) {
1154 char buf[128];
1155 memset(buf, 0xcc, sizeof(buf));
1156 bufferlist bl;
1157 bl.append(buf, sizeof(buf));
1158
1159 const uint32_t n_objects = 16;
1160 for (unsigned i=0; i<n_objects; ++i) {
1161 ASSERT_EQ(0, ioctx.write(stringify(i), bl, sizeof(buf), 0));
1162 }
1163
1164 ObjectCursor begin = ioctx.object_list_begin();
1165 ObjectCursor end = ioctx.object_list_end();
1166
1167 // Step through an odd number of shards
1168 unsigned m = 5;
1169 std::set<std::string> saw_obj;
1170 for (unsigned n = 0; n < m; ++n) {
1171 ObjectCursor shard_start;
1172 ObjectCursor shard_end;
1173
1174 ioctx.object_list_slice(
1175 begin,
1176 end,
1177 n,
1178 m,
1179 &shard_start,
1180 &shard_end);
1181
1182 ObjectCursor c(shard_start);
1183 while(c < shard_end)
1184 {
1185 std::vector<ObjectItem> result;
1186 int r = ioctx.object_list(c, shard_end, 12, {}, &result, &c);
1187 ASSERT_GE(r, 0);
1188
1189 for (const auto & i : result) {
1190 const auto &oid = i.oid;
1191 if (saw_obj.count(oid)) {
1192 std::cerr << "duplicate obj " << oid << std::endl;
1193 }
1194 ASSERT_FALSE(saw_obj.count(oid));
1195 saw_obj.insert(oid);
1196 }
1197 }
1198 }
1199
1200 for (unsigned i=0; i<n_objects; ++i) {
1201 if (!saw_obj.count(stringify(i))) {
1202 std::cerr << "missing object " << i << std::endl;
1203 }
1204 ASSERT_TRUE(saw_obj.count(stringify(i)));
1205 }
1206 ASSERT_EQ(n_objects, saw_obj.size());
1207 }
1208
1209
1210 TEST_F(LibRadosListPP, EnumerateObjectsFilterPP) {
1211 char buf[128];
1212 memset(buf, 0xcc, sizeof(buf));
1213 bufferlist obj_content;
1214 obj_content.append(buf, sizeof(buf));
1215
1216 std::string target_str = "content";
1217
1218 // Write xattr bare, no ::encod'ing
1219 bufferlist target_val;
1220 target_val.append(target_str);
1221 bufferlist nontarget_val;
1222 nontarget_val.append("rhubarb");
1223
1224 ASSERT_EQ(0, ioctx.write("has_xattr", obj_content, obj_content.length(), 0));
1225 ASSERT_EQ(0, ioctx.write("has_wrong_xattr", obj_content, obj_content.length(), 0));
1226 ASSERT_EQ(0, ioctx.write("no_xattr", obj_content, obj_content.length(), 0));
1227
1228 ASSERT_EQ(0, ioctx.setxattr("has_xattr", "theattr", target_val));
1229 ASSERT_EQ(0, ioctx.setxattr("has_wrong_xattr", "theattr", nontarget_val));
1230
1231 bufferlist filter_bl;
1232 std::string filter_name = "plain";
1233 ::encode(filter_name, filter_bl);
1234 ::encode("_theattr", filter_bl);
1235 ::encode(target_str, filter_bl);
1236
1237 ObjectCursor c = ioctx.object_list_begin();
1238 ObjectCursor end = ioctx.object_list_end();
1239 bool foundit = false;
1240 while(!ioctx.object_list_is_end(c))
1241 {
1242 std::vector<ObjectItem> result;
1243 int r = ioctx.object_list(c, end, 12, filter_bl, &result, &c);
1244 ASSERT_GE(r, 0);
1245 ASSERT_EQ(r, (int)result.size());
1246 for (int i = 0; i < r; ++i) {
1247 auto oid = result[i].oid;
1248 // We should only see the object that matches the filter
1249 ASSERT_EQ(oid, "has_xattr");
1250 // We should only see it once
1251 ASSERT_FALSE(foundit);
1252 foundit = true;
1253 }
1254 }
1255 ASSERT_TRUE(foundit);
1256 }