Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (9.2 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 5f6e9cb9 Klaus Aehlig
balance :: (Node.List, Instance.List) -> (Node.List, Instance.List)
103 5f6e9cb9 Klaus Aehlig
balance (nl, il) =
104 5f6e9cb9 Klaus Aehlig
  let ini_cv = Cluster.compCV nl
105 5f6e9cb9 Klaus Aehlig
      ini_tbl = Cluster.Table nl il ini_cv []
106 5f6e9cb9 Klaus Aehlig
      balanceStep tbl = Cluster.tryBalance tbl True True False 0.0 0.0
107 5f6e9cb9 Klaus Aehlig
      (Cluster.Table nl' il' _ _) = fromJust . last . takeWhile isJust
108 5f6e9cb9 Klaus Aehlig
                                    $ iterate (>>= balanceStep) (Just ini_tbl)
109 5f6e9cb9 Klaus Aehlig
  in (nl', il')
110 5f6e9cb9 Klaus Aehlig
111 c407510c Klaus Aehlig
-- | In a configuration, mark a node as online or offline.
112 c407510c Klaus Aehlig
onlineOfflineNode :: Bool -> (Node.List, Instance.List) -> Ndx ->
113 c407510c Klaus Aehlig
                     (Node.List, Instance.List)
114 c407510c Klaus Aehlig
onlineOfflineNode offline (nl, il) ndx =
115 5f6e9cb9 Klaus Aehlig
  let nd = Container.find ndx nl
116 c407510c Klaus Aehlig
      nd' = Node.setOffline nd offline
117 5f6e9cb9 Klaus Aehlig
      nl' = Container.add ndx nd' nl
118 5f6e9cb9 Klaus Aehlig
  in (nl', il)
119 5f6e9cb9 Klaus Aehlig
120 c407510c Klaus Aehlig
-- | Offline or online a list nodes, and return the state after a balancing
121 c407510c Klaus Aehlig
-- attempt.
122 c407510c Klaus Aehlig
onlineOfflineNodes :: Bool -> [Ndx] -> (Node.List, Instance.List)
123 c407510c Klaus Aehlig
                      -> (Node.List, Instance.List)
124 c407510c Klaus Aehlig
onlineOfflineNodes offline ndxs conf =
125 c407510c Klaus Aehlig
  let conf' = foldl (onlineOfflineNode offline) conf ndxs
126 5f6e9cb9 Klaus Aehlig
  in balance conf'
127 5f6e9cb9 Klaus Aehlig
128 c407510c Klaus Aehlig
-- | Offline a list of nodes, and return the state after balancing.
129 c407510c Klaus Aehlig
offlineNodes :: [Ndx] -> (Node.List, Instance.List)
130 c407510c Klaus Aehlig
                -> (Node.List, Instance.List)
131 c407510c Klaus Aehlig
offlineNodes = onlineOfflineNodes True
132 c407510c Klaus Aehlig
133 c407510c Klaus Aehlig
-- | Online a list of nodes, and return the state after balancing.
134 c407510c Klaus Aehlig
onlineNodes :: [Ndx] -> (Node.List, Instance.List)
135 c407510c Klaus Aehlig
               -> (Node.List, Instance.List)
136 c407510c Klaus Aehlig
onlineNodes = onlineOfflineNodes False
137 c407510c Klaus Aehlig
138 c407510c Klaus Aehlig
-- | Predicate on whether a list of nodes can be offlined or onlined
139 c407510c Klaus Aehlig
-- simultaneously in a given configuration, while still leaving enough
140 c407510c Klaus Aehlig
-- capacity on every node for the given instance.
141 c407510c Klaus Aehlig
canOnlineOffline :: Bool -> Instance.Instance -> (Node.List, Instance.List)
142 c407510c Klaus Aehlig
                    -> [Node.Node] ->Bool
143 c407510c Klaus Aehlig
canOnlineOffline offline inst conf nds = 
144 c407510c Klaus Aehlig
  let conf' = onlineOfflineNodes offline (map Node.idx nds) conf
145 5f6e9cb9 Klaus Aehlig
  in allInstancesOnOnlineNodes conf' && allNodesCapacityFor inst conf'
146 5f6e9cb9 Klaus Aehlig
147 c407510c Klaus Aehlig
-- | Predicate on whether a list of nodes can be offlined simultaneously.
148 c407510c Klaus Aehlig
canOffline :: Instance.Instance -> (Node.List, Instance.List) ->
149 c407510c Klaus Aehlig
              [Node.Node] -> Bool
150 c407510c Klaus Aehlig
canOffline = canOnlineOffline True
151 c407510c Klaus Aehlig
152 c407510c Klaus Aehlig
-- | Predicate on whether onlining a list of nodes suffices to get enough
153 c407510c Klaus Aehlig
-- free resources for given instance.
154 c407510c Klaus Aehlig
sufficesOnline :: Instance.Instance -> (Node.List, Instance.List)
155 c407510c Klaus Aehlig
                  -> [Node.Node] ->  Bool
156 c407510c Klaus Aehlig
sufficesOnline = canOnlineOffline False
157 c407510c Klaus Aehlig
158 5f6e9cb9 Klaus Aehlig
-- | Greedily offline the nodes, starting from the last element, and return
159 5f6e9cb9 Klaus Aehlig
-- the list of nodes that could simultaneously be offlined, while keeping
160 5f6e9cb9 Klaus Aehlig
-- the resources specified by an instance.
161 5f6e9cb9 Klaus Aehlig
greedyOfflineNodes :: Instance.Instance -> (Node.List, Instance.List) 
162 5f6e9cb9 Klaus Aehlig
                      -> [Node.Node] -> [Node.Node]
163 5f6e9cb9 Klaus Aehlig
greedyOfflineNodes _ _ [] = []
164 5f6e9cb9 Klaus Aehlig
greedyOfflineNodes inst conf (nd:nds) =
165 5f6e9cb9 Klaus Aehlig
  let nds' = greedyOfflineNodes inst conf nds
166 c407510c Klaus Aehlig
  in if canOffline inst conf (nd:nds') then nd:nds' else nds'
167 c407510c Klaus Aehlig
168 c407510c Klaus Aehlig
-- | Try to provide enough resources by onlining an initial segment of
169 c407510c Klaus Aehlig
-- a list of nodes. Return Nothing, if even onlining all of them is not
170 c407510c Klaus Aehlig
-- enough.
171 c407510c Klaus Aehlig
tryOnline :: Instance.Instance -> (Node.List, Instance.List) -> [Node.Node]
172 c407510c Klaus Aehlig
             -> Maybe [Node.Node]
173 c407510c Klaus Aehlig
tryOnline inst conf = listToMaybe . filter (sufficesOnline inst conf) . inits
174 5f6e9cb9 Klaus Aehlig
175 5f6e9cb9 Klaus Aehlig
-- | From a specification, name, and factor create an instance that uses that
176 5f6e9cb9 Klaus Aehlig
-- factor times the specification, rounded down.
177 5f6e9cb9 Klaus Aehlig
instanceFromSpecAndFactor :: String -> Double -> ISpec -> Instance.Instance
178 5f6e9cb9 Klaus Aehlig
instanceFromSpecAndFactor name f spec =
179 5f6e9cb9 Klaus Aehlig
  Instance.create name
180 5f6e9cb9 Klaus Aehlig
    (floor (f * fromIntegral (iSpecMemorySize spec)))
181 5f6e9cb9 Klaus Aehlig
    0 []
182 5f6e9cb9 Klaus Aehlig
    (floor (f * fromIntegral (iSpecCpuCount spec)))
183 5f6e9cb9 Klaus Aehlig
    Running [] False Node.noSecondary Node.noSecondary DTExt
184 5f6e9cb9 Klaus Aehlig
    (floor (f * fromIntegral (iSpecSpindleUse spec)))
185 5f6e9cb9 Klaus Aehlig
    []
186 5f6e9cb9 Klaus Aehlig
187 5f6e9cb9 Klaus Aehlig
-- | Main function.
188 5f6e9cb9 Klaus Aehlig
main :: Options -> [String] -> IO ()
189 5f6e9cb9 Klaus Aehlig
main opts args = do
190 5f6e9cb9 Klaus Aehlig
  unless (null args) $ exitErr "This program doesn't take any arguments."
191 5f6e9cb9 Klaus Aehlig
192 5f6e9cb9 Klaus Aehlig
  let verbose = optVerbose opts
193 5f6e9cb9 Klaus Aehlig
      targetf = optTargetResources opts
194 c407510c Klaus Aehlig
      minf = optMinResources opts
195 5f6e9cb9 Klaus Aehlig
196 5f6e9cb9 Klaus Aehlig
  ini_cdata@(ClusterData _ nlf ilf _ ipol) <- loadExternalData opts
197 5f6e9cb9 Klaus Aehlig
198 5f6e9cb9 Klaus Aehlig
  maybeSaveData (optSaveCluster opts) "original" "before hsqueeze run" ini_cdata
199 5f6e9cb9 Klaus Aehlig
200 c407510c Klaus Aehlig
  let nodelist = IntMap.elems nlf
201 c407510c Klaus Aehlig
      offlineCandidates = 
202 5f6e9cb9 Klaus Aehlig
        sortBy (flip compare `on` length . Node.pList)
203 5f6e9cb9 Klaus Aehlig
        . filter (foldl (liftA2 (&&)) (const True)
204 5f6e9cb9 Klaus Aehlig
                  [ not . Node.offline
205 5f6e9cb9 Klaus Aehlig
                  , not . Node.isMaster
206 5f6e9cb9 Klaus Aehlig
                  , onlyExternal (nlf, ilf)
207 5f6e9cb9 Klaus Aehlig
                  ])
208 c407510c Klaus Aehlig
        $ nodelist
209 c407510c Klaus Aehlig
      onlineCandidates =
210 c407510c Klaus Aehlig
        filter (liftA2 (&&) Node.offline hasStandbyTag) nodelist
211 5f6e9cb9 Klaus Aehlig
      conf = (nlf, ilf)
212 5f6e9cb9 Klaus Aehlig
      std = iPolicyStdSpec ipol
213 5f6e9cb9 Klaus Aehlig
      targetInstance = instanceFromSpecAndFactor "targetInstance" targetf std
214 c407510c Klaus Aehlig
      minInstance = instanceFromSpecAndFactor "targetInstance" minf std
215 5f6e9cb9 Klaus Aehlig
      toOffline = greedyOfflineNodes targetInstance conf offlineCandidates
216 c407510c Klaus Aehlig
      (fin_off_nl, fin_off_il) = offlineNodes (map Node.idx toOffline) conf
217 c407510c Klaus Aehlig
      final_off_cdata =
218 c407510c Klaus Aehlig
        ini_cdata { cdNodes = fin_off_nl, cdInstances = fin_off_il }
219 c407510c Klaus Aehlig
      toOnline = tryOnline minInstance conf onlineCandidates
220 c407510c Klaus Aehlig
      nodesToOnline = fromMaybe onlineCandidates toOnline
221 c407510c Klaus Aehlig
      (fin_on_nl, fin_on_il) = onlineNodes (map Node.idx nodesToOnline) conf
222 c407510c Klaus Aehlig
      final_on_cdata =
223 c407510c Klaus Aehlig
        ini_cdata { cdNodes = fin_on_nl, cdInstances = fin_on_il }
224 5f6e9cb9 Klaus Aehlig
225 5f6e9cb9 Klaus Aehlig
  when (verbose > 1) . putStrLn 
226 5f6e9cb9 Klaus Aehlig
    $ "Offline candidates: " ++ commaJoin (map Node.name offlineCandidates)
227 5f6e9cb9 Klaus Aehlig
228 c407510c Klaus Aehlig
  when (verbose > 1) . putStrLn
229 c407510c Klaus Aehlig
    $ "Online candidates: " ++ commaJoin (map Node.name onlineCandidates)
230 c407510c Klaus Aehlig
231 c407510c Klaus Aehlig
  if not (allNodesCapacityFor minInstance conf)
232 c407510c Klaus Aehlig
    then do
233 c407510c Klaus Aehlig
      unless (optNoHeaders opts) $
234 c407510c Klaus Aehlig
        putStrLn "'Nodes to online'"
235 c407510c Klaus Aehlig
      mapM_ (putStrLn . Node.name) nodesToOnline
236 c407510c Klaus Aehlig
      when (verbose > 1 && isNothing toOnline) . putStrLn $
237 c407510c Klaus Aehlig
        "Onlining all nodes will not yield enough capacity"
238 c407510c Klaus Aehlig
      maybeSaveData (optSaveCluster opts)
239 c407510c Klaus Aehlig
         "squeezed" "after hsqueeze expansion" final_on_cdata
240 c407510c Klaus Aehlig
    else
241 c407510c Klaus Aehlig
      if null toOffline
242 c407510c Klaus Aehlig
        then do      
243 c407510c Klaus Aehlig
          unless (optNoHeaders opts) $
244 c407510c Klaus Aehlig
            putStrLn "'No action'"
245 c407510c Klaus Aehlig
          maybeSaveData (optSaveCluster opts)
246 c407510c Klaus Aehlig
            "squeezed" "after hsqueeze doing nothing" ini_cdata
247 c407510c Klaus Aehlig
        else do
248 c407510c Klaus Aehlig
          unless (optNoHeaders opts) $
249 c407510c Klaus Aehlig
            putStrLn "'Nodes to offline'"
250 c407510c Klaus Aehlig
251 c407510c Klaus Aehlig
          mapM_ (putStrLn . Node.name) toOffline
252 c407510c Klaus Aehlig
253 c407510c Klaus Aehlig
          maybeSaveData (optSaveCluster opts)
254 c407510c Klaus Aehlig
            "squeezed" "after hsqueeze run" final_off_cdata