Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / Errors.hs @ 32be18fc

History | View | Annotate | Download (6.5 kB)

1
{-# LANGUAGE TemplateHaskell #-}
2

    
3
{-| Implementation of the Ganeti error types.
4

    
5
This module implements our error hierarchy. Currently we implement one
6
identical to the Python one; later we might one to have separate ones
7
for frontend (clients), master and backend code.
8

    
9
-}
10

    
11
{-
12

    
13
Copyright (C) 2012 Google Inc.
14

    
15
This program is free software; you can redistribute it and/or modify
16
it under the terms of the GNU General Public License as published by
17
the Free Software Foundation; either version 2 of the License, or
18
(at your option) any later version.
19

    
20
This program is distributed in the hope that it will be useful, but
21
WITHOUT ANY WARRANTY; without even the implied warranty of
22
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23
General Public License for more details.
24

    
25
You should have received a copy of the GNU General Public License
26
along with this program; if not, write to the Free Software
27
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28
02110-1301, USA.
29

    
30
-}
31

    
32
module Ganeti.Errors
33
  ( ErrorCode(..)
34
  , GanetiException(..)
35
  , ErrorResult
36
  , errToResult
37
  , errorExitCode
38
  , excName
39
  , formatError
40
  , ResultG
41
  , maybeToError
42
  ) where
43

    
44
import Text.JSON hiding (Result, Ok)
45
import System.Exit
46

    
47
import Ganeti.THH
48
import Ganeti.BasicTypes
49
import qualified Ganeti.Constants as C
50

    
51
-- | Error code types for 'OpPrereqError'.
52
$(declareSADT "ErrorCode"
53
  [ ("ECodeResolver",  'C.errorsEcodeResolver)
54
  , ("ECodeNoRes",     'C.errorsEcodeNores)
55
  , ("ECodeTempNoRes", 'C.errorsEcodeTempNores)
56
  , ("ECodeInval",     'C.errorsEcodeInval)
57
  , ("ECodeState",     'C.errorsEcodeState)
58
  , ("ECodeNoEnt",     'C.errorsEcodeNoent)
59
  , ("ECodeExists",    'C.errorsEcodeExists)
60
  , ("ECodeNotUnique", 'C.errorsEcodeNotunique)
61
  , ("ECodeFault",     'C.errorsEcodeFault)
62
  , ("ECodeEnviron",   'C.errorsEcodeEnviron)
63
  ])
64
$(makeJSONInstance ''ErrorCode)
65

    
66
$(genException "GanetiException"
67
  [ ("GenericError", [excErrMsg])
68
  , ("LockError", [excErrMsg])
69
  , ("PidFileLockError", [excErrMsg])
70
  , ("HypervisorError", [excErrMsg])
71
  , ("ProgrammerError", [excErrMsg])
72
  , ("BlockDeviceError", [excErrMsg])
73
  , ("ConfigurationError", [excErrMsg])
74
  , ("ConfigVersionMismatch", [ ("expVer", [t| Int |])
75
                              , ("actVer", [t| Int |])])
76
  , ("ReservationError", [excErrMsg])
77
  , ("RemoteError", [excErrMsg])
78
  , ("SignatureError", [excErrMsg])
79
  , ("ParameterError", [excErrMsg])
80
  , ("ResultValidationError", [excErrMsg])
81
  , ("OpPrereqError", [excErrMsg, ("errCode", [t| ErrorCode |])])
82
  , ("OpExecError", [excErrMsg])
83
  , ("OpResultError", [excErrMsg])
84
  , ("OpCodeUnknown", [excErrMsg])
85
  , ("JobLost", [excErrMsg])
86
  , ("JobFileCorrupted", [excErrMsg])
87
  , ("ResolverError", [ ("errHostname", [t| String |])
88
                      , ("errResolverCode", [t| Int |])
89
                      , ("errResolverMsg", [t| String |])])
90
  , ("HooksFailure", [excErrMsg])
91
  , ("HooksAbort", [("errs", [t| [(String, String, String)] |])])
92
  , ("UnitParseError", [excErrMsg])
93
  , ("ParseError", [excErrMsg])
94
  , ("TypeEnforcementError", [excErrMsg])
95
  , ("X509CertError", [ ("certFileName", [t| String |])
96
                      , excErrMsg ])
97
  , ("TagError", [excErrMsg])
98
  , ("CommandError", [excErrMsg])
99
  , ("StorageError", [excErrMsg])
100
  , ("InotifyError", [excErrMsg])
101
  , ("JobQueueError", [excErrMsg])
102
  , ("JobQueueDrainError", [excErrMsg])
103
  , ("JobQueueFull", [])
104
  , ("ConfdMagicError", [excErrMsg])
105
  , ("ConfdClientError", [excErrMsg])
106
  , ("UdpDataSizeError", [excErrMsg])
107
  , ("NoCtypesError", [excErrMsg])
108
  , ("IPAddressError", [excErrMsg])
109
  , ("LuxiError", [excErrMsg])
110
  , ("QueryFilterParseError", [excErrMsg]) -- not consistent with Python
111
  , ("RapiTestResult", [excErrMsg])
112
  , ("FileStoragePathError", [excErrMsg])
113
  ])
114

    
115
instance Error GanetiException where
116
  strMsg = GenericError
117

    
118
instance JSON GanetiException where
119
  showJSON = saveGanetiException
120
  readJSON = loadGanetiException
121

    
122
-- | Error monad using 'GanetiException' type alias.
123
type ErrorResult = GenericResult GanetiException
124

    
125
$(genStrOfOp ''GanetiException "excName")
126

    
127
-- | Returns the exit code of a program that should be used if we got
128
-- back an exception from masterd.
129
errorExitCode :: GanetiException -> ExitCode
130
errorExitCode (ConfigurationError {}) = ExitFailure 2
131
errorExitCode _ = ExitFailure 1
132

    
133
-- | Formats an exception.
134
formatError :: GanetiException -> String
135
formatError (ConfigurationError msg) =
136
  "Corrup configuration file: " ++ msg ++ "\nAborting."
137
formatError (HooksAbort errs) =
138
  unlines $
139
  "Failure: hooks execution failed:":
140
  map (\(node, script, out) ->
141
         "  node: " ++ node ++ ", script: " ++ script ++
142
                    if null out
143
                      then " (no output)"
144
                      else ", output: " ++ out
145
      ) errs
146
formatError (HooksFailure msg) =
147
  "Failure: hooks general failure: " ++ msg
148
formatError (ResolverError host _ _) =
149
  -- FIXME: in Python, this uses the system hostname to format the
150
  -- error differently if we are failing to resolve our own hostname
151
  "Failure: can't resolve hostname " ++ host
152
formatError (OpPrereqError msg code) =
153
  "Failure: prerequisites not met for this" ++
154
  " operation:\nerror type: " ++ show code ++ ", error details:\n" ++ msg
155
formatError (OpExecError msg) =
156
  "Failure: command execution error:\n" ++ msg
157
formatError (TagError msg) =
158
  "Failure: invalid tag(s) given:\n" ++ msg
159
formatError (JobQueueDrainError _)=
160
  "Failure: the job queue is marked for drain and doesn't accept new requests"
161
formatError JobQueueFull =
162
  "Failure: the job queue is full and doesn't accept new" ++
163
  " job submissions until old jobs are archived"
164
formatError (TypeEnforcementError msg) =
165
  "Parameter Error: " ++ msg
166
formatError (ParameterError msg) =
167
  "Failure: unknown/wrong parameter name '" ++ msg ++ "'"
168
formatError (JobLost msg) =
169
  "Error checking job status: " ++ msg
170
formatError (QueryFilterParseError msg) =
171
  -- FIXME: in Python, this has a more complex error message
172
  "Error while parsing query filter: " ++ msg
173
formatError (GenericError msg) =
174
  "Unhandled Ganeti error: " ++ msg
175
formatError err =
176
  "Unhandled exception: " ++ show err
177

    
178
-- | A type for IO actions with errors properly handled as
179
-- 'GanetiException's.
180
-- TODO: Move to Errors.hs
181
type ResultG = ResultT GanetiException IO
182

    
183
-- | Convert from an 'ErrorResult' to a standard 'Result'.
184
errToResult :: ErrorResult a -> Result a
185
errToResult (Ok a)  = Ok a
186
errToResult (Bad e) = Bad $ formatError e
187

    
188
-- | Convert from a 'Maybe' to a an 'ErrorResult'.
189
maybeToError :: String -> Maybe a -> ErrorResult a
190
maybeToError _ (Just a) = Ok a
191
maybeToError m  Nothing = Bad $ GenericError m