diff options
author | Rob Crittenden <rcritten@redhat.com> | 2009-04-20 13:58:26 -0400 |
---|---|---|
committer | Rob Crittenden <rcritten@redhat.com> | 2009-04-20 13:58:26 -0400 |
commit | 64fa3dd4c3a03e7a677453c9150f84ffc4e91c7a (patch) | |
tree | a4543df175f8bf0efcd200662a9e7f00fea7bf52 /ipalib | |
parent | a9387b48e66ca93cc8323869de25fe3f777567b6 (diff) | |
download | freeipa-64fa3dd4c3a03e7a677453c9150f84ffc4e91c7a.tar.gz freeipa-64fa3dd4c3a03e7a677453c9150f84ffc4e91c7a.tar.xz freeipa-64fa3dd4c3a03e7a677453c9150f84ffc4e91c7a.zip |
Finish work replacing the errors module with errors2
Once this is committed we can start the process of renaming errors2 as errors.
I thought that combinig this into one commit would be more difficult to
review.
Diffstat (limited to 'ipalib')
-rw-r--r-- | ipalib/cli.py | 8 | ||||
-rw-r--r-- | ipalib/errors.py | 441 | ||||
-rw-r--r-- | ipalib/errors2.py | 174 | ||||
-rw-r--r-- | ipalib/plugins/basegroup.py | 2 | ||||
-rw-r--r-- | ipalib/plugins/passwd.py | 14 | ||||
-rw-r--r-- | ipalib/util.py | 21 |
6 files changed, 194 insertions, 466 deletions
diff --git a/ipalib/cli.py b/ipalib/cli.py index 11b56e36f..36c945f2b 100644 --- a/ipalib/cli.py +++ b/ipalib/cli.py @@ -36,8 +36,7 @@ import frontend import backend import plugable import util -from errors2 import PublicError, CommandError, HelpError, InternalError -import errors +from errors2 import PublicError, CommandError, HelpError, InternalError, NoSuchNamespaceError, ValidationError from constants import CLI_TAB from parameters import Password, Bytes from request import ugettext as _ @@ -456,7 +455,7 @@ class show_api(frontend.Application): else: for name in namespaces: if name not in self.api: - raise errors.NoSuchNamespaceError(name) + raise NoSuchNamespaceError(name=name) names = namespaces lines = self.__traverse(names) ml = max(len(l[1]) for l in lines) @@ -478,7 +477,6 @@ class show_api(frontend.Application): s = '%d attributes show.' % len(lines) self.Backend.textui.print_dashed(s) - def __traverse(self, names): lines = [] for name in names: @@ -635,7 +633,7 @@ class cli(backend.Executioner): if value is not None: kw[param.name] = value break - except errors.ValidationError, e: + except ValidationError, e: error = e.error diff --git a/ipalib/errors.py b/ipalib/errors.py deleted file mode 100644 index c27d85dea..000000000 --- a/ipalib/errors.py +++ /dev/null @@ -1,441 +0,0 @@ -# Authors: -# Jason Gerard DeRose <jderose@redhat.com> -# -# Copyright (C) 2008 Red Hat -# see file 'COPYING' for use and warranty inmsgion -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -All custom errors raised by `ipalib` package. - -Also includes a few utility functions for raising exceptions. -""" - -IPA_ERROR_BASE = 1000 - -TYPE_FORMAT = '%s: need a %r; got %r' - -def raise_TypeError(value, type_, name): - """ - Raises a TypeError with a nicely formatted message and helpful attributes. - - The TypeError raised will have three custom attributes: - - ``value`` - The value (of incorrect type) passed as argument. - - ``type`` - The type expected for the argument. - - ``name`` - The name (identifier) of the argument in question. - - There is no edict that all TypeError should be raised with raise_TypeError, - but when it fits, use it... it makes the unit tests faster to write and - the debugging easier to read. - - Here is an example: - - >>> raise_TypeError(u'Hello, world!', str, 'message') - Traceback (most recent call last): - File "<stdin>", line 1, in <module> - File "ipalib/errors.py", line 65, in raise_TypeError - raise e - TypeError: message: need a <type 'str'>; got u'Hello, world!' - - :param value: The value (of incorrect type) passed as argument. - :param type_: The type expected for the argument. - :param name: The name (identifier) of the argument in question. - """ - - assert type(type_) is type, TYPE_FORMAT % ('type_', type, type_) - assert type(value) is not type_, 'value: %r is a %r' % (value, type_) - assert type(name) is str, TYPE_FORMAT % ('name', str, name) - e = TypeError(TYPE_FORMAT % (name, type_, value)) - setattr(e, 'value', value) - setattr(e, 'type', type_) - setattr(e, 'name', name) - raise e - - -def check_type(value, type_, name, allow_none=False): - assert type(name) is str, TYPE_FORMAT % ('name', str, name) - assert type(type_) is type, TYPE_FORMAT % ('type_', type, type_) - assert type(allow_none) is bool, TYPE_FORMAT % ('allow_none', bool, allow_none) - if value is None and allow_none: - return - if type(value) is not type_: - raise_TypeError(value, type_, name) - return value - - -def check_isinstance(value, type_, name, allow_none=False): - assert type(type_) is type, TYPE_FORMAT % ('type_', type, type_) - assert type(name) is str, TYPE_FORMAT % ('name', str, name) - assert type(allow_none) is bool, TYPE_FORMAT % ('allow_none', bool, allow_none) - if value is None and allow_none: - return - if not isinstance(value, type_): - raise_TypeError(value, type_, name) - return value - - -class IPAError(StandardError): - """ - Base class for all custom IPA errors. - - Use this base class for your custom IPA errors unless there is a - specific reason to subclass from AttributeError, KeyError, etc. - """ - - format = None - faultCode = 1 - - def __init__(self, *args): - self.args = args - - def __str__(self): - """ - Returns the string representation of this exception. - """ - return self.format % self.args - - -class InvocationError(IPAError): - pass - - -class UnknownCommandError(InvocationError): - format = 'unknown command "%s"' - -class NoSuchNamespaceError(InvocationError): - format = 'api has no such namespace: %s' - -def _(text): - return text - - -class SubprocessError(StandardError): - def __init__(self, returncode, argv): - self.returncode = returncode - self.argv = argv - StandardError.__init__(self, - 'return code %d from %r' % (returncode, argv) - ) - -class HandledError(StandardError): - """ - Base class for errors that can be raised across a remote procedure call. - """ - - code = 1 - - def __init__(self, message=None, **kw): - self.kw = kw - if message is None: - message = self.format % kw - StandardError.__init__(self, message) - - -class UnknownError(HandledError): - """ - Raised when the true error is not a handled error. - """ - - format = _('An unknown internal error has occurred') - - -class CommandError(HandledError): - """ - Raised when an unknown command is called client-side. - """ - format = _('Unknown command %(name)r') - - -class RemoteCommandError(HandledError): - format = 'Server at %(uri)r has no command %(name)r' - - -class UnknownHelpError(InvocationError): - format = 'no command nor topic "%s"' - - -class ArgumentError(IPAError): - """ - Raised when a command is called with wrong number of arguments. - """ - - format = '%s %s' - - def __init__(self, command, error): - self.command = command - self.error = error - IPAError.__init__(self, command.name, error) - - -class ValidationError(IPAError): - """ - Base class for all types of validation errors. - """ - - format = 'invalid %r value %r: %s' - - def __init__(self, name, value, error, index=None): - """ - :param name: The name of the value that failed validation. - :param value: The value that failed validation. - :param error: The error message describing the failure. - :param index: If multivalue, index of value in multivalue tuple - """ - assert type(name) is str - assert index is None or (type(index) is int and index >= 0) - self.name = name - self.value = value - self.error = error - self.index = index - IPAError.__init__(self, name, value, error) - - -class ConversionError(ValidationError): - """ - Raised when a value cannot be converted to the correct type. - """ - - def __init__(self, name, value, type_, index=None): - self.type = type_ - ValidationError.__init__(self, name, value, type_.conversion_error, - index=index, - ) - - -class RuleError(ValidationError): - """ - Raised when a value fails a validation rule. - """ - def __init__(self, name, value, error, rule, index=None): - assert callable(rule) - self.rule = rule - ValidationError.__init__(self, name, value, error, index=index) - - -class RequirementError(ValidationError): - """ - Raised when a required option was not provided. - """ - def __init__(self, name): - ValidationError.__init__(self, name, None, 'Required') - - -class RegistrationError(IPAError): - """ - Base class for errors that occur during plugin registration. - """ - - -class SubclassError(RegistrationError): - """ - Raised when registering a plugin that is not a subclass of one of the - allowed bases. - """ - msg = 'plugin %r not subclass of any base in %r' - - def __init__(self, cls, allowed): - self.cls = cls - self.allowed = allowed - - def __str__(self): - return self.msg % (self.cls, self.allowed) - - -class DuplicateError(RegistrationError): - """ - Raised when registering a plugin whose exact class has already been - registered. - """ - msg = '%r at %d was already registered' - - def __init__(self, cls): - self.cls = cls - - def __str__(self): - return self.msg % (self.cls, id(self.cls)) - - -class OverrideError(RegistrationError): - """ - Raised when override=False yet registering a plugin that overrides an - existing plugin in the same namespace. - """ - msg = 'unexpected override of %s.%s with %r (use override=True if intended)' - - def __init__(self, base, cls): - self.base = base - self.cls = cls - - def __str__(self): - return self.msg % (self.base.__name__, self.cls.__name__, self.cls) - - -class MissingOverrideError(RegistrationError): - """ - Raised when override=True yet no preexisting plugin with the same name - and base has been registered. - """ - msg = '%s.%s has not been registered, cannot override with %r' - - def __init__(self, base, cls): - self.base = base - self.cls = cls - - def __str__(self): - return self.msg % (self.base.__name__, self.cls.__name__, self.cls) - -class GenericError(IPAError): - """Base class for our custom exceptions""" - faultCode = 1000 - fromFault = False - def __str__(self): - try: - return str(self.args[0]['args'][0]) - except: - try: - return str(self.args[0]) - except: - return str(self.__dict__) - -class DatabaseError(GenericError): - """A database error has occurred""" - faultCode = 1001 - -class MidairCollision(GenericError): - """Change collided with another change""" - faultCode = 1002 - -class MissingDN(GenericError): - """The distinguished name (DN) is missing""" - faultCode = 1005 - -class EmptyModlist(GenericError): - """No modifications to be performed""" - faultCode = 1006 - -class InputError(GenericError): - """Error on input""" - faultCode = 1007 - -class SameGroupError(InputError): - """You can't add a group to itself""" - faultCode = 1008 - -class NotGroupMember(InputError): - """This entry is not a member of the group""" - faultCode = 1009 - -class AdminsImmutable(InputError): - """The admins group cannot be renamed""" - faultCode = 1010 - -class UsernameTooLong(InputError): - """The requested username is too long""" - faultCode = 1011 - -class PrincipalError(GenericError): - """There is a problem with the kerberos principal""" - faultCode = 1012 - -class PrincipalRequired(PrincipalError): - """You cannot remove IPA server service principals""" - faultCode = 1015 - -class InactivationError(GenericError): - """This entry cannot be inactivated""" - faultCode = 1016 - -class AlreadyActiveError(InactivationError): - """This entry is already locked""" - faultCode = 1017 - -class AlreadyInactiveError(InactivationError): - """This entry is already unlocked""" - faultCode = 1018 - -class HasNSAccountLock(InactivationError): - """This entry appears to have the nsAccountLock attribute in it so the Class of Service activation/inactivation will not work. You will need to remove the attribute nsAccountLock for this to work.""" - faultCode = 1019 - -class ConnectionError(GenericError): - """Connection to database failed""" - faultCode = 1020 - -class NoCCacheError(GenericError): - """No Kerberos credentials cache is available. Connection cannot be made""" - faultCode = 1021 - -class GSSAPIError(GenericError): - """GSSAPI Authorization error""" - faultCode = 1022 - -class ServerUnwilling(GenericError): - """Account inactivated. Server is unwilling to perform""" - faultCode = 1023 - -class ConfigurationError(GenericError): - """A configuration error occurred""" - faultCode = 1024 - -class DefaultGroup(ConfigurationError): - """You cannot remove the default users group""" - faultCode = 1025 - -class InvalidUserPrincipal(GenericError): - """Invalid user principal""" - faultCode = 1028 - -class FunctionDeprecated(GenericError): - """Raised by a deprecated function""" - faultCode = 2000 - -def convertFault(fault): - """Convert a fault to the corresponding Exception type, if possible""" - code = getattr(fault,'faultCode',None) - if code is None: - return fault - for v in globals().values(): - if type(v) == type(Exception) and issubclass(v,GenericError) and \ - code == getattr(v,'faultCode',None): - ret = v(fault.faultString) - ret.fromFault = True - return ret - #otherwise... - return fault - -def listFaults(): - """Return a list of faults - - Returns a list of dictionaries whose keys are: - faultCode: the numeric code used in fault conversion - name: the name of the exception - desc: the description of the exception (docstring) - """ - ret = [] - for n,v in globals().items(): - if type(v) == type(Exception) and issubclass(v,GenericError): - code = getattr(v,'faultCode',None) - if code is None: - continue - info = {} - info['faultCode'] = code - info['name'] = n - info['desc'] = getattr(v,'__doc__',None) - ret.append(info) - ret.sort(lambda a,b: cmp(a['faultCode'],b['faultCode'])) - return ret diff --git a/ipalib/errors2.py b/ipalib/errors2.py index 7e752d824..341421e56 100644 --- a/ipalib/errors2.py +++ b/ipalib/errors2.py @@ -699,6 +699,21 @@ class ValidationError(InvocationError): format = _('invalid %(name)r: %(error)s') +class NoSuchNamespaceError(InvocationError): + """ + **3010** Raised when an unknown namespace is requested. + + For example: + + >>> raise NoSuchNamespaceError(name='Plugins') + Traceback (most recent call last): + ... + NoSuchNamespaceError: api has no such namespace: Plugins + """ + + errno = 3010 + format = _('api has no such namespace: %(name)r') + ############################################################################## # 4000 - 4999: Execution errors @@ -822,6 +837,102 @@ class AlreadyPosixGroup(ExecutionError): errno = 4007 format = _('This is already a posix group') +class MalformedUserPrincipal(ExecutionError): + """ + **4008** Raised when a user principal is not of the form: user@REALM + + For example: + + >>> raise MalformedUserPrincipal(principal=jsmith@@EXAMPLE.COM) + Traceback (most recent call last): + ... + MalformedUserPrincipal: Principal is not of the form user@REALM: jsmith@@EXAMPLE.COM + + """ + + errno = 4008 + format = _('Principal is not of the form user@REALM: %(principal)r') + +class AlreadyActive(ExecutionError): + """ + **4009** Raised when an entry is made active that is already active + + For example: + + >>> raise AlreadyActive() + Traceback (most recent call last): + ... + AlreadyActive: This entry is already unlocked + + """ + + errno = 4009 + format = _('This entry is already unlocked') + +class AlreadyInactive(ExecutionError): + """ + **4010** Raised when an entry is made inactive that is already inactive + + For example: + + >>> raise AlreadyInactive() + Traceback (most recent call last): + ... + AlreadyInactive: This entry is already locked + + """ + + errno = 4010 + format = _('This entry is already locked') + +class HasNSAccountLock(ExecutionError): + """ + **4011** Raised when an entry has the nsAccountLock attribute set + + For example: + + >>> raise HasNSAccountLock() + Traceback (most recent call last): + ... + HasNSAccountLock: This entry has nsAccountLock set, it cannot be locked or unlocked + + """ + + errno = 4011 + format = _('This entry has nsAccountLock set, it cannot be locked or unlocked') + +class NotGroupMember(ExecutionError): + """ + **4012** Raised when a non-member is attempted to be removed from a group + + For example: + + >>> raise NotGroupMember() + Traceback (most recent call last): + ... + NotGroupMember: This entry is not a member of the group + + """ + + errno = 4012 + format = _('This entry is not a member of the group') + +class RecursiveGroup(ExecutionError): + """ + **4013** Raised when a group is added as a member of itself + + For example: + + >>> raise RecursiveGroup() + Traceback (most recent call last): + ... + RecursiveGroup: A group may not be a member of itself + + """ + + errno = 4013 + format = _('A group may not be a member of itself') + class BuiltinError(ExecutionError): """ **4100** Base class for builtin execution errors (*4100 - 4199*). @@ -854,6 +965,69 @@ class LDAPError(ExecutionError): errno = 4200 +class MidairCollision(ExecutionError): + """ + **4201** Raised when a change collides with another change + + For example: + + >>> raise MidairCollision() + Traceback (most recent call last): + ... + MidairCollision: change collided with another change + """ + + errno = 4201 + format = _('change collided with another change') + + +class EmptyModlist(ExecutionError): + """ + **4202** Raised when an LDAP update makes no changes + + For example: + + >>> raise EmptyModlist() + Traceback (most recent call last): + ... + EmptyModlist: no modifications to be performed + """ + + errno = 4202 + format = _('no modifications to be performed') + + +class DatabaseError(ExecutionError): + """ + **4203** Raised when an LDAP error is not otherwise handled + + For example: + + >>> raise DatabaseError(desc="Can't contact LDAP server", info="") + Traceback (most recent call last): + ... + DatabaseError: Can't contact LDAP server: + """ + + errno = 4203 + format = _('%(desc)r:%(info)r') + + +class LimitsExceeded(ExecutionError): + """ + **4204** Raised when search limits are exceeded. + + For example: + + >>> raise LimitsExceeded() + Traceback (most recent call last): + ... + LimitsExceeded: limits exceeded for this query + """ + + errno = 4204 + format = _('limits exceeded for this query') + ############################################################################## # 5000 - 5999: Generic errors diff --git a/ipalib/plugins/basegroup.py b/ipalib/plugins/basegroup.py index 7cba75a47..b93fdc8d9 100644 --- a/ipalib/plugins/basegroup.py +++ b/ipalib/plugins/basegroup.py @@ -21,7 +21,7 @@ Base plugin for groups. """ -from ipalib import api, crud, errors, errors2 +from ipalib import api, crud, errors2 from ipalib import Object, Command # Plugin base classes from ipalib import Str, Int, Flag, List # Parameter types from ldap.dn import escape_dn_chars diff --git a/ipalib/plugins/passwd.py b/ipalib/plugins/passwd.py index a599276f4..cc7ab5861 100644 --- a/ipalib/plugins/passwd.py +++ b/ipalib/plugins/passwd.py @@ -21,7 +21,7 @@ Frontend plugins for password changes. """ -from ipalib import api, errors, util +from ipalib import api, errors2, util from ipalib import Command # Plugin base classes from ipalib import Str, Password # Parameter types @@ -30,13 +30,13 @@ class passwd(Command): 'Edit existing password policy.' takes_args = ( - Password('password'), - Str('principal?', + Str('principal', cli_name='user', primary_key=True, autofill=True, - default_from=util.get_current_principal, + create_default=lambda **kw: util.get_current_principal(), ), + Password('password'), ) def execute(self, principal, password): @@ -48,13 +48,13 @@ class passwd(Command): Returns the entry - :param param uid: The login name of the user being updated. - :param kw: Not used. + :param principal: The login name or principal of the user + :param password: the new password """ if principal.find('@') > 0: u = principal.split('@') if len(u) > 2: - raise errors.InvalidUserPrincipal(principal) + raise errors2.MalformedUserPrincipal(principal=principal) else: principal = principal+"@"+self.api.env.realm dn = self.Backend.ldap.find_entry_dn( diff --git a/ipalib/util.py b/ipalib/util.py index 3e47cc3a1..54529b14c 100644 --- a/ipalib/util.py +++ b/ipalib/util.py @@ -22,24 +22,21 @@ Various utility functions. """ import os -from os import path import imp import optparse import logging import time -from types import NoneType -from xmlrpclib import Binary import krbV import socket +from ipalib import errors2 def get_current_principal(): try: - return krbV.default_context().default_ccache().principal().name + return unicode(krbV.default_context().default_ccache().principal().name) except krbV.Krb5Error: - #TODO: do a kinit - print "Unable to get kerberos principal" - return None + #TODO: do a kinit? + raise errors2.CCacheError() def get_fqdn(): fqdn = "" @@ -57,16 +54,16 @@ def find_modules_in_dir(src_dir): """ Iterate through module names found in ``src_dir``. """ - if not (path.abspath(src_dir) == src_dir and path.isdir(src_dir)): + if not (os.path.abspath(src_dir) == src_dir and os.path.isdir(src_dir)): return - if path.islink(src_dir): + if os.path.islink(src_dir): return suffix = '.py' for name in sorted(os.listdir(src_dir)): if not name.endswith(suffix): continue - pyfile = path.join(src_dir, name) - if path.islink(pyfile) or not path.isfile(pyfile): + pyfile = os.path.join(src_dir, name) + if os.path.islink(pyfile) or not os.path.isfile(pyfile): continue module = name[:-len(suffix)] if module == '__init__': @@ -92,7 +89,7 @@ def import_plugins_subpackage(name): plugins = __import__(name + '.plugins').plugins except ImportError: return - src_dir = path.dirname(path.abspath(plugins.__file__)) + src_dir = os.path.dirname(os.path.abspath(plugins.__file__)) for name in find_modules_in_dir(src_dir): full_name = '%s.%s' % (plugins.__name__, name) __import__(full_name) |