Revision 525bfb36

b/htools/Ganeti/HTools/CLI.hs
1 1
{-| Implementation of command-line functions.
2 2

  
3
This module holds the common cli-related functions for the binaries,
4
separated into this module since Utils.hs is used in many other places
5
and this is more IO oriented.
3
This module holds the common command-line related functions for the
4
binaries, separated into this module since "Ganeti.HTools.Utils" is
5
used in many other places and this is more IO oriented.
6 6

  
7 7
-}
8 8

  
......
89 89
import Ganeti.HTools.Types
90 90
import Ganeti.HTools.Utils
91 91

  
92
-- | The default value for the luxi socket
92
-- * Constants
93

  
94
-- | The default value for the luxi socket.
95
--
96
-- This is re-exported from the "Ganeti.Constants" module.
93 97
defaultLuxiSocket :: FilePath
94 98
defaultLuxiSocket = C.masterSocket
95 99

  
100
-- * Data types
101

  
96 102
-- | Command line options structure.
97 103
data Options = Options
98 104
    { optDataFile    :: Maybe FilePath -- ^ Path to the cluster data file
......
170 176
 , optVerbose     = 1
171 177
 }
172 178

  
173
-- | Abrreviation for the option type
179
-- | Abrreviation for the option type.
174 180
type OptType = OptDescr (Options -> Result Options)
175 181

  
182
-- * Command line options
183

  
176 184
oDataFile :: OptType
177 185
oDataFile = Option "t" ["text-data"]
178 186
            (ReqArg (\ f o -> Ok o { optDataFile = Just f }) "FILE")
......
394 402
           (NoArg (\ opts -> Ok opts { optVerbose = optVerbose opts + 1 }))
395 403
           "increase the verbosity level"
396 404

  
397
-- | Usage info
405
-- * Functions
406

  
407
-- | Usage info.
398 408
usageHelp :: String -> [OptType] -> String
399 409
usageHelp progname =
400 410
    usageInfo (printf "%s %s\nUsage: %s [OPTION...]"
401 411
               progname Version.version progname)
402 412

  
403
-- | Command line parser, using the 'options' structure.
413
-- | Command line parser, using the 'Options' structure.
404 414
parseOpts :: [String]               -- ^ The command line arguments
405 415
          -> String                 -- ^ The program name
406 416
          -> [OptType]              -- ^ The supported command line options
b/htools/Ganeti/HTools/Cluster.hs
1 1
{-| Implementation of cluster-wide logic.
2 2

  
3 3
This module holds all pure cluster-logic; I\/O related functionality
4
goes into the "Main" module for the individual binaries.
4
goes into the /Main/ module for the individual binaries.
5 5

  
6 6
-}
7 7

  
......
106 106

  
107 107

  
108 108
-- | A type denoting the valid allocation mode/pairs.
109
--
109 110
-- For a one-node allocation, this will be a @Left ['Node.Node']@,
110 111
-- whereas for a two-node allocation, this will be a @Right
111 112
-- [('Node.Node', 'Node.Node')]@.
112 113
type AllocNodes = Either [Ndx] [(Ndx, Ndx)]
113 114

  
114
-- | The empty solution we start with when computing allocations
115
-- | The empty solution we start with when computing allocations.
115 116
emptySolution :: AllocSolution
116 117
emptySolution = AllocSolution { asFailures = [], asAllocs = 0
117 118
                              , asSolutions = [], asLog = [] }
118 119

  
119
-- | The complete state for the balancing solution
120
-- | The complete state for the balancing solution.
120 121
data Table = Table Node.List Instance.List Score [Placement]
121 122
             deriving (Show, Read)
122 123

  
......
144 145
                     }
145 146
            deriving (Show, Read)
146 147

  
147
-- | Currently used, possibly to allocate, unallocable
148
-- | Currently used, possibly to allocate, unallocable.
148 149
type AllocStats = (RSpec, RSpec, RSpec)
149 150

  
150 151
-- * Utility functions
......
170 171
  in
171 172
    (bad_nodes, bad_instances)
172 173

  
173
-- | Zero-initializer for the CStats type
174
-- | Zero-initializer for the CStats type.
174 175
emptyCStats :: CStats
175 176
emptyCStats = CStats 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
176 177

  
177
-- | Update stats with data from a new node
178
-- | Update stats with data from a new node.
178 179
updateCStats :: CStats -> Node.Node -> CStats
179 180
updateCStats cs node =
180 181
    let CStats { csFmem = x_fmem, csFdsk = x_fdsk,
......
243 244
               (truncate t_dsk - fromIntegral f_idsk)
244 245
    in (rini, rfin, runa)
245 246

  
246
-- | The names and weights of the individual elements in the CV list
247
-- | The names and weights of the individual elements in the CV list.
247 248
detailedCVInfo :: [(Double, String)]
248 249
detailedCVInfo = [ (1,  "free_mem_cv")
249 250
                 , (1,  "free_disk_cv")
......
311 312
compCV :: Node.List -> Double
312 313
compCV = sum . zipWith (*) detailedCVWeights . compDetailedCV
313 314

  
314
-- | Compute online nodes from a Node.List
315
-- | Compute online nodes from a 'Node.List'.
315 316
getOnline :: Node.List -> [Node.Node]
316 317
getOnline = filter (not . Node.offline) . Container.elems
317 318

  
318
-- * hbal functions
319
-- * Balancing functions
319 320

  
320 321
-- | Compute best table. Note that the ordering of the arguments is important.
321 322
compareTables :: Table -> Table -> Table
......
534 535
       then ini_tbl -- no advancement
535 536
       else best_tbl
536 537

  
537
-- | Check if we are allowed to go deeper in the balancing
538
-- | Check if we are allowed to go deeper in the balancing.
538 539
doNextBalance :: Table     -- ^ The starting table
539 540
              -> Int       -- ^ Remaining length
540 541
              -> Score     -- ^ Score at which to stop
......
544 545
        ini_plc_len = length ini_plc
545 546
    in (max_rounds < 0 || ini_plc_len < max_rounds) && ini_cv > min_score
546 547

  
547
-- | Run a balance move
548
-- | Run a balance move.
548 549
tryBalance :: Table       -- ^ The starting table
549 550
           -> Bool        -- ^ Allow disk moves
550 551
           -> Bool        -- ^ Allow instance moves
......
574 575

  
575 576
-- * Allocation functions
576 577

  
577
-- | Build failure stats out of a list of failures
578
-- | Build failure stats out of a list of failures.
578 579
collapseFailures :: [FailMode] -> FailStats
579 580
collapseFailures flst =
580 581
    map (\k -> (k, length $ filter (k ==) flst)) [minBound..maxBound]
581 582

  
582 583
-- | Update current Allocation solution and failure stats with new
583
-- elements
584
-- elements.
584 585
concatAllocs :: AllocSolution -> OpResult Node.AllocElement -> AllocSolution
585 586
concatAllocs as (OpFail reason) = as { asFailures = reason : asFailures as }
586 587

  
......
611 612
sumAllocs (AllocSolution af aa as al) (AllocSolution bf ba bs bl) =
612 613
    AllocSolution (af ++ bf) (aa + ba) (as ++ bs) (al ++ bl)
613 614

  
614
-- | Given a solution, generates a reasonable description for it
615
-- | Given a solution, generates a reasonable description for it.
615 616
describeSolution :: AllocSolution -> String
616 617
describeSolution as =
617 618
  let fcnt = asFailures as
......
629 630
                     " for node(s) %s") cv (asAllocs as) (length fcnt) freasons
630 631
             (intercalate "/" . map Node.name $ nodes)
631 632

  
632
-- | Annotates a solution with the appropriate string
633
-- | Annotates a solution with the appropriate string.
633 634
annotateSolution :: AllocSolution -> AllocSolution
634 635
annotateSolution as = as { asLog = describeSolution as : asLog as }
635 636

  
......
678 679
       then fail "No online nodes"
679 680
       else return $ annotateSolution sols
680 681

  
681
-- | Given a group/result, describe it as a nice (list of) messages
682
-- | Given a group/result, describe it as a nice (list of) messages.
682 683
solutionDescription :: Group.List -> (Gdx, Result AllocSolution) -> [String]
683 684
solutionDescription gl (groupId, result) =
684 685
  case result of
......
690 691

  
691 692
-- | From a list of possibly bad and possibly empty solutions, filter
692 693
-- only the groups with a valid result. Note that the result will be
693
-- reversed compared to the original list
694
-- reversed compared to the original list.
694 695
filterMGResults :: Group.List
695 696
                -> [(Gdx, Result AllocSolution)]
696 697
                -> [(Gdx, AllocSolution)]
......
703 704
                       | unallocable gdx -> accu
704 705
                       | otherwise -> (gdx, sol):accu
705 706

  
706
-- | Sort multigroup results based on policy and score
707
-- | Sort multigroup results based on policy and score.
707 708
sortMGResults :: Group.List
708 709
             -> [(Gdx, AllocSolution)]
709 710
             -> [(Gdx, AllocSolution)]
......
782 783
                Just v -> return v
783 784
  tryReloc nl il xid ncount ex_ndx
784 785

  
785
-- | Change an instance's secondary node
786
-- | Change an instance's secondary node.
786 787
evacInstance :: (Monad m) =>
787 788
                [Ndx]                      -- ^ Excluded nodes
788 789
             -> Instance.List              -- ^ The current instance list
......
854 855
      let sol = foldl' sumAllocs emptySolution results
855 856
      return $ annotateSolution sol
856 857

  
857
-- | Recursively place instances on the cluster until we're out of space
858
-- | Recursively place instances on the cluster until we're out of space.
858 859
iterateAlloc :: Node.List
859 860
             -> Instance.List
860 861
             -> Instance.Instance
......
879 880
                 _ -> Bad "Internal error: multiple solutions for single\
880 881
                          \ allocation"
881 882

  
882
-- | The core of the tiered allocation mode
883
-- | The core of the tiered allocation mode.
883 884
tieredAlloc :: Node.List
884 885
            -> Instance.List
885 886
            -> Instance.Instance
......
1072 1073

  
1073 1074
-- * Node group functions
1074 1075

  
1075
-- | Computes the group of an instance
1076
-- | Computes the group of an instance.
1076 1077
instanceGroup :: Node.List -> Instance.Instance -> Result Gdx
1077 1078
instanceGroup nl i =
1078 1079
  let sidx = Instance.sNode i
......
1087 1088
                show pgroup ++ ", secondary " ++ show sgroup)
