Revision a0090487
b/htools/Ganeti/Luxi.hs | ||
---|---|---|
1 |
{-# LANGUAGE TemplateHaskell #-} |
|
2 |
|
|
1 | 3 |
{-| Implementation of the Ganeti LUXI interface. |
2 | 4 |
|
3 | 5 |
-} |
... | ... | |
46 | 48 |
|
47 | 49 |
import Ganeti.Jobs (JobStatus) |
48 | 50 |
import Ganeti.OpCodes (OpCode) |
51 |
import Ganeti.THH |
|
49 | 52 |
|
50 | 53 |
-- * Utility functions |
51 | 54 |
|
... | ... | |
59 | 62 |
|
60 | 63 |
-- * Generic protocol functionality |
61 | 64 |
|
62 |
-- | Currently supported Luxi operations. |
|
63 |
data LuxiOp = QueryInstances [String] [String] Bool |
|
64 |
| QueryNodes [String] [String] Bool |
|
65 |
| QueryGroups [String] [String] Bool |
|
66 |
| QueryJobs [Int] [String] |
|
67 |
| QueryExports [String] Bool |
|
68 |
| QueryConfigValues [String] |
|
69 |
| QueryClusterInfo |
|
70 |
| QueryTags String String |
|
71 |
| SubmitJob [OpCode] |
|
72 |
| SubmitManyJobs [[OpCode]] |
|
73 |
| WaitForJobChange Int [String] JSValue JSValue Int |
|
74 |
| ArchiveJob Int |
|
75 |
| AutoArchiveJobs Int Int |
|
76 |
| CancelJob Int |
|
77 |
| SetDrainFlag Bool |
|
78 |
| SetWatcherPause Double |
|
79 |
deriving (Show, Read) |
|
65 |
-- | Currently supported Luxi operations and JSON serialization. |
|
66 |
$(genLuxiOp "LuxiOp" |
|
67 |
[ ("QueryNodes", |
|
68 |
[ ("names", [t| [String] |], [| id |]) |
|
69 |
, ("fields", [t| [String] |], [| id |]) |
|
70 |
, ("lock", [t| Bool |], [| id |]) |
|
71 |
], |
|
72 |
[| J.showJSON |]) |
|
73 |
, ("QueryGroups", |
|
74 |
[ ("names", [t| [String] |], [| id |]) |
|
75 |
, ("fields", [t| [String] |], [| id |]) |
|
76 |
, ("lock", [t| Bool |], [| id |]) |
|
77 |
], |
|
78 |
[| J.showJSON |]) |
|
79 |
, ("QueryInstances", |
|
80 |
[ ("names", [t| [String] |], [| id |]) |
|
81 |
, ("fields", [t| [String] |], [| id |]) |
|
82 |
, ("lock", [t| Bool |], [| id |]) |
|
83 |
], |
|
84 |
[| J.showJSON |]) |
|
85 |
, ("QueryJobs", |
|
86 |
[ ("ids", [t| [Int] |], [| map show |]) |
|
87 |
, ("fields", [t| [String] |], [| id |]) |
|
88 |
], |
|
89 |
[| J.showJSON |]) |
|
90 |
, ("QueryExports", |
|
91 |
[ ("nodes", [t| [String] |], [| id |]) |
|
92 |
, ("lock", [t| Bool |], [| id |]) |
|
93 |
], |
|
94 |
[| J.showJSON |]) |
|
95 |
, ("QueryConfigValues", |
|
96 |
[ ("fields", [t| [String] |], [| id |]) ], |
|
97 |
[| J.showJSON |]) |
|
98 |
, ("QueryClusterInfo", |
|
99 |
[], |
|
100 |
[| J.showJSON |]) |
|
101 |
, ("QueryTags", |
|
102 |
[ ("kind", [t| String |], [| id |]) |
|
103 |
, ("name", [t| String |], [| id |]) |
|
104 |
], |
|
105 |
[| J.showJSON |]) |
|
106 |
, ("SubmitJob", |
|
107 |
[ ("job", [t| [OpCode] |], [| id |]) ], |
|
108 |
[| J.showJSON |]) |
|
109 |
, ("SubmitManyJobs", |
|
110 |
[ ("ops", [t| [[OpCode]] |], [| id |]) ], |
|
111 |
[| J.showJSON |]) |
|
112 |
, ("WaitForJobChange", |
|
113 |
[ ("job", [t| Int |], [| J.showJSON |]) |
|
114 |
, ("fields", [t| [String]|], [| J.showJSON |]) |
|
115 |
, ("prev_job", [t| JSValue |], [| J.showJSON |]) |
|
116 |
, ("prev_log", [t| JSValue |], [| J.showJSON |]) |
|
117 |
, ("tmout", [t| Int |], [| J.showJSON |]) |
|
118 |
], |
|
119 |
[| \(j, f, pj, pl, t) -> JSArray [j, f, pj, pl, t] |]) |
|
120 |
, ("ArchiveJob", |
|
121 |
[ ("job", [t| Int |], [| show |]) ], |
|
122 |
[| J.showJSON |]) |
|
123 |
, ("AutoArchiveJobs", |
|
124 |
[ ("age", [t| Int |], [| id |]) |
|
125 |
, ("tmout", [t| Int |], [| id |]) |
|
126 |
], |
|
127 |
[| J.showJSON |]) |
|
128 |
, ("CancelJob", |
|
129 |
[("job", [t| Int |], [| show |]) ], |
|
130 |
[| J.showJSON |]) |
|
131 |
, ("SetDrainFlag", |
|
132 |
[ ("flag", [t| Bool |], [| id |]) ], |
|
133 |
[| J.showJSON |]) |
|
134 |
, ("SetWatcherPause", |
|
135 |
[ ("duration", [t| Double |], [| \x -> [x] |]) ], |
|
136 |
[| J.showJSON |]) |
|
137 |
]) |
|
80 | 138 |
|
81 | 139 |
-- | The serialisation of LuxiOps into strings in messages. |
82 |
strOfOp :: LuxiOp -> String |
|
83 |
strOfOp QueryNodes {} = "QueryNodes" |
|
84 |
strOfOp QueryGroups {} = "QueryGroups" |
|
85 |
strOfOp QueryInstances {} = "QueryInstances" |
|
86 |
strOfOp QueryJobs {} = "QueryJobs" |
|
87 |
strOfOp QueryExports {} = "QueryExports" |
|
88 |
strOfOp QueryConfigValues {} = "QueryConfigValues" |
|
89 |
strOfOp QueryClusterInfo {} = "QueryClusterInfo" |
|
90 |
strOfOp QueryTags {} = "QueryTags" |
|
91 |
strOfOp SubmitManyJobs {} = "SubmitManyJobs" |
|
92 |
strOfOp WaitForJobChange {} = "WaitForJobChange" |
|
93 |
strOfOp SubmitJob {} = "SubmitJob" |
|
94 |
strOfOp ArchiveJob {} = "ArchiveJob" |
|
95 |
strOfOp AutoArchiveJobs {} = "AutoArchiveJobs" |
|
96 |
strOfOp CancelJob {} = "CancelJob" |
|
97 |
strOfOp SetDrainFlag {} = "SetDrainFlag" |
|
98 |
strOfOp SetWatcherPause {} = "SetWatcherPause" |
|
140 |
$(genStrOfOp ''LuxiOp "strOfOp") |
|
99 | 141 |
|
100 | 142 |
-- | The end-of-message separator. |
101 | 143 |
eOM :: Char |
... | ... | |
108 | 150 |
| Result |
109 | 151 |
|
110 | 152 |
-- | The serialisation of MsgKeys into strings in messages. |
111 |
strOfKey :: MsgKeys -> String |
|
112 |
strOfKey Method = "method" |
|
113 |
strOfKey Args = "args" |
|
114 |
strOfKey Success = "success" |
|
115 |
strOfKey Result = "result" |
|
153 |
$(genStrOfKey ''MsgKeys "strOfKey") |
|
116 | 154 |
|
117 | 155 |
-- | Luxi client encapsulation. |
118 | 156 |
data Client = Client { socket :: S.Socket -- ^ The socket of the client |
... | ... | |
161 | 199 |
writeIORef (rbuf s) nbuf |
162 | 200 |
return msg |
163 | 201 |
|
164 |
-- | Compute the serialized form of a Luxi operation. |
|
165 |
opToArgs :: LuxiOp -> JSValue |
|
166 |
opToArgs (QueryNodes names fields lock) = J.showJSON (names, fields, lock) |
|
167 |
opToArgs (QueryGroups names fields lock) = J.showJSON (names, fields, lock) |
|
168 |
opToArgs (QueryInstances names fields lock) = J.showJSON (names, fields, lock) |
|
169 |
opToArgs (QueryJobs ids fields) = J.showJSON (map show ids, fields) |
|
170 |
opToArgs (QueryExports nodes lock) = J.showJSON (nodes, lock) |
|
171 |
opToArgs (QueryConfigValues fields) = J.showJSON fields |
|
172 |
opToArgs (QueryClusterInfo) = J.showJSON () |
|
173 |
opToArgs (QueryTags kind name) = J.showJSON (kind, name) |
|
174 |
opToArgs (SubmitJob j) = J.showJSON j |
|
175 |
opToArgs (SubmitManyJobs ops) = J.showJSON ops |
|
176 |
-- This is special, since the JSON library doesn't export an instance |
|
177 |
-- of a 5-tuple |
|
178 |
opToArgs (WaitForJobChange a b c d e) = |
|
179 |
JSArray [ J.showJSON a, J.showJSON b, J.showJSON c |
|
180 |
, J.showJSON d, J.showJSON e] |
|
181 |
opToArgs (ArchiveJob a) = J.showJSON (show a) |
|
182 |
opToArgs (AutoArchiveJobs a b) = J.showJSON (a, b) |
|
183 |
opToArgs (CancelJob a) = J.showJSON (show a) |
|
184 |
opToArgs (SetDrainFlag flag) = J.showJSON flag |
|
185 |
opToArgs (SetWatcherPause duration) = J.showJSON [duration] |
|
186 |
|
|
187 | 202 |
-- | Serialize a request to String. |
188 | 203 |
buildCall :: LuxiOp -- ^ The method |
189 | 204 |
-> String -- ^ The serialized form |
b/htools/Ganeti/THH.hs | ||
---|---|---|
34 | 34 |
, genOpID |
35 | 35 |
, genOpCode |
36 | 36 |
, noDefault |
37 |
, genStrOfOp |
|
38 |
, genStrOfKey |
|
39 |
, genLuxiOp |
|
37 | 40 |
) where |
38 | 41 |
|
39 | 42 |
import Control.Monad (liftM) |
... | ... | |
222 | 225 |
constructorName (RecC name _) = return name |
223 | 226 |
constructorName x = fail $ "Unhandled constructor " ++ show x |
224 | 227 |
|
225 |
-- | Builds the constructor-to-string function. |
|
228 |
-- | Builds the generic constructor-to-string function.
|
|
226 | 229 |
-- |
227 | 230 |
-- This generates a simple function of the following form: |
228 | 231 |
-- |
229 | 232 |
-- @ |
230 |
-- fname (ConStructorOne {}) = "CON_STRUCTOR_ONE"
|
|
231 |
-- fname (ConStructorTwo {}) = "CON_STRUCTOR_TWO"
|
|
233 |
-- fname (ConStructorOne {}) = trans_fun("ConStructorOne")
|
|
234 |
-- fname (ConStructorTwo {}) = trans_fun("ConStructorTwo")
|
|
232 | 235 |
-- @ |
233 | 236 |
-- |
234 | 237 |
-- This builds a custom list of name/string pairs and then uses |
235 | 238 |
-- 'genToString' to actually generate the function |
236 |
genOpID :: Name -> String -> Q [Dec]
|
|
237 |
genOpID name fname = do
|
|
239 |
genConstrToStr :: (String -> String) -> Name -> String -> Q [Dec]
|
|
240 |
genConstrToStr trans_fun name fname = do
|
|
238 | 241 |
TyConI (DataD _ _ _ cons _) <- reify name |
239 | 242 |
cnames <- mapM (liftM nameBase . constructorName) cons |
240 |
let svalues = map (Left . deCamelCase) cnames
|
|
243 |
let svalues = map (Left . trans_fun) cnames
|
|
241 | 244 |
genToString (mkName fname) name $ zip cnames svalues |
242 | 245 |
|
246 |
-- | Constructor-to-string for OpCode. |
|
247 |
genOpID :: Name -> String -> Q [Dec] |
|
248 |
genOpID = genConstrToStr deCamelCase |
|
243 | 249 |
|
244 | 250 |
-- | OpCode parameter (field) type |
245 | 251 |
type OpParam = (String, Q Type, Q Exp) |
... | ... | |
400 | 406 |
-- | No default type. |
401 | 407 |
noDefault :: Q Exp |
402 | 408 |
noDefault = conE 'Nothing |
409 |
|
|
410 |
-- * Template code for luxi |
|
411 |
|
|
412 |
-- | Constructor-to-string for LuxiOp. |
|
413 |
genStrOfOp :: Name -> String -> Q [Dec] |
|
414 |
genStrOfOp = genConstrToStr id |
|
415 |
|
|
416 |
-- | Constructor-to-string for MsgKeys. |
|
417 |
genStrOfKey :: Name -> String -> Q [Dec] |
|
418 |
genStrOfKey = genConstrToStr ensureLower |
|
419 |
|
|
420 |
-- | LuxiOp parameter type. |
|
421 |
type LuxiParam = (String, Q Type, Q Exp) |
|
422 |
|
|
423 |
-- | Generates the LuxiOp data type. |
|
424 |
-- |
|
425 |
-- This takes a Luxi operation definition and builds both the |
|
426 |
-- datatype and the function trnasforming the arguments to JSON. |
|
427 |
-- We can't use anything less generic, because the way different |
|
428 |
-- operations are serialized differs on both parameter- and top-level. |
|
429 |
-- |
|
430 |
-- There are three things to be defined for each parameter: |
|
431 |
-- |
|
432 |
-- * name |
|
433 |
-- |
|
434 |
-- * type |
|
435 |
-- |
|
436 |
-- * operation; this is the operation performed on the parameter before |
|
437 |
-- serialization |
|
438 |
-- |
|
439 |
genLuxiOp :: String -> [(String, [LuxiParam], Q Exp)] -> Q [Dec] |
|
440 |
genLuxiOp name cons = do |
|
441 |
decl_d <- mapM (\(cname, fields, _) -> do |
|
442 |
fields' <- mapM (\(_, qt, _) -> |
|
443 |
qt >>= \t -> return (NotStrict, t)) |
|
444 |
fields |
|
445 |
return $ NormalC (mkName cname) fields') |
|
446 |
cons |
|
447 |
let declD = DataD [] (mkName name) [] decl_d [''Show, ''Read] |
|
448 |
(savesig, savefn) <- genSaveLuxiOp cons |
|
449 |
return [declD, savesig, savefn] |
|
450 |
|
|
451 |
-- | Generates the \"save\" clause for entire LuxiOp constructor. |
|
452 |
saveLuxiConstructor :: (String, [LuxiParam], Q Exp) -> Q Clause |
|
453 |
saveLuxiConstructor (sname, fields, finfn) = |
|
454 |
let cname = mkName sname |
|
455 |
fnames = map (\(nm, _, _) -> mkName nm) fields |
|
456 |
pat = conP cname (map varP fnames) |
|
457 |
flist = map (\(nm, _, fn) -> appE fn $ varNameE nm) fields |
|
458 |
finval = appE finfn (tupE flist) |
|
459 |
in |
|
460 |
clause [pat] (normalB finval) [] |
|
461 |
|
|
462 |
-- | Generates the main save LuxiOp function. |
|
463 |
genSaveLuxiOp :: [(String, [LuxiParam], Q Exp)] -> Q (Dec, Dec) |
|
464 |
genSaveLuxiOp opdefs = do |
|
465 |
sigt <- [t| $(conT (mkName "LuxiOp")) -> JSON.JSValue |] |
|
466 |
let fname = mkName "opToArgs" |
|
467 |
cclauses <- mapM saveLuxiConstructor opdefs |
|
468 |
return $ (SigD fname sigt, FunD fname cclauses) |
Also available in: Unified diff