Revision 78694255

b/Ganeti/HTools/CLI.hs
76 76
    -- | Whether to be less verbose.
77 77
    silent     :: a -> Bool
78 78

  
79
-- | Usage info
80
usageHelp :: (CLIOptions a) => String -> [OptDescr (a -> a)] -> String
81
usageHelp progname options =
82
    usageInfo (printf "%s %s\nUsage: %s [OPTION...]"
83
               progname Version.version progname) options
84

  
79 85
-- | Command line parser, using the 'options' structure.
80 86
parseOpts :: (CLIOptions b) =>
81 87
             [String]            -- ^ The command line arguments
......
90 96
          do
91 97
            let resu@(po, _) = (foldl (flip id) defaultOptions o, n)
92 98
            when (showHelp po) $ do
93
              putStr $ usageInfo header options
99
              putStr $ usageHelp progname options
94 100
              exitWith ExitSuccess
95 101
            when (showVersion po) $ do
96 102
              printf "%s %s\ncompiled with %s %s\nrunning on %s %s\n"
......
100 106
              exitWith ExitSuccess
101 107
            return resu
102 108
      (_, _, errs) ->
103
          ioError (userError (concat errs ++ usageInfo header options))
104
      where header = printf "%s %s\nUsage: %s [OPTION...]"
105
                     progname Version.version progname
109
          ioError (userError (concat errs ++ usageHelp progname options))
106 110

  
107 111
-- | Parse the environment and return the node\/instance names.
108 112
--
b/Ganeti/HTools/Cluster.hs
30 30
    (
31 31
     -- * Types
32 32
      Placement
33
    , AllocSolution
33 34
    , Solution(..)
34 35
    , Table(..)
35 36
    , Removal
......
78 79
-- | The description of an instance placement.
79 80
type Placement = (Idx, Ndx, Ndx, Score)
80 81

  
82
-- | Allocation/relocation solution.
83
type AllocSolution = [(Maybe Node.List, Instance.Instance, [Node.Node])]
84

  
81 85
-- | A cluster solution described as the solution delta and the list
82 86
-- of placements.
83 87
data Solution = Solution Int [Placement]
......
158 162
        mem_cv = varianceCoeff mem_l
159 163
        dsk_cv = varianceCoeff dsk_l
160 164
        n1_l = length $ filter Node.failN1 nodes
161
        n1_score = (fromIntegral n1_l) / (fromIntegral $ length nodes)
165
        n1_score = ((fromIntegral n1_l) /
166
                    (fromIntegral $ length nodes))::Double
162 167
        res_l = map Node.p_rem nodes
163 168
        res_cv = varianceCoeff res_l
164 169
        offline_inst = sum . map (\n -> (length . Node.plist $ n) +
165 170
                                        (length . Node.slist $ n)) $ offline
166 171
        online_inst = sum . map (\n -> (length . Node.plist $ n) +
167 172
                                       (length . Node.slist $ n)) $ nodes
168
        off_score = (fromIntegral offline_inst) /
169
                    (fromIntegral $ online_inst + offline_inst)
173
        off_score = ((fromIntegral offline_inst) /
174
                     (fromIntegral $ online_inst + offline_inst))::Double
170 175
        cpu_l = map Node.p_cpu nodes
171 176
        cpu_cv = varianceCoeff cpu_l
172 177
    in (mem_cv, dsk_cv, n1_score, res_cv, off_score, cpu_cv)
......
631 636
         -> Instance.List     -- ^ The instance list
632 637
         -> Instance.Instance -- ^ The instance to allocate
633 638
         -> Int               -- ^ Required number of nodes
634
         -> m [(Maybe Node.List, Instance.Instance, [Node.Node])]
635
                              -- ^ Possible solution list
639
         -> m AllocSolution   -- ^ Possible solution list
636 640
tryAlloc nl _ inst 2 =
637 641
    let all_nodes = getOnline nl
638 642
        all_pairs = liftM2 (,) all_nodes all_nodes
......
655 659

  
656 660
-- | Try to allocate an instance on the cluster.
657 661
tryReloc :: (Monad m) =>
658
            Node.List     -- ^ The node list
659
         -> Instance.List -- ^ The instance list
660
         -> Idx           -- ^ The index of the instance to move
661
         -> Int           -- ^ The numver of nodes required
662
         -> [Ndx]         -- ^ Nodes which should not be used
663
         -> m [(Maybe Node.List, Instance.Instance, [Node.Node])]
664
                          -- ^ Solution list
662
            Node.List       -- ^ The node list
663
         -> Instance.List   -- ^ The instance list
664
         -> Idx             -- ^ The index of the instance to move
665
         -> Int             -- ^ The numver of nodes required
666
         -> [Ndx]           -- ^ Nodes which should not be used
667
         -> m AllocSolution -- ^ Solution list
665 668
tryReloc nl il xid 1 ex_idx =
666 669
    let all_nodes = getOnline nl
667 670
        inst = Container.find xid il
......
782 785
    let snl = sortBy (compare `on` Node.idx) (Container.elems nl)
783 786
        m_name = maximum . map (length . Node.name) $ snl
784 787
        helper = Node.list m_name
785
        header = printf
786
                 "%2s %-*s %5s %5s %5s %5s %5s %5s %5s %5s %4s %4s \
787
                 \%3s %3s %6s %6s %5s"
788
                 " F" m_name "Name"
789
                 "t_mem" "n_mem" "i_mem" "x_mem" "f_mem" "r_mem"
790
                 "t_dsk" "f_dsk" "pcpu" "vcpu"
791
                 "pri" "sec" "p_fmem" "p_fdsk" "r_cpu"
788
        header = (printf
789
                  "%2s %-*s %5s %5s %5s %5s %5s %5s %5s %5s %4s %4s \
790
                  \%3s %3s %6s %6s %5s"
791
                  " F" m_name "Name"
792
                  "t_mem" "n_mem" "i_mem" "x_mem" "f_mem" "r_mem"
793
                  "t_dsk" "f_dsk" "pcpu" "vcpu"
794
                  "pri" "sec" "p_fmem" "p_fdsk" "r_cpu")::String
792 795
    in unlines $ (header:map helper snl)
793 796

  
794 797
-- | Shows statistics for a given node list.
b/Ganeti/HTools/Loader.hs
36 36
    , Request(..)
37 37
    ) where
