Instance.hs: add an 'arPolicy' field for auto-repair policy
[ganeti-local] / src / Ganeti / HTools / ExtLoader.hs
1 {-| External data loader.
2
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.
6
7 -}
8
9 {-
10
11 Copyright (C) 2009, 2010, 2011, 2012 Google Inc.
12
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.
17
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.
22
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
26 02110-1301, USA.
27
28 -}
29
30 module Ganeti.HTools.ExtLoader
31   ( loadExternalData
32   , commonSuffix
33   , maybeSaveData
34   ) where
35
36 import Control.Monad
37 import Control.Exception
38 import Data.Maybe (isJust, fromJust)
39 import System.FilePath
40 import System.IO
41 import Text.Printf (hPrintf)
42
43 import qualified Ganeti.HTools.Backend.Luxi as Luxi
44 import qualified Ganeti.HTools.Backend.Rapi as Rapi
45 import qualified Ganeti.HTools.Backend.Simu as Simu
46 import qualified Ganeti.HTools.Backend.Text as Text
47 import qualified Ganeti.HTools.Backend.IAlloc as IAlloc
48 import Ganeti.HTools.Loader (mergeData, checkData, ClusterData(..)
49                             , commonSuffix)
50
51 import Ganeti.BasicTypes
52 import Ganeti.HTools.Types
53 import Ganeti.HTools.CLI
54 import Ganeti.Utils (sepSplit, tryRead, exitIfBad, exitWhen)
55
56 -- | Error beautifier.
57 wrapIO :: IO (Result a) -> IO (Result a)
58 wrapIO = handle (\e -> return . Bad . show $ (e::IOException))
59
60 -- | Parses a user-supplied utilisation string.
61 parseUtilisation :: String -> Result (String, DynUtil)
62 parseUtilisation line =
63   case sepSplit ' ' line of
64     [name, cpu, mem, dsk, net] ->
65       do
66         rcpu <- tryRead name cpu
67         rmem <- tryRead name mem
68         rdsk <- tryRead name dsk
69         rnet <- tryRead name net
70         let du = DynUtil { cpuWeight = rcpu, memWeight = rmem
71                          , dskWeight = rdsk, netWeight = rnet }
72         return (name, du)
73     _ -> Bad $ "Cannot parse line " ++ line
74
75 -- | External tool data loader from a variety of sources.
76 loadExternalData :: Options
77                  -> IO ClusterData
78 loadExternalData opts = do
79   let mhost = optMaster opts
80       lsock = optLuxi opts
81       tfile = optDataFile opts
82       simdata = optNodeSim opts
83       iallocsrc = optIAllocSrc opts
84       setRapi = mhost /= ""
85       setLuxi = isJust lsock
86       setSim = (not . null) simdata
87       setFile = isJust tfile
88       setIAllocSrc = isJust iallocsrc
89       allSet = filter id [setRapi, setLuxi, setFile]
90       exTags = case optExTags opts of
91                  Nothing -> []
92                  Just etl -> map (++ ":") etl
93       selInsts = optSelInst opts
94       exInsts = optExInst opts
95
96   exitWhen (length allSet > 1) "Only one of the rapi, luxi, and data\
97                                \ files options should be given."
98
99   util_contents <- maybe (return "") readFile (optDynuFile opts)
100   util_data <- exitIfBad "can't parse utilisation data" .
101                mapM parseUtilisation $ lines util_contents
102   input_data <-
103     case () of
104       _ | setRapi -> wrapIO $ Rapi.loadData mhost
105         | setLuxi -> wrapIO . Luxi.loadData $ fromJust lsock
106         | setSim -> Simu.loadData simdata
107         | setFile -> wrapIO . Text.loadData $ fromJust tfile
108         | setIAllocSrc -> wrapIO . IAlloc.loadData $ fromJust iallocsrc
109         | otherwise -> return $ Bad "No backend selected! Exiting."
110
111   let ldresult = input_data >>= mergeData util_data exTags selInsts exInsts
112   cdata <- exitIfBad "failed to load data, aborting" ldresult
113   let (fix_msgs, nl) = checkData (cdNodes cdata) (cdInstances cdata)
114
115   unless (optVerbose opts == 0) $ maybeShowWarnings fix_msgs
116
117   return cdata {cdNodes = nl}
118
119 -- | Function to save the cluster data to a file.
120 maybeSaveData :: Maybe FilePath -- ^ The file prefix to save to
121               -> String         -- ^ The suffix (extension) to add
122               -> String         -- ^ Informational message
123               -> ClusterData    -- ^ The cluster data
124               -> IO ()
125 maybeSaveData Nothing _ _ _ = return ()
126 maybeSaveData (Just path) ext msg cdata = do
127   let adata = Text.serializeCluster cdata
128       out_path = path <.> ext
129   writeFile out_path adata
130   hPrintf stderr "The cluster state %s has been written to file '%s'\n"
131           msg out_path