netbox-toolkit-plugin 0.1.1__tar.gz → 0.1.3__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.
Files changed (71) hide show
  1. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/PKG-INFO +2 -2
  2. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/README.md +1 -1
  3. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/__init__.py +1 -1
  4. netbox_toolkit_plugin-0.1.3/netbox_toolkit_plugin/admin.py +20 -0
  5. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/api/mixins.py +20 -16
  6. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/api/schemas.py +53 -74
  7. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/api/serializers.py +10 -11
  8. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/api/urls.py +2 -1
  9. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/api/views/__init__.py +4 -3
  10. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/api/views/command_logs.py +80 -73
  11. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/api/views/commands.py +140 -134
  12. netbox_toolkit_plugin-0.1.3/netbox_toolkit_plugin/connectors/__init__.py +15 -0
  13. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/connectors/base.py +30 -31
  14. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/connectors/factory.py +22 -26
  15. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/connectors/netmiko_connector.py +18 -28
  16. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/connectors/scrapli_connector.py +17 -16
  17. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/exceptions.py +0 -7
  18. netbox_toolkit_plugin-0.1.3/netbox_toolkit_plugin/filtersets.py +69 -0
  19. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/forms.py +13 -11
  20. netbox_toolkit_plugin-0.1.3/netbox_toolkit_plugin/migrations/0008_remove_parsed_data_storage.py +26 -0
  21. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/models.py +2 -17
  22. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/navigation.py +3 -0
  23. netbox_toolkit_plugin-0.1.3/netbox_toolkit_plugin/search.py +24 -0
  24. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/services/__init__.py +1 -1
  25. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/services/command_service.py +7 -10
  26. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/services/device_service.py +40 -32
  27. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/services/rate_limiting_service.py +4 -3
  28. netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/config.py → netbox_toolkit_plugin-0.1.3/netbox_toolkit_plugin/settings.py +17 -7
  29. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/static/netbox_toolkit_plugin/js/toolkit.js +245 -119
  30. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/tables.py +10 -1
  31. netbox_toolkit_plugin-0.1.3/netbox_toolkit_plugin/templates/netbox_toolkit_plugin/commandlog.html +102 -0
  32. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/templates/netbox_toolkit_plugin/device_toolkit.html +37 -33
  33. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/urls.py +10 -3
  34. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/utils/connection.py +54 -54
  35. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/utils/error_parser.py +128 -109
  36. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/utils/logging.py +1 -0
  37. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/utils/network.py +74 -47
  38. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/views.py +51 -22
  39. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin.egg-info/PKG-INFO +2 -2
  40. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin.egg-info/SOURCES.txt +2 -1
  41. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/pyproject.toml +2 -2
  42. netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/admin.py +0 -16
  43. netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/connectors/__init__.py +0 -15
  44. netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/filtersets.py +0 -85
  45. netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/search.py +0 -21
  46. netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/templates/netbox_toolkit_plugin/commandlog.html +0 -170
  47. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/LICENSE +0 -0
  48. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/api/__init__.py +0 -0
  49. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/migrations/0001_initial.py +0 -0
  50. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/migrations/0002_alter_command_options_alter_command_unique_together_and_more.py +0 -0
  51. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/migrations/0003_permission_system_update.py +0 -0
  52. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/migrations/0004_remove_django_permissions.py +0 -0
  53. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/migrations/0005_alter_command_options_and_more.py +0 -0
  54. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/migrations/0006_commandlog_parsed_data_commandlog_parsing_success_and_more.py +0 -0
  55. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/migrations/0007_alter_commandlog_parsing_template.py +0 -0
  56. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/migrations/__init__.py +0 -0
  57. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/static/netbox_toolkit_plugin/css/toolkit.css +0 -0
  58. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/templates/netbox_toolkit/command.html +0 -0
  59. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/templates/netbox_toolkit/command_list.html +0 -0
  60. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/templates/netbox_toolkit/commandlog.html +0 -0
  61. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/templates/netbox_toolkit/device_toolkit.html +0 -0
  62. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/templates/netbox_toolkit_plugin/command.html +0 -0
  63. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/templates/netbox_toolkit_plugin/command_edit.html +0 -0
  64. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/templates/netbox_toolkit_plugin/command_list.html +0 -0
  65. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/templates/netbox_toolkit_plugin/commandlog_list.html +0 -0
  66. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin/utils/__init__.py +0 -0
  67. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin.egg-info/dependency_links.txt +0 -0
  68. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin.egg-info/entry_points.txt +0 -0
  69. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin.egg-info/requires.txt +0 -0
  70. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/netbox_toolkit_plugin.egg-info/top_level.txt +0 -0
  71. {netbox_toolkit_plugin-0.1.1 → netbox_toolkit_plugin-0.1.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: netbox-toolkit-plugin
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: NetBox plugin for running pre-defined commands on network devices
5
5
  Author: Andy Norwood
6
6
  Classifier: Development Status :: 3 - Alpha
@@ -67,7 +67,7 @@ A NetBox plugin that allows you to run commands on network devices directly from
67
67
 
68
68
  ## Contributing
69
69
 
70
- **🚀 Want to Contribute?** Start with the [Contributor Guide](./docs/contributing.md) for a fast overview of the codebase.
70
+ **🚀 Want to Contribute?** Start with the [Contributor Guide](./docs/development/contributing.md) for a fast overview of the codebase.
71
71
 
72
72
 
73
73
  ## Future ideas:
@@ -46,7 +46,7 @@ A NetBox plugin that allows you to run commands on network devices directly from
46
46
 
47
47
  ## Contributing
48
48
 
49
- **🚀 Want to Contribute?** Start with the [Contributor Guide](./docs/contributing.md) for a fast overview of the codebase.
49
+ **🚀 Want to Contribute?** Start with the [Contributor Guide](./docs/development/contributing.md) for a fast overview of the codebase.
50
50
 
51
51
 
52
52
  ## Future ideas:
@@ -1,7 +1,7 @@
1
1
  from netbox.plugins import PluginConfig
2
2
 
3
3
  __author__ = "Andy Norwood"
4
- __version__ = "0.1.1"
4
+ __version__ = "0.1.3"
5
5
 
6
6
 
7
7
  class ToolkitPluginConfig(PluginConfig):
@@ -0,0 +1,20 @@
1
+ from django.contrib import admin
2
+
3
+ from netbox.admin import NetBoxModelAdmin
4
+
5
+ from .models import Command, CommandLog
6
+
7
+
8
+ @admin.register(Command)
9
+ class CommandAdmin(NetBoxModelAdmin):
10
+ list_display = ("name", "platform", "command_type", "description")
11
+ list_filter = ("platform", "command_type")
12
+ search_fields = ("name", "command", "description")
13
+
14
+
15
+ @admin.register(CommandLog)
16
+ class CommandLogAdmin(NetBoxModelAdmin):
17
+ list_display = ("command", "device", "username", "execution_time")
18
+ list_filter = ("command", "device", "username", "execution_time")
19
+ search_fields = ("command__name", "device__name", "username", "output")
20
+ readonly_fields = ("output", "execution_time")
@@ -1,54 +1,58 @@
1
1
  """
2
2
  Common mixins and utilities for NetBox Toolkit API views
3
3
  """
4
- from rest_framework.response import Response
4
+
5
5
  from django.contrib.contenttypes.models import ContentType
6
+
6
7
  from users.models import ObjectPermission
7
8
 
8
9
 
9
10
  class APIResponseMixin:
10
11
  """Mixin to add consistent API response headers"""
11
-
12
+
12
13
  def finalize_response(self, request, response, *args, **kwargs):
13
14
  """Add custom headers to all responses"""
14
15
  response = super().finalize_response(request, response, *args, **kwargs)
15
-
16
+
16
17
  # Add API version header
17
- response['X-NetBox-Toolkit-API-Version'] = '1.0'
18
-
18
+ response["X-NetBox-Toolkit-API-Version"] = "1.0"
19
+
19
20
  return response
20
21
 
21
22
 
22
23
  class PermissionCheckMixin:
23
24
  """Mixin for NetBox ObjectPermission checking"""
24
-
25
+
25
26
  def _user_has_action_permission(self, user, obj, action):
26
27
  """Check if user has permission for a specific action on an object using NetBox's ObjectPermission system"""
27
28
  # Get content type for the object
28
29
  content_type = ContentType.objects.get_for_model(obj)
29
-
30
+
30
31
  # Check if user has any ObjectPermissions with the required action
31
32
  user_permissions = ObjectPermission.objects.filter(
32
- object_types__in=[content_type],
33
- actions__contains=[action],
34
- enabled=True
33
+ object_types__in=[content_type], actions__contains=[action], enabled=True
35
34
  )
36
-
35
+
37
36
  # Check if user is assigned to any groups with this permission
38
37
  user_groups = user.groups.all()
39
38
  for permission in user_permissions:
40
39
  # Check if permission applies to user or user's groups
41
- if (permission.users.filter(id=user.id).exists() or
42
- permission.groups.filter(id__in=user_groups.values_list('id', flat=True)).exists()):
43
-
40
+ if (
41
+ permission.users.filter(id=user.id).exists()
42
+ or permission.groups.filter(
43
+ id__in=user_groups.values_list("id", flat=True)
44
+ ).exists()
45
+ ):
44
46
  # If there are constraints, evaluate them
45
47
  if permission.constraints:
46
48
  # Create a queryset with the constraints and check if the object matches
47
- queryset = content_type.model_class().objects.filter(**permission.constraints)
49
+ queryset = content_type.model_class().objects.filter(
50
+ **permission.constraints
51
+ )
48
52
  if queryset.filter(id=obj.id).exists():
49
53
  return True
50
54
  else:
51
55
  # No constraints means permission applies to all objects of this type
52
56
  return True
53
-
57
+
54
58
  return False
@@ -1,44 +1,40 @@
1
1
  """
2
2
  OpenAPI schema definitions for NetBox Toolkit API
3
3
  """
4
- from drf_spectacular.utils import extend_schema, OpenApiResponse
5
4
 
5
+ from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema
6
6
 
7
7
  # Command ViewSet Schemas
8
8
  COMMAND_LIST_SCHEMA = extend_schema(
9
9
  summary="List commands",
10
10
  description="Retrieve a list of all commands available in the system.",
11
- tags=["Commands"]
11
+ tags=["Commands"],
12
12
  )
13
13
 
14
14
  COMMAND_RETRIEVE_SCHEMA = extend_schema(
15
15
  summary="Retrieve command",
16
16
  description="Retrieve details of a specific command.",
17
- tags=["Commands"]
17
+ tags=["Commands"],
18
18
  )
19
19
 
20
20
  COMMAND_CREATE_SCHEMA = extend_schema(
21
- summary="Create command",
22
- description="Create a new command.",
23
- tags=["Commands"]
21
+ summary="Create command", description="Create a new command.", tags=["Commands"]
24
22
  )
25
23
 
26
24
  COMMAND_UPDATE_SCHEMA = extend_schema(
27
25
  summary="Update command",
28
26
  description="Update an existing command.",
29
- tags=["Commands"]
27
+ tags=["Commands"],
30
28
  )
31
29
 
32
30
  COMMAND_PARTIAL_UPDATE_SCHEMA = extend_schema(
33
31
  summary="Partial update command",
34
32
  description="Partially update an existing command.",
35
- tags=["Commands"]
33
+ tags=["Commands"],
36
34
  )
37
35
 
38
36
  COMMAND_DESTROY_SCHEMA = extend_schema(
39
- summary="Delete command",
40
- description="Delete a command.",
41
- tags=["Commands"]
37
+ summary="Delete command", description="Delete a command.", tags=["Commands"]
42
38
  )
43
39
 
44
40
  COMMAND_EXECUTE_SCHEMA = extend_schema(
@@ -57,36 +53,25 @@ COMMAND_EXECUTE_SCHEMA = extend_schema(
57
53
  "command": {
58
54
  "id": 1,
59
55
  "name": "show interfaces",
60
- "command_type": "show"
61
- },
62
- "device": {
63
- "id": 1,
64
- "name": "switch01.example.com"
65
- },
66
- "syntax_error": {
67
- "detected": False
56
+ "command_type": "show",
68
57
  },
58
+ "device": {"id": 1, "name": "switch01.example.com"},
59
+ "syntax_error": {"detected": False},
69
60
  "parsed_output": {
70
61
  "success": True,
71
62
  "method": "textfsm",
72
- "data": [{"interface": "GigabitEthernet1/0/1", "status": "up"}]
73
- }
63
+ "data": [{"interface": "GigabitEthernet1/0/1", "status": "up"}],
64
+ },
74
65
  }
75
- ]
66
+ ],
76
67
  ),
77
68
  400: OpenApiResponse(
78
69
  description="Bad request - validation errors or command execution failed"
79
70
  ),
80
- 403: OpenApiResponse(
81
- description="Forbidden - insufficient permissions"
82
- ),
83
- 404: OpenApiResponse(
84
- description="Not found - command or device not found"
85
- ),
86
- 429: OpenApiResponse(
87
- description="Too many requests - rate limit exceeded"
88
- )
89
- }
71
+ 403: OpenApiResponse(description="Forbidden - insufficient permissions"),
72
+ 404: OpenApiResponse(description="Not found - command or device not found"),
73
+ 429: OpenApiResponse(description="Too many requests - rate limit exceeded"),
74
+ },
90
75
  )
91
76
 
92
77
  COMMAND_BULK_EXECUTE_SCHEMA = extend_schema(
@@ -104,13 +89,13 @@ COMMAND_BULK_EXECUTE_SCHEMA = extend_schema(
104
89
  "command_id": {"type": "integer"},
105
90
  "device_id": {"type": "integer"},
106
91
  "username": {"type": "string"},
107
- "password": {"type": "string", "format": "password"}
92
+ "password": {"type": "string", "format": "password"},
108
93
  },
109
- "required": ["command_id", "device_id", "username", "password"]
110
- }
94
+ "required": ["command_id", "device_id", "username", "password"],
95
+ },
111
96
  }
