Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / common / cmdparse.cc
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
15 #include "json_spirit/json_spirit.h"
16 #include "common/debug.h"
17
18 using namespace std;
19
20 /**
21  * Given a cmddesc like "foo baz name=bar,type=CephString",
22  * return the prefix "foo baz".
23  */
24 std::string cmddesc_get_prefix(const std::string &cmddesc)
25 {
26   stringstream ss(cmddesc);
27   std::string word;
28   std::ostringstream result;
29   bool first = true;
30   while (std::getline(ss, word, ' ')) {
31     if (word.find_first_of(",=") != string::npos) {
32       break;
33     }
34
35     if (!first) {
36       result << " ";
37     }
38     result << word;
39     first = false;
40   }
41
42   return result.str();
43 }
44
45 /**
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.
49  */
50
51 void
52 dump_cmd_to_json(Formatter *f, const string& cmd)
53 {
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
57
58   stringstream ss(cmd);
59   std::string word;
60
61   while (std::getline(ss, word, ' ')) {
62     // if no , or =, must be a plain word to put out
63     if (word.find_first_of(",=") == string::npos) {
64       f->dump_string("arg", word);
65       continue;
66     }
67     // Snarf up all the key=val,key=val pairs, put 'em in a dict.
68     // no '=val' implies '=True'.
69     std::stringstream argdesc(word);
70     std::string keyval;
71     std::map<std::string, std::string>desckv;
72     // accumulate descriptor keywords in desckv
73
74     while (std::getline(argdesc, keyval, ',')) {
75       // key=value; key by itself implies value is bool true
76       // name="name" means arg dict will be titled 'name'
77       size_t pos = keyval.find('=');
78       std::string key, val;
79       if (pos != std::string::npos) {
80         key = keyval.substr(0, pos);
81         val = keyval.substr(pos+1);
82       } else {
83         key = keyval;
84         val = true;
85       }
86       desckv.insert(std::pair<std::string, std::string> (key, val));
87     }
88     // name the individual desc object based on the name key
89     f->open_object_section(desckv["name"].c_str());
90     // dump all the keys including name into the array
91     for (std::map<std::string, std::string>::iterator it = desckv.begin();
92          it != desckv.end(); ++it) {
93       f->dump_string(it->first.c_str(), it->second);
94     }
95     f->close_section(); // attribute object for individual desc
96   }
97 }
98
99 void
100 dump_cmd_and_help_to_json(Formatter *jf,
101                           const string& secname,
102                           const string& cmdsig,
103                           const string& helptext)
104 {
105       jf->open_object_section(secname.c_str());
106       jf->open_array_section("sig");
107       dump_cmd_to_json(jf, cmdsig);
108       jf->close_section(); // sig array
109       jf->dump_string("help", helptext.c_str());
110       jf->close_section(); // cmd
111 }
112
113 void
114 dump_cmddesc_to_json(Formatter *jf,
115                      const string& secname,
116                      const string& cmdsig,
117                      const string& helptext,
118                      const string& module,
119                      const string& perm,
120                      const string& avail,
121                      uint64_t flags)
122 {
123       jf->open_object_section(secname.c_str());
124       jf->open_array_section("sig");
125       dump_cmd_to_json(jf, cmdsig);
126       jf->close_section(); // sig array
127       jf->dump_string("help", helptext.c_str());
128       jf->dump_string("module", module.c_str());
129       jf->dump_string("perm", perm.c_str());
130       jf->dump_string("avail", avail.c_str());
131       jf->dump_int("flags", flags);
132       jf->close_section(); // cmd
133 }
134
135 void cmdmap_dump(const cmdmap_t &cmdmap, Formatter *f)
136 {
137   assert(f != nullptr);
138
139   class dump_visitor : public boost::static_visitor<void>
140   {
141     Formatter *f;
142     std::string const &key;
143     public:
144     dump_visitor(Formatter *f_, std::string const &key_)
145       : f(f_), key(key_)
146     {
147     }
148
149     void operator()(const std::string &operand) const
150     {
151       f->dump_string(key.c_str(), operand);
152     }
153
154     void operator()(const bool &operand) const
155     {
156       f->dump_bool(key.c_str(), operand);
157     }
158
159     void operator()(const int64_t &operand) const
160     {
161       f->dump_int(key.c_str(), operand);
162     }
163
164     void operator()(const double &operand) const
165     {
166       f->dump_float(key.c_str(), operand);
167     }
168
169     void operator()(const std::vector<std::string> &operand) const
170     {
171       f->open_array_section(key.c_str());
172       for (const auto i : operand) {
173         f->dump_string("item", i);
174       }
175       f->close_section();
176     }
177
178     void operator()(const std::vector<int64_t> &operand) const
179     {
180       f->open_array_section(key.c_str());
181       for (const auto i : operand) {
182         f->dump_int("item", i);
183       }
184       f->close_section();
185     }
186
187     void operator()(const std::vector<double> &operand) const
188     {
189       f->open_array_section(key.c_str());
190       for (const auto i : operand) {
191         f->dump_float("item", i);
192       }
193       f->close_section();
194     }
195   };
196
197   //f->open_object_section("cmdmap");
198   for (const auto &i : cmdmap) {
199     boost::apply_visitor(dump_visitor(f, i.first), i.second);
200   }
201   //f->close_section();
202 }
203
204
205 /** Parse JSON in vector cmd into a map from field to map of values
206  * (use mValue/mObject)
207  * 'cmd' should not disappear over lifetime of map
208  * 'mapp' points to the caller's map
209  * 'ss' captures any errors during JSON parsing; if function returns
210  * false, ss is valid */
211
212 bool
213 cmdmap_from_json(vector<string> cmd, map<string, cmd_vartype> *mapp, stringstream &ss)
214 {
215   json_spirit::mValue v;
216
217   string fullcmd;
218   // First, join all cmd strings
219   for (vector<string>::iterator it = cmd.begin();
220        it != cmd.end(); ++it)
221     fullcmd += *it;
222
223   try {
224     if (!json_spirit::read(fullcmd, v))
225       throw runtime_error("unparseable JSON " + fullcmd);
226     if (v.type() != json_spirit::obj_type)
227       throw(runtime_error("not JSON object " + fullcmd));
228
229     // allocate new mObject (map) to return
230     // make sure all contents are simple types (not arrays or objects)
231     json_spirit::mObject o = v.get_obj();
232     for (map<string, json_spirit::mValue>::iterator it = o.begin();
233          it != o.end(); ++it) {
234
235       // ok, marshal it into our string->cmd_vartype map, or throw an
236       // exception if it's not a simple datatype.  This is kind of
237       // annoying, since json_spirit has a boost::variant inside it
238       // already, but it's not public.  Oh well.
239
240       switch (it->second.type()) {
241
242       case json_spirit::obj_type:
243       default:
244         throw(runtime_error("JSON array/object not allowed " + fullcmd));
245         break;
246
247       case json_spirit::array_type:
248         {
249           // array is a vector of values.  Unpack it to a vector
250           // of strings, doubles, or int64_t, the only types we handle.
251           const vector<json_spirit::mValue>& spvals = it->second.get_array();
252           if (spvals.empty()) {
253             // if an empty array is acceptable, the caller should always check for
254             // vector<string> if the expected value of "vector<int64_t>" in the
255             // cmdmap is missing.
256             (*mapp)[it->first] = vector<string>();
257           } else if (spvals.front().type() == json_spirit::str_type) {
258             vector<string> outv;
259             for (const auto& sv : spvals) {
260               if (sv.type() != json_spirit::str_type) {
261                 throw(runtime_error("Can't handle arrays of multiple types"));
262               }
263               outv.push_back(sv.get_str());
264             }
265             (*mapp)[it->first] = std::move(outv);
266           } else if (spvals.front().type() == json_spirit::int_type) {
267             vector<int64_t> outv;
268             for (const auto& sv : spvals) {
269               if (spvals.front().type() != json_spirit::int_type) {
270                 throw(runtime_error("Can't handle arrays of multiple types"));
271               }
272               outv.push_back(sv.get_int64());
273             }
274             (*mapp)[it->first] = std::move(outv);
275           } else if (spvals.front().type() == json_spirit::real_type) {
276             vector<double> outv;
277             for (const auto& sv : spvals) {
278               if (spvals.front().type() != json_spirit::real_type) {
279                 throw(runtime_error("Can't handle arrays of multiple types"));
280               }
281               outv.push_back(sv.get_real());
282             }
283             (*mapp)[it->first] = std::move(outv);
284           } else {
285             throw(runtime_error("Can't handle arrays of types other than "
286                                 "int, string, or double"));
287           }
288         }
289         break;
290       case json_spirit::str_type:
291         (*mapp)[it->first] = it->second.get_str();
292         break;
293
294       case json_spirit::bool_type:
295         (*mapp)[it->first] = it->second.get_bool();
296         break;
297
298       case json_spirit::int_type:
299         (*mapp)[it->first] = it->second.get_int64();
300         break;
301
302       case json_spirit::real_type:
303         (*mapp)[it->first] = it->second.get_real();
304         break;
305       }
306     }
307     return true;
308   } catch (runtime_error &e) {
309     ss << e.what();
310     return false;
311   }
312 }
313
314 class stringify_visitor : public boost::static_visitor<string>
315 {
316   public:
317     template <typename T>
318     string operator()(T &operand) const
319       {
320         ostringstream oss;
321         oss << operand;
322         return oss.str();
323       }
324 };
325
326 string 
327 cmd_vartype_stringify(const cmd_vartype &v)
328 {
329   return boost::apply_visitor(stringify_visitor(), v);
330 }
331
332
333 void
334 handle_bad_get(CephContext *cct, const string& k, const char *tname)
335 {
336   ostringstream errstr;
337   int status;
338   const char *typestr = abi::__cxa_demangle(tname, 0, 0, &status);
339   if (status != 0) 
340     typestr = tname;
341   errstr << "bad boost::get: key " << k << " is not type " << typestr;
342   lderr(cct) << errstr.str() << dendl;
343
344   ostringstream oss;
345   oss << BackTrace(1);
346   lderr(cct) << oss.rdbuf() << dendl;
347   if (status == 0)
348     free((char *)typestr);
349 }
350
351 long parse_pos_long(const char *s, std::ostream *pss)
352 {
353   if (*s == '-' || *s == '+') {
354     if (pss)
355       *pss << "expected numerical value, got: " << s;
356     return -EINVAL;
357   }
358
359   string err;
360   long r = strict_strtol(s, 10, &err);
361   if ((r == 0) && !err.empty()) {
362     if (pss)
363       *pss << err;
364     return -1;
365   }
366   if (r < 0) {
367     if (pss)
368       *pss << "unable to parse positive integer '" << s << "'";
369     return -1;
370   }
371   return r;
372 }
373
374 int parse_osd_id(const char *s, std::ostream *pss)
375 {
376   // osd.NNN?
377   if (strncmp(s, "osd.", 4) == 0) {
378     s += 4;
379   }
380
381   // NNN?
382   ostringstream ss;
383   long id = parse_pos_long(s, &ss);
384   if (id < 0) {
385     *pss << ss.str();
386     return id;
387   }
388   if (id > 0xffff) {
389     *pss << "osd id " << id << " is too large";
390     return -ERANGE;
391   }
392   return id;
393 }