#!/usr/bin/env pmpython # # Copyright (c) 2018-2020 Red Hat. # Copyright (c) 2011 SGI. All Rights Reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # pylint: disable=bad-continuation,line-too-long,too-many-lines,bare-except # from ctypes import c_char_p, c_long, c_uint, c_ulonglong from ctypes import cast, sizeof, POINTER, Structure from pcp.pmapi import pmUnits from pcp.pmda import PMDA, pmdaMetric, pmdaIndom import cpmapi as c_api import os import re import sys if sys.version >= '3': long = int # python2 to python3 portability (no long() in python3) text_type = str else: text_type = unicode # pylint: disable=undefined-variable NOVAL = c_api.PM_ERR_APPVERSION UNITS_NONE = pmUnits(0, 0, 0, 0, 0, 0) UNITS_SECS = pmUnits(0, 1, 0, 0, c_api.PM_TIME_SEC, 0) UNITS_COUNT = pmUnits(0, 0, 1, 0, 0, c_api.PM_COUNT_ONE) UNITS_MSECS = pmUnits(0, 1, 0, 0, c_api.PM_TIME_MSEC, 0) UNITS_BYTES = pmUnits(1, 0, 0, c_api.PM_SPACE_BYTE, 0, 0) if sizeof(c_long) == 4: CTYPES_ULONG = c_uint PMDA_ULONG = c_api.PM_TYPE_U32 elif sizeof(c_long) == 8: CTYPES_ULONG = c_ulonglong PMDA_ULONG = c_api.PM_TYPE_U64 else: # cannot figure out how represent kernel long sys.exit(1) COMMON_OPS = [ # ops available in v3, v4 and v4.1 'null', 'getattr', 'setattr', 'lookup', 'access', 'readlink', 'read', 'write', 'create', 'symlink', 'remove', 'rename', 'link', 'readdir', 'fsinfo', 'pathconf', 'commit'] V3ONLY_OPS = [ # ops only in v3 'fsstat', 'mkdir', 'mknod', 'readdirplus', 'rmdir'] V4_OPS = [ # ops ADDED in v4, # note: some v3 ops are invalid - fsstat, mkdir, mknod, readdirplus, rmdir 'close', 'create_session', 'delegreturn', 'destroy_session', 'exchange_id', 'fs_locations', 'getacl', 'getdeviceinfo', 'get_lease_time', 'layoutcommit', 'layoutget', 'layoutreturn', 'lock', 'lockt', 'locku', 'lookup_root', 'open', 'open_confirm', 'open_downgrade', 'open_noattr', 'reclaim_complete', 'release_lockowner', 'renew', 'secinfo', 'sequence', 'server_caps', 'setacl', 'setclientid', 'setclientid_confirm', 'statfs'] V41_OPS = [ # ops ADDED in v4.1 # note: same v3 ops removed as in v4, all v4 ops should exist 'bind_conn_to_session', 'destroy_clientid', 'free_stateid', 'getdevicelist', 'secinfo_no_name', 'test_stateid'] V42_OPS = [ # ops ADDED in v4.2 'seek', 'allocate', 'deallocate', 'fsid_present', 'layoutstats', 'clone', 'copy', 'offload_cancel', 'lookupp', 'layouterror'] class NFSOPTIONS(Structure): """ Define the structure for all of the NFS mount options Needs to differentiate between options that are not available based on NFS version, versus options that are not set and therefore default values apply - prepopulate based on this constraint. """ _fields_ = [ ('readmode', c_char_p), ('sync', c_uint), ('atime', c_uint), ('diratime', c_uint), ('vers', c_char_p), ('rsize', c_uint), ('wsize', c_uint), ('bsize', c_uint), ('namlen', c_uint), ('acregmin', c_uint), ('acregmax', c_uint), ('acdirmin', c_uint), ('acdirmax', c_uint), ('recovery', c_char_p), ('posix', c_uint), ('cto', c_uint), ('ac', c_uint), ('lock', c_uint), ('acl', c_uint), ('rdirplus', c_uint), ('sharecache', c_uint), ('resvport', c_uint), ('proto', c_char_p), ('port', c_uint), ('timeo', c_uint), ('retrans', c_uint), ('sec', c_char_p), # NFS 3 only options but can be NULL also ('mountaddr', c_char_p), ('mountvers', c_uint), ('mountport', c_uint), ('mountproto', c_char_p), # NFS 4 only ('clientaddr', c_char_p), # All versions ('fsc', c_uint), ('migration', c_uint), ('lookupcache', c_char_p), ('local_lock', c_char_p), ('string', c_char_p), ] def __init__(self): """ Set any non-zero default values for the per-mount fields """ Structure.__init__(self) self.atime = 1 self.diratime = 1 self.vers = b'3' self.recovery = b'hard' self.cto = 1 self.ac = 1 self.lock = 1 self.acl = 1 self.rdirplus = 1 self.sharecache = 1 self.resvport = 1 self.proto = b'tcp' self.timeo = 600 self.retrans = 3 self.sec = b'sys' self.mountaddr = b'unspecified' self.mountproto = b'' self.clientaddr = b'' self.lookupcache = b'' self.local_lock = b'none' class NFSEVENTS(Structure): """ Defines the structure for all of the NFS events """ _fields_ = [ ('inoderevalidate', c_uint), ('dentryrevalidate', c_uint), ('datainvalidate', c_uint), ('attrinvalidate', c_uint), ('vfsopen', c_uint), ('vfslookup', c_uint), ('vfsaccess', c_uint), ('vfsupdatepage', c_uint), ('vfsreadpage', c_uint), ('vfsreadpages', c_uint), ('vfswritepage', c_uint), ('vfswritepages', c_uint), ('vfsgetdents', c_uint), ('vfssetattr', c_uint), ('vfsflush', c_uint), ('vfsfsync', c_uint), ('vfslock', c_uint), ('vfsrelease', c_uint), ('congestionwait', c_uint), ('setattrtrunc', c_uint), ('extendwrite', c_uint), ('sillyrename', c_uint), ('shortread', c_uint), ('shortwrite', c_uint), ('delay', c_uint), ('pnfsread', c_uint), ('pnfswrite', c_uint), ] class NFSBYTESREAD(Structure): _fields_ = [ ('normal', c_ulonglong), ('direct', c_ulonglong), ('server', c_ulonglong), ] class NFSBYTESWRITE(Structure): _fields_ = [ ('normal', c_ulonglong), ('direct', c_ulonglong), ('server', c_ulonglong), ] class NFSBYTES(Structure): _fields_ = [ ('read', NFSBYTESREAD), ('write', NFSBYTESWRITE), ] class NFSPAGES(Structure): _fields_ = [ ('read', c_ulonglong), ('write', c_ulonglong), ] class NFSEXPORT(Structure): _fields_ = [ ('srcport', c_char_p), ('bind_count', c_uint), ('connect_count', c_uint), ('connect_time', c_uint), ('idle_time', c_uint), ('sends', c_uint), ('recvs', c_uint), ('bad_xids', c_uint), ('req_u', c_ulonglong), ('backlog_u', c_ulonglong), ('sending_u', c_ulonglong), ('pending_u', c_ulonglong), ('max_slots', CTYPES_ULONG), ('read_chunks', CTYPES_ULONG), ('write_chunks', CTYPES_ULONG), ('reply_chunks', CTYPES_ULONG), ('total_rdma_req', c_ulonglong), ('total_rdma_rep', c_ulonglong), ('pullup', c_ulonglong), ('fixup', c_ulonglong), ('hardway', CTYPES_ULONG), ('failed_marshal', CTYPES_ULONG), ('bad_reply', CTYPES_ULONG), ('nomsg_call', CTYPES_ULONG), ('mrs_recycled', CTYPES_ULONG), ('mrs_orphaned', CTYPES_ULONG), ('mrs_allocated', CTYPES_ULONG), ('local_inv_needed', CTYPES_ULONG), ('empty_sendctxq', CTYPES_ULONG), ('reply_waits', CTYPES_ULONG), ] def __init__(self): """ Set any non-zero default values for the per-mount fields """ Structure.__init__(self) self.srcport = b'' class NFSOPSTATS(Structure): _fields_ = [ ('ops', CTYPES_ULONG), ('ntrans', CTYPES_ULONG), ('timeouts', CTYPES_ULONG), ('bytes_sent', c_ulonglong), ('bytes_recv', c_ulonglong), ('queue', c_ulonglong), ('rtt', c_ulonglong), ('execute', c_ulonglong), ('errors', CTYPES_ULONG), ] def __init__(self): Structure.__init__(self) self.ops = NOVAL self.ntrans = NOVAL self.timeouts = NOVAL self.bytes_sent = NOVAL self.bytes_recv = NOVAL self.queue = NOVAL self.rtt = NOVAL self.execute = NOVAL self.errors = NOVAL class NFSOPS(Structure): _fields_ = [(op, NFSOPSTATS) for op in COMMON_OPS + V3ONLY_OPS + V4_OPS + V41_OPS + V42_OPS] class NFSCLIENT(Structure): """ Defines the structure associated with an individual NFS mount """ _fields_ = [ ('export', c_char_p), ('mountpoint', c_char_p), ('options', NFSOPTIONS), ('age', c_uint), ('capabilities', c_char_p), ('security', c_char_p), ('nfsv4', c_char_p), ('events', NFSEVENTS), ('bytes', NFSBYTES), ('pages', NFSPAGES), ('xprt', NFSEXPORT), ('ops', NFSOPS), ] def __init__(self): """ Set any non-zero default values for the per-mount fields """ Structure.__init__(self) self.capabilities = b'' self.security = b'' self.nfsv4 = b'' class NFSCLIENTPMDA(PMDA): """ PMDA class for nfsclient performance metrics """ # The instance domain is 0 for all nfsclient stats NFSCLIENT_INDOM = 0 # mountstats or other file to use MOUNTSTATS_PATH = '/proc/self/mountstats' # prefix for all metric names NAME = '' # The stats dict is keyed on the 'mount' string (the mountpoint string), # e.g. '/home'. The value is a dict containing dictionaries of all of # the stats data keyed on metric name nfs = {} @staticmethod def chars(string): return string.encode('utf-8') @staticmethod def longs(string): return long(string) def nfsclient_refresh(self): """ Parse /proc/self/mountstats and store stats, one pass through the file. """ client = NFSCLIENT() instance = '' # mountstats output has a section for each mounted filesystems on the # system. Each section starts with a line like: 'device X mounted on # Y with fstype Z'. Some filesystems (like NFS) print additional info # that we want to capture as metrics. with open(self.MOUNTSTATS_PATH, 'r') as STATS: while True: line = STATS.readline() if line == '': break # self.log(line) # does this line represent a mount? m = re.match(r'device (\S*) mounted on (\S*) with fstype', line) if m: instance = m.group(2) export = m.group(1) # is it an NFS mount? if re.search('nfs(4)? statvers=', line): client = NFSCLIENT() client.export = self.chars(export) client.mountpoint = self.chars(instance) self.nfs[instance] = client else: instance = '' if instance == '': continue # opts m = re.match(r'\topts:\t(.*)$', line) if m: options = m.group(1) client.options.string = self.chars(options) for mountopt in options.split(','): mountopt = mountopt.rstrip() if mountopt == 'sync': client.options.sync = 1 continue if mountopt == 'noatime': client.options.atime = 0 continue if mountopt == 'nodirtime': client.options.diratime = 0 continue if mountopt == 'posix': client.options.posix = 1 continue if mountopt == 'nocto': client.options.cto = 0 continue if mountopt == 'noac': client.options.ac = 0 continue if mountopt == 'nolock': client.options.lock = 0 continue if mountopt == 'noacl': client.options.acl = 0 continue if mountopt == 'nordirplus': client.options.rdirplus = 0 continue if mountopt == 'nosharecache': client.options.sharecache = 0 continue if mountopt == 'noresvport': client.options.resvport = 0 continue if mountopt == 'fsc': client.options.fsc = 1 continue if mountopt == 'migration': client.options.migration = 1 continue m = re.match(r'(ro|rw)$', mountopt) if m: client.options.readmode = self.chars(m.group(1)) continue m = re.match(r'vers=([2-4](\.[0-9])?)$', mountopt) if m: client.options.vers = self.chars(m.group(1)) continue m = re.match(r'rsize=(\d+)$', mountopt) if m: client.options.rsize = self.longs(m.group(1)) continue m = re.match(r'wsize=(\d+)$', mountopt) if m: client.options.wsize = self.longs(m.group(1)) continue m = re.match(r'bsize=(\d+)$', mountopt) if m: client.options.bsize = self.longs(m.group(1)) continue m = re.match(r'namlen=(\d+)$', mountopt) if m: client.options.namlen = self.longs(m.group(1)) continue m = re.match(r'acregmin=(\d+)$', mountopt) if m: client.options.acregmin = self.longs(m.group(1)) continue m = re.match(r'acregmax=(\d+)$', mountopt) if m: client.options.acregmax = self.longs(m.group(1)) continue m = re.match(r'acdirmin=(\d+)$', mountopt) if m: client.options.acdirmin = self.longs(m.group(1)) continue m = re.match(r'acdirmax=(\d+)$', mountopt) if m: client.options.acdirmax = self.longs(m.group(1)) continue m = re.match(r'(soft|hard)$', mountopt) if m: client.options.recovery = self.chars(m.group(1)) continue m = re.match(r'proto=(tcp|udp|rdma)$', mountopt) if m: client.options.proto = self.chars(m.group(1)) continue m = re.match(r'port=(\d+)$', mountopt) if m: client.options.port = self.longs(m.group(1)) continue m = re.match(r'timeo=(\d+)$', mountopt) if m: client.options.timeo = self.longs(m.group(1)) continue m = re.match(r'retrans=(\d+)$', mountopt) if m: client.options.retrans = self.longs(m.group(1)) continue m = re.match(r'sec=(null|sys|krb5|krb5i|krb5p|lkey|lkeyi|lkeyp|spkm|spkmi|spkmp|unknown)$', mountopt) if m: client.options.sec = self.chars(m.group(1)) continue m = re.match(r'mountaddr=(.*)$', mountopt) # NFS3 only if m: client.options.mountaddr = self.chars(m.group(1)) continue m = re.match(r'mountvers=(\d+)$', mountopt) # NFS3 only if m: client.options.mountvers = self.longs(m.group(1)) continue m = re.match(r'mountport=(\d+)$', mountopt) # NFS3 only if m: client.options.mountport = self.longs(m.group(1)) continue m = re.match(r'mountproto=(udp|tcp|auto|udp6|tcp6)$', mountopt) # NFS3 only if m: client.options.mountproto = self.chars(m.group(1)) continue m = re.match(r'clientaddr=(.*)$', mountopt) # NFS3 only if m: client.options.clientaddr = self.chars(m.group(1)) continue m = re.match(r'lookupcache=(none|pos)$', mountopt) # NFS3 only if m: client.options.lookupcache = self.chars(m.group(1)) continue m = re.match(r'local_lock=(none|all|flock|posix)$', mountopt) # NFS3 only if m: client.options.local_lock = self.chars(m.group(1)) continue m = re.match(r'\tage:\t(\d+)$', line) if m: client.age = self.longs(m.group(1)) continue m = re.match(r'\tcaps:\t(.*)$', line) if m: client.capabilities = self.chars(m.group(1)) continue m = re.match(r'\tnfsv4:\t(.*)$', line) if m: client.nfsv4 = self.chars(m.group(1)) continue m = re.match(r'\tsec:\t(.*)$', line) if m: client.security = self.chars(m.group(1)) continue # events m = re.match(r'\tevents:\t(.*)$', line) if m: events = m.group(1).split(' ') client.events.inoderevalidate = self.longs(events[0]) client.events.dentryrevalidate = self.longs(events[1]) client.events.datainvalidate = self.longs(events[2]) client.events.attrinvalidate = self.longs(events[3]) client.events.vfsopen = self.longs(events[4]) client.events.vfslookup = self.longs(events[5]) client.events.vfsaccess = self.longs(events[6]) client.events.vfsupdatepage = self.longs(events[7]) client.events.vfsreadpage = self.longs(events[8]) client.events.vfsreadpages = self.longs(events[9]) client.events.vfswritepage = self.longs(events[10]) client.events.vfswritepages = self.longs(events[11]) client.events.vfsgetdents = self.longs(events[12]) client.events.vfssetattr = self.longs(events[13]) client.events.vfsflush = self.longs(events[14]) client.events.vfsfsync = self.longs(events[15]) client.events.vfslock = self.longs(events[16]) client.events.vfsrelease = self.longs(events[17]) client.events.congestionwait = self.longs(events[18]) client.events.setattrtrunc = self.longs(events[19]) client.events.extendwrite = self.longs(events[20]) client.events.sillyrename = self.longs(events[21]) client.events.shortread = self.longs(events[22]) client.events.shortwrite = self.longs(events[23]) client.events.delay = self.longs(events[24]) client.events.pnfsread = self.longs(events[25]) client.events.pnfswrite = self.longs(events[26]) continue # bytes m = re.match(r'\tbytes:\t(.*)$', line) if m: values = m.group(1).split(' ') client.bytes.read.normal = self.longs(values[0]) client.bytes.write.normal = self.longs(values[1]) client.bytes.read.direct = self.longs(values[2]) client.bytes.write.direct = self.longs(values[3]) client.bytes.read.server = self.longs(values[4]) client.bytes.write.server = self.longs(values[5]) client.pages.read = self.longs(values[6]) client.pages.write = self.longs(values[7]) # xprt m = re.match(r'\txprt:\t(.*)$', line) if m: values = m.group(1).split(' ') xprt_prot = values[0] if xprt_prot == 'tcp': client.xprt.srcport = self.chars(values[1]) client.xprt.bind_count = self.longs(values[2]) client.xprt.connect_count = self.longs(values[3]) client.xprt.connect_time = self.longs(values[4]) client.xprt.idle_time = self.longs(values[5]) client.xprt.sends = self.longs(values[6]) client.xprt.recvs = self.longs(values[7]) client.xprt.bad_xids = self.longs(values[8]) client.xprt.req_u = self.longs(values[9]) client.xprt.backlog_u = self.longs(values[10]) client.xprt.maxslots = self.longs(values[11]) client.xprt.sending_u = self.longs(values[12]) client.xprt.pending_u = self.longs(values[13]) elif xprt_prot == 'udp': client.xprt.srcport = self.chars(values[1]) client.xprt.bind_count = self.longs(values[2]) client.xprt.sends = self.longs(values[3]) client.xprt.recvs = self.longs(values[4]) client.xprt.bad_xids = self.longs(values[5]) client.xprt.req_u = self.longs(values[6]) client.xprt.backlog_u = self.longs(values[7]) client.xprt.maxslots = self.longs(values[8]) client.xprt.sending_u = self.longs(values[9]) client.xprt.pending_u = self.longs(values[10]) elif xprt_prot == 'rdma': client.xprt.srcport = self.chars(values[1]) client.xprt.bind_count = self.longs(values[2]) client.xprt.connect_count = self.longs(values[3]) client.xprt.connect_time = self.longs(values[4]) client.xprt.idle_time = self.longs(values[5]) client.xprt.sends = self.longs(values[6]) client.xprt.recvs = self.longs(values[7]) client.xprt.bad_xids = self.longs(values[8]) client.xprt.req_u = self.longs(values[9]) client.xprt.backlog_u = self.longs(values[10]) client.xprt.read_chunks = self.longs(values[11]) client.xprt.write_chunks = self.longs(values[12]) client.xprt.reply_chunks = self.longs(values[13]) client.xprt.total_rdma_req = self.longs(values[14]) client.xprt.total_rdma_rep = self.longs(values[15]) client.xprt.pullup = self.longs(values[16]) client.xprt.fixup = self.longs(values[17]) client.xprt.hardway = self.longs(values[18]) client.xprt.failed_marshal = self.longs(values[19]) client.xprt.bad_reply = self.longs(values[20]) client.xprt.nomsg_call = self.longs(values[21]) client.xprt.mrs_recycled = self.longs(values[22]) client.xprt.mrs_orphaned = self.longs(values[23]) client.xprt.mrs_allocated = self.longs(values[24]) client.xprt.local_inv_needed = self.longs(values[25]) client.xprt.empty_sendctxq = self.longs(values[26]) client.xprt.reply_waits = self.longs(values[27]) else: continue # per-operation statistics m = re.match(r'\tper-op statistics$', line) if m: # We do these a bit differently since they are not # all on the same line. Loop until we don't match # anymore. Note v4 ops can have underscore. while True: line = STATS.readline() if line == '': break m = re.match(r'\s*([A-Z_]*): (\d*) (\d*) (\d*) (\d*) (\d*) (\d*) (\d*) (\d*)$', line) if not m: break opname = m.group(1).lower() try: op = getattr(client.ops, opname) op.ops = self.longs(m.group(2)) op.ntrans = self.longs(m.group(3)) op.timeouts = self.longs(m.group(4)) op.bytes_sent = self.longs(m.group(5)) op.bytes_recv = self.longs(m.group(6)) op.queue = self.longs(m.group(7)) op.rtt = self.longs(m.group(8)) op.execute = self.longs(m.group(9)) op.errors = self.longs(m.group(10)) setattr(client.ops, opname, op) except: # self.log("Unrecognised op: %s" % (line)) pass # not yet implemented def nfsclient_instance(self, serial): """ Called once per "instance" PDU """ self.nfsclient_refresh() def nfsclient_fetch(self): """ Called once per "fetch" PDU, before callbacks """ self.nfs.clear() self.nfsclient_refresh() self.replace_indom(self.NFSCLIENT_INDOM, self.nfs) def nfsclient_fetch_callback(self, cluster, item, inst): """ Called for each instance of each fetched metric """ voidp = self.inst_lookup(self.NFSCLIENT_INDOM, inst) if voidp is None: return [c_api.PM_ERR_INST, 0] cache = cast(voidp, POINTER(NFSCLIENT)) metric = self.pmid_name_lookup(cluster, item) if metric is None: return [c_api.PM_ERR_PMID, 0] names = metric.split(".") names = names[1:] # cull self.NAME value = cache.contents try: for name in names: if value is None: raise KeyError(name) value = getattr(value, name) if value is None: raise KeyError(name) if isinstance(value, (bytes, text_type)): return [str(value.decode()), 1] return [value, 1] except KeyError: pass return [NOVAL, 0] def __init__(self, name, domain): """ NFS client PMDA constructor """ PMDA.__init__(self, name, domain) self.connect_pmcd() self.NAME = name # Check env variable for mountstats file to use self.MOUNTSTATS_PATH = os.getenv('NFSCLIENT_MOUNTSTATS_PATH', '/proc/self/mountstats') # general - indom 0 self.NFSCLIENT_INDOM = self.indom(0) self.add_indom(pmdaIndom(self.NFSCLIENT_INDOM, self.nfs), 'NFS client mount points', '') # general - cluster 0 self.add_metric(name + '.export', pmdaMetric(self.pmid(0, 1), c_api.PM_TYPE_STRING, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'Export', '') self.add_metric(name + '.mountpoint', pmdaMetric(self.pmid(0, 2), c_api.PM_TYPE_STRING, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'Mount Point', '') # opts - cluster 1 # # Options in order: # NULL is not present # from super.c in nfs_show_mount_options # # ro | rw # sync | NULL # noatime | NULL # nodiratime | NULL # vers=([2-4](\.[0-9])?) # rsize=%u # wsize=%u # bsize=%u | NULL # namlen=%u # acregmin=%u | NULL # acregmax=%u | NULL # acdirmin=%u | NULL # acdirmax=%u | NULL # soft | hard # posix | NULL # nocto | NULL # noac | NULL # nolock | NULL # noacl | NULL # nordirplus | NULL # nosharecache | NULL # noresvport | NULL # proto=(tcp|udp|rdma) # port=%u | NULL # timeo=%lu # retrans=%u # sec=(null|sys|krb5|krb5i|krb5p|lkey|lkeyi|lkeyp|spkm|spkmi|spkmp|unknown) # null and unknown are those exact literal strings # NFS 3 only # Below block may not exist at all if NFS_MOUNT_LEGACY_INTERFACE # mountaddr="ip4addr|ip6addr|unspecified" # mountvers=%u | NULL # mountport=%u | NULL # mountproto=(udp|tcp|auto|udp6|tcp6|NULL) # NULL if showdefaults is false # NFS 4 Only # clientaddr=(ip4address|ip6address) # fsc | NULL # migration | NULL # lookupcache=(none|pos) | NULL # local_lock=(none|all|flock|posix) self.add_metric(name + '.options.string', pmdaMetric(self.pmid(1, 1), c_api.PM_TYPE_STRING, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'Full Options String', '') self.add_metric(name + '.options.readmode', pmdaMetric(self.pmid(1, 2), c_api.PM_TYPE_STRING, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'rw or ro mount mode', '') self.add_metric(name + '.options.sync', pmdaMetric(self.pmid(1, 3), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'boolean sync mount option used', '') self.add_metric(name + '.options.atime', pmdaMetric(self.pmid(1, 4), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'boolean atime mount option used', '') self.add_metric(name + '.options.diratime', pmdaMetric(self.pmid(1, 5), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'boolean diratime mount option used', '') self.add_metric(name + '.options.vers', pmdaMetric(self.pmid(1, 6), c_api.PM_TYPE_STRING, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'nfs version', '') self.add_metric(name + '.options.rsize', pmdaMetric(self.pmid(1, 7), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'rsize mount parameter', '') self.add_metric(name + '.options.wsize', pmdaMetric(self.pmid(1, 8), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'wsize mount parameter', '') self.add_metric(name + '.options.bsize', pmdaMetric(self.pmid(1, 9), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'bsize mount parameter', '') self.add_metric(name + '.options.namlen', pmdaMetric(self.pmid(1, 10), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'namlen mount parameter', '') self.add_metric(name + '.options.acregmin', pmdaMetric(self.pmid(1, 11), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'acregmin mount parameter', '') self.add_metric(name + '.options.acregmax', pmdaMetric(self.pmid(1, 12), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'acregmax mount parameter', '') self.add_metric(name + '.options.acdirmin', pmdaMetric(self.pmid(1, 13), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'acdirmin mount parameter', '') self.add_metric(name + '.options.acdirmax', pmdaMetric(self.pmid(1, 14), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'acdirmax mount parameter', '') self.add_metric(name + '.options.recovery', pmdaMetric(self.pmid(1, 15), c_api.PM_TYPE_STRING, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'hard or soft recovery behavior', '') self.add_metric(name + '.options.posix', pmdaMetric(self.pmid(1, 16), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'boolean posix mount option used', '') self.add_metric(name + '.options.cto', pmdaMetric(self.pmid(1, 17), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'boolean cto mount option used', '') self.add_metric(name + '.options.ac', pmdaMetric(self.pmid(1, 18), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'boolean ac mount option used', '') self.add_metric(name + '.options.lock', pmdaMetric(self.pmid(1, 19), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'boolean lock mount option used', '') self.add_metric(name + '.options.acl', pmdaMetric(self.pmid(1, 20), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'boolean acl mount option used', '') self.add_metric(name + '.options.rdirplus', pmdaMetric(self.pmid(1, 21), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'boolean rdirplus mount option used', '') self.add_metric(name + '.options.sharecache', pmdaMetric(self.pmid(1, 22), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'boolean sharecache mount option used', '') self.add_metric(name + '.options.resvport', pmdaMetric(self.pmid(1, 23), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'boolean resvport mount option used', '') self.add_metric(name + '.options.proto', pmdaMetric(self.pmid(1, 24), c_api.PM_TYPE_STRING, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'nfs protocol: udp|tcp|rdma', '') self.add_metric(name + '.options.port', pmdaMetric(self.pmid(1, 25), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'port mount parameter', '') self.add_metric(name + '.options.timeo', pmdaMetric(self.pmid(1, 26), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'timeo mount parameter', '') self.add_metric(name + '.options.retrans', pmdaMetric(self.pmid(1, 27), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'retrans mount parameter', '') self.add_metric(name + '.options.sec', pmdaMetric(self.pmid(1, 28), c_api.PM_TYPE_STRING, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'sec mount parameter', '') # NFS3 only self.add_metric(name + '.options.mountaddr', pmdaMetric(self.pmid(1, 29), c_api.PM_TYPE_STRING, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'mountaddr mount parameter', '') self.add_metric(name + '.options.mountvers', pmdaMetric(self.pmid(1, 30), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'mountvers mount parameter', '') self.add_metric(name + '.options.mountport', pmdaMetric(self.pmid(1, 31), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'mountport mount parameter', '') self.add_metric(name + '.options.mountproto', pmdaMetric(self.pmid(1, 32), c_api.PM_TYPE_STRING, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'mountproto mount parameter', '') # NFS4 only self.add_metric(name + '.options.clientaddr', pmdaMetric(self.pmid(1, 33), c_api.PM_TYPE_STRING, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'clientaddr mount parameter', '') # All self.add_metric(name + '.options.fsc', pmdaMetric(self.pmid(1, 34), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'fsc mount parameter', '') self.add_metric(name + '.options.migration', pmdaMetric(self.pmid(1, 35), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'migration mount parameter', '') self.add_metric(name + '.options.lookupcache', pmdaMetric(self.pmid(1, 36), c_api.PM_TYPE_STRING, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'lookupcache mount parameter', '') self.add_metric(name + '.options.local_lock', pmdaMetric(self.pmid(1, 37), c_api.PM_TYPE_STRING, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'local_lock mount parameter', '') # age - cluster 2 self.add_metric(name + '.age', pmdaMetric(self.pmid(2, 1), PMDA_ULONG, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_SECS), 'Age in Seconds', '') # caps - cluster 3 - Any use in parsing this ? self.add_metric(name + '.capabilities', pmdaMetric(self.pmid(3, 1), c_api.PM_TYPE_STRING, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'Capabilities', '') self.add_metric(name + '.nfsv4', pmdaMetric(self.pmid(3, 2), c_api.PM_TYPE_STRING, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'NFSv4 supported attributes', '') # sec - cluster 4 - Any use in parsing this ? self.add_metric(name + '.security', pmdaMetric(self.pmid(4, 1), c_api.PM_TYPE_STRING, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'Security Flavor', '') # events - cluster 5 self.add_metric(name + '.events.inoderevalidate', pmdaMetric(self.pmid(5, 1), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_INODEREVALIDATE', 'Incremented in __nfs_revalidate_inode whenever an inode is revalidated') self.add_metric(name + '.events.dentryrevalidate', pmdaMetric(self.pmid(5, 2), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_DENTRYREVALIDATE', 'Incremented in nfs_lookup_revalidate whenever a dentry is revalidated') self.add_metric(name + '.events.datainvalidate', pmdaMetric(self.pmid(5, 3), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_DATAINVALIDATE', "Incremented in nfs_invalidate_mapping_nolock when data cache for an inode\n" + 'is invalidated') self.add_metric(name + '.events.attrinvalidate', pmdaMetric(self.pmid(5, 4), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_ATTRINVALIDATE', "Incremented in nfs_zap_caches_locked and nfs_update_inode when an the\n" + 'attribute cache for an inode has been invalidated') self.add_metric(name + '.events.vfsopen', pmdaMetric(self.pmid(5, 5), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_VFSOPEN', "Incremented in nfs_file_open and nfs_opendir whenever a file or directory\n" + "is opened\n") self.add_metric(name + '.events.vfslookup', pmdaMetric(self.pmid(5, 6), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_VFSLOOKUP', 'Incremented in nfs_lookup on every lookup') self.add_metric(name + '.events.vfsaccess', pmdaMetric(self.pmid(5, 7), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_VFSACCESS', 'Incremented in nfs_permission on every access check') self.add_metric(name + '.events.vfsupdatepage', pmdaMetric(self.pmid(5, 8), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_VFSUPDATEPAGE', "Incremented in nfs_updatepage when updating and possibly writing a cached\n" + "page of an NFS file.\n") self.add_metric(name + '.events.vfsreadpage', pmdaMetric(self.pmid(5, 9), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_VFSREADPAGE', 'Incremented in nfs_readpage when reading a single page over NFS') self.add_metric(name + '.events.vfsreadpages', pmdaMetric(self.pmid(5, 10), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_VFSREADPAGES', 'Incremented in nfs_readpages when reading a page range over NFS') self.add_metric(name + '.events.vfswritepage', pmdaMetric(self.pmid(5, 11), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_VFSWRITEPAGE', 'Incremented in nfs_writepage_locked writing a mmapped page to the server') self.add_metric(name + '.events.vfswritepages', pmdaMetric(self.pmid(5, 12), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_VFSWRITEPAGES', 'Incremented in nfs_writepages to write pages under VM writeback control') self.add_metric(name + '.events.vfsgetdents', pmdaMetric(self.pmid(5, 13), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_VFSGETDENTS', 'Incremented in nfs_readdir when reading directory entries') self.add_metric(name + '.events.vfssetattr', pmdaMetric(self.pmid(5, 14), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_VFSSETATTR', 'Incremented in nfs_setattr when changing attributes (metadata) of an inode') self.add_metric(name + '.events.vfsflush', pmdaMetric(self.pmid(5, 15), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_VFSFLUSH', 'Incremented in nfs_file_flush when flushing dirty pages') self.add_metric(name + '.events.vfsfsync', pmdaMetric(self.pmid(5, 16), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_VFSFSYNC', 'Incremented in nfs_file_fsync_commit when flushing dirty pages for a process') self.add_metric(name + '.events.vfslock', pmdaMetric(self.pmid(5, 17), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_VFSLOCK', 'Incremented in nfs_lock when locking a (portion of a) file') self.add_metric(name + '.events.vfsrelease', pmdaMetric(self.pmid(5, 18), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_VFSRELEASE', 'Incremented in nfs_file_release when closing and releasing a file') self.add_metric(name + '.events.congestionwait', pmdaMetric(self.pmid(5, 19), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'No longer used in the kernel', '') self.add_metric(name + '.events.setattrtrunc', pmdaMetric(self.pmid(5, 20), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_SETATTRTRUNC', "Incremented in nfs_setattr_update_inode when updating inode size after a\n" + "setattr call (e.g. from a truncate(2) userspace operation)") self.add_metric(name + '.events.extendwrite', pmdaMetric(self.pmid(5, 21), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_EXTENDWRITE', "Incremented in nfs_grow_file when adjusting file length if writing beyond\n" + "the end") self.add_metric(name + '.events.sillyrename', pmdaMetric(self.pmid(5, 22), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_SILLYRENAME', "Incremented in nfs_sillyrename to handle rename of unlinked, open files.\n" + "NFSv2/3 is stateless and the server doesn't know when the client is\n" + "holding a file open. To prevent application problems when a file is\n" + "unlinked while it's still open, the client performs a \"silly-rename\".\n" + "That is, it renames the file to a hidden file in the same directory,\n" + "and only performs the unlink once the last reference to it is put.") self.add_metric(name + '.events.shortread', pmdaMetric(self.pmid(5, 23), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_SHORTREAD', "Incremented in nfs_readpage_retry to handle the situation where the NFS\n" + "server initially read only part of a requested range - the read may be\n" + "retried at the end of the header") self.add_metric(name + '.events.shortwrite', pmdaMetric(self.pmid(5, 24), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_SHORTWRITE', "Incremented in nfs_writeback_result when a server write result indicates\n" + "server initially wrote only part of a requested range - the write may be\n" + "continued where the server left off, but may also be resent as a stable\n" + "(sync) write in order to avoid headaches in the case of a server crash.") self.add_metric(name + '.events.delay', pmdaMetric(self.pmid(5, 25), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_DELAY', 'Incremented in nfs3_async_handle_jukebox or nfs4_do_handle_exception') self.add_metric(name + '.events.pnfsread', pmdaMetric(self.pmid(5, 26), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_PNFS_READ', "Incremented in pnfs_try_to_read_data when calling through the parallel\n" + "I/O subsystem read function.") self.add_metric(name + '.events.pnfswrite', pmdaMetric(self.pmid(5, 27), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_PNFS_WRITE', "Incremented in pnfs_try_to_write_data when calling through the parallel\n" + "I/O subsystem write function.") # bytes - cluster 6 self.add_metric(name + '.bytes.read.normal', pmdaMetric(self.pmid(6, 1), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_BYTES), 'NFSIOS_NORMALREADBYTES', "The number of bytes read by applications via the read system call\n" + 'interface') self.add_metric(name + '.bytes.write.normal', pmdaMetric(self.pmid(6, 2), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_BYTES), 'NFSIOS_NORMALWRITTENBYTES', "The number of bytes written by applications via the write system call\n" + 'interface') self.add_metric(name + '.bytes.read.direct', pmdaMetric(self.pmid(6, 3), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_BYTES), 'NFSIOS_DIRECTREADBYTES', "The number of bytes read by applications from files opened with the\n" + 'O_DIRECT flag') self.add_metric(name + '.bytes.write.direct', pmdaMetric(self.pmid(6, 4), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_BYTES), 'NFSIOS_DIRECTWRITTENBYTES', "The number of bytes written by applications to files opened with the\n" + 'O_DIRECT flag') self.add_metric(name + '.bytes.read.server', pmdaMetric(self.pmid(6, 5), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_BYTES), 'NFSIOS_SERVERREADBYTES', "The number of bytes read from the nfs server by the nfs client via nfs\n" + 'read requests') self.add_metric(name + '.bytes.write.server', pmdaMetric(self.pmid(6, 6), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_BYTES), 'NFSIOS_SERVERWRITTENBYTES', "The number of bytes written to the nfs server by the nfs client via nfs\n" + 'write requests') self.add_metric(name + '.pages.read', pmdaMetric(self.pmid(6, 7), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_READPAGES', 'The number of pages read via nfs_readpage or nfs_readpages') self.add_metric(name + '.pages.write', pmdaMetric(self.pmid(6, 8), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'NFSIOS_WRITEPAGES', 'The number of pages written via nfs_writepage or nfs_writepages') # xprt - cluster 7 # # We have three possible transports: udp, tcp, rdma. # Fill in 0 for ones that dont apply. Types for RDMA from net/sunrpc/xprtrdma/transport.c : xprt_rdma_print_stats # self.add_metric(name + '.xprt.srcport', pmdaMetric(self.pmid(7, 1), c_api.PM_TYPE_STRING, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_NONE), 'tcp source port', 'Source port on the client') self.add_metric(name + '.xprt.bind_count', pmdaMetric(self.pmid(7, 2), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'count of rpcbind get_port calls', "Incremented in rpcb_getport_async: \"obtain the port for a given RPC\n" + 'service on a given host\"') self.add_metric(name + '.xprt.connect_count', pmdaMetric(self.pmid(7, 3), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'count of tcp connects', "Incremented in xs_tcp_finish_connecting and xprt_connect_status.\n" + "This is a count of the number of tcp (re)connects (one of which is\n" + 'active at a time) for this mount.') self.add_metric(name + '.xprt.connect_time', pmdaMetric(self.pmid(7, 4), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'jiffies waiting for connect', "Summed in xprt_connect_status, it is stored and printed in jiffies.\n" + "This is the sum of all connection attempts: how long was spent waiting\n" + 'to connect.') self.add_metric(name + '.xprt.idle_time', pmdaMetric(self.pmid(7, 5), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_INSTANT, UNITS_SECS), 'transport idle time', "How long has it been since the transport has been used.\n" + 'Stored and calculated in jiffies and reported in seconds.') self.add_metric(name + '.xprt.sends', pmdaMetric(self.pmid(7, 6), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'count of tcp transmits', 'Incremented in xprt_transmit upon transmit completion of each rpc.') self.add_metric(name + '.xprt.recvs', pmdaMetric(self.pmid(7, 7), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'count of tcp receives', 'Incremented in xprt_complete_rqst when reply processing is complete.') self.add_metric(name + '.xprt.bad_xids', pmdaMetric(self.pmid(7, 8), c_api.PM_TYPE_U32, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'count of bad transaction identifiers', "When processing an rpc reply it is necessary to look up the original\n" + "rpc_rqst using xprt_lookup_rqst. If the rpc_rqst that prompted the\n" + 'reply cannot be found on the transport then bad_xids is incremented.') self.add_metric(name + '.xprt.req_u', pmdaMetric(self.pmid(7, 9), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'average requests on the wire', "The comment in struct stat says: \"average requests on the wire\",\n" + "but the actual calculation in xprt_transmit is:\n" + " xprt->stat.req_u += xprt->stat.sends - xprt->stat.recvs;\n" + 'The kernel-exported value for this metric may be broken.') self.add_metric(name + '.xprt.backlog_u', pmdaMetric(self.pmid(7, 10), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'backlog queue utilization', "This is the calculation in xprt_transmit:\n" + " xprt->stat.bklog_u += xprt->backlog.qlen;\n" + "qlen is incremented in __rpc_add_wait_queue and decremented in\n" + '__rpc_remove_wait_queue.') self.add_metric(name + '.xprt.read_chunks', pmdaMetric(self.pmid(7, 11), PMDA_ULONG, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA read chunk count', '') self.add_metric(name + '.xprt.write_chunks', pmdaMetric(self.pmid(7, 12), PMDA_ULONG, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA write chunk count', '') self.add_metric(name + '.xprt.reply_chunks', pmdaMetric(self.pmid(7, 13), PMDA_ULONG, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA reply chunk count', '') self.add_metric(name + '.xprt.total_rdma_req', pmdaMetric(self.pmid(7, 14), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA total requests', '') self.add_metric(name + '.xprt.total_rdma_rep', pmdaMetric(self.pmid(7, 15), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA total replies', '') self.add_metric(name + '.xprt.pullup', pmdaMetric(self.pmid(7, 16), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA pullup copy count', '') self.add_metric(name + '.xprt.fixup', pmdaMetric(self.pmid(7, 17), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA fixup copy count', '') self.add_metric(name + '.xprt.hardway', pmdaMetric(self.pmid(7, 18), PMDA_ULONG, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA hardway register count', '') self.add_metric(name + '.xprt.failed_marshal', pmdaMetric(self.pmid(7, 19), PMDA_ULONG, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA failed marshal count', '') self.add_metric(name + '.xprt.bad_reply', pmdaMetric(self.pmid(7, 20), PMDA_ULONG, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA bad reply count', '') self.add_metric(name + '.xprt.max_slots', pmdaMetric(self.pmid(7, 21), PMDA_ULONG, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA max RPC slots used', '') self.add_metric(name + '.xprt.sending_u', pmdaMetric(self.pmid(7, 22), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA send queue utilization', '') self.add_metric(name + '.xprt.pending_u', pmdaMetric(self.pmid(7, 23), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA pending queue utilization', '') self.add_metric(name + '.xprt.nomsg_call', pmdaMetric(self.pmid(7, 24), PMDA_ULONG, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA no msg count', '') self.add_metric(name + '.xprt.mrs_recycled', pmdaMetric(self.pmid(7, 25), PMDA_ULONG, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA MRs recycled', '') self.add_metric(name + '.xprt.mrs_orphaned', pmdaMetric(self.pmid(7, 26), PMDA_ULONG, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA MRs orphaned', '') self.add_metric(name + '.xprt.mrs_allocated', pmdaMetric(self.pmid(7, 27), PMDA_ULONG, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA MRs allocated', '') self.add_metric(name + '.xprt.local_inv_needed', pmdaMetric(self.pmid(7, 28), PMDA_ULONG, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA LOCAL_INV requests', '') self.add_metric(name + '.xprt.empty_sendctxq', pmdaMetric(self.pmid(7, 29), PMDA_ULONG, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA Send Queue empty', '') self.add_metric(name + '.xprt.reply_waits', pmdaMetric(self.pmid(7, 30), PMDA_ULONG, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'RPCRDMA reply waits for send', '') # ops - cluster 8 # # Different versions have different operations. We just list them all and # fill in only what is appropriate for this version. Non-matching ones will # return no-value from fetch. opitem = 1 for opname in COMMON_OPS + V3ONLY_OPS + V4_OPS + V41_OPS + V42_OPS: self.add_metric(name + '.ops.' + opname + '.ops', pmdaMetric(self.pmid(8, opitem), PMDA_ULONG, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'count of operations', 'RPC count for this op, only bumped in rpc_count_iostats') opitem += 1 self.add_metric(name + '.ops.' + opname + '.ntrans', pmdaMetric(self.pmid(8, opitem), PMDA_ULONG, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'count of transmissions', 'There can be more than one transmission per rpc') opitem += 1 self.add_metric(name + '.ops.' + opname + '.timeouts', pmdaMetric(self.pmid(8, opitem), PMDA_ULONG, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'count of major timeouts', '') opitem += 1 self.add_metric(name + '.ops.' + opname + '.bytes_sent', pmdaMetric(self.pmid(8, opitem), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_BYTES), 'count of bytes out', "How many bytes are sent for this procedure type. This indicates how much\n" + "load this procedure puts on the network. It includes the RPC and ULP\n" + 'headers, and the request payload') opitem += 1 self.add_metric(name + '.ops.' + opname + '.bytes_recv', pmdaMetric(self.pmid(8, opitem), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_BYTES), 'count of bytes in', "How many bytes are received for this procedure type. This indicates how\n" + "much load this procedure is putting on the network. It includes RPC and\n" + 'ULP headers, and the request payload') opitem += 1 self.add_metric(name + '.ops.' + opname + '.queue', pmdaMetric(self.pmid(8, opitem), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_MSECS), 'milliseconds queued for transmit', "The length of time an RPC request waits in queue before transmission in\n" + 'milliseconds.') opitem += 1 self.add_metric(name + '.ops.' + opname + '.rtt', pmdaMetric(self.pmid(8, opitem), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_MSECS), 'milliseconds for rpc round trip time', 'The network + server latency of the request in milliseconds.') opitem += 1 self.add_metric(name + '.ops.' + opname + '.execute', pmdaMetric(self.pmid(8, opitem), c_api.PM_TYPE_U64, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_MSECS), 'milliseconds for rpc execution', 'The total time the request spent from init to release in milliseconds.') opitem += 1 self.add_metric(name + '.ops.' + opname + '.errors', pmdaMetric(self.pmid(8, opitem), PMDA_ULONG, self.NFSCLIENT_INDOM, c_api.PM_SEM_COUNTER, UNITS_COUNT), 'count of completions with an error', 'Cumulative count of rpc operations completing with errors') opitem += 1 self.nfsclient_refresh() self.set_fetch(self.nfsclient_fetch) self.set_instance(self.nfsclient_instance) self.set_fetch_callback(self.nfsclient_fetch_callback) if __name__ == "__main__": NFSCLIENTPMDA("nfsclient", 62).run()