netbox-custom-widget 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- netbox_custom_widget-0.1.0/LICENSE +15 -0
- netbox_custom_widget-0.1.0/PKG-INFO +82 -0
- netbox_custom_widget-0.1.0/README.md +54 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget/__init__.py +93 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget/api/__init__.py +0 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget/api/serializers.py +40 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget/api/urls.py +10 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget/api/views.py +15 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget/filtersets.py +26 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget/forms.py +91 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget/migrations/0001_initial.py +132 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget/migrations/__init__.py +0 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget/models.py +106 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget/navigation.py +28 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget/tables.py +40 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget/templates/netbox_custom_widget/customapiendpoint.html +114 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget/templates/netbox_custom_widget/widgets/custom_api.html +24 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget/templates/netbox_custom_widget/widgets/custom_api_content.html +68 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget/urls.py +68 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget/utils.py +185 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget/views.py +126 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget/widgets.py +97 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget.egg-info/PKG-INFO +82 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget.egg-info/SOURCES.txt +27 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget.egg-info/dependency_links.txt +1 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget.egg-info/requires.txt +6 -0
- netbox_custom_widget-0.1.0/netbox_custom_widget.egg-info/top_level.txt +1 -0
- netbox_custom_widget-0.1.0/pyproject.toml +54 -0
- netbox_custom_widget-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
you may not use this file except in compliance with the License.
|
|
7
|
+
You may obtain a copy of the License at
|
|
8
|
+
|
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
|
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
See the License for the specific language governing permissions and
|
|
15
|
+
limitations under the License.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: netbox-custom-widget
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: NetBox plugin for custom API dashboard widgets - call any API and display results
|
|
5
|
+
Author-email: sieteunoseis <jeremy.worden@gmail.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/sieteunoseis/netbox-custom-widget
|
|
8
|
+
Project-URL: Repository, https://github.com/sieteunoseis/netbox-custom-widget
|
|
9
|
+
Project-URL: Issues, https://github.com/sieteunoseis/netbox-custom-widget/issues
|
|
10
|
+
Keywords: netbox,netbox-plugin,dashboard,widget,api
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Framework :: Django
|
|
13
|
+
Classifier: Intended Audience :: System Administrators
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: requests>=2.28.0
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: black; extra == "dev"
|
|
25
|
+
Requires-Dist: flake8; extra == "dev"
|
|
26
|
+
Requires-Dist: isort; extra == "dev"
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# NetBox Custom Widget
|
|
30
|
+
|
|
31
|
+
A NetBox plugin that provides configurable dashboard widgets for calling external APIs and displaying results. Similar to Homepage's Custom API widget.
|
|
32
|
+
|
|
33
|
+
## Features
|
|
34
|
+
|
|
35
|
+
- Call any HTTP API endpoint and display JSON results on your NetBox dashboard
|
|
36
|
+
- Configurable field mappings with dot-notation for nested JSON access
|
|
37
|
+
- Auto-refresh via HTMX at configurable intervals
|
|
38
|
+
- Adaptive color coding for status values (e.g., UCCE process monitoring)
|
|
39
|
+
- Pre-define endpoints in plugin configuration for automatic provisioning
|
|
40
|
+
- Full CRUD interface for managing API endpoints
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install netbox-custom-widget
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Add to your NetBox configuration:
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
PLUGINS = ['netbox_custom_widget']
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Configuration
|
|
55
|
+
|
|
56
|
+
Define API endpoints in your `plugins.py`:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
PLUGINS_CONFIG = {
|
|
60
|
+
'netbox_custom_widget': {
|
|
61
|
+
'verify_ssl': False,
|
|
62
|
+
'endpoints': [
|
|
63
|
+
{
|
|
64
|
+
'name': 'My API Status',
|
|
65
|
+
'url': 'https://api.example.com/status',
|
|
66
|
+
'headers': {'Authorization': 'Bearer token123'},
|
|
67
|
+
'mappings': [
|
|
68
|
+
{'field': 'status', 'label': 'Status', 'color': 'adaptive'},
|
|
69
|
+
{'field': 'uptime', 'label': 'Uptime', 'suffix': 'hours'},
|
|
70
|
+
],
|
|
71
|
+
'display_mode': 'list',
|
|
72
|
+
'refresh_interval': 30,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Requirements
|
|
80
|
+
|
|
81
|
+
- NetBox >= 4.0.0
|
|
82
|
+
- Python >= 3.10
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# NetBox Custom Widget
|
|
2
|
+
|
|
3
|
+
A NetBox plugin that provides configurable dashboard widgets for calling external APIs and displaying results. Similar to Homepage's Custom API widget.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Call any HTTP API endpoint and display JSON results on your NetBox dashboard
|
|
8
|
+
- Configurable field mappings with dot-notation for nested JSON access
|
|
9
|
+
- Auto-refresh via HTMX at configurable intervals
|
|
10
|
+
- Adaptive color coding for status values (e.g., UCCE process monitoring)
|
|
11
|
+
- Pre-define endpoints in plugin configuration for automatic provisioning
|
|
12
|
+
- Full CRUD interface for managing API endpoints
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install netbox-custom-widget
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Add to your NetBox configuration:
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
PLUGINS = ['netbox_custom_widget']
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Configuration
|
|
27
|
+
|
|
28
|
+
Define API endpoints in your `plugins.py`:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
PLUGINS_CONFIG = {
|
|
32
|
+
'netbox_custom_widget': {
|
|
33
|
+
'verify_ssl': False,
|
|
34
|
+
'endpoints': [
|
|
35
|
+
{
|
|
36
|
+
'name': 'My API Status',
|
|
37
|
+
'url': 'https://api.example.com/status',
|
|
38
|
+
'headers': {'Authorization': 'Bearer token123'},
|
|
39
|
+
'mappings': [
|
|
40
|
+
{'field': 'status', 'label': 'Status', 'color': 'adaptive'},
|
|
41
|
+
{'field': 'uptime', 'label': 'Uptime', 'suffix': 'hours'},
|
|
42
|
+
],
|
|
43
|
+
'display_mode': 'list',
|
|
44
|
+
'refresh_interval': 30,
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Requirements
|
|
52
|
+
|
|
53
|
+
- NetBox >= 4.0.0
|
|
54
|
+
- Python >= 3.10
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
NetBox Custom Widget Plugin
|
|
3
|
+
|
|
4
|
+
Configurable dashboard widgets for calling external APIs and displaying results.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
|
|
9
|
+
from django.conf import settings
|
|
10
|
+
from django.db.models.signals import post_migrate
|
|
11
|
+
from netbox.plugins import PluginConfig
|
|
12
|
+
|
|
13
|
+
__version__ = "0.1.0"
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def provision_endpoints(sender, **kwargs):
|
|
19
|
+
"""Create or update CustomAPIEndpoint objects from plugin configuration."""
|
|
20
|
+
if sender.name != "netbox_custom_widget":
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
from django.db import OperationalError, ProgrammingError
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
from .models import CustomAPIEndpoint
|
|
27
|
+
|
|
28
|
+
config = settings.PLUGINS_CONFIG.get("netbox_custom_widget", {})
|
|
29
|
+
endpoints_config = config.get("endpoints", [])
|
|
30
|
+
global_verify_ssl = config.get("verify_ssl", True)
|
|
31
|
+
|
|
32
|
+
for ep_config in endpoints_config:
|
|
33
|
+
name = ep_config.get("name")
|
|
34
|
+
if not name:
|
|
35
|
+
logger.warning("Skipping endpoint config without 'name'")
|
|
36
|
+
continue
|
|
37
|
+
|
|
38
|
+
defaults = {
|
|
39
|
+
"url": ep_config.get("url", ""),
|
|
40
|
+
"http_method": ep_config.get("http_method", "GET"),
|
|
41
|
+
"headers": ep_config.get("headers", {}),
|
|
42
|
+
"body": ep_config.get("body", ""),
|
|
43
|
+
"mappings": ep_config.get("mappings", []),
|
|
44
|
+
"display_mode": ep_config.get("display_mode", "list"),
|
|
45
|
+
"refresh_interval": ep_config.get("refresh_interval", 30),
|
|
46
|
+
"verify_ssl": ep_config.get("verify_ssl", global_verify_ssl),
|
|
47
|
+
"timeout": ep_config.get("timeout", 30),
|
|
48
|
+
"description": ep_config.get("description", ""),
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
obj, created = CustomAPIEndpoint.objects.update_or_create(
|
|
52
|
+
name=name,
|
|
53
|
+
defaults=defaults,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
if created:
|
|
57
|
+
logger.info(f"Created API endpoint: {name}")
|
|
58
|
+
else:
|
|
59
|
+
logger.debug(f"Updated API endpoint: {name}")
|
|
60
|
+
|
|
61
|
+
except (OperationalError, ProgrammingError):
|
|
62
|
+
pass
|
|
63
|
+
except Exception as e:
|
|
64
|
+
logger.warning(f"Could not provision endpoints: {e}")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class CustomWidgetConfig(PluginConfig):
|
|
68
|
+
"""Plugin configuration for NetBox Custom Widget."""
|
|
69
|
+
|
|
70
|
+
name = "netbox_custom_widget"
|
|
71
|
+
verbose_name = "Custom Widget"
|
|
72
|
+
description = "Configurable dashboard widgets for calling external APIs"
|
|
73
|
+
version = __version__
|
|
74
|
+
author = "Jeremy Worden"
|
|
75
|
+
author_email = "jeremy.worden@gmail.com"
|
|
76
|
+
base_url = "custom-widget"
|
|
77
|
+
min_version = "4.0.0"
|
|
78
|
+
|
|
79
|
+
required_settings = []
|
|
80
|
+
|
|
81
|
+
default_settings = {
|
|
82
|
+
"verify_ssl": True,
|
|
83
|
+
"endpoints": [],
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
def ready(self):
|
|
87
|
+
"""Register signal and import widgets."""
|
|
88
|
+
super().ready()
|
|
89
|
+
post_migrate.connect(provision_endpoints, sender=self)
|
|
90
|
+
from . import widgets # noqa: F401
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
config = CustomWidgetConfig
|
|
File without changes
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""API serializers for NetBox Custom Widget plugin."""
|
|
2
|
+
|
|
3
|
+
from netbox.api.serializers import NetBoxModelSerializer
|
|
4
|
+
from rest_framework import serializers
|
|
5
|
+
|
|
6
|
+
from ..models import CustomAPIEndpoint
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CustomAPIEndpointSerializer(NetBoxModelSerializer):
|
|
10
|
+
"""Serializer for CustomAPIEndpoint."""
|
|
11
|
+
|
|
12
|
+
url = serializers.HyperlinkedIdentityField(
|
|
13
|
+
view_name="plugins-api:netbox_custom_widget-api:customapiendpoint-detail"
|
|
14
|
+
)
|
|
15
|
+
endpoint_url = serializers.CharField(source="url", read_only=True)
|
|
16
|
+
|
|
17
|
+
class Meta:
|
|
18
|
+
model = CustomAPIEndpoint
|
|
19
|
+
fields = [
|
|
20
|
+
"id",
|
|
21
|
+
"url",
|
|
22
|
+
"display",
|
|
23
|
+
"name",
|
|
24
|
+
"endpoint_url",
|
|
25
|
+
"http_method",
|
|
26
|
+
"headers",
|
|
27
|
+
"body",
|
|
28
|
+
"mappings",
|
|
29
|
+
"display_mode",
|
|
30
|
+
"refresh_interval",
|
|
31
|
+
"verify_ssl",
|
|
32
|
+
"timeout",
|
|
33
|
+
"description",
|
|
34
|
+
"comments",
|
|
35
|
+
"tags",
|
|
36
|
+
"custom_fields",
|
|
37
|
+
"created",
|
|
38
|
+
"last_updated",
|
|
39
|
+
]
|
|
40
|
+
brief_fields = ["id", "url", "display", "name"]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""API views for NetBox Custom Widget plugin."""
|
|
2
|
+
|
|
3
|
+
from netbox.api.viewsets import NetBoxModelViewSet
|
|
4
|
+
|
|
5
|
+
from ..filtersets import CustomAPIEndpointFilterSet
|
|
6
|
+
from ..models import CustomAPIEndpoint
|
|
7
|
+
from .serializers import CustomAPIEndpointSerializer
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CustomAPIEndpointViewSet(NetBoxModelViewSet):
|
|
11
|
+
"""API viewset for CustomAPIEndpoint objects."""
|
|
12
|
+
|
|
13
|
+
queryset = CustomAPIEndpoint.objects.prefetch_related("tags")
|
|
14
|
+
serializer_class = CustomAPIEndpointSerializer
|
|
15
|
+
filterset_class = CustomAPIEndpointFilterSet
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Filtersets for NetBox Custom Widget plugin."""
|
|
2
|
+
|
|
3
|
+
import django_filters
|
|
4
|
+
from netbox.filtersets import NetBoxModelFilterSet
|
|
5
|
+
|
|
6
|
+
from .models import CustomAPIEndpoint, DisplayModeChoices, HTTPMethodChoices
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CustomAPIEndpointFilterSet(NetBoxModelFilterSet):
|
|
10
|
+
"""Filterset for CustomAPIEndpoint model."""
|
|
11
|
+
|
|
12
|
+
name = django_filters.CharFilter(lookup_expr="icontains")
|
|
13
|
+
http_method = django_filters.MultipleChoiceFilter(choices=HTTPMethodChoices)
|
|
14
|
+
display_mode = django_filters.MultipleChoiceFilter(choices=DisplayModeChoices)
|
|
15
|
+
|
|
16
|
+
class Meta:
|
|
17
|
+
model = CustomAPIEndpoint
|
|
18
|
+
fields = ["id", "name", "http_method", "display_mode"]
|
|
19
|
+
|
|
20
|
+
def search(self, queryset, name, value):
|
|
21
|
+
"""Search across multiple fields."""
|
|
22
|
+
if not value.strip():
|
|
23
|
+
return queryset
|
|
24
|
+
from django.db.models import Q
|
|
25
|
+
|
|
26
|
+
return queryset.filter(Q(name__icontains=value) | Q(url__icontains=value) | Q(description__icontains=value))
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Forms for NetBox Custom Widget plugin."""
|
|
2
|
+
|
|
3
|
+
from django import forms
|
|
4
|
+
from netbox.forms import NetBoxModelBulkEditForm, NetBoxModelFilterSetForm, NetBoxModelForm, NetBoxModelImportForm
|
|
5
|
+
from utilities.forms.fields import CommentField, TagFilterField
|
|
6
|
+
from utilities.forms.rendering import FieldSet
|
|
7
|
+
|
|
8
|
+
from .models import CustomAPIEndpoint, DisplayModeChoices, HTTPMethodChoices
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CustomAPIEndpointForm(NetBoxModelForm):
|
|
12
|
+
"""Form for creating/editing CustomAPIEndpoint objects."""
|
|
13
|
+
|
|
14
|
+
comments = CommentField()
|
|
15
|
+
|
|
16
|
+
fieldsets = (
|
|
17
|
+
FieldSet("name", "description", name="General"),
|
|
18
|
+
FieldSet("url", "http_method", "headers", "body", "verify_ssl", "timeout", name="API Configuration"),
|
|
19
|
+
FieldSet("mappings", "display_mode", "refresh_interval", name="Display"),
|
|
20
|
+
FieldSet("comments", "tags", name="Details"),
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
class Meta:
|
|
24
|
+
model = CustomAPIEndpoint
|
|
25
|
+
fields = [
|
|
26
|
+
"name",
|
|
27
|
+
"url",
|
|
28
|
+
"http_method",
|
|
29
|
+
"headers",
|
|
30
|
+
"body",
|
|
31
|
+
"mappings",
|
|
32
|
+
"display_mode",
|
|
33
|
+
"refresh_interval",
|
|
34
|
+
"verify_ssl",
|
|
35
|
+
"timeout",
|
|
36
|
+
"description",
|
|
37
|
+
"comments",
|
|
38
|
+
"tags",
|
|
39
|
+
]
|
|
40
|
+
widgets = {
|
|
41
|
+
"body": forms.Textarea(attrs={"rows": 3}),
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class CustomAPIEndpointFilterForm(NetBoxModelFilterSetForm):
|
|
46
|
+
"""Filter form for CustomAPIEndpoint list view."""
|
|
47
|
+
|
|
48
|
+
model = CustomAPIEndpoint
|
|
49
|
+
|
|
50
|
+
name = forms.CharField(required=False)
|
|
51
|
+
http_method = forms.MultipleChoiceField(choices=HTTPMethodChoices, required=False)
|
|
52
|
+
display_mode = forms.MultipleChoiceField(choices=DisplayModeChoices, required=False)
|
|
53
|
+
tag = TagFilterField(model)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class CustomAPIEndpointImportForm(NetBoxModelImportForm):
|
|
57
|
+
"""Import form for CustomAPIEndpoint objects."""
|
|
58
|
+
|
|
59
|
+
class Meta:
|
|
60
|
+
model = CustomAPIEndpoint
|
|
61
|
+
fields = [
|
|
62
|
+
"name",
|
|
63
|
+
"url",
|
|
64
|
+
"http_method",
|
|
65
|
+
"headers",
|
|
66
|
+
"body",
|
|
67
|
+
"mappings",
|
|
68
|
+
"display_mode",
|
|
69
|
+
"refresh_interval",
|
|
70
|
+
"verify_ssl",
|
|
71
|
+
"timeout",
|
|
72
|
+
"description",
|
|
73
|
+
"comments",
|
|
74
|
+
"tags",
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class CustomAPIEndpointBulkEditForm(NetBoxModelBulkEditForm):
|
|
79
|
+
"""Bulk edit form for CustomAPIEndpoint objects."""
|
|
80
|
+
|
|
81
|
+
model = CustomAPIEndpoint
|
|
82
|
+
|
|
83
|
+
http_method = forms.ChoiceField(choices=HTTPMethodChoices, required=False)
|
|
84
|
+
display_mode = forms.ChoiceField(choices=DisplayModeChoices, required=False)
|
|
85
|
+
refresh_interval = forms.IntegerField(required=False)
|
|
86
|
+
verify_ssl = forms.NullBooleanField(required=False)
|
|
87
|
+
timeout = forms.IntegerField(required=False)
|
|
88
|
+
description = forms.CharField(max_length=200, required=False)
|
|
89
|
+
comments = CommentField()
|
|
90
|
+
|
|
91
|
+
nullable_fields = ["description", "comments", "body"]
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import django.db.models.deletion
|
|
2
|
+
import taggit.managers
|
|
3
|
+
import utilities.json
|
|
4
|
+
from django.db import migrations, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
|
|
9
|
+
initial = True
|
|
10
|
+
|
|
11
|
+
dependencies = [
|
|
12
|
+
("extras", "0001_initial"),
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
operations = [
|
|
16
|
+
migrations.CreateModel(
|
|
17
|
+
name="CustomAPIEndpoint",
|
|
18
|
+
fields=[
|
|
19
|
+
(
|
|
20
|
+
"id",
|
|
21
|
+
models.BigAutoField(
|
|
22
|
+
auto_created=True,
|
|
23
|
+
primary_key=True,
|
|
24
|
+
serialize=False,
|
|
25
|
+
),
|
|
26
|
+
),
|
|
27
|
+
("created", models.DateTimeField(auto_now_add=True, null=True)),
|
|
28
|
+
("last_updated", models.DateTimeField(auto_now=True, null=True)),
|
|
29
|
+
(
|
|
30
|
+
"custom_field_data",
|
|
31
|
+
models.JSONField(
|
|
32
|
+
blank=True,
|
|
33
|
+
default=dict,
|
|
34
|
+
encoder=utilities.json.CustomFieldJSONEncoder,
|
|
35
|
+
),
|
|
36
|
+
),
|
|
37
|
+
(
|
|
38
|
+
"name",
|
|
39
|
+
models.CharField(
|
|
40
|
+
help_text="Display name for this endpoint",
|
|
41
|
+
max_length=100,
|
|
42
|
+
unique=True,
|
|
43
|
+
),
|
|
44
|
+
),
|
|
45
|
+
(
|
|
46
|
+
"url",
|
|
47
|
+
models.CharField(
|
|
48
|
+
help_text="API endpoint URL",
|
|
49
|
+
max_length=500,
|
|
50
|
+
),
|
|
51
|
+
),
|
|
52
|
+
(
|
|
53
|
+
"http_method",
|
|
54
|
+
models.CharField(
|
|
55
|
+
default="GET",
|
|
56
|
+
max_length=10,
|
|
57
|
+
),
|
|
58
|
+
),
|
|
59
|
+
(
|
|
60
|
+
"headers",
|
|
61
|
+
models.JSONField(
|
|
62
|
+
blank=True,
|
|
63
|
+
default=dict,
|
|
64
|
+
help_text="Custom HTTP headers as JSON (e.g., authorization)",
|
|
65
|
+
),
|
|
66
|
+
),
|
|
67
|
+
(
|
|
68
|
+
"body",
|
|
69
|
+
models.TextField(
|
|
70
|
+
blank=True,
|
|
71
|
+
help_text="Request body for POST requests",
|
|
72
|
+
),
|
|
73
|
+
),
|
|
74
|
+
(
|
|
75
|
+
"mappings",
|
|
76
|
+
models.JSONField(
|
|
77
|
+
blank=True,
|
|
78
|
+
default=list,
|
|
79
|
+
help_text="Field mapping configuration as JSON array",
|
|
80
|
+
),
|
|
81
|
+
),
|
|
82
|
+
(
|
|
83
|
+
"display_mode",
|
|
84
|
+
models.CharField(
|
|
85
|
+
default="list",
|
|
86
|
+
max_length=10,
|
|
87
|
+
),
|
|
88
|
+
),
|
|
89
|
+
(
|
|
90
|
+
"refresh_interval",
|
|
91
|
+
models.PositiveIntegerField(
|
|
92
|
+
default=30,
|
|
93
|
+
help_text="Auto-refresh interval in seconds (0 to disable)",
|
|
94
|
+
),
|
|
95
|
+
),
|
|
96
|
+
(
|
|
97
|
+
"verify_ssl",
|
|
98
|
+
models.BooleanField(
|
|
99
|
+
default=True,
|
|
100
|
+
help_text="Verify SSL certificates",
|
|
101
|
+
),
|
|
102
|
+
),
|
|
103
|
+
(
|
|
104
|
+
"timeout",
|
|
105
|
+
models.PositiveIntegerField(
|
|
106
|
+
default=30,
|
|
107
|
+
help_text="Request timeout in seconds",
|
|
108
|
+
),
|
|
109
|
+
),
|
|
110
|
+
(
|
|
111
|
+
"description",
|
|
112
|
+
models.TextField(blank=True),
|
|
113
|
+
),
|
|
114
|
+
(
|
|
115
|
+
"comments",
|
|
116
|
+
models.TextField(blank=True),
|
|
117
|
+
),
|
|
118
|
+
(
|
|
119
|
+
"tags",
|
|
120
|
+
taggit.managers.TaggableManager(
|
|
121
|
+
through="extras.TaggedItem",
|
|
122
|
+
to="extras.Tag",
|
|
123
|
+
),
|
|
124
|
+
),
|
|
125
|
+
],
|
|
126
|
+
options={
|
|
127
|
+
"verbose_name": "API Endpoint",
|
|
128
|
+
"verbose_name_plural": "API Endpoints",
|
|
129
|
+
"ordering": ["name"],
|
|
130
|
+
},
|
|
131
|
+
),
|
|
132
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""Models for NetBox Custom Widget plugin."""
|
|
2
|
+
|
|
3
|
+
from django.db import models
|
|
4
|
+
from django.urls import reverse
|
|
5
|
+
from netbox.models import NetBoxModel
|
|
6
|
+
from utilities.choices import ChoiceSet
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class HTTPMethodChoices(ChoiceSet):
|
|
10
|
+
"""HTTP method choices."""
|
|
11
|
+
|
|
12
|
+
key = "CustomAPIEndpoint.http_method"
|
|
13
|
+
|
|
14
|
+
METHOD_GET = "GET"
|
|
15
|
+
METHOD_POST = "POST"
|
|
16
|
+
|
|
17
|
+
CHOICES = [
|
|
18
|
+
(METHOD_GET, "GET", "blue"),
|
|
19
|
+
(METHOD_POST, "POST", "green"),
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class DisplayModeChoices(ChoiceSet):
|
|
24
|
+
"""Display mode choices for widget rendering."""
|
|
25
|
+
|
|
26
|
+
key = "CustomAPIEndpoint.display_mode"
|
|
27
|
+
|
|
28
|
+
MODE_LIST = "list"
|
|
29
|
+
MODE_BLOCK = "block"
|
|
30
|
+
|
|
31
|
+
CHOICES = [
|
|
32
|
+
(MODE_LIST, "List", "blue"),
|
|
33
|
+
(MODE_BLOCK, "Block", "gray"),
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class CustomAPIEndpoint(NetBoxModel):
|
|
38
|
+
"""
|
|
39
|
+
Stores configuration for an external API endpoint.
|
|
40
|
+
|
|
41
|
+
Each endpoint defines a URL, authentication, field mappings,
|
|
42
|
+
and display settings for use in dashboard widgets.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
name = models.CharField(
|
|
46
|
+
max_length=100,
|
|
47
|
+
unique=True,
|
|
48
|
+
help_text="Display name for this endpoint",
|
|
49
|
+
)
|
|
50
|
+
url = models.CharField(
|
|
51
|
+
max_length=500,
|
|
52
|
+
help_text="API endpoint URL",
|
|
53
|
+
)
|
|
54
|
+
http_method = models.CharField(
|
|
55
|
+
max_length=10,
|
|
56
|
+
choices=HTTPMethodChoices,
|
|
57
|
+
default=HTTPMethodChoices.METHOD_GET,
|
|
58
|
+
)
|
|
59
|
+
headers = models.JSONField(
|
|
60
|
+
default=dict,
|
|
61
|
+
blank=True,
|
|
62
|
+
help_text="Custom HTTP headers as JSON (e.g., authorization)",
|
|
63
|
+
)
|
|
64
|
+
body = models.TextField(
|
|
65
|
+
blank=True,
|
|
66
|
+
help_text="Request body for POST requests",
|
|
67
|
+
)
|
|
68
|
+
mappings = models.JSONField(
|
|
69
|
+
default=list,
|
|
70
|
+
blank=True,
|
|
71
|
+
help_text="Field mapping configuration as JSON array",
|
|
72
|
+
)
|
|
73
|
+
display_mode = models.CharField(
|
|
74
|
+
max_length=10,
|
|
75
|
+
choices=DisplayModeChoices,
|
|
76
|
+
default=DisplayModeChoices.MODE_LIST,
|
|
77
|
+
)
|
|
78
|
+
refresh_interval = models.PositiveIntegerField(
|
|
79
|
+
default=30,
|
|
80
|
+
help_text="Auto-refresh interval in seconds (0 to disable)",
|
|
81
|
+
)
|
|
82
|
+
verify_ssl = models.BooleanField(
|
|
83
|
+
default=True,
|
|
84
|
+
help_text="Verify SSL certificates",
|
|
85
|
+
)
|
|
86
|
+
timeout = models.PositiveIntegerField(
|
|
87
|
+
default=30,
|
|
88
|
+
help_text="Request timeout in seconds",
|
|
89
|
+
)
|
|
90
|
+
description = models.TextField(
|
|
91
|
+
blank=True,
|
|
92
|
+
)
|
|
93
|
+
comments = models.TextField(
|
|
94
|
+
blank=True,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
class Meta:
|
|
98
|
+
ordering = ["name"]
|
|
99
|
+
verbose_name = "API Endpoint"
|
|
100
|
+
verbose_name_plural = "API Endpoints"
|
|
101
|
+
|
|
102
|
+
def __str__(self):
|
|
103
|
+
return self.name
|
|
104
|
+
|
|
105
|
+
def get_absolute_url(self):
|
|
106
|
+
return reverse("plugins:netbox_custom_widget:customapiendpoint", args=[self.pk])
|