Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / HTools / Types.hs @ 1c7c4578

History | View | Annotate | Download (11.5 kB)

1
{-| Some common types.
2

    
3
-}
4

    
5
{-
6

    
7
Copyright (C) 2009, 2010, 2011 Google Inc.
8

    
9
This program is free software; you can redistribute it and/or modify
10
it under the terms of the GNU General Public License as published by
11
the Free Software Foundation; either version 2 of the License, or
12
(at your option) any later version.
13

    
14
This program is distributed in the hope that it will be useful, but
15
WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
General Public License for more details.
18

    
19
You should have received a copy of the GNU General Public License
20
along with this program; if not, write to the Free Software
21
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22
02110-1301, USA.
23

    
24
-}
25

    
26
module Ganeti.HTools.Types
27
    ( Idx
28
    , Ndx
29
    , Gdx
30
    , NameAssoc
31
    , Score
32
    , Weight
33
    , GroupID
34
    , AllocPolicy(..)
35
    , apolFromString
36
    , apolToString
37
    , RSpec(..)
38
    , DynUtil(..)
39
    , zeroUtil
40
    , baseUtil
41
    , addUtil
42
    , subUtil
43
    , defVcpuRatio
44
    , defReservedDiskRatio
45
    , unitMem
46
    , unitCpu
47
    , unitDsk
48
    , unknownField
49
    , Placement
50
    , IMove(..)
51
    , DiskTemplate(..)
52
    , dtToString
53
    , dtFromString
54
    , MoveJob
55
    , JobSet
56
    , Result(..)
57
    , isOk
58
    , isBad
59
    , eitherToResult
60
    , Element(..)
61
    , FailMode(..)
62
    , FailStats
63
    , OpResult(..)
64
    , opToResult
65
    , connTimeout
66
    , queryTimeout
67
    , EvacMode(..)
68
    ) where
69

    
70
import Control.Monad
71
import qualified Data.Map as M
72
import qualified Text.JSON as JSON
73

    
74
import qualified Ganeti.Constants as C
75

    
76
-- | The instance index type.
77
type Idx = Int
78

    
79
-- | The node index type.
80
type Ndx = Int
81

    
82
-- | The group index type.
83
type Gdx = Int
84

    
85
-- | The type used to hold name-to-idx mappings.
86
type NameAssoc = M.Map String Int
87

    
88
-- | A separate name for the cluster score type.
89
type Score = Double
90

    
91
-- | A separate name for a weight metric.
92
type Weight = Double
93

    
94
-- | The Group UUID type.
95
type GroupID = String
96

    
97
-- | The Group allocation policy type.
98
--
99
-- Note that the order of constructors is important as the automatic
100
-- Ord instance will order them in the order they are defined, so when
101
-- changing this data type be careful about the interaction with the
102
-- desired sorting order.
103
data AllocPolicy
104
    = AllocPreferred   -- ^ This is the normal status, the group
105
                       -- should be used normally during allocations
106
    | AllocLastResort  -- ^ This group should be used only as
107
                       -- last-resort, after the preferred groups
108
    | AllocUnallocable -- ^ This group must not be used for new
109
                       -- allocations
110
      deriving (Show, Read, Eq, Ord, Enum, Bounded)
111

    
112
-- | Convert a string to an alloc policy.
113
apolFromString :: (Monad m) => String -> m AllocPolicy
114
apolFromString s =
115
    case () of
116
      _ | s == C.allocPolicyPreferred -> return AllocPreferred
117
        | s == C.allocPolicyLastResort -> return AllocLastResort
118
        | s == C.allocPolicyUnallocable -> return AllocUnallocable
119
        | otherwise -> fail $ "Invalid alloc policy mode: " ++ s
120

    
121
-- | Convert an alloc policy to the Ganeti string equivalent.
122
apolToString :: AllocPolicy -> String
123
apolToString AllocPreferred   = C.allocPolicyPreferred
124
apolToString AllocLastResort  = C.allocPolicyLastResort
125
apolToString AllocUnallocable = C.allocPolicyUnallocable
126

    
127
instance JSON.JSON AllocPolicy where
128
    showJSON = JSON.showJSON . apolToString
129
    readJSON s = case JSON.readJSON s of
130
                   JSON.Ok s' -> apolFromString s'
131
                   JSON.Error e -> JSON.Error $
132
                                   "Can't parse alloc_policy: " ++ e
133

    
134
-- | The resource spec type.
135
data RSpec = RSpec
136
    { rspecCpu  :: Int  -- ^ Requested VCPUs
137
    , rspecMem  :: Int  -- ^ Requested memory
138
    , rspecDsk  :: Int  -- ^ Requested disk
139
    } deriving (Show, Read, Eq)
140

    
141
-- | The dynamic resource specs of a machine (i.e. load or load
142
-- capacity, as opposed to size).
143
data DynUtil = DynUtil
144
    { cpuWeight :: Weight -- ^ Standardised CPU usage
145
    , memWeight :: Weight -- ^ Standardised memory load
146
    , dskWeight :: Weight -- ^ Standardised disk I\/O usage
147
    , netWeight :: Weight -- ^ Standardised network usage
148
    } deriving (Show, Read, Eq)
149

    
150
-- | Initial empty utilisation.
151
zeroUtil :: DynUtil
152
zeroUtil = DynUtil { cpuWeight = 0, memWeight = 0
153
                   , dskWeight = 0, netWeight = 0 }
154

    
155
-- | Base utilisation (used when no actual utilisation data is
156
-- supplied).
157
baseUtil :: DynUtil
158
baseUtil = DynUtil { cpuWeight = 1, memWeight = 1
159
                   , dskWeight = 1, netWeight = 1 }
160

    
161
-- | Sum two utilisation records.
162
addUtil :: DynUtil -> DynUtil -> DynUtil
163
addUtil (DynUtil a1 a2 a3 a4) (DynUtil b1 b2 b3 b4) =
164
    DynUtil (a1+b1) (a2+b2) (a3+b3) (a4+b4)
165

    
166
-- | Substracts one utilisation record from another.
167
subUtil :: DynUtil -> DynUtil -> DynUtil
168
subUtil (DynUtil a1 a2 a3 a4) (DynUtil b1 b2 b3 b4) =
169
    DynUtil (a1-b1) (a2-b2) (a3-b3) (a4-b4)
170

    
171
-- | The description of an instance placement. It contains the
172
-- instance index, the new primary and secondary node, the move being
173
-- performed and the score of the cluster after the move.
174
type Placement = (Idx, Ndx, Ndx, IMove, Score)
175

    
176
-- | An instance move definition.
177
data IMove = Failover                -- ^ Failover the instance (f)
178
           | ReplacePrimary Ndx      -- ^ Replace primary (f, r:np, f)
179
           | ReplaceSecondary Ndx    -- ^ Replace secondary (r:ns)
180
           | ReplaceAndFailover Ndx  -- ^ Replace secondary, failover (r:np, f)
181
           | FailoverAndReplace Ndx  -- ^ Failover, replace secondary (f, r:ns)
182
             deriving (Show, Read)
183

    
184
-- | Instance disk template type.
185
data DiskTemplate = DTDiskless
186
                  | DTFile
187
                  | DTSharedFile
188
                  | DTPlain
189
                  | DTBlock
190
                  | DTDrbd8
191
                    deriving (Show, Read, Eq, Enum, Bounded)
192

    
193
-- | Converts a DiskTemplate to String.
194
dtToString :: DiskTemplate -> String
195
dtToString DTDiskless   = C.dtDiskless
196
dtToString DTFile       = C.dtFile
197
dtToString DTSharedFile = C.dtSharedFile
198
dtToString DTPlain      = C.dtPlain
199
dtToString DTBlock      = C.dtBlock
200
dtToString DTDrbd8      = C.dtDrbd8
201

    
202
-- | Converts a DiskTemplate from String.
203
dtFromString :: (Monad m) => String -> m DiskTemplate
204
dtFromString s =
205
    case () of
206
      _ | s == C.dtDiskless   -> return DTDiskless
207
        | s == C.dtFile       -> return DTFile
208
        | s == C.dtSharedFile -> return DTSharedFile
209
        | s == C.dtPlain      -> return DTPlain
210
        | s == C.dtBlock      -> return DTBlock
211
        | s == C.dtDrbd8      -> return DTDrbd8
212
        | otherwise           -> fail $ "Invalid disk template: " ++ s
213

    
214
instance JSON.JSON DiskTemplate where
215
    showJSON = JSON.showJSON . dtToString
216
    readJSON s = case JSON.readJSON s of
217
                   JSON.Ok s' -> dtFromString s'
218
                   JSON.Error e -> JSON.Error $
219
                                   "Can't parse disk_template as string: " ++ e
