Format data size units, use it in quota commands
authorStavros Sachtouris <saxtouri@admin.grnet.gr>
Tue, 22 Jan 2013 12:32:21 +0000 (14:32 +0200)
committerStavros Sachtouris <saxtouri@admin.grnet.gr>
Tue, 22 Jan 2013 12:32:21 +0000 (14:32 +0200)
quota returns value in units (KiB, MiB, etc)
new argument -b, --bytes to return value in bytes
setquota can accept a unit string
    setquota 2.3GB

kamaki/cli/commands/pithos_cli.py
kamaki/cli/utils.py

index 7f8c575..13f39f8 100644 (file)
@@ -36,6 +36,7 @@ from kamaki.cli.command_tree import CommandTree
 from kamaki.cli.errors import raiseCLIError, CLISyntaxError
 from kamaki.cli.utils import (
     format_size,
+    to_bytes,
     print_dict,
     pretty_keys,
     page_hold,
@@ -88,7 +89,7 @@ def raise_connection_errors(e):
             '- total quota:      /store quota',
             '- container quota:  /store quota <container>',
             'Users shall set a higher container quota, if available:',
-            '-                  /store setquota <limit in KB> <container>'
+            '-                  /store setquota <quota>[unit] <container>'
             ])
 
 
@@ -1780,7 +1781,11 @@ class store_delmeta(_store_container_command):
 
 @command(pithos_cmds)
 class store_quota(_store_account_command):
-    """Get quota (in KB) for account or container"""
+    """Get quota for account or container"""
+
+    arguments = dict(
+        in_bytes=FlagArgument('Show result in bytes', ('-b', '--bytes'))
+        )
 
     def main(self, container=None):
         super(self.__class__, self).main()
@@ -1799,15 +1804,47 @@ class store_quota(_store_account_command):
             raiseCLIError(err)
         except Exception as err:
             raiseCLIError(err)
+        if not self['in_bytes']:
+            for k in reply:
+                reply[k] = format_size(reply[k])
         print_dict(pretty_keys(reply, '-'))
 
 
 @command(pithos_cmds)
 class store_setquota(_store_account_command):
-    """Set new quota (in KB) for account or container"""
+    """Set new quota for account or container
+    By default, quota is set in bytes
+    Users may specify a different unit, e.g:
+    /store setquota 2.3GB mycontainer
+    Accepted units: B, KiB (1024 B), KB (1000 B), MiB, MB, GiB, GB, TiB, TB
+    """
+
+    def _calculate_quota(self, user_input):
+        quota = 0
+        try:
+            quota = int(user_input)
+        except ValueError:
+            index = 0
+            digits = [str(num) for num in range(0, 10)] + ['.']
+            while user_input[index] in digits:
+                index += 1
+            quota = user_input[:index]
+            format = user_input[index:]
+            try:
+                return to_bytes(quota, format)
+            except Exception as qe:
+                raiseCLIError(qe,
+                    'Failed to convert %s to bytes' % user_input,
+                    details=['Syntax: setquota <quota>[format] [container]',
+                        'e.g.: setquota 2.3GB mycontainer',
+                        'Acceptable formats:',
+                        '(*1024): B, KiB, MiB, GiB, TiB',
+                        '(*1000): B, KB, MB, GB, TB'])
+        return quota
 
     def main(self, quota, container=None):
         super(self.__class__, self).main()
+        quota = self._calculate_quota(quota)
         try:
             if container is None:
                 self.client.set_account_quota(quota)
index 9f2dc50..e3551f5 100644 (file)
@@ -257,7 +257,7 @@ def print_items(items,
 
 
 def format_size(size):
-    units = ('B', 'K', 'M', 'G', 'T')
+    units = ('B', 'KiB', 'MiB', 'GiB', 'TiB')
     try:
         size = float(size)
     except ValueError as err:
@@ -265,13 +265,37 @@ def format_size(size):
     for unit in units:
         if size < 1024:
             break
-        size /= 1024
-    s = ('%.1f' % size)
-    if '.0' == s[-2:]:
-        s = s[:-2]
+        size /= 1024.0
+    s = ('%.2f' % size)
+    while '.' in s and s[-1] in ('0', '.'):
+        s = s[:-1]
     return s + unit
 
 
+def to_bytes(size, format):
+    """
+    :param size: (float) the size in the given format
+    :param format: (case insensitive) KiB, KB, MiB, MB, GiB, GB, TiB, TB
+
+    :returns: (int) the size in bytes
+    """
+    format = format.upper()
+    if format == 'B':
+        return int(size)
+    size = float(size)
+    units_dc = ('KB', 'MB', 'GB', 'TB')
+    units_bi = ('KIB', 'MIB', 'GIB', 'TIB')
+
+    factor = 1024 if format in units_bi else 1000 if format in units_dc else 0
+    if not factor:
+        raise ValueError('Invalid data size format %s' % format)
+    for prefix in ('K', 'M', 'G', 'T'):
+        size *= factor
+        if format.startswith(prefix):
+            break
+    return int(size)
+
+
 def dict2file(d, f, depth=0):
     for k, v in d.items():
         f.write('%s%s: ' % ('\t' * depth, k))
@@ -325,14 +349,14 @@ def split_input(line):
     return terms
 
 
-def ask_user(msg, true_responses=('Y', 'y')):
+def ask_user(msg, true_resp=['Y', 'y']):
     """Print msg and read user response
 
-    :param true_responses: (tuple of chars)
+    :param true_resp: (tuple of chars)
 
     :returns: (bool) True if reponse in true responses, False otherwise
     """
-    stdout.write('%s (%s for yes):' % (msg, true_responses))
+    stdout.write('%s (%s or enter for yes):' % (msg, ', '.join(true_resp)))
     stdout.flush()
-    user_response = stdin.read(1)
-    return user_response[0] in true_responses
+    user_response = stdin.readline()
+    return user_response[0] in true_resp + ['\n']