Revision 1cdcf8f3

b/htools/Ganeti/HTools/QC.hs
537 537
          cndlist = flist ++ tlist ++ [undefined]
538 538

  
539 539
prop_Utils_parseUnit (NonNegative n) =
540
  Utils.parseUnit (show n) == Types.Ok n &&
541
  Utils.parseUnit (show n ++ "m") == Types.Ok n &&
542
  (case Utils.parseUnit (show n ++ "M") of
543
     Types.Ok m -> if n > 0
544
                     then m < n  -- for positive values, X MB is < than X MiB
545
                     else m == 0 -- but for 0, 0 MB == 0 MiB
546
     Types.Bad _ -> False) &&
547
  Utils.parseUnit (show n ++ "g") == Types.Ok (n*1024) &&
548
  Utils.parseUnit (show n ++ "t") == Types.Ok (n*1048576) &&
549
  Types.isBad (Utils.parseUnit (show n ++ "x")::Types.Result Int)
550
    where _types = n::Int
540
  Utils.parseUnit (show n) ==? Types.Ok n .&&.
541
  Utils.parseUnit (show n ++ "m") ==? Types.Ok n .&&.
542
  Utils.parseUnit (show n ++ "M") ==? Types.Ok (truncate n_mb::Int) .&&.
543
  Utils.parseUnit (show n ++ "g") ==? Types.Ok (n*1024) .&&.
544
  Utils.parseUnit (show n ++ "G") ==? Types.Ok (truncate n_gb::Int) .&&.
545
  Utils.parseUnit (show n ++ "t") ==? Types.Ok (n*1048576) .&&.
546
  Utils.parseUnit (show n ++ "T") ==? Types.Ok (truncate n_tb::Int) .&&.
547
  printTestCase "Internal error/overflow?"
548
    (n_mb >=0 && n_gb >= 0 && n_tb >= 0) .&&.
549
  property (Types.isBad (Utils.parseUnit (show n ++ "x")::Types.Result Int))
550
  where _types = (n::Int)
551
        n_mb = (fromIntegral n::Rational) * 1000 * 1000 / 1024 / 1024
552
        n_gb = n_mb * 1000
553
        n_tb = n_gb * 1000
551 554

  
552 555
-- | Test list for the Utils module.
553 556
testSuite "Utils"
b/htools/Ganeti/HTools/Utils.hs
40 40

  
41 41
import Data.Char (toUpper)
42 42
import Data.List
43
import Data.Ratio ((%))
43 44

  
44 45
import Debug.Trace
45 46

  
......
163 164
  unlines . map ((++) lp) . map ((:) ' ' . unwords) $
164 165
  formatTable (header:rows) isnum
165 166

  
167
-- | Converts a unit (e.g. m or GB) into a scaling factor.
168
parseUnitValue :: (Monad m) => String -> m Rational
169
parseUnitValue unit
170
  -- binary conversions first
171
  | null unit                     = return 1
172
  | unit == "m" || upper == "MIB" = return 1
173
  | unit == "g" || upper == "GIB" = return kbBinary
174
  | unit == "t" || upper == "TIB" = return $ kbBinary * kbBinary
175
  -- SI conversions
176
  | unit == "M" || upper == "MB"  = return mbFactor
177
  | unit == "G" || upper == "GB"  = return $ mbFactor * kbDecimal
178
  | unit == "T" || upper == "TB"  = return $ mbFactor * kbDecimal * kbDecimal
179
  | otherwise = fail $ "Unknown unit '" ++ unit ++ "'"
180
  where upper = map toUpper unit
181
        kbBinary = 1024
182
        kbDecimal = 1000
183
        decToBin = kbDecimal / kbBinary -- factor for 1K conversion
184
        mbFactor = decToBin * decToBin -- twice the factor for just 1K
185

  
166 186
-- | Tries to extract number and scale from the given string.
167 187
--
168 188
-- Input must be in the format NUMBER+ SPACE* [UNIT]. If no unit is
......
172 192
parseUnit str =
173 193
  -- TODO: enhance this by splitting the unit parsing code out and
174 194
  -- accepting floating-point numbers
175
  case reads str of
195
  case (reads str::[(Int, String)]) of
176 196
    [(v, suffix)] ->
177 197
      let unit = dropWhile (== ' ') suffix
178
          upper = map toUpper unit
179
          siConvert x = x * 1000000 `div` 1048576
180
      in case () of
181
           _ | null unit -> return v
182
             | unit == "m" || upper == "MIB" -> return v
183
             | unit == "M" || upper == "MB"  -> return $ siConvert v
184
             | unit == "g" || upper == "GIB" -> return $ v * 1024
185
             | unit == "G" || upper == "GB"  -> return $ siConvert
186
                                                (v * 1000)
187
             | unit == "t" || upper == "TIB" -> return $ v * 1048576
188
             | unit == "T" || upper == "TB"  -> return $
189
                                                siConvert (v * 1000000)
190
             | otherwise -> fail $ "Unknown unit '" ++ unit ++ "'"
198
      in do
199
        scaling <- parseUnitValue unit
200
        return $ truncate (fromIntegral v * scaling)
191 201
    _ -> fail $ "Can't parse string '" ++ str ++ "'"

Also available in: Unified diff