Do not rely on no-secondary for movable tests
[ganeti-local] / htools / Ganeti / HTools / Types.hs
1 {-# LANGUAGE TemplateHaskell #-}
2
3 {-| Some common types.
4
5 -}
6
7 {-
8
9 Copyright (C) 2009, 2010, 2011, 2012 Google Inc.
10
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 02110-1301, USA.
25
26 -}
27
28 module Ganeti.HTools.Types
29   ( Idx
30   , Ndx
31   , Gdx
32   , NameAssoc
33   , Score
34   , Weight
35   , GroupID
36   , defaultGroupID
37   , AllocPolicy(..)
38   , allocPolicyFromRaw
39   , allocPolicyToRaw
40   , InstanceStatus(..)
41   , instanceStatusFromRaw
42   , instanceStatusToRaw
43   , RSpec(..)
44   , AllocInfo(..)
45   , AllocStats
46   , DynUtil(..)
47   , zeroUtil
48   , baseUtil
49   , addUtil
50   , subUtil
51   , defReservedDiskRatio
52   , unitMem
53   , unitCpu
54   , unitDsk
55   , unknownField
56   , Placement
57   , IMove(..)
58   , DiskTemplate(..)
59   , diskTemplateToRaw
60   , diskTemplateFromRaw
61   , MoveJob
62   , JobSet
63   , Result(..)
64   , isOk
65   , isBad
66   , eitherToResult
67   , annotateResult
68   , Element(..)
69   , FailMode(..)
70   , FailStats
71   , OpResult(..)
72   , opToResult
73   , connTimeout
74   , queryTimeout
75   , EvacMode(..)
76   , ISpec(..)
77   , IPolicy(..)
78   , defIPolicy
79   , rspecFromISpec
80   ) where
81
82 import qualified Data.Map as M
83 import Text.JSON (makeObj, readJSON, showJSON)
84
85 import qualified Ganeti.Constants as C
86 import qualified Ganeti.THH as THH
87 import Ganeti.BasicTypes
88 import Ganeti.HTools.JSON
89
90 -- | The instance index type.
91 type Idx = Int
92
93 -- | The node index type.
94 type Ndx = Int
95
96 -- | The group index type.
97 type Gdx = Int
98
99 -- | The type used to hold name-to-idx mappings.
100 type NameAssoc = M.Map String Int
101
102 -- | A separate name for the cluster score type.
103 type Score = Double
104
105 -- | A separate name for a weight metric.
106 type Weight = Double
107
108 -- | The Group UUID type.
109 type GroupID = String
110
111 -- | Default group UUID (just a string, not a real UUID).
112 defaultGroupID :: GroupID
113 defaultGroupID = "00000000-0000-0000-0000-000000000000"
114
115 -- | Instance disk template type.
116 $(THH.declareSADT "DiskTemplate"
117        [ ("DTDiskless",   'C.dtDiskless)
118        , ("DTFile",       'C.dtFile)
119        , ("DTSharedFile", 'C.dtSharedFile)
120        , ("DTPlain",      'C.dtPlain)
121        , ("DTBlock",      'C.dtBlock)
122        , ("DTDrbd8",      'C.dtDrbd8)
123        , ("DTRbd",        'C.dtRbd)
124        ])
125 $(THH.makeJSONInstance ''DiskTemplate)
126
127 -- | The Group allocation policy type.
128 --
129 -- Note that the order of constructors is important as the automatic
130 -- Ord instance will order them in the order they are defined, so when
131 -- changing this data type be careful about the interaction with the
132 -- desired sorting order.
133 $(THH.declareSADT "AllocPolicy"
134        [ ("AllocPreferred",   'C.allocPolicyPreferred)
135        , ("AllocLastResort",  'C.allocPolicyLastResort)
136        , ("AllocUnallocable", 'C.allocPolicyUnallocable)
137        ])
138 $(THH.makeJSONInstance ''AllocPolicy)
139
140 -- | The Instance real state type.
141 $(THH.declareSADT "InstanceStatus"
142        [ ("AdminDown", 'C.inststAdmindown)
143        , ("AdminOffline", 'C.inststAdminoffline)
144        , ("ErrorDown", 'C.inststErrordown)
145        , ("ErrorUp", 'C.inststErrorup)
146        , ("NodeDown", 'C.inststNodedown)
147        , ("NodeOffline", 'C.inststNodeoffline)
148        , ("Running", 'C.inststRunning)
149        , ("WrongNode", 'C.inststWrongnode)
150        ])
151 $(THH.makeJSONInstance ''InstanceStatus)
152
153 -- | The resource spec type.
154 data RSpec = RSpec
155   { rspecCpu  :: Int  -- ^ Requested VCPUs
156   , rspecMem  :: Int  -- ^ Requested memory
157   , rspecDsk  :: Int  -- ^ Requested disk
158   } deriving (Show, Read, Eq)
159
160 -- | Allocation stats type. This is used instead of 'RSpec' (which was
161 -- used at first), because we need to track more stats. The actual
162 -- data can refer either to allocated, or available, etc. values
163 -- depending on the context. See also
164 -- 'Cluster.computeAllocationDelta'.
165 data AllocInfo = AllocInfo
166   { allocInfoVCpus :: Int    -- ^ VCPUs
167   , allocInfoNCpus :: Double -- ^ Normalised CPUs
168   , allocInfoMem   :: Int    -- ^ Memory
169   , allocInfoDisk  :: Int    -- ^ Disk
170   } deriving (Show, Read, Eq)
171
172 -- | Currently used, possibly to allocate, unallocable.
173 type AllocStats = (AllocInfo, AllocInfo, AllocInfo)
174
175 -- | Instance specification type.
176 $(THH.buildObject "ISpec" "iSpec"
177   [ THH.renameField "MemorySize" $ THH.simpleField C.ispecMemSize   [t| Int |]
178   , THH.renameField "CpuCount"   $ THH.simpleField C.ispecCpuCount  [t| Int |]
179   , THH.renameField "DiskSize"   $ THH.simpleField C.ispecDiskSize  [t| Int |]
180   , THH.renameField "DiskCount"  $ THH.simpleField C.ispecDiskCount [t| Int |]
181   , THH.renameField "NicCount"   $ THH.simpleField C.ispecNicCount  [t| Int |]
182   ])
183
184 -- | The default minimum ispec.
185 defMinISpec :: ISpec
186 defMinISpec = ISpec { iSpecMemorySize = C.ipolicyDefaultsMinMemorySize
187                     , iSpecCpuCount   = C.ipolicyDefaultsMinCpuCount
188                     , iSpecDiskSize   = C.ipolicyDefaultsMinDiskSize
189                     , iSpecDiskCount  = C.ipolicyDefaultsMinDiskCount
190                     , iSpecNicCount   = C.ipolicyDefaultsMinNicCount
191                     }
192
193 -- | The default standard ispec.
194 defStdISpec :: ISpec
195 defStdISpec = ISpec { iSpecMemorySize = C.ipolicyDefaultsStdMemorySize
196                     , iSpecCpuCount   = C.ipolicyDefaultsStdCpuCount
197                     , iSpecDiskSize   = C.ipolicyDefaultsStdDiskSize
198                     , iSpecDiskCount  = C.ipolicyDefaultsStdDiskCount
199                     , iSpecNicCount   = C.ipolicyDefaultsStdNicCount
200                     }
201
202 -- | The default max ispec.
203 defMaxISpec :: ISpec
204 defMaxISpec = ISpec { iSpecMemorySize = C.ipolicyDefaultsMaxMemorySize
205                     , iSpecCpuCount   = C.ipolicyDefaultsMaxCpuCount
206                     , iSpecDiskSize   = C.ipolicyDefaultsMaxDiskSize
207                     , iSpecDiskCount  = C.ipolicyDefaultsMaxDiskCount
208                     , iSpecNicCount   = C.ipolicyDefaultsMaxNicCount
209                     }
210
211 -- | Instance policy type.
212 $(THH.buildObject "IPolicy" "iPolicy"
213   [ THH.renameField "StdSpec" $ THH.simpleField C.ispecsStd [t| ISpec |]
214   , THH.renameField "MinSpec" $ THH.simpleField C.ispecsMin [t| ISpec |]
215   , THH.renameField "MaxSpec" $ THH.simpleField C.ispecsMax [t| ISpec |]
216   , THH.renameField "DiskTemplates" $
217       THH.simpleField C.ipolicyDts [t| [DiskTemplate] |]
218   , THH.renameField "VcpuRatio" $
219       THH.simpleField C.ipolicyVcpuRatio [t| Double |]
220   , THH.renameField "SpindleRatio" $
221       THH.simpleField C.ipolicySpindleRatio [t| Double |]
222   ])
223
224 -- | Converts an ISpec type to a RSpec one.
225 rspecFromISpec :: ISpec -> RSpec
226 rspecFromISpec ispec = RSpec { rspecCpu = iSpecCpuCount ispec
227                              , rspecMem = iSpecMemorySize ispec
228                              , rspecDsk = iSpecDiskSize ispec
229                              }
230
231 -- | The default instance policy.
232 defIPolicy :: IPolicy
233 defIPolicy = IPolicy { iPolicyStdSpec = defStdISpec
234                      , iPolicyMinSpec = defMinISpec
235                      , iPolicyMaxSpec = defMaxISpec
236                      -- hardcoding here since Constants.hs exports the
237                      -- string values, not the actual type; and in
238                      -- htools, we are mostly looking at DRBD
239                      , iPolicyDiskTemplates = [DTDrbd8, DTPlain]
240                      , iPolicyVcpuRatio = C.ipolicyDefaultsVcpuRatio
241                      , iPolicySpindleRatio = C.ipolicyDefaultsSpindleRatio
242                      }
243
244 -- | The dynamic resource specs of a machine (i.e. load or load
245 -- capacity, as opposed to size).
246 data DynUtil = DynUtil
247   { cpuWeight :: Weight -- ^ Standardised CPU usage
248   , memWeight :: Weight -- ^ Standardised memory load
249   , dskWeight :: Weight -- ^ Standardised disk I\/O usage
250   , netWeight :: Weight -- ^ Standardised network usage
251   } deriving (Show, Read, Eq)
252
253 -- | Initial empty utilisation.
254 zeroUtil :: DynUtil
255 zeroUtil = DynUtil { cpuWeight = 0, memWeight = 0
256                    , dskWeight = 0, netWeight = 0 }
257
258 -- | Base utilisation (used when no actual utilisation data is
259 -- supplied).
260 baseUtil :: DynUtil
261 baseUtil = DynUtil { cpuWeight = 1, memWeight = 1
262                    , dskWeight = 1, netWeight = 1 }
263
264 -- | Sum two utilisation records.
265 addUtil :: DynUtil -> DynUtil -> DynUtil
266 addUtil (DynUtil a1 a2 a3 a4) (DynUtil b1 b2 b3 b4) =
267   DynUtil (a1+b1) (a2+b2) (a3+b3) (a4+b4)
268
269 -- | Substracts one utilisation record from another.
270 subUtil :: DynUtil -> DynUtil -> DynUtil
271 subUtil (DynUtil a1 a2 a3 a4) (DynUtil b1 b2 b3 b4) =
272   DynUtil (a1-b1) (a2-b2) (a3-b3) (a4-b4)
273
274 -- | The description of an instance placement. It contains the
275 -- instance index, the new primary and secondary node, the move being
276 -- performed and the score of the cluster after the move.
277 type Placement = (Idx, Ndx, Ndx, IMove, Score)
278
279 -- | An instance move definition.
280 data IMove = Failover                -- ^ Failover the instance (f)
281            | ReplacePrimary Ndx      -- ^ Replace primary (f, r:np, f)
282            | ReplaceSecondary Ndx    -- ^ Replace secondary (r:ns)
283            | ReplaceAndFailover Ndx  -- ^ Replace secondary, failover (r:np, f)
284            | FailoverAndReplace Ndx  -- ^ Failover, replace secondary (f, r:ns)
285              deriving (Show, Read)
286
287 -- | Formatted solution output for one move (involved nodes and
288 -- commands.
289 type MoveJob = ([Ndx], Idx, IMove, [String])
290
291 -- | Unknown field in table output.
292 unknownField :: String
293 unknownField = "<unknown field>"
294
295 -- | A list of command elements.
296 type JobSet = [MoveJob]
297
298 -- | Connection timeout (when using non-file methods).
299 connTimeout :: Int
300 connTimeout = 15
301
302 -- | The default timeout for queries (when using non-file methods).
303 queryTimeout :: Int
304 queryTimeout = 60
305
306 -- | Default max disk usage ratio.
307 defReservedDiskRatio :: Double
308 defReservedDiskRatio = 0
309
310 -- | Base memory unit.
311 unitMem :: Int
312 unitMem = 64
313
314 -- | Base disk unit.
315 unitDsk :: Int
316 unitDsk = 256
317
318 -- | Base vcpus unit.
319 unitCpu :: Int
320 unitCpu = 1
321
322 -- | Reason for an operation's falure.
323 data FailMode = FailMem  -- ^ Failed due to not enough RAM
324               | FailDisk -- ^ Failed due to not enough disk
325               | FailCPU  -- ^ Failed due to not enough CPU capacity
326               | FailN1   -- ^ Failed due to not passing N1 checks
327               | FailTags -- ^ Failed due to tag exclusion
328                 deriving (Eq, Enum, Bounded, Show, Read)
329
330 -- | List with failure statistics.
331 type FailStats = [(FailMode, Int)]
332
333 -- | Either-like data-type customized for our failure modes.
334 --
335 -- The failure values for this monad track the specific allocation
336 -- failures, so this is not a general error-monad (compare with the
337 -- 'Result' data type). One downside is that this type cannot encode a
338 -- generic failure mode, hence 'fail' for this monad is not defined
339 -- and will cause an exception.
340 data OpResult a = OpFail FailMode -- ^ Failed operation
341                 | OpGood a        -- ^ Success operation
342                   deriving (Show, Read)
343
344 instance Monad OpResult where
345   (OpGood x) >>= fn = fn x
346   (OpFail y) >>= _ = OpFail y
347   return = OpGood
348
349 -- | Conversion from 'OpResult' to 'Result'.
350 opToResult :: OpResult a -> Result a
351 opToResult (OpFail f) = Bad $ show f
352 opToResult (OpGood v) = Ok v
353
354 -- | A generic class for items that have updateable names and indices.
355 class Element a where
356   -- | Returns the name of the element
357   nameOf  :: a -> String
358   -- | Returns all the known names of the element
359   allNames :: a -> [String]
360   -- | Returns the index of the element
361   idxOf   :: a -> Int
362   -- | Updates the alias of the element
363   setAlias :: a -> String -> a
364   -- | Compute the alias by stripping a given suffix (domain) from
365   -- the name
366   computeAlias :: String -> a -> a
367   computeAlias dom e = setAlias e alias
368     where alias = take (length name - length dom) name
369           name = nameOf e
370   -- | Updates the index of the element
371   setIdx  :: a -> Int -> a
372
373 -- | The iallocator node-evacuate evac_mode type.
374 $(THH.declareSADT "EvacMode"
375        [ ("ChangePrimary",   'C.iallocatorNevacPri)
376        , ("ChangeSecondary", 'C.iallocatorNevacSec)
377        , ("ChangeAll",       'C.iallocatorNevacAll)
378        ])
379 $(THH.makeJSONInstance ''EvacMode)