Merge pull request #180 from irina11y/enh-moulinette-error

[enh] Simplify moulinette errors
This commit is contained in:
Alexandre Aubin 2018-12-15 15:08:58 +01:00 committed by GitHub
commit e6c41b100b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 75 additions and 148 deletions

View file

@ -27,7 +27,6 @@
"operation_interrupted": "Operation interrupted",
"password": "Password",
"pattern_not_match": "Does not match pattern",
"permission_denied": "Permission denied",
"root_required": "You must be root to perform this action",
"server_already_running": "A server is already running on that port",
"success": "Success!",

View file

@ -137,5 +137,5 @@ def cli(namespaces, args, use_cache=True, output_as=None,
except MoulinetteError as e:
import logging
logging.getLogger(namespaces[0]).error(e.strerror)
return e.errno
return 1
return 0

View file

@ -2,7 +2,6 @@
import os
import re
import errno
import logging
import yaml
import cPickle as pickle
@ -187,9 +186,8 @@ class PatternParameter(_ExtraParameter):
if msg == message:
msg = m18n.g(message)
raise MoulinetteError(errno.EINVAL,
m18n.g('invalid_argument',
argument=arg_name, error=msg))
raise MoulinetteError('invalid_argument',
argument=arg_name, error=msg)
return arg_value
@staticmethod
@ -218,9 +216,8 @@ class RequiredParameter(_ExtraParameter):
if required and (arg_value is None or arg_value == ''):
logger.debug("argument '%s' is required",
arg_name)
raise MoulinetteError(errno.EINVAL,
m18n.g('argument_required',
argument=arg_name))
raise MoulinetteError('argument_required',
argument=arg_name)
return arg_value
@staticmethod
@ -285,7 +282,7 @@ class ExtraArgumentParser(object):
except Exception as e:
logger.error("unable to validate extra parameter '%s' "
"for argument '%s': %s", p, arg_name, e)
raise MoulinetteError(errno.EINVAL, m18n.g('error_see_log'))
raise MoulinetteError('error_see_log')
return parameters
@ -501,7 +498,7 @@ class ActionsMap(object):
except (AttributeError, ImportError):
logger.exception("unable to load function %s.%s",
namespace, func_name)
raise MoulinetteError(errno.EIO, m18n.g('error_see_log'))
raise MoulinetteError('error_see_log')
else:
log_id = start_action_logging()
if logger.isEnabledFor(logging.DEBUG):

View file

@ -1,10 +1,8 @@
# -*- coding: utf-8 -*-
import errno
import gnupg
import logging
from moulinette import m18n
from moulinette.cache import open_cachefile
from moulinette.core import MoulinetteError
@ -97,7 +95,7 @@ class BaseAuthenticator(object):
except TypeError:
logger.error("unable to extract token parts from '%s'", token)
if password is None:
raise MoulinetteError(errno.EINVAL, m18n.g('error_see_log'))
raise MoulinetteError('error_see_log')
logger.info("session will not be stored")
store_session = False
@ -114,7 +112,7 @@ class BaseAuthenticator(object):
except:
logger.exception("authentication (name: '%s', vendor: '%s') fails",
self.name, self.vendor)
raise MoulinetteError(errno.EACCES, m18n.g('unable_authenticate'))
raise MoulinetteError('unable_authenticate')
# Store session
if store_session:
@ -149,8 +147,7 @@ class BaseAuthenticator(object):
enc_pwd = f.read()
except IOError:
logger.debug("unable to retrieve session", exc_info=1)
raise MoulinetteError(errno.ENOENT,
m18n.g('unable_retrieve_session'))
raise MoulinetteError('unable_retrieve_session')
else:
gpg = gnupg.GPG()
gpg.encoding = 'utf-8'
@ -159,6 +156,5 @@ class BaseAuthenticator(object):
if decrypted.ok is not True:
logger.error("unable to decrypt password for the session: %s",
decrypted.status)
raise MoulinetteError(errno.EINVAL,
m18n.g('unable_retrieve_session'))
raise MoulinetteError('unable_retrieve_session')
return decrypted.data

View file

@ -2,7 +2,6 @@
# TODO: Use Python3 to remove this fix!
from __future__ import absolute_import
import errno
import logging
import random
import string
@ -10,7 +9,6 @@ import crypt
import ldap
import ldap.modlist as modlist
from moulinette import m18n
from moulinette.core import MoulinetteError
from moulinette.authenticators import BaseAuthenticator
@ -82,10 +80,10 @@ class Authenticator(BaseAuthenticator):
else:
con.simple_bind_s()
except ldap.INVALID_CREDENTIALS:
raise MoulinetteError(errno.EACCES, m18n.g('invalid_password'))
raise MoulinetteError('invalid_password')
except ldap.SERVER_DOWN:
logger.exception('unable to reach the server to authenticate')
raise MoulinetteError(169, m18n.g('ldap_server_down'))
raise MoulinetteError('ldap_server_down')
else:
self.con = con
self._ensure_password_uses_strong_hash(password)
@ -137,7 +135,7 @@ class Authenticator(BaseAuthenticator):
except Exception as e:
logger.exception("error during LDAP search operation with: base='%s', "
"filter='%s', attrs=%s and exception %s", base, filter, attrs, e)
raise MoulinetteError(169, m18n.g('ldap_operation_error'))
raise MoulinetteError('ldap_operation_error')
result_list = []
if not attrs or 'dn' not in attrs:
@ -168,7 +166,7 @@ class Authenticator(BaseAuthenticator):
except Exception as e:
logger.exception("error during LDAP add operation with: rdn='%s', "
"attr_dict=%s and exception %s", rdn, attr_dict, e)
raise MoulinetteError(169, m18n.g('ldap_operation_error'))
raise MoulinetteError('ldap_operation_error')
else:
return True
@ -188,7 +186,7 @@ class Authenticator(BaseAuthenticator):
self.con.delete_s(dn)
except Exception as e:
logger.exception("error during LDAP delete operation with: rdn='%s' and exception %s", rdn, e)
raise MoulinetteError(169, m18n.g('ldap_operation_error'))
raise MoulinetteError('ldap_operation_error')
else:
return True
@ -219,7 +217,7 @@ class Authenticator(BaseAuthenticator):
logger.exception("error during LDAP update operation with: rdn='%s', "
"attr_dict=%s, new_rdn=%s and exception: %s", rdn, attr_dict,
new_rdn, e)
raise MoulinetteError(169, m18n.g('ldap_operation_error'))
raise MoulinetteError('ldap_operation_error')
else:
return True
@ -238,10 +236,9 @@ class Authenticator(BaseAuthenticator):
if attr_found:
logger.info("attribute '%s' with value '%s' is not unique",
attr_found[0], attr_found[1])
raise MoulinetteError(errno.EEXIST,
m18n.g('ldap_attribute_already_exists',
attribute=attr_found[0],
value=attr_found[1]))
raise MoulinetteError('ldap_attribute_already_exists',
attribute=attr_found[0],
value=attr_found[1])
return True
def get_conflict(self, value_dict, base_dn=None):

View file

@ -3,7 +3,6 @@
import os
import time
import json
import errno
import logging
import psutil
@ -344,7 +343,7 @@ def init_interface(name, kwargs={}, actionsmap={}):
mod = import_module('moulinette.interfaces.%s' % name)
except ImportError:
logger.exception("unable to load interface '%s'", name)
raise MoulinetteError(errno.EINVAL, moulinette.m18n.g('error_see_log'))
raise MoulinetteError('error_see_log')
else:
try:
# Retrieve interface classes
@ -352,7 +351,7 @@ def init_interface(name, kwargs={}, actionsmap={}):
interface = mod.Interface
except AttributeError:
logger.exception("unable to retrieve classes of interface '%s'", name)
raise MoulinetteError(errno.EIO, moulinette.m18n.g('error_see_log'))
raise MoulinetteError('error_see_log')
# Instantiate or retrieve ActionsMap
if isinstance(actionsmap, dict):
@ -361,7 +360,7 @@ def init_interface(name, kwargs={}, actionsmap={}):
amap = actionsmap
else:
logger.error("invalid actionsmap value %r", actionsmap)
raise MoulinetteError(errno.EINVAL, moulinette.m18n.g('error_see_log'))
raise MoulinetteError('error_see_log')
return interface(amap, **kwargs)
@ -382,7 +381,7 @@ def init_authenticator((vendor, name), kwargs={}):
mod = import_module('moulinette.authenticators.%s' % vendor)
except ImportError:
logger.exception("unable to load authenticator vendor '%s'", vendor)
raise MoulinetteError(errno.EINVAL, moulinette.m18n.g('error_see_log'))
raise MoulinetteError('error_see_log')
else:
return mod.Authenticator(name, **kwargs)
@ -411,9 +410,15 @@ def clean_session(session_id, profiles=[]):
# Moulinette core classes ----------------------------------------------
class MoulinetteError(OSError):
class MoulinetteError(Exception):
"""Moulinette base exception"""
pass
def __init__(self, key, raw_msg=False, *args, **kwargs):
if raw_msg:
msg = key
else:
msg = moulinette.m18n.g(key, *args, **kwargs)
super(MoulinetteError, self).__init__(msg)
self.strerror = msg
class MoulinetteLock(object):
@ -471,8 +476,7 @@ class MoulinetteLock(object):
break
if self.timeout is not None and (time.time() - start_time) > self.timeout:
raise MoulinetteError(errno.EBUSY,
moulinette.m18n.g('instance_already_running'))
raise MoulinetteError('instance_already_running')
# Wait before checking again
time.sleep(self.interval)
@ -495,10 +499,7 @@ class MoulinetteLock(object):
with open(self._lockfile, 'w') as f:
f.write(str(os.getpid()))
except IOError:
raise MoulinetteError(
errno.EPERM, '%s. %s.'.format(
moulinette.m18n.g('permission_denied'),
moulinette.m18n.g('root_required')))
raise MoulinetteError('root_required')
def _lock_PIDs(self):

View file

@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
import re
import errno
import logging
import argparse
import copy
@ -142,7 +141,7 @@ class BaseActionsMapParser(object):
# Validate tid and namespace
if not isinstance(tid, tuple) and \
(namespace is None or not hasattr(namespace, TO_RETURN_PROP)):
raise MoulinetteError(errno.EINVAL, m18n.g('invalid_usage'))
raise MoulinetteError('invalid_usage')
elif not tid:
tid = GLOBAL_SECTION
@ -158,8 +157,7 @@ class BaseActionsMapParser(object):
# TODO: Catch errors
auth = msignals.authenticate(cls(), **auth_conf)
if not auth.is_authenticated:
raise MoulinetteError(errno.EACCES,
m18n.g('authentication_required_long'))
raise MoulinetteError('authentication_required_long')
if self.get_conf(tid, 'argument_auth') and \
self.get_conf(tid, 'authenticate') == 'all':
namespace.auth = auth
@ -263,7 +261,7 @@ class BaseActionsMapParser(object):
else:
logger.error("expecting 'all', 'False' or a list for "
"configuration 'authenticate', got %r", ifaces)
raise MoulinetteError(errno.EINVAL, m18n.g('error_see_log'))
raise MoulinetteError('error_see_log')
# -- 'authenticator'
try:
@ -278,7 +276,7 @@ class BaseActionsMapParser(object):
except KeyError:
logger.error("requesting profile '%s' which is undefined in "
"global configuration of 'authenticator'", auth)
raise MoulinetteError(errno.EINVAL, m18n.g('error_see_log'))
raise MoulinetteError('error_see_log')
elif is_global and isinstance(auth, dict):
if len(auth) == 0:
logger.warning('no profile defined in global configuration '
@ -301,7 +299,7 @@ class BaseActionsMapParser(object):
else:
logger.error("expecting a dict of profile(s) or a profile name "
"for configuration 'authenticator', got %r", auth)
raise MoulinetteError(errno.EINVAL, m18n.g('error_see_log'))
raise MoulinetteError('error_see_log')
# -- 'argument_auth'
try:
@ -314,7 +312,7 @@ class BaseActionsMapParser(object):
else:
logger.error("expecting a boolean for configuration "
"'argument_auth', got %r", arg_auth)
raise MoulinetteError(errno.EINVAL, m18n.g('error_see_log'))
raise MoulinetteError('error_see_log')
# -- 'lock'
try:
@ -327,7 +325,7 @@ class BaseActionsMapParser(object):
else:
logger.error("expecting a boolean for configuration 'lock', "
"got %r", lock)
raise MoulinetteError(errno.EINVAL, m18n.g('error_see_log'))
raise MoulinetteError('error_see_log')
return conf
@ -427,7 +425,7 @@ class _CallbackAction(argparse.Action):
except:
logger.exception("cannot get value from callback method "
"'{0}'".format(self.callback_method))
raise MoulinetteError(errno.EINVAL, m18n.g('error_see_log'))
raise MoulinetteError('error_see_log')
else:
if value:
if self.callback_return:

View file

@ -189,7 +189,7 @@ class _HTTPArgumentParser(object):
def _error(self, message):
# TODO: Raise a proper exception
raise MoulinetteError(1, message)
raise MoulinetteError(message)
class _ActionsMapPlugin(object):
@ -347,7 +347,7 @@ class _ActionsMapPlugin(object):
self.logout(profile)
except:
pass
raise error_to_response(e)
raise HTTPUnauthorizedResponse(e.strerror)
else:
# Update dicts with new values
s_hashes[profile] = s_hash
@ -434,7 +434,7 @@ class _ActionsMapPlugin(object):
try:
ret = self.actionsmap.process(arguments, timeout=30, route=_route)
except MoulinetteError as e:
raise error_to_response(e)
raise HTTPBadRequestResponse(e.strerror)
except Exception as e:
if isinstance(e, HTTPResponse):
raise e
@ -518,38 +518,12 @@ class HTTPUnauthorizedResponse(HTTPResponse):
super(HTTPUnauthorizedResponse, self).__init__(output, 401)
class HTTPForbiddenResponse(HTTPResponse):
def __init__(self, output=''):
super(HTTPForbiddenResponse, self).__init__(output, 403)
class HTTPErrorResponse(HTTPResponse):
def __init__(self, output=''):
super(HTTPErrorResponse, self).__init__(output, 500)
def error_to_response(error):
"""Convert a MoulinetteError to relevant HTTP response."""
if error.errno == errno.EPERM:
return HTTPForbiddenResponse(error.strerror)
elif error.errno == errno.EACCES:
return HTTPUnauthorizedResponse(error.strerror)
# Client-side error
elif error.errno in [errno.ENOENT, errno.ESRCH, errno.ENXIO, errno.EEXIST,
errno.ENODEV, errno.EINVAL, errno.ENOPKG, errno.EDESTADDRREQ]:
return HTTPBadRequestResponse(error.strerror)
# Server-side error
elif error.errno in [errno.EIO, errno.EBUSY, errno.ENODATA, errno.EINTR,
errno.ENETUNREACH]:
return HTTPErrorResponse(error.strerror)
else:
logger.debug('unknown relevant response for error [%s] %s',
error.errno, error.strerror)
return HTTPErrorResponse(error.strerror)
def format_for_response(content):
"""Format the resulted content of a request for the HTTP response."""
if request.method == 'POST':
@ -660,7 +634,7 @@ class ActionsMapParser(BaseActionsMapParser):
tid, parser = self._parsers[route]
except KeyError:
logger.error("no argument parser found for route '%s'", route)
raise MoulinetteError(errno.EINVAL, m18n.g('error_see_log'))
raise MoulinetteError('error_see_log')
ret = argparse.Namespace()
# Perform authentication if needed
@ -673,7 +647,7 @@ class ActionsMapParser(BaseActionsMapParser):
# TODO: Catch errors
auth = msignals.authenticate(klass(), **auth_conf)
if not auth.is_authenticated:
raise MoulinetteError(errno.EACCES, m18n.g('authentication_required_long'))
raise MoulinetteError('authentication_required_long')
if self.get_conf(tid, 'argument_auth') and \
self.get_conf(tid, 'authenticate') == 'all':
ret.auth = auth
@ -796,9 +770,8 @@ class Interface(BaseInterface):
logger.exception("unable to start the server instance on %s:%d",
host, port)
if e.args[0] == errno.EADDRINUSE:
raise MoulinetteError(errno.EADDRINUSE,
m18n.g('server_already_running'))
raise MoulinetteError(errno.EIO, m18n.g('error_see_log'))
raise MoulinetteError('server_already_running')
raise MoulinetteError('error_see_log')
# Routes handlers

View file

@ -2,7 +2,6 @@
import os
import sys
import errno
import getpass
import locale
import logging
@ -362,7 +361,7 @@ class ActionsMapParser(BaseActionsMapParser):
raise
except:
logger.exception("unable to parse arguments '%s'", ' '.join(args))
raise MoulinetteError(errno.EINVAL, m18n.g('error_see_log'))
raise MoulinetteError('error_see_log')
else:
self.prepare_action_namespace(getattr(ret, '_tid', None), ret)
self._parser.dequeue_callbacks(ret)
@ -409,7 +408,7 @@ class Interface(BaseInterface):
"""
if output_as and output_as not in ['json', 'plain', 'none']:
raise MoulinetteError(errno.EINVAL, m18n.g('invalid_usage'))
raise MoulinetteError('invalid_usage')
# auto-complete
argcomplete.autocomplete(self.actionsmap.parser._parser)
@ -422,7 +421,7 @@ class Interface(BaseInterface):
try:
ret = self.actionsmap.process(args, timeout=timeout)
except (KeyboardInterrupt, EOFError):
raise MoulinetteError(errno.EINTR, m18n.g('operation_interrupted'))
raise MoulinetteError('operation_interrupted')
if ret is None or output_as == 'none':
return
@ -472,7 +471,7 @@ class Interface(BaseInterface):
if confirm:
m = message[0].lower() + message[1:]
if prompt(m18n.g('confirm', prompt=m)) != value:
raise MoulinetteError(errno.EINVAL, m18n.g('values_mismatch'))
raise MoulinetteError('values_mismatch')
return value

View file

@ -23,21 +23,16 @@ def read_file(file_path):
# Check file exists
if not os.path.isfile(file_path):
raise MoulinetteError(errno.ENOENT,
m18n.g('file_not_exist', path=file_path))
raise MoulinetteError('file_not_exist', path=file_path)
# Open file and read content
try:
with open(file_path, "r") as f:
file_content = f.read()
except IOError as e:
raise MoulinetteError(errno.EACCES,
m18n.g('cannot_open_file',
file=file_path, error=str(e)))
raise MoulinetteError('cannot_open_file', file=file_path, error=str(e))
except Exception as e:
raise MoulinetteError(errno.EIO,
m18n.g('error_reading_file',
file=file_path, error=str(e)))
raise MoulinetteError('error_reading_file', file=file_path, error=str(e))
return file_content
@ -57,9 +52,7 @@ def read_json(file_path):
try:
loaded_json = json.loads(file_content)
except ValueError as e:
raise MoulinetteError(errno.EINVAL,
m18n.g('corrupted_json',
ressource=file_path, error=str(e)))
raise MoulinetteError('corrupted_json', ressource=file_path, error=str(e))
return loaded_json
@ -79,9 +72,7 @@ def read_yaml(file_path):
try:
loaded_yaml = yaml.safe_load(file_content)
except ValueError as e:
raise MoulinetteError(errno.EINVAL,
m18n.g('corrupted_yaml',
ressource=file_path, error=str(e)))
raise MoulinetteError('corrupted_yaml', ressource=file_path, error=str(e))
return loaded_yaml
@ -111,13 +102,9 @@ def write_to_file(file_path, data, file_mode="w"):
with open(file_path, file_mode) as f:
f.write(data)
except IOError as e:
raise MoulinetteError(errno.EACCES,
m18n.g('cannot_write_file',
file=file_path, error=str(e)))
raise MoulinetteError('cannot_write_file', file=file_path, error=str(e))
except Exception as e:
raise MoulinetteError(errno.EIO,
m18n.g('error_writing_file',
file=file_path, error=str(e)))
raise MoulinetteError('error_writing_file', file=file_path, error=str(e))
def append_to_file(file_path, data):
@ -152,13 +139,9 @@ def write_to_json(file_path, data):
with open(file_path, "w") as f:
json.dump(data, f)
except IOError as e:
raise MoulinetteError(errno.EACCES,
m18n.g('cannot_write_file',
file=file_path, error=str(e)))
raise MoulinetteError('cannot_write_file', file=file_path, error=str(e))
except Exception as e:
raise MoulinetteError(errno.EIO,
m18n.g('_error_writing_file',
file=file_path, error=str(e)))
raise MoulinetteError('_error_writing_file', file=file_path, error=str(e))
def mkdir(path, mode=0777, parents=False, uid=None, gid=None, force=False):
@ -223,16 +206,14 @@ def chown(path, uid=None, gid=None, recursive=False):
try:
uid = getpwnam(uid).pw_uid
except KeyError:
raise MoulinetteError(errno.EINVAL,
m18n.g('unknown_user', user=uid))
raise MoulinetteError('unknown_user', user=uid)
elif uid is None:
uid = -1
if isinstance(gid, basestring):
try:
gid = grp.getgrnam(gid).gr_gid
except KeyError:
raise MoulinetteError(errno.EINVAL,
m18n.g('unknown_group', group=gid))
raise MoulinetteError('unknown_group', group=gid)
elif gid is None:
gid = -1
@ -245,9 +226,7 @@ def chown(path, uid=None, gid=None, recursive=False):
for f in files:
os.chown(os.path.join(root, f), uid, gid)
except Exception as e:
raise MoulinetteError(errno.EIO,
m18n.g('error_changing_file_permissions',
path=path, error=str(e)))
raise MoulinetteError('error_changing_file_permissions', path=path, error=str(e))
def chmod(path, mode, fmode=None, recursive=False):
@ -271,9 +250,7 @@ def chmod(path, mode, fmode=None, recursive=False):
for f in files:
os.chmod(os.path.join(root, f), fmode)
except Exception as e:
raise MoulinetteError(errno.EIO,
m18n.g('error_changing_file_permissions',
path=path, error=str(e)))
raise MoulinetteError('error_changing_file_permissions', path=path, error=str(e))
def rm(path, recursive=False, force=False):
@ -292,6 +269,4 @@ def rm(path, recursive=False, force=False):
os.remove(path)
except OSError as e:
if not force:
raise MoulinetteError(errno.EIO,
m18n.g('error_removing',
path=path, error=str(e)))
raise MoulinetteError('error_removing', path=path, error=str(e))

View file

@ -1,7 +1,5 @@
import errno
import json
from moulinette import m18n
from moulinette.core import MoulinetteError
@ -25,27 +23,22 @@ def download_text(url, timeout=30, expected_status_code=200):
r = requests.get(url, timeout=timeout)
# Invalid URL
except requests.exceptions.ConnectionError:
raise MoulinetteError(errno.EBADE,
m18n.g('invalid_url', url=url))
raise MoulinetteError('invalid_url', url=url)
# SSL exceptions
except requests.exceptions.SSLError:
raise MoulinetteError(errno.EBADE,
m18n.g('download_ssl_error', url=url))
raise MoulinetteError('download_ssl_error', url=url)
# Timeout exceptions
except requests.exceptions.Timeout:
raise MoulinetteError(errno.ETIME,
m18n.g('download_timeout', url=url))
raise MoulinetteError('download_timeout', url=url)
# Unknown stuff
except Exception as e:
raise MoulinetteError(errno.ECONNRESET,
m18n.g('download_unknown_error',
url=url, error=str(e)))
raise MoulinetteError('download_unknown_error',
url=url, error=str(e))
# Assume error if status code is not 200 (OK)
if expected_status_code is not None \
and r.status_code != expected_status_code:
raise MoulinetteError(errno.EBADE,
m18n.g('download_bad_status_code',
url=url, code=str(r.status_code)))
raise MoulinetteError('download_bad_status_code',
url=url, code=str(r.status_code))
return r.text
@ -66,7 +59,6 @@ def download_json(url, timeout=30, expected_status_code=200):
try:
loaded_json = json.loads(text)
except ValueError:
raise MoulinetteError(errno.EINVAL,
m18n.g('corrupted_json', ressource=url))
raise MoulinetteError('corrupted_json', ressource=url)
return loaded_json