Statistics
| Branch: | Tag: | Revision:

root / lib / errors.py @ 58502c9e

History | View | Annotate | Download (11.4 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Ganeti exception handling.
23

24
"""
25

    
26
from ganeti import compat
27

    
28

    
29
# OpPrereqError failure types
30

    
31
#: Resolver errors
32
ECODE_RESOLVER = "resolver_error"
33

    
34
#: Not enough resources (iallocator failure, disk space, memory, etc.)
35
ECODE_NORES = "insufficient_resources"
36

    
37
#: Temporarily out of resources; operation can be tried again
38
ECODE_TEMP_NORES = "temp_insufficient_resources"
39

    
40
#: Wrong arguments (at syntax level)
41
ECODE_INVAL = "wrong_input"
42

    
43
#: Wrong entity state
44
ECODE_STATE = "wrong_state"
45

    
46
#: Entity not found
47
ECODE_NOENT = "unknown_entity"
48

    
49
#: Entity already exists
50
ECODE_EXISTS = "already_exists"
51

    
52
#: Resource not unique (e.g. MAC or IP duplication)
53
ECODE_NOTUNIQUE = "resource_not_unique"
54

    
55
#: Internal cluster error
56
ECODE_FAULT = "internal_error"
57

    
58
#: Environment error (e.g. node disk error)
59
ECODE_ENVIRON = "environment_error"
60

    
61
#: List of all failure types
62
ECODE_ALL = compat.UniqueFrozenset([
63
  ECODE_RESOLVER,
64
  ECODE_NORES,
65
  ECODE_TEMP_NORES,
66
  ECODE_INVAL,
67
  ECODE_STATE,
68
  ECODE_NOENT,
69
  ECODE_EXISTS,
70
  ECODE_NOTUNIQUE,
71
  ECODE_FAULT,
72
  ECODE_ENVIRON,
73
  ])
74

    
75

    
76
class GenericError(Exception):
77
  """Base exception for Ganeti.
78

79
  """
80

    
81

    
82
class LockError(GenericError):
83
  """Lock error exception.
84

85
  This signifies problems in the locking subsystem.
86

87
  """
88

    
89

    
90
class PidFileLockError(LockError):
91
  """PID file is already locked by another process.
92

93
  """
94

    
95

    
96
class HypervisorError(GenericError):
97
  """Hypervisor-related exception.
98

99
  This is raised in case we can't communicate with the hypervisor
100
  properly.
101

102
  """
103

    
104

    
105
class HotplugError(HypervisorError):
106
  """Hotplug-related exception.
107

108
  This is raised in case a hotplug action fails or is not supported.
109
  It is currently used only by KVM hypervisor.
110

111
  """
112

    
113

    
114
class ProgrammerError(GenericError):
115
  """Programming-related error.
116

117
  This is raised in cases we determine that the calling conventions
118
  have been violated, meaning we got some desynchronisation between
119
  parts of our code. It signifies a real programming bug.
120

121
  """
122

    
123

    
124
class BlockDeviceError(GenericError):
125
  """Block-device related exception.
126

127
  This is raised in case we can't setup the instance's block devices
128
  properly.
129

130
  """
131

    
132

    
133
class ConfigurationError(GenericError):
134
  """Configuration related exception.
135

136
  Things like having an instance with a primary node that doesn't
137
  exist in the config or such raise this exception.
138

139
  """
140

    
141

    
142
class ConfigVersionMismatch(ConfigurationError):
143
  """Version mismatch in the configuration file.
144

145
  The error has two arguments: the expected and the actual found
146
  version.
147

148
  """
149

    
150

    
151
class AddressPoolError(GenericError):
152
  """Errors related to IP address pools.
153

154
  """
155

    
156

    
157
class ReservationError(GenericError):
158
  """Errors reserving a resource.
159

160
  """
161

    
162

    
163
class RemoteError(GenericError):
164
  """Programming-related error on remote call.
165

166
  This is raised when an unhandled error occurs in a call to a
167
  remote node.  It usually signifies a real programming bug.
168

169
  """
170

    
171

    
172
class SignatureError(GenericError):
173
  """Error authenticating a remote message.
174

175
  This is raised when the hmac signature on a message doesn't verify correctly
176
  to the message itself. It can happen because of network unreliability or
177
  because of spurious traffic.
178

179
  """
180

    
181

    
182
class ParameterError(GenericError):
183
  """A passed parameter to a command is invalid.
184

185
  This is raised when the parameter passed to a request function is
186
  invalid. Correct code should have verified this before passing the
187
  request structure.
188

189
  The argument to this exception should be the parameter name.
190

191
  """
192

    
193

    
194
class ResultValidationError(GenericError):
195
  """The iallocation results fails validation.
196

197
  """
198

    
199

    
200
class OpPrereqError(GenericError):
201
  """Prerequisites for the OpCode are not fulfilled.
202

203
  This exception has two arguments: an error message, and one of the
204
  ECODE_* codes.
205

206
  """
207

    
208

    
209
class OpExecError(GenericError):
210
  """Error during OpCode execution.
211

212
  """
213

    
214

    
215
class OpResultError(GenericError):
216
  """Issue with OpCode result.
217

218
  """
219

    
220

    
221
class DeviceCreationError(GenericError):
222
  """Error during the creation of a device.
223

224
  This exception should contain the list of the devices actually created
225
  up to now, in the form of pairs (node, device)
226

227
  """
228
  def __init__(self, message, created_devices):
229
    GenericError.__init__(self)
230
    self.message = message
231
    self.created_devices = created_devices
232

    
233
  def __str__(self):
234
    return self.message
235

    
236

    
237
class OpCodeUnknown(GenericError):
238
  """Unknown opcode submitted.
239

240
  This signifies a mismatch between the definitions on the client and
241
  server side.
242

243
  """
244

    
245

    
246
class JobLost(GenericError):
247
  """Submitted job lost.
248

249
  The job was submitted but it cannot be found in the current job
250
  list.
251

252
  """
253

    
254

    
255
class JobFileCorrupted(GenericError):
256
  """Job file could not be properly decoded/restored.
257

258
  """
259

    
260

    
261
class ResolverError(GenericError):
262
  """Host name cannot be resolved.
263

264
  This is not a normal situation for Ganeti, as we rely on having a
265
  working resolver.
266

267
  The non-resolvable hostname is available as the first element of the
268
  args tuple; the other two elements of the tuple are the first two
269
  args of the socket.gaierror exception (error code and description).
270

271
  """
272

    
273

    
274
class HooksFailure(GenericError):
275
  """A generic hook failure.
276

277
  This signifies usually a setup misconfiguration.
278

279
  """
280

    
281

    
282
class HooksAbort(HooksFailure):
283
  """A required hook has failed.
284

285
  This caused an abort of the operation in the initial phase. This
286
  exception always has an attribute args which is a list of tuples of:
287
    - node: the source node on which this hooks has failed
288
    - script: the name of the script which aborted the run
289

290
  """
291

    
292

    
293
class UnitParseError(GenericError):
294
  """Unable to parse size unit.
295

296
  """
297

    
298

    
299
class ParseError(GenericError):
300
  """Generic parse error.
301

302
  Raised when unable to parse user input.
303

304
  """
305

    
306

    
307
class TypeEnforcementError(GenericError):
308
  """Unable to enforce data type.
309

310
  """
311

    
312

    
313
class X509CertError(GenericError):
314
  """Invalid X509 certificate.
315

316
  This error has two arguments: the certificate filename and the error cause.
317

318
  """
319

    
320

    
321
class TagError(GenericError):
322
  """Generic tag error.
323

324
  The argument to this exception will show the exact error.
325

326
  """
327

    
328

    
329
class CommandError(GenericError):
330
  """External command error.
331

332
  """
333

    
334

    
335
class StorageError(GenericError):
336
  """Storage-related exception.
337

338
  """
339

    
340

    
341
class InotifyError(GenericError):
342
  """Error raised when there is a failure setting up an inotify watcher.
343

344
  """
345

    
346

    
347
class QuitGanetiException(Exception):
348
  """Signal Ganeti that it must quit.
349

350
  This is not necessarily an error (and thus not a subclass of
351
  GenericError), but it's an exceptional circumstance and it is thus
352
  treated. This exception should be instantiated with two values. The
353
  first one will specify the return code to the caller, and the second
354
  one will be the returned result (either as an error or as a normal
355
  result). Usually only the leave cluster rpc call should return
356
  status True (as there it's expected we quit), every other call will
357
  return status False (as a critical error was encountered).
358

359
  Examples::
360

361
    # Return a result of "True" to the caller, but quit ganeti afterwards
362
    raise QuitGanetiException(True, None)
363
    # Send an error to the caller, and quit ganeti
364
    raise QuitGanetiException(False, "Fatal safety violation, shutting down")
365

366
  """
367

    
368

    
369
class JobQueueError(GenericError):
370
  """Job queue error.
371

372
  """
373

    
374

    
375
class JobQueueDrainError(JobQueueError):
376
  """Job queue is marked for drain error.
377

378
  This is raised when a job submission attempt is made but the queue
379
  is marked for drain.
380

381
  """
382

    
383

    
384
class JobQueueFull(JobQueueError):
385
  """Job queue full error.
386

387
  Raised when job queue size reached its hard limit.
388

389
  """
390

    
391

    
392
class ConfdMagicError(GenericError):
393
  """A magic fourcc error in Ganeti confd.
394

395
  Errors processing the fourcc in ganeti confd datagrams.
396

397
  """
398

    
399

    
400
class ConfdClientError(GenericError):
401
  """A magic fourcc error in Ganeti confd.
402

403
  Errors in the confd client library.
404

405
  """
406

    
407

    
408
class UdpDataSizeError(GenericError):
409
  """UDP payload too big.
410

411
  """
412

    
413

    
414
class NoCtypesError(GenericError):
415
  """python ctypes module is not found in the system.
416

417
  """
418

    
419

    
420
class IPAddressError(GenericError):
421
  """Generic IP address error.
422

423
  """
424

    
425

    
426
class LuxiError(GenericError):
427
  """LUXI error.
428

429
  """
430

    
431

    
432
class QueryFilterParseError(ParseError):
433
  """Error while parsing query filter.
434

435
  This exception must be instantiated with two values. The first one is a
436
  string with an error description, the second one is an instance of a subclass
437
  of C{pyparsing.ParseBaseException} (used to display the exact error
438
  location).
439

440
  """
441
  def GetDetails(self):
442
    """Returns a list of strings with details about the error.
443

444
    """
445
    try:
446
      (_, inner) = self.args
447
    except IndexError:
448
      return None
449

    
450
    return [str(inner.line),
451
            (" " * (inner.column - 1)) + "^",
452
            str(inner)]
453

    
454

    
455
class RapiTestResult(GenericError):
456
  """Exception containing results from RAPI test utilities.
457

458
  """
459

    
460

    
461
class FileStoragePathError(GenericError):
462
  """Error from file storage path validation.
463

464
  """
465

    
466

    
467
# errors should be added above
468

    
469

    
470
def GetErrorClass(name):
471
  """Return the class of an exception.
472

473
  Given the class name, return the class itself.
474

475
  @type name: str
476
  @param name: the exception name
477
  @rtype: class
478
  @return: the actual class, or None if not found
479

480
  """
481
  item = globals().get(name, None)
482
  if item is not None:
483
    if not (isinstance(item, type(Exception)) and
484
            issubclass(item, GenericError)):
485
      item = None
486
  return item
487

    
488

    
489
def EncodeException(err):
490
  """Encodes an exception into a format that L{MaybeRaise} will recognise.
491

492
  The passed L{err} argument will be formatted as a tuple (exception
493
  name, arguments) that the MaybeRaise function will recognise.
494

495
  @type err: GenericError child
496
  @param err: usually a child of GenericError (but any exception
497
      will be accepted)
498
  @rtype: tuple
499
  @return: tuple of (exception name, exception arguments)
500

501
  """
502
  return (err.__class__.__name__, err.args)
503

    
504

    
505
def GetEncodedError(result):
506
  """If this looks like an encoded Ganeti exception, return it.
507

508
  This function tries to parse the passed argument and if it looks
509
  like an encoding done by EncodeException, it will return the class
510
  object and arguments.
511

512
  """
513
  tlt = (tuple, list)
514
  if (isinstance(result, tlt) and len(result) == 2 and
515
      isinstance(result[1], tlt)):
516
    # custom ganeti errors
517
    errcls = GetErrorClass(result[0])
518
    if errcls:
519
      return (errcls, tuple(result[1]))
520

    
521
  return None
522

    
523

    
524
def MaybeRaise(result):
525
  """If this looks like an encoded Ganeti exception, raise it.
526

527
  This function tries to parse the passed argument and if it looks
528
  like an encoding done by EncodeException, it will re-raise it.
529

530
  """
531
  error = GetEncodedError(result)
532
  if error:
533
    (errcls, args) = error
534
    # pylint: disable=W0142
535
    raise errcls(*args)