#!/usr/bin/env python
#===============================================================================
# Copyright 2014 NetApp, Inc. All Rights Reserved,
# contribution by Jorge Mora <mora@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 nfstest_config as c
from nfstest.file_io import *
from optparse import OptionParser, OptionGroup, IndentedHelpFormatter

# Module constants
__author__    = 'Jorge Mora (%s)' % c.NFSTEST_AUTHOR_EMAIL
__version__   = '1.0.1'
__copyright__ = "Copyright (C) 2014 NetApp, Inc."
__license__   = "GPL v2"


USAGE = """%prog -d <datadir> [options]

I/O tool
========
This I/O tool is used to create and manipulate files of different types.
The arguments allow running for a specified period of time as well as running
multiple processes. Each process modifies a single file at a time and the
file name space is different for each process so there are no collisions
between two different processes modifying the same file."""

################################################################################
# Entry point
################################################################################
# Define command line options
opts = OptionParser(USAGE, formatter = IndentedHelpFormatter(2, 25), version = "%prog " + __version__)
opts.add_option("-d", "--datadir", help="Top level directory where files will be created, it will be created if it does not exist")
opts.add_option("-s", "--seed",    type="int", default=None, help="Seed to initialized the random number generator [default: automatically generated]")
opts.add_option("-n", "--nprocs",  type="int", default=1, help="Number of processes to use [default: %default]")
opts.add_option("-r", "--runtime", type="int", default=0, help="Run time [default: '%default']")
opts.add_option("-v", "--verbose", default="none", help="Verbose level: none|info|debug|dbg1-7|all [default: '%default']")
opts.add_option("-e", "--exiterr", action="store_true", default=False, help="Exit on first error")

writegroup = OptionGroup(opts, "Read and write")
writegroup.add_option("--read",     type="int", default=P_READ,   help="Read file percentage [default: %default]")
writegroup.add_option("--write",    type="int", default=P_WRITE,  help="Write file percentage [default: %default]")
writegroup.add_option("--rdwr",     type="int", default=P_RDWR,   help="Read/write file percentage [default: %default]")
writegroup.add_option("--randio",   type="int", default=P_RANDIO, help="Random file access percentage [default: %default]")
writegroup.add_option("--iodelay",  type="float", default=P_IODELAY, help="Seconds to delay I/O operations [default: %default]")
writegroup.add_option("--direct",   action="store_true", default=False, help="Use direct I/O")
writegroup.add_option("--rdwronly", action="store_true", default=False, help="Use read and write only, no rename, remove, etc.")
opts.add_option_group(writegroup)

opgroup = OptionGroup(opts, "File operations")
opgroup.add_option("--create",   type="int", default=P_CREATE,   help="Create file percentage [default: %default]")
opgroup.add_option("--odgrade",  type="int", default=P_ODGRADE,  help="Open downgrade percentage [default: %default]")
opgroup.add_option("--osync",    type="int", default=P_OSYNC,    help="Open file with O_SYNC [default: %default]")
opgroup.add_option("--fsync",    type="int", default=P_FSYNC,    help="Percentage of fsync after write [default: %default]")
opgroup.add_option("--rename",   type="int", default=P_RENAME,   help="Rename file percentage [default: %default]")
opgroup.add_option("--remove",   type="int", default=P_REMOVE,   help="Remove file percentage [default: %default]")
opgroup.add_option("--trunc",    type="int", default=P_TRUNC,    help="Truncate file percentage [default: %default]")
opgroup.add_option("--ftrunc",   type="int", default=P_FTRUNC,   help="Truncate opened file percentage [default: %default]")
opgroup.add_option("--link",     type="int", default=P_LINK,     help="Create hard link percentage [default: %default]")
opgroup.add_option("--slink",    type="int", default=P_SLINK,    help="Create symbolic link percentage [default: %default]")
opgroup.add_option("--readdir",  type="int", default=P_READDIR,  help="List contents of directory percentage [default: %default]")
opgroup.add_option("--lock",     type="int", default=P_LOCK,     help="Lock file percentage [default: %default]")
opgroup.add_option("--unlock",   type="int", default=P_UNLOCK,   help="Unlock file percentage [default: %default]")
opgroup.add_option("--tlock",    type="int", default=P_TLOCK,    help="Lock test percentage [default: %default]")
opgroup.add_option("--lockfull", type="int", default=P_LOCKFULL, help="Lock full file percentage [default: %default]")
opgroup.add_option("--minfiles", default=str(MIN_FILES),  help="Mininum number of files to create before any file operation is executed [default: %default]")
opts.add_option_group(opgroup)

filegroup = OptionGroup(opts, "File size options")
filegroup.add_option("--fsizeavg", default=P_FILESIZE, help="File size average [default: %default]")
filegroup.add_option("--fsizedev", default=P_FSIZEDEV, help="File size standard deviation [default: %default]")
filegroup.add_option("--rsize",    default=P_RSIZE,    help="Read block size [default: %default]")
filegroup.add_option("--rsizedev", default=P_RSIZEDEV, help="Read block size standard deviation [default: %default]")
filegroup.add_option("--wsize",    default=P_WSIZE,    help="Write block size [default: %default]")
filegroup.add_option("--wsizedev", default=P_WSIZEDEV, help="Write block size standard deviation [default: %default]")
filegroup.add_option("--sizemult", default=P_SIZEMULT, help="Size multiplier [default: %default]")
opts.add_option_group(filegroup)

loggroup = OptionGroup(opts, "Logging options")
loggroup.add_option("--createlog",  action="store_true", default=P_CREATELOG,  help="Create log file")
loggroup.add_option("--createlogs", action="store_true", default=P_CREATELOGS, help="Create a log file for each process")
loggroup.add_option("--logdir", default=P_TMPDIR, help="Log directory [default: '%default']")
opts.add_option_group(loggroup)

# Run parse_args to get options and process dependencies
vopts, args = opts.parse_args()
if vopts.rdwronly:
    # Set new defaults
    opts.set_defaults(rename=0)
    opts.set_defaults(remove=0)
    opts.set_defaults(trunc=0)
    opts.set_defaults(ftrunc=0)
    opts.set_defaults(link=0)
    opts.set_defaults(slink=0)
    opts.set_defaults(readdir=0)
    opts.set_defaults(lock=0)
    opts.set_defaults(unlock=0)
    opts.set_defaults(tlock=0)
    opts.set_defaults(lockfull=0)

# Defaults given above are for displaying purposes only
# Set defaults for read and write to know which options are given
opts.set_defaults(write=None)
opts.set_defaults(read=None)
opts.set_defaults(rdwr=None)

# Re-run parse_args with new default values
vopts, args = opts.parse_args()
if vopts.datadir is None:
    opts.error("datadir option is required")

# Remove empty keys
empty_keys = [k for k,v in vopts.__dict__.iteritems() if v is None]
for k in empty_keys:
    del vopts.__dict__[k]

x = FileIO(**vopts.__dict__)
x.run()
