mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-07-22 21:00:22 -07:00
Adding new playbook objects for v2
* Playbook * TaskInclude
This commit is contained in:
parent
cbad867f24
commit
229d49fe36
42 changed files with 2041 additions and 81 deletions
|
@ -63,8 +63,9 @@ class ModuleArgsParser:
|
|||
Args may also be munged for certain shell command parameters.
|
||||
"""
|
||||
|
||||
def __init__(self, task=None):
|
||||
self._task = task
|
||||
def __init__(self, task_ds=dict()):
|
||||
assert isinstance(task_ds, dict)
|
||||
self._task_ds = task_ds
|
||||
|
||||
|
||||
def _split_module_string(self, str):
|
||||
|
@ -144,7 +145,7 @@ class ModuleArgsParser:
|
|||
# form is like: local_action: copy src=a dest=b ... pretty common
|
||||
args = parse_kv(thing)
|
||||
else:
|
||||
raise AnsibleParsingError("unexpected parameter type in action: %s" % type(thing), obj=self._task)
|
||||
raise AnsibleParsingError("unexpected parameter type in action: %s" % type(thing), obj=self._task_ds)
|
||||
return args
|
||||
|
||||
def _normalize_new_style_args(self, thing):
|
||||
|
@ -179,19 +180,17 @@ class ModuleArgsParser:
|
|||
|
||||
else:
|
||||
# need a dict or a string, so giving up
|
||||
raise AnsibleParsingError("unexpected parameter type in action: %s" % type(thing), obj=self._task)
|
||||
raise AnsibleParsingError("unexpected parameter type in action: %s" % type(thing), obj=self._task_ds)
|
||||
|
||||
return (action, args)
|
||||
|
||||
def parse(self, ds):
|
||||
def parse(self):
|
||||
'''
|
||||
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.
|
||||
'''
|
||||
|
||||
assert isinstance(ds, dict)
|
||||
|
||||
thing = None
|
||||
|
||||
action = None
|
||||
|
@ -204,38 +203,38 @@ class ModuleArgsParser:
|
|||
#
|
||||
|
||||
# action
|
||||
if 'action' in ds:
|
||||
if 'action' in self._task_ds:
|
||||
|
||||
# an old school 'action' statement
|
||||
thing = ds['action']
|
||||
thing = self._task_ds['action']
|
||||
delegate_to = None
|
||||
action, args = self._normalize_parameters(thing)
|
||||
|
||||
# local_action
|
||||
if 'local_action' in ds:
|
||||
if 'local_action' in self._task_ds:
|
||||
|
||||
# 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)
|
||||
thing = ds.get('local_action', '')
|
||||
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)
|
||||
|
||||
# module: <stuff> is the more new-style invocation
|
||||
|
||||
# walk the input dictionary to see we recognize a module name
|
||||
for (item, value) in iteritems(ds):
|
||||
for (item, value) in iteritems(self._task_ds):
|
||||
if item in module_finder:
|
||||
# finding more than one module name is a problem
|
||||
if action is not None:
|
||||
raise AnsibleParserError("conflicting action statements", obj=self._task)
|
||||
raise AnsibleParserError("conflicting action statements", obj=self._task_ds)
|
||||
action = item
|
||||
thing = value
|
||||
action, args = self._normalize_parameters(value, action=action)
|
||||
|
||||
# if we didn't see any module in the task at all, it's not a task really
|
||||
if action is None:
|
||||
raise AnsibleParserError("no action detected in task", obj=self._task)
|
||||
raise AnsibleParserError("no action detected in task", obj=self._task_ds)
|
||||
|
||||
# shell modules require special handling
|
||||
(action, args) = self._handle_shell_weirdness(action, args)
|
||||
|
|
|
@ -27,6 +27,7 @@ from yaml import load, YAMLError
|
|||
from ansible.errors import AnsibleParserError
|
||||
|
||||
from ansible.parsing.vault import VaultLib
|
||||
from ansible.parsing.splitter import unquote
|
||||
from ansible.parsing.yaml.loader import AnsibleLoader
|
||||
from ansible.parsing.yaml.objects import AnsibleBaseYAMLObject
|
||||
from ansible.parsing.yaml.strings import YAML_SYNTAX_ERROR
|
||||
|
@ -55,6 +56,7 @@ class DataLoader():
|
|||
_FILE_CACHE = dict()
|
||||
|
||||
def __init__(self, vault_password=None):
|
||||
self._basedir = '.'
|
||||
self._vault = VaultLib(password=vault_password)
|
||||
|
||||
def load(self, data, file_name='<string>', show_content=True):
|
||||
|
@ -70,13 +72,15 @@ class DataLoader():
|
|||
try:
|
||||
# if loading JSON failed for any reason, we go ahead
|
||||
# and try to parse it as YAML instead
|
||||
return self._safe_load(data)
|
||||
return self._safe_load(data, file_name=file_name)
|
||||
except YAMLError as yaml_exc:
|
||||
self._handle_error(yaml_exc, file_name, show_content)
|
||||
|
||||
def load_from_file(self, file_name):
|
||||
''' Loads data from a file, which can contain either JSON or YAML. '''
|
||||
|
||||
file_name = self.path_dwim(file_name)
|
||||
|
||||
# if the file has already been read in and cached, we'll
|
||||
# return those results to avoid more file/vault operations
|
||||
if file_name in self._FILE_CACHE:
|
||||
|
@ -100,9 +104,14 @@ class DataLoader():
|
|||
def is_file(self, path):
|
||||
return os.path.isfile(path)
|
||||
|
||||
def _safe_load(self, stream):
|
||||
def _safe_load(self, stream, file_name=None):
|
||||
''' Implements yaml.safe_load(), except using our custom loader class. '''
|
||||
return load(stream, AnsibleLoader)
|
||||
|
||||
loader = AnsibleLoader(stream, file_name)
|
||||
try:
|
||||
return loader.get_single_data()
|
||||
finally:
|
||||
loader.dispose()
|
||||
|
||||
def _get_file_contents(self, file_name):
|
||||
'''
|
||||
|
@ -139,3 +148,23 @@ class DataLoader():
|
|||
|
||||
raise AnsibleParserError(YAML_SYNTAX_ERROR, obj=err_obj, show_content=show_content)
|
||||
|
||||
def set_basedir(self, basedir):
|
||||
''' sets the base directory, used to find files when a relative path is given '''
|
||||
|
||||
if basedir is not None:
|
||||
self._basedir = basedir
|
||||
|
||||
def path_dwim(self, given):
|
||||
'''
|
||||
make relative paths work like folks expect.
|
||||
'''
|
||||
|
||||
given = unquote(given)
|
||||
|
||||
if given.startswith("/"):
|
||||
return os.path.abspath(given)
|
||||
elif given.startswith("~"):
|
||||
return os.path.abspath(os.path.expanduser(given))
|
||||
else:
|
||||
return os.path.abspath(os.path.join(self._basedir, given))
|
||||
|
||||
|
|
|
@ -23,6 +23,10 @@ from yaml.constructor import Constructor
|
|||
from ansible.parsing.yaml.objects import AnsibleMapping
|
||||
|
||||
class AnsibleConstructor(Constructor):
|
||||
def __init__(self, file_name=None):
|
||||
self._ansible_file_name = file_name
|
||||
super(AnsibleConstructor, self).__init__()
|
||||
|
||||
def construct_yaml_map(self, node):
|
||||
data = AnsibleMapping()
|
||||
yield data
|
||||
|
@ -36,7 +40,16 @@ class AnsibleConstructor(Constructor):
|
|||
ret = AnsibleMapping(super(Constructor, self).construct_mapping(node, deep))
|
||||
ret._line_number = node.__line__
|
||||
ret._column_number = node.__column__
|
||||
ret._data_source = node.__datasource__
|
||||
|
||||
# in some cases, we may have pre-read the data and then
|
||||
# passed it to the load() call for YAML, in which case we
|
||||
# want to override the default datasource (which would be
|
||||
# '<string>') to the actual filename we read in
|
||||
if self._ansible_file_name:
|
||||
ret._data_source = self._ansible_file_name
|
||||
else:
|
||||
ret._data_source = node.__datasource__
|
||||
|
||||
return ret
|
||||
|
||||
AnsibleConstructor.add_constructor(
|
||||
|
|
|
@ -28,11 +28,11 @@ from ansible.parsing.yaml.composer import AnsibleComposer
|
|||
from ansible.parsing.yaml.constructor import AnsibleConstructor
|
||||
|
||||
class AnsibleLoader(Reader, Scanner, Parser, AnsibleComposer, AnsibleConstructor, Resolver):
|
||||
def __init__(self, stream):
|
||||
def __init__(self, stream, file_name=None):
|
||||
Reader.__init__(self, stream)
|
||||
Scanner.__init__(self)
|
||||
Parser.__init__(self)
|
||||
AnsibleComposer.__init__(self)
|
||||
AnsibleConstructor.__init__(self)
|
||||
AnsibleConstructor.__init__(self, file_name=file_name)
|
||||
Resolver.__init__(self)
|
||||
|
||||
|
|
|
@ -26,8 +26,8 @@ class AnsibleBaseYAMLObject:
|
|||
|
||||
'''
|
||||
_data_source = None
|
||||
_line_number = None
|
||||
_column_number = None
|
||||
_line_number = 0
|
||||
_column_number = 0
|
||||
|
||||
def get_position_info(self):
|
||||
return (self._data_source, self._line_number, self._column_number)
|
||||
|
|
|
@ -34,8 +34,8 @@ Syntax Error while loading YAML.
|
|||
"""
|
||||
|
||||
YAML_POSITION_DETAILS = """\
|
||||
The error appears to have been in '%s': line %s, column %s,
|
||||
but may actually be before there depending on the exact syntax problem.
|
||||
The error appears to have been in '%s': line %s, column %s, but may
|
||||
be elsewhere in the file depending on the exact syntax problem.
|
||||
"""
|
||||
|
||||
YAML_COMMON_DICT_ERROR = """\
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue