Revision ebf38064

b/htools/Ganeti/HTools/Compat.hs
2 2

  
3 3
{- | Compatibility helper module.
4 4

  
5
This module holds definitions that help with supporting multiple library versions or transitions between versions.
5
This module holds definitions that help with supporting multiple
6
library versions or transitions between versions.
6 7

  
7 8
-}
8 9

  
......
28 29
-}
29 30

  
30 31
module Ganeti.HTools.Compat
31
    ( rwhnf
32
    , Control.Parallel.Strategies.parMap
33
    ) where
32
  ( rwhnf
33
  , Control.Parallel.Strategies.parMap
34
  ) where
34 35

  
35 36
import qualified Control.Parallel.Strategies
36 37

  
b/htools/Ganeti/HTools/Container.hs
27 27
-}
28 28

  
29 29
module Ganeti.HTools.Container
30
    (
31
     -- * Types
32
     Container
33
    , Key
34
     -- * Creation
35
    , IntMap.empty
36
    , IntMap.singleton
37
    , IntMap.fromList
38
     -- * Query
39
    , IntMap.size
40
    , IntMap.null
41
    , find
42
    , IntMap.findMax
43
    , IntMap.member
44
     -- * Update
45
    , add
46
    , addTwo
47
    , IntMap.map
48
    , IntMap.mapAccum
49
    , IntMap.filter
50
    -- * Conversion
51
    , IntMap.elems
52
    , IntMap.keys
53
    -- * Element functions
54
    , nameOf
55
    , findByName
56
    ) where
30
  ( -- * Types
31
    Container
32
  , Key
33
  -- * Creation
34
  , IntMap.empty
35
  , IntMap.singleton
36
  , IntMap.fromList
37
  -- * Query
38
  , IntMap.size
39
  , IntMap.null
40
  , find
41
  , IntMap.findMax
42
  , IntMap.member
43
  -- * Update
44
  , add
45
  , addTwo
46
  , IntMap.map
47
  , IntMap.mapAccum
48
  , IntMap.filter
49
  -- * Conversion
50
  , IntMap.elems
51
  , IntMap.keys
52
  -- * Element functions
53
  , nameOf
54
  , findByName
55
  ) where
57 56

  
58 57
import qualified Data.IntMap as IntMap
59 58

  
......
86 85
findByName :: (T.Element a, Monad m) =>
87 86
              Container a -> String -> m a
88 87
findByName c n =
89
    let all_elems = IntMap.elems c
90
        result = filter ((n `elem`) . T.allNames) all_elems
91
    in case result of
92
         [item] -> return item
93
         _ -> fail $ "Wrong number of elems found with name " ++ n
88
  let all_elems = IntMap.elems c
89
      result = filter ((n `elem`) . T.allNames) all_elems
90
  in case result of
91
       [item] -> return item
92
       _ -> fail $ "Wrong number of elems found with name " ++ n
b/htools/Ganeti/HTools/ExtLoader.hs
28 28
-}
29 29

  
30 30
module Ganeti.HTools.ExtLoader
31
    ( loadExternalData
32
    , commonSuffix
33
    , maybeSaveData
34
    ) where
31
  ( loadExternalData
32
  , commonSuffix
33
  , maybeSaveData
34
  ) where
35 35

  
36 36
import Control.Monad
37 37
import Data.Maybe (isJust, fromJust)
......
58 58
-- | Parses a user-supplied utilisation string.
59 59
parseUtilisation :: String -> Result (String, DynUtil)
60 60
parseUtilisation line =
61
    case sepSplit ' ' line of
62
      [name, cpu, mem, dsk, net] ->
63
          do
64
            rcpu <- tryRead name cpu
65
            rmem <- tryRead name mem
66
            rdsk <- tryRead name dsk
67
            rnet <- tryRead name net
68
            let du = DynUtil { cpuWeight = rcpu, memWeight = rmem
69
                             , dskWeight = rdsk, netWeight = rnet }
70
            return (name, du)
71
      _ -> Bad $ "Cannot parse line " ++ line
61
  case sepSplit ' ' line of
62
    [name, cpu, mem, dsk, net] ->
63
      do
64
        rcpu <- tryRead name cpu
65
        rmem <- tryRead name mem
66
        rdsk <- tryRead name dsk
67
        rnet <- tryRead name net
68
        let du = DynUtil { cpuWeight = rcpu, memWeight = rmem
69
                         , dskWeight = rdsk, netWeight = rnet }
70
        return (name, du)
71
    _ -> Bad $ "Cannot parse line " ++ line
72 72

  
73 73
-- | External tool data loader from a variety of sources.
74 74
loadExternalData :: Options
......
100 100
                      Nothing -> return "")
101 101
  let util_data = mapM parseUtilisation $ lines util_contents
102 102
  util_data' <- (case util_data of
103
                   Ok x -> return x
103
                   Ok x  -> return x
104 104
                   Bad y -> do
105 105
                     hPutStrLn stderr ("Error: can't parse utilisation" ++
106 106
                                       " data: " ++ show y)
107 107
                     exitWith $ ExitFailure 1)
108 108
  input_data <-
109
      case () of
110
        _ | setRapi -> wrapIO $ Rapi.loadData mhost
111
          | setLuxi -> wrapIO $ Luxi.loadData $ fromJust lsock
112
          | setSim -> Simu.loadData simdata
113
          | setFile -> wrapIO $ Text.loadData $ fromJust tfile
114
          | otherwise -> return $ Bad "No backend selected! Exiting."
109
    case () of
110
      _ | setRapi -> wrapIO $ Rapi.loadData mhost
111
        | setLuxi -> wrapIO $ Luxi.loadData $ fromJust lsock
112
        | setSim -> Simu.loadData simdata
113
        | setFile -> wrapIO $ Text.loadData $ fromJust tfile
114
        | otherwise -> return $ Bad "No backend selected! Exiting."
115 115

  
116 116
  let ldresult = input_data >>= mergeData util_data' exTags selInsts exInsts
117 117
  cdata <-
118
      (case ldresult of
119
         Ok x -> return x
120
         Bad s -> do
121
           hPrintf stderr
122
             "Error: failed to load data, aborting. Details:\n%s\n" s:: IO ()
123
           exitWith $ ExitFailure 1
124
      )
118
    (case ldresult of
119
       Ok x -> return x
120
       Bad s -> do
121
         hPrintf stderr
122
           "Error: failed to load data, aborting. Details:\n%s\n" s:: IO ()
123
         exitWith $ ExitFailure 1
124
    )
125 125
  let (fix_msgs, nl) = checkData (cdNodes cdata) (cdInstances cdata)
126 126

  
127 127
  unless (optVerbose opts == 0) $ maybeShowWarnings fix_msgs
