root / src / Ganeti / HTools / Program / Hsqueeze.hs @ bbc6620d
History | View | Annotate | Download (9.5 kB)
1 | 5f6e9cb9 | Klaus Aehlig | {-| Node freeing scheduler |
---|---|---|---|
2 | 5f6e9cb9 | Klaus Aehlig | |
3 | 5f6e9cb9 | Klaus Aehlig | -} |
4 | 5f6e9cb9 | Klaus Aehlig | |
5 | 5f6e9cb9 | Klaus Aehlig | {- |
6 | 5f6e9cb9 | Klaus Aehlig | |
7 | 5f6e9cb9 | Klaus Aehlig | Copyright (C) 2013 Google Inc. |
8 | 5f6e9cb9 | Klaus Aehlig | |
9 | 5f6e9cb9 | Klaus Aehlig | This program is free software; you can redistribute it and/or modify |
10 | 5f6e9cb9 | Klaus Aehlig | it under the terms of the GNU General Public License as published by |
11 | 5f6e9cb9 | Klaus Aehlig | the Free Software Foundation; either version 2 of the License, or |
12 | 5f6e9cb9 | Klaus Aehlig | (at your option) any later version. |
13 | 5f6e9cb9 | Klaus Aehlig | |
14 | 5f6e9cb9 | Klaus Aehlig | This program is distributed in the hope that it will be useful, but |
15 | 5f6e9cb9 | Klaus Aehlig | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | 5f6e9cb9 | Klaus Aehlig | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | 5f6e9cb9 | Klaus Aehlig | General Public License for more details. |
18 | 5f6e9cb9 | Klaus Aehlig | |
19 | 5f6e9cb9 | Klaus Aehlig | You should have received a copy of the GNU General Public License |
20 | 5f6e9cb9 | Klaus Aehlig | along with this program; if not, write to the Free Software |
21 | 5f6e9cb9 | Klaus Aehlig | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
22 | 5f6e9cb9 | Klaus Aehlig | 02110-1301, USA. |
23 | 5f6e9cb9 | Klaus Aehlig | |
24 | 5f6e9cb9 | Klaus Aehlig | -} |
25 | 5f6e9cb9 | Klaus Aehlig | |
26 | 5f6e9cb9 | Klaus Aehlig | module Ganeti.HTools.Program.Hsqueeze |
27 | 5f6e9cb9 | Klaus Aehlig | (main |
28 | 5f6e9cb9 | Klaus Aehlig | , options |
29 | 5f6e9cb9 | Klaus Aehlig | , arguments |
30 | 5f6e9cb9 | Klaus Aehlig | ) where |
31 | 5f6e9cb9 | Klaus Aehlig | |
32 | 5f6e9cb9 | Klaus Aehlig | import Control.Applicative |
33 | 5f6e9cb9 | Klaus Aehlig | import Control.Monad |
34 | 5f6e9cb9 | Klaus Aehlig | import Data.Function |
35 | 5f6e9cb9 | Klaus Aehlig | import Data.List |
36 | 5f6e9cb9 | Klaus Aehlig | import Data.Maybe |
37 | 5f6e9cb9 | Klaus Aehlig | import qualified Data.IntMap as IntMap |
38 | 5f6e9cb9 | Klaus Aehlig | |
39 | 5f6e9cb9 | Klaus Aehlig | import Ganeti.BasicTypes |
40 | 5f6e9cb9 | Klaus Aehlig | import Ganeti.Common |
41 | 5f6e9cb9 | Klaus Aehlig | import Ganeti.HTools.CLI |
42 | 5f6e9cb9 | Klaus Aehlig | import qualified Ganeti.HTools.Container as Container |
43 | 5f6e9cb9 | Klaus Aehlig | import qualified Ganeti.HTools.Cluster as Cluster |
44 | 5f6e9cb9 | Klaus Aehlig | import Ganeti.HTools.ExtLoader |
45 | 5f6e9cb9 | Klaus Aehlig | import qualified Ganeti.HTools.Instance as Instance |
46 | 5f6e9cb9 | Klaus Aehlig | import Ganeti.HTools.Loader |
47 | 5f6e9cb9 | Klaus Aehlig | import qualified Ganeti.HTools.Node as Node |
48 | 5f6e9cb9 | Klaus Aehlig | import Ganeti.HTools.Types |
49 | 5f6e9cb9 | Klaus Aehlig | import Ganeti.Utils |
50 | 5f6e9cb9 | Klaus Aehlig | |
51 | 5f6e9cb9 | Klaus Aehlig | -- | Options list and functions. |
52 | 5f6e9cb9 | Klaus Aehlig | options :: IO [OptType] |
53 | 5f6e9cb9 | Klaus Aehlig | options = do |
54 | 5f6e9cb9 | Klaus Aehlig | luxi <- oLuxiSocket |
55 | 5f6e9cb9 | Klaus Aehlig | return |
56 | 5f6e9cb9 | Klaus Aehlig | [ luxi |
57 | 5f6e9cb9 | Klaus Aehlig | , oDataFile |
58 | c407510c | Klaus Aehlig | , oMinResources |
59 | 5f6e9cb9 | Klaus Aehlig | , oTargetResources |
60 | 5f6e9cb9 | Klaus Aehlig | , oSaveCluster |
61 | 5f6e9cb9 | Klaus Aehlig | , oVerbose |
62 | 5f6e9cb9 | Klaus Aehlig | , oNoHeaders |
63 | 5f6e9cb9 | Klaus Aehlig | ] |
64 | 5f6e9cb9 | Klaus Aehlig | |
65 | 5f6e9cb9 | Klaus Aehlig | -- | The list of arguments supported by the program. |
66 | 5f6e9cb9 | Klaus Aehlig | arguments :: [ArgCompletion] |
67 | 5f6e9cb9 | Klaus Aehlig | arguments = [] |
68 | 5f6e9cb9 | Klaus Aehlig | |
69 | c407510c | Klaus Aehlig | -- | The tag-prefix indicating that hsqueeze should consider a node |
70 | c407510c | Klaus Aehlig | -- as being standby. |
71 | c407510c | Klaus Aehlig | standbyPrefix :: String |
72 | c407510c | Klaus Aehlig | standbyPrefix = "htools:standby:" |
73 | c407510c | Klaus Aehlig | |
74 | c407510c | Klaus Aehlig | -- | Predicate of having a standby tag. |
75 | c407510c | Klaus Aehlig | hasStandbyTag :: Node.Node -> Bool |
76 | c407510c | Klaus Aehlig | hasStandbyTag = any (standbyPrefix `isPrefixOf`) . Node.nTags |
77 | c407510c | Klaus Aehlig | |
78 | 5f6e9cb9 | Klaus Aehlig | -- | Within a cluster configuration, decide if the node hosts only |
79 | 5f6e9cb9 | Klaus Aehlig | -- externally-mirrored instances. |
80 | 5f6e9cb9 | Klaus Aehlig | onlyExternal :: (Node.List, Instance.List) -> Node.Node -> Bool |
81 | 5f6e9cb9 | Klaus Aehlig | onlyExternal (_, il) nd = |
82 | 5f6e9cb9 | Klaus Aehlig | not |
83 | 5f6e9cb9 | Klaus Aehlig | . any (Instance.usesLocalStorage . flip Container.find il) |
84 | 5f6e9cb9 | Klaus Aehlig | $ Node.pList nd |
85 | 5f6e9cb9 | Klaus Aehlig | |
86 | 5f6e9cb9 | Klaus Aehlig | -- | Predicate whether, in a configuration, all running instances are on |
87 | 5f6e9cb9 | Klaus Aehlig | -- online nodes. |
88 | 5f6e9cb9 | Klaus Aehlig | allInstancesOnOnlineNodes :: (Node.List, Instance.List) -> Bool |
89 | 5f6e9cb9 | Klaus Aehlig | allInstancesOnOnlineNodes (nl, il) = |
90 | 5f6e9cb9 | Klaus Aehlig | all (not . Node.offline . flip Container.find nl . Instance.pNode) |
91 | 5f6e9cb9 | Klaus Aehlig | . IntMap.elems |
92 | 5f6e9cb9 | Klaus Aehlig | $ il |
93 | 5f6e9cb9 | Klaus Aehlig | |
94 | 5f6e9cb9 | Klaus Aehlig | -- | Predicate whether, in a configuration, each node has enough resources |
95 | 5f6e9cb9 | Klaus Aehlig | -- to additionally host the given instance. |
96 | 5f6e9cb9 | Klaus Aehlig | allNodesCapacityFor :: Instance.Instance -> (Node.List, Instance.List) -> Bool |
97 | 5f6e9cb9 | Klaus Aehlig | allNodesCapacityFor inst (nl, _) = |
98 | 5f6e9cb9 | Klaus Aehlig | all (isOk . flip Node.addPri inst) . IntMap.elems $ nl |
99 | 5f6e9cb9 | Klaus Aehlig | |
100 | 5f6e9cb9 | Klaus Aehlig | -- | Balance a configuration, possible for 0 steps, till no further improvement |
101 | 5f6e9cb9 | Klaus Aehlig | -- is possible. |
102 | bbc6620d | Klaus Aehlig | balance :: (Node.List, Instance.List) |
103 | bbc6620d | Klaus Aehlig | -> ((Node.List, Instance.List), [MoveJob]) |
104 | 5f6e9cb9 | Klaus Aehlig | balance (nl, il) = |
105 | 5f6e9cb9 | Klaus Aehlig | let ini_cv = Cluster.compCV nl |
106 | 5f6e9cb9 | Klaus Aehlig | ini_tbl = Cluster.Table nl il ini_cv [] |
107 | 5f6e9cb9 | Klaus Aehlig | balanceStep tbl = Cluster.tryBalance tbl True True False 0.0 0.0 |
108 | bbc6620d | Klaus Aehlig | bTables = map fromJust . takeWhile isJust |
109 | bbc6620d | Klaus Aehlig | $ iterate (>>= balanceStep) (Just ini_tbl) |
110 | bbc6620d | Klaus Aehlig | (Cluster.Table nl' il' _ _) = last bTables |
111 | bbc6620d | Klaus Aehlig | moves = zip bTables (drop 1 bTables) >>= Cluster.getMoves |
112 | bbc6620d | Klaus Aehlig | in ((nl', il'), reverse moves) |
113 | 5f6e9cb9 | Klaus Aehlig | |
114 | c407510c | Klaus Aehlig | -- | In a configuration, mark a node as online or offline. |
115 | c407510c | Klaus Aehlig | onlineOfflineNode :: Bool -> (Node.List, Instance.List) -> Ndx -> |
116 | c407510c | Klaus Aehlig | (Node.List, Instance.List) |
117 | c407510c | Klaus Aehlig | onlineOfflineNode offline (nl, il) ndx = |
118 | 5f6e9cb9 | Klaus Aehlig | let nd = Container.find ndx nl |
119 | c407510c | Klaus Aehlig | nd' = Node.setOffline nd offline |
120 | 5f6e9cb9 | Klaus Aehlig | nl' = Container.add ndx nd' nl |
121 | 5f6e9cb9 | Klaus Aehlig | in (nl', il) |
122 | 5f6e9cb9 | Klaus Aehlig | |
123 | c407510c | Klaus Aehlig | -- | Offline or online a list nodes, and return the state after a balancing |
124 | bbc6620d | Klaus Aehlig | -- attempt together with the sequence of moves that lead there. |
125 | c407510c | Klaus Aehlig | onlineOfflineNodes :: Bool -> [Ndx] -> (Node.List, Instance.List) |
126 | bbc6620d | Klaus Aehlig | -> ((Node.List, Instance.List), [MoveJob]) |
127 | c407510c | Klaus Aehlig | onlineOfflineNodes offline ndxs conf = |
128 | c407510c | Klaus Aehlig | let conf' = foldl (onlineOfflineNode offline) conf ndxs |
129 | 5f6e9cb9 | Klaus Aehlig | in balance conf' |
130 | 5f6e9cb9 | Klaus Aehlig | |
131 | bbc6620d | Klaus Aehlig | -- | Offline a list of nodes, and return the state after balancing with |
132 | bbc6620d | Klaus Aehlig | -- the sequence of moves that lead there. |
133 | c407510c | Klaus Aehlig | offlineNodes :: [Ndx] -> (Node.List, Instance.List) |
134 | bbc6620d | Klaus Aehlig | -> ((Node.List, Instance.List), [MoveJob]) |
135 | c407510c | Klaus Aehlig | offlineNodes = onlineOfflineNodes True |
136 | c407510c | Klaus Aehlig | |
137 | bbc6620d | Klaus Aehlig | -- | Online a list of nodes, and return the state after balancing with |
138 | bbc6620d | Klaus Aehlig | -- the sequence of moves that lead there. |
139 | c407510c | Klaus Aehlig | onlineNodes :: [Ndx] -> (Node.List, Instance.List) |
140 | bbc6620d | Klaus Aehlig | -> ((Node.List, Instance.List), [MoveJob]) |
141 | c407510c | Klaus Aehlig | onlineNodes = onlineOfflineNodes False |
142 | c407510c | Klaus Aehlig | |
143 | c407510c | Klaus Aehlig | -- | Predicate on whether a list of nodes can be offlined or onlined |
144 | c407510c | Klaus Aehlig | -- simultaneously in a given configuration, while still leaving enough |
145 | c407510c | Klaus Aehlig | -- capacity on every node for the given instance. |
146 | c407510c | Klaus Aehlig | canOnlineOffline :: Bool -> Instance.Instance -> (Node.List, Instance.List) |
147 | c407510c | Klaus Aehlig | -> [Node.Node] ->Bool |
148 | c407510c | Klaus Aehlig | canOnlineOffline offline inst conf nds = |
149 | bbc6620d | Klaus Aehlig | let conf' = fst $ onlineOfflineNodes offline (map Node.idx nds) conf |
150 | 5f6e9cb9 | Klaus Aehlig | in allInstancesOnOnlineNodes conf' && allNodesCapacityFor inst conf' |
151 | 5f6e9cb9 | Klaus Aehlig | |
152 | c407510c | Klaus Aehlig | -- | Predicate on whether a list of nodes can be offlined simultaneously. |
153 | c407510c | Klaus Aehlig | canOffline :: Instance.Instance -> (Node.List, Instance.List) -> |
154 | c407510c | Klaus Aehlig | [Node.Node] -> Bool |
155 | c407510c | Klaus Aehlig | canOffline = canOnlineOffline True |
156 | c407510c | Klaus Aehlig | |
157 | c407510c | Klaus Aehlig | -- | Predicate on whether onlining a list of nodes suffices to get enough |
158 | c407510c | Klaus Aehlig | -- free resources for given instance. |
159 | c407510c | Klaus Aehlig | sufficesOnline :: Instance.Instance -> (Node.List, Instance.List) |
160 | c407510c | Klaus Aehlig | -> [Node.Node] -> Bool |
161 | c407510c | Klaus Aehlig | sufficesOnline = canOnlineOffline False |
162 | c407510c | Klaus Aehlig | |
163 | 5f6e9cb9 | Klaus Aehlig | -- | Greedily offline the nodes, starting from the last element, and return |
164 | 5f6e9cb9 | Klaus Aehlig | -- the list of nodes that could simultaneously be offlined, while keeping |
165 | 5f6e9cb9 | Klaus Aehlig | -- the resources specified by an instance. |
166 | 5f6e9cb9 | Klaus Aehlig | greedyOfflineNodes :: Instance.Instance -> (Node.List, Instance.List) |
167 | 5f6e9cb9 | Klaus Aehlig | -> [Node.Node] -> [Node.Node] |
168 | 5f6e9cb9 | Klaus Aehlig | greedyOfflineNodes _ _ [] = [] |
169 | 5f6e9cb9 | Klaus Aehlig | greedyOfflineNodes inst conf (nd:nds) = |
170 | 5f6e9cb9 | Klaus Aehlig | let nds' = greedyOfflineNodes inst conf nds |
171 | c407510c | Klaus Aehlig | in if canOffline inst conf (nd:nds') then nd:nds' else nds' |
172 | c407510c | Klaus Aehlig | |
173 | c407510c | Klaus Aehlig | -- | Try to provide enough resources by onlining an initial segment of |
174 | c407510c | Klaus Aehlig | -- a list of nodes. Return Nothing, if even onlining all of them is not |
175 | c407510c | Klaus Aehlig | -- enough. |
176 | c407510c | Klaus Aehlig | tryOnline :: Instance.Instance -> (Node.List, Instance.List) -> [Node.Node] |
177 | c407510c | Klaus Aehlig | -> Maybe [Node.Node] |
178 | c407510c | Klaus Aehlig | tryOnline inst conf = listToMaybe . filter (sufficesOnline inst conf) . inits |
179 | 5f6e9cb9 | Klaus Aehlig | |
180 | 5f6e9cb9 | Klaus Aehlig | -- | From a specification, name, and factor create an instance that uses that |
181 | 5f6e9cb9 | Klaus Aehlig | -- factor times the specification, rounded down. |
182 | 5f6e9cb9 | Klaus Aehlig | instanceFromSpecAndFactor :: String -> Double -> ISpec -> Instance.Instance |
183 | 5f6e9cb9 | Klaus Aehlig | instanceFromSpecAndFactor name f spec = |
184 | 5f6e9cb9 | Klaus Aehlig | Instance.create name |
185 | 5f6e9cb9 | Klaus Aehlig | (floor (f * fromIntegral (iSpecMemorySize spec))) |
186 | 5f6e9cb9 | Klaus Aehlig | 0 [] |
187 | 5f6e9cb9 | Klaus Aehlig | (floor (f * fromIntegral (iSpecCpuCount spec))) |
188 | 5f6e9cb9 | Klaus Aehlig | Running [] False Node.noSecondary Node.noSecondary DTExt |
189 | 5f6e9cb9 | Klaus Aehlig | (floor (f * fromIntegral (iSpecSpindleUse spec))) |
190 | 5f6e9cb9 | Klaus Aehlig | [] |
191 | 5f6e9cb9 | Klaus Aehlig | |
192 | 5f6e9cb9 | Klaus Aehlig | -- | Main function. |
193 | 5f6e9cb9 | Klaus Aehlig | main :: Options -> [String] -> IO () |
194 | 5f6e9cb9 | Klaus Aehlig | main opts args = do |
195 | 5f6e9cb9 | Klaus Aehlig | unless (null args) $ exitErr "This program doesn't take any arguments." |
196 | 5f6e9cb9 | Klaus Aehlig | |
197 | 5f6e9cb9 | Klaus Aehlig | let verbose = optVerbose opts |
198 | 5f6e9cb9 | Klaus Aehlig | targetf = optTargetResources opts |
199 | c407510c | Klaus Aehlig | minf = optMinResources opts |
200 | 5f6e9cb9 | Klaus Aehlig | |
201 | 5f6e9cb9 | Klaus Aehlig | ini_cdata@(ClusterData _ nlf ilf _ ipol) <- loadExternalData opts |
202 | 5f6e9cb9 | Klaus Aehlig | |
203 | 5f6e9cb9 | Klaus Aehlig | maybeSaveData (optSaveCluster opts) "original" "before hsqueeze run" ini_cdata |
204 | 5f6e9cb9 | Klaus Aehlig | |
205 | c407510c | Klaus Aehlig | let nodelist = IntMap.elems nlf |
206 | c407510c | Klaus Aehlig | offlineCandidates = |
207 | 5f6e9cb9 | Klaus Aehlig | sortBy (flip compare `on` length . Node.pList) |
208 | 5f6e9cb9 | Klaus Aehlig | . filter (foldl (liftA2 (&&)) (const True) |
209 | 5f6e9cb9 | Klaus Aehlig | [ not . Node.offline |
210 | 5f6e9cb9 | Klaus Aehlig | , not . Node.isMaster |
211 | 5f6e9cb9 | Klaus Aehlig | , onlyExternal (nlf, ilf) |
212 | 5f6e9cb9 | Klaus Aehlig | ]) |
213 | c407510c | Klaus Aehlig | $ nodelist |
214 | c407510c | Klaus Aehlig | onlineCandidates = |
215 | c407510c | Klaus Aehlig | filter (liftA2 (&&) Node.offline hasStandbyTag) nodelist |
216 | 5f6e9cb9 | Klaus Aehlig | conf = (nlf, ilf) |
217 | 5f6e9cb9 | Klaus Aehlig | std = iPolicyStdSpec ipol |
218 | 5f6e9cb9 | Klaus Aehlig | targetInstance = instanceFromSpecAndFactor "targetInstance" targetf std |
219 | c407510c | Klaus Aehlig | minInstance = instanceFromSpecAndFactor "targetInstance" minf std |
220 | 5f6e9cb9 | Klaus Aehlig | toOffline = greedyOfflineNodes targetInstance conf offlineCandidates |
221 | bbc6620d | Klaus Aehlig | (fin_off_nl, fin_off_il) = |
222 | bbc6620d | Klaus Aehlig | fst $ offlineNodes (map Node.idx toOffline) conf |
223 | c407510c | Klaus Aehlig | final_off_cdata = |
224 | c407510c | Klaus Aehlig | ini_cdata { cdNodes = fin_off_nl, cdInstances = fin_off_il } |
225 | c407510c | Klaus Aehlig | toOnline = tryOnline minInstance conf onlineCandidates |
226 | c407510c | Klaus Aehlig | nodesToOnline = fromMaybe onlineCandidates toOnline |
227 | bbc6620d | Klaus Aehlig | (fin_on_nl, fin_on_il) = |
228 | bbc6620d | Klaus Aehlig | fst $ onlineNodes (map Node.idx nodesToOnline) conf |
229 | c407510c | Klaus Aehlig | final_on_cdata = |
230 | c407510c | Klaus Aehlig | ini_cdata { cdNodes = fin_on_nl, cdInstances = fin_on_il } |
231 | 5f6e9cb9 | Klaus Aehlig | |
232 | 5f6e9cb9 | Klaus Aehlig | when (verbose > 1) . putStrLn |
233 | 5f6e9cb9 | Klaus Aehlig | $ "Offline candidates: " ++ commaJoin (map Node.name offlineCandidates) |
234 | 5f6e9cb9 | Klaus Aehlig | |
235 | c407510c | Klaus Aehlig | when (verbose > 1) . putStrLn |
236 | c407510c | Klaus Aehlig | $ "Online candidates: " ++ commaJoin (map Node.name onlineCandidates) |
237 | c407510c | Klaus Aehlig | |
238 | c407510c | Klaus Aehlig | if not (allNodesCapacityFor minInstance conf) |
239 | c407510c | Klaus Aehlig | then do |
240 | c407510c | Klaus Aehlig | unless (optNoHeaders opts) $ |
241 | c407510c | Klaus Aehlig | putStrLn "'Nodes to online'" |
242 | c407510c | Klaus Aehlig | mapM_ (putStrLn . Node.name) nodesToOnline |
243 | c407510c | Klaus Aehlig | when (verbose > 1 && isNothing toOnline) . putStrLn $ |
244 | c407510c | Klaus Aehlig | "Onlining all nodes will not yield enough capacity" |
245 | c407510c | Klaus Aehlig | maybeSaveData (optSaveCluster opts) |
246 | c407510c | Klaus Aehlig | "squeezed" "after hsqueeze expansion" final_on_cdata |
247 | c407510c | Klaus Aehlig | else |
248 | c407510c | Klaus Aehlig | if null toOffline |
249 | c407510c | Klaus Aehlig | then do |
250 | c407510c | Klaus Aehlig | unless (optNoHeaders opts) $ |
251 | c407510c | Klaus Aehlig | putStrLn "'No action'" |
252 | c407510c | Klaus Aehlig | maybeSaveData (optSaveCluster opts) |
253 | c407510c | Klaus Aehlig | "squeezed" "after hsqueeze doing nothing" ini_cdata |
254 | c407510c | Klaus Aehlig | else do |
255 | c407510c | Klaus Aehlig | unless (optNoHeaders opts) $ |
256 | c407510c | Klaus Aehlig | putStrLn "'Nodes to offline'" |
257 | c407510c | Klaus Aehlig | |
258 | c407510c | Klaus Aehlig | mapM_ (putStrLn . Node.name) toOffline |
259 | c407510c | Klaus Aehlig | |
260 | c407510c | Klaus Aehlig | maybeSaveData (optSaveCluster opts) |
261 | c407510c | Klaus Aehlig | "squeezed" "after hsqueeze run" final_off_cdata |