1 {-| Parsing data from text-files
3 This module holds the code for loading the cluster state from text
4 files, as produced by gnt-node and gnt-instance list.
10 Copyright (C) 2009 Google Inc.
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.
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.
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
29 module Ganeti.HTools.Text
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
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 ++ "'"
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
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
56 if any (== "?") [tm,nm,fm,td,fd,tc] || fo == "Y" then
57 return $ Node.create name 0 0 0 0 0 0 True
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 ++ "'"
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 ++ "'"
85 -- | Convert newline and delimiter-separated text.
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
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
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)