Skip to content

Commit ab31397

Browse files
author
Peter Sprygada
committed
initial add of junos_template
This adds a new module, junos_template, that can read in a template config and push the changes to the device. It can also backup the current config. This module is implemented over cli
1 parent 8c46a08 commit ab31397

File tree

1 file changed

+181
-0
lines changed

1 file changed

+181
-0
lines changed

network/junos/junos_template.py

+181
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
#!/usr/bin/python
2+
#
3+
# This file is part of Ansible
4+
#
5+
# Ansible is free software: you can redistribute it and/or modify
6+
# it under the terms of the GNU General Public License as published by
7+
# the Free Software Foundation, either version 3 of the License, or
8+
# (at your option) any later version.
9+
#
10+
# Ansible is distributed in the hope that it will be useful,
11+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
# GNU General Public License for more details.
14+
#
15+
# You should have received a copy of the GNU General Public License
16+
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
17+
#
18+
DOCUMENTATION = """
19+
---
20+
module: junos_template
21+
version_added: "2.1"
22+
author: "Peter sprygada (@privateip)"
23+
short_description: Manage Juniper JUNOS device configurations
24+
description:
25+
- Manages network device configurations over SSH. This module
26+
allows implementors to work with the device configuration. It
27+
provides a way to push a set of commands onto a network device
28+
by evaluting the current configuration and only pushing
29+
commands that are not already configured.
30+
extends_documentation_fragment: junos
31+
options:
32+
src:
33+
description:
34+
- The path to the config source. The source can be either a
35+
file with config or a template that will be merged during
36+
runtime. By default the task will search for the source
37+
file in role or playbook root folder in templates directory.
38+
required: false
39+
default: null
40+
force:
41+
description:
42+
- The force argument instructs the module to not consider the
43+
current devices configuration. When set to true, this will
44+
cause the module to push the contents of I(src) into the device
45+
without first checking if already configured.
46+
required: false
47+
default: false
48+
choices: BOOLEANS
49+
backup:
50+
description:
51+
- When this argument is configured true, the module will backup
52+
the configuration from the node prior to making any changes.
53+
The backup file will be written to backup_{{ hostname }} in
54+
the root of the playbook directory.
55+
required: false
56+
default: false
57+
choices: BOOLEANS
58+
config:
59+
description:
60+
- The module, by default, will connect to the remote device and
61+
retrieve the current configuration to use as a base for comparing
62+
against the contents of source. There are times when it is not
63+
desirable to have the task get the current configuration for
64+
every task in a playbook. The I(config) argument allows the
65+
implementer to pass in the configuruation to use as the base
66+
config for comparision.
67+
required: false
68+
default: null
69+
"""
70+
71+
EXAMPLES = """
72+
73+
- name: push a configuration onto the device
74+
junos_template:
75+
src: config.j2
76+
77+
- name: forceable push a configuration onto the device
78+
junos_template:
79+
src: config.j2
80+
force: yes
81+
82+
- name: provide the base configuration for comparision
83+
junos_template:
84+
src: candidate_config.txt
85+
config: current_config.txt
86+
87+
"""
88+
89+
RETURN = """
90+
91+
commands:
92+
description: The set of commands that will be pushed to the remote device
93+
returned: always
94+
type: list
95+
sample: [...]
96+
97+
"""
98+
99+
def compare(this, other):
100+
parents = [item.text for item in this.parents]
101+
for entry in other:
102+
if this == entry:
103+
return None
104+
return this
105+
106+
def expand(obj, action='set'):
107+
cmd = [action]
108+
cmd.extend([p.text for p in obj.parents])
109+
cmd.append(obj.text)
110+
return ' '.join(cmd)
111+
112+
def flatten(data, obj):
113+
for k, v in data.items():
114+
obj.append(k)
115+
flatten(v, obj)
116+
return obj
117+
118+
def to_lines(config):
119+
lines = list()
120+
for item in config:
121+
if item.raw.endswith(';'):
122+
line = [p.text for p in item.parents]
123+
line.append(item.text)
124+
lines.append(' '.join(line))
125+
return lines
126+
127+
def get_config(module):
128+
config = module.params['config'] or list()
129+
if not config and not module.params['force']:
130+
config = module.config
131+
return config
132+
133+
def main():
134+
""" main entry point for module execution
135+
"""
136+
137+
argument_spec = dict(
138+
src=dict(),
139+
force=dict(default=False, type='bool'),
140+
backup=dict(default=False, type='bool'),
141+
config=dict(),
142+
)
143+
144+
mutually_exclusive = [('config', 'backup'), ('config', 'force')]
145+
146+
module = get_module(argument_spec=argument_spec,
147+
mutually_exclusive=mutually_exclusive,
148+
supports_check_mode=True)
149+
150+
result = dict(changed=False)
151+
152+
parsed = module.parse_config(module.params['src'])
153+
commands = to_lines(parsed)
154+
155+
contents = get_config(module)
156+
result['_backup'] = module.config
157+
158+
parsed = module.parse_config(contents)
159+
config = to_lines(parsed)
160+
161+
candidate = list()
162+
for item in commands:
163+
if item not in config:
164+
candidate.append('set %s' % item)
165+
166+
if candidate:
167+
if not module.check_mode:
168+
module.configure(candidate)
169+
result['changed'] = True
170+
171+
result['updates'] = candidate
172+
return module.exit_json(**result)
173+
174+
175+
from ansible.module_utils.basic import *
176+
from ansible.module_utils.shell import *
177+
from ansible.module_utils.netcfg import *
178+
from ansible.module_utils.junos import *
179+
if __name__ == '__main__':
180+
main()
181+

0 commit comments

Comments
 (0)