runbooks 0.7.6__py3-none-any.whl ā 0.7.9__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.
- runbooks/__init__.py +1 -1
- runbooks/base.py +5 -1
- runbooks/cfat/__init__.py +8 -4
- runbooks/cfat/assessment/collectors.py +171 -14
- runbooks/cfat/assessment/compliance.py +871 -0
- runbooks/cfat/assessment/runner.py +122 -11
- runbooks/cfat/models.py +6 -2
- runbooks/common/logger.py +14 -0
- runbooks/common/rich_utils.py +451 -0
- runbooks/enterprise/__init__.py +68 -0
- runbooks/enterprise/error_handling.py +411 -0
- runbooks/enterprise/logging.py +439 -0
- runbooks/enterprise/multi_tenant.py +583 -0
- runbooks/finops/README.md +468 -241
- runbooks/finops/__init__.py +39 -3
- runbooks/finops/cli.py +83 -18
- runbooks/finops/cross_validation.py +375 -0
- runbooks/finops/dashboard_runner.py +812 -164
- runbooks/finops/enhanced_dashboard_runner.py +525 -0
- runbooks/finops/finops_dashboard.py +1892 -0
- runbooks/finops/helpers.py +485 -51
- runbooks/finops/optimizer.py +823 -0
- runbooks/finops/tests/__init__.py +19 -0
- runbooks/finops/tests/results_test_finops_dashboard.xml +1 -0
- runbooks/finops/tests/run_comprehensive_tests.py +421 -0
- runbooks/finops/tests/run_tests.py +305 -0
- runbooks/finops/tests/test_finops_dashboard.py +705 -0
- runbooks/finops/tests/test_integration.py +477 -0
- runbooks/finops/tests/test_performance.py +380 -0
- runbooks/finops/tests/test_performance_benchmarks.py +500 -0
- runbooks/finops/tests/test_reference_images_validation.py +867 -0
- runbooks/finops/tests/test_single_account_features.py +715 -0
- runbooks/finops/tests/validate_test_suite.py +220 -0
- runbooks/finops/types.py +1 -1
- runbooks/hitl/enhanced_workflow_engine.py +725 -0
- runbooks/inventory/artifacts/scale-optimize-status.txt +12 -0
- runbooks/inventory/collectors/aws_comprehensive.py +442 -0
- runbooks/inventory/collectors/enterprise_scale.py +281 -0
- runbooks/inventory/core/collector.py +172 -13
- runbooks/inventory/discovery.md +1 -1
- runbooks/inventory/list_ec2_instances.py +18 -20
- runbooks/inventory/list_ssm_parameters.py +31 -3
- runbooks/inventory/organizations_discovery.py +1269 -0
- runbooks/inventory/rich_inventory_display.py +393 -0
- runbooks/inventory/run_on_multi_accounts.py +35 -19
- runbooks/inventory/runbooks.security.report_generator.log +0 -0
- runbooks/inventory/runbooks.security.run_script.log +0 -0
- runbooks/inventory/vpc_flow_analyzer.py +1030 -0
- runbooks/main.py +2215 -119
- runbooks/metrics/dora_metrics_engine.py +599 -0
- runbooks/operate/__init__.py +2 -2
- runbooks/operate/base.py +122 -10
- runbooks/operate/deployment_framework.py +1032 -0
- runbooks/operate/deployment_validator.py +853 -0
- runbooks/operate/dynamodb_operations.py +10 -6
- runbooks/operate/ec2_operations.py +319 -11
- runbooks/operate/executive_dashboard.py +779 -0
- runbooks/operate/mcp_integration.py +750 -0
- runbooks/operate/nat_gateway_operations.py +1120 -0
- runbooks/operate/networking_cost_heatmap.py +685 -0
- runbooks/operate/privatelink_operations.py +940 -0
- runbooks/operate/s3_operations.py +10 -6
- runbooks/operate/vpc_endpoints.py +644 -0
- runbooks/operate/vpc_operations.py +1038 -0
- runbooks/remediation/__init__.py +2 -2
- runbooks/remediation/acm_remediation.py +1 -1
- runbooks/remediation/base.py +1 -1
- runbooks/remediation/cloudtrail_remediation.py +1 -1
- runbooks/remediation/cognito_remediation.py +1 -1
- runbooks/remediation/dynamodb_remediation.py +1 -1
- runbooks/remediation/ec2_remediation.py +1 -1
- runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -1
- runbooks/remediation/kms_enable_key_rotation.py +1 -1
- runbooks/remediation/kms_remediation.py +1 -1
- runbooks/remediation/lambda_remediation.py +1 -1
- runbooks/remediation/multi_account.py +1 -1
- runbooks/remediation/rds_remediation.py +1 -1
- runbooks/remediation/s3_block_public_access.py +1 -1
- runbooks/remediation/s3_enable_access_logging.py +1 -1
- runbooks/remediation/s3_encryption.py +1 -1
- runbooks/remediation/s3_remediation.py +1 -1
- runbooks/remediation/vpc_remediation.py +475 -0
- runbooks/security/__init__.py +3 -1
- runbooks/security/compliance_automation.py +632 -0
- runbooks/security/report_generator.py +10 -0
- runbooks/security/run_script.py +31 -5
- runbooks/security/security_baseline_tester.py +169 -30
- runbooks/security/security_export.py +477 -0
- runbooks/validation/__init__.py +10 -0
- runbooks/validation/benchmark.py +484 -0
- runbooks/validation/cli.py +356 -0
- runbooks/validation/mcp_validator.py +768 -0
- runbooks/vpc/__init__.py +38 -0
- runbooks/vpc/config.py +212 -0
- runbooks/vpc/cost_engine.py +347 -0
- runbooks/vpc/heatmap_engine.py +605 -0
- runbooks/vpc/manager_interface.py +634 -0
- runbooks/vpc/networking_wrapper.py +1260 -0
- runbooks/vpc/rich_formatters.py +679 -0
- runbooks/vpc/tests/__init__.py +5 -0
- runbooks/vpc/tests/conftest.py +356 -0
- runbooks/vpc/tests/test_cli_integration.py +530 -0
- runbooks/vpc/tests/test_config.py +458 -0
- runbooks/vpc/tests/test_cost_engine.py +479 -0
- runbooks/vpc/tests/test_networking_wrapper.py +512 -0
- {runbooks-0.7.6.dist-info ā runbooks-0.7.9.dist-info}/METADATA +40 -12
- {runbooks-0.7.6.dist-info ā runbooks-0.7.9.dist-info}/RECORD +111 -50
- {runbooks-0.7.6.dist-info ā runbooks-0.7.9.dist-info}/WHEEL +0 -0
- {runbooks-0.7.6.dist-info ā runbooks-0.7.9.dist-info}/entry_points.txt +0 -0
- {runbooks-0.7.6.dist-info ā runbooks-0.7.9.dist-info}/licenses/LICENSE +0 -0
- {runbooks-0.7.6.dist-info ā runbooks-0.7.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
"""
|
2
|
+
Enterprise enhancements for CloudOps-Runbooks.
|
3
|
+
|
4
|
+
This module provides enterprise-grade enhancements including:
|
5
|
+
- Advanced error handling with actionable guidance
|
6
|
+
- Structured logging for enterprise monitoring
|
7
|
+
- Security hardening and compliance validation
|
8
|
+
- Enhanced configuration management
|
9
|
+
- Professional documentation standards
|
10
|
+
"""
|
11
|
+
|
12
|
+
from .error_handling import (
|
13
|
+
AWSServiceError,
|
14
|
+
ConfigurationError,
|
15
|
+
EnterpriseErrorHandler,
|
16
|
+
RunbooksException,
|
17
|
+
SecurityError,
|
18
|
+
ValidationError,
|
19
|
+
create_user_friendly_error,
|
20
|
+
)
|
21
|
+
from .logging import (
|
22
|
+
AuditLogger,
|
23
|
+
EnterpriseLogger,
|
24
|
+
PerformanceLogger,
|
25
|
+
configure_enterprise_logging,
|
26
|
+
)
|
27
|
+
from .security import (
|
28
|
+
ComplianceChecker,
|
29
|
+
SecurityValidator,
|
30
|
+
ZeroTrustValidator,
|
31
|
+
sanitize_input,
|
32
|
+
validate_aws_permissions,
|
33
|
+
)
|
34
|
+
from .validation import (
|
35
|
+
ConfigValidator,
|
36
|
+
InputValidator,
|
37
|
+
TypeValidator,
|
38
|
+
validate_configuration,
|
39
|
+
validate_user_input,
|
40
|
+
)
|
41
|
+
|
42
|
+
__all__ = [
|
43
|
+
# Error handling
|
44
|
+
"EnterpriseErrorHandler",
|
45
|
+
"RunbooksException",
|
46
|
+
"ConfigurationError",
|
47
|
+
"ValidationError",
|
48
|
+
"SecurityError",
|
49
|
+
"AWSServiceError",
|
50
|
+
"create_user_friendly_error",
|
51
|
+
# Logging
|
52
|
+
"EnterpriseLogger",
|
53
|
+
"AuditLogger",
|
54
|
+
"PerformanceLogger",
|
55
|
+
"configure_enterprise_logging",
|
56
|
+
# Security
|
57
|
+
"SecurityValidator",
|
58
|
+
"ComplianceChecker",
|
59
|
+
"ZeroTrustValidator",
|
60
|
+
"sanitize_input",
|
61
|
+
"validate_aws_permissions",
|
62
|
+
# Validation
|
63
|
+
"ConfigValidator",
|
64
|
+
"InputValidator",
|
65
|
+
"TypeValidator",
|
66
|
+
"validate_configuration",
|
67
|
+
"validate_user_input",
|
68
|
+
]
|
@@ -0,0 +1,411 @@
|
|
1
|
+
"""
|
2
|
+
Enterprise-grade error handling for CloudOps-Runbooks.
|
3
|
+
|
4
|
+
This module provides comprehensive error handling with actionable user guidance,
|
5
|
+
structured error logging, and user-friendly error messages that help users
|
6
|
+
resolve issues quickly.
|
7
|
+
"""
|
8
|
+
|
9
|
+
import sys
|
10
|
+
import traceback
|
11
|
+
from datetime import datetime
|
12
|
+
from pathlib import Path
|
13
|
+
from typing import Any, Dict, List, Optional, Type, Union
|
14
|
+
|
15
|
+
import click
|
16
|
+
from loguru import logger
|
17
|
+
|
18
|
+
try:
|
19
|
+
from rich.console import Console
|
20
|
+
from rich.panel import Panel
|
21
|
+
from rich.table import Table
|
22
|
+
from rich.text import Text
|
23
|
+
|
24
|
+
_HAS_RICH = True
|
25
|
+
except ImportError:
|
26
|
+
_HAS_RICH = False
|
27
|
+
|
28
|
+
|
29
|
+
class RunbooksException(Exception):
|
30
|
+
"""Base exception for all CloudOps-Runbooks errors."""
|
31
|
+
|
32
|
+
def __init__(
|
33
|
+
self,
|
34
|
+
message: str,
|
35
|
+
error_code: str = "RUNBOOKS_ERROR",
|
36
|
+
suggestion: Optional[str] = None,
|
37
|
+
documentation_url: Optional[str] = None,
|
38
|
+
context: Optional[Dict[str, Any]] = None,
|
39
|
+
):
|
40
|
+
"""
|
41
|
+
Initialize enterprise exception.
|
42
|
+
|
43
|
+
Args:
|
44
|
+
message: Error message
|
45
|
+
error_code: Unique error code for troubleshooting
|
46
|
+
suggestion: Actionable suggestion to resolve the issue
|
47
|
+
documentation_url: URL to relevant documentation
|
48
|
+
context: Additional context for debugging
|
49
|
+
"""
|
50
|
+
super().__init__(message)
|
51
|
+
self.error_code = error_code
|
52
|
+
self.suggestion = suggestion
|
53
|
+
self.documentation_url = documentation_url
|
54
|
+
self.context = context or {}
|
55
|
+
self.timestamp = datetime.utcnow().isoformat()
|
56
|
+
|
57
|
+
|
58
|
+
class ConfigurationError(RunbooksException):
|
59
|
+
"""Configuration-related errors."""
|
60
|
+
|
61
|
+
def __init__(self, message: str, **kwargs):
|
62
|
+
super().__init__(
|
63
|
+
message,
|
64
|
+
error_code="CONFIG_ERROR",
|
65
|
+
documentation_url="https://cloudops.oceansoft.io/configuration/",
|
66
|
+
**kwargs,
|
67
|
+
)
|
68
|
+
|
69
|
+
|
70
|
+
class ValidationError(RunbooksException):
|
71
|
+
"""Input validation errors."""
|
72
|
+
|
73
|
+
def __init__(self, message: str, **kwargs):
|
74
|
+
super().__init__(
|
75
|
+
message,
|
76
|
+
error_code="VALIDATION_ERROR",
|
77
|
+
documentation_url="https://cloudops.oceansoft.io/validation/",
|
78
|
+
**kwargs,
|
79
|
+
)
|
80
|
+
|
81
|
+
|
82
|
+
class SecurityError(RunbooksException):
|
83
|
+
"""Security-related errors."""
|
84
|
+
|
85
|
+
def __init__(self, message: str, **kwargs):
|
86
|
+
super().__init__(
|
87
|
+
message, error_code="SECURITY_ERROR", documentation_url="https://cloudops.oceansoft.io/security/", **kwargs
|
88
|
+
)
|
89
|
+
|
90
|
+
|
91
|
+
class AWSServiceError(RunbooksException):
|
92
|
+
"""AWS service operation errors."""
|
93
|
+
|
94
|
+
def __init__(
|
95
|
+
self,
|
96
|
+
message: str,
|
97
|
+
service: str = "unknown",
|
98
|
+
operation: str = "unknown",
|
99
|
+
aws_error_code: Optional[str] = None,
|
100
|
+
**kwargs,
|
101
|
+
):
|
102
|
+
super().__init__(
|
103
|
+
message,
|
104
|
+
error_code=f"AWS_{service.upper()}_ERROR",
|
105
|
+
documentation_url=f"https://cloudops.oceansoft.io/aws/{service}/",
|
106
|
+
**kwargs,
|
107
|
+
)
|
108
|
+
self.service = service
|
109
|
+
self.operation = operation
|
110
|
+
self.aws_error_code = aws_error_code
|
111
|
+
|
112
|
+
|
113
|
+
class EnterpriseErrorHandler:
|
114
|
+
"""Enterprise-grade error handler with comprehensive error management."""
|
115
|
+
|
116
|
+
def __init__(self, console: Optional[Console] = None):
|
117
|
+
"""Initialize error handler."""
|
118
|
+
self.console = console or Console()
|
119
|
+
self.error_registry: Dict[str, Dict[str, Any]] = {}
|
120
|
+
self._setup_error_registry()
|
121
|
+
|
122
|
+
def _setup_error_registry(self) -> None:
|
123
|
+
"""Setup common error patterns and their solutions."""
|
124
|
+
self.error_registry = {
|
125
|
+
"NoCredentialsError": {
|
126
|
+
"title": "AWS Credentials Not Found",
|
127
|
+
"suggestion": "Configure AWS credentials using 'aws configure' or set environment variables",
|
128
|
+
"commands": ["aws configure", "export AWS_PROFILE=your-profile"],
|
129
|
+
"documentation": "https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html",
|
130
|
+
},
|
131
|
+
"UnauthorizedOperation": {
|
132
|
+
"title": "AWS Permission Denied",
|
133
|
+
"suggestion": "Check your AWS IAM permissions for the required service",
|
134
|
+
"commands": ["aws sts get-caller-identity", "runbooks security assess --profile your-profile"],
|
135
|
+
"documentation": "https://cloudops.oceansoft.io/security/permissions/",
|
136
|
+
},
|
137
|
+
"ProfileNotFound": {
|
138
|
+
"title": "AWS Profile Not Found",
|
139
|
+
"suggestion": "Verify the AWS profile exists in ~/.aws/credentials or ~/.aws/config",
|
140
|
+
"commands": ["aws configure list-profiles", "runbooks --profile default"],
|
141
|
+
"documentation": "https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html",
|
142
|
+
},
|
143
|
+
"InvalidRegion": {
|
144
|
+
"title": "Invalid AWS Region",
|
145
|
+
"suggestion": "Use a valid AWS region (e.g., us-east-1, eu-west-1)",
|
146
|
+
"commands": ["aws ec2 describe-regions", "runbooks --region us-east-1"],
|
147
|
+
"documentation": "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html",
|
148
|
+
},
|
149
|
+
"FileNotFoundError": {
|
150
|
+
"title": "Configuration File Not Found",
|
151
|
+
"suggestion": "Initialize configuration or check file path",
|
152
|
+
"commands": ["runbooks --help", "runbooks cfat assess --create-config"],
|
153
|
+
"documentation": "https://cloudops.oceansoft.io/configuration/",
|
154
|
+
},
|
155
|
+
}
|
156
|
+
|
157
|
+
def handle_exception(
|
158
|
+
self,
|
159
|
+
exc: Exception,
|
160
|
+
command_name: Optional[str] = None,
|
161
|
+
context: Optional[Dict[str, Any]] = None,
|
162
|
+
show_traceback: bool = False,
|
163
|
+
) -> None:
|
164
|
+
"""
|
165
|
+
Handle exception with enterprise-grade error reporting.
|
166
|
+
|
167
|
+
Args:
|
168
|
+
exc: Exception to handle
|
169
|
+
command_name: Name of command that failed
|
170
|
+
context: Additional context information
|
171
|
+
show_traceback: Whether to show full traceback
|
172
|
+
"""
|
173
|
+
error_info = self._analyze_exception(exc, context)
|
174
|
+
|
175
|
+
# Log structured error for monitoring
|
176
|
+
logger.error(
|
177
|
+
"Enterprise error handler activated",
|
178
|
+
error_type=type(exc).__name__,
|
179
|
+
error_code=getattr(exc, "error_code", "UNKNOWN"),
|
180
|
+
command=command_name,
|
181
|
+
context=context,
|
182
|
+
error_message=str(exc),
|
183
|
+
)
|
184
|
+
|
185
|
+
# Display user-friendly error
|
186
|
+
self._display_user_error(error_info, show_traceback)
|
187
|
+
|
188
|
+
# Save error details for support
|
189
|
+
self._save_error_details(exc, error_info, context)
|
190
|
+
|
191
|
+
def _analyze_exception(self, exc: Exception, context: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
192
|
+
"""Analyze exception and determine appropriate response."""
|
193
|
+
exc_name = type(exc).__name__
|
194
|
+
exc_str = str(exc)
|
195
|
+
|
196
|
+
# Check for known patterns
|
197
|
+
error_pattern = None
|
198
|
+
for pattern, info in self.error_registry.items():
|
199
|
+
if pattern in exc_name or pattern.lower() in exc_str.lower():
|
200
|
+
error_pattern = info
|
201
|
+
break
|
202
|
+
|
203
|
+
# Handle RunbooksException instances
|
204
|
+
if isinstance(exc, RunbooksException):
|
205
|
+
return {
|
206
|
+
"title": exc.error_code.replace("_", " ").title(),
|
207
|
+
"message": str(exc),
|
208
|
+
"suggestion": exc.suggestion,
|
209
|
+
"documentation": exc.documentation_url,
|
210
|
+
"context": exc.context,
|
211
|
+
"commands": [],
|
212
|
+
"error_code": exc.error_code,
|
213
|
+
}
|
214
|
+
|
215
|
+
# Use pattern if found
|
216
|
+
if error_pattern:
|
217
|
+
return {
|
218
|
+
**error_pattern,
|
219
|
+
"message": str(exc),
|
220
|
+
"context": context or {},
|
221
|
+
"error_code": exc_name,
|
222
|
+
}
|
223
|
+
|
224
|
+
# Generic error handling
|
225
|
+
return {
|
226
|
+
"title": "Unexpected Error",
|
227
|
+
"message": str(exc),
|
228
|
+
"suggestion": "This appears to be an unexpected error. Please check the logs for details.",
|
229
|
+
"commands": ["runbooks --debug", "Check logs in ~/.runbooks/logs/"],
|
230
|
+
"documentation": "https://cloudops.oceansoft.io/troubleshooting/",
|
231
|
+
"context": context or {},
|
232
|
+
"error_code": exc_name,
|
233
|
+
}
|
234
|
+
|
235
|
+
def _display_user_error(self, error_info: Dict[str, Any], show_traceback: bool = False) -> None:
|
236
|
+
"""Display user-friendly error information."""
|
237
|
+
if _HAS_RICH:
|
238
|
+
self._display_rich_error(error_info, show_traceback)
|
239
|
+
else:
|
240
|
+
self._display_plain_error(error_info, show_traceback)
|
241
|
+
|
242
|
+
def _display_rich_error(self, error_info: Dict[str, Any], show_traceback: bool = False) -> None:
|
243
|
+
"""Display error using Rich formatting."""
|
244
|
+
# Main error panel
|
245
|
+
error_text = Text()
|
246
|
+
error_text.append("ā ", style="red bold")
|
247
|
+
error_text.append(error_info["title"], style="red bold")
|
248
|
+
error_text.append(f"\n\n{error_info['message']}", style="red")
|
249
|
+
|
250
|
+
if error_info.get("suggestion"):
|
251
|
+
error_text.append(f"\n\nš” Suggestion: {error_info['suggestion']}", style="yellow")
|
252
|
+
|
253
|
+
panel = Panel(
|
254
|
+
error_text,
|
255
|
+
title="CloudOps Runbooks Error",
|
256
|
+
title_align="left",
|
257
|
+
border_style="red",
|
258
|
+
padding=(1, 2),
|
259
|
+
)
|
260
|
+
self.console.print(panel)
|
261
|
+
|
262
|
+
# Commands to try
|
263
|
+
if error_info.get("commands"):
|
264
|
+
self.console.print("\nš§ Try these commands:", style="cyan bold")
|
265
|
+
for cmd in error_info["commands"]:
|
266
|
+
self.console.print(f" {cmd}", style="cyan")
|
267
|
+
|
268
|
+
# Documentation link
|
269
|
+
if error_info.get("documentation"):
|
270
|
+
self.console.print(f"\nš Documentation: {error_info['documentation']}", style="blue underline")
|
271
|
+
|
272
|
+
# Error code for support
|
273
|
+
if error_info.get("error_code"):
|
274
|
+
self.console.print(f"\nš·ļø Error Code: {error_info['error_code']}", style="dim")
|
275
|
+
|
276
|
+
# Traceback if requested
|
277
|
+
if show_traceback:
|
278
|
+
self.console.print("\nš Traceback:", style="dim")
|
279
|
+
self.console.print(traceback.format_exc(), style="dim")
|
280
|
+
|
281
|
+
def _display_plain_error(self, error_info: Dict[str, Any], show_traceback: bool = False) -> None:
|
282
|
+
"""Display error using plain text formatting."""
|
283
|
+
print(f"\nā {error_info['title']}")
|
284
|
+
print(f"\n{error_info['message']}")
|
285
|
+
|
286
|
+
if error_info.get("suggestion"):
|
287
|
+
print(f"\nš” Suggestion: {error_info['suggestion']}")
|
288
|
+
|
289
|
+
if error_info.get("commands"):
|
290
|
+
print(f"\nš§ Try these commands:")
|
291
|
+
for cmd in error_info["commands"]:
|
292
|
+
print(f" {cmd}")
|
293
|
+
|
294
|
+
if error_info.get("documentation"):
|
295
|
+
print(f"\nš Documentation: {error_info['documentation']}")
|
296
|
+
|
297
|
+
if error_info.get("error_code"):
|
298
|
+
print(f"\nš·ļø Error Code: {error_info['error_code']}")
|
299
|
+
|
300
|
+
if show_traceback:
|
301
|
+
print(f"\nš Traceback:")
|
302
|
+
print(traceback.format_exc())
|
303
|
+
|
304
|
+
def _save_error_details(
|
305
|
+
self, exc: Exception, error_info: Dict[str, Any], context: Optional[Dict[str, Any]] = None
|
306
|
+
) -> None:
|
307
|
+
"""Save error details for support and debugging."""
|
308
|
+
try:
|
309
|
+
error_log_dir = Path.home() / ".runbooks" / "logs" / "errors"
|
310
|
+
error_log_dir.mkdir(parents=True, exist_ok=True)
|
311
|
+
|
312
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
313
|
+
error_file = error_log_dir / f"error_{timestamp}.json"
|
314
|
+
|
315
|
+
error_data = {
|
316
|
+
"timestamp": datetime.utcnow().isoformat(),
|
317
|
+
"error_type": type(exc).__name__,
|
318
|
+
"error_message": str(exc),
|
319
|
+
"error_info": error_info,
|
320
|
+
"context": context,
|
321
|
+
"traceback": traceback.format_exc(),
|
322
|
+
"system_info": {
|
323
|
+
"python_version": sys.version,
|
324
|
+
"platform": sys.platform,
|
325
|
+
},
|
326
|
+
}
|
327
|
+
|
328
|
+
import json
|
329
|
+
|
330
|
+
with open(error_file, "w") as f:
|
331
|
+
json.dump(error_data, f, indent=2, default=str)
|
332
|
+
|
333
|
+
logger.debug(f"Error details saved to {error_file}")
|
334
|
+
|
335
|
+
except Exception as save_error:
|
336
|
+
logger.error(f"Failed to save error details: {save_error}")
|
337
|
+
|
338
|
+
|
339
|
+
def create_user_friendly_error(
|
340
|
+
message: str,
|
341
|
+
error_type: Type[Exception] = RunbooksException,
|
342
|
+
suggestion: Optional[str] = None,
|
343
|
+
commands: Optional[List[str]] = None,
|
344
|
+
**kwargs,
|
345
|
+
) -> Exception:
|
346
|
+
"""
|
347
|
+
Create a user-friendly error with actionable guidance.
|
348
|
+
|
349
|
+
Args:
|
350
|
+
message: Error message
|
351
|
+
error_type: Type of exception to create
|
352
|
+
suggestion: Suggested solution
|
353
|
+
commands: List of commands to try
|
354
|
+
**kwargs: Additional arguments for exception
|
355
|
+
|
356
|
+
Returns:
|
357
|
+
Configured exception instance
|
358
|
+
"""
|
359
|
+
if issubclass(error_type, RunbooksException):
|
360
|
+
return error_type(message=message, suggestion=suggestion, **kwargs)
|
361
|
+
else:
|
362
|
+
return error_type(message)
|
363
|
+
|
364
|
+
|
365
|
+
# Global error handler instance
|
366
|
+
_global_error_handler: Optional[EnterpriseErrorHandler] = None
|
367
|
+
|
368
|
+
|
369
|
+
def get_error_handler() -> EnterpriseErrorHandler:
|
370
|
+
"""Get global error handler instance."""
|
371
|
+
global _global_error_handler
|
372
|
+
if _global_error_handler is None:
|
373
|
+
_global_error_handler = EnterpriseErrorHandler()
|
374
|
+
return _global_error_handler
|
375
|
+
|
376
|
+
|
377
|
+
def handle_cli_exception(exc: Exception, command_name: Optional[str] = None, debug: bool = False) -> None:
|
378
|
+
"""
|
379
|
+
Handle CLI exception with proper error reporting.
|
380
|
+
|
381
|
+
Args:
|
382
|
+
exc: Exception to handle
|
383
|
+
command_name: Name of CLI command that failed
|
384
|
+
debug: Whether to show debug information
|
385
|
+
"""
|
386
|
+
handler = get_error_handler()
|
387
|
+
handler.handle_exception(
|
388
|
+
exc,
|
389
|
+
command_name=command_name,
|
390
|
+
show_traceback=debug,
|
391
|
+
)
|
392
|
+
|
393
|
+
|
394
|
+
def enterprise_exception_handler(func):
|
395
|
+
"""Decorator for enterprise exception handling."""
|
396
|
+
from functools import wraps
|
397
|
+
|
398
|
+
@wraps(func)
|
399
|
+
def wrapper(*args, **kwargs):
|
400
|
+
try:
|
401
|
+
return func(*args, **kwargs)
|
402
|
+
except Exception as e:
|
403
|
+
handler = get_error_handler()
|
404
|
+
handler.handle_exception(
|
405
|
+
e,
|
406
|
+
command_name=func.__name__,
|
407
|
+
context={"args": str(args), "kwargs": str(kwargs)},
|
408
|
+
)
|
409
|
+
raise
|
410
|
+
|
411
|
+
return wrapper
|