mirror of
https://github.com/ceph/ceph
synced 2025-02-20 17:37:29 +00:00
Merge PR #41509 into master
* refs/pull/41509/head: common/cmdparse: fix CephBool validation for tell commands mgr/nfs: fix 'nfs export create' argument order common/cmdparse: emit proper json mon/MonCommands: add -- seperator to example qa/tasks/cephfs/test_nfs: fix export create test mgr: make mgr commands compat with pre-quincy mon doc/_ext/ceph_commands: handle non-positional args in docs mgr: fix reweight-by-utilization cephbool flag mon/MonCommands: convert some CephChoices to CephBool mgr/k8sevents: fix help strings pybind/mgr/mgr_module: fix help desc formatting mgr/orchestrator: clean up 'orch {daemon add,apply} rgw' args mgr/orchestrator: add end_positional to a few methods mgr/orchestrator: reformat a few methods pybind/ceph_argparse: stop parsing when we run out of positional args pybind/ceph_argparse: remove dead code pybind/mgr/mgr_module: infer non-positional args pybind/mgr/mgr_module: add separator for non-positional args command/cmdparse: use -- to separate positional from non-positional args pybind/ceph_argparse: adjust help text for non-positional args pybind/ceph_argparse: track a 'positional' property on cli args Reviewed-by: Kefu Chai <kchai@redhat.com>
This commit is contained in:
commit
b18427da4b
@ -82,7 +82,7 @@ class CmdParam(object):
|
||||
|
||||
def __init__(self, type, name,
|
||||
who=None, n=None, req=True, range=None, strings=None,
|
||||
goodchars=None):
|
||||
goodchars=None, positional=True):
|
||||
self.type = type
|
||||
self.name = name
|
||||
self.who = who
|
||||
@ -91,6 +91,7 @@ class CmdParam(object):
|
||||
self.range = range.split('|') if range else []
|
||||
self.strings = strings.split('|') if strings else []
|
||||
self.goodchars = goodchars
|
||||
self.positional = positional != 'false'
|
||||
|
||||
assert who == None
|
||||
|
||||
@ -200,7 +201,7 @@ TEMPLATE = '''
|
||||
|
||||
{%- if command.params %}
|
||||
:Parameters:{% for param in command.params -%}
|
||||
{{" -" | indent(12, not loop.first) }} **{{param.name}}**: {{ param.help() }}
|
||||
{{" -" | indent(12, not loop.first) }} **{% if param.positional %}{{param.name}}{% else %}--{{param.name}}{% endif %}**: {{ param.help() }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
:Ceph Module: {{ command.module }}
|
||||
|
@ -348,7 +348,8 @@ class TestNFS(MgrTestCase):
|
||||
# Export-3 for subvolume with r only
|
||||
self._cmd('fs', 'subvolume', 'create', self.fs_name, 'sub_vol')
|
||||
fs_path = self._cmd('fs', 'subvolume', 'getpath', self.fs_name, 'sub_vol').strip()
|
||||
self._create_export(export_id='3', extra_cmd=[self.pseudo_path+'2', '--readonly', fs_path])
|
||||
self._create_export(export_id='3', extra_cmd=[self.pseudo_path+'2', '--readonly',
|
||||
'--path', fs_path])
|
||||
# Export-4 for subvolume
|
||||
self._create_export(export_id='4', extra_cmd=[self.pseudo_path+'3', fs_path])
|
||||
# Check if exports gets listed
|
||||
|
@ -138,13 +138,20 @@ dump_cmd_to_json(Formatter *f, uint64_t features, const string& cmd)
|
||||
|
||||
stringstream ss(cmd);
|
||||
std::string word;
|
||||
bool positional = true;
|
||||
|
||||
while (std::getline(ss, word, ' ')) {
|
||||
if (word == "--") {
|
||||
positional = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// if no , or =, must be a plain word to put out
|
||||
if (word.find_first_of(",=") == string::npos) {
|
||||
f->dump_string("arg", word);
|
||||
continue;
|
||||
}
|
||||
|
||||
// accumulate descriptor keywords in desckv
|
||||
auto desckv = cmddesc_get_args(word);
|
||||
// name the individual desc object based on the name key
|
||||
@ -168,8 +175,20 @@ dump_cmd_to_json(Formatter *f, uint64_t features, const string& cmd)
|
||||
}
|
||||
|
||||
// dump all the keys including name into the array
|
||||
if (!positional) {
|
||||
desckv["positional"] = "false";
|
||||
}
|
||||
for (auto [key, value] : desckv) {
|
||||
f->dump_string(key, value);
|
||||
if (key == "positional") {
|
||||
if (!HAVE_FEATURE(features, SERVER_QUINCY)) {
|
||||
continue;
|
||||
}
|
||||
f->dump_bool(key, value == "true" || value == "True");
|
||||
} else if (key == "req" && HAVE_FEATURE(features, SERVER_QUINCY)) {
|
||||
f->dump_bool(key, value == "true" || value == "True");
|
||||
} else {
|
||||
f->dump_string(key, value);
|
||||
}
|
||||
}
|
||||
f->close_section(); // attribute object for individual desc
|
||||
}
|
||||
@ -550,6 +569,30 @@ bool validate_str_arg(std::string_view value,
|
||||
}
|
||||
}
|
||||
|
||||
bool validate_bool(CephContext *cct,
|
||||
const cmdmap_t& cmdmap,
|
||||
const arg_desc_t& desc,
|
||||
const std::string_view name,
|
||||
const std::string_view type,
|
||||
std::ostream& os)
|
||||
{
|
||||
bool v;
|
||||
try {
|
||||
if (!cmd_getval(cmdmap, string(name), v)) {
|
||||
if (auto req = desc.find("req");
|
||||
req != end(desc) && req->second == "false") {
|
||||
return true;
|
||||
} else {
|
||||
os << "missing required parameter: '" << name << "'";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (const bad_cmd_get& e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool is_vector,
|
||||
typename T,
|
||||
typename Value = std::conditional_t<is_vector,
|
||||
@ -629,6 +672,9 @@ bool validate_cmd(CephContext* cct,
|
||||
} else if (type == "CephFloat") {
|
||||
return !validate_arg<false, double>(cct, cmdmap, arg_desc,
|
||||
name, type, os);
|
||||
} else if (type == "CephBool") {
|
||||
return !validate_bool(cct, cmdmap, arg_desc,
|
||||
name, type, os);
|
||||
} else {
|
||||
return !validate_arg<false, string>(cct, cmdmap, arg_desc,
|
||||
name, type, os);
|
||||
@ -671,4 +717,23 @@ bool cmd_getval(const cmdmap_t& cmdmap,
|
||||
}
|
||||
}
|
||||
|
||||
bool cmd_getval_compat_cephbool(
|
||||
const cmdmap_t& cmdmap,
|
||||
const std::string& k, bool& val)
|
||||
{
|
||||
try {
|
||||
return cmd_getval(cmdmap, k, val);
|
||||
} catch (bad_cmd_get& e) {
|
||||
// try as legacy/compat CephChoices
|
||||
std::string t;
|
||||
if (!cmd_getval(cmdmap, k, t)) {
|
||||
return false;
|
||||
}
|
||||
std::string expected = "--"s + k;
|
||||
std::replace(expected.begin(), expected.end(), '_', '-');
|
||||
val = (t == expected);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -62,6 +62,10 @@ struct bad_cmd_get : public std::exception {
|
||||
bool cmd_getval(const cmdmap_t& cmdmap,
|
||||
std::string_view k, bool& val);
|
||||
|
||||
bool cmd_getval_compat_cephbool(
|
||||
const cmdmap_t& cmdmap,
|
||||
const std::string& k, bool& val);
|
||||
|
||||
template <typename T>
|
||||
bool cmd_getval(const cmdmap_t& cmdmap,
|
||||
std::string_view k, T& val)
|
||||
|
@ -1442,7 +1442,7 @@ bool DaemonServer::_handle_command(
|
||||
return true;
|
||||
}
|
||||
bool no_increasing = false;
|
||||
cmd_getval(cmdctx->cmdmap, "no_increasing", no_increasing);
|
||||
cmd_getval_compat_cephbool(cmdctx->cmdmap, "no_increasing", no_increasing);
|
||||
string out_str;
|
||||
mempool::osdmap::map<int32_t, uint32_t> new_weights;
|
||||
r = cluster_state.with_osdmap_and_pgmap([&](const OSDMap &osdmap, const PGMap& pgmap) {
|
||||
|
@ -111,7 +111,7 @@ COMMAND("osd reweight-by-utilization " \
|
||||
"name=oload,type=CephInt,req=false " \
|
||||
"name=max_change,type=CephFloat,req=false " \
|
||||
"name=max_osds,type=CephInt,req=false " \
|
||||
"name=no_increasing,type=CephChoices,strings=--no-increasing,req=false",\
|
||||
"name=no_increasing,type=CephBool,req=false",\
|
||||
"reweight OSDs by utilization [overload-percentage-for-consideration, default 120]", \
|
||||
"osd", "rw")
|
||||
COMMAND("osd test-reweight-by-utilization " \
|
||||
@ -181,7 +181,7 @@ COMMAND("service status",
|
||||
"dump service state", "service", "r")
|
||||
|
||||
COMMAND("config show " \
|
||||
"name=who,type=CephString name=key,type=CephString,req=False",
|
||||
"name=who,type=CephString name=key,type=CephString,req=false",
|
||||
"Show running configuration",
|
||||
"mgr", "r")
|
||||
COMMAND("config show-with-defaults " \
|
||||
@ -203,7 +203,7 @@ COMMAND("device ls-by-host name=host,type=CephString",
|
||||
"mgr", "r")
|
||||
COMMAND("device set-life-expectancy name=devid,type=CephString "\
|
||||
"name=from,type=CephString "\
|
||||
"name=to,type=CephString,req=False",
|
||||
"name=to,type=CephString,req=false",
|
||||
"Set predicted device life expectancy",
|
||||
"mgr", "rw")
|
||||
COMMAND("device rm-life-expectancy name=devid,type=CephString",
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
#include "common/errno.h"
|
||||
#include "common/signal.h"
|
||||
@ -258,6 +259,12 @@ void MgrStandby::send_beacon()
|
||||
std::vector<MonCommand> commands = mgr_commands;
|
||||
std::vector<MonCommand> py_commands = py_module_registry.get_commands();
|
||||
commands.insert(commands.end(), py_commands.begin(), py_commands.end());
|
||||
if (monc.monmap.min_mon_release < ceph_release_t::quincy) {
|
||||
dout(10) << " stripping out positional=false quincy-ism" << dendl;
|
||||
for (auto& i : commands) {
|
||||
boost::replace_all(i.cmdstring, ",positional=false", "");
|
||||
}
|
||||
}
|
||||
m->set_command_descs(commands);
|
||||
dout(4) << "going active, including " << m->get_command_descs().size()
|
||||
<< " commands in beacon" << dendl;
|
||||
|
@ -1184,10 +1184,10 @@ bool MgrMonitor::prepare_command(MonOpRequestRef op)
|
||||
ss << "module '" << module << "' is already enabled (always-on)";
|
||||
goto out;
|
||||
}
|
||||
string force;
|
||||
cmd_getval(cmdmap, "force", force);
|
||||
bool force = false;
|
||||
cmd_getval_compat_cephbool(cmdmap, "force", force);
|
||||
if (!pending_map.all_support_module(module) &&
|
||||
force != "--force") {
|
||||
!force) {
|
||||
ss << "all mgr daemons do not support module '" << module << "', pass "
|
||||
<< "--force to force enablement";
|
||||
r = -ENOENT;
|
||||
@ -1195,7 +1195,7 @@ bool MgrMonitor::prepare_command(MonOpRequestRef op)
|
||||
}
|
||||
|
||||
std::string can_run_error;
|
||||
if (force != "--force" && !pending_map.can_run_module(module, &can_run_error)) {
|
||||
if (!force && !pending_map.can_run_module(module, &can_run_error)) {
|
||||
ss << "module '" << module << "' reports that it cannot run on the active "
|
||||
"manager daemon: " << can_run_error << " (pass --force to force "
|
||||
"enablement)";
|
||||
|
@ -77,7 +77,9 @@
|
||||
*
|
||||
* COMMAND("auth add "
|
||||
* "name=entity,type=CephString "
|
||||
* "name=caps,type=CephString,n=N,req=false",
|
||||
* "name=caps,type=CephString,n=N,req=false "
|
||||
* "-- "
|
||||
* "name=some_option,type=CephString,req=false",
|
||||
* "add auth info for <name> from input file, or random key "
|
||||
* "if no input given, and/or any caps specified in the command")
|
||||
*
|
||||
@ -88,6 +90,12 @@
|
||||
* enters auth add client.admin 'mon rwx' 'osd *'. The result will be a
|
||||
* JSON object like {"prefix":"auth add", "entity":"client.admin",
|
||||
* "caps":["mon rwx", "osd *"]}.
|
||||
*
|
||||
* The -- separates positional from non-positional (and, by implication,
|
||||
* optional) arguments. Note that CephBool is assumed to be non-positional
|
||||
* and will also implicitly mark that any following arguments are
|
||||
* non-positional.
|
||||
*
|
||||
* Note that
|
||||
* - string literals are accumulated into 'prefix'
|
||||
* - n=1 descriptors are given normal string or int object values
|
||||
@ -474,7 +482,7 @@ COMMAND_WITH_FLAG("mon remove "
|
||||
"remove monitor named <name>", "mon", "rw",
|
||||
FLAG(DEPRECATED))
|
||||
COMMAND("mon feature ls "
|
||||
"name=with_value,type=CephChoices,strings=--with-value,req=false",
|
||||
"name=with_value,type=CephBool,req=false",
|
||||
"list available mon map features to be set/unset",
|
||||
"mon", "r")
|
||||
COMMAND("mon feature set "
|
||||
@ -750,7 +758,7 @@ COMMAND("osd crush rule rename "
|
||||
"rename crush rule <srcname> to <dstname>",
|
||||
"osd", "rw")
|
||||
COMMAND("osd crush tree "
|
||||
"name=shadow,type=CephChoices,strings=--show-shadow,req=false",
|
||||
"name=show_shadow,type=CephBool,req=false",
|
||||
"dump crush buckets and items in a tree view",
|
||||
"osd", "r")
|
||||
COMMAND("osd crush ls name=node,type=CephString,goodchars=[A-Za-z0-9-_.]",
|
||||
@ -1166,7 +1174,7 @@ COMMAND("osd force_recovery_stretch_mode " \
|
||||
COMMAND("osd tier add "
|
||||
"name=pool,type=CephPoolname "
|
||||
"name=tierpool,type=CephPoolname "
|
||||
"name=force_nonempty,type=CephChoices,strings=--force-nonempty,req=false",
|
||||
"name=force_nonempty,type=CephBool,req=false",
|
||||
"add the tier <tierpool> (the second one) to base pool <pool> (the first one)",
|
||||
"osd", "rw")
|
||||
COMMAND("osd tier rm "
|
||||
@ -1256,7 +1264,7 @@ COMMAND("mgr services",
|
||||
"mgr", "r")
|
||||
COMMAND("mgr module enable "
|
||||
"name=module,type=CephString "
|
||||
"name=force,type=CephChoices,strings=--force,req=false",
|
||||
"name=force,type=CephBool,req=false",
|
||||
"enable mgr module", "mgr", "rw")
|
||||
COMMAND("mgr module disable "
|
||||
"name=module,type=CephString",
|
||||
@ -1286,7 +1294,7 @@ COMMAND("config rm"
|
||||
"config", "rw")
|
||||
COMMAND("config get "
|
||||
"name=who,type=CephString "
|
||||
"name=key,type=CephString,req=False",
|
||||
"name=key,type=CephString,req=false",
|
||||
"Show configuration option(s) for an entity",
|
||||
"config", "r")
|
||||
COMMAND("config dump",
|
||||
@ -1302,7 +1310,7 @@ COMMAND("config ls",
|
||||
COMMAND("config assimilate-conf",
|
||||
"Assimilate options from a conf, and return a new, minimal conf file",
|
||||
"config", "rw")
|
||||
COMMAND("config log name=num,type=CephInt,req=False",
|
||||
COMMAND("config log name=num,type=CephInt,req=false",
|
||||
"Show recent history of config changes",
|
||||
"config", "r")
|
||||
COMMAND("config reset "
|
||||
@ -1352,7 +1360,7 @@ COMMAND_WITH_FLAG("connection scores reset",
|
||||
"mon", "rwx",
|
||||
FLAG(TELL))
|
||||
COMMAND_WITH_FLAG("sync_force "
|
||||
"name=validate,type=CephChoices,strings=--yes-i-really-mean-it,req=false",
|
||||
"name=yes_i_really_mean_it,type=CephBool,req=false",
|
||||
"force sync of and clear monitor store",
|
||||
"mon", "rw",
|
||||
FLAG(TELL))
|
||||
|
@ -337,9 +337,15 @@ int Monitor::do_admin_command(
|
||||
} else if (command == "quorum_status") {
|
||||
_quorum_status(f, out);
|
||||
} else if (command == "sync_force") {
|
||||
string validate;
|
||||
if ((!cmd_getval(cmdmap, "validate", validate)) ||
|
||||
(validate != "--yes-i-really-mean-it")) {
|
||||
bool validate = false;
|
||||
if (!cmd_getval(cmdmap, "yes_i_really_mean_it", validate)) {
|
||||
std::string v;
|
||||
if (cmd_getval(cmdmap, "validate", v) &&
|
||||
v == "--yes-i-really-mean-it") {
|
||||
validate = true;
|
||||
}
|
||||
}
|
||||
if (!validate) {
|
||||
err << "are you SURE? this will mean the monitor store will be erased "
|
||||
"the next time the monitor is restarted. pass "
|
||||
"'--yes-i-really-mean-it' if you really do.";
|
||||
|
@ -395,11 +395,7 @@ bool MonmapMonitor::preprocess_command(MonOpRequestRef op)
|
||||
} else if (prefix == "mon feature ls") {
|
||||
|
||||
bool list_with_value = false;
|
||||
string with_value;
|
||||
if (cmd_getval(cmdmap, "with_value", with_value) &&
|
||||
with_value == "--with-value") {
|
||||
list_with_value = true;
|
||||
}
|
||||
cmd_getval_compat_cephbool(cmdmap, "with_value", list_with_value);
|
||||
|
||||
MonMap *p = mon.monmap;
|
||||
|
||||
|
@ -6652,9 +6652,14 @@ bool OSDMonitor::preprocess_command(MonOpRequestRef op)
|
||||
rs << "\n";
|
||||
rdata.append(rs.str());
|
||||
} else if (prefix == "osd crush tree") {
|
||||
string shadow;
|
||||
cmd_getval(cmdmap, "shadow", shadow);
|
||||
bool show_shadow = shadow == "--show-shadow";
|
||||
bool show_shadow = false;
|
||||
if (!cmd_getval_compat_cephbool(cmdmap, "show_shadow", show_shadow)) {
|
||||
std::string shadow;
|
||||
if (cmd_getval(cmdmap, "shadow", shadow) &&
|
||||
shadow == "--show-shadow") {
|
||||
show_shadow = true;
|
||||
}
|
||||
}
|
||||
boost::scoped_ptr<Formatter> f(Formatter::create(format));
|
||||
if (f) {
|
||||
f->open_object_section("crush_tree");
|
||||
@ -12951,11 +12956,11 @@ bool OSDMonitor::prepare_command_impl(MonOpRequestRef op,
|
||||
}
|
||||
|
||||
// make sure new tier is empty
|
||||
string force_nonempty;
|
||||
cmd_getval(cmdmap, "force_nonempty", force_nonempty);
|
||||
bool force_nonempty = false;
|
||||
cmd_getval_compat_cephbool(cmdmap, "force_nonempty", force_nonempty);
|
||||
const pool_stat_t *pstats = mon.mgrstatmon()->get_pool_stat(tierpool_id);
|
||||
if (pstats && pstats->stats.sum.num_objects != 0 &&
|
||||
force_nonempty != "--force-nonempty") {
|
||||
!force_nonempty) {
|
||||
ss << "tier pool '" << tierpoolstr << "' is not empty; --force-nonempty to force";
|
||||
err = -ENOTEMPTY;
|
||||
goto reply;
|
||||
@ -12967,8 +12972,8 @@ bool OSDMonitor::prepare_command_impl(MonOpRequestRef op,
|
||||
goto reply;
|
||||
}
|
||||
if ((!tp->removed_snaps.empty() || !tp->snaps.empty()) &&
|
||||
((force_nonempty != "--force-nonempty") ||
|
||||
(!g_conf()->mon_debug_unsafe_allow_tier_with_nonempty_snaps))) {
|
||||
(!force_nonempty ||
|
||||
!g_conf()->mon_debug_unsafe_allow_tier_with_nonempty_snaps)) {
|
||||
ss << "tier pool '" << tierpoolstr << "' has snapshot state; it cannot be added as a tier without breaking the pool";
|
||||
err = -ENOTEMPTY;
|
||||
goto reply;
|
||||
|
@ -168,32 +168,34 @@ class CephArgtype(object):
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def _compound_type_to_argdesc(tp, attrs):
|
||||
def _compound_type_to_argdesc(tp, attrs, positional):
|
||||
# generate argdesc from Sequence[T], Tuple[T,..] and Optional[T]
|
||||
orig_type = get_origin(tp)
|
||||
type_args = get_args(tp)
|
||||
if orig_type in (abc.Sequence, Sequence, List, list):
|
||||
assert len(type_args) == 1
|
||||
attrs['n'] = 'N'
|
||||
return CephArgtype.to_argdesc(type_args[0], attrs)
|
||||
return CephArgtype.to_argdesc(type_args[0], attrs, positional=positional)
|
||||
elif orig_type is Tuple:
|
||||
assert len(type_args) >= 1
|
||||
inner_tp = type_args[0]
|
||||
assert type_args.count(inner_tp) == len(type_args), \
|
||||
f'all elements in {tp} should be identical'
|
||||
attrs['n'] = str(len(type_args))
|
||||
return CephArgtype.to_argdesc(inner_tp, attrs)
|
||||
return CephArgtype.to_argdesc(inner_tp, attrs, positional=positional)
|
||||
elif get_origin(tp) is Union:
|
||||
# should be Union[t, NoneType]
|
||||
assert len(type_args) == 2 and isinstance(None, type_args[1])
|
||||
return CephArgtype.to_argdesc(type_args[0], attrs, True)
|
||||
return CephArgtype.to_argdesc(type_args[0], attrs, True, positional)
|
||||
else:
|
||||
raise ValueError(f"unknown type '{tp}': '{attrs}'")
|
||||
|
||||
@staticmethod
|
||||
def to_argdesc(tp, attrs, has_default=False):
|
||||
def to_argdesc(tp, attrs, has_default=False, positional=True):
|
||||
if has_default:
|
||||
attrs['req'] = 'false'
|
||||
if not positional:
|
||||
attrs['positional'] = 'false'
|
||||
CEPH_ARG_TYPES = {
|
||||
str: CephString,
|
||||
int: CephInt,
|
||||
@ -208,7 +210,7 @@ class CephArgtype(object):
|
||||
elif isinstance(tp, type) and issubclass(tp, enum.Enum):
|
||||
return CephChoices(tp=tp).argdesc(attrs)
|
||||
else:
|
||||
return CephArgtype._compound_type_to_argdesc(tp, attrs)
|
||||
return CephArgtype._compound_type_to_argdesc(tp, attrs, positional)
|
||||
|
||||
def argdesc(self, attrs):
|
||||
attrs['type'] = type(self).__name__
|
||||
@ -758,7 +760,8 @@ class CephPrefix(CephArgtype):
|
||||
class argdesc(object):
|
||||
"""
|
||||
argdesc(typename, name='name', n=numallowed|N,
|
||||
req=False, helptext=helptext, **kwargs (type-specific))
|
||||
req=False, positional=True,
|
||||
helptext=helptext, **kwargs (type-specific))
|
||||
|
||||
validation rules:
|
||||
typename: type(**kwargs) will be constructed
|
||||
@ -767,6 +770,7 @@ class argdesc(object):
|
||||
name is used for parse errors and for constructing JSON output
|
||||
n is a numeric literal or 'n|N', meaning "at least one, but maybe more"
|
||||
req=False means the argument need not be present in the list
|
||||
positional=False means the argument name must be specified, e.g. "--myoption value"
|
||||
helptext is the associated help for the command
|
||||
anything else are arguments to pass to the type constructor.
|
||||
|
||||
@ -775,15 +779,19 @@ class argdesc(object):
|
||||
valid() will later be called with input to validate against it,
|
||||
and will store the validated value in self.instance.val for extraction.
|
||||
"""
|
||||
def __init__(self, t, name=None, n=1, req=True, **kwargs):
|
||||
def __init__(self, t, name=None, n=1, req=True, positional=True, **kwargs):
|
||||
if isinstance(t, basestring):
|
||||
self.t = CephPrefix
|
||||
self.typeargs = {'prefix': t}
|
||||
self.req = True
|
||||
self.positional = True
|
||||
else:
|
||||
self.t = t
|
||||
self.typeargs = kwargs
|
||||
self.req = req in (True, 'True', 'true')
|
||||
self.positional = positional in (True, 'True', 'true')
|
||||
if not positional:
|
||||
assert not req
|
||||
|
||||
self.name = name
|
||||
self.N = (n in ['n', 'N'])
|
||||
@ -828,34 +836,61 @@ class argdesc(object):
|
||||
like str(), but omit parameter names (except for CephString,
|
||||
which really needs them)
|
||||
"""
|
||||
if self.t == CephBool:
|
||||
chunk = "--{0}".format(self.name.replace("_", "-"))
|
||||
elif self.t == CephPrefix:
|
||||
chunk = str(self.instance)
|
||||
elif self.t == CephChoices:
|
||||
if self.name == 'format':
|
||||
chunk = f'--{self.name} {{{str(self.instance)}}}'
|
||||
else:
|
||||
if self.positional:
|
||||
if self.t == CephBool:
|
||||
chunk = "--{0}".format(self.name.replace("_", "-"))
|
||||
elif self.t == CephPrefix:
|
||||
chunk = str(self.instance)
|
||||
elif self.t == CephOsdName:
|
||||
# it just so happens all CephOsdName commands are named 'id' anyway,
|
||||
# so <id|osd.id> is perfect.
|
||||
chunk = '<id|osd.id>'
|
||||
elif self.t == CephName:
|
||||
# CephName commands similarly only have one arg of the
|
||||
# type, so <type.id> is good.
|
||||
chunk = '<type.id>'
|
||||
elif self.t == CephInt:
|
||||
chunk = '<{0}:int>'.format(self.name)
|
||||
elif self.t == CephFloat:
|
||||
chunk = '<{0}:float>'.format(self.name)
|
||||
elif self.t == CephChoices:
|
||||
if self.name == 'format':
|
||||
# this is for talking to legacy clusters only; new clusters
|
||||
# should properly mark format args as non-positional.
|
||||
chunk = f'--{self.name} {{{str(self.instance)}}}'
|
||||
else:
|
||||
chunk = f'<{self.name}:{self.instance}>'
|
||||
elif self.t == CephOsdName:
|
||||
# it just so happens all CephOsdName commands are named 'id' anyway,
|
||||
# so <id|osd.id> is perfect.
|
||||
chunk = '<id|osd.id>'
|
||||
elif self.t == CephName:
|
||||
# CephName commands similarly only have one arg of the
|
||||
# type, so <type.id> is good.
|
||||
chunk = '<type.id>'
|
||||
elif self.t == CephInt:
|
||||
chunk = '<{0}:int>'.format(self.name)
|
||||
elif self.t == CephFloat:
|
||||
chunk = '<{0}:float>'.format(self.name)
|
||||
else:
|
||||
chunk = '<{0}>'.format(self.name)
|
||||
s = chunk
|
||||
if self.N:
|
||||
s += '...'
|
||||
if not self.req:
|
||||
s = '[' + s + ']'
|
||||
else:
|
||||
chunk = '<{0}>'.format(self.name)
|
||||
s = chunk
|
||||
if self.N:
|
||||
s += '...'
|
||||
if not self.req:
|
||||
s = '[' + s + ']'
|
||||
# non-positional
|
||||
if self.t == CephBool:
|
||||
chunk = "--{0}".format(self.name.replace("_", "-"))
|
||||
elif self.t == CephPrefix:
|
||||
chunk = str(self.instance)
|
||||
elif self.t == CephChoices:
|
||||
chunk = f'--{self.name} {{{str(self.instance)}}}'
|
||||
elif self.t == CephOsdName:
|
||||
chunk = f'--{self.name} <id|osd.id>'
|
||||
elif self.t == CephName:
|
||||
chunk = f'--{self.name} <type.id>'
|
||||
elif self.t == CephInt:
|
||||
chunk = f'--{self.name} <int>'
|
||||
elif self.t == CephFloat:
|
||||
chunk = f'--{self.name} <float>'
|
||||
else:
|
||||
chunk = f'--{self.name} <value>'
|
||||
s = chunk
|
||||
if self.N:
|
||||
s += '...'
|
||||
if not self.req: # req should *always* be false
|
||||
s = '[' + s + ']'
|
||||
|
||||
return s
|
||||
|
||||
def complete(self, s):
|
||||
@ -917,12 +952,13 @@ def parse_funcsig(sig: Sequence[Union[str, Dict[str, str]]]) -> List[argdesc]:
|
||||
|
||||
kwargs = dict()
|
||||
for key, val in desc.items():
|
||||
if key not in ['type', 'name', 'n', 'req']:
|
||||
if key not in ['type', 'name', 'n', 'req', 'positional']:
|
||||
kwargs[key] = val
|
||||
newsig.append(argdesc(t,
|
||||
name=desc.get('name', None),
|
||||
n=desc.get('n', 1),
|
||||
req=desc.get('req', True),
|
||||
positional=desc.get('positional', True),
|
||||
**kwargs))
|
||||
return newsig
|
||||
|
||||
@ -1123,10 +1159,6 @@ def validate(args: List[str],
|
||||
# argdesc for the keyword argument, if we find one
|
||||
kwarg_desc = None
|
||||
|
||||
# Track whether we need to push value back onto
|
||||
# myargs in the case that this isn't a valid k=v
|
||||
consumed_next = False
|
||||
|
||||
# Try both styles of keyword argument
|
||||
kwarg_match = re.match(KWARG_EQUALS, myarg)
|
||||
if kwarg_match:
|
||||
@ -1164,6 +1196,10 @@ def validate(args: List[str],
|
||||
store_arg(kwarg_desc, d)
|
||||
continue
|
||||
|
||||
if not desc.positional:
|
||||
# No more positional args!
|
||||
raise ArgumentValid(f"Unexpected argument '{myarg}'")
|
||||
|
||||
# Don't handle something as a positional argument if it
|
||||
# has a leading "--" unless it's a CephChoices (used for
|
||||
# "--yes-i-really-mean-it")
|
||||
|
@ -1026,12 +1026,12 @@ class Module(MgrModule):
|
||||
},
|
||||
{
|
||||
"cmd": "k8sevents set-access name=key,type=CephString",
|
||||
"desc": "Set kubernetes access credentials. <key> must be cacrt or token and use -i <filename> syntax.\ne.g. ceph k8sevents set-access cacrt -i /root/ca.crt",
|
||||
"desc": "Set kubernetes access credentials. <key> must be cacrt or token and use -i <filename> syntax (e.g., ceph k8sevents set-access cacrt -i /root/ca.crt).",
|
||||
"perm": "rw"
|
||||
},
|
||||
{
|
||||
"cmd": "k8sevents set-config name=key,type=CephString name=value,type=CephString",
|
||||
"desc": "Set kubernetes config paramters. <key> must be server or namespace.\ne.g. ceph k8sevents set-config server https://localhost:30433",
|
||||
"desc": "Set kubernetes config paramters. <key> must be server or namespace (e.g., ceph k8sevents set-config server https://localhost:30433).",
|
||||
"perm": "rw"
|
||||
},
|
||||
{
|
||||
|
@ -321,22 +321,35 @@ class CLICommand(object):
|
||||
|
||||
@staticmethod
|
||||
def load_func_metadata(f: HandlerFuncType) -> Tuple[str, Dict[str, Any], int, str]:
|
||||
desc = inspect.getdoc(f) or ''
|
||||
desc = (inspect.getdoc(f) or '').replace('\n', ' ')
|
||||
full_argspec = inspect.getfullargspec(f)
|
||||
arg_spec = full_argspec.annotations
|
||||
first_default = len(arg_spec)
|
||||
if full_argspec.defaults:
|
||||
first_default -= len(full_argspec.defaults)
|
||||
args = []
|
||||
positional = True
|
||||
for index, arg in enumerate(full_argspec.args):
|
||||
if arg in CLICommand.KNOWN_ARGS:
|
||||
continue
|
||||
if arg == '_end_positional_':
|
||||
positional = False
|
||||
continue
|
||||
if (
|
||||
arg == 'format'
|
||||
or arg_spec[arg] is Optional[bool]
|
||||
or arg_spec[arg] is bool
|
||||
):
|
||||
# implicit switch to non-positional on any
|
||||
# Optional[bool] or the --format option
|
||||
positional = False
|
||||
assert arg in arg_spec, \
|
||||
f"'{arg}' is not annotated for {f}: {full_argspec}"
|
||||
has_default = index >= first_default
|
||||
args.append(CephArgtype.to_argdesc(arg_spec[arg],
|
||||
dict(name=arg),
|
||||
has_default))
|
||||
has_default,
|
||||
positional))
|
||||
return desc, arg_spec, first_default, ' '.join(args)
|
||||
|
||||
def store_func_metadata(self, f: HandlerFuncType) -> None:
|
||||
|
@ -28,8 +28,8 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
|
||||
fsname: str,
|
||||
clusterid: str,
|
||||
binding: str,
|
||||
readonly: bool = False,
|
||||
path: str = '/') -> Tuple[int, str, str]:
|
||||
path: str = '/',
|
||||
readonly: bool = False) -> Tuple[int, str, str]:
|
||||
"""Create a cephfs export"""
|
||||
# TODO Extend export creation for rgw.
|
||||
return self.export_mgr.create_export(fsal_type='cephfs', fs_name=fsname,
|
||||
|
@ -611,6 +611,7 @@ class OrchestratorCli(OrchestratorClientMixin, MgrModule,
|
||||
@_cli_read_command('orch ps')
|
||||
def _list_daemons(self,
|
||||
hostname: Optional[str] = None,
|
||||
_end_positional_: int = 0,
|
||||
service_name: Optional[str] = None,
|
||||
daemon_type: Optional[str] = None,
|
||||
daemon_id: Optional[str] = None,
|
||||
@ -900,9 +901,10 @@ Usage:
|
||||
@_cli_write_command('orch daemon add rgw')
|
||||
def _rgw_add(self,
|
||||
svc_id: str,
|
||||
placement: Optional[str] = None,
|
||||
_end_positional_: int = 0,
|
||||
port: Optional[int] = None,
|
||||
ssl: bool = False,
|
||||
placement: Optional[str] = None,
|
||||
inbuf: Optional[str] = None) -> HandleCommandResult:
|
||||
"""Start RGW daemon(s)"""
|
||||
if inbuf:
|
||||
@ -974,7 +976,9 @@ Usage:
|
||||
return HandleCommandResult(stdout=completion.result_str())
|
||||
|
||||
@_cli_write_command('orch daemon redeploy')
|
||||
def _daemon_action_redeploy(self, name: str, image: Optional[str] = None) -> HandleCommandResult:
|
||||
def _daemon_action_redeploy(self,
|
||||
name: str,
|
||||
image: Optional[str] = None) -> HandleCommandResult:
|
||||
"""Redeploy a daemon (with a specifc image)"""
|
||||
if '.' not in name:
|
||||
raise OrchestratorError('%s is not a valid daemon name' % name)
|
||||
@ -1088,11 +1092,12 @@ Usage:
|
||||
@_cli_write_command('orch apply rgw')
|
||||
def _apply_rgw(self,
|
||||
svc_id: str,
|
||||
placement: Optional[str] = None,
|
||||
_end_positional_: int = 0,
|
||||
realm: Optional[str] = None,
|
||||
zone: Optional[str] = None,
|
||||
port: Optional[int] = None,
|
||||
ssl: bool = False,
|
||||
placement: Optional[str] = None,
|
||||
dry_run: bool = False,
|
||||
format: Format = Format.plain,
|
||||
unmanaged: bool = False,
|
||||
@ -1342,6 +1347,7 @@ Usage:
|
||||
@_cli_write_command('orch upgrade start')
|
||||
def _upgrade_start(self,
|
||||
image: Optional[str] = None,
|
||||
_end_positional_: int = 0,
|
||||
ceph_version: Optional[str] = None) -> HandleCommandResult:
|
||||
"""Initiate upgrade"""
|
||||
self._upgrade_check_image_name(image, ceph_version)
|
||||
|
Loading…
Reference in New Issue
Block a user