Revision 3603605a

b/Makefile.am
1170 1170
.PHONY: hlint
1171 1171
hlint: $(HS_BUILT_SRCS)
1172 1172
	if tty -s; then C="-c"; else C=""; fi; \
1173
	hlint --report=doc/hs-lint.html $$C htools
1173
	hlint --report=doc/hs-lint.html --cross $$C \
1174
	  --ignore "Use first" \
1175
	  --ignore "Use comparing" \
1176
	  --ignore "Use on" \
1177
	  --ignore "Use Control.Exception.catch" \
1178
	  --ignore "Reduce duplication" \
1179
	  $(filter-out htools/Ganeti/THH.hs,$(HS_LIB_SRCS))
1174 1180

  
1175 1181
# a dist hook rule for updating the vcs-version file; this is
1176 1182
# hardcoded due to where it needs to build the file...
b/doc/devnotes.rst
36 36
- `HsColour <http://hackage.haskell.org/package/hscolour>`_, again
37 37
  used for documentation (it's source-code pretty-printing)
38 38
- `hlint <http://community.haskell.org/~ndm/hlint/>`_, a source code
39
  linter (equivalent to pylint for Python)
39
  linter (equivalent to pylint for Python), recommended version 1.8 or
40
  above (tested with 1.8.15)
40 41
- the `QuickCheck <http://hackage.haskell.org/package/QuickCheck>`_
41 42
  library, version 2.x
42 43
- ``hpc``, which comes with the compiler, so you should already have
......
69 70

  
70 71
  make hlint
71 72

  
72
This is not enabled by default as it gets many false positives, and
73
thus the normal output is not “clean”. The above command will generate
74
both output on the terminal and also a HTML report at
73
This is not enabled by default (as the htools component is
74
optional). The above command will generate both output on the terminal
75
and, if any warnings are found, also an HTML report at
75 76
``doc/hs-lint.html``.
76 77

  
77 78
When writing or debugging TemplateHaskell code, it's useful to see
b/htools/Ganeti/HTools/CLI.hs
452 452
    (o, n, []) ->
453 453
      do
454 454
        let (pr, args) = (foldM (flip id) defaultOptions o, n)
455
        po <- (case pr of
456
                 Bad msg -> do
457
                   hPutStrLn stderr "Error while parsing command\
458
                                    \line arguments:"
459
                   hPutStrLn stderr msg
460
                   exitWith $ ExitFailure 1
461
                 Ok val -> return val)
455
        po <- case pr of
456
                Bad msg -> do
457
                  hPutStrLn stderr "Error while parsing command\
458
                                   \line arguments:"
459
                  hPutStrLn stderr msg
460
                  exitWith $ ExitFailure 1
461
                Ok val -> return val
462 462
        when (optShowHelp po) $ do
463 463
          putStr $ usageHelp progname options
464 464
          exitWith ExitSuccess
......
534 534
      m_cpu = optMcpu opts
535 535
      m_dsk = optMdsk opts
536 536

  
537
  when (not (null offline_wrong)) $ do
537
  unless (null offline_wrong) $ do
538 538
         hPrintf stderr "Error: Wrong node name(s) set as offline: %s\n"
539 539
                     (commaJoin (map lrContent offline_wrong)) :: IO ()
540 540
         exitWith $ ExitFailure 1
b/htools/Ganeti/HTools/Cluster.hs
373 373
  let (old_pdx, old_sdx, old_p, old_s) = instanceNodes nl inst
374 374
      int_p = Node.removePri old_p inst
375 375
      int_s = Node.removeSec old_s inst
376
      force_p = Node.offline old_p
377 376
      new_nl = do -- Maybe monad
378
        new_p <- Node.addPriEx force_p int_s inst
377
        new_p <- Node.addPriEx (Node.offline old_p) int_s inst
379 378
        new_s <- Node.addSec int_p inst old_sdx
380 379
        let new_inst = Instance.setBoth inst old_sdx old_pdx
381 380
        return (Container.addTwo old_pdx new_s old_sdx new_p nl,
......
526 525
checkInstanceMove nodes_idx disk_moves inst_moves ini_tbl target =
527 526
  let opdx = Instance.pNode target
528 527
      osdx = Instance.sNode target
529
      nodes = filter (\idx -> idx /= opdx && idx /= osdx) nodes_idx
528
      bad_nodes = [opdx, osdx]
529
      nodes = filter (`notElem` bad_nodes) nodes_idx
530 530
      use_secondary = elem osdx nodes_idx && inst_moves
531 531
      aft_failover = if use_secondary -- if allowed to failover
532 532
                       then checkSingleStep ini_tbl target ini_tbl Failover
......
1308 1308
                 _ -> fs
1309 1309
      snl = sortBy (comparing Node.idx) (Container.elems nl)
1310 1310
      (header, isnum) = unzip $ map Node.showHeader fields
1311
  in unlines . map ((:) ' ' .  intercalate " ") $
1311
  in unlines . map ((:) ' ' .  unwords) $
1312 1312
     formatTable (header:map (Node.list fields) snl) isnum
1313 1313

  
1314 1314
-- | Print the instance list.
......
1335 1335
      header = [ "F", "Name", "Pri_node", "Sec_node", "Auto_bal"
1336 1336
               , "vcpu", "mem" , "dsk", "lCpu", "lMem", "lDsk", "lNet" ]
1337 1337
      isnum = False:False:False:False:False:repeat True
1338
  in unlines . map ((:) ' ' . intercalate " ") $
1338
  in unlines . map ((:) ' ' . unwords) $
1339 1339
     formatTable (header:map helper sil) isnum
1340 1340

  
1341 1341
-- | Shows statistics for a given node list.
b/htools/Ganeti/HTools/ExtLoader.hs
95 95
                           " files options should be given.")
96 96
         exitWith $ ExitFailure 1
97 97

  
98
  util_contents <- (case optDynuFile opts of
99
                      Just path -> readFile path
100
                      Nothing -> return "")
98
  util_contents <- maybe (return "") readFile (optDynuFile opts)
101 99
  let util_data = mapM parseUtilisation $ lines util_contents
102
  util_data' <- (case util_data of
103
                   Ok x  -> return x
104
                   Bad y -> do
105
                     hPutStrLn stderr ("Error: can't parse utilisation" ++
106
                                       " data: " ++ show y)
107
                     exitWith $ ExitFailure 1)
