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.
This commit is contained in:
Rob Crittenden
2009-04-20 13:58:26 -04:00
parent a9387b48e6
commit 64fa3dd4c3
15 changed files with 350 additions and 854 deletions

View File

@@ -26,7 +26,7 @@ try:
from ipapython import entity, ipautil, config
from ipaserver.install import installutils
from ipaserver.install.ldapupdate import LDAPUpdate, BadSyntax, UPDATES_DIR
from ipalib import errors, errors2
from ipalib import errors2
import ldap
import logging
import re

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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(

View File

@@ -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)

View File

@@ -29,7 +29,7 @@ from ipaserver.install import installutils
from ipaserver import ipaldap
from ipapython import entity, ipautil
from ipalib import util
from ipalib import errors, errors2
from ipalib import errors2
import ldap
import logging
import krbV
@@ -310,10 +310,10 @@ class LDAPUpdate:
while True:
try:
entry = self.conn.getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)", attrlist)
except errors2.NotFound:
except errors2.NotFound, e:
logging.error("Task not found: %s", dn)
return
except errors.DatabaseError, e:
except errors2.DatabaseError, e:
logging.error("Task lookup failure %s", e)
return
@@ -484,7 +484,7 @@ class LDAPUpdate:
# Doesn't exist, start with the default entry
entry = new_entry
logging.info("New entry: %s", entry.dn)
except errors.DatabaseError:
except errors2.DatabaseError:
# Doesn't exist, start with the default entry
entry = new_entry
logging.info("New entry, using default value: %s", entry.dn)
@@ -521,10 +521,10 @@ class LDAPUpdate:
if self.live_run and updated:
self.conn.updateEntry(entry.dn, entry.origDataDict(), entry.toDict())
logging.info("Done")
except errors.EmptyModlist:
except errors2.EmptyModlist:
logging.info("Entry already up-to-date")
updated = False
except errors.DatabaseError, e:
except errors2.DatabaseError, e:
logging.error("Update failed: %s", e)
updated = False

View File

