Statistics
| Branch: | Tag: | Revision:

root / lib / errors.py @ b54ecf12

History | View | Annotate | Download (11.2 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 ProgrammerError(GenericError):
106
  """Programming-related error.
107

108
  This is raised in cases we determine that the calling conventions
109
  have been violated, meaning we got some desynchronisation between
110
  parts of our code. It signifies a real programming bug.
111

112
  """
113

    
114

    
115
class BlockDeviceError(GenericError):
116
  """Block-device related exception.
117

118
  This is raised in case we can't setup the instance's block devices
119
  properly.
120

121
  """
122

    
123

    
124
class ConfigurationError(GenericError):
125
  """Configuration related exception.
126

127
  Things like having an instance with a primary node that doesn't
128
  exist in the config or such raise this exception.
129

130
  """
131

    
132

    
133
class ConfigVersionMismatch(ConfigurationError):
134
  """Version mismatch in the configuration file.
135

136
  The error has two arguments: the expected and the actual found
137
  version.
138

139
  """
140

    
141

    
142
class AddressPoolError(GenericError):
143
  """Errors related to IP address pools.
144

145
  """
146

    
147

    
148
class ReservationError(GenericError):
149
  """Errors reserving a resource.
150

151
  """
152

    
153

    
154
class RemoteError(GenericError):
155
  """Programming-related error on remote call.
156

157
  This is raised when an unhandled error occurs in a call to a
158
  remote node.  It usually signifies a real programming bug.
159

160
  """
161

    
162

    
163
class SignatureError(GenericError):
164
  """Error authenticating a remote message.
165

166
  This is raised when the hmac signature on a message doesn't verify correctly
167
  to the message itself. It can happen because of network unreliability or
168
  because of spurious traffic.
169

170
  """
171

    
172

    
173
class ParameterError(GenericError):
174
  """A passed parameter to a command is invalid.
175

176
  This is raised when the parameter passed to a request function is
177
  invalid. Correct code should have verified this before passing the
178
  request structure.
179

180
  The argument to this exception should be the parameter name.
181

182
  """
183

    
184

    
185
class ResultValidationError(GenericError):
186
  """The iallocation results fails validation.
187

188
  """
189

    
190

    
191
class OpPrereqError(GenericError):
192
  """Prerequisites for the OpCode are not fulfilled.
193

194
  This exception has two arguments: an error message, and one of the
195
  ECODE_* codes.
196

197
  """
198

    
199

    
200
class OpExecError(GenericError):
201
  """Error during OpCode execution.
202

203
  """
204

    
205

    
206
class OpResultError(GenericError):
207
  """Issue with OpCode result.
208

209
  """
210

    
211

    
212
class DeviceCreationError(GenericError):
213
  """Error during the creation of a device.
214

215
  This exception should contain the list of the devices actually created
216
  up to now, in the form of pairs (node, device)
217

218
  """
219
  def __init__(self, message, created_devices):
220
    GenericError.__init__(self)
221
    self.message = message
222
    self.created_devices = created_devices
223

    
224
  def __str__(self):
225
    return self.message
226

    
227

    
228
class OpCodeUnknown(GenericError):
229
  """Unknown opcode submitted.
230

231
  This signifies a mismatch between the definitions on the client and
232
  server side.
233

234
  """
235

    
236

    
237
class JobLost(GenericError):
238
  """Submitted job lost.
239

240
  The job was submitted but it cannot be found in the current job
241
  list.
242

243
  """
244

    
245

    
246
class JobFileCorrupted(GenericError):
247
  """Job file could not be properly decoded/restored.
248

249
  """
250

    
251

    
252
class ResolverError(GenericError):
253
  """Host name cannot be resolved.
254

255
  This is not a normal situation for Ganeti, as we rely on having a
256
  working resolver.
257

258
  The non-resolvable hostname is available as the first element of the
259
  args tuple; the other two elements of the tuple are the first two
260
  args of the socket.gaierror exception (error code and description).
261

262
  """
263

    
264

    
265
class HooksFailure(GenericError):
266
  """A generic hook failure.
267

268
  This signifies usually a setup misconfiguration.
269

270
  """
271

    
272

    
273
class HooksAbort(HooksFailure):
274
  """A required hook has failed.
275

276
  This caused an abort of the operation in the initial phase. This
277
  exception always has an attribute args which is a list of tuples of:
278
    - node: the source node on which this hooks has failed
279
    - script: the name of the script which aborted the run
280

281
  """
282

    
283

    
284
class UnitParseError(GenericError):
285
  """Unable to parse size unit.
286

287
  """
288

    
289

    
290
class ParseError(GenericError):
291
  """Generic parse error.
292

293
  Raised when unable to parse user input.
294

295
  """
296

    
297

    
298
class TypeEnforcementError(GenericError):
299
  """Unable to enforce data type.
300

301
  """
302

    
303

    
304
class X509CertError(GenericError):
305
  """Invalid X509 certificate.
306

307
  This error has two arguments: the certificate filename and the error cause.
308

309
  """
310

    
311

    
312
class TagError(GenericError):
313
  """Generic tag error.
314

315
  The argument to this exception will show the exact error.
316

317
  """
318

    
319

    
320
class CommandError(GenericError):
321
  """External command error.
322

323
  """
324

    
325

    
326
class StorageError(GenericError):
327
  """Storage-related exception.
328

329
  """
330

    
331

    
332
class InotifyError(GenericError):
333
  """Error raised when there is a failure setting up an inotify watcher.
334

335
  """
336

    
337

    
338
class QuitGanetiException(Exception):
339
  """Signal Ganeti that it must quit.
340

341
  This is not necessarily an error (and thus not a subclass of
342
  GenericError), but it's an exceptional circumstance and it is thus
343
  treated. This exception should be instantiated with two values. The
344
  first one will specify the return code to the caller, and the second
345
  one will be the returned result (either as an error or as a normal
346
  result). Usually only the leave cluster rpc call should return
347
  status True (as there it's expected we quit), every other call will
348
  return status False (as a critical error was encountered).
349

350
  Examples::
351

352
    # Return a result of "True" to the caller, but quit ganeti afterwards
353
    raise QuitGanetiException(True, None)
354
    # Send an error to the caller, and quit ganeti
355
    raise QuitGanetiException(False, "Fatal safety violation, shutting down")
356

357
  """
358

    
359

    
360
class JobQueueError(GenericError):
361
  """Job queue error.
362

363
  """
364

    
365

    
366
class JobQueueDrainError(JobQueueError):
367
  """Job queue is marked for drain error.
368

369
  This is raised when a job submission attempt is made but the queue
370
  is marked for drain.
371

372
  """
373

    
374

    
375
class JobQueueFull(JobQueueError):
376
  """Job queue full error.
377

378
  Raised when job queue size reached its hard limit.
379

380
  """
381

    
382

    
383
class ConfdMagicError(GenericError):
384
  """A magic fourcc error in Ganeti confd.
385

386
  Errors processing the fourcc in ganeti confd datagrams.
387

388
  """
389

    
390

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

394
  Errors in the confd client library.
395

396
  """
397

    
398

    
399
class UdpDataSizeError(GenericError):
400
  """UDP payload too big.
401

402
  """
403

    
404

    
405
class NoCtypesError(GenericError):
406
  """python ctypes module is not found in the system.
407

408
  """
409

    
410

    
411
class IPAddressError(GenericError):
412
  """Generic IP address error.
413

414
  """
415

    
416

    
417
class LuxiError(GenericError):
418
  """LUXI error.
419

420
  """
421

    
422

    
423
class QueryFilterParseError(ParseError):
424
  """Error while parsing query filter.
425

426
  This exception must be instantiated with two values. The first one is a
427
  string with an error description, the second one is an instance of a subclass
428
  of C{pyparsing.ParseBaseException} (used to display the exact error
429
  location).
430

431
  """
432
  def GetDetails(self):
433
    """Returns a list of strings with details about the error.
434

435
    """
436
    try:
437
      (_, inner) = self.args
438
    except IndexError:
439
      return None
440

    
441
    return [str(inner.line),
442
            (" " * (inner.column - 1)) + "^",
443
            str(inner)]
444

    
445

    
446
class RapiTestResult(GenericError):
447
  """Exception containing results from RAPI test utilities.
448

449
  """
450

    
451

    
452
class FileStoragePathError(GenericError):
453
  """Error from file storage path validation.
454

455
  """
456

    
457

    
458
# errors should be added above
459

    
460

    
461
def GetErrorClass(name):
462
  """Return the class of an exception.
463

464
  Given the class name, return the class itself.
465

466
  @type name: str
467
  @param name: the exception name
468
  @rtype: class
469
  @return: the actual class, or None if not found
470

471
  """
472
  item = globals().get(name, None)
473
  if item is not None:
474
    if not (isinstance(item, type(Exception)) and
475
            issubclass(item, GenericError)):
476
      item = None
477
  return item
478

    
479

    
480
def EncodeException(err):
481
  """Encodes an exception into a format that L{MaybeRaise} will recognise.
482

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

486
  @type err: GenericError child
487
  @param err: usually a child of GenericError (but any exception
488
      will be accepted)
489
  @rtype: tuple
490
  @return: tuple of (exception name, exception arguments)
491

492
  """
493
  return (err.__class__.__name__, err.args)
494

    
495

    
496
def GetEncodedError(result):
497
  """If this looks like an encoded Ganeti exception, return it.
498

499
  This function tries to parse the passed argument and if it looks
500
  like an encoding done by EncodeException, it will return the class
501
  object and arguments.
502

503
  """
504
  tlt = (tuple, list)
505
  if (isinstance(result, tlt) and len(result) == 2 and
506
      isinstance(result[1], tlt)):
507
    # custom ganeti errors
508
    errcls = GetErrorClass(result[0])
509
    if errcls:
510
      return (errcls, tuple(result[1]))
511

    
512
  return None
513

    
514

    
515
def MaybeRaise(result):
516
  """If this looks like an encoded Ganeti exception, raise it.
517

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

521
  """
522
  error = GetEncodedError(result)
523
  if error:
524
    (errcls, args) = error
525
    # pylint: disable=W0142
526
    raise errcls(*args)