Revision 0cc3d742

b/src/Ganeti/HTools/Cluster.hs
150 150
data CStats = CStats
151 151
  { csFmem :: Integer -- ^ Cluster free mem
152 152
  , csFdsk :: Integer -- ^ Cluster free disk
153
  , csFspn :: Integer -- ^ Cluster free spindles
153 154
  , csAmem :: Integer -- ^ Cluster allocatable mem
154 155
  , csAdsk :: Integer -- ^ Cluster allocatable disk
155 156
  , csAcpu :: Integer -- ^ Cluster allocatable cpus
......
158 159
  , csMcpu :: Integer -- ^ Max node allocatable cpu
159 160
  , csImem :: Integer -- ^ Instance used mem
160 161
  , csIdsk :: Integer -- ^ Instance used disk
162
  , csIspn :: Integer -- ^ Instance used spindles
161 163
  , csIcpu :: Integer -- ^ Instance used cpu
162 164
  , csTmem :: Double  -- ^ Cluster total mem
163 165
  , csTdsk :: Double  -- ^ Cluster total disk
166
  , csTspn :: Double  -- ^ Cluster total spindles
164 167
  , csTcpu :: Double  -- ^ Cluster total cpus
165 168
  , csVcpu :: Integer -- ^ Cluster total virtual cpus
166 169
  , csNcpu :: Double  -- ^ Equivalent to 'csIcpu' but in terms of
......
222 225

  
223 226
-- | Zero-initializer for the CStats type.
224 227
emptyCStats :: CStats
225
emptyCStats = CStats 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
228
emptyCStats = CStats 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
226 229

  
227 230
-- | Update stats with data from a new node.
228 231
updateCStats :: CStats -> Node.Node -> CStats
......
233 236
               csImem = x_imem, csIdsk = x_idsk, csIcpu = x_icpu,
234 237
               csTmem = x_tmem, csTdsk = x_tdsk, csTcpu = x_tcpu,
235 238
               csVcpu = x_vcpu, csNcpu = x_ncpu,
236
               csXmem = x_xmem, csNmem = x_nmem, csNinst = x_ninst
239
               csXmem = x_xmem, csNmem = x_nmem, csNinst = x_ninst,
240
               csFspn = x_fspn, csIspn = x_ispn, csTspn = x_tspn
237 241
             }
238 242
        = cs
239 243
      inc_amem = Node.fMem node - Node.rMem node
......
243 247
                 - Node.xMem node - Node.fMem node
244 248
      inc_icpu = Node.uCpu node
245 249
      inc_idsk = truncate (Node.tDsk node) - Node.fDsk node
250
      inc_ispn = Node.tSpindles node - Node.fSpindles node
246 251
      inc_vcpu = Node.hiCpu node
247 252
      inc_acpu = Node.availCpu node
248 253
      inc_ncpu = fromIntegral (Node.uCpu node) /
249 254
                 iPolicyVcpuRatio (Node.iPolicy node)
250 255
  in cs { csFmem = x_fmem + fromIntegral (Node.fMem node)
251 256
        , csFdsk = x_fdsk + fromIntegral (Node.fDsk node)
257
        , csFspn = x_fspn + fromIntegral (Node.fSpindles node)
252 258
        , csAmem = x_amem + fromIntegral inc_amem'
253 259
        , csAdsk = x_adsk + fromIntegral inc_adsk
254 260
        , csAcpu = x_acpu + fromIntegral inc_acpu
......
257 263
        , csMcpu = max x_mcpu (fromIntegral inc_acpu)
258 264
        , csImem = x_imem + fromIntegral inc_imem
259 265
        , csIdsk = x_idsk + fromIntegral inc_idsk
266
        , csIspn = x_ispn + fromIntegral inc_ispn
260 267
        , csIcpu = x_icpu + fromIntegral inc_icpu
261 268
        , csTmem = x_tmem + Node.tMem node
262 269
        , csTdsk = x_tdsk + Node.tDsk node
270
        , csTspn = x_tspn + fromIntegral (Node.tSpindles node)
263 271
        , csTcpu = x_tcpu + Node.tCpu node
264 272
        , csVcpu = x_vcpu + fromIntegral inc_vcpu
265 273
        , csNcpu = x_ncpu + inc_ncpu
......
283 291
computeAllocationDelta :: CStats -> CStats -> AllocStats
284 292
computeAllocationDelta cini cfin =
285 293
  let CStats {csImem = i_imem, csIdsk = i_idsk, csIcpu = i_icpu,
286
              csNcpu = i_ncpu } = cini
294
              csNcpu = i_ncpu, csIspn = i_ispn } = cini
287 295
      CStats {csImem = f_imem, csIdsk = f_idsk, csIcpu = f_icpu,
288 296
              csTmem = t_mem, csTdsk = t_dsk, csVcpu = f_vcpu,
289
              csNcpu = f_ncpu, csTcpu = f_tcpu } = cfin
297
              csNcpu = f_ncpu, csTcpu = f_tcpu,
298
              csIspn = f_ispn, csTspn = t_spn } = cfin
290 299
      rini = AllocInfo { allocInfoVCpus = fromIntegral i_icpu
291 300
                       , allocInfoNCpus = i_ncpu
292 301
                       , allocInfoMem   = fromIntegral i_imem
293 302
                       , allocInfoDisk  = fromIntegral i_idsk
303
                       , allocInfoSpn   = fromIntegral i_ispn
294 304
                       }
295 305
      rfin = AllocInfo { allocInfoVCpus = fromIntegral (f_icpu - i_icpu)
296 306
                       , allocInfoNCpus = f_ncpu - i_ncpu
297 307
                       , allocInfoMem   = fromIntegral (f_imem - i_imem)
298 308
                       , allocInfoDisk  = fromIntegral (f_idsk - i_idsk)
309
                       , allocInfoSpn   = fromIntegral (f_ispn - i_ispn)
299 310
                       }
300 311
      runa = AllocInfo { allocInfoVCpus = fromIntegral (f_vcpu - f_icpu)
301 312
                       , allocInfoNCpus = f_tcpu - f_ncpu
302 313
                       , allocInfoMem   = truncate t_mem - fromIntegral f_imem
303 314
                       , allocInfoDisk  = truncate t_dsk - fromIntegral f_idsk
315
                       , allocInfoSpn   = truncate t_spn - fromIntegral f_ispn
304 316
                       }
305 317
  in (rini, rfin, runa)
306 318

  
b/src/Ganeti/HTools/Node.hs
74 74
  , computeGroups
75 75
  , mkNodeGraph
76 76
  , mkRebootNodeGraph
77
  , haveExclStorage
77 78
  ) where
78 79

  
79 80
import Control.Monad (liftM, liftM2)
......
222 223
decIf True  base delta = base - delta
223 224
decIf False base _     = base
224 225

  
226
-- | Is exclusive storage enabled on any node?
227
haveExclStorage :: List -> Bool
228
haveExclStorage nl =
229
  any exclStorage $ Container.elems nl
230

  
225 231
-- * Initialization functions
226 232

  
227 233
-- | Create a new node.
b/src/Ganeti/HTools/Program/Hspace.hs
128 128
cpuEff :: Cluster.CStats -> Double
129 129
cpuEff = effFn Cluster.csIcpu (fromIntegral . Cluster.csVcpu)
130 130

  
131
-- | Spindles efficiency.
132
spnEff :: Cluster.CStats -> Double
133
spnEff = effFn Cluster.csIspn Cluster.csTspn
134

  
131 135
-- | Holds data for converting a 'Cluster.CStats' structure into
132 136
-- detailed statistics.
133 137
statsData :: [(String, Cluster.CStats -> String)]
......
147 151
               \cs -> printf "%d" (Cluster.csFdsk cs - Cluster.csAdsk cs))
148 152
            , ("DSK_INST", printf "%d" . Cluster.csIdsk)
149 153
            , ("DSK_EFF", printf "%.8f" . dskEff)
154
            , ("SPN_FREE", printf "%d" . Cluster.csFspn)
155
            , ("SPN_INST", printf "%d" . Cluster.csIspn)
156
            , ("SPN_EFF", printf "%.8f" . spnEff)
150 157
            , ("CPU_INST", printf "%d" . Cluster.csIcpu)
151 158
            , ("CPU_EFF", printf "%.8f" . cpuEff)
152 159
            , ("MNODE_MEM_AVAIL", printf "%d" . Cluster.csMmem)
......
160 167
           , ("CPU", printf "%d" . rspecCpu)
161 168
           ]
162 169

  
170
-- | 'RSpec' formatting information including spindles.
171
specDataSpn :: [(String, RSpec -> String)]
172
specDataSpn = specData ++ [("SPN", printf "%d" . rspecSpn)]
173

  
163 174
-- | List holding 'Cluster.CStats' formatting information.
164 175
clusterData :: [(String, Cluster.CStats -> String)]
165 176
clusterData = [ ("MEM", printf "%.0f" . Cluster.csTmem)
......
168 179
              , ("VCPU", printf "%d" . Cluster.csVcpu)
169 180
              ]
170 181

  
182
-- | 'Cluster.CStats' formatting information including spindles
183
clusterDataSpn :: [(String, Cluster.CStats -> String)]
184
clusterDataSpn = clusterData ++ [("SPN", printf "%.0f" . Cluster.csTspn)]
185

  
171 186
-- | Function to print stats for a given phase.
172 187
printStats :: Phase -> Cluster.CStats -> [(String, String)]
173 188
printStats ph cs =
......
182 197
printFRScores ini_nl fin_nl sreason = do
183 198
  printf "  - most likely failure reason: %s\n" $ failureReason sreason::IO ()
184 199
  printClusterScores ini_nl fin_nl
185
  printClusterEff (Cluster.totalResources fin_nl)
200
  printClusterEff (Cluster.totalResources fin_nl) (Node.haveExclStorage fin_nl)
186 201

  
187 202
-- | Print final stats and related metrics.
188 203
printResults :: Bool -> Node.List -> Node.List -> Int -> Int
......
233 248
-- | Formats a spec map to strings.
234 249
formatSpecMap :: [(RSpec, Int)] -> [String]
235 250
formatSpecMap =
236
  map (\(spec, cnt) -> printf "%d,%d,%d=%d" (rspecMem spec)
237
                       (rspecDsk spec) (rspecCpu spec) cnt)
251
  map (\(spec, cnt) -> printf "%d,%d,%d,%d=%d" (rspecMem spec)
252
                       (rspecDsk spec) (rspecCpu spec) (rspecSpn spec) cnt)
238 253

  
239 254
-- | Formats \"key-metrics\" values.
240 255
formatRSpec :: String -> AllocInfo -> [(String, String)]
......
243 258
  , ("KM_" ++ s ++ "_NPU", show $ allocInfoNCpus r)
244 259
  , ("KM_" ++ s ++ "_MEM", show $ allocInfoMem r)
245 260
  , ("KM_" ++ s ++ "_DSK", show $ allocInfoDisk r)
261
  , ("KM_" ++ s ++ "_SPN", show $ allocInfoSpn r)
246 262
  ]
247 263

  
248 264
-- | Shows allocations stats.
......
269 285
                     , show (Instance.mem i)
270 286
                     , show (Instance.dsk i)
271 287
                     , show (Instance.vcpus i)
288
                     , if Node.haveExclStorage nl
289
                       then case Instance.getTotalSpindles i of
290
                              Nothing -> "?"
291
                              Just sp -> show sp
292
                       else ""
272 293
                     ]
273 294

  
274 295
-- | Optionally print the allocation map.
......
282 303
                        -- This is the numberic-or-not field
283 304
                        -- specification; the first three fields are
284 305
                        -- strings, whereas the rest are numeric
285
                       [False, False, False, True, True, True]
306
                       [False, False, False, True, True, True, True]
286 307

  
287 308
-- | Formats nicely a list of resources.
288 309
formatResources :: a -> [(String, a->String)] -> String
......
290 311
    intercalate ", " . map (\(a, fn) -> a ++ " " ++ fn res)
291 312

  
292 313
-- | Print the cluster resources.
293
printCluster :: Bool -> Cluster.CStats -> Int -> IO ()
294
printCluster True ini_stats node_count = do
295
  printKeysHTS $ map (\(a, fn) -> ("CLUSTER_" ++ a, fn ini_stats)) clusterData
314
printCluster :: Bool -> Cluster.CStats -> Int -> Bool -> IO ()
315
printCluster True ini_stats node_count _ = do
316
  printKeysHTS $ map (\(a, fn) -> ("CLUSTER_" ++ a, fn ini_stats))
317
    clusterDataSpn
296 318
  printKeysHTS [("CLUSTER_NODES", printf "%d" node_count)]
297 319
  printKeysHTS $ printStats PInitial ini_stats
298 320

  
299
printCluster False ini_stats node_count = do
321
printCluster False ini_stats node_count print_spn = do
322
  let cldata = if print_spn then clusterDataSpn else clusterData
300 323
  printf "The cluster has %d nodes and the following resources:\n  %s.\n"
301
         node_count (formatResources ini_stats clusterData)::IO ()
324
         node_count (formatResources ini_stats cldata)::IO ()
302 325
  printf "There are %s initial instances on the cluster.\n"
303 326
             (if inst_count > 0 then show inst_count else "no" )
304 327
      where inst_count = Cluster.csNinst ini_stats
305 328

  
306 329
-- | Prints the normal instance spec.
307
printISpec :: Bool -> RSpec -> SpecType -> DiskTemplate -> IO ()
308
printISpec True ispec spec disk_template = do
309
  printKeysHTS $ map (\(a, fn) -> (prefix ++ "_" ++ a, fn ispec)) specData
330
printISpec :: Bool -> RSpec -> SpecType -> DiskTemplate -> Bool -> IO ()
331
printISpec True ispec spec disk_template _ = do
332
  printKeysHTS $ map (\(a, fn) -> (prefix ++ "_" ++ a, fn ispec)) specDataSpn
310 333
  printKeysHTS [ (prefix ++ "_RQN", printf "%d" req_nodes) ]
311 334
  printKeysHTS [ (prefix ++ "_DISK_TEMPLATE",
312 335
                  diskTemplateToRaw disk_template) ]
313 336
      where req_nodes = Instance.requiredNodes disk_template
314 337
            prefix = specPrefix spec
315 338

  
316
printISpec False ispec spec disk_template =
317
  printf "%s instance spec is:\n  %s, using disk\
318
         \ template '%s'.\n"
319
         (specDescription spec)
320
         (formatResources ispec specData) (diskTemplateToRaw disk_template)
339
printISpec False ispec spec disk_template print_spn =
340
  let spdata = if print_spn then specDataSpn else specData
341
  in printf "%s instance spec is:\n  %s, using disk\
342
            \ template '%s'.\n"
343
            (specDescription spec)
344
            (formatResources ispec spdata) (diskTemplateToRaw disk_template)
321 345

  
322 346
-- | Prints the tiered results.
323 347
printTiered :: Bool -> [(RSpec, Int)]
......
329 353

  
330 354
printTiered False spec_map ini_nl fin_nl sreason = do
331 355
  _ <- printf "Tiered allocation results:\n"
356
  let spdata = if Node.haveExclStorage ini_nl then specDataSpn else specData
332 357
  if null spec_map
333 358
    then putStrLn "  - no instances allocated"
334 359
    else mapM_ (\(ispec, cnt) ->
335 360
                  printf "  - %3d instances of spec %s\n" cnt
336
                           (formatResources ispec specData)) spec_map
361
                           (formatResources ispec spdata)) spec_map
337 362
  printFRScores ini_nl fin_nl sreason
338 363

  
339 364
-- | Displays the initial/final cluster scores.
......
343 368
  printf "  -   final cluster score: %.8f\n" $ Cluster.compCV fin_nl
344 369

  
345 370
-- | Displays the cluster efficiency.
346
printClusterEff :: Cluster.CStats -> IO ()
347
printClusterEff cs =
371
printClusterEff :: Cluster.CStats -> Bool -> IO ()
372
printClusterEff cs print_spn = do
373
  let format = [("memory", memEff),
374
                ("disk", dskEff),
375
                ("vcpu", cpuEff)] ++
376
               [("spindles", spnEff) | print_spn]
377
      len = maximum $ map (length . fst) format
348 378
  mapM_ (\(s, fn) ->
349
           printf "  - %s usage efficiency: %5.2f%%\n" s (fn cs * 100))
350
          [("memory", memEff),
351
           ("  disk", dskEff),
352
           ("  vcpu", cpuEff)]
379
          printf "  - %*s usage efficiency: %5.2f%%\n" len s (fn cs * 100))
380
    format
353 381

  
354 382
-- | Computes the most likely failure reason.
355 383
failureReason :: [(FailMode, Int)] -> String
......
377 405
  let name = specName mode
378 406
      descr = name ++ " allocation"
379 407
      ldescr = "after " ++ map toLower descr
408
      excstor = Node.haveExclStorage new_nl
380 409

  
381
  printISpec (optMachineReadable opts) spec mode dt
410
  printISpec (optMachineReadable opts) spec mode dt excstor
382 411

  
383 412
  printAllocationMap (optVerbose opts) descr new_nl new_ixes
384 413

  
......
446 475
                 (Cluster.compCV nl) (Cluster.printStats "  " nl)
447 476

  
448 477
  printCluster machine_r (Cluster.totalResources nl) (length all_nodes)
478
    (Node.haveExclStorage nl)
449 479

  
450 480
  let stop_allocation = case Cluster.computeBadItems nl il of
451 481
                          ([], _) -> Nothing
b/src/Ganeti/HTools/Types.hs
155 155
  , allocInfoNCpus :: Double -- ^ Normalised CPUs
156 156
  , allocInfoMem   :: Int    -- ^ Memory
157 157
  , allocInfoDisk  :: Int    -- ^ Disk
158
  , allocInfoSpn   :: Int    -- ^ Spindles
158 159
  } deriving (Show, Eq)
159 160

  
160 161
-- | Currently used, possibly to allocate, unallocable.

Also available in: Unified diff