@@ -32,7 +32,7 @@ import ldap.sasl
from ldap.controls import LDAPControl,DecodeControlTuples,EncodeControlTuples
from ldap.ldapobject import SimpleLDAPObject
from ipaserver import ipautil
from ipalib import errors, errors2
from ipalib import errors2
# Global variable to define SASL auth
sasl_auth = ldap.sasl.sasl({},'GSSAPI')
@@ -264,6 +264,50 @@ class IPAdmin(SimpleLDAPObject):
return sctrl
def __handle_errors(self, e, **kw):
"""
Centralize error handling in one place.
e is the error to be raised
**kw is an exception-specific list of options
"""
if not isinstance(e,ldap.TIMEOUT):
desc = e.args[0]['desc'].strip()
info = e.args[0].get('info','').strip()
else:
desc = ''
info = ''
try:
# re-raise the error so we can handle it
raise e
except ldap.NO_SUCH_OBJECT, e:
args = kw.get('args', '')
raise errors2.NotFound(msg=notfound(args))
except ldap.ALREADY_EXISTS, e:
raise errors2.DuplicateEntry()
except ldap.CONSTRAINT_VIOLATION, e:
# This error gets thrown by the uniqueness plugin
if info == 'Another entry with the same attribute value already exists':
raise errors2.DuplicateEntry()
else:
raise errors2.DatabaseError(desc=desc,info=info)
except ldap.INSUFFICIENT_ACCESS, e:
raise errors2.ACIError(info=info)
except ldap.NO_SUCH_ATTRIBUTE:
# this is raised when a 'delete' attribute isn't found.
# it indicates the previous attribute was removed by another
# update, making the oldentry stale.
raise errors2.MidairCollision()
except ldap.ADMINLIMIT_EXCEEDED, e:
raise errors2.LimitsExceeded()
except ldap.SIZELIMIT_EXCEEDED, e:
raise errors2.LimitsExceeded()
except ldap.TIMELIMIT_EXCEEDED, e:
raise errors2.LimitsExceeded()
except ldap.LDAPError, e:
raise errors2.DatabaseError(desc=desc,info=info)
def toLDAPURL(self):
return "ldap://%s:%d/" % (self.host,self.port)
@@ -271,11 +315,14 @@ class IPAdmin(SimpleLDAPObject):
self.proxydn = proxydn
def set_krbccache(self, krbccache, principal):
if krbccache is not None:
os.environ["KRB5CCNAME"] = krbccache
self.sasl_interactive_bind_s("", sasl_auth)
self.principal = principal
self.proxydn = None
try:
if krbccache is not None:
os.environ["KRB5CCNAME"] = krbccache
self.sasl_interactive_bind_s("", sasl_auth)
self.principal = principal
self.proxydn = None
except ldap.LDAPError, e:
self.__handle_errors(e, **{})
def do_simple_bind(self, binddn="cn=directory manager", bindpw=""):
self.binddn = binddn
@@ -293,10 +340,9 @@ class IPAdmin(SimpleLDAPObject):
try:
res = self.search(*args)
objtype, obj = self.result(res)
except ldap.NO_SUCH_OBJECT, e:
raise errors2.NotFound(msg=notfound(args))
except ldap.LDAPError, e:
raise errors.DatabaseError, e
kw = {'args': args}
self.__handle_errors(e, **kw)
if not obj:
raise errors2.NotFound(msg=notfound(args))
@@ -316,11 +362,9 @@ class IPAdmin(SimpleLDAPObject):
try:
res = self.search(*args)
objtype, obj = self.result(res)
except (ldap.ADMINLIMIT_EXCEEDED, ldap.SIZELIMIT_EXCEEDED), e:
# Too many results returned by search
raise e
except ldap.LDAPError, e:
raise errors.DatabaseError, e
kw = {'args': args}
self.__handle_errors(e, **kw)
if not obj:
raise errors2.NotFound(msg=notfound(args))
@@ -357,7 +401,8 @@ class IPAdmin(SimpleLDAPObject):
ldap.TIMELIMIT_EXCEEDED), e:
partial = 1
except ldap.LDAPError, e:
raise errors.DatabaseError, e
kw = {'args': args}
self.__handle_errors(e, **kw)
if not entries:
raise errors2.NotFound(msg=notfound(args))
@@ -379,18 +424,9 @@ class IPAdmin(SimpleLDAPObject):
if sctrl is not None:
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
self.add_s(*args)
except ldap.ALREADY_EXISTS, e:
raise errors2.DuplicateEntry
except ldap.CONSTRAINT_VIOLATION, e:
# This error gets thrown by the uniqueness plugin
if e.args[0].get('info','') == 'Another entry with the same attribute value already exists':
raise errors2.DuplicateEntry
else:
raise errors.DatabaseError, e
except ldap.INSUFFICIENT_ACCESS, e:
raise errors2.ACIError(info=e.args[0].get('info',''))
except ldap.LDAPError, e:
raise errors.DatabaseError, e
kw = {'args': args}
self.__handle_errors(e, **kw)
return True
def updateRDN(self, dn, newrdn):
@@ -407,7 +443,8 @@ class IPAdmin(SimpleLDAPObject):
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
self.modrdn_s(dn, newrdn, delold=1)
except ldap.LDAPError, e:
raise errors.DatabaseError, e
kw = {'args': args}
self.__handle_errors(e, **kw)
return True
def updateEntry(self,dn,oldentry,newentry):
@@ -425,15 +462,9 @@ class IPAdmin(SimpleLDAPObject):
if sctrl is not None:
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
self.modify_s(dn, modlist)
# this is raised when a 'delete' attribute isn't found.
# it indicates the previous attribute was removed by another
# update, making the oldentry stale.
except ldap.NO_SUCH_ATTRIBUTE:
raise errors.MidairCollision
except ldap.INSUFFICIENT_ACCESS, e:
raise errors2.ACIError(info=e.args[0].get('info',''))
except ldap.LDAPError, e:
raise errors.DatabaseError, e
kw = {'args': args}
self.__handle_errors(e, **kw)
return True
def generateModList(self, old_entry, new_entry):
@@ -491,7 +522,8 @@ class IPAdmin(SimpleLDAPObject):
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
self.modify_s(dn, modlist)
except ldap.LDAPError, e:
raise errors.DatabaseError, e
kw = {'args': args}
self.__handle_errors(e, **kw)
return True
def deleteEntry(self,*args):
@@ -503,10 +535,9 @@ class IPAdmin(SimpleLDAPObject):
if sctrl is not None:
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
self.delete_s(*args)
except ldap.INSUFFICIENT_ACCESS, e:
raise errors2.ACIError(info=e.args[0].get('info',''))
except ldap.LDAPError, e:
raise errors.DatabaseError, e
kw = {'args': args}
self.__handle_errors(e, **kw)
return True
def modifyPassword(self,dn,oldpass,newpass):
@@ -524,7 +555,8 @@ class IPAdmin(SimpleLDAPObject):
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
self.passwd_s(dn, oldpass, newpass)
except ldap.LDAPError, e:
raise errors.DatabaseError, e
kw = {'args': args}
self.__handle_errors(e, **kw)
return True
def __wrapmethods(self):

