Statistics
| Branch: | Tag: | Revision:

root / hn1.hs @ 040afc35

History | View | Annotate | Download (7.6 kB)

1
{-| Solver for N+1 cluster errors
2

    
3
-}
4

    
5
module Main (main) where
6

    
7
import Data.List
8
import Data.Function
9
import Monad
10
import System
11
import System.IO
12
import System.Console.GetOpt
13
import qualified System
14

    
15
import Text.Printf (printf)
16

    
17
import qualified Ganeti.HTools.Container as Container
18
import qualified Ganeti.HTools.Instance as Instance
19
import qualified Ganeti.HTools.Cluster as Cluster
20
import qualified Ganeti.HTools.CLI as CLI
21
import qualified Ganeti.HTools.Rapi as Rapi
22
import qualified Ganeti.HTools.Text as Text
23
import Ganeti.HTools.Utils
24

    
25
-- | Command line options structure.
26
data Options = Options
27
    { optShowNodes   :: Bool
28
    , optShowCmds    :: Bool
29
    , optNodef       :: FilePath
30
    , optNodeSet     :: Bool     -- ^ The nodes have been set by options
31
    , optInstf       :: FilePath -- ^ Path to the instances file
32
    , optInstSet     :: Bool     -- ^ The insts have been set by options
33
    , optMinDepth    :: Int
34
    , optMaxRemovals :: Int
35
    , optMinDelta    :: Int
36
    , optMaxDelta    :: Int
37
    , optMaster      :: String
38
    , optShowVer     :: Bool     -- ^ Just show the program version
39
    , optShowHelp    :: Bool     -- ^ Just show the help
40
    } deriving Show
41

    
42
-- | Default values for the command line options.
43
defaultOptions :: Options
44
defaultOptions    = Options
45
 { optShowNodes   = False
46
 , optShowCmds    = False
47
 , optNodef       = "nodes"
48
 , optNodeSet     = False
49
 , optInstf       = "instances"
50
 , optInstSet     = False
51
 , optMinDepth    = 1
52
 , optMaxRemovals = -1
53
 , optMinDelta    = 0
54
 , optMaxDelta    = -1
55
 , optMaster      = ""
56
 , optShowVer     = False
57
 , optShowHelp    = False
58
 }
59

    
60
{- | Start computing the solution at the given depth and recurse until
61
we find a valid solution or we exceed the maximum depth.
62

    
63
-}
64
iterateDepth :: Cluster.NodeList
65
             -> [Instance.Instance]
66
             -> Int
67
             -> Int
68
             -> Int
69
             -> Int
70
             -> IO (Maybe Cluster.Solution)
71
iterateDepth nl bad_instances depth max_removals min_delta max_delta =
72
    let
73
        max_depth = length bad_instances
74
        sol = Cluster.computeSolution nl bad_instances depth
75
              max_removals min_delta max_delta
76
    in
77
      do
78
        printf "%d " depth
79
        hFlush stdout
80
        case sol `seq` sol of
81
          Nothing ->
82
              if depth > max_depth then
83
                  return Nothing
84
              else
85
                  iterateDepth nl bad_instances (depth + 1)
86
                               max_removals min_delta max_delta
87
          _ -> return sol
88

    
89
-- | Options list and functions
90
options :: [OptDescr (Options -> Options)]
91
options =
92
    [ Option ['p']     ["print-nodes"]
93
      (NoArg (\ opts -> opts { optShowNodes = True }))
94
      "print the final node list"
95
    , Option ['C']     ["print-commands"]
96
      (NoArg (\ opts -> opts { optShowCmds = True }))
97
      "print the ganeti command list for reaching the solution"
98
    , Option ['n']     ["nodes"]
99
      (ReqArg (\ f opts -> opts { optNodef = f, optNodeSet = True }) "FILE")
100
      "the node list FILE"
101
    , Option ['i']     ["instances"]
102
      (ReqArg (\ f opts -> opts { optInstf =  f, optInstSet = True }) "FILE")
103
      "the instance list FILE"
104
    , Option ['d']     ["depth"]
105
      (ReqArg (\ i opts -> opts { optMinDepth =  (read i)::Int }) "D")
106
      "start computing the solution at depth D"
107
    , Option ['r']     ["max-removals"]
108
      (ReqArg (\ i opts -> opts { optMaxRemovals =  (read i)::Int }) "R")
109
      "do not process more than R removal sets (useful for high depths)"
110
    , Option ['L']     ["max-delta"]
111
      (ReqArg (\ i opts -> opts { optMaxDelta =  (read i)::Int }) "L")
112
      "refuse solutions with delta higher than L"
113
    , Option ['l']     ["min-delta"]
114
      (ReqArg (\ i opts -> opts { optMinDelta =  (read i)::Int }) "L")
115
      "return once a solution with delta L or lower has been found"
116
    , Option ['m']     ["master"]
117
      (ReqArg (\ m opts -> opts { optMaster = m }) "ADDRESS")
118
      "collect data via RAPI at the given ADDRESS"
119
    , Option ['V']     ["version"]
120
      (NoArg (\ opts -> opts { optShowVer = True}))
121
      "show the version of the program"
122
    , Option ['h']     ["help"]
123
      (NoArg (\ opts -> opts { optShowHelp = True}))
124
      "show help"
125
    ]
