]>
Commit | Line | Data |
---|---|---|
31f18b77 FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "common/Formatter.h" | |
5 | #include "include/stringify.h" | |
6 | #include "json_spirit/json_spirit.h" | |
7 | #include "test_common.h" | |
8 | ||
9 | namespace { | |
10 | ||
11 | using namespace ceph; | |
12 | ||
13 | int wait_for_healthy(rados_t *cluster) | |
14 | { | |
15 | bool healthy = false; | |
16 | // This timeout is very long because the tests are sometimes | |
17 | // run on a thrashing cluster | |
18 | int timeout = 3600; | |
19 | int slept = 0; | |
20 | ||
21 | while(!healthy) { | |
22 | JSONFormatter cmd_f; | |
23 | cmd_f.open_object_section("command"); | |
24 | cmd_f.dump_string("prefix", "status"); | |
25 | cmd_f.dump_string("format", "json"); | |
26 | cmd_f.close_section(); | |
27 | std::ostringstream cmd_stream; | |
28 | cmd_f.flush(cmd_stream); | |
29 | const std::string serialized_cmd = cmd_stream.str(); | |
30 | ||
31 | const char *cmd[2]; | |
32 | cmd[1] = NULL; | |
33 | cmd[0] = serialized_cmd.c_str(); | |
34 | ||
35 | char *outbuf = NULL; | |
36 | size_t outlen = 0; | |
37 | int ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0, | |
38 | &outbuf, &outlen, NULL, NULL); | |
39 | if (ret) { | |
40 | return ret; | |
41 | } | |
42 | ||
43 | std::string out(outbuf, outlen); | |
44 | rados_buffer_free(outbuf); | |
45 | ||
46 | json_spirit::mValue root; | |
47 | assert(json_spirit::read(out, root)); | |
48 | json_spirit::mObject root_obj = root.get_obj(); | |
49 | json_spirit::mObject pgmap = root_obj["pgmap"].get_obj(); | |
50 | json_spirit::mArray pgs_by_state = pgmap["pgs_by_state"].get_array(); | |
51 | ||
52 | if (pgs_by_state.size() == 1) { | |
53 | json_spirit::mObject state = pgs_by_state[0].get_obj(); | |
54 | std::string state_name = state["state_name"].get_str(); | |
55 | if (state_name != std::string("active+clean")) { | |
56 | healthy = false; | |
57 | } else { | |
58 | healthy = true; | |
59 | } | |
60 | } else { | |
61 | healthy = false; | |
62 | } | |
63 | ||
64 | if (slept >= timeout) { | |
65 | return -ETIMEDOUT; | |
66 | }; | |
67 | ||
68 | if (!healthy) { | |
69 | sleep(1); | |
70 | slept += 1; | |
71 | } | |
72 | } | |
73 | ||
74 | return 0; | |
75 | } | |
76 | ||
77 | int rados_pool_set( | |
78 | rados_t *cluster, | |
79 | const std::string &pool_name, | |
80 | const std::string &var, | |
81 | const std::string &val) | |
82 | { | |
83 | JSONFormatter cmd_f; | |
84 | cmd_f.open_object_section("command"); | |
85 | cmd_f.dump_string("prefix", "osd pool set"); | |
86 | cmd_f.dump_string("pool", pool_name); | |
87 | cmd_f.dump_string("var", var); | |
88 | cmd_f.dump_string("val", val); | |
89 | cmd_f.close_section(); | |
90 | ||
91 | std::ostringstream cmd_stream; | |
92 | cmd_f.flush(cmd_stream); | |
93 | ||
94 | const std::string serialized_cmd = cmd_stream.str(); | |
95 | ||
96 | const char *cmd[2]; | |
97 | cmd[1] = NULL; | |
98 | cmd[0] = serialized_cmd.c_str(); | |
99 | int ret = rados_mon_command(*cluster, (const char **)cmd, 1, "", 0, NULL, | |
100 | NULL, NULL, NULL); | |
101 | return ret; | |
102 | } | |
103 | ||
28e407b8 AA |
104 | struct pool_op_error : std::exception { |
105 | string msg; | |
106 | pool_op_error(const std::string& pool_name, | |
107 | const std::string& func_name, | |
108 | int err) { | |
109 | std::ostringstream oss; | |
110 | oss << func_name << "(" << pool_name << ") failed with error " << err; | |
111 | msg = oss.str(); | |
31f18b77 | 112 | } |
28e407b8 AA |
113 | const char* what() const noexcept override { |
114 | return msg.c_str(); | |
31f18b77 | 115 | } |
28e407b8 | 116 | }; |
31f18b77 | 117 | |
28e407b8 AA |
118 | template<typename Func> |
119 | std::string with_healthy_cluster(rados_t* cluster, | |
120 | const std::string& pool_name, | |
121 | Func&& func) | |
122 | { | |
123 | try { | |
124 | // Wait for 'creating/backfilling' to clear | |
125 | int r = wait_for_healthy(cluster); | |
126 | if (r != 0) { | |
127 | throw pool_op_error{pool_name, "wait_for_healthy", r}; | |
128 | } | |
129 | func(); | |
130 | // Wait for 'creating/backfilling' to clear | |
131 | r = wait_for_healthy(cluster); | |
132 | if (r != 0) { | |
133 | throw pool_op_error{pool_name, "wait_for_healthy", r}; | |
134 | } | |
135 | } catch (const pool_op_error& e) { | |
136 | rados_shutdown(*cluster); | |
137 | return e.what(); | |
31f18b77 | 138 | } |
31f18b77 | 139 | return ""; |
28e407b8 AA |
140 | } |
141 | } | |
142 | ||
143 | std::string set_pg_num( | |
144 | rados_t *cluster, const std::string &pool_name, uint32_t pg_num) | |
145 | { | |
146 | return with_healthy_cluster(cluster, pool_name, [&] { | |
147 | // Adjust pg_num | |
148 | int r = rados_pool_set(cluster, pool_name, "pg_num", | |
149 | stringify(pg_num)); | |
150 | if (r != 0) { | |
151 | throw pool_op_error{pool_name, "set_pg_num", r}; | |
152 | } | |
153 | }); | |
154 | } | |
31f18b77 | 155 | |
28e407b8 AA |
156 | std::string set_pgp_num( |
157 | rados_t *cluster, const std::string &pool_name, uint32_t pgp_num) | |
158 | { | |
159 | return with_healthy_cluster(cluster, pool_name, [&] { | |
160 | // Adjust pgp_num | |
161 | int r = rados_pool_set(cluster, pool_name, "pgp_num", | |
162 | stringify(pgp_num)); | |
163 | if (r != 0) { | |
164 | throw pool_op_error{pool_name, "set_pgp_num", r}; | |
165 | } | |
166 | }); | |
31f18b77 | 167 | } |