several fixes to cli tools

- fixed issue with previous commit with bad constants vs C ref on become
- added list-tags
- rearranged common options to utils/cli.py
- added generic validate for both vault and become conflicts
- removed dupes and conflicting options
This commit is contained in:
Brian Coca 2015-03-21 00:35:56 -04:00
parent 93c9803818
commit b370728439
3 changed files with 63 additions and 49 deletions

View file

@ -38,7 +38,7 @@ class SortedOptParser(optparse.OptionParser):
self.option_list.sort(key=operator.methodcaller('get_opt_string')) self.option_list.sort(key=operator.methodcaller('get_opt_string'))
return optparse.OptionParser.format_help(self, formatter=None) return optparse.OptionParser.format_help(self, formatter=None)
def base_parser(usage="", output_opts=False, runas_opts=False, def base_parser(usage="", output_opts=False, runas_opts=False, meta_opts=False,
async_opts=False, connect_opts=False, subset_opts=False, check_opts=False, diff_opts=False): async_opts=False, connect_opts=False, subset_opts=False, check_opts=False, diff_opts=False):
''' create an options parser for any ansible script ''' ''' create an options parser for any ansible script '''
@ -52,7 +52,7 @@ def base_parser(usage="", output_opts=False, runas_opts=False,
help="specify inventory host file (default=%s)" % C.DEFAULT_HOST_LIST, help="specify inventory host file (default=%s)" % C.DEFAULT_HOST_LIST,
default=C.DEFAULT_HOST_LIST) default=C.DEFAULT_HOST_LIST)
parser.add_option('-k', '--ask-pass', default=False, dest='ask_pass', action='store_true', parser.add_option('-k', '--ask-pass', default=False, dest='ask_pass', action='store_true',
help='ask for SSH password') help='ask for connection password')
parser.add_option('--private-key', default=C.DEFAULT_PRIVATE_KEY_FILE, dest='private_key_file', parser.add_option('--private-key', default=C.DEFAULT_PRIVATE_KEY_FILE, dest='private_key_file',
help='use this file to authenticate the connection') help='use this file to authenticate the connection')
parser.add_option('--ask-vault-pass', default=False, dest='ask_vault_pass', action='store_true', parser.add_option('--ask-vault-pass', default=False, dest='ask_vault_pass', action='store_true',
@ -64,14 +64,16 @@ def base_parser(usage="", output_opts=False, runas_opts=False,
parser.add_option('-M', '--module-path', dest='module_path', parser.add_option('-M', '--module-path', dest='module_path',
help="specify path(s) to module library (default=%s)" % C.DEFAULT_MODULE_PATH, help="specify path(s) to module library (default=%s)" % C.DEFAULT_MODULE_PATH,
default=None) default=None)
parser.add_option('-e', '--extra-vars', dest="extra_vars", action="append",
help="set additional variables as key=value or YAML/JSON", default=[])
if subset_opts: if subset_opts:
parser.add_option('-l', '--limit', default=C.DEFAULT_SUBSET, dest='subset', parser.add_option('-l', '--limit', default=C.DEFAULT_SUBSET, dest='subset',
help='further limit selected hosts to an additional pattern') help='further limit selected hosts to an additional pattern')
parser.add_option('-t', '--tags', dest='tags', default='all',
parser.add_option('-T', '--timeout', default=C.DEFAULT_TIMEOUT, type='int', help="only run plays and tasks tagged with these values")
dest='timeout', parser.add_option('--skip-tags', dest='skip_tags',
help="override the SSH timeout in seconds (default=%s)" % C.DEFAULT_TIMEOUT) help="only run plays and tasks whose tags do not match these values")
if output_opts: if output_opts:
parser.add_option('-o', '--one-line', dest='one_line', action='store_true', parser.add_option('-o', '--one-line', dest='one_line', action='store_true',
@ -85,28 +87,32 @@ def base_parser(usage="", output_opts=False, runas_opts=False,
help='ask for sudo password (deprecated, use become)') help='ask for sudo password (deprecated, use become)')
parser.add_option('--ask-su-pass', default=False, dest='ask_su_pass', action='store_true', parser.add_option('--ask-su-pass', default=False, dest='ask_su_pass', action='store_true',
help='ask for su password (deprecated, use become)') help='ask for su password (deprecated, use become)')
parser.add_option("-s", "--sudo", default=constants.DEFAULT_SUDO, action="store_true", dest='sudo', parser.add_option("-s", "--sudo", default=C.DEFAULT_SUDO, action="store_true", dest='sudo',
help="run operations with sudo (nopasswd) (deprecated, use become)") help="run operations with sudo (nopasswd) (deprecated, use become)")
parser.add_option('-U', '--sudo-user', dest='sudo_user', default=None, parser.add_option('-U', '--sudo-user', dest='sudo_user', default=None,
help='desired sudo user (default=root) (deprecated, use become)') help='desired sudo user (default=root) (deprecated, use become)')
parser.add_option('-S', '--su', default=constants.DEFAULT_SU, action='store_true', parser.add_option('-S', '--su', default=C.DEFAULT_SU, action='store_true',
help='run operations with su (deprecated, use become)') help='run operations with su (deprecated, use become)')
parser.add_option('-R', '--su-user', default=None, parser.add_option('-R', '--su-user', default=None,
help='run operations with su as this user (default=%s) (deprecated, use become)' % constants.DEFAULT_SU_USER) help='run operations with su as this user (default=%s) (deprecated, use become)' % C.DEFAULT_SU_USER)
# consolidated privilege escalation (become) # consolidated privilege escalation (become)
parser.add_option("-b", "--become", default=constants.DEFAULT_BECOME, action="store_true", dest='become', parser.add_option("-b", "--become", default=C.DEFAULT_BECOME, action="store_true", dest='become',
help="run operations with become (nopasswd implied)") help="run operations with become (nopasswd implied)")
parser.add_option('--become-method', dest='become_method', default=constants.DEFAULT_BECOME_METHOD, type='string', parser.add_option('--become-method', dest='become_method', default=C.DEFAULT_BECOME_METHOD, type='string',
help="privilege escalation method to use (default=%s), valid choices: [ %s ]" % (constants.DEFAULT_BECOME_METHOD, ' | '.join(constants.BECOME_METHODS))) help="privilege escalation method to use (default=%s), valid choices: [ %s ]" % (C.DEFAULT_BECOME_METHOD, ' | '.join(C.BECOME_METHODS)))
parser.add_option('--become-user', default=None, dest='become_user', type='string', parser.add_option('--become-user', default=None, dest='become_user', type='string',
help='run operations as this user (default=%s)' % constants.DEFAULT_BECOME_USER) help='run operations as this user (default=%s)' % C.DEFAULT_BECOME_USER)
parser.add_option('--ask-become-pass', default=False, dest='become_ask_pass', action='store_true', parser.add_option('--ask-become-pass', default=False, dest='become_ask_pass', action='store_true',
help='ask for privilege escalation password') help='ask for privilege escalation password')
if connect_opts: if connect_opts:
parser.add_option('-c', '--connection', dest='connection', default=C.DEFAULT_TRANSPORT, help="connection type to use (default=%s)" % C.DEFAULT_TRANSPORT) parser.add_option('-c', '--connection', dest='connection', default=C.DEFAULT_TRANSPORT,
help="connection type to use (default=%s)" % C.DEFAULT_TRANSPORT)
parser.add_option('-T', '--timeout', default=C.DEFAULT_TIMEOUT, type='int', dest='timeout',
help="override the connection timeout in seconds (default=%s)" % C.DEFAULT_TIMEOUT)
if async_opts: if async_opts:
parser.add_option('-P', '--poll', default=C.DEFAULT_POLL_INTERVAL, type='int', parser.add_option('-P', '--poll', default=C.DEFAULT_POLL_INTERVAL, type='int',
@ -117,14 +123,20 @@ def base_parser(usage="", output_opts=False, runas_opts=False,
if check_opts: if check_opts:
parser.add_option("-C", "--check", default=False, dest='check', action='store_true', parser.add_option("-C", "--check", default=False, dest='check', action='store_true',
help="don't make any changes; instead, try to predict some of the changes that may occur" help="don't make any changes; instead, try to predict some of the changes that may occur")
) parser.add_option('--syntax-check', dest='syntax', action='store_true',
help="perform a syntax check on the playbook, but do not execute it")
if diff_opts: if diff_opts:
parser.add_option("-D", "--diff", default=False, dest='diff', action='store_true', parser.add_option("-D", "--diff", default=False, dest='diff', action='store_true',
help="when changing (small) files and templates, show the differences in those files; works great with --check" help="when changing (small) files and templates, show the differences in those files; works great with --check"
) )
if meta_opts:
parser.add_option('--force-handlers', dest='force_handlers', action='store_true',
help="run handlers even if a task fails")
parser.add_option('--flush-cache', dest='flush_cache', action='store_true',
help="clear the fact cache")
return parser return parser
@ -219,3 +231,23 @@ def _gitinfo():
f.close() f.close()
return result return result
def validate_conflicts(parser, options):
# Check for vault related conflicts
if (options.ask_vault_pass and options.vault_password_file):
parser.error("--ask-vault-pass and --vault-password-file are mutually exclusive")
# Check for privilege escalation conflicts
if (options.su or options.su_user or options.ask_su_pass) and \
(options.sudo or options.sudo_user or options.ask_sudo_pass) or \
(options.su or options.su_user or options.ask_su_pass) and \
(options.become or options.become_user or options.become_ask_pass) or \
(options.sudo or options.sudo_user or options.ask_sudo_pass) and \
(options.become or options.become_user or options.become_ask_pass):
parser.error("Sudo arguments ('--sudo', '--sudo-user', and '--ask-sudo-pass') "
"and su arguments ('-su', '--su-user', and '--ask-su-pass') "
"and become arguments ('--become', '--become-user', and '--ask-become-pass')"
" are exclusive of each other")

View file

@ -29,7 +29,7 @@ from ansible.inventory import Inventory
from ansible.parsing import DataLoader from ansible.parsing import DataLoader
from ansible.parsing.splitter import parse_kv from ansible.parsing.splitter import parse_kv
from ansible.playbook.play import Play from ansible.playbook.play import Play
from ansible.utils.cli import base_parser from ansible.utils.cli import base_parser, validate_conflicts
from ansible.vars import VariableManager from ansible.vars import VariableManager
######################################################## ########################################################
@ -45,15 +45,14 @@ class Cli(object):
parser = base_parser( parser = base_parser(
usage='%prog <host-pattern> [options]', usage='%prog <host-pattern> [options]',
runas_opts=True, runas_opts=True,
subset_opts=True,
async_opts=True, async_opts=True,
output_opts=True, output_opts=True,
connect_opts=True, connect_opts=True,
check_opts=True, check_opts=True,
diff_opts=False,
) )
# options unique to ansible ad-hoc
parser.add_option('-a', '--args', dest='module_args', parser.add_option('-a', '--args', dest='module_args',
help="module arguments", default=C.DEFAULT_MODULE_ARGS) help="module arguments", default=C.DEFAULT_MODULE_ARGS)
parser.add_option('-m', '--module-name', dest='module_name', parser.add_option('-m', '--module-name', dest='module_name',
@ -66,15 +65,7 @@ class Cli(object):
parser.print_help() parser.print_help()
sys.exit(1) sys.exit(1)
# su and sudo command line arguments need to be mutually exclusive validate_conflicts(parser,options)
if (options.su or options.su_user or options.ask_su_pass) and \
(options.sudo or options.sudo_user or options.ask_sudo_pass):
parser.error("Sudo arguments ('--sudo', '--sudo-user', and '--ask-sudo-pass') "
"and su arguments ('-su', '--su-user', and '--ask-su-pass') are "
"mutually exclusive")
if (options.ask_vault_pass and options.vault_password_file):
parser.error("--ask-vault-pass and --vault-password-file are mutually exclusive")
return (options, args) return (options, args)
@ -113,8 +104,6 @@ class Cli(object):
variable_manager = VariableManager() variable_manager = VariableManager()
inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list=options.inventory) inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list=options.inventory)
if options.subset:
inventory.subset(options.subset)
hosts = inventory.list_hosts(pattern) hosts = inventory.list_hosts(pattern)
if len(hosts) == 0: if len(hosts) == 0:

View file

@ -12,7 +12,7 @@ from ansible.parsing import DataLoader
from ansible.parsing.splitter import parse_kv from ansible.parsing.splitter import parse_kv
from ansible.playbook import Playbook from ansible.playbook import Playbook
from ansible.playbook.task import Task from ansible.playbook.task import Task
from ansible.utils.cli import base_parser from ansible.utils.cli import base_parser, validate_conflicts
from ansible.utils.unicode import to_unicode from ansible.utils.unicode import to_unicode
from ansible.utils.vars import combine_vars from ansible.utils.vars import combine_vars
from ansible.utils.vault import read_vault_file from ansible.utils.vault import read_vault_file
@ -30,31 +30,22 @@ def main(args):
parser = base_parser( parser = base_parser(
usage = "%prog playbook.yml", usage = "%prog playbook.yml",
connect_opts=True, connect_opts=True,
meta_opts=True,
runas_opts=True, runas_opts=True,
subset_opts=True, subset_opts=True,
check_opts=True, check_opts=True,
diff_opts=True diff_opts=True,
) )
parser.add_option('--vault-password', dest="vault_password",
help="password for vault encrypted files") # ansible playbook specific opts
parser.add_option('-e', '--extra-vars', dest="extra_vars", action="append",
help="set additional variables as key=value or YAML/JSON", default=[])
parser.add_option('-t', '--tags', dest='tags', default='all',
help="only run plays and tasks tagged with these values")
parser.add_option('--skip-tags', dest='skip_tags',
help="only run plays and tasks whose tags do not match these values")
parser.add_option('--syntax-check', dest='syntax', action='store_true',
help="perform a syntax check on the playbook, but do not execute it")
parser.add_option('--list-tasks', dest='listtasks', action='store_true', parser.add_option('--list-tasks', dest='listtasks', action='store_true',
help="list all tasks that would be executed") help="list all tasks that would be executed")
parser.add_option('--step', dest='step', action='store_true', parser.add_option('--step', dest='step', action='store_true',
help="one-step-at-a-time: confirm each task before running") help="one-step-at-a-time: confirm each task before running")
parser.add_option('--start-at-task', dest='start_at', parser.add_option('--start-at-task', dest='start_at',
help="start the playbook at the task matching this name") help="start the playbook at the task matching this name")
parser.add_option('--force-handlers', dest='force_handlers', action='store_true', parser.add_option('--list-tags', dest='listtags', action='store_true',
help="run handlers even if a task fails") help="list all available tags")
parser.add_option('--flush-cache', dest='flush_cache', action='store_true',
help="clear the fact cache")
options, args = parser.parse_args(args) options, args = parser.parse_args(args)
@ -62,6 +53,8 @@ def main(args):
parser.print_help(file=sys.stderr) parser.print_help(file=sys.stderr)
return 1 return 1
validate_conflicts(parser,options)
vault_pass = None vault_pass = None
if options.ask_vault_pass: if options.ask_vault_pass:
# FIXME: prompt here # FIXME: prompt here