If _UnlockedLookupNetwork() fails raise error
[ganeti-local] / src / Ganeti / Errors.hs
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   ) where
41
42 import Text.JSON hiding (Result, Ok)
43 import System.Exit
44
45 import Ganeti.THH
46 import Ganeti.BasicTypes
47 import qualified Ganeti.Constants as C
48
49 -- | Error code types for 'OpPrereqError'.
50 $(declareSADT "ErrorCode"
51   [ ("ECodeResolver",  'C.errorsEcodeResolver)
52   , ("ECodeNoRes",     'C.errorsEcodeNores)
53   , ("ECodeTempNoRes", 'C.errorsEcodeTempNores)
54   , ("ECodeInval",     'C.errorsEcodeInval)
55   , ("ECodeState",     'C.errorsEcodeState)
56   , ("ECodeNoEnt",     'C.errorsEcodeNoent)
57   , ("ECodeExists",    'C.errorsEcodeExists)
58   , ("ECodeNotUnique", 'C.errorsEcodeNotunique)
59   , ("ECodeFault",     'C.errorsEcodeFault)
60   , ("ECodeEnviron",   'C.errorsEcodeEnviron)
61   ])
62 $(makeJSONInstance ''ErrorCode)
63
64 $(genException "GanetiException"
65   [ ("GenericError", [excErrMsg])
66   , ("LockError", [excErrMsg])
67   , ("PidFileLockError", [excErrMsg])
68   , ("HypervisorError", [excErrMsg])
69   , ("ProgrammerError", [excErrMsg])
70   , ("BlockDeviceError", [excErrMsg])
71   , ("ConfigurationError", [excErrMsg])
72   , ("ConfigVersionMismatch", [ ("expVer", [t| Int |])
73                               , ("actVer", [t| Int |])])
74   , ("ReservationError", [excErrMsg])
75   , ("RemoteError", [excErrMsg])
76   , ("SignatureError", [excErrMsg])
77   , ("ParameterError", [excErrMsg])
78   , ("ResultValidationError", [excErrMsg])
79   , ("OpPrereqError", [excErrMsg, ("errCode", [t| ErrorCode |])])
80   , ("OpExecError", [excErrMsg])
81   , ("OpResultError", [excErrMsg])
82   , ("OpCodeUnknown", [excErrMsg])
83   , ("JobLost", [excErrMsg])
84   , ("JobFileCorrupted", [excErrMsg])
85   , ("ResolverError", [ ("errHostname", [t| String |])
86                       , ("errResolverCode", [t| Int |])
87                       , ("errResolverMsg", [t| String |])])
88   , ("HooksFailure", [excErrMsg])
89   , ("HooksAbort", [("errs", [t| [(String, String, String)] |])])
90   , ("UnitParseError", [excErrMsg])
91   , ("ParseError", [excErrMsg])
92   , ("TypeEnforcementError", [excErrMsg])
93   , ("X509CertError", [ ("certFileName", [t| String |])
94                       , excErrMsg ])
95   , ("TagError", [excErrMsg])
96   , ("CommandError", [excErrMsg])
97   , ("StorageError", [excErrMsg])
98   , ("InotifyError", [excErrMsg])
99   , ("JobQueueError", [excErrMsg])
100   , ("JobQueueDrainError", [excErrMsg])
101   , ("JobQueueFull", [])
102   , ("ConfdMagicError", [excErrMsg])
103   , ("ConfdClientError", [excErrMsg])
104   , ("UdpDataSizeError", [excErrMsg])
105   , ("NoCtypesError", [excErrMsg])
106   , ("IPAddressError", [excErrMsg])
107   , ("LuxiError", [excErrMsg])
108   , ("QueryFilterParseError", [excErrMsg]) -- not consistent with Python
109   , ("RapiTestResult", [excErrMsg])
110   , ("FileStoragePathError", [excErrMsg])
111   ])
112
113 instance JSON GanetiException where
114   showJSON = saveGanetiException
115   readJSON = loadGanetiException
116
117 instance FromString GanetiException where
118   mkFromString = GenericError
119
120 -- | Error monad using 'GanetiException' type alias.
121 type ErrorResult = GenericResult GanetiException
122
123 $(genStrOfOp ''GanetiException "excName")
124
125 -- | Returns the exit code of a program that should be used if we got
126 -- back an exception from masterd.
127 errorExitCode :: GanetiException -> ExitCode
128 errorExitCode (ConfigurationError {}) = ExitFailure 2
129 errorExitCode _ = ExitFailure 1
130
131 -- | Formats an exception.
132 formatError :: GanetiException -> String
133 formatError (ConfigurationError msg) =
134   "Corrup configuration file: " ++ msg ++ "\nAborting."
135 formatError (HooksAbort errs) =
136   unlines $
137   "Failure: hooks execution failed:":
138   map (\(node, script, out) ->
139          "  node: " ++ node ++ ", script: " ++ script ++
140                     if null out
141                       then " (no output)"
142                       else ", output: " ++ out
143       ) errs
144 formatError (HooksFailure msg) =
145   "Failure: hooks general failure: " ++ msg
146 formatError (ResolverError host _ _) =
147   -- FIXME: in Python, this uses the system hostname to format the
148   -- error differently if we are failing to resolve our own hostname
149   "Failure: can't resolve hostname " ++ host
150 formatError (OpPrereqError msg code) =
151   "Failure: prerequisites not met for this" ++
152   " operation:\nerror type: " ++ show code ++ ", error details:\n" ++ msg
153 formatError (OpExecError msg) =
154   "Failure: command execution error:\n" ++ msg
155 formatError (TagError msg) =
156   "Failure: invalid tag(s) given:\n" ++ msg
157 formatError (JobQueueDrainError _)=
158   "Failure: the job queue is marked for drain and doesn't accept new requests"
159 formatError JobQueueFull =
160   "Failure: the job queue is full and doesn't accept new" ++
161   " job submissions until old jobs are archived"
162 formatError (TypeEnforcementError msg) =
163   "Parameter Error: " ++ msg
164 formatError (ParameterError msg) =
165   "Failure: unknown/wrong parameter name '" ++ msg ++ "'"
166 formatError (JobLost msg) =
167   "Error checking job status: " ++ msg
168 formatError (QueryFilterParseError msg) =
169   -- FIXME: in Python, this has a more complex error message
170   "Error while parsing query filter: " ++ msg
171 formatError (GenericError msg) =
172   "Unhandled Ganeti error: " ++ msg
173 formatError err =
174   "Unhandled exception: " ++ show err
175
176 -- | Convert from an 'ErrorResult' to a standard 'Result'.
177 errToResult :: ErrorResult a -> Result a
178 errToResult (Ok a)  = Ok a
179 errToResult (Bad e) = Bad $ formatError e