netbox-toolkit-plugin 0.1.1__py3-none-any.whl → 0.1.3__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 +1 -1
- netbox_toolkit_plugin/admin.py +11 -7
- netbox_toolkit_plugin/api/mixins.py +20 -16
- netbox_toolkit_plugin/api/schemas.py +53 -74
- netbox_toolkit_plugin/api/serializers.py +10 -11
- netbox_toolkit_plugin/api/urls.py +2 -1
- netbox_toolkit_plugin/api/views/__init__.py +4 -3
- netbox_toolkit_plugin/api/views/command_logs.py +80 -73
- netbox_toolkit_plugin/api/views/commands.py +140 -134
- netbox_toolkit_plugin/connectors/__init__.py +9 -9
- netbox_toolkit_plugin/connectors/base.py +30 -31
- netbox_toolkit_plugin/connectors/factory.py +22 -26
- netbox_toolkit_plugin/connectors/netmiko_connector.py +18 -28
- netbox_toolkit_plugin/connectors/scrapli_connector.py +17 -16
- netbox_toolkit_plugin/exceptions.py +0 -7
- netbox_toolkit_plugin/filtersets.py +26 -42
- netbox_toolkit_plugin/forms.py +13 -11
- netbox_toolkit_plugin/migrations/0008_remove_parsed_data_storage.py +26 -0
- netbox_toolkit_plugin/models.py +2 -17
- netbox_toolkit_plugin/navigation.py +3 -0
- netbox_toolkit_plugin/search.py +12 -9
- netbox_toolkit_plugin/services/__init__.py +1 -1
- netbox_toolkit_plugin/services/command_service.py +7 -10
- netbox_toolkit_plugin/services/device_service.py +40 -32
- netbox_toolkit_plugin/services/rate_limiting_service.py +4 -3
- netbox_toolkit_plugin/{config.py → settings.py} +17 -7
- netbox_toolkit_plugin/static/netbox_toolkit_plugin/js/toolkit.js +245 -119
- netbox_toolkit_plugin/tables.py +10 -1
- netbox_toolkit_plugin/templates/netbox_toolkit_plugin/commandlog.html +16 -84
- netbox_toolkit_plugin/templates/netbox_toolkit_plugin/device_toolkit.html +37 -33
- netbox_toolkit_plugin/urls.py +10 -3
- netbox_toolkit_plugin/utils/connection.py +54 -54
- netbox_toolkit_plugin/utils/error_parser.py +128 -109
- netbox_toolkit_plugin/utils/logging.py +1 -0
- netbox_toolkit_plugin/utils/network.py +74 -47
- netbox_toolkit_plugin/views.py +51 -22
- {netbox_toolkit_plugin-0.1.1.dist-info → netbox_toolkit_plugin-0.1.3.dist-info}/METADATA +2 -2
- netbox_toolkit_plugin-0.1.3.dist-info/RECORD +61 -0
- netbox_toolkit_plugin-0.1.1.dist-info/RECORD +0 -60
- {netbox_toolkit_plugin-0.1.1.dist-info → netbox_toolkit_plugin-0.1.3.dist-info}/WHEEL +0 -0
- {netbox_toolkit_plugin-0.1.1.dist-info → netbox_toolkit_plugin-0.1.3.dist-info}/entry_points.txt +0 -0
- {netbox_toolkit_plugin-0.1.1.dist-info → netbox_toolkit_plugin-0.1.3.dist-info}/licenses/LICENSE +0 -0
- {netbox_toolkit_plugin-0.1.1.dist-info → netbox_toolkit_plugin-0.1.3.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,13 @@
|
|
1
1
|
"""Utility for parsing and detecting vendor-specific error messages in command output."""
|
2
|
+
|
2
3
|
import re
|
3
|
-
from typing import Dict, List, Optional, Tuple
|
4
4
|
from dataclasses import dataclass
|
5
5
|
from enum import Enum
|
6
6
|
|
7
|
+
|
7
8
|
class ErrorType(Enum):
|
8
9
|
"""Types of errors that can be detected in command output."""
|
10
|
+
|
9
11
|
SYNTAX_ERROR = "syntax_error"
|
10
12
|
PERMISSION_ERROR = "permission_error"
|
11
13
|
COMMAND_NOT_FOUND = "command_not_found"
|
@@ -13,18 +15,22 @@ class ErrorType(Enum):
|
|
13
15
|
TIMEOUT_ERROR = "timeout_error"
|
14
16
|
UNKNOWN_ERROR = "unknown_error"
|
15
17
|
|
18
|
+
|
16
19
|
@dataclass
|
17
20
|
class ErrorPattern:
|
18
21
|
"""Represents an error pattern for a specific vendor."""
|
22
|
+
|
19
23
|
pattern: str
|
20
24
|
error_type: ErrorType
|
21
25
|
vendor: str
|
22
26
|
case_sensitive: bool = False
|
23
27
|
description: str = ""
|
24
28
|
|
29
|
+
|
25
30
|
@dataclass
|
26
31
|
class ParsedError:
|
27
32
|
"""Result of error parsing."""
|
33
|
+
|
28
34
|
error_type: ErrorType
|
29
35
|
vendor: str
|
30
36
|
original_message: str
|
@@ -32,13 +38,14 @@ class ParsedError:
|
|
32
38
|
guidance: str
|
33
39
|
confidence: float # 0.0 to 1.0, how confident we are this is an error
|
34
40
|
|
41
|
+
|
35
42
|
class VendorErrorParser:
|
36
43
|
"""Parser for detecting vendor-specific error messages."""
|
37
|
-
|
44
|
+
|
38
45
|
def __init__(self):
|
39
46
|
self.error_patterns = self._initialize_error_patterns()
|
40
|
-
|
41
|
-
def _initialize_error_patterns(self) ->
|
47
|
+
|
48
|
+
def _initialize_error_patterns(self) -> list[ErrorPattern]:
|
42
49
|
"""Initialize patterns for various vendor error messages."""
|
43
50
|
patterns = [
|
44
51
|
# Cisco IOS/IOS-XE Syntax Errors
|
@@ -46,336 +53,348 @@ class VendorErrorParser:
|
|
46
53
|
pattern=r"% Invalid input detected at '\^' marker\.",
|
47
54
|
error_type=ErrorType.SYNTAX_ERROR,
|
48
55
|
vendor="cisco_ios",
|
49
|
-
description="Invalid command syntax - caret indicates error position"
|
56
|
+
description="Invalid command syntax - caret indicates error position",
|
50
57
|
),
|
51
58
|
ErrorPattern(
|
52
59
|
pattern=r"% Ambiguous command:",
|
53
60
|
error_type=ErrorType.SYNTAX_ERROR,
|
54
61
|
vendor="cisco_ios",
|
55
|
-
description="Command is ambiguous - need more specific input"
|
62
|
+
description="Command is ambiguous - need more specific input",
|
56
63
|
),
|
57
64
|
ErrorPattern(
|
58
65
|
pattern=r"% Incomplete command\.",
|
59
66
|
error_type=ErrorType.SYNTAX_ERROR,
|
60
67
|
vendor="cisco_ios",
|
61
|
-
description="Command is incomplete - missing parameters"
|
68
|
+
description="Command is incomplete - missing parameters",
|
62
69
|
),
|
63
70
|
ErrorPattern(
|
64
71
|
pattern=r"% Unknown command or computer name, or unable to find computer address",
|
65
72
|
error_type=ErrorType.COMMAND_NOT_FOUND,
|
66
73
|
vendor="cisco_ios",
|
67
|
-
description="Command not recognized"
|
74
|
+
description="Command not recognized",
|
68
75
|
),
|
69
|
-
|
70
76
|
# Cisco NX-OS Syntax Errors
|
71
77
|
ErrorPattern(
|
72
78
|
pattern=r"% Invalid command at '\^' marker\.",
|
73
79
|
error_type=ErrorType.SYNTAX_ERROR,
|
74
80
|
vendor="cisco_nxos",
|
75
|
-
description="Invalid command syntax"
|
81
|
+
description="Invalid command syntax",
|
76
82
|
),
|
77
83
|
ErrorPattern(
|
78
84
|
pattern=r"% Invalid parameter detected at '\^' marker\.",
|
79
85
|
error_type=ErrorType.SYNTAX_ERROR,
|
80
86
|
vendor="cisco_nxos",
|
81
|
-
description="Invalid parameter in command"
|
87
|
+
description="Invalid parameter in command",
|
82
88
|
),
|
83
89
|
ErrorPattern(
|
84
90
|
pattern=r"% Command incomplete\.",
|
85
91
|
error_type=ErrorType.SYNTAX_ERROR,
|
86
92
|
vendor="cisco_nxos",
|
87
|
-
description="Command requires additional parameters"
|
93
|
+
description="Command requires additional parameters",
|
88
94
|
),
|
89
|
-
|
90
95
|
# Cisco IOS-XR Syntax Errors
|
91
96
|
ErrorPattern(
|
92
97
|
pattern=r"% Invalid input detected at '\^' marker\.",
|
93
98
|
error_type=ErrorType.SYNTAX_ERROR,
|
94
99
|
vendor="cisco_iosxr",
|
95
|
-
description="Invalid command syntax"
|
100
|
+
description="Invalid command syntax",
|
96
101
|
),
|
97
102
|
ErrorPattern(
|
98
103
|
pattern=r"% Incomplete command\.",
|
99
104
|
error_type=ErrorType.SYNTAX_ERROR,
|
100
105
|
vendor="cisco_iosxr",
|
101
|
-
description="Command is incomplete"
|
106
|
+
description="Command is incomplete",
|
102
107
|
),
|
103
|
-
|
104
108
|
# Juniper Junos Syntax Errors
|
105
109
|
ErrorPattern(
|
106
110
|
pattern=r"syntax error, expecting <[\w\-]+>",
|
107
111
|
error_type=ErrorType.SYNTAX_ERROR,
|
108
112
|
vendor="juniper_junos",
|
109
|
-
description="Syntax error - expected different keyword"
|
113
|
+
description="Syntax error - expected different keyword",
|
110
114
|
),
|
111
115
|
ErrorPattern(
|
112
116
|
pattern=r"syntax error\.",
|
113
117
|
error_type=ErrorType.SYNTAX_ERROR,
|
114
118
|
vendor="juniper_junos",
|
115
|
-
description="General syntax error"
|
119
|
+
description="General syntax error",
|
116
120
|
),
|
117
121
|
ErrorPattern(
|
118
122
|
pattern=r"unknown command\.",
|
119
123
|
error_type=ErrorType.COMMAND_NOT_FOUND,
|
120
124
|
vendor="juniper_junos",
|
121
|
-
description="Command not recognized"
|
125
|
+
description="Command not recognized",
|
122
126
|
),
|
123
127
|
ErrorPattern(
|
124
128
|
pattern=r"error: .*",
|
125
129
|
error_type=ErrorType.UNKNOWN_ERROR,
|
126
130
|
vendor="juniper_junos",
|
127
|
-
description="General error from device"
|
131
|
+
description="General error from device",
|
128
132
|
),
|
129
|
-
|
130
133
|
# Arista EOS Syntax Errors
|
131
134
|
ErrorPattern(
|
132
135
|
pattern=r"% Invalid input",
|
133
136
|
error_type=ErrorType.SYNTAX_ERROR,
|
134
137
|
vendor="arista_eos",
|
135
|
-
description="Invalid command input"
|
138
|
+
description="Invalid command input",
|
136
139
|
),
|
137
140
|
ErrorPattern(
|
138
141
|
pattern=r"% Incomplete command",
|
139
142
|
error_type=ErrorType.SYNTAX_ERROR,
|
140
143
|
vendor="arista_eos",
|
141
|
-
description="Command requires additional parameters"
|
144
|
+
description="Command requires additional parameters",
|
142
145
|
),
|
143
146
|
ErrorPattern(
|
144
147
|
pattern=r"% Ambiguous command",
|
145
148
|
error_type=ErrorType.SYNTAX_ERROR,
|
146
149
|
vendor="arista_eos",
|
147
|
-
description="Command is ambiguous"
|
150
|
+
description="Command is ambiguous",
|
148
151
|
),
|
149
|
-
|
150
152
|
# HPE/Aruba Syntax Errors
|
151
153
|
ErrorPattern(
|
152
154
|
pattern=r"Invalid input:",
|
153
155
|
error_type=ErrorType.SYNTAX_ERROR,
|
154
156
|
vendor="aruba",
|
155
|
-
description="Invalid command syntax"
|
157
|
+
description="Invalid command syntax",
|
156
158
|
),
|
157
159
|
ErrorPattern(
|
158
160
|
pattern=r"Unknown command\.",
|
159
161
|
error_type=ErrorType.COMMAND_NOT_FOUND,
|
160
162
|
vendor="aruba",
|
161
|
-
description="Command not recognized"
|
163
|
+
description="Command not recognized",
|
162
164
|
),
|
163
|
-
|
164
165
|
# Huawei Syntax Errors
|
165
166
|
ErrorPattern(
|
166
167
|
pattern=r"Error: Unrecognized command found at '\^' position\.",
|
167
168
|
error_type=ErrorType.SYNTAX_ERROR,
|
168
169
|
vendor="huawei",
|
169
|
-
description="Unrecognized command syntax"
|
170
|
+
description="Unrecognized command syntax",
|
170
171
|
),
|
171
172
|
ErrorPattern(
|
172
173
|
pattern=r"Error: Incomplete command found at '\^' position\.",
|
173
174
|
error_type=ErrorType.SYNTAX_ERROR,
|
174
175
|
vendor="huawei",
|
175
|
-
description="Incomplete command"
|
176
|
+
description="Incomplete command",
|
176
177
|
),
|
177
|
-
|
178
178
|
# Fortinet FortiOS Syntax Errors
|
179
179
|
ErrorPattern(
|
180
180
|
pattern=r"command parse error before",
|
181
181
|
error_type=ErrorType.SYNTAX_ERROR,
|
182
182
|
vendor="fortinet",
|
183
|
-
description="Command parsing error"
|
183
|
+
description="Command parsing error",
|
184
184
|
),
|
185
185
|
ErrorPattern(
|
186
186
|
pattern=r"Unknown action",
|
187
187
|
error_type=ErrorType.COMMAND_NOT_FOUND,
|
188
188
|
vendor="fortinet",
|
189
|
-
description="Unknown command or action"
|
189
|
+
description="Unknown command or action",
|
190
190
|
),
|
191
|
-
|
192
191
|
# Palo Alto PAN-OS Syntax Errors
|
193
192
|
ErrorPattern(
|
194
193
|
pattern=r"Invalid syntax\.",
|
195
194
|
error_type=ErrorType.SYNTAX_ERROR,
|
196
195
|
vendor="paloalto",
|
197
|
-
description="Invalid command syntax"
|
196
|
+
description="Invalid command syntax",
|
198
197
|
),
|
199
198
|
ErrorPattern(
|
200
199
|
pattern=r"Unknown command:",
|
201
200
|
error_type=ErrorType.COMMAND_NOT_FOUND,
|
202
201
|
vendor="paloalto",
|
203
|
-
description="Command not recognized"
|
202
|
+
description="Command not recognized",
|
204
203
|
),
|
205
|
-
|
206
204
|
# Generic Permission Errors (across vendors)
|
207
205
|
ErrorPattern(
|
208
206
|
pattern=r"Permission denied",
|
209
207
|
error_type=ErrorType.PERMISSION_ERROR,
|
210
208
|
vendor="generic",
|
211
|
-
description="Insufficient permissions for command"
|
209
|
+
description="Insufficient permissions for command",
|
212
210
|
),
|
213
211
|
ErrorPattern(
|
214
212
|
pattern=r"Access denied",
|
215
213
|
error_type=ErrorType.PERMISSION_ERROR,
|
216
214
|
vendor="generic",
|
217
|
-
description="Access denied for command"
|
215
|
+
description="Access denied for command",
|
218
216
|
),
|
219
217
|
ErrorPattern(
|
220
218
|
pattern=r"Insufficient privileges",
|
221
219
|
error_type=ErrorType.PERMISSION_ERROR,
|
222
220
|
vendor="generic",
|
223
|
-
description="User lacks required privileges"
|
221
|
+
description="User lacks required privileges",
|
224
222
|
),
|
225
|
-
|
226
223
|
# Generic Command Not Found Errors
|
227
224
|
ErrorPattern(
|
228
225
|
pattern=r"command not found",
|
229
226
|
error_type=ErrorType.COMMAND_NOT_FOUND,
|
230
227
|
vendor="generic",
|
231
228
|
case_sensitive=False,
|
232
|
-
description="Command not found on system"
|
229
|
+
description="Command not found on system",
|
233
230
|
),
|
234
231
|
ErrorPattern(
|
235
232
|
pattern=r"bad command or file name",
|
236
233
|
error_type=ErrorType.COMMAND_NOT_FOUND,
|
237
234
|
vendor="generic",
|
238
235
|
case_sensitive=False,
|
239
|
-
description="Command not recognized"
|
236
|
+
description="Command not recognized",
|
240
237
|
),
|
241
238
|
]
|
242
|
-
|
239
|
+
|
243
240
|
return patterns
|
244
|
-
|
245
|
-
def parse_command_output(
|
241
|
+
|
242
|
+
def parse_command_output(
|
243
|
+
self, output: str, device_platform: str | None = None
|
244
|
+
) -> ParsedError | None:
|
246
245
|
"""
|
247
246
|
Parse command output to detect vendor-specific errors.
|
248
|
-
|
247
|
+
|
249
248
|
Args:
|
250
249
|
output: The command output to analyze
|
251
250
|
device_platform: Optional platform hint to prioritize certain patterns
|
252
|
-
|
251
|
+
|
253
252
|
Returns:
|
254
253
|
ParsedError if an error is detected, None otherwise
|
255
254
|
"""
|
256
255
|
if not output or not output.strip():
|
257
256
|
return None
|
258
|
-
|
257
|
+
|
259
258
|
# Normalize the output for consistent parsing
|
260
|
-
output_lines = output.strip().split(
|
261
|
-
|
259
|
+
output_lines = output.strip().split("\n")
|
260
|
+
|
262
261
|
# Check each line for error patterns
|
263
262
|
best_match = None
|
264
263
|
highest_confidence = 0.0
|
265
|
-
|
264
|
+
|
266
265
|
for line in output_lines:
|
267
266
|
line = line.strip()
|
268
267
|
if not line:
|
269
268
|
continue
|
270
|
-
|
269
|
+
|
271
270
|
for pattern in self.error_patterns:
|
272
271
|
match = self._match_pattern(line, pattern, device_platform)
|
273
272
|
if match and match.confidence > highest_confidence:
|
274
273
|
best_match = match
|
275
274
|
highest_confidence = match.confidence
|
276
|
-
|
275
|
+
|
277
276
|
return best_match
|
278
|
-
|
279
|
-
def _match_pattern(
|
277
|
+
|
278
|
+
def _match_pattern(
|
279
|
+
self, line: str, pattern: ErrorPattern, device_platform: str | None = None
|
280
|
+
) -> ParsedError | None:
|
280
281
|
"""Check if a line matches an error pattern."""
|
281
282
|
flags = 0 if pattern.case_sensitive else re.IGNORECASE
|
282
|
-
|
283
|
+
|
283
284
|
if re.search(pattern.pattern, line, flags):
|
284
285
|
# Calculate confidence based on vendor match and pattern specificity
|
285
286
|
confidence = 0.7 # Base confidence
|
286
|
-
|
287
|
+
|
287
288
|
# Boost confidence if vendor matches device platform
|
288
|
-
if device_platform and self._platforms_match(
|
289
|
+
if device_platform and self._platforms_match(
|
290
|
+
pattern.vendor, device_platform
|
291
|
+
):
|
289
292
|
confidence += 0.2
|
290
|
-
|
293
|
+
|
291
294
|
# Boost confidence for more specific patterns
|
292
295
|
if len(pattern.pattern) > 20: # Longer patterns are typically more specific
|
293
296
|
confidence += 0.1
|
294
|
-
|
297
|
+
|
295
298
|
# Cap confidence at 1.0
|
296
299
|
confidence = min(confidence, 1.0)
|
297
|
-
|
300
|
+
|
298
301
|
enhanced_message = self._enhance_error_message(line, pattern)
|
299
302
|
guidance = self._get_error_guidance(pattern, line)
|
300
|
-
|
303
|
+
|
301
304
|
return ParsedError(
|
302
305
|
error_type=pattern.error_type,
|
303
306
|
vendor=pattern.vendor,
|
304
307
|
original_message=line,
|
305
308
|
enhanced_message=enhanced_message,
|
306
309
|
guidance=guidance,
|
307
|
-
confidence=confidence
|
310
|
+
confidence=confidence,
|
308
311
|
)
|
309
|
-
|
312
|
+
|
310
313
|
return None
|
311
|
-
|
314
|
+
|
312
315
|
def _platforms_match(self, pattern_vendor: str, device_platform: str) -> bool:
|
313
316
|
"""Check if pattern vendor matches device platform."""
|
314
317
|
if not device_platform:
|
315
318
|
return False
|
316
|
-
|
319
|
+
|
317
320
|
device_platform = device_platform.lower().strip()
|
318
321
|
pattern_vendor = pattern_vendor.lower().strip()
|
319
|
-
|
322
|
+
|
320
323
|
# Direct match
|
321
324
|
if pattern_vendor == device_platform:
|
322
325
|
return True
|
323
|
-
|
326
|
+
|
324
327
|
# Check for platform family matches
|
325
|
-
cisco_platforms = [
|
326
|
-
|
327
|
-
|
328
|
-
|
328
|
+
cisco_platforms = [
|
329
|
+
"cisco_ios",
|
330
|
+
"cisco_nxos",
|
331
|
+
"cisco_iosxr",
|
332
|
+
"ios",
|
333
|
+
"nxos",
|
334
|
+
"iosxr",
|
335
|
+
]
|
336
|
+
juniper_platforms = ["juniper_junos", "junos"]
|
337
|
+
arista_platforms = ["arista_eos", "eos"]
|
338
|
+
|
329
339
|
if pattern_vendor in cisco_platforms and device_platform in cisco_platforms:
|
330
340
|
return True
|
331
341
|
if pattern_vendor in juniper_platforms and device_platform in juniper_platforms:
|
332
342
|
return True
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
def _enhance_error_message(self, original_message: str, pattern: ErrorPattern) -> str:
|
343
|
+
return bool(pattern_vendor in arista_platforms and device_platform in arista_platforms)
|
344
|
+
|
345
|
+
def _enhance_error_message(
|
346
|
+
self, original_message: str, pattern: ErrorPattern
|
347
|
+
) -> str:
|
339
348
|
"""Enhance the original error message with additional context."""
|
340
349
|
vendor_name = self._get_vendor_display_name(pattern.vendor)
|
341
|
-
|
350
|
+
|
342
351
|
enhanced = f"[{vendor_name}] {original_message}"
|
343
|
-
|
352
|
+
|
344
353
|
if pattern.description:
|
345
354
|
enhanced += f"\n\nDescription: {pattern.description}"
|
346
|
-
|
355
|
+
|
347
356
|
return enhanced
|
348
|
-
|
357
|
+
|
349
358
|
def _get_vendor_display_name(self, vendor: str) -> str:
|
350
359
|
"""Get a human-readable vendor name."""
|
351
360
|
vendor_map = {
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
361
|
+
"cisco_ios": "Cisco IOS/IOS-XE",
|
362
|
+
"cisco_nxos": "Cisco NX-OS",
|
363
|
+
"cisco_iosxr": "Cisco IOS-XR",
|
364
|
+
"juniper_junos": "Juniper Junos",
|
365
|
+
"arista_eos": "Arista EOS",
|
366
|
+
"aruba": "HPE Aruba",
|
367
|
+
"huawei": "Huawei",
|
368
|
+
"fortinet": "Fortinet FortiOS",
|
369
|
+
"paloalto": "Palo Alto PAN-OS",
|
370
|
+
"generic": "Generic",
|
362
371
|
}
|
363
|
-
|
372
|
+
|
364
373
|
return vendor_map.get(vendor, vendor.title())
|
365
|
-
|
374
|
+
|
366
375
|
def _get_error_guidance(self, pattern: ErrorPattern, original_message: str) -> str:
|
367
376
|
"""Get vendor-specific guidance for resolving the error."""
|
368
377
|
base_guidance = {
|
369
|
-
ErrorType.SYNTAX_ERROR: self._get_syntax_error_guidance(
|
370
|
-
|
371
|
-
|
372
|
-
ErrorType.
|
378
|
+
ErrorType.SYNTAX_ERROR: self._get_syntax_error_guidance(
|
379
|
+
pattern.vendor, original_message
|
380
|
+
),
|
381
|
+
ErrorType.PERMISSION_ERROR: self._get_permission_error_guidance(
|
382
|
+
pattern.vendor
|
383
|
+
),
|
384
|
+
ErrorType.COMMAND_NOT_FOUND: self._get_command_not_found_guidance(
|
385
|
+
pattern.vendor
|
386
|
+
),
|
387
|
+
ErrorType.CONFIGURATION_ERROR: self._get_configuration_error_guidance(
|
388
|
+
pattern.vendor
|
389
|
+
),
|
373
390
|
ErrorType.TIMEOUT_ERROR: self._get_timeout_error_guidance(pattern.vendor),
|
374
|
-
ErrorType.UNKNOWN_ERROR: self._get_unknown_error_guidance(pattern.vendor)
|
391
|
+
ErrorType.UNKNOWN_ERROR: self._get_unknown_error_guidance(pattern.vendor),
|
375
392
|
}
|
376
|
-
|
377
|
-
return base_guidance.get(
|
378
|
-
|
393
|
+
|
394
|
+
return base_guidance.get(
|
395
|
+
pattern.error_type, "Check device documentation for error details."
|
396
|
+
)
|
397
|
+
|
379
398
|
def _get_syntax_error_guidance(self, vendor: str, message: str) -> str:
|
380
399
|
"""Get syntax error guidance based on vendor."""
|
381
400
|
common_guidance = (
|
@@ -383,9 +402,9 @@ class VendorErrorParser:
|
|
383
402
|
"• Verify the command syntax is correct for this device platform\n"
|
384
403
|
"• Ensure command has correct mode set (e.g., show or config)\n"
|
385
404
|
)
|
386
|
-
|
405
|
+
|
387
406
|
return common_guidance
|
388
|
-
|
407
|
+
|
389
408
|
def _get_permission_error_guidance(self, vendor: str) -> str:
|
390
409
|
"""Get permission error guidance."""
|
391
410
|
return (
|
@@ -393,7 +412,7 @@ class VendorErrorParser:
|
|
393
412
|
"• Verify your user account has the necessary privileges\n"
|
394
413
|
"• Review device AAA configuration for command authorization\n"
|
395
414
|
)
|
396
|
-
|
415
|
+
|
397
416
|
def _get_command_not_found_guidance(self, vendor: str) -> str:
|
398
417
|
"""Get command not found guidance."""
|
399
418
|
return (
|
@@ -402,21 +421,21 @@ class VendorErrorParser:
|
|
402
421
|
"• Check the command spelling and syntax\n"
|
403
422
|
"• Try the command manually on device\n"
|
404
423
|
)
|
405
|
-
|
424
|
+
|
406
425
|
def _get_configuration_error_guidance(self, vendor: str) -> str:
|
407
426
|
"""Get configuration error guidance."""
|
408
427
|
return (
|
409
428
|
"Configuration Error Troubleshooting:\n"
|
410
429
|
"• Review the configuration syntax for this platform\n"
|
411
430
|
)
|
412
|
-
|
431
|
+
|
413
432
|
def _get_timeout_error_guidance(self, vendor: str) -> str:
|
414
433
|
"""Get timeout error guidance."""
|
415
434
|
return (
|
416
435
|
"Timeout Error Troubleshooting:\n"
|
417
436
|
"• The command may be taking longer than expected\n"
|
418
437
|
)
|
419
|
-
|
438
|
+
|
420
439
|
def _get_unknown_error_guidance(self, vendor: str) -> str:
|
421
440
|
"""Get unknown error guidance."""
|
422
441
|
return (
|