Statistics
| Branch: | Tag: | Revision:

root / hail.hs @ 58709f92

History | View | Annotate | Download (7.5 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 Data.Maybe (isJust, fromJust, fromMaybe)
10
import Monad
11
import System
12
import System.IO
13
import System.Console.GetOpt
14
import qualified System
15

    
16
import Text.Printf (printf)
17

    
18
import qualified Ganeti.HTools.Container as Container
19
import qualified Ganeti.HTools.Cluster as Cluster
20
import qualified Ganeti.HTools.Node as Node
21
import qualified Ganeti.HTools.Instance as Instance
22
import qualified Ganeti.HTools.CLI as CLI
23
import Ganeti.HTools.IAlloc
24
import Ganeti.HTools.Utils
25
import Ganeti.HTools.Types
26

    
27
-- | Command line options structure.
28
data Options = Options
29
    { optShowNodes :: Bool           -- ^ Whether to show node status
30
    , optShowCmds  :: Maybe FilePath -- ^ Whether to show the command list
31
    , optOneline   :: Bool           -- ^ Switch output to a single line
32
    , optNodef     :: FilePath       -- ^ Path to the nodes file
33
    , optNodeSet   :: Bool           -- ^ The nodes have been set by options
34
    , optInstf     :: FilePath       -- ^ Path to the instances file
35
    , optInstSet   :: Bool           -- ^ The insts have been set by options
36
    , optMaxLength :: Int            -- ^ Stop after this many steps
37
    , optMaster    :: String         -- ^ Collect data from RAPI
38
    , optVerbose   :: Int            -- ^ Verbosity level
39
    , optOffline   :: [String]       -- ^ Names of offline nodes
40
    , optMinScore  :: Cluster.Score  -- ^ The minimum score we aim for
41
    , optShowVer   :: Bool           -- ^ Just show the program version
42
    , optShowHelp  :: Bool           -- ^ Just show the help
43
    } deriving Show
44

    
45
instance CLI.CLIOptions Options where
46
    showVersion = optShowVer
47
    showHelp    = optShowHelp
48

    
49
-- | Default values for the command line options.
50
defaultOptions :: Options
51
defaultOptions  = Options
52
 { optShowNodes = False
53
 , optShowCmds  = Nothing
54
 , optOneline   = False
55
 , optNodef     = "nodes"
56
 , optNodeSet   = False
57
 , optInstf     = "instances"
58
 , optInstSet   = False
59
 , optMaxLength = -1
60
 , optMaster    = ""
61
 , optVerbose   = 1
62
 , optOffline   = []
63
 , optMinScore  = 1e-9
64
 , optShowVer   = False
65
 , optShowHelp  = False
66
 }
67

    
68
-- | Options list and functions
69
options :: [OptDescr (Options -> Options)]
70
options =
71
    [ Option ['p']     ["print-nodes"]
72
      (NoArg (\ opts -> opts { optShowNodes = True }))
73
      "print the final node list"
74
    , Option ['C']     ["print-commands"]
75
      (OptArg ((\ f opts -> opts { optShowCmds = Just f }) . fromMaybe "-")
76
                  "FILE")
77
      "print the ganeti command list for reaching the solution,\
78
      \if an argument is passed then write the commands to a file named\
79
      \ as such"
80
    , Option ['o']     ["oneline"]
81
      (NoArg (\ opts -> opts { optOneline = True }))
82
      "print the ganeti command list for reaching the solution"
83
    , Option ['n']     ["nodes"]
84
      (ReqArg (\ f opts -> opts { optNodef = f, optNodeSet = True }) "FILE")
85
      "the node list FILE"
86
    , Option ['i']     ["instances"]
87
      (ReqArg (\ f opts -> opts { optInstf =  f, optInstSet = True }) "FILE")
88
      "the instance list FILE"
89
    , Option ['m']     ["master"]
90
      (ReqArg (\ m opts -> opts { optMaster = m }) "ADDRESS")
91
      "collect data via RAPI at the given ADDRESS"
92
    , Option ['l']     ["max-length"]
93
      (ReqArg (\ i opts -> opts { optMaxLength =  (read i)::Int }) "N")
94
      "cap the solution at this many moves (useful for very unbalanced \
95
      \clusters)"
96
    , Option ['v']     ["verbose"]
97
      (NoArg (\ opts -> opts { optVerbose = (optVerbose opts) + 1 }))
98
      "increase the verbosity level"
99
    , Option ['q']     ["quiet"]
100
      (NoArg (\ opts -> opts { optVerbose = (optVerbose opts) - 1 }))
101
      "decrease the verbosity level"
102
    , Option ['O']     ["offline"]
103
      (ReqArg (\ n opts -> opts { optOffline = n:optOffline opts }) "NODE")
104
      " set node as offline"
105
    , Option ['e']     ["min-score"]
106
      (ReqArg (\ e opts -> opts { optMinScore = read e }) "EPSILON")
107
      " mininum score to aim for"
108
    , Option ['V']     ["version"]
109
      (NoArg (\ opts -> opts { optShowVer = True}))
110
      "show the version of the program"
111
    , Option ['h']     ["help"]
112
      (NoArg (\ opts -> opts { optShowHelp = True}))
113
      "show help"
114
    ]
