Make IAlloc.loadData return maps
[ganeti-local] / Ganeti / HTools / Text.hs
1 {-| Parsing data from text-files
2
3 This module holds the code for loading the cluster state from text
4 files, as produced by gnt-node/gnt-instance list.
5
6 -}
7
8 module Ganeti.HTools.Text
9     where
10
11 import Control.Monad
12
13 import Ganeti.HTools.Utils
14 import Ganeti.HTools.Loader
15 import Ganeti.HTools.Types
16 import qualified Ganeti.HTools.Node as Node
17 import qualified Ganeti.HTools.Instance as Instance
18
19 -- | Safe 'read' function returning data encapsulated in a Result
20 tryRead :: (Monad m, Read a) => String -> String -> m a
21 tryRead name s =
22     let sols = readsPrec 0 s
23     in case sols of
24          (v, ""):[] -> return v
25          (_, e):[] -> fail $ name ++ ": leftover characters when parsing '"
26                       ++ s ++ "': '" ++ e ++ "'"
27          _ -> fail $ name ++ ": cannot parse string '" ++ s ++ "'"
28
29 -- | Load a node from a field list
30 loadNode :: (Monad m) => [String] -> m (String, Node.Node)
31 loadNode (name:tm:nm:fm:td:fd:fo:[]) = do
32   new_node <-
33       if any (== "?") [tm,nm,fm,td,fd] || fo == "Y" then
34           return $ Node.create name 0 0 0 0 0 True
35       else do
36         vtm <- tryRead name tm
37         vnm <- tryRead name nm
38         vfm <- tryRead name fm
39         vtd <- tryRead name td
40         vfd <- tryRead name fd
41         return $ Node.create name vtm vnm vfm vtd vfd False
42   return (name, new_node)
43 loadNode s = fail $ "Invalid/incomplete node data: '" ++ (show s) ++ "'"
44
45 -- | Load an instance from a field list
46 loadInst :: (Monad m) =>
47             [(String, Int)] -> [String] -> m (String, Instance.Instance)
48 loadInst ktn (name:mem:dsk:status:pnode:snode:[]) = do
49   pidx <- lookupNode ktn name pnode
50   sidx <- (if null snode then return Node.noSecondary
51            else lookupNode ktn name snode)
52   vmem <- tryRead name mem
53   vdsk <- tryRead name dsk
54   when (sidx == pidx) $ fail $ "Instance " ++ name ++
55            " has same primary and secondary node - " ++ pnode
56   let newinst = Instance.create name vmem vdsk status pidx sidx
57   return (name, newinst)
58 loadInst _ s = fail $ "Invalid/incomplete instance data: '" ++ (show s) ++ "'"
59
60 {- | Convert newline and delimiter-separated text.
61
62 This function converts a text in tabular format as generated by
63 @gnt-instance list@ and @gnt-node list@ to a list of objects using a
64 supplied conversion function.
65
66 -}
67 loadTabular :: (Monad m, Element a) =>
68                String -> ([String] -> m (String, a))
69             -> m ([(String, Int)], [(Int, a)])
70 loadTabular text_data convert_fn = do
71   let lines_data = lines text_data
72       rows = map (sepSplit '|') lines_data
73   kerows <- mapM convert_fn rows
74   return $ assignIndices kerows
75
76 loadData :: String -- ^ Node data in string format
77          -> String -- ^ Instance data in string format
78          -> IO (Result (NameAssoc, Node.AssocList,
79                         NameAssoc, Instance.AssocList))
80 loadData nfile ifile = do -- IO monad
81   ndata <- readFile nfile
82   idata <- readFile ifile
83   return $ do
84     {- node file: name t_mem n_mem f_mem t_disk f_disk -}
85     (ktn, nl) <- loadTabular ndata loadNode
86     {- instance file: name mem disk status pnode snode -}
87     (kti, il) <- loadTabular idata (loadInst ktn)
88     return (ktn, nl, kti, il)