Migrate cli and dependencies to use global display
This commit is contained in:
parent
1b7d3f2898
commit
318bfbb207
12 changed files with 167 additions and 131 deletions
|
@ -79,7 +79,7 @@ if __name__ == '__main__':
|
|||
else:
|
||||
raise
|
||||
|
||||
cli = mycli(sys.argv, display=display)
|
||||
cli = mycli(sys.argv)
|
||||
cli.parse()
|
||||
sys.exit(cli.run())
|
||||
|
||||
|
|
|
@ -33,7 +33,14 @@ from ansible import __version__
|
|||
from ansible import constants as C
|
||||
from ansible.errors import AnsibleError, AnsibleOptionsError
|
||||
from ansible.utils.unicode import to_bytes
|
||||
from ansible.utils.display import Display
|
||||
|
||||
try:
|
||||
from __main__ import display
|
||||
display = display
|
||||
except ImportError:
|
||||
from ansible.utils.display import Display
|
||||
display = Display()
|
||||
|
||||
|
||||
class SortedOptParser(optparse.OptionParser):
|
||||
'''Optparser which sorts the options by opt before outputting --help'''
|
||||
|
@ -44,6 +51,7 @@ class SortedOptParser(optparse.OptionParser):
|
|||
self.option_list.sort(key=operator.methodcaller('get_opt_string'))
|
||||
return optparse.OptionParser.format_help(self, formatter=None)
|
||||
|
||||
|
||||
class CLI(object):
|
||||
''' code behind bin/ansible* programs '''
|
||||
|
||||
|
@ -59,7 +67,7 @@ class CLI(object):
|
|||
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 __init__(self, args, display=None):
|
||||
def __init__(self, args):
|
||||
"""
|
||||
Base init method for all command line programs
|
||||
"""
|
||||
|
@ -69,11 +77,6 @@ class CLI(object):
|
|||
self.parser = None
|
||||
self.action = None
|
||||
|
||||
if display is None:
|
||||
self.display = Display()
|
||||
else:
|
||||
self.display = display
|
||||
|
||||
def set_action(self):
|
||||
"""
|
||||
Get the action the user wants to execute from the sys argv list.
|
||||
|
@ -102,9 +105,9 @@ class CLI(object):
|
|||
|
||||
if self.options.verbosity > 0:
|
||||
if C.CONFIG_FILE:
|
||||
self.display.display("Using %s as config file" % C.CONFIG_FILE)
|
||||
display.display("Using %s as config file" % C.CONFIG_FILE)
|
||||
else:
|
||||
self.display.display("No config file found; using defaults")
|
||||
display.display("No config file found; using defaults")
|
||||
|
||||
@staticmethod
|
||||
def ask_vault_passwords(ask_new_vault_pass=False, rekey=False):
|
||||
|
@ -135,7 +138,6 @@ class CLI(object):
|
|||
|
||||
return vault_pass, new_vault_pass
|
||||
|
||||
|
||||
def ask_passwords(self):
|
||||
''' prompt for connection and become passwords if needed '''
|
||||
|
||||
|
@ -164,7 +166,6 @@ class CLI(object):
|
|||
|
||||
return (sshpass, becomepass)
|
||||
|
||||
|
||||
def normalize_become_options(self):
|
||||
''' this keeps backwards compatibility with sudo/su self.options '''
|
||||
self.options.become_ask_pass = self.options.become_ask_pass or self.options.ask_sudo_pass or self.options.ask_su_pass or C.DEFAULT_BECOME_ASK_PASS
|
||||
|
@ -179,7 +180,6 @@ class CLI(object):
|
|||
self.options.become = True
|
||||
self.options.become_method = 'su'
|
||||
|
||||
|
||||
def validate_conflicts(self, vault_opts=False, runas_opts=False, fork_opts=False):
|
||||
''' check for conflicting options '''
|
||||
|
||||
|
@ -190,7 +190,6 @@ class CLI(object):
|
|||
if (op.ask_vault_pass and op.vault_password_file):
|
||||
self.parser.error("--ask-vault-pass and --vault-password-file are mutually exclusive")
|
||||
|
||||
|
||||
if runas_opts:
|
||||
# Check for privilege escalation conflicts
|
||||
if (op.su or op.su_user or op.ask_su_pass) and \
|
||||
|
@ -215,7 +214,7 @@ class CLI(object):
|
|||
|
||||
@staticmethod
|
||||
def base_parser(usage="", output_opts=False, runas_opts=False, meta_opts=False, runtask_opts=False, vault_opts=False, module_opts=False,
|
||||
async_opts=False, connect_opts=False, subset_opts=False, check_opts=False, inventory_opts=False, epilog=None, fork_opts=False):
|
||||
async_opts=False, connect_opts=False, subset_opts=False, check_opts=False, inventory_opts=False, epilog=None, fork_opts=False):
|
||||
''' create an options parser for most ansible scripts '''
|
||||
|
||||
# TODO: implement epilog parsing
|
||||
|
@ -257,7 +256,6 @@ class CLI(object):
|
|||
parser.add_option('--output', default=None, dest='output_file',
|
||||
help='output file name for encrypt or decrypt; use - for stdout')
|
||||
|
||||
|
||||
if subset_opts:
|
||||
parser.add_option('-t', '--tags', dest='tags', default='all',
|
||||
help="only run plays and tasks tagged with these values")
|
||||
|
@ -295,7 +293,6 @@ class CLI(object):
|
|||
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('-k', '--ask-pass', default=C.DEFAULT_ASK_PASS, dest='ask_pass', action='store_true',
|
||||
help='ask for connection password')
|
||||
|
@ -427,7 +424,7 @@ class CLI(object):
|
|||
result = CLI._git_repo_info(repo_path)
|
||||
submodules = os.path.join(basedir, '.gitmodules')
|
||||
if not os.path.exists(submodules):
|
||||
return result
|
||||
return result
|
||||
f = open(submodules)
|
||||
for line in f:
|
||||
tokens = line.strip().split(' ')
|
||||
|
@ -440,21 +437,20 @@ class CLI(object):
|
|||
f.close()
|
||||
return result
|
||||
|
||||
|
||||
def pager(self, text):
|
||||
''' find reasonable way to display text '''
|
||||
# this is a much simpler form of what is in pydoc.py
|
||||
if not sys.stdout.isatty():
|
||||
self.display.display(text)
|
||||
display.display(text)
|
||||
elif 'PAGER' in os.environ:
|
||||
if sys.platform == 'win32':
|
||||
self.display.display(text)
|
||||
display.display(text)
|
||||
else:
|
||||
self.pager_pipe(text, os.environ['PAGER'])
|
||||
elif subprocess.call('(less --version) 2> /dev/null', shell = True) == 0:
|
||||
self.pager_pipe(text, 'less')
|
||||
else:
|
||||
self.display.display(text)
|
||||
display.display(text)
|
||||
|
||||
@staticmethod
|
||||
def pager_pipe(text, cmd):
|
||||
|
@ -521,4 +517,3 @@ class CLI(object):
|
|||
if os.pathsep in data:
|
||||
data = data.split(os.pathsep)[0]
|
||||
return data
|
||||
|
||||
|
|
|
@ -34,6 +34,14 @@ from ansible.plugins import get_all_plugin_loaders
|
|||
from ansible.utils.vars import load_extra_vars
|
||||
from ansible.vars import VariableManager
|
||||
|
||||
try:
|
||||
from __main__ import display
|
||||
display = display
|
||||
except ImportError:
|
||||
from ansible.utils.display import Display
|
||||
display = Display()
|
||||
|
||||
|
||||
########################################################
|
||||
|
||||
class AdHocCLI(CLI):
|
||||
|
@ -68,7 +76,7 @@ class AdHocCLI(CLI):
|
|||
if len(self.args) != 1:
|
||||
raise AnsibleOptionsError("Missing target hosts")
|
||||
|
||||
self.display.verbosity = self.options.verbosity
|
||||
display.verbosity = self.options.verbosity
|
||||
self.validate_conflicts(runas_opts=True, vault_opts=True, fork_opts=True)
|
||||
|
||||
return True
|
||||
|
@ -86,7 +94,6 @@ class AdHocCLI(CLI):
|
|||
|
||||
super(AdHocCLI, self).run()
|
||||
|
||||
|
||||
# only thing left should be host pattern
|
||||
pattern = self.args[0]
|
||||
|
||||
|
@ -121,7 +128,7 @@ class AdHocCLI(CLI):
|
|||
hosts = inventory.list_hosts(pattern)
|
||||
no_hosts = False
|
||||
if len(hosts) == 0:
|
||||
self.display.warning("provided hosts list is empty, only localhost is available")
|
||||
display.warning("provided hosts list is empty, only localhost is available")
|
||||
no_hosts = True
|
||||
|
||||
if self.options.subset:
|
||||
|
@ -131,9 +138,9 @@ class AdHocCLI(CLI):
|
|||
raise AnsibleError("Specified --limit does not match any hosts")
|
||||
|
||||
if self.options.listhosts:
|
||||
self.display.display(' hosts (%d):' % len(hosts))
|
||||
display.display(' hosts (%d):' % len(hosts))
|
||||
for host in hosts:
|
||||
self.display.display(' %s' % host)
|
||||
display.display(' %s' % host)
|
||||
return 0
|
||||
|
||||
if self.options.module_name in C.MODULE_REQUIRE_ARGS and not self.options.module_args:
|
||||
|
@ -168,7 +175,6 @@ class AdHocCLI(CLI):
|
|||
inventory=inventory,
|
||||
variable_manager=variable_manager,
|
||||
loader=loader,
|
||||
display=self.display,
|
||||
options=self.options,
|
||||
passwords=passwords,
|
||||
stdout_callback=cb,
|
||||
|
|
|
@ -31,15 +31,23 @@ from ansible.plugins import module_loader
|
|||
from ansible.cli import CLI
|
||||
from ansible.utils import module_docs
|
||||
|
||||
try:
|
||||
from __main__ import display
|
||||
display = display
|
||||
except ImportError:
|
||||
from ansible.utils.display import Display
|
||||
display = Display()
|
||||
|
||||
|
||||
class DocCLI(CLI):
|
||||
""" Vault command line class """
|
||||
|
||||
BLACKLIST_EXTS = ('.pyc', '.swp', '.bak', '~', '.rpm', '.md', '.txt')
|
||||
IGNORE_FILES = [ "COPYING", "CONTRIBUTING", "LICENSE", "README", "VERSION", "GUIDELINES", "test-docs.sh"]
|
||||
|
||||
def __init__(self, args, display=None):
|
||||
def __init__(self, args):
|
||||
|
||||
super(DocCLI, self).__init__(args, display)
|
||||
super(DocCLI, self).__init__(args)
|
||||
self.module_list = []
|
||||
|
||||
def parse(self):
|
||||
|
@ -56,8 +64,7 @@ class DocCLI(CLI):
|
|||
help='Show playbook snippet for specified module(s)')
|
||||
|
||||
self.options, self.args = self.parser.parse_args()
|
||||
self.display.verbosity = self.options.verbosity
|
||||
|
||||
display.verbosity = self.options.verbosity
|
||||
|
||||
def run(self):
|
||||
|
||||
|
@ -86,7 +93,7 @@ class DocCLI(CLI):
|
|||
try:
|
||||
filename = module_loader.find_plugin(module)
|
||||
if filename is None:
|
||||
self.display.warning("module %s not found in %s\n" % (module, DocCLI.print_paths(module_loader)))
|
||||
display.warning("module %s not found in %s\n" % (module, DocCLI.print_paths(module_loader)))
|
||||
continue
|
||||
|
||||
if any(filename.endswith(x) for x in self.BLACKLIST_EXTS):
|
||||
|
@ -95,8 +102,8 @@ class DocCLI(CLI):
|
|||
try:
|
||||
doc, plainexamples, returndocs = module_docs.get_docstring(filename, verbose=(self.options.verbosity > 0))
|
||||
except:
|
||||
self.display.vvv(traceback.print_exc())
|
||||
self.display.error("module %s has a documentation error formatting or is missing documentation\nTo see exact traceback use -vvv" % module)
|
||||
display.vvv(traceback.print_exc())
|
||||
display.error("module %s has a documentation error formatting or is missing documentation\nTo see exact traceback use -vvv" % module)
|
||||
continue
|
||||
|
||||
if doc is not None:
|
||||
|
@ -122,7 +129,7 @@ class DocCLI(CLI):
|
|||
# probably a quoting issue.
|
||||
raise AnsibleError("Parsing produced an empty object.")
|
||||
except Exception as e:
|
||||
self.display.vvv(traceback.print_exc())
|
||||
display.vvv(traceback.print_exc())
|
||||
raise AnsibleError("module %s missing documentation (or could not parse documentation): %s\n" % (module, str(e)))
|
||||
|
||||
self.pager(text)
|
||||
|
@ -150,9 +157,8 @@ class DocCLI(CLI):
|
|||
module = os.path.splitext(module)[0] # removes the extension
|
||||
self.module_list.append(module)
|
||||
|
||||
|
||||
def get_module_list_text(self):
|
||||
columns = self.display.columns
|
||||
columns = display.columns
|
||||
displace = max(len(x) for x in self.module_list)
|
||||
linelimit = columns - displace - 5
|
||||
text = []
|
||||
|
@ -189,7 +195,6 @@ class DocCLI(CLI):
|
|||
text.extend(deprecated)
|
||||
return "\n".join(text)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def print_paths(finder):
|
||||
''' Returns a string suitable for printing of the search path '''
|
||||
|
@ -209,7 +214,7 @@ class DocCLI(CLI):
|
|||
text.append(" action: %s" % (doc['module']))
|
||||
pad = 31
|
||||
subdent = ''.join([" " for a in xrange(pad)])
|
||||
limit = self.display.columns - pad
|
||||
limit = display.columns - pad
|
||||
|
||||
for o in sorted(doc['options'].keys()):
|
||||
opt = doc['options'][o]
|
||||
|
@ -229,8 +234,8 @@ class DocCLI(CLI):
|
|||
opt_indent=" "
|
||||
text = []
|
||||
text.append("> %s\n" % doc['module'].upper())
|
||||
pad = self.display.columns * 0.20
|
||||
limit = max(self.display.columns - int(pad), 70)
|
||||
pad = display.columns * 0.20
|
||||
limit = max(display.columns - int(pad), 70)
|
||||
|
||||
if isinstance(doc['description'], list):
|
||||
desc = " ".join(doc['description'])
|
||||
|
|
|
@ -28,7 +28,6 @@ import sys
|
|||
import yaml
|
||||
|
||||
from collections import defaultdict
|
||||
from distutils.version import LooseVersion
|
||||
from jinja2 import Environment
|
||||
|
||||
import ansible.constants as C
|
||||
|
@ -39,16 +38,24 @@ from ansible.galaxy.api import GalaxyAPI
|
|||
from ansible.galaxy.role import GalaxyRole
|
||||
from ansible.playbook.role.requirement import RoleRequirement
|
||||
|
||||
try:
|
||||
from __main__ import display
|
||||
display = display
|
||||
except ImportError:
|
||||
from ansible.utils.display import Display
|
||||
display = Display()
|
||||
|
||||
|
||||
class GalaxyCLI(CLI):
|
||||
|
||||
VALID_ACTIONS = ("init", "info", "install", "list", "remove", "search")
|
||||
SKIP_INFO_KEYS = ("name", "description", "readme_html", "related", "summary_fields", "average_aw_composite", "average_aw_score", "url" )
|
||||
|
||||
def __init__(self, args, display=None):
|
||||
def __init__(self, args):
|
||||
|
||||
self.api = None
|
||||
self.galaxy = None
|
||||
super(GalaxyCLI, self).__init__(args, display)
|
||||
super(GalaxyCLI, self).__init__(args)
|
||||
|
||||
def parse(self):
|
||||
''' create an options parser for bin/ansible '''
|
||||
|
@ -58,7 +65,6 @@ class GalaxyCLI(CLI):
|
|||
epilog = "\nSee '%s <command> --help' for more information on a specific command.\n\n" % os.path.basename(sys.argv[0])
|
||||
)
|
||||
|
||||
|
||||
self.set_action()
|
||||
|
||||
# options specific to actions
|
||||
|
@ -109,8 +115,8 @@ class GalaxyCLI(CLI):
|
|||
|
||||
# get options, args and galaxy object
|
||||
self.options, self.args =self.parser.parse_args()
|
||||
self.display.verbosity = self.options.verbosity
|
||||
self.galaxy = Galaxy(self.options, self.display)
|
||||
display.verbosity = self.options.verbosity
|
||||
self.galaxy = Galaxy(self.options)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -178,10 +184,10 @@ class GalaxyCLI(CLI):
|
|||
if os.path.isfile(role_path):
|
||||
raise AnsibleError("- the path %s already exists, but is a file - aborting" % role_path)
|
||||
elif not force:
|
||||
raise AnsibleError("- the directory %s already exists." % role_path + \
|
||||
"you can use --force to re-initialize this directory,\n" + \
|
||||
"however it will reset any main.yml files that may have\n" + \
|
||||
"been modified there already.")
|
||||
raise AnsibleError("- the directory %s already exists."
|
||||
"you can use --force to re-initialize this directory,\n"
|
||||
"however it will reset any main.yml files that may have\n"
|
||||
"been modified there already." % role_path)
|
||||
|
||||
# create the default README.md
|
||||
if not os.path.exists(role_path):
|
||||
|
@ -234,7 +240,7 @@ class GalaxyCLI(CLI):
|
|||
f = open(main_yml_path, 'w')
|
||||
f.write('---\n# %s file for %s\n' % (dir,role_name))
|
||||
f.close()
|
||||
self.display.display("- %s was created successfully" % role_name)
|
||||
display.display("- %s was created successfully" % role_name)
|
||||
|
||||
def execute_info(self):
|
||||
"""
|
||||
|
@ -297,7 +303,7 @@ class GalaxyCLI(CLI):
|
|||
# the user needs to specify one of either --role-file
|
||||
# or specify a single user/role name
|
||||
raise AnsibleOptionsError("- you must specify a user/role name or a roles file")
|
||||
elif len(self.args) == 1 and not role_file is None:
|
||||
elif len(self.args) == 1 and role_file is not None:
|
||||
# using a role file is mutually exclusive of specifying
|
||||
# the role name on the command line
|
||||
raise AnsibleOptionsError("- please specify a user/role name, or a roles file, but not both")
|
||||
|
@ -320,22 +326,22 @@ class GalaxyCLI(CLI):
|
|||
|
||||
for role in required_roles:
|
||||
role = RoleRequirement.role_yaml_parse(role)
|
||||
self.display.debug('found role %s in yaml file' % str(role))
|
||||
display.debug('found role %s in yaml file' % str(role))
|
||||
if 'name' not in role and 'scm' not in role:
|
||||
raise AnsibleError("Must specify name or src for role")
|
||||
roles_left.append(GalaxyRole(self.galaxy, **role))
|
||||
else:
|
||||
self.display.deprecated("going forward only the yaml format will be supported")
|
||||
display.deprecated("going forward only the yaml format will be supported")
|
||||
# roles listed in a file, one per line
|
||||
for rline in f.readlines():
|
||||
if rline.startswith("#") or rline.strip() == '':
|
||||
continue
|
||||
self.display.debug('found role %s in text file' % str(rline))
|
||||
display.debug('found role %s in text file' % str(rline))
|
||||
role = RoleRequirement.role_yaml_parse(rline.strip())
|
||||
roles_left.append(GalaxyRole(self.galaxy, **role))
|
||||
f.close()
|
||||
except (IOError, OSError) as e:
|
||||
self.display.error('Unable to open %s: %s' % (role_file, str(e)))
|
||||
display.error('Unable to open %s: %s' % (role_file, str(e)))
|
||||
else:
|
||||
# roles were specified directly, so we'll just go out grab them
|
||||
# (and their dependencies, unless the user doesn't want us to).
|
||||
|
@ -343,18 +349,17 @@ class GalaxyCLI(CLI):
|
|||
roles_left.append(GalaxyRole(self.galaxy, rname.strip()))
|
||||
|
||||
for role in roles_left:
|
||||
self.display.debug('Installing role %s ' % role.name)
|
||||
display.debug('Installing role %s ' % role.name)
|
||||
# query the galaxy API for the role data
|
||||
role_data = None
|
||||
|
||||
if role.install_info is not None and not force:
|
||||
self.display.display('- %s is already installed, skipping.' % role.name)
|
||||
display.display('- %s is already installed, skipping.' % role.name)
|
||||
continue
|
||||
|
||||
try:
|
||||
installed = role.install()
|
||||
except AnsibleError as e:
|
||||
self.display.warning("- %s was NOT installed successfully: %s " % (role.name, str(e)))
|
||||
display.warning("- %s was NOT installed successfully: %s " % (role.name, str(e)))
|
||||
self.exit_without_ignore()
|
||||
continue
|
||||
|
||||
|
@ -362,7 +367,7 @@ class GalaxyCLI(CLI):
|
|||
if not no_deps and installed:
|
||||
role_dependencies = role.metadata.get('dependencies') or []
|
||||
for dep in role_dependencies:
|
||||
self.display.debug('Installing dep %s' % dep)
|
||||
display.debug('Installing dep %s' % dep)
|
||||
dep_req = RoleRequirement()
|
||||
dep_info = dep_req.role_yaml_parse(dep)
|
||||
dep_role = GalaxyRole(self.galaxy, **dep_info)
|
||||
|
@ -372,15 +377,15 @@ class GalaxyCLI(CLI):
|
|||
continue
|
||||
if dep_role.install_info is None or force:
|
||||
if dep_role not in roles_left:
|
||||
self.display.display('- adding dependency: %s' % dep_role.name)
|
||||
display.display('- adding dependency: %s' % dep_role.name)
|
||||
roles_left.append(dep_role)
|
||||
else:
|
||||
self.display.display('- dependency %s already pending installation.' % dep_role.name)
|
||||
display.display('- dependency %s already pending installation.' % dep_role.name)
|
||||
else:
|
||||
self.display.display('- dependency %s is already installed, skipping.' % dep_role.name)
|
||||
display.display('- dependency %s is already installed, skipping.' % dep_role.name)
|
||||
|
||||
if not installed:
|
||||
self.display.warning("- %s was NOT installed successfully." % role.name)
|
||||
display.warning("- %s was NOT installed successfully." % role.name)
|
||||
self.exit_without_ignore()
|
||||
|
||||
return 0
|
||||
|
@ -398,9 +403,9 @@ class GalaxyCLI(CLI):
|
|||
role = GalaxyRole(self.galaxy, role_name)
|
||||
try:
|
||||
if role.remove():
|
||||
self.display.display('- successfully removed %s' % role_name)
|
||||
display.display('- successfully removed %s' % role_name)
|
||||
else:
|
||||
self.display.display('- %s is not installed, skipping.' % role_name)
|
||||
display.display('- %s is not installed, skipping.' % role_name)
|
||||
except Exception as e:
|
||||
raise AnsibleError("Failed to remove role %s: %s" % (role_name, str(e)))
|
||||
|
||||
|
@ -429,9 +434,9 @@ class GalaxyCLI(CLI):
|
|||
if not version:
|
||||
version = "(unknown version)"
|
||||
# show some more info about single roles here
|
||||
self.display.display("- %s, %s" % (name, version))
|
||||
display.display("- %s, %s" % (name, version))
|
||||
else:
|
||||
self.display.display("- the role %s was not found" % name)
|
||||
display.display("- the role %s was not found" % name)
|
||||
else:
|
||||
# show all valid roles in the roles_path directory
|
||||
roles_path = self.get_opt('roles_path')
|
||||
|
@ -450,7 +455,7 @@ class GalaxyCLI(CLI):
|
|||
version = install_info.get("version", None)
|
||||
if not version:
|
||||
version = "(unknown version)"
|
||||
self.display.display("- %s, %s" % (path_file, version))
|
||||
display.display("- %s, %s" % (path_file, version))
|
||||
return 0
|
||||
|
||||
def execute_search(self):
|
||||
|
@ -464,7 +469,7 @@ class GalaxyCLI(CLI):
|
|||
response = self.api.search_roles(search, self.options.platforms, self.options.tags)
|
||||
|
||||
if 'count' in response:
|
||||
self.galaxy.display.display("Found %d roles matching your search:\n" % response['count'])
|
||||
display.display("Found %d roles matching your search:\n" % response['count'])
|
||||
|
||||
data = ''
|
||||
if 'results' in response:
|
||||
|
|
|
@ -33,6 +33,14 @@ from ansible.parsing.dataloader import DataLoader
|
|||
from ansible.utils.vars import load_extra_vars
|
||||
from ansible.vars import VariableManager
|
||||
|
||||
try:
|
||||
from __main__ import display
|
||||
display = display
|
||||
except ImportError:
|
||||
from ansible.utils.display import Display
|
||||
display = Display()
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------------------------
|
||||
|
||||
class PlaybookCLI(CLI):
|
||||
|
@ -73,7 +81,7 @@ class PlaybookCLI(CLI):
|
|||
if len(self.args) == 0:
|
||||
raise AnsibleOptionsError("You must specify a playbook file to run")
|
||||
|
||||
self.display.verbosity = self.options.verbosity
|
||||
display.verbosity = self.options.verbosity
|
||||
self.validate_conflicts(runas_opts=True, vault_opts=True, fork_opts=True)
|
||||
|
||||
def run(self):
|
||||
|
@ -129,7 +137,7 @@ class PlaybookCLI(CLI):
|
|||
no_hosts = False
|
||||
if len(inventory.list_hosts()) == 0:
|
||||
# Empty inventory
|
||||
self.display.warning("provided hosts list is empty, only localhost is available")
|
||||
display.warning("provided hosts list is empty, only localhost is available")
|
||||
no_hosts = True
|
||||
inventory.subset(self.options.subset)
|
||||
if len(inventory.list_hosts()) == 0 and no_hosts is False:
|
||||
|
@ -137,14 +145,14 @@ class PlaybookCLI(CLI):
|
|||
raise AnsibleError("Specified --limit does not match any hosts")
|
||||
|
||||
# create the playbook executor, which manages running the plays via a task queue manager
|
||||
pbex = PlaybookExecutor(playbooks=self.args, inventory=inventory, variable_manager=variable_manager, loader=loader, display=self.display, options=self.options, passwords=passwords)
|
||||
pbex = PlaybookExecutor(playbooks=self.args, inventory=inventory, variable_manager=variable_manager, loader=loader, options=self.options, passwords=passwords)
|
||||
|
||||
results = pbex.run()
|
||||
|
||||
if isinstance(results, list):
|
||||
for p in results:
|
||||
|
||||
self.display.display('\nplaybook: %s' % p['playbook'])
|
||||
display.display('\nplaybook: %s' % p['playbook'])
|
||||
i = 1
|
||||
for play in p['plays']:
|
||||
if play.name:
|
||||
|
@ -164,7 +172,7 @@ class PlaybookCLI(CLI):
|
|||
for host in playhosts:
|
||||
msg += "\n %s" % host
|
||||
|
||||
self.display.display(msg)
|
||||
display.display(msg)
|
||||
|
||||
if self.options.listtags or self.options.listtasks:
|
||||
taskmsg = ' tasks:'
|
||||
|
@ -180,7 +188,7 @@ class PlaybookCLI(CLI):
|
|||
taskmsg += " TAGS: [%s]" % ','.join(mytags.union(set(task.tags)))
|
||||
j = j + 1
|
||||
|
||||
self.display.display(taskmsg)
|
||||
display.display(taskmsg)
|
||||
|
||||
i = i + 1
|
||||
return 0
|
||||
|
|
|
@ -33,6 +33,14 @@ from ansible.cli import CLI
|
|||
from ansible.plugins import module_loader
|
||||
from ansible.utils.cmd_functions import run_cmd
|
||||
|
||||
try:
|
||||
from __main__ import display
|
||||
display = display
|
||||
except ImportError:
|
||||
from ansible.utils.display import Display
|
||||
display = Display()
|
||||
|
||||
|
||||
########################################################
|
||||
|
||||
class PullCLI(CLI):
|
||||
|
@ -94,7 +102,7 @@ class PullCLI(CLI):
|
|||
if self.options.module_name not in self.SUPPORTED_REPO_MODULES:
|
||||
raise AnsibleOptionsError("Unsuported repo module %s, choices are %s" % (self.options.module_name, ','.join(self.SUPPORTED_REPO_MODULES)))
|
||||
|
||||
self.display.verbosity = self.options.verbosity
|
||||
display.verbosity = self.options.verbosity
|
||||
self.validate_conflicts(vault_opts=True)
|
||||
|
||||
def run(self):
|
||||
|
@ -104,8 +112,8 @@ class PullCLI(CLI):
|
|||
|
||||
# log command line
|
||||
now = datetime.datetime.now()
|
||||
self.display.display(now.strftime("Starting Ansible Pull at %F %T"))
|
||||
self.display.display(' '.join(sys.argv))
|
||||
display.display(now.strftime("Starting Ansible Pull at %F %T"))
|
||||
display.display(' '.join(sys.argv))
|
||||
|
||||
# Build Checkout command
|
||||
# Now construct the ansible command
|
||||
|
@ -152,19 +160,19 @@ class PullCLI(CLI):
|
|||
|
||||
# Nap?
|
||||
if self.options.sleep:
|
||||
self.display.display("Sleeping for %d seconds..." % self.options.sleep)
|
||||
time.sleep(self.options.sleep);
|
||||
display.display("Sleeping for %d seconds..." % self.options.sleep)
|
||||
time.sleep(self.options.sleep)
|
||||
|
||||
# RUN the Checkout command
|
||||
rc, out, err = run_cmd(cmd, live=True)
|
||||
|
||||
if rc != 0:
|
||||
if self.options.force:
|
||||
self.display.warning("Unable to update repository. Continuing with (forced) run of playbook.")
|
||||
display.warning("Unable to update repository. Continuing with (forced) run of playbook.")
|
||||
else:
|
||||
return rc
|
||||
elif self.options.ifchanged and '"changed": true' not in out:
|
||||
self.display.display("Repository has not changed, quitting.")
|
||||
display.display("Repository has not changed, quitting.")
|
||||
return 0
|
||||
|
||||
playbook = self.select_playbook(path)
|
||||
|
@ -197,11 +205,10 @@ class PullCLI(CLI):
|
|||
try:
|
||||
shutil.rmtree(self.options.dest)
|
||||
except Exception as e:
|
||||
self.display.error("Failed to remove %s: %s" % (self.options.dest, str(e)))
|
||||
display.error("Failed to remove %s: %s" % (self.options.dest, str(e)))
|
||||
|
||||
return rc
|
||||
|
||||
|
||||
def try_playbook(self, path):
|
||||
if not os.path.exists(path):
|
||||
return 1
|
||||
|
@ -215,7 +222,7 @@ class PullCLI(CLI):
|
|||
playbook = os.path.join(path, self.args[0])
|
||||
rc = self.try_playbook(playbook)
|
||||
if rc != 0:
|
||||
self.display.warning("%s: %s" % (playbook, self.PLAYBOOK_ERRORS[rc]))
|
||||
display.warning("%s: %s" % (playbook, self.PLAYBOOK_ERRORS[rc]))
|
||||
return None
|
||||
return playbook
|
||||
else:
|
||||
|
@ -232,5 +239,5 @@ class PullCLI(CLI):
|
|||
else:
|
||||
errors.append("%s: %s" % (pb, self.PLAYBOOK_ERRORS[rc]))
|
||||
if playbook is None:
|
||||
self.display.warning("\n".join(errors))
|
||||
display.warning("\n".join(errors))
|
||||
return playbook
|
||||
|
|
|
@ -27,16 +27,24 @@ from ansible.parsing.dataloader import DataLoader
|
|||
from ansible.parsing.vault import VaultEditor
|
||||
from ansible.cli import CLI
|
||||
|
||||
try:
|
||||
from __main__ import display
|
||||
display = display
|
||||
except ImportError:
|
||||
from ansible.utils.display import Display
|
||||
display = Display()
|
||||
|
||||
|
||||
class VaultCLI(CLI):
|
||||
""" Vault command line class """
|
||||
|
||||
VALID_ACTIONS = ("create", "decrypt", "edit", "encrypt", "rekey", "view")
|
||||
|
||||
def __init__(self, args, display=None):
|
||||
def __init__(self, args):
|
||||
|
||||
self.vault_pass = None
|
||||
self.new_vault_pass = None
|
||||
super(VaultCLI, self).__init__(args, display)
|
||||
super(VaultCLI, self).__init__(args)
|
||||
|
||||
def parse(self):
|
||||
|
||||
|
@ -63,7 +71,7 @@ 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
|
||||
display.verbosity = self.options.verbosity
|
||||
|
||||
can_output = ['encrypt', 'decrypt']
|
||||
|
||||
|
@ -117,24 +125,24 @@ class VaultCLI(CLI):
|
|||
def execute_encrypt(self):
|
||||
|
||||
if len(self.args) == 0 and sys.stdin.isatty():
|
||||
self.display.display("Reading plaintext input from stdin", stderr=True)
|
||||
display.display("Reading plaintext input from stdin", stderr=True)
|
||||
|
||||
for f in self.args or ['-']:
|
||||
self.editor.encrypt_file(f, output_file=self.options.output_file)
|
||||
|
||||
if sys.stdout.isatty():
|
||||
self.display.display("Encryption successful", stderr=True)
|
||||
display.display("Encryption successful", stderr=True)
|
||||
|
||||
def execute_decrypt(self):
|
||||
|
||||
if len(self.args) == 0 and sys.stdin.isatty():
|
||||
self.display.display("Reading ciphertext input from stdin", stderr=True)
|
||||
display.display("Reading ciphertext input from stdin", stderr=True)
|
||||
|
||||
for f in self.args or ['-']:
|
||||
self.editor.decrypt_file(f, output_file=self.options.output_file)
|
||||
|
||||
if sys.stdout.isatty():
|
||||
self.display.display("Decryption successful", stderr=True)
|
||||
display.display("Decryption successful", stderr=True)
|
||||
|
||||
def execute_create(self):
|
||||
|
||||
|
@ -160,4 +168,4 @@ class VaultCLI(CLI):
|
|||
for f in self.args:
|
||||
self.editor.rekey_file(f, self.new_vault_pass)
|
||||
|
||||
self.display.display("Rekey successful", stderr=True)
|
||||
display.display("Rekey successful", stderr=True)
|
||||
|
|
|
@ -27,7 +27,6 @@ import sys
|
|||
|
||||
from ansible.compat.six import string_types
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.executor.task_queue_manager import TaskQueueManager
|
||||
from ansible.playbook import Playbook
|
||||
from ansible.template import Templar
|
||||
|
@ -36,6 +35,14 @@ from ansible.utils.color import colorize, hostcolor
|
|||
from ansible.utils.encrypt import do_encrypt
|
||||
from ansible.utils.unicode import to_unicode
|
||||
|
||||
try:
|
||||
from __main__ import display
|
||||
display = display
|
||||
except ImportError:
|
||||
from ansible.utils.display import Display
|
||||
display = Display()
|
||||
|
||||
|
||||
class PlaybookExecutor:
|
||||
|
||||
'''
|
||||
|
@ -43,12 +50,11 @@ class PlaybookExecutor:
|
|||
basis for bin/ansible-playbook operation.
|
||||
'''
|
||||
|
||||
def __init__(self, playbooks, inventory, variable_manager, loader, display, options, passwords):
|
||||
def __init__(self, playbooks, inventory, variable_manager, loader, options, passwords):
|
||||
self._playbooks = playbooks
|
||||
self._inventory = inventory
|
||||
self._variable_manager = variable_manager
|
||||
self._loader = loader
|
||||
self._display = display
|
||||
self._options = options
|
||||
self.passwords = passwords
|
||||
self._unreachable_hosts = dict()
|
||||
|
@ -56,7 +62,7 @@ class PlaybookExecutor:
|
|||
if options.listhosts or options.listtasks or options.listtags or options.syntax:
|
||||
self._tqm = None
|
||||
else:
|
||||
self._tqm = TaskQueueManager(inventory=inventory, variable_manager=variable_manager, loader=loader, display=display, options=options, passwords=self.passwords)
|
||||
self._tqm = TaskQueueManager(inventory=inventory, variable_manager=variable_manager, loader=loader, options=options, passwords=self.passwords)
|
||||
|
||||
def run(self):
|
||||
|
||||
|
@ -81,7 +87,7 @@ class PlaybookExecutor:
|
|||
|
||||
i = 1
|
||||
plays = pb.get_plays()
|
||||
self._display.vv('%d plays in %s' % (len(plays), playbook_path))
|
||||
display.vv('%d plays in %s' % (len(plays), playbook_path))
|
||||
|
||||
for play in plays:
|
||||
if play._included_path is not None:
|
||||
|
@ -178,18 +184,18 @@ class PlaybookExecutor:
|
|||
self._cleanup()
|
||||
|
||||
if self._options.syntax:
|
||||
self.display.display("No issues encountered")
|
||||
display.display("No issues encountered")
|
||||
return result
|
||||
|
||||
# TODO: this stat summary stuff should be cleaned up and moved
|
||||
# to a new method, if it even belongs here...
|
||||
self._display.banner("PLAY RECAP")
|
||||
display.banner("PLAY RECAP")
|
||||
|
||||
hosts = sorted(self._tqm._stats.processed.keys())
|
||||
for h in hosts:
|
||||
t = self._tqm._stats.summarize(h)
|
||||
|
||||
self._display.display(u"%s : %s %s %s %s" % (
|
||||
display.display(u"%s : %s %s %s %s" % (
|
||||
hostcolor(h, t),
|
||||
colorize(u'ok', t['ok'], 'green'),
|
||||
colorize(u'changed', t['changed'], 'yellow'),
|
||||
|
@ -198,7 +204,7 @@ class PlaybookExecutor:
|
|||
screen_only=True
|
||||
)
|
||||
|
||||
self._display.display(u"%s : %s %s %s %s" % (
|
||||
display.display(u"%s : %s %s %s %s" % (
|
||||
hostcolor(h, t, False),
|
||||
colorize(u'ok', t['ok'], None),
|
||||
colorize(u'changed', t['changed'], None),
|
||||
|
@ -207,7 +213,7 @@ class PlaybookExecutor:
|
|||
log_only=True
|
||||
)
|
||||
|
||||
self._display.display("", screen_only=True)
|
||||
display.display("", screen_only=True)
|
||||
# END STATS STUFF
|
||||
|
||||
return result
|
||||
|
@ -230,7 +236,7 @@ class PlaybookExecutor:
|
|||
serial_pct = int(play.serial.replace("%",""))
|
||||
serial = int((serial_pct/100.0) * len(all_hosts))
|
||||
else:
|
||||
if play.serial is None:
|
||||
if play.serial is None:
|
||||
serial = -1
|
||||
else:
|
||||
serial = int(play.serial)
|
||||
|
@ -281,12 +287,12 @@ class PlaybookExecutor:
|
|||
second = do_prompt("confirm " + msg, private)
|
||||
if result == second:
|
||||
break
|
||||
self._display.display("***** VALUES ENTERED DO NOT MATCH ****")
|
||||
display.display("***** VALUES ENTERED DO NOT MATCH ****")
|
||||
else:
|
||||
result = do_prompt(msg, private)
|
||||
else:
|
||||
result = None
|
||||
self._display.warning("Not prompting as we are not in interactive mode")
|
||||
display.warning("Not prompting as we are not in interactive mode")
|
||||
|
||||
# if result is false and default is not None
|
||||
if not result and default is not None:
|
||||
|
@ -298,5 +304,3 @@ class PlaybookExecutor:
|
|||
# handle utf-8 chars
|
||||
result = to_unicode(result, errors='strict')
|
||||
return result
|
||||
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ __metaclass__ = type
|
|||
|
||||
import multiprocessing
|
||||
import os
|
||||
import socket
|
||||
import tempfile
|
||||
|
||||
from ansible import constants as C
|
||||
|
@ -34,8 +33,16 @@ from ansible.playbook.play_context import PlayContext
|
|||
from ansible.plugins import callback_loader, strategy_loader, module_loader
|
||||
from ansible.template import Templar
|
||||
|
||||
try:
|
||||
from __main__ import display
|
||||
display = display
|
||||
except ImportError:
|
||||
from ansible.utils.display import Display
|
||||
display = Display()
|
||||
|
||||
__all__ = ['TaskQueueManager']
|
||||
|
||||
|
||||
class TaskQueueManager:
|
||||
|
||||
'''
|
||||
|
@ -48,12 +55,11 @@ class TaskQueueManager:
|
|||
which dispatches the Play's tasks to hosts.
|
||||
'''
|
||||
|
||||
def __init__(self, inventory, variable_manager, loader, display, options, passwords, stdout_callback=None):
|
||||
def __init__(self, inventory, variable_manager, loader, options, passwords, stdout_callback=None):
|
||||
|
||||
self._inventory = inventory
|
||||
self._variable_manager = variable_manager
|
||||
self._loader = loader
|
||||
self._display = display
|
||||
self._options = options
|
||||
self._stats = AggregateStats()
|
||||
self.passwords = passwords
|
||||
|
@ -155,7 +161,9 @@ class TaskQueueManager:
|
|||
elif callback_needs_whitelist and (C.DEFAULT_CALLBACK_WHITELIST is None or callback_name not in C.DEFAULT_CALLBACK_WHITELIST):
|
||||
continue
|
||||
|
||||
self._callback_plugins.append(callback_plugin(self._display))
|
||||
# is it too late to change the API for v2 callback plugins?
|
||||
# display is not necessary.
|
||||
self._callback_plugins.append(callback_plugin(display))
|
||||
else:
|
||||
self._callback_plugins.append(callback_plugin())
|
||||
|
||||
|
@ -221,7 +229,7 @@ class TaskQueueManager:
|
|||
return play_return
|
||||
|
||||
def cleanup(self):
|
||||
self._display.debug("RUNNING CLEANUP")
|
||||
display.debug("RUNNING CLEANUP")
|
||||
self.terminate()
|
||||
self._final_q.close()
|
||||
self._cleanup_processes()
|
||||
|
@ -275,4 +283,4 @@ class TaskQueueManager:
|
|||
v1_method = method.replace('v2_','')
|
||||
v1_method(*args, **kwargs)
|
||||
except Exception:
|
||||
self._display.warning('Error when using %s: %s' % (method, str(e)))
|
||||
display.warning('Error when using %s: %s' % (method, str(e)))
|
||||
|
|
|
@ -28,7 +28,6 @@ import os
|
|||
from ansible.compat.six import string_types
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.utils.display import Display
|
||||
|
||||
# default_readme_template
|
||||
# default_meta_template
|
||||
|
@ -37,12 +36,7 @@ from ansible.utils.display import Display
|
|||
class Galaxy(object):
|
||||
''' Keeps global galaxy info '''
|
||||
|
||||
def __init__(self, options, display=None):
|
||||
|
||||
if display is None:
|
||||
self.display = Display()
|
||||
else:
|
||||
self.display = display
|
||||
def __init__(self, options):
|
||||
|
||||
self.options = options
|
||||
roles_paths = getattr(self.options, 'roles_path', [])
|
||||
|
@ -65,11 +59,9 @@ class Galaxy(object):
|
|||
def remove_role(self, role_name):
|
||||
del self.roles[role_name]
|
||||
|
||||
|
||||
def _str_from_data_file(self, filename):
|
||||
myfile = os.path.join(self.DATA_PATH, filename)
|
||||
try:
|
||||
return open(myfile).read()
|
||||
except Exception as e:
|
||||
raise AnsibleError("Could not open %s: %s" % (filename, str(e)))
|
||||
|
||||
|
|
|
@ -61,7 +61,6 @@ class TestPlayIterator(unittest.TestCase):
|
|||
})
|
||||
|
||||
mock_inventory = MagicMock()
|
||||
mock_display = MagicMock()
|
||||
mock_var_manager = MagicMock()
|
||||
|
||||
# fake out options to use the syntax CLI switch, which will ensure
|
||||
|
@ -74,7 +73,6 @@ class TestPlayIterator(unittest.TestCase):
|
|||
inventory=mock_inventory,
|
||||
variable_manager=mock_var_manager,
|
||||
loader=fake_loader,
|
||||
display=mock_display,
|
||||
options=mock_options,
|
||||
passwords=[],
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue