Revision 260d0bda

b/htools/Ganeti/HTools/Luxi.hs
69 69
    fail $ "Invalid query result, expected array but got " ++ show o
70 70

  
71 71
-- | Prepare resulting output as parsers expect it.
72
extractArray :: (Monad m) => JSValue -> m [JSValue]
72
extractArray :: (Monad m) => JSValue -> m [[(JSValue, JSValue)]]
73 73
extractArray v =
74
  getData v >>= parseQueryResult >>= (return . map (JSArray . map snd))
74
  getData v >>= parseQueryResult
75

  
76
-- | Testing result status for more verbose error message.
77
fromJValWithStatus :: (Text.JSON.JSON a, Monad m) => (JSValue, JSValue) -> m a
78
fromJValWithStatus (st, v) = do
79
    st' <- fromJVal st
80
    L.checkRS st' v >>= fromJVal
75 81

  
76 82
-- | Annotate errors when converting values with owner/attribute for
77 83
-- better debugging.
78 84
genericConvert :: (Text.JSON.JSON a) =>
79
                  String     -- ^ The object type
80
               -> String     -- ^ The object name
81
               -> String     -- ^ The attribute we're trying to convert
82
               -> JSValue    -- ^ The value we try to convert
83
               -> Result a   -- ^ The annotated result
85
                  String             -- ^ The object type
86
               -> String             -- ^ The object name
87
               -> String             -- ^ The attribute we're trying to convert
88
               -> (JSValue, JSValue) -- ^ The value we're trying to convert
89
               -> Result a           -- ^ The annotated result
84 90
genericConvert otype oname oattr =
85 91
    annotateResult (otype ++ " '" ++ oname ++
86 92
                    "', error while reading attribute '" ++
87
                    oattr ++ "'") . fromJVal
93
                    oattr ++ "'") . fromJValWithStatus
88 94

  
89 95
-- * Data querying functionality
90 96

  
......
135 141

  
136 142
-- | Construct an instance from a JSON object.
137 143
parseInstance :: NameAssoc
138
              -> JSValue
144
              -> [(JSValue, JSValue)]
139 145
              -> Result (String, Instance.Instance)
140
parseInstance ktn (JSArray [ name, disk, mem, vcpus
141
                           , status, pnode, snodes, tags, oram
142
                           , auto_balance, disk_template ]) = do
143
  xname <- annotateResult "Parsing new instance" (fromJVal name)
146
parseInstance ktn [ name, disk, mem, vcpus
147
                  , status, pnode, snodes, tags, oram
148
                  , auto_balance, disk_template ] = do
149
  xname <- annotateResult "Parsing new instance" (fromJValWithStatus name)
144 150
  let convert a = genericConvert "Instance" xname a
145 151
  xdisk <- convert "disk_usage" disk
146
  xmem <- (case oram of
147
             JSRational _ _ -> convert "oper_ram" oram
152
  xmem <- (case oram of -- FIXME: remove the "guessing"
153
             (_, JSRational _ _) -> convert "oper_ram" oram
148 154
             _ -> convert "be/memory" mem)
149 155
  xvcpus <- convert "be/vcpus" vcpus
150 156
  xpnode <- convert "pnode" pnode >>= lookupNode ktn xname
......
166 172
getNodes ktg arr = extractArray arr >>= mapM (parseNode ktg)
167 173

  
168 174
-- | Construct a node from a JSON object.
169
parseNode :: NameAssoc -> JSValue -> Result (String, Node.Node)
170
parseNode ktg (JSArray [ name, mtotal, mnode, mfree, dtotal, dfree
171
                       , ctotal, offline, drained, vm_capable, g_uuid ])
175
parseNode :: NameAssoc -> [(JSValue, JSValue)] -> Result (String, Node.Node)
176
parseNode ktg [ name, mtotal, mnode, mfree, dtotal, dfree
177
              , ctotal, offline, drained, vm_capable, g_uuid ]
172 178
    = do
173
  xname <- annotateResult "Parsing new node" (fromJVal name)
179
  xname <- annotateResult "Parsing new node" (fromJValWithStatus name)
174 180
  let convert a = genericConvert "Node" xname a
175 181
  xoffline <- convert "offline" offline
176 182
  xdrained <- convert "drained" drained
......
203 209
getGroups jsv = extractArray jsv >>= mapM parseGroup
204 210

  
205 211
-- | Parses a given group information.
206
parseGroup :: JSValue -> Result (String, Group.Group)
207
parseGroup (JSArray [uuid, name, apol]) = do
208
  xname <- annotateResult "Parsing new group" (fromJVal name)
212
parseGroup :: [(JSValue, JSValue)] -> Result (String, Group.Group)
213
parseGroup [uuid, name, apol] = do
214
  xname <- annotateResult "Parsing new group" (fromJValWithStatus name)
209 215
  let convert a = genericConvert "Group" xname a
210 216
  xuuid <- convert "uuid" uuid
211 217
  xapol <- convert "alloc_policy" apol
b/htools/Ganeti/Luxi.hs
28 28
module Ganeti.Luxi
29 29
    ( LuxiOp(..)
30 30
    , QrViaLuxi(..)
31
    , ResultStatus(..)
31 32
    , Client
33
    , checkRS
32 34
    , getClient
33 35
    , closeClient
34 36
    , callMethod
......
145 147
-- | The serialisation of LuxiOps into strings in messages.
146 148
$(genStrOfOp ''LuxiOp "strOfOp")
147 149

  
150
$(declareIADT "ResultStatus"
151
     [ ("RSNormal", 'rsNormal)
152
     , ("RSUnknown", 'rsUnknown)
153
     , ("RSNoData", 'rsNodata)
154
     , ("RSUnavailable", 'rsUnavail)
155
     , ("RSOffline", 'rsOffline)
156
     ])
157
$(makeJSONInstanceInt ''ResultStatus)
158

  
159
-- | Check that ResultStatus is success or fail with descriptive message.
160
checkRS :: (Monad m) => ResultStatus -> a -> m a
161
checkRS RSNormal val    = return val
162
checkRS RSUnknown _     = fail "Unknown field"
163
checkRS RSNoData _      = fail "No data for a field"
164
checkRS RSUnavailable _ = fail "Ganeti reports unavailable data"
165
checkRS RSOffline _     = fail "Ganeti reports resource as offline"
166

  
148 167
-- | The end-of-message separator.
149 168
eOM :: Char
150 169
eOM = '\3'
b/htools/Ganeti/THH.hs
30 30
-}
31 31

  
32 32
module Ganeti.THH ( declareSADT
33
                  , declareIADT
33 34
                  , makeJSONInstance
35
                  , makeJSONInstanceInt
34 36
                  , genOpID
35 37
                  , genOpCode
36 38
                  , noDefault
......
68 70
toStrName :: String -> Name
69 71
toStrName = mkName . (++ "ToString") . ensureLower
70 72

  
73
-- | ToInt function name.
74
toIntName :: String -> Name
75
toIntName= mkName . (++ "ToInt") . ensureLower
76

  
71 77
-- | FromString function name.
72 78
fromStrName :: String -> Name
73 79
fromStrName = mkName . (++ "FromString") . ensureLower
74 80

  
81
-- | FromInt function name.
82
fromIntName:: String -> Name
83
fromIntName = mkName . (++ "FromInt") . ensureLower
84

  
75 85
-- | Converts a name to it's varE/litE representations.
76 86
--
77 87
reprE :: Either String Name -> Q Exp
......
85 95
appFn f x | f == VarE 'id = x
86 96
          | otherwise = AppE f x
87 97

  
98
-- * Template code for simple integer-equivalent ADTs
99

  
100
-- | Generates a data type declaration.
101
--
102
-- The type will have a fixed list of instances.
103
intADTDecl :: Name -> [String] -> Dec
104
intADTDecl name constructors =
105
    DataD [] name []
106
              (map (flip NormalC [] . mkName) constructors)
107
              [''Show]
108

  
109
-- | Generates a toInt function.
110
genToInt :: Name -> Name -> [(String, Name)] -> Q [Dec]
111
genToInt fname tname constructors = do
112
  sigt <- [t| $(conT tname) -> Int |]
113
  clauses <- mapM  (\(c, v) -> clause [recP (mkName c) []]
114
                             (normalB (varE v)) []) constructors
115
  return [SigD fname sigt, FunD fname clauses]
116

  
117
-- | Generates a fromInt function.
118
genFromInt :: Name -> Name -> [(String, Name)] -> Q [Dec]
119
genFromInt fname tname constructors = do
120
  sigt <- [t| (Monad m) => Int-> m $(conT tname) |]
121
  let varp = mkName "s"
122
      varpe = varE varp
123
  clauses <- mapM (\(c, v) -> do
124
                     g <- normalG [| $varpe == $(varE v) |]
125
                     r <- [| return $(conE (mkName c)) |]
126
                     return (g, r)) constructors
127
  oth_clause <- do
128
    g <- normalG [| otherwise |]
129
    r <- [|fail ("Invalid int value for type " ++
130
                 $(litE (stringL (nameBase tname))) ++ ": " ++ show $varpe) |]
131
    return (g, r)
132
  let fun = FunD fname [Clause [VarP varp]
133
                        (GuardedB (clauses++[oth_clause])) []]
134
  return [SigD fname sigt, fun]
135

  
136
-- | Generates a data type from a given string format.
137
declareIADT:: String -> [(String, Name)] -> Q [Dec]
138
declareIADT sname cons = do
139
  let name = mkName sname
140
      ddecl = intADTDecl name (map fst cons)
141
  tostr <- genToInt (toIntName sname) name cons
142
  fromstr <- genFromInt (fromIntName sname) name cons
143
  return $ ddecl:tostr ++ fromstr
144

  
145
-- | Creates the showJSON member of a JSON instance declaration.
146
genShowJSONInt :: String -> Q [Dec]
147
genShowJSONInt name = [d| showJSON = JSON.showJSON . $(varE (toIntName name)) |]
148

  
149
-- | Creates the readJSON member of a JSON instance declaration.
150
genReadJSONInt :: String -> Q Dec
151
genReadJSONInt name = do
152
  let s = mkName "s"
153
  body <- [| case JSON.readJSON $(varE s) of
154
               JSON.Ok s' -> $(varE (fromIntName name)) s'
155
               JSON.Error e ->
156
                   JSON.Error $ "Can't parse int value for type " ++
157
                           $(stringE name) ++ ": " ++ e
158
           |]
159
  return $ FunD (mkName "readJSON") [Clause [VarP s] (NormalB body) []]
160

  
161
-- | Generates a JSON instance for a given type.
162
makeJSONInstanceInt :: Name -> Q [Dec]
163
makeJSONInstanceInt name = do
164
  let base = nameBase name
165
  showJ <- genShowJSONInt base
166
  readJ <- genReadJSONInt base
167
  return [InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name)) (readJ:showJ)]
168

  
88 169
-- * Template code for simple string-equivalent ADTs
89 170

  
90 171
-- | Generates a data type declaration.

Also available in: Unified diff