pybind: ceph_argparse: validate incorrectly formed targets

Prior to this patch find_cmd_target() would perform the following
parsing for any given command:

- check if it's a "tell" to a parseable CephName
  (i.e., ceph tell <type.id> ...)
  - if so, return <type>, <id>

- check if it's a "tell" to a parseable PG id
  (e.g., ceph tell 0.4a)
  - if so, return 'pg', <pgid>

- check if it's a "pg" command to a parseable PG id
  (e.g., ceph pg 0.4a)
  - if so, return 'pg', <pgid>

- otherwise return 'mon', ''

However, parsing of CephName and CephPgid is performed in a relaxed
fashion, and tightening those checks requirements end up having
nefarious effects on properly formed commands, whereas keeping them
relaxed ends up having us returning 'mon','' in the end for a clearly
malformed target (e.g., 'ceph tell foo ...').

This patch fixes this behavior by adding a new check:

- if command is a "tell" and we were not able to parse either a CephName
  nor a PG id, then explicitely validate the target as a CephName (given
  we would be sending to a monitor anyway, we can just as well validate
  a 'tell' as a CephName).
  - if validation fails, we will propagate exceptions referring to the
    cause of the validation failure.

Fixes: #10439

Signed-off-by: Joao Eduardo Luis <joao@redhat.com>
This commit is contained in:
Joao Eduardo Luis 2015-01-02 17:39:36 +00:00
parent 8618a5373e
commit c4548f68ca
2 changed files with 36 additions and 2 deletions

View File

@ -425,7 +425,12 @@ def new_style_command(parsed_args, cmdargs, target, sigdict, inbuf, verbose):
if interactive_input in ['q', 'quit', 'Q']: if interactive_input in ['q', 'quit', 'Q']:
return 0, '', '' return 0, '', ''
cmdargs = parse_cmdargs(interactive_input.split())[2] cmdargs = parse_cmdargs(interactive_input.split())[2]
target = find_cmd_target(cmdargs) try:
target = find_cmd_target(cmdargs)
except Exception as e:
print >> sys.stderr, \
'error handling command target: {0}'.format(e)
return 1
valid_dict = validate_command(sigdict, cmdargs, verbose) valid_dict = validate_command(sigdict, cmdargs, verbose)
if valid_dict: if valid_dict:
if parsed_args.output_format: if parsed_args.output_format:
@ -753,7 +758,12 @@ def main():
if parsed_args.status: if parsed_args.status:
childargs.insert(0, 'status') childargs.insert(0, 'status')
target = find_cmd_target(childargs) try:
target = find_cmd_target(childargs)
except Exception as e:
print >> sys.stderr, \
'error handling command target: {0}'.format(e)
return 1
# Repulsive hack to handle tell: lop off 'tell' and target # Repulsive hack to handle tell: lop off 'tell' and target
# and validate the rest of the command. 'target' is already # and validate the rest of the command. 'target' is already

View File

@ -1005,6 +1005,30 @@ def find_cmd_target(childargs):
# pg doesn't need revalidation; the string is fine # pg doesn't need revalidation; the string is fine
return 'pg', valid_dict['pgid'] return 'pg', valid_dict['pgid']
# If we reached this far it must mean that so far we've been unable to
# obtain a proper target from childargs. This may mean that we are not
# dealing with a 'tell' command, or that the specified target is invalid.
# If the latter, we likely were unable to catch it because we were not
# really looking for it: first we tried to parse a 'CephName' (osd, mon,
# mds, followed by and id); given our failure to parse, we tried to parse
# a 'CephPgid' instead (e.g., 0.4a). Considering we got this far though
# we were unable to do so.
#
# We will now check if this is a tell and, if so, forcefully validate the
# target as a 'CephName'. This must be so because otherwise we will end
# up sending garbage to a monitor, which is the default target when a
# target is not explicitly specified.
# e.g.,
# 'ceph status' -> target is any one monitor
# 'ceph tell mon.* status -> target is all monitors
# 'ceph tell foo status -> target is invalid!
if len(childargs) > 1 and childargs[0] == 'tell':
name = CephName()
# CephName.valid() raises on validation error; find_cmd_target()'s
# caller should handle them
name.valid(childargs[1])
return name.nametype, name.nameid
sig = parse_funcsig(['pg', {'name':'pgid', 'type':'CephPgid'}]) sig = parse_funcsig(['pg', {'name':'pgid', 'type':'CephPgid'}])
try: try:
valid_dict = validate(childargs, sig, partial=True) valid_dict = validate(childargs, sig, partial=True)