diff mbox

[nfs-utils,v4,05/14] mountstats: Convert existing option parsing to use the argparse module

Message ID 1418411697-65535-6-git-send-email-smayhew@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Scott Mayhew Dec. 12, 2014, 7:14 p.m. UTC
Made mountstats, nfsstat, and iostat all subcommands (note that the
nfsstat function is still unimplemented here).  If no sub-command is
given, then the mountstats sub-command will run by default (so any
scripts that run older versions of the mountstats command will still
work).

Also removed the --start and --end options since all they do is throw
exceptions.

Signed-off-by: Scott Mayhew <smayhew@redhat.com>
---
 tools/mountstats/mountstats.py | 254 ++++++++++++++++++-----------------------
 1 file changed, 110 insertions(+), 144 deletions(-)
diff mbox

Patch

diff --git a/tools/mountstats/mountstats.py b/tools/mountstats/mountstats.py
index 912d31a..b6f573d 100644
--- a/tools/mountstats/mountstats.py
+++ b/tools/mountstats/mountstats.py
@@ -25,6 +25,12 @@  MA 02110-1301 USA
 
 import sys, os, time
 from operator import itemgetter
+try:
+    import argparse
+except ImportError:
+    print('%s:  Failed to import argparse - make sure argparse is installed!'
+        % sys.argv[0])
+    sys.exit(1)
 
 Mountstats_version = '0.2'
 
@@ -533,66 +539,12 @@  def parse_stats_file(filename):
 
     return ms_dict
 
-def print_mountstats_help(name):
-    print('usage: %s [ options ] <mount point>' % name)
-    print()
-    print(' Version %s' % Mountstats_version)
-    print()
-    print(' Display NFS client per-mount statistics.')
-    print()
-    print('  --version    display the version of this command')
-    print('  --nfs        display only the NFS statistics')
-    print('  --rpc        display only the RPC statistics')
-    print('  --start      sample and save statistics')
-    print('  --end        resample statistics and compare them with saved')
-    print()
-
-def mountstats_command():
+def mountstats_command(args):
     """Mountstats command
     """
-    mountpoints = []
-    nfs_only = False
-    rpc_only = False
-
-    for arg in sys.argv:
-        if arg in ['-h', '--help', 'help', 'usage']:
-            print_mountstats_help(prog)
-            return
-
-        if arg in ['-v', '--version', 'version']:
-            print('%s version %s' % (sys.argv[0], Mountstats_version))
-            sys.exit(0)
-
-        if arg in ['-n', '--nfs']:
-            nfs_only = True
-            continue
-
-        if arg in ['-r', '--rpc']:
-            rpc_only = True
-            continue
-
-        if arg in ['-s', '--start']:
-            raise Exception('Sampling is not yet implemented')
-
-        if arg in ['-e', '--end']:
-            raise Exception('Sampling is not yet implemented')
-
-        if arg == sys.argv[0]:
-            continue
-
-        mountpoints += [arg]
-
-    if mountpoints == []:
-        print_mountstats_help(prog)
-        return
-
-    if rpc_only == True and nfs_only == True:
-        print_mountstats_help(prog)
-        return
-
     mountstats = parse_stats_file('/proc/self/mountstats')
 
-    for mp in mountpoints:
+    for mp in args.mountpoints:
         if mp not in mountstats:
             print('Statistics for mount point %s not found' % mp)
             continue
@@ -604,11 +556,11 @@  def mountstats_command():
             print('Mount point %s exists but is not an NFS mount' % mp)
             continue
 
-        if nfs_only:
+        if args.nfs_only:
            stats.display_nfs_options()
            stats.display_nfs_events()
            stats.display_nfs_bytes()
-        elif rpc_only:
+        elif args.rpc_only:
            stats.display_rpc_generic_stats()
            stats.display_rpc_op_stats()
         else:
@@ -617,38 +569,8 @@  def mountstats_command():
            stats.display_rpc_generic_stats()
            stats.display_rpc_op_stats()
 
-def print_nfsstat_help(name):
-    print('usage: %s [ options ]' % name)
-    print()
-    print(' Version %s' % Mountstats_version)
-    print()
-    print(' nfsstat-like program that uses NFS client per-mount statistics.')
-    print()
-
-def nfsstat_command():
-    print_nfsstat_help(prog)
-
-def print_iostat_help(name):
-    print('usage: %s [ <interval> [ <count> ] ] [ <mount point> ] ' % name)
-    print()
-    print(' Version %s' % Mountstats_version)
-    print()
-    print(' iostat-like program to display NFS client per-mount statistics.')
-    print()
-    print(' The <interval> parameter specifies the amount of time in seconds between')
-    print(' each report.  The first report contains statistics for the time since each')
-    print(' file system was mounted.  Each subsequent report contains statistics')
-    print(' collected during the interval since the previous report.')
-    print()
-    print(' If the <count> parameter is specified, the value of <count> determines the')
-    print(' number of reports generated at <interval> seconds apart.  If the interval')
-    print(' parameter is specified without the <count> parameter, the command generates')
-    print(' reports continuously.')
-    print()
-    print(' If one or more <mount point> names are specified, statistics for only these')
-    print(' mount points will be displayed.  Otherwise, all NFS mount points on the')
-    print(' client are listed.')
-    print()
+def nfsstat_command(args):
+    return
 
 def print_iostat_summary(old, new, devices, time):
     for device in devices:
@@ -662,42 +584,11 @@  def print_iostat_summary(old, new, devices, time):
             diff_stats = stats.compare_iostats(old_stats)
             diff_stats.display_iostats(time)
 
-def iostat_command():
+def iostat_command(args):
     """iostat-like command for NFS mount points
     """
     mountstats = parse_stats_file('/proc/self/mountstats')
-    devices = []
-    interval_seen = False
-    count_seen = False
-
-    for arg in sys.argv:
-        if arg in ['-h', '--help', 'help', 'usage']:
-            print_iostat_help(prog)
-            return
-
-        if arg in ['-v', '--version', 'version']:
-            print('%s version %s' % (sys.argv[0], Mountstats_version))
-            return
-
-        if arg == sys.argv[0]:
-            continue
-
-        if arg in mountstats:
-            devices += [arg]
-        elif not interval_seen:
-            interval = int(arg)
-            if interval > 0:
-                interval_seen = True
-            else:
-                print('Illegal <interval> value')
-                return
-        elif not count_seen:
-            count = int(arg)
-            if count > 0:
-                count_seen = True
-            else:
-                print('Illegal <count> value')
-                return
+    devices = args.mountpoints
 
     # make certain devices contains only NFS mount points
     if len(devices) > 0:
@@ -721,44 +612,119 @@  def iostat_command():
     old_mountstats = None
     sample_time = 0
 
-    if not interval_seen:
+    if args.interval is None:
         print_iostat_summary(old_mountstats, mountstats, devices, sample_time)
         return
 
-    if count_seen:
+    if args.count is not None:
+        count = args.count
         while count != 0:
             print_iostat_summary(old_mountstats, mountstats, devices, sample_time)
             old_mountstats = mountstats
-            time.sleep(interval)
-            sample_time = interval
+            time.sleep(args.interval)
+            sample_time = args.interval
             mountstats = parse_stats_file('/proc/self/mountstats')
             count -= 1
     else: 
         while True:
             print_iostat_summary(old_mountstats, mountstats, devices, sample_time)
             old_mountstats = mountstats
-            time.sleep(interval)
-            sample_time = interval
+            time.sleep(args.interval)
+            sample_time = args.interval
             mountstats = parse_stats_file('/proc/self/mountstats')
 
-#
-# Main
-#
-prog = os.path.basename(sys.argv[0])
+class ICMAction(argparse.Action):
+    """Custom action to deal with interval, count, and mountpoints.
+    """
+    def __call__(self, parser, namespace, values, option_string=None):
+        if namespace.mountpoints is None:
+            namespace.mountpoints = []
+        if values is None:
+            return
+        elif (type(values) == type([])):
+            for value in values:
+                self._handle_one(namespace, value)
+        else:
+            self._handle_one(namespace, values)
+
+    def _handle_one(self, namespace, value):
+        try:
+            intval = int(value)
+            self._handle_int(namespace, intval)
+        except ValueError:
+            namespace.mountpoints.append(value)
+
+    def _handle_int(self, namespace, value):
+        if namespace.interval is None:
+            namespace.interval = value
+        elif namespace.count is None:
+            namespace.count = value
+        else:
+            raise argparse.ArgumentError(self, "too many integer arguments")
+
+def main():
+    parser = argparse.ArgumentParser(epilog='For specific sub-command help, '
+        'run \'mountstats SUB-COMMAND -h|--help\'')
+    subparsers = parser.add_subparsers(help='sub-command help')
+
+    common_parser = argparse.ArgumentParser(add_help=False)
+    common_parser.add_argument('-v', '--version', action='version',
+        version='mountstats ' + Mountstats_version)
+
+    mountstats_parser = subparsers.add_parser('mountstats',
+        parents=[common_parser],
+        help='Display a combination of per-op RPC statistics, NFS event counts, and NFS byte counts. '
+            'This is the default sub-command if no sub-command is given.')
+    group = mountstats_parser.add_mutually_exclusive_group()
+    group.add_argument('-n', '--nfs', action='store_true', dest='nfs_only',
+        help='Display only the NFS statistics')
+    group.add_argument('-r', '--rpc', action='store_true', dest='rpc_only',
+        help='Display only the RPC statistics')
+    # The mountpoints argument cannot be moved into the common_parser because
+    # it will screw up the parsing of the iostat arguments (interval and count)
+    mountstats_parser.add_argument('mountpoints', nargs='+', metavar='mountpoint',
+        help='Display statistics for this mountpoint. More than one may be specified.')
+    mountstats_parser.set_defaults(func=mountstats_command)
+
+    nfsstat_parser = subparsers.add_parser('nfsstat',
+        parents=[common_parser],
+        help='Display nfsstat-like statistics.')
+    nfsstat_parser.set_defaults(func=nfsstat_command)
+
+    iostat_parser = subparsers.add_parser('iostat',
+        parents=[common_parser],
+        help='Display iostat-like statistics.')
+    iostat_parser.add_argument('interval', nargs='?', action=ICMAction,
+        help='Number of seconds between reports. If absent, only one report will '
+            'be generated.')
+    iostat_parser.add_argument('count', nargs='?', action=ICMAction,
+        help='Number of reports generated at <interval> seconds apart. If absent, '
+            'reports will be generated continuously.')
+    # The mountpoints argument cannot be moved into the common_parser because
+    # it will screw up the parsing of the iostat arguments (interval and count)
+    iostat_parser.add_argument('mountpoints', nargs='*', action=ICMAction, metavar='mountpoint',
+        help='Display statsistics for this mountpoint. More than one may be specified. '
+            'If absent, statistics for all NFS mountpoints will be generated.')
+    iostat_parser.set_defaults(func=iostat_command)
+ 
+    args = parser.parse_args()
+    return args.func(args)
 
 try:
-    if prog == 'mountstats':
-        mountstats_command()
-    elif prog == 'ms-nfsstat':
-        nfsstat_command()
-    elif prog == 'ms-iostat':
-        iostat_command()
-    sys.stdout.close()
-    sys.stderr.close()
-except KeyboardInterrupt:
-    print('Caught ^C... exiting')
+    if __name__ == '__main__':
+        # Run the mounstats sub-command if no sub-command (or the help flag)
+        # is given.  If the argparse module ever gets support for optional
+        # (default) sub-commands, then this can be changed.
+        if len(sys.argv) == 1:
+            sys.argv.insert(1, 'mountstats')
+        elif sys.argv[1] not in ['-h', '--help', 'mountstats', 'iostat', 'nfsstat']:
+            sys.argv.insert(1, 'mountstats')
+        res = main()
+        sys.stdout.close()
+        sys.stderr.close()
+        sys.exit(res)
+except (SystemExit, KeyboardInterrupt, RuntimeError):
     sys.exit(1)
 except IOError:
     pass
 
-sys.exit(0)