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