View File

@@ -44,7 +44,7 @@ from ldap.controls import LDAPControl
from ldap.ldapobject import SimpleLDAPObject
from ipalib import api
from ipalib import errors, errors2
from ipalib import errors2
from ipalib.crud import CrudBackend
# attribute syntax to python type mapping, 'SYNTAX OID': type
@@ -87,7 +87,7 @@ def _load_schema(host, port):
conn.unbind_s()
except _ldap.LDAPError, e:
# TODO: raise a more appropriate exception
raise errors.DatabaseError
self.__handle_errors(e, **{})
except IndexError:
# no 'cn=schema' entry in LDAP? some servers use 'cn=subschema'
# TODO: DS uses 'cn=schema', support for other server?
@@ -168,6 +168,51 @@ class ldap2(CrudBackend):
else:
entry_attrs[k] = attr_type(v)
def __handle_errors(self, e, **kw):
"""
Centralize error handling in one place.
e is the error to be raised
**kw is an exception-specific list of options
"""
if not isinstance(e,ldap.TIMEOUT):
desc = e.args[0]['desc'].strip()
info = e.args[0].get('info','').strip()
else:
desc = ''
info = ''
try:
# re-raise the error so we can handle it
raise e
except _ldap.NO_SUCH_OBJECT, e:
# args = kw.get('args', '')
# raise errors2.NotFound(msg=notfound(args))
raise errors2.NotFound()
except _ldap.ALREADY_EXISTS, e:
raise errors2.DuplicateEntry()
except _ldap.CONSTRAINT_VIOLATION, e:
# This error gets thrown by the uniqueness plugin
if info == 'Another entry with the same attribute value already exists':
raise errors2.DuplicateEntry()
else:
raise errors2.DatabaseError(desc=desc,info=info)
except _ldap.INSUFFICIENT_ACCESS, e:
raise errors2.ACIError(info=info)
except _ldap.NO_SUCH_ATTRIBUTE:
# this is raised when a 'delete' attribute isn't found.
# it indicates the previous attribute was removed by another
# update, making the oldentry stale.
raise errors2.MidairCollision()
except _ldap.ADMINLIMIT_EXCEEDED, e:
raise errors2.LimitsExceeded()
except _ldap.SIZELIMIT_EXCEEDED, e:
raise errors2.LimitsExceeded()
except _ldap.TIMELIMIT_EXCEEDED, e:
raise errors2.LimitsExceeded()
except _ldap.LDAPError, e:
raise errors2.DatabaseError(desc=desc,info=info)
def create_connection(self, host=None, port=None, ccache=None,
bind_dn='', bind_pw='', debug_level=255,
tls_cacertfile=None, tls_certfile=None, tls_keyfile=None):
@@ -291,15 +336,8 @@ class ldap2(CrudBackend):
# pass arguments to python-ldap
try:
self.conn.add_s(dn, list(entry_attrs_copy.iteritems()))
except _ldap.ALREADY_EXISTS, e:
raise errors2.DuplicateEntry
except _ldap.CONSTRAINT_VIOLATION, e:
if e.args[0].get('info', '') == _uniqueness_plugin_error:
raise errors2.DuplicateEntry
else:
raise errors.DatabaseError, e
except _ldap.LDAPError, e:
raise errors.DatabaseError, e
self.__handle_errors(e, **{})
# generating filters for find_entry
# some examples:
@@ -403,7 +441,7 @@ class ldap2(CrudBackend):
_ldap.SIZELIMIT_EXCEEDED), e:
raise e
except _ldap.LDAPError, e:
raise errors.DatabaseError, e
self.__handle_errors(e, **{})
if not res:
raise errors2.NotFound()
@@ -450,7 +488,7 @@ class ldap2(CrudBackend):
try:
self.conn.rename_s(dn, new_rdn, delold=int(del_old))
except _ldap.LDAPError, e:
raise errors.DatabaseError, e
self.__handle_errors(e, **{})
def _generate_modlist(self, dn, entry_attrs):
# get original entry
@@ -500,15 +538,13 @@ class ldap2(CrudBackend):
# generate modlist
modlist = self._generate_modlist(dn, entry_attrs_copy)
if not modlist:
raise errors.EmptyModlist
raise errors2.EmptyModlist()
# pass arguments to python-ldap
try:
self.conn.modify_s(dn, modlist)
except _ldap.NO_SUCH_ATTRIBUTE:
raise errors.MidairCollision
except _ldap.LDAPError, e:
raise errors.DatabaseError, e
self.__handle_errors(e, **{})
def delete_entry(self, dn):
"""Delete entry."""
@@ -519,10 +555,8 @@ class ldap2(CrudBackend):
# pass arguments to python-ldap
try:
self.conn.delete_s(dn)
except _ldap.INSUFFICIENT_ACCESS, e:
raise errors.InsuficientAccess, e
except _ldap.LDAPError, e:
raise errors.DatabaseError, e
self.__handle_errors(e, **{})
def modify_password(self, dn, old_pass, new_pass):
"""Set user password."""
@@ -536,7 +570,7 @@ class ldap2(CrudBackend):
try:
self.passwd_s(dn, odl_pass, new_pass)
except _ldap.LDAPError, e:
raise errors.DatabaseError, e
self.__handle_errors(e, **{})
def add_entry_to_group(self, dn, group_dn, member_attr='member'):
"""Add entry to group."""
@@ -545,7 +579,7 @@ class ldap2(CrudBackend):
group_dn = self.normalize_dn(group_dn)
# check if we're not trying to add group into itself
if dn == group_dn:
raise errors.SameGroupError
raise errors2.SameGroupError()
# check if the entry exists
(dn, entry_attrs) = self.get_entry(dn, ['objectClass'])
@@ -575,7 +609,7 @@ class ldap2(CrudBackend):
try:
members.remove(dn)
except ValueError:
raise errors.NotGroupMember
raise errors2.NotGroupMember()
group_entry_attrs[member_attr] = members
# update group entry
@@ -592,11 +626,11 @@ class ldap2(CrudBackend):
account_lock_attr = account_lock_attr[0].lower()
if active:
if account_lock_attr == 'false':
raise errors.AlreadyActiveError
raise errors2.AlreadyActive()
else:
if account_lock_attr == 'true':
raise errors.AlreadyInactiveError
raise errors2.AlreadyInactive()
# check if nsAccountLock attribute is in the entry itself
is_member = False
member_of_attr = entry_attrs.get('memberOf', [])
@@ -605,7 +639,7 @@ class ldap2(CrudBackend):
is_member = True
break
if not is_member and entry_attrs.has_key('nsAccountLock'):
raise errors.HasNSAccountLock
raise errors2.HasNSAccountLock()
activated_filter = '(cn=activated)'
inactivated_filter = '(cn=inactivated)'
@@ -619,7 +653,7 @@ class ldap2(CrudBackend):
(group_dn, group_entry_attrs) = entries[0]
try:
self.remove_entry_from_group(dn, group_dn)
except errors.NotGroupMember:
except errors2.NotGroupMember:
pass
# add the entry to the activated/inactivated group if necessary
@@ -638,11 +672,11 @@ class ldap2(CrudBackend):
(group_dn, group_entry_attrs) = entries[0]
try:
self.add_entry_to_group(dn, group_dn)
except errors.EmptyModlist:
except errors2.EmptyModlist:
if active:
raise errors.AlreadyActiveError
raise errors2.AlreadyActive()
else:
raise errors.AlreadyInactiveError
raise errors2.AlreadyInactive()
def activate_entry(self, dn):
"""Mark entry active."""

View File

@@ -23,7 +23,7 @@ import re
from ipalib.request import context
from ipaserver import ipaldap
import ipautil
from ipalib import errors, errors2
from ipalib import errors2
from ipalib import api
def convert_entry(ent):
@@ -341,16 +341,16 @@ def mark_entry_active (dn):
if entry.get('nsaccountlock', 'false').lower() == "false":
api.log.debug("IPA: already active")
raise errors.AlreadyActiveError
raise errors2.AlreadyActive()
if has_nsaccountlock(dn):
api.log.debug("IPA: appears to have the nsaccountlock attribute")
raise errors.HasNSAccountLock
raise errors2.HasNSAccountLock()
group = get_entry_by_cn("inactivated", None)
try:
remove_member_from_group(entry.get('dn'), group.get('dn'))
except errors.NotGroupMember:
except errors2.NotGroupMember:
# Perhaps the user is there as a result of group membership
pass
@@ -377,18 +377,18 @@ def mark_entry_inactive (dn):
if entry.get('nsaccountlock', 'false').lower() == "true":
api.log.debug("IPA: already marked as inactive")
raise errors.AlreadyInactiveError
raise errors2.AlreadyInactive()
if has_nsaccountlock(dn):
api.log.debug("IPA: appears to have the nsaccountlock attribute")
raise errors.HasNSAccountLock
raise errors2.HasNSAccountLock()
# First see if they are in the activated group as this will override
# the our inactivation.
group = get_entry_by_cn("activated", None)
try:
remove_member_from_group(dn, group.get('dn'))
except errors.NotGroupMember:
except errors2.NotGroupMember:
# this is fine, they may not be explicitly in this group
pass
@@ -405,7 +405,7 @@ def add_member_to_group(member_dn, group_dn, memberattr='member'):
api.log.info("IPA: add_member_to_group '%s' to '%s'" % (member_dn, group_dn))
if member_dn.lower() == group_dn.lower():
# You can't add a group to itself
raise errors.SameGroupError
raise errors2.RecursiveGroup()
group = get_entry_by_dn(group_dn, None)
if group is None:
@@ -423,10 +423,7 @@ def add_member_to_group(member_dn, group_dn, memberattr='member'):
members.append(member_dn)
group[memberattr] = members
try:
return update_entry(group)
except errors.EmptyModlist:
raise
return update_entry(group)
def remove_member_from_group(member_dn, group_dn, memberattr='member'):
"""Remove a member_dn from an existing group."""
@@ -444,7 +441,7 @@ def remove_member_from_group(member_dn, group_dn, memberattr='member'):
members = group.get(memberattr, False)
if not members:
raise errors.NotGroupMember
raise errors2.NotGroupMember()
if isinstance(members,basestring):
members = [members]
@@ -453,15 +450,10 @@ def remove_member_from_group(member_dn, group_dn, memberattr='member'):
try:
members.remove(member_dn)
except ValueError:
# member is not in the group
# FIXME: raise more specific error?
raise errors.NotGroupMember
raise errors2.NotGroupMember()
except Exception, e:
raise e
group[memberattr] = members
try:
return update_entry(group)
except errors.EmptyModlist:
raise
return update_entry(group)

