Statistics
| Branch: | Tag: | Revision:

root / hn1.hs @ 75d1edf8

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 qualified Ganeti.HTools.Loader as Loader
24
import Ganeti.HTools.Types
25

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

    
43
instance CLI.CLIOptions Options where
44
    showVersion = optShowVer
45
    showHelp    = optShowHelp
46

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

    
65
{- | Start computing the solution at the given depth and recurse until
66
we find a valid solution or we exceed the maximum depth.
67

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

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

    
132
-- | Main function.
133
main :: IO ()
134
main = do
135
  cmd_args <- System.getArgs
136
  (opts, args) <- CLI.parseOpts cmd_args "hn1" options
137
                  defaultOptions
138

    
139
  unless (null args) $ do
140
         hPutStrLn stderr "Error: this program doesn't take any arguments."
141
         exitWith $ ExitFailure 1
142

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

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

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

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

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

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

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

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

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

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

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

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

    
206
  printf "found.\n"
207

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

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

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

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