ostringstream secname;
secname << "cmd" << setfill('0') << std::setw(3) << cmdnum;
dump_cmd_and_help_to_json(&jf,
+ CEPH_FEATURES_ALL,
secname.str().c_str(),
info.desc,
info.help);
*/
void
-dump_cmd_to_json(Formatter *f, const string& cmd)
+dump_cmd_to_json(Formatter *f, uint64_t features, const string& cmd)
{
// put whole command signature in an already-opened container
// elements are: "name", meaning "the typeless name that means a literal"
auto desckv = cmddesc_get_args(word);
// name the individual desc object based on the name key
f->open_object_section(string(desckv["name"]).c_str());
+
+ // Compatibility for pre-nautilus clients that don't know about CephBool
+ if (!HAVE_FEATURE(features, SERVER_NAUTILUS)) {
+ auto i = desckv.find("type");
+ if (i != desckv.end() && i->second == "CephBool") {
+ // Instruct legacy clients to send --foo-bar string in place
+ // of a 'true'/'false' value
+ std::ostringstream oss;
+ oss << std::string("--") << desckv["name"];
+ std::string val = oss.str();
+ std::replace(val.begin(), val.end(), '_', '-');
+
+ desckv["type"] = "CephChoices";
+ desckv["strings"] = val;
+ }
+ }
+
// dump all the keys including name into the array
for (auto [key, value] : desckv) {
f->dump_string(string(key).c_str(), string(value));
void
dump_cmd_and_help_to_json(Formatter *jf,
+ uint64_t features,
const string& secname,
const string& cmdsig,
const string& helptext)
{
jf->open_object_section(secname.c_str());
jf->open_array_section("sig");
- dump_cmd_to_json(jf, cmdsig);
+ dump_cmd_to_json(jf, features, cmdsig);
jf->close_section(); // sig array
jf->dump_string("help", helptext.c_str());
jf->close_section(); // cmd
void
dump_cmddesc_to_json(Formatter *jf,
+ uint64_t features,
const string& secname,
const string& cmdsig,
const string& helptext,
{
jf->open_object_section(secname.c_str());
jf->open_array_section("sig");
- dump_cmd_to_json(jf, cmdsig);
+ dump_cmd_to_json(jf, features, cmdsig);
jf->close_section(); // sig array
jf->dump_string("help", helptext.c_str());
jf->dump_string("module", module.c_str());
}
});
}
+
+bool cmd_getval(CephContext *cct, const cmdmap_t& cmdmap,
+ const std::string& k, bool& val)
+{
+ /*
+ * Specialized getval for booleans. CephBool didn't exist before Nautilus,
+ * so earlier clients are sent a CephChoices argdesc instead, and will
+ * send us a "--foo-bar" value string for boolean arguments.
+ */
+ if (cmdmap.count(k)) {
+ try {
+ val = boost::get<bool>(cmdmap.find(k)->second);
+ return true;
+ } catch (boost::bad_get&) {
+ try {
+ std::string expected = "--" + k;
+ std::replace(expected.begin(), expected.end(), '_', '-');
+
+ std::string v_str = boost::get<std::string>(cmdmap.find(k)->second);
+ if (v_str == expected) {
+ val = true;
+ return true;
+ } else {
+ throw bad_cmd_get(k, cmdmap);
+ }
+ } catch (boost::bad_get&) {
+ throw bad_cmd_get(k, cmdmap);
+ }
+ }
+ }
+ return false;
+}
+
+
typedef std::map<std::string, cmd_vartype, std::less<>> cmdmap_t;
std::string cmddesc_get_prefix(const std::string &cmddesc);
-void dump_cmd_to_json(ceph::Formatter *f, const std::string& cmd);
+void dump_cmd_to_json(ceph::Formatter *f, uint64_t features,
+ const std::string& cmd);
void dump_cmd_and_help_to_json(ceph::Formatter *f,
+ uint64_t features,
const std::string& secname,
const std::string& cmd,
const std::string& helptext);
void dump_cmddesc_to_json(ceph::Formatter *jf,
+ uint64_t features,
const std::string& secname,
const std::string& cmdsig,
const std::string& helptext,
}
};
+bool cmd_getval(CephContext *cct, const cmdmap_t& cmdmap,
+ const std::string& k, bool& val);
+
template <typename T>
bool cmd_getval(CephContext *cct, const cmdmap_t& cmdmap,
const std::string& k, T& val)
for (auto& c : get_commands()) {
ostringstream secname;
secname << "cmd" << setfill('0') << std::setw(3) << cmdnum;
- dump_cmddesc_to_json(f.get(), secname.str(), c.cmdstring, c.helpstring,
+ dump_cmddesc_to_json(f.get(), m->get_connection()->get_features(),
+ secname.str(), c.cmdstring, c.helpstring,
c.module, "*", 0);
cmdnum++;
}
JSONFormatter f;
f.open_object_section("command_descriptions");
- auto dump_cmd = [&cmdnum, &f](const MonCommand &mc){
+ auto dump_cmd = [&cmdnum, &f, m](const MonCommand &mc){
ostringstream secname;
secname << "cmd" << setfill('0') << std::setw(3) << cmdnum;
- dump_cmddesc_to_json(&f, secname.str(), mc.cmdstring, mc.helpstring,
+ dump_cmddesc_to_json(&f, m->get_connection()->get_features(),
+ secname.str(), mc.cmdstring, mc.helpstring,
mc.module, mc.req_perms, 0);
cmdnum++;
};
COMMAND("osd unpause", "unpause osd", "osd", "rw")
COMMAND("osd erasure-code-profile set " \
"name=name,type=CephString,goodchars=[A-Za-z0-9-_.] " \
- "name=profile,type=CephString,n=N,req=false", \
+ "name=profile,type=CephString,n=N,req=false " \
+ "name=force,type=CephBool,req=false", \
"create erasure code profile <name> with [<key[=value]> ...] pairs. Add a --force at the end to override an existing profile (VERY DANGEROUS)", \
"osd", "rw")
COMMAND("osd erasure-code-profile get " \
COMMAND_WITH_FLAG("osd pool delete " \
"name=pool,type=CephPoolname " \
"name=pool2,type=CephPoolname,req=false " \
- "name=sure,type=CephChoices,strings=--yes-i-really-really-mean-it|" \
- "--yes-i-really-really-mean-it-not-faking,req=false", \
+ "name=yes_i_really_really_mean_it,type=CephBool,req=false "
+ "name=yes_i_really_really_mean_it_not_faking,type=CephBool,req=false ", \
"delete pool", \
"osd", "rw", \
FLAG(DEPRECATED))
COMMAND("osd pool rm " \
"name=pool,type=CephPoolname " \
"name=pool2,type=CephPoolname,req=false " \
- "name=sure,type=CephChoices,strings=--yes-i-really-really-mean-it|" \
- "--yes-i-really-really-mean-it-not-faking,req=false", \
+ "name=yes_i_really_really_mean_it,type=CephBool,req=false "
+ "name=yes_i_really_really_mean_it_not_faking,type=CephBool,req=false ", \
"remove pool", \
"osd", "rw")
COMMAND("osd pool rename " \
void Monitor::format_command_descriptions(const std::vector<MonCommand> &commands,
Formatter *f,
+ uint64_t features,
bufferlist *rdata)
{
int cmdnum = 0;
unsigned flags = cmd.flags;
ostringstream secname;
secname << "cmd" << setfill('0') << std::setw(3) << cmdnum;
- dump_cmddesc_to_json(f, secname.str(),
+ dump_cmddesc_to_json(f, features, secname.str(),
cmd.cmdstring, cmd.helpstring, cmd.module,
cmd.req_perms, flags);
cmdnum++;
}
}
- format_command_descriptions(commands, f, &rdata);
+ auto features = m->get_connection()->get_features();
+ format_command_descriptions(commands, f, features, &rdata);
delete f;
reply_command(op, 0, "", rdata, 0);
return;
public:
static void format_command_descriptions(const std::vector<MonCommand> &commands,
Formatter *f,
+ uint64_t features,
bufferlist *rdata);
const std::vector<MonCommand> &get_local_commands(mon_feature_t f) {
cmd_getval(cct, cmdmap, "name", name);
vector<string> profile;
cmd_getval(cct, cmdmap, "profile", profile);
- bool force;
- if (profile.size() > 0 && profile.back() == "--force") {
- profile.pop_back();
- force = true;
- } else {
- force = false;
- }
+
+ bool force = false;
+ cmd_getval(cct, cmdmap, "force", force);
+
map<string,string> profile_map;
err = parse_erasure_code_profile(profile, &profile_map, &ss);
if (err)
string poolstr, poolstr2, sure;
cmd_getval(cct, cmdmap, "pool", poolstr);
cmd_getval(cct, cmdmap, "pool2", poolstr2);
- cmd_getval(cct, cmdmap, "sure", sure);
int64_t pool = osdmap.lookup_pg_pool_name(poolstr.c_str());
if (pool < 0) {
ss << "pool '" << poolstr << "' does not exist";
goto reply;
}
- bool force_no_fake = sure == "--yes-i-really-really-mean-it-not-faking";
+ bool force_no_fake = false;
+ cmd_getval(cct, cmdmap, "yes_i_really_really_mean_it", force_no_fake);
+ bool force = false;
+ cmd_getval(cct, cmdmap, "yes_i_really_really_mean_it_not_faking", force);
if (poolstr2 != poolstr ||
- (sure != "--yes-i-really-really-mean-it" && !force_no_fake)) {
+ (!force && !force_no_fake)) {
ss << "WARNING: this will *PERMANENTLY DESTROY* all data stored in pool " << poolstr
<< ". If you are *ABSOLUTELY CERTAIN* that is what you want, pass the pool name *twice*, "
<< "followed by --yes-i-really-really-mean-it.";
ostringstream secname;
secname << "cmd" << setfill('0') << std::setw(3) << cmdnum;
- dump_cmddesc_to_json(f, secname.str(), cp->cmdstring, cp->helpstring,
+ dump_cmddesc_to_json(f, con->get_features(),
+ secname.str(), cp->cmdstring, cp->helpstring,
cp->module, cp->perm, 0);
cmdnum++;
}
return all_elems
+class CephBool(CephArgtype):
+ """
+ A boolean argument, values may be case insensitive 'true', 'false', '0',
+ '1'. In keyword form, value may be left off (implies true).
+ """
+ def __init__(self, strings='', **kwargs):
+ self.strings = strings.split('|')
+
+ def valid(self, s, partial=False):
+ lower_case = s.lower()
+ if lower_case in ['true', '1']:
+ self.val = True
+ elif lower_case in ['false', '0']:
+ self.val = False
+ else:
+ raise ArgumentValid("{0} not one of 'true', 'false'".format(s))
+
+ def __str__(self):
+ return '<bool>'
+
+
class CephFilepath(CephArgtype):
"""
Openable file
"""
if self.t == CephString:
chunk = '<{0}>'.format(self.name)
+ elif self.t == CephBool:
+ chunk = "--{0}".format(self.name.replace("_", "-"))
else:
chunk = str(self.instance)
s = chunk
# A keyword argument?
if myarg:
-
# argdesc for the keyword argument, if we find one
kwarg_desc = None
if kwarg_match:
# We have a "--foo=bar" style argument
kwarg_k, kwarg_v = kwarg_match.groups()
+
+ # Either "--foo-bar" or "--foo_bar" style is accepted
+ kwarg_k = kwarg_k.replace('-', '_')
+
kwarg_desc = arg_descs_by_name.get(kwarg_k, None)
- elif len(myargs): # Some trailing arguments exist
- # Maybe this is a "--foo bar" style argument
+ else:
+ # Maybe this is a "--foo bar" or "--bool" style argument
key_match = re.match(KWARG_SPACE, myarg)
if key_match:
kwarg_k = key_match.group(1)
+
+ # Permit --foo-bar=123 form or --foo_bar=123 form,
+ # assuming all command definitions use foo_bar argument
+ # naming style
+ kwarg_k = kwarg_k.replace('-', '_')
+
kwarg_desc = arg_descs_by_name.get(kwarg_k, None)
if kwarg_desc:
- kwarg_v = myargs.pop(0)
+ if kwarg_desc.t == CephBool:
+ kwarg_v = 'true'
+ elif len(myargs): # Some trailing arguments exist
+ kwarg_v = myargs.pop(0)
+ else:
+ # Forget it, this is not a valid kwarg
+ kwarg_desc = None
if kwarg_desc:
validate_one(kwarg_v, kwarg_desc)
# "--yes-i-really-mean-it")
if myarg and myarg.startswith("--"):
# Special cases for instances of confirmation flags
- # that were defined as CephString instead of CephChoices
+ # that were defined as CephString/CephChoices instead of CephBool
# in pre-nautilus versions of Ceph daemons.
is_value = desc.t == CephChoices \
or myarg == "--yes-i-really-mean-it" \
or myarg == "--yes-i-really-really-mean-it" \
or myarg == "--yes-i-really-really-mean-it-not-faking" \
+ or myarg == "--force" \
or injectargs
if not is_value:
- sys.stderr.write("desc={0} t={1}".format(
- desc, desc.t))
-
# Didn't get caught by kwarg handling, but has a "--", so
# we must assume it's something invalid, to avoid naively
# passing through mis-typed options as the values of
{
bufferlist rdata;
Formatter *f = Formatter::create("json");
- Monitor::format_command_descriptions(mon_commands, f, &rdata);
+ Monitor::format_command_descriptions(mon_commands, f,
+ CEPH_FEATURES_ALL, &rdata);
delete f;
string data(rdata.c_str(), rdata.length());
cout << data << std::endl;
assert_equal({}, validate_command(sigdict,
['osd', 'pool', 'create', "foo", "8", "--foo=bar"]))
+ def test_foo(self):
+ # Long form of a boolean argument (--foo=true)
+ assert_equal(
+ {
+ "prefix": "osd pool delete",
+ "pool": "foo",
+ "pool2": "foo",
+ "yes_i_really_really_mean_it": True
+ }, validate_command(sigdict, [
+ 'osd', 'pool', 'delete', "foo", "foo",
+ "--yes-i-really-really-mean-it=true"]))
+
+ def test_pool_bool_args(self):
+ """
+ Use pool deletion to exercise boolean arguments since it has
+ the --yes-i-really-really-mean-it flags
+ """
+
+ # Short form of a boolean argument (--foo)
+ assert_equal(
+ {
+ "prefix": "osd pool delete",
+ "pool": "foo",
+ "pool2": "foo",
+ "yes_i_really_really_mean_it": True
+ }, validate_command(sigdict, [
+ 'osd', 'pool', 'delete', "foo", "foo",
+ "--yes-i-really-really-mean-it"]))
+
+ # Long form of a boolean argument (--foo=true)
+ assert_equal(
+ {
+ "prefix": "osd pool delete",
+ "pool": "foo",
+ "pool2": "foo",
+ "yes_i_really_really_mean_it": True
+ }, validate_command(sigdict, [
+ 'osd', 'pool', 'delete', "foo", "foo",
+ "--yes-i-really-really-mean-it=true"]))
+
+ # Negative form of a boolean argument (--foo=false)
+ assert_equal(
+ {
+ "prefix": "osd pool delete",
+ "pool": "foo",
+ "pool2": "foo",
+ "yes_i_really_really_mean_it": False
+ }, validate_command(sigdict, [
+ 'osd', 'pool', 'delete', "foo", "foo",
+ "--yes-i-really-really-mean-it=false"]))
+
+ # Invalid value boolean argument (--foo=somethingelse)
+ assert_equal({}, validate_command(sigdict, [
+ 'osd', 'pool', 'delete', "foo", "foo",
+ "--yes-i-really-really-mean-it=rhubarb"]))
+
def test_pool_create(self):
self.assert_valid_command(['osd', 'pool', 'create',
'poolname', '128'])