Coverage for lib/ansible/parsing/mod_args.py : 47%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# (c) 2014 Michael DeHaan, <michael@ansible.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/>.
# Make coding more python3-ish
# For filtering out modules correctly below 'command', 'win_command', 'shell', 'win_shell', 'script', 'include', 'include_vars', 'include_tasks', 'include_role', 'import_tasks', 'import_role', 'add_host', 'group_by', 'set_fact', 'raw', 'meta', ])
""" There are several ways a module and argument set can be expressed:
# legacy form (for a shell command) - action: shell echo hi
# common shorthand for local actions vs delegate_to - local_action: shell echo hi
# most commonly: - copy: src=a dest=b
# legacy form - action: copy src=a dest=b
# complex args form, for passing structured data - copy: src: a dest: b
# gross, but technically legal - action: module: copy args: src: a dest: b
# Standard YAML form for command-type modules. In this case, the args specified # will act as 'defaults' and will be overridden by any args specified # in one of the other formats (complex args under the action, or # parsed from the k=v string - command: 'pwd' args: chdir: '/tmp'
This class has some of the logic to canonicalize these into the form
- module: <module_name> delegate_to: <optional> args: <args>
Args may also be munged for certain shell command parameters. """
raise AnsibleAssertionError("the type of 'task_ds' should be a dict, but is a %s" % type(task_ds))
''' when module names are expressed like: action: copy src=a dest=b the first part of the string is the name of the module and the rest are strings pertaining to the arguments. '''
tokens = split_args(module_string) if len(tokens) > 1: return (tokens[0], " ".join(tokens[1:])) else: return (tokens[0], "")
''' arguments can be fuzzy. Deal with all the forms. '''
# final args are the ones we'll eventually return, so first update # them with any additional args specified, which have lower priority # than those which may be parsed/normalized next if isinstance(additional_args, string_types): templar = Templar(loader=None) if templar._contains_vars(additional_args): final_args['_variable_params'] = additional_args else: raise AnsibleParserError("Complex args containing variables cannot use bare variables, and must use the full variable style " "('{{var_name}}')") elif isinstance(additional_args, dict): final_args.update(additional_args) else: raise AnsibleParserError('Complex args must be a dictionary or variable string ("{{var}}").')
# how we normalize depends if we figured out what the module name is # yet. If we have already figured it out, it's a 'new style' invocation. # otherwise, it's not
else: (action, args) = self._normalize_old_style_args(thing)
# this can occasionally happen, simplify if args and 'args' in args: tmp_args = args.pop('args') if isinstance(tmp_args, string_types): tmp_args = parse_kv(tmp_args) args.update(tmp_args)
# only internal variables can start with an underscore, so # we don't allow users to set them directly in arguments raise AnsibleError("invalid parameter specified for action '%s': '%s'" % (action, arg))
# finally, update the args we're going to return with the ones # which were normalized above
''' deals with fuzziness in new style module invocations accepting key=value pairs and dictionaries, and returns a dictionary of arguments
possible example inputs: 'echo hi', 'shell' {'region': 'xyz'}, 'ec2' standardized outputs like: { _raw_params: 'echo hi', _uses_shell: True } '''
# form is like: { xyz: { x: 2, y: 3 } } # form is like: copy: src=a dest=b # this can happen with modules which take no params, like ping: else: raise AnsibleParserError("unexpected parameter type in action: %s" % type(thing), obj=self._task_ds)
''' deals with fuzziness in old-style (action/local_action) module invocations returns tuple of (module_name, dictionary_args)
possible example inputs: { 'shell' : 'echo hi' } 'shell echo hi' {'module': 'ec2', 'x': 1 } standardized outputs like: ('ec2', { 'x': 1} ) '''
action = None args = None
actions_allowing_raw = ('command', 'win_command', 'shell', 'win_shell', 'script', 'raw') if isinstance(thing, dict): # form is like: action: { module: 'copy', src: 'a', dest: 'b' } thing = thing.copy() if 'module' in thing: action, module_args = self._split_module_string(thing['module']) args = thing.copy() check_raw = action in actions_allowing_raw args.update(parse_kv(module_args, check_raw=check_raw)) del args['module']
elif isinstance(thing, string_types): # form is like: action: copy src=a dest=b (action, args) = self._split_module_string(thing) check_raw = action in actions_allowing_raw args = parse_kv(args, check_raw=check_raw)
else: # need a dict or a string, so giving up raise AnsibleParserError("unexpected parameter type in action: %s" % type(thing), obj=self._task_ds)
return (action, args)
''' Given a task in one of the supported forms, parses and returns returns the action, arguments, and delegate_to values for the task, dealing with all sorts of levels of fuzziness. '''
# This is the standard YAML form for command-type modules. We grab # the args and pass them in as additional arguments, which can/will # be overwritten via dict updates from the other arg sources below
# We can have one of action, local_action, or module specified # action # an old school 'action' statement thing = self._task_ds['action'] action, args = self._normalize_parameters(thing, action=action, additional_args=additional_args)
# local_action # local_action is similar but also implies a delegate_to if action is not None: raise AnsibleParserError("action and local_action are mutually exclusive", obj=self._task_ds) thing = self._task_ds.get('local_action', '') delegate_to = 'localhost' action, args = self._normalize_parameters(thing, action=action, additional_args=additional_args)
# module: <stuff> is the more new-style invocation
# walk the input dictionary to see we recognize a module name # finding more than one module name is a problem raise AnsibleParserError("conflicting action statements: %s, %s" % (action, item), obj=self._task_ds)
# if we didn't see any module in the task at all, it's not a task really if 'ping' not in module_loader: raise AnsibleParserError("The requested action was not found in configured module paths. " "Additionally, core modules are missing. If this is a checkout, " "run 'git pull --rebase' to correct this problem.", obj=self._task_ds)
else: raise AnsibleParserError("no action detected in task. This often indicates a misspelled module name, or incorrect module path.", obj=self._task_ds) templar = Templar(loader=None) raw_params = args.pop('_raw_params') if templar._contains_vars(raw_params): args['_variable_params'] = raw_params else: raise AnsibleParserError("this task '%s' has extra params, which is only allowed in the following modules: %s" % (action, ", ".join(RAW_PARAM_MODULES)), obj=self._task_ds)
|