From e94a818ec03cc157c7c90439ae2d2ee1f03bcab8 Mon Sep 17 00:00:00 2001
From: Mike Lake <Mike.Lake@uts.edu.au>
Date: Mon, 1 Nov 2021 16:01:14 +1100
Subject: [PATCH] Use docopt and not argparse, use a virtualenv.

---
 check_utilisation.py | 68 +++++++++++++++++++++++++-------------------
 1 file changed, 39 insertions(+), 29 deletions(-)

diff --git a/check_utilisation.py b/check_utilisation.py
index cc232df..0c52539 100755
--- a/check_utilisation.py
+++ b/check_utilisation.py
@@ -3,14 +3,14 @@
 
 '''
 This script checks the utilisation of HPC jobs.
-Run the program to get help on usage.
+Run the program with -h or --help to get help on usage.
 
 Note to installers: 
 1. This program should be installed on just the login node under /opt/eresearch/
 2. The public version of the users database needs to be updated every time the
    main database is updated.
 
-Notes:
+Notes to myself:
 Cannot use job key 'euser'from pbs_connect(pbs_server):
   At first I was using job['euser'] from the jobs returned from PBS to get the user 
   running the job. It turns out that this key is only available to PBS admin users 
@@ -21,14 +21,20 @@ Cannot use job key 'euser'from pbs_connect(pbs_server):
 
 Author: Mike Lake
 Releases: 
-2021-04-29 First release.
+  2021-04-29 First release.
+  2021-11-01 Use docopt arg parsing instead or argparse. Had to add a virtualenv for this.
 
 '''
 
-import argparse
+# Load python from this virtual environment and unload the system python.
+activate_this_file = "/opt/eresearch/virtualenvs/hpc_py3/bin/activate_this.py"
+with open(activate_this_file) as f:
+    exec(f.read(), dict(__file__=activate_this_file))
+
 import sys, os, re
 import pwd
 import datetime
+from docopt import docopt
 
 # Append what ever pbs directory is under the directory that this script is located
 # in. This ensures that we use /opt/eresearch/pbs for the version used by users and 
@@ -68,6 +74,23 @@ from_email = 'YourEmail@example.com'
 # Your login nodes mail server.
 mail_server = 'postoffice.example.com'
 
+# The programs usage comes from this string being parsed by docopt.
+usage_doc = '''
+Check Your HPC Utilisation
+
+Usage: 
+  check_utilisation.py running  [-e <email>] [-u user]
+  check_utilisation.py finished 
+  check_utilisation.py all
+  check_utilisation.py -h | --help
+
+Options:
+  -e, --email <email>    Email a copy of this report to yourself.
+  -u, --user  <user>     Only show jobs for this user.
+
+For further help contact the author: Mike.Lake@uts.edu.au
+'''
+
 prefix = '''
 <p>Hi</p>
 
@@ -334,23 +357,10 @@ def main():
     ##################################################
     # Check program args and access to required files.
     ##################################################
-
-    # I have replaced the default help's message with a clearer one.
-    parser = argparse.ArgumentParser(\
-        description='Check Your HPC Utilisation', \
-        usage="%(prog)s  running|finished|all  [-h] [-u USER] [-e EMAIL]", \
-        epilog='Contact %s for further help.' % from_email, \
-    ) 
-
-    parser.add_argument('state', choices=['running','finished','all'], default='running', \
-        help='Select one job state to report on.')
-    parser.add_argument('-u', '--user', help='Only show jobs for this user.')
-    parser.add_argument('-e', '--email', help='Email a copy of this report to yourself.')
-   
-    args = parser.parse_args()
-    state = args.state
-    user_id = args.user
-    recipient_email = args.email
+    args = docopt(usage_doc, argv=None, help=True, version=None, options_first=False)
+ 
+    user_id = None
+    recipient_email = None
 
     # Check that we can access the HPC user database.
     dirpath = os.path.dirname(sys.argv[0])
@@ -375,7 +385,7 @@ def main():
 
     conn = pbs.pbs_connect(pbs_server)
 
-    if state == 'running':
+    if args['running']:
         # This will get just current jobs; queued, running, and exiting.
         # We have added the 't' to also include current array jobs.
         jobs = get_jobs(conn, extend='t')
@@ -390,7 +400,7 @@ def main():
         # This will also remove array parent jobs as they are state 'B".
         jobs = [j for j in jobs if j['job_state'] == 'R']
         print('Found %d running jobs out of %d total jobs.' % (len(jobs), total))
-    elif state == 'finished':
+    elif args['finished']:
         # This will get ALL jobs, current and finished.
         jobs = get_jobs(conn, extend='xt')
         total = len(jobs)
@@ -416,7 +426,7 @@ def main():
 
         jobs = jobs_tmp
         print('Found %d finished jobs from last %d days.' % (len(jobs), past_days))
-    elif state == 'all':
+    elif args['all']:
         # This will get ALL jobs, current and finished.
         jobs = get_jobs(conn, extend='xt')
         total = len(jobs)
@@ -440,7 +450,7 @@ def main():
 
     else:
         # We should never get here.
-        print("Invalid state %s" % state)
+        print("Invalid positional argument.")
         sys.exit()
 
     pbs.pbs_disconnect(conn)
@@ -466,17 +476,17 @@ def main():
         sys.exit()
     fh.write(prefix)
 
-    if state == 'running':
+    if args['running']:
         fh.write("<p>Running Jobs</p>")
         fh.write(print_table_start())
         print_jobs(jobs, fh)
         fh.write(print_table_end())
-    elif state == 'finished':
+    elif args['finished']:
         fh.write("<p>Finished Jobs</p>")
         fh.write(print_table_start())
         print_jobs(jobs, fh)
         fh.write(print_table_end())
-    elif state == 'all':
+    elif args['all']:
         # Write the running jobs.
         fh.write("<a name='finished'/>")
         fh.write("<p><b>Finished Jobs</b> - Go to list of <a href='#running'>running jobs</a></p>")
@@ -493,7 +503,7 @@ def main():
         fh.write(print_table_end())
     else:
         # We should never get here.
-        print("Invalid state %s" % state)
+        print("Invalid positional argument." )
 
     fh.write(postfix)
     fh.close()
-- 
GitLab