|
|
|
|
@ -31,6 +31,34 @@ from ansible.utils.display import Display
|
|
|
|
|
|
|
|
|
|
display = Display()
|
|
|
|
|
|
|
|
|
|
SAFE_OUTPUT_ENV = {
|
|
|
|
|
'ANSIBLE_CALLBACK_RESULT_FORMAT': 'json',
|
|
|
|
|
'ANSIBLE_LOAD_CALLBACK_PLUGINS': '0',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def safe_output_env(f):
|
|
|
|
|
|
|
|
|
|
def wrapper(*args, **kwargs):
|
|
|
|
|
|
|
|
|
|
orig = {}
|
|
|
|
|
|
|
|
|
|
for k, v in SAFE_OUTPUT_ENV.items():
|
|
|
|
|
orig[k] = os.environ.get(k, None)
|
|
|
|
|
os.environ[k] = v
|
|
|
|
|
|
|
|
|
|
result = f(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
for key in orig.keys():
|
|
|
|
|
if orig[key] is None:
|
|
|
|
|
del os.environ[key]
|
|
|
|
|
else:
|
|
|
|
|
os.environ[key] = orig[key]
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
return wrapper
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PullCLI(CLI):
|
|
|
|
|
""" Used to pull a remote copy of ansible on each managed node,
|
|
|
|
|
@ -42,7 +70,7 @@ class PullCLI(CLI):
|
|
|
|
|
you should use an external scheduler and/or locking to ensure there are no clashing operations.
|
|
|
|
|
|
|
|
|
|
The setup playbook can be tuned to change the cron frequency, logging locations, and parameters to ansible-pull.
|
|
|
|
|
This is useful both for extreme scale-out as well as periodic remediation.
|
|
|
|
|
This is useful both for extreme scale-out and periodic remediation.
|
|
|
|
|
Usage of the 'fetch' module to retrieve logs from ansible-pull runs would be an
|
|
|
|
|
excellent way to gather and analyze remote logs from ansible-pull.
|
|
|
|
|
"""
|
|
|
|
|
@ -76,8 +104,9 @@ class PullCLI(CLI):
|
|
|
|
|
return inv_opts
|
|
|
|
|
|
|
|
|
|
def init_parser(self):
|
|
|
|
|
""" create an options parser for bin/ansible """
|
|
|
|
|
""" Specific args/option parser for pull """
|
|
|
|
|
|
|
|
|
|
# signature is different from parent as caller should not need to add usage/desc
|
|
|
|
|
super(PullCLI, self).init_parser(
|
|
|
|
|
usage='%prog -U <repository> [options] [<playbook.yml>]',
|
|
|
|
|
desc="pulls playbooks from a VCS repo and executes them on target host")
|
|
|
|
|
@ -106,10 +135,12 @@ class PullCLI(CLI):
|
|
|
|
|
help='path to the directory to which Ansible will checkout the repository.')
|
|
|
|
|
self.parser.add_argument('-U', '--url', dest='url', default=None, help='URL of the playbook repository')
|
|
|
|
|
self.parser.add_argument('--full', dest='fullclone', action='store_true', help='Do a full clone, instead of a shallow one.')
|
|
|
|
|
# TODO: resolve conflict with check mode, added manually below
|
|
|
|
|
self.parser.add_argument('-C', '--checkout', dest='checkout',
|
|
|
|
|
help='branch/tag/commit to checkout. Defaults to behavior of repository module.')
|
|
|
|
|
self.parser.add_argument('--accept-host-key', default=False, dest='accept_host_key', action='store_true',
|
|
|
|
|
help='adds the hostkey for the repo url if not already added')
|
|
|
|
|
# Overloaded with adhoc ... but really passthrough to adhoc
|
|
|
|
|
self.parser.add_argument('-m', '--module-name', dest='module_name', default=self.DEFAULT_REPO_TYPE,
|
|
|
|
|
help='Repository module name, which ansible will use to check out the repo. Choices are %s. Default is %s.'
|
|
|
|
|
% (self.REPO_CHOICES, self.DEFAULT_REPO_TYPE))
|
|
|
|
|
@ -121,7 +152,7 @@ class PullCLI(CLI):
|
|
|
|
|
self.parser.add_argument('--track-subs', dest='tracksubs', default=False, action='store_true',
|
|
|
|
|
help='submodules will track the latest changes. This is equivalent to specifying the --remote flag to git submodule update')
|
|
|
|
|
# add a subset of the check_opts flag group manually, as the full set's
|
|
|
|
|
# shortcodes conflict with above --checkout/-C
|
|
|
|
|
# shortcodes conflict with above --checkout/-C, see to-do above
|
|
|
|
|
self.parser.add_argument("--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")
|
|
|
|
|
self.parser.add_argument("--diff", default=C.DIFF_ALWAYS, dest='diff', action='store_true',
|
|
|
|
|
@ -177,7 +208,7 @@ class PullCLI(CLI):
|
|
|
|
|
limit_opts = 'localhost,127.0.0.1'
|
|
|
|
|
base_opts = '-c local '
|
|
|
|
|
if context.CLIARGS['verbosity'] > 0:
|
|
|
|
|
base_opts += ' -%s' % ''.join(["v" for x in range(0, context.CLIARGS['verbosity'])])
|
|
|
|
|
base_opts += ' -%s' % ''.join(["v" for dummy in range(0, context.CLIARGS['verbosity'])])
|
|
|
|
|
|
|
|
|
|
# Attempt to use the inventory passed in as an argument
|
|
|
|
|
# It might not yet have been downloaded so use localhost as default
|
|
|
|
|
@ -250,15 +281,21 @@ class PullCLI(CLI):
|
|
|
|
|
# RUN the Checkout command
|
|
|
|
|
display.debug("running ansible with VCS module to checkout repo")
|
|
|
|
|
display.vvvv('EXEC: %s' % cmd)
|
|
|
|
|
rc, b_out, b_err = run_cmd(cmd, live=True)
|
|
|
|
|
rc, b_out, b_err = safe_output_env(run_cmd)(cmd, live=True)
|
|
|
|
|
|
|
|
|
|
if rc != 0:
|
|
|
|
|
if context.CLIARGS['force']:
|
|
|
|
|
display.warning("Unable to update repository. Continuing with (forced) run of playbook.")
|
|
|
|
|
else:
|
|
|
|
|
return rc
|
|
|
|
|
elif context.CLIARGS['ifchanged'] and b'"changed": true' not in b_out:
|
|
|
|
|
display.display("Repository has not changed, quitting.")
|
|
|
|
|
elif context.CLIARGS['ifchanged']:
|
|
|
|
|
# detect json/yaml/header, any count as 'changed'
|
|
|
|
|
for detect in (b'"changed": true', b"changed: True", b"| CHANGED =>"):
|
|
|
|
|
if detect in b_out:
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
# no change, we bail
|
|
|
|
|
display.display(f"Repository has not changed, quitting: {b_out!r}")
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
playbook = self.select_playbook(context.CLIARGS['dest'])
|
|
|
|
|
|