b/htools/Ganeti/HTools/Group.hs
24 24
-}
25 25

  
26 26
module Ganeti.HTools.Group
27
    ( Group(..)
28
    , List
29
    , AssocList
30
    -- * Constructor
31
    , create
32
    , setIdx
33
    , isAllocable
34
    ) where
27
  ( Group(..)
28
  , List
29
  , AssocList
30
  -- * Constructor
31
  , create
32
  , setIdx
33
  , isAllocable
34
  ) where
35 35

  
36 36
import qualified Ganeti.HTools.Container as Container
37 37

  
......
41 41

  
42 42
-- | The node group type.
43 43
data Group = Group
44
    { name        :: String        -- ^ The node name
45
    , uuid        :: T.GroupID     -- ^ The UUID of the group
46
    , idx         :: T.Gdx         -- ^ Internal index for book-keeping
47
    , allocPolicy :: T.AllocPolicy -- ^ The allocation policy for this group
48
    } deriving (Show, Read, Eq)
44
  { name        :: String        -- ^ The node name
45
  , uuid        :: T.GroupID     -- ^ The UUID of the group
46
  , idx         :: T.Gdx         -- ^ Internal index for book-keeping
47
  , allocPolicy :: T.AllocPolicy -- ^ The allocation policy for this group
48
  } deriving (Show, Read, Eq)
49 49

  
50 50
-- Note: we use the name as the alias, and the UUID as the official
51 51
-- name
52 52
instance T.Element Group where
53
    nameOf     = uuid
54
    idxOf      = idx
55
    setAlias   = setName
56
    setIdx     = setIdx
57
    allNames n = [name n, uuid n]
53
  nameOf     = uuid
54
  idxOf      = idx
55
  setAlias   = setName
56
  setIdx     = setIdx
57
  allNames n = [name n, uuid n]
58 58

  
59 59
-- | A simple name for the int, node association list.
60 60
type AssocList = [(T.Gdx, Group)]
......
67 67
-- | Create a new group.
68 68
create :: String -> T.GroupID -> T.AllocPolicy -> Group
69 69
create name_init id_init apol_init =
70
    Group { name        = name_init
71
          , uuid        = id_init
72
          , allocPolicy = apol_init
73
          , idx         = -1
74
          }
70
  Group { name        = name_init
71
        , uuid        = id_init
72
        , allocPolicy = apol_init
73
        , idx         = -1
74
        }
75 75

  
76 76
-- | Sets the group index.
77 77
--
b/htools/Ganeti/HTools/Instance.hs
27 27
-}
28 28

  
29 29
module Ganeti.HTools.Instance
30
    ( Instance(..)
31
    , AssocList
32
    , List
33
    , create
34
    , instanceRunning
35
    , instanceOffline
36
    , instanceDown
37
    , applyIfOnline
38
    , setIdx
39
    , setName
40
    , setAlias
41
    , setPri
42
    , setSec
43
    , setBoth
44
    , setMovable
45
    , specOf
46
    , shrinkByType
47
    , localStorageTemplates
48
    , hasSecondary
49
    , requiredNodes
50
    , allNodes
51
    , usesLocalStorage
52
    ) where
30
  ( Instance(..)
31
  , AssocList
32
  , List
33
  , create
34
  , instanceRunning
35
  , instanceOffline
36
  , instanceDown
37
  , applyIfOnline
38
  , setIdx
39
  , setName
40
  , setAlias
41
  , setPri
42
  , setSec
43
  , setBoth
44
  , setMovable
45
  , specOf
46
  , shrinkByType
47
  , localStorageTemplates
48
  , hasSecondary
49
  , requiredNodes
50
  , allNodes
51
  , usesLocalStorage
52
  ) where
53 53

  
54 54
import qualified Ganeti.HTools.Types as T
55 55
import qualified Ganeti.HTools.Container as Container
......
61 61

  
62 62
-- | The instance type.
63 63
data Instance = Instance
64
    { name         :: String    -- ^ The instance name
65
    , alias        :: String    -- ^ The shortened name
66
    , mem          :: Int       -- ^ Memory of the instance
67
    , dsk          :: Int       -- ^ Disk size of instance
68
    , vcpus        :: Int       -- ^ Number of VCPUs
69
    , runSt        :: T.InstanceStatus -- ^ Original run status
70
    , pNode        :: T.Ndx     -- ^ Original primary node
71
    , sNode        :: T.Ndx     -- ^ Original secondary node
72
    , idx          :: T.Idx     -- ^ Internal index
73
    , util         :: T.DynUtil -- ^ Dynamic resource usage
74
    , movable      :: Bool      -- ^ Can and should the instance be moved?
75
    , autoBalance  :: Bool      -- ^ Is the instance auto-balanced?
76
    , tags         :: [String]  -- ^ List of instance tags
77
    , diskTemplate :: T.DiskTemplate -- ^ The disk template of the instance
78
    } deriving (Show, Read)
64
  { name         :: String    -- ^ The instance name
65
  , alias        :: String    -- ^ The shortened name
66
  , mem          :: Int       -- ^ Memory of the instance
67
  , dsk          :: Int       -- ^ Disk size of instance
68
  , vcpus        :: Int       -- ^ Number of VCPUs
69
  , runSt        :: T.InstanceStatus -- ^ Original run status
70
  , pNode        :: T.Ndx     -- ^ Original primary node
71
  , sNode        :: T.Ndx     -- ^ Original secondary node
72
  , idx          :: T.Idx     -- ^ Internal index
73
  , util         :: T.DynUtil -- ^ Dynamic resource usage
74
  , movable      :: Bool      -- ^ Can and should the instance be moved?
75
  , autoBalance  :: Bool      -- ^ Is the instance auto-balanced?
76
  , tags         :: [String]  -- ^ List of instance tags
77
  , diskTemplate :: T.DiskTemplate -- ^ The disk template of the instance
78
  } deriving (Show, Read)
79 79

  
80 80
instance T.Element Instance where
81
    nameOf   = name
82
    idxOf    = idx
83
    setAlias = setAlias
84
    setIdx   = setIdx
85
    allNames n = [name n, alias n]
81
  nameOf   = name
82
  idxOf    = idx
83
  setAlias = setAlias
84
  setIdx   = setIdx
85
  allNames n = [name n, alias n]
86 86

  
87 87
-- | Check if instance is running.
88 88
instanceRunning :: Instance -> Bool
89 89
instanceRunning (Instance {runSt = T.Running}) = True
90 90
instanceRunning (Instance {runSt = T.ErrorUp}) = True
91
instanceRunning _                               = False
91
instanceRunning _                              = False
92 92

  
93 93
-- | Check if instance is offline.
94 94
instanceOffline :: Instance -> Bool
95 95
instanceOffline (Instance {runSt = T.AdminOffline}) = True
96
instanceOffline _                                    = False
96
instanceOffline _                                   = False
97 97

  
98 98
-- | Check if instance is down.
99 99
instanceDown :: Instance -> Bool
......
141 141
       -> [String] -> Bool -> T.Ndx -> T.Ndx -> T.DiskTemplate -> Instance
142 142
create name_init mem_init dsk_init vcpus_init run_init tags_init
143 143
       auto_balance_init pn sn dt =
144
    Instance { name = name_init
145
             , alias = name_init
146
             , mem = mem_init
147
             , dsk = dsk_init
148
             , vcpus = vcpus_init
149
             , runSt = run_init
150
             , pNode = pn
151
             , sNode = sn
152
             , idx = -1
153
             , util = T.baseUtil
154
             , tags = tags_init
155
             , movable = supportsMoves dt
156
             , autoBalance = auto_balance_init
157
             , diskTemplate = dt
158
             }
144
  Instance { name = name_init
145
           , alias = name_init
146
           , mem = mem_init
147
           , dsk = dsk_init
148
           , vcpus = vcpus_init
149
           , runSt = run_init
150
           , pNode = pn
151
           , sNode = sn
152
           , idx = -1
153
           , util = T.baseUtil
154
           , tags = tags_init
155
           , movable = supportsMoves dt
156
           , autoBalance = auto_balance_init
157
           , diskTemplate = dt
158
           }
159 159

  
160 160
-- | Changes the index.
161 161
--
......
228 228
-- | Return the spec of an instance.
229 229
specOf :: Instance -> T.RSpec
230 230
specOf Instance { mem = m, dsk = d, vcpus = c } =
231
    T.RSpec { T.rspecCpu = c, T.rspecMem = m, T.rspecDsk = d }
231
  T.RSpec { T.rspecCpu = c, T.rspecMem = m, T.rspecDsk = d }
232 232

  
233 233
-- | Checks whether the instance uses a secondary node.
234 234
--
b/htools/Ganeti/HTools/JSON.hs
22 22
-}
23 23

  
24 24
module Ganeti.HTools.JSON
25
    ( fromJResult
26
    , readEitherString
27
    , JSRecord
28
    , loadJSArray
29
    , fromObj
30
    , maybeFromObj
31
    , fromObjWithDefault
32
    , fromJVal
33
    , asJSObject
34
    , asObjectList
35
    )
36
    where
25
  ( fromJResult
26
  , readEitherString
27
  , JSRecord
28
  , loadJSArray
29
  , fromObj
30
  , maybeFromObj
31
  , fromObjWithDefault
32
  , fromJVal
33
  , asJSObject
34
  , asObjectList
35
  )
36
  where
37 37

  
38 38
import Control.Monad (liftM)
39 39
import Data.Maybe (fromMaybe)
......
57 57
-- context of the current monad.
58 58
readEitherString :: (Monad m) => J.JSValue -> m String
59 59
readEitherString v =
60
    case v of
61
      J.JSString s -> return $ J.fromJSString s
62
      _ -> fail "Wrong JSON type"
60
  case v of
61
    J.JSString s -> return $ J.fromJSString s
62
    _ -> fail "Wrong JSON type"
63 63

  
64 64
-- | Converts a JSON message into an array of JSON objects.
65 65
loadJSArray :: (Monad m)
......
71 71
-- | Reads the value of a key in a JSON object.
72 72
fromObj :: (J.JSON a, Monad m) => JSRecord -> String -> m a
73 73
fromObj o k =
74
    case lookup k o of
75
      Nothing -> fail $ printf "key '%s' not found, object contains only %s"
76
                 k (show (map fst o))
77
      Just val -> fromKeyValue k val
74
  case lookup k o of
75
    Nothing -> fail $ printf "key '%s' not found, object contains only %s"
76
               k (show (map fst o))
77
    Just val -> fromKeyValue k val
78 78

  
79 79
-- | Reads the value of an optional key in a JSON object.
80 80
maybeFromObj :: (J.JSON a, Monad m) =>
81 81
                JSRecord -> String -> m (Maybe a)
82 82
maybeFromObj o k =
83
    case lookup k o of
84
      Nothing -> return Nothing
85
      Just val -> liftM Just (fromKeyValue k val)
83
  case lookup k o of
84
    Nothing -> return Nothing
85
    Just val -> liftM Just (fromKeyValue k val)
86 86

  
87 87
-- | Reads the value of a key in a JSON object with a default if missing.
88 88
fromObjWithDefault :: (J.JSON a, Monad m) =>
......
100 100
-- | Small wrapper over readJSON.
101 101
fromJVal :: (Monad m, J.JSON a) => J.JSValue -> m a
102 102
fromJVal v =
103
    case J.readJSON v of
104
      J.Error s -> fail ("Cannot convert value '" ++ show v ++
105
                         "', error: " ++ s)
106
      J.Ok x -> return x
103
  case J.readJSON v of
104
    J.Error s -> fail ("Cannot convert value '" ++ show v ++
105
                       "', error: " ++ s)
106
    J.Ok x -> return x
107 107

  
108 108
-- | Converts a JSON value into a JSON object.
109 109
asJSObject :: (Monad m) => J.JSValue -> m (J.JSObject J.JSValue)
b/htools/Ganeti/HTools/Loader.hs
27 27
-}
28 28

  
29 29
module Ganeti.HTools.Loader
30
    ( mergeData
31
    , checkData
32
    , assignIndices
33
    , lookupName
34
    , goodLookupResult
35
    , lookupNode
36
    , lookupInstance
37
    , lookupGroup
38
    , commonSuffix
39
    , RqType(..)
40
    , Request(..)
41
    , ClusterData(..)
42
    , emptyCluster
43
    , compareNameComponent
44
    , prefixMatch
45
    , LookupResult(..)
46
    , MatchPriority(..)
47
    ) where
30
  ( mergeData
31
  , checkData
32
  , assignIndices
33
  , lookupName
34
  , goodLookupResult
35
  , lookupNode
36
  , lookupInstance
37
  , lookupGroup
38
  , commonSuffix
39
  , RqType(..)
40
  , Request(..)
41
  , ClusterData(..)
42
  , emptyCluster
43
  , compareNameComponent
44
  , prefixMatch
45
  , LookupResult(..)
46
  , MatchPriority(..)
47
  ) where
48 48

  
49 49
import Data.List
50 50
import Data.Function
......
74 74

  
75 75
-}
76 76
data RqType
77
    = Allocate Instance.Instance Int -- ^ A new instance allocation
78
    | Relocate Idx Int [Ndx]         -- ^ Choose a new secondary node
79
    | NodeEvacuate [Idx] EvacMode    -- ^ node-evacuate mode
80
    | ChangeGroup [Gdx] [Idx]        -- ^ Multi-relocate mode
77
  = Allocate Instance.Instance Int -- ^ A new instance allocation
78
  | Relocate Idx Int [Ndx]         -- ^ Choose a new secondary node
79
  | NodeEvacuate [Idx] EvacMode    -- ^ node-evacuate mode
80
  | ChangeGroup [Gdx] [Idx]        -- ^ Multi-relocate mode
81 81
    deriving (Show, Read)
82 82

  
83 83
-- | A complete request, as received from Ganeti.
84 84
data Request = Request RqType ClusterData
85
    deriving (Show, Read)
85
               deriving (Show, Read)
86 86

  
87 87
-- | The cluster state.
88 88
data ClusterData = ClusterData
89
    { cdGroups    :: Group.List    -- ^ The node group list
90
    , cdNodes     :: Node.List     -- ^ The node list
91
    , cdInstances :: Instance.List -- ^ The instance list
92
    , cdTags      :: [String]      -- ^ The cluster tags
93
    } deriving (Show, Read)
89
  { cdGroups    :: Group.List    -- ^ The node group list
90
  , cdNodes     :: Node.List     -- ^ The node list
91
  , cdInstances :: Instance.List -- ^ The instance list
92
  , cdTags      :: [String]      -- ^ The cluster tags
93
  } deriving (Show, Read)
94 94

  
95 95
-- | The priority of a match in a lookup result.
96 96
data MatchPriority = ExactMatch
......
101 101

  
102 102
-- | The result of a name lookup in a list.
103 103
data LookupResult = LookupResult
104
    { lrMatchPriority :: MatchPriority -- ^ The result type
105
    -- | Matching value (for ExactMatch, PartialMatch), Lookup string otherwise
106
    , lrContent :: String
107
    } deriving (Show, Read)
104
  { lrMatchPriority :: MatchPriority -- ^ The result type
105
  -- | Matching value (for ExactMatch, PartialMatch), Lookup string otherwise
106
  , lrContent :: String
107
  } deriving (Show, Read)
108 108

  
109 109
-- | Lookup results have an absolute preference ordering.
110 110
instance Eq LookupResult where
......
122 122
-- | Lookups a node into an assoc list.
123 123
lookupNode :: (Monad m) => NameAssoc -> String -> String -> m Ndx
124 124
lookupNode ktn inst node =
125
    case M.lookup node ktn of
126
      Nothing -> fail $ "Unknown node '" ++ node ++ "' for instance " ++ inst
127
      Just idx -> return idx
125
  case M.lookup node ktn of
126
    Nothing -> fail $ "Unknown node '" ++ node ++ "' for instance " ++ inst
127
    Just idx -> return idx
128 128

  
129 129
-- | Lookups an instance into an assoc list.
130 130
lookupInstance :: (Monad m) => NameAssoc -> String -> m Idx
131 131
lookupInstance kti inst =
132
    case M.lookup inst kti of
133
      Nothing -> fail $ "Unknown instance '" ++ inst ++ "'"
134
      Just idx -> return idx
132
  case M.lookup inst kti of
133
    Nothing -> fail $ "Unknown instance '" ++ inst ++ "'"
134
    Just idx -> return idx
135 135

  
136 136
-- | Lookups a group into an assoc list.
137 137
lookupGroup :: (Monad m) => NameAssoc -> String -> String -> m Gdx
138 138
lookupGroup ktg nname gname =
139
    case M.lookup gname ktg of
140
      Nothing -> fail $ "Unknown group '" ++ gname ++ "' for node " ++ nname
141
      Just idx -> return idx
139
  case M.lookup gname ktg of
140
    Nothing -> fail $ "Unknown group '" ++ gname ++ "' for node " ++ nname
141
    Just idx -> return idx
142 142

  
143 143
-- | Check for prefix matches in names.
144 144
-- Implemented in Ganeti core utils.text.MatchNameComponent
......
206 206
         -> Instance.Instance
207 207
         -> Node.List
208 208
fixNodes accu inst =
209
    let
210
        pdx = Instance.pNode inst
211
        sdx = Instance.sNode inst
212
        pold = Container.find pdx accu
213
        pnew = Node.setPri pold inst
214
        ac2 = Container.add pdx pnew accu
215
    in
216
      if sdx /= Node.noSecondary
217
      then let sold = Container.find sdx accu
218
               snew = Node.setSec sold inst
219
           in Container.add sdx snew ac2
220
      else ac2
209
  let pdx = Instance.pNode inst
210
      sdx = Instance.sNode inst
211
      pold = Container.find pdx accu
212
      pnew = Node.setPri pold inst
213
      ac2 = Container.add pdx pnew accu
214
  in if sdx /= Node.noSecondary
215
       then let sold = Container.find sdx accu
216
                snew = Node.setSec sold inst
217
            in Container.add sdx snew ac2
218
       else ac2
221 219

  
222 220
-- | Remove non-selected tags from the exclusion list.
223 221
filterExTags :: [String] -> Instance.Instance -> Instance.Instance
224 222
filterExTags tl inst =
225
    let old_tags = Instance.tags inst
226
        new_tags = filter (\tag -> any (`isPrefixOf` tag) tl)
227
                   old_tags
228
    in inst { Instance.tags = new_tags }
223
  let old_tags = Instance.tags inst
224
      new_tags = filter (\tag -> any (`isPrefixOf` tag) tl) old_tags
225
  in inst { Instance.tags = new_tags }
229 226

  
230 227
-- | Update the movable attribute.
231 228
updateMovable :: [String]           -- ^ Selected instances (if not empty)
......
233 230
              -> Instance.Instance  -- ^ Target Instance
234 231
              -> Instance.Instance  -- ^ Target Instance with updated attribute
235 232
updateMovable selinsts exinsts inst =
236
    if Instance.sNode inst == Node.noSecondary ||
237
       Instance.name inst `elem` exinsts ||
238
       not (null selinsts || Instance.name inst `elem` selinsts)
233
  if Instance.sNode inst == Node.noSecondary ||
234
     Instance.name inst `elem` exinsts ||
235
     not (null selinsts || Instance.name inst `elem` selinsts)
239 236
    then Instance.setMovable inst False
240 237
    else inst
241 238

  
......
244 241
longestDomain :: [String] -> String
245 242
longestDomain [] = ""
246 243
longestDomain (x:xs) =
247
      foldr (\ suffix accu -> if all (isSuffixOf suffix) xs
248
                              then suffix
249
                              else accu)
250
      "" $ filter (isPrefixOf ".") (tails x)
244
  foldr (\ suffix accu -> if all (isSuffixOf suffix) xs
245
                            then suffix
246
                            else accu)
247
          "" $ filter (isPrefixOf ".") (tails x)
251 248

  
252 249
-- | Extracts the exclusion tags from the cluster configuration.
253 250
extractExTags :: [String] -> [String]
254 251
extractExTags =
255
    map (drop (length exTagsPrefix)) .
256
    filter (isPrefixOf exTagsPrefix)
252
  map (drop (length exTagsPrefix)) .
253
  filter (isPrefixOf exTagsPrefix)
257 254

  
258 255
-- | Extracts the common suffix from node\/instance names.
259 256
commonSuffix :: Node.List -> Instance.List -> String
260 257
commonSuffix nl il =
261
    let node_names = map Node.name $ Container.elems nl
262
        inst_names = map Instance.name $ Container.elems il
263
    in longestDomain (node_names ++ inst_names)
258
  let node_names = map Node.name $ Container.elems nl
259
      inst_names = map Instance.name $ Container.elems il
260
  in longestDomain (node_names ++ inst_names)
264 261

  
265 262
-- | Initializer function that loads the data from a node and instance
266 263
-- list and massages it into the correct format.
......
328 325
-- | Compute the amount of memory used by primary instances on a node.
329 326
nodeImem :: Node.Node -> Instance.List -> Int
330 327
nodeImem node il =
331
    let rfind = flip Container.find il
332
        il' = map rfind $ Node.pList node
333
        oil' = filter (not . Instance.instanceOffline) il'
334
    in sum . map Instance.mem $ oil'
328
  let rfind = flip Container.find il
329
      il' = map rfind $ Node.pList node
330
      oil' = filter (not . Instance.instanceOffline) il'
331
  in sum . map Instance.mem $ oil'
335 332

  
336 333

  
337 334
-- | Compute the amount of disk used by instances on a node (either primary
338 335
-- or secondary).
339 336
nodeIdsk :: Node.Node -> Instance.List -> Int
340 337
nodeIdsk node il =
341
    let rfind = flip Container.find il
342
    in sum . map (Instance.dsk . rfind)
343
           $ Node.pList node ++ Node.sList node
338
  let rfind = flip Container.find il
339
  in sum . map (Instance.dsk . rfind)
340
       $ Node.pList node ++ Node.sList node
b/htools/Ganeti/HTools/Luxi.hs
24 24
-}
25 25

  
26 26
module Ganeti.HTools.Luxi
27
    (
28
      loadData
29
    , parseData
30
    ) where
27
  ( loadData
28
  , parseData
29
  ) where
31 30

  
32 31
import qualified Control.Exception as E
33 32
import Text.JSON.Types
......
53 52
parseQueryField :: (Monad m) => JSValue -> m (JSValue, JSValue)
54 53
parseQueryField (JSArray [status, result]) = return (status, result)
55 54
parseQueryField o =
56
    fail $ "Invalid query field, expected (status, value) but got " ++ show o
55
  fail $ "Invalid query field, expected (status, value) but got " ++ show o
57 56

  
58 57
-- | Parse a result row.
59 58
parseQueryRow :: (Monad m) => JSValue -> m [(JSValue, JSValue)]
60 59
parseQueryRow (JSArray arr) = mapM parseQueryField arr
61 60
parseQueryRow o =
62
    fail $ "Invalid query row result, expected array but got " ++ show o
61
  fail $ "Invalid query row result, expected array but got " ++ show o
63 62

  
64 63
-- | Parse an overall query result and get the [(status, value)] list
65 64
-- for each element queried.
66 65
parseQueryResult :: (Monad m) => JSValue -> m [[(JSValue, JSValue)]]
67 66
parseQueryResult (JSArray arr) = mapM parseQueryRow arr
68 67
parseQueryResult o =
69
    fail $ "Invalid query result, expected array but got " ++ show o
68
  fail $ "Invalid query result, expected array but got " ++ show o
70 69

  
71 70
-- | Prepare resulting output as parsers expect it.
72 71
extractArray :: (Monad m) => JSValue -> m [[(JSValue, JSValue)]]
......
76 75
-- | Testing result status for more verbose error message.
77 76
fromJValWithStatus :: (Text.JSON.JSON a, Monad m) => (JSValue, JSValue) -> m a
78 77
fromJValWithStatus (st, v) = do
79
    st' <- fromJVal st
80
    L.checkRS st' v >>= fromJVal
78
  st' <- fromJVal st
79
  L.checkRS st' v >>= fromJVal
81 80

  
82 81
-- | Annotate errors when converting values with owner/attribute for
83 82
-- better debugging.
......
88 87
               -> (JSValue, JSValue) -- ^ The value we're trying to convert
89 88
               -> Result a           -- ^ The annotated result
90 89
genericConvert otype oname oattr =
91
    annotateResult (otype ++ " '" ++ oname ++
92
                    "', error while reading attribute '" ++
93
                    oattr ++ "'") . fromJValWithStatus
90
  annotateResult (otype ++ " '" ++ oname ++
91
                  "', error while reading attribute '" ++
92
                  oattr ++ "'") . fromJValWithStatus
94 93

  
95 94
-- * Data querying functionality
96 95

  
......
104 103
-- | The input data for instance query.
105 104
queryInstancesMsg :: L.LuxiOp
106 105
queryInstancesMsg =
107
    L.Query L.QRInstance ["name", "disk_usage", "be/memory", "be/vcpus",
108
                          "status", "pnode", "snodes", "tags", "oper_ram",
109
                          "be/auto_balance", "disk_template"] ()
106
  L.Query L.QRInstance ["name", "disk_usage", "be/memory", "be/vcpus",
107
                        "status", "pnode", "snodes", "tags", "oper_ram",
108
                        "be/auto_balance", "disk_template"] ()
110 109

  
111 110
-- | The input data for cluster query.
112 111
queryClusterInfoMsg :: L.LuxiOp
b/htools/Ganeti/HTools/PeerMap.hs
8 8

  
9 9
{-
10 10

  
11
Copyright (C) 2009 Google Inc.
11
Copyright (C) 2009, 2011 Google Inc.
12 12

  
13 13
This program is free software; you can redistribute it and/or modify
14 14
it under the terms of the GNU General Public License as published by
......
28 28
-}
29 29

  
30 30
module Ganeti.HTools.PeerMap
31
    ( PeerMap
32
    , Key
33
    , Elem
34
    , empty
35
    , accumArray
36
    , Ganeti.HTools.PeerMap.find
37
    , add
38
    , remove
39
    , maxElem
40
    ) where
31
  ( PeerMap
32
  , Key
33
  , Elem
34
  , empty
35
  , accumArray
36
  , Ganeti.HTools.PeerMap.find
37
  , add
38
  , remove
39
  , maxElem
40
  ) where
41 41

  
42 42
import Data.Maybe (fromMaybe)
43 43
import Data.List
......
70 70
-- | Add or update (via a custom function) an element.
71 71
addWith :: (Elem -> Elem -> Elem) -> Key -> Elem -> PeerMap -> PeerMap
72 72
addWith fn k v lst =
73
    case lookup k lst of
74
      Nothing -> insertBy pmCompare (k, v) lst
75
      Just o -> insertBy pmCompare (k, fn o v) (remove k lst)
73
  case lookup k lst of
74
    Nothing -> insertBy pmCompare (k, v) lst
75
    Just o -> insertBy pmCompare (k, fn o v) (remove k lst)
