Take the foldl out of Loader.fixNodes
[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 and gnt-instance list.
5
6 -}
7
8 {-
9
10 Copyright (C) 2009 Google Inc.
11
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful, but
18 WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 02110-1301, USA.
26
27 -}
28
29 module Ganeti.HTools.Text
30     where
31
32 import Control.Monad
33
34 import Ganeti.HTools.Utils
35 import Ganeti.HTools.Loader
36 import Ganeti.HTools.Types
37 import qualified Ganeti.HTools.Node as Node
38 import qualified Ganeti.HTools.Instance as Instance
39
40 -- | Parse results from readsPrec
41 parseChoices :: (Monad m, Read a) => String -> String -> [(a, String)] -> m a
42 parseChoices _ _ ((v, ""):[]) = return v
43 parseChoices name s ((_, e):[]) =
44     fail $ name ++ ": leftover characters when parsing '"
45            ++ s ++ "': '" ++ e ++ "'"
46 parseChoices name s _ = fail $ name ++ ": cannot parse string '" ++ s ++ "'"
47
48 -- | Safe 'read' function returning data encapsulated in a Result.
49 tryRead :: (Monad m, Read a) => String -> String -> m a
50 tryRead name s = parseChoices name s $ reads s
51
52 -- | Load a node from a field list.
53 loadNode :: (Monad m) => [String] -> m (String, Node.Node)
54 loadNode (name:tm:nm:fm:td:fd:tc:fo:[]) = do
55   new_node <-
56       if any (== "?") [tm,nm,fm,td,fd,tc] || fo == "Y" then
57           return $ Node.create name 0 0 0 0 0 0 True
58       else do
59         vtm <- tryRead name tm
60         vnm <- tryRead name nm
61         vfm <- tryRead name fm
62         vtd <- tryRead name td
63         vfd <- tryRead name fd
64         vtc <- tryRead name tc
65         return $ Node.create name vtm vnm vfm vtd vfd vtc False
66   return (name, new_node)
67 loadNode s = fail $ "Invalid/incomplete node data: '" ++ show s ++ "'"
68
69 -- | Load an instance from a field list.
70 loadInst :: (Monad m) =>
71             [(String, Ndx)] -> [String] -> m (String, Instance.Instance)
72 loadInst ktn (name:mem:dsk:vcpus:status:pnode:snode:[]) = do
73   pidx <- lookupNode ktn name pnode
74   sidx <- (if null snode then return Node.noSecondary
75            else lookupNode ktn name snode)
76   vmem <- tryRead name mem
77   vdsk <- tryRead name dsk
78   vvcpus <- tryRead name vcpus
79   when (sidx == pidx) $ fail $ "Instance " ++ name ++
80            " has same primary and secondary node - " ++ pnode
81   let newinst = Instance.create name vmem vdsk vvcpus status pidx sidx
82   return (name, newinst)
83 loadInst _ s = fail $ "Invalid/incomplete instance data: '" ++ show s ++ "'"
84
85 -- | Convert newline and delimiter-separated text.
86 --
87 -- This function converts a text in tabular format as generated by
88 -- @gnt-instance list@ and @gnt-node list@ to a list of objects using
89 -- a supplied conversion function.
90 loadTabular :: (Monad m, Element a) =>
91                String -> ([String] -> m (String, a))
92             -> m ([(String, Int)], [(Int, a)])
93 loadTabular text_data convert_fn = do
94   let lines_data = lines text_data
95       rows = map (sepSplit '|') lines_data
96   kerows <- mapM convert_fn rows
97   return $ assignIndices kerows
98
99 -- | Builds the cluster data from node\/instance files.
100 loadData :: String -- ^ Node data in string format
101          -> String -- ^ Instance data in string format
102          -> IO (Result (Node.AssocList, Instance.AssocList))
103 loadData nfile ifile = do -- IO monad
104   ndata <- readFile nfile
105   idata <- readFile ifile
106   return $ do
107     {- node file: name t_mem n_mem f_mem t_disk f_disk -}
108     (ktn, nl) <- loadTabular ndata loadNode
109     {- instance file: name mem disk status pnode snode -}
110     (_, il) <- loadTabular idata (loadInst ktn)
111     return (nl, il)