220

    
221
-- | Formatted solution output for one move (involved nodes and
222
-- commands.
223
type MoveJob = ([Ndx], Idx, IMove, [String])
224

    
225
-- | Unknown field in table output.
226
unknownField :: String
227
unknownField = "<unknown field>"
228

    
229
-- | A list of command elements.
230
type JobSet = [MoveJob]
231

    
232
-- | Connection timeout (when using non-file methods).
233
connTimeout :: Int
234
connTimeout = 15
235

    
236
-- | The default timeout for queries (when using non-file methods).
237
queryTimeout :: Int
238
queryTimeout = 60
239

    
240
-- | Default vcpu-to-pcpu ratio (randomly chosen value).
241
defVcpuRatio :: Double
242
defVcpuRatio = 64
243

    
244
-- | Default max disk usage ratio.
245
defReservedDiskRatio :: Double
246
defReservedDiskRatio = 0
247

    
248
-- | Base memory unit.
249
unitMem :: Int
250
unitMem = 64
251

    
252
-- | Base disk unit.
253
unitDsk :: Int
254
unitDsk = 256
255

    
256
-- | Base vcpus unit.
257
unitCpu :: Int
258
unitCpu = 1
259

    
260
-- | This is similar to the JSON library Result type - /very/ similar,
261
-- but we want to use it in multiple places, so we abstract it into a
262
-- mini-library here.
263
--
264
-- The failure value for this monad is simply a string.
265
data Result a
266
    = Bad String
267
    | Ok a
268
    deriving (Show, Read, Eq)
269

    
270
instance Monad Result where
271
    (>>=) (Bad x) _ = Bad x
272
    (>>=) (Ok x) fn = fn x
273
    return = Ok
274
    fail = Bad
275

    
276
instance MonadPlus Result where
277
    mzero = Bad "zero Result when used as MonadPlus"
278
    -- for mplus, when we 'add' two Bad values, we concatenate their
279
    -- error descriptions
280
    (Bad x) `mplus` (Bad y) = Bad (x ++ "; " ++ y)
281
    (Bad _) `mplus` x = x
282
    x@(Ok _) `mplus` _ = x
283

    
284
-- | Simple checker for whether a 'Result' is OK.
285
isOk :: Result a -> Bool
286
isOk (Ok _) = True
287
isOk _ = False
288

    
289
-- | Simple checker for whether a 'Result' is a failure.
290
isBad :: Result a  -> Bool
291
isBad = not . isOk
292

    
293
-- | Converter from Either String to 'Result'.
294
eitherToResult :: Either String a -> Result a
295
eitherToResult (Left s) = Bad s
296
eitherToResult (Right v) = Ok v
297

    
298
-- | Reason for an operation's falure.
299
data FailMode = FailMem  -- ^ Failed due to not enough RAM
300
              | FailDisk -- ^ Failed due to not enough disk
301
              | FailCPU  -- ^ Failed due to not enough CPU capacity
302
              | FailN1   -- ^ Failed due to not passing N1 checks
303
              | FailTags -- ^ Failed due to tag exclusion
304
                deriving (Eq, Enum, Bounded, Show, Read)
305

    
306
-- | List with failure statistics.
307
type FailStats = [(FailMode, Int)]
308

    
309
-- | Either-like data-type customized for our failure modes.
310
--
311
-- The failure values for this monad track the specific allocation
312
-- failures, so this is not a general error-monad (compare with the
313
-- 'Result' data type). One downside is that this type cannot encode a
314
-- generic failure mode, hence 'fail' for this monad is not defined
315
-- and will cause an exception.
316
data OpResult a = OpFail FailMode -- ^ Failed operation
317
                | OpGood a        -- ^ Success operation
318
                  deriving (Show, Read)
319

    
320
instance Monad OpResult where
321
    (OpGood x) >>= fn = fn x
322
    (OpFail y) >>= _ = OpFail y
323
    return = OpGood
324

    
325
-- | Conversion from 'OpResult' to 'Result'.
326
opToResult :: OpResult a -> Result a
327
opToResult (OpFail f) = Bad $ show f
328
opToResult (OpGood v) = Ok v
329

    
330
-- | A generic class for items that have updateable names and indices.
331
class Element a where
332
    -- | Returns the name of the element
333
    nameOf  :: a -> String
334
    -- | Returns all the known names of the element
335
    allNames :: a -> [String]
336
    -- | Returns the index of the element
337
    idxOf   :: a -> Int
338
    -- | Updates the alias of the element
339
    setAlias :: a -> String -> a
340
    -- | Compute the alias by stripping a given suffix (domain) from
341
    -- the name
342
    computeAlias :: String -> a -> a
343
    computeAlias dom e = setAlias e alias
344
        where alias = take (length name - length dom) name
345
              name = nameOf e
346
    -- | Updates the index of the element
347
    setIdx  :: a -> Int -> a
348

    
349
-- | The iallocator node-evacuate evac_mode type.
350
data EvacMode = ChangePrimary
351
              | ChangeSecondary
352
              | ChangeAll
353
                deriving (Show, Read)
354

    
355
instance JSON.JSON EvacMode where
356
    showJSON mode = case mode of
357
                      ChangeAll       -> JSON.showJSON C.iallocatorNevacAll
358
                      ChangePrimary   -> JSON.showJSON C.iallocatorNevacPri
359
                      ChangeSecondary -> JSON.showJSON C.iallocatorNevacSec
360
    readJSON v =
361
        case JSON.readJSON v of
362
          JSON.Ok s | s == C.iallocatorNevacAll -> return ChangeAll
363
                    | s == C.iallocatorNevacPri -> return ChangePrimary
364
                    | s == C.iallocatorNevacSec -> return ChangeSecondary
365
                    | otherwise -> fail $ "Invalid evacuate mode " ++ s
366
          JSON.Error e -> JSON.Error $
367
                          "Can't parse evacuate mode as string: " ++ e