From b370728439b17de1265f6c9227f151dec803bc75 Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Sat, 21 Mar 2015 00:35:56 -0400 Subject: [PATCH] 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 --- v2/ansible/utils/cli.py | 64 ++++++++++++++++++++++++++++++----------- v2/bin/ansible | 23 ++++----------- v2/bin/ansible-playbook | 25 ++++++---------- 3 files changed, 63 insertions(+), 49 deletions(-) diff --git a/v2/ansible/utils/cli.py b/v2/ansible/utils/cli.py index 6ef416b974..3b899e49c5 100644 --- a/v2/ansible/utils/cli.py +++ b/v2/ansible/utils/cli.py @@ -38,7 +38,7 @@ class SortedOptParser(optparse.OptionParser): self.option_list.sort(key=operator.methodcaller('get_opt_string')) 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): ''' 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, default=C.DEFAULT_HOST_LIST) 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', help='use this file to authenticate the connection') 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', help="specify path(s) to module library (default=%s)" % C.DEFAULT_MODULE_PATH, 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: parser.add_option('-l', '--limit', default=C.DEFAULT_SUBSET, dest='subset', help='further limit selected hosts to an additional pattern') - - parser.add_option('-T', '--timeout', default=C.DEFAULT_TIMEOUT, type='int', - dest='timeout', - help="override the SSH timeout in seconds (default=%s)" % C.DEFAULT_TIMEOUT) + 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") if output_opts: 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)') parser.add_option('--ask-su-pass', default=False, dest='ask_su_pass', action='store_true', 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)") parser.add_option('-U', '--sudo-user', dest='sudo_user', default=None, 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)') 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) - 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)") - parser.add_option('--become-method', dest='become_method', default=constants.DEFAULT_BECOME_METHOD, type='string', - help="privilege escalation method to use (default=%s), valid choices: [ %s ]" % (constants.DEFAULT_BECOME_METHOD, ' | '.join(constants.BECOME_METHODS))) + 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 ]" % (C.DEFAULT_BECOME_METHOD, ' | '.join(C.BECOME_METHODS))) 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', help='ask for privilege escalation password') 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: 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: 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: 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" ) + 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 @@ -219,3 +231,23 @@ def _gitinfo(): f.close() 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") + diff --git a/v2/bin/ansible b/v2/bin/ansible index c51040c6a8..1e298623f5 100755 --- a/v2/bin/ansible +++ b/v2/bin/ansible @@ -29,7 +29,7 @@ from ansible.inventory import Inventory from ansible.parsing import DataLoader from ansible.parsing.splitter import parse_kv 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 ######################################################## @@ -45,15 +45,14 @@ class Cli(object): parser = base_parser( usage='%prog [options]', - runas_opts=True, - subset_opts=True, + runas_opts=True, async_opts=True, - output_opts=True, - connect_opts=True, + output_opts=True, + connect_opts=True, check_opts=True, - diff_opts=False, ) + # options unique to ansible ad-hoc parser.add_option('-a', '--args', dest='module_args', help="module arguments", default=C.DEFAULT_MODULE_ARGS) parser.add_option('-m', '--module-name', dest='module_name', @@ -66,15 +65,7 @@ class Cli(object): parser.print_help() sys.exit(1) - # su and sudo command line arguments need to be mutually exclusive - 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") + validate_conflicts(parser,options) return (options, args) @@ -113,8 +104,6 @@ class Cli(object): variable_manager = VariableManager() inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list=options.inventory) - if options.subset: - inventory.subset(options.subset) hosts = inventory.list_hosts(pattern) if len(hosts) == 0: diff --git a/v2/bin/ansible-playbook b/v2/bin/ansible-playbook index bdd9598ec8..26bbe14c7a 100755 --- a/v2/bin/ansible-playbook +++ b/v2/bin/ansible-playbook @@ -12,7 +12,7 @@ from ansible.parsing import DataLoader from ansible.parsing.splitter import parse_kv from ansible.playbook import Playbook 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.vars import combine_vars from ansible.utils.vault import read_vault_file @@ -30,31 +30,22 @@ def main(args): parser = base_parser( usage = "%prog playbook.yml", connect_opts=True, + meta_opts=True, runas_opts=True, subset_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") - 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") + + # ansible playbook specific opts parser.add_option('--list-tasks', dest='listtasks', action='store_true', help="list all tasks that would be executed") parser.add_option('--step', dest='step', action='store_true', help="one-step-at-a-time: confirm each task before running") parser.add_option('--start-at-task', dest='start_at', help="start the playbook at the task matching this name") - 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") + parser.add_option('--list-tags', dest='listtags', action='store_true', + help="list all available tags") options, args = parser.parse_args(args) @@ -62,6 +53,8 @@ def main(args): parser.print_help(file=sys.stderr) return 1 + validate_conflicts(parser,options) + vault_pass = None if options.ask_vault_pass: # FIXME: prompt here