1 {-| External data loader.
3 This module holds the external data loading, and thus is the only one
4 depending (via the specialized Text\/Rapi\/Luxi modules) on the actual
5 libraries implementing the low-level protocols.
11 Copyright (C) 2009, 2010, 2011 Google Inc.
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
18 This program is distributed in the hope that it will be useful, but
19 WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
30 module Ganeti.HTools.ExtLoader
37 import Data.Maybe (isJust, fromJust)
38 import System.FilePath
41 import Text.Printf (hPrintf)
43 import qualified Ganeti.HTools.Luxi as Luxi
44 import qualified Ganeti.HTools.Rapi as Rapi
45 import qualified Ganeti.HTools.Simu as Simu
46 import qualified Ganeti.HTools.Text as Text
47 import Ganeti.HTools.Loader (mergeData, checkData, ClusterData(..)
50 import Ganeti.HTools.Types
51 import Ganeti.HTools.CLI
52 import Ganeti.HTools.Utils (sepSplit, tryRead)
54 -- | Error beautifier.
55 wrapIO :: IO (Result a) -> IO (Result a)
56 wrapIO = flip catch (return . Bad . show)
58 -- | Parses a user-supplied utilisation string.
59 parseUtilisation :: String -> Result (String, DynUtil)
60 parseUtilisation line =
61 case sepSplit ' ' line of
62 [name, cpu, mem, dsk, net] ->
64 rcpu <- tryRead name cpu
65 rmem <- tryRead name mem
66 rdsk <- tryRead name dsk
67 rnet <- tryRead name net
68 let du = DynUtil { cpuWeight = rcpu, memWeight = rmem
69 , dskWeight = rdsk, netWeight = rnet }
71 _ -> Bad $ "Cannot parse line " ++ line
73 -- | External tool data loader from a variety of sources.
74 loadExternalData :: Options
76 loadExternalData opts = do
77 let mhost = optMaster opts
79 tfile = optDataFile opts
80 simdata = optNodeSim opts
82 setLuxi = isJust lsock
83 setSim = (not . null) simdata
84 setFile = isJust tfile
85 allSet = filter id [setRapi, setLuxi, setFile]
86 exTags = case optExTags opts of
88 Just etl -> map (++ ":") etl
89 selInsts = optSelInst opts
90 exInsts = optExInst opts
92 when (length allSet > 1) $
94 hPutStrLn stderr ("Error: Only one of the rapi, luxi, and data" ++
95 " files options should be given.")
96 exitWith $ ExitFailure 1
98 util_contents <- (case optDynuFile opts of
99 Just path -> readFile path
100 Nothing -> return "")
101 let util_data = mapM parseUtilisation $ lines util_contents
102 util_data' <- (case util_data of
105 hPutStrLn stderr ("Error: can't parse utilisation" ++
107 exitWith $ ExitFailure 1)
110 _ | setRapi -> wrapIO $ Rapi.loadData mhost
111 | setLuxi -> wrapIO $ Luxi.loadData $ fromJust lsock
112 | setSim -> Simu.loadData simdata
113 | setFile -> wrapIO $ Text.loadData $ fromJust tfile
114 | otherwise -> return $ Bad "No backend selected! Exiting."
116 let ldresult = input_data >>= mergeData util_data' exTags selInsts exInsts
122 "Error: failed to load data, aborting. Details:\n%s\n" s:: IO ()
123 exitWith $ ExitFailure 1
125 let (fix_msgs, nl) = checkData (cdNodes cdata) (cdInstances cdata)
127 unless (optVerbose opts == 0) $ maybeShowWarnings fix_msgs
129 return cdata {cdNodes = nl}
131 -- | Function to save the cluster data to a file.
132 maybeSaveData :: Maybe FilePath -- ^ The file prefix to save to
133 -> String -- ^ The suffix (extension) to add
134 -> String -- ^ Informational message
135 -> ClusterData -- ^ The cluster data
137 maybeSaveData Nothing _ _ _ = return ()
138 maybeSaveData (Just path) ext msg cdata = do
139 let adata = Text.serializeCluster cdata
140 out_path = path <.> ext
141 writeFile out_path adata
142 hPrintf stderr "The cluster state %s has been written to file '%s'\n"