New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
adds jinja2 filters for working with network devices #24216
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -318,6 +318,35 @@ address. For example, to get the IP address itself from a CIDR, you can use:: | |
More information about ``ipaddr`` filter and complete usage guide can be found | ||
in :doc:`playbooks_filters_ipaddr`. | ||
|
||
.. _network_filter: | ||
|
||
Network filters | ||
``````````````` | ||
|
||
.. versionadded:: 2.4 | ||
|
||
To generate a list of command diffs for a network configuration: | ||
|
||
{{ myvar | diff_network_config(current_config, indent=1 }} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. need to close out the function: e.g. Also might be a good idea to show example using a file lookup. |
||
|
||
To return the entire configuration block: | ||
|
||
{{ myvar | diff_network_config(current_config, indent=1, replace='block' }} | ||
|
||
To match the block exactly: | ||
|
||
{{ myvar | diff_network_config(current_config, indent=1, match='exact' }} | ||
|
||
To apply a TextFSM filter to the output of a CLI command: | ||
|
||
{{ myvar | parse_cli(('cisco_show_version') }} | ||
|
||
Templates can also be downloaded from URLs: | ||
|
||
{{ myvar | parse_cli('http://myserver/cisco_show_version') }} | ||
|
||
The CLI parsing filter depends on the TextFSM library being installed on | ||
the local system | ||
|
||
.. _hash_filters: | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
# | ||
# {c) 2017 Red Hat, Inc. | ||
# | ||
# 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 | ||
from __future__ import (absolute_import, division, print_function) | ||
__metaclass__ = type | ||
|
||
import os | ||
import urllib2 | ||
import urlparse | ||
|
||
try: | ||
import textfsm | ||
HAS_TEXTFSM = True | ||
except ImportError: | ||
HAS_TEXTFSM = False | ||
|
||
from ansible.module_utils.six import StringIO | ||
from ansible.module_utils.netcfg import NetworkConfig, dumps | ||
from ansible.errors import AnsibleError | ||
|
||
try: | ||
from __main__ import display | ||
except ImportError: | ||
from ansible.utils.display import Display | ||
display = Display() | ||
|
||
NETWORK_OS = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if this should be called |
||
'ios': 1, | ||
'iosxr': 1, | ||
'nxos': 2, | ||
'eos': 4 | ||
} | ||
|
||
def diff_network_config(value, base, network_os=None, indent=1, match='line', replace='line'): | ||
|
||
assert match in ('line', 'strict', 'exact', 'none') | ||
assert replace in ('line', 'block') | ||
assert network_os in NETWORK_OS.keys() | ||
|
||
if network_os: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure how this would ever hit base on the previous assert? Perhaps the assert should be here? |
||
indent = NETWORK_OS.get(network_os, indent) | ||
|
||
candidate = NetworkConfig(indent=indent, contents=value) | ||
base_config = NetworkConfig(indent=indent, contents=base) | ||
|
||
objs = candidate.difference(base_config, match=match, replace=replace) | ||
commands = dumps(objs, 'commands') | ||
|
||
return commands | ||
|
||
def parse_cli(value, template): | ||
if not HAS_TEXTFSM: | ||
raise AnsibleError('parse_cli filter requires TextFSM library to be installed') | ||
|
||
url = urlparse.urlparse(template) | ||
if url.scheme.startswith('http'): | ||
try: | ||
handler = {} | ||
if 'HTTP_PROXY' in os.environ: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be worth adding some comments to say why you set the proxy to localhost. |
||
handler['http'] = '127.0.0.1' | ||
if 'HTTPS_PROXY' in os.environ: | ||
handler['https'] = '127.0.0.1' | ||
if handler: | ||
opener = urllib2.build_opener(urllib2.ProxyHandler(handler)) | ||
urllib2.install_opener(opener) | ||
resp = urllib2.urlopen(template) | ||
except urllib2.HTTPError as exc: | ||
raise AnsibleError(str(exc)) | ||
data = StringIO() | ||
data.write(resp.read()) | ||
data.seek(0) | ||
template = data | ||
else: | ||
if not os.path.exists(template): | ||
raise AnsibleError('unable to locate parse_cli template: %s' % template) | ||
try: | ||
template = open(template) | ||
except IOError as exc: | ||
raise AnsibleError(str(exc)) | ||
re_table = textfsm.TextFSM(template) | ||
fsm_results = re_table.ParseText(value) | ||
results = list() | ||
for item in fsm_results: | ||
results.append(dict(zip(re_table.header, item))) | ||
return results | ||
|
||
class FilterModule(object): | ||
"""Filters for working with output from network devices""" | ||
|
||
filter_map = { | ||
'parse_cli': parse_cli, | ||
'diff_network_config': diff_network_config | ||
} | ||
|
||
def filters(self): | ||
return self.filter_map |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For code blocks in RST you use
::
, blank line, four spacese.g.