Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cli: error code discovery #6003

Merged
merged 2 commits into from
Jun 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions components/tools/OmeroPy/src/omero/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,34 @@ def _check_admin(*args, **kwargs):
return _admin_only


class Error(object):
"""
Wrapper for error messages which can be registered by an BaseControl
subclass in its _configure method. Example:

class MyControl(BaseControl):
def _configure(self, parser):
self.add_error("NAME", 100, "some message: %s")
...
def __call__(self, *args):
self.raise_error("NAME", "my text")
"""

def __init__(self, ctx, rcode, msg):
self.ctx = ctx
self.rcode = rcode
self.msg = msg

def die(self, *args):
"""
Call ctx.die passing the return code and the message for this instance
"""
self.ctx.die(self.rcode, self.msg % tuple(args))

def __str__(self):
return "Error(%d, '%s')" % (self.rcode, self.msg)


class BaseControl(object):
"""Controls get registered with a CLI instance on loadplugins().

Expand Down Expand Up @@ -679,6 +707,32 @@ def __init__(self, ctx=None, dir=OMERODIR):
self.ctx = ctx
if self.ctx is None:
self.ctx = Context() # Prevents unncessary stop_event creation
self.__errors = {}

def add_error(self, name, rcode, msg):
"""
Register an Error by name both for discovery via the ErrorsControl
as well as for raising an exception via raise_error.
"""
err = self.__errors.get(name)
if err is not None:
self.ctx.die(2, "Error already exists: %s (%s)" % (name, err))
self.__errors[name] = Error(self.ctx, rcode, msg)

def get_errors(self):
"""
Returns a mapping from name to Error object
"""
return dict(self.__errors)

def raise_error(self, name, *args):
"""
Call die on the named Error using the arguments to format the message
"""
err = self.__errors.get(name)
if err is None:
self.ctx.die(2, "Error doesn't exist: %s" % name)
err.die(*args)

def _isWindows(self):
p_s = platform.system()
Expand Down
64 changes: 39 additions & 25 deletions components/tools/OmeroPy/src/omero/plugins/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,34 @@ def _complete(self, text, line, begidx, endidx):
def _configure(self, parser):
sub = parser.sub()
self._add_diagnostics(parser, sub)
self.add_error(
"NOT_WINDOWS", 123,
"Not Windows")
self.add_error(
"SETUP", 200,
"Error during service user set up: (%s) %s")
self.add_error(
"RUNNING", 201,
"%s is already running. Use stop first")
self.add_error(
"NO_SERVICE", 202,
"%s service deleted.")
self.add_error(
"BAD_CONFIG", 300,
"Bad configuration: No IceGrid.Node.Data property")
self.add_error(
"WIN_CONFIG", 400, """

%s is not in this directory. Aborting...

Please see the installation instructions on modifying
the files for your installation (%s)
with bin\winconfig.bat

""")
self.add_error(
"NO_WIN32", 666,
"Could not import win32service and/or win32evtlogutil")
self.actions = {}

class Action(object):
Expand Down Expand Up @@ -551,8 +579,7 @@ def _start_service(self, config, descript, svc_name, pasw, user):
policy_handle, sid_obj, ('SeServiceLogonRight',))
win32security.LsaClose(policy_handle)
except pywintypes.error, details:
self.ctx.die(200, "Error during service user set up:"
" (%s) %s" % (details[0], details[2]))
self.raise_error("SETUP", details[0], details[2])
if not pasw:
try:
pasw = config.as_map()["omero.windows.pass"]
Expand Down Expand Up @@ -581,8 +608,7 @@ def _start_service(self, config, descript, svc_name, pasw, user):

# Then check if the server is already running
if 0 <= output.find("RUNNING"):
self.ctx.die(201, "%s is already running. Use stop first"
% svc_name)
self.raise_error("RUNNING", svc_name)

# Finally, try to start the service - delete if startup fails
hscm = win32service.OpenSCManager(
Expand All @@ -597,7 +623,7 @@ def _start_service(self, config, descript, svc_name, pasw, user):
self.ctx.out("%s service startup failed: (%s) %s"
% (svc_name, details[0], details[2]))
win32service.DeleteService(hs)
self.ctx.die(202, "%s service deleted." % svc_name)
self.raise_error("NO_SERVICE", svc_name)
finally:
win32service.CloseServiceHandle(hs)
win32service.CloseServiceHandle(hscm)
Expand All @@ -615,20 +641,17 @@ def DumpRecord(record):
else:

def events(self, svc_name):
self.ctx.die(
666, "Could not import win32service and/or win32evtlogutil")
self.raise_error("NO_WIN32")

def _query_service(self, svc_name):
self.ctx.die(
666, "Could not import win32service and/or win32evtlogutil")
self.raise_error("NO_WIN32")

def _start_service(self, config, descript, svc_name, pasw, user):
self.ctx.die(
666, "Could not import win32service and/or win32evtlogutil")
self.raise_error("NO_WIN32")

def _stop_service(self, svc_name):
self.ctx.die(
666, "Could not import win32service and/or win32evtlogutil")
self.raise_error("NO_WIN32")

#
# End Windows Methods
#
Expand Down Expand Up @@ -693,15 +716,14 @@ def checkwindows(self, args):
"""
self.check_access(os.R_OK)
if not self._isWindows():
self.ctx.die(123, "Not Windows")
self.raise_error("NOT_WINDOWS")

import Ice
key = "IceGrid.Node.Data"
properties = Ice.createProperties([self._icecfg()])
nodedata = properties.getProperty(key)
if not nodedata:
self.ctx.die(300,
"Bad configuration: No IceGrid.Node.Data property")
self.raise_error("BAD_CONFIG")
nodepath = path(nodedata)
pp = nodepath.parpath(self.ctx.dir)
if pp:
Expand All @@ -713,15 +735,7 @@ def checkwindows(self, args):
count = win_set_path(dir=self.ctx.dir)
if count:
return
self.ctx.die(400, """

%s is not in this directory. Aborting...

Please see the installation instructions on modifying
the files for your installation (%s)
with bin\winconfig.bat

""" % (nodedata, self.ctx.dir))
self.raise_error("WIN_CONFIG", nodedata, self.ctx.dir)

##############################################
#
Expand Down
37 changes: 37 additions & 0 deletions components/tools/OmeroPy/src/omero/plugins/basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

import sys

from collections import defaultdict

from omero_ext.argparse import FileType

from omero.cli import BaseControl
Expand Down Expand Up @@ -240,9 +242,44 @@ def __call__(self, args):
elif args.topic:
self.print_single_command_or_topic(args)


class ErrorsControl(BaseControl):

def _configure(self, parser):
parser.set_defaults(func=self.__call__)
parser.add_argument("--length", default=50, type=int,
help="Length of message to print")
parser.add_argument("plugins", nargs="*", default=(),
help="Limit to these plugins; otherwise all")

def __call__(self, args):
arranged = defaultdict(lambda: defaultdict(
lambda: defaultdict(list)))
for name, control in self.ctx.controls.items():
if not args.plugins or name in args.plugins:
combined = []
if hasattr(control, "get_errors"):
combined.extend(control.get_errors().items())
combined.sort(lambda a, b: cmp(a[1].rcode, b[1].rcode))
for key, err in combined:
arranged[err.rcode][name][key].append(err)

for rcode, names in sorted(arranged.items()):
for name, keys in sorted(names.items()):
for key, errors in sorted(keys.items()):
for err in errors:
msg = err.msg
if len(msg) > (args.length+1):
msg = msg[:args.length] + "..."
msg = msg.replace("\n", " ")
msg = msg.strip()
t = (err.rcode, name, key, msg)
self.ctx.out("%5d\t%10s\t%10s\t'%s'" % t)

controls = {
"help": (HelpControl, "Syntax help for all commands"),
"quit": (QuitControl, "Quit application"),
"errors": (ErrorsControl, "Display all plugin error codes"),
"shell": (ShellControl, """Starts an IPython interpreter session

All arguments not understood vi %(prog)s will be passed to the shell.
Expand Down
13 changes: 9 additions & 4 deletions components/tools/OmeroPy/src/omero/plugins/hql.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,19 @@ def _configure(self, parser):
parser.add_limit_arguments()
parser.add_style_argument()
parser.add_login_arguments()
self.add_error("NO_QUIET", 67,
"Can't ask for query with --quiet option")
self.add_error("NOT_ADMIN", 53,
("SecurityViolation: Current user is not an"
" admin and cannot use '--admin'"))
self.add_error("BAD_QUERY", 52, "Bad query: %s")

def __call__(self, args):
if args.query:
self.hql(args)
else:
if self.ctx.isquiet:
self.ctx.die(67, "Can't ask for query with --quiet option")
self.raise_error("NO_QUIET")
while True:
args.query = self.ctx.input("Enter query:")
if not args.query:
Expand Down Expand Up @@ -246,13 +252,12 @@ def project(self, querySvc, queryStr, params, ice_map):
return rv
except omero.SecurityViolation, sv:
if "omero.group" in ice_map:
self.ctx.die(53, "SecurityViolation: Current user is not an"
" admin and cannot use '--admin'")
self.raise_error("NOT_ADMIN")
else:
self.ctx.die(54, "SecurityViolation: %s" % sv)
except omero.QueryException, qe:
self.ctx.set("last.hql.rv", [])
self.ctx.die(52, "Bad query: %s" % qe.message)
self.raise_error("BAD_QUERY", qe.message)

try:
register("hql", HqlControl, HELP)
Expand Down
3 changes: 2 additions & 1 deletion components/tools/OmeroPy/src/omero/plugins/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def _configure(self, parser):
parser.add(sub, self.indexer, help="Start OMERO.indexer")
# web = parser.add(sub, self.web, help = "Start OMERO.web")
# web.add_argument("arg", nargs="*")
self.add_error("NO_CONFIG", 201, "No --Ice.Config provided")

def _prop(self, data, key):
return data.properties.getProperty("omero."+key)
Expand All @@ -43,7 +44,7 @@ def _checkIceConfig(self, args):
try:
args["--Ice.Config"]
except KeyError:
self.ctx.die(201, "No --Ice.Config provided")
self.raise_error("NO_CONFIG")
pre = []
post = []
for arg in args.args:
Expand Down