76 76

  
77 77
-- | Create a PeerMap from an association list, with possible duplicates.
78 78
accumArray :: (Elem -> Elem -> Elem) -- ^ function used to merge the elements
b/htools/Ganeti/HTools/Program/Hail.hs
39 39
-- | Options list and functions.
40 40
options :: [OptType]
41 41
options =
42
    [ oPrintNodes
43
    , oSaveCluster
44
    , oDataFile
45
    , oNodeSim
46
    , oVerbose
47
    , oShowVer
48
    , oShowHelp
49
    ]
42
  [ oPrintNodes
43
  , oSaveCluster
44
  , oDataFile
45
  , oNodeSim
46
  , oVerbose
47
  , oShowVer
48
  , oShowHelp
49
  ]
50 50

  
51 51
-- | Main function.
52 52
main :: IO ()
b/htools/Ganeti/HTools/Program/Hscan.hs
49 49
-- | Options list and functions.
50 50
options :: [OptType]
51 51
options =
52
    [ oPrintNodes
53
    , oOutputDir
54
    , oLuxiSocket
55
    , oVerbose
56
    , oNoHeaders
57
    , oShowVer
58
    , oShowHelp
59
    ]
52
  [ oPrintNodes
53
  , oOutputDir
54
  , oLuxiSocket
55
  , oVerbose
56
  , oNoHeaders
57
  , oShowVer
58
  , oShowHelp
59
  ]
60 60

  
61 61
-- | Return a one-line summary of cluster state.
62 62
printCluster :: Node.List -> Instance.List
63 63
             -> String
64 64
printCluster nl il =
65
    let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
66
        ccv = Cluster.compCV nl
67
        nodes = Container.elems nl
68
        insts = Container.elems il
69
        t_ram = sum . map Node.tMem $ nodes
70
        t_dsk = sum . map Node.tDsk $ nodes
71
        f_ram = sum . map Node.fMem $ nodes
72
        f_dsk = sum . map Node.fDsk $ nodes
73
    in
74
      printf "%5d %5d %5d %5d %6.0f %6d %6.0f %6d %.8f"
75
                 (length nodes) (length insts)
76
                 (length bad_nodes) (length bad_instances)
77
                 t_ram f_ram
78
                 (t_dsk / 1024) (f_dsk `div` 1024)
79
                 ccv
80

  
65
  let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
66
      ccv = Cluster.compCV nl
67
      nodes = Container.elems nl
68
      insts = Container.elems il
69
      t_ram = sum . map Node.tMem $ nodes
70
      t_dsk = sum . map Node.tDsk $ nodes
71
      f_ram = sum . map Node.fMem $ nodes
72
      f_dsk = sum . map Node.fDsk $ nodes
73
  in printf "%5d %5d %5d %5d %6.0f %6d %6.0f %6d %.8f"
74
       (length nodes) (length insts)
75
       (length bad_nodes) (length bad_instances)
76
       t_ram f_ram (t_dsk / 1024) (f_dsk `div` 1024) ccv
81 77

  
82 78
-- | Replace slashes with underscore for saving to filesystem.
83 79
fixSlash :: String -> String
84 80
fixSlash = map (\x -> if x == '/' then '_' else x)
85 81

  
86

  
87 82
-- | Generates serialized data from loader input.
88 83
processData :: ClusterData -> Result ClusterData
89 84
processData input_data = do
b/htools/Ganeti/HTools/QCHelper.hs
26 26
-}
27 27

  
28 28
module Ganeti.HTools.QCHelper
29
    ( testSuite
30
    ) where
29
  ( testSuite
30
  ) where
31 31

  
32 32
import Test.QuickCheck
33 33
import Language.Haskell.TH
b/htools/Ganeti/HTools/Rapi.hs
26 26
{-# LANGUAGE BangPatterns, CPP #-}
27 27

  
28 28
module Ganeti.HTools.Rapi
29
    (
30
      loadData
31
    , parseData
32
    ) where
29
  ( loadData
30
  , parseData
31
  ) where
33 32

  
34 33
import Data.Maybe (fromMaybe)
35 34
#ifndef NO_CURL
......
76 75
-- | Append the default port if not passed in.
77 76
formatHost :: String -> String
78 77
formatHost master =
79
    if ':' `elem` master then  master
78
  if ':' `elem` master
79
    then  master
80 80
    else "https://" ++ master ++ ":" ++ show C.defaultRapiPort
81 81

  
82 82
-- | Parse a instance list in JSON format.
......
84 84
             -> String
85 85
             -> Result [(String, Instance.Instance)]
86 86
getInstances ktn body =
87
    loadJSArray "Parsing instance data" body >>=
88
    mapM (parseInstance ktn . fromJSObject)
87
  loadJSArray "Parsing instance data" body >>=
88
  mapM (parseInstance ktn . fromJSObject)
89 89

  
90 90
-- | Parse a node list in JSON format.
91 91
getNodes :: NameAssoc -> String -> Result [(String, Node.Node)]
92 92
getNodes ktg body = loadJSArray "Parsing node data" body >>=
93
                mapM (parseNode ktg . fromJSObject)
93
                    mapM (parseNode ktg . fromJSObject)
94 94

  
95 95
-- | Parse a group list in JSON format.
96 96
getGroups :: String -> Result [(String, Group.Group)]
97 97
getGroups body = loadJSArray "Parsing group data" body >>=
98
                mapM (parseGroup . fromJSObject)
98
                 mapM (parseGroup . fromJSObject)
99 99

  
100 100
-- | Construct an instance from a JSON object.
101 101
parseInstance :: NameAssoc
b/htools/Ganeti/HTools/Simu.hs
26 26
-}
27 27

  
28 28
module Ganeti.HTools.Simu
29
    (
30
      loadData
31
    , parseData
32
    ) where
29
  ( loadData
30
  , parseData
31
  ) where
33 32

  
34 33
import Control.Monad (mplus)
35 34
import Text.Printf (printf)
......
52 51
-- | Parse the string description into nodes.
53 52
parseDesc :: String -> Result (AllocPolicy, Int, Int, Int, Int)
54 53
parseDesc desc =
55
    case sepSplit ',' desc of
56
      [a, n, d, m, c] -> do
57
        apol <- allocPolicyFromRaw a `mplus` apolAbbrev a
58
        ncount <- tryRead "node count" n
59
        disk <- annotateResult "disk size" (parseUnit d)
60
        mem <- annotateResult "memory size" (parseUnit m)
61
        cpu <- tryRead "cpu count" c
62
        return (apol, ncount, disk, mem, cpu)
63
      es -> fail $ printf
64
            "Invalid cluster specification, expected 5 comma-separated\
65
            \ sections (allocation policy, node count, disk size,\
66
            \ memory size, number of CPUs) but got %d: '%s'" (length es) desc
54
  case sepSplit ',' desc of
55
    [a, n, d, m, c] -> do
56
      apol <- allocPolicyFromRaw a `mplus` apolAbbrev a
57
      ncount <- tryRead "node count" n
58
      disk <- annotateResult "disk size" (parseUnit d)
59
      mem <- annotateResult "memory size" (parseUnit m)
60
      cpu <- tryRead "cpu count" c
61
      return (apol, ncount, disk, mem, cpu)
62
    es -> fail $ printf
63
          "Invalid cluster specification, expected 5 comma-separated\
64
          \ sections (allocation policy, node count, disk size,\
65
          \ memory size, number of CPUs) but got %d: '%s'" (length es) desc
67 66

  
68 67
-- | Creates a node group with the given specifications.
69 68
createGroup :: Int    -- ^ The group index
......
72 71
createGroup grpIndex spec = do
73 72
  (apol, ncount, disk, mem, cpu) <- parseDesc spec
74 73
  let nodes = map (\idx ->
75
                       Node.create (printf "node-%02d-%03d" grpIndex idx)
76
                               (fromIntegral mem) 0 mem
77
                               (fromIntegral disk) disk
78
                               (fromIntegral cpu) False grpIndex
74
                     Node.create (printf "node-%02d-%03d" grpIndex idx)
75
                           (fromIntegral mem) 0 mem
76
                           (fromIntegral disk) disk
77
                           (fromIntegral cpu) False grpIndex
79 78
                  ) [1..ncount]
80 79
      grp = Group.create (printf "group-%02d" grpIndex)
81 80
            (printf "fake-uuid-%02d" grpIndex) apol
b/htools/Ganeti/HTools/Text.hs
27 27
-}
28 28

  
29 29
module Ganeti.HTools.Text
30
    (
31
      loadData
32
    , parseData
33
    , loadInst
34
    , loadNode
35
    , serializeInstances
36
    , serializeNode
37
    , serializeNodes
38
    , serializeCluster
39
    ) where
30
  ( loadData
31
  , parseData
32
  , loadInst
33
  , loadNode
34
  , serializeInstances
35
  , serializeNode
36
  , serializeNodes
37
  , serializeCluster
38
  ) where
40 39

  
41 40
import Control.Monad
42 41
import Data.List
......
56 55
-- | Serialize a single group.
57 56
serializeGroup :: Group.Group -> String
58 57
serializeGroup grp =
59
    printf "%s|%s|%s" (Group.name grp) (Group.uuid grp)
60
               (allocPolicyToRaw (Group.allocPolicy grp))
58
  printf "%s|%s|%s" (Group.name grp) (Group.uuid grp)
59
           (allocPolicyToRaw (Group.allocPolicy grp))
61 60

  
62 61
-- | Generate group file data from a group list.
63 62
serializeGroups :: Group.List -> String
......
68 67
              -> Node.Node  -- ^ The node to be serialised
69 68
              -> String
70 69
serializeNode gl node =
71
    printf "%s|%.0f|%d|%d|%.0f|%d|%.0f|%c|%s" (Node.name node)
72
               (Node.tMem node) (Node.nMem node) (Node.fMem node)
73
               (Node.tDsk node) (Node.fDsk node) (Node.tCpu node)
74
               (if Node.offline node then 'Y' else 'N')
75
               (Group.uuid grp)
70
  printf "%s|%.0f|%d|%d|%.0f|%d|%.0f|%c|%s" (Node.name node)
71
           (Node.tMem node) (Node.nMem node) (Node.fMem node)
72
           (Node.tDsk node) (Node.fDsk node) (Node.tCpu node)
73
           (if Node.offline node then 'Y' else 'N')
74
           (Group.uuid grp)
76 75
    where grp = Container.find (Node.group node) gl
77 76

  
78 77
-- | Generate node file data from node objects.
......
85 84
                  -> Instance.Instance -- ^ The instance to be serialised
86 85
                  -> String
87 86
serializeInstance nl inst =
88
    let
89
        iname = Instance.name inst
90
        pnode = Container.nameOf nl (Instance.pNode inst)
91
        sidx = Instance.sNode inst
92
        snode = (if sidx == Node.noSecondary
93
                    then ""
94
                    else Container.nameOf nl sidx)
95
    in
96
      printf "%s|%d|%d|%d|%s|%s|%s|%s|%s|%s"
97
             iname (Instance.mem inst) (Instance.dsk inst)
98
             (Instance.vcpus inst) (instanceStatusToRaw (Instance.runSt inst))
99
             (if Instance.autoBalance inst then "Y" else "N")
100
             pnode snode (diskTemplateToRaw (Instance.diskTemplate inst))
101
             (intercalate "," (Instance.tags inst))
87
  let iname = Instance.name inst
88
      pnode = Container.nameOf nl (Instance.pNode inst)
89
      sidx = Instance.sNode inst
90
      snode = (if sidx == Node.noSecondary
91
                 then ""
92
                 else Container.nameOf nl sidx)
93
  in printf "%s|%d|%d|%d|%s|%s|%s|%s|%s|%s"
94
       iname (Instance.mem inst) (Instance.dsk inst)
95
       (Instance.vcpus inst) (instanceStatusToRaw (Instance.runSt inst))
96
       (if Instance.autoBalance inst then "Y" else "N")
97
       pnode snode (diskTemplateToRaw (Instance.diskTemplate inst))
98
       (intercalate "," (Instance.tags inst))
102 99

  
103 100
-- | Generate instance file data from instance objects.
104 101
serializeInstances :: Node.List -> Instance.List -> String
105 102
serializeInstances nl =
106
    unlines . map (serializeInstance nl) . Container.elems
103
  unlines . map (serializeInstance nl) . Container.elems
107 104

  
108 105
-- | Generate complete cluster data from node and instance lists.
109 106
serializeCluster :: ClusterData -> String
b/htools/Ganeti/HTools/Types.hs
26 26
-}
27 27

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

  
75 75
import Control.Monad
76 76
import qualified Data.Map as M
......
107 107
-- changing this data type be careful about the interaction with the
108 108
-- desired sorting order.
109 109
$(THH.declareSADT "AllocPolicy"
110
         [ ("AllocPreferred",   'C.allocPolicyPreferred)
111
         , ("AllocLastResort",  'C.allocPolicyLastResort)
112
         , ("AllocUnallocable", 'C.allocPolicyUnallocable)
113
         ])
110
       [ ("AllocPreferred",   'C.allocPolicyPreferred)
111
       , ("AllocLastResort",  'C.allocPolicyLastResort)
112
       , ("AllocUnallocable", 'C.allocPolicyUnallocable)
113
       ])
114 114
$(THH.makeJSONInstance ''AllocPolicy)
115 115

  
116 116
-- | The Instance real state type.
117 117
$(THH.declareSADT "InstanceStatus"
118
         [ ("AdminDown", 'C.inststAdmindown)
119
         , ("AdminOffline", 'C.inststAdminoffline)
120
         , ("ErrorDown", 'C.inststErrordown)
121
         , ("ErrorUp", 'C.inststErrorup)
122
         , ("NodeDown", 'C.inststNodedown)
123
         , ("NodeOffline", 'C.inststNodeoffline)
124
         , ("Running", 'C.inststRunning)
125
         , ("WrongNode", 'C.inststWrongnode)
126
         ])
118
       [ ("AdminDown", 'C.inststAdmindown)
119
       , ("AdminOffline", 'C.inststAdminoffline)
120
       , ("ErrorDown", 'C.inststErrordown)
121
       , ("ErrorUp", 'C.inststErrorup)
122
       , ("NodeDown", 'C.inststNodedown)
123
       , ("NodeOffline", 'C.inststNodeoffline)
124
       , ("Running", 'C.inststRunning)
125
       , ("WrongNode", 'C.inststWrongnode)
126
       ])
127 127
$(THH.makeJSONInstance ''InstanceStatus)
128 128

  
129 129
-- | The resource spec type.
130 130
data RSpec = RSpec
131
    { rspecCpu  :: Int  -- ^ Requested VCPUs
132
    , rspecMem  :: Int  -- ^ Requested memory
133
    , rspecDsk  :: Int  -- ^ Requested disk
134
    } deriving (Show, Read, Eq)
131
  { rspecCpu  :: Int  -- ^ Requested VCPUs
132
  , rspecMem  :: Int  -- ^ Requested memory
133
  , rspecDsk  :: Int  -- ^ Requested disk
134
  } deriving (Show, Read, Eq)
135 135

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

  
145 145
-- | Initial empty utilisation.
146 146
zeroUtil :: DynUtil
......
156 156
-- | Sum two utilisation records.
157 157
addUtil :: DynUtil -> DynUtil -> DynUtil
158 158
addUtil (DynUtil a1 a2 a3 a4) (DynUtil b1 b2 b3 b4) =
159
    DynUtil (a1+b1) (a2+b2) (a3+b3) (a4+b4)
159
  DynUtil (a1+b1) (a2+b2) (a3+b3) (a4+b4)
160 160

  
161 161
-- | Substracts one utilisation record from another.
162 162
subUtil :: DynUtil -> DynUtil -> DynUtil
163 163
subUtil (DynUtil a1 a2 a3 a4) (DynUtil b1 b2 b3 b4) =
164
    DynUtil (a1-b1) (a2-b2) (a3-b3) (a4-b4)
164
  DynUtil (a1-b1) (a2-b2) (a3-b3) (a4-b4)
165 165

  
166 166
-- | The description of an instance placement. It contains the
167 167
-- instance index, the new primary and secondary node, the move being
......
178 178

  
179 179
-- | Instance disk template type.
180 180
$(THH.declareSADT "DiskTemplate"
181
     [ ("DTDiskless",   'C.dtDiskless)
182
     , ("DTFile",       'C.dtFile)
183
     , ("DTSharedFile", 'C.dtSharedFile)
184
     , ("DTPlain",      'C.dtPlain)
185
     , ("DTBlock",      'C.dtBlock)
186
     , ("DTDrbd8",      'C.dtDrbd8)
187
     ])
181
       [ ("DTDiskless",   'C.dtDiskless)
182
       , ("DTFile",       'C.dtFile)
183
       , ("DTSharedFile", 'C.dtSharedFile)
184
       , ("DTPlain",      'C.dtPlain)
185
       , ("DTBlock",      'C.dtBlock)
186
       , ("DTDrbd8",      'C.dtDrbd8)
187
       ])
188 188
$(THH.makeJSONInstance ''DiskTemplate)
189 189

  
190 190
-- | Formatted solution output for one move (involved nodes and
......
237 237
    deriving (Show, Read, Eq)
238 238

  
239 239
instance Monad Result where
240
    (>>=) (Bad x) _ = Bad x
241
    (>>=) (Ok x) fn = fn x
242
    return = Ok
243
    fail = Bad
240
  (>>=) (Bad x) _ = Bad x
241
  (>>=) (Ok x) fn = fn x
242
  return = Ok
243
  fail = Bad
244 244

  
245 245
instance MonadPlus Result where
246
    mzero = Bad "zero Result when used as MonadPlus"
247
    -- for mplus, when we 'add' two Bad values, we concatenate their
248
    -- error descriptions
249
    (Bad x) `mplus` (Bad y) = Bad (x ++ "; " ++ y)
250
    (Bad _) `mplus` x = x
251
    x@(Ok _) `mplus` _ = x
246
  mzero = Bad "zero Result when used as MonadPlus"
247
  -- for mplus, when we 'add' two Bad values, we concatenate their
248
  -- error descriptions
249
  (Bad x) `mplus` (Bad y) = Bad (x ++ "; " ++ y)
250
  (Bad _) `mplus` x = x
251
  x@(Ok _) `mplus` _ = x
252 252

  
253 253
-- | Simple checker for whether a 'Result' is OK.
254 254
isOk :: Result a -> Bool
......
287 287
                  deriving (Show, Read)
288 288

  
289 289
instance Monad OpResult where
290
    (OpGood x) >>= fn = fn x
291
    (OpFail y) >>= _ = OpFail y
292
    return = OpGood
290
  (OpGood x) >>= fn = fn x
291
  (OpFail y) >>= _ = OpFail y
292
  return = OpGood
293 293

  
294 294
-- | Conversion from 'OpResult' to 'Result'.
295 295
opToResult :: OpResult a -> Result a
......
298 298

  
299 299
-- | A generic class for items that have updateable names and indices.
300 300
class Element a where
301
    -- | Returns the name of the element
302
    nameOf  :: a -> String
303
    -- | Returns all the known names of the element
304
    allNames :: a -> [String]
305
    -- | Returns the index of the element
306
    idxOf   :: a -> Int
307
    -- | Updates the alias of the element
308
    setAlias :: a -> String -> a
309
    -- | Compute the alias by stripping a given suffix (domain) from
310
    -- the name
311
    computeAlias :: String -> a -> a
312
    computeAlias dom e = setAlias e alias
313
        where alias = take (length name - length dom) name
314
              name = nameOf e
315
    -- | Updates the index of the element
316
    setIdx  :: a -> Int -> a
301
  -- | Returns the name of the element
302
  nameOf  :: a -> String
303
  -- | Returns all the known names of the element
304
  allNames :: a -> [String]
305
  -- | Returns the index of the element
306
  idxOf   :: a -> Int
307
  -- | Updates the alias of the element
308
  setAlias :: a -> String -> a
309
  -- | Compute the alias by stripping a given suffix (domain) from
310
  -- the name
311
  computeAlias :: String -> a -> a
312
  computeAlias dom e = setAlias e alias
313
    where alias = take (length name - length dom) name
314
          name = nameOf e
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff