ExtLoader.hs 4.81 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
43
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.Posix.Env
import System.IO
import System
import Text.Printf (printf, hPrintf)

import qualified Ganeti.HTools.Luxi as Luxi
44
#ifndef NO_CURL
45
import qualified Ganeti.HTools.Rapi as Rapi
46
#endif
47
48
49
50
51
52
53
54
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
55
import Ganeti.HTools.Utils (sepSplit, tryRead)
56
57
58
59
60
61
62
63
64
65
66
67

-- | Parse the environment and return the node\/instance names.
--
-- This also hardcodes here the default node\/instance file names.
parseEnv :: () -> IO (String, String)
parseEnv () = do
  a <- getEnvDefault "HTOOLS_NODES" "nodes"
  b <- getEnvDefault "HTOOLS_INSTANCES" "instances"
  return (a, b)

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

70
71
72
73
74
75
76
77
78
79
80
81
82
83
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

84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
-- | External tool data loader from a variety of sources.
loadExternalData :: Options
                 -> IO (Node.List, Instance.List, String)
loadExternalData opts = do
  (env_node, env_inst) <- parseEnv ()
  let nodef = if optNodeSet opts then optNodeFile opts
              else env_node
      instf = if optInstSet opts then optInstFile opts
              else env_inst
      mhost = optMaster opts
      lsock = optLuxi opts
      simdata = optNodeSim opts
      setRapi = mhost /= ""
      setLuxi = isJust lsock
      setSim = isJust simdata
      setFiles = optNodeSet opts || optInstSet opts
      allSet = filter id [setRapi, setLuxi, setFiles]
  when (length allSet > 1) $
       do
103
104
         hPutStrLn stderr ("Error: Only one of the rapi, luxi, and data" ++
                           " files options should be given.")
105
106
         exitWith $ ExitFailure 1

107
108
109
110
111
112
113
114
115
116
  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)
117
118
  input_data <-
      case () of
119
120
121
122
123
124
        _ | setRapi ->
#ifdef NO_CURL
              return $ Bad "RAPI/curl backend disabled at compile time"
#else
              wrapIO $ Rapi.loadData mhost
#endif
125
126
127
128
          | setLuxi -> wrapIO $ Luxi.loadData $ fromJust lsock
          | setSim -> Simu.loadData $ fromJust simdata
          | otherwise -> wrapIO $ Text.loadData nodef instf

129
  let ldresult = input_data >>= Loader.mergeData util_data'
130
131
132
133
134
135
136
137
138
139
140
141
142
143
  (loaded_nl, il, csf) <-
      (case ldresult of
         Ok x -> return x
         Bad s -> do
           hPrintf stderr "Error: failed to load data. Details:\n%s\n" s
           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

  return (fixed_nl, il, csf)