Skip to content

Commit 8b222e1

Browse files
Enhancement: Lookup - Add lookup for NetBox Plugins (#360)
1 parent 815ce23 commit 8b222e1

File tree

1 file changed

+112
-37
lines changed

1 file changed

+112
-37
lines changed

plugins/lookup/nb_lookup.py

Lines changed: 112 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from __future__ import absolute_import, division, print_function
1313

14+
import functools
1415
from pprint import pformat
1516

1617
from ansible.errors import AnsibleError
@@ -45,6 +46,10 @@
4546
description:
4647
- The api_filter to use.
4748
required: False
49+
plugin:
50+
description:
51+
- The Netbox plugin to query
52+
required: False
4853
token:
4954
description:
5055
- The API token created through Netbox
@@ -98,6 +103,18 @@
98103
- name: "Obtain secrets for R1-Device"
99104
debug:
100105
msg: "{{ query('netbox.netbox.nb_lookup', 'secrets', api_filter='device=R1-Device', api_endpoint='http://localhost/', token='<redacted>', key_file='~/.ssh/id_rsa') }}"
106+
107+
# Fetch bgp sessions for R1-device
108+
tasks:
109+
- name: "Obtain bgp sessions for R1-Device"
110+
debug:
111+
msg: "{{ query('netbox.netbox.nb_lookup', 'bgp_sessions',
112+
api_filter='device=R1-Device',
113+
api_endpoint='http://localhost/',
114+
token='<redacted>',
115+
plugin='mycustomstuff') }}"
116+
117+
msg: "{{ query('netbox.netbox.nb_lookup', 'secrets', api_filter='device=R1-Device', api_endpoint='http://localhost/', token='<redacted>', key_file='~/.ssh/id_rsa') }}"
101118
"""
102119

103120
RETURN = """
@@ -187,6 +204,78 @@ def get_endpoint(netbox, term):
187204
return netbox_endpoint_map[term]["endpoint"]
188205

189206

207+
def build_filters(filters):
208+
"""
209+
This will build the filters to be handed to NetBox endpoint call if they exist.
210+
211+
Args:
212+
filters (str): String of filters to parse.
213+
214+
Returns:
215+
result (list): List of dictionaries to filter by.
216+
"""
217+
filter = {}
218+
args_split = split_args(filters)
219+
args = [parse_kv(x) for x in args_split]
220+
for arg in args:
221+
for k, v in arg.items():
222+
if k not in filter:
223+
filter[k] = list()
224+
filter[k].append(v)
225+
else:
226+
filter[k].append(v)
227+
228+
return filter
229+
230+
231+
def get_plugin_endpoint(netbox, plugin, term):
232+
"""
233+
get_plugin_endpoint(netbox, plugin, term)
234+
netbox: a predefined pynetbox.api() pointing to a valid instance
235+
of Netbox
236+
plugin: a string referencing the plugin name
237+
term: the term passed to the lookup function upon which the api
238+
call will be identified
239+
"""
240+
attr = "plugins.%s.%s" % (plugin, term)
241+
242+
def _getattr(netbox, attr):
243+
return getattr(netbox, attr)
244+
245+
return functools.reduce(_getattr, [netbox] + attr.split("."))
246+
247+
248+
def make_netbox_call(nb_endpoint, filters=None):
249+
"""
250+
Wrapper for calls to NetBox and handle any possible errors.
251+
252+
Args:
253+
nb_endpoint (object): The NetBox endpoint object to make calls.
254+
255+
Returns:
256+
results (object): Pynetbox result.
257+
258+
Raises:
259+
AnsibleError: Ansible Error containing an error message.
260+
"""
261+
try:
262+
if filters:
263+
results = nb_endpoint.filter(**filters)
264+
else:
265+
results = nb_endpoint.all()
266+
except pynetbox.RequestError as e:
267+
if e.req.status_code == 404 and "plugins" in e:
268+
raise AnsibleError(
269+
"{0} - Not a valid plugin endpoint, please make sure to provide valid plugin endpoint.".format(
270+
e.error
271+
)
272+
)
273+
else:
274+
raise AnsibleError(e.error)
275+
276+
return results
277+
278+
190279
class LookupModule(LookupBase):
191280
"""
192281
LookupModule(LookupBase) is defined by Ansible
@@ -200,6 +289,7 @@ def run(self, terms, variables=None, **kwargs):
200289
netbox_private_key_file = kwargs.get("key_file")
201290
netbox_api_filter = kwargs.get("api_filter")
202291
netbox_raw_return = kwargs.get("raw_data")
292+
netbox_plugin = kwargs.get("plugin")
203293

204294
if not isinstance(terms, list):
205295
terms = [terms]
@@ -222,54 +312,39 @@ def run(self, terms, variables=None, **kwargs):
222312

223313
results = []
224314
for term in terms:
225-
226-
try:
227-
endpoint = get_endpoint(netbox, term)
228-
except KeyError:
229-
raise AnsibleError("Unrecognised term %s. Check documentation" % term)
315+
if netbox_plugin:
316+
endpoint = get_plugin_endpoint(netbox, netbox_plugin, term)
317+
else:
318+
try:
319+
endpoint = get_endpoint(netbox, term)
320+
except KeyError:
321+
raise AnsibleError(
322+
"Unrecognised term %s. Check documentation" % term
323+
)
230324

231325
Display().vvvv(
232326
u"Netbox lookup for %s to %s using token %s filter %s"
233327
% (term, netbox_api_endpoint, netbox_api_token, netbox_api_filter)
234328
)
235329

236330
if netbox_api_filter:
237-
args_split = split_args(netbox_api_filter)
238-
args = [parse_kv(x) for x in args_split]
239-
filter = {}
240-
for arg in args:
241-
for k, v in arg.items():
242-
if k not in filter:
243-
filter[k] = list()
244-
filter[k].append(v)
245-
else:
246-
filter[k].append(v)
331+
filter = build_filters(netbox_api_filter)
247332

248333
Display().vvvv("filter is %s" % filter)
249334

250-
for res in endpoint.filter(**filter):
251-
252-
Display().vvvvv(pformat(dict(res)))
253-
254-
if netbox_raw_return:
255-
results.append(dict(res))
256-
257-
else:
258-
key = dict(res)["id"]
259-
result = {key: dict(res)}
260-
results.extend(self._flatten_hash_to_list(result))
261-
262-
else:
263-
for res in endpoint.all():
264-
265-
Display().vvvvv(pformat(dict(res)))
335+
# Make call to NetBox API and capture any failures
336+
nb_data = make_netbox_call(
337+
endpoint, filters=filter if netbox_api_filter else None
338+
)
266339

267-
if netbox_raw_return:
268-
results.append(dict(res))
340+
for data in nb_data:
341+
Display().vvvvv(pformat(dict(data)))
269342

270-
else:
271-
key = dict(res)["id"]
272-
result = {key: dict(res)}
273-
results.extend(self._flatten_hash_to_list(result))
343+
if netbox_raw_return:
344+
results.append(dict(data))
345+
else:
346+
key = dict(data)["id"]
347+
result = {key: dict(data)}
348+
results.extend(self._flatten_hash_to_list(result))
274349

275350
return results

0 commit comments

Comments
 (0)