258 lines
9.2 KiB
Python
258 lines
9.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# (c) 2018, Ansible by Red Hat, inc
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# 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 = """
|
|
lookup: network_template
|
|
author: Ansible Network
|
|
version_added: "2.5"
|
|
short_description: template device configuration
|
|
description:
|
|
- This plugin will lookup the file and template it into a network
|
|
configuration.
|
|
deprecated:
|
|
removed_in: "2.7.7"
|
|
options:
|
|
_terms:
|
|
description: list of files to template
|
|
"""
|
|
|
|
EXAMPLES = """
|
|
- name: show config template results
|
|
debug: msg="{{ lookup('network_template', './config_template.j2') }}
|
|
"""
|
|
|
|
RETURN = """
|
|
_raw:
|
|
description: file(s) content after templating
|
|
"""
|
|
|
|
|
|
import collections
|
|
|
|
from ansible.plugins.lookup import LookupBase, display
|
|
from ansible.module_utils.common._collections_compat import Mapping
|
|
from ansible.module_utils.network.common.utils import to_list
|
|
from ansible.module_utils.six import iteritems, string_types
|
|
from ansible.module_utils._text import to_text, to_bytes
|
|
from ansible.errors import AnsibleError, AnsibleUndefinedVariable
|
|
|
|
|
|
class LookupModule(LookupBase):
|
|
|
|
def run(self, terms, variables, **kwargs):
|
|
self.ds = variables.copy()
|
|
|
|
config_lines = list()
|
|
|
|
for term in to_list(terms[0]):
|
|
display.debug("File lookup term: %s" % term)
|
|
|
|
lookupfile = self.find_file_in_search_path(variables, 'templates', term)
|
|
display.vvvv("File lookup using %s as file" % lookupfile)
|
|
|
|
if lookupfile:
|
|
with open(to_bytes(lookupfile, errors='surrogate_or_strict'), 'rb'):
|
|
tasks = self._loader.load_from_file(lookupfile)
|
|
|
|
for task in tasks:
|
|
task.pop('name', None)
|
|
register = task.pop('register', None)
|
|
|
|
when = task.pop('when', None)
|
|
if when is not None:
|
|
if not self._check_conditional(when, self.ds):
|
|
display.vvv('skipping task due to conditional check failure')
|
|
continue
|
|
|
|
loop = task.pop('loop', None)
|
|
|
|
if loop:
|
|
loop = self.template(loop, self.ds)
|
|
loop_result = list()
|
|
|
|
if isinstance(loop, Mapping):
|
|
for loop_key, loop_value in iteritems(loop):
|
|
self.ds['item'] = {'key': loop_key, 'value': loop_value}
|
|
res = self._process_directive(task)
|
|
if res:
|
|
loop_result.extend(to_list(res))
|
|
|
|
elif isinstance(loop, collections.Iterable) and not isinstance(loop, string_types):
|
|
for loop_item in loop:
|
|
self.ds['item'] = loop_item
|
|
res = self._process_directive(task)
|
|
if res:
|
|
loop_result.extend(to_list(res))
|
|
|
|
config_lines.extend(loop_result)
|
|
|
|
if register:
|
|
self.ds[register] = loop_result
|
|
|
|
else:
|
|
res = self._process_directive(task)
|
|
if res:
|
|
config_lines.extend(to_list(res))
|
|
if register:
|
|
self.ds[register] = res
|
|
|
|
else:
|
|
raise AnsibleError("the template file %s could not be found for the lookup" % term)
|
|
|
|
return [to_text('\n'.join(config_lines)).strip()]
|
|
|
|
def do_context(self, block):
|
|
|
|
results = list()
|
|
|
|
for entry in block:
|
|
task = entry.copy()
|
|
task.pop('name', None)
|
|
task.pop('register', None)
|
|
|
|
when = task.pop('when', None)
|
|
if when is not None:
|
|
if not self._check_conditional(when, self.ds):
|
|
display.vvv('skipping context due to conditional check failure')
|
|
continue
|
|
|
|
loop = task.pop('loop', None)
|
|
if loop:
|
|
loop = self.template(loop, self.ds)
|
|
|
|
if 'context' in task:
|
|
res = self.do_context(task['context'])
|
|
if res:
|
|
results.extend(res)
|
|
|
|
elif isinstance(loop, Mapping):
|
|
loop_result = list()
|
|
for loop_key, loop_value in iteritems(loop):
|
|
self.ds['item'] = {'key': loop_key, 'value': loop_value}
|
|
loop_result.extend(to_list(self._process_directive(task)))
|
|
results.extend(loop_result)
|
|
|
|
elif isinstance(loop, collections.Iterable) and not isinstance(loop, string_types):
|
|
loop_result = list()
|
|
for loop_item in loop:
|
|
self.ds['item'] = loop_item
|
|
loop_result.extend(to_list(self._process_directive(task)))
|
|
results.extend(loop_result)
|
|
|
|
else:
|
|
res = self._process_directive(task)
|
|
if res:
|
|
results.extend(to_list(res))
|
|
|
|
return results
|
|
|
|
def _process_directive(self, task):
|
|
for directive, args in iteritems(task):
|
|
if directive == 'context':
|
|
meth = getattr(self, 'do_%s' % directive)
|
|
if meth:
|
|
return meth(args)
|
|
else:
|
|
meth = getattr(self, 'do_%s' % directive)
|
|
if meth:
|
|
return meth(**args)
|
|
|
|
def do_lines_template(self, template, join=False, when=None, required=False):
|
|
templated_lines = list()
|
|
_processed = list()
|
|
|
|
if when is not None:
|
|
if not self._check_conditional(when, self.ds):
|
|
display.vvv("skipping due to conditional failure")
|
|
return templated_lines
|
|
|
|
for line in to_list(template):
|
|
res = self.template(line, self.ds)
|
|
if res:
|
|
_processed.append(res)
|
|
elif not res and join:
|
|
break
|
|
|
|
if required and not _processed:
|
|
raise AnsibleError('unabled to templated required line')
|
|
elif _processed and join:
|
|
templated_lines.append(' '.join(_processed))
|
|
elif _processed:
|
|
templated_lines.extend(_processed)
|
|
|
|
return templated_lines
|
|
|
|
def _process_include(self, item, variables):
|
|
name = item.get('name')
|
|
include = item['include']
|
|
|
|
src = self.template(include, variables)
|
|
source = self._find_needle('templates', src)
|
|
|
|
when = item.get('when')
|
|
|
|
if when:
|
|
conditional = "{%% if %s %%}True{%% else %%}False{%% endif %%}"
|
|
if not self.template(conditional % when, variables, fail_on_undefined=False):
|
|
display.vvvvv("include '%s' skipped due to conditional check failure" % name)
|
|
return []
|
|
|
|
display.display('including file %s' % source)
|
|
include_data = self._loader.load_from_file(source)
|
|
|
|
template_data = item.copy()
|
|
|
|
# replace include directive with block directive and contents of
|
|
# included file. this will preserve other values such as loop,
|
|
# loop_control, etc
|
|
template_data.pop('include')
|
|
template_data['block'] = include_data
|
|
|
|
return self.build([template_data], variables)
|
|
|
|
def template(self, data, variables, convert_bare=False):
|
|
|
|
if isinstance(data, Mapping):
|
|
templated_data = {}
|
|
for key, value in iteritems(data):
|
|
templated_key = self.template(key, variables, convert_bare=convert_bare)
|
|
templated_data[templated_key] = self.template(value, variables, convert_bare=convert_bare)
|
|
return templated_data
|
|
|
|
elif isinstance(data, collections.Iterable) and not isinstance(data, string_types):
|
|
return [self.template(i, variables, convert_bare=convert_bare) for i in data]
|
|
|
|
else:
|
|
data = data or {}
|
|
tmp_avail_vars = self._templar._available_variables
|
|
self._templar.set_available_variables(variables)
|
|
try:
|
|
resp = self._templar.template(data, convert_bare=convert_bare)
|
|
resp = self._coerce_to_native(resp)
|
|
except AnsibleUndefinedVariable:
|
|
resp = None
|
|
finally:
|
|
self._templar.set_available_variables(tmp_avail_vars)
|
|
return resp
|
|
|
|
def _coerce_to_native(self, value):
|
|
if not isinstance(value, bool):
|
|
try:
|
|
value = int(value)
|
|
except Exception:
|
|
if value is None or len(value) == 0:
|
|
return None
|
|
return value
|
|
|
|
def _check_conditional(self, when, variables):
|
|
conditional = "{%% if %s %%}True{%% else %%}False{%% endif %%}"
|
|
return self.template(conditional % when, variables)
|