Skip to content

Commit 07b71c2

Browse files
author
Zach Moody
authored
Merge pull request #48 from digitalocean/napalm-endpoint
Fixes #45 - NAPALM Endpoint Support
2 parents 1069258 + 4b2a010 commit 07b71c2

File tree

7 files changed

+101
-3
lines changed

7 files changed

+101
-3
lines changed

pynetbox/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.2.0
1+
3.3.0

pynetbox/dcim.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
limitations under the License.
1515
'''
1616
from pynetbox.lib.response import Record
17+
from pynetbox.lib.endpoint import RODetailEndpoint
1718
from pynetbox.ipam import IpAddresses
1819

1920

@@ -43,6 +44,27 @@ class Devices(Record):
4344
primary_ip4 = IpAddresses
4445
primary_ip6 = IpAddresses
4546

47+
@property
48+
def napalm(self):
49+
""" Represents the ``napalm`` detail endpoint.
50+
51+
Returns a DetailEndpoint object that is the interface for
52+
viewing response from the napalm endpoint.
53+
54+
:returns: :py:class:`.DetailEndpoint`
55+
56+
:Examples:
57+
58+
>>> device = nb.ipam.devices.get(123)
59+
>>> device.napalm.list(method='get_facts')
60+
{"get_facts": {"interface_list": ["ge-0/0/0"]}}
61+
62+
"""
63+
return RODetailEndpoint(
64+
'napalm',
65+
parent_obj=self,
66+
)
67+
4668

4769
class InterfaceConnections(Record):
4870

pynetbox/lib/endpoint.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
'''
1616
from collections import defaultdict
1717

18-
from pynetbox.lib.query import Request
18+
from pynetbox.lib.query import Request, url_param_builder
1919
from pynetbox.lib.response import Record, IPRecord
2020

2121
CACHE = defaultdict(list)
@@ -327,14 +327,25 @@ def __init__(self, name, parent_obj=None):
327327
version=self.version,
328328
)
329329

330-
def list(self):
330+
def list(self, **kwargs):
331331
"""The view operation for a detail endpoint
332332
333333
Returns the response from NetBox for a detail endpoint.
334334
335+
:args \**kwargs: key/value pairs that get converted into url
336+
parameters when passed to the endpoint.
337+
E.g. ``.list(method='get_facts')`` would be converted to
338+
``.../?method=get_facts``.
339+
335340
:returns: A dictionary or list of dictionaries its retrieved
336341
from NetBox.
337342
"""
343+
if kwargs:
344+
self.request_kwargs['base'] = '{}{}'.format(
345+
self.url,
346+
url_param_builder(kwargs)
347+
)
348+
338349
return Request(**self.request_kwargs).get()
339350

340351
def create(self, data={}):
@@ -351,3 +362,11 @@ def create(self, data={}):
351362
NetBox.
352363
"""
353364
return Request(**self.request_kwargs).post(data)
365+
366+
367+
class RODetailEndpoint(DetailEndpoint):
368+
369+
def create(self, data={}):
370+
raise NotImplementedError(
371+
'Writes are not supported for this endpoint.'
372+
)

pynetbox/lib/query.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@
1919
import requests
2020

2121

22+
def url_param_builder(param_dict):
23+
'''Builds url parameters
24+
25+
Creates URL paramters (e.g. '.../?xyz=r21&abc=123') from a dict
26+
passed in param_dict
27+
'''
28+
param_list = []
29+
for key, val in param_dict.items():
30+
param_list.append('{}={}'.format(key, val))
31+
return '?{}'.format('&'.join(param_list))
32+
33+
2234
class RequestError(Exception):
2335
"""Basic Request Exception
2436

tests/fixtures/dcim/napalm.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"get_facts": {
3+
"interface_list": [
4+
"xe-0/0/0"
5+
],
6+
"serial_number": "ABC!@#",
7+
"vendor": "PacketPusher",
8+
"os_version": "1.1",
9+
"hostname": "test-1",
10+
"model": "UnobtaniumX",
11+
"fqdn": "None",
12+
"uptime": 2
13+
}
14+
}

tests/test_dcim.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,23 @@ def test_get_recurse(self, mock):
249249
self.ret
250250
))
251251

252+
@patch(
253+
'pynetbox.lib.query.requests.get',
254+
side_effect=[
255+
Response(fixture='dcim/device.json'),
256+
Response(fixture='dcim/napalm.json'),
257+
]
258+
)
259+
def test_get_napalm(self, mock):
260+
test = nb.devices.get(1)
261+
ret = test.napalm.list(method='get_facts')
262+
mock.assert_called_with(
263+
'http://localhost:8000/api/dcim/devices/1/napalm/?method=get_facts',
264+
headers=HEADERS
265+
)
266+
self.assertTrue(ret)
267+
self.assertTrue(ret['get_facts'])
268+
252269

253270
class SiteTestCase(unittest.TestCase, GenericTest):
254271
name = 'sites'

tests/test_query.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import unittest
2+
from collections import OrderedDict
3+
4+
from pynetbox.lib.query import url_param_builder
5+
6+
7+
class TestQuery(unittest.TestCase):
8+
9+
def test_url_param_builder(self):
10+
test_params = OrderedDict([
11+
('abc', '123'),
12+
('xyz', '321'),
13+
])
14+
self.assertEqual(url_param_builder(test_params), '?abc=123&xyz=321')

0 commit comments

Comments
 (0)