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