]>
Commit | Line | Data |
---|---|---|
c07f9fc5 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 "acconfig.h" | |
5 | #include "options.h" | |
6 | #include "common/Formatter.h" | |
20effc67 | 7 | #include "common/options/build_options.h" |
c07f9fc5 FG |
8 | |
9 | // Helpers for validators | |
10 | #include "include/stringify.h" | |
9f95a23c | 11 | #include "include/common_fwd.h" |
c07f9fc5 FG |
12 | #include <boost/algorithm/string.hpp> |
13 | #include <boost/lexical_cast.hpp> | |
11fdf7f2 | 14 | #include <regex> |
c07f9fc5 | 15 | |
3efd9988 FG |
16 | // Definitions for enums |
17 | #include "common/perf_counters.h" | |
18 | ||
20effc67 | 19 | // rbd feature and io operation validation |
11fdf7f2 | 20 | #include "librbd/Features.h" |
20effc67 | 21 | #include "librbd/io/IoOperations.h" |
11fdf7f2 | 22 | |
f67539c2 TL |
23 | using std::ostream; |
24 | using std::ostringstream; | |
25 | ||
26 | using ceph::Formatter; | |
27 | using ceph::parse_timespan; | |
28 | ||
11fdf7f2 | 29 | namespace { |
20effc67 | 30 | class printer { |
11fdf7f2 TL |
31 | ostream& out; |
32 | public: | |
33 | explicit printer(ostream& os) | |
34 | : out(os) {} | |
35 | template<typename T> | |
36 | void operator()(const T& v) const { | |
37 | out << v; | |
38 | } | |
20effc67 | 39 | void operator()(std::monostate) const { |
11fdf7f2 TL |
40 | return; |
41 | } | |
42 | void operator()(bool v) const { | |
43 | out << (v ? "true" : "false"); | |
44 | } | |
45 | void operator()(double v) const { | |
46 | out << std::fixed << v << std::defaultfloat; | |
47 | } | |
48 | void operator()(const Option::size_t& v) const { | |
49 | out << v.value; | |
50 | } | |
51 | void operator()(const std::chrono::seconds v) const { | |
52 | out << v.count(); | |
53 | } | |
f67539c2 TL |
54 | void operator()(const std::chrono::milliseconds v) const { |
55 | out << v.count(); | |
56 | } | |
11fdf7f2 TL |
57 | }; |
58 | } | |
59 | ||
60 | ostream& operator<<(ostream& os, const Option::value_t& v) { | |
61 | printer p{os}; | |
20effc67 | 62 | std::visit(p, v); |
11fdf7f2 TL |
63 | return os; |
64 | } | |
c07f9fc5 FG |
65 | |
66 | void Option::dump_value(const char *field_name, | |
67 | const Option::value_t &v, Formatter *f) const | |
68 | { | |
20effc67 | 69 | if (v == value_t{}) { |
c07f9fc5 FG |
70 | // This should be nil but Formatter doesn't allow it. |
71 | f->dump_string(field_name, ""); | |
11fdf7f2 TL |
72 | return; |
73 | } | |
74 | switch (type) { | |
75 | case TYPE_INT: | |
20effc67 | 76 | f->dump_int(field_name, std::get<int64_t>(v)); break; |
11fdf7f2 | 77 | case TYPE_UINT: |
20effc67 | 78 | f->dump_unsigned(field_name, std::get<uint64_t>(v)); break; |
11fdf7f2 | 79 | case TYPE_STR: |
20effc67 | 80 | f->dump_string(field_name, std::get<std::string>(v)); break; |
11fdf7f2 | 81 | case TYPE_FLOAT: |
20effc67 | 82 | f->dump_float(field_name, std::get<double>(v)); break; |
11fdf7f2 | 83 | case TYPE_BOOL: |
20effc67 | 84 | f->dump_bool(field_name, std::get<bool>(v)); break; |
11fdf7f2 TL |
85 | default: |
86 | f->dump_stream(field_name) << v; break; | |
c07f9fc5 FG |
87 | } |
88 | } | |
89 | ||
90 | int Option::pre_validate(std::string *new_value, std::string *err) const | |
91 | { | |
92 | if (validator) { | |
93 | return validator(new_value, err); | |
94 | } else { | |
95 | return 0; | |
96 | } | |
97 | } | |
98 | ||
99 | int Option::validate(const Option::value_t &new_value, std::string *err) const | |
100 | { | |
101 | // Generic validation: min | |
20effc67 | 102 | if (min != value_t{}) { |
c07f9fc5 FG |
103 | if (new_value < min) { |
104 | std::ostringstream oss; | |
105 | oss << "Value '" << new_value << "' is below minimum " << min; | |
106 | *err = oss.str(); | |
107 | return -EINVAL; | |
108 | } | |
109 | } | |
110 | ||
111 | // Generic validation: max | |
20effc67 | 112 | if (max != value_t{}) { |
c07f9fc5 FG |
113 | if (new_value > max) { |
114 | std::ostringstream oss; | |
115 | oss << "Value '" << new_value << "' exceeds maximum " << max; | |
116 | *err = oss.str(); | |
117 | return -EINVAL; | |
118 | } | |
119 | } | |
120 | ||
121 | // Generic validation: enum | |
122 | if (!enum_allowed.empty() && type == Option::TYPE_STR) { | |
123 | auto found = std::find(enum_allowed.begin(), enum_allowed.end(), | |
20effc67 | 124 | std::get<std::string>(new_value)); |
c07f9fc5 FG |
125 | if (found == enum_allowed.end()) { |
126 | std::ostringstream oss; | |
127 | oss << "'" << new_value << "' is not one of the permitted " | |
128 | "values: " << joinify(enum_allowed.begin(), | |
129 | enum_allowed.end(), | |
130 | std::string(", ")); | |
131 | *err = oss.str(); | |
132 | return -EINVAL; | |
133 | } | |
134 | } | |
135 | ||
136 | return 0; | |
137 | } | |
138 | ||
11fdf7f2 TL |
139 | int Option::parse_value( |
140 | const std::string& raw_val, | |
141 | value_t *out, | |
142 | std::string *error_message, | |
143 | std::string *normalized_value) const | |
144 | { | |
145 | std::string val = raw_val; | |
146 | ||
147 | int r = pre_validate(&val, error_message); | |
148 | if (r != 0) { | |
149 | return r; | |
150 | } | |
151 | ||
152 | if (type == Option::TYPE_INT) { | |
20effc67 | 153 | int64_t f = strict_si_cast<int64_t>(val, error_message); |
11fdf7f2 TL |
154 | if (!error_message->empty()) { |
155 | return -EINVAL; | |
156 | } | |
157 | *out = f; | |
158 | } else if (type == Option::TYPE_UINT) { | |
20effc67 | 159 | uint64_t f = strict_si_cast<uint64_t>(val, error_message); |
11fdf7f2 TL |
160 | if (!error_message->empty()) { |
161 | return -EINVAL; | |
162 | } | |
163 | *out = f; | |
164 | } else if (type == Option::TYPE_STR) { | |
165 | *out = val; | |
166 | } else if (type == Option::TYPE_FLOAT) { | |
167 | double f = strict_strtod(val.c_str(), error_message); | |
168 | if (!error_message->empty()) { | |
169 | return -EINVAL; | |
170 | } else { | |
171 | *out = f; | |
172 | } | |
173 | } else if (type == Option::TYPE_BOOL) { | |
f67539c2 TL |
174 | bool b = strict_strtob(val.c_str(), error_message); |
175 | if (!error_message->empty()) { | |
176 | return -EINVAL; | |
11fdf7f2 | 177 | } else { |
f67539c2 | 178 | *out = b; |
11fdf7f2 TL |
179 | } |
180 | } else if (type == Option::TYPE_ADDR) { | |
181 | entity_addr_t addr; | |
20effc67 | 182 | if (!addr.parse(val)){ |
11fdf7f2 TL |
183 | return -EINVAL; |
184 | } | |
185 | *out = addr; | |
9f95a23c | 186 | } else if (type == Option::TYPE_ADDRVEC) { |
11fdf7f2 TL |
187 | entity_addrvec_t addr; |
188 | if (!addr.parse(val.c_str())){ | |
189 | return -EINVAL; | |
190 | } | |
191 | *out = addr; | |
192 | } else if (type == Option::TYPE_UUID) { | |
193 | uuid_d uuid; | |
194 | if (!uuid.parse(val.c_str())) { | |
195 | return -EINVAL; | |
196 | } | |
197 | *out = uuid; | |
198 | } else if (type == Option::TYPE_SIZE) { | |
20effc67 | 199 | Option::size_t sz{strict_iecstrtoll(val, error_message)}; |
11fdf7f2 TL |
200 | if (!error_message->empty()) { |
201 | return -EINVAL; | |
202 | } | |
203 | *out = sz; | |
204 | } else if (type == Option::TYPE_SECS) { | |
205 | try { | |
206 | *out = parse_timespan(val); | |
f67539c2 TL |
207 | } catch (const std::invalid_argument& e) { |
208 | *error_message = e.what(); | |
209 | return -EINVAL; | |
210 | } | |
211 | } else if (type == Option::TYPE_MILLISECS) { | |
212 | try { | |
20effc67 TL |
213 | *out = std::chrono::milliseconds(std::stoull(val)); |
214 | } catch (const std::logic_error& e) { | |
11fdf7f2 TL |
215 | *error_message = e.what(); |
216 | return -EINVAL; | |
217 | } | |
218 | } else { | |
219 | ceph_abort(); | |
220 | } | |
221 | ||
222 | r = validate(*out, error_message); | |
223 | if (r != 0) { | |
224 | return r; | |
225 | } | |
226 | ||
227 | if (normalized_value) { | |
228 | *normalized_value = to_str(*out); | |
229 | } | |
230 | return 0; | |
231 | } | |
232 | ||
c07f9fc5 FG |
233 | void Option::dump(Formatter *f) const |
234 | { | |
c07f9fc5 FG |
235 | f->dump_string("name", name); |
236 | ||
237 | f->dump_string("type", type_to_str(type)); | |
c07f9fc5 FG |
238 | |
239 | f->dump_string("level", level_to_str(level)); | |
240 | ||
241 | f->dump_string("desc", desc); | |
242 | f->dump_string("long_desc", long_desc); | |
243 | ||
244 | dump_value("default", value, f); | |
245 | dump_value("daemon_default", daemon_value, f); | |
246 | ||
247 | f->open_array_section("tags"); | |
248 | for (const auto t : tags) { | |
249 | f->dump_string("tag", t); | |
250 | } | |
251 | f->close_section(); | |
252 | ||
253 | f->open_array_section("services"); | |
254 | for (const auto s : services) { | |
255 | f->dump_string("service", s); | |
256 | } | |
257 | f->close_section(); | |
258 | ||
259 | f->open_array_section("see_also"); | |
260 | for (const auto sa : see_also) { | |
261 | f->dump_string("see_also", sa); | |
262 | } | |
263 | f->close_section(); | |
264 | ||
265 | if (type == TYPE_STR) { | |
266 | f->open_array_section("enum_values"); | |
267 | for (const auto &ea : enum_allowed) { | |
268 | f->dump_string("enum_value", ea); | |
269 | } | |
270 | f->close_section(); | |
271 | } | |
272 | ||
273 | dump_value("min", min, f); | |
274 | dump_value("max", max, f); | |
275 | ||
11fdf7f2 TL |
276 | f->dump_bool("can_update_at_runtime", can_update_at_runtime()); |
277 | ||
278 | f->open_array_section("flags"); | |
279 | if (has_flag(FLAG_RUNTIME)) { | |
280 | f->dump_string("option", "runtime"); | |
281 | } | |
282 | if (has_flag(FLAG_NO_MON_UPDATE)) { | |
283 | f->dump_string("option", "no_mon_update"); | |
284 | } | |
285 | if (has_flag(FLAG_STARTUP)) { | |
286 | f->dump_string("option", "startup"); | |
287 | } | |
288 | if (has_flag(FLAG_CLUSTER_CREATE)) { | |
289 | f->dump_string("option", "cluster_create"); | |
290 | } | |
291 | if (has_flag(FLAG_CREATE)) { | |
292 | f->dump_string("option", "create"); | |
293 | } | |
c07f9fc5 FG |
294 | f->close_section(); |
295 | } | |
296 | ||
11fdf7f2 TL |
297 | std::string Option::to_str(const Option::value_t& v) |
298 | { | |
299 | return stringify(v); | |
300 | } | |
301 | ||
302 | void Option::print(ostream *out) const | |
303 | { | |
304 | *out << name << " - " << desc << "\n"; | |
305 | *out << " (" << type_to_str(type) << ", " << level_to_str(level) << ")\n"; | |
20effc67 | 306 | if (daemon_value != value_t{}) { |
11fdf7f2 TL |
307 | *out << " Default (non-daemon): " << stringify(value) << "\n"; |
308 | *out << " Default (daemon): " << stringify(daemon_value) << "\n"; | |
309 | } else { | |
310 | *out << " Default: " << stringify(value) << "\n"; | |
311 | } | |
312 | if (!enum_allowed.empty()) { | |
313 | *out << " Possible values: "; | |
314 | for (auto& i : enum_allowed) { | |
315 | *out << " " << stringify(i); | |
316 | } | |
317 | *out << "\n"; | |
318 | } | |
20effc67 | 319 | if (min != value_t{}) { |
11fdf7f2 TL |
320 | *out << " Minimum: " << stringify(min) << "\n" |
321 | << " Maximum: " << stringify(max) << "\n"; | |
322 | } | |
323 | *out << " Can update at runtime: " | |
324 | << (can_update_at_runtime() ? "true" : "false") << "\n"; | |
325 | if (!services.empty()) { | |
326 | *out << " Services: " << services << "\n"; | |
327 | } | |
328 | if (!tags.empty()) { | |
329 | *out << " Tags: " << tags << "\n"; | |
330 | } | |
331 | if (!see_also.empty()) { | |
332 | *out << " See also: " << see_also << "\n"; | |
333 | } | |
334 | ||
335 | if (long_desc.size()) { | |
336 | *out << "\n" << long_desc << "\n"; | |
337 | } | |
338 | } | |
339 | ||
c07f9fc5 | 340 | const std::vector<Option> ceph_options = build_options(); |