Statistics
| Branch: | Tag: | Revision:

root / Ganeti / HTools / Text.hs @ 78694255

History | View | Annotate | Download (4 kB)

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 $ readsPrec 0 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)