1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
|
# 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 InsufficientAccess(GenericError):
"""You do not have permission to perform this task"""
faultCode = 1027
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
|