Commit 9de6fea2 by Brian Coca

one cli to bind them all

parent 040a39f2
...@@ -34,11 +34,12 @@ from ansible.utils.unicode import to_bytes ...@@ -34,11 +34,12 @@ from ansible.utils.unicode import to_bytes
class SortedOptParser(optparse.OptionParser): class SortedOptParser(optparse.OptionParser):
'''Optparser which sorts the options by opt before outputting --help''' '''Optparser which sorts the options by opt before outputting --help'''
def format_help(self, formatter=None): #FIXME: epilog parsing: OptionParser.format_epilog = lambda self, formatter: self.epilog
def format_help(self, formatter=None, epilog=None):
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)
#TODO: move many cli only functions in this file into the CLI class
class CLI(object): class CLI(object):
''' code behind bin/ansible* programs ''' ''' code behind bin/ansible* programs '''
...@@ -71,8 +72,7 @@ class CLI(object): ...@@ -71,8 +72,7 @@ class CLI(object):
break break
if not self.action: if not self.action:
self.parser.print_help() raise AnsibleOptionsError("Missing required action")
raise AnsibleError("Missing required action")
def execute(self): def execute(self):
""" """
...@@ -184,37 +184,38 @@ class CLI(object): ...@@ -184,37 +184,38 @@ class CLI(object):
" are exclusive of each other") " are exclusive of each other")
@staticmethod @staticmethod
def base_parser(usage="", output_opts=False, runas_opts=False, meta_opts=False, def base_parser(usage="", output_opts=False, runas_opts=False, meta_opts=False, runtask_opts=False, vault_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, epilog=None):
''' create an options parser for most ansible scripts ''' ''' create an options parser for most ansible scripts '''
parser = SortedOptParser(usage, version=CLI.version("%prog")) #FIXME: implemente epilog parsing
#OptionParser.format_epilog = lambda self, formatter: self.epilog
parser.add_option('-u', '--user', default=C.DEFAULT_REMOTE_USER, dest='remote_user', # base opts
help='connect as this user (default=%s)' % C.DEFAULT_REMOTE_USER) parser = SortedOptParser(usage, version=CLI.version("%prog"))
parser.add_option('-v','--verbose', dest='verbosity', default=0, action="count", parser.add_option('-v','--verbose', dest='verbosity', default=0, action="count",
help="verbose mode (-vvv for more, -vvvv to enable connection debugging)") help="verbose mode (-vvv for more, -vvvv to enable connection debugging)")
if runtask_opts:
parser.add_option('-f','--forks', dest='forks', default=C.DEFAULT_FORKS, type='int', parser.add_option('-f','--forks', dest='forks', default=C.DEFAULT_FORKS, type='int',
help="specify number of parallel processes to use (default=%s)" % C.DEFAULT_FORKS) help="specify number of parallel processes to use (default=%s)" % C.DEFAULT_FORKS)
parser.add_option('-i', '--inventory-file', dest='inventory', parser.add_option('-i', '--inventory-file', dest='inventory',
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',
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',
help='ask for vault password')
parser.add_option('--vault-password-file', default=C.DEFAULT_VAULT_PASSWORD_FILE,
dest='vault_password_file', help="vault password file")
parser.add_option('--list-hosts', dest='listhosts', action='store_true', parser.add_option('--list-hosts', dest='listhosts', action='store_true',
help='outputs a list of matching hosts; does not execute anything else') help='outputs a list of matching hosts; does not execute anything else')
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", parser.add_option('-e', '--extra-vars', dest="extra_vars", action="append",
help="set additional variables as key=value or YAML/JSON", default=[]) help="set additional variables as key=value or YAML/JSON", default=[])
if vault_opts:
parser.add_option('--ask-vault-pass', default=False, dest='ask_vault_pass', action='store_true',
help='ask for vault password')
parser.add_option('--vault-password-file', default=C.DEFAULT_VAULT_PASSWORD_FILE,
dest='vault_password_file', help="vault password file")
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')
...@@ -256,6 +257,12 @@ class CLI(object): ...@@ -256,6 +257,12 @@ class CLI(object):
if connect_opts: if connect_opts:
parser.add_option('-k', '--ask-pass', default=False, dest='ask_pass', action='store_true',
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('-u', '--user', default=C.DEFAULT_REMOTE_USER, dest='remote_user',
help='connect as this user (default=%s)' % C.DEFAULT_REMOTE_USER)
parser.add_option('-c', '--connection', dest='connection', default=C.DEFAULT_TRANSPORT, parser.add_option('-c', '--connection', dest='connection', default=C.DEFAULT_TRANSPORT,
help="connection type to use (default=%s)" % 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', parser.add_option('-T', '--timeout', default=C.DEFAULT_TIMEOUT, type='int', dest='timeout',
...@@ -292,7 +299,7 @@ class CLI(object): ...@@ -292,7 +299,7 @@ class CLI(object):
def version(prog): def version(prog):
''' return ansible version ''' ''' return ansible version '''
result = "{0} {1}".format(prog, __version__) result = "{0} {1}".format(prog, __version__)
gitinfo = _gitinfo() gitinfo = CLI._gitinfo()
if gitinfo: if gitinfo:
result = result + " {0}".format(gitinfo) result = result + " {0}".format(gitinfo)
result = result + "\n configured module search path = %s" % C.DEFAULT_MODULE_PATH result = result + "\n configured module search path = %s" % C.DEFAULT_MODULE_PATH
...@@ -369,7 +376,7 @@ class CLI(object): ...@@ -369,7 +376,7 @@ class CLI(object):
def _gitinfo(): def _gitinfo():
basedir = os.path.join(os.path.dirname(__file__), '..', '..', '..') basedir = os.path.join(os.path.dirname(__file__), '..', '..', '..')
repo_path = os.path.join(basedir, '.git') repo_path = os.path.join(basedir, '.git')
result = _git_repo_info(repo_path) result = CLI._git_repo_info(repo_path)
submodules = os.path.join(basedir, '.gitmodules') submodules = os.path.join(basedir, '.gitmodules')
if not os.path.exists(submodules): if not os.path.exists(submodules):
return result return result
...@@ -378,7 +385,7 @@ class CLI(object): ...@@ -378,7 +385,7 @@ class CLI(object):
tokens = line.strip().split(' ') tokens = line.strip().split(' ')
if tokens[0] == 'path': if tokens[0] == 'path':
submodule_path = tokens[2] submodule_path = tokens[2]
submodule_info =_git_repo_info(os.path.join(basedir, submodule_path, '.git')) submodule_info = CLI._git_repo_info(os.path.join(basedir, submodule_path, '.git'))
if not submodule_info: if not submodule_info:
submodule_info = ' not found - use git submodule update --init ' + submodule_path submodule_info = ' not found - use git submodule update --init ' + submodule_path
result += "\n {0}: {1}".format(submodule_path, submodule_info) result += "\n {0}: {1}".format(submodule_path, submodule_info)
......
...@@ -16,17 +16,14 @@ ...@@ -16,17 +16,14 @@
# along with Ansible. If not, see <http://www.gnu.org/licenses/>. # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
######################################################## ########################################################
import os
import sys
from ansible import constants as C from ansible import constants as C
from ansible.errors import * from ansible.errors import AnsibleError, AnsibleOptionsError
from ansible.executor.task_queue_manager import TaskQueueManager from ansible.executor.task_queue_manager import TaskQueueManager
from ansible.inventory import Inventory 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 CLI from ansible.cli import CLI
from ansible.utils.display import Display from ansible.utils.display import Display
from ansible.utils.vault import read_vault_file from ansible.utils.vault import read_vault_file
from ansible.vars import VariableManager from ansible.vars import VariableManager
...@@ -46,6 +43,8 @@ class AdHocCLI(CLI): ...@@ -46,6 +43,8 @@ class AdHocCLI(CLI):
output_opts=True, output_opts=True,
connect_opts=True, connect_opts=True,
check_opts=True, check_opts=True,
runtask_opts=True,
vault_opts=True,
) )
# options unique to ansible ad-hoc # options unique to ansible ad-hoc
...@@ -101,7 +100,7 @@ class AdHocCLI(CLI): ...@@ -101,7 +100,7 @@ class AdHocCLI(CLI):
if self.options.listhosts: if self.options.listhosts:
for host in hosts: for host in hosts:
self.display.display(' %s' % host.name) self.display.display(' %s' % host)
return 0 return 0
if self.options.module_name in C.MODULE_REQUIRE_ARGS and not self.options.module_args: if self.options.module_name in C.MODULE_REQUIRE_ARGS and not self.options.module_args:
......
# (c) 2014, James Tanner <tanner.jc@gmail.com>
#
# Ansible 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 3 of the License, or
# (at your option) any later version.
#
# Ansible 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.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
# ansible-vault is a script that encrypts/decrypts YAML files. See
# http://docs.ansible.com/playbooks_vault.html for more details.
import os
import sys
import traceback
from ansible import constants as C
from ansible.errors import AnsibleError, AnsibleOptionsError
from ansible.cli import CLI
#from ansible.utils import module_docs
class DocCLI(CLI):
""" Vault command line class """
BLACKLIST_EXTS = ('.pyc', '.swp', '.bak', '~', '.rpm')
IGNORE_FILES = [ "COPYING", "CONTRIBUTING", "LICENSE", "README", "VERSION"]
_ITALIC = re.compile(r"I\(([^)]+)\)")
_BOLD = re.compile(r"B\(([^)]+)\)")
_MODULE = re.compile(r"M\(([^)]+)\)")
_URL = re.compile(r"U\(([^)]+)\)")
_CONST = re.compile(r"C\(([^)]+)\)")
PAGER = 'less'
LESS_OPTS = 'FRSX' # -F (quit-if-one-screen) -R (allow raw ansi control chars)
# -S (chop long lines) -X (disable termcap init and de-init)
def parse(self):
self.parser = optparse.OptionParser(
version=version("%prog"),
usage='usage: %prog [options] [module...]',
description='Show Ansible module documentation',
)
self.parser.add_option("-M", "--module-path", action="store", dest="module_path", default=C.DEFAULT_MODULE_PATH,
help="Ansible modules/ directory")
self.parser.add_option("-l", "--list", action="store_true", default=False, dest='list_dir',
help='List available modules')
self.parser.add_option("-s", "--snippet", action="store_true", default=False, dest='show_snippet',
help='Show playbook snippet for specified module(s)')
self.parser.add_option('-v', action='version', help='Show version number and exit')
self.options, self.args = self.parser.parse_args()
self.display.verbosity = self.options.verbosity
def run(self):
if options.module_path is not None:
for i in options.module_path.split(os.pathsep):
utils.plugins.module_finder.add_directory(i)
if options.list_dir:
# list modules
paths = utils.plugins.module_finder._get_paths()
module_list = []
for path in paths:
find_modules(path, module_list)
pager(get_module_list_text(module_list))
if len(args) == 0:
raise AnsibleOptionsError("Incorrect options passed")
...@@ -40,13 +40,13 @@ from optparse import OptionParser ...@@ -40,13 +40,13 @@ from optparse import OptionParser
import ansible.constants as C import ansible.constants as C
import ansible.utils import ansible.utils
import ansible.galaxy import ansible.galaxy
from ansible.cli import CLI
from ansible.errors import AnsibleError, AnsibleOptionsError from ansible.errors import AnsibleError, AnsibleOptionsError
from ansible.galaxy import Galaxy from ansible.galaxy import Galaxy
from ansible.galaxy.api import GalaxyAPI from ansible.galaxy.api import GalaxyAPI
from ansible.galaxy.role import GalaxyRole from ansible.galaxy.role import GalaxyRole
from ansible.playbook.role.requirement import RoleRequirement from ansible.playbook.role.requirement import RoleRequirement
from ansible.utils.display import Display from ansible.utils.display import Display
from ansible.utils.cli import CLI
class GalaxyCLI(CLI): class GalaxyCLI(CLI):
...@@ -62,17 +62,13 @@ class GalaxyCLI(CLI): ...@@ -62,17 +62,13 @@ class GalaxyCLI(CLI):
def parse(self): def parse(self):
''' create an options parser for bin/ansible ''' ''' create an options parser for bin/ansible '''
usage = "usage: %%prog [%s] [--help] [options] ..." % "|".join(self.VALID_ACTIONS) self.parser = CLI.base_parser(
usage = "usage: %%prog [%s] [--help] [options] ..." % "|".join(self.VALID_ACTIONS),
epilog = "\nSee '%s <command> --help' for more information on a specific command.\n\n" % os.path.basename(sys.argv[0]) epilog = "\nSee '%s <command> --help' for more information on a specific command.\n\n" % os.path.basename(sys.argv[0])
OptionParser.format_epilog = lambda self, formatter: self.epilog )
parser = OptionParser(usage=usage, epilog=epilog)
self.parser = parser
self.set_action()
# verbose self.set_action()
self.parser.add_option('-v','--verbose', dest='verbosity', default=0, action="count",
help="verbose mode (-vvv for more, -vvvv to enable connection debugging)")
# options specific to actions # options specific to actions
if self.action == "info": if self.action == "info":
......
...@@ -23,6 +23,7 @@ import stat ...@@ -23,6 +23,7 @@ import stat
import sys import sys
from ansible import constants as C from ansible import constants as C
from ansible.cli import CLI
from ansible.errors import AnsibleError from ansible.errors import AnsibleError
from ansible.executor.playbook_executor import PlaybookExecutor from ansible.executor.playbook_executor import PlaybookExecutor
from ansible.inventory import Inventory from ansible.inventory import Inventory
...@@ -30,7 +31,6 @@ from ansible.parsing import DataLoader ...@@ -30,7 +31,6 @@ 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 CLI
from ansible.utils.display import Display from ansible.utils.display import Display
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
...@@ -53,6 +53,8 @@ class PlaybookCLI(CLI): ...@@ -53,6 +53,8 @@ class PlaybookCLI(CLI):
subset_opts=True, subset_opts=True,
check_opts=True, check_opts=True,
diff_opts=True, diff_opts=True,
runtask_opts=True,
vault_opts=True,
) )
# ansible playbook specific opts # ansible playbook specific opts
...@@ -68,8 +70,7 @@ class PlaybookCLI(CLI): ...@@ -68,8 +70,7 @@ class PlaybookCLI(CLI):
self.options, self.args = parser.parse_args() self.options, self.args = parser.parse_args()
if len(self.args) == 0: if len(self.args) == 0:
parser.print_help(file=sys.stderr) raise AnsibleOptionsError("You must specify a playbook file to run")
raise AnsibleError("You must specify a playbook file to run")
self.parser = parser self.parser = parser
......
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
#
# This file is part of Ansible
#
# Ansible 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 3 of the License, or
# (at your option) any later version.
#
# Ansible 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.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
########################################################
import os
import sys
from ansible import constants as C
from ansible.errors import *
from ansible.cli import CLI
from ansible.executor.task_queue_manager import TaskQueueManager
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.display import Display
from ansible.utils.vault import read_vault_file
from ansible.vars import VariableManager
########################################################
class PullCLI(CLI):
''' code behind ansible ad-hoc cli'''
def parse(self):
''' create an options parser for bin/ansible '''
self.parser = CLI.base_parser(
usage='%prog <host-pattern> [options]',
runas_opts=True,
async_opts=True,
output_opts=True,
connect_opts=True,
check_opts=True,
runtask_opts=True,
vault_opts=True,
)
# options unique to pull
self.options, self.args = self.parser.parse_args()
if len(self.args) != 1:
raise AnsibleOptionsError("Missing target hosts")
self.display.verbosity = self.options.verbosity
self.validate_conflicts()
return True
def run(self):
''' use Runner lib to do SSH things '''
raise AnsibleError("Not ported to v2 yet")
...@@ -20,9 +20,10 @@ import os ...@@ -20,9 +20,10 @@ import os
import sys import sys
import traceback import traceback
from ansible import constants as C
from ansible.errors import AnsibleError, AnsibleOptionsError from ansible.errors import AnsibleError, AnsibleOptionsError
from ansible.parsing.vault import VaultEditor from ansible.parsing.vault import VaultEditor
from ansible.utils.cli import CLI from ansible.cli import CLI
from ansible.utils.display import Display from ansible.utils.display import Display
class VaultCLI(CLI): class VaultCLI(CLI):
...@@ -34,13 +35,14 @@ class VaultCLI(CLI): ...@@ -34,13 +35,14 @@ class VaultCLI(CLI):
def __init__(self, args, display=None): def __init__(self, args, display=None):
self.vault_pass = None self.vault_pass = None
super(VaultCli, self).__init__(args, display) super(VaultCLI, self).__init__(args, display)
def parse(self): def parse(self):
# create parser for CLI options
self.parser = CLI.base_parser( self.parser = CLI.base_parser(
usage = "%prog vaultfile.yml", vault_opts=True,
usage = "usage: %%prog [%s] [--help] [options] vaultfile.yml" % "|".join(self.VALID_ACTIONS),
epilog = "\nSee '%s <command> --help' for more information on a specific command.\n\n" % os.path.basename(sys.argv[0])
) )
self.set_action() self.set_action()
...@@ -60,10 +62,10 @@ class VaultCLI(CLI): ...@@ -60,10 +62,10 @@ class VaultCLI(CLI):
self.parser.set_usage("usage: %prog rekey [options] file_name") self.parser.set_usage("usage: %prog rekey [options] file_name")
self.options, self.args = self.parser.parse_args() self.options, self.args = self.parser.parse_args()
self.display.verbosity = self.options.verbosity
if len(self.args) == 0 or len(self.args) > 1: if len(self.args) == 0 or len(self.args) > 1:
self.parser.print_help() raise AnsibleOptionsError("Vault requires a single filename as a parameter")
raise AnsibleError("Vault requires a single filename as a parameter")
def run(self): def run(self):
......
#!/usr/bin/env python
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
#
# This file is part of Ansible
#
# Ansible 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 3 of the License, or
# (at your option) any later version.
#
# Ansible 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.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
########################################################
from __future__ import (absolute_import)
__metaclass__ = type
__requires__ = ['ansible']
try:
import pkg_resources
except Exception:
# Use pkg_resources to find the correct versions of libraries and set
# sys.path appropriately when there are multiversion installs. But we
# have code that better expresses the errors in the places where the code
# is actually used (the deps are optional for many code paths) so we don't
# want to fail here.
pass
import os
import sys
from ansible.errors import AnsibleError, AnsibleOptionsError
from ansible.utils.display import Display
########################################################
if __name__ == '__main__':
cli = None
display = Display()
me = os.path.basename(__file__)
try:
if me == 'ansible-playbook':
from ansible.cli.playbook import PlaybookCLI as mycli
elif me == 'ansible':
from ansible.cli.adhoc import AdHocCLI as mycli
elif me == 'ansible-pull':
from ansible.cli.pull import PullCLI as mycli
elif me == 'ansible-doc':
from ansible.cli.doc import DocCLI as mycli
elif me == 'ansible-vault':
from ansible.cli.vault import VaultCLI as mycli
elif me == 'ansible-galaxy':
from ansible.cli.galaxy import GalaxyCLI as mycli
cli = mycli(sys.argv, display=display)
if cli:
cli.parse()
sys.exit(cli.run())
else:
raise AnsibleError("Program not implemented: %s" % me)
except AnsibleOptionsError as e:
cli.parser.print_help()
display.display(str(e), stderr=True, color='red')
sys.exit(1)
except AnsibleError as e:
display.display(str(e), stderr=True, color='red')
sys.exit(2)
except KeyboardInterrupt:
display.error("interrupted")
sys.exit(4)
ansible
\ No newline at end of file
ansible
\ No newline at end of file
ansible
\ No newline at end of file
ansible
\ No newline at end of file
ansible
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment