X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=tools%2Fnfs-iostat%2Fnfs-iostat.py;h=d9096321f59aae9eb131340cb8a5cff5b38d7ccd;hp=2e5ac3dcfd3b8a5248ba0cc72c9ff66fc0f5f9d5;hb=3ad7dea527170ae12c9cdc2e428d0676ac261144;hpb=88deba4a8db06d371659aa85b668460b85900d48 diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py index 2e5ac3d..d909632 100644 --- a/tools/nfs-iostat/nfs-iostat.py +++ b/tools/nfs-iostat/nfs-iostat.py @@ -17,10 +17,12 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301 USA """ import sys, os, time +from optparse import OptionParser, OptionGroup Iostats_version = '0.2' @@ -84,6 +86,12 @@ class DeviceData: self.__nfs_data['fstype'] = words[7] if words[7] == 'nfs': self.__nfs_data['statvers'] = words[8] + elif 'nfs' in words or 'nfs4' in words: + self.__nfs_data['export'] = words[0] + self.__nfs_data['mountpoint'] = words[3] + self.__nfs_data['fstype'] = words[6] + if words[6] == 'nfs': + self.__nfs_data['statvers'] = words[7] elif words[0] == 'age:': self.__nfs_data['age'] = long(words[1]) elif words[0] == 'opts:': @@ -353,12 +361,24 @@ class DeviceData: print '\t%7.3f' % rtt_per_op, print '\t%7.3f' % exe_per_op + def ops(self, sample_time): + sends = float(self.__rpc_data['rpcsends']) + if sample_time == 0: + sample_time = float(self.__nfs_data['age']) + return (sends / sample_time) + def display_iostats(self, sample_time, which): """Display NFS and RPC stats in an iostat-like way """ sends = float(self.__rpc_data['rpcsends']) if sample_time == 0: sample_time = float(self.__nfs_data['age']) + # sample_time could still be zero if the export was just mounted. + # Set it to 1 to avoid divide by zero errors in this case since we'll + # likely still have relevant mount statistics to show. + # + if sample_time == 0: + sample_time = 1; if sends != 0: backlog = (float(self.__rpc_data['backlogutil']) / sends) / sample_time else: @@ -395,33 +415,6 @@ class DeviceData: # Functions # -def print_iostat_help(name): - print 'usage: %s [ [ ] ] [ ] [ ] ' % name - print - print ' Version %s' % Iostats_version - print - print ' Sample iostat-like program to display NFS client per-mount statistics.' - print - print ' The 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 parameter is specified, the value of determines the' - print ' number of reports generated at seconds apart. If the interval' - print ' parameter is specified without the parameter, the command generates' - print ' reports continuously.' - print - print ' Options include "--attr", which displays statistics related to the attribute' - print ' cache, "--dir", which displays statistics related to directory operations,' - print ' and "--page", which displays statistics related to the page cache.' - print ' By default, if no option is specified, statistics related to file I/O are' - print ' displayed.' - print - print ' If one or more names are specified, statistics for only these' - print ' mount points will be displayed. Otherwise, all NFS mount points on the' - print ' client are listed.' - def parse_stats_file(filename): """pop the contents of a mountstats file into a dictionary, keyed by mount point. each value object is a list of the @@ -439,6 +432,9 @@ def parse_stats_file(filename): if words[0] == 'device': key = words[4] new = [ line.strip() ] + elif 'nfs' in words or 'nfs4' in words: + key = words[3] + new = [ line.strip() ] else: new += [ line.strip() ] ms_dict[key] = new @@ -446,7 +442,9 @@ def parse_stats_file(filename): return ms_dict -def print_iostat_summary(old, new, devices, time, ac): +def print_iostat_summary(old, new, devices, time, options): + stats = {} + diff_stats = {} if old: # Trim device list to only include intersection of old and new data, # this addresses umounts due to autofs mountpoints @@ -455,15 +453,33 @@ def print_iostat_summary(old, new, devices, time, ac): devicelist = devices for device in devicelist: - stats = DeviceData() - stats.parse_stats(new[device]) - if not old: - stats.display_iostats(time, ac) - else: + stats[device] = DeviceData() + stats[device].parse_stats(new[device]) + if old: old_stats = DeviceData() old_stats.parse_stats(old[device]) - diff_stats = stats.compare_iostats(old_stats) - diff_stats.display_iostats(time, ac) + diff_stats[device] = stats[device].compare_iostats(old_stats) + + if options.sort: + if old: + # We now have compared data and can print a comparison + # ordered by mountpoint ops per second + devicelist.sort(key=lambda x: diff_stats[x].ops(time), reverse=True) + else: + # First iteration, just sort by newly parsed ops/s + devicelist.sort(key=lambda x: stats[x].ops(time), reverse=True) + + count = 1 + for device in devicelist: + if old: + diff_stats[device].display_iostats(time, options.which) + else: + stats[device].display_iostats(time, options.which) + + count += 1 + if (count > options.list): + return + def list_nfs_mounts(givenlist, mountstats): """return a list of NFS mounts given a list to validate or @@ -491,30 +507,61 @@ def iostat_command(name): mountstats = parse_stats_file('/proc/self/mountstats') devices = [] origdevices = [] - which = 0 interval_seen = False count_seen = False - for arg in sys.argv: - if arg in ['-h', '--help', 'help', 'usage']: - print_iostat_help(name) - return - - if arg in ['-v', '--version', 'version']: - print '%s version %s' % (name, Iostats_version) - return - - if arg in ['-a', '--attr']: - which = 1 - continue - - if arg in ['-d', '--dir']: - which = 2 - continue - - if arg in ['-p', '--page']: - which = 3 - continue + mydescription= """ +Sample iostat-like program to display NFS client per-mount' +statistics. The parameter specifies the amount of time in seconds +between each report. The first report contains statistics for the time since +each file system was mounted. Each subsequent report contains statistics +collected during the interval since the previous report. If the +parameter is specified, the value of determines the number of reports +generated at seconds apart. If the interval parameter is specified +without the parameter, the command generates reports continuously. +If one or more names are specified, statistics for only these +mount points will be displayed. Otherwise, all NFS mount points on the +client are listed. +""" + parser = OptionParser( + usage="usage: %prog [ [ ] ] [ ] [ ]", + description=mydescription, + version='version %s' % Iostats_version) + parser.set_defaults(which=0, sort=False, list=sys.maxint) + + statgroup = OptionGroup(parser, "Statistics Options", + 'File I/O is displayed unless one of the following is specified:') + statgroup.add_option('-a', '--attr', + action="store_const", + dest="which", + const=1, + help='displays statistics related to the attribute cache') + statgroup.add_option('-d', '--dir', + action="store_const", + dest="which", + const=2, + help='displays statistics related to directory operations') + statgroup.add_option('-p', '--page', + action="store_const", + dest="which", + const=3, + help='displays statistics related to the page cache') + parser.add_option_group(statgroup) + displaygroup = OptionGroup(parser, "Display Options", + 'Options affecting display format:') + displaygroup.add_option('-s', '--sort', + action="store_true", + dest="sort", + help="Sort NFS mount points by ops/second") + displaygroup.add_option('-l','--list', + action="store", + type="int", + dest="list", + help="only print stats for first LIST mount points") + parser.add_option_group(displaygroup) + + (options, args) = parser.parse_args(sys.argv) + for arg in args: if arg == sys.argv[0]: continue @@ -522,18 +569,26 @@ def iostat_command(name): if arg in mountstats: origdevices += [arg] elif not interval_seen: - interval = int(arg) + try: + interval = int(arg) + except: + print 'Illegal value %s' % arg + return if interval > 0: interval_seen = True else: - print 'Illegal value' + print 'Illegal value %s' % arg return elif not count_seen: - count = int(arg) + try: + count = int(arg) + except: + print 'Ilegal value %s' % arg + return if count > 0: count_seen = True else: - print 'Illegal value' + print 'Illegal value %s' % arg return # make certain devices contains only NFS mount points @@ -547,12 +602,12 @@ def iostat_command(name): sample_time = 0.0 if not interval_seen: - print_iostat_summary(old_mountstats, mountstats, devices, sample_time, which) + print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options) return if count_seen: while count != 0: - print_iostat_summary(old_mountstats, mountstats, devices, sample_time, which) + print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options) old_mountstats = mountstats time.sleep(interval) sample_time = interval @@ -566,7 +621,7 @@ def iostat_command(name): count -= 1 else: while True: - print_iostat_summary(old_mountstats, mountstats, devices, sample_time, which) + print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options) old_mountstats = mountstats time.sleep(interval) sample_time = interval