Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / HTools / Program / Hsqueeze.hs @ a5efec93

History | View | Annotate | Download (10.4 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 268fb0bd Klaus Aehlig
import Text.Printf (printf)
39 5f6e9cb9 Klaus Aehlig
40 5f6e9cb9 Klaus Aehlig
import Ganeti.BasicTypes
41 5f6e9cb9 Klaus Aehlig
import Ganeti.Common
42 5f6e9cb9 Klaus Aehlig
import Ganeti.HTools.CLI
43 5f6e9cb9 Klaus Aehlig
import qualified Ganeti.HTools.Container as Container
44 5f6e9cb9 Klaus Aehlig
import qualified Ganeti.HTools.Cluster as Cluster
45 5f6e9cb9 Klaus Aehlig
import Ganeti.HTools.ExtLoader
46 5f6e9cb9 Klaus Aehlig
import qualified Ganeti.HTools.Instance as Instance
47 5f6e9cb9 Klaus Aehlig
import Ganeti.HTools.Loader
48 5f6e9cb9 Klaus Aehlig
import qualified Ganeti.HTools.Node as Node
49 5f6e9cb9 Klaus Aehlig
import Ganeti.HTools.Types
50 5f6e9cb9 Klaus Aehlig
import Ganeti.Utils
51 5f6e9cb9 Klaus Aehlig
52 5f6e9cb9 Klaus Aehlig
-- | Options list and functions.
53 5f6e9cb9 Klaus Aehlig
options :: IO [OptType]
54 5f6e9cb9 Klaus Aehlig
options = do
55 5f6e9cb9 Klaus Aehlig
  luxi <- oLuxiSocket
56 5f6e9cb9 Klaus Aehlig
  return
57 5f6e9cb9 Klaus Aehlig
    [ luxi
58 5f6e9cb9 Klaus Aehlig
    , oDataFile
59 c407510c Klaus Aehlig
    , oMinResources
60 5f6e9cb9 Klaus Aehlig
    , oTargetResources
61 5f6e9cb9 Klaus Aehlig
    , oSaveCluster
62 268fb0bd Klaus Aehlig
    , oPrintCommands
63 5f6e9cb9 Klaus Aehlig
    , oVerbose
64 5f6e9cb9 Klaus Aehlig
    , oNoHeaders
65 5f6e9cb9 Klaus Aehlig
    ]
66 5f6e9cb9 Klaus Aehlig
67 5f6e9cb9 Klaus Aehlig
-- | The list of arguments supported by the program.
68 5f6e9cb9 Klaus Aehlig
arguments :: [ArgCompletion]
69 5f6e9cb9 Klaus Aehlig
arguments = []
70 5f6e9cb9 Klaus Aehlig
71 c407510c Klaus Aehlig
-- | The tag-prefix indicating that hsqueeze should consider a node
72 c407510c Klaus Aehlig
-- as being standby.
73 c407510c Klaus Aehlig
standbyPrefix :: String
74 c407510c Klaus Aehlig
standbyPrefix = "htools:standby:"
75 c407510c Klaus Aehlig
76 c407510c Klaus Aehlig
-- | Predicate of having a standby tag.
77 c407510c Klaus Aehlig
hasStandbyTag :: Node.Node -> Bool
78 c407510c Klaus Aehlig
hasStandbyTag = any (standbyPrefix `isPrefixOf`) . Node.nTags
79 c407510c Klaus Aehlig
80 5f6e9cb9 Klaus Aehlig
-- | Within a cluster configuration, decide if the node hosts only
81 5f6e9cb9 Klaus Aehlig
-- externally-mirrored instances.
82 5f6e9cb9 Klaus Aehlig
onlyExternal ::  (Node.List, Instance.List) -> Node.Node -> Bool
83 5f6e9cb9 Klaus Aehlig
onlyExternal (_, il) nd =
84 5f6e9cb9 Klaus Aehlig
  not
85 5f6e9cb9 Klaus Aehlig
  . any (Instance.usesLocalStorage . flip Container.find il)
86 5f6e9cb9 Klaus Aehlig
  $ Node.pList nd
87 5f6e9cb9 Klaus Aehlig
88 2a58a7b1 Klaus Aehlig
-- | Predicate of not being secondary node for any instance
89 2a58a7b1 Klaus Aehlig
noSecondaries :: Node.Node -> Bool
90 2a58a7b1 Klaus Aehlig
noSecondaries = null . Node.sList
91 2a58a7b1 Klaus Aehlig
92 5f6e9cb9 Klaus Aehlig
-- | Predicate whether, in a configuration, all running instances are on
93 5f6e9cb9 Klaus Aehlig
-- online nodes.
94 5f6e9cb9 Klaus Aehlig
allInstancesOnOnlineNodes :: (Node.List, Instance.List) -> Bool
95 5f6e9cb9 Klaus Aehlig
allInstancesOnOnlineNodes (nl, il) =
96 5f6e9cb9 Klaus Aehlig
 all (not . Node.offline . flip Container.find nl . Instance.pNode)
97 5f6e9cb9 Klaus Aehlig
 . IntMap.elems
98 5f6e9cb9 Klaus Aehlig
 $ il
99 5f6e9cb9 Klaus Aehlig
100 5f6e9cb9 Klaus Aehlig
-- | Predicate whether, in a configuration, each node has enough resources 
101 5f6e9cb9 Klaus Aehlig
-- to additionally host the given instance.
102 5f6e9cb9 Klaus Aehlig
allNodesCapacityFor :: Instance.Instance -> (Node.List, Instance.List) -> Bool
103 5f6e9cb9 Klaus Aehlig
allNodesCapacityFor inst (nl, _) =
104 5f6e9cb9 Klaus Aehlig
  all (isOk . flip Node.addPri inst) . IntMap.elems $ nl
105 5f6e9cb9 Klaus Aehlig
106 5f6e9cb9 Klaus Aehlig
-- | Balance a configuration, possible for 0 steps, till no further improvement
107 5f6e9cb9 Klaus Aehlig
-- is possible.
108 bbc6620d Klaus Aehlig
balance :: (Node.List, Instance.List) 
109 bbc6620d Klaus Aehlig
           -> ((Node.List, Instance.List), [MoveJob])
110 5f6e9cb9 Klaus Aehlig
balance (nl, il) =
111 5f6e9cb9 Klaus Aehlig
  let ini_cv = Cluster.compCV nl
112 5f6e9cb9 Klaus Aehlig
      ini_tbl = Cluster.Table nl il ini_cv []
113 5f6e9cb9 Klaus Aehlig
      balanceStep tbl = Cluster.tryBalance tbl True True False 0.0 0.0
114 bbc6620d Klaus Aehlig
      bTables = map fromJust . takeWhile isJust
115 bbc6620d Klaus Aehlig
                  $ iterate (>>= balanceStep) (Just ini_tbl)
116 bbc6620d Klaus Aehlig
      (Cluster.Table nl' il' _ _) = last bTables
117 bbc6620d Klaus Aehlig
      moves = zip bTables (drop 1 bTables) >>= Cluster.getMoves
118 bbc6620d Klaus Aehlig
  in ((nl', il'), reverse moves)
119 5f6e9cb9 Klaus Aehlig
120 c407510c Klaus Aehlig
-- | In a configuration, mark a node as online or offline.
121 c407510c Klaus Aehlig
onlineOfflineNode :: Bool -> (Node.List, Instance.List) -> Ndx ->
122 c407510c Klaus Aehlig
                     (Node.List, Instance.List)
123 c407510c Klaus Aehlig
onlineOfflineNode offline (nl, il) ndx =
124 5f6e9cb9 Klaus Aehlig
  let nd = Container.find ndx nl
125 c407510c Klaus Aehlig
      nd' = Node.setOffline nd offline
126 5f6e9cb9 Klaus Aehlig
      nl' = Container.add ndx nd' nl
127 5f6e9cb9 Klaus Aehlig
  in (nl', il)
128 5f6e9cb9 Klaus Aehlig
129 c407510c Klaus Aehlig
-- | Offline or online a list nodes, and return the state after a balancing
130 bbc6620d Klaus Aehlig
-- attempt together with the sequence of moves that lead there.
131 c407510c Klaus Aehlig
onlineOfflineNodes :: Bool -> [Ndx] -> (Node.List, Instance.List)
132 bbc6620d Klaus Aehlig
                      -> ((Node.List, Instance.List), [MoveJob])
133 c407510c Klaus Aehlig
onlineOfflineNodes offline ndxs conf =
134 c407510c Klaus Aehlig
  let conf' = foldl (onlineOfflineNode offline) conf ndxs
135 5f6e9cb9 Klaus Aehlig
  in balance conf'
136 5f6e9cb9 Klaus Aehlig
137 bbc6620d Klaus Aehlig
-- | Offline 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
offlineNodes :: [Ndx] -> (Node.List, Instance.List)
140 bbc6620d Klaus Aehlig
                -> ((Node.List, Instance.List), [MoveJob])
141 c407510c Klaus Aehlig
offlineNodes = onlineOfflineNodes True
142 c407510c Klaus Aehlig
143 bbc6620d Klaus Aehlig
-- | Online a list of nodes, and return the state after balancing with
144 bbc6620d Klaus Aehlig
-- the sequence of moves that lead there.
145 c407510c Klaus Aehlig
onlineNodes :: [Ndx] -> (Node.List, Instance.List)
146 bbc6620d Klaus Aehlig
               -> ((Node.List, Instance.List), [MoveJob])
147 c407510c Klaus Aehlig
onlineNodes = onlineOfflineNodes False
148 c407510c Klaus Aehlig
149 c407510c Klaus Aehlig
-- | Predicate on whether a list of nodes can be offlined or onlined
150 c407510c Klaus Aehlig
-- simultaneously in a given configuration, while still leaving enough
151 c407510c Klaus Aehlig
-- capacity on every node for the given instance.
152 c407510c Klaus Aehlig
canOnlineOffline :: Bool -> Instance.Instance -> (Node.List, Instance.List)
153 c407510c Klaus Aehlig
                    -> [Node.Node] ->Bool
154 c407510c Klaus Aehlig
canOnlineOffline offline inst conf nds = 
155 bbc6620d Klaus Aehlig
  let conf' = fst $ onlineOfflineNodes offline (map Node.idx nds) conf
156 5f6e9cb9 Klaus Aehlig
  in allInstancesOnOnlineNodes conf' && allNodesCapacityFor inst conf'
157 5f6e9cb9 Klaus Aehlig
158 c407510c Klaus Aehlig
-- | Predicate on whether a list of nodes can be offlined simultaneously.
159 c407510c Klaus Aehlig
canOffline :: Instance.Instance -> (Node.List, Instance.List) ->
160 c407510c Klaus Aehlig
              [Node.Node] -> Bool
161 c407510c Klaus Aehlig
canOffline = canOnlineOffline True
162 c407510c Klaus Aehlig
163 c407510c Klaus Aehlig
-- | Predicate on whether onlining a list of nodes suffices to get enough
164 c407510c Klaus Aehlig
-- free resources for given instance.
165 c407510c Klaus Aehlig
sufficesOnline :: Instance.Instance -> (Node.List, Instance.List)
166 c407510c Klaus Aehlig
                  -> [Node.Node] ->  Bool
167 c407510c Klaus Aehlig
sufficesOnline = canOnlineOffline False
168 c407510c Klaus Aehlig
169 5f6e9cb9 Klaus Aehlig
-- | Greedily offline the nodes, starting from the last element, and return
170 5f6e9cb9 Klaus Aehlig
-- the list of nodes that could simultaneously be offlined, while keeping
171 5f6e9cb9 Klaus Aehlig
-- the resources specified by an instance.
172 5f6e9cb9 Klaus Aehlig
greedyOfflineNodes :: Instance.Instance -> (Node.List, Instance.List) 
173 5f6e9cb9 Klaus Aehlig
                      -> [Node.Node] -> [Node.Node]
174 5f6e9cb9 Klaus Aehlig
greedyOfflineNodes _ _ [] = []
175 5f6e9cb9 Klaus Aehlig
greedyOfflineNodes inst conf (nd:nds) =
176 5f6e9cb9 Klaus Aehlig
  let nds' = greedyOfflineNodes inst conf nds
177 c407510c Klaus Aehlig
  in if canOffline inst conf (nd:nds') then nd:nds' else nds'
178 c407510c Klaus Aehlig
179 c407510c Klaus Aehlig
-- | Try to provide enough resources by onlining an initial segment of
180 c407510c Klaus Aehlig
-- a list of nodes. Return Nothing, if even onlining all of them is not
181 c407510c Klaus Aehlig
-- enough.
182 c407510c Klaus Aehlig
tryOnline :: Instance.Instance -> (Node.List, Instance.List) -> [Node.Node]
183 c407510c Klaus Aehlig
             -> Maybe [Node.Node]
184 c407510c Klaus Aehlig
tryOnline inst conf = listToMaybe . filter (sufficesOnline inst conf) . inits
185 5f6e9cb9 Klaus Aehlig
186 5f6e9cb9 Klaus Aehlig
-- | From a specification, name, and factor create an instance that uses that
187 5f6e9cb9 Klaus Aehlig
-- factor times the specification, rounded down.
188 5f6e9cb9 Klaus Aehlig
instanceFromSpecAndFactor :: String -> Double -> ISpec -> Instance.Instance
189 5f6e9cb9 Klaus Aehlig
instanceFromSpecAndFactor name f spec =
190 5f6e9cb9 Klaus Aehlig
  Instance.create name
191 5f6e9cb9 Klaus Aehlig
    (floor (f * fromIntegral (iSpecMemorySize spec)))
192 5f6e9cb9 Klaus Aehlig
    0 []
193 5f6e9cb9 Klaus Aehlig
    (floor (f * fromIntegral (iSpecCpuCount spec)))
194 5f6e9cb9 Klaus Aehlig
    Running [] False Node.noSecondary Node.noSecondary DTExt
195 5f6e9cb9 Klaus Aehlig
    (floor (f * fromIntegral (iSpecSpindleUse spec)))
196 5f6e9cb9 Klaus Aehlig
    []
197 5f6e9cb9 Klaus Aehlig
198 5f6e9cb9 Klaus Aehlig
-- | Main function.
199 5f6e9cb9 Klaus Aehlig
main :: Options -> [String] -> IO ()
200 5f6e9cb9 Klaus Aehlig
main opts args = do
201 5f6e9cb9 Klaus Aehlig
  unless (null args) $ exitErr "This program doesn't take any arguments."
202 5f6e9cb9 Klaus Aehlig
203 5f6e9cb9 Klaus Aehlig
  let verbose = optVerbose opts
204 5f6e9cb9 Klaus Aehlig
      targetf = optTargetResources opts
205 c407510c Klaus Aehlig
      minf = optMinResources opts
206 5f6e9cb9 Klaus Aehlig
207 5f6e9cb9 Klaus Aehlig
  ini_cdata@(ClusterData _ nlf ilf _ ipol) <- loadExternalData opts
208 5f6e9cb9 Klaus Aehlig
209 5f6e9cb9 Klaus Aehlig
  maybeSaveData (optSaveCluster opts) "original" "before hsqueeze run" ini_cdata
210 5f6e9cb9 Klaus Aehlig
211 c407510c Klaus Aehlig
  let nodelist = IntMap.elems nlf
212 c407510c Klaus Aehlig
      offlineCandidates = 
213 5f6e9cb9 Klaus Aehlig
        sortBy (flip compare `on` length . Node.pList)
214 5f6e9cb9 Klaus Aehlig
        . filter (foldl (liftA2 (&&)) (const True)
215 5f6e9cb9 Klaus Aehlig
                  [ not . Node.offline
216 5f6e9cb9 Klaus Aehlig
                  , not . Node.isMaster
217 2a58a7b1 Klaus Aehlig
                  , noSecondaries
218 5f6e9cb9 Klaus Aehlig
                  , onlyExternal (nlf, ilf)
219 5f6e9cb9 Klaus Aehlig
                  ])
220 c407510c Klaus Aehlig
        $ nodelist
221 c407510c Klaus Aehlig
      onlineCandidates =
222 c407510c Klaus Aehlig
        filter (liftA2 (&&) Node.offline hasStandbyTag) nodelist
223 5f6e9cb9 Klaus Aehlig
      conf = (nlf, ilf)
224 5f6e9cb9 Klaus Aehlig
      std = iPolicyStdSpec ipol
225 5f6e9cb9 Klaus Aehlig
      targetInstance = instanceFromSpecAndFactor "targetInstance" targetf std
226 c407510c Klaus Aehlig
      minInstance = instanceFromSpecAndFactor "targetInstance" minf std
227 5f6e9cb9 Klaus Aehlig
      toOffline = greedyOfflineNodes targetInstance conf offlineCandidates
228 268fb0bd Klaus Aehlig
      ((fin_off_nl, fin_off_il), off_mvs) =
229 268fb0bd Klaus Aehlig
        offlineNodes (map Node.idx toOffline) conf
230 c407510c Klaus Aehlig
      final_off_cdata =
231 c407510c Klaus Aehlig
        ini_cdata { cdNodes = fin_off_nl, cdInstances = fin_off_il }
232 268fb0bd Klaus Aehlig
      off_jobs = Cluster.splitJobs off_mvs
233 268fb0bd Klaus Aehlig
      off_cmd =
234 268fb0bd Klaus Aehlig
        Cluster.formatCmds off_jobs
235 dcd54d32 Klaus Aehlig
        ++ "\necho Tagging Commands\n"
236 edd09726 Klaus Aehlig
        ++ (toOffline >>= printf "  gnt-node add-tags %s htools:standby:auto\n" 
237 edd09726 Klaus Aehlig
                          . Node.alias)
238 268fb0bd Klaus Aehlig
        ++ "\necho Power Commands\n"
239 81879d92 Klaus Aehlig
        ++ (toOffline >>= printf "  gnt-node power -f off %s\n" . Node.alias)
240 c407510c Klaus Aehlig
      toOnline = tryOnline minInstance conf onlineCandidates
241 c407510c Klaus Aehlig
      nodesToOnline = fromMaybe onlineCandidates toOnline
242 268fb0bd Klaus Aehlig
      ((fin_on_nl, fin_on_il), on_mvs) =
243 268fb0bd Klaus Aehlig
        onlineNodes (map Node.idx nodesToOnline) conf
244 c407510c Klaus Aehlig
      final_on_cdata =
245 c407510c Klaus Aehlig
        ini_cdata { cdNodes = fin_on_nl, cdInstances = fin_on_il }
246 268fb0bd Klaus Aehlig
      on_jobs = Cluster.splitJobs on_mvs
247 268fb0bd Klaus Aehlig
      on_cmd =
248 268fb0bd Klaus Aehlig
        "echo Power Commands\n"
249 81879d92 Klaus Aehlig
        ++ (nodesToOnline >>= printf "  gnt-node power -f on %s\n" . Node.alias)
250 268fb0bd Klaus Aehlig
        ++ Cluster.formatCmds on_jobs
251 5f6e9cb9 Klaus Aehlig
252 5f6e9cb9 Klaus Aehlig
  when (verbose > 1) . putStrLn 
253 5f6e9cb9 Klaus Aehlig
    $ "Offline candidates: " ++ commaJoin (map Node.name offlineCandidates)
254 5f6e9cb9 Klaus Aehlig
255 c407510c Klaus Aehlig
  when (verbose > 1) . putStrLn
256 c407510c Klaus Aehlig
    $ "Online candidates: " ++ commaJoin (map Node.name onlineCandidates)
257 c407510c Klaus Aehlig
258 c407510c Klaus Aehlig
  if not (allNodesCapacityFor minInstance conf)
259 c407510c Klaus Aehlig
    then do
260 c407510c Klaus Aehlig
      unless (optNoHeaders opts) $
261 c407510c Klaus Aehlig
        putStrLn "'Nodes to online'"
262 c407510c Klaus Aehlig
      mapM_ (putStrLn . Node.name) nodesToOnline
263 c407510c Klaus Aehlig
      when (verbose > 1 && isNothing toOnline) . putStrLn $
264 c407510c Klaus Aehlig
        "Onlining all nodes will not yield enough capacity"
265 268fb0bd Klaus Aehlig
      maybeSaveCommands "Commands to run:" opts on_cmd
266 c407510c Klaus Aehlig
      maybeSaveData (optSaveCluster opts)
267 c407510c Klaus Aehlig
         "squeezed" "after hsqueeze expansion" final_on_cdata
268 c407510c Klaus Aehlig
    else
269 c407510c Klaus Aehlig
      if null toOffline
270 c407510c Klaus Aehlig
        then do      
271 c407510c Klaus Aehlig
          unless (optNoHeaders opts) $
272 c407510c Klaus Aehlig
            putStrLn "'No action'"
273 268fb0bd Klaus Aehlig
          maybeSaveCommands "Commands to run:" opts "echo Nothing to do"
274 c407510c Klaus Aehlig
          maybeSaveData (optSaveCluster opts)
275 c407510c Klaus Aehlig
            "squeezed" "after hsqueeze doing nothing" ini_cdata
276 c407510c Klaus Aehlig
        else do
277 c407510c Klaus Aehlig
          unless (optNoHeaders opts) $
278 c407510c Klaus Aehlig
            putStrLn "'Nodes to offline'"
279 c407510c Klaus Aehlig
          mapM_ (putStrLn . Node.name) toOffline
280 268fb0bd Klaus Aehlig
          maybeSaveCommands "Commands to run:" opts off_cmd
281 c407510c Klaus Aehlig
          maybeSaveData (optSaveCluster opts)
282 c407510c Klaus Aehlig
            "squeezed" "after hsqueeze run" final_off_cdata