slurpit-netbox 0.8.36__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.
- slurpit_netbox-0.8.36/PKG-INFO +64 -0
- slurpit_netbox-0.8.36/README.md +48 -0
- slurpit_netbox-0.8.36/pyproject.toml +23 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/__init__.py +39 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/api/__init__.py +1 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/api/serializers.py +55 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/api/urls.py +11 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/api/views.py +172 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/decorators.py +57 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/filtersets.py +79 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/forms.py +182 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/importer.py +285 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/management/__init__.py +0 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/management/choices.py +49 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/migrations/0001_initial.py +135 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/migrations/0002_alter_slurpitplanning_name_and_more.py +23 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/migrations/0003_alter_slurpitimporteddevice_ipv4_and_more.py +23 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/migrations/0004_slurpitmapping.py +31 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/migrations/__init__.py +0 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/models/__init__.py +122 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/models/device.py +69 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/models/logs.py +41 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/models/mapping.py +9 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/models/planning.py +22 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/models/setting.py +38 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/navigation.py +49 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/search.py +11 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/slurpitch.py +15 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/tables.py +136 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/template_content.py +10 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/templates/slurpit_netbox/bulk_edit.html +129 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/templates/slurpit_netbox/comingsoon.html +49 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/templates/slurpit_netbox/data_mapping.html +86 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/templates/slurpit_netbox/logo.html +33 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/templates/slurpit_netbox/mapping/netbox_to_slurpit.html +217 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/templates/slurpit_netbox/onboard/conflict_device_list.html +75 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/templates/slurpit_netbox/onboard/migrate_device_list.html +75 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/templates/slurpit_netbox/onboard/new_device_list.html +74 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/templates/slurpit_netbox/onboard/onboarded_device_list.html +38 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/templates/slurpit_netbox/onboard_device.html +97 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/templates/slurpit_netbox/planning_table.html +120 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/templates/slurpit_netbox/settings/data_tabs.html +81 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/templates/slurpit_netbox/settings/general.html +216 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/templates/slurpit_netbox/settings.html +70 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/templates/slurpit_netbox/slurpitlog_list.html +54 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/urls.py +26 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/utilities.py +17 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/validator.py +30 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/views/__init__.py +5 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/views/datamapping.py +216 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/views/logging.py +14 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/views/onboarding.py +294 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/views/reconcile.py +13 -0
- slurpit_netbox-0.8.36/src/slurpit_netbox/views/setting.py +434 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: slurpit_netbox
|
|
3
|
+
Version: 0.8.36
|
|
4
|
+
Summary:
|
|
5
|
+
Author: Pieter
|
|
6
|
+
Author-email: pieter@slurpit.io
|
|
7
|
+
Requires-Python: >=3.10,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Requires-Dist: djangorestframework-bulk (>=0.2.1,<0.3.0)
|
|
13
|
+
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
## Compatibility
|
|
17
|
+
|
|
18
|
+
| NetBox Version | Plugin Version |
|
|
19
|
+
|----------------|----------------|
|
|
20
|
+
| NetBox 3.7 | >= 0.8.0 |
|
|
21
|
+
|
|
22
|
+
## How to install the plugin on your local NetBox server
|
|
23
|
+
|
|
24
|
+
### Activate the Virtual Environment
|
|
25
|
+
|
|
26
|
+
Firstly, to make the plugin available within NetBox, we must activate the Python virtual environment that was set up during the NetBox installation; you can do this by finding the path of the virtual environment, which typically is /opt/netbox/venv/ if you followed the default installation guide.
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
$ source /opt/netbox/venv/bin/activate
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Run setup.py
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
$ python setup.py develop
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Configure NetBox
|
|
39
|
+
|
|
40
|
+
To enable our new plugin, we have to configure NetBox by opening the configuration.py file located in the netbox/netbox directory and modifying the PLUGINS parameter.
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
# configuration.py
|
|
44
|
+
PLUGINS = [
|
|
45
|
+
'slurpit_netbox',
|
|
46
|
+
]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
### Apply Migrations
|
|
52
|
+
|
|
53
|
+
Use the 'migrate' management command to apply the migration file.
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
python manage.py migrate slurpit_netbox
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
After saving the file, start the NetBox development server if it isn't already running.
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
$ python manage.py runserver 0.0.0.0:8000 --insecure
|
|
63
|
+
```
|
|
64
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
## Compatibility
|
|
2
|
+
|
|
3
|
+
| NetBox Version | Plugin Version |
|
|
4
|
+
|----------------|----------------|
|
|
5
|
+
| NetBox 3.7 | >= 0.8.0 |
|
|
6
|
+
|
|
7
|
+
## How to install the plugin on your local NetBox server
|
|
8
|
+
|
|
9
|
+
### Activate the Virtual Environment
|
|
10
|
+
|
|
11
|
+
Firstly, to make the plugin available within NetBox, we must activate the Python virtual environment that was set up during the NetBox installation; you can do this by finding the path of the virtual environment, which typically is /opt/netbox/venv/ if you followed the default installation guide.
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
$ source /opt/netbox/venv/bin/activate
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Run setup.py
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
$ python setup.py develop
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Configure NetBox
|
|
24
|
+
|
|
25
|
+
To enable our new plugin, we have to configure NetBox by opening the configuration.py file located in the netbox/netbox directory and modifying the PLUGINS parameter.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
# configuration.py
|
|
29
|
+
PLUGINS = [
|
|
30
|
+
'slurpit_netbox',
|
|
31
|
+
]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Apply Migrations
|
|
37
|
+
|
|
38
|
+
Use the 'migrate' management command to apply the migration file.
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
python manage.py migrate slurpit_netbox
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
After saving the file, start the NetBox development server if it isn't already running.
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
$ python manage.py runserver 0.0.0.0:8000 --insecure
|
|
48
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "slurpit_netbox"
|
|
3
|
+
version = "0.8.36"
|
|
4
|
+
description = ""
|
|
5
|
+
authors = ["Pieter <pieter@slurpit.io>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
packages = [{include = "slurpit_netbox", from = "src"}]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
[tool.poetry.dependencies]
|
|
11
|
+
python = "^3.10"
|
|
12
|
+
requests = "^2.31.0"
|
|
13
|
+
djangorestframework-bulk = "^0.2.1"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
[tool.poetry.group.develop.dependencies]
|
|
17
|
+
django = "^4.2.6"
|
|
18
|
+
djangorestframework = "^3.14.0"
|
|
19
|
+
django-tables2 = "^2.6.0"
|
|
20
|
+
|
|
21
|
+
[build-system]
|
|
22
|
+
requires = ["poetry-core"]
|
|
23
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from django.apps import apps
|
|
4
|
+
from django.db.models.signals import post_migrate
|
|
5
|
+
from extras.plugins import PluginConfig, get_plugin_config
|
|
6
|
+
|
|
7
|
+
class SlurpitConfig(PluginConfig):
|
|
8
|
+
name = "slurpit_netbox"
|
|
9
|
+
verbose_name = "Slurp'it Plugin"
|
|
10
|
+
description = "Sync Slurp'it into NetBox"
|
|
11
|
+
version = '0.8.36'
|
|
12
|
+
base_url = "slurpit"
|
|
13
|
+
default_settings = {
|
|
14
|
+
'DeviceType': {'model': "Slurp'it", 'slug': 'slurpit'},
|
|
15
|
+
'DeviceRole': {'name': "Slurp'it", 'slug': 'slurpit'},
|
|
16
|
+
'Site': {'name': "Slurp'it", 'slug': 'slurpit'},
|
|
17
|
+
'Location': {'name': 'Slurp\'it', 'slug': 'slurpit'},
|
|
18
|
+
'Region': {'name': 'Slurp\'it', 'slug': 'slurpit'},
|
|
19
|
+
'SiteGroup': {'name': 'Slurp\'it', 'slug': 'slurpit'},
|
|
20
|
+
'Rack': {'name': 'Slurp\'it'},
|
|
21
|
+
'ConfigTemplate': {'name': 'Slurp\'it'},
|
|
22
|
+
'Manufacturer': {'name': 'OEM', 'slug': 'oem'},
|
|
23
|
+
'unattended_import': False,
|
|
24
|
+
'version': version
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
def ready(self):
|
|
28
|
+
from .models import post_migration
|
|
29
|
+
deps_app = apps.get_app_config("virtualization")
|
|
30
|
+
post_migrate.connect(post_migration, sender=deps_app, weak=False)
|
|
31
|
+
super().ready()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
config = SlurpitConfig
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_config(cfg):
|
|
38
|
+
return get_plugin_config(get_config.__module__, cfg)
|
|
39
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .serializers import * # noqa: F401, F403
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from core.choices import DataSourceStatusChoices
|
|
2
|
+
from netbox.api.fields import ChoiceField
|
|
3
|
+
from netbox.api.serializers import NetBoxModelSerializer
|
|
4
|
+
from rest_framework import serializers
|
|
5
|
+
|
|
6
|
+
from slurpit_netbox.models import SlurpitPlanning, SlurpitImportedDevice, SlurpitStagedDevice, SlurpitLog, SlurpitSetting, SlurpitSnapshot, SlurpitMapping
|
|
7
|
+
|
|
8
|
+
__all__ = (
|
|
9
|
+
'SlurpitPlanningSerializer',
|
|
10
|
+
'SlurpitStagedDeviceSerializer',
|
|
11
|
+
'SlurpitImportedDeviceSerializer',
|
|
12
|
+
'SlurpitLogSerializer',
|
|
13
|
+
'SlurpitSettingSerializer',
|
|
14
|
+
'SlurpitSnapshotSerializer'
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
class SlurpitPlanningSerializer(NetBoxModelSerializer):
|
|
18
|
+
id = serializers.IntegerField(source='planning_id')
|
|
19
|
+
comment = serializers.CharField(source='comments')
|
|
20
|
+
|
|
21
|
+
class Meta:
|
|
22
|
+
model = SlurpitPlanning
|
|
23
|
+
fields = ['id', "name", "comment", "display"]
|
|
24
|
+
|
|
25
|
+
class SlurpitStagedDeviceSerializer(serializers.ModelSerializer):
|
|
26
|
+
class Meta:
|
|
27
|
+
model = SlurpitStagedDevice
|
|
28
|
+
fields = '__all__'
|
|
29
|
+
|
|
30
|
+
class SlurpitSnapshotSerializer(serializers.ModelSerializer):
|
|
31
|
+
class Meta:
|
|
32
|
+
model = SlurpitSnapshot
|
|
33
|
+
fields = '__all__'
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class SlurpitImportedDeviceSerializer(serializers.ModelSerializer):
|
|
37
|
+
id = serializers.IntegerField(source='slurpit_id')
|
|
38
|
+
class Meta:
|
|
39
|
+
model = SlurpitImportedDevice
|
|
40
|
+
fields = ['id', 'disabled', 'hostname', 'fqdn', 'ipv4', 'device_os', 'device_type', 'brand', 'createddate', 'changeddate']
|
|
41
|
+
|
|
42
|
+
class SlurpitLogSerializer(NetBoxModelSerializer):
|
|
43
|
+
class Meta:
|
|
44
|
+
model = SlurpitLog
|
|
45
|
+
fields = '__all__'
|
|
46
|
+
|
|
47
|
+
class SlurpitSettingSerializer(NetBoxModelSerializer):
|
|
48
|
+
class Meta:
|
|
49
|
+
model = SlurpitSetting
|
|
50
|
+
fields = '__all__'
|
|
51
|
+
|
|
52
|
+
class SlurpitMappingSerializer(NetBoxModelSerializer):
|
|
53
|
+
class Meta:
|
|
54
|
+
model = SlurpitMapping
|
|
55
|
+
fields = '__all__'
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from netbox.api.routers import NetBoxRouter
|
|
2
|
+
from . import views
|
|
3
|
+
|
|
4
|
+
router = NetBoxRouter()
|
|
5
|
+
router.APIRootView = views.SlurpitRootView
|
|
6
|
+
router.register("planning", views.SlurpitPlanningViewSet)
|
|
7
|
+
router.register("planning-data", views.SlurpitSnapshotViewSet)
|
|
8
|
+
router.register("device", views.DeviceViewSet)
|
|
9
|
+
router.register('test', views.SlurpitTestAPIView)
|
|
10
|
+
app_name = 'slurpit-api'
|
|
11
|
+
urlpatterns = router.urls
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
|
|
2
|
+
from netbox.api.viewsets import NetBoxModelViewSet
|
|
3
|
+
from dcim.models import Device
|
|
4
|
+
from dcim.choices import DeviceStatusChoices
|
|
5
|
+
from slurpit_netbox.models import SlurpitPlanning, SlurpitSnapshot, SlurpitImportedDevice, SlurpitStagedDevice, SlurpitLog
|
|
6
|
+
from slurpit_netbox.filtersets import SlurpitPlanningFilterSet, SlurpitSnapshotFilterSet, SlurpitImportedDeviceFilterSet
|
|
7
|
+
from .serializers import SlurpitPlanningSerializer, SlurpitSnapshotSerializer, SlurpitImportedDeviceSerializer
|
|
8
|
+
from rest_framework.routers import APIRootView
|
|
9
|
+
from rest_framework_bulk import BulkCreateModelMixin, BulkDestroyModelMixin
|
|
10
|
+
from rest_framework.decorators import action
|
|
11
|
+
from rest_framework.response import Response
|
|
12
|
+
from rest_framework.permissions import IsAuthenticated
|
|
13
|
+
from rest_framework.views import APIView
|
|
14
|
+
from rest_framework import status
|
|
15
|
+
from django.db import transaction
|
|
16
|
+
from django.http import JsonResponse
|
|
17
|
+
import json
|
|
18
|
+
from ..validator import device_validator
|
|
19
|
+
from ..importer import process_import, import_devices, import_plannings
|
|
20
|
+
from ..management.choices import *
|
|
21
|
+
|
|
22
|
+
__all__ = (
|
|
23
|
+
'SlurpitPlanningViewSet',
|
|
24
|
+
'SlurpitRootView',
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
class SlurpitRootView(APIRootView):
|
|
28
|
+
"""
|
|
29
|
+
Slurpit API root view
|
|
30
|
+
"""
|
|
31
|
+
def get_view_name(self):
|
|
32
|
+
return 'Slurpit'
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class SlurpitPlanningViewSet(
|
|
36
|
+
NetBoxModelViewSet
|
|
37
|
+
):
|
|
38
|
+
queryset = SlurpitPlanning.objects.all()
|
|
39
|
+
serializer_class = SlurpitPlanningSerializer
|
|
40
|
+
filterset_class = SlurpitPlanningFilterSet
|
|
41
|
+
|
|
42
|
+
@action(detail=False, methods=['delete'], url_path='delete-all')
|
|
43
|
+
def delete_all(self, request, *args, **kwargs):
|
|
44
|
+
"""
|
|
45
|
+
A custom action to delete all SlurpitPlanning objects.
|
|
46
|
+
Be careful with this operation: it cannot be undone!
|
|
47
|
+
"""
|
|
48
|
+
self.queryset.delete()
|
|
49
|
+
SlurpitSnapshot.objects.all().delete()
|
|
50
|
+
SlurpitLog.info(category=LogCategoryChoices.PLANNING, message=f"Api deleted all snapshots and plannings")
|
|
51
|
+
return Response(status=status.HTTP_204_NO_CONTENT)
|
|
52
|
+
|
|
53
|
+
def get_queryset(self):
|
|
54
|
+
if self.request.method == 'GET':
|
|
55
|
+
# Customize this queryset to suit your requirements for GET requests
|
|
56
|
+
return SlurpitPlanning.objects.filter(selected=True)
|
|
57
|
+
# For other methods, use the default queryset
|
|
58
|
+
return self.queryset
|
|
59
|
+
|
|
60
|
+
@action(detail=False, methods=['delete'], url_path='delete/(?P<planning_id>[^/.]+)')
|
|
61
|
+
def delete(self, request, *args, **kwargs):
|
|
62
|
+
planning_id = kwargs.get('planning_id')
|
|
63
|
+
planning = SlurpitPlanning.objects.filter(planning_id=planning_id).first()
|
|
64
|
+
if not planning:
|
|
65
|
+
return Response(f"Unknown planning id {planning_id}", status=status.HTTP_400_BAD_REQUEST)
|
|
66
|
+
|
|
67
|
+
planning.delete()
|
|
68
|
+
count = SlurpitSnapshot.objects.filter(planning_id=planning_id).delete()[0]
|
|
69
|
+
SlurpitLog.info(category=LogCategoryChoices.PLANNING, message=f"Api deleted all {count} snapshots and planning {planning.name}")
|
|
70
|
+
return Response(status=status.HTTP_204_NO_CONTENT)
|
|
71
|
+
|
|
72
|
+
@action(detail=False, methods=['post'], url_path='sync')
|
|
73
|
+
def sync(self, request):
|
|
74
|
+
if not isinstance(request.data, list):
|
|
75
|
+
return Response("Should be a list", status=status.HTTP_400_BAD_REQUEST)
|
|
76
|
+
import_plannings(request.data)
|
|
77
|
+
return JsonResponse({'status': 'success'})
|
|
78
|
+
|
|
79
|
+
def create(self, request):
|
|
80
|
+
if not isinstance(request.data, list):
|
|
81
|
+
return Response("Should be a list", status=status.HTTP_400_BAD_REQUEST)
|
|
82
|
+
|
|
83
|
+
import_plannings(request.data, False)
|
|
84
|
+
return JsonResponse({'status': 'success'})
|
|
85
|
+
|
|
86
|
+
class SlurpitSnapshotViewSet(
|
|
87
|
+
BulkCreateModelMixin,
|
|
88
|
+
BulkDestroyModelMixin,
|
|
89
|
+
NetBoxModelViewSet
|
|
90
|
+
):
|
|
91
|
+
queryset = SlurpitSnapshot.objects.all()
|
|
92
|
+
serializer_class = SlurpitSnapshotSerializer
|
|
93
|
+
filterset_class = SlurpitSnapshotFilterSet
|
|
94
|
+
|
|
95
|
+
@action(detail=False, methods=['delete'], url_path='delete-all/(?P<hostname>[^/.]+)/(?P<planning_id>[^/.]+)')
|
|
96
|
+
def delete_all(self, request, *args, **kwargs):
|
|
97
|
+
planning_id = kwargs.get('planning_id')
|
|
98
|
+
planning = SlurpitPlanning.objects.filter(planning_id=planning_id).first()
|
|
99
|
+
if not planning:
|
|
100
|
+
return Response(f"Unknown planning id {planning_id}", status=status.HTTP_400_BAD_REQUEST)
|
|
101
|
+
|
|
102
|
+
hostname = kwargs.get('hostname')
|
|
103
|
+
if not hostname:
|
|
104
|
+
return Response(f"No hostname was given", status=status.HTTP_400_BAD_REQUEST)
|
|
105
|
+
|
|
106
|
+
count = SlurpitSnapshot.objects.filter(hostname=hostname, planning_id=planning_id).delete()[0]
|
|
107
|
+
|
|
108
|
+
SlurpitLog.info(category=LogCategoryChoices.PLANNING, message=f"Api deleted all {count} snapshots for planning {planning.name} and hostname {hostname}")
|
|
109
|
+
|
|
110
|
+
return Response(status=status.HTTP_204_NO_CONTENT)
|
|
111
|
+
|
|
112
|
+
@action(detail=False, methods=['delete'], url_path='clear/(?P<planning_id>[^/.]+)')
|
|
113
|
+
def clear(self, request, *args, **kwargs):
|
|
114
|
+
planning_id = kwargs.get('planning_id')
|
|
115
|
+
planning = SlurpitPlanning.objects.filter(planning_id=planning_id).first()
|
|
116
|
+
if not planning:
|
|
117
|
+
return Response(f"Unknown planning id {planning_id}", status=status.HTTP_400_BAD_REQUEST)
|
|
118
|
+
count = SlurpitSnapshot.objects.filter(planning_id=planning_id).delete()[0]
|
|
119
|
+
SlurpitLog.info(category=LogCategoryChoices.PLANNING, message=f"Api deleted all {count} snapshots for planning {planning.name}")
|
|
120
|
+
|
|
121
|
+
return Response(status=status.HTTP_204_NO_CONTENT)
|
|
122
|
+
|
|
123
|
+
class DeviceViewSet(
|
|
124
|
+
BulkCreateModelMixin,
|
|
125
|
+
BulkDestroyModelMixin,
|
|
126
|
+
NetBoxModelViewSet
|
|
127
|
+
):
|
|
128
|
+
queryset = SlurpitImportedDevice.objects.all()
|
|
129
|
+
serializer_class = SlurpitImportedDeviceSerializer
|
|
130
|
+
filterset_class = SlurpitImportedDeviceFilterSet
|
|
131
|
+
|
|
132
|
+
@action(detail=False, methods=['delete'], url_path='delete-all')
|
|
133
|
+
def delete_all(self, request, *args, **kwargs):
|
|
134
|
+
with transaction.atomic():
|
|
135
|
+
Device.objects.select_related('slurpitimporteddevice').update(status=DeviceStatusChoices.STATUS_OFFLINE)
|
|
136
|
+
SlurpitStagedDevice.objects.all().delete()
|
|
137
|
+
SlurpitImportedDevice.objects.filter(mapped_device__isnull=True).delete()
|
|
138
|
+
|
|
139
|
+
return Response(status=status.HTTP_204_NO_CONTENT)
|
|
140
|
+
|
|
141
|
+
@action(detail=False, methods=['delete'], url_path='delete/(?P<hostname>[^/.]+)')
|
|
142
|
+
def delete(self, request, *args, **kwargs):
|
|
143
|
+
hostname_to_delete = kwargs.get('hostname')
|
|
144
|
+
with transaction.atomic():
|
|
145
|
+
to_delete = SlurpitImportedDevice.objects.filter(hostname=hostname_to_delete)
|
|
146
|
+
Device.objects.filter(slurpitimporteddevice__in=to_delete).update(status=DeviceStatusChoices.STATUS_OFFLINE)
|
|
147
|
+
to_delete.filter(mapped_device__isnull=True).delete()
|
|
148
|
+
SlurpitStagedDevice.objects.filter(hostname=hostname_to_delete).delete()
|
|
149
|
+
|
|
150
|
+
return Response(status=status.HTTP_204_NO_CONTENT)
|
|
151
|
+
|
|
152
|
+
def create(self, request):
|
|
153
|
+
errors = device_validator(request.data)
|
|
154
|
+
|
|
155
|
+
if errors:
|
|
156
|
+
return JsonResponse({'status': 'error', 'errors': errors}, status=400)
|
|
157
|
+
|
|
158
|
+
import_devices(request.data)
|
|
159
|
+
process_import(delete=False)
|
|
160
|
+
|
|
161
|
+
return JsonResponse({'status': 'success'})
|
|
162
|
+
|
|
163
|
+
class SlurpitTestAPIView(NetBoxModelViewSet):
|
|
164
|
+
queryset = SlurpitImportedDevice.objects.all()
|
|
165
|
+
serializer_class = SlurpitImportedDeviceSerializer
|
|
166
|
+
filterset_class = SlurpitImportedDeviceFilterSet
|
|
167
|
+
|
|
168
|
+
permission_classes = [IsAuthenticated]
|
|
169
|
+
|
|
170
|
+
@action(detail=False, methods=['get'], url_path='api')
|
|
171
|
+
def api(self, request, *args, **kwargs):
|
|
172
|
+
return JsonResponse({'status': 'success'})
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
from .models import SlurpitSetting
|
|
3
|
+
from django.core.exceptions import ObjectDoesNotExist
|
|
4
|
+
from django.contrib import messages
|
|
5
|
+
from account.models import UserToken
|
|
6
|
+
|
|
7
|
+
def slurpit_plugin_registered(view_func):
|
|
8
|
+
@wraps(view_func)
|
|
9
|
+
def _wrapped_view(request, *args, **kwargs):
|
|
10
|
+
|
|
11
|
+
paths = [
|
|
12
|
+
'plugins/slurpit/settings/',
|
|
13
|
+
'plugins/slurpit/devices/',
|
|
14
|
+
'plugins/slurpit/data_mapping/',
|
|
15
|
+
'plugins/slurpit/reconcile/',
|
|
16
|
+
'plugins/slurpit/slurpitlog/'
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
for path in paths:
|
|
20
|
+
if path in request.path and request.method == 'GET':
|
|
21
|
+
# Ignore test case
|
|
22
|
+
test_param = request.GET.get('test',None)
|
|
23
|
+
if test_param == 'test':
|
|
24
|
+
continue
|
|
25
|
+
|
|
26
|
+
appliance_type_param = request.GET.get('appliance_type', None)
|
|
27
|
+
if appliance_type_param:
|
|
28
|
+
continue
|
|
29
|
+
|
|
30
|
+
reset_param = request.GET.get('reset', None)
|
|
31
|
+
if reset_param:
|
|
32
|
+
continue
|
|
33
|
+
|
|
34
|
+
tokens = UserToken.objects.filter(user=request.user).count()
|
|
35
|
+
|
|
36
|
+
if tokens == 0:
|
|
37
|
+
messages.warning(request, "To use the Slurp'it plugin, it is necessary to first generate a Plugin API Key on the Setting Page.")
|
|
38
|
+
return view_func(request, *args, **kwargs)
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
setting = SlurpitSetting.objects.get()
|
|
42
|
+
server_url = setting.server_url
|
|
43
|
+
api_key = setting.api_key
|
|
44
|
+
appliance_type = setting.appliance_type
|
|
45
|
+
|
|
46
|
+
if appliance_type == '':
|
|
47
|
+
messages.warning(request, "To use the Slurp'it plugin, it is necessary to first select the Appliance Type on the Setting Page.")
|
|
48
|
+
elif appliance_type != 'cloud' and (api_key == '' or server_url == ''):
|
|
49
|
+
messages.warning(request, "To use the Slurp'it plugin, you should first configure the server settings. Go to settings and configure the Slurp'it server in the parameter section.")
|
|
50
|
+
except Exception as e:
|
|
51
|
+
setting = None
|
|
52
|
+
messages.warning(request, "To use the Slurp'it plugin, it is necessary to first select the Appliance Type on the Setting Page.")
|
|
53
|
+
|
|
54
|
+
return view_func(request, *args, **kwargs)
|
|
55
|
+
|
|
56
|
+
return view_func(request, *args, **kwargs)
|
|
57
|
+
return _wrapped_view
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import django_filters
|
|
2
|
+
from core.choices import DataSourceStatusChoices
|
|
3
|
+
from django.db.models import Q
|
|
4
|
+
from netbox.filtersets import NetBoxModelFilterSet, BaseFilterSet
|
|
5
|
+
from .models import SlurpitLog, SlurpitPlanning, SlurpitSnapshot, SlurpitImportedDevice
|
|
6
|
+
from django.utils.translation import gettext as _
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class LoggingFilterSet(BaseFilterSet):
|
|
10
|
+
q = django_filters.CharFilter(
|
|
11
|
+
method='search',
|
|
12
|
+
label=_('Search'),
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
class Meta:
|
|
16
|
+
model = SlurpitLog
|
|
17
|
+
fields = [
|
|
18
|
+
'log_time', 'level', 'category', 'message'
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
def search(self, queryset, name, value):
|
|
22
|
+
if not value.strip():
|
|
23
|
+
return queryset
|
|
24
|
+
return queryset.filter(
|
|
25
|
+
Q(level__icontains=value) |
|
|
26
|
+
Q(category__icontains=value) |
|
|
27
|
+
Q(message__icontains=value)
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
class SlurpitPlanningFilterSet(BaseFilterSet):
|
|
31
|
+
q = django_filters.CharFilter(
|
|
32
|
+
method='search',
|
|
33
|
+
label=_('Search'),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
class Meta:
|
|
37
|
+
model = SlurpitPlanning
|
|
38
|
+
fields = ["id", "name", "planning_id"]
|
|
39
|
+
|
|
40
|
+
def search(self, queryset, name, value):
|
|
41
|
+
if not value.strip():
|
|
42
|
+
return queryset
|
|
43
|
+
return queryset.filter(
|
|
44
|
+
Q(name__icontains=value)
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
class SlurpitSnapshotFilterSet(BaseFilterSet):
|
|
48
|
+
q = django_filters.CharFilter(
|
|
49
|
+
method='search',
|
|
50
|
+
label=_('Search'),
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
class Meta:
|
|
54
|
+
model = SlurpitSnapshot
|
|
55
|
+
fields = ["id", "hostname", "planning_id"]
|
|
56
|
+
|
|
57
|
+
def search(self, queryset, name, value):
|
|
58
|
+
if not value.strip():
|
|
59
|
+
return queryset
|
|
60
|
+
return queryset.filter(
|
|
61
|
+
Q(name__icontains=value)
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
class SlurpitImportedDeviceFilterSet(BaseFilterSet):
|
|
65
|
+
q = django_filters.CharFilter(
|
|
66
|
+
method='search',
|
|
67
|
+
label=_('Search'),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
class Meta:
|
|
71
|
+
model = SlurpitImportedDevice
|
|
72
|
+
fields = ["id", "hostname"]
|
|
73
|
+
|
|
74
|
+
def search(self, queryset, name, value):
|
|
75
|
+
if not value.strip():
|
|
76
|
+
return queryset
|
|
77
|
+
return queryset.filter(
|
|
78
|
+
Q(name__icontains=value)
|
|
79
|
+
)
|