]>
git.proxmox.com Git - ceph.git/blob - ceph/src/common/cmdparse.cc
592b889b4597f83ab3decc9a162f75da1aba5c47
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2013 Inktank Storage, Inc.
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.
15 #include "json_spirit/json_spirit.h"
16 #include "common/debug.h"
21 * Given a cmddesc like "foo baz name=bar,type=CephString",
22 * return the prefix "foo baz".
24 std::string
cmddesc_get_prefix(const std::string
&cmddesc
)
26 stringstream
ss(cmddesc
);
28 std::ostringstream result
;
30 while (std::getline(ss
, word
, ' ')) {
31 if (word
.find_first_of(",=") != string::npos
) {
46 * Read a command description list out of cmd, and dump it to f.
47 * A signature description is a set of space-separated words;
48 * see MonCommands.h for more info.
52 dump_cmd_to_json(Formatter
*f
, const string
& cmd
)
54 // put whole command signature in an already-opened container
55 // elements are: "name", meaning "the typeless name that means a literal"
56 // an object {} with key:value pairs representing an argument
62 while (std::getline(ss
, word
, ' ')) {
64 // if no , or =, must be a plain word to put out
65 if (word
.find_first_of(",=") == string::npos
) {
66 f
->dump_string("arg", word
);
69 // Snarf up all the key=val,key=val pairs, put 'em in a dict.
70 // no '=val' implies '=True'.
71 std::stringstream
argdesc(word
);
73 std::map
<std::string
, std::string
>desckv
;
74 // accumulate descriptor keywords in desckv
76 while (std::getline(argdesc
, keyval
, ',')) {
77 // key=value; key by itself implies value is bool true
78 // name="name" means arg dict will be titled 'name'
79 size_t pos
= keyval
.find('=');
81 if (pos
!= std::string::npos
) {
82 key
= keyval
.substr(0, pos
);
83 val
= keyval
.substr(pos
+1);
88 desckv
.insert(std::pair
<std::string
, std::string
> (key
, val
));
90 // name the individual desc object based on the name key
91 f
->open_object_section(desckv
["name"].c_str());
92 // dump all the keys including name into the array
93 for (std::map
<std::string
, std::string
>::iterator it
= desckv
.begin();
94 it
!= desckv
.end(); ++it
) {
95 f
->dump_string(it
->first
.c_str(), it
->second
);
97 f
->close_section(); // attribute object for individual desc
102 dump_cmd_and_help_to_json(Formatter
*jf
,
103 const string
& secname
,
104 const string
& cmdsig
,
105 const string
& helptext
)
107 jf
->open_object_section(secname
.c_str());
108 jf
->open_array_section("sig");
109 dump_cmd_to_json(jf
, cmdsig
);
110 jf
->close_section(); // sig array
111 jf
->dump_string("help", helptext
.c_str());
112 jf
->close_section(); // cmd
116 dump_cmddesc_to_json(Formatter
*jf
,
117 const string
& secname
,
118 const string
& cmdsig
,
119 const string
& helptext
,
120 const string
& module
,
125 jf
->open_object_section(secname
.c_str());
126 jf
->open_array_section("sig");
127 dump_cmd_to_json(jf
, cmdsig
);
128 jf
->close_section(); // sig array
129 jf
->dump_string("help", helptext
.c_str());
130 jf
->dump_string("module", module
.c_str());
131 jf
->dump_string("perm", perm
.c_str());
132 jf
->dump_string("avail", avail
.c_str());
133 jf
->dump_int("flags", flags
);
134 jf
->close_section(); // cmd
137 void cmdmap_dump(const cmdmap_t
&cmdmap
, Formatter
*f
)
139 assert(f
!= nullptr);
141 class dump_visitor
: public boost::static_visitor
<void>
144 std::string
const &key
;
146 dump_visitor(Formatter
*f_
, std::string
const &key_
)
151 void operator()(const std::string
&operand
) const
153 f
->dump_string(key
.c_str(), operand
);
156 void operator()(const bool &operand
) const
158 f
->dump_bool(key
.c_str(), operand
);
161 void operator()(const int64_t &operand
) const
163 f
->dump_int(key
.c_str(), operand
);
166 void operator()(const double &operand
) const
168 f
->dump_float(key
.c_str(), operand
);
171 void operator()(const std::vector
<std::string
> &operand
) const
173 f
->open_array_section(key
.c_str());
174 for (const auto i
: operand
) {
175 f
->dump_string("item", i
);
180 void operator()(const std::vector
<int64_t> &operand
) const
182 f
->open_array_section(key
.c_str());
183 for (const auto i
: operand
) {
184 f
->dump_int("item", i
);
189 void operator()(const std::vector
<double> &operand
) const
191 f
->open_array_section(key
.c_str());
192 for (const auto i
: operand
) {
193 f
->dump_float("item", i
);
199 //f->open_object_section("cmdmap");
200 for (const auto &i
: cmdmap
) {
201 boost::apply_visitor(dump_visitor(f
, i
.first
), i
.second
);
203 //f->close_section();
207 /** Parse JSON in vector cmd into a map from field to map of values
208 * (use mValue/mObject)
209 * 'cmd' should not disappear over lifetime of map
210 * 'mapp' points to the caller's map
211 * 'ss' captures any errors during JSON parsing; if function returns
212 * false, ss is valid */
215 cmdmap_from_json(vector
<string
> cmd
, map
<string
, cmd_vartype
> *mapp
, stringstream
&ss
)
217 json_spirit::mValue v
;
220 // First, join all cmd strings
221 for (vector
<string
>::iterator it
= cmd
.begin();
222 it
!= cmd
.end(); ++it
)
226 if (!json_spirit::read(fullcmd
, v
))
227 throw runtime_error("unparseable JSON " + fullcmd
);
228 if (v
.type() != json_spirit::obj_type
)
229 throw(runtime_error("not JSON object " + fullcmd
));
231 // allocate new mObject (map) to return
232 // make sure all contents are simple types (not arrays or objects)
233 json_spirit::mObject o
= v
.get_obj();
234 for (map
<string
, json_spirit::mValue
>::iterator it
= o
.begin();
235 it
!= o
.end(); ++it
) {
237 // ok, marshal it into our string->cmd_vartype map, or throw an
238 // exception if it's not a simple datatype. This is kind of
239 // annoying, since json_spirit has a boost::variant inside it
240 // already, but it's not public. Oh well.
242 switch (it
->second
.type()) {
244 case json_spirit::obj_type
:
246 throw(runtime_error("JSON array/object not allowed " + fullcmd
));
249 case json_spirit::array_type
:
251 // array is a vector of values. Unpack it to a vector
252 // of strings, doubles, or int64_t, the only types we handle.
253 const vector
<json_spirit::mValue
>& spvals
= it
->second
.get_array();
254 if (spvals
.empty()) {
255 // if an empty array is acceptable, the caller should always check for
256 // vector<string> if the expected value of "vector<int64_t>" in the
257 // cmdmap is missing.
258 (*mapp
)[it
->first
] = vector
<string
>();
259 } else if (spvals
.front().type() == json_spirit::str_type
) {
261 for (const auto& sv
: spvals
) {
262 if (sv
.type() != json_spirit::str_type
) {
263 throw(runtime_error("Can't handle arrays of multiple types"));
265 outv
.push_back(sv
.get_str());
267 (*mapp
)[it
->first
] = std::move(outv
);
268 } else if (spvals
.front().type() == json_spirit::int_type
) {
269 vector
<int64_t> outv
;
270 for (const auto& sv
: spvals
) {
271 if (spvals
.front().type() != json_spirit::int_type
) {
272 throw(runtime_error("Can't handle arrays of multiple types"));
274 outv
.push_back(sv
.get_int64());
276 (*mapp
)[it
->first
] = std::move(outv
);
277 } else if (spvals
.front().type() == json_spirit::real_type
) {
279 for (const auto& sv
: spvals
) {
280 if (spvals
.front().type() != json_spirit::real_type
) {
281 throw(runtime_error("Can't handle arrays of multiple types"));
283 outv
.push_back(sv
.get_real());
285 (*mapp
)[it
->first
] = std::move(outv
);
287 throw(runtime_error("Can't handle arrays of types other than "
288 "int, string, or double"));
292 case json_spirit::str_type
:
293 (*mapp
)[it
->first
] = it
->second
.get_str();
296 case json_spirit::bool_type
:
297 (*mapp
)[it
->first
] = it
->second
.get_bool();
300 case json_spirit::int_type
:
301 (*mapp
)[it
->first
] = it
->second
.get_int64();
304 case json_spirit::real_type
:
305 (*mapp
)[it
->first
] = it
->second
.get_real();
310 } catch (runtime_error
&e
) {
316 class stringify_visitor
: public boost::static_visitor
<string
>
319 template <typename T
>
320 string
operator()(T
&operand
) const
329 cmd_vartype_stringify(const cmd_vartype
&v
)
331 return boost::apply_visitor(stringify_visitor(), v
);
336 handle_bad_get(CephContext
*cct
, const string
& k
, const char *tname
)
338 ostringstream errstr
;
340 const char *typestr
= abi::__cxa_demangle(tname
, 0, 0, &status
);
343 errstr
<< "bad boost::get: key " << k
<< " is not type " << typestr
;
344 lderr(cct
) << errstr
.str() << dendl
;
348 lderr(cct
) << oss
.rdbuf() << dendl
;
350 free((char *)typestr
);
353 long parse_pos_long(const char *s
, std::ostream
*pss
)
355 if (*s
== '-' || *s
== '+') {
357 *pss
<< "expected numerical value, got: " << s
;
362 long r
= strict_strtol(s
, 10, &err
);
363 if ((r
== 0) && !err
.empty()) {
370 *pss
<< "unable to parse positive integer '" << s
<< "'";
376 int parse_osd_id(const char *s
, std::ostream
*pss
)
379 if (strncmp(s
, "osd.", 4) == 0) {
385 long id
= parse_pos_long(s
, &ss
);
391 *pss
<< "osd id " << id
<< " is too large";