Merge PR #35977 into master

* refs/pull/35977/head:
	doc/cephfs: Update about cephfs-shell custom exit codes
	cephfs-shell: Define cephfs-shell exit code

Reviewed-by: Rishabh Dave <ridave@redhat.com>
Reviewed-by: Patrick Donnelly <pdonnell@redhat.com>
This commit is contained in:
Patrick Donnelly 2020-07-29 20:00:06 -07:00
commit ac88abfbe9
No known key found for this signature in database
GPG Key ID: 3A2A7E25BEA8AADB
2 changed files with 148 additions and 92 deletions

View File

@ -507,3 +507,56 @@ Options :
--max_bytes MAX_BYTES Set max cumulative size of the data under this directory
--max_files MAX_FILES Set total number of files under this directory tree
Exit Code
=========
Following exit codes are returned by cephfs shell
+-----------------------------------------------+-----------+
| Error Type | Exit Code |
+===============================================+===========+
| Miscellaneous | 1 |
+-----------------------------------------------+-----------+
| Keyboard Interrupt | 2 |
+-----------------------------------------------+-----------+
| Operation not permitted | 3 |
+-----------------------------------------------+-----------+
| Permission denied | 4 |
+-----------------------------------------------+-----------+
| No such file or directory | 5 |
+-----------------------------------------------+-----------+
| I/O error | 6 |
+-----------------------------------------------+-----------+
| No space left on device | 7 |
+-----------------------------------------------+-----------+
| File exists | 8 |
+-----------------------------------------------+-----------+
| No data available | 9 |
+-----------------------------------------------+-----------+
| Invalid argument | 10 |
+-----------------------------------------------+-----------+
| Operation not supported on transport endpoint | 11 |
+-----------------------------------------------+-----------+
| Range error | 12 |
+-----------------------------------------------+-----------+
| Operation would block | 13 |
+-----------------------------------------------+-----------+
| Directory not empty | 14 |
+-----------------------------------------------+-----------+
| Not a directory | 15 |
+-----------------------------------------------+-----------+
| Disk quota exceeded | 16 |
+-----------------------------------------------+-----------+
| Broken pipe | 17 |
+-----------------------------------------------+-----------+
| Cannot send after transport endpoint shutdown | 18 |
+-----------------------------------------------+-----------+
| Connection aborted | 19 |
+-----------------------------------------------+-----------+
| Connection refused | 20 |
+-----------------------------------------------+-----------+
| Connection reset | 21 |
+-----------------------------------------------+-----------+
| Interrupted function call | 22 |
+-----------------------------------------------+-----------+

View File