115

    
116
-- | Try to allocate an instance on the cluster
117
tryAlloc :: NodeList
118
         -> InstanceList
119
         -> Instance.Instance
120
         -> Int
121
         -> Result (String, [Node.Node])
122
tryAlloc nl il xi _ = Bad "alloc not implemented"
123

    
124
-- | Try to allocate an instance on the cluster
125
tryReloc :: NodeList
126
         -> InstanceList
127
         -> Int
128
         -> Int
129
         -> [Int]
130
         -> Result (String, [Node.Node])
131
tryReloc nl il xid 1 ex_idx =
132
    let all_nodes = Container.elems nl
133
        inst = Container.find xid il
134
        valid_nodes = filter (not . flip elem ex_idx . idx) all_nodes
135
        valid_idxes = map Node.idx valid_nodes
136
        nl' = Container.map (\n -> if elem (Node.idx n) ex_idx then
137
                                       Node.setOffline n True
138
                                   else n) nl
139
        sols1 = map (\x -> let (mnl, _, _, _) =
140
                                    Cluster.applyMove nl' inst
141
                                               (Cluster.ReplaceSecondary x)
142
                            in (mnl, x)
143
                     ) valid_idxes
144
        sols2 = filter (isJust . fst) sols1
145
    in if null sols1 then
146
           Bad "No nodes onto which to relocate at all"
147
       else if null sols2 then
148
                Bad "No valid solutions"
149
            else
150
                let sols3 = map (\(x, y) ->
151
                                      (Cluster.compCV $ fromJust x,
152
                                                  (fromJust x, y)))
153
                             sols2
154
                    sols4 = sortBy (compare `on` fst) sols3
155
                    (best, (final_nl, winner)) = head sols4
156
                    (worst, (_, loser)) = last sols4
157
                    wnode = Container.find winner final_nl
158
                    lnode = Container.find loser nl
159
                    info = printf "Valid results: %d, best score: %.8f \
160
                                  \(node %s), worst score: %.8f (node %s)"
161
                                  (length sols3) best (Node.name wnode)
162
                                  worst (Node.name lnode)
163
                in Ok (info, [wnode])
164

    
165
tryReloc _ _ _ reqn _  = Bad $ "Unsupported number of relocation \
166
                               \destinations required (" ++ (show reqn) ++
167
                                                 "), only one supported"
168

    
169
-- | Main function.
170
main :: IO ()
171
main = do
172
  cmd_args <- System.getArgs
173
  (opts, args) <- CLI.parseOpts cmd_args "hail" options
174
                  defaultOptions
175

    
176
  when (null args) $ do
177
         hPutStrLn stderr "Error: this program needs an input file."
178
         exitWith $ ExitFailure 1
179

    
180
  let input_file = head args
181
  input_data <- readFile input_file
182

    
183
  request <- case (parseData input_data) of
184
               Bad err -> do
185
                 putStrLn $ "Error: " ++ err
186
                 exitWith $ ExitFailure 1
187
               Ok rq -> return rq
188

    
189
  let Request rqtype nl il csf = request
190
      new_nodes = case rqtype of
191
                    Allocate xi reqn -> tryAlloc nl il xi reqn
192
                    Relocate idx reqn exnodes ->
193
                        tryReloc nl il idx reqn exnodes
194
  let (ok, info, rn) = case new_nodes of
195
               Ok (info, sn) -> (True, "Request successful: " ++ info,
196
                                     map name sn)
197
               Bad s -> (False, "Request failed: " ++ s, [])
198
      resp = formatResponse ok info rn
199
  putStrLn resp