1088 1089
     else return pgroup
1089 1090

  
1090
-- | Computes the group of an instance per the primary node
1091
-- | Computes the group of an instance per the primary node.
1091 1092
instancePriGroup :: Node.List -> Instance.Instance -> Gdx
1092 1093
instancePriGroup nl i =
1093 1094
  let pnode = Container.find (Instance.pNode i) nl
1094 1095
  in  Node.group pnode
1095 1096

  
1096 1097
-- | Compute the list of badly allocated instances (split across node
1097
-- groups)
1098
-- groups).
1098 1099
findSplitInstances :: Node.List -> Instance.List -> [Instance.Instance]
1099 1100
findSplitInstances nl =
1100 1101
  filter (not . isOk . instanceGroup nl) . Container.elems
1101 1102

  
1102
-- | Splits a cluster into the component node groups
1103
-- | Splits a cluster into the component node groups.
1103 1104
splitCluster :: Node.List -> Instance.List ->
1104 1105
                [(Gdx, (Node.List, Instance.List))]
1105 1106
splitCluster nl il =
b/htools/Ganeti/HTools/ExtLoader.hs
1
{-| External data loader
1
{-| External data loader.
2 2

  
3 3
This module holds the external data loading, and thus is the only one
4 4
depending (via the specialized Text\/Rapi\/Luxi modules) on the actual
......
51 51
import Ganeti.HTools.CLI
52 52
import Ganeti.HTools.Utils (sepSplit, tryRead)
53 53

  
54
-- | Error beautifier
54
-- | Error beautifier.
55 55
wrapIO :: IO (Result a) -> IO (Result a)
56 56
wrapIO = flip catch (return . Bad . show)
57 57

  
b/htools/Ganeti/HTools/IAlloc.hs
60 60
  let running = "running"
61 61
  return (n, Instance.create n mem disk vcpus running tags True 0 0)
62 62

  
63
-- | Parses an instance as found in the cluster instance listg.
63
-- | Parses an instance as found in the cluster instance list.
64 64
parseInstance :: NameAssoc -- ^ The node name-to-index association list
65 65
              -> String    -- ^ The name of the instance
66 66
              -> JSRecord  -- ^ The JSON object
b/htools/Ganeti/HTools/Instance.hs
49 49

  
50 50
-- * Type declarations
51 51

  
52
-- | The instance type
52
-- | The instance type.
53 53
data Instance = Instance
54 54
    { name         :: String    -- ^ The instance name
55 55
    , alias        :: String    -- ^ The shortened name
......
74 74
    setIdx   = setIdx
75 75
    allNames n = [name n, alias n]
76 76

  
77
-- | Running instance states.
77
-- | Constant holding the running instance states.
78 78
runningStates :: [String]
79 79
runningStates = [C.inststRunning, C.inststErrorup]
80 80

  
b/htools/Ganeti/HTools/Loader.hs
1
{-| Generic data loader
1
{-| Generic data loader.
2 2

  
3 3
This module holds the common code for parsing the input data after it
4 4
has been loaded from external sources.
......
54 54

  
55 55
-- * Constants
56 56

  
57
-- | The exclusion tag prefix
57
-- | The exclusion tag prefix.
58 58
exTagsPrefix :: String
59 59
exTagsPrefix = "htools:iextags:"
60 60

  
......
147 147
           in Container.add sdx snew ac2
148 148
      else ac2
149 149

  
150
-- | Remove non-selected tags from the exclusion list
150
-- | Remove non-selected tags from the exclusion list.
151 151
filterExTags :: [String] -> Instance.Instance -> Instance.Instance
152 152
filterExTags tl inst =
153 153
    let old_tags = Instance.tags inst
......
155 155
                   old_tags
156 156
    in inst { Instance.tags = new_tags }
157 157

  
158
-- | Update the movable attribute
158
-- | Update the movable attribute.
159 159
updateMovable :: [String]           -- ^ Selected instances (if not empty)
160 160
              -> [String]           -- ^ Excluded instances
161 161
              -> Instance.Instance  -- ^ Target Instance
......
168 168
    else inst
169 169

  
170 170
-- | Compute the longest common suffix of a list of strings that
171
-- | starts with a dot.
171
-- starts with a dot.
172 172
longestDomain :: [String] -> String
173 173
longestDomain [] = ""
174 174
longestDomain (x:xs) =
......
177 177
                              else accu)
178 178
      "" $ filter (isPrefixOf ".") (tails x)
179 179

  
180
-- | Extracts the exclusion tags from the cluster configuration
180
-- | Extracts the exclusion tags from the cluster configuration.
181 181
extractExTags :: [String] -> [String]
182 182
extractExTags =
183 183
    map (drop (length exTagsPrefix)) .
184 184
    filter (isPrefixOf exTagsPrefix)
185 185

  
186
-- | Extracts the common suffix from node\/instance names
186
-- | Extracts the common suffix from node\/instance names.
187 187
commonSuffix :: Node.List -> Instance.List -> String
188 188
commonSuffix nl il =
189 189
    let node_names = map Node.name $ Container.elems nl
b/htools/Ganeti/HTools/Luxi.hs
187 187

  
188 188
-- * Main loader functionality
189 189

  
190
-- | Builds the cluster data from an URL.
190
-- | Builds the cluster data by querying a given socket name.
191 191
readData :: String -- ^ Unix socket to use as source
192 192
         -> IO (Result JSValue, Result JSValue, Result JSValue, Result JSValue)
193 193
readData master =
......
202 202
          return (groups, nodes, instances, cinfo)
203 203
       )
204 204

  
205
-- | Converts the output of 'readData' into the internal cluster
206
-- representation.
205 207
parseData :: (Result JSValue, Result JSValue, Result JSValue, Result JSValue)
206 208
          -> Result ClusterData
207 209
parseData (groups, nodes, instances, cinfo) = do
......
214 216
  ctags <- cinfo >>= getClusterTags
215 217
  return (ClusterData group_idx node_idx inst_idx ctags)
216 218

  
217
-- | Top level function for data loading
219
-- | Top level function for data loading.
218 220
loadData :: String -- ^ Unix socket to use as source
219 221
         -> IO (Result ClusterData)
220 222
loadData = fmap parseData . readData
b/htools/Ganeti/HTools/Node.hs
85 85

  
86 86
-- * Type declarations
87 87

  
88
-- | The tag map type
88
-- | The tag map type.
89 89
type TagMap = Map.Map String Int
90 90

  
91 91
-- | The node type.
......
140 140
type List = Container.Container Node
141 141

  
142 142
-- | A simple name for an allocation element (here just for logistic
143
-- reasons)
143
-- reasons).
144 144
type AllocElement = (List, Instance.Instance, [Node], T.Score)
145 145

  
146 146
-- | Constant node index for a non-moveable instance.
......
149 149

  
150 150
-- * Helper functions
151 151

  
152
-- | Add a tag to a tagmap
152
-- | Add a tag to a tagmap.
153 153
addTag :: TagMap -> String -> TagMap
154 154
addTag t s = Map.insertWith (+) s 1 t
155 155

  
156
-- | Add multiple tags
156
-- | Add multiple tags.
157 157
addTags :: TagMap -> [String] -> TagMap
158 158
addTags = foldl' addTag
159 159

  
160
-- | Adjust or delete a tag from a tagmap
160
-- | Adjust or delete a tag from a tagmap.
161 161
delTag :: TagMap -> String -> TagMap
162 162
delTag t s = Map.update (\v -> if v > 1
163 163
                               then Just (v-1)
164 164
                               else Nothing)
165 165
             s t
166 166

  
167
-- | Remove multiple tags
167
-- | Remove multiple tags.
168 168
delTags :: TagMap -> [String] -> TagMap
169 169
delTags = foldl' delTag
170 170

  
171
-- | Check if we can add a list of tags to a tagmap
171
-- | Check if we can add a list of tags to a tagmap.
172 172
rejectAddTags :: TagMap -> [String] -> Bool
173 173
rejectAddTags t = any (`Map.member` t)
174 174

  
......
221 221
         , group = group_init
222 222
         }
223 223

  
224
-- | Conversion formula from mDsk\/tDsk to loDsk
224
-- | Conversion formula from mDsk\/tDsk to loDsk.
225 225
mDskToloDsk :: Double -> Double -> Int
226 226
mDskToloDsk mval tdsk = floor (mval * tdsk)
227 227

  
228
-- | Conversion formula from mCpu\/tCpu to hiCpu
228
-- | Conversion formula from mCpu\/tCpu to hiCpu.
229 229
mCpuTohiCpu :: Double -> Double -> Int
230 230
mCpuTohiCpu mval tcpu = floor (mval * tcpu)
231 231

  
......
249 249
setXmem :: Node -> Int -> Node
250 250
setXmem t val = t { xMem = val }
251 251

  
252
-- | Sets the max disk usage ratio
252
-- | Sets the max disk usage ratio.
253 253
setMdsk :: Node -> Double -> Node
254 254
setMdsk t val = t { mDsk = val, loDsk = mDskToloDsk val (tDsk t) }
255 255

  
256
-- | Sets the max cpu usage ratio
256
-- | Sets the max cpu usage ratio.
257 257
setMcpu :: Node -> Double -> Node
258 258
setMcpu t val = t { mCpu = val, hiCpu = mCpuTohiCpu val (tCpu t) }
259 259

  
......
435 435

  
436 436
-- * Stats functions
437 437

  
438
-- | Computes the amount of available disk on a given node
438
-- | Computes the amount of available disk on a given node.
439 439
availDisk :: Node -> Int
440 440
availDisk t =
441 441
    let _f = fDsk t
......
444 444
       then 0
445 445
       else _f - _l
446 446

  
447
-- | Computes the amount of used disk on a given node
447
-- | Computes the amount of used disk on a given node.
448 448
iDsk :: Node -> Int
449 449
iDsk t = truncate (tDsk t) - fDsk t
450 450

  
451
-- | Computes the amount of available memory on a given node
451
-- | Computes the amount of available memory on a given node.
452 452
availMem :: Node -> Int
453 453
availMem t =
454 454
    let _f = fMem t
......
457 457
       then 0
458 458
       else _f - _l
459 459

  
460
-- | Computes the amount of available memory on a given node
460
-- | Computes the amount of available memory on a given node.
461 461
availCpu :: Node -> Int
462 462
availCpu t =
463 463
    let _u = uCpu t
......
472 472

  
473 473
-- * Display functions
474 474

  
475
showField :: Node -> String -> String
475
-- | Return a field for a given node.
476
showField :: Node   -- ^ Node which we're querying
477
          -> String -- ^ Field name
478
          -> String -- ^ Field value as string
476 479
showField t field =
477 480
    case field of
478 481
      "idx"  -> printf "%4d" $ idx t
......
512 515
      T.DynUtil { T.cpuWeight = uC, T.memWeight = uM,
513 516
                  T.dskWeight = uD, T.netWeight = uN } = utilLoad t
514 517

  
515
-- | Returns the header and numeric propery of a field
518
-- | Returns the header and numeric propery of a field.
516 519
showHeader :: String -> (String, Bool)
517 520
showHeader field =
518 521
    case field of
......
552 555
list fields t = map (showField t) fields
553 556

  
554 557

  
558
-- | Constant holding the fields we're displaying by default.
555 559
defaultFields :: [String]
556 560
defaultFields =
557 561
    [ "status", "name", "tmem", "nmem", "imem", "xmem", "fmem"
......
560 564
    , "cload", "mload", "dload", "nload" ]
561 565

  
562 566
-- | Split a list of nodes into a list of (node group UUID, list of
563
-- associated nodes)
567
-- associated nodes).
564 568
computeGroups :: [Node] -> [(T.Gdx, [Node])]
565 569
computeGroups nodes =
566 570
  let nodes' = sortBy (comparing group) nodes
b/htools/Ganeti/HTools/PeerMap.hs
1
{-|
2
  Module abstracting the peer map implementation.
1
{-| Module abstracting the peer map implementation.
3 2

  
4 3
This is abstracted separately since the speed of peermap updates can
5 4
be a significant part of the total runtime, and as such changing the
......
46 45

  
47 46
import Ganeti.HTools.Types
48 47

  
48
-- * Type definitions
49

  
50
-- | Our key type.
49 51
type Key = Ndx
52

  
53
-- | Our element type.
54

  
50 55
type Elem = Int
56

  
57
-- | The definition of a peer map.
51 58
type PeerMap = [(Key, Elem)]
52 59

  
53 60
-- * Initialization functions
......
67 74
      Nothing -> insertBy pmCompare (k, v) lst
68 75
      Just o -> insertBy pmCompare (k, fn o v) (remove k lst)
69 76

  
70
-- | Create a PeerMap from an association list, with possible duplicates
77
-- | Create a PeerMap from an association list, with possible duplicates.
71 78
accumArray :: (Elem -> Elem -> Elem) -- ^ function used to merge the elements
72 79
              -> [(Key, Elem)]       -- ^ source data
73 80
              -> PeerMap             -- ^ results
......
76 83

  
77 84
-- * Basic operations
78 85

  
79
-- | Returns either the value for a key or zero if not found
86
-- | Returns either the value for a key or zero if not found.
80 87
find :: Key -> PeerMap -> Elem
81 88
find k = fromMaybe 0 . lookup k
82 89

  
83
-- | Add an element to a peermap, overwriting the previous value
90
-- | Add an element to a peermap, overwriting the previous value.
84 91
add :: Key -> Elem -> PeerMap -> PeerMap
85 92
add = addWith (flip const)
86 93

  
87
-- | Remove an element from a peermap
94
-- | Remove an element from a peermap.
88 95
remove :: Key -> PeerMap -> PeerMap
89 96
remove _ [] = []
90 97
remove k ((x@(x', _)):xs) = if k == x'
b/htools/Ganeti/HTools/QC.hs
1
{-| Unittests for ganeti-htools
1
{-| Unittests for ganeti-htools.
2 2

  
3 3
-}
4 4

  
......
70 70

  
71 71
-- * Constants
72 72

  
73
-- | Maximum memory (1TiB, somewhat random value)
73
-- | Maximum memory (1TiB, somewhat random value).
74 74
maxMem :: Int
75 75
maxMem = 1024 * 1024
76 76

  
77
-- | Maximum disk (8TiB, somewhat random value)
77
-- | Maximum disk (8TiB, somewhat random value).
78 78
maxDsk :: Int
79 79
maxDsk = 1024 * 1024 * 8
80 80

  
81
-- | Max CPUs (1024, somewhat random value)
81
-- | Max CPUs (1024, somewhat random value).
82 82
maxCpu :: Int
83 83
maxCpu = 1024
84 84

  
......
95 95

  
96 96
-- * Helper functions
97 97

  
98
-- | Simple checker for whether OpResult is fail or pass
98
-- | Simple checker for whether OpResult is fail or pass.
99 99
isFailure :: Types.OpResult a -> Bool
100 100
isFailure (Types.OpFail _) = True
101 101
isFailure _ = False
102 102

  
103
-- | Update an instance to be smaller than a node
103
-- | Update an instance to be smaller than a node.
104 104
setInstanceSmallerThanNode node inst =
105 105
    inst { Instance.mem = Node.availMem node `div` 2
106 106
         , Instance.dsk = Node.availDisk node `div` 2
107 107
         , Instance.vcpus = Node.availCpu node `div` 2
108 108
         }
109 109

  
110
-- | Create an instance given its spec
110
-- | Create an instance given its spec.
111 111
createInstance mem dsk vcpus =
112 112
    Instance.create "inst-unnamed" mem dsk vcpus "running" [] True (-1) (-1)
113 113

  
114
-- | Create a small cluster by repeating a node spec
114
-- | Create a small cluster by repeating a node spec.
115 115
makeSmallCluster :: Node.Node -> Int -> Node.List
116 116
makeSmallCluster node count =
117 117
    let fn = Node.buildPeers node Container.empty
......
119 119
        (_, nlst) = Loader.assignIndices namelst
120 120
    in nlst
121 121

  
122
-- | Checks if a node is "big" enough
122
-- | Checks if a node is "big" enough.
123 123
isNodeBig :: Node.Node -> Int -> Bool
124 124
isNodeBig node size = Node.availDisk node > size * Types.unitDsk
125 125
                      && Node.availMem node > size * Types.unitMem
......
129 129
canBalance tbl dm im evac = isJust $ Cluster.tryBalance tbl dm im evac 0 0
130 130

  
131 131
-- | Assigns a new fresh instance to a cluster; this is not
132
-- allocation, so no resource checks are done
132
-- allocation, so no resource checks are done.
133 133
assignInstance :: Node.List -> Instance.List -> Instance.Instance ->
134 134
                  Types.Idx -> Types.Idx ->
135 135
                  (Node.List, Instance.List)
......
149 149

  
150 150
-- * Arbitrary instances
151 151

  
152
-- | Defines a DNS name.
152 153
newtype DNSChar = DNSChar { dnsGetChar::Char }
154

  
153 155
instance Arbitrary DNSChar where
154 156
    arbitrary = do
155 157
      x <- elements (['a'..'z'] ++ ['0'..'9'] ++ "_-")
......
189 191
      vcpus <- choose (0, maxCpu)
190 192
      return $ Instance.create name mem dsk vcpus run_st [] True pn sn
191 193

  
192
genNode :: Maybe Int -> Maybe Int -> Gen Node.Node
194
-- | Generas an arbitrary node based on sizing information.
195
genNode :: Maybe Int -- ^ Minimum node size in terms of units
196
        -> Maybe Int -- ^ Maximum node size (when Nothing, bounded
197
                     -- just by the max... constants)
198
        -> Gen Node.Node
193 199
genNode min_multiplier max_multiplier = do
194 200
  let (base_mem, base_dsk, base_cpu) =
195 201
          case min_multiplier of
......
253 259
instance Arbitrary Jobs.JobStatus where
254 260
  arbitrary = elements [minBound..maxBound]
255 261

  
262
newtype SmallRatio = SmallRatio Double deriving Show
263
instance Arbitrary SmallRatio where
264
    arbitrary = do
265
      v <- choose (0, 1)
266
      return $ SmallRatio v
267

  
256 268
-- * Actual tests
257 269

  
258
-- If the list is not just an empty element, and if the elements do
259
-- not contain commas, then join+split should be idepotent
270
-- ** Utils tests
271

  
272
-- | If the list is not just an empty element, and if the elements do
273
-- not contain commas, then join+split should be idempotent.
260 274
prop_Utils_commaJoinSplit =
261 275
    forAll (arbitrary `suchThat`
262 276
            (\l -> l /= [""] && all (not . elem ',') l )) $ \lst ->
263 277
    Utils.sepSplit ',' (Utils.commaJoin lst) == lst
264 278

  
265
-- Split and join should always be idempotent
279
-- | Split and join should always be idempotent.
266 280
prop_Utils_commaSplitJoin s = Utils.commaJoin (Utils.sepSplit ',' s) == s
267 281

  
268 282
-- | fromObjWithDefault, we test using the Maybe monad and an integer
269
-- value
283
-- value.
270 284
prop_Utils_fromObjWithDefault def_value random_key =
271 285
    -- a missing key will be returned with the default
272 286
    Utils.fromObjWithDefault [] random_key def_value == Just def_value &&
......
275 289
         random_key (def_value+1) == Just def_value
276 290
        where _types = def_value :: Integer
277 291

  
292
-- | Test list for the Utils module.
278 293
testUtils =
279 294
  [ run prop_Utils_commaJoinSplit
280 295
  , run prop_Utils_commaSplitJoin
281 296
  , run prop_Utils_fromObjWithDefault
282 297
  ]
283 298

  
284
-- | Make sure add is idempotent
299
-- ** PeerMap tests
300

  
301
-- | Make sure add is idempotent.
285 302
prop_PeerMap_addIdempotent pmap key em =
286 303
    fn puniq == fn (fn puniq)
287 304
    where _types = (pmap::PeerMap.PeerMap,
......
289 306
          fn = PeerMap.add key em
290 307
          puniq = PeerMap.accumArray const pmap
291 308

  
292
-- | Make sure remove is idempotent
309
-- | Make sure remove is idempotent.
293 310
prop_PeerMap_removeIdempotent pmap key =
294 311
    fn puniq == fn (fn puniq)
295 312
    where _types = (pmap::PeerMap.PeerMap, key::PeerMap.Key)
296 313
          fn = PeerMap.remove key
297 314
          puniq = PeerMap.accumArray const pmap
298 315

  
299
-- | Make sure a missing item returns 0
316
-- | Make sure a missing item returns 0.
300 317
prop_PeerMap_findMissing pmap key =
301 318
    PeerMap.find key (PeerMap.remove key puniq) == 0
302 319
    where _types = (pmap::PeerMap.PeerMap, key::PeerMap.Key)
303 320
          puniq = PeerMap.accumArray const pmap
304 321

  
305
-- | Make sure an added item is found
322
-- | Make sure an added item is found.
306 323
prop_PeerMap_addFind pmap key em =
307 324
    PeerMap.find key (PeerMap.add key em puniq) == em
308 325
    where _types = (pmap::PeerMap.PeerMap,
309 326
                    key::PeerMap.Key, em::PeerMap.Elem)
310 327
          puniq = PeerMap.accumArray const pmap
311 328

  
312
-- | Manual check that maxElem returns the maximum indeed, or 0 for null
329
-- | Manual check that maxElem returns the maximum indeed, or 0 for null.
313 330
prop_PeerMap_maxElem pmap =
314 331
    PeerMap.maxElem puniq == if null puniq then 0
315 332
                             else (maximum . snd . unzip) puniq
316 333
    where _types = pmap::PeerMap.PeerMap
317 334
          puniq = PeerMap.accumArray const pmap
318 335

  
336
-- | List of tests for the PeerMap module.
319 337
testPeerMap =
320 338
    [ run prop_PeerMap_addIdempotent
321 339
    , run prop_PeerMap_removeIdempotent
......
324 342
    , run prop_PeerMap_findMissing
325 343
    ]
326 344

  
327
-- Container tests
345
-- ** Container tests
328 346

  
329 347
prop_Container_addTwo cdata i1 i2 =
330 348
    fn i1 i2 cont == fn i2 i1 cont &&
......
339 357
      fnode = head (Container.elems nl)
340 358
  in Container.nameOf nl (Node.idx fnode) == Node.name fnode
341 359

  
342
-- We test that in a cluster, given a random node, we can find it by
360
-- | We test that in a cluster, given a random node, we can find it by
343 361
-- its name and alias, as long as all names and aliases are unique,
344
-- and that we fail to find a non-existing name
362
-- and that we fail to find a non-existing name.
345 363
prop_Container_findByName node othername =
346 364
  forAll (choose (1, 20)) $ \ cnt ->
347 365
  forAll (choose (0, cnt - 1)) $ \ fidx ->
......
367 385
    , run prop_Container_findByName
368 386
    ]
369 387

  
388
-- ** Instance tests
389

  
370 390
-- Simple instance tests, we only have setter/getters
371 391

  
372 392
prop_Instance_creat inst =
......
471 491
    , run prop_Instance_setMovable
472 492
    ]
473 493

  
494
-- ** Text backend tests
495

  
474 496
-- Instance text loader tests
475 497

  
476 498
prop_Text_Load_Instance name mem dsk vcpus status
......
565 587
    , run prop_Text_NodeLSIdempotent
566 588
    ]
567 589

  
568
-- Node tests
590
-- ** Node tests
569 591

  
570 592
prop_Node_setAlias node name =
571 593
    Node.name newnode == Node.name node &&
......
585 607
    Node.mCpu newnode == mc
586 608
    where newnode = Node.setMcpu node mc
587 609

  
588
-- | Check that an instance add with too high memory or disk will be rejected
610
-- | Check that an instance add with too high memory or disk will be
611
-- rejected.
589 612
prop_Node_addPriFM node inst = Instance.mem inst >= Node.fMem node &&
590 613
                               not (Node.failN1 node)
591 614
                               ==>
......
615 638
          inst' = setInstanceSmallerThanNode node inst
616 639
          inst'' = inst' { Instance.vcpus = Node.availCpu node + extra }
617 640

  
618
-- | Check that an instance add with too high memory or disk will be rejected
641
-- | Check that an instance add with too high memory or disk will be
642
-- rejected.
619 643
prop_Node_addSec node inst pdx =
620 644
    (Instance.mem inst >= (Node.fMem node - Node.rMem node) ||
621 645
     Instance.dsk inst >= Node.fDsk node) &&
......
623 647
    ==> isFailure (Node.addSec node inst pdx)
624 648
        where _types = (node::Node.Node, inst::Instance.Instance, pdx::Int)
625 649

  
626
-- | Checks for memory reservation changes
650
-- | Checks for memory reservation changes.
627 651
prop_Node_rMem inst =
628 652
    forAll (arbitrary `suchThat` ((> 0) . Node.fMem)) $ \node ->
629 653
    -- ab = auto_balance, nb = non-auto_balance
......
655 679
         x -> printTestCase ("Failed to add/remove instances: " ++ show x)
656 680
              False
657 681

  
658
newtype SmallRatio = SmallRatio Double deriving Show
659
instance Arbitrary SmallRatio where
660
    arbitrary = do
661
      v <- choose (0, 1)
662
      return $ SmallRatio v
663

  
664
-- | Check mdsk setting
682
-- | Check mdsk setting.
665 683
prop_Node_setMdsk node mx =
666 684
    Node.loDsk node' >= 0 &&
667 685
    fromIntegral (Node.loDsk node') <= Node.tDsk node &&
......
715 733
    ]
716 734

  
717 735

  
718
-- Cluster tests
736
-- ** Cluster tests
719 737

  
720
-- | Check that the cluster score is close to zero for a homogeneous cluster
738
-- | Check that the cluster score is close to zero for a homogeneous
739
-- cluster.
721 740
prop_Score_Zero node =
722 741
    forAll (choose (1, 1024)) $ \count ->
723 742
    (not (Node.offline node) && not (Node.failN1 node) && (count > 0) &&
......
730 749
    -- this should be much lower than the default score in CLI.hs
731 750
    in score <= 1e-12
732 751

  
733
-- | Check that cluster stats are sane
752
-- | Check that cluster stats are sane.
734 753
prop_CStats_sane node =
735 754
    forAll (choose (1, 1024)) $ \count ->
736 755
    (not (Node.offline node) && not (Node.failN1 node) &&
......
743 762
       Cluster.csAdsk cstats <= Cluster.csFdsk cstats
744 763

  
745 764
-- | Check that one instance is allocated correctly, without
746
-- rebalances needed
765
-- rebalances needed.
747 766
prop_ClusterAlloc_sane node inst =
748 767
    forAll (choose (5, 20)) $ \count ->
749 768
    not (Node.offline node)
......
768 787

  
769 788
-- | Checks that on a 2-5 node cluster, we can allocate a random
770 789
-- instance spec via tiered allocation (whatever the original instance
771
-- spec), on either one or two nodes
790
-- spec), on either one or two nodes.
772 791
prop_ClusterCanTieredAlloc node inst =
773 792
    forAll (choose (2, 5)) $ \count ->
774 793
    forAll (choose (1, 2)) $ \rqnodes ->
......
787 806
                                      length ixes == length cstats
788 807

  
789 808
-- | Checks that on a 4-8 node cluster, once we allocate an instance,
790
-- we can also evacuate it
809
-- we can also evacuate it.
791 810
prop_ClusterAllocEvac node inst =
792 811
    forAll (choose (4, 8)) $ \count ->
793 812
    not (Node.offline node)
......
812 831
               _ -> False
813 832

  
814 833
-- | Check that allocating multiple instances on a cluster, then
815
-- adding an empty node, results in a valid rebalance
834
-- adding an empty node, results in a valid rebalance.
816 835
prop_ClusterAllocBalance =
817 836
    forAll (genNode (Just 5) (Just 128)) $ \node ->
818 837
    forAll (choose (3, 5)) $ \count ->
......
831 850
                       tbl = Cluster.Table ynl il' cv []
832 851
                   in canBalance tbl True True False
833 852

  
834
-- | Checks consistency
853
-- | Checks consistency.
835 854
prop_ClusterCheckConsistency node inst =
836 855
  let nl = makeSmallCluster node 3
837 856
      [node1, node2, node3] = Container.elems nl
......
845 864
     null (ccheck [(0, inst2)]) &&
846 865
     (not . null $ ccheck [(0, inst3)])
847 866

  
848
-- For now, we only test that we don't lose instances during the split
867
-- | For now, we only test that we don't lose instances during the split.
849 868
prop_ClusterSplitCluster node inst =
850 869
  forAll (choose (0, 100)) $ \icnt ->
851 870
  let nl = makeSmallCluster node 2
......
867 886
    , run prop_ClusterSplitCluster
868 887
    ]
869 888

  
870
-- | Check that opcode serialization is idempotent
889
-- ** OpCodes tests
871 890

  
891
-- | Check that opcode serialization is idempotent.
872 892
prop_OpCodes_serialization op =
873 893
  case J.readJSON (J.showJSON op) of
874 894
    J.Error _ -> False
......
879 899
  [ run prop_OpCodes_serialization
880 900
  ]
881 901

  
882
-- | Check that (queued) job\/opcode status serialization is idempotent
902
-- ** Jobs tests
903

  
904
-- | Check that (queued) job\/opcode status serialization is idempotent.
883 905
prop_OpStatus_serialization os =
884 906
  case J.readJSON (J.showJSON os) of
885 907
    J.Error _ -> False
......
897 919
  , run prop_JobStatus_serialization
898 920
  ]
899 921

  
900
-- | Loader tests
922
-- ** Loader tests
901 923

  
902 924
prop_Loader_lookupNode ktn inst node =
903 925
  Loader.lookupNode nl inst node == Data.Map.lookup node nl
......
915 937
   else True)
916 938
  where (nassoc, kt) = Loader.assignIndices (map (\n -> (Node.name n, n)) nodes)
917 939

  
918

  
919 940
-- | Checks that the number of primary instances recorded on the nodes
920
-- is zero
941
-- is zero.
921 942
prop_Loader_mergeData ns =
922 943
  let na = Container.fromList $ map (\n -> (Node.idx n, n)) ns
923 944
  in case Loader.mergeData [] [] [] []
b/htools/Ganeti/HTools/Rapi.hs
57 57

  
58 58
#else
59 59

  
60
-- | The curl options we use
60
-- | The curl options we use.
61 61
curlOpts :: [CurlOption]
62 62
curlOpts = [ CurlSSLVerifyPeer False
63 63
           , CurlSSLVerifyHost 0
......
97 97
getGroups body = loadJSArray "Parsing group data" body >>=
98 98
                mapM (parseGroup . fromJSObject)
99 99

  
100
-- | Generates a fake group list.
100 101
getFakeGroups :: Result [(String, Group.Group)]
101 102
getFakeGroups =
102 103
  return [(defaultGroupID,
......
173 174
  tags_body <- getUrl $ printf "%s/2/tags" url
174 175
  return (group_body, node_body, inst_body, tags_body)
175 176

  
176
-- | Builds the cluster data from the raw Rapi content
177
-- | Builds the cluster data from the raw Rapi content.
177 178
parseData :: (Result String, Result String, Result String, Result String)
178 179
          -> Result ClusterData
179 180
parseData (group_body, node_body, inst_body, tags_body) = do
......
191 192
  tags_data <- tags_body >>= (fromJResult "Parsing tags data" . decodeStrict)
192 193
  return (ClusterData group_idx node_idx inst_idx tags_data)
193 194

  
194
-- | Top level function for data loading
195
-- | Top level function for data loading.
195 196
loadData :: String -- ^ Cluster or URL to use as source
196 197
         -> IO (Result ClusterData)
197 198
loadData = fmap parseData . readData
b/htools/Ganeti/HTools/Simu.hs
1
{-| Parsing data from a simulated description of the cluster
1
{-| Parsing data from a simulated description of the cluster.
2 2

  
3 3
This module holds the code for parsing a cluster description.
4 4

  
b/htools/Ganeti/HTools/Text.hs
1
{-| Parsing data from text-files
1
{-| Parsing data from text-files.
2 2

  
3 3
This module holds the code for loading the cluster state from text
4
files, as produced by gnt-node and gnt-instance list.
4
files, as produced by @gnt-node@ and @gnt-instance@ @list@ command.
5 5

  
6 6
-}
7 7

  
......
51 51
import qualified Ganeti.HTools.Node as Node
52 52
import qualified Ganeti.HTools.Instance as Instance
53 53

  
54
-- | Serialize a single group
54
-- * Serialisation functions
55

  
56
-- | Serialize a single group.
55 57
serializeGroup :: Group.Group -> String
56 58
serializeGroup grp =
57 59
    printf "%s|%s|%s" (Group.name grp) (Group.uuid grp)
58 60
               (apolToString (Group.allocPolicy grp))
59 61

  
60
-- | Generate group file data from a group list
62
-- | Generate group file data from a group list.
61 63
serializeGroups :: Group.List -> String
62 64
serializeGroups = unlines . map serializeGroup . Container.elems
63 65

  
64
-- | Serialize a single node
65
serializeNode :: Group.List -> Node.Node -> String
66
-- | Serialize a single node.
67
serializeNode :: Group.List -- ^ The list of groups (needed for group uuid)
68
              -> Node.Node  -- ^ The node to be serialised
69
              -> String
66 70
serializeNode gl node =
67 71
    printf "%s|%.0f|%d|%d|%.0f|%d|%.0f|%c|%s" (Node.name node)
68 72
               (Node.tMem node) (Node.nMem node) (Node.fMem node)
......
71 75
               (Group.uuid grp)
72 76
    where grp = Container.find (Node.group node) gl
73 77

  
74
-- | Generate node file data from node objects
78
-- | Generate node file data from node objects.
75 79
serializeNodes :: Group.List -> Node.List -> String
76 80
serializeNodes gl = unlines . map (serializeNode gl) . Container.elems
77 81

  
78
-- | Serialize a single instance
79
serializeInstance :: Node.List -> Instance.Instance -> String
82
-- | Serialize a single instance.
83
serializeInstance :: Node.List         -- ^ The node list (needed for
84
                                       -- node names)
85
                  -> Instance.Instance -- ^ The instance to be serialised
86
                  -> String
80 87
serializeInstance nl inst =
81 88
    let
82 89
        iname = Instance.name inst
......
92 99
             (if Instance.auto_balance inst then "Y" else "N")
93 100
             pnode snode (intercalate "," (Instance.tags inst))
94 101

  
95
-- | Generate instance file data from instance objects
102
-- | Generate instance file data from instance objects.
96 103
serializeInstances :: Node.List -> Instance.List -> String
97 104
serializeInstances nl =
98 105
    unlines . map (serializeInstance nl) . Container.elems
99 106

  
100
-- | Generate complete cluster data from node and instance lists
107
-- | Generate complete cluster data from node and instance lists.
101 108
serializeCluster :: ClusterData -> String
102 109
serializeCluster (ClusterData gl nl il ctags) =
103 110
  let gdata = serializeGroups gl
......
106 113
  -- note: not using 'unlines' as that adds too many newlines
107 114
  in intercalate "\n" [gdata, ndata, idata, unlines ctags]
108 115

  
116
-- * Parsing functions
117

  
109 118
-- | Load a group from a field list.
110
loadGroup :: (Monad m) => [String] -> m (String, Group.Group)
119
loadGroup :: (Monad m) => [String]
120
          -> m (String, Group.Group) -- ^ The result, a tuple of group
121
                                     -- UUID and group object
111 122
loadGroup [name, gid, apol] = do
112 123
  xapol <- apolFromString apol
113 124
  return (gid, Group.create name gid xapol)
......
115 126
loadGroup s = fail $ "Invalid/incomplete group data: '" ++ show s ++ "'"
116 127

  
117 128
-- | Load a node from a field list.
118
loadNode :: (Monad m) => NameAssoc -> [String] -> m (String, Node.Node)
129
loadNode :: (Monad m) =>
130
            NameAssoc             -- ^ Association list with current groups
131
         -> [String]              -- ^ Input data as a list of fields
132
         -> m (String, Node.Node) -- ^ The result, a tuple o node name
133
                                  -- and node object
119 134
loadNode ktg [name, tm, nm, fm, td, fd, tc, fo, gu] = do
120 135
  gdx <- lookupGroup ktg name gu
121 136
  new_node <-
......
134 149

  
135 150
-- | Load an instance from a field list.
136 151
loadInst :: (Monad m) =>
137
            NameAssoc -> [String] -> m (String, Instance.Instance)
152
            NameAssoc                     -- ^ Association list with
153
                                          -- the current nodes
154
         -> [String]                      -- ^ Input data as a list of
155
                                          -- fields
156
         -> m (String, Instance.Instance) -- ^ The result, a tuple of
157
                                          -- instance name and the
158
                                          -- instance object
138 159
loadInst ktn [name, mem, dsk, vcpus, status, auto_bal, pnode, snode, tags] = do
139 160
  pidx <- lookupNode ktn name pnode
140 161
  sidx <- (if null snode then return Node.noSecondary
......
161 182
-- @gnt-instance list@ and @gnt-node list@ to a list of objects using
162 183
-- a supplied conversion function.
163 184
loadTabular :: (Monad m, Element a) =>
164
               [String] -> ([String] -> m (String, a))
165
            -> m (NameAssoc, Container.Container a)
185
               [String] -- ^ Input data, as a list of lines
186
            -> ([String] -> m (String, a)) -- ^ Conversion function
187
            -> m ( NameAssoc
188
                 , Container.Container a ) -- ^ A tuple of an
189
                                           -- association list (name
190
                                           -- to object) and a set as
191
                                           -- used in
192
                                           -- "Ganeti.HTools.Container"
193

  
166 194
loadTabular lines_data convert_fn = do
167 195
  let rows = map (sepSplit '|') lines_data
168 196
  kerows <- mapM convert_fn rows
169 197
  return $ assignIndices kerows
170 198

  
171 199
-- | Load the cluser data from disk.
172
readData :: String -- ^ Path to the text file
173
         -> IO String
200
--
201
-- This is an alias to 'readFile' just for consistency with the other
202
-- modules.
203
readData :: String    -- ^ Path to the text file
204
         -> IO String -- ^ Contents of the file
174 205
readData = readFile
175 206

  
176 207
-- | Builds the cluster data from text input.
......
192 223
  {- the tags are simply line-based, no processing needed -}
193 224
  return (ClusterData gl nl il ctags)
194 225

  
195
-- | Top level function for data loading
226
-- | Top level function for data loading.
196 227
loadData :: String -- ^ Path to the text file
197 228
         -> IO (Result ClusterData)
198 229
loadData = fmap parseData . readData
b/htools/Ganeti/HTools/Types.hs
102 102
                       -- allocations
103 103
      deriving (Show, Read, Eq, Ord)
104 104

  
105
-- | Convert a string to an alloc policy
105
-- | Convert a string to an alloc policy.
106 106
apolFromString :: (Monad m) => String -> m AllocPolicy
107 107
apolFromString s =
108 108
    case () of
......
111 111
        | s == C.allocPolicyUnallocable -> return AllocUnallocable
112 112
        | otherwise -> fail $ "Invalid alloc policy mode: " ++ s
113 113

  
114
-- | Convert an alloc policy to the Ganeti string equivalent
114
-- | Convert an alloc policy to the Ganeti string equivalent.
115 115
apolToString :: AllocPolicy -> String
116 116
apolToString AllocPreferred   = C.allocPolicyPreferred
117 117
apolToString AllocLastResort  = C.allocPolicyLastResort
......
140 140
    , netWeight :: Weight -- ^ Standardised network usage
141 141
    } deriving (Show, Read, Eq)
142 142

  
143
-- | Initial empty utilisation
143
-- | Initial empty utilisation.
144 144
zeroUtil :: DynUtil
145 145
zeroUtil = DynUtil { cpuWeight = 0, memWeight = 0
146 146
                   , dskWeight = 0, netWeight = 0 }
147 147

  
148
-- | Base utilisation (used when no actual utilisation data is
149
-- supplied).
148 150
baseUtil :: DynUtil
149 151
baseUtil = DynUtil { cpuWeight = 1, memWeight = 1
150 152
                   , dskWeight = 1, netWeight = 1 }
151 153

  
154
-- | Sum two utilisation records.
152 155
addUtil :: DynUtil -> DynUtil -> DynUtil
153 156
addUtil (DynUtil a1 a2 a3 a4) (DynUtil b1 b2 b3 b4) =
154 157
    DynUtil (a1+b1) (a2+b2) (a3+b3) (a4+b4)
155 158

  
159
-- | Substracts one utilisation record from another.
156 160
subUtil :: DynUtil -> DynUtil -> DynUtil
157 161
subUtil (DynUtil a1 a2 a3 a4) (DynUtil b1 b2 b3 b4) =
158 162
    DynUtil (a1-b1) (a2-b2) (a3-b3) (a4-b4)
......
162 166
-- performed and the score of the cluster after the move.
163 167
type Placement = (Idx, Ndx, Ndx, IMove, Score)
164 168

  
165
-- | An instance move definition
169
-- | An instance move definition.
166 170
data IMove = Failover                -- ^ Failover the instance (f)
167 171
           | ReplacePrimary Ndx      -- ^ Replace primary (f, r:np, f)
168 172
           | ReplaceSecondary Ndx    -- ^ Replace secondary (r:ns)
......
171 175
             deriving (Show, Read)
172 176

  
173 177
-- | Formatted solution output for one move (involved nodes and
174
-- commands
178
-- commands.
175 179
type MoveJob = ([Ndx], Idx, IMove, [String])
176 180

  
177
-- | Unknown field in table output
181
-- | Unknown field in table output.
178 182
unknownField :: String
179 183
unknownField = "<unknown field>"
180 184

  
181
-- | A list of command elements
185
-- | A list of command elements.
182 186
type JobSet = [MoveJob]
183 187

  
184 188
-- | Connection timeout (when using non-file methods).
......
211 215

  
212 216
{-|
213 217

  
214
This is similar to the JSON library Result type - *very* similar, but
218
This is similar to the JSON library Result type - /very/ similar, but
215 219
we want to use it in multiple places, so we abstract it into a
216 220
mini-library here
217 221

  
......
227 231
    return = Ok
228 232
    fail = Bad
229 233

  
230
-- | Simple checker for whether Result is OK
234
-- | Simple checker for whether a 'Result' is OK.
231 235
isOk :: Result a -> Bool
232 236
isOk (Ok _) = True
233 237
isOk _ = False
234 238

  
235
-- | Simple checker for whether Result is a failure
239
-- | Simple checker for whether a 'Result' is a failure.
236 240
isBad :: Result a  -> Bool
237 241
isBad = not . isOk
238 242

  
239
-- | Reason for an operation's falure
243
-- | Reason for an operation's falure.
240 244
data FailMode = FailMem  -- ^ Failed due to not enough RAM
241 245
              | FailDisk -- ^ Failed due to not enough disk
242 246
              | FailCPU  -- ^ Failed due to not enough CPU capacity
......
244 248
              | FailTags -- ^ Failed due to tag exclusion
245 249
                deriving (Eq, Enum, Bounded, Show, Read)
246 250

  
247
-- | List with failure statistics
251
-- | List with failure statistics.
248 252
type FailStats = [(FailMode, Int)]
249 253

  
250
-- | Either-like data-type customized for our failure modes
254
-- | Either-like data-type customized for our failure modes.
251 255
data OpResult a = OpFail FailMode -- ^ Failed operation
252 256
                | OpGood a        -- ^ Success operation
253 257
                  deriving (Show, Read)
......
268 272
    -- | Updates the alias of the element
269 273
    setAlias :: a -> String -> a
270 274
    -- | Compute the alias by stripping a given suffix (domain) from
271
    -- | the name
275
    -- the name
272 276
    computeAlias :: String -> a -> a
273 277
    computeAlias dom e = setAlias e alias
274 278
        where alias = take (length name - length dom) name
b/htools/Ganeti/HTools/Utils.hs
1
{-| Utility functions -}
1
{-| Utility functions. -}
2 2

  
3 3
{-
4 4

  
......
62 62
debug :: Show a => a -> a
63 63
debug x = trace (show x) x
64 64

  
65
-- | Displays a modified form of the second parameter before returning it
65
-- | Displays a modified form of the second parameter before returning
66
-- it.
66 67
debugFn :: Show b => (a -> b) -> a -> a
67 68
debugFn fn x = debug (fn x) `seq` x
68 69

  
69
-- | Show the first parameter before returning the second one
70
-- | Show the first parameter before returning the second one.
70 71
debugXy :: Show a => a -> b -> b
71 72
debugXy a b = debug a `seq` b
72 73

  
73
-- * Miscelaneous
74
-- * Miscellaneous
74 75

  
75 76
-- | Comma-join a string list.
76 77
commaJoin :: [String] -> String
......
91 92
-- Simple and slow statistical functions, please replace with better
92 93
-- versions
93 94

  
94
-- | Standard deviation function
95
-- | Standard deviation function.
95 96
stdDev :: [Double] -> Double
96 97
stdDev lst =
97 98
  -- first, calculate the list length and sum lst in a single step,
......
107 108

  
108 109
-- * JSON-related functions
109 110

  
110
-- | A type alias for the list-based representation of J.JSObject
111
-- | A type alias for the list-based representation of J.JSObject.
111 112
type JSRecord = [(String, J.JSValue)]
112 113

  
113 114
-- | Converts a JSON Result into a monadic value.
......
153 154
                      JSRecord -> String -> a -> m a
154 155
fromObjWithDefault o k d = liftM (fromMaybe d) $ maybeFromObj o k
155 156

  
156
-- | Reads a JValue, that originated from an object key
157
-- | Reads a JValue, that originated from an object key.
157 158
fromKeyValue :: (J.JSON a, Monad m)
158 159
              => String     -- ^ The key name
159 160
              -> J.JSValue  -- ^ The value to read
......
161 162
fromKeyValue k val =
162 163
  fromJResult (printf "key '%s', value '%s'" k (show val)) (J.readJSON val)
163 164

  
164
-- | Annotate a Result with an ownership information
165
-- | Annotate a Result with an ownership information.
165 166
annotateResult :: String -> Result a -> Result a
166 167
annotateResult owner (Bad s) = Bad $ owner ++ ": " ++ s
167 168
annotateResult _ v = v
168 169

  
169 170
-- | Try to extract a key from a object with better error reporting
170
-- than fromObj
171
-- than fromObj.
171 172
tryFromObj :: (J.JSON a) =>
172 173
              String     -- ^ Textual "owner" in error messages
173 174
           -> JSRecord   -- ^ The object array
......
194 195

  
195 196
-- * Parsing utility functions
196 197

  
197
-- | Parse results from readsPrec
198
-- | Parse results from readsPrec.
198 199
parseChoices :: (Monad m, Read a) => String -> String -> [(a, String)] -> m a
199 200
parseChoices _ _ ((v, ""):[]) = return v
200 201
parseChoices name s ((_, e):[]) =
......
206 207
tryRead :: (Monad m, Read a) => String -> String -> m a
207 208
tryRead name s = parseChoices name s $ reads s
208 209

  
209
-- | Format a table of strings to maintain consistent length
210
-- | Format a table of strings to maintain consistent length.
210 211
formatTable :: [[String]] -> [Bool] -> [[String]]
211 212
formatTable vals numpos =
212 213
    let vtrans = transpose vals  -- transpose, so that we work on rows
......
225 226
                    ) (zip3 vtrans numpos mlens)
226 227
   in transpose expnd
227 228

  
228
-- | Default group UUID (just a string, not a real UUID)
229
-- | Default group UUID (just a string, not a real UUID).
229 230
defaultGroupID :: GroupID
230 231
defaultGroupID = "00000000-0000-0000-0000-000000000000"
b/htools/Ganeti/Jobs.hs
33 33

  
34 34
import qualified Ganeti.Constants as C
35 35

  
36
-- | Our ADT for the OpCode status at runtime (while in a job).
36 37
data OpStatus = OP_STATUS_QUEUED
37 38
              | OP_STATUS_WAITLOCK
38 39
              | OP_STATUS_CANCELING
b/htools/Ganeti/Luxi.hs
161 161
  writeIORef (rbuf s) nbuf
162 162
  return msg
163 163

  
164
-- | Compute the serialized form of a Luxi operation
164
-- | Compute the serialized form of a Luxi operation.
165 165
opToArgs :: LuxiOp -> JSValue
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff