ExtLoader.hs 4.57 KB
Newer Older
1
2
{-# LANGUAGE CPP #-}

3
4
5
{-| External data loader

This module holds the external data loading, and thus is the only one
6
depending (via the specialized Text\/Rapi\/Luxi modules) on the actual
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
libraries implementing the low-level protocols.

-}

{-

Copyright (C) 2009 Google Inc.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.

-}

module Ganeti.HTools.ExtLoader
    ( loadExternalData
    ) where

import Data.Maybe (isJust, fromJust)
import Monad
import System.IO
import System
import Text.Printf (printf, hPrintf)

import qualified Ganeti.HTools.Luxi as Luxi
43
#ifndef NO_CURL
44
import qualified Ganeti.HTools.Rapi as Rapi
45
#endif
46
47
48
49
50
51
52
53
import qualified Ganeti.HTools.Simu as Simu
import qualified Ganeti.HTools.Text as Text
import qualified Ganeti.HTools.Loader as Loader
import qualified Ganeti.HTools.Instance as Instance
import qualified Ganeti.HTools.Node as Node

import Ganeti.HTools.Types
import Ganeti.HTools.CLI
54
import Ganeti.HTools.Utils (sepSplit, tryRead)
55
56
57

-- | Error beautifier
wrapIO :: IO (Result a) -> IO (Result a)
58
wrapIO = flip catch (return . Bad . show)
59

60
61
62
63
64
65
66
67
68
69
70
71
72
73
parseUtilisation :: String -> Result (String, DynUtil)
parseUtilisation line =
    let columns = sepSplit ' ' line
    in case columns 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

74
75
-- | External tool data loader from a variety of sources.
loadExternalData :: Options
76
                 -> IO (Node.List, Instance.List, [String], String)
77
loadExternalData opts = do
78
  let mhost = optMaster opts
79
      lsock = optLuxi opts
80
      tfile = optDataFile opts
81
82
83
84
      simdata = optNodeSim opts
      setRapi = mhost /= ""
      setLuxi = isJust lsock
      setSim = isJust simdata
85
86
      setFile = isJust tfile
      allSet = filter id [setRapi, setLuxi, setFile]
87
88
89
      exTags = case optExTags opts of
                 Nothing -> []
                 Just etl -> map (++ ":") etl
90
      exInsts = optExInst opts
91

92
93
  when (length allSet > 1) $
       do
94
95
         hPutStrLn stderr ("Error: Only one of the rapi, luxi, and data" ++
                           " files options should be given.")
96
97
         exitWith $ ExitFailure 1

98
99
100
101
102
103
104
105
106
107
  util_contents <- (case optDynuFile opts of
                      Just path -> readFile path
                      Nothing -> return "")
  let util_data = mapM parseUtilisation $ lines util_contents
  util_data' <- (case util_data of
                   Ok x -> return x
                   Bad y -> do
                     hPutStrLn stderr ("Error: can't parse utilisation" ++
                                       " data: " ++ show y)
                     exitWith $ ExitFailure 1)
108
109
  input_data <-
      case () of
110
111
112
113
114
115
        _ | setRapi ->
#ifdef NO_CURL
              return $ Bad "RAPI/curl backend disabled at compile time"
#else
              wrapIO $ Rapi.loadData mhost
#endif
116
117
          | setLuxi -> wrapIO $ Luxi.loadData $ fromJust lsock
          | setSim -> Simu.loadData $ fromJust simdata
118
119
          | setFile -> wrapIO $ Text.loadData $ fromJust tfile
          | otherwise -> return $ Bad "No backend selected! Exiting."
120

121
  let ldresult = input_data >>= Loader.mergeData util_data' exTags exInsts
122
  (loaded_nl, il, tags, csf) <-
123
124
125
126
      (case ldresult of
         Ok x -> return x
         Bad s -> do
           hPrintf stderr "Error: failed to load data. Details:\n%s\n" s
Iustin Pop's avatar
Iustin Pop committed
127
               :: IO ()
128
129
130
131
132
133
134
135
           exitWith $ ExitFailure 1
      )
  let (fix_msgs, fixed_nl) = Loader.checkData loaded_nl il

  unless (null fix_msgs || optVerbose opts == 0) $ do
         hPutStrLn stderr "Warning: cluster has inconsistent data:"
         hPutStrLn stderr . unlines . map (printf "  - %s") $ fix_msgs

136
  return (fixed_nl, il, tags, csf)