Skip to content

Commit 2ddeb8e

Browse files
author
kamatha1
committed
Creating a napalm_get_config module to fetch device configurations using NAPALM driver get_config method
1 parent 634e177 commit 2ddeb8e

File tree

2 files changed

+237
-0
lines changed

2 files changed

+237
-0
lines changed
+236
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
"""
2+
(c) 2020 Kirk Byers <[email protected]>
3+
(c) 2016 Elisa Jasinska <[email protected]>
4+
Original prototype by David Barroso <[email protected]>
5+
6+
This file is part of Ansible
7+
8+
Ansible is free software: you can redistribute it and/or modify
9+
it under the terms of the GNU General Public License as published by
10+
the Free Software Foundation, either version 3 of the License, or
11+
(at your option) any later version.
12+
13+
Ansible is distributed in the hope that it will be useful,
14+
but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
GNU General Public License for more details.
17+
18+
You should have received a copy of the GNU General Public License
19+
along with Ansible. If not, see <http://www.gnu.org/licenses/>.
20+
"""
21+
from __future__ import unicode_literals, print_function
22+
import os.path
23+
from ansible.module_utils.basic import AnsibleModule
24+
25+
26+
# FIX for Ansible 2.8 moving this function and making it private
27+
# greatly simplified for napalm-ansible's use
28+
def return_values(obj):
29+
""" Return native stringified values from datastructures.
30+
31+
For use with removing sensitive values pre-jsonification."""
32+
yield str(obj)
33+
34+
35+
DOCUMENTATION = """
36+
---
37+
module: napalm_get_config
38+
author: "Anirudh Kamath (@anirudhkamath)"
39+
version_added: "2.9"
40+
short_description: "Gathers configuration from a network device via napalm"
41+
description:
42+
- "Gathers configuration from a network device via the Python module napalm"
43+
requirements:
44+
- napalm
45+
options:
46+
hostname:
47+
description:
48+
- IP or FQDN of the device you want to connect to
49+
required: False
50+
username:
51+
description:
52+
- Username
53+
required: False
54+
password:
55+
description:
56+
- Password
57+
required: False
58+
dev_os:
59+
description:
60+
- OS of the device
61+
required: False
62+
provider:
63+
description:
64+
- Dictionary which acts as a collection of arguments used to define the characteristics
65+
of how to connect to the device.
66+
Note - hostname, username, password and dev_os must be defined in either provider
67+
or local param
68+
Note - local param takes precedence, e.g. hostname is preferred to provider['hostname']
69+
required: False
70+
timeout:
71+
description:
72+
- Time in seconds to wait for the device to respond
73+
required: False
74+
default: 60
75+
optional_args:
76+
description:
77+
- Dictionary of additional arguments passed to underlying driver
78+
required: False
79+
default: None
80+
retrieve:
81+
description:
82+
- Which configuration type you want to populate, default is all of them.
83+
required: False
84+
default: "all"
85+
full:
86+
description:
87+
- Retrieve all the configuration. For instance, on ios, "sh run all".
88+
required: False
89+
default: False
90+
sanitized:
91+
description:
92+
- Remove secret data
93+
required: False
94+
default: False
95+
"""
96+
97+
EXAMPLES = """
98+
- name: Collect device configuration object using NAPALM
99+
napalm_get_config:
100+
hostname: "{{ inventory_hostname }}"
101+
username: "{{ user }}"
102+
password: "{{ password }}"
103+
dev_os: "os"
104+
register: config_result
105+
106+
- name: Write running config to backup file
107+
copy:
108+
content: "{{ config_result.napalm_config.running }}"
109+
dest: "{{ file }}"
110+
"""
111+
112+
RETURN = """
113+
napalm_config:
114+
description: "The object returned is a dictionary with a key for each configuration store:
115+
- running
116+
- candidate
117+
- startup
118+
returned: always
119+
type: dict
120+
sample: "{
121+
"running": "",
122+
"startup": "",
123+
"candidate": "",
124+
}"
125+
"""
126+
127+
napalm_found = False
128+
try:
129+
from napalm import get_network_driver
130+
from napalm.base import ModuleImportError
131+
132+
napalm_found = True
133+
except ImportError:
134+
pass
135+
136+
137+
def main():
138+
module = AnsibleModule(
139+
argument_spec=dict(
140+
hostname=dict(type="str", required=False, aliases=["host"]),
141+
username=dict(type="str", required=False),
142+
password=dict(type="str", required=False, no_log=True),
143+
provider=dict(type="dict", required=False),
144+
timeout=dict(type="int", required=False, default=60),
145+
optional_args=dict(required=False, type="dict", default=None),
146+
dev_os=dict(type="str", required=False),
147+
retrieve=dict(type="str", required=False, default=None),
148+
full=dict(type="str", required=False, default=None),
149+
sanitized=dict(type="str", required=False, default=None),
150+
),
151+
)
152+
153+
if not napalm_found:
154+
module.fail_json(msg="the python module napalm is required")
155+
156+
provider = module.params["provider"] or {}
157+
158+
no_log = ["password", "secret"]
159+
for param in no_log:
160+
if provider.get(param):
161+
module.no_log_values.update(return_values(provider[param]))
162+
if provider.get("optional_args") and provider["optional_args"].get(param):
163+
module.no_log_values.update(
164+
return_values(provider["optional_args"].get(param))
165+
)
166+
if module.params.get("optional_args") and module.params["optional_args"].get(
167+
param
168+
):
169+
module.no_log_values.update(
170+
return_values(module.params["optional_args"].get(param))
171+
)
172+
173+
# allow host or hostname
174+
provider["hostname"] = provider.get("hostname", None) or provider.get("host", None)
175+
# allow local params to override provider
176+
for param, pvalue in provider.items():
177+
if module.params.get(param) is not False:
178+
module.params[param] = module.params.get(param) or pvalue
179+
180+
hostname = module.params["hostname"]
181+
username = module.params["username"]
182+
dev_os = module.params["dev_os"]
183+
password = module.params["password"]
184+
timeout = module.params["timeout"]
185+
retrieve = module.params["retrieve"]
186+
full = module.params["full"]
187+
sanitized = module.params["sanitized"]
188+
189+
argument_check = {"hostname": hostname, "username": username, "dev_os": dev_os}
190+
for key, val in argument_check.items():
191+
if val is None:
192+
module.fail_json(msg=str(key) + " is required")
193+
194+
if module.params["optional_args"] is None:
195+
optional_args = {}
196+
else:
197+
optional_args = module.params["optional_args"]
198+
199+
try:
200+
network_driver = get_network_driver(dev_os)
201+
except ModuleImportError as e:
202+
module.fail_json(msg="Failed to import napalm driver: " + str(e))
203+
204+
try:
205+
device = network_driver(
206+
hostname=hostname,
207+
username=username,
208+
password=password,
209+
timeout=timeout,
210+
optional_args=optional_args,
211+
)
212+
device.open()
213+
except Exception as e:
214+
module.fail_json(msg="cannot connect to device: " + str(e))
215+
216+
try:
217+
get_config_kwargs = {} # kwargs should match driver specific `get_config` method specs
218+
if retrieve is not None:
219+
get_config_kwargs["retrieve"] = retrieve
220+
if full is not None:
221+
get_config_kwargs["full"] = full
222+
if sanitized is not None:
223+
get_config_kwargs["sanitized"] = sanitized
224+
config_dict = device.get_config(**get_config_kwargs)
225+
results = {
226+
"changed": False,
227+
"napalm_config": config_dict,
228+
}
229+
except Exception as e:
230+
module.fail_json(msg="cannot retrieve device config:" + str(e))
231+
232+
module.exit_json(**results)
233+
234+
235+
if __name__ == "__main__":
236+
main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
napalm.py

0 commit comments

Comments
 (0)