112
97
  },
113
- "required": ["executions"]
98
+ "required": ["executions"],
114
99
  },
115
100
  responses={
116
101
  200: OpenApiResponse(
@@ -119,54 +104,54 @@ COMMAND_BULK_EXECUTE_SCHEMA = extend_schema(
119
104
  {
120
105
  "results": [
121
106
  {"execution_id": 1, "success": True, "command_log_id": 123},
122
- {"execution_id": 2, "success": False, "error": "Permission denied"}
107
+ {
108
+ "execution_id": 2,
109
+ "success": False,
110
+ "error": "Permission denied",
111
+ },
123
112
  ],
124
- "summary": {
125
- "total": 2,
126
- "successful": 1,
127
- "failed": 1
128
- }
113
+ "summary": {"total": 2, "successful": 1, "failed": 1},
129
114
  }
130
- ]
115
+ ],
131
116
  )
132
- }
117
+ },
133
118
  )
134
119
 
135
120
  # Command Log ViewSet Schemas
136
121
  COMMAND_LOG_LIST_SCHEMA = extend_schema(
137
122
  summary="List command logs",
138
123
  description="Retrieve a list of command execution logs with filtering and search capabilities.",
139
- tags=["Command Logs"]
124
+ tags=["Command Logs"],
140
125
  )
