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