]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/librados/misc.cc
update sources to v12.1.0
[ceph.git] / ceph / src / test / librados / misc.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 #include "gtest/gtest.h"
4
5 #include "mds/mdstypes.h"
6 #include "include/err.h"
7 #include "include/buffer.h"
8 #include "include/rbd_types.h"
9 #include "include/rados/librados.h"
10 #include "include/rados/librados.hpp"
11 #include "include/stringify.h"
12 #include "common/Checksummer.h"
13 #include "global/global_context.h"
14 #include "test/librados/test.h"
15 #include "test/librados/TestCase.h"
16 #include "gtest/gtest.h"
17
18 #include <errno.h>
19 #include <map>
20 #include <sstream>
21 #include <string>
22
23
24 using namespace librados;
25 using std::map;
26 using std::ostringstream;
27 using std::string;
28
29 typedef RadosTest LibRadosMisc;
30 typedef RadosTestPP LibRadosMiscPP;
31
32 TEST(LibRadosMiscVersion, Version) {
33 int major, minor, extra;
34 rados_version(&major, &minor, &extra);
35 }
36
37 TEST(LibRadosMiscVersion, VersionPP) {
38 int major, minor, extra;
39 Rados::version(&major, &minor, &extra);
40 }
41
42 static void test_rados_log_cb(void *arg,
43 const char *line,
44 const char *who,
45 uint64_t sec, uint64_t nsec,
46 uint64_t seq, const char *level,
47 const char *msg)
48 {
49 std::cerr << "monitor log callback invoked" << std::endl;
50 }
51
52 TEST(LibRadosMiscConnectFailure, ConnectFailure) {
53 rados_t cluster;
54
55 char *id = getenv("CEPH_CLIENT_ID");
56 if (id)
57 std::cerr << "Client id is: " << id << std::endl;
58
59 ASSERT_EQ(0, rados_create(&cluster, NULL));
60 ASSERT_EQ(0, rados_conf_read_file(cluster, NULL));
61 ASSERT_EQ(0, rados_conf_parse_env(cluster, NULL));
62
63 ASSERT_EQ(0, rados_conf_set(cluster, "client_mount_timeout", "0.000000001"));
64 ASSERT_EQ(0, rados_conf_set(cluster, "debug_monc", "20"));
65 ASSERT_EQ(0, rados_conf_set(cluster, "debug_ms", "1"));
66 ASSERT_EQ(0, rados_conf_set(cluster, "log_to_stderr", "true"));
67
68 ASSERT_EQ(-ENOTCONN, rados_monitor_log(cluster, "error",
69 test_rados_log_cb, NULL));
70
71 // try this a few times; sometimes we don't schedule fast enough for the
72 // cond to time out
73 int r;
74 for (unsigned i=0; i<16; ++i) {
75 cout << i << std::endl;
76 r = rados_connect(cluster);
77 if (r < 0)
78 break; // yay, we timed out
79 // try again
80 rados_shutdown(cluster);
81 ASSERT_EQ(0, rados_create(&cluster, NULL));
82 }
83 ASSERT_NE(0, r);
84
85 rados_shutdown(cluster);
86 }
87
88 TEST(LibRadosMiscPool, PoolCreationRace) {
89 rados_t cluster_a, cluster_b;
90
91 char *id = getenv("CEPH_CLIENT_ID");
92 if (id)
93 std::cerr << "Client id is: " << id << std::endl;
94
95 ASSERT_EQ(0, rados_create(&cluster_a, NULL));
96 ASSERT_EQ(0, rados_conf_read_file(cluster_a, NULL));
97 // kludge: i want to --log-file foo and only get cluster b
98 //ASSERT_EQ(0, rados_conf_parse_env(cluster_a, NULL));
99 ASSERT_EQ(0, rados_connect(cluster_a));
100
101 ASSERT_EQ(0, rados_create(&cluster_b, NULL));
102 ASSERT_EQ(0, rados_conf_read_file(cluster_b, NULL));
103 ASSERT_EQ(0, rados_conf_parse_env(cluster_b, NULL));
104 ASSERT_EQ(0, rados_conf_set(cluster_b,
105 "objecter_debug_inject_relock_delay", "true"));
106 ASSERT_EQ(0, rados_connect(cluster_b));
107
108 char poolname[80];
109 snprintf(poolname, sizeof(poolname), "poolrace.%d", rand());
110 rados_pool_create(cluster_a, poolname);
111 rados_ioctx_t a, b;
112 rados_ioctx_create(cluster_a, poolname, &a);
113 int64_t poolid = rados_ioctx_get_id(a);
114
115 rados_ioctx_create2(cluster_b, poolid+1, &b);
116
117 char pool2name[80];
118 snprintf(pool2name, sizeof(pool2name), "poolrace2.%d", rand());
119 rados_pool_create(cluster_a, pool2name);
120
121 list<rados_completion_t> cls;
122 // this should normally trigger pretty easily, but we need to bound
123 // the requests because if we get too many we'll get stuck by always
124 // sending enough messages that we hit the socket failure injection.
125 int max = 512;
126 while (max--) {
127 char buf[100];
128 rados_completion_t c;
129 rados_aio_create_completion(0, 0, 0, &c);
130 cls.push_back(c);
131 rados_aio_read(b, "PoolCreationRaceObj", c, buf, 100, 0);
132 cout << "started " << (void*)c << std::endl;
133 if (rados_aio_is_complete(cls.front())) {
134 break;
135 }
136 }
137 while (!rados_aio_is_complete(cls.front())) {
138 cout << "waiting 1 sec" << std::endl;
139 sleep(1);
140 }
141
142 cout << " started " << cls.size() << " aios" << std::endl;
143 for (auto c : cls) {
144 cout << "waiting " << (void*)c << std::endl;
145 rados_aio_wait_for_complete_and_cb(c);
146 rados_aio_release(c);
147 }
148 cout << "done." << std::endl;
149
150 rados_ioctx_destroy(a);
151 rados_ioctx_destroy(b);
152 rados_pool_delete(cluster_a, poolname);
153 rados_pool_delete(cluster_a, pool2name);
154 rados_shutdown(cluster_b);
155 rados_shutdown(cluster_a);
156 }
157
158 TEST_F(LibRadosMisc, ClusterFSID) {
159 char fsid[37];
160 ASSERT_EQ(-ERANGE, rados_cluster_fsid(cluster, fsid, sizeof(fsid) - 1));
161 ASSERT_EQ(sizeof(fsid) - 1,
162 (size_t)rados_cluster_fsid(cluster, fsid, sizeof(fsid)));
163 }
164
165 TEST_F(LibRadosMiscPP, WaitOSDMapPP) {
166 ASSERT_EQ(0, cluster.wait_for_latest_osdmap());
167 }
168
169 TEST_F(LibRadosMiscPP, LongNamePP) {
170 bufferlist bl;
171 bl.append("content");
172 int maxlen = g_conf->osd_max_object_name_len;
173 ASSERT_EQ(0, ioctx.write(string(maxlen/2, 'a').c_str(), bl, bl.length(), 0));
174 ASSERT_EQ(0, ioctx.write(string(maxlen-1, 'a').c_str(), bl, bl.length(), 0));
175 ASSERT_EQ(0, ioctx.write(string(maxlen, 'a').c_str(), bl, bl.length(), 0));
176 ASSERT_EQ(-ENAMETOOLONG, ioctx.write(string(maxlen+1, 'a').c_str(), bl, bl.length(), 0));
177 ASSERT_EQ(-ENAMETOOLONG, ioctx.write(string(maxlen*2, 'a').c_str(), bl, bl.length(), 0));
178 }
179
180 TEST_F(LibRadosMiscPP, LongLocatorPP) {
181 bufferlist bl;
182 bl.append("content");
183 int maxlen = g_conf->osd_max_object_name_len;
184 ioctx.locator_set_key(
185 string((maxlen/2), 'a'));
186 ASSERT_EQ(
187 0,
188 ioctx.write(
189 string("a").c_str(),
190 bl, bl.length(), 0));
191 ioctx.locator_set_key(
192 string(maxlen - 1, 'a'));
193 ASSERT_EQ(
194 0,
195 ioctx.write(
196 string("a").c_str(),
197 bl, bl.length(), 0));
198 ioctx.locator_set_key(
199 string(maxlen, 'a'));
200 ASSERT_EQ(
201 0,
202 ioctx.write(
203 string("a").c_str(),
204 bl, bl.length(), 0));
205 ioctx.locator_set_key(
206 string(maxlen+1, 'a'));
207 ASSERT_EQ(
208 -ENAMETOOLONG,
209 ioctx.write(
210 string("a").c_str(),
211 bl, bl.length(), 0));
212 ioctx.locator_set_key(
213 string((maxlen*2), 'a'));
214 ASSERT_EQ(
215 -ENAMETOOLONG,
216 ioctx.write(
217 string("a").c_str(),
218 bl, bl.length(), 0));
219 }
220
221 TEST_F(LibRadosMiscPP, LongNSpacePP) {
222 bufferlist bl;
223 bl.append("content");
224 int maxlen = g_conf->osd_max_object_namespace_len;
225 ioctx.set_namespace(
226 string((maxlen/2), 'a'));
227 ASSERT_EQ(
228 0,
229 ioctx.write(
230 string("a").c_str(),
231 bl, bl.length(), 0));
232 ioctx.set_namespace(
233 string(maxlen - 1, 'a'));
234 ASSERT_EQ(
235 0,
236 ioctx.write(
237 string("a").c_str(),
238 bl, bl.length(), 0));
239 ioctx.set_namespace(
240 string(maxlen, 'a'));
241 ASSERT_EQ(
242 0,
243 ioctx.write(
244 string("a").c_str(),
245 bl, bl.length(), 0));
246 ioctx.set_namespace(
247 string(maxlen+1, 'a'));
248 ASSERT_EQ(
249 -ENAMETOOLONG,
250 ioctx.write(
251 string("a").c_str(),
252 bl, bl.length(), 0));
253 ioctx.set_namespace(
254 string((maxlen*2), 'a'));
255 ASSERT_EQ(
256 -ENAMETOOLONG,
257 ioctx.write(
258 string("a").c_str(),
259 bl, bl.length(), 0));
260 }
261
262 TEST_F(LibRadosMiscPP, LongAttrNamePP) {
263 bufferlist bl;
264 bl.append("content");
265 int maxlen = g_conf->osd_max_attr_name_len;
266 ASSERT_EQ(0, ioctx.setxattr("bigattrobj", string(maxlen/2, 'a').c_str(), bl));
267 ASSERT_EQ(0, ioctx.setxattr("bigattrobj", string(maxlen-1, 'a').c_str(), bl));
268 ASSERT_EQ(0, ioctx.setxattr("bigattrobj", string(maxlen, 'a').c_str(), bl));
269 ASSERT_EQ(-ENAMETOOLONG, ioctx.setxattr("bigattrobj", string(maxlen+1, 'a').c_str(), bl));
270 ASSERT_EQ(-ENAMETOOLONG, ioctx.setxattr("bigattrobj", string(maxlen*2, 'a').c_str(), bl));
271 }
272
273 static std::string read_key_from_tmap(IoCtx& ioctx, const std::string &obj,
274 const std::string &key)
275 {
276 bufferlist bl;
277 int r = ioctx.read(obj, bl, 0, 0);
278 if (r <= 0) {
279 ostringstream oss;
280 oss << "ioctx.read(" << obj << ", bl, 0, 0) returned " << r;
281 return oss.str();
282 }
283 bufferlist::iterator p = bl.begin();
284 bufferlist header;
285 map<string, bufferlist> m;
286 ::decode(header, p);
287 ::decode(m, p);
288 map<string, bufferlist>::iterator i = m.find(key);
289 if (i == m.end())
290 return "";
291 std::string retstring;
292 ::decode(retstring, i->second);
293 return retstring;
294 }
295
296 static std::string add_key_to_tmap(IoCtx &ioctx, const std::string &obj,
297 const std::string &key, const std::string &val)
298 {
299 __u8 c = CEPH_OSD_TMAP_SET;
300
301 bufferlist tmbl;
302 ::encode(c, tmbl);
303 ::encode(key, tmbl);
304 bufferlist blbl;
305 ::encode(val, blbl);
306 ::encode(blbl, tmbl);
307 int ret = ioctx.tmap_update(obj, tmbl);
308 if (ret) {
309 ostringstream oss;
310 oss << "ioctx.tmap_update(obj=" << obj << ", key="
311 << key << ", val=" << val << ") failed with error " << ret;
312 return oss.str();
313 }
314 return "";
315 }
316
317 static int remove_key_from_tmap(IoCtx &ioctx, const std::string &obj,
318 const std::string &key)
319 {
320 __u8 c = CEPH_OSD_TMAP_RM;
321
322 bufferlist tmbl;
323 ::encode(c, tmbl);
324 ::encode(key, tmbl);
325 int ret = ioctx.tmap_update(obj, tmbl);
326 if (ret) {
327 ostringstream oss;
328 oss << "ioctx.tmap_update(obj=" << obj << ", key="
329 << key << ") failed with error " << ret;
330 }
331 return ret;
332 }
333
334 TEST_F(LibRadosMiscPP, TmapUpdatePP) {
335 // create tmap
336 {
337 __u8 c = CEPH_OSD_TMAP_CREATE;
338 std::string my_tmap("my_tmap");
339 bufferlist emptybl;
340
341 bufferlist tmbl;
342 ::encode(c, tmbl);
343 ::encode(my_tmap, tmbl);
344 ::encode(emptybl, tmbl);
345 ASSERT_EQ(0, ioctx.tmap_update("foo", tmbl));
346 }
347
348 ASSERT_EQ(string(""), add_key_to_tmap(ioctx, "foo", "key1", "val1"));
349
350 ASSERT_EQ(string(""), add_key_to_tmap(ioctx, "foo", "key2", "val2"));
351
352 // read key1 from the tmap
353 ASSERT_EQ(string("val1"), read_key_from_tmap(ioctx, "foo", "key1"));
354
355 // remove key1 from tmap
356 ASSERT_EQ(0, remove_key_from_tmap(ioctx, "foo", "key1"));
357 ASSERT_EQ(-ENOENT, remove_key_from_tmap(ioctx, "foo", "key1"));
358
359 // key should be removed
360 ASSERT_EQ(string(""), read_key_from_tmap(ioctx, "foo", "key1"));
361 }
362
363 TEST_F(LibRadosMiscPP, TmapUpdateMisorderedPP) {
364 // create tmap
365 {
366 __u8 c = CEPH_OSD_TMAP_CREATE;
367 std::string my_tmap("my_tmap");
368 bufferlist emptybl;
369
370 bufferlist tmbl;
371 ::encode(c, tmbl);
372 ::encode(my_tmap, tmbl);
373 ::encode(emptybl, tmbl);
374 ASSERT_EQ(0, ioctx.tmap_update("foo", tmbl));
375 }
376
377 // good update
378 {
379 __u8 c = CEPH_OSD_TMAP_SET;
380 bufferlist tmbl;
381 ::encode(c, tmbl);
382 ::encode("a", tmbl);
383 bufferlist blbl;
384 ::encode("old", blbl);
385 ::encode(blbl, tmbl);
386
387 ::encode(c, tmbl);
388 ::encode("b", tmbl);
389 ::encode(blbl, tmbl);
390
391 ::encode(c, tmbl);
392 ::encode("c", tmbl);
393 ::encode(blbl, tmbl);
394
395 ASSERT_EQ(0, ioctx.tmap_update("foo", tmbl));
396 }
397
398 // bad update
399 {
400 __u8 c = CEPH_OSD_TMAP_SET;
401 bufferlist tmbl;
402 ::encode(c, tmbl);
403 ::encode("b", tmbl);
404 bufferlist blbl;
405 ::encode("new", blbl);
406 ::encode(blbl, tmbl);
407
408 ::encode(c, tmbl);
409 ::encode("a", tmbl);
410 ::encode(blbl, tmbl);
411
412 ::encode(c, tmbl);
413 ::encode("c", tmbl);
414 ::encode(blbl, tmbl);
415
416 ASSERT_EQ(0, ioctx.tmap_update("foo", tmbl));
417 }
418
419 // check
420 ASSERT_EQ(string("new"), read_key_from_tmap(ioctx, "foo", "a"));
421 ASSERT_EQ(string("new"), read_key_from_tmap(ioctx, "foo", "b"));
422 ASSERT_EQ(string("new"), read_key_from_tmap(ioctx, "foo", "c"));
423
424 ASSERT_EQ(0, remove_key_from_tmap(ioctx, "foo", "a"));
425 ASSERT_EQ(string(""), read_key_from_tmap(ioctx, "foo", "a"));
426
427 ASSERT_EQ(0, remove_key_from_tmap(ioctx, "foo", "b"));
428 ASSERT_EQ(string(""), read_key_from_tmap(ioctx, "foo", "a"));
429 }
430
431 TEST_F(LibRadosMiscPP, TmapUpdateMisorderedPutPP) {
432 // create unsorted tmap
433 string h("header");
434 bufferlist bl;
435 ::encode(h, bl);
436 uint32_t n = 3;
437 ::encode(n, bl);
438 ::encode(string("b"), bl);
439 ::encode(string("bval"), bl);
440 ::encode(string("a"), bl);
441 ::encode(string("aval"), bl);
442 ::encode(string("c"), bl);
443 ::encode(string("cval"), bl);
444 bufferlist orig = bl; // tmap_put steals bl content
445 ASSERT_EQ(0, ioctx.tmap_put("foo", bl));
446
447 // check
448 bufferlist newbl;
449 ioctx.read("foo", newbl, orig.length(), 0);
450 ASSERT_EQ(orig.contents_equal(newbl), false);
451 }
452
453 TEST_F(LibRadosMiscPP, Tmap2OmapPP) {
454 // create tmap
455 bufferlist hdr;
456 hdr.append("header");
457 map<string, bufferlist> omap;
458 omap["1"].append("a");
459 omap["2"].append("b");
460 omap["3"].append("c");
461 {
462 bufferlist bl;
463 ::encode(hdr, bl);
464 ::encode(omap, bl);
465 ASSERT_EQ(0, ioctx.tmap_put("foo", bl));
466 }
467
468 // convert tmap to omap
469 ASSERT_EQ(0, ioctx.tmap_to_omap("foo", false));
470
471 // if tmap was truncated ?
472 {
473 uint64_t size;
474 time_t mtime;
475 ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
476 ASSERT_EQ(0U, size);
477 }
478
479 // if 'nullok' works
480 ASSERT_EQ(0, ioctx.tmap_to_omap("foo", true));
481 ASSERT_LE(ioctx.tmap_to_omap("foo", false), 0);
482
483 {
484 // read omap
485 bufferlist got;
486 map<string, bufferlist> m;
487 ObjectReadOperation o;
488 o.omap_get_header(&got, NULL);
489 o.omap_get_vals2("", 1024, &m, nullptr, nullptr);
490 ASSERT_EQ(0, ioctx.operate("foo", &o, NULL));
491
492 // compare header
493 ASSERT_TRUE(hdr.contents_equal(got));
494
495 // compare values
496 ASSERT_EQ(omap.size(), m.size());
497 bool same = true;
498 for (map<string, bufferlist>::iterator p = omap.begin(); p != omap.end(); ++p) {
499 map<string, bufferlist>::iterator q = m.find(p->first);
500 if (q == m.end() || !p->second.contents_equal(q->second)) {
501 same = false;
502 break;
503 }
504 }
505 ASSERT_TRUE(same);
506 }
507 }
508
509 TEST_F(LibRadosMisc, Exec) {
510 char buf[128];
511 memset(buf, 0xcc, sizeof(buf));
512 ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
513 char buf2[512];
514 int res = rados_exec(ioctx, "foo", "rbd", "get_all_features",
515 NULL, 0, buf2, sizeof(buf2));
516 ASSERT_GT(res, 0);
517 bufferlist bl;
518 bl.append(buf2, res);
519 bufferlist::iterator iter = bl.begin();
520 uint64_t all_features;
521 ::decode(all_features, iter);
522 // make sure *some* features are specified; don't care which ones
523 ASSERT_NE(all_features, (unsigned)0);
524 }
525
526 TEST_F(LibRadosMiscPP, ExecPP) {
527 bufferlist bl;
528 ASSERT_EQ(0, ioctx.write("foo", bl, 0, 0));
529 bufferlist bl2, out;
530 int r = ioctx.exec("foo", "rbd", "get_all_features", bl2, out);
531 ASSERT_EQ(0, r);
532 bufferlist::iterator iter = out.begin();
533 uint64_t all_features;
534 ::decode(all_features, iter);
535 // make sure *some* features are specified; don't care which ones
536 ASSERT_NE(all_features, (unsigned)0);
537 }
538
539 void set_completion_complete(rados_completion_t cb, void *arg)
540 {
541 bool *my_aio_complete = (bool*)arg;
542 *my_aio_complete = true;
543 }
544
545 TEST_F(LibRadosMiscPP, BadFlagsPP) {
546 unsigned badflags = CEPH_OSD_FLAG_PARALLELEXEC;
547 {
548 bufferlist bl;
549 bl.append("data");
550 ASSERT_EQ(0, ioctx.write("badfoo", bl, bl.length(), 0));
551 }
552 {
553 ASSERT_EQ(-EINVAL, ioctx.remove("badfoo", badflags));
554 }
555 }
556
557 TEST_F(LibRadosMiscPP, Operate1PP) {
558 ObjectWriteOperation o;
559 {
560 bufferlist bl;
561 o.write(0, bl);
562 }
563 std::string val1("val1");
564 {
565 bufferlist bl;
566 bl.append(val1.c_str(), val1.size() + 1);
567 o.setxattr("key1", bl);
568 o.omap_clear(); // shouldn't affect attrs!
569 }
570 ASSERT_EQ(0, ioctx.operate("foo", &o));
571
572 ObjectWriteOperation empty;
573 ASSERT_EQ(0, ioctx.operate("foo", &empty));
574
575 {
576 bufferlist bl;
577 ASSERT_GT(ioctx.getxattr("foo", "key1", bl), 0);
578 ASSERT_EQ(0, strcmp(bl.c_str(), val1.c_str()));
579 }
580 ObjectWriteOperation o2;
581 {
582 bufferlist bl;
583 bl.append(val1);
584 o2.cmpxattr("key1", CEPH_OSD_CMPXATTR_OP_EQ, bl);
585 o2.rmxattr("key1");
586 }
587 ASSERT_EQ(-ECANCELED, ioctx.operate("foo", &o2));
588 ObjectWriteOperation o3;
589 {
590 bufferlist bl;
591 bl.append(val1);
592 o3.cmpxattr("key1", CEPH_OSD_CMPXATTR_OP_EQ, bl);
593 }
594 ASSERT_EQ(-ECANCELED, ioctx.operate("foo", &o3));
595 }
596
597 TEST_F(LibRadosMiscPP, Operate2PP) {
598 ObjectWriteOperation o;
599 {
600 bufferlist bl;
601 bl.append("abcdefg");
602 o.write(0, bl);
603 }
604 std::string val1("val1");
605 {
606 bufferlist bl;
607 bl.append(val1.c_str(), val1.size() + 1);
608 o.setxattr("key1", bl);
609 o.truncate(0);
610 }
611 ASSERT_EQ(0, ioctx.operate("foo", &o));
612 uint64_t size;
613 time_t mtime;
614 ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
615 ASSERT_EQ(0U, size);
616 }
617
618 TEST_F(LibRadosMiscPP, BigObjectPP) {
619 bufferlist bl;
620 bl.append("abcdefg");
621 ASSERT_EQ(0, ioctx.write("foo", bl, bl.length(), 0));
622
623 {
624 ObjectWriteOperation o;
625 o.truncate(500000000000ull);
626 ASSERT_EQ(-EFBIG, ioctx.operate("foo", &o));
627 }
628 {
629 ObjectWriteOperation o;
630 o.zero(500000000000ull, 1);
631 ASSERT_EQ(-EFBIG, ioctx.operate("foo", &o));
632 }
633 {
634 ObjectWriteOperation o;
635 o.zero(1, 500000000000ull);
636 ASSERT_EQ(-EFBIG, ioctx.operate("foo", &o));
637 }
638 {
639 ObjectWriteOperation o;
640 o.zero(500000000000ull, 500000000000ull);
641 ASSERT_EQ(-EFBIG, ioctx.operate("foo", &o));
642 }
643
644 #ifdef __LP64__
645 // this test only works on 64-bit platforms
646 ASSERT_EQ(-EFBIG, ioctx.write("foo", bl, bl.length(), 500000000000ull));
647 #endif
648 }
649
650 TEST_F(LibRadosMiscPP, AioOperatePP) {
651 bool my_aio_complete = false;
652 AioCompletion *my_completion = cluster.aio_create_completion(
653 (void*)&my_aio_complete, set_completion_complete, NULL);
654 AioCompletion *my_completion_null = NULL;
655 ASSERT_NE(my_completion, my_completion_null);
656
657 ObjectWriteOperation o;
658 {
659 bufferlist bl;
660 o.write(0, bl);
661 }
662 std::string val1("val1");
663 {
664 bufferlist bl;
665 bl.append(val1.c_str(), val1.size() + 1);
666 o.setxattr("key1", bl);
667 bufferlist bl2;
668 char buf2[1024];
669 memset(buf2, 0xdd, sizeof(buf2));
670 bl2.append(buf2, sizeof(buf2));
671 o.append(bl2);
672 }
673 ASSERT_EQ(0, ioctx.aio_operate("foo", my_completion, &o));
674 ASSERT_EQ(0, my_completion->wait_for_complete_and_cb());
675 ASSERT_EQ(my_aio_complete, true);
676 my_completion->release();
677
678 uint64_t size;
679 time_t mtime;
680 ASSERT_EQ(0, ioctx.stat("foo", &size, &mtime));
681 ASSERT_EQ(1024U, size);
682 }
683
684 TEST_F(LibRadosMiscPP, AssertExistsPP) {
685 char buf[64];
686 memset(buf, 0xcc, sizeof(buf));
687 bufferlist bl;
688 bl.append(buf, sizeof(buf));
689
690 ObjectWriteOperation op;
691 op.assert_exists();
692 op.write(0, bl);
693 ASSERT_EQ(-ENOENT, ioctx.operate("asdffoo", &op));
694 ASSERT_EQ(0, ioctx.create("asdffoo", true));
695 ASSERT_EQ(0, ioctx.operate("asdffoo", &op));
696 ASSERT_EQ(-EEXIST, ioctx.create("asdffoo", true));
697 }
698
699 TEST_F(LibRadosMiscPP, AssertVersionPP) {
700 char buf[64];
701 memset(buf, 0xcc, sizeof(buf));
702 bufferlist bl;
703 bl.append(buf, sizeof(buf));
704
705 // Create test object...
706 ASSERT_EQ(0, ioctx.create("asdfbar", true));
707 // ...then write it again to guarantee that the
708 // (unsigned) version must be at least 1 (not 0)
709 // since we want to decrement it by 1 later.
710 ASSERT_EQ(0, ioctx.write_full("asdfbar", bl));
711
712 uint64_t v = ioctx.get_last_version();
713 ObjectWriteOperation op1;
714 op1.assert_version(v+1);
715 op1.write(0, bl);
716 ASSERT_EQ(-EOVERFLOW, ioctx.operate("asdfbar", &op1));
717 ObjectWriteOperation op2;
718 op2.assert_version(v-1);
719 op2.write(0, bl);
720 ASSERT_EQ(-ERANGE, ioctx.operate("asdfbar", &op2));
721 ObjectWriteOperation op3;
722 op3.assert_version(v);
723 op3.write(0, bl);
724 ASSERT_EQ(0, ioctx.operate("asdfbar", &op3));
725 }
726
727 TEST_F(LibRadosMiscPP, BigAttrPP) {
728 char buf[64];
729 memset(buf, 0xcc, sizeof(buf));
730 bufferlist bl;
731 bl.append(buf, sizeof(buf));
732
733 ASSERT_EQ(0, ioctx.create("foo", true));
734
735 bufferlist got;
736
737 cout << "osd_max_attr_size = " << g_conf->osd_max_attr_size << std::endl;
738 if (g_conf->osd_max_attr_size) {
739 bl.clear();
740 got.clear();
741 bl.append(buffer::create(g_conf->osd_max_attr_size));
742 ASSERT_EQ(0, ioctx.setxattr("foo", "one", bl));
743 ASSERT_EQ((int)bl.length(), ioctx.getxattr("foo", "one", got));
744 ASSERT_TRUE(bl.contents_equal(got));
745
746 bl.clear();
747 bl.append(buffer::create(g_conf->osd_max_attr_size+1));
748 ASSERT_EQ(-EFBIG, ioctx.setxattr("foo", "one", bl));
749 } else {
750 cout << "osd_max_attr_size == 0; skipping test" << std::endl;
751 }
752
753 for (int i=0; i<1000; i++) {
754 bl.clear();
755 got.clear();
756 bl.append(buffer::create(MIN(g_conf->osd_max_attr_size, 1024)));
757 char n[10];
758 snprintf(n, sizeof(n), "a%d", i);
759 ASSERT_EQ(0, ioctx.setxattr("foo", n, bl));
760 ASSERT_EQ((int)bl.length(), ioctx.getxattr("foo", n, got));
761 ASSERT_TRUE(bl.contents_equal(got));
762 }
763 }
764
765 TEST_F(LibRadosMiscPP, CopyPP) {
766 bufferlist bl, x;
767 bl.append("hi there");
768 x.append("bar");
769
770 // small object
771 bufferlist blc = bl;
772 bufferlist xc = x;
773 ASSERT_EQ(0, ioctx.write_full("foo", blc));
774 ASSERT_EQ(0, ioctx.setxattr("foo", "myattr", xc));
775
776 version_t uv = ioctx.get_last_version();
777 {
778 // pass future version
779 ObjectWriteOperation op;
780 op.copy_from2("foo", ioctx, uv + 1, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
781 ASSERT_EQ(-EOVERFLOW, ioctx.operate("foo.copy", &op));
782 }
783 {
784 // pass old version
785 ObjectWriteOperation op;
786 op.copy_from2("foo", ioctx, uv - 1, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
787 ASSERT_EQ(-ERANGE, ioctx.operate("foo.copy", &op));
788 }
789 {
790 ObjectWriteOperation op;
791 op.copy_from2("foo", ioctx, uv, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
792 ASSERT_EQ(0, ioctx.operate("foo.copy", &op));
793
794 bufferlist bl2, x2;
795 ASSERT_EQ((int)bl.length(), ioctx.read("foo.copy", bl2, 10000, 0));
796 ASSERT_TRUE(bl.contents_equal(bl2));
797 ASSERT_EQ((int)x.length(), ioctx.getxattr("foo.copy", "myattr", x2));
798 ASSERT_TRUE(x.contents_equal(x2));
799 }
800
801 // small object without a version
802 {
803 ObjectWriteOperation op;
804 op.copy_from2("foo", ioctx, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
805 ASSERT_EQ(0, ioctx.operate("foo.copy2", &op));
806
807 bufferlist bl2, x2;
808 ASSERT_EQ((int)bl.length(), ioctx.read("foo.copy2", bl2, 10000, 0));
809 ASSERT_TRUE(bl.contents_equal(bl2));
810 ASSERT_EQ((int)x.length(), ioctx.getxattr("foo.copy2", "myattr", x2));
811 ASSERT_TRUE(x.contents_equal(x2));
812 }
813
814 // do a big object
815 bl.append(buffer::create(g_conf->osd_copyfrom_max_chunk * 3));
816 bl.zero();
817 bl.append("tail");
818 blc = bl;
819 xc = x;
820 ASSERT_EQ(0, ioctx.write_full("big", blc));
821 ASSERT_EQ(0, ioctx.setxattr("big", "myattr", xc));
822
823 {
824 ObjectWriteOperation op;
825 op.copy_from2("big", ioctx, ioctx.get_last_version(), LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
826 ASSERT_EQ(0, ioctx.operate("big.copy", &op));
827
828 bufferlist bl2, x2;
829 ASSERT_EQ((int)bl.length(), ioctx.read("big.copy", bl2, bl.length(), 0));
830 ASSERT_TRUE(bl.contents_equal(bl2));
831 ASSERT_EQ((int)x.length(), ioctx.getxattr("foo.copy", "myattr", x2));
832 ASSERT_TRUE(x.contents_equal(x2));
833 }
834
835 {
836 ObjectWriteOperation op;
837 op.copy_from2("big", ioctx, 0, LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL);
838 ASSERT_EQ(0, ioctx.operate("big.copy2", &op));
839
840 bufferlist bl2, x2;
841 ASSERT_EQ((int)bl.length(), ioctx.read("big.copy2", bl2, bl.length(), 0));
842 ASSERT_TRUE(bl.contents_equal(bl2));
843 ASSERT_EQ((int)x.length(), ioctx.getxattr("foo.copy2", "myattr", x2));
844 ASSERT_TRUE(x.contents_equal(x2));
845 }
846 }
847
848 class LibRadosTwoPoolsECPP : public RadosTestECPP
849 {
850 public:
851 LibRadosTwoPoolsECPP() {};
852 ~LibRadosTwoPoolsECPP() override {};
853 protected:
854 static void SetUpTestCase() {
855 pool_name = get_temp_pool_name();
856 ASSERT_EQ("", create_one_ec_pool_pp(pool_name, s_cluster));
857 src_pool_name = get_temp_pool_name();
858 ASSERT_EQ(0, s_cluster.pool_create(src_pool_name.c_str()));
859 }
860 static void TearDownTestCase() {
861 ASSERT_EQ(0, s_cluster.pool_delete(src_pool_name.c_str()));
862 ASSERT_EQ(0, destroy_one_ec_pool_pp(pool_name, s_cluster));
863 }
864 static std::string src_pool_name;
865
866 void SetUp() override {
867 RadosTestECPP::SetUp();
868 ASSERT_EQ(0, cluster.ioctx_create(src_pool_name.c_str(), src_ioctx));
869 src_ioctx.set_namespace(nspace);
870 }
871 void TearDown() override {
872 // wait for maps to settle before next test
873 cluster.wait_for_latest_osdmap();
874
875 RadosTestECPP::TearDown();
876
877 cleanup_default_namespace(src_ioctx);
878 cleanup_namespace(src_ioctx, nspace);
879
880 src_ioctx.close();
881 }
882
883 librados::IoCtx src_ioctx;
884 };
885 std::string LibRadosTwoPoolsECPP::src_pool_name;
886
887 //copy_from between ecpool and no-ecpool.
888 TEST_F(LibRadosTwoPoolsECPP, CopyFrom) {
889 bufferlist z;
890 z.append_zero(4194304*2);
891 bufferlist b;
892 b.append("copyfrom");
893
894 // create big object w/ omapheader
895 {
896 ASSERT_EQ(0, src_ioctx.write_full("foo", z));
897 ASSERT_EQ(0, src_ioctx.omap_set_header("foo", b));
898 version_t uv = src_ioctx.get_last_version();
899 ObjectWriteOperation op;
900 op.copy_from("foo", src_ioctx, uv);
901 ASSERT_EQ(-EOPNOTSUPP, ioctx.operate("foo.copy", &op));
902 }
903
904 // same with small object
905 {
906 ASSERT_EQ(0, src_ioctx.omap_set_header("bar", b));
907 version_t uv = src_ioctx.get_last_version();
908 ObjectWriteOperation op;
909 op.copy_from("bar", src_ioctx, uv);
910 ASSERT_EQ(-EOPNOTSUPP, ioctx.operate("bar.copy", &op));
911 }
912 }
913
914 TEST_F(LibRadosMiscPP, CopyScrubPP) {
915 bufferlist inbl, bl, x;
916 for (int i=0; i<100; ++i)
917 x.append("barrrrrrrrrrrrrrrrrrrrrrrrrr");
918 bl.append(buffer::create(g_conf->osd_copyfrom_max_chunk * 3));
919 bl.zero();
920 bl.append("tail");
921 bufferlist cbl;
922
923 map<string, bufferlist> to_set;
924 for (int i=0; i<1000; ++i)
925 to_set[string("foo") + stringify(i)] = x;
926
927 // small
928 cbl = x;
929 ASSERT_EQ(0, ioctx.write_full("small", cbl));
930 ASSERT_EQ(0, ioctx.setxattr("small", "myattr", x));
931
932 // big
933 cbl = bl;
934 ASSERT_EQ(0, ioctx.write_full("big", cbl));
935
936 // without header
937 cbl = bl;
938 ASSERT_EQ(0, ioctx.write_full("big2", cbl));
939 ASSERT_EQ(0, ioctx.setxattr("big2", "myattr", x));
940 ASSERT_EQ(0, ioctx.setxattr("big2", "myattr2", x));
941 ASSERT_EQ(0, ioctx.omap_set("big2", to_set));
942
943 // with header
944 cbl = bl;
945 ASSERT_EQ(0, ioctx.write_full("big3", cbl));
946 ASSERT_EQ(0, ioctx.omap_set_header("big3", x));
947 ASSERT_EQ(0, ioctx.omap_set("big3", to_set));
948
949 // deep scrub to ensure digests are in place
950 {
951 for (int i=0; i<10; ++i) {
952 ostringstream ss;
953 ss << "{\"prefix\": \"pg deep-scrub\", \"pgid\": \""
954 << ioctx.get_id() << "." << i
955 << "\"}";
956 cluster.mon_command(ss.str(), inbl, NULL, NULL);
957 }
958
959 // give it a few seconds to go. this is sloppy but is usually enough time
960 cout << "waiting for initial deep scrubs..." << std::endl;
961 sleep(30);
962 cout << "done waiting, doing copies" << std::endl;
963 }
964
965 {
966 ObjectWriteOperation op;
967 op.copy_from("small", ioctx, 0);
968 ASSERT_EQ(0, ioctx.operate("small.copy", &op));
969 }
970
971 {
972 ObjectWriteOperation op;
973 op.copy_from("big", ioctx, 0);
974 ASSERT_EQ(0, ioctx.operate("big.copy", &op));
975 }
976
977 {
978 ObjectWriteOperation op;
979 op.copy_from("big2", ioctx, 0);
980 ASSERT_EQ(0, ioctx.operate("big2.copy", &op));
981 }
982
983 {
984 ObjectWriteOperation op;
985 op.copy_from("big3", ioctx, 0);
986 ASSERT_EQ(0, ioctx.operate("big3.copy", &op));
987 }
988
989 // deep scrub to ensure digests are correct
990 {
991 for (int i=0; i<10; ++i) {
992 ostringstream ss;
993 ss << "{\"prefix\": \"pg deep-scrub\", \"pgid\": \""
994 << ioctx.get_id() << "." << i
995 << "\"}";
996 cluster.mon_command(ss.str(), inbl, NULL, NULL);
997 }
998
999 // give it a few seconds to go. this is sloppy but is usually enough time
1000 cout << "waiting for final deep scrubs..." << std::endl;
1001 sleep(30);
1002 cout << "done waiting" << std::endl;
1003 }
1004 }
1005
1006 TEST_F(LibRadosMiscPP, WriteSamePP) {
1007 bufferlist bl;
1008 char buf[128];
1009 bufferlist fl;
1010 char full[128 * 4];
1011 char *cmp;
1012
1013 /* zero the full range before using writesame */
1014 memset(full, 0, sizeof(full));
1015 fl.append(full, sizeof(full));
1016 ASSERT_EQ(0, ioctx.write("ws", fl, fl.length(), 0));
1017
1018 memset(buf, 0xcc, sizeof(buf));
1019 bl.clear();
1020 bl.append(buf, sizeof(buf));
1021 /* write the same buf four times */
1022 ASSERT_EQ(0, ioctx.writesame("ws", bl, sizeof(full), 0));
1023
1024 /* read back the full buffer and confirm that it matches */
1025 fl.clear();
1026 fl.append(full, sizeof(full));
1027 ASSERT_EQ((int)fl.length(), ioctx.read("ws", fl, fl.length(), 0));
1028
1029 for (cmp = fl.c_str(); cmp < fl.c_str() + fl.length(); cmp += sizeof(buf)) {
1030 ASSERT_EQ(0, memcmp(cmp, buf, sizeof(buf)));
1031 }
1032
1033 /* write_len not a multiple of data_len should throw error */
1034 bl.clear();
1035 bl.append(buf, sizeof(buf));
1036 ASSERT_EQ(-EINVAL, ioctx.writesame("ws", bl, (sizeof(buf) * 4) - 1, 0));
1037 ASSERT_EQ(-EINVAL,
1038 ioctx.writesame("ws", bl, bl.length() / 2, 0));
1039 /* write_len = data_len, i.e. same as write() */
1040 ASSERT_EQ(0, ioctx.writesame("ws", bl, sizeof(buf), 0));
1041 bl.clear();
1042 ASSERT_EQ(-EINVAL,
1043 ioctx.writesame("ws", bl, sizeof(buf), 0));
1044 }
1045
1046 TEST_F(LibRadosMisc, WriteSame) {
1047 char buf[128];
1048 char full[128 * 4];
1049 char *cmp;
1050
1051 /* zero the full range before using writesame */
1052 memset(full, 0, sizeof(full));
1053 ASSERT_EQ(0, rados_write(ioctx, "ws", full, sizeof(full), 0));
1054
1055 memset(buf, 0xcc, sizeof(buf));
1056 /* write the same buf four times */
1057 ASSERT_EQ(0, rados_writesame(ioctx, "ws", buf, sizeof(buf), sizeof(full), 0));
1058
1059 /* read back the full buffer and confirm that it matches */
1060 ASSERT_EQ((int)sizeof(full), rados_read(ioctx, "ws", full, sizeof(full), 0));
1061
1062 for (cmp = full; cmp < full + sizeof(full); cmp += sizeof(buf)) {
1063 ASSERT_EQ(0, memcmp(cmp, buf, sizeof(buf)));
1064 }
1065
1066 /* write_len not a multiple of data_len should throw error */
1067 ASSERT_EQ(-EINVAL, rados_writesame(ioctx, "ws", buf, sizeof(buf),
1068 (sizeof(buf) * 4) - 1, 0));
1069 ASSERT_EQ(-EINVAL,
1070 rados_writesame(ioctx, "ws", buf, sizeof(buf), sizeof(buf) / 2, 0));
1071 ASSERT_EQ(-EINVAL,
1072 rados_writesame(ioctx, "ws", buf, 0, sizeof(buf), 0));
1073 /* write_len = data_len, i.e. same as rados_write() */
1074 ASSERT_EQ(0, rados_writesame(ioctx, "ws", buf, sizeof(buf), sizeof(buf), 0));
1075 }
1076
1077 template <typename T>
1078 class LibRadosChecksum : public LibRadosMiscPP {
1079 public:
1080 typedef typename T::alg_t alg_t;
1081 typedef typename T::value_t value_t;
1082 typedef typename alg_t::init_value_t init_value_t;
1083
1084 static const rados_checksum_type_t type = T::type;
1085
1086 bufferlist content_bl;
1087
1088 using LibRadosMiscPP::SetUpTestCase;
1089 using LibRadosMiscPP::TearDownTestCase;
1090
1091 void SetUp() override {
1092 LibRadosMiscPP::SetUp();
1093
1094 std::string content(4096, '\0');
1095 for (size_t i = 0; i < content.length(); ++i) {
1096 content[i] = static_cast<char>(rand() % (126 - 33) + 33);
1097 }
1098 content_bl.append(content);
1099 ASSERT_EQ(0, ioctx.write("foo", content_bl, content_bl.length(), 0));
1100 }
1101 };
1102
1103 template <rados_checksum_type_t _type, typename AlgT, typename ValueT>
1104 class LibRadosChecksumParams {
1105 public:
1106 typedef AlgT alg_t;
1107 typedef ValueT value_t;
1108 static const rados_checksum_type_t type = _type;
1109 };
1110
1111 typedef ::testing::Types<
1112 LibRadosChecksumParams<LIBRADOS_CHECKSUM_TYPE_XXHASH32,
1113 Checksummer::xxhash32, uint32_t>,
1114 LibRadosChecksumParams<LIBRADOS_CHECKSUM_TYPE_XXHASH64,
1115 Checksummer::xxhash64, uint64_t>,
1116 LibRadosChecksumParams<LIBRADOS_CHECKSUM_TYPE_CRC32C,
1117 Checksummer::crc32c, uint32_t>
1118 > LibRadosChecksumTypes;
1119
1120 TYPED_TEST_CASE(LibRadosChecksum, LibRadosChecksumTypes);
1121
1122 TYPED_TEST(LibRadosChecksum, Subset) {
1123 uint32_t chunk_size = 1024;
1124 uint32_t csum_count = this->content_bl.length() / chunk_size;
1125
1126 typename TestFixture::init_value_t init_value = -1;
1127 bufferlist init_value_bl;
1128 ::encode(init_value, init_value_bl);
1129
1130 std::vector<bufferlist> checksum_bls(csum_count);
1131 std::vector<int> checksum_rvals(csum_count);
1132
1133 // individual checksum ops for each chunk
1134 ObjectReadOperation op;
1135 for (uint32_t i = 0; i < csum_count; ++i) {
1136 op.checksum(TestFixture::type, init_value_bl, i * chunk_size, chunk_size,
1137 0, &checksum_bls[i], &checksum_rvals[i]);
1138 }
1139 ASSERT_EQ(0, this->ioctx.operate("foo", &op, NULL));
1140
1141 for (uint32_t i = 0; i < csum_count; ++i) {
1142 ASSERT_EQ(0, checksum_rvals[i]);
1143
1144 auto bl_it = checksum_bls[i].begin();
1145 uint32_t count;
1146 ::decode(count, bl_it);
1147 ASSERT_EQ(1U, count);
1148
1149 typename TestFixture::value_t value;
1150 ::decode(value, bl_it);
1151
1152 bufferlist content_sub_bl;
1153 content_sub_bl.substr_of(this->content_bl, i * chunk_size, chunk_size);
1154
1155 typename TestFixture::value_t expected_value;
1156 bufferptr expected_value_bp = buffer::create_static(
1157 sizeof(expected_value), reinterpret_cast<char*>(&expected_value));
1158 Checksummer::template calculate<typename TestFixture::alg_t>(
1159 init_value, chunk_size, 0, chunk_size, content_sub_bl,
1160 &expected_value_bp);
1161 ASSERT_EQ(expected_value, value);
1162 }
1163 }
1164
1165 TYPED_TEST(LibRadosChecksum, Chunked) {
1166 uint32_t chunk_size = 1024;
1167 uint32_t csum_count = this->content_bl.length() / chunk_size;
1168
1169 typename TestFixture::init_value_t init_value = -1;
1170 bufferlist init_value_bl;
1171 ::encode(init_value, init_value_bl);
1172
1173 bufferlist checksum_bl;
1174 int checksum_rval;
1175
1176 // single op with chunked checksum results
1177 ObjectReadOperation op;
1178 op.checksum(TestFixture::type, init_value_bl, 0, this->content_bl.length(),
1179 chunk_size, &checksum_bl, &checksum_rval);
1180 ASSERT_EQ(0, this->ioctx.operate("foo", &op, NULL));
1181 ASSERT_EQ(0, checksum_rval);
1182
1183 auto bl_it = checksum_bl.begin();
1184 uint32_t count;
1185 ::decode(count, bl_it);
1186 ASSERT_EQ(csum_count, count);
1187
1188 std::vector<typename TestFixture::value_t> expected_values(csum_count);
1189 bufferptr expected_values_bp = buffer::create_static(
1190 csum_count * sizeof(typename TestFixture::value_t),
1191 reinterpret_cast<char*>(&expected_values[0]));
1192
1193 Checksummer::template calculate<typename TestFixture::alg_t>(
1194 init_value, chunk_size, 0, this->content_bl.length(), this->content_bl,
1195 &expected_values_bp);
1196
1197 for (uint32_t i = 0; i < csum_count; ++i) {
1198 typename TestFixture::value_t value;
1199 ::decode(value, bl_it);
1200 ASSERT_EQ(expected_values[i], value);
1201 }
1202 }
1203
1204 TEST_F(LibRadosMiscPP, CmpExtPP) {
1205 bufferlist cmp_bl, bad_cmp_bl, write_bl;
1206 char stored_str[] = "1234567891";
1207 char mismatch_str[] = "1234577777";
1208
1209 write_bl.append(stored_str);
1210 ioctx.write("cmpextpp", write_bl, write_bl.length(), 0);
1211 cmp_bl.append(stored_str);
1212 ASSERT_EQ(0, ioctx.cmpext("cmpextpp", 0, cmp_bl));
1213
1214 bad_cmp_bl.append(mismatch_str);
1215 ASSERT_EQ(-MAX_ERRNO - 5, ioctx.cmpext("cmpextpp", 0, bad_cmp_bl));
1216 }
1217
1218 TEST_F(LibRadosMisc, CmpExt) {
1219 bufferlist cmp_bl, bad_cmp_bl, write_bl;
1220 char stored_str[] = "1234567891";
1221 char mismatch_str[] = "1234577777";
1222
1223 ASSERT_EQ(0,
1224 rados_write(ioctx, "cmpextpp", stored_str, sizeof(stored_str), 0));
1225
1226 ASSERT_EQ(0,
1227 rados_cmpext(ioctx, "cmpextpp", stored_str, sizeof(stored_str), 0));
1228
1229 ASSERT_EQ(-MAX_ERRNO - 5,
1230 rados_cmpext(ioctx, "cmpextpp", mismatch_str, sizeof(mismatch_str), 0));
1231 }