Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / test / librados / test.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*
2 // vim: ts=8 sw=2 smarttab
3
4 #include "include/rados/librados.h"
5 #include "include/rados/librados.hpp"
6 #include "test/librados/test.h"
7
8 #include "include/stringify.h"
9 #include "common/ceph_context.h"
10 #include "common/config.h"
11
12 #include <errno.h>
13 #include <sstream>
14 #include <stdlib.h>
15 #include <string>
16 #include <time.h>
17 #include <unistd.h>
18 #include <iostream>
19 #include "gtest/gtest.h"
20
21 using namespace librados;
22
23 std::string get_temp_pool_name(const std::string &prefix)
24 {
25   char hostname[80];
26   char out[160];
27   memset(hostname, 0, sizeof(hostname));
28   memset(out, 0, sizeof(out));
29   gethostname(hostname, sizeof(hostname)-1);
30   static int num = 1;
31   snprintf(out, sizeof(out), "%s-%d-%d", hostname, getpid(), num);
32   num++;
33   return prefix + out;
34 }
35
36
37 std::string create_one_pool(
38     const std::string &pool_name, rados_t *cluster, uint32_t pg_num)
39 {
40   std::string err_str = connect_cluster(cluster);
41   if (err_str.length())
42     return err_str;
43
44   int ret = rados_pool_create(*cluster, pool_name.c_str());
45   if (ret) {
46     rados_shutdown(*cluster);
47     std::ostringstream oss;
48     oss << "create_one_pool(" << pool_name << ") failed with error " << ret;
49     return oss.str();
50   }
51
52   rados_ioctx_t ioctx;
53   ret = rados_ioctx_create(*cluster, pool_name.c_str(), &ioctx);
54   if (ret < 0) {
55     rados_shutdown(*cluster);
56     std::ostringstream oss;
57     oss << "rados_ioctx_create(" << pool_name << ") failed with error " << ret;
58     return oss.str();
59   }
60
61   rados_application_enable(ioctx, "rados", 1);
62   rados_ioctx_destroy(ioctx);
63   return "";
64 }
65
66 int destroy_ec_profile(rados_t *cluster,
67                        const std::string& pool_name,
68                        std::ostream &oss)
69 {
70   char buf[1000];
71   snprintf(buf, sizeof(buf),
72            "{\"prefix\": \"osd erasure-code-profile rm\", \"name\": \"testprofile-%s\"}",
73            pool_name.c_str());
74   char *cmd[2];
75   cmd[0] = buf;
76   cmd[1] = NULL;
77   int ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0, NULL,
78                               0, NULL, 0);
79   if (ret)
80     oss << "rados_mon_command: erasure-code-profile rm testprofile-"
81         << pool_name << " failed with error " << ret;
82   return ret;
83 }
84
85 int destroy_ruleset(rados_t *cluster,
86                     std::string ruleset,
87                     std::ostream &oss)
88 {
89   char *cmd[2];
90   std::string tmp = ("{\"prefix\": \"osd crush rule rm\", \"name\":\"" +
91                      ruleset + "\"}");
92   cmd[0] = (char*)tmp.c_str();
93   cmd[1] = NULL;
94   int ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0);
95   if (ret)
96     oss << "rados_mon_command: osd crush rule rm " + ruleset + " failed with error " << ret;
97   return ret;
98 }
99
100 int destroy_ec_profile_and_ruleset(rados_t *cluster,
101                                    std::string ruleset,
102                                    std::ostream &oss)
103 {
104   int ret;
105   ret = destroy_ec_profile(cluster, ruleset, oss);
106   if (ret)
107     return ret;
108   return destroy_ruleset(cluster, ruleset, oss);
109 }
110
111
112 std::string create_one_ec_pool(const std::string &pool_name, rados_t *cluster)
113 {
114   std::string err = connect_cluster(cluster);
115   if (err.length())
116     return err;
117
118   std::ostringstream oss;
119   int ret = destroy_ec_profile_and_ruleset(cluster, pool_name, oss);
120   if (ret) {
121     rados_shutdown(*cluster);
122     return oss.str();
123   }
124     
125   char *cmd[2];
126   cmd[1] = NULL;
127
128   std::string profile_create = "{\"prefix\": \"osd erasure-code-profile set\", \"name\": \"testprofile-" + pool_name + "\", \"profile\": [ \"k=2\", \"m=1\", \"crush-failure-domain=osd\"]}";
129   cmd[0] = (char *)profile_create.c_str();
130   ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0);
131   if (ret) {
132     rados_shutdown(*cluster);
133     oss << "rados_mon_command erasure-code-profile set name:testprofile-" << pool_name << " failed with error " << ret;
134     return oss.str();
135   }
136
137   std::string cmdstr = "{\"prefix\": \"osd pool create\", \"pool\": \"" +
138      pool_name + "\", \"pool_type\":\"erasure\", \"pg_num\":8, \"pgp_num\":8, \"erasure_code_profile\":\"testprofile-" + pool_name + "\"}";
139   cmd[0] = (char *)cmdstr.c_str();
140   ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0, NULL, 0, NULL, 0);
141   if (ret) {
142     destroy_ec_profile(cluster, pool_name, oss);
143     rados_shutdown(*cluster);
144     oss << "rados_mon_command osd pool create failed with error " << ret;
145     return oss.str();
146   }
147
148   rados_wait_for_latest_osdmap(*cluster);
149   return "";
150 }
151
152 std::string create_one_pool_pp(const std::string &pool_name, Rados &cluster)
153 {
154     return create_one_pool_pp(pool_name, cluster, {});
155 }
156 std::string create_one_pool_pp(const std::string &pool_name, Rados &cluster,
157                                const std::map<std::string, std::string> &config)
158 {
159   std::string err = connect_cluster_pp(cluster, config);
160   if (err.length())
161     return err;
162   int ret = cluster.pool_create(pool_name.c_str());
163   if (ret) {
164     cluster.shutdown();
165     std::ostringstream oss;
166     oss << "cluster.pool_create(" << pool_name << ") failed with error " << ret;
167     return oss.str();
168   }
169
170   IoCtx ioctx;
171   ret = cluster.ioctx_create(pool_name.c_str(), ioctx);
172   if (ret < 0) {
173     cluster.shutdown();
174     std::ostringstream oss;
175     oss << "cluster.ioctx_create(" << pool_name << ") failed with error "
176         << ret;
177     return oss.str();
178   }
179   ioctx.application_enable("rados", true);
180   return "";
181 }
182
183 int destroy_ruleset_pp(Rados &cluster,
184                        std::string ruleset,
185                        std::ostream &oss)
186 {
187   bufferlist inbl;
188   int ret = cluster.mon_command("{\"prefix\": \"osd crush rule rm\", \"name\":\"" +
189                                 ruleset + "\"}", inbl, NULL, NULL);
190   if (ret)
191     oss << "mon_command: osd crush rule rm " + ruleset + " failed with error " << ret << std::endl;
192   return ret;
193 }
194
195 int destroy_ec_profile_pp(Rados &cluster, const std::string& pool_name,
196                           std::ostream &oss)
197 {
198   bufferlist inbl;
199   int ret = cluster.mon_command("{\"prefix\": \"osd erasure-code-profile rm\", \"name\": \"testprofile-" + pool_name + "\"}",
200                                 inbl, NULL, NULL);
201   if (ret)
202     oss << "mon_command: osd erasure-code-profile rm testprofile-" << pool_name << " failed with error " << ret << std::endl;
203   return ret;
204 }
205
206 int destroy_ec_profile_and_ruleset_pp(Rados &cluster,
207                                       std::string ruleset,
208                                       std::ostream &oss)
209 {
210   int ret;
211   ret = destroy_ec_profile_pp(cluster, ruleset, oss);
212   if (ret)
213     return ret;
214   return destroy_ruleset_pp(cluster, ruleset, oss);
215 }
216
217 std::string create_one_ec_pool_pp(const std::string &pool_name, Rados &cluster)
218 {
219   std::string err = connect_cluster_pp(cluster);
220   if (err.length())
221     return err;
222
223   std::ostringstream oss;
224   int ret = destroy_ec_profile_and_ruleset_pp(cluster, pool_name, oss);
225   if (ret) {
226     cluster.shutdown();
227     return oss.str();
228   }
229
230   bufferlist inbl;
231   ret = cluster.mon_command(
232     "{\"prefix\": \"osd erasure-code-profile set\", \"name\": \"testprofile-" + pool_name + "\", \"profile\": [ \"k=2\", \"m=1\", \"crush-failure-domain=osd\"]}",
233     inbl, NULL, NULL);
234   if (ret) {
235     cluster.shutdown();
236     oss << "mon_command erasure-code-profile set name:testprofile-" << pool_name << " failed with error " << ret;
237     return oss.str();
238   }
239     
240   ret = cluster.mon_command(
241     "{\"prefix\": \"osd pool create\", \"pool\": \"" + pool_name + "\", \"pool_type\":\"erasure\", \"pg_num\":8, \"pgp_num\":8, \"erasure_code_profile\":\"testprofile-" + pool_name + "\"}",
242     inbl, NULL, NULL);
243   if (ret) {
244     bufferlist inbl;
245     destroy_ec_profile_pp(cluster, pool_name, oss);
246     cluster.shutdown();
247     oss << "mon_command osd pool create pool:" << pool_name << " pool_type:erasure failed with error " << ret;
248     return oss.str();
249   }
250
251   cluster.wait_for_latest_osdmap();
252   return "";
253 }
254
255 std::string connect_cluster(rados_t *cluster)
256 {
257   char *id = getenv("CEPH_CLIENT_ID");
258   if (id) std::cerr << "Client id is: " << id << std::endl;
259
260   int ret;
261   ret = rados_create(cluster, NULL);
262   if (ret) {
263     std::ostringstream oss;
264     oss << "rados_create failed with error " << ret;
265     return oss.str();
266   }
267   ret = rados_conf_read_file(*cluster, NULL);
268   if (ret) {
269     rados_shutdown(*cluster);
270     std::ostringstream oss;
271     oss << "rados_conf_read_file failed with error " << ret;
272     return oss.str();
273   }
274   rados_conf_parse_env(*cluster, NULL);
275   ret = rados_connect(*cluster);
276   if (ret) {
277     rados_shutdown(*cluster);
278     std::ostringstream oss;
279     oss << "rados_connect failed with error " << ret;
280     return oss.str();
281   }
282   return "";
283 }
284
285 std::string connect_cluster_pp(librados::Rados &cluster)
286 {
287   return connect_cluster_pp(cluster, {});
288 }
289
290 std::string connect_cluster_pp(librados::Rados &cluster,
291                                const std::map<std::string, std::string> &config)
292 {
293   char *id = getenv("CEPH_CLIENT_ID");
294   if (id) std::cerr << "Client id is: " << id << std::endl;
295
296   int ret;
297   ret = cluster.init(id);
298   if (ret) {
299     std::ostringstream oss;
300     oss << "cluster.init failed with error " << ret;
301     return oss.str();
302   }
303   ret = cluster.conf_read_file(NULL);
304   if (ret) {
305     cluster.shutdown();
306     std::ostringstream oss;
307     oss << "cluster.conf_read_file failed with error " << ret;
308     return oss.str();
309   }
310   cluster.conf_parse_env(NULL);
311
312   for (auto &setting : config) {
313     ret = cluster.conf_set(setting.first.c_str(), setting.second.c_str());
314     if (ret) {
315       std::ostringstream oss;
316       oss << "failed to set config value " << setting.first << " to '"
317           << setting.second << "': " << strerror(-ret);
318       return oss.str();
319     }
320   }
321
322   ret = cluster.connect();
323   if (ret) {
324     cluster.shutdown();
325     std::ostringstream oss;
326     oss << "cluster.connect failed with error " << ret;
327     return oss.str();
328   }
329   return "";
330 }
331
332 int destroy_one_pool(const std::string &pool_name, rados_t *cluster)
333 {
334   int ret = rados_pool_delete(*cluster, pool_name.c_str());
335   if (ret) {
336     rados_shutdown(*cluster);
337     return ret;
338   }
339   rados_shutdown(*cluster);
340   return 0;
341 }
342
343 int destroy_one_ec_pool(const std::string &pool_name, rados_t *cluster)
344 {
345   int ret = rados_pool_delete(*cluster, pool_name.c_str());
346   if (ret) {
347     rados_shutdown(*cluster);
348     return ret;
349   }
350
351   CephContext *cct = static_cast<CephContext*>(rados_cct(*cluster));
352   if (!cct->_conf->mon_fake_pool_delete) { // hope this is in [global]
353     std::ostringstream oss;
354     ret = destroy_ec_profile_and_ruleset(cluster, pool_name, oss);
355     if (ret) {
356       rados_shutdown(*cluster);
357       return ret;
358     }
359   }
360
361   rados_wait_for_latest_osdmap(*cluster);
362   rados_shutdown(*cluster);
363   return ret;
364 }
365
366 int destroy_one_pool_pp(const std::string &pool_name, Rados &cluster)
367 {
368   int ret = cluster.pool_delete(pool_name.c_str());
369   if (ret) {
370     cluster.shutdown();
371     return ret;
372   }
373   cluster.shutdown();
374   return 0;
375 }
376
377 int destroy_one_ec_pool_pp(const std::string &pool_name, Rados &cluster)
378 {
379   int ret = cluster.pool_delete(pool_name.c_str());
380   if (ret) {
381     cluster.shutdown();
382     return ret;
383   }
384
385   CephContext *cct = static_cast<CephContext*>(cluster.cct());
386   if (!cct->_conf->mon_fake_pool_delete) { // hope this is in [global]
387     std::ostringstream oss;
388     ret = destroy_ec_profile_and_ruleset_pp(cluster, pool_name, oss);
389     if (ret) {
390       cluster.shutdown();
391       return ret;
392     }
393   }
394
395   cluster.wait_for_latest_osdmap();
396   cluster.shutdown();
397   return ret;
398 }
399
400 void assert_eq_sparse(bufferlist& expected,
401                       const std::map<uint64_t, uint64_t>& extents,
402                       bufferlist& actual) {
403   auto i = expected.begin();
404   auto p = actual.begin();
405   uint64_t pos = 0;
406   for (auto extent : extents) {
407     const uint64_t start = extent.first;
408     const uint64_t end = start + extent.second;
409     for (; pos < end; ++i, ++pos) {
410       ASSERT_FALSE(i.end());
411       if (pos < start) {
412         // check the hole
413         ASSERT_EQ('\0', *i);
414       } else {
415         // then the extent
416         ASSERT_EQ(*i, *p);
417         ++p;
418       }
419     }
420   }
421   ASSERT_EQ(expected.length(), pos);
422 }