Skip to content

Commit 36771e8

Browse files
authored
10520 remove Napalm code references (#11768)
* 10520 remove all Napalm code references * 10520 remove lldp * 10520 remove config, status - rebuild js * 10520 re-add config parameters * 10520 re-add serializer * 10520 update docs
1 parent 927371b commit 36771e8

File tree

26 files changed

+18
-1096
lines changed

26 files changed

+18
-1096
lines changed

docs/configuration/napalm.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# NAPALM Parameters
22

3+
!!! **Note:** As of NetBox v3.5, NAPALM integration has been moved to a plugin and these configuration parameters are now deprecated.
4+
35
## NAPALM_USERNAME
46

57
## NAPALM_PASSWORD

docs/features/api-integration.md

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ To learn more about this feature, check out the [webhooks documentation](../inte
3636

3737
To learn more about this feature, check out the [NAPALM documentation](../integrations/napalm.md).
3838

39+
As of NetBox v3.5, NAPALM integration has been moved to a plugin. Please see the [netbox_napalm_plugin](https://github.com/netbox-community/netbox-napalm) for installation instructions.
40+
3941
## Prometheus Metrics
4042

4143
NetBox includes a special `/metrics` view which exposes metrics for a [Prometheus](https://prometheus.io/) scraper, powered by the open source [django-prometheus](https://github.com/korfuri/django-prometheus) library. To learn more about this feature, check out the [Prometheus metrics documentation](../integrations/prometheus-metrics.md).

docs/installation/3-netbox.md

-8
Original file line numberDiff line numberDiff line change
@@ -199,14 +199,6 @@ When you have finished modifying the configuration, remember to save the file.
199199

200200
All Python packages required by NetBox are listed in `requirements.txt` and will be installed automatically. NetBox also supports some optional packages. If desired, these packages must be listed in `local_requirements.txt` within the NetBox root directory.
201201

202-
### NAPALM
203-
204-
Integration with the [NAPALM automation](../integrations/napalm.md) library allows NetBox to fetch live data from devices and return it to a requester via its REST API. The `NAPALM_USERNAME` and `NAPALM_PASSWORD` configuration parameters define the credentials to be used when connecting to a device.
205-
206-
```no-highlight
207-
sudo sh -c "echo 'napalm' >> /opt/netbox/local_requirements.txt"
208-
```
209-
210202
### Remote File Storage
211203

212204
By default, NetBox will use the local filesystem to store uploaded files. To use a remote filesystem, install the [`django-storages`](https://django-storages.readthedocs.io/en/stable/) library and configure your [desired storage backend](../configuration/system.md#storage_backend) in `configuration.py`.

docs/integrations/napalm.md

+1-72
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,3 @@
11
# NAPALM
22

3-
NetBox supports integration with the [NAPALM automation](https://github.com/napalm-automation/napalm) library. NAPALM allows NetBox to serve a proxy for operational data, fetching live data from network devices and returning it to a requester via its REST API. Note that NetBox does not store any NAPALM data locally.
4-
5-
The NetBox UI will display tabs for status, LLDP neighbors, and configuration under the device view if the following conditions are met:
6-
7-
* Device status is "Active"
8-
* A primary IP has been assigned to the device
9-
* A platform with a NAPALM driver has been assigned
10-
* The authenticated user has the `dcim.napalm_read_device` permission
11-
12-
!!! note
13-
To enable this integration, the NAPALM library must be installed. See [installation steps](../../installation/3-netbox/#napalm) for more information.
14-
15-
Below is an example REST API request and response:
16-
17-
```no-highlight
18-
GET /api/dcim/devices/1/napalm/?method=get_environment
19-
20-
{
21-
"get_environment": {
22-
...
23-
}
24-
}
25-
```
26-
27-
!!! note
28-
To make NAPALM requests via the NetBox REST API, a NetBox user must have assigned a permission granting the `napalm_read` action for the device object type.
29-
30-
## Authentication
31-
32-
By default, the [`NAPALM_USERNAME`](../configuration/napalm.md#napalm_username) and [`NAPALM_PASSWORD`](../configuration/napalm.md#napalm_password) configuration parameters are used for NAPALM authentication. They can be overridden for an individual API call by specifying the `X-NAPALM-Username` and `X-NAPALM-Password` headers.
33-
34-
```
35-
$ curl "http://localhost/api/dcim/devices/1/napalm/?method=get_environment" \
36-
-H "Authorization: Token $TOKEN" \
37-
-H "Content-Type: application/json" \
38-
-H "Accept: application/json; indent=4" \
39-
-H "X-NAPALM-Username: foo" \
40-
-H "X-NAPALM-Password: bar"
41-
```
42-
43-
## Method Support
44-
45-
The list of supported NAPALM methods depends on the [NAPALM driver](https://napalm.readthedocs.io/en/latest/support/index.html#general-support-matrix) configured for the platform of a device. Because there is no granular mechanism in place for limiting potentially disruptive requests, NetBox supports only read-only [get](https://napalm.readthedocs.io/en/latest/support/index.html#getters-support-matrix) methods.
46-
47-
## Multiple Methods
48-
49-
It is possible to request the output of multiple NAPALM methods in a single API request by passing multiple `method` parameters. For example:
50-
51-
```no-highlight
52-
GET /api/dcim/devices/1/napalm/?method=get_ntp_servers&method=get_ntp_peers
53-
54-
{
55-
"get_ntp_servers": {
56-
...
57-
},
58-
"get_ntp_peers": {
59-
...
60-
}
61-
}
62-
```
63-
64-
## Optional Arguments
65-
66-
The behavior of NAPALM drivers can be adjusted according to the [optional arguments](https://napalm.readthedocs.io/en/latest/support/index.html#optional-arguments). NetBox exposes those arguments using headers prefixed with `X-NAPALM-`. For example, the SSH port is changed to 2222 in this API call:
67-
68-
```
69-
$ curl "http://localhost/api/dcim/devices/1/napalm/?method=get_environment" \
70-
-H "Authorization: Token $TOKEN" \
71-
-H "Content-Type: application/json" \
72-
-H "Accept: application/json; indent=4" \
73-
-H "X-NAPALM-port: 2222"
74-
```
3+
As of NetBox v3.5, NAPALM integration has been moved to a plugin. Please see the [netbox_napalm_plugin](https://github.com/netbox-community/netbox-napalm) for installation instructions. **Note:** All previously entered NAPALM configuration data will be saved and automatically imported by the new plugin.

netbox/core/forms/model_forms.py

-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ def fieldsets(self):
5151

5252
def __init__(self, *args, **kwargs):
5353
super().__init__(*args, **kwargs)
54-
5554
# Determine the selected backend type
5655
backend_type = get_field_value(self, 'type')
5756
backend = registry['data_backends'].get(backend_type)

netbox/dcim/api/views.py

-118
Original file line numberDiff line numberDiff line change
@@ -419,124 +419,6 @@ def get_serializer_class(self):
419419

420420
return serializers.DeviceWithConfigContextSerializer
421421

422-
@swagger_auto_schema(
423-
manual_parameters=[
424-
Parameter(
425-
name='method',
426-
in_='query',
427-
required=True,
428-
type=openapi.TYPE_STRING
429-
)
430-
],
431-
responses={'200': serializers.DeviceNAPALMSerializer}
432-
)
433-
@action(detail=True, url_path='napalm')
434-
def napalm(self, request, pk):
435-
"""
436-
Execute a NAPALM method on a Device
437-
"""
438-
device = get_object_or_404(self.queryset, pk=pk)
439-
if not device.primary_ip:
440-
raise ServiceUnavailable("This device does not have a primary IP address configured.")
441-
if device.platform is None:
442-
raise ServiceUnavailable("No platform is configured for this device.")
443-
if not device.platform.napalm_driver:
444-
raise ServiceUnavailable(f"No NAPALM driver is configured for this device's platform: {device.platform}.")
445-
446-
# Check for primary IP address from NetBox object
447-
if device.primary_ip:
448-
host = str(device.primary_ip.address.ip)
449-
else:
450-
# Raise exception for no IP address and no Name if device.name does not exist
451-
if not device.name:
452-
raise ServiceUnavailable(
453-
"This device does not have a primary IP address or device name to lookup configured."
454-
)
455-
try:
456-
# Attempt to complete a DNS name resolution if no primary_ip is set
457-
host = socket.gethostbyname(device.name)
458-
except socket.gaierror:
459-
# Name lookup failure
460-
raise ServiceUnavailable(
461-
f"Name lookup failure, unable to resolve IP address for {device.name}. Please set Primary IP or "
462-
f"setup name resolution.")
463-
464-
# Check that NAPALM is installed
465-
try:
466-
import napalm
467-
from napalm.base.exceptions import ModuleImportError
468-
except ModuleNotFoundError as e:
469-
if getattr(e, 'name') == 'napalm':
470-
raise ServiceUnavailable("NAPALM is not installed. Please see the documentation for instructions.")
471-
raise e
472-
473-
# Validate the configured driver
474-
try:
475-
driver = napalm.get_network_driver(device.platform.napalm_driver)
476-
except ModuleImportError:
477-
raise ServiceUnavailable("NAPALM driver for platform {} not found: {}.".format(
478-
device.platform, device.platform.napalm_driver
479-
))
480-
481-
# Verify user permission
482-
if not request.user.has_perm('dcim.napalm_read_device'):
483-
return HttpResponseForbidden()
484-
485-
napalm_methods = request.GET.getlist('method')
486-
response = {m: None for m in napalm_methods}
487-
488-
config = get_config()
489-
username = config.NAPALM_USERNAME
490-
password = config.NAPALM_PASSWORD
491-
timeout = config.NAPALM_TIMEOUT
492-
optional_args = config.NAPALM_ARGS.copy()
493-
if device.platform.napalm_args is not None:
494-
optional_args.update(device.platform.napalm_args)
495-
496-
# Update NAPALM parameters according to the request headers
497-
for header in request.headers:
498-
if header[:9].lower() != 'x-napalm-':
499-
continue
500-
501-
key = header[9:]
502-
if key.lower() == 'username':
503-
username = request.headers[header]
504-
elif key.lower() == 'password':
505-
password = request.headers[header]
506-
elif key:
507-
optional_args[key.lower()] = request.headers[header]
508-
509-
# Connect to the device
510-
d = driver(
511-
hostname=host,
512-
username=username,
513-
password=password,
514-
timeout=timeout,
515-
optional_args=optional_args
516-
)
517-
try:
518-
d.open()
519-
except Exception as e:
520-
raise ServiceUnavailable("Error connecting to the device at {}: {}".format(host, e))
521-
522-
# Validate and execute each specified NAPALM method
523-
for method in napalm_methods:
524-
if not hasattr(driver, method):
525-
response[method] = {'error': 'Unknown NAPALM method'}
526-
continue
527-
if not method.startswith('get_'):
528-
response[method] = {'error': 'Only get_* NAPALM methods are supported'}
529-
continue
530-
try:
531-
response[method] = getattr(d, method)()
532-
except NotImplementedError:
533-
response[method] = {'error': 'Method {} not implemented for NAPALM driver {}'.format(method, driver)}
534-
except Exception as e:
535-
response[method] = {'error': 'Method {} failed: {}'.format(method, e)}
536-
d.close()
537-
538-
return Response(response)
539-
540422

541423
class VirtualDeviceContextViewSet(NetBoxModelViewSet):
542424
queryset = VirtualDeviceContext.objects.prefetch_related(

netbox/dcim/filtersets.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,7 @@ class PlatformFilterSet(OrganizationalModelFilterSet):
806806

807807
class Meta:
808808
model = Platform
809-
fields = ['id', 'name', 'slug', 'napalm_driver', 'description']
809+
fields = ['id', 'name', 'slug', 'description']
810810

811811

812812
class DeviceFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet, LocalConfigContextFilterSet):

netbox/dcim/forms/bulk_edit.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -476,10 +476,6 @@ class PlatformBulkEditForm(NetBoxModelBulkEditForm):
476476
queryset=Manufacturer.objects.all(),
477477
required=False
478478
)
479-
napalm_driver = forms.CharField(
480-
max_length=50,
481-
required=False
482-
)
483479
config_template = DynamicModelChoiceField(
484480
queryset=ConfigTemplate.objects.all(),
485481
required=False
@@ -491,9 +487,9 @@ class PlatformBulkEditForm(NetBoxModelBulkEditForm):
491487

492488
model = Platform
493489
fieldsets = (
494-
(None, ('manufacturer', 'config_template', 'napalm_driver', 'description')),
490+
(None, ('manufacturer', 'config_template', 'description')),
495491
)
496-
nullable_fields = ('manufacturer', 'config_template', 'napalm_driver', 'description')
492+
nullable_fields = ('manufacturer', 'config_template', 'description')
497493

498494

499495
class DeviceBulkEditForm(NetBoxModelBulkEditForm):

netbox/dcim/forms/bulk_import.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ class PlatformImportForm(NetBoxModelImportForm):
342342
class Meta:
343343
model = Platform
344344
fields = (
345-
'name', 'slug', 'manufacturer', 'config_template', 'napalm_driver', 'napalm_args', 'description', 'tags',
345+
'name', 'slug', 'manufacturer', 'config_template', 'description', 'tags',
346346
)
347347

348348

netbox/dcim/forms/model_forms.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -451,19 +451,15 @@ class PlatformForm(NetBoxModelForm):
451451

452452
fieldsets = (
453453
('Platform', (
454-
'name', 'slug', 'manufacturer', 'config_template', 'napalm_driver', 'napalm_args', 'description', 'tags',
455-
454+
'name', 'slug', 'manufacturer', 'config_template', 'description', 'tags',
456455
)),
457456
)
458457

459458
class Meta:
460459
model = Platform
461460
fields = [
462-
'name', 'slug', 'manufacturer', 'config_template', 'napalm_driver', 'napalm_args', 'description', 'tags',
461+
'name', 'slug', 'manufacturer', 'config_template', 'description', 'tags',
463462
]
464-
widgets = {
465-
'napalm_args': forms.Textarea(),
466-
}
467463

468464

469465
class DeviceForm(TenancyForm, NetBoxModelForm):

netbox/dcim/search.py

-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,6 @@ class PlatformIndex(SearchIndex):
172172
fields = (
173173
('name', 100),
174174
('slug', 110),
175-
('napalm_driver', 300),
176175
('description', 500),
177176
)
178177

netbox/dcim/tables/devices.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,11 @@ class PlatformTable(NetBoxTable):
133133
class Meta(NetBoxTable.Meta):
134134
model = models.Platform
135135
fields = (
136-
'pk', 'id', 'name', 'manufacturer', 'device_count', 'vm_count', 'slug', 'config_template', 'napalm_driver',
137-
'napalm_args', 'description', 'tags', 'actions', 'created', 'last_updated',
136+
'pk', 'id', 'name', 'manufacturer', 'device_count', 'vm_count', 'slug', 'config_template', 'description',
137+
'tags', 'actions', 'created', 'last_updated',
138138
)
139139
default_columns = (
140-
'pk', 'name', 'manufacturer', 'device_count', 'vm_count', 'napalm_driver', 'description',
140+
'pk', 'name', 'manufacturer', 'device_count', 'vm_count', 'description',
141141
)
142142

143143

netbox/dcim/tests/test_filtersets.py

+3-7
Original file line numberDiff line numberDiff line change
@@ -1469,9 +1469,9 @@ def setUpTestData(cls):
14691469
Manufacturer.objects.bulk_create(manufacturers)
14701470

14711471
platforms = (
1472-
Platform(name='Platform 1', slug='platform-1', manufacturer=manufacturers[0], napalm_driver='driver-1', description='A'),
1473-
Platform(name='Platform 2', slug='platform-2', manufacturer=manufacturers[1], napalm_driver='driver-2', description='B'),
1474-
Platform(name='Platform 3', slug='platform-3', manufacturer=manufacturers[2], napalm_driver='driver-3', description='C'),
1472+
Platform(name='Platform 1', slug='platform-1', manufacturer=manufacturers[0], description='A'),
1473+
Platform(name='Platform 2', slug='platform-2', manufacturer=manufacturers[1], description='B'),
1474+
Platform(name='Platform 3', slug='platform-3', manufacturer=manufacturers[2], description='C'),
14751475
)
14761476
Platform.objects.bulk_create(platforms)
14771477

@@ -1487,10 +1487,6 @@ def test_description(self):
14871487
params = {'description': ['A', 'B']}
14881488
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
14891489

1490-
def test_napalm_driver(self):
1491-
params = {'napalm_driver': ['driver-1', 'driver-2']}
1492-
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
1493-
14941490
def test_manufacturer(self):
14951491
manufacturers = Manufacturer.objects.all()[:2]
14961492
params = {'manufacturer_id': [manufacturers[0].pk, manufacturers[1].pk]}

netbox/dcim/tests/test_views.py

-3
Original file line numberDiff line numberDiff line change
@@ -1591,8 +1591,6 @@ def setUpTestData(cls):
15911591
'name': 'Platform X',
15921592
'slug': 'platform-x',
15931593
'manufacturer': manufacturer.pk,
1594-
'napalm_driver': 'junos',
1595-
'napalm_args': None,
15961594
'description': 'A new platform',
15971595
'tags': [t.pk for t in tags],
15981596
}
@@ -1612,7 +1610,6 @@ def setUpTestData(cls):
16121610
)
16131611

16141612
cls.bulk_edit_data = {
1615-
'napalm_driver': 'ios',
16161613
'description': 'New description',
16171614
}
16181615

0 commit comments

Comments
 (0)