Skip to content
Snippets Groups Projects
telegraf_mountstats.py 5.33 KiB
Newer Older
#!/usr/bin/python
xprtudpcounters="proto,port,bind_count,rpcsends,rpcreceives,badxids,inflightsends,backlogutil".split(',')
xprttcpcounters="proto,port,bind_count,connect_count,connect_time,idle_time,rpcsends,rpcreceives,badxids,inflightsends,backlogutil".split(',')
xprtrdmacounters="proto,port,bind_count,connect_count,idle_time,rpcsends,rpcreceives,badxids,backlogutil,read_chunks,write_chunks,reply_chunks,total_rdma_req',total_rdma_rep,pullup,fixup,hardway,failed_marshal,bad_reply".split(',')
NfsOpCounters="operations,transmissions,major_timeouts,bytes_sent,bytes_recv,queue_time,response_time,request_time".split(',')
OPS="WRITE,OPEN,CLOSE,ACCESS,RENAME,SYMLINK,CREATE".split(',')

NfsEventCounters = [
    'inoderevalidates',
    'dentryrevalidates',
    'datainvalidates',
    'attrinvalidates',
    'vfsopen',
    'vfslookup',
    'vfspermission',
    'vfsupdatepage',
    'vfsreadpage',
    'vfsreadpages',
    'vfswritepage',
    'vfswritepages',
    'vfsreaddir',
    'vfssetattr',
    'vfsflush',
    'vfsfsync',
    'vfslock',
    'vfsrelease',
    'congestionwait',
    'setattrtrunc',
    'extendwrite',
    'sillyrenames',
    'shortreads',
    'shortwrites',
    'delay'
]

NfsByteCounters = [
    'normalreadbytes',
    'normalwritebytes',
    'directreadbytes',
    'directwritebytes',
    'serverreadbytes',
    'serverwritebytes',
    'readpages',
    'writepages'
]

class DeviceData:
    """DeviceData objects provide methods for parsing and displaying
    data for a single mount grabbed from /proc/self/mountstats
    """
    def __init__(self):
        self.__nfs = dict()
        self.__nfs_device = dict()

    def fstype(self):
        return self.__nfs_device['fstype']

    def tags(self):
        return ",".join(["{}={}".format(key,value) for key,value in self.__nfs_device.items()])

    def values(self):
        try:
            values = ",".join(["{}={}".format(key,value) for key,value in self.__nfs['bytes']])
            values +=","
            values += ",".join(["{}={}".format(key,value) for key,value in self.__nfs['events']])
            values +=","
            opvalues = []
            for op in OPS:
                opvalues.append(",".join(["{}_{}={}".format(op,key,value) for key,value in self.__nfs[op]]))
            values += ",".join(opvalues)
        except KeyError as e:
            # key error occurs if we haven't filtered the lustre mount points from the NFS mount points yet
            return None
        return values


    def __parse_device_line(self, words):
        if words[0] == 'device':
            self.__nfs_device['export'] = words[1]
            self.__nfs_device['mountpoint'] = words[4]
            self.__nfs_device['fstype'] = words[7]

    def __parse_bytes_line(self, words):
        if words[0] == 'bytes:':
            self.__nfs['bytes'] = zip(NfsByteCounters,[ int(x) for x in words[1:]])

    def __parse_events_line(self,words):
        if words[0] == 'events:':
            self.__nfs['events'] = zip(NfsEventCounters,[int(x) for x in words[1:]])

    def __parse_ops_line(self,words):
        if words[0][:-1] in OPS:
            self.__nfs[words[0][:-1]] = zip(NfsOpCounters, [ int(x) for x in words[1:]])

    def __parse_xprt_line(self, words):
        if words[0] == 'xprt:':
            if words[1] == 'udp':
                self._rpc = zip(xprtudpcounters, words[1:11])
            if words[1] == 'tcp':
                self._rpc = zip(xprttcpcounters, words[1:11])
            if words[1] == 'rdma':
                self._rpc = zip(xprtrdmacounters, words[1:11])


    def parse_stats(self, lines):
        """Turn a list of lines from a mount stat file into a 
        dictionary full of stats, keyed by name
        """
        foundnfs = False
        foundrpc = False
        for line in lines:
            words = line.split()
            if len(words) == 0:
                continue
            self.__parse_device_line(words)
            self.__parse_bytes_line(words)
            self.__parse_events_line(words)
            self.__parse_ops_line(words)
            self.__parse_xprt_line(words)


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
    lines in the mountstats file corresponding to the mount
    point named in the key.
    """
    ms_dict = dict()
    key = ''

    f = open(filename)
    for line in f.readlines():
        words = line.split()
        if len(words) == 0:
            continue
        if line.startswith("no device mounted") :
            continue
        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
    f.close

    return ms_dict


def iostats(mountstats):
    stats = {}
    for device in mountstats:
        stats[device] = DeviceData()
        stats[device].parse_stats(mountstats[device])
    return stats

def print_influx_line_proto(device,stats):

    try:
        if not 'nfs' in stats.fstype():
            return
        print("mountstats,{} {}".format(stats.tags(), stats.values()))
    except:
        return

mountstats = parse_stats_file("/proc/self/mountstats")
stats = iostats(mountstats)
for device in stats:
    print_influx_line_proto(device,stats[device])