Statistics
| Branch: | Tag: | Revision:

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