38 38

  
39
import Data.Function (on)
39 40
import Data.List
40 41
import Data.Maybe (fromJust)
41 42
import Text.Printf (printf)
......
88 89
    unzip . map (\ (idx, (k, v)) -> ((k, idx), (idx, setIdx v idx)))
89 90
          . zip [0..]
90 91

  
92
-- | Assoc element comparator
93
assocEqual :: (Eq a) => (a, b) -> (a, b) -> Bool
94
assocEqual = (==) `on` fst
95

  
91 96
-- | For each instance, add its index to its primary and secondary nodes.
92 97
fixNodes :: [(Ndx, Node.Node)]
93 98
         -> [(Idx, Instance.Instance)]
......
95 100
fixNodes nl il =
96 101
    foldl' (\accu (idx, inst) ->
97 102
                let
98
                    assocEqual = (\ (i, _) (j, _) -> i == j)
99 103
                    pdx = Instance.pnode inst
100 104
                    sdx = Instance.snode inst
101 105
                    pold = fromJust $ lookup pdx accu
......
169 173
                             - (nodeIdsk node il)
170 174
                 newn = Node.setFmem (Node.setXmem node delta_mem)
171 175
                        (Node.f_mem node - adj_mem)
172
                 umsg1 = if delta_mem > 512 || delta_dsk > 1024
173
                         then [printf "node %s is missing %d MB ram \
174
                                     \and %d GB disk"
175
                                     nname delta_mem (delta_dsk `div` 1024)]
176
                         else []
176
                 umsg1 = (if delta_mem > 512 || delta_dsk > 1024
177
                          then [printf "node %s is missing %d MB ram \
178
                                       \and %d GB disk"
179
                                       nname delta_mem (delta_dsk `div` 1024)]
180
                          else [])::[String]
