Add some very basic options test
[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 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   , DynUtil(..)
45   , zeroUtil
46   , baseUtil
47   , addUtil
48   , subUtil
49   , defVcpuRatio
50   , defReservedDiskRatio
51   , unitMem
52   , unitCpu
53   , unitDsk
54   , unknownField
55   , Placement
56   , IMove(..)
57   , DiskTemplate(..)
58   , diskTemplateToRaw
59   , diskTemplateFromRaw
60   , MoveJob
61   , JobSet
62   , Result(..)
63   , isOk
64   , isBad
65   , eitherToResult
66   , annotateResult
67   , Element(..)
68   , FailMode(..)
69   , FailStats
70   , OpResult(..)
71   , opToResult
72   , connTimeout
73   , queryTimeout
74   , EvacMode(..)
75   , ISpec(..)
76   , IPolicy(..)
77   , defIPolicy
78   , rspecFromISpec
79   ) where
80
81 import qualified Data.Map as M
82 import Text.JSON (makeObj, readJSON, showJSON)
83
84 import qualified Ganeti.Constants as C
85 import qualified Ganeti.THH as THH
86 import Ganeti.BasicTypes
87 import Ganeti.HTools.JSON
88
89 -- | The instance index type.
90 type Idx = Int
91
92 -- | The node index type.
93 type Ndx = Int
94
95 -- | The group index type.
96 type Gdx = Int
97
98 -- | The type used to hold name-to-idx mappings.
99 type NameAssoc = M.Map String Int
100
101 -- | A separate name for the cluster score type.
102 type Score = Double
103
104 -- | A separate name for a weight metric.
105 type Weight = Double
106
107 -- | The Group UUID type.
108 type GroupID = String
109
110 -- | Default group UUID (just a string, not a real UUID).
111 defaultGroupID :: GroupID
112 defaultGroupID = "00000000-0000-0000-0000-000000000000"
113
114 -- | Instance disk template type.
115 $(THH.declareSADT "DiskTemplate"
116        [ ("DTDiskless",   'C.dtDiskless)
117        , ("DTFile",       'C.dtFile)
118        , ("DTSharedFile", 'C.dtSharedFile)
119        , ("DTPlain",      'C.dtPlain)
120        , ("DTBlock",      'C.dtBlock)
121        , ("DTDrbd8",      'C.dtDrbd8)
122        ])
123 $(THH.makeJSONInstance ''DiskTemplate)
124
125 -- | The Group allocation policy type.
126 --
127 -- Note that the order of constructors is important as the automatic
128 -- Ord instance will order them in the order they are defined, so when
129 -- changing this data type be careful about the interaction with the
130 -- desired sorting order.
131 $(THH.declareSADT "AllocPolicy"
132        [ ("AllocPreferred",   'C.allocPolicyPreferred)
133        , ("AllocLastResort",  'C.allocPolicyLastResort)
134        , ("AllocUnallocable", 'C.allocPolicyUnallocable)
135        ])
136 $(THH.makeJSONInstance ''AllocPolicy)
137
138 -- | The Instance real state type.
139 $(THH.declareSADT "InstanceStatus"
140        [ ("AdminDown", 'C.inststAdmindown)
141        , ("AdminOffline", 'C.inststAdminoffline)
142        , ("ErrorDown", 'C.inststErrordown)
143        , ("ErrorUp", 'C.inststErrorup)
144        , ("NodeDown", 'C.inststNodedown)
145        , ("NodeOffline", 'C.inststNodeoffline)
146        , ("Running", 'C.inststRunning)
147        , ("WrongNode", 'C.inststWrongnode)
148        ])
149 $(THH.makeJSONInstance ''InstanceStatus)
150
151 -- | The resource spec type.
152 data RSpec = RSpec
153   { rspecCpu  :: Int  -- ^ Requested VCPUs
154   , rspecMem  :: Int  -- ^ Requested memory
155   , rspecDsk  :: Int  -- ^ Requested disk
156   } deriving (Show, Read, Eq)
157
158
159 -- | Instance specification type.
160 $(THH.buildObject "ISpec" "iSpec"
161   [ THH.renameField "MemorySize" $ THH.simpleField "memory-size" [t| Int |]
162   , THH.renameField "CpuCount"   $ THH.simpleField "cpu-count"   [t| Int |]
163   , THH.renameField "DiskSize"   $ THH.simpleField "disk-size"   [t| Int |]
164   , THH.renameField "DiskCount"  $ THH.simpleField "disk-count"  [t| Int |]
165   , THH.renameField "NicCount"   $ THH.simpleField "nic-count"   [t| Int |]
166   ])
167
168 -- | The default minimum ispec.
169 defMinISpec :: ISpec
170 defMinISpec = ISpec { iSpecMemorySize = C.ipolicyDefaultsMinMemorySize
171                     , iSpecCpuCount   = C.ipolicyDefaultsMinCpuCount
172                     , iSpecDiskSize   = C.ipolicyDefaultsMinDiskSize
173                     , iSpecDiskCount  = C.ipolicyDefaultsMinDiskCount
174                     , iSpecNicCount   = C.ipolicyDefaultsMinNicCount
175                     }
176
177 -- | The default standard ispec.
178 defStdISpec :: ISpec
179 defStdISpec = ISpec { iSpecMemorySize = C.ipolicyDefaultsStdMemorySize
180                     , iSpecCpuCount   = C.ipolicyDefaultsStdCpuCount
181                     , iSpecDiskSize   = C.ipolicyDefaultsStdDiskSize
182                     , iSpecDiskCount  = C.ipolicyDefaultsStdDiskCount
183                     , iSpecNicCount   = C.ipolicyDefaultsStdNicCount
184                     }
185
186 -- | The default max ispec.
187 defMaxISpec :: ISpec
188 defMaxISpec = ISpec { iSpecMemorySize = C.ipolicyDefaultsMaxMemorySize
189                     , iSpecCpuCount   = C.ipolicyDefaultsMaxCpuCount
190                     , iSpecDiskSize   = C.ipolicyDefaultsMaxDiskSize
191                     , iSpecDiskCount  = C.ipolicyDefaultsMaxDiskCount
192                     , iSpecNicCount   = C.ipolicyDefaultsMaxNicCount
193                     }
194
195 -- | Instance policy type.
196 $(THH.buildObject "IPolicy" "iPolicy"
197   [ THH.renameField "StdSpec" $ THH.simpleField "std" [t| ISpec |]
198   , THH.renameField "MinSpec" $ THH.simpleField "min" [t| ISpec |]
199   , THH.renameField "MaxSpec" $ THH.simpleField "max" [t| ISpec |]
200   , THH.renameField "DiskTemplates" $
201       THH.simpleField "disk_templates" [t| [DiskTemplate] |]
202   ])
203
204 -- | Converts an ISpec type to a RSpec one.
205 rspecFromISpec :: ISpec -> RSpec
206 rspecFromISpec ispec = RSpec { rspecCpu = iSpecCpuCount ispec
207                              , rspecMem = iSpecMemorySize ispec
208                              , rspecDsk = iSpecDiskSize ispec
209                              }
210
211 -- | The default instance policy.
212 defIPolicy :: IPolicy
213 defIPolicy = IPolicy { iPolicyStdSpec = defStdISpec
214                      , iPolicyMinSpec = defMinISpec
215                      , iPolicyMaxSpec = defMaxISpec
216                      -- hardcoding here since Constants.hs exports the
217                      -- string values, not the actual type; and in
218                      -- htools, we are mostly looking at DRBD
219                      , iPolicyDiskTemplates = [DTDrbd8, DTPlain]
220                      }
221
222 -- | The dynamic resource specs of a machine (i.e. load or load
223 -- capacity, as opposed to size).
224 data DynUtil = DynUtil
225   { cpuWeight :: Weight -- ^ Standardised CPU usage
226   , memWeight :: Weight -- ^ Standardised memory load
227   , dskWeight :: Weight -- ^ Standardised disk I\/O usage
228   , netWeight :: Weight -- ^ Standardised network usage
229   } deriving (Show, Read, Eq)
230
231 -- | Initial empty utilisation.
232 zeroUtil :: DynUtil
233 zeroUtil = DynUtil { cpuWeight = 0, memWeight = 0
234                    , dskWeight = 0, netWeight = 0 }
235
236 -- | Base utilisation (used when no actual utilisation data is
237 -- supplied).
238 baseUtil :: DynUtil
239 baseUtil = DynUtil { cpuWeight = 1, memWeight = 1
240                    , dskWeight = 1, netWeight = 1 }
241
242 -- | Sum two utilisation records.
243 addUtil :: DynUtil -> DynUtil -> DynUtil
244 addUtil (DynUtil a1 a2 a3 a4) (DynUtil b1 b2 b3 b4) =
245   DynUtil (a1+b1) (a2+b2) (a3+b3) (a4+b4)
246
247 -- | Substracts one utilisation record from another.
248 subUtil :: DynUtil -> DynUtil -> DynUtil
249 subUtil (DynUtil a1 a2 a3 a4) (DynUtil b1 b2 b3 b4) =
250   DynUtil (a1-b1) (a2-b2) (a3-b3) (a4-b4)
251
252 -- | The description of an instance placement. It contains the
253 -- instance index, the new primary and secondary node, the move being
254 -- performed and the score of the cluster after the move.
255 type Placement = (Idx, Ndx, Ndx, IMove, Score)
256
257 -- | An instance move definition.
258 data IMove = Failover                -- ^ Failover the instance (f)
259            | ReplacePrimary Ndx      -- ^ Replace primary (f, r:np, f)
260            | ReplaceSecondary Ndx    -- ^ Replace secondary (r:ns)
261            | ReplaceAndFailover Ndx  -- ^ Replace secondary, failover (r:np, f)
262            | FailoverAndReplace Ndx  -- ^ Failover, replace secondary (f, r:ns)
263              deriving (Show, Read)
264
265 -- | Formatted solution output for one move (involved nodes and
266 -- commands.
267 type MoveJob = ([Ndx], Idx, IMove, [String])
268
269 -- | Unknown field in table output.
270 unknownField :: String
271 unknownField = "<unknown field>"
272
273 -- | A list of command elements.
274 type JobSet = [MoveJob]
275
276 -- | Connection timeout (when using non-file methods).
277 connTimeout :: Int
278 connTimeout = 15
279
280 -- | The default timeout for queries (when using non-file methods).
281 queryTimeout :: Int
282 queryTimeout = 60
283
284 -- | Default vcpu-to-pcpu ratio (randomly chosen value).
285 defVcpuRatio :: Double
286 defVcpuRatio = 64
287
288 -- | Default max disk usage ratio.
289 defReservedDiskRatio :: Double
290 defReservedDiskRatio = 0
291
292 -- | Base memory unit.
293 unitMem :: Int
294 unitMem = 64
295
296 -- | Base disk unit.
297 unitDsk :: Int
298 unitDsk = 256
299
300 -- | Base vcpus unit.
301 unitCpu :: Int
302 unitCpu = 1
303
304 -- | Reason for an operation's falure.
305 data FailMode = FailMem  -- ^ Failed due to not enough RAM
306               | FailDisk -- ^ Failed due to not enough disk
307               | FailCPU  -- ^ Failed due to not enough CPU capacity
308               | FailN1   -- ^ Failed due to not passing N1 checks
309               | FailTags -- ^ Failed due to tag exclusion
310                 deriving (Eq, Enum, Bounded, Show, Read)
311
312 -- | List with failure statistics.
313 type FailStats = [(FailMode, Int)]
314
315 -- | Either-like data-type customized for our failure modes.
316 --
317 -- The failure values for this monad track the specific allocation
318 -- failures, so this is not a general error-monad (compare with the
319 -- 'Result' data type). One downside is that this type cannot encode a
320 -- generic failure mode, hence 'fail' for this monad is not defined
321 -- and will cause an exception.
322 data OpResult a = OpFail FailMode -- ^ Failed operation
323                 | OpGood a        -- ^ Success operation
324                   deriving (Show, Read)
325
326 instance Monad OpResult where
327   (OpGood x) >>= fn = fn x
328   (OpFail y) >>= _ = OpFail y
329   return = OpGood
330
331 -- | Conversion from 'OpResult' to 'Result'.
332 opToResult :: OpResult a -> Result a
333 opToResult (OpFail f) = Bad $ show f
334 opToResult (OpGood v) = Ok v
335
336 -- | A generic class for items that have updateable names and indices.
337 class Element a where
338   -- | Returns the name of the element
339   nameOf  :: a -> String
340   -- | Returns all the known names of the element
341   allNames :: a -> [String]
342   -- | Returns the index of the element
343   idxOf   :: a -> Int
344   -- | Updates the alias of the element
345   setAlias :: a -> String -> a
346   -- | Compute the alias by stripping a given suffix (domain) from
347   -- the name
348   computeAlias :: String -> a -> a
349   computeAlias dom e = setAlias e alias
350     where alias = take (length name - length dom) name
351           name = nameOf e
352   -- | Updates the index of the element
353   setIdx  :: a -> Int -> a
354
355 -- | The iallocator node-evacuate evac_mode type.
356 $(THH.declareSADT "EvacMode"
357        [ ("ChangePrimary",   'C.iallocatorNevacPri)
358        , ("ChangeSecondary", 'C.iallocatorNevacSec)
359        , ("ChangeAll",       'C.iallocatorNevacAll)
360        ])
361 $(THH.makeJSONInstance ''EvacMode)