#!/usr/bin/python
# Copyright (c) 2009, Purdue University
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# Redistributions in binary form must reproduce the above copyright notice, this
# list of conditions and the following disclaimer in the documentation and/or
# other materials provided with the distribution.
#
# Neither the name of the Purdue University nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Core flags lib, flags in all tools."""
__copyright__ = 'Copyright (C) 2009, Purdue University'
__license__ = 'BSD'
__version__ = '#TRUNK#'
from copy import deepcopy
import getpass
from optparse import OptionParser
from roster_user_tools import cli_common_lib
[docs]class FlagsError(Exception):
pass
[docs]class CoreFlags(object):
"""Command line common library"""
def __init__(self, command, commands, args, usage, bootstrapper=False):
"""Initializes parser, sets flags for all classes"""
self.parser = OptionParser(version='%%prog (Roster %s)' % __version__,
usage=usage)
self.bootstrapper = bootstrapper
self.args = args
self.command = command
self.SetCommands(commands)
if( self.command and self.command not in self.functions_dict ):
cli_common_lib.DnsError(
'This tool does not have a %s command.' % self.command, 1)
self.SetCoreFlags()
self.SetActionFlags()
self.SetDataFlags()
if( hasattr(self, 'SetToolFlags') ):
self.SetToolFlags()
## Organize flags into dict {'flag_name': '-f/--flag-name'}
self.avail_flags = {}
for flag in self.parser.option_list[2:]:
self.avail_flags[flag.dest] = flag
self.options = self.parser.parse_args(self.args)[0]
self.CheckDataFlags()
[docs] def SetCoreFlags(self):
"""Sets core flags for parser"""
if( not self.bootstrapper ):
self.parser.add_option(
'-u', '--username', action='store', dest='username',
help='Run as different username.', metavar='<username>',
default=unicode(getpass.getuser()))
self.SetAllFlagRule('username', required=False)
self.parser.add_option(
'-p', '--password', action='store', dest='password',
help='Password string, NOTE: It is insecure to use this '
'flag on the command line.', metavar='<password>', default=None)
self.SetAllFlagRule('password', required=False)
self.parser.add_option(
'--cred-string', action='store', dest='credstring',
help='String of credential.', metavar='<cred-string>', default=None)
self.SetAllFlagRule('credstring', required=False)
self.parser.add_option(
'-s', '--server', action='store', dest='server',
help='XML RPC Server URL.', metavar='<server>', default=None)
self.SetAllFlagRule('server', required=False)
self.parser.add_option(
'-c', '--cred-file', action='store', dest='credfile',
help='Location of credential file.', metavar='<cred-file>',
default=cli_common_lib.DEFAULT_CRED_FILE)
self.SetAllFlagRule('credfile', required=False)
self.parser.add_option(
'--config-file', action='store', dest='config_file',
help='Config file location.', metavar='<file>', default=None)
self.SetAllFlagRule('config_file', required=False)
[docs] def CheckDataFlags(self):
"""Returns the action the tool should perform
Inputs:
function: string of function, must be in functions
functions: dictionary of uses from tool
Outputs:
string: string from uses keys of correct action
"""
if( not self.command ):
cli_common_lib.DnsError('A command must be specified.', 1)
## Possilby print help
elif( self.command and self.command not in self.functions_dict ):
cli_common_lib.DnsError(
'This tool does not have a %s command.' % self.command, 1)
## Possilby print help
## Find used flags {'flag_name': 'flag_value'}
used_flags = {}
for flag in self.parser.option_list[2:]:
option_var = eval('self.options.%s' % flag.dest)
if( option_var != self.parser.defaults[flag.dest] ):
used_flags[flag.dest] = option_var
## Find irrelevant flags
for used_flag in used_flags:
found = False
for flag_type in self.functions_dict[self.command]:
if( flag_type in ['dependent_args', 'independent_args'] ):
for group in self.functions_dict[self.command][flag_type]:
if( used_flag in group ):
found = True
if( used_flag in self.functions_dict[self.command][flag_type] ):
found = True
if( not found ):
cli_common_lib.DnsError(
'The %s flag cannot be used with the %s command.' % (
self.avail_flags[used_flag], self.command), 1)
## Check if all required arguments are used
for flag in self.functions_dict[self.command]['args']:
if( flag not in used_flags and self.functions_dict[self.command]['args'][
flag] ):
cli_common_lib.DnsError(
'The %s flag is required.' % (self.avail_flags[flag]), 1)
## Possilby print help
## Check independent arguments
for flags in self.functions_dict[self.command]['independent_args']:
if( len(flags) == 0 ):
continue
flags_real = [] # Real flags strings like -a/--acl
for flag in flags:
flags_real.append(str(self.avail_flags[flag]))
independent_flags = 0
for flag in flags:
if( flag in used_flags ):
independent_flags += 1
if( independent_flags > 1 ):
cli_common_lib.DnsError('%s cannot be used simultaneously.' % (
' and '.join(sorted(flags_real))), 1)
if( independent_flags == 0 and flags[flag] ):
cli_common_lib.DnsError('Either %s must be used.' % (
' or '.join(sorted(flags_real))), 1)
## Check dependent arguments
used = 0
unused = 0
for flags in self.functions_dict[self.command]['dependent_args']:
if( len(flags) == 0 ):
continue
flags_real = [] # Real flags strings like -a/--acl
for flag in flags:
flags_real.append(str(self.avail_flags[flag]))
for flag in flags:
if( flag in used_flags ):
used += 1
else:
unused += 1
if( used != 0 and unused != 0 ):
cli_common_lib.DnsError('%s must be used together.' % (
' and '.join(sorted(flags_real))), 1)
[docs] def SetCommands(self, functions):
"""Sets self.functions_dict according to functions list
Inputs:
functions: list of strings of functions
Sets the self.functions_dict
ex:
{'list': {'args': {}, 'forbidden_args':{}, 'independent_args': [],
'dependent_args': []},
{'remove': {'args': {}, 'forbidden_args':{}, 'independent_args': [],
'dependent_args': []}}
The list portion of the dictionary may be populated as shown below:
{'list': {'args': {'user': True, 'verbose': False},
'forbidden_args': {'make_all': False},
'independent_args': [{'allow': False, 'deny': False},
{'quiet': False, 'verbose': False}],
'dependent_args': [{'allow': False, 'allow_level': True}]}}
Where 'user' is a required argument. The value represents whether or not
the argument is required: True/False. Note 'user' is the name given in
optparse, NOT the flag '-u/--user'. Verbose is not a required argument,
but is listed in args and should be in case of any code changes. All
args should be represented in portions of this dictionary. If 'user'
is not supplied, the function will exit with error. 'make_all' is a
forbidden argument. If it is used in this certain portion of a function,
the user is probably mistaking what is happening with the command and
the function will exit with error. Independent args must not be given
simultaneously otherwise the function will exit with error. If these args
are required, it will exit with error if one or the other is not
specified. Dependent args are arguments that depend on each other,
the function will error out if both are not supplied simultaneously.
"""
self.functions_dict = {} # Reset if not empty
functions_dict = {'args': {}, 'forbidden_args': {},
'independent_args': [], 'dependent_args': []}
for function in functions:
self.functions_dict[function] = deepcopy(functions_dict)
[docs] def SetDefaultFlags(self):
"""Sets all availible default flags to not required
for use with one command functions such as list functions
"""
for flag in self.parser.defaults:
self.AddFlagRule(flag, required=False)
[docs] def AddFlagRule(self, flag_name, command=None, required=True,
flag_type='args'):
"""Adds a key to functions dict
Inputs:
flag_name: string of flag name
command: string of command (defaults to self.command)
required: boolean of required flag (defaults to true)
flag_type: type of flag (defaults to 'args')
"""
if( command is None and self.command is None):
return
if( command is None):
command = self.command
if( flag_type in ['independent_args', 'dependent_args']
and type(flag_name) == tuple ):
new_dict = {}
for flag_n in flag_name:
new_dict[flag_n] = required
self.functions_dict[command][flag_type].append(new_dict)
elif( type(flag_name) == str ):
self.functions_dict[command][flag_type][flag_name] = required
else:
raise FlagsError('flag_name type and flag_type mismatch.')
[docs] def SetAllFlagRule(self, flag_name, required=True, flag_type='args'):
"""Adds a key to all args portions of functions dicts
Inputs:
flag_name: string of optparse flag name
required: boolean of required flag (defaults to True)
flag_type: type of flag (defaults to 'args')
"""
for command in self.functions_dict:
self.AddFlagRule(flag_name, command=command, required=required,
flag_type=flag_type)