@ -14,6 +14,7 @@ import math
import re
import shlex
import stat
import errno
from cmd2 import Cmd
from cmd2 import __version__ as cmd2_version
@ -57,6 +58,29 @@ except ImportError:
cephfs = None # holds CephFS Python bindings
shell = None # holds instance of class CephFSShell
exit_codes = {'Misc': 1,
'KeyboardInterrupt': 2,
errno.EPERM: 3,
errno.EACCES: 4,
errno.ENOENT: 5,
errno.EIO: 6,
errno.ENOSPC: 7,
errno.EEXIST: 8,
errno.ENODATA: 9,
errno.EINVAL: 10,
errno.EOPNOTSUPP: 11,
errno.ERANGE: 12,
errno.EWOULDBLOCK: 13,
errno.ENOTEMPTY: 14,
errno.ENOTDIR: 15,
errno.EDQUOT: 16,
errno.EPIPE: 17,
errno.ESHUTDOWN: 18,
errno.ECONNABORTED: 19,
errno.ECONNREFUSED: 20,
errno.ECONNRESET: 21,
errno.EINTR: 22}
#########################################################################
#
@ -73,6 +97,18 @@ def perror(msg, **kwargs):
shell.perror(msg, **kwargs)
def set_exit_code_msg(errcode='Misc', msg=''):
"""
Set exit code and print error message
"""
if isinstance(msg, libcephfs.Error):
shell.exit_code = exit_codes[msg.get_error_code()]
else:
shell.exit_code = exit_codes[errcode]
if msg:
perror(msg)
def mode_notation(mode):
"""
"""
@ -133,8 +169,7 @@ def ls(path, opts=''):
continue
yield dent
except libcephfs.ObjectNotFound as e:
perror(e)
shell.exit_code = e.get_error_code()
set_exit_code_msg(msg=e)
def glob(path, pattern):
@ -262,17 +297,15 @@ def copy_from_local(local_path, remote_path):
else:
try:
file_ = open(local_path, 'rb')
except PermissionError:
shell.exit_code = 1
perror('error: no permission to read local file {}'.format(
local_path.decode('utf-8')))
except PermissionError as e:
set_exit_code_msg(e.errno, 'error: no permission to read local file {}'.format(
local_path.decode('utf-8')))
return
stdin = 1
try:
fd = cephfs.open(remote_path, 'w', 0o666)
except libcephfs.Error as e:
perror(e)
shell.exit_code = 1
set_exit_code_msg(msg=e)
return
progress = 0
while True:
@ -360,7 +393,7 @@ class CephFSShell(Cmd):
try:
argparse_args = getattr(self, 'argparse_' + command)
except AttributeError:
self.exit_code = 1
set_exit_code_msg()
return None
doc_lines = getattr(
self, 'do_' + command).__doc__.expandtabs().splitlines()
@ -416,22 +449,13 @@ class CephFSShell(Cmd):
self.set_prompt()
return res
except ConnectionError as e:
perror('***\n{}'.format(e))
self.exit_code = 3
set_exit_code_msg(e.errno, f'***\n{e}')
except KeyboardInterrupt:
perror('Command aborted')
self.exit_code = 130
set_exit_code_msg('KeyboardInterrupt', 'Command aborted')
except (libcephfs.Error, Exception) as e:
perror(e)
if shell.debug:
traceback.print_exc(file=sys.stdout)
if hasattr(e, 'get_error_code'):
self.exit_code = e.get_error_code()
else:
# XXX: Setting it to 3 so that exceptions not stemming from
# CephFS Python bindings can be distinguished.
self.exit_code = 3
set_exit_code_msg(msg=e)
class path_to_bytes(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
@ -556,8 +580,7 @@ class CephFSShell(Cmd):
try:
cephfs.mkdir(path, permission)
except libcephfs.Error as e:
perror(e)
self.exit_code = e.get_error_code()
set_exit_code_msg(e)
def complete_put(self, text, line, begidx, endidx):
"""
@ -606,9 +629,8 @@ class CephFSShell(Cmd):
else:
root_dst_dir = a[0]
else:
perror('error: no filename specified for destination')
# TODO: perhaps, we need a more suitable retval?
self.exit_code = 1
set_exit_code_msg(errno.EINVAL, 'error: no filename specified '
'for destination')
return
if root_dst_dir[-1] != b'/':
@ -619,9 +641,9 @@ class CephFSShell(Cmd):
if os.path.isfile(root_src_dir):
dst_file = root_dst_dir
if is_file_exists(dst_file):
perror('{}: file exists! use --force to '
'overwrite'.format(dst_file.decode('utf-8')))
self.exit_code = 1
set_exit_code_msg(errno.EEXIST,
f"{dst_file.decode('utf-8')}: file "
"exists! use --force to overwrite")
return
if args.local_path == b'-':
root_src_dir = b'-'
@ -694,8 +716,7 @@ class CephFSShell(Cmd):
root_src_dir = cephfs.getcwd()
if args.local_path == b'-':
if args.remote_path == b'.' or args.remote_path == b'./':
perror('error: no remote file name specified')
self.exit_code = 1
set_exit_code_msg(errno.EINVAL, 'error: no remote file name specified')
return
copy_to_local(root_src_dir, b'-')
elif is_file_exists(args.remote_path):
@ -712,9 +733,8 @@ class CephFSShell(Cmd):
if args.force:
pass
else:
perror('{}: already exists! use --force to overwrite'.format(
root_src_dir.decode('utf-8')))
self.exit_code = e.errno
set_exit_code_msg(e.errno, f"{root_src_dir.decode('utf-8')}: "
"already exists! use --force to overwrite")
return
for file_ in files:
@ -732,9 +752,8 @@ class CephFSShell(Cmd):
if not args.force:
try:
os.stat(dst_path)
perror('{}: file already exists! use --force to '
'override'.format(file_.decode('utf-8')))
self.exit_code = 1
set_exit_code_msg(errno.EEXIST, f"{file_.decode('utf-8')}: "
"file already exists! use --force to override")
return
except OSError:
copy_to_local(file_, dst_path)
@ -892,8 +911,7 @@ class CephFSShell(Cmd):
try:
cephfs.rmdir(path)
except libcephfs.Error as e:
perror(e)
self.exit_code = e.get_error_code()
set_exit_code_msg(msg=e)
def complete_rm(self, text, line, begidx, endidx):
"""
@ -920,8 +938,7 @@ class CephFSShell(Cmd):
cephfs.unlink(path)
except libcephfs.Error as e:
# NOTE: perhaps we need a better msg here
perror(e)
self.exit_code = e.get_error_code()
set_exit_code_msg(msg=e)
def complete_mv(self, text, line, begidx, endidx):
"""
@ -988,8 +1005,7 @@ class CephFSShell(Cmd):
try:
cephfs.chmod(path, mode)
except libcephfs.Error as e:
perror(e)
self.exit_code = e.get_error_code()
set_exit_code_msg(msg=e)
def complete_cat(self, text, line, begidx, endidx):
"""
@ -1010,8 +1026,8 @@ class CephFSShell(Cmd):
if is_file_exists(path):
copy_to_local(path, b'-')
else:
perror('{}: no such file'.format(path.decode('utf-8')))
self.exit_code = 1
set_exit_code_msg(errno.ENOENT, '{}: no such file'.format(
path.decode('utf-8')))
umask_parser = argparse.ArgumentParser(description='Set umask value.')
umask_parser.add_argument('mode', help='Mode', type=str, action=ModeAction,
@ -1064,8 +1080,8 @@ class CephFSShell(Cmd):
try:
os.chdir(os.path.expanduser(args.path))
except OSError as e:
perror("Cannot change to {}: {}".format(e.filename, e.strerror))
self.exit_code = e.errno
set_exit_code_msg(e.errno, "Cannot change to "
f"{e.filename.decode('utf-8')}: {e.strerror}")
def complete_lls(self, text, line, begidx, endidx):
"""
@ -1093,8 +1109,8 @@ class CephFSShell(Cmd):
poutput("{}:".format(path.decode('utf-8')))
print_list(items)
except OSError as e:
perror("'{}': {}".format(e.filename, e.strerror))
self.exit_code = e.errno
set_exit_code_msg(e.errno, f"{e.filename.decode('utf-8')}: "
f"{e.strerror}")
# Arguments to the with_argpaser decorator function are sticky.
# The items in args.path do not get overwritten in subsequent calls.
# The arguments remain in args.paths after the function exits and we
@ -1152,9 +1168,8 @@ class CephFSShell(Cmd):
stat.st_size, available, str(int(use)) + '%',
file.decode('utf-8')))
except libcephfs.OSError as e:
perror("could not statfs {}: {}".format(file.decode('utf-8'),
e.strerror))
self.exit_code = e.get_error_code()
set_exit_code_msg(e.get_error_code(), "could not statfs {}: {}".format(
file.decode('utf-8'), e.strerror))
locate_parser = argparse.ArgumentParser(
description='Find file within file system')
@ -1225,8 +1240,7 @@ class CephFSShell(Cmd):
poutput('{:10s} {}'.format(humansize(dusage),
f.decode('utf-8')))
except libcephfs.Error as e:
perror(e)
self.exit_code = e.get_error_code()
set_exit_code_msg(msg=e)
continue
for path in args.paths:
@ -1254,15 +1268,14 @@ class CephFSShell(Cmd):
Quota management.
"""
if not is_dir_exists(args.path):
perror('error: no such directory {}'.format(args.path.decode('utf-8')))
self.exit_code = 1
set_exit_code_msg(errno.ENOENT, 'error: no such directory {}'.format(
args.path.decode('utf-8')))
return
if args.op == 'set':
if (args.max_bytes == -1) and (args.max_files == -1):
perror('please specify either --max_bytes or --max_files or '
'both')
self.exit_code = 1
set_exit_code_msg(errno.EINVAL, 'please specify either '
'--max_bytes or --max_files or both')
return
if args.max_bytes >= 0:
@ -1274,8 +1287,8 @@ class CephFSShell(Cmd):
except libcephfs.Error as e:
cephfs.setxattr(args.path, 'ceph.quota.max_bytes',
max_bytes, os.XATTR_REPLACE)
perror('max_bytes reset to %d' % args.max_bytes)
self.exit_code = e.get_error_code()
set_exit_code_msg(e.get_error_code(), 'max_bytes reset to '
f'{args.max_bytes}')
if args.max_files >= 0:
max_files = to_bytes(str(args.max_files))
@ -1286,8 +1299,8 @@ class CephFSShell(Cmd):
except libcephfs.Error as e:
cephfs.setxattr(args.path, 'ceph.quota.max_files',
max_files, os.XATTR_REPLACE)
perror('max_files reset to %d' % args.max_files)
self.exit_code = e.get_error_code()
set_exit_code_msg(e.get_error_code(), 'max_files reset to '
f'{args.max_files}')
elif args.op == 'get':
max_bytes = '0'
max_files = '0'
@ -1295,15 +1308,13 @@ class CephFSShell(Cmd):
max_bytes = cephfs.getxattr(args.path, 'ceph.quota.max_bytes')
poutput('max_bytes: {}'.format(max_bytes.decode('utf-8')))
except libcephfs.Error as e:
perror('max_bytes is not set')
self.exit_code = e.get_error_code()
set_exit_code_msg(e.get_error_code(), 'max_bytes is not set')
try:
max_files = cephfs.getxattr(args.path, 'ceph.quota.max_files')
poutput('max_files: {}'.format(max_files.decode('utf-8')))
except libcephfs.Error as e:
perror('max_files is not set')
self.exit_code = e.get_error_code()
set_exit_code_msg(e.get_error_code(), 'max_files is not set')
snap_parser = argparse.ArgumentParser(description='Snapshot Management')
snap_parser.add_argument('op', type=str,
@ -1333,13 +1344,12 @@ class CephFSShell(Cmd):
if is_dir_exists(args.dir):
cephfs.mkdir(os.path.join(args.dir, snapdir, args.name), 0o755)
else:
self.perror("'{}': no such directory".format(
args.dir.decode('utf-8')))
self.exit_code = 1
set_exit_code_msg(errno.ENOENT, "'{}': no such directory".format(
args.dir.decode('utf-8')))
except libcephfs.Error as e:
self.perror("snapshot '{}' already exists".format(
args.name.decode('utf-8')))
self.exit_code = e.get_error_code()
set_exit_code_msg(e.get_error_code(),
"snapshot '{}' already exists".format(
args.name.decode('utf-8')))
elif args.op == 'delete':
snap_dir = os.path.join(args.dir, snapdir, args.name)
try:
@ -1347,17 +1357,14 @@ class CephFSShell(Cmd):
newargs = argparse.Namespace(paths=[snap_dir], parent=False)
self.do_rmdir_helper(newargs)
else:
self.perror("'{}': no such snapshot".format(
set_exit_code_msg(errno.ENOENT, "'{}': no such snapshot".format(
args.name.decode('utf-8')))
self.exit_code = 1
except libcephfs.Error as e:
self.perror("error while deleting '{}'".format(
snap_dir.decode('utf-8')))
self.exit_code = e.get_error_code()
set_exit_code_msg(e.get_error_code(), "error while deleting "
"'{}'".format(snap_dir.decode('utf-8')))
else:
self.perror("snapshot can only be created or deleted; check - "
"help snap")
self.exit_code = 1
set_exit_code_msg(errno.EINVAL, "snapshot can only be created or "
"deleted; check - help snap")
def do_help(self, line):
"""
@ -1412,8 +1419,7 @@ class CephFSShell(Cmd):
stat.st_uid, stat.st_gid, atime,
mtime, ctime))
except libcephfs.Error as e:
perror(e)
self.exit_code = e.get_error_code()
set_exit_code_msg(msg=e)
setxattr_parser = argparse.ArgumentParser(
description='Set extended attribute for a file')
@ -1435,8 +1441,7 @@ class CephFSShell(Cmd):
cephfs.setxattr(args.path, name_bytes, val_bytes, os.XATTR_REPLACE)
poutput('{} is successfully reset to {}'.format(args.name, args.value))
except libcephfs.Error as e:
perror(e)
self.exit_code = e.get_error_code()
set_exit_code_msg(msg=e)
getxattr_parser = argparse.ArgumentParser(
description='Get extended attribute set for a file')
@ -1453,8 +1458,7 @@ class CephFSShell(Cmd):
poutput('{}'.format(cephfs.getxattr(args.path,
to_bytes(args.name)).decode('utf-8')))
except libcephfs.Error as e:
perror(e)
self.exit_code = e.get_error_code()
set_exit_code_msg(msg=e)
listxattr_parser = argparse.ArgumentParser(
description='List extended attributes set for a file')
@ -1473,8 +1477,7 @@ class CephFSShell(Cmd):
else:
poutput('No extended attribute is set')
except libcephfs.Error as e:
perror(e)
self.exit_code = e.get_error_code()
set_exit_code_msg(msg=e)
#######################################################
@ -1491,12 +1494,12 @@ def setup_cephfs():
try:
cephfs = libcephfs.LibCephFS(conffile='')
cephfs.mount()
except libcephfs.ObjectNotFound:
except libcephfs.ObjectNotFound as e:
print('couldn\'t find ceph configuration not found')
sys.exit(1)
sys.exit(e.get_error_code())
except libcephfs.Error as e:
print(e)
sys.exit(1)
sys.exit(e.get_error_code())
def str_to_bool(val):