netbox-toolkit-plugin 0.1.0__tar.gz → 0.1.1__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 (73) hide show
  1. {netbox_toolkit_plugin-0.1.0/netbox_toolkit_plugin.egg-info → netbox_toolkit_plugin-0.1.1}/PKG-INFO +2 -2
  2. {netbox_toolkit_plugin-0.1.0 → netbox_toolkit_plugin-0.1.1}/README.md +1 -1
  3. netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/__init__.py +32 -0
  4. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/api/serializers.py +71 -35
  5. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/api/urls.py +3 -3
  6. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/config.py +80 -73
  7. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/connectors/factory.py +170 -111
  8. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/connectors/netmiko_connector.py +242 -179
  9. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/connectors/scrapli_connector.py +256 -172
  10. netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/migrations/0001_initial.py +108 -0
  11. netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/migrations/0002_alter_command_options_alter_command_unique_together_and_more.py +70 -0
  12. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/migrations/0003_permission_system_update.py +26 -12
  13. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/migrations/0004_remove_django_permissions.py +27 -29
  14. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/migrations/0005_alter_command_options_and_more.py +7 -8
  15. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/migrations/0006_commandlog_parsed_data_commandlog_parsing_success_and_more.py +7 -8
  16. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/migrations/0007_alter_commandlog_parsing_template.py +6 -4
  17. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/models.py +31 -32
  18. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/navigation.py +6 -6
  19. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/services/command_service.py +188 -128
  20. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/services/rate_limiting_service.py +104 -97
  21. netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/tables.py +51 -0
  22. netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/templates/netbox_toolkit/command.html +108 -0
  23. netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/templates/netbox_toolkit/command_list.html +12 -0
  24. netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/templates/netbox_toolkit/commandlog.html +170 -0
  25. netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/templates/netbox_toolkit/device_toolkit.html +557 -0
  26. {netbox_toolkit_plugin-0.1.0/netbox_toolkit/templates/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/templates/netbox_toolkit_plugin}/command.html +5 -5
  27. {netbox_toolkit_plugin-0.1.0/netbox_toolkit/templates/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/templates/netbox_toolkit_plugin}/command_list.html +2 -2
  28. {netbox_toolkit_plugin-0.1.0/netbox_toolkit/templates/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/templates/netbox_toolkit_plugin}/commandlog.html +2 -2
  29. {netbox_toolkit_plugin-0.1.0/netbox_toolkit/templates/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/templates/netbox_toolkit_plugin}/device_toolkit.html +6 -6
  30. netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/urls.py +38 -0
  31. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/utils/logging.py +20 -19
  32. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/views.py +251 -169
  33. {netbox_toolkit_plugin-0.1.0 → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin.egg-info}/PKG-INFO +2 -2
  34. netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin.egg-info/SOURCES.txt +63 -0
  35. netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin.egg-info/entry_points.txt +2 -0
  36. netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin.egg-info/top_level.txt +1 -0
  37. {netbox_toolkit_plugin-0.1.0 → netbox_toolkit_plugin-0.1.1}/pyproject.toml +3 -3
  38. netbox_toolkit_plugin-0.1.0/netbox_toolkit/__init__.py +0 -30
  39. netbox_toolkit_plugin-0.1.0/netbox_toolkit/migrations/0001_initial.py +0 -54
  40. netbox_toolkit_plugin-0.1.0/netbox_toolkit/migrations/0002_alter_command_options_alter_command_unique_together_and_more.py +0 -66
  41. netbox_toolkit_plugin-0.1.0/netbox_toolkit/tables.py +0 -37
  42. netbox_toolkit_plugin-0.1.0/netbox_toolkit/urls.py +0 -22
  43. netbox_toolkit_plugin-0.1.0/netbox_toolkit_plugin.egg-info/SOURCES.txt +0 -59
  44. netbox_toolkit_plugin-0.1.0/netbox_toolkit_plugin.egg-info/entry_points.txt +0 -2
  45. netbox_toolkit_plugin-0.1.0/netbox_toolkit_plugin.egg-info/top_level.txt +0 -1
  46. {netbox_toolkit_plugin-0.1.0 → netbox_toolkit_plugin-0.1.1}/LICENSE +0 -0
  47. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/admin.py +0 -0
  48. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/api/__init__.py +0 -0
  49. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/api/mixins.py +0 -0
  50. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/api/schemas.py +0 -0
  51. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/api/views/__init__.py +0 -0
  52. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/api/views/command_logs.py +0 -0
  53. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/api/views/commands.py +0 -0
  54. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/connectors/__init__.py +0 -0
  55. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/connectors/base.py +0 -0
  56. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/exceptions.py +0 -0
  57. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/filtersets.py +0 -0
  58. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/forms.py +0 -0
  59. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/migrations/__init__.py +0 -0
  60. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/search.py +0 -0
  61. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/services/__init__.py +0 -0
  62. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/services/device_service.py +0 -0
  63. {netbox_toolkit_plugin-0.1.0/netbox_toolkit/static/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/static/netbox_toolkit_plugin}/css/toolkit.css +0 -0
  64. {netbox_toolkit_plugin-0.1.0/netbox_toolkit/static/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/static/netbox_toolkit_plugin}/js/toolkit.js +0 -0
  65. {netbox_toolkit_plugin-0.1.0/netbox_toolkit/templates/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/templates/netbox_toolkit_plugin}/command_edit.html +0 -0
  66. {netbox_toolkit_plugin-0.1.0/netbox_toolkit/templates/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin/templates/netbox_toolkit_plugin}/commandlog_list.html +0 -0
  67. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/utils/__init__.py +0 -0
  68. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/utils/connection.py +0 -0
  69. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/utils/error_parser.py +0 -0
  70. {netbox_toolkit_plugin-0.1.0/netbox_toolkit → netbox_toolkit_plugin-0.1.1/netbox_toolkit_plugin}/utils/network.py +0 -0
  71. {netbox_toolkit_plugin-0.1.0 → netbox_toolkit_plugin-0.1.1}/netbox_toolkit_plugin.egg-info/dependency_links.txt +0 -0
  72. {netbox_toolkit_plugin-0.1.0 → netbox_toolkit_plugin-0.1.1}/netbox_toolkit_plugin.egg-info/requires.txt +0 -0
  73. {netbox_toolkit_plugin-0.1.0 → netbox_toolkit_plugin-0.1.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: netbox-toolkit-plugin
3
- Version: 0.1.0
3
+ Version: 0.1.1
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
@@ -58,7 +58,7 @@ A NetBox plugin that allows you to run commands on network devices directly from
58
58
 
59
59
  #### 📋 Command Management
60
60
  - [📋 Command Creation](./docs/user/command-creation.md) - Create platform-specific commands
61
- - [🔐 Permissions Setup](./docs/user/PERMISSIONS_SETUP_GUIDE.md) - Configure granular access control
61
+ - [🔐 Permissions Setup](./docs/user/permissions-setup-guide.md) - Configure granular access control
62
62
  - [📝 Permission Examples](./docs/user/permission-examples.md) - Example permission configuration
63
63
 
64
64
  #### 🔧 Troubleshooting
@@ -37,7 +37,7 @@ A NetBox plugin that allows you to run commands on network devices directly from
37
37
 
38
38
  #### 📋 Command Management
39
39
  - [📋 Command Creation](./docs/user/command-creation.md) - Create platform-specific commands
40
- - [🔐 Permissions Setup](./docs/user/PERMISSIONS_SETUP_GUIDE.md) - Configure granular access control
40
+ - [🔐 Permissions Setup](./docs/user/permissions-setup-guide.md) - Configure granular access control
41
41
  - [📝 Permission Examples](./docs/user/permission-examples.md) - Example permission configuration
42
42
 
43
43
  #### 🔧 Troubleshooting
@@ -0,0 +1,32 @@
1
+ from netbox.plugins import PluginConfig
2
+
3
+ __author__ = "Andy Norwood"
4
+ __version__ = "0.1.1"
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={'input_type': 'password'},
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['device_id'])
53
- data['device'] = device
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='plugins-api:netbox_toolkit-api:command-detail'
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 = ('id', 'url', 'name', 'display')
84
+ fields = ("id", "url", "name", "display")
85
+
81
86
 
82
87
  class CommandSerializer(NetBoxModelSerializer):
83
88
  url = serializers.HyperlinkedIdentityField(
84
- view_name='plugins-api:netbox_toolkit-api:command-detail'
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
- 'id', 'url', 'display', 'name', 'command', 'description',
92
- 'platform', 'command_type',
93
- 'tags', 'custom_fields', 'created', 'last_updated'
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 = ('id', 'url', 'display', 'name', 'command_type', 'platform')
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='plugins-api:netbox_toolkit-api:commandlog-detail'
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
- 'id', 'url', 'display', 'command', 'device', 'output',
108
- 'username', 'execution_time', 'success', 'error_message', 'execution_duration',
109
- 'parsed_data', 'parsing_success', 'parsing_template',
110
- 'created', 'last_updated'
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
- brief_fields = ('id', 'url', 'display', 'command', 'device', 'username', 'execution_time', 'success')
149
+
113
150
 
114
151
  class BulkCommandExecutionSerializer(serializers.Serializer):
115
152
  """Serializer for bulk command execution validation"""
116
- command_id = serializers.IntegerField(
117
- help_text="ID of the command to execute"
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={'input_type': 'password'},
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 = 'netbox_toolkit'
4
+ app_name = "netbox_toolkit_plugin"
5
5
 
6
6
  router = NetBoxRouter()
7
- router.register('commands', CommandViewSet)
8
- router.register('command-logs', CommandLogViewSet)
7
+ router.register("commands", CommandViewSet)
8
+ router.register("command-logs", CommandLogViewSet)
9
9
 
10
10
  urlpatterns = router.urls
@@ -1,81 +1,82 @@
1
1
  """Configuration settings for the NetBox Toolkit plugin."""
2
+
2
3
  from typing import Dict, Any
3
4
  from django.conf import settings
4
5
 
5
6
 
6
- class ToolkitConfig:
7
+ class ToolkitSettings:
7
8
  """Configuration class for toolkit settings."""
8
-
9
+
9
10
  # Default connection timeouts
10
11
  DEFAULT_TIMEOUTS = {
11
- 'socket': 15,
12
- 'transport': 15,
13
- 'ops': 30,
14
- 'banner': 15,
15
- 'auth': 15,
12
+ "socket": 15,
13
+ "transport": 15,
14
+ "ops": 30,
15
+ "banner": 15,
16
+ "auth": 15,
16
17
  }
17
-
18
+
18
19
  # Device-specific timeout overrides
19
20
  DEVICE_TIMEOUTS = {
20
- 'catalyst': {
21
- 'socket': 20,
22
- 'transport': 20,
23
- 'ops': 45,
21
+ "catalyst": {
22
+ "socket": 20,
23
+ "transport": 20,
24
+ "ops": 45,
24
25
  },
25
- 'nexus': {
26
- 'socket': 25,
27
- 'transport': 25,
28
- 'ops': 60,
26
+ "nexus": {
27
+ "socket": 25,
28
+ "transport": 25,
29
+ "ops": 60,
29
30
  },
30
31
  }
31
-
32
+
32
33
  # SSH transport options
33
34
  SSH_TRANSPORT_OPTIONS = {
34
- 'disabled_algorithms': {
35
- 'kex': [], # Don't disable any key exchange methods
35
+ "disabled_algorithms": {
36
+ "kex": [], # Don't disable any key exchange methods
36
37
  },
37
- 'allowed_kex': [
38
+ "allowed_kex": [
38
39
  # Modern algorithms
39
- 'diffie-hellman-group-exchange-sha256',
40
- 'diffie-hellman-group16-sha512',
41
- 'diffie-hellman-group18-sha512',
42
- 'diffie-hellman-group14-sha256',
40
+ "diffie-hellman-group-exchange-sha256",
41
+ "diffie-hellman-group16-sha512",
42
+ "diffie-hellman-group18-sha512",
43
+ "diffie-hellman-group14-sha256",
43
44
  # Legacy algorithms for older devices
44
- 'diffie-hellman-group-exchange-sha1',
45
- 'diffie-hellman-group14-sha1',
46
- 'diffie-hellman-group1-sha1',
45
+ "diffie-hellman-group-exchange-sha1",
46
+ "diffie-hellman-group14-sha1",
47
+ "diffie-hellman-group1-sha1",
47
48
  ],
48
49
  }
49
-
50
+
50
51
  # Netmiko configuration for fallback connections
51
52
  NETMIKO_CONFIG = {
52
- 'banner_timeout': 20,
53
- 'auth_timeout': 20,
54
- 'global_delay_factor': 1,
55
- 'use_keys': False, # Disable SSH key authentication
56
- 'allow_agent': False, # Disable SSH agent
53
+ "banner_timeout": 20,
54
+ "auth_timeout": 20,
55
+ "global_delay_factor": 1,
56
+ "use_keys": False, # Disable SSH key authentication
57
+ "allow_agent": False, # Disable SSH agent
57
58
  # Session logging (disabled by default)
58
- 'session_log': None,
59
+ "session_log": None,
59
60
  # Connection options for legacy devices
60
- 'fast_cli': False, # Disable for older devices
61
- 'session_log_record_writes': False,
62
- 'session_log_file_mode': 'write',
61
+ "fast_cli": False, # Disable for older devices
62
+ "session_log_record_writes": False,
63
+ "session_log_file_mode": "write",
63
64
  }
64
-
65
+
65
66
  # Retry configuration
66
67
  RETRY_CONFIG = {
67
- 'max_retries': 2,
68
- 'retry_delay': 1, # Reduced from 3s to 1s for faster fallback
69
- 'backoff_multiplier': 1.5, # Reduced from 2 to 1.5 for faster progression
68
+ "max_retries": 2,
69
+ "retry_delay": 1, # Reduced from 3s to 1s for faster fallback
70
+ "backoff_multiplier": 1.5, # Reduced from 2 to 1.5 for faster progression
70
71
  }
71
-
72
+
72
73
  # Fast connection test timeouts (for initial Scrapli viability testing)
73
74
  FAST_TEST_TIMEOUTS = {
74
- 'socket': 8, # Reduced from 15s to 8s for faster detection
75
- 'transport': 8, # Reduced from 15s to 8s for faster detection
76
- 'ops': 15, # Keep ops timeout reasonable for actual commands
75
+ "socket": 8, # Reduced from 15s to 8s for faster detection
76
+ "transport": 8, # Reduced from 15s to 8s for faster detection
77
+ "ops": 15, # Keep ops timeout reasonable for actual commands
77
78
  }
78
-
79
+
79
80
  # Error patterns that should trigger immediate fallback to Netmiko
80
81
  SCRAPLI_FAST_FAIL_PATTERNS = [
81
82
  "No matching key exchange",
@@ -83,77 +84,83 @@ class ToolkitConfig:
83
84
  "No matching MAC",
84
85
  "connection not opened",
85
86
  "Error reading SSH protocol banner",
86
- "Connection refused",
87
+ "Connection refused",
87
88
  "Operation timed out",
88
89
  "SSH handshake failed",
89
90
  "Protocol version not supported",
90
91
  "Unable to connect to port 22",
91
92
  "Name or service not known",
92
- "Network is unreachable"
93
+ "Network is unreachable",
93
94
  ]
94
-
95
+
95
96
  # Platform mappings for better recognition
96
97
  PLATFORM_ALIASES = {
97
- 'ios': 'cisco_ios',
98
- 'iosxe': 'cisco_ios',
99
- 'nxos': 'cisco_nxos',
100
- 'iosxr': 'cisco_iosxr',
101
- 'junos': 'juniper_junos',
102
- 'eos': 'arista_eos',
98
+ "ios": "cisco_ios",
99
+ "iosxe": "cisco_ios",
100
+ "nxos": "cisco_nxos",
101
+ "iosxr": "cisco_iosxr",
102
+ "junos": "juniper_junos",
103
+ "eos": "arista_eos",
103
104
  }
104
-
105
+
105
106
  @classmethod
106
107
  def get_fast_test_timeouts(cls) -> Dict[str, int]:
107
108
  """Get fast connection test timeouts for initial viability testing."""
108
109
  return cls.FAST_TEST_TIMEOUTS.copy()
109
-
110
+
110
111
  @classmethod
111
112
  def should_fast_fail_to_netmiko(cls, error_message: str) -> bool:
112
113
  """Check if error message indicates immediate fallback to Netmiko is needed."""
113
114
  error_lower = error_message.lower()
114
- return any(pattern.lower() in error_lower for pattern in cls.SCRAPLI_FAST_FAIL_PATTERNS)
115
-
115
+ return any(
116
+ pattern.lower() in error_lower for pattern in cls.SCRAPLI_FAST_FAIL_PATTERNS
117
+ )
118
+
116
119
  @classmethod
117
- def get_timeouts_for_device(cls, device_type_model: str = None) -> Dict[str, int]:
120
+ def get_timeouts_for_device(cls, device_type_model: str = "") -> Dict[str, int]:
118
121
  """Get timeout configuration for a specific device type."""
119
122
  timeouts = cls.DEFAULT_TIMEOUTS.copy()
120
-
123
+
121
124
  if device_type_model:
122
125
  model_lower = device_type_model.lower()
123
126
  for device_keyword, custom_timeouts in cls.DEVICE_TIMEOUTS.items():
124
127
  if device_keyword in model_lower:
125
128
  timeouts.update(custom_timeouts)
126
129
  break
127
-
130
+
128
131
  return timeouts
129
-
132
+
130
133
  @classmethod
131
134
  def normalize_platform(cls, platform: str) -> str:
132
135
  """Normalize platform name using aliases."""
133
136
  if not platform:
134
- return None
135
-
137
+ return ""
138
+
136
139
  platform_lower = platform.lower()
137
140
  return cls.PLATFORM_ALIASES.get(platform_lower, platform_lower)
138
-
141
+
139
142
  @classmethod
140
143
  def get_ssh_options(cls) -> Dict[str, Any]:
141
144
  """Get SSH transport options."""
142
145
  return cls.SSH_TRANSPORT_OPTIONS.copy()
143
-
146
+
144
147
  @classmethod
145
148
  def get_retry_config(cls) -> Dict[str, int]:
146
149
  """Get retry configuration."""
147
150
  return cls.RETRY_CONFIG.copy()
148
-
151
+
149
152
  @classmethod
150
153
  def get_ssh_transport_options(cls) -> Dict[str, Any]:
151
154
  """Get SSH transport options for Scrapli."""
152
- user_config = getattr(settings, 'NETBOX_TOOLKIT', {})
153
- return {**cls.SSH_TRANSPORT_OPTIONS, **user_config.get('ssh_options', {})}
154
-
155
+ user_config = getattr(settings, "PLUGINS_CONFIG", {}).get(
156
+ "netbox_toolkit_plugin", {}
157
+ )
158
+ return {**cls.SSH_TRANSPORT_OPTIONS, **user_config.get("ssh_options", {})}
159
+
155
160
  @classmethod
156
161
  def get_netmiko_config(cls) -> Dict[str, Any]:
157
162
  """Get Netmiko configuration for fallback connections."""
158
- user_config = getattr(settings, 'NETBOX_TOOLKIT', {})
159
- return {**cls.NETMIKO_CONFIG, **user_config.get('netmiko', {})}
163
+ user_config = getattr(settings, "PLUGINS_CONFIG", {}).get(
164
+ "netbox_toolkit_plugin", {}
165
+ )
166
+ return {**cls.NETMIKO_CONFIG, **user_config.get("netmiko", {})}