126

    
127
-- | Main function.
128
main :: IO ()
129
main = do
130
  cmd_args <- System.getArgs
131
  (opts, args) <- CLI.parseOpts cmd_args "hn1" options
132
                  defaultOptions optShowHelp
133

    
134
  unless (null args) $ do
135
         hPutStrLn stderr "Error: this program doesn't take any arguments."
136
         exitWith $ ExitFailure 1
137

    
138
  when (optShowVer opts) $ do
139
         putStr $ CLI.showVersion "hn1"
140
         exitWith ExitSuccess
141

    
142
  (env_node, env_inst) <- CLI.parseEnv ()
143
  let nodef = if optNodeSet opts then optNodef opts
144
              else env_node
145
      instf = if optInstSet opts then optInstf opts
146
              else env_inst
147
      min_depth = optMinDepth opts
148

    
149
  input_data <-
150
      case optMaster opts of
151
        "" -> Text.loadData nodef instf
152
        host -> Rapi.loadData host
153
  let ldresult = input_data >>= Cluster.loadData
154

    
155
  (loaded_nl, il, csf, ktn, kti) <-
156
      (case ldresult of
157
         Ok x -> return x
158
         Bad s -> do
159
           printf "Error: failed to load data. Details:\n%s\n" s
160
           exitWith $ ExitFailure 1
161
      )
162
  let (fix_msgs, nl) = Cluster.checkData loaded_nl il ktn kti
163

    
164
  unless (null fix_msgs) $ do
165
         putStrLn "Warning: cluster has inconsistent data:"
166
         putStrLn . unlines . map (\s -> printf "  - %s" s) $ fix_msgs
167

    
168
  printf "Loaded %d nodes, %d instances\n"
169
             (Container.size nl)
170
             (Container.size il)
171

    
172
  when (length csf > 0) $ do
173
         printf "Note: Stripping common suffix of '%s' from names\n" csf
174

    
175
  let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
176
  printf "Initial check done: %d bad nodes, %d bad instances.\n"
177
             (length bad_nodes) (length bad_instances)
178

    
179
  when (null bad_instances) $ do
180
         putStrLn "Happy time! Cluster is fine, no need to burn CPU."
181
         exitWith ExitSuccess
182

    
183
  when (length bad_instances < min_depth) $ do
184
         printf "Error: depth %d is higher than the number of bad instances.\n"
185
                min_depth
186
         exitWith $ ExitFailure 2
187

    
188
  let ini_cv = Cluster.compCV nl
189
  printf "Initial coefficients: overall %.8f, %s\n"
190
         ini_cv (Cluster.printStats nl)
191

    
192
  putStr "Computing solution: depth "
193
  hFlush stdout
194

    
195
  result <- iterateDepth nl bad_instances min_depth (optMaxRemovals opts)
196
            (optMinDelta opts) (optMaxDelta opts)
197
  let (min_d, solution) =
198
          case result of
199
            Just (Cluster.Solution a b) -> (a, reverse b)
200
            Nothing -> (-1, [])
201
  when (min_d == -1) $ do
202
         putStrLn "failed. Try to run with higher depth."
203
         exitWith $ ExitFailure 1
204

    
205
  printf "found.\n"
206

    
207
  let
208
      ns = Cluster.applySolution nl il solution
209
      fin_cv = Cluster.compCV ns
210

    
211
  printf "Final coefficients:   overall %.8f, %s\n"
212
         fin_cv
213
         (Cluster.printStats ns)
214

    
215
  printf "Solution (delta=%d):\n" $! min_d
216
  let (sol_strs, cmd_strs) = Cluster.printSolution il ktn kti solution
217
  putStr $ unlines $ sol_strs
218
  when (optShowCmds opts) $
219
       do
220
         putStrLn ""
221
         putStrLn "Commands to run to reach the above solution:"
222
         putStr . Cluster.formatCmds . reverse $ cmd_strs
223

    
224
  when (optShowNodes opts) $
225
       do
226
         let (orig_mem, orig_disk) = Cluster.totalResources nl
227
             (final_mem, final_disk) = Cluster.totalResources ns
228
         putStrLn ""
229
         putStrLn "Final cluster status:"
230
         putStrLn $ Cluster.printNodes ktn ns
231
         printf "Original: mem=%d disk=%d\n" orig_mem orig_disk
232
         printf "Final:    mem=%d disk=%d\n" final_mem final_disk