walters / rpms / nfs-utils

Forked from rpms/nfs-utils 6 years ago
Clone
4f0634d
commit ba18e469a8507befdf8969c5ce7a25564744ae01
4f0634d
Author: Chuck Lever <chuck.lever@oracle.com>
4f0634d
Date:   Mon Jun 23 12:56:14 2008 -0400
4f0634d
4f0634d
    The "nfs-iostat" utility is a Python program that extracts and displays NFS
4f0634d
    client performance information from /proc/self/mountstats.
4f0634d
    
4f0634d
    Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
4f0634d
    Signed-off-by: Steve Dickson <steved@redhat.com>
4f0634d
4f0634d
diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py
4f0634d
new file mode 100644
4f0634d
index 0000000..794d4a8
4f0634d
--- /dev/null
4f0634d
+++ b/tools/nfs-iostat/nfs-iostat.py
4f0634d
@@ -0,0 +1,544 @@
4f0634d
+#!/usr/bin/env python
4f0634d
+# -*- python-mode -*-
4f0634d
+"""Emulate iostat for NFS mount points using /proc/self/mountstats
4f0634d
+"""
4f0634d
+
4f0634d
+__copyright__ = """
4f0634d
+Copyright (C) 2005, Chuck Lever <cel@netapp.com>
4f0634d
+
4f0634d
+This program is free software; you can redistribute it and/or modify
4f0634d
+it under the terms of the GNU General Public License version 2 as
4f0634d
+published by the Free Software Foundation.
4f0634d
+
4f0634d
+This program is distributed in the hope that it will be useful,
4f0634d
+but WITHOUT ANY WARRANTY; without even the implied warranty of
4f0634d
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
4f0634d
+GNU General Public License for more details.
4f0634d
+
4f0634d
+You should have received a copy of the GNU General Public License
4f0634d
+along with this program; if not, write to the Free Software
4f0634d
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
4f0634d
+"""
4f0634d
+
4f0634d
+import sys, os, time
4f0634d
+
4f0634d
+Iostats_version = '0.2'
4f0634d
+
4f0634d
+def difference(x, y):
4f0634d
+    """Used for a map() function
4f0634d
+    """
4f0634d
+    return x - y
4f0634d
+
4f0634d
+NfsEventCounters = [
4f0634d
+    'inoderevalidates',
4f0634d
+    'dentryrevalidates',
4f0634d
+    'datainvalidates',
4f0634d
+    'attrinvalidates',
4f0634d
+    'vfsopen',
4f0634d
+    'vfslookup',
4f0634d
+    'vfspermission',
4f0634d
+    'vfsupdatepage',
4f0634d
+    'vfsreadpage',
4f0634d
+    'vfsreadpages',
4f0634d
+    'vfswritepage',
4f0634d
+    'vfswritepages',
4f0634d
+    'vfsreaddir',
4f0634d
+    'vfssetattr',
4f0634d
+    'vfsflush',
4f0634d
+    'vfsfsync',
4f0634d
+    'vfslock',
4f0634d
+    'vfsrelease',
4f0634d
+    'congestionwait',
4f0634d
+    'setattrtrunc',
4f0634d
+    'extendwrite',
4f0634d
+    'sillyrenames',
4f0634d
+    'shortreads',
4f0634d
+    'shortwrites',
4f0634d
+    'delay'
4f0634d
+]
4f0634d
+
4f0634d
+NfsByteCounters = [
4f0634d
+    'normalreadbytes',
4f0634d
+    'normalwritebytes',
4f0634d
+    'directreadbytes',
4f0634d
+    'directwritebytes',
4f0634d
+    'serverreadbytes',
4f0634d
+    'serverwritebytes',
4f0634d
+    'readpages',
4f0634d
+    'writepages'
4f0634d
+]
4f0634d
+
4f0634d
+class DeviceData:
4f0634d
+    """DeviceData objects provide methods for parsing and displaying
4f0634d
+    data for a single mount grabbed from /proc/self/mountstats
4f0634d
+    """
4f0634d
+    def __init__(self):
4f0634d
+        self.__nfs_data = dict()
4f0634d
+        self.__rpc_data = dict()
4f0634d
+        self.__rpc_data['ops'] = []
4f0634d
+
4f0634d
+    def __parse_nfs_line(self, words):
4f0634d
+        if words[0] == 'device':
4f0634d
+            self.__nfs_data['export'] = words[1]
4f0634d
+            self.__nfs_data['mountpoint'] = words[4]
4f0634d
+            self.__nfs_data['fstype'] = words[7]
4f0634d
+            if words[7] == 'nfs':
4f0634d
+                self.__nfs_data['statvers'] = words[8]
4f0634d
+        elif words[0] == 'age:':
4f0634d
+            self.__nfs_data['age'] = long(words[1])
4f0634d
+        elif words[0] == 'opts:':
4f0634d
+            self.__nfs_data['mountoptions'] = ''.join(words[1:]).split(',')
4f0634d
+        elif words[0] == 'caps:':
4f0634d
+            self.__nfs_data['servercapabilities'] = ''.join(words[1:]).split(',')
4f0634d
+        elif words[0] == 'nfsv4:':
4f0634d
+            self.__nfs_data['nfsv4flags'] = ''.join(words[1:]).split(',')
4f0634d
+        elif words[0] == 'sec:':
4f0634d
+            keys = ''.join(words[1:]).split(',')
4f0634d
+            self.__nfs_data['flavor'] = int(keys[0].split('=')[1])
4f0634d
+            self.__nfs_data['pseudoflavor'] = 0
4f0634d
+            if self.__nfs_data['flavor'] == 6:
4f0634d
+                self.__nfs_data['pseudoflavor'] = int(keys[1].split('=')[1])
4f0634d
+        elif words[0] == 'events:':
4f0634d
+            i = 1
4f0634d
+            for key in NfsEventCounters:
4f0634d
+                self.__nfs_data[key] = int(words[i])
4f0634d
+                i += 1
4f0634d
+        elif words[0] == 'bytes:':
4f0634d
+            i = 1
4f0634d
+            for key in NfsByteCounters:
4f0634d
+                self.__nfs_data[key] = long(words[i])
4f0634d
+                i += 1
4f0634d
+
4f0634d
+    def __parse_rpc_line(self, words):
4f0634d
+        if words[0] == 'RPC':
4f0634d
+            self.__rpc_data['statsvers'] = float(words[3])
4f0634d
+            self.__rpc_data['programversion'] = words[5]
4f0634d
+        elif words[0] == 'xprt:':
4f0634d
+            self.__rpc_data['protocol'] = words[1]
4f0634d
+            if words[1] == 'udp':
4f0634d
+                self.__rpc_data['port'] = int(words[2])
4f0634d
+                self.__rpc_data['bind_count'] = int(words[3])
4f0634d
+                self.__rpc_data['rpcsends'] = int(words[4])
4f0634d
+                self.__rpc_data['rpcreceives'] = int(words[5])
4f0634d
+                self.__rpc_data['badxids'] = int(words[6])
4f0634d
+                self.__rpc_data['inflightsends'] = long(words[7])
4f0634d
+                self.__rpc_data['backlogutil'] = long(words[8])
4f0634d
+            elif words[1] == 'tcp':
4f0634d
+                self.__rpc_data['port'] = words[2]
4f0634d
+                self.__rpc_data['bind_count'] = int(words[3])
4f0634d
+                self.__rpc_data['connect_count'] = int(words[4])
4f0634d
+                self.__rpc_data['connect_time'] = int(words[5])
4f0634d
+                self.__rpc_data['idle_time'] = int(words[6])
4f0634d
+                self.__rpc_data['rpcsends'] = int(words[7])
4f0634d
+                self.__rpc_data['rpcreceives'] = int(words[8])
4f0634d
+                self.__rpc_data['badxids'] = int(words[9])
4f0634d
+                self.__rpc_data['inflightsends'] = long(words[10])
4f0634d
+                self.__rpc_data['backlogutil'] = long(words[11])
4f0634d
+        elif words[0] == 'per-op':
4f0634d
+            self.__rpc_data['per-op'] = words
4f0634d
+        else:
4f0634d
+            op = words[0][:-1]
4f0634d
+            self.__rpc_data['ops'] += [op]
4f0634d
+            self.__rpc_data[op] = [long(word) for word in words[1:]]
4f0634d
+
4f0634d
+    def parse_stats(self, lines):
4f0634d
+        """Turn a list of lines from a mount stat file into a 
4f0634d
+        dictionary full of stats, keyed by name
4f0634d
+        """
4f0634d
+        found = False
4f0634d
+        for line in lines:
4f0634d
+            words = line.split()
4f0634d
+            if len(words) == 0:
4f0634d
+                continue
4f0634d
+            if (not found and words[0] != 'RPC'):
4f0634d
+                self.__parse_nfs_line(words)
4f0634d
+                continue
4f0634d
+
4f0634d
+            found = True
4f0634d
+            self.__parse_rpc_line(words)
4f0634d
+
4f0634d
+    def is_nfs_mountpoint(self):
4f0634d
+        """Return True if this is an NFS or NFSv4 mountpoint,
4f0634d
+        otherwise return False
4f0634d
+        """
4f0634d
+        if self.__nfs_data['fstype'] == 'nfs':
4f0634d
+            return True
4f0634d
+        elif self.__nfs_data['fstype'] == 'nfs4':
4f0634d
+            return True
4f0634d
+        return False
4f0634d
+
4f0634d
+    def compare_iostats(self, old_stats):
4f0634d
+        """Return the difference between two sets of stats
4f0634d
+        """
4f0634d
+        result = DeviceData()
4f0634d
+
4f0634d
+        # copy self into result
4f0634d
+        for key, value in self.__nfs_data.iteritems():
4f0634d
+            result.__nfs_data[key] = value
4f0634d
+        for key, value in self.__rpc_data.iteritems():
4f0634d
+            result.__rpc_data[key] = value
4f0634d
+
4f0634d
+        # compute the difference of each item in the list
4f0634d
+        # note the copy loop above does not copy the lists, just
4f0634d
+        # the reference to them.  so we build new lists here
4f0634d
+        # for the result object.
4f0634d
+        for op in result.__rpc_data['ops']:
4f0634d
+            result.__rpc_data[op] = map(difference, self.__rpc_data[op], old_stats.__rpc_data[op])
4f0634d
+
4f0634d
+        # update the remaining keys we care about
4f0634d
+        result.__rpc_data['rpcsends'] -= old_stats.__rpc_data['rpcsends']
4f0634d
+        result.__rpc_data['backlogutil'] -= old_stats.__rpc_data['backlogutil']
4f0634d
+
4f0634d
+        for key in NfsEventCounters:
4f0634d
+            result.__nfs_data[key] -= old_stats.__nfs_data[key]
4f0634d
+        for key in NfsByteCounters:
4f0634d
+            result.__nfs_data[key] -= old_stats.__nfs_data[key]
4f0634d
+
4f0634d
+        return result
4f0634d
+
4f0634d
+    def __print_data_cache_stats(self):
4f0634d
+        """Print the data cache hit rate
4f0634d
+        """
4f0634d
+        nfs_stats = self.__nfs_data
4f0634d
+        app_bytes_read = float(nfs_stats['normalreadbytes'])
4f0634d
+        if app_bytes_read != 0:
4f0634d
+            client_bytes_read = float(nfs_stats['serverreadbytes'] - nfs_stats['directreadbytes'])
4f0634d
+            ratio = ((app_bytes_read - client_bytes_read) * 100) / app_bytes_read
4f0634d
+
4f0634d
+            print
4f0634d
+            print 'app bytes: %f  client bytes %f' % (app_bytes_read, client_bytes_read)
4f0634d
+            print 'Data cache hit ratio: %4.2f%%' % ratio
4f0634d
+
4f0634d
+    def __print_attr_cache_stats(self, sample_time):
4f0634d
+        """Print attribute cache efficiency stats
4f0634d
+        """
4f0634d
+        nfs_stats = self.__nfs_data
4f0634d
+        getattr_stats = self.__rpc_data['GETATTR']
4f0634d
+
4f0634d
+        if nfs_stats['inoderevalidates'] != 0:
4f0634d
+            getattr_ops = float(getattr_stats[1])
4f0634d
+            opens = float(nfs_stats['vfsopen'])
4f0634d
+            revalidates = float(nfs_stats['inoderevalidates']) - opens
4f0634d
+            if revalidates != 0:
4f0634d
+                ratio = ((revalidates - getattr_ops) * 100) / revalidates
4f0634d
+            else:
4f0634d
+                ratio = 0.0
4f0634d
+
4f0634d
+            data_invalidates = float(nfs_stats['datainvalidates'])
4f0634d
+            attr_invalidates = float(nfs_stats['attrinvalidates'])
4f0634d
+
4f0634d
+            print
4f0634d
+            print '%d inode revalidations, hitting in cache %4.2f%% of the time' % \
4f0634d
+                (revalidates, ratio)
4f0634d
+            print '%d open operations (mandatory GETATTR requests)' % opens
4f0634d
+            if getattr_ops != 0:
4f0634d
+                print '%4.2f%% of GETATTRs resulted in data cache invalidations' % \
4f0634d
+                   ((data_invalidates * 100) / getattr_ops)
4f0634d
+
4f0634d
+    def __print_dir_cache_stats(self, sample_time):
4f0634d
+        """Print directory stats
4f0634d
+        """
4f0634d
+        nfs_stats = self.__nfs_data
4f0634d
+        lookup_ops = self.__rpc_data['LOOKUP'][0]
4f0634d
+        readdir_ops = self.__rpc_data['READDIR'][0]
4f0634d
+        if self.__rpc_data.has_key('READDIRPLUS'):
4f0634d
+            readdir_ops += self.__rpc_data['READDIRPLUS'][0]
4f0634d
+
4f0634d
+        dentry_revals = nfs_stats['dentryrevalidates']
4f0634d
+        opens = nfs_stats['vfsopen']
4f0634d
+        lookups = nfs_stats['vfslookup']
4f0634d
+        getdents = nfs_stats['vfsreaddir']
4f0634d
+
4f0634d
+        print
4f0634d
+        print '%d open operations (pathname lookups)' % opens
4f0634d
+        print '%d dentry revalidates and %d vfs lookup requests' % \
4f0634d
+            (dentry_revals, lookups),
4f0634d
+        print 'resulted in %d LOOKUPs on the wire' % lookup_ops
4f0634d
+        print '%d vfs getdents calls resulted in %d READDIRs on the wire' % \
4f0634d
+            (getdents, readdir_ops)
4f0634d
+
4f0634d
+    def __print_page_stats(self, sample_time):
4f0634d
+        """Print page cache stats
4f0634d
+        """
4f0634d
+        nfs_stats = self.__nfs_data
4f0634d
+
4f0634d
+        vfsreadpage = nfs_stats['vfsreadpage']
4f0634d
+        vfsreadpages = nfs_stats['vfsreadpages']
4f0634d
+        pages_read = nfs_stats['readpages']
4f0634d
+        vfswritepage = nfs_stats['vfswritepage']
4f0634d
+        vfswritepages = nfs_stats['vfswritepages']
4f0634d
+        pages_written = nfs_stats['writepages']
4f0634d
+
4f0634d
+        print
4f0634d
+        print '%d nfs_readpage() calls read %d pages' % \
4f0634d
+            (vfsreadpage, vfsreadpage)
4f0634d
+        print '%d nfs_readpages() calls read %d pages' % \
4f0634d
+            (vfsreadpages, pages_read - vfsreadpage),
4f0634d
+        if vfsreadpages != 0:
4f0634d
+            print '(%.1f pages per call)' % \
4f0634d
+                (float(pages_read - vfsreadpage) / vfsreadpages)
4f0634d
+        else:
4f0634d
+            print
4f0634d
+
4f0634d
+        print
4f0634d
+        print '%d nfs_updatepage() calls' % nfs_stats['vfsupdatepage']
4f0634d
+        print '%d nfs_writepage() calls wrote %d pages' % \
4f0634d
+            (vfswritepage, vfswritepage)
4f0634d
+        print '%d nfs_writepages() calls wrote %d pages' % \
4f0634d
+            (vfswritepages, pages_written - vfswritepage),
4f0634d
+        if (vfswritepages) != 0:
4f0634d
+            print '(%.1f pages per call)' % \
4f0634d
+                (float(pages_written - vfswritepage) / vfswritepages)
4f0634d
+        else:
4f0634d
+            print
4f0634d
+
4f0634d
+        congestionwaits = nfs_stats['congestionwait']
4f0634d
+        if congestionwaits != 0:
4f0634d
+            print
4f0634d
+            print '%d congestion waits' % congestionwaits
4f0634d
+
4f0634d
+    def __print_rpc_op_stats(self, op, sample_time):
4f0634d
+        """Print generic stats for one RPC op
4f0634d
+        """
4f0634d
+        if not self.__rpc_data.has_key(op):
4f0634d
+            return
4f0634d
+
4f0634d
+        rpc_stats = self.__rpc_data[op]
4f0634d
+        ops = float(rpc_stats[0])
4f0634d
+        retrans = float(rpc_stats[1] - rpc_stats[0])
4f0634d
+        kilobytes = float(rpc_stats[3] + rpc_stats[4]) / 1024
4f0634d
+        rtt = float(rpc_stats[6])
4f0634d
+        exe = float(rpc_stats[7])
4f0634d
+
4f0634d
+        # prevent floating point exceptions
4f0634d
+        if ops != 0:
4f0634d
+            kb_per_op = kilobytes / ops
4f0634d
+            retrans_percent = (retrans * 100) / ops
4f0634d
+            rtt_per_op = rtt / ops
4f0634d
+            exe_per_op = exe / ops
4f0634d
+        else:
4f0634d
+            kb_per_op = 0.0
4f0634d
+            retrans_percent = 0.0
4f0634d
+            rtt_per_op = 0.0
4f0634d
+            exe_per_op = 0.0
4f0634d
+
4f0634d
+        op += ':'
4f0634d
+        print '%s' % op.lower().ljust(15),
4f0634d
+        print '  ops/s\t\t   Kb/s\t\t  Kb/op\t\tretrans\t\tavg RTT (ms)\tavg exe (ms)'
4f0634d
+
4f0634d
+        print '\t\t%7.3f' % (ops / sample_time),
4f0634d
+        print '\t%7.3f' % (kilobytes / sample_time),
4f0634d
+        print '\t%7.3f' % kb_per_op,
4f0634d
+        print ' %7d (%3.1f%%)' % (retrans, retrans_percent),
4f0634d
+        print '\t%7.3f' % rtt_per_op,
4f0634d
+        print '\t%7.3f' % exe_per_op
4f0634d
+
4f0634d
+    def display_iostats(self, sample_time, which):
4f0634d
+        """Display NFS and RPC stats in an iostat-like way
4f0634d
+        """
4f0634d
+        sends = float(self.__rpc_data['rpcsends'])
4f0634d
+        if sample_time == 0:
4f0634d
+            sample_time = float(self.__nfs_data['age'])
4f0634d
+        if sends != 0:
4f0634d
+            backlog = (float(self.__rpc_data['backlogutil']) / sends) / sample_time
4f0634d
+        else:
4f0634d
+            backlog = 0.0
4f0634d
+
4f0634d
+        print
4f0634d
+        print '%s mounted on %s:' % \
4f0634d
+            (self.__nfs_data['export'], self.__nfs_data['mountpoint'])
4f0634d
+        print
4f0634d
+
4f0634d
+        print '   op/s\t\trpc bklog'
4f0634d
+        print '%7.2f' % (sends / sample_time), 
4f0634d
+        print '\t%7.2f' % backlog
4f0634d
+
4f0634d
+        if which == 0:
4f0634d
+            self.__print_rpc_op_stats('READ', sample_time)
4f0634d
+            self.__print_rpc_op_stats('WRITE', sample_time)
4f0634d
+        elif which == 1:
4f0634d
+            self.__print_rpc_op_stats('GETATTR', sample_time)
4f0634d
+            self.__print_rpc_op_stats('ACCESS', sample_time)
4f0634d
+            self.__print_attr_cache_stats(sample_time)
4f0634d
+        elif which == 2:
4f0634d
+            self.__print_rpc_op_stats('LOOKUP', sample_time)
4f0634d
+            self.__print_rpc_op_stats('READDIR', sample_time)
4f0634d
+            if self.__rpc_data.has_key('READDIRPLUS'):
4f0634d
+                self.__print_rpc_op_stats('READDIRPLUS', sample_time)
4f0634d
+            self.__print_dir_cache_stats(sample_time)
4f0634d
+        elif which == 3:
4f0634d
+            self.__print_rpc_op_stats('READ', sample_time)
4f0634d
+            self.__print_rpc_op_stats('WRITE', sample_time)
4f0634d
+            self.__print_page_stats(sample_time)
4f0634d
+
4f0634d
+#
4f0634d
+# Functions
4f0634d
+#
4f0634d
+
4f0634d
+def print_iostat_help(name):
4f0634d
+    print 'usage: %s [ <interval> [ <count> ] ] [ <options> ] [ <mount point> ] ' % name
4f0634d
+    print
4f0634d
+    print ' Version %s' % Iostats_version
4f0634d
+    print
4f0634d
+    print ' Sample iostat-like program to display NFS client per-mount statistics.'
4f0634d
+    print
4f0634d
+    print ' The <interval> parameter specifies the amount of time in seconds between'
4f0634d
+    print ' each report.  The first report contains statistics for the time since each'
4f0634d
+    print ' file system was mounted.  Each subsequent report contains statistics'
4f0634d
+    print ' collected during the interval since the previous report.'
4f0634d
+    print
4f0634d
+    print ' If the <count> parameter is specified, the value of <count> determines the'
4f0634d
+    print ' number of reports generated at <interval> seconds apart.  If the interval'
4f0634d
+    print ' parameter is specified without the <count> parameter, the command generates'
4f0634d
+    print ' reports continuously.'
4f0634d
+    print
4f0634d
+    print ' Options include "--attr", which displays statistics related to the attribute'
4f0634d
+    print ' cache, "--dir", which displays statistics related to directory operations,'
4f0634d
+    print ' and "--page", which displays statistics related to the page cache.'
4f0634d
+    print ' By default, if no option is specified, statistics related to file I/O are'
4f0634d
+    print ' displayed.'
4f0634d
+    print
4f0634d
+    print ' If one or more <mount point> names are specified, statistics for only these'
4f0634d
+    print ' mount points will be displayed.  Otherwise, all NFS mount points on the'
4f0634d
+    print ' client are listed.'
4f0634d
+
4f0634d
+def parse_stats_file(filename):
4f0634d
+    """pop the contents of a mountstats file into a dictionary,
4f0634d
+    keyed by mount point.  each value object is a list of the
4f0634d
+    lines in the mountstats file corresponding to the mount
4f0634d
+    point named in the key.
4f0634d
+    """
4f0634d
+    ms_dict = dict()
4f0634d
+    key = ''
4f0634d
+
4f0634d
+    f = file(filename)
4f0634d
+    for line in f.readlines():
4f0634d
+        words = line.split()
4f0634d
+        if len(words) == 0:
4f0634d
+            continue
4f0634d
+        if words[0] == 'device':
4f0634d
+            key = words[4]
4f0634d
+            new = [ line.strip() ]
4f0634d
+        else:
4f0634d
+            new += [ line.strip() ]
4f0634d
+        ms_dict[key] = new
4f0634d
+    f.close
4f0634d
+
4f0634d
+    return ms_dict
4f0634d
+
4f0634d
+def print_iostat_summary(old, new, devices, time, ac):
4f0634d
+    for device in devices:
4f0634d
+        stats = DeviceData()
4f0634d
+        stats.parse_stats(new[device])
4f0634d
+        if not old:
4f0634d
+            stats.display_iostats(time, ac)
4f0634d
+        else:
4f0634d
+            old_stats = DeviceData()
4f0634d
+            old_stats.parse_stats(old[device])
4f0634d
+            diff_stats = stats.compare_iostats(old_stats)
4f0634d
+            diff_stats.display_iostats(time, ac)
4f0634d
+
4f0634d
+def iostat_command(name):
4f0634d
+    """iostat-like command for NFS mount points
4f0634d
+    """
4f0634d
+    mountstats = parse_stats_file('/proc/self/mountstats')
4f0634d
+    devices = []
4f0634d
+    which = 0
4f0634d
+    interval_seen = False
4f0634d
+    count_seen = False
4f0634d
+
4f0634d
+    for arg in sys.argv:
4f0634d
+        if arg in ['-h', '--help', 'help', 'usage']:
4f0634d
+            print_iostat_help(name)
4f0634d
+            return
4f0634d
+
4f0634d
+        if arg in ['-v', '--version', 'version']:
4f0634d
+            print '%s version %s' % (name, Iostats_version)
4f0634d
+            return
4f0634d
+
4f0634d
+        if arg in ['-a', '--attr']:
4f0634d
+            which = 1
4f0634d
+            continue
4f0634d
+
4f0634d
+        if arg in ['-d', '--dir']:
4f0634d
+            which = 2
4f0634d
+            continue
4f0634d
+
4f0634d
+        if arg in ['-p', '--page']:
4f0634d
+            which = 3
4f0634d
+            continue
4f0634d
+
4f0634d
+        if arg == sys.argv[0]:
4f0634d
+            continue
4f0634d
+
4f0634d
+        if arg in mountstats:
4f0634d
+            devices += [arg]
4f0634d
+        elif not interval_seen:
4f0634d
+            interval = int(arg)
4f0634d
+            if interval > 0:
4f0634d
+                interval_seen = True
4f0634d
+            else:
4f0634d
+                print 'Illegal <interval> value'
4f0634d
+                return
4f0634d
+        elif not count_seen:
4f0634d
+            count = int(arg)
4f0634d
+            if count > 0:
4f0634d
+                count_seen = True
4f0634d
+            else:
4f0634d
+                print 'Illegal <count> value'
4f0634d
+                return
4f0634d
+
4f0634d
+    # make certain devices contains only NFS mount points
4f0634d
+    if len(devices) > 0:
4f0634d
+        check = []
4f0634d
+        for device in devices:
4f0634d
+            stats = DeviceData()
4f0634d
+            stats.parse_stats(mountstats[device])
4f0634d
+            if stats.is_nfs_mountpoint():
4f0634d
+                check += [device]
4f0634d
+        devices = check
4f0634d
+    else:
4f0634d
+        for device, descr in mountstats.iteritems():
4f0634d
+            stats = DeviceData()
4f0634d
+            stats.parse_stats(descr)
4f0634d
+            if stats.is_nfs_mountpoint():
4f0634d
+                devices += [device]
4f0634d
+    if len(devices) == 0:
4f0634d
+        print 'No NFS mount points were found'
4f0634d
+        return
4f0634d
+
4f0634d
+    old_mountstats = None
4f0634d
+    sample_time = 0.0
4f0634d
+
4f0634d
+    if not interval_seen:
4f0634d
+        print_iostat_summary(old_mountstats, mountstats, devices, sample_time, which)
4f0634d
+        return
4f0634d
+
4f0634d
+    if count_seen:
4f0634d
+        while count != 0:
4f0634d
+            print_iostat_summary(old_mountstats, mountstats, devices, sample_time, which)
4f0634d
+            old_mountstats = mountstats
4f0634d
+            time.sleep(interval)
4f0634d
+            sample_time = interval
4f0634d
+            mountstats = parse_stats_file('/proc/self/mountstats')
4f0634d
+            count -= 1
4f0634d
+    else: 
4f0634d
+        while True:
4f0634d
+            print_iostat_summary(old_mountstats, mountstats, devices, sample_time, which)
4f0634d
+            old_mountstats = mountstats
4f0634d
+            time.sleep(interval)
4f0634d
+            sample_time = interval
4f0634d
+            mountstats = parse_stats_file('/proc/self/mountstats')
4f0634d
+
4f0634d
+#
4f0634d
+# Main
4f0634d
+#
4f0634d
+prog = os.path.basename(sys.argv[0])
4f0634d
+
4f0634d
+try:
4f0634d
+    iostat_command(prog)
4f0634d
+except KeyboardInterrupt:
4f0634d
+    print 'Caught ^C... exiting'
4f0634d
+    sys.exit(1)
4f0634d
+
4f0634d
+sys.exit(0)