blob: 56e2e80109a051c13de2c599e8f9b17051b9267f [file] [log] [blame]
{-| External data loader.
This module holds the external data loading, and thus is the only one
depending (via the specialized Text\/Rapi\/Luxi modules) on the actual
libraries implementing the low-level protocols.
-}
{-
Copyright (C) 2009, 2010, 2011, 2012 Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-}
module Ganeti.HTools.ExtLoader
( loadExternalData
, commonSuffix
, maybeSaveData
) where
import Control.Monad
import Control.Monad.Writer (runWriterT)
import Control.Exception
import Data.Maybe (isJust, fromJust)
import Data.Monoid (getAll)
import System.FilePath
import System.IO
import System.Time (getClockTime)
import Text.Printf (hPrintf)
import Ganeti.BasicTypes
import qualified Ganeti.HTools.Backend.Luxi as Luxi
import qualified Ganeti.HTools.Backend.Rapi as Rapi
import qualified Ganeti.HTools.Backend.Simu as Simu
import qualified Ganeti.HTools.Backend.Text as Text
import qualified Ganeti.HTools.Backend.IAlloc as IAlloc
import qualified Ganeti.HTools.Backend.MonD as MonD
import Ganeti.HTools.CLI
import Ganeti.HTools.Loader (mergeData, checkData, ClusterData(..)
, commonSuffix, clearDynU)
import Ganeti.HTools.Types
import Ganeti.Utils (sepSplit, tryRead, exitIfBad, exitWhen)
-- | Error beautifier.
wrapIO :: IO (Result a) -> IO (Result a)
wrapIO = handle (\e -> return . Bad . show $ (e::IOException))
-- | Parses a user-supplied utilisation string.
parseUtilisation :: String -> Result (String, DynUtil)
parseUtilisation line =
case sepSplit ' ' line of
[name, cpu, mem, dsk, net] ->
do
rcpu <- tryRead name cpu
rmem <- tryRead name mem
rdsk <- tryRead name dsk
rnet <- tryRead name net
let du = DynUtil { cpuWeight = rcpu, memWeight = rmem
, dskWeight = rdsk, netWeight = rnet }
return (name, du)
_ -> Bad $ "Cannot parse line " ++ line
-- | External tool data loader from a variety of sources.
loadExternalData :: Options
-> IO ClusterData
loadExternalData opts = do
let mhost = optMaster opts
lsock = optLuxi opts
tfile = optDataFile opts
simdata = optNodeSim opts
iallocsrc = optIAllocSrc opts
setRapi = mhost /= ""
setLuxi = isJust lsock
setSim = (not . null) simdata
setFile = isJust tfile
setIAllocSrc = isJust iallocsrc
allSet = filter id [setRapi, setLuxi, setFile]
exTags = case optExTags opts of
Nothing -> []
Just etl -> map (++ ":") etl
selInsts = optSelInst opts
exInsts = optExInst opts
exitWhen (length allSet > 1) "Only one of the rapi, luxi, and data\
\ files options should be given."
util_contents <- maybe (return "") readFile (optDynuFile opts)
util_data <- exitIfBad "can't parse utilisation data" .
mapM parseUtilisation $ lines util_contents
input_data <-
case () of
_ | setRapi -> wrapIO $ Rapi.loadData mhost
| setLuxi -> wrapIO . Luxi.loadData $ fromJust lsock
| setSim -> Simu.loadData simdata
| setFile -> wrapIO . Text.loadData $ fromJust tfile
| setIAllocSrc -> wrapIO . IAlloc.loadData $ fromJust iallocsrc
| otherwise -> return $ Bad "No backend selected! Exiting."
now <- getClockTime
let ignoreDynU = optIgnoreDynu opts
eff_u = if ignoreDynU then [] else util_data
ldresult = input_data >>= (if ignoreDynU then clearDynU else return)
>>= mergeData eff_u exTags selInsts exInsts now
cdata <- exitIfBad "failed to load data, aborting" ldresult
(cdata', ok) <- runWriterT $ if optMonD opts
then MonD.queryAllMonDDCs cdata opts
else return cdata
exitWhen (optMonDExitMissing opts && not (getAll ok))
"Not all required data available"
let (fix_msgs, nl) = checkData (cdNodes cdata') (cdInstances cdata')
unless (optVerbose opts == 0) $ maybeShowWarnings fix_msgs
return cdata' {cdNodes = nl}
-- | Function to save the cluster data to a file.
maybeSaveData :: Maybe FilePath -- ^ The file prefix to save to
-> String -- ^ The suffix (extension) to add
-> String -- ^ Informational message
-> ClusterData -- ^ The cluster data
-> IO ()
maybeSaveData Nothing _ _ _ = return ()
maybeSaveData (Just path) ext msg cdata = do
let adata = Text.serializeCluster cdata
out_path = path <.> ext
writeFile out_path adata
hPrintf stderr "The cluster state %s has been written to file '%s'\n"
msg out_path