Revision 9fbfbb7b

b/lib/cli.py
171 171
                      help="Separator between output fields"
172 172
                      " (defaults to one space)")
173 173

  
174
USEUNITS_OPT = make_option("--human-readable", default=False,
175
                           action="store_true", dest="human_readable",
176
                           help="Print sizes in human readable format")
174
USEUNITS_OPT = make_option("--units", default=None,
175
                           dest="units", choices=('h', 'm', 'g', 't'),
176
                           help="Specify units for output (one of hmgt)")
177 177

  
178 178
FIELDS_OPT = make_option("-o", "--output", dest="output", action="store",
179 179
                         type="string", help="Comma separated list of"
......
747 747

  
748 748

  
749 749
def GenerateTable(headers, fields, separator, data,
750
                  numfields=None, unitfields=None):
750
                  numfields=None, unitfields=None,
751
                  units=None):
751 752
  """Prints a table with headers and different fields.
752 753

  
753
  Args:
754
    headers: Dict of header titles or None if no headers should be shown
755
    fields: List of fields to show
756
    separator: String used to separate fields or None for spaces
757
    data: Data to be printed
758
    numfields: List of fields to be aligned to right
759
    unitfields: List of fields to be formatted as units
754
  @type headers: dict
755
  @param headers: dictionary mapping field names to headers for
756
      the table
757
  @type fields: list
758
  @param fields: the field names corresponding to each row in
759
      the data field
760
  @param separator: the separator to be used; if this is None,
761
      the default 'smart' algorithm is used which computes optimal
762
      field width, otherwise just the separator is used between
763
      each field
764
  @type data: list
765
  @param data: a list of lists, each sublist being one row to be output
766
  @type numfields: list
767
  @param numfields: a list with the fields that hold numeric
768
      values and thus should be right-aligned
769
  @type unitfields: list
770
  @param unitfields: a list with the fields that hold numeric
771
      values that should be formatted with the units field
772
  @type units: string or None
773
  @param units: the units we should use for formatting, or None for
774
      automatic choice (human-readable for non-separator usage, otherwise
775
      megabytes); this is a one-letter string
760 776

  
761 777
  """
778
  if units is None:
779
    if separator:
780
      units = "m"
781
    else:
782
      units = "h"
783

  
762 784
  if numfields is None:
763 785
    numfields = []
764 786
  if unitfields is None:
......
795 817
        except ValueError:
796 818
          pass
797 819
        else:
798
          val = row[idx] = utils.FormatUnit(val)
820
          val = row[idx] = utils.FormatUnit(val, units)
799 821
      val = row[idx] = str(val)
800 822
      if separator is None:
801 823
        mlens[idx] = max(mlens[idx], len(val))
b/lib/utils.py
638 638
  return template % args
639 639

  
640 640

  
641
def FormatUnit(value):
641
def FormatUnit(value, units):
642 642
  """Formats an incoming number of MiB with the appropriate unit.
643 643

  
644 644
  @type value: int
645 645
  @param value: integer representing the value in MiB (1048576)
646
  @type units: char
647
  @param units: the type of formatting we should do:
648
      - 'h' for automatic scaling
649
      - 'm' for MiBs
650
      - 'g' for GiBs
651
      - 't' for TiBs
646 652
  @rtype: str
647 653
  @return: the formatted value (with suffix)
648 654

  
649 655
  """
650
  if value < 1024:
651
    return "%dM" % round(value, 0)
656
  if units not in ('m', 'g', 't', 'h'):
657
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
652 658

  
653
  elif value < (1024 * 1024):
654
    return "%0.1fG" % round(float(value) / 1024, 1)
659
  suffix = ''
660

  
661
  if units == 'm' or (units == 'h' and value < 1024):
662
    if units == 'h':
663
      suffix = 'M'
664
    return "%d%s" % (round(value, 0), suffix)
665

  
666
  elif units == 'g' or (units == 'h' and value < (1024 * 1024)):
667
    if units == 'h':
668
      suffix = 'G'
669
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
655 670

  
656 671
  else:
657
    return "%0.1fT" % round(float(value) / 1024 / 1024, 1)
672
    if units == 'h':
673
      suffix = 'T'
674
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
658 675

  
659 676

  
660 677
def ParseUnit(input_string):
b/scripts/gnt-instance
228 228
  else:
229 229
    headers = None
230 230

  
231
  if opts.human_readable:
232
    unitfields = ["be/memory", "oper_ram", "sd(a|b)_size", "disk\.size/.*"]
233
  else:
234
    unitfields = None
235

  
231
  unitfields = ["be/memory", "oper_ram", "sd(a|b)_size", "disk\.size/.*"]
236 232
  numfields = ["be/memory", "oper_ram", "sd(a|b)_size", "be/vcpus",
237 233
               "serial_no", "(disk|nic)\.count", "disk\.size/.*"]
238 234

  
......
270 266

  
271 267
  data = GenerateTable(separator=opts.separator, headers=headers,
272 268
                       fields=selected_fields, unitfields=unitfields,
273
                       numfields=numfields, data=output)
269
                       numfields=numfields, data=output, units=opts.units)
274 270

  
275 271
  for line in data:
276 272
    ToStdout(line)
b/scripts/gnt-job
106 106

  
107 107
  data = GenerateTable(separator=opts.separator, headers=headers,
108 108
                       fields=selected_fields, unitfields=unitfields,
109
                       numfields=numfields, data=output)
109
                       numfields=numfields, data=output, units=opts.units)
110 110
  for line in data:
111 111
    ToStdout(line)
112 112

  
b/scripts/gnt-node
115 115
  else:
116 116
    headers = None
117 117

  
118
  if opts.human_readable:
119
    unitfields = ["dtotal", "dfree", "mtotal", "mnode", "mfree"]
120
  else:
121
    unitfields = None
118
  unitfields = ["dtotal", "dfree", "mtotal", "mnode", "mfree"]
122 119

  
123 120
  numfields = ["dtotal", "dfree",
124 121
               "mtotal", "mnode", "mfree",
......
138 135

  
139 136
  data = GenerateTable(separator=opts.separator, headers=headers,
140 137
                       fields=selected_fields, unitfields=unitfields,
141
                       numfields=numfields, data=output)
138
                       numfields=numfields, data=output, units=opts.units)
142 139
  for line in data:
143 140
    ToStdout(line)
144 141

  
......
343 340
  else:
344 341
    headers = None
345 342

  
346
  if opts.human_readable:
347
    unitfields = ["size"]
348
  else:
349
    unitfields = None
343
  unitfields = ["size"]
350 344

  
351 345
  numfields = ["size"]
352 346

  
353 347
  data = GenerateTable(separator=opts.separator, headers=headers,
354 348
                       fields=selected_fields, unitfields=unitfields,
355
                       numfields=numfields, data=output)
349
                       numfields=numfields, data=output, units=opts.units)
356 350

  
357 351
  for line in data:
358 352
    ToStdout(line)
b/scripts/gnt-os
55 55
    headers = None
56 56

  
57 57
  data = GenerateTable(separator=None, headers=headers, fields=["name"],
58
                       data=[[row[0]] for row in result if row[1]])
58
                       data=[[row[0]] for row in result if row[1]],
59
                       units=None)
59 60

  
60 61
  for line in data:
61 62
    ToStdout(line)
b/test/ganeti.utils_unittest.py
324 324
  """Test case for the FormatUnit function"""
325 325

  
326 326
  def testMiB(self):
327
    self.assertEqual(FormatUnit(1), '1M')
328
    self.assertEqual(FormatUnit(100), '100M')
329
    self.assertEqual(FormatUnit(1023), '1023M')
327
    self.assertEqual(FormatUnit(1, 'h'), '1M')
328
    self.assertEqual(FormatUnit(100, 'h'), '100M')
329
    self.assertEqual(FormatUnit(1023, 'h'), '1023M')
330

  
331
    self.assertEqual(FormatUnit(1, 'm'), '1')
332
    self.assertEqual(FormatUnit(100, 'm'), '100')
333
    self.assertEqual(FormatUnit(1023, 'm'), '1023')
334

  
335
    self.assertEqual(FormatUnit(1024, 'm'), '1024')
336
    self.assertEqual(FormatUnit(1536, 'm'), '1536')
337
    self.assertEqual(FormatUnit(17133, 'm'), '17133')
338
    self.assertEqual(FormatUnit(1024 * 1024 - 1, 'm'), '1048575')
330 339

  
331 340
  def testGiB(self):
332
    self.assertEqual(FormatUnit(1024), '1.0G')
333
    self.assertEqual(FormatUnit(1536), '1.5G')
334
    self.assertEqual(FormatUnit(17133), '16.7G')
335
    self.assertEqual(FormatUnit(1024 * 1024 - 1), '1024.0G')
341
    self.assertEqual(FormatUnit(1024, 'h'), '1.0G')
342
    self.assertEqual(FormatUnit(1536, 'h'), '1.5G')
343
    self.assertEqual(FormatUnit(17133, 'h'), '16.7G')
344
    self.assertEqual(FormatUnit(1024 * 1024 - 1, 'h'), '1024.0G')
345

  
346
    self.assertEqual(FormatUnit(1024, 'g'), '1.0')
347
    self.assertEqual(FormatUnit(1536, 'g'), '1.5')
348
    self.assertEqual(FormatUnit(17133, 'g'), '16.7')
349
    self.assertEqual(FormatUnit(1024 * 1024 - 1, 'g'), '1024.0')
350

  
351
    self.assertEqual(FormatUnit(1024 * 1024, 'g'), '1024.0')
352
    self.assertEqual(FormatUnit(5120 * 1024, 'g'), '5120.0')
353
    self.assertEqual(FormatUnit(29829 * 1024, 'g'), '29829.0')
336 354

  
337 355
  def testTiB(self):
338
    self.assertEqual(FormatUnit(1024 * 1024), '1.0T')
339
    self.assertEqual(FormatUnit(5120 * 1024), '5.0T')
340
    self.assertEqual(FormatUnit(29829 * 1024), '29.1T')
356
    self.assertEqual(FormatUnit(1024 * 1024, 'h'), '1.0T')
357
    self.assertEqual(FormatUnit(5120 * 1024, 'h'), '5.0T')
358
    self.assertEqual(FormatUnit(29829 * 1024, 'h'), '29.1T')
341 359

  
360
    self.assertEqual(FormatUnit(1024 * 1024, 't'), '1.0')
361
    self.assertEqual(FormatUnit(5120 * 1024, 't'), '5.0')
362
    self.assertEqual(FormatUnit(29829 * 1024, 't'), '29.1')
342 363

  
343 364
class TestParseUnit(unittest.TestCase):
344 365
  """Test case for the ParseUnit function"""

Also available in: Unified diff