]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2013 Inktank Storage, Inc. | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public | |
10 | * License version 2, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
9f95a23c | 15 | #include "include/common_fwd.h" |
11fdf7f2 TL |
16 | #include "common/cmdparse.h" |
17 | #include "common/Formatter.h" | |
7c673cae | 18 | #include "common/debug.h" |
11fdf7f2 TL |
19 | #include "common/strtol.h" |
20 | #include "json_spirit/json_spirit.h" | |
7c673cae | 21 | |
f67539c2 TL |
22 | using std::is_same_v; |
23 | using std::ostringstream; | |
24 | using std::string; | |
25 | using std::stringstream; | |
26 | using std::string_view; | |
27 | using std::vector; | |
28 | ||
20effc67 TL |
29 | using namespace std::literals; |
30 | ||
7c673cae FG |
31 | /** |
32 | * Given a cmddesc like "foo baz name=bar,type=CephString", | |
33 | * return the prefix "foo baz". | |
34 | */ | |
1e59de90 | 35 | namespace ceph::common { |
9f95a23c | 36 | std::string cmddesc_get_prefix(const std::string_view &cmddesc) |
7c673cae | 37 | { |
9f95a23c TL |
38 | string tmp(cmddesc); // FIXME: stringstream ctor can't take string_view :( |
39 | stringstream ss(tmp); | |
7c673cae FG |
40 | std::string word; |
41 | std::ostringstream result; | |
42 | bool first = true; | |
43 | while (std::getline(ss, word, ' ')) { | |
44 | if (word.find_first_of(",=") != string::npos) { | |
45 | break; | |
46 | } | |
47 | ||
48 | if (!first) { | |
49 | result << " "; | |
50 | } | |
51 | result << word; | |
52 | first = false; | |
53 | } | |
54 | ||
55 | return result.str(); | |
56 | } | |
57 | ||
11fdf7f2 TL |
58 | using arg_desc_t = std::map<std::string_view, std::string_view>; |
59 | ||
60 | // Snarf up all the key=val,key=val pairs, put 'em in a dict. | |
9f95a23c | 61 | arg_desc_t cmddesc_get_args(const string_view cmddesc) |
11fdf7f2 TL |
62 | { |
63 | arg_desc_t arg_desc; | |
64 | for_each_substr(cmddesc, ",", [&](auto kv) { | |
65 | // key=value; key by itself implies value is bool true | |
66 | // name="name" means arg dict will be titled 'name' | |
67 | auto equal = kv.find('='); | |
68 | if (equal == kv.npos) { | |
69 | // it should be the command | |
70 | return; | |
71 | } | |
72 | auto key = kv.substr(0, equal); | |
73 | auto val = kv.substr(equal + 1); | |
74 | arg_desc[key] = val; | |
75 | }); | |
76 | return arg_desc; | |
77 | } | |
78 | ||
79 | std::string cmddesc_get_prenautilus_compat(const std::string &cmddesc) | |
80 | { | |
81 | std::vector<std::string> out; | |
82 | stringstream ss(cmddesc); | |
83 | std::string word; | |
84 | bool changed = false; | |
85 | while (std::getline(ss, word, ' ')) { | |
86 | // if no , or =, must be a plain word to put out | |
87 | if (word.find_first_of(",=") == string::npos) { | |
88 | out.push_back(word); | |
89 | continue; | |
90 | } | |
91 | auto desckv = cmddesc_get_args(word); | |
92 | auto j = desckv.find("type"); | |
93 | if (j != desckv.end() && j->second == "CephBool") { | |
94 | // Instruct legacy clients or mons to send --foo-bar string in place | |
95 | // of a 'true'/'false' value | |
96 | std::ostringstream oss; | |
20effc67 | 97 | oss << "--" << desckv["name"]; |
11fdf7f2 TL |
98 | std::string val = oss.str(); |
99 | std::replace(val.begin(), val.end(), '_', '-'); | |
100 | desckv["type"] = "CephChoices"; | |
101 | desckv["strings"] = val; | |
102 | std::ostringstream fss; | |
103 | for (auto k = desckv.begin(); k != desckv.end(); ++k) { | |
104 | if (k != desckv.begin()) { | |
105 | fss << ","; | |
106 | } | |
107 | fss << k->first << "=" << k->second; | |
108 | } | |
109 | out.push_back(fss.str()); | |
110 | changed = true; | |
111 | } else { | |
112 | out.push_back(word); | |
113 | } | |
114 | } | |
115 | if (!changed) { | |
116 | return cmddesc; | |
117 | } | |
118 | std::string o; | |
119 | for (auto i = out.begin(); i != out.end(); ++i) { | |
120 | if (i != out.begin()) { | |
121 | o += " "; | |
122 | } | |
123 | o += *i; | |
124 | } | |
125 | return o; | |
126 | } | |
127 | ||
7c673cae FG |
128 | /** |
129 | * Read a command description list out of cmd, and dump it to f. | |
130 | * A signature description is a set of space-separated words; | |
131 | * see MonCommands.h for more info. | |
132 | */ | |
133 | ||
134 | void | |
11fdf7f2 | 135 | dump_cmd_to_json(Formatter *f, uint64_t features, const string& cmd) |
7c673cae FG |
136 | { |
137 | // put whole command signature in an already-opened container | |
138 | // elements are: "name", meaning "the typeless name that means a literal" | |
139 | // an object {} with key:value pairs representing an argument | |
140 | ||
7c673cae FG |
141 | stringstream ss(cmd); |
142 | std::string word; | |
20effc67 | 143 | bool positional = true; |
7c673cae FG |
144 | |
145 | while (std::getline(ss, word, ' ')) { | |
20effc67 TL |
146 | if (word == "--") { |
147 | positional = false; | |
148 | continue; | |
149 | } | |
150 | ||
7c673cae FG |
151 | // if no , or =, must be a plain word to put out |
152 | if (word.find_first_of(",=") == string::npos) { | |
153 | f->dump_string("arg", word); | |
154 | continue; | |
155 | } | |
20effc67 | 156 | |
7c673cae | 157 | // accumulate descriptor keywords in desckv |
11fdf7f2 TL |
158 | auto desckv = cmddesc_get_args(word); |
159 | // name the individual desc object based on the name key | |
9f95a23c | 160 | f->open_object_section(desckv["name"]); |
11fdf7f2 TL |
161 | |
162 | // Compatibility for pre-nautilus clients that don't know about CephBool | |
163 | std::string val; | |
164 | if (!HAVE_FEATURE(features, SERVER_NAUTILUS)) { | |
165 | auto i = desckv.find("type"); | |
166 | if (i != desckv.end() && i->second == "CephBool") { | |
167 | // Instruct legacy clients to send --foo-bar string in place | |
168 | // of a 'true'/'false' value | |
169 | std::ostringstream oss; | |
20effc67 | 170 | oss << "--" << desckv["name"]; |
11fdf7f2 TL |
171 | val = oss.str(); |
172 | std::replace(val.begin(), val.end(), '_', '-'); | |
173 | ||
174 | desckv["type"] = "CephChoices"; | |
175 | desckv["strings"] = val; | |
7c673cae | 176 | } |
7c673cae | 177 | } |
11fdf7f2 | 178 | |
7c673cae | 179 | // dump all the keys including name into the array |
20effc67 TL |
180 | if (!positional) { |
181 | desckv["positional"] = "false"; | |
182 | } | |
11fdf7f2 | 183 | for (auto [key, value] : desckv) { |
20effc67 TL |
184 | if (key == "positional") { |
185 | if (!HAVE_FEATURE(features, SERVER_QUINCY)) { | |
186 | continue; | |
187 | } | |
188 | f->dump_bool(key, value == "true" || value == "True"); | |
189 | } else if (key == "req" && HAVE_FEATURE(features, SERVER_QUINCY)) { | |
190 | f->dump_bool(key, value == "true" || value == "True"); | |
191 | } else { | |
192 | f->dump_string(key, value); | |
193 | } | |
7c673cae FG |
194 | } |
195 | f->close_section(); // attribute object for individual desc | |
196 | } | |
197 | } | |
198 | ||
199 | void | |
200 | dump_cmd_and_help_to_json(Formatter *jf, | |
11fdf7f2 | 201 | uint64_t features, |
7c673cae FG |
202 | const string& secname, |
203 | const string& cmdsig, | |
204 | const string& helptext) | |
205 | { | |
9f95a23c | 206 | jf->open_object_section(secname); |
7c673cae | 207 | jf->open_array_section("sig"); |
11fdf7f2 | 208 | dump_cmd_to_json(jf, features, cmdsig); |
7c673cae | 209 | jf->close_section(); // sig array |
9f95a23c | 210 | jf->dump_string("help", helptext); |
7c673cae FG |
211 | jf->close_section(); // cmd |
212 | } | |
213 | ||
214 | void | |
215 | dump_cmddesc_to_json(Formatter *jf, | |
11fdf7f2 | 216 | uint64_t features, |
7c673cae FG |
217 | const string& secname, |
218 | const string& cmdsig, | |
219 | const string& helptext, | |
220 | const string& module, | |
221 | const string& perm, | |
7c673cae FG |
222 | uint64_t flags) |
223 | { | |
9f95a23c | 224 | jf->open_object_section(secname); |
7c673cae | 225 | jf->open_array_section("sig"); |
11fdf7f2 | 226 | dump_cmd_to_json(jf, features, cmdsig); |
7c673cae | 227 | jf->close_section(); // sig array |
9f95a23c TL |
228 | jf->dump_string("help", helptext); |
229 | jf->dump_string("module", module); | |
230 | jf->dump_string("perm", perm); | |
7c673cae FG |
231 | jf->dump_int("flags", flags); |
232 | jf->close_section(); // cmd | |
233 | } | |
234 | ||
235 | void cmdmap_dump(const cmdmap_t &cmdmap, Formatter *f) | |
236 | { | |
11fdf7f2 | 237 | ceph_assert(f != nullptr); |
7c673cae FG |
238 | |
239 | class dump_visitor : public boost::static_visitor<void> | |
240 | { | |
241 | Formatter *f; | |
242 | std::string const &key; | |
243 | public: | |
244 | dump_visitor(Formatter *f_, std::string const &key_) | |
245 | : f(f_), key(key_) | |
246 | { | |
247 | } | |
248 | ||
249 | void operator()(const std::string &operand) const | |
250 | { | |
9f95a23c | 251 | f->dump_string(key, operand); |
7c673cae FG |
252 | } |
253 | ||
254 | void operator()(const bool &operand) const | |
255 | { | |
9f95a23c | 256 | f->dump_bool(key, operand); |
7c673cae FG |
257 | } |
258 | ||
259 | void operator()(const int64_t &operand) const | |
260 | { | |
9f95a23c | 261 | f->dump_int(key, operand); |
7c673cae FG |
262 | } |
263 | ||
264 | void operator()(const double &operand) const | |
265 | { | |
9f95a23c | 266 | f->dump_float(key, operand); |
7c673cae FG |
267 | } |
268 | ||
269 | void operator()(const std::vector<std::string> &operand) const | |
270 | { | |
9f95a23c TL |
271 | f->open_array_section(key); |
272 | for (const auto& i : operand) { | |
7c673cae FG |
273 | f->dump_string("item", i); |
274 | } | |
275 | f->close_section(); | |
276 | } | |
277 | ||
278 | void operator()(const std::vector<int64_t> &operand) const | |
279 | { | |
9f95a23c | 280 | f->open_array_section(key); |
7c673cae FG |
281 | for (const auto i : operand) { |
282 | f->dump_int("item", i); | |
283 | } | |
284 | f->close_section(); | |
285 | } | |
c07f9fc5 FG |
286 | |
287 | void operator()(const std::vector<double> &operand) const | |
288 | { | |
9f95a23c | 289 | f->open_array_section(key); |
c07f9fc5 FG |
290 | for (const auto i : operand) { |
291 | f->dump_float("item", i); | |
292 | } | |
293 | f->close_section(); | |
294 | } | |
7c673cae FG |
295 | }; |
296 | ||
297 | //f->open_object_section("cmdmap"); | |
298 | for (const auto &i : cmdmap) { | |
299 | boost::apply_visitor(dump_visitor(f, i.first), i.second); | |
300 | } | |
301 | //f->close_section(); | |
302 | } | |
303 | ||
304 | ||
305 | /** Parse JSON in vector cmd into a map from field to map of values | |
306 | * (use mValue/mObject) | |
307 | * 'cmd' should not disappear over lifetime of map | |
308 | * 'mapp' points to the caller's map | |
309 | * 'ss' captures any errors during JSON parsing; if function returns | |
310 | * false, ss is valid */ | |
311 | ||
312 | bool | |
f67539c2 | 313 | cmdmap_from_json(const vector<string>& cmd, cmdmap_t *mapp, std::ostream& ss) |
7c673cae FG |
314 | { |
315 | json_spirit::mValue v; | |
316 | ||
317 | string fullcmd; | |
318 | // First, join all cmd strings | |
9f95a23c TL |
319 | for (auto& c : cmd) |
320 | fullcmd += c; | |
7c673cae FG |
321 | |
322 | try { | |
323 | if (!json_spirit::read(fullcmd, v)) | |
f67539c2 | 324 | throw std::runtime_error("unparseable JSON " + fullcmd); |
7c673cae | 325 | if (v.type() != json_spirit::obj_type) |
f67539c2 | 326 | throw std::runtime_error("not JSON object " + fullcmd); |
7c673cae FG |
327 | |
328 | // allocate new mObject (map) to return | |
329 | // make sure all contents are simple types (not arrays or objects) | |
330 | json_spirit::mObject o = v.get_obj(); | |
f67539c2 | 331 | for (auto it = o.begin(); it != o.end(); ++it) { |
7c673cae FG |
332 | |
333 | // ok, marshal it into our string->cmd_vartype map, or throw an | |
334 | // exception if it's not a simple datatype. This is kind of | |
335 | // annoying, since json_spirit has a boost::variant inside it | |
336 | // already, but it's not public. Oh well. | |
337 | ||
338 | switch (it->second.type()) { | |
339 | ||
340 | case json_spirit::obj_type: | |
341 | default: | |
f67539c2 | 342 | throw std::runtime_error("JSON array/object not allowed " + fullcmd); |
7c673cae FG |
343 | break; |
344 | ||
345 | case json_spirit::array_type: | |
346 | { | |
347 | // array is a vector of values. Unpack it to a vector | |
c07f9fc5 | 348 | // of strings, doubles, or int64_t, the only types we handle. |
7c673cae FG |
349 | const vector<json_spirit::mValue>& spvals = it->second.get_array(); |
350 | if (spvals.empty()) { | |
351 | // if an empty array is acceptable, the caller should always check for | |
352 | // vector<string> if the expected value of "vector<int64_t>" in the | |
353 | // cmdmap is missing. | |
354 | (*mapp)[it->first] = vector<string>(); | |
355 | } else if (spvals.front().type() == json_spirit::str_type) { | |
356 | vector<string> outv; | |
357 | for (const auto& sv : spvals) { | |
358 | if (sv.type() != json_spirit::str_type) { | |
f67539c2 | 359 | throw std::runtime_error("Can't handle arrays of multiple types"); |
7c673cae FG |
360 | } |
361 | outv.push_back(sv.get_str()); | |
362 | } | |
363 | (*mapp)[it->first] = std::move(outv); | |
364 | } else if (spvals.front().type() == json_spirit::int_type) { | |
365 | vector<int64_t> outv; | |
366 | for (const auto& sv : spvals) { | |
367 | if (spvals.front().type() != json_spirit::int_type) { | |
f67539c2 | 368 | throw std::runtime_error("Can't handle arrays of multiple types"); |
7c673cae FG |
369 | } |
370 | outv.push_back(sv.get_int64()); | |
371 | } | |
372 | (*mapp)[it->first] = std::move(outv); | |
c07f9fc5 FG |
373 | } else if (spvals.front().type() == json_spirit::real_type) { |
374 | vector<double> outv; | |
375 | for (const auto& sv : spvals) { | |
376 | if (spvals.front().type() != json_spirit::real_type) { | |
f67539c2 | 377 | throw std::runtime_error("Can't handle arrays of multiple types"); |
c07f9fc5 FG |
378 | } |
379 | outv.push_back(sv.get_real()); | |
380 | } | |
381 | (*mapp)[it->first] = std::move(outv); | |
7c673cae | 382 | } else { |
f67539c2 TL |
383 | throw std::runtime_error("Can't handle arrays of types other than " |
384 | "int, string, or double"); | |
7c673cae FG |
385 | } |
386 | } | |
387 | break; | |
388 | case json_spirit::str_type: | |
389 | (*mapp)[it->first] = it->second.get_str(); | |
390 | break; | |
391 | ||
392 | case json_spirit::bool_type: | |
393 | (*mapp)[it->first] = it->second.get_bool(); | |
394 | break; | |
395 | ||
396 | case json_spirit::int_type: | |
397 | (*mapp)[it->first] = it->second.get_int64(); | |
398 | break; | |
399 | ||
400 | case json_spirit::real_type: | |
401 | (*mapp)[it->first] = it->second.get_real(); | |
402 | break; | |
403 | } | |
404 | } | |
405 | return true; | |
f67539c2 | 406 | } catch (const std::runtime_error &e) { |
7c673cae FG |
407 | ss << e.what(); |
408 | return false; | |
409 | } | |
410 | } | |
411 | ||
412 | class stringify_visitor : public boost::static_visitor<string> | |
413 | { | |
414 | public: | |
415 | template <typename T> | |
416 | string operator()(T &operand) const | |
417 | { | |
418 | ostringstream oss; | |
419 | oss << operand; | |
420 | return oss.str(); | |
421 | } | |
422 | }; | |
423 | ||
424 | string | |
425 | cmd_vartype_stringify(const cmd_vartype &v) | |
426 | { | |
427 | return boost::apply_visitor(stringify_visitor(), v); | |
428 | } | |
429 | ||
430 | ||
431 | void | |
31f18b77 | 432 | handle_bad_get(CephContext *cct, const string& k, const char *tname) |
7c673cae FG |
433 | { |
434 | ostringstream errstr; | |
435 | int status; | |
436 | const char *typestr = abi::__cxa_demangle(tname, 0, 0, &status); | |
437 | if (status != 0) | |
438 | typestr = tname; | |
439 | errstr << "bad boost::get: key " << k << " is not type " << typestr; | |
440 | lderr(cct) << errstr.str() << dendl; | |
441 | ||
442 | ostringstream oss; | |
20effc67 | 443 | oss << ClibBackTrace(1); |
b32b8144 FG |
444 | lderr(cct) << oss.str() << dendl; |
445 | ||
7c673cae FG |
446 | if (status == 0) |
447 | free((char *)typestr); | |
448 | } | |
31f18b77 FG |
449 | |
450 | long parse_pos_long(const char *s, std::ostream *pss) | |
451 | { | |
452 | if (*s == '-' || *s == '+') { | |
453 | if (pss) | |
454 | *pss << "expected numerical value, got: " << s; | |
455 | return -EINVAL; | |
456 | } | |
457 | ||
458 | string err; | |
459 | long r = strict_strtol(s, 10, &err); | |
460 | if ((r == 0) && !err.empty()) { | |
461 | if (pss) | |
462 | *pss << err; | |
463 | return -1; | |
464 | } | |
465 | if (r < 0) { | |
466 | if (pss) | |
467 | *pss << "unable to parse positive integer '" << s << "'"; | |
468 | return -1; | |
469 | } | |
470 | return r; | |
471 | } | |
472 | ||
473 | int parse_osd_id(const char *s, std::ostream *pss) | |
474 | { | |
475 | // osd.NNN? | |
476 | if (strncmp(s, "osd.", 4) == 0) { | |
477 | s += 4; | |
478 | } | |
479 | ||
480 | // NNN? | |
481 | ostringstream ss; | |
482 | long id = parse_pos_long(s, &ss); | |
483 | if (id < 0) { | |
484 | *pss << ss.str(); | |
485 | return id; | |
486 | } | |
487 | if (id > 0xffff) { | |
488 | *pss << "osd id " << id << " is too large"; | |
489 | return -ERANGE; | |
490 | } | |
491 | return id; | |
492 | } | |
11fdf7f2 TL |
493 | |
494 | namespace { | |
495 | template <typename Func> | |
496 | bool find_first_in(std::string_view s, const char *delims, Func&& f) | |
497 | { | |
498 | auto pos = s.find_first_not_of(delims); | |
499 | while (pos != s.npos) { | |
500 | s.remove_prefix(pos); | |
501 | auto end = s.find_first_of(delims); | |
502 | if (f(s.substr(0, end))) { | |
503 | return true; | |
504 | } | |
505 | pos = s.find_first_not_of(delims, end); | |
506 | } | |
507 | return false; | |
508 | } | |
509 | ||
510 | template<typename T> | |
511 | T str_to_num(const std::string& s) | |
512 | { | |
513 | if constexpr (is_same_v<T, int>) { | |
514 | return std::stoi(s); | |
515 | } else if constexpr (is_same_v<T, long>) { | |
516 | return std::stol(s); | |
517 | } else if constexpr (is_same_v<T, long long>) { | |
518 | return std::stoll(s); | |
519 | } else if constexpr (is_same_v<T, double>) { | |
520 | return std::stod(s); | |
521 | } | |
522 | } | |
523 | ||
524 | template<typename T> | |
525 | bool arg_in_range(T value, const arg_desc_t& desc, std::ostream& os) { | |
526 | auto range = desc.find("range"); | |
527 | if (range == desc.end()) { | |
528 | return true; | |
529 | } | |
530 | auto min_max = get_str_list(string(range->second), "|"); | |
531 | auto min = str_to_num<T>(min_max.front()); | |
f67539c2 | 532 | auto max = std::numeric_limits<T>::max(); |
11fdf7f2 TL |
533 | if (min_max.size() > 1) { |
534 | max = str_to_num<T>(min_max.back()); | |
535 | } | |
536 | if (value < min || value > max) { | |
537 | os << "'" << value << "' out of range: " << min_max; | |
538 | return false; | |
539 | } | |
540 | return true; | |
541 | } | |
542 | ||
543 | bool validate_str_arg(std::string_view value, | |
544 | std::string_view type, | |
545 | const arg_desc_t& desc, | |
546 | std::ostream& os) | |
547 | { | |
548 | if (type == "CephIPAddr") { | |
549 | entity_addr_t addr; | |
20effc67 | 550 | if (addr.parse(value)) { |
11fdf7f2 TL |
551 | return true; |
552 | } else { | |
553 | os << "failed to parse addr '" << value << "', should be ip:[port]"; | |
554 | return false; | |
555 | } | |
556 | } else if (type == "CephChoices") { | |
557 | auto choices = desc.find("strings"); | |
558 | ceph_assert(choices != end(desc)); | |
559 | auto strings = choices->second; | |
560 | if (find_first_in(strings, "|", [=](auto choice) { | |
561 | return (value == choice); | |
562 | })) { | |
563 | return true; | |
564 | } else { | |
565 | os << "'" << value << "' not belong to '" << strings << "'"; | |
566 | return false; | |
567 | } | |
568 | } else { | |
569 | // CephString or other types like CephPgid | |
570 | return true; | |
571 | } | |
572 | } | |
573 | ||
1e59de90 | 574 | bool validate_bool(const cmdmap_t& cmdmap, |
20effc67 TL |
575 | const arg_desc_t& desc, |
576 | const std::string_view name, | |
577 | const std::string_view type, | |
578 | std::ostream& os) | |
579 | { | |
580 | bool v; | |
581 | try { | |
582 | if (!cmd_getval(cmdmap, name, v)) { | |
583 | if (auto req = desc.find("req"); | |
584 | req != end(desc) && req->second == "false") { | |
585 | return true; | |
586 | } else { | |
587 | os << "missing required parameter: '" << name << "'"; | |
588 | return false; | |
589 | } | |
590 | } | |
591 | return true; | |
592 | } catch (const bad_cmd_get& e) { | |
593 | return false; | |
594 | } | |
595 | } | |
596 | ||
11fdf7f2 TL |
597 | template<bool is_vector, |
598 | typename T, | |
f67539c2 TL |
599 | typename Value = std::conditional_t<is_vector, |
600 | vector<T>, | |
601 | T>> | |
1e59de90 | 602 | bool validate_arg(const cmdmap_t& cmdmap, |
11fdf7f2 TL |
603 | const arg_desc_t& desc, |
604 | const std::string_view name, | |
605 | const std::string_view type, | |
606 | std::ostream& os) | |
607 | { | |
608 | Value v; | |
609 | try { | |
20effc67 | 610 | if (!cmd_getval(cmdmap, name, v)) { |
11fdf7f2 TL |
611 | if constexpr (is_vector) { |
612 | // an empty list is acceptable. | |
613 | return true; | |
614 | } else { | |
615 | if (auto req = desc.find("req"); | |
616 | req != end(desc) && req->second == "false") { | |
617 | return true; | |
618 | } else { | |
619 | os << "missing required parameter: '" << name << "'"; | |
620 | return false; | |
621 | } | |
622 | } | |
623 | } | |
624 | } catch (const bad_cmd_get& e) { | |
625 | return false; | |
626 | } | |
627 | auto validate = [&](const T& value) { | |
628 | if constexpr (is_same_v<std::string, T>) { | |
629 | return validate_str_arg(value, type, desc, os); | |
630 | } else if constexpr (is_same_v<int64_t, T> || | |
631 | is_same_v<double, T>) { | |
632 | return arg_in_range(value, desc, os); | |
633 | } | |
634 | }; | |
635 | if constexpr(is_vector) { | |
636 | return find_if_not(begin(v), end(v), validate) == end(v); | |
637 | } else { | |
638 | return validate(v); | |
639 | } | |
640 | } | |
641 | } // anonymous namespace | |
642 | ||
1e59de90 | 643 | bool validate_cmd(const std::string& desc, |
11fdf7f2 TL |
644 | const cmdmap_t& cmdmap, |
645 | std::ostream& os) | |
646 | { | |
647 | return !find_first_in(desc, " ", [&](auto desc) { | |
648 | auto arg_desc = cmddesc_get_args(desc); | |
649 | if (arg_desc.empty()) { | |
650 | return false; | |
651 | } | |
652 | ceph_assert(arg_desc.count("name")); | |
653 | ceph_assert(arg_desc.count("type")); | |
654 | auto name = arg_desc["name"]; | |
655 | auto type = arg_desc["type"]; | |
656 | if (arg_desc.count("n")) { | |
657 | if (type == "CephInt") { | |
1e59de90 | 658 | return !validate_arg<true, int64_t>(cmdmap, arg_desc, |
11fdf7f2 TL |
659 | name, type, os); |
660 | } else if (type == "CephFloat") { | |
1e59de90 | 661 | return !validate_arg<true, double>(cmdmap, arg_desc, |
11fdf7f2 TL |
662 | name, type, os); |
663 | } else { | |
1e59de90 | 664 | return !validate_arg<true, string>(cmdmap, arg_desc, |
11fdf7f2 TL |
665 | name, type, os); |
666 | } | |
667 | } else { | |
668 | if (type == "CephInt") { | |
1e59de90 | 669 | return !validate_arg<false, int64_t>(cmdmap, arg_desc, |
11fdf7f2 TL |
670 | name, type, os); |
671 | } else if (type == "CephFloat") { | |
1e59de90 | 672 | return !validate_arg<false, double>(cmdmap, arg_desc, |
11fdf7f2 | 673 | name, type, os); |
20effc67 | 674 | } else if (type == "CephBool") { |
1e59de90 | 675 | return !validate_bool(cmdmap, arg_desc, |
20effc67 | 676 | name, type, os); |
11fdf7f2 | 677 | } else { |
1e59de90 | 678 | return !validate_arg<false, string>(cmdmap, arg_desc, |
11fdf7f2 TL |
679 | name, type, os); |
680 | } | |
681 | } | |
682 | }); | |
683 | } | |
684 | ||
9f95a23c | 685 | bool cmd_getval(const cmdmap_t& cmdmap, |
20effc67 | 686 | std::string_view k, bool& val) |
11fdf7f2 TL |
687 | { |
688 | /* | |
689 | * Specialized getval for booleans. CephBool didn't exist before Nautilus, | |
690 | * so earlier clients are sent a CephChoices argdesc instead, and will | |
691 | * send us a "--foo-bar" value string for boolean arguments. | |
692 | */ | |
20effc67 TL |
693 | auto found = cmdmap.find(k); |
694 | if (found == cmdmap.end()) { | |
695 | return false; | |
696 | } | |
697 | try { | |
698 | val = boost::get<bool>(found->second); | |
699 | return true; | |
700 | } catch (boost::bad_get&) { | |
11fdf7f2 | 701 | try { |
20effc67 TL |
702 | std::string expected{"--"}; |
703 | expected += k; | |
704 | std::replace(expected.begin(), expected.end(), '_', '-'); | |
705 | ||
706 | std::string v_str = boost::get<std::string>(found->second); | |
707 | if (v_str == expected) { | |
708 | val = true; | |
709 | return true; | |
710 | } else { | |
711 | throw bad_cmd_get(k, cmdmap); | |
11fdf7f2 | 712 | } |
20effc67 TL |
713 | } catch (boost::bad_get&) { |
714 | throw bad_cmd_get(k, cmdmap); | |
11fdf7f2 TL |
715 | } |
716 | } | |
20effc67 TL |
717 | } |
718 | ||
719 | bool cmd_getval_compat_cephbool( | |
720 | const cmdmap_t& cmdmap, | |
721 | const std::string& k, bool& val) | |
722 | { | |
723 | try { | |
724 | return cmd_getval(cmdmap, k, val); | |
725 | } catch (bad_cmd_get& e) { | |
726 | // try as legacy/compat CephChoices | |
727 | std::string t; | |
728 | if (!cmd_getval(cmdmap, k, t)) { | |
729 | return false; | |
730 | } | |
731 | std::string expected = "--"s + k; | |
732 | std::replace(expected.begin(), expected.end(), '_', '-'); | |
733 | val = (t == expected); | |
734 | return true; | |
735 | } | |
11fdf7f2 TL |
736 | } |
737 | ||
9f95a23c | 738 | } |