pull/75215/merge
Brian Coca 1 day ago committed by GitHub
commit 2cdf1f22e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -46,6 +46,7 @@ _ACTION_ALL_PROPER_INCLUDE_IMPORT_TASKS = _ACTION_INCLUDE_TASKS + _ACTION_IMPORT
_ACTION_ALL_INCLUDE_ROLE_TASKS = _ACTION_INCLUDE_ROLE + _ACTION_INCLUDE_TASKS
_ACTION_FACT_GATHERING = _ACTION_SETUP + add_internal_fqcns(('gather_facts', ))
_ACTION_WITH_CLEAN_FACTS = _ACTION_SET_FACT + _ACTION_INCLUDE_VARS
_ACTION_VARS = add_internal_fqcns(('set_var', ))
# http://nezzen.net/2008/06/23/colored-text-in-python-using-ansi-escape-sequences/
COLOR_CODES = {
@ -125,6 +126,7 @@ MODULE_NO_JSON = tuple(add_internal_fqcns(('command', 'win_command', 'shell', 'w
RESTRICTED_RESULT_KEYS = ('ansible_rsync_path', 'ansible_playbook_python', 'ansible_facts')
SYNTHETIC_COLLECTIONS = ('ansible.builtin', 'ansible.legacy')
TREE_DIR = None
VALID_VAR_SCOPES = ('extra', 'host', 'host_fact', 'parent', 'play')
VAULT_VERSION_MIN = 1.0
VAULT_VERSION_MAX = 1.0

@ -668,6 +668,11 @@ class TaskExecutor:
),
)
if '_ansible_vars' in result and self._task.action not in C._ACTION_VARS:
del result['_ansible_vars']
else:
vars_copy.update(results['_ansible_vars'])
if 'ansible_facts' in result and self._task.action not in C._ACTION_DEBUG:
if self._task.action in C._ACTION_WITH_CLEAN_FACTS:
if self._task.delegate_to and self._task.delegate_facts:
@ -772,6 +777,9 @@ class TaskExecutor:
if self._task.register:
variables[self._task.register] = result
if '_ansible_vars' in result and self._task.action not in C._ACTION_VARS:
del result['_ansible_vars']
if 'ansible_facts' in result and self._task.action not in C._ACTION_DEBUG:
if self._task.action in C._ACTION_WITH_CLEAN_FACTS:
variables.update(result['ansible_facts'])

@ -0,0 +1,64 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = r'''
---
module: set_vars
short_description: set a variable to a value at a given scope
description:
- This module allows setting variables in the current ansible run at different scopesA
- If a variable already exists it will be overriden or merged depending on 'hash behaviour'
otherwise it will be created
author: Ansible Project
options:
defs:
description:
- A list of dictionaries that represent a variable definition
type: list
required: true
scope:
required: false
choices: ['host', 'host_fact', 'extra', 'play']
description:
- scope to which variables apply to, this sets the default for every definition but each one can override it
type: string
default: 'host'
version_added: "2.12"
'''
EXAMPLES = r'''
- name: create/update an extra var
set_vars:
scope: extra
defs:
- name: packages_to_install
value:
- apache
- nginx
- haproxy
- pound
- name: set a host fact (not the same as set_facts + cacheable=true, only 1 var is created)
set_vars:
defs:
- name: pretty
value: no
scope: host_fact
- name: set a host variable (higher priority than facts), this is equivalent of set_fact + cacheable=false
set_vars:
defs:
- name: presents
value: all
scope: host
'''
RETURNS = r'''
# should i return list of var names + scope set?
'''

@ -0,0 +1,85 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible import constants as C
from ansible.errors import AnsibleActionFail
from ansible.module_utils.common._collections_compat import Mapping, Sequence
from ansible.module_utils.six import string_types
from ansible.plugins.action import ActionBase
from ansible.utils.vars import isidentifier, combine_vars
class ActionModule(ActionBase):
TRANSFERS_FILES = False
_VALID_VARS_DEF = frozenset(('scope', 'name', 'value'))
_VALID_ARGS = frozenset(('scope', 'defs'))
def _extract_validated_var_definition(self, var_def, global_scope):
if not isinstance(var_def, Mapping):
raise AnsibleActionFail("The variable definition should be a dictionary, but got a %s" % type(var_def))
try:
name = var_def.pop('name')
except KeyError:
raise AnsibleActionFail("The variable definition requires a 'name' entry")
if not isidentifier(name):
raise AnsibleActionFail("The variable name '%s' is not valid. Variables must start with a letter or underscore character, and contain only "
"letters, numbers and underscores." % name)
try:
value = var_def.pop('value')
except KeyError:
raise AnsibleActionFail("The variable definition requires a 'value' entry")
scope = var_def.pop('scope', global_scope)
if not isinstance(scope, string_types):
raise AnsibleActionFail("The 'scope' option for '%s' should be a string, but got a %s" % (name, type(scope)))
elif scope not in C.VALID_VAR_SCOPES:
raise AnsibleActionFail("Invalid 'scope' option for '%s', got '%s' but should be one of: %s" % (name, scope, ', '.join(C.VALID_VAR_SCOPES)))
if len(var_def) > 0:
raise AnsibleActionFail("Unknown arguments were passed into variable definition for '%s', only '%s' are valid." % (name, ', '.join(self._VALID_VARS_DEF)))
return name, value, scope
def run(self, tmp=None, task_vars=None):
if task_vars is None:
task_vars = dict()
result = super(ActionModule, self).run(tmp, task_vars)
result['_ansible_vars'] = {}
if self._task.args:
try:
vars_list = self._task.args.pop('defs')
except KeyError:
raise AnsibleActionFail("set_var requires a 'defs' option, none was provided")
scope = self._task.args.pop('scope', 'host')
if len(self._task.args) > 0:
raise AnsibleActionFail("Unknown arguments were passed into set_var, only valid ones are: %s" % ', '.join(self._VALID_ARGS))
if isinstance(vars_list, string_types) or not isinstance(vars_list, Sequence):
raise AnsibleActionFail("set_var takes a list of variable definitions, but got a '%s' instead" % type(vars_list))
for var_def in vars_list:
name, value, scope = self._extract_validated_var_definition(var_def, scope)
if scope not in result['_ansible_vars']:
result['_ansible_vars'][scope] = {}
# allow for setting in loop
if '_ansible_vars' in task_vars and scope in task_vars['_ansilbe_vars'] and name in task_vars['_ansible_vars'][scope]:
result['_ansible_vars'][scope][name] = combine_vars(task_vars['_ansible_facts'][scope][name], value)
else:
result['_ansible_vars'][scope][name] = value
result['changed'] = False
return result

@ -422,6 +422,15 @@ class StrategyBase:
host_name = result.get('_ansible_delegated_vars', {}).get('ansible_delegated_host', None)
return [host_name or task.delegate_to]
def get_target_hosts(self, iterator, result, task, facts=False):
if task.delegate_to is not None and (not facts or task.delegate_facts):
host_list = self.get_delegated_hosts(result, task)
else:
host_list = self.get_task_hosts(iterator, result._host, task)
return host_list
def _set_always_delegated_facts(self, result, task):
"""Sets host facts for ``delegate_to`` hosts for facts that should
always be delegated
@ -689,6 +698,9 @@ class StrategyBase:
if result_item.get('failed', False):
task_result._return_data['failed'] = True
# always clean up
del result_item['_ansible_vars']
if 'ansible_facts' in result_item and original_task.action not in C._ACTION_DEBUG:
# if delegated fact and we are delegating facts, we need to change target host for them
if original_task.delegate_to is not None and original_task.delegate_facts:
@ -719,6 +731,30 @@ class StrategyBase:
if is_set_fact:
self._variable_manager.set_nonpersistent_facts(target_host, result_item['ansible_facts'].copy())
if '_ansible_vars' in result_item:
if original_task.action not in C._ACTINON_VARS:
display.warning('Removed unexpected _ansible_vars key from results of "%s", '
'possibly a result from an attempt to bypass security' % original_task.action)
else:
for scope in result_item['_ansible_vars']:
if scope == 'host':
host_list = self.get_target_hosts(iterator, result_item, original_task)
for target_host in host_list:
for var_name, var_value in iteritems(result_item['_ansible_vars']['host']):
self._variable_manager.set_host_variable(target_host, var_name, var_value)
elif scope == 'host_facts':
if original_task.delegate_facts:
host_list = self.get_target_hosts(iterator, result_item, original_task, facts=True)
for target_host in host_list:
self._variable_manager.set_nonpersistent_facts(target_host, result_item['_ansible_vars']['host_facts'])
elif scope == 'play':
self._variable_manager.set_play_vars(iterator._play, result_item['_ansible_vars']['extra'])
elif scope == 'extra':
self._variable_manager.set_extra_vars(result_item['_ansible_vars']['extra'])
else:
# we really should never hit this
display.warning('Ignoring "%s" as it is an invalid scope for set_var' % scope)
if 'ansible_stats' in result_item and 'data' in result_item['ansible_stats'] and result_item['ansible_stats']['data']:
if 'per_host' not in result_item['ansible_stats'] or result_item['ansible_stats']['per_host']:

@ -0,0 +1,3 @@
#/usr/bin/bash
echo '{"failed": "false", "msg": "tested module"}'
Loading…
Cancel
Save