runbooks 1.0.2__py3-none-any.whl → 1.1.0__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 +9 -4
- runbooks/__init__.py.backup +134 -0
- runbooks/__init___optimized.py +110 -0
- runbooks/cloudops/base.py +56 -3
- runbooks/cloudops/cost_optimizer.py +496 -42
- runbooks/common/aws_pricing.py +236 -80
- runbooks/common/business_logic.py +485 -0
- runbooks/common/cli_decorators.py +219 -0
- runbooks/common/error_handling.py +424 -0
- runbooks/common/lazy_loader.py +186 -0
- runbooks/common/module_cli_base.py +378 -0
- runbooks/common/performance_monitoring.py +512 -0
- runbooks/common/profile_utils.py +133 -6
- runbooks/enterprise/logging.py +30 -2
- runbooks/enterprise/validation.py +177 -0
- runbooks/finops/README.md +311 -236
- runbooks/finops/aws_client.py +1 -1
- runbooks/finops/business_case_config.py +723 -19
- runbooks/finops/cli.py +136 -0
- runbooks/finops/commvault_ec2_analysis.py +25 -9
- runbooks/finops/config.py +272 -0
- runbooks/finops/dashboard_runner.py +136 -23
- runbooks/finops/ebs_cost_optimizer.py +39 -40
- runbooks/finops/enhanced_trend_visualization.py +7 -2
- runbooks/finops/enterprise_wrappers.py +45 -18
- runbooks/finops/finops_dashboard.py +50 -25
- runbooks/finops/finops_scenarios.py +22 -7
- runbooks/finops/helpers.py +115 -2
- runbooks/finops/multi_dashboard.py +7 -5
- runbooks/finops/optimizer.py +97 -6
- runbooks/finops/scenario_cli_integration.py +247 -0
- runbooks/finops/scenarios.py +12 -1
- runbooks/finops/unlimited_scenarios.py +393 -0
- runbooks/finops/validation_framework.py +19 -7
- runbooks/finops/workspaces_analyzer.py +1 -5
- runbooks/inventory/mcp_inventory_validator.py +2 -1
- runbooks/main.py +132 -94
- runbooks/main_final.py +358 -0
- runbooks/main_minimal.py +84 -0
- runbooks/main_optimized.py +493 -0
- runbooks/main_ultra_minimal.py +47 -0
- {runbooks-1.0.2.dist-info → runbooks-1.1.0.dist-info}/METADATA +15 -15
- {runbooks-1.0.2.dist-info → runbooks-1.1.0.dist-info}/RECORD +47 -31
- {runbooks-1.0.2.dist-info → runbooks-1.1.0.dist-info}/WHEEL +0 -0
- {runbooks-1.0.2.dist-info → runbooks-1.1.0.dist-info}/entry_points.txt +0 -0
- {runbooks-1.0.2.dist-info → runbooks-1.1.0.dist-info}/licenses/LICENSE +0 -0
- {runbooks-1.0.2.dist-info → runbooks-1.1.0.dist-info}/top_level.txt +0 -0
runbooks/enterprise/logging.py
CHANGED
@@ -19,8 +19,35 @@ try:
|
|
19
19
|
from loguru import logger as loguru_logger
|
20
20
|
_HAS_LOGURU = True
|
21
21
|
except ImportError:
|
22
|
+
# Create a mock loguru-like object for compatibility
|
22
23
|
import logging
|
23
|
-
|
24
|
+
|
25
|
+
class MockLoguru:
|
26
|
+
def __init__(self):
|
27
|
+
self.logger = logging.getLogger(__name__)
|
28
|
+
|
29
|
+
def remove(self, *args, **kwargs):
|
30
|
+
pass # No-op for standard logging
|
31
|
+
|
32
|
+
def add(self, *args, **kwargs):
|
33
|
+
pass # No-op for standard logging
|
34
|
+
|
35
|
+
def bind(self, **kwargs):
|
36
|
+
return self # Return self for chaining
|
37
|
+
|
38
|
+
def info(self, message, *args, **kwargs):
|
39
|
+
self.logger.info(message, *args)
|
40
|
+
|
41
|
+
def warning(self, message, *args, **kwargs):
|
42
|
+
self.logger.warning(message, *args)
|
43
|
+
|
44
|
+
def error(self, message, *args, **kwargs):
|
45
|
+
self.logger.error(message, *args)
|
46
|
+
|
47
|
+
def debug(self, message, *args, **kwargs):
|
48
|
+
self.logger.debug(message, *args)
|
49
|
+
|
50
|
+
loguru_logger = MockLoguru()
|
24
51
|
_HAS_LOGURU = False
|
25
52
|
|
26
53
|
# Rich CLI integration
|
@@ -103,7 +130,8 @@ class EnterpriseRichLogger:
|
|
103
130
|
|
104
131
|
def _setup_loguru_logging(self, enable_console: bool, enable_file: bool, enable_audit: bool) -> None:
|
105
132
|
"""Setup Loguru-based logging."""
|
106
|
-
|
133
|
+
global loguru_logger
|
134
|
+
# Remove default handler (MockLoguru handles this gracefully)
|
107
135
|
loguru_logger.remove()
|
108
136
|
|
109
137
|
# Console handler
|
@@ -0,0 +1,177 @@
|
|
1
|
+
"""
|
2
|
+
Enterprise Validation Module - Enhanced Logging and Validation
|
3
|
+
|
4
|
+
This module provides enterprise-grade logging and validation capabilities
|
5
|
+
for the CloudOps Runbooks framework.
|
6
|
+
|
7
|
+
Features:
|
8
|
+
- Enhanced structured logging with performance monitoring
|
9
|
+
- Cross-module validation utilities
|
10
|
+
- Rich CLI integration with enterprise UX standards
|
11
|
+
- Enterprise compliance validation
|
12
|
+
"""
|
13
|
+
|
14
|
+
import logging
|
15
|
+
from typing import Any, Dict, List, Optional
|
16
|
+
from rich.console import Console
|
17
|
+
from rich.logging import RichHandler
|
18
|
+
|
19
|
+
# Rich console for enterprise logging
|
20
|
+
console = Console()
|
21
|
+
|
22
|
+
class EnhancedLogging:
|
23
|
+
"""Enhanced logging capabilities for enterprise operations."""
|
24
|
+
|
25
|
+
def __init__(self, module_name: str = "runbooks"):
|
26
|
+
self.module_name = module_name
|
27
|
+
self.logger = self._setup_enhanced_logger()
|
28
|
+
|
29
|
+
def _setup_enhanced_logger(self) -> logging.Logger:
|
30
|
+
"""Setup enhanced logger with Rich integration."""
|
31
|
+
logger = logging.getLogger(f"runbooks.{self.module_name}")
|
32
|
+
|
33
|
+
# Skip if already configured
|
34
|
+
if logger.handlers:
|
35
|
+
return logger
|
36
|
+
|
37
|
+
logger.setLevel(logging.INFO)
|
38
|
+
|
39
|
+
# Rich handler for beautiful console output
|
40
|
+
rich_handler = RichHandler(
|
41
|
+
console=console,
|
42
|
+
show_time=True,
|
43
|
+
show_path=True,
|
44
|
+
rich_tracebacks=True
|
45
|
+
)
|
46
|
+
|
47
|
+
formatter = logging.Formatter(
|
48
|
+
fmt="[%(name)s] %(message)s",
|
49
|
+
datefmt="[%X]"
|
50
|
+
)
|
51
|
+
rich_handler.setFormatter(formatter)
|
52
|
+
|
53
|
+
logger.addHandler(rich_handler)
|
54
|
+
logger.propagate = False
|
55
|
+
|
56
|
+
return logger
|
57
|
+
|
58
|
+
def info(self, message: str, **kwargs) -> None:
|
59
|
+
"""Log info message with Rich formatting."""
|
60
|
+
self.logger.info(message, **kwargs)
|
61
|
+
|
62
|
+
def warning(self, message: str, **kwargs) -> None:
|
63
|
+
"""Log warning message with Rich formatting."""
|
64
|
+
self.logger.warning(message, **kwargs)
|
65
|
+
|
66
|
+
def error(self, message: str, **kwargs) -> None:
|
67
|
+
"""Log error message with Rich formatting."""
|
68
|
+
self.logger.error(message, **kwargs)
|
69
|
+
|
70
|
+
def debug(self, message: str, **kwargs) -> None:
|
71
|
+
"""Log debug message with Rich formatting."""
|
72
|
+
self.logger.debug(message, **kwargs)
|
73
|
+
|
74
|
+
|
75
|
+
class ConfigValidator:
|
76
|
+
"""Configuration validation utilities."""
|
77
|
+
|
78
|
+
@staticmethod
|
79
|
+
def validate_aws_profile(profile: str) -> bool:
|
80
|
+
"""Validate AWS profile configuration."""
|
81
|
+
if not profile or not isinstance(profile, str):
|
82
|
+
return False
|
83
|
+
return len(profile.strip()) > 0
|
84
|
+
|
85
|
+
@staticmethod
|
86
|
+
def validate_region(region: str) -> bool:
|
87
|
+
"""Validate AWS region format."""
|
88
|
+
if not region or not isinstance(region, str):
|
89
|
+
return False
|
90
|
+
# Basic AWS region format validation
|
91
|
+
return len(region.split('-')) >= 3
|
92
|
+
|
93
|
+
|
94
|
+
class InputValidator:
|
95
|
+
"""Input validation utilities."""
|
96
|
+
|
97
|
+
@staticmethod
|
98
|
+
def validate_account_id(account_id: str) -> bool:
|
99
|
+
"""Validate AWS account ID format."""
|
100
|
+
if not account_id or not isinstance(account_id, str):
|
101
|
+
return False
|
102
|
+
return account_id.isdigit() and len(account_id) == 12
|
103
|
+
|
104
|
+
@staticmethod
|
105
|
+
def validate_instance_id(instance_id: str) -> bool:
|
106
|
+
"""Validate EC2 instance ID format."""
|
107
|
+
if not instance_id or not isinstance(instance_id, str):
|
108
|
+
return False
|
109
|
+
return instance_id.startswith('i-') and len(instance_id) >= 10
|
110
|
+
|
111
|
+
|
112
|
+
class TypeValidator:
|
113
|
+
"""Type validation utilities."""
|
114
|
+
|
115
|
+
@staticmethod
|
116
|
+
def validate_list_of_strings(value: Any) -> bool:
|
117
|
+
"""Validate that value is a list of strings."""
|
118
|
+
if not isinstance(value, list):
|
119
|
+
return False
|
120
|
+
return all(isinstance(item, str) for item in value)
|
121
|
+
|
122
|
+
@staticmethod
|
123
|
+
def validate_dict(value: Any, required_keys: Optional[List[str]] = None) -> bool:
|
124
|
+
"""Validate dictionary structure."""
|
125
|
+
if not isinstance(value, dict):
|
126
|
+
return False
|
127
|
+
|
128
|
+
if required_keys:
|
129
|
+
return all(key in value for key in required_keys)
|
130
|
+
|
131
|
+
return True
|
132
|
+
|
133
|
+
|
134
|
+
def validate_configuration(config: Dict[str, Any]) -> Dict[str, Any]:
|
135
|
+
"""Validate configuration dictionary."""
|
136
|
+
validator = ConfigValidator()
|
137
|
+
errors = []
|
138
|
+
|
139
|
+
# Validate profile if present
|
140
|
+
if 'profile' in config:
|
141
|
+
if not validator.validate_aws_profile(config['profile']):
|
142
|
+
errors.append("Invalid AWS profile configuration")
|
143
|
+
|
144
|
+
# Validate region if present
|
145
|
+
if 'region' in config:
|
146
|
+
if not validator.validate_region(config['region']):
|
147
|
+
errors.append("Invalid AWS region format")
|
148
|
+
|
149
|
+
return {
|
150
|
+
'valid': len(errors) == 0,
|
151
|
+
'errors': errors
|
152
|
+
}
|
153
|
+
|
154
|
+
|
155
|
+
def validate_user_input(user_input: Dict[str, Any]) -> Dict[str, Any]:
|
156
|
+
"""Validate user input data."""
|
157
|
+
validator = InputValidator()
|
158
|
+
errors = []
|
159
|
+
|
160
|
+
# Validate account ID if present
|
161
|
+
if 'account_id' in user_input:
|
162
|
+
if not validator.validate_account_id(user_input['account_id']):
|
163
|
+
errors.append("Invalid AWS account ID format")
|
164
|
+
|
165
|
+
# Validate instance ID if present
|
166
|
+
if 'instance_id' in user_input:
|
167
|
+
if not validator.validate_instance_id(user_input['instance_id']):
|
168
|
+
errors.append("Invalid EC2 instance ID format")
|
169
|
+
|
170
|
+
return {
|
171
|
+
'valid': len(errors) == 0,
|
172
|
+
'errors': errors
|
173
|
+
}
|
174
|
+
|
175
|
+
|
176
|
+
# Create default enhanced logger
|
177
|
+
enhanced_logger = EnhancedLogging("enterprise")
|