#!/usr/bin/python
"""
Copyright 2012 NetApp, Inc. All Rights Reserved,
contribution by Weston Andros Adamson <dros@netapp.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 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.
"""

import getopt
import posix
import sys
import os

from nfsometerlib import trace

from nfsometerlib.cmd import *
from nfsometerlib.config import *
from nfsometerlib.workloads import *

from nfsometerlib.report import ReportSet
from nfsometerlib.collection import TestCollection


DEFAULTS = {
  'mountopts': '|'.join(('v3', 'v4.0', 'v4.1')),
  'num_runs':  '1',
  'resultdir': RESULTS_DIR,
}

def usage(msg=''):
    print >>sys.stderr, \
    """
usage: %s [options] <server:path> [workload1] [workload2] [...]

Available workloads:
  %s

Unavailable workloads:
  %s

If no workloads are specified, all available workloads are run.

General Options:

  --fetch-only     - just fetch any files needed by workloads -- don't 
                     run any traces.

  --run-only       - just run the traces, don't generate reports

  --reports-only   - don't run traces, just generate reports

  -r (--resultdir) - the directory where results are saved
                     (default: %s)

Trace Options:

  -m (--mountopts) - '|' separated list of mount option sets to iterate
                     through
                     (default: %s)

  -n (--num-runs)  - number of runs of each (workload, mountopt) combination
                     (default %s)

Admin Script Options:

  --admin-script=<script> -        script used to toggle serverside settings

  --admin-server=<[user@]server> - server to use for script, defaults to
                                   server component of server:path argument

Client Script Options:

  --client-script=<script> -     script to toggle client-side settings

  --client-arg="<key>=<value>" - argument to pass to client-script.  
                                 mutliple --client-args may be defined.

PNFS Environment Options:

  --path-remote=<path> - do all requested traces with both this path and the 
                         path from the <server:path> argument.  traces using
                         this path will be tagged as "remote"

Report Options:

  --serial-graphs - generate graphs inline while generating reports. useful
                    for debugging graphing issues.

    """.strip() % \
        (sys.argv[0],
         '\n  '.join(available_workloads()),
         '\n  '.join(unavailable_workloads()),
         DEFAULTS['resultdir'],
         DEFAULTS['mountopts'],
         DEFAULTS['num_runs'],
        )

    if msg:
        print >>sys.stderr
        print >>sys.stderr, "Error: " + msg

    sys.exit(1)

def main():
    try:
        opts, args = getopt.getopt(sys.argv[1:], 'hr:m:n:t:',
                         ['help',
                          'resultdir=',
                          'mountopts=',
                          'admin-script=',
                          'admin-server=',
                          'client-script=',
                          'client-arg=',
                          'path-remote=',
                          'num-runs=',
                          'fetch-only',
                          'run-only',
                          'reports-only'])

    except getopt.GetoptError, err:
        print str(err)
        usage()

    resultdir = None
    mountopts = None
    num_runs = None
    fetch_only = False
    run_only = False
    reports_only = False
    admin_script = None
    admin_server = None
    client_script = None
    client_args = []
    path_remote = None
    serial_graph_gen = False
    for o, a in opts:
        if o in ('-r', '--resultdir'):
            resultdir = a
        elif o in ('-m', '--mountopts'):
            mountopts = a
        elif o in ('-n', '--num-runs'):
            num_runs = a
        elif o in ('--fetch-only'):
            fetch_only = True
        elif o in ('--run-only'):
            run_only = True
        elif o in ('--reports-only'):
            reports_only = True
        elif o in ('--client-script'):
            client_script = a
        elif o in ('--client-arg'):
            client_args.append(a)
        elif o in ('--admin-script'):
            admin_script = a
        elif o in ('--admin-server'):
            admin_server = a
        elif o in ('--path-remote'):
            path_remote = a
        elif o in ('--serial-graphs'):
            serial_graph_gen = True
        elif o in ('-h', '--help'):
            usage()
        else:
            usage('Unhandled option: %s' % o)

    # parse arguments
    if run_only and reports_only:
        usage('Cannot have --run-only and --reports-only set at the same time')

    if len(args) < 1 and not (fetch_only or reports_only):
        usage('Too few arguments')

    if len(args) >= 1:
        serverpath = args[0]
        workloads_requested = args[1:]

        if serverpath.find(':') < 0:
            usage('expected <server:path> for first argument, but no : found')
    else:
        serverpath = None
        workloads_requested = []

    # handle defaults
    if resultdir == None:
        resultdir = DEFAULTS['resultdir']

    if mountopts == None:
        mountopts = DEFAULTS['mountopts']

    if num_runs == None:
        num_runs = DEFAULTS['num_runs']

    if not admin_server and serverpath:
        admin_server = serverpath.split(':', 1)[0]

    # massage arguments
    mountopts = mountopts.split('|')
    num_runs = long(num_runs)

    if admin_script and not os.path.isfile(admin_script):
        usage("admin_script='%s' doesn't exist or isn't a file!" % admin_script)

    if path_remote and serverpath and \
        path_remote == serverpath.split(':', 1)[1]:
        usage("path-remote is the same as the specified 'local' path")

    if not os.path.isdir(resultdir):
        try:
            os.mkdir(resultdir)
        except:
            usage("can't make resultdir: %s" % resultdir)

    try:
        trace.idle_check()
    except:
        res = cmd('mount | grep " type nfs "',
                  raiseerrorout=False, raiseerrorcode=False)
        mounts = '\n'.join(res[0])
        res = cmd('mount | grep " type nfs4 "',
                  raiseerrorout=False, raiseerrorcode=False)
        mounts += '\n'.join(res[0])

        if not mounts.strip():
            usage("NFS client not idle (check /proc/fs/nfsfs/servers)")
        else:
            usage("NFSometer cannot be run with any other nfs mounts:\n%s" % mounts)

    if not reports_only:
        trace.run_traces(serverpath, workloads_requested, resultdir, mountopts,
                 num_runs, fetch_only, admin_script,
                 admin_server, client_script, client_args, path_remote)

        print

    if not run_only:
        inform('Generating reports:')
        collection = TestCollection(resultdir)
        rpt = ReportSet(collection, serial_graph_gen)
        rpt.generate_reports()
        inform("Report index: %s" % os.path.join(resultdir, 'index.html'))

if __name__ == '__main__':
    main()

