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