diff --git a/v2/ansible/cli/__init__.py b/v2/ansible/cli/__init__.py
index e1ea576301..115a2176f5 100644
--- a/v2/ansible/cli/__init__.py
+++ b/v2/ansible/cli/__init__.py
@@ -34,11 +34,12 @@ from ansible.utils.unicode import to_bytes
class SortedOptParser(optparse.OptionParser):
'''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'))
return optparse.OptionParser.format_help(self, formatter=None)
-#TODO: move many cli only functions in this file into the CLI class
class CLI(object):
''' code behind bin/ansible* programs '''
@@ -71,8 +72,7 @@ class CLI(object):
break
if not self.action:
- self.parser.print_help()
- raise AnsibleError("Missing required action")
+ raise AnsibleOptionsError("Missing required action")
def execute(self):
"""
@@ -184,36 +184,37 @@ class CLI(object):
" are exclusive of each other")
@staticmethod
- 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):
+ 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, epilog=None):
''' 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',
- help='connect as this user (default=%s)' % C.DEFAULT_REMOTE_USER)
+ # base opts
+ parser = SortedOptParser(usage, version=CLI.version("%prog"))
parser.add_option('-v','--verbose', dest='verbosity', default=0, action="count",
help="verbose mode (-vvv for more, -vvvv to enable connection debugging)")
- 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)
- parser.add_option('-i', '--inventory-file', dest='inventory',
- 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 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',
- help='outputs a list of matching hosts; does not execute anything else')
- 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 runtask_opts:
+ 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)
+ parser.add_option('-i', '--inventory-file', dest='inventory',
+ help="specify inventory host file (default=%s)" % C.DEFAULT_HOST_LIST,
+ default=C.DEFAULT_HOST_LIST)
+ parser.add_option('--list-hosts', dest='listhosts', action='store_true',
+ help='outputs a list of matching hosts; does not execute anything else')
+ 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 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:
parser.add_option('-l', '--limit', default=C.DEFAULT_SUBSET, dest='subset',
@@ -256,6 +257,12 @@ class CLI(object):
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,
help="connection type to use (default=%s)" % C.DEFAULT_TRANSPORT)
parser.add_option('-T', '--timeout', default=C.DEFAULT_TIMEOUT, type='int', dest='timeout',
@@ -292,7 +299,7 @@ class CLI(object):
def version(prog):
''' return ansible version '''
result = "{0} {1}".format(prog, __version__)
- gitinfo = _gitinfo()
+ gitinfo = CLI._gitinfo()
if gitinfo:
result = result + " {0}".format(gitinfo)
result = result + "\n configured module search path = %s" % C.DEFAULT_MODULE_PATH
@@ -369,7 +376,7 @@ class CLI(object):
def _gitinfo():
basedir = os.path.join(os.path.dirname(__file__), '..', '..', '..')
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')
if not os.path.exists(submodules):
return result
@@ -378,7 +385,7 @@ class CLI(object):
tokens = line.strip().split(' ')
if tokens[0] == 'path':
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:
submodule_info = ' not found - use git submodule update --init ' + submodule_path
result += "\n {0}: {1}".format(submodule_path, submodule_info)
diff --git a/v2/ansible/cli/adhoc.py b/v2/ansible/cli/adhoc.py
index 5b34acf13e..16c2dc9e42 100644
--- a/v2/ansible/cli/adhoc.py
+++ b/v2/ansible/cli/adhoc.py
@@ -16,17 +16,14 @@
# along with Ansible. If not, see .
########################################################
-import os
-import sys
-
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.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 CLI
+from ansible.cli import CLI
from ansible.utils.display import Display
from ansible.utils.vault import read_vault_file
from ansible.vars import VariableManager
@@ -46,6 +43,8 @@ class AdHocCLI(CLI):
output_opts=True,
connect_opts=True,
check_opts=True,
+ runtask_opts=True,
+ vault_opts=True,
)
# options unique to ansible ad-hoc
@@ -101,7 +100,7 @@ class AdHocCLI(CLI):
if self.options.listhosts:
for host in hosts:
- self.display.display(' %s' % host.name)
+ self.display.display(' %s' % host)
return 0
if self.options.module_name in C.MODULE_REQUIRE_ARGS and not self.options.module_args:
diff --git a/v2/ansible/cli/doc.py b/v2/ansible/cli/doc.py
new file mode 100644
index 0000000000..ec09cb158d
--- /dev/null
+++ b/v2/ansible/cli/doc.py
@@ -0,0 +1,83 @@
+# (c) 2014, James Tanner
+#
+# 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 .
+#
+# 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")
+
diff --git a/v2/ansible/cli/galaxy.py b/v2/ansible/cli/galaxy.py
index 76633162ed..abe85e0af8 100644
--- a/v2/ansible/cli/galaxy.py
+++ b/v2/ansible/cli/galaxy.py
@@ -40,13 +40,13 @@ from optparse import OptionParser
import ansible.constants as C
import ansible.utils
import ansible.galaxy
+from ansible.cli import CLI
from ansible.errors import AnsibleError, AnsibleOptionsError
from ansible.galaxy import Galaxy
from ansible.galaxy.api import GalaxyAPI
from ansible.galaxy.role import GalaxyRole
from ansible.playbook.role.requirement import RoleRequirement
from ansible.utils.display import Display
-from ansible.utils.cli import CLI
class GalaxyCLI(CLI):
@@ -62,18 +62,14 @@ class GalaxyCLI(CLI):
def parse(self):
''' create an options parser for bin/ansible '''
- usage = "usage: %%prog [%s] [--help] [options] ..." % "|".join(self.VALID_ACTIONS)
- epilog = "\nSee '%s --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 = CLI.base_parser(
+ usage = "usage: %%prog [%s] [--help] [options] ..." % "|".join(self.VALID_ACTIONS),
+ epilog = "\nSee '%s --help' for more information on a specific command.\n\n" % os.path.basename(sys.argv[0])
+ )
+
- self.parser = parser
self.set_action()
- # verbose
- 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
if self.action == "info":
self.parser.set_usage("usage: %prog info [options] role_name[,version]")
diff --git a/v2/ansible/cli/playbook.py b/v2/ansible/cli/playbook.py
index e7666682e3..c2b881d2b6 100644
--- a/v2/ansible/cli/playbook.py
+++ b/v2/ansible/cli/playbook.py
@@ -23,6 +23,7 @@ import stat
import sys
from ansible import constants as C
+from ansible.cli import CLI
from ansible.errors import AnsibleError
from ansible.executor.playbook_executor import PlaybookExecutor
from ansible.inventory import Inventory
@@ -30,7 +31,6 @@ 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 CLI
from ansible.utils.display import Display
from ansible.utils.unicode import to_unicode
from ansible.utils.vars import combine_vars
@@ -53,6 +53,8 @@ class PlaybookCLI(CLI):
subset_opts=True,
check_opts=True,
diff_opts=True,
+ runtask_opts=True,
+ vault_opts=True,
)
# ansible playbook specific opts
@@ -68,8 +70,7 @@ class PlaybookCLI(CLI):
self.options, self.args = parser.parse_args()
if len(self.args) == 0:
- parser.print_help(file=sys.stderr)
- raise AnsibleError("You must specify a playbook file to run")
+ raise AnsibleOptionsError("You must specify a playbook file to run")
self.parser = parser
diff --git a/v2/ansible/cli/pull.py b/v2/ansible/cli/pull.py
new file mode 100644
index 0000000000..65741e9544
--- /dev/null
+++ b/v2/ansible/cli/pull.py
@@ -0,0 +1,69 @@
+# (c) 2012, Michael DeHaan
+#
+# 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 .
+
+########################################################
+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 [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")
diff --git a/v2/ansible/cli/vault.py b/v2/ansible/cli/vault.py
index 62ec5a373b..6231f74332 100644
--- a/v2/ansible/cli/vault.py
+++ b/v2/ansible/cli/vault.py
@@ -20,9 +20,10 @@ import os
import sys
import traceback
+from ansible import constants as C
from ansible.errors import AnsibleError, AnsibleOptionsError
from ansible.parsing.vault import VaultEditor
-from ansible.utils.cli import CLI
+from ansible.cli import CLI
from ansible.utils.display import Display
class VaultCLI(CLI):
@@ -34,13 +35,14 @@ class VaultCLI(CLI):
def __init__(self, args, display=None):
self.vault_pass = None
- super(VaultCli, self).__init__(args, display)
+ super(VaultCLI, self).__init__(args, display)
def parse(self):
- # create parser for CLI options
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 --help' for more information on a specific command.\n\n" % os.path.basename(sys.argv[0])
)
self.set_action()
@@ -60,10 +62,10 @@ class VaultCLI(CLI):
self.parser.set_usage("usage: %prog rekey [options] file_name")
self.options, self.args = self.parser.parse_args()
+ self.display.verbosity = self.options.verbosity
if len(self.args) == 0 or len(self.args) > 1:
- self.parser.print_help()
- raise AnsibleError("Vault requires a single filename as a parameter")
+ raise AnsibleOptionsError("Vault requires a single filename as a parameter")
def run(self):
diff --git a/v2/bin/ansible b/v2/bin/ansible
new file mode 100755
index 0000000000..467dd505a2
--- /dev/null
+++ b/v2/bin/ansible
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+
+# (c) 2012, Michael DeHaan
+#
+# 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 .
+
+########################################################
+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)
diff --git a/v2/bin/ansible-doc b/v2/bin/ansible-doc
new file mode 120000
index 0000000000..cabb1f519a
--- /dev/null
+++ b/v2/bin/ansible-doc
@@ -0,0 +1 @@
+ansible
\ No newline at end of file
diff --git a/v2/bin/ansible-galaxy b/v2/bin/ansible-galaxy
new file mode 120000
index 0000000000..cabb1f519a
--- /dev/null
+++ b/v2/bin/ansible-galaxy
@@ -0,0 +1 @@
+ansible
\ No newline at end of file
diff --git a/v2/bin/ansible-playbook b/v2/bin/ansible-playbook
new file mode 120000
index 0000000000..cabb1f519a
--- /dev/null
+++ b/v2/bin/ansible-playbook
@@ -0,0 +1 @@
+ansible
\ No newline at end of file
diff --git a/v2/bin/ansible-pull b/v2/bin/ansible-pull
new file mode 120000
index 0000000000..cabb1f519a
--- /dev/null
+++ b/v2/bin/ansible-pull
@@ -0,0 +1 @@
+ansible
\ No newline at end of file
diff --git a/v2/bin/ansible-vault b/v2/bin/ansible-vault
new file mode 120000
index 0000000000..cabb1f519a
--- /dev/null
+++ b/v2/bin/ansible-vault
@@ -0,0 +1 @@
+ansible
\ No newline at end of file