177 181
             in (msgs ++ umsg1, newn)
178 182
        ) [] nl
179 183

  
b/Ganeti/HTools/Text.hs
37 37
import qualified Ganeti.HTools.Node as Node
38 38
import qualified Ganeti.HTools.Instance as Instance
39 39

  
40
-- | Parse results from readsPrec
41
parseChoices :: (Monad m, Read a) => String -> String -> [(a, String)] -> m a
42
parseChoices _ _ ((v, ""):[]) = return v
43
parseChoices name s ((_, e):[]) =
44
    fail $ name ++ ": leftover characters when parsing '"
45
           ++ s ++ "': '" ++ e ++ "'"
46
parseChoices name s _ = fail $ name ++ ": cannot parse string '" ++ s ++ "'"
47

  
40 48
-- | Safe 'read' function returning data encapsulated in a Result.
41 49
tryRead :: (Monad m, Read a) => String -> String -> m a
42
tryRead name s =
43
    let sols = readsPrec 0 s
44
    in case sols of
45
         (v, ""):[] -> return v
46
         (_, e):[] -> fail $ name ++ ": leftover characters when parsing '"
47
                      ++ s ++ "': '" ++ e ++ "'"
48
         _ -> fail $ name ++ ": cannot parse string '" ++ s ++ "'"
50
tryRead name s = parseChoices name s $ readsPrec 0 s
49 51

  
50 52
-- | Load a node from a field list.
51 53
loadNode :: (Monad m) => [String] -> m (String, Node.Node)
b/Ganeti/HTools/Utils.hs
85 85
meanValue :: Floating a => [a] -> a
86 86
meanValue lst = (sum lst) / (fromIntegral $ length lst)
87 87

  
88
-- | Squaring function
89
square :: (Num a) => a -> a
90
square = (^ 2)
91

  
88 92
-- | Standard deviation.
89 93
stdDev :: Floating a => [a] -> a
90 94
stdDev lst =
91 95
    let mv = meanValue lst
92
        square = (^ (2::Int)) -- silences "defaulting the constraint..."
93 96
        av = sum $ map square $ map (\e -> e - mv) lst
94 97
        bv = sqrt (av / (fromIntegral $ length lst))
95 98
    in bv
b/hail.hs
92 92
        sols'' = sortBy (compare `on` fst) sols'
93 93
        (best, w) = head sols''
94 94
        (worst, l) = last sols''
95
        info = printf "Valid results: %d, best score: %.8f for node(s) %s, \
96
                      \worst score: %.8f for node(s) %s" (length sols'')
97
                      best (intercalate "/" . map Node.name $ w)
98
                      worst (intercalate "/" . map Node.name $ l)
95
        info = (printf "Valid results: %d, best score: %.8f for node(s) %s, \
96
                       \worst score: %.8f for node(s) %s" (length sols'')
97
                       best (intercalate "/" . map Node.name $ w)
98
                       worst (intercalate "/" . map Node.name $ l))::String
99 99
    in return (info, w)
100 100

  
101
-- | Process a request and return new node lists
102
processRequest ::
103
                  Request
104
               -> Result [(Maybe Node.List, Instance.Instance, [Node.Node])]
105
processRequest request =
106
  let Request rqtype nl il _ = request
107
  in case rqtype of
108
       Allocate xi reqn -> Cluster.tryAlloc nl il xi reqn
109
       Relocate idx reqn exnodes -> Cluster.tryReloc nl il idx reqn exnodes
110

  
101 111
-- | Main function.
102 112
main :: IO ()
103 113
main = do
......
117 127
                 exitWith $ ExitFailure 1
118 128
               Ok rq -> return rq
119 129

  
120
  let Request rqtype nl il csf = request
121
      new_nodes = case rqtype of
122
                    Allocate xi reqn -> Cluster.tryAlloc nl il xi reqn
123
                    Relocate idx reqn exnodes ->
124
                        Cluster.tryReloc nl il idx reqn exnodes
125
  let sols = new_nodes >>= filterFails >>= processResults
130
  let Request _ _ _ csf = request
131
      sols = processRequest request >>= filterFails >>= processResults
126 132
  let (ok, info, rn) = case sols of
127 133
               Ok (info, sn) -> (True, "Request successful: " ++ info,
128 134
                                     map ((++ csf) . Node.name) sn)
b/hbal.hs
272 272
                         nmlen imlen [] oneline min_cv
273 273
  let (Cluster.Table fin_nl _ fin_cv fin_plc) = fin_tbl
274 274
      ord_plc = reverse fin_plc
275
      sol_msg = if null fin_plc
276
                then printf "No solution found\n"
277
                else (if verbose > 2
278
                      then printf "Final coefficients:   overall %.8f, %s\n"
279
                           fin_cv (Cluster.printStats fin_nl)
280
                      else printf "Cluster score improved from %.8f to %.8f\n"
281
                           ini_cv fin_cv
282
                     )
275
      sol_msg = (if null fin_plc
276
                 then printf "No solution found\n"
277
                 else (if verbose > 2
278
                       then printf "Final coefficients:   overall %.8f, %s\n"
279
                            fin_cv (Cluster.printStats fin_nl)
280
                       else printf "Cluster score improved from %.8f to %.8f\n"
281
                            ini_cv fin_cv
282
                      ))::String
283 283

  
284 284
  unless oneline $ putStr sol_msg
285 285

  
b/hscan.hs
93 93
      "show help"
94 94
    ]
95 95

  
96
-- | Serialize a single node
97
serializeNode :: String -> Node.Node -> String
98
serializeNode csf node =
99
    let name = Node.name node ++ csf
100
        t_mem = (truncate $ Node.t_mem node)::Int
101
        t_dsk = (truncate $ Node.t_dsk node)::Int
102
    in
103
      printf "%s|%d|%d|%d|%d|%d|%c" name
104
             t_mem (Node.n_mem node) (Node.f_mem node)
105
             t_dsk (Node.f_dsk node)
106
             (if Node.offline node then 'Y' else 'N')
107

  
96 108
-- | Generate node file data from node objects
97
serializeNodes :: Node.List -> String -> String
98
serializeNodes nl csf =
99
    let nodes = Container.elems nl
100
        nlines = map
101
                 (\node ->
102
                      let name = Node.name node ++ csf
103
                          t_mem = (truncate $ Node.t_mem node)::Int
104
                          t_dsk = (truncate $ Node.t_dsk node)::Int
105
                      in
106
                        printf "%s|%d|%d|%d|%d|%d|%c" name
107
                                   t_mem (Node.n_mem node) (Node.f_mem node)
108
                                   t_dsk (Node.f_dsk node)
109
                                   (if Node.offline node then 'Y' else 'N')
110
                 )
111
                 nodes
112
    in unlines nlines
109
serializeNodes :: String -> Node.List -> String
110
serializeNodes csf =
111
    unlines . map (serializeNode csf) . Container.elems
112

  
113
-- | Serialize a single instance
114
serializeInstance :: String -> Node.List -> Instance.Instance -> String
115
serializeInstance csf nl inst =
116
    let
117
        iname = Instance.name inst ++ csf
118
        pnode = Container.nameOf nl $ Instance.pnode inst
119
        snode = Container.nameOf nl $ Instance.snode inst
120
    in
121
      printf "%s|%d|%d|%s|%s|%s"
122
             iname (Instance.mem inst) (Instance.dsk inst)
123
             (Instance.run_st inst)
124
             pnode snode
113 125

  
114 126
-- | Generate instance file data from instance objects
115
serializeInstances :: Node.List -> Instance.List
116
                   -> String -> String
117
serializeInstances nl il csf =
118
    let instances = Container.elems il
119
        nlines = map
120
                 (\inst ->
121
                      let
122
                          iname = Instance.name inst ++ csf
123
                          pnode = Container.nameOf nl $ Instance.pnode inst
124
                          snode = Container.nameOf nl $ Instance.snode inst
125
                      in
126
                        printf "%s|%d|%d|%s|%s|%s"
127
                               iname (Instance.mem inst) (Instance.dsk inst)
128
                               (Instance.run_st inst)
129
                               pnode snode
130
                 )
131
                 instances
132
    in unlines nlines
127
serializeInstances :: String -> Node.List -> Instance.List -> String
128
serializeInstances csf nl =
129
    unlines . map (serializeInstance csf nl) . Container.elems
133 130

  
134 131
-- | Return a one-line summary of cluster state
135 132
printCluster :: Node.List -> Instance.List
......
139 136
        ccv = Cluster.compCV nl
140 137
        nodes = Container.elems nl
141 138
        insts = Container.elems il
142
        t_ram = truncate . sum . map Node.t_mem $ nodes
143
        t_dsk = truncate . sum . map Node.t_dsk $ nodes
139
        t_ram = sum . map Node.t_mem $ nodes
140
        t_dsk = sum . map Node.t_dsk $ nodes
144 141
        f_ram = sum . map Node.f_mem $ nodes
145 142
        f_dsk = sum . map Node.f_dsk $ nodes
146 143
    in
147
      printf "%5d %5d %5d %5d %6d %6d %6d %6d %.8f"
144
      printf "%5d %5d %5d %5d %6.0f %6d %6.0f %6d %.8f"
148 145
                 (length nodes) (length insts)
149 146
                 (length bad_nodes) (length bad_instances)
150
                 (t_ram::Integer) f_ram
151
                 ((t_dsk::Integer) `div` 1024) (f_dsk `div` 1024)
147
                 t_ram f_ram
148
                 (t_dsk / 1024) (f_dsk `div` 1024)
152 149
                 ccv
153 150

  
154 151

  
......
187 184
                   putStrLn $ printCluster fix_nl il
188 185
                   when (optShowNodes opts) $ do
189 186
                           putStr $ Cluster.printNodes fix_nl
190
                   let ndata = serializeNodes nl csf
191
                       idata = serializeInstances nl il csf
187
                   let ndata = serializeNodes csf nl
188
                       idata = serializeInstances csf nl il
192 189
                       oname = odir </> (fixSlash name)
193 190
                   writeFile (oname <.> "nodes") ndata
194 191
                   writeFile (oname <.> "instances") idata)
b/hspace.hs
137 137
      "show help"
138 138
    ]
139 139

  
140
filterFails :: (Monad m) => [(Maybe Node.List, Instance.Instance, [Node.Node])]
141
            -> m [(Node.List, Instance.Instance, [Node.Node])]
140
filterFails :: Cluster.AllocSolution
141
            -> Maybe [(Node.List, Instance.Instance, [Node.Node])]
142 142
filterFails sols =
143
    if null sols then fail "No nodes onto which to allocate at all"
143
    if null sols then Nothing -- No nodes onto which to allocate at all
144 144
    else let sols' = filter (isJust . fst3) sols
145 145
         in if null sols' then
146
                fail "No valid allocation solutions"
146
                Nothing -- No valid allocation solutions
147 147
            else
148 148
                return $ map (\(x, y, z) -> (fromJust x, y, z)) sols'
149 149

  
......
162 162
             -> (Node.List, [Instance.Instance])
163 163
iterateDepth nl il newinst nreq ixes =
164 164
      let depth = length ixes
165
          newname = printf "new-%d" depth
165
          newname = (printf "new-%d" depth)::String
166 166
          newidx = (length $ Container.elems il) + depth
167 167
          newi2 = Instance.setIdx (Instance.setName newinst newname) newidx
168
          sols = Cluster.tryAlloc nl il newi2 nreq
168
          sols = (Cluster.tryAlloc nl il newi2 nreq)::
169
                 Maybe Cluster.AllocSolution
169 170
          orig = (nl, ixes)
170 171
      in
171 172
        if isNothing sols then orig

Also available in: Unified diff