View File

@@ -1,289 +0,0 @@
# Authors:
# Jason Gerard DeRose <jderose@redhat.com>
#
# Copyright (C) 2008 Red Hat
# see file 'COPYING' for use and warranty information
#
# 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
"""
Test the `ipalib.errors` module.
"""
from tests.util import raises, ClassChecker
from ipalib import errors
type_format = '%s: need a %r; got %r'
def check_TypeError(f, value, type_, name, **kw):
e = raises(TypeError, f, value, type_, name, **kw)
assert e.value is value
assert e.type is type_
assert e.name is name
assert str(e) == type_format % (name, type_, value)
def test_raise_TypeError():
"""
Test the `ipalib.errors.raise_TypeError` function.
"""
f = errors.raise_TypeError
value = 'Hello.'
type_ = unicode
name = 'message'
check_TypeError(f, value, type_, name)
# name not an str
fail_name = 42
e = raises(AssertionError, f, value, type_, fail_name)
assert str(e) == type_format % ('name', str, fail_name), str(e)
# type_ not a type:
fail_type = unicode()
e = raises(AssertionError, f, value, fail_type, name)
assert str(e) == type_format % ('type_', type, fail_type)
# type(value) is type_:
fail_value = u'How are you?'
e = raises(AssertionError, f, fail_value, type_, name)
assert str(e) == 'value: %r is a %r' % (fail_value, type_)
def test_check_type():
"""
Test the `ipalib.errors.check_type` function.
"""
f = errors.check_type
value = 'How are you?'
type_ = str
name = 'greeting'
# Should pass:
assert value is f(value, type_, name)
assert None is f(None, type_, name, allow_none=True)
# Should raise TypeError
check_TypeError(f, None, type_, name)
check_TypeError(f, value, basestring, name)
check_TypeError(f, value, unicode, name)
# name not an str
fail_name = unicode(name)
e = raises(AssertionError, f, value, type_, fail_name)
assert str(e) == type_format % ('name', str, fail_name)
# type_ not a type:
fail_type = 42
e = raises(AssertionError, f, value, fail_type, name)
assert str(e) == type_format % ('type_', type, fail_type)
# allow_none not a bool:
fail_bool = 0
e = raises(AssertionError, f, value, type_, name, allow_none=fail_bool)
assert str(e) == type_format % ('allow_none', bool, fail_bool)
def test_check_isinstance():
"""
Test the `ipalib.errors.check_isinstance` function.
"""
f = errors.check_isinstance
value = 'How are you?'
type_ = str
name = 'greeting'
# Should pass:
assert value is f(value, type_, name)
assert value is f(value, basestring, name)
assert None is f(None, type_, name, allow_none=True)
# Should raise TypeError
check_TypeError(f, None, type_, name)
check_TypeError(f, value, unicode, name)
# name not an str
fail_name = unicode(name)
e = raises(AssertionError, f, value, type_, fail_name)
assert str(e) == type_format % ('name', str, fail_name)
# type_ not a type:
fail_type = 42
e = raises(AssertionError, f, value, fail_type, name)
assert str(e) == type_format % ('type_', type, fail_type)
# allow_none not a bool:
fail_bool = 0
e = raises(AssertionError, f, value, type_, name, allow_none=fail_bool)
assert str(e) == type_format % ('allow_none', bool, fail_bool)
class test_IPAError(ClassChecker):
"""
Test the `ipalib.errors.IPAError` exception.
"""
_cls = errors.IPAError
def test_class(self):
"""
Test the `ipalib.errors.IPAError` exception.
"""
assert self.cls.__bases__ == (StandardError,)
def test_init(self):
"""
Test the `ipalib.errors.IPAError.__init__` method.
"""
args = ('one fish', 'two fish')
e = self.cls(*args)
assert e.args == args
assert self.cls().args == tuple()
def test_str(self):
"""
Test the `ipalib.errors.IPAError.__str__` method.
"""
f = 'The %s color is %s.'
class custom_error(self.cls):
format = f
for args in [('sexiest', 'red'), ('most-batman-like', 'black')]:
e = custom_error(*args)
assert e.args == args
assert str(e) == f % args
class test_ValidationError(ClassChecker):
"""
Test the `ipalib.errors.ValidationError` exception.
"""
_cls = errors.ValidationError
def test_class(self):
"""
Test the `ipalib.errors.ValidationError` exception.
"""
assert self.cls.__bases__ == (errors.IPAError,)
def test_init(self):
"""
Test the `ipalib.errors.ValidationError.__init__` method.
"""
name = 'login'
value = 'Whatever'
error = 'Must be lowercase.'
for index in (None, 3):
e = self.cls(name, value, error, index=index)
assert e.name is name
assert e.value is value
assert e.error is error
assert e.index is index
assert str(e) == 'invalid %r value %r: %s' % (name, value, error)
# Check that index default is None:
assert self.cls(name, value, error).index is None
# Check non str name raises AssertionError:
raises(AssertionError, self.cls, unicode(name), value, error)
# Check non int index raises AssertionError:
raises(AssertionError, self.cls, name, value, error, index=5.0)
# Check negative index raises AssertionError:
raises(AssertionError, self.cls, name, value, error, index=-2)
class test_ConversionError(ClassChecker):
"""
Test the `ipalib.errors.ConversionError` exception.
"""
_cls = errors.ConversionError
def test_class(self):
"""
Test the `ipalib.errors.ConversionError` exception.
"""
assert self.cls.__bases__ == (errors.ValidationError,)
def test_init(self):
"""
Test the `ipalib.errors.ConversionError.__init__` method.
"""
name = 'some_arg'
value = '42.0'
class type_(object):
conversion_error = 'Not an integer'
for index in (None, 7):
e = self.cls(name, value, type_, index=index)
assert e.name is name
assert e.value is value
assert e.type is type_
assert e.error is type_.conversion_error
assert e.index is index
assert str(e) == 'invalid %r value %r: %s' % (name, value,
type_.conversion_error)
# Check that index default is None:
assert self.cls(name, value, type_).index is None
class test_RuleError(ClassChecker):
"""
Test the `ipalib.errors.RuleError` exception.
"""
_cls = errors.RuleError
def test_class(self):
"""
Test the `ipalib.errors.RuleError` exception.
"""
assert self.cls.__bases__ == (errors.ValidationError,)
def test_init(self):
"""
Test the `ipalib.errors.RuleError.__init__` method.
"""
name = 'whatever'
value = 'The smallest weird number.'
def my_rule(value):
return 'Value is bad.'
error = my_rule(value)
for index in (None, 42):
e = self.cls(name, value, error, my_rule, index=index)
assert e.name is name
assert e.value is value
assert e.error is error
assert e.rule is my_rule
# Check that index default is None:
assert self.cls(name, value, error, my_rule).index is None
class test_RequirementError(ClassChecker):
"""
Test the `ipalib.errors.RequirementError` exception.
"""
_cls = errors.RequirementError
def test_class(self):
"""
Test the `ipalib.errors.RequirementError` exception.
"""
assert self.cls.__bases__ == (errors.ValidationError,)
def test_init(self):
"""
Test the `ipalib.errors.RequirementError.__init__` method.
"""
name = 'givenname'
e = self.cls(name)
assert e.name is name
assert e.value is None
assert e.error == 'Required'
assert e.index is None

View File

@@ -26,7 +26,7 @@ from tests.util import check_TypeError, ClassChecker, create_test_api
from tests.util import assert_equal
from ipalib.constants import TYPE_ERROR
from ipalib.base import NameSpace
from ipalib import frontend, backend, plugable, errors2, errors, parameters, config
from ipalib import frontend, backend, plugable, errors2, parameters, config
def test_RULE_FLAG():
assert frontend.RULE_FLAG == 'validation_rule'

View File

@@ -25,7 +25,7 @@ import inspect
from tests.util import raises, no_set, no_del, read_only
from tests.util import getitem, setitem, delitem
from tests.util import ClassChecker, create_test_api
from ipalib import plugable, errors, errors2
from ipalib import plugable, errors2
class test_SetProxy(ClassChecker):

View File

@@ -26,7 +26,6 @@ import socket
import nose
from ipalib import api, request
from ipalib import errors2
from ipalib import errors
# Initialize the API. We do this here so that one can run the tests
# individually instead of at the top-level. If API.bootstrap()