All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V3 1/2] nfs-utils: add new tool nfsdclts to parse output from proc files
@ 2020-05-18 14:10 Steve Dickson
  2020-05-18 14:10 ` [PATCH V2 2/2] nfsdclnts: add man page for tool nfsdclnts Steve Dickson
  2020-05-18 17:14 ` [PATCH V3 1/2] nfs-utils: add new tool nfsdclts to parse output from proc files Steve Dickson
  0 siblings, 2 replies; 4+ messages in thread
From: Steve Dickson @ 2020-05-18 14:10 UTC (permalink / raw)
  To: Linux NFS Mailing list

From: Achilles Gaikwad <agaikwad@redhat.com>

This tool parses the output from the following files

 /proc/fs/nfsd/clients/*/{states,info}

Signed-off-by: Achilles Gaikwad <agaikwad@redhat.com>
Signed-off-by: Kenneth D'souza <kdsouza@redhat.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
---
 configure.ac                 |   1 +
 tools/Makefile.am            |   2 +-
 tools/nfsdclnts/Makefile.am  |   9 ++
 tools/nfsdclnts/nfsdclnts.py | 221 +++++++++++++++++++++++++++++++++++
 4 files changed, 232 insertions(+), 1 deletion(-)
 create mode 100644 tools/nfsdclnts/Makefile.am
 create mode 100755 tools/nfsdclnts/nfsdclnts.py

v3: rename command to nfsdclnts 

diff --git a/configure.ac b/configure.ac
index 0b1c8cc..9631278 100644
--- a/configure.ac
+++ b/configure.ac
@@ -694,6 +694,7 @@ AC_CONFIG_FILES([
 	tools/rpcgen/Makefile
 	tools/mountstats/Makefile
 	tools/nfs-iostat/Makefile
+	tools/nfsdclnts/Makefile
 	tools/nfsconf/Makefile
 	tools/nfsdclddb/Makefile
 	utils/Makefile
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 432d35d..9b4b080 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -12,6 +12,6 @@ if CONFIG_NFSDCLD
 OPTDIRS += nfsdclddb
 endif
 
-SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat $(OPTDIRS)
+SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat nfsdclnts $(OPTDIRS)
 
 MAINTAINERCLEANFILES = Makefile.in
diff --git a/tools/nfsdclnts/Makefile.am b/tools/nfsdclnts/Makefile.am
new file mode 100644
index 0000000..c1f12a0
--- /dev/null
+++ b/tools/nfsdclnts/Makefile.am
@@ -0,0 +1,9 @@
+## Process this file with automake to produce Makefile.in
+PYTHON_FILES = nfsdclnts.py
+
+all-local: $(PYTHON_FILES)
+
+install-data-hook:
+	$(INSTALL) -m 755 nfsdclnts.py $(DESTDIR)$(sbindir)/nfsdclnts
+
+MAINTAINERCLEANFILES=Makefile.in
diff --git a/tools/nfsdclnts/nfsdclnts.py b/tools/nfsdclnts/nfsdclnts.py
new file mode 100755
index 0000000..2d0ad0a
--- /dev/null
+++ b/tools/nfsdclnts/nfsdclnts.py
@@ -0,0 +1,221 @@
+#!/bin/python3
+# -*- python-mode -*-
+'''
+    Copyright (C) 2020
+    Authors:    Achilles Gaikwad <agaikwad@redhat.com>
+                Kenneth  D'souza <kdsouza@redhat.com>
+
+    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 3 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.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+'''
+
+import multiprocessing as mp
+import os
+import signal
+import sys
+
+try:
+    import argparse
+except ImportError:
+    print('%s:  Failed to import argparse - make sure argparse is installed!'
+        % sys.argv[0])
+    sys.exit(1)
+try:
+    import yaml
+except ImportError:
+    print('%s:  Failed to import yaml - make sure python3-pyyaml is installed!'
+        % sys.argv[0])
+    sys.exit(1)
+
+BBOLD = '\033[1;30;47m' #Bold black text with white background.
+ENDC = '\033[m' #Rest to defaults
+
+def init_worker():
+    signal.signal(signal.SIGINT, signal.SIG_IGN)
+
+# this function converts the info file to a dictionary format, sorta. 
+def file_to_dict(path):
+    client_info = {}
+    with open(path) as f:
+        for line in f:
+            try:
+                (key, val) = line.split(':')
+                client_info[key] = val
+    # FIXME: There has to be a better way of converting the info file to a dictionary.
+            except ValueError:
+                try:
+                    (key, val) = line.split()
+                    client_info[key] = val
+                except:
+                    pass
+    return client_info
+
+# this function gets the paths from /proc/fs/nfsd/clients/
+# returns a list of paths for each client which has nfs-share mounted.
+def getpaths():
+    path = []
+    try:
+        dirs = os.listdir('/proc/fs/nfsd/clients/')
+    except OSError as reason:
+        exit('%s' % reason)
+    if len(dirs) !=0:
+	    for i in dirs:
+		    path.append('/proc/fs/nfsd/clients/' + i + '/states')
+	    return (path)
+    else:
+	    exit('Nothing to process')
+
+# A single function to rule them all, in this function we gather all the data
+# from already populated data_list and client_info.
+def printer(data_list, argument):
+    client_info_path = data_list.pop()
+    client_info = file_to_dict(client_info_path)
+    for i in data_list:
+        for key in i:
+            inode = i[key]['superblock'].split(':')[-1]
+            # get the ip address from client_info as 'address:' note the extra
+            # ':' as a suffix to address. If there is a better way to convert
+            # the file to dictionary, please change the following value too.
+            client_ip = client_info['address:']
+            # The ip address is quoted, so we dequote it.
+            client_ip = client_ip[1:-1]
+            try:
+                # if the nfs-server reboots while the nfs-client holds the files open,
+                # the nfs-server would print the filename as '/'. For such instaces we
+                # print the output as disconnected dentry instead of '/'.
+                if(i[key]['filename']=='/'):
+                    fname = 'disconnected dentry'
+                else:
+                    fname = i[key]['filename'].split('/')[-1]
+            except KeyError:
+                # for older kernels which do not have the fname patch in kernel, they
+                # won't be able to see the fname field. Therefore post it as N/A.
+                fname = "N/A"
+            otype = i[key]['type']
+            try:
+                access = i[key]['access']
+            except:
+                access = ''
+            try:
+                deny = i[key]['deny']
+            except:
+                deny = ''
+            hostname = client_info['name'].split()[-1].split('"')[0]
+            hostname =  hostname.split('.')[0]
+            otype = i[key]['type']
+            # if the hostname is too long, it messes up with the output being in columns,
+            # therefore we truncate the hostname followed by two '..' as suffix.
+            if len(hostname) > 20:
+                hostname = hostname[0:20] + '..'
+            clientid = client_info['clientid'].strip()
+            minorversion = client_info['minor version'].rstrip().rsplit()[0]
+            # since some fields do not have deny column, we drop those if -t is either
+            # layout or lock.
+            drop = ['layout', 'lock']
+
+            # Printing the output this way instead of a single string which is concatenated
+            # this makes it better to quickly add more columns in future.
+            if(otype == argument.type or  argument.type == 'all'):
+                print('%-13s' %inode, end='| ')
+                print('%-7s' %otype, end='| ')
+                if (argument.type not in drop):
+                    print('%-7s' %access, end='| ')
+                if (argument.type not in drop and argument.type !='deleg'):
+                    print('%-5s' %deny, end='| ')
+                if (argument.hostname == True):
+                    print('%-22s' %hostname, end='| ')
+                else:
+                   print('%-22s' %client_ip, end='| ')
+                if (argument.clientinfo == True) :
+                    print('%-20s' %clientid, end='| ')
+                    print('4.%-3s' %minorversion, end='| ')
+                print(fname)
+
+def opener(path):
+    try:
+        with open(path, 'r') as nfsdata:
+            data = yaml.load(nfsdata, Loader = yaml.BaseLoader)
+            if data is not None:
+                clientinfo = path.rsplit('/', 1)[0] + '/info'
+                data.append(clientinfo)
+                return data
+
+    except OSError as reason:
+        print('%s' % reason)
+
+def print_cols(argument):
+    title_inode = 'Inode number'
+    title_otype = 'Type'
+    title_access = 'Access'
+    title_deny = 'Deny'
+    title_fname = 'Filename'
+    title_clientID = 'Client ID'
+    title_hostname = 'Hostname'
+    title_ip = 'ip address'
+    title_nfsvers = 'vers'
+
+    drop = ['lock', 'layout']
+    print(BBOLD, end='')
+    print('%-13s' %title_inode, end='| ')
+    print('%-7s' %title_otype, end='| ')
+    if (argument.type not in drop):
+        print('%-7s' %title_access, end='| ')
+    if (argument.type not in drop and argument.type !='deleg'):
+        print('%-5s' %title_deny, end='| ')
+    if (argument.hostname == True):
+        print('%-22s' %title_hostname, end='| ')
+    else:
+        print('%-22s' %title_ip, end='| ')
+    if (argument.clientinfo == True):
+        print('%-20s' %title_clientID, end='| ')
+        print('%-5s' %title_nfsvers, end='| ')
+    print(title_fname, end='')
+    print(ENDC)
+
+def nfsd4_show():
+
+    parser = argparse.ArgumentParser(description = 'Parse the nfsd states and clientinfo files.')
+    parser.add_argument('-t', '--type', metavar = 'type', type = str, choices = ['open',
+        'deleg', 'lock', 'layout', 'all'],
+        default = 'all',
+        help = 'Input the type that you want to be printed: open, lock, deleg, layout, all')
+    parser.add_argument('--clientinfo', action = 'store_true',
+        help = 'output clients information, --hostname is implied.')
+    parser.add_argument('--hostname', action = 'store_true',
+        help = 'print hostname of client instead of its ip address. Longer hostnames are truncated.')
+    parser.add_argument('-q', '--quiet', action = 'store_true',
+        help = 'don\'t print the header information')
+
+    args = parser.parse_args()
+    paths = getpaths()
+    p = mp.Pool(mp.cpu_count(), init_worker)
+    try:
+        result = p.map(opener, paths)
+        ### Drop None entries from list
+        final_result = list(filter(None, result))
+        p.close()
+        p.join()
+
+        if len(final_result) !=0 and not args.quiet:
+            print_cols(args)
+
+        for item in final_result:
+            printer(item, args)
+
+    except KeyboardInterrupt:
+        print('Caught KeyboardInterrupt, terminating workers')
+        p.terminate()
+        p.join()
+
+if __name__ == "__main__":
+    nfsd4_show()
-- 
2.24.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH V2 2/2] nfsdclnts: add man page for tool nfsdclnts
  2020-05-18 14:10 [PATCH V3 1/2] nfs-utils: add new tool nfsdclts to parse output from proc files Steve Dickson
@ 2020-05-18 14:10 ` Steve Dickson
  2020-05-18 17:14   ` Steve Dickson
  2020-05-18 17:14 ` [PATCH V3 1/2] nfs-utils: add new tool nfsdclts to parse output from proc files Steve Dickson
  1 sibling, 1 reply; 4+ messages in thread
From: Steve Dickson @ 2020-05-18 14:10 UTC (permalink / raw)
  To: Linux NFS Mailing list

From: Achilles Gaikwad <agaikwad@redhat.com>

This patch adds man page for the tool nfsdclnts.

Signed-off-by: Achilles Gaikwad <agaikwad@redhat.com>
Signed-off-by: Kenneth D'souza <kdsouza@redhat.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
---
 tools/nfsdclnts/Makefile.am   |   4 +
 tools/nfsdclnts/nfsdclnts.man | 156 ++++++++++++++++++++++++++++++++++
 2 files changed, 160 insertions(+)
 create mode 100644 tools/nfsdclnts/nfsdclnts.man

v2: Renamed command to nfsdclnts

diff --git a/tools/nfsdclnts/Makefile.am b/tools/nfsdclnts/Makefile.am
index c1f12a0..d513edb 100644
--- a/tools/nfsdclnts/Makefile.am
+++ b/tools/nfsdclnts/Makefile.am
@@ -1,6 +1,10 @@
 ## Process this file with automake to produce Makefile.in
 PYTHON_FILES = nfsdclnts.py
 
+man8_MANS       = nfsdclnts.man
+
+EXTRA_DIST      = $(man8_MANS) $(PYTHON_FILES)
+
 all-local: $(PYTHON_FILES)
 
 install-data-hook:
diff --git a/tools/nfsdclnts/nfsdclnts.man b/tools/nfsdclnts/nfsdclnts.man
new file mode 100644
index 0000000..3701de9
--- /dev/null
+++ b/tools/nfsdclnts/nfsdclnts.man
@@ -0,0 +1,156 @@
+.\"
+.\" nfsdclnts(8)
+.\"
+.TH "NFSDCLTS" "8" "2020-05-09" "nfsdclnts" "nfsdclnts"
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.ss \n[.ss] 0
+.nh
+.ad l
+.de URL
+\fI\\$2\fP <\\$1>\\$3
+..
+.als MTO URL
+.if \n[.g] \{\
+.  mso www.tmac
+.  am URL
+.    ad l
+.  .
+.  am MTO
+.    ad l
+.  .
+.  LINKSTYLE blue R < >
+.\}
+.SH "NAME"
+nfsdclnts \- print various nfs client information for knfsd server.
+.SH "SYNOPSIS"
+.sp
+\fBnfsdclnts\fP [\fI\-h\fP] [\fI\-t type\fP] [\fI\-\-clientinfo\fP] [\fI\-\-hostname\fP] [\fI\-q\fP]
+.SH "DESCRIPTION"
+.sp
+The nfsdclnts(8) command parses the content present in /proc/fs/nfsd/clients/ directories. nfsdclnts(8) displays files which are open, locked, delegated by the nfs\-client. It also prints useful client information such as hostname, clientID, NFS version mounted by the nfs\-client.
+.SH "OPTIONS"
+.sp
+\fB\-t, \-\-type\fP=TYPE
+.RS 4
+Specify the type of file to be displayed. Takes only one TYPE at a time.
+.sp
+\fIopen\fP, \fIlock\fP, \fIdeleg\fP, \fIlayout\fP, or \fIall\fP
+.sp
+open: displays the open files by nfs\-client(s).
+.sp
+lock: displays the files locked by nfs\-client(s).
+.sp
+layout: displays the files for which layout is given.
+.sp
+deleg: displays delegated files information and delegation type.
+.sp
+all: prints all the above type.
+.RE
+.sp
+\fB\-\-clientinfo\fP
+.RS 4
+displays various nfs\-client info fields such as version of nfs mounted at nfs\-client and clientID.
+.RE
+.sp
+\fB\-\-hostname\fP
+.RS 4
+Print hostname of nfs\-client instead of ip-address.
+.RE
+.sp
+\fB\-q, \-\-quiet\fP
+.RS 4
+Hide the header information.
+.RE
+.sp
+\fB\-h, \-\-help\fP
+.RS 4
+Print help explaining the command line options.
+.SH "EXAMPLES"
+.sp
+\fBnfsdclnts \-\-type open\fP
+.RS 4
+List all files with open type only.
+.RE
+.sp
+.if n .RS 4
+.nf
+Inode number | Type   | Access | Deny | ip address            | Filename
+33823232     | open   | r\-     | \-\-   | [::1]:757             | testfile
+.fi
+.if n .RE
+.sp
+\fBnfsdclnts \-\-type deleg\fP
+.RS 4
+List all files with deleg type only.
+.RE
+.sp
+.if n .RS 4
+.nf
+Inode number | Type   | Access | ip address            | Filename
+33823232     | deleg  | r      | [::1]:757             | testfile
+.fi
+.if n .RE
+.sp
+\fBnfsdclnts \-\-hostname\fP
+.RS 4
+Print hostname instead of ip\-address.
+.RE
+.sp
+.if n .RS 4
+.nf
+Inode number | Type   | Access | Deny | Hostname              | Filename
+33823232     | open   | r\-     | \-\-   | nfs\-server            | testfile
+33823232     | deleg  | r      |      | nfs\-server            | testfile
+.fi
+.if n .RE
+.sp
+\fBnfsdclnts \-\-clientinfo\fP
+.RS 4
+Print client information.
+.RE
+.sp
+.if n .RS 4
+.nf
+Inode number | Type   | Access | Deny | ip address            | Client ID           | vers | Filename
+33823232     | open   | r\-     | \-\-   | [::1]:757             | 0xc79a009f5eb65e84  | 4.2  | testfile
+33823232     | deleg  | r      |      | [::1]:757             | 0xc79a009f5eb65e84  | 4.2  | testfile
+.fi
+.if n .RE
+.sp
+\fBnfsdclnts.py \-\-quiet \-\-hostname\fP
+.RS 4
+Hide the header information.
+.RE
+.sp
+.if n .RS 4
+.nf
+33823232     | open   | r\-     | \-\-   | nfs\-server            | testfile
+33823232     | deleg  | r      |      | nfs\-server            | testfile
+.fi
+.if n .RE
+.SH "FILES"
+.sp
+\fB/proc/fs/nfsd/clients/\fP
+.sp
+Displays basic information about each NFSv4 client.
+.sp
+\fB/proc/fs/nfsd/clients/#/info\fP
+.sp
+Displays information about all the opens held by the given client, including open modes, device numbers, inode numbers, and open owners.
+.sp
+\fB/proc/fs/nfsd/clients/#/states\fP
+.SH "NOTES"
+.sp
+/proc/fs/nfsd/clients/ support was initially introduced in 5.3 kernel and is only implemented for mount points using NFSv4.
+.SH "BUGS"
+Please report any BUGs to \c
+.MTO "linux\-nfs\(atvger.kernel.org" "" ""
+.SH SEE ALSO
+.BR nfsd (8),
+.BR exportfs (8),
+.BR idmapd (8),
+.BR statd (8)
+.SH "AUTHORS"
+Achilles Gaikwad <agaikwad@redhat.com> and
+Kenneth D'souza  <kdsouza@redhat.com>
-- 
2.24.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH V3 1/2] nfs-utils: add new tool nfsdclts to parse output from proc files
  2020-05-18 14:10 [PATCH V3 1/2] nfs-utils: add new tool nfsdclts to parse output from proc files Steve Dickson
  2020-05-18 14:10 ` [PATCH V2 2/2] nfsdclnts: add man page for tool nfsdclnts Steve Dickson
@ 2020-05-18 17:14 ` Steve Dickson
  1 sibling, 0 replies; 4+ messages in thread
From: Steve Dickson @ 2020-05-18 17:14 UTC (permalink / raw)
  To: Linux NFS Mailing list



On 5/18/20 10:10 AM, Steve Dickson wrote:
> From: Achilles Gaikwad <agaikwad@redhat.com>
> 
> This tool parses the output from the following files
> 
>  /proc/fs/nfsd/clients/*/{states,info}
> 
> Signed-off-by: Achilles Gaikwad <agaikwad@redhat.com>
> Signed-off-by: Kenneth D'souza <kdsouza@redhat.com>
> Signed-off-by: Steve Dickson <steved@redhat.com>
Committed (tag: nfs-utils-2-4-4-rc5)