141
126
 
142
127
  COMMAND_LOG_RETRIEVE_SCHEMA = extend_schema(
143
128
  summary="Retrieve command log",
144
129
  description="Retrieve details of a specific command execution log.",
145
- tags=["Command Logs"]
130
+ tags=["Command Logs"],
146
131
  )
147
132
 
148
133
  COMMAND_LOG_CREATE_SCHEMA = extend_schema(
149
134
  summary="Create command log",
150
135
  description="Create a new command execution log entry.",
151
- tags=["Command Logs"]
136
+ tags=["Command Logs"],
152
137
  )
153
138
 
154
139
  COMMAND_LOG_UPDATE_SCHEMA = extend_schema(
155
140
  summary="Update command log",
156
141
  description="Update an existing command log entry.",
157
- tags=["Command Logs"]
142
+ tags=["Command Logs"],
158
143
  )
159
144
 
160
145
  COMMAND_LOG_PARTIAL_UPDATE_SCHEMA = extend_schema(
161
146
  summary="Partial update command log",
162
147
  description="Partially update an existing command log entry.",
163
- tags=["Command Logs"]
148
+ tags=["Command Logs"],
164
149
  )
165
150
 
166
151
  COMMAND_LOG_DESTROY_SCHEMA = extend_schema(
167
152
  summary="Delete command log",
168
153
  description="Delete a command log entry.",
169
- tags=["Command Logs"]
154
+ tags=["Command Logs"],
170
155
  )
171
156
 
172
157
  COMMAND_LOG_STATISTICS_SCHEMA = extend_schema(
@@ -180,55 +165,49 @@ COMMAND_LOG_STATISTICS_SCHEMA = extend_schema(
180
165
  {
181
166
  "total_logs": 1000,
182
167
  "success_rate": 85.5,
183
- "last_24h": {
184
- "total": 50,
185
- "successful": 45,
186
- "failed": 5
187
- },
168
+ "last_24h": {"total": 50, "successful": 45, "failed": 5},
188
169
  "top_commands": [
189
170
  {"command_name": "show interfaces", "count": 150},
190
- {"command_name": "show version", "count": 120}
171
+ {"command_name": "show version", "count": 120},
191
172
  ],
192
173
  "common_errors": [
193
174
  {"error": "Connection timeout", "count": 10},
194
- {"error": "Invalid command", "count": 5}
195
- ]
175
+ {"error": "Invalid command", "count": 5},
176
+ ],
196
177
  }
197
- ]
178
+ ],
198
179
  )
199
- }
180
+ },
200
181
  )
201
182
 
202
- from drf_spectacular.utils import OpenApiParameter
203
-
204
183
  COMMAND_LOG_EXPORT_SCHEMA = extend_schema(
205
184
  summary="Export command logs",
206
185
  description="Export command logs in various formats (CSV, JSON).",
207
186
  tags=["Command Logs"],
208
187
  parameters=[
209
188
  OpenApiParameter(
210
- name='format',
211
- description='Export format',
189
+ name="format",
190
+ description="Export format",
212
191
  required=False,
213
192
  type=str,
214
- enum=['csv', 'json'],
215
- default='json'
193
+ enum=["csv", "json"],
194
+ default="json",
216
195
  ),
217
196
  OpenApiParameter(
218
- name='start_date',
219
- description='Start date for export (YYYY-MM-DD)',
197
+ name="start_date",
198
+ description="Start date for export (YYYY-MM-DD)",
220
199
  required=False,
221
- type=str
200
+ type=str,
222
201
  ),
223
202
  OpenApiParameter(
224
- name='end_date',
225
- description='End date for export (YYYY-MM-DD)',
203
+ name="end_date",
204
+ description="End date for export (YYYY-MM-DD)",
226
205
  required=False,
227
- type=str
206
+ type=str,
228
207
  ),
229
208
  ],
230
209
  responses={
231
210
  200: OpenApiResponse(description="Export data"),
232
- 400: OpenApiResponse(description="Invalid parameters")
233
- }
211
+ 400: OpenApiResponse(description="Invalid parameters"),
212
+ },
234
213
  )
@@ -1,6 +1,8 @@
1
- from rest_framework import serializers
1
+ from dcim.api.serializers import DeviceSerializer, PlatformSerializer
2
2
  from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
3
- from dcim.api.serializers import PlatformSerializer, DeviceSerializer
3
+
4
+ from rest_framework import serializers
5
+
4
6
  from ..models import Command, CommandLog
5
7
 
6
8
 
@@ -44,8 +46,8 @@ class CommandExecutionSerializer(serializers.Serializer):
44
46
  "Device must have a primary IP address for command execution"
45
47
  )
46
48
  return value
47
- except Device.DoesNotExist:
48
- raise serializers.ValidationError("Device not found")
49
+ except Device.DoesNotExist as e:
50
+ raise serializers.ValidationError("Device not found") from e
49
51
 
50
52
  def validate(self, data):
51
53
  """Cross-field validation and object retrieval"""
@@ -130,9 +132,6 @@ class CommandLogSerializer(NetBoxModelSerializer):
130
132
  "success",
131
133
  "error_message",
132
134
  "execution_duration",
133
- "parsed_data",
134
- "parsing_success",
135
- "parsing_template",
136
135
  "created",
137
136
  "last_updated",
138
137
  )
@@ -176,8 +175,8 @@ class BulkCommandExecutionSerializer(serializers.Serializer):
176
175
  try:
177
176
  Command.objects.get(id=value)
178
177
  return value
179
- except Command.DoesNotExist:
180
- raise serializers.ValidationError("Command not found")
178
+ except Command.DoesNotExist as e:
179
+ raise serializers.ValidationError("Command not found") from e
181
180
 
182
181
  def validate_device_id(self, value):
183
182
  """Validate that the device exists"""
@@ -190,5 +189,5 @@ class BulkCommandExecutionSerializer(serializers.Serializer):
190
189
  "Device must have a platform assigned"
191
190
  )
192
191
  return value
193
- except Device.DoesNotExist:
194
- raise serializers.ValidationError("Device not found")
192
+ except Device.DoesNotExist as e:
193
+ raise serializers.ValidationError("Device not found") from e
@@ -1,5 +1,6 @@
1
1
  from netbox.api.routers import NetBoxRouter
2
- from .views import CommandViewSet, CommandLogViewSet
2
+
3
+ from .views import CommandLogViewSet, CommandViewSet
3
4
 
4
5
  app_name = "netbox_toolkit_plugin"
5
6
 
@@ -1,10 +1,11 @@
1
1
  """
2
2
  Import all viewsets for easier access
3
3
  """
4
- from .commands import CommandViewSet
4
+
5
5
  from .command_logs import CommandLogViewSet
6
+ from .commands import CommandViewSet
6
7
 
7
8
  __all__ = [
8
- 'CommandViewSet',
9
- 'CommandLogViewSet',
9
+ "CommandViewSet",
10
+ "CommandLogViewSet",
10
11
  ]