netbox-toolkit-plugin 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl
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_toolkit_plugin/__init__.py +32 -0
- {netbox_toolkit → netbox_toolkit_plugin}/api/serializers.py +71 -35
- {netbox_toolkit → netbox_toolkit_plugin}/api/urls.py +3 -3
- {netbox_toolkit → netbox_toolkit_plugin}/connectors/factory.py +170 -111
- {netbox_toolkit → netbox_toolkit_plugin}/connectors/netmiko_connector.py +242 -179
- {netbox_toolkit → netbox_toolkit_plugin}/connectors/scrapli_connector.py +256 -172
- netbox_toolkit_plugin/migrations/0001_initial.py +108 -0
- netbox_toolkit_plugin/migrations/0002_alter_command_options_alter_command_unique_together_and_more.py +70 -0
- {netbox_toolkit → netbox_toolkit_plugin}/migrations/0003_permission_system_update.py +26 -12
- {netbox_toolkit → netbox_toolkit_plugin}/migrations/0004_remove_django_permissions.py +27 -29
- {netbox_toolkit → netbox_toolkit_plugin}/migrations/0005_alter_command_options_and_more.py +7 -8
- {netbox_toolkit → netbox_toolkit_plugin}/migrations/0006_commandlog_parsed_data_commandlog_parsing_success_and_more.py +7 -8
- {netbox_toolkit → netbox_toolkit_plugin}/migrations/0007_alter_commandlog_parsing_template.py +6 -4
- {netbox_toolkit → netbox_toolkit_plugin}/models.py +31 -32
- {netbox_toolkit → netbox_toolkit_plugin}/navigation.py +6 -6
- {netbox_toolkit → netbox_toolkit_plugin}/services/command_service.py +188 -128
- {netbox_toolkit → netbox_toolkit_plugin}/services/rate_limiting_service.py +104 -97
- netbox_toolkit_plugin/settings.py +176 -0
- netbox_toolkit_plugin/tables.py +51 -0
- netbox_toolkit_plugin/templates/netbox_toolkit/command.html +108 -0
- netbox_toolkit_plugin/templates/netbox_toolkit/command_list.html +12 -0
- netbox_toolkit_plugin/templates/netbox_toolkit/commandlog.html +170 -0
- netbox_toolkit_plugin/templates/netbox_toolkit/device_toolkit.html +557 -0
- {netbox_toolkit/templates/netbox_toolkit → netbox_toolkit_plugin/templates/netbox_toolkit_plugin}/command.html +5 -5
- {netbox_toolkit/templates/netbox_toolkit → netbox_toolkit_plugin/templates/netbox_toolkit_plugin}/command_list.html +2 -2
- {netbox_toolkit/templates/netbox_toolkit → netbox_toolkit_plugin/templates/netbox_toolkit_plugin}/commandlog.html +2 -2
- {netbox_toolkit/templates/netbox_toolkit → netbox_toolkit_plugin/templates/netbox_toolkit_plugin}/device_toolkit.html +6 -6
- netbox_toolkit_plugin/urls.py +38 -0
- {netbox_toolkit → netbox_toolkit_plugin}/utils/logging.py +20 -19
- {netbox_toolkit → netbox_toolkit_plugin}/views.py +251 -169
- {netbox_toolkit_plugin-0.1.0.dist-info → netbox_toolkit_plugin-0.1.2.dist-info}/METADATA +2 -2
- netbox_toolkit_plugin-0.1.2.dist-info/RECORD +60 -0
- netbox_toolkit_plugin-0.1.2.dist-info/entry_points.txt +2 -0
- netbox_toolkit_plugin-0.1.2.dist-info/top_level.txt +1 -0
- netbox_toolkit/__init__.py +0 -30
- netbox_toolkit/config.py +0 -159
- netbox_toolkit/migrations/0001_initial.py +0 -54
- netbox_toolkit/migrations/0002_alter_command_options_alter_command_unique_together_and_more.py +0 -66
- netbox_toolkit/tables.py +0 -37
- netbox_toolkit/urls.py +0 -22
- netbox_toolkit_plugin-0.1.0.dist-info/RECORD +0 -56
- netbox_toolkit_plugin-0.1.0.dist-info/entry_points.txt +0 -2
- netbox_toolkit_plugin-0.1.0.dist-info/top_level.txt +0 -1
- {netbox_toolkit → netbox_toolkit_plugin}/admin.py +0 -0
- {netbox_toolkit → netbox_toolkit_plugin}/api/__init__.py +0 -0
- {netbox_toolkit → netbox_toolkit_plugin}/api/mixins.py +0 -0
- {netbox_toolkit → netbox_toolkit_plugin}/api/schemas.py +0 -0
- {netbox_toolkit → netbox_toolkit_plugin}/api/views/__init__.py +0 -0
- {netbox_toolkit → netbox_toolkit_plugin}/api/views/command_logs.py +0 -0
- {netbox_toolkit → netbox_toolkit_plugin}/api/views/commands.py +0 -0
- {netbox_toolkit → netbox_toolkit_plugin}/connectors/__init__.py +0 -0
- {netbox_toolkit → netbox_toolkit_plugin}/connectors/base.py +0 -0
- {netbox_toolkit → netbox_toolkit_plugin}/exceptions.py +0 -0
- {netbox_toolkit → netbox_toolkit_plugin}/filtersets.py +0 -0
- {netbox_toolkit → netbox_toolkit_plugin}/forms.py +0 -0
- {netbox_toolkit → netbox_toolkit_plugin}/migrations/__init__.py +0 -0
- {netbox_toolkit → netbox_toolkit_plugin}/search.py +0 -0
- {netbox_toolkit → netbox_toolkit_plugin}/services/__init__.py +0 -0
- {netbox_toolkit → netbox_toolkit_plugin}/services/device_service.py +0 -0
- {netbox_toolkit/static/netbox_toolkit → netbox_toolkit_plugin/static/netbox_toolkit_plugin}/css/toolkit.css +0 -0
- {netbox_toolkit/static/netbox_toolkit → netbox_toolkit_plugin/static/netbox_toolkit_plugin}/js/toolkit.js +0 -0
- {netbox_toolkit/templates/netbox_toolkit → netbox_toolkit_plugin/templates/netbox_toolkit_plugin}/command_edit.html +0 -0
- {netbox_toolkit/templates/netbox_toolkit → netbox_toolkit_plugin/templates/netbox_toolkit_plugin}/commandlog_list.html +0 -0
- {netbox_toolkit → netbox_toolkit_plugin}/utils/__init__.py +0 -0
- {netbox_toolkit → netbox_toolkit_plugin}/utils/connection.py +0 -0
- {netbox_toolkit → netbox_toolkit_plugin}/utils/error_parser.py +0 -0
- {netbox_toolkit → netbox_toolkit_plugin}/utils/network.py +0 -0
- {netbox_toolkit_plugin-0.1.0.dist-info → netbox_toolkit_plugin-0.1.2.dist-info}/WHEEL +0 -0
- {netbox_toolkit_plugin-0.1.0.dist-info → netbox_toolkit_plugin-0.1.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
from netbox.plugins import PluginConfig
|
2
|
+
|
3
|
+
__author__ = "Andy Norwood"
|
4
|
+
__version__ = "0.1.2"
|
5
|
+
|
6
|
+
|
7
|
+
class ToolkitPluginConfig(PluginConfig):
|
8
|
+
"""NetBox plugin configuration for the Toolkit plugin."""
|
9
|
+
|
10
|
+
name = "netbox_toolkit_plugin"
|
11
|
+
verbose_name = "Command Toolkit Plugin"
|
12
|
+
description = "NetBox plugin for running pre-defined commands on network devices"
|
13
|
+
version = __version__
|
14
|
+
author = __author__
|
15
|
+
base_url = "toolkit"
|
16
|
+
min_version = "4.2.0"
|
17
|
+
|
18
|
+
# Database migrations
|
19
|
+
required_settings = []
|
20
|
+
|
21
|
+
# Default plugin settings
|
22
|
+
default_settings = {
|
23
|
+
"rate_limiting_enabled": True,
|
24
|
+
"device_command_limit": 10,
|
25
|
+
"time_window_minutes": 5,
|
26
|
+
"bypass_users": [],
|
27
|
+
"bypass_groups": [],
|
28
|
+
"debug_logging": False, # Enable debug logging for this plugin
|
29
|
+
}
|
30
|
+
|
31
|
+
|
32
|
+
config = ToolkitPluginConfig
|
@@ -3,33 +3,36 @@ from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializ
|
|
3
3
|
from dcim.api.serializers import PlatformSerializer, DeviceSerializer
|
4
4
|
from ..models import Command, CommandLog
|
5
5
|
|
6
|
+
|
6
7
|
class CommandExecutionSerializer(serializers.Serializer):
|
7
8
|
"""Serializer for command execution input validation"""
|
9
|
+
|
8
10
|
device_id = serializers.IntegerField(
|
9
11
|
help_text="ID of the device to execute the command on"
|
10
12
|
)
|
11
13
|
username = serializers.CharField(
|
12
14
|
max_length=100,
|
13
15
|
help_text="Username for device authentication",
|
14
|
-
trim_whitespace=True
|
16
|
+
trim_whitespace=True,
|
15
17
|
)
|
16
18
|
password = serializers.CharField(
|
17
19
|
max_length=255,
|
18
|
-
style={
|
20
|
+
style={"input_type": "password"},
|
19
21
|
help_text="Password for device authentication",
|
20
|
-
trim_whitespace=False
|
22
|
+
trim_whitespace=False,
|
21
23
|
)
|
22
24
|
timeout = serializers.IntegerField(
|
23
25
|
required=False,
|
24
26
|
default=30,
|
25
27
|
min_value=5,
|
26
28
|
max_value=300,
|
27
|
-
help_text="Command execution timeout in seconds (5-300)"
|
29
|
+
help_text="Command execution timeout in seconds (5-300)",
|
28
30
|
)
|
29
|
-
|
31
|
+
|
30
32
|
def validate_device_id(self, value):
|
31
33
|
"""Validate that the device exists and has required attributes"""
|
32
34
|
from dcim.models import Device
|
35
|
+
|
33
36
|
try:
|
34
37
|
device = Device.objects.get(id=value)
|
35
38
|
if not device.platform:
|
@@ -43,17 +46,17 @@ class CommandExecutionSerializer(serializers.Serializer):
|
|
43
46
|
return value
|
44
47
|
except Device.DoesNotExist:
|
45
48
|
raise serializers.ValidationError("Device not found")
|
46
|
-
|
49
|
+
|
47
50
|
def validate(self, data):
|
48
51
|
"""Cross-field validation and object retrieval"""
|
49
52
|
from dcim.models import Device
|
50
|
-
|
53
|
+
|
51
54
|
# Get the actual device object for use in views
|
52
|
-
device = Device.objects.get(id=data[
|
53
|
-
data[
|
54
|
-
|
55
|
+
device = Device.objects.get(id=data["device_id"])
|
56
|
+
data["device"] = device
|
57
|
+
|
55
58
|
return data
|
56
|
-
|
59
|
+
|
57
60
|
def validate_username(self, value):
|
58
61
|
"""Validate username format"""
|
59
62
|
if not value.strip():
|
@@ -61,7 +64,7 @@ class CommandExecutionSerializer(serializers.Serializer):
|
|
61
64
|
if len(value.strip()) < 2:
|
62
65
|
raise serializers.ValidationError("Username must be at least 2 characters")
|
63
66
|
return value.strip()
|
64
|
-
|
67
|
+
|
65
68
|
def validate_password(self, value):
|
66
69
|
"""Validate password"""
|
67
70
|
if not value:
|
@@ -70,33 +73,45 @@ class CommandExecutionSerializer(serializers.Serializer):
|
|
70
73
|
raise serializers.ValidationError("Password must be at least 3 characters")
|
71
74
|
return value
|
72
75
|
|
76
|
+
|
73
77
|
class NestedCommandSerializer(WritableNestedSerializer):
|
74
78
|
url = serializers.HyperlinkedIdentityField(
|
75
|
-
view_name=
|
79
|
+
view_name="plugins-api:netbox_toolkit_plugin-api:command-detail"
|
76
80
|
)
|
77
81
|
|
78
82
|
class Meta:
|
79
83
|
model = Command
|
80
|
-
fields = (
|
84
|
+
fields = ("id", "url", "name", "display")
|
85
|
+
|
81
86
|
|
82
87
|
class CommandSerializer(NetBoxModelSerializer):
|
83
88
|
url = serializers.HyperlinkedIdentityField(
|
84
|
-
view_name=
|
89
|
+
view_name="plugins-api:netbox_toolkit_plugin-api:command-detail"
|
85
90
|
)
|
86
91
|
platform = PlatformSerializer(nested=True)
|
87
92
|
|
88
93
|
class Meta:
|
89
94
|
model = Command
|
90
95
|
fields = (
|
91
|
-
|
92
|
-
|
93
|
-
|
96
|
+
"id",
|
97
|
+
"url",
|
98
|
+
"display",
|
99
|
+
"name",
|
100
|
+
"command",
|
101
|
+
"description",
|
102
|
+
"platform",
|
103
|
+
"command_type",
|
104
|
+
"tags",
|
105
|
+
"custom_fields",
|
106
|
+
"created",
|
107
|
+
"last_updated",
|
94
108
|
)
|
95
|
-
brief_fields = (
|
109
|
+
brief_fields = ("id", "url", "display", "name", "command_type", "platform")
|
110
|
+
|
96
111
|
|
97
112
|
class CommandLogSerializer(NetBoxModelSerializer):
|
98
113
|
url = serializers.HyperlinkedIdentityField(
|
99
|
-
view_name=
|
114
|
+
view_name="plugins-api:netbox_toolkit_plugin-api:commandlog-detail"
|
100
115
|
)
|
101
116
|
command = NestedCommandSerializer()
|
102
117
|
device = DeviceSerializer(nested=True)
|
@@ -104,38 +119,58 @@ class CommandLogSerializer(NetBoxModelSerializer):
|
|
104
119
|
class Meta:
|
105
120
|
model = CommandLog
|
106
121
|
fields = (
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
122
|
+
"id",
|
123
|
+
"url",
|
124
|
+
"display",
|
125
|
+
"command",
|
126
|
+
"device",
|
127
|
+
"output",
|
128
|
+
"username",
|
129
|
+
"execution_time",
|
130
|
+
"success",
|
131
|
+
"error_message",
|
132
|
+
"execution_duration",
|
133
|
+
"parsed_data",
|
134
|
+
"parsing_success",
|
135
|
+
"parsing_template",
|
136
|
+
"created",
|
137
|
+
"last_updated",
|
138
|
+
)
|
139
|
+
brief_fields = (
|
140
|
+
"id",
|
141
|
+
"url",
|
142
|
+
"display",
|
143
|
+
"command",
|
144
|
+
"device",
|
145
|
+
"username",
|
146
|
+
"execution_time",
|
147
|
+
"success",
|
111
148
|
)
|
112
|
-
|
149
|
+
|
113
150
|
|
114
151
|
class BulkCommandExecutionSerializer(serializers.Serializer):
|
115
152
|
"""Serializer for bulk command execution validation"""
|
116
|
-
|
117
|
-
|
118
|
-
)
|
153
|
+
|
154
|
+
command_id = serializers.IntegerField(help_text="ID of the command to execute")
|
119
155
|
device_id = serializers.IntegerField(
|
120
156
|
help_text="ID of the device to execute the command on"
|
121
157
|
)
|
122
158
|
username = serializers.CharField(
|
123
|
-
max_length=100,
|
124
|
-
help_text="Username for device authentication"
|
159
|
+
max_length=100, help_text="Username for device authentication"
|
125
160
|
)
|
126
161
|
password = serializers.CharField(
|
127
162
|
max_length=255,
|
128
|
-
style={
|
129
|
-
help_text="Password for device authentication"
|
163
|
+
style={"input_type": "password"},
|
164
|
+
help_text="Password for device authentication",
|
130
165
|
)
|
131
166
|
timeout = serializers.IntegerField(
|
132
167
|
required=False,
|
133
168
|
default=30,
|
134
169
|
min_value=5,
|
135
170
|
max_value=300,
|
136
|
-
help_text="Command execution timeout in seconds"
|
171
|
+
help_text="Command execution timeout in seconds",
|
137
172
|
)
|
138
|
-
|
173
|
+
|
139
174
|
def validate_command_id(self, value):
|
140
175
|
"""Validate that the command exists"""
|
141
176
|
try:
|
@@ -143,10 +178,11 @@ class BulkCommandExecutionSerializer(serializers.Serializer):
|
|
143
178
|
return value
|
144
179
|
except Command.DoesNotExist:
|
145
180
|
raise serializers.ValidationError("Command not found")
|
146
|
-
|
181
|
+
|
147
182
|
def validate_device_id(self, value):
|
148
183
|
"""Validate that the device exists"""
|
149
184
|
from dcim.models import Device
|
185
|
+
|
150
186
|
try:
|
151
187
|
device = Device.objects.get(id=value)
|
152
188
|
if not device.platform:
|
@@ -1,10 +1,10 @@
|
|
1
1
|
from netbox.api.routers import NetBoxRouter
|
2
2
|
from .views import CommandViewSet, CommandLogViewSet
|
3
3
|
|
4
|
-
app_name =
|
4
|
+
app_name = "netbox_toolkit_plugin"
|
5
5
|
|
6
6
|
router = NetBoxRouter()
|
7
|
-
router.register(
|
8
|
-
router.register(
|
7
|
+
router.register("commands", CommandViewSet)
|
8
|
+
router.register("command-logs", CommandLogViewSet)
|
9
9
|
|
10
10
|
urlpatterns = router.urls
|