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