Skip to content

Commit

Permalink
10520 remove Napalm code references (#11768)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
arthanson authored Feb 24, 2023
1 parent 927371b commit 36771e8
Show file tree
Hide file tree
Showing 26 changed files with 18 additions and 1,096 deletions.
2 changes: 2 additions & 0 deletions docs/configuration/napalm.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# NAPALM Parameters

!!! **Note:** As of NetBox v3.5, NAPALM integration has been moved to a plugin and these configuration parameters are now deprecated.

## NAPALM_USERNAME

## NAPALM_PASSWORD
Expand Down
2 changes: 2 additions & 0 deletions docs/features/api-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ To learn more about this feature, check out the [webhooks documentation](../inte

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

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.

## Prometheus Metrics

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).
8 changes: 0 additions & 8 deletions docs/installation/3-netbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,14 +199,6 @@ When you have finished modifying the configuration, remember to save the file.

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.

### NAPALM

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.

```no-highlight
sudo sh -c "echo 'napalm' >> /opt/netbox/local_requirements.txt"
```

### Remote File Storage

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`.
Expand Down
73 changes: 1 addition & 72 deletions docs/integrations/napalm.md
Original file line number Diff line number Diff line change
@@ -1,74 +1,3 @@
# NAPALM

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.

The NetBox UI will display tabs for status, LLDP neighbors, and configuration under the device view if the following conditions are met:

* Device status is "Active"
* A primary IP has been assigned to the device
* A platform with a NAPALM driver has been assigned
* The authenticated user has the `dcim.napalm_read_device` permission

!!! note
To enable this integration, the NAPALM library must be installed. See [installation steps](../../installation/3-netbox/#napalm) for more information.

Below is an example REST API request and response:

```no-highlight
GET /api/dcim/devices/1/napalm/?method=get_environment
{
"get_environment": {
...
}
}
```

!!! note
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.

## Authentication

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.

```
$ curl "http://localhost/api/dcim/devices/1/napalm/?method=get_environment" \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json; indent=4" \
-H "X-NAPALM-Username: foo" \
-H "X-NAPALM-Password: bar"
```

## Method Support

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.

## Multiple Methods

It is possible to request the output of multiple NAPALM methods in a single API request by passing multiple `method` parameters. For example:

```no-highlight
GET /api/dcim/devices/1/napalm/?method=get_ntp_servers&method=get_ntp_peers
{
"get_ntp_servers": {
...
},
"get_ntp_peers": {
...
}
}
```

## Optional Arguments

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:

```
$ curl "http://localhost/api/dcim/devices/1/napalm/?method=get_environment" \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json; indent=4" \
-H "X-NAPALM-port: 2222"
```
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.
1 change: 0 additions & 1 deletion netbox/core/forms/model_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ def fieldsets(self):

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

# Determine the selected backend type
backend_type = get_field_value(self, 'type')
backend = registry['data_backends'].get(backend_type)
Expand Down
118 changes: 0 additions & 118 deletions netbox/dcim/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,124 +419,6 @@ def get_serializer_class(self):

return serializers.DeviceWithConfigContextSerializer

@swagger_auto_schema(
manual_parameters=[
Parameter(
name='method',
in_='query',
required=True,
type=openapi.TYPE_STRING
)
],
responses={'200': serializers.DeviceNAPALMSerializer}
)
@action(detail=True, url_path='napalm')
def napalm(self, request, pk):
"""
Execute a NAPALM method on a Device
"""
device = get_object_or_404(self.queryset, pk=pk)
if not device.primary_ip:
raise ServiceUnavailable("This device does not have a primary IP address configured.")
if device.platform is None:
raise ServiceUnavailable("No platform is configured for this device.")
if not device.platform.napalm_driver:
raise ServiceUnavailable(f"No NAPALM driver is configured for this device's platform: {device.platform}.")

# Check for primary IP address from NetBox object
if device.primary_ip:
host = str(device.primary_ip.address.ip)
else:
# Raise exception for no IP address and no Name if device.name does not exist
if not device.name:
raise ServiceUnavailable(
"This device does not have a primary IP address or device name to lookup configured."
)
try:
# Attempt to complete a DNS name resolution if no primary_ip is set
host = socket.gethostbyname(device.name)
except socket.gaierror:
# Name lookup failure
raise ServiceUnavailable(
f"Name lookup failure, unable to resolve IP address for {device.name}. Please set Primary IP or "
f"setup name resolution.")

# Check that NAPALM is installed
try:
import napalm
from napalm.base.exceptions import ModuleImportError
except ModuleNotFoundError as e:
if getattr(e, 'name') == 'napalm':
raise ServiceUnavailable("NAPALM is not installed. Please see the documentation for instructions.")
raise e

# Validate the configured driver
try:
driver = napalm.get_network_driver(device.platform.napalm_driver)
except ModuleImportError:
raise ServiceUnavailable("NAPALM driver for platform {} not found: {}.".format(
device.platform, device.platform.napalm_driver
))

# Verify user permission
if not request.user.has_perm('dcim.napalm_read_device'):
return HttpResponseForbidden()

napalm_methods = request.GET.getlist('method')
response = {m: None for m in napalm_methods}

config = get_config()
username = config.NAPALM_USERNAME
password = config.NAPALM_PASSWORD
timeout = config.NAPALM_TIMEOUT
optional_args = config.NAPALM_ARGS.copy()
if device.platform.napalm_args is not None:
optional_args.update(device.platform.napalm_args)

# Update NAPALM parameters according to the request headers
for header in request.headers:
if header[:9].lower() != 'x-napalm-':
continue

key = header[9:]
if key.lower() == 'username':
username = request.headers[header]
elif key.lower() == 'password':
password = request.headers[header]
elif key:
optional_args[key.lower()] = request.headers[header]

# Connect to the device
d = driver(
hostname=host,
username=username,
password=password,
timeout=timeout,
optional_args=optional_args
)
try:
d.open()
except Exception as e:
raise ServiceUnavailable("Error connecting to the device at {}: {}".format(host, e))

# Validate and execute each specified NAPALM method
for method in napalm_methods:
if not hasattr(driver, method):
response[method] = {'error': 'Unknown NAPALM method'}
continue
if not method.startswith('get_'):
response[method] = {'error': 'Only get_* NAPALM methods are supported'}
continue
try:
response[method] = getattr(d, method)()
except NotImplementedError:
response[method] = {'error': 'Method {} not implemented for NAPALM driver {}'.format(method, driver)}
except Exception as e:
response[method] = {'error': 'Method {} failed: {}'.format(method, e)}
d.close()

return Response(response)


class VirtualDeviceContextViewSet(NetBoxModelViewSet):
queryset = VirtualDeviceContext.objects.prefetch_related(
Expand Down
2 changes: 1 addition & 1 deletion netbox/dcim/filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,7 @@ class PlatformFilterSet(OrganizationalModelFilterSet):

class Meta:
model = Platform
fields = ['id', 'name', 'slug', 'napalm_driver', 'description']
fields = ['id', 'name', 'slug', 'description']


class DeviceFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet, LocalConfigContextFilterSet):
Expand Down
8 changes: 2 additions & 6 deletions netbox/dcim/forms/bulk_edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -476,10 +476,6 @@ class PlatformBulkEditForm(NetBoxModelBulkEditForm):
queryset=Manufacturer.objects.all(),
required=False
)
napalm_driver = forms.CharField(
max_length=50,
required=False
)
config_template = DynamicModelChoiceField(
queryset=ConfigTemplate.objects.all(),
required=False
Expand All @@ -491,9 +487,9 @@ class PlatformBulkEditForm(NetBoxModelBulkEditForm):

model = Platform
fieldsets = (
(None, ('manufacturer', 'config_template', 'napalm_driver', 'description')),
(None, ('manufacturer', 'config_template', 'description')),
)
nullable_fields = ('manufacturer', 'config_template', 'napalm_driver', 'description')
nullable_fields = ('manufacturer', 'config_template', 'description')


class DeviceBulkEditForm(NetBoxModelBulkEditForm):
Expand Down
2 changes: 1 addition & 1 deletion netbox/dcim/forms/bulk_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ class PlatformImportForm(NetBoxModelImportForm):
class Meta:
model = Platform
fields = (
'name', 'slug', 'manufacturer', 'config_template', 'napalm_driver', 'napalm_args', 'description', 'tags',
'name', 'slug', 'manufacturer', 'config_template', 'description', 'tags',
)


Expand Down
8 changes: 2 additions & 6 deletions netbox/dcim/forms/model_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,19 +451,15 @@ class PlatformForm(NetBoxModelForm):

fieldsets = (
('Platform', (
'name', 'slug', 'manufacturer', 'config_template', 'napalm_driver', 'napalm_args', 'description', 'tags',

'name', 'slug', 'manufacturer', 'config_template', 'description', 'tags',
)),
)

class Meta:
model = Platform
fields = [
'name', 'slug', 'manufacturer', 'config_template', 'napalm_driver', 'napalm_args', 'description', 'tags',
'name', 'slug', 'manufacturer', 'config_template', 'description', 'tags',
]
widgets = {
'napalm_args': forms.Textarea(),
}


class DeviceForm(TenancyForm, NetBoxModelForm):
Expand Down
1 change: 0 additions & 1 deletion netbox/dcim/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ class PlatformIndex(SearchIndex):
fields = (
('name', 100),
('slug', 110),
('napalm_driver', 300),
('description', 500),
)

Expand Down
6 changes: 3 additions & 3 deletions netbox/dcim/tables/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,11 @@ class PlatformTable(NetBoxTable):
class Meta(NetBoxTable.Meta):
model = models.Platform
fields = (
'pk', 'id', 'name', 'manufacturer', 'device_count', 'vm_count', 'slug', 'config_template', 'napalm_driver',
'napalm_args', 'description', 'tags', 'actions', 'created', 'last_updated',
'pk', 'id', 'name', 'manufacturer', 'device_count', 'vm_count', 'slug', 'config_template', 'description',
'tags', 'actions', 'created', 'last_updated',
)
default_columns = (
'pk', 'name', 'manufacturer', 'device_count', 'vm_count', 'napalm_driver', 'description',
'pk', 'name', 'manufacturer', 'device_count', 'vm_count', 'description',
)


Expand Down
10 changes: 3 additions & 7 deletions netbox/dcim/tests/test_filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1469,9 +1469,9 @@ def setUpTestData(cls):
Manufacturer.objects.bulk_create(manufacturers)

platforms = (
Platform(name='Platform 1', slug='platform-1', manufacturer=manufacturers[0], napalm_driver='driver-1', description='A'),
Platform(name='Platform 2', slug='platform-2', manufacturer=manufacturers[1], napalm_driver='driver-2', description='B'),
Platform(name='Platform 3', slug='platform-3', manufacturer=manufacturers[2], napalm_driver='driver-3', description='C'),
Platform(name='Platform 1', slug='platform-1', manufacturer=manufacturers[0], description='A'),
Platform(name='Platform 2', slug='platform-2', manufacturer=manufacturers[1], description='B'),
Platform(name='Platform 3', slug='platform-3', manufacturer=manufacturers[2], description='C'),
)
Platform.objects.bulk_create(platforms)

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

def test_napalm_driver(self):
params = {'napalm_driver': ['driver-1', 'driver-2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)

def test_manufacturer(self):
manufacturers = Manufacturer.objects.all()[:2]
params = {'manufacturer_id': [manufacturers[0].pk, manufacturers[1].pk]}
Expand Down
3 changes: 0 additions & 3 deletions netbox/dcim/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1591,8 +1591,6 @@ def setUpTestData(cls):
'name': 'Platform X',
'slug': 'platform-x',
'manufacturer': manufacturer.pk,
'napalm_driver': 'junos',
'napalm_args': None,
'description': 'A new platform',
'tags': [t.pk for t in tags],
}
Expand All @@ -1612,7 +1610,6 @@ def setUpTestData(cls):
)

cls.bulk_edit_data = {
'napalm_driver': 'ios',
'description': 'New description',
}

Expand Down
Loading

0 comments on commit 36771e8

Please sign in to comment.