100
  util_data' <- case util_data of
101
                  Ok x  -> return x
102
                  Bad y -> do
103
                    hPutStrLn stderr ("Error: can't parse utilisation" ++
104
                                      " data: " ++ show y)
105
                    exitWith $ ExitFailure 1
108 106
  input_data <-
109 107
    case () of
110 108
      _ | setRapi -> wrapIO $ Rapi.loadData mhost
......
115 113

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

  
127 124
  unless (optVerbose opts == 0) $ maybeShowWarnings fix_msgs
b/htools/Ganeti/HTools/IAlloc.hs
50 50
import Ganeti.HTools.Utils
51 51
import Ganeti.HTools.Types
52 52

  
53
{-# ANN module "HLint: ignore Eta reduce" #-}
54

  
53 55
-- | Type alias for the result of an IAllocator call.
54 56
type IAllocResult = (String, JSValue, Node.List, Instance.List)
55 57

  
......
83 85
           else readEitherString $ head nodes
84 86
  pidx <- lookupNode ktn n pnode
85 87
  let snodes = tail nodes
86
  sidx <- (if null snodes then return Node.noSecondary
87
           else readEitherString (head snodes) >>= lookupNode ktn n)
88
  sidx <- if null snodes
89
            then return Node.noSecondary
90
            else readEitherString (head snodes) >>= lookupNode ktn n
88 91
  return (n, Instance.setBoth (snd base) pidx sidx)
89 92

  
90 93
-- | Parses a node as found in the cluster node list.
......
101 104
  vm_capable  <- annotateResult desc $ maybeFromObj a "vm_capable"
102 105
  let vm_capable' = fromMaybe True vm_capable
103 106
  gidx <- lookupGroup ktg n guuid
104
  node <- (if offline || drained || not vm_capable'
105
           then return $ Node.create n 0 0 0 0 0 0 True gidx
106
           else do
107
             mtotal <- extract "total_memory"
108
             mnode  <- extract "reserved_memory"
109
             mfree  <- extract "free_memory"
110
             dtotal <- extract "total_disk"
111
             dfree  <- extract "free_disk"
112
             ctotal <- extract "total_cpus"
113
             return $ Node.create n mtotal mnode mfree
114
                    dtotal dfree ctotal False gidx)
107
  node <- if offline || drained || not vm_capable'
108
            then return $ Node.create n 0 0 0 0 0 0 True gidx
109
            else do
110
              mtotal <- extract "total_memory"
111
              mnode  <- extract "reserved_memory"
112
              mfree  <- extract "free_memory"
113
              dtotal <- extract "total_disk"
114
              dfree  <- extract "free_disk"
115
              ctotal <- extract "total_cpus"
116
              return $ Node.create n mtotal mnode mfree
117
                     dtotal dfree ctotal False gidx
115 118
  return (n, node)
116 119

  
117 120
-- | Parses a group as found in the cluster group list.
......
330 333
            hPutStrLn stderr $ "Error: " ++ err
331 334
            exitWith $ ExitFailure 1
332 335
          Ok (fix_msgs, rq) -> maybeShowWarnings fix_msgs >> return rq
333
  (if isJust (optDataFile opts) ||  (not . null . optNodeSim) opts
334
   then do
335
     cdata <- loadExternalData opts
336
     let Request rqt _ = r1
337
     return $ Request rqt cdata
338
   else return r1)
336
  if isJust (optDataFile opts) ||  (not . null . optNodeSim) opts
337
    then do
338
      cdata <- loadExternalData opts
339
      let Request rqt _ = r1
340
      return $ Request rqt cdata
341
    else return r1
339 342

  
340 343
-- | Main iallocator pipeline.
341 344
runIAllocator :: Request -> (Maybe (Node.List, Instance.List), String)
b/htools/Ganeti/HTools/Loader.hs
317 317
                        (Node.fMem node - adj_mem)
318 318
                 umsg1 =
319 319
                   if delta_mem > 512 || delta_dsk > 1024
320
                      then (printf "node %s is missing %d MB ram \
321
                                   \and %d GB disk"
322
                                   nname delta_mem (delta_dsk `div` 1024)):
323
                           msgs
320
                      then printf "node %s is missing %d MB ram \
321
                                  \and %d GB disk"
322
                                  nname delta_mem (delta_dsk `div` 1024):msgs
324 323
                      else msgs
325 324
             in (umsg1, newn)
326 325
        ) [] nl
b/htools/Ganeti/HTools/Luxi.hs
41 41
import Ganeti.HTools.Utils (fromJVal, annotateResult, tryFromObj, asJSObject,
42 42
                            fromObj)
43 43

  
44
{-# ANN module "HLint: ignore Eta reduce" #-}
45

  
44 46
-- * Utility functions
45 47

  
46 48
-- | Get values behind \"data\" part of the result.
......
148 150
  xname <- annotateResult "Parsing new instance" (fromJValWithStatus name)
149 151
  let convert a = genericConvert "Instance" xname a
150 152
  xdisk <- convert "disk_usage" disk
151
  xmem <- (case oram of -- FIXME: remove the "guessing"
152
             (_, JSRational _ _) -> convert "oper_ram" oram
153
             _ -> convert "be/memory" mem)
153
  xmem <- case oram of -- FIXME: remove the "guessing"
154
            (_, JSRational _ _) -> convert "oper_ram" oram
155
            _ -> convert "be/memory" mem
154 156
  xvcpus <- convert "be/vcpus" vcpus
155 157
  xpnode <- convert "pnode" pnode >>= lookupNode ktn xname
156 158
  xsnodes <- convert "snodes" snodes::Result [JSString]
157
  snode <- (if null xsnodes then return Node.noSecondary
158
            else lookupNode ktn xname (fromJSString $ head xsnodes))
159
  snode <- if null xsnodes
160
             then return Node.noSecondary
161
             else lookupNode ktn xname (fromJSString $ head xsnodes)
159 162
  xrunning <- convert "status" status
160 163
  xtags <- convert "tags" tags
161 164
  xauto_balance <- convert "auto_balance" auto_balance
......
181 184
  xdrained <- convert "drained" drained
182 185
  xvm_capable <- convert "vm_capable" vm_capable
183 186
  xgdx   <- convert "group.uuid" g_uuid >>= lookupGroup ktg xname
184
  node <- (if xoffline || xdrained || not xvm_capable
185
           then return $ Node.create xname 0 0 0 0 0 0 True xgdx
186
           else do
187
             xmtotal  <- convert "mtotal" mtotal
188
             xmnode   <- convert "mnode" mnode
189
             xmfree   <- convert "mfree" mfree
190
             xdtotal  <- convert "dtotal" dtotal
191
             xdfree   <- convert "dfree" dfree
192
             xctotal  <- convert "ctotal" ctotal
193
             return $ Node.create xname xmtotal xmnode xmfree
194
                    xdtotal xdfree xctotal False xgdx)
187
  node <- if xoffline || xdrained || not xvm_capable
188
            then return $ Node.create xname 0 0 0 0 0 0 True xgdx
189
            else do
190
              xmtotal  <- convert "mtotal" mtotal
191
              xmnode   <- convert "mnode" mnode
192
              xmfree   <- convert "mfree" mfree
193
              xdtotal  <- convert "dtotal" dtotal
194
              xdfree   <- convert "dfree" dfree
195
              xctotal  <- convert "ctotal" ctotal
196
              return $ Node.create xname xmtotal xmnode xmfree
197
                     xdtotal xdfree xctotal False xgdx
195 198
  return (xname, node)
196 199

  
197 200
parseNode _ v = fail ("Invalid node query result: " ++ show v)
b/htools/Ganeti/HTools/Node.hs
328 328
removeSec :: Node -> Instance.Instance -> Node
329 329
removeSec t inst =
330 330
  let iname = Instance.idx inst
331
      uses_disk = Instance.usesLocalStorage inst
332 331
      cur_dsk = fDsk t
333 332
      pnode = Instance.pNode inst
334 333
      new_slist = delete iname (sList t)
335
      new_dsk = if uses_disk
334
      new_dsk = if Instance.usesLocalStorage inst
336 335
                  then cur_dsk + Instance.dsk inst
337 336
                  else cur_dsk
338 337
      old_peers = peers t
b/htools/Ganeti/HTools/Program/Hail.hs
26 26
module Ganeti.HTools.Program.Hail (main) where
27 27

  
28 28
import Control.Monad
29
import Data.Maybe (fromMaybe)
29 30
import System.Environment (getArgs)
30 31
import System.IO
31 32

  
......
74 75
  maybeSaveData savecluster "pre-ialloc" "before iallocator run" cdata
75 76

  
76 77
  let (maybe_ni, resp) = runIAllocator request
77
      (fin_nl, fin_il) = maybe (cdNodes cdata, cdInstances cdata) id maybe_ni
78
      (fin_nl, fin_il) = fromMaybe (cdNodes cdata, cdInstances cdata) maybe_ni
78 79
  putStrLn resp
79 80

  
80 81
  maybePrintNodes shownodes "Final cluster" (Cluster.printNodes fin_nl)
b/htools/Ganeti/HTools/Program/Hbal.hs
143 143
saveBalanceCommands opts cmd_data = do
144 144
  let out_path = fromJust $ optShowCmds opts
145 145
  putStrLn ""
146
  (if out_path == "-" then
147
       printf "Commands to run to reach the above solution:\n%s"
148
                  (unlines . map ("  " ++) .
149
                   filter (/= "  check") .
150
                   lines $ cmd_data)
151
   else do
152
     writeFile out_path (shTemplate ++ cmd_data)
153
     printf "The commands have been written to file '%s'\n" out_path)
146
  if out_path == "-"
147
    then printf "Commands to run to reach the above solution:\n%s"
148
           (unlines . map ("  " ++) .
149
            filter (/= "  check") .
150
            lines $ cmd_data)
151
    else do
152
      writeFile out_path (shTemplate ++ cmd_data)
153
      printf "The commands have been written to file '%s'\n" out_path
154 154

  
155 155
-- | Polls a set of jobs at a fixed interval until all are finished
156 156
-- one way or another.
......
176 176
execWrapper _      _  _  _    [] = return True
177 177
execWrapper master nl il cref alljss = do
178 178
  cancel <- readIORef cref
179
  (if cancel > 0
180
   then do
181
     hPrintf stderr "Exiting early due to user request, %d\
182
                    \ jobset(s) remaining." (length alljss)::IO ()
183
     return False
184
   else execJobSet master nl il cref alljss)
179
  if cancel > 0
180
    then do
181
      hPrintf stderr "Exiting early due to user request, %d\
182
                     \ jobset(s) remaining." (length alljss)::IO ()
183
      return False
184
    else execJobSet master nl il cref alljss
185 185

  
186 186
-- | Execute an entire jobset.
187 187
execJobSet :: String -> Node.List
......
202 202
                putStrLn $ "Got job IDs " ++ commaJoin x
203 203
                waitForJobs client x
204 204
         )
205
  (case jrs of
206
     Bad x -> do
207
       hPutStrLn stderr $ "Cannot compute job status, aborting: " ++ show x
208
       return False
209
     Ok x -> if checkJobsStatus x
210
             then execWrapper master nl il cref jss
211
             else do
212
               hPutStrLn stderr $ "Not all jobs completed successfully: " ++
213
                         show x
214
               hPutStrLn stderr "Aborting."
215
               return False)
205
  case jrs of
206
    Bad x -> do
207
      hPutStrLn stderr $ "Cannot compute job status, aborting: " ++ show x
208
      return False
209
    Ok x -> if checkJobsStatus x
210
              then execWrapper master nl il cref jss
211
              else do
212
                hPutStrLn stderr $ "Not all jobs completed successfully: " ++
213
                          show x
214
                hPutStrLn stderr "Aborting."
215
                return False
216 216

  
217 217
-- | Executes the jobs, if possible and desired.
218 218
maybeExecJobs :: Options
......
279 279
        exitWith $ ExitFailure 1
280 280
      Just grp ->
281 281
          case lookup (Group.idx grp) ngroups of
282
            Nothing -> do
282
            Nothing ->
283 283
              -- This will only happen if there are no nodes assigned
284 284
              -- to this group
285 285
              return (Group.name grp, (Container.empty, Container.empty))
......
375 375

  
376 376
  checkNeedRebalance opts ini_cv
377 377

  
378
  (if verbose > 2
379
   then printf "Initial coefficients: overall %.8f, %s\n"
380
        ini_cv (Cluster.printStats nl)::IO ()
381
   else printf "Initial score: %.8f\n" ini_cv)
378
  if verbose > 2
379
    then printf "Initial coefficients: overall %.8f, %s\n"
380
           ini_cv (Cluster.printStats nl)::IO ()
381
    else printf "Initial score: %.8f\n" ini_cv
382 382

  
383 383
  putStrLn "Trying to minimize the CV..."
384 384
  let imlen = maximum . map (length . Instance.alias) $ Container.elems il
b/htools/Ganeti/HTools/Program/Hspace.hs
268 268
printAllocationMap verbose msg nl ixes =
269 269
  when (verbose > 1) $ do
270 270
    hPutStrLn stderr (msg ++ " map")
271
    hPutStr stderr . unlines . map ((:) ' ' .  intercalate " ") $
271
    hPutStr stderr . unlines . map ((:) ' ' .  unwords) $
272 272
            formatTable (map (printInstance nl) (reverse ixes))
273 273
                        -- This is the numberic-or-not field
274 274
                        -- specification; the first three fields are
......
315 315
            -> Node.List -> Node.List -> [(FailMode, Int)] -> IO ()
316 316
printTiered True spec_map m_cpu nl trl_nl _ = do
317 317
  printKeys $ printStats PTiered (Cluster.totalResources trl_nl)
318
  printKeys [("TSPEC", intercalate " " (formatSpecMap spec_map))]
318
  printKeys [("TSPEC", unwords (formatSpecMap spec_map))]
319 319
  printAllocationStats m_cpu nl trl_nl
320 320

  
321 321
printTiered False spec_map _ ini_nl fin_nl sreason = do
......
433 433

  
434 434
  -- Run the tiered allocation, if enabled
435 435

  
436
  (case optTieredSpec opts of
437
     Nothing -> return ()
438
     Just tspec -> do
439
       (treason, trl_nl, _, spec_map) <-
436
  case optTieredSpec opts of
437
    Nothing -> return ()
438
    Just tspec -> do
439
         (treason, trl_nl, _, spec_map) <-
440 440
           runAllocation cdata stop_allocation
441
                   (Cluster.tieredAlloc nl il alloclimit (iofspec tspec)
442
                           allocnodes [] []) tspec SpecTiered opts
441
             (Cluster.tieredAlloc nl il alloclimit (iofspec tspec)
442
                     allocnodes [] []) tspec SpecTiered opts
443 443

  
444
       printTiered machine_r spec_map (optMcpu opts) nl trl_nl treason
445
       )
444
         printTiered machine_r spec_map (optMcpu opts) nl trl_nl treason
446 445

  
447 446
  -- Run the standard (avg-mode) allocation
448 447

  
b/htools/Ganeti/HTools/QC.hs
133 133
      (_, nlst) = Loader.assignIndices namelst
134 134
  in nlst
135 135

  
136
-- | Make a small cluster, both nodes and instances.
137
makeSmallEmptyCluster :: Node.Node -> Int -> Instance.Instance
138
                      -> (Node.List, Instance.List, Instance.Instance)
139
makeSmallEmptyCluster node count inst =
140
  (makeSmallCluster node count, Container.empty,
141
   setInstanceSmallerThanNode node inst)
142

  
136 143
-- | Checks if a node is "big" enough.
137 144
isNodeBig :: Node.Node -> Int -> Bool
138 145
isNodeBig node size = Node.availDisk node > size * Types.unitDsk
......
246 253
                      , "OP_INSTANCE_FAILOVER"
247 254
                      , "OP_INSTANCE_MIGRATE"
248 255
                      ]
249
    (case op_id of
250
       "OP_TEST_DELAY" ->
251
         liftM3 OpCodes.OpTestDelay arbitrary arbitrary arbitrary
252
       "OP_INSTANCE_REPLACE_DISKS" ->
253
         liftM5 OpCodes.OpInstanceReplaceDisks arbitrary arbitrary
254
         arbitrary arbitrary arbitrary
255
       "OP_INSTANCE_FAILOVER" ->
256
         liftM3 OpCodes.OpInstanceFailover arbitrary arbitrary
257
                arbitrary
258
       "OP_INSTANCE_MIGRATE" ->
259
         liftM5 OpCodes.OpInstanceMigrate arbitrary arbitrary
260
                arbitrary arbitrary arbitrary
261
       _ -> fail "Wrong opcode")
256
    case op_id of
257
      "OP_TEST_DELAY" ->
258
        liftM3 OpCodes.OpTestDelay arbitrary arbitrary arbitrary
259
      "OP_INSTANCE_REPLACE_DISKS" ->
260
        liftM5 OpCodes.OpInstanceReplaceDisks arbitrary arbitrary
261
          arbitrary arbitrary arbitrary
262
      "OP_INSTANCE_FAILOVER" ->
263
        liftM3 OpCodes.OpInstanceFailover arbitrary arbitrary
264
          arbitrary
265
      "OP_INSTANCE_MIGRATE" ->
266
        liftM5 OpCodes.OpInstanceMigrate arbitrary arbitrary
267
          arbitrary arbitrary arbitrary
268
      _ -> fail "Wrong opcode"
262 269

  
263 270
instance Arbitrary Jobs.OpStatus where
264 271
  arbitrary = elements [minBound..maxBound]
......
283 290

  
284 291
instance Arbitrary a => Arbitrary (Types.OpResult a) where
285 292
  arbitrary = arbitrary >>= \c ->
286
              case c of
287
                False -> liftM Types.OpFail arbitrary
288
                True -> liftM Types.OpGood arbitrary
293
              if c
294
                then liftM Types.OpGood arbitrary
295
                else liftM Types.OpFail arbitrary
289 296

  
290 297
-- * Actual tests
291 298

  
......
295 302
-- not contain commas, then join+split should be idempotent.
296 303
prop_Utils_commaJoinSplit =
297 304
  forAll (arbitrary `suchThat`
298
          (\l -> l /= [""] && all (not . elem ',') l )) $ \lst ->
305
          (\l -> l /= [""] && all (notElem ',') l )) $ \lst ->
299 306
  Utils.sepSplit ',' (Utils.commaJoin lst) ==? lst
300 307

  
301 308
-- | Split and join should always be idempotent.
......
323 330
                  -> [Int]    -- ^ List of True values
324 331
                  -> Gen Prop -- ^ Test result
325 332
prop_Utils_select def lst1 lst2 =
326
  Utils.select def cndlist ==? expectedresult
333
  Utils.select def (flist ++ tlist) ==? expectedresult
327 334
  where expectedresult = Utils.if' (null lst2) def (head lst2)
328 335
        flist = map (\e -> (False, e)) lst1
329 336
        tlist = map (\e -> (True, e)) lst2
330
        cndlist = flist ++ tlist
331 337

  
332 338
-- | Test basic select functionality with undefined default
333 339
prop_Utils_select_undefd :: [Int]            -- ^ List of False values
334 340
                         -> NonEmptyList Int -- ^ List of True values
335 341
                         -> Gen Prop         -- ^ Test result
336 342
prop_Utils_select_undefd lst1 (NonEmpty lst2) =
337
  Utils.select undefined cndlist ==? head lst2
343
  Utils.select undefined (flist ++ tlist) ==? head lst2
338 344
  where flist = map (\e -> (False, e)) lst1
339 345
        tlist = map (\e -> (True, e)) lst2
340
        cndlist = flist ++ tlist
341 346

  
342 347
-- | Test basic select functionality with undefined list values
343 348
prop_Utils_select_undefv :: [Int]            -- ^ List of False values
......
422 427

  
423 428
-- ** Container tests
424 429

  
430
-- we silence the following due to hlint bug fixed in later versions
431
{-# ANN prop_Container_addTwo "HLint: ignore Avoid lambda" #-}
425 432
prop_Container_addTwo cdata i1 i2 =
426 433
  fn i1 i2 cont == fn i2 i1 cont &&
427 434
  fn i1 i2 cont == fn i1 i2 (fn i1 i2 cont)
......
444 451
  forAll (vector cnt) $ \ names ->
445 452
  (length . nub) (map fst names ++ map snd names) ==
446 453
  length names * 2 &&
447
  not (othername `elem` (map fst names ++ map snd names)) ==>
454
  othername `notElem` (map fst names ++ map snd names) ==>
448 455
  let nl = makeSmallCluster node cnt
449 456
      nodes = Container.elems nl
450 457
      nodes' = map (\((name, alias), nn) -> (Node.idx nn,
......
455 462
      target = snd (nodes' !! fidx)
456 463
  in Container.findByName nl' (Node.name target) == Just target &&
457 464
     Container.findByName nl' (Node.alias target) == Just target &&
458
     Container.findByName nl' othername == Nothing
465
     isNothing (Container.findByName nl' othername)
459 466

  
460 467
testSuite "Container"
461 468
            [ 'prop_Container_addTwo
......
765 772
           -- this is not related to rMem, but as good a place to
766 773
           -- test as any
767 774
           inst_idx `elem` Node.sList a_ab &&
768
           not (inst_idx `elem` Node.sList d_ab)
775
           inst_idx `notElem` Node.sList d_ab
769 776
       x -> printTestCase ("Failed to add/remove instances: " ++ show x) False
770 777

  
771 778
-- | Check mdsk setting.
......
858 865
        && Node.availDisk node > 0
859 866
        && Node.availMem node > 0
860 867
        ==>
861
  let nl = makeSmallCluster node count
862
      il = Container.empty
863
      inst' = setInstanceSmallerThanNode node inst
868
  let (nl, il, inst') = makeSmallEmptyCluster node count inst
864 869
  in case Cluster.genAllocNodes defGroupList nl 2 True >>=
865 870
     Cluster.tryAlloc nl il inst' of
866 871
       Types.Bad _ -> False
......
900 905
        && not (Node.failN1 node)
901 906
        && isNodeBig node 4
902 907
        ==>
903
  let nl = makeSmallCluster node count
904
      il = Container.empty
905
      inst' = setInstanceSmallerThanNode node inst
908
  let (nl, il, inst') = makeSmallEmptyCluster node count inst
906 909
  in case Cluster.genAllocNodes defGroupList nl 2 True >>=
907 910
     Cluster.tryAlloc nl il inst' of
908 911
       Types.Bad _ -> False
b/htools/Ganeti/HTools/Rapi.hs
48 48
import qualified Ganeti.HTools.Instance as Instance
49 49
import qualified Ganeti.Constants as C
50 50

  
51
{-# ANN module "HLint: ignore Eta reduce" #-}
52

  
51 53
-- | Read an URL via curl and return the body if successful.
52 54
getUrl :: (Monad m) => String -> IO (m String)
53 55

  
......
108 110
  disk <- extract "disk_usage" a
109 111
  beparams <- liftM fromJSObject (extract "beparams" a)
110 112
  omem <- extract "oper_ram" a
111
  mem <- (case omem of
112
            JSRational _ _ -> annotateResult owner_name (fromJVal omem)
113
            _ -> extract "memory" beparams)
113
  mem <- case omem of
114
           JSRational _ _ -> annotateResult owner_name (fromJVal omem)
115
           _ -> extract "memory" beparams
114 116
  vcpus <- extract "vcpus" beparams
115 117
  pnode <- extract "pnode" a >>= lookupNode ktn name
116 118
  snodes <- extract "snodes" a
117
  snode <- (if null snodes then return Node.noSecondary
118
            else readEitherString (head snodes) >>= lookupNode ktn name)
119
  snode <- if null snodes
120
             then return Node.noSecondary
121
             else readEitherString (head snodes) >>= lookupNode ktn name
119 122
  running <- extract "status" a
120 123
  tags <- extract "tags" a
121 124
  auto_balance <- extract "auto_balance" beparams
......
136 139
  let vm_cap' = fromMaybe True vm_cap
137 140
  guuid   <- annotateResult desc $ maybeFromObj a "group.uuid"
138 141
  guuid' <-  lookupGroup ktg name (fromMaybe defaultGroupID guuid)
139
  node <- (if offline || drained || not vm_cap'
140
           then return $ Node.create name 0 0 0 0 0 0 True guuid'
141
           else do
142
             mtotal  <- extract "mtotal"
143
             mnode   <- extract "mnode"
144
             mfree   <- extract "mfree"
145
             dtotal  <- extract "dtotal"
146
             dfree   <- extract "dfree"
147
             ctotal  <- extract "ctotal"
148
             return $ Node.create name mtotal mnode mfree
149
                    dtotal dfree ctotal False guuid')
142
  node <- if offline || drained || not vm_cap'
143
            then return $ Node.create name 0 0 0 0 0 0 True guuid'
144
            else do
145
              mtotal  <- extract "mtotal"
146
              mnode   <- extract "mnode"
147
              mfree   <- extract "mfree"
148
              dtotal  <- extract "dtotal"
149
              dfree   <- extract "dfree"
150
              ctotal  <- extract "ctotal"
151
              return $ Node.create name mtotal mnode mfree
152
                     dtotal dfree ctotal False guuid'
150 153
  return (name, node)
151 154

  
152 155
-- | Construct a group from a JSON object.
b/htools/Ganeti/HTools/Text.hs
154 154
loadInst ktn [ name, mem, dsk, vcpus, status, auto_bal, pnode, snode
155 155
             , dt, tags ] = do
156 156
  pidx <- lookupNode ktn name pnode
157
  sidx <- (if null snode then return Node.noSecondary
158
           else lookupNode ktn name snode)
157
  sidx <- if null snode
158
            then return Node.noSecondary
159
            else lookupNode ktn name snode
159 160
  vmem <- tryRead name mem
160 161
  vdsk <- tryRead name dsk
161 162
  vvcpus <- tryRead name vcpus
b/htools/Ganeti/Luxi.hs
60 60
withTimeout :: Int -> String -> IO a -> IO a
61 61
withTimeout secs descr action = do
62 62
  result <- timeout (secs * 1000000) action
63
  (case result of
64
     Nothing -> fail $ "Timeout in " ++ descr
65
     Just v -> return v)
63
  case result of
64
    Nothing -> fail $ "Timeout in " ++ descr
65
    Just v -> return v
66 66

  
67 67
-- * Generic protocol functionality
68 68

  
......
213 213
              nbuf <- withTimeout queryTimeout "reading luxi response" $
214 214
                      S.recv (socket s) 4096
215 215
              let (msg, remaining) = break (eOM ==) nbuf
216
              (if null remaining
217
               then _recv (obuf ++ msg)
218
               else return (obuf ++ msg, tail remaining))
216
              if null remaining
217
                then _recv (obuf ++ msg)
218
                else return (obuf ++ msg, tail remaining)
219 219
  cbuf <- readIORef $ rbuf s
220 220
  let (imsg, ibuf) = break (eOM ==) cbuf
221 221
  (msg, nbuf) <-
222
      (if null ibuf      -- if old buffer didn't contain a full message
223
       then _recv cbuf   -- then we read from network
224
       else return (imsg, tail ibuf)) -- else we return data from our buffer
222
    if null ibuf      -- if old buffer didn't contain a full message
223
      then _recv cbuf   -- then we read from network
224
      else return (imsg, tail ibuf) -- else we return data from our buffer
225 225
  writeIORef (rbuf s) nbuf
226 226
  return msg
227 227

  
......
244 244
  let arr = J.fromJSObject oarr
245 245
  status <- fromObj arr (strOfKey Success)::Result Bool
246 246
  let rkey = strOfKey Result
247
  (if status
248
   then fromObj arr rkey
249
   else fromObj arr rkey >>= fail)
247
  if status
248
    then fromObj arr rkey
249
    else fromObj arr rkey >>= fail
250 250

  
251 251
-- | Generic luxi method call.
252 252
callMethod :: LuxiOp -> Client -> IO (Result JSValue)
b/htools/test.hs
136 136
         Nothing -> return Nothing
137 137
         Just str -> do
138 138
           let vs = sepSplit ',' str
139
           (case vs of
140
              [rng, size] -> return $ Just (read rng, read size)
141
              _ -> fail "Invalid state given")
139
           case vs of
140
             [rng, size] -> return $ Just (read rng, read size)
141
             _ -> fail "Invalid state given"
142 142
  return args { chatty = optVerbose opts > 1,
143 143
                replay = r
144 144
              }
......
149 149
  let wrap = map (wrapTest errs)
150 150
  cmd_args <- getArgs
151 151
  (opts, args) <- parseOpts cmd_args "test" options
152
  tests <- (if null args
153
              then return allTests
154
              else (let args' = map lower args
155
                        selected = filter ((`elem` args') . lower .
156
                                           extractName) allTests
157
                    in if null selected
158
                         then do
159
                           hPutStrLn stderr $ "No tests matching '"
160
                              ++ intercalate " " args ++ "', available tests: "
161
                              ++ intercalate ", " (map extractName allTests)
162
                           exitWith $ ExitFailure 1
163
                         else return selected))
152
  tests <- if null args
153
             then return allTests
154
             else let args' = map lower args
155
                      selected = filter ((`elem` args') . lower .
156
                                         extractName) allTests
157
                  in if null selected
158
                       then do
159
                         hPutStrLn stderr $ "No tests matching '"
160
                            ++ unwords args ++ "', available tests: "
161
                            ++ intercalate ", " (map extractName allTests)
162
                         exitWith $ ExitFailure 1
163
                       else return selected
164 164

  
165 165
  let max_count = maximum $ map (\(_, (_, t)) -> length t) tests
166 166
  mapM_ (\(targs, (name, tl)) ->
167 167
           transformTestOpts targs opts >>= \newargs ->
168 168
           runTests name newargs (wrap tl) max_count) tests
169 169
  terr <- readIORef errs
170
  (if terr > 0
171
   then do
172
     hPutStrLn stderr $ "A total of " ++ show terr ++ " tests failed."
173
     exitWith $ ExitFailure 1
174
   else putStrLn "All tests succeeded.")
170
  if terr > 0
171
    then do
172
      hPutStrLn stderr $ "A total of " ++ show terr ++ " tests failed."
173
      exitWith $ ExitFailure 1
174
    else putStrLn "All tests succeeded."

Also available in: Unified diff