steved.
> ---
>  configure.ac                 |   1 +
>  tools/Makefile.am            |   2 +-
>  tools/nfsdclnts/Makefile.am  |   9 ++
>  tools/nfsdclnts/nfsdclnts.py | 221 +++++++++++++++++++++++++++++++++++
>  4 files changed, 232 insertions(+), 1 deletion(-)
>  create mode 100644 tools/nfsdclnts/Makefile.am
>  create mode 100755 tools/nfsdclnts/nfsdclnts.py
> 
> v3: rename command to nfsdclnts 
> 
> diff --git a/configure.ac b/configure.ac
> index 0b1c8cc..9631278 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -694,6 +694,7 @@ AC_CONFIG_FILES([
>  	tools/rpcgen/Makefile
>  	tools/mountstats/Makefile
>  	tools/nfs-iostat/Makefile
> +	tools/nfsdclnts/Makefile
>  	tools/nfsconf/Makefile
>  	tools/nfsdclddb/Makefile
>  	utils/Makefile
> diff --git a/tools/Makefile.am b/tools/Makefile.am
> index 432d35d..9b4b080 100644
> --- a/tools/Makefile.am
> +++ b/tools/Makefile.am
> @@ -12,6 +12,6 @@ if CONFIG_NFSDCLD
>  OPTDIRS += nfsdclddb
>  endif
>  
> -SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat $(OPTDIRS)
> +SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat nfsdclnts $(OPTDIRS)
>  
>  MAINTAINERCLEANFILES = Makefile.in
> diff --git a/tools/nfsdclnts/Makefile.am b/tools/nfsdclnts/Makefile.am
> new file mode 100644
> index 0000000..c1f12a0
> --- /dev/null
> +++ b/tools/nfsdclnts/Makefile.am
> @@ -0,0 +1,9 @@
> +## Process this file with automake to produce Makefile.in
> +PYTHON_FILES = nfsdclnts.py
> +
> +all-local: $(PYTHON_FILES)
> +
> +install-data-hook:
> +	$(INSTALL) -m 755 nfsdclnts.py $(DESTDIR)$(sbindir)/nfsdclnts
> +
> +MAINTAINERCLEANFILES=Makefile.in
> diff --git a/tools/nfsdclnts/nfsdclnts.py b/tools/nfsdclnts/nfsdclnts.py
> new file mode 100755
> index 0000000..2d0ad0a
> --- /dev/null
> +++ b/tools/nfsdclnts/nfsdclnts.py
> @@ -0,0 +1,221 @@
> +#!/bin/python3
> +# -*- python-mode -*-
> +'''
> +    Copyright (C) 2020
> +    Authors:    Achilles Gaikwad <agaikwad@redhat.com>
> +                Kenneth  D'souza <kdsouza@redhat.com>
> +
> +    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 3 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.
> +
> +    You should have received a copy of the GNU General Public License
> +    along with this program.  If not, see <https://www.gnu.org/licenses/>.
> +'''
> +
> +import multiprocessing as mp
> +import os
> +import signal
> +import sys
> +
> +try:
> +    import argparse
> +except ImportError:
> +    print('%s:  Failed to import argparse - make sure argparse is installed!'
> +        % sys.argv[0])
> +    sys.exit(1)
> +try:
> +    import yaml
> +except ImportError:
> +    print('%s:  Failed to import yaml - make sure python3-pyyaml is installed!'
> +        % sys.argv[0])
> +    sys.exit(1)
> +
> +BBOLD = '\033[1;30;47m' #Bold black text with white background.
> +ENDC = '\033[m' #Rest to defaults
> +
> +def init_worker():
> +    signal.signal(signal.SIGINT, signal.SIG_IGN)
> +
> +# this function converts the info file to a dictionary format, sorta. 
> +def file_to_dict(path):
> +    client_info = {}
> +    with open(path) as f:
> +        for line in f:
> +            try:
> +                (key, val) = line.split(':')
> +                client_info[key] = val
> +    # FIXME: There has to be a better way of converting the info file to a dictionary.
> +            except ValueError:
> +                try:
> +                    (key, val) = line.split()
> +                    client_info[key] = val
> +                except:
> +                    pass
> +    return client_info
> +
> +# this function gets the paths from /proc/fs/nfsd/clients/
> +# returns a list of paths for each client which has nfs-share mounted.
> +def getpaths():
> +    path = []
> +    try:
> +        dirs = os.listdir('/proc/fs/nfsd/clients/')
> +    except OSError as reason:
> +        exit('%s' % reason)
> +    if len(dirs) !=0:
> +	    for i in dirs:
> +		    path.append('/proc/fs/nfsd/clients/' + i + '/states')
> +	    return (path)
> +    else:
> +	    exit('Nothing to process')
> +
> +# A single function to rule them all, in this function we gather all the data
> +# from already populated data_list and client_info.
> +def printer(data_list, argument):
> +    client_info_path = data_list.pop()
> +    client_info = file_to_dict(client_info_path)
> +    for i in data_list:
> +        for key in i:
> +            inode = i[key]['superblock'].split(':')[-1]
> +            # get the ip address from client_info as 'address:' note the extra
> +            # ':' as a suffix to address. If there is a better way to convert
> +            # the file to dictionary, please change the following value too.
> +            client_ip = client_info['address:']
> +            # The ip address is quoted, so we dequote it.
> +            client_ip = client_ip[1:-1]
> +            try:
> +                # if the nfs-server reboots while the nfs-client holds the files open,
> +                # the nfs-server would print the filename as '/'. For such instaces we
> +                # print the output as disconnected dentry instead of '/'.
> +                if(i[key]['filename']=='/'):
> +                    fname = 'disconnected dentry'
> +                else:
> +                    fname = i[key]['filename'].split('/')[-1]
> +            except KeyError:
> +                # for older kernels which do not have the fname patch in kernel, they
> +                # won't be able to see the fname field. Therefore post it as N/A.
> +                fname = "N/A"
> +            otype = i[key]['type']
> +            try:
> +                access = i[key]['access']
> +            except:
> +                access = ''
> +            try:
> +                deny = i[key]['deny']
> +            except:
> +                deny = ''
> +            hostname = client_info['name'].split()[-1].split('"')[0]
> +            hostname =  hostname.split('.')[0]
> +            otype = i[key]['type']
> +            # if the hostname is too long, it messes up with the output being in columns,
> +            # therefore we truncate the hostname followed by two '..' as suffix.
> +            if len(hostname) > 20:
> +                hostname = hostname[0:20] + '..'
> +            clientid = client_info['clientid'].strip()
> +            minorversion = client_info['minor version'].rstrip().rsplit()[0]
> +            # since some fields do not have deny column, we drop those if -t is either
> +            # layout or lock.
> +            drop = ['layout', 'lock']
> +
> +            # Printing the output this way instead of a single string which is concatenated
> +            # this makes it better to quickly add more columns in future.
> +            if(otype == argument.type or  argument.type == 'all'):
> +                print('%-13s' %inode, end='| ')
> +                print('%-7s' %otype, end='| ')
> +                if (argument.type not in drop):
> +                    print('%-7s' %access, end='| ')
> +                if (argument.type not in drop and argument.type !='deleg'):
> +                    print('%-5s' %deny, end='| ')
> +                if (argument.hostname == True):
> +                    print('%-22s' %hostname, end='| ')
> +                else:
> +                   print('%-22s' %client_ip, end='| ')
> +                if (argument.clientinfo == True) :
> +                    print('%-20s' %clientid, end='| ')
> +                    print('4.%-3s' %minorversion, end='| ')
> +                print(fname)
> +
> +def opener(path):
> +    try:
> +        with open(path, 'r') as nfsdata:
> +            data = yaml.load(nfsdata, Loader = yaml.BaseLoader)
> +            if data is not None:
> +                clientinfo = path.rsplit('/', 1)[0] + '/info'
> +                data.append(clientinfo)
> +                return data
> +
> +    except OSError as reason:
> +        print('%s' % reason)
> +
> +def print_cols(argument):
> +    title_inode = 'Inode number'
> +    title_otype = 'Type'
> +    title_access = 'Access'
> +    title_deny = 'Deny'
> +    title_fname = 'Filename'
> +    title_clientID = 'Client ID'
> +    title_hostname = 'Hostname'
> +    title_ip = 'ip address'
> +    title_nfsvers = 'vers'
> +
> +    drop = ['lock', 'layout']
> +    print(BBOLD, end='')
> +    print('%-13s' %title_inode, end='| ')
> +    print('%-7s' %title_otype, end='| ')
> +    if (argument.type not in drop):
> +        print('%-7s' %title_access, end='| ')
> +    if (argument.type not in drop and argument.type !='deleg'):
> +        print('%-5s' %title_deny, end='| ')
> +    if (argument.hostname == True):
> +        print('%-22s' %title_hostname, end='| ')
> +    else:
> +        print('%-22s' %title_ip, end='| ')
> +    if (argument.clientinfo == True):
> +        print('%-20s' %title_clientID, end='| ')
> +        print('%-5s' %title_nfsvers, end='| ')
> +    print(title_fname, end='')
> +    print(ENDC)
> +
> +def nfsd4_show():
> +
> +    parser = argparse.ArgumentParser(description = 'Parse the nfsd states and clientinfo files.')
> +    parser.add_argument('-t', '--type', metavar = 'type', type = str, choices = ['open',
> +        'deleg', 'lock', 'layout', 'all'],
> +        default = 'all',
> +        help = 'Input the type that you want to be printed: open, lock, deleg, layout, all')
> +    parser.add_argument('--clientinfo', action = 'store_true',
> +        help = 'output clients information, --hostname is implied.')
> +    parser.add_argument('--hostname', action = 'store_true',
> +        help = 'print hostname of client instead of its ip address. Longer hostnames are truncated.')
> +    parser.add_argument('-q', '--quiet', action = 'store_true',
> +        help = 'don\'t print the header information')
> +
> +    args = parser.parse_args()
> +    paths = getpaths()
> +    p = mp.Pool(mp.cpu_count(), init_worker)
> +    try:
> +        result = p.map(opener, paths)
> +        ### Drop None entries from list
> +        final_result = list(filter(None, result))
> +        p.close()
> +        p.join()
> +
> +        if len(final_result) !=0 and not args.quiet:
> +            print_cols(args)
> +
> +        for item in final_result:
> +            printer(item, args)
> +
> +    except KeyboardInterrupt:
> +        print('Caught KeyboardInterrupt, terminating workers')
> +        p.terminate()
> +        p.join()
> +
> +if __name__ == "__main__":
> +    nfsd4_show()
> 


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH V2 2/2] nfsdclnts: add man page for tool nfsdclnts
  2020-05-18 14:10 ` [PATCH V2 2/2] nfsdclnts: add man page for tool nfsdclnts Steve Dickson
@ 2020-05-18 17:14   ` Steve Dickson
  0 siblings, 0 replies; 4+ messages in thread
From: Steve Dickson @ 2020-05-18 17:14 UTC (permalink / raw)
  To: Linux NFS Mailing list



On 5/18/20 10:10 AM, Steve Dickson wrote:
> From: Achilles Gaikwad <agaikwad@redhat.com>
> 
> This patch adds man page for the tool nfsdclnts.
> 
> Signed-off-by: Achilles Gaikwad <agaikwad@redhat.com>
> Signed-off-by: Kenneth D'souza <kdsouza@redhat.com>
> Signed-off-by: Steve Dickson <steved@redhat.com>
Committed (tag: nfs-utils-2-4-4-rc5)

steved.
> ---
>  tools/nfsdclnts/Makefile.am   |   4 +
>  tools/nfsdclnts/nfsdclnts.man | 156 ++++++++++++++++++++++++++++++++++
>  2 files changed, 160 insertions(+)
>  create mode 100644 tools/nfsdclnts/nfsdclnts.man
> 
> v2: Renamed command to nfsdclnts
> 
> diff --git a/tools/nfsdclnts/Makefile.am b/tools/nfsdclnts/Makefile.am
> index c1f12a0..d513edb 100644
> --- a/tools/nfsdclnts/Makefile.am
> +++ b/tools/nfsdclnts/Makefile.am
> @@ -1,6 +1,10 @@
>  ## Process this file with automake to produce Makefile.in
>  PYTHON_FILES = nfsdclnts.py
>  
> +man8_MANS       = nfsdclnts.man
> +
> +EXTRA_DIST      = $(man8_MANS) $(PYTHON_FILES)
> +
>  all-local: $(PYTHON_FILES)
>  
>  install-data-hook:
> diff --git a/tools/nfsdclnts/nfsdclnts.man b/tools/nfsdclnts/nfsdclnts.man
> new file mode 100644
> index 0000000..3701de9
> --- /dev/null
> +++ b/tools/nfsdclnts/nfsdclnts.man
> @@ -0,0 +1,156 @@
> +.\"
> +.\" nfsdclnts(8)
> +.\"
> +.TH "NFSDCLTS" "8" "2020-05-09" "nfsdclnts" "nfsdclnts"
> +.ie \n(.g .ds Aq \(aq
> +.el       .ds Aq '
> +.ss \n[.ss] 0
> +.nh
> +.ad l
> +.de URL
> +\fI\\$2\fP <\\$1>\\$3
> +..
> +.als MTO URL
> +.if \n[.g] \{\
> +.  mso www.tmac
> +.  am URL
> +.    ad l
> +.  .
> +.  am MTO
> +.    ad l
> +.  .
> +.  LINKSTYLE blue R < >
> +.\}
> +.SH "NAME"
> +nfsdclnts \- print various nfs client information for knfsd server.
> +.SH "SYNOPSIS"
> +.sp
> +\fBnfsdclnts\fP [\fI\-h\fP] [\fI\-t type\fP] [\fI\-\-clientinfo\fP] [\fI\-\-hostname\fP] [\fI\-q\fP]
> +.SH "DESCRIPTION"
> +.sp
> +The nfsdclnts(8) command parses the content present in /proc/fs/nfsd/clients/ directories. nfsdclnts(8) displays files which are open, locked, delegated by the nfs\-client. It also prints useful client information such as hostname, clientID, NFS version mounted by the nfs\-client.
> +.SH "OPTIONS"
> +.sp
> +\fB\-t, \-\-type\fP=TYPE
> +.RS 4
> +Specify the type of file to be displayed. Takes only one TYPE at a time.
> +.sp
> +\fIopen\fP, \fIlock\fP, \fIdeleg\fP, \fIlayout\fP, or \fIall\fP
> +.sp
> +open: displays the open files by nfs\-client(s).
> +.sp
> +lock: displays the files locked by nfs\-client(s).
> +.sp
> +layout: displays the files for which layout is given.
> +.sp
> +deleg: displays delegated files information and delegation type.
> +.sp
> +all: prints all the above type.
> +.RE
> +.sp
> +\fB\-\-clientinfo\fP
> +.RS 4
> +displays various nfs\-client info fields such as version of nfs mounted at nfs\-client and clientID.
> +.RE
> +.sp
> +\fB\-\-hostname\fP
> +.RS 4
> +Print hostname of nfs\-client instead of ip-address.
> +.RE
> +.sp
> +\fB\-q, \-\-quiet\fP
> +.RS 4
> +Hide the header information.
> +.RE
> +.sp
> +\fB\-h, \-\-help\fP
> +.RS 4
> +Print help explaining the command line options.
> +.SH "EXAMPLES"
> +.sp
> +\fBnfsdclnts \-\-type open\fP
> +.RS 4
> +List all files with open type only.
> +.RE
> +.sp
> +.if n .RS 4
> +.nf
> +Inode number | Type   | Access | Deny | ip address            | Filename
> +33823232     | open   | r\-     | \-\-   | [::1]:757             | testfile
> +.fi
> +.if n .RE
> +.sp
> +\fBnfsdclnts \-\-type deleg\fP
> +.RS 4
> +List all files with deleg type only.
> +.RE
> +.sp
> +.if n .RS 4
> +.nf
> +Inode number | Type   | Access | ip address            | Filename
> +33823232     | deleg  | r      | [::1]:757             | testfile
> +.fi
> +.if n .RE
> +.sp
> +\fBnfsdclnts \-\-hostname\fP
> +.RS 4
> +Print hostname instead of ip\-address.
> +.RE
> +.sp
> +.if n .RS 4
> +.nf
> +Inode number | Type   | Access | Deny | Hostname              | Filename
> +33823232     | open   | r\-     | \-\-   | nfs\-server            | testfile
> +33823232     | deleg  | r      |      | nfs\-server            | testfile
> +.fi
> +.if n .RE
> +.sp
> +\fBnfsdclnts \-\-clientinfo\fP
> +.RS 4
> +Print client information.
> +.RE
> +.sp
> +.if n .RS 4
> +.nf
> +Inode number | Type   | Access | Deny | ip address            | Client ID           | vers | Filename
> +33823232     | open   | r\-     | \-\-   | [::1]:757             | 0xc79a009f5eb65e84  | 4.2  | testfile
> +33823232     | deleg  | r      |      | [::1]:757             | 0xc79a009f5eb65e84  | 4.2  | testfile
> +.fi
> +.if n .RE
> +.sp
> +\fBnfsdclnts.py \-\-quiet \-\-hostname\fP
> +.RS 4
> +Hide the header information.
> +.RE
> +.sp
> +.if n .RS 4
> +.nf
> +33823232     | open   | r\-     | \-\-   | nfs\-server            | testfile
> +33823232     | deleg  | r      |      | nfs\-server            | testfile
> +.fi
> +.if n .RE
> +.SH "FILES"
> +.sp
> +\fB/proc/fs/nfsd/clients/\fP
> +.sp
> +Displays basic information about each NFSv4 client.
> +.sp
> +\fB/proc/fs/nfsd/clients/#/info\fP
> +.sp
> +Displays information about all the opens held by the given client, including open modes, device numbers, inode numbers, and open owners.
> +.sp
> +\fB/proc/fs/nfsd/clients/#/states\fP
> +.SH "NOTES"
> +.sp
> +/proc/fs/nfsd/clients/ support was initially introduced in 5.3 kernel and is only implemented for mount points using NFSv4.
> +.SH "BUGS"
> +Please report any BUGs to \c
> +.MTO "linux\-nfs\(atvger.kernel.org" "" ""
> +.SH SEE ALSO
> +.BR nfsd (8),
> +.BR exportfs (8),
> +.BR idmapd (8),
> +.BR statd (8)
> +.SH "AUTHORS"
> +Achilles Gaikwad <agaikwad@redhat.com> and
> +Kenneth D'souza  <kdsouza@redhat.com>
> 


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2020-05-18 17:15 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-18 14:10 [PATCH V3 1/2] nfs-utils: add new tool nfsdclts to parse output from proc files Steve Dickson
2020-05-18 14:10 ` [PATCH V2 2/2] nfsdclnts: add man page for tool nfsdclnts Steve Dickson
2020-05-18 17:14   ` Steve Dickson
2020-05-18 17:14 ` [PATCH V3 1/2] nfs-utils: add new tool nfsdclts to parse output from proc files Steve Dickson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.