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