runbooks 1.0.3__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 +10 -5
- 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.3.dist-info → runbooks-1.1.0.dist-info}/METADATA +1 -1
- {runbooks-1.0.3.dist-info → runbooks-1.1.0.dist-info}/RECORD +47 -31
- {runbooks-1.0.3.dist-info → runbooks-1.1.0.dist-info}/WHEEL +0 -0
- {runbooks-1.0.3.dist-info → runbooks-1.1.0.dist-info}/entry_points.txt +0 -0
- {runbooks-1.0.3.dist-info → runbooks-1.1.0.dist-info}/licenses/LICENSE +0 -0
- {runbooks-1.0.3.dist-info → runbooks-1.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,186 @@
|
|
1
|
+
"""
|
2
|
+
Lazy Loading Architecture for Performance Optimization
|
3
|
+
|
4
|
+
This module implements deferred initialization to eliminate startup overhead
|
5
|
+
for basic CLI operations like --help and --version.
|
6
|
+
|
7
|
+
Performance Goals:
|
8
|
+
- Basic CLI operations < 0.5s
|
9
|
+
- Defer AWS/MCP initialization until needed
|
10
|
+
- Clean startup without warning pollution
|
11
|
+
"""
|
12
|
+
|
13
|
+
import threading
|
14
|
+
from typing import Any, Callable, Optional, Dict
|
15
|
+
from functools import wraps
|
16
|
+
import importlib
|
17
|
+
import sys
|
18
|
+
|
19
|
+
|
20
|
+
class LazyLoader:
|
21
|
+
"""Thread-safe lazy loader for expensive imports and initializations."""
|
22
|
+
|
23
|
+
def __init__(self):
|
24
|
+
self._cache: Dict[str, Any] = {}
|
25
|
+
self._lock = threading.Lock()
|
26
|
+
|
27
|
+
def get_or_load(self, key: str, loader_func: Callable[[], Any]) -> Any:
|
28
|
+
"""Get cached value or load it if not present."""
|
29
|
+
if key in self._cache:
|
30
|
+
return self._cache[key]
|
31
|
+
|
32
|
+
with self._lock:
|
33
|
+
# Double-check pattern
|
34
|
+
if key in self._cache:
|
35
|
+
return self._cache[key]
|
36
|
+
|
37
|
+
self._cache[key] = loader_func()
|
38
|
+
return self._cache[key]
|
39
|
+
|
40
|
+
|
41
|
+
# Global lazy loader instance
|
42
|
+
_lazy_loader = LazyLoader()
|
43
|
+
|
44
|
+
|
45
|
+
def lazy_aws_session():
|
46
|
+
"""Lazy load AWS session creation."""
|
47
|
+
def _load_aws():
|
48
|
+
import boto3
|
49
|
+
from runbooks.common.profile_utils import create_management_session
|
50
|
+
return create_management_session()
|
51
|
+
|
52
|
+
return _lazy_loader.get_or_load("aws_session", _load_aws)
|
53
|
+
|
54
|
+
|
55
|
+
def lazy_mcp_validator():
|
56
|
+
"""Lazy load MCP validator."""
|
57
|
+
def _load_mcp():
|
58
|
+
try:
|
59
|
+
from runbooks.finops.embedded_mcp_validator import FinOpsMCPValidator
|
60
|
+
return FinOpsMCPValidator()
|
61
|
+
except ImportError:
|
62
|
+
return None
|
63
|
+
|
64
|
+
return _lazy_loader.get_or_load("mcp_validator", _load_mcp)
|
65
|
+
|
66
|
+
|
67
|
+
def lazy_rich_console():
|
68
|
+
"""Lazy load Rich console."""
|
69
|
+
def _load_rich():
|
70
|
+
try:
|
71
|
+
from rich.console import Console
|
72
|
+
return Console()
|
73
|
+
except ImportError:
|
74
|
+
# Fallback console
|
75
|
+
class SimpleConsole:
|
76
|
+
def print(self, *args, **kwargs):
|
77
|
+
print(*args)
|
78
|
+
return SimpleConsole()
|
79
|
+
|
80
|
+
return _lazy_loader.get_or_load("rich_console", _load_rich)
|
81
|
+
|
82
|
+
|
83
|
+
def lazy_performance_monitor():
|
84
|
+
"""Lazy load performance monitoring."""
|
85
|
+
def _load_monitor():
|
86
|
+
from runbooks.common.performance_monitor import get_performance_benchmark
|
87
|
+
return get_performance_benchmark
|
88
|
+
|
89
|
+
return _lazy_loader.get_or_load("performance_monitor", _load_monitor)
|
90
|
+
|
91
|
+
|
92
|
+
def lazy_pricing_api():
|
93
|
+
"""Lazy load pricing API without startup warnings."""
|
94
|
+
def _load_pricing():
|
95
|
+
try:
|
96
|
+
from runbooks.common.aws_pricing_api import PricingAPI
|
97
|
+
return PricingAPI()
|
98
|
+
except Exception:
|
99
|
+
# Return fallback pricing without warnings during basic operations
|
100
|
+
class FallbackPricing:
|
101
|
+
def get_nat_gateway_price(self, region="us-east-1"):
|
102
|
+
return 32.4 # Standard fallback rate
|
103
|
+
return FallbackPricing()
|
104
|
+
|
105
|
+
return _lazy_loader.get_or_load("pricing_api", _load_pricing)
|
106
|
+
|
107
|
+
|
108
|
+
def lazy_inventory_collector():
|
109
|
+
"""Lazy load inventory collector."""
|
110
|
+
def _load_collector():
|
111
|
+
from runbooks.inventory.core.collector import InventoryCollector
|
112
|
+
return InventoryCollector
|
113
|
+
|
114
|
+
return _lazy_loader.get_or_load("inventory_collector", _load_collector)
|
115
|
+
|
116
|
+
|
117
|
+
def lazy_import(module_name: str, attribute: Optional[str] = None):
|
118
|
+
"""Lazy import a module or module attribute."""
|
119
|
+
cache_key = f"{module_name}.{attribute}" if attribute else module_name
|
120
|
+
|
121
|
+
def _load_module():
|
122
|
+
module = importlib.import_module(module_name)
|
123
|
+
if attribute:
|
124
|
+
return getattr(module, attribute)
|
125
|
+
return module
|
126
|
+
|
127
|
+
return _lazy_loader.get_or_load(cache_key, _load_module)
|
128
|
+
|
129
|
+
|
130
|
+
def requires_aws(func):
|
131
|
+
"""Decorator to ensure AWS session is loaded before function execution."""
|
132
|
+
@wraps(func)
|
133
|
+
def wrapper(*args, **kwargs):
|
134
|
+
lazy_aws_session() # Ensure AWS is loaded
|
135
|
+
return func(*args, **kwargs)
|
136
|
+
return wrapper
|
137
|
+
|
138
|
+
|
139
|
+
def requires_mcp(func):
|
140
|
+
"""Decorator to ensure MCP validator is loaded before function execution."""
|
141
|
+
@wraps(func)
|
142
|
+
def wrapper(*args, **kwargs):
|
143
|
+
lazy_mcp_validator() # Ensure MCP is loaded
|
144
|
+
return func(*args, **kwargs)
|
145
|
+
return wrapper
|
146
|
+
|
147
|
+
|
148
|
+
def fast_startup_mode() -> bool:
|
149
|
+
"""Check if we're in fast startup mode (basic operations only)."""
|
150
|
+
import sys
|
151
|
+
|
152
|
+
# Basic operations that should be fast
|
153
|
+
fast_operations = {'--help', '-h', '--version', '-V', 'help'}
|
154
|
+
|
155
|
+
# Check if any CLI args match fast operations
|
156
|
+
return any(arg in fast_operations for arg in sys.argv)
|
157
|
+
|
158
|
+
|
159
|
+
def clear_lazy_cache():
|
160
|
+
"""Clear the lazy loading cache (useful for testing)."""
|
161
|
+
global _lazy_loader
|
162
|
+
_lazy_loader._cache.clear()
|
163
|
+
|
164
|
+
|
165
|
+
# Utility functions for deferred initialization
|
166
|
+
def defer_expensive_imports():
|
167
|
+
"""
|
168
|
+
Replace expensive imports with lazy alternatives.
|
169
|
+
Call this early in main.py to optimize startup.
|
170
|
+
"""
|
171
|
+
# Only defer if we're not in fast startup mode
|
172
|
+
if fast_startup_mode():
|
173
|
+
return
|
174
|
+
|
175
|
+
# Defer expensive imports for basic operations
|
176
|
+
modules_to_defer = [
|
177
|
+
'runbooks.finops.embedded_mcp_validator',
|
178
|
+
'runbooks.common.aws_pricing_api',
|
179
|
+
'runbooks.inventory.core.collector',
|
180
|
+
'runbooks.cfat.runner',
|
181
|
+
]
|
182
|
+
|
183
|
+
for module in modules_to_defer:
|
184
|
+
if module in sys.modules:
|
185
|
+
# Replace with lazy loader
|
186
|
+
sys.modules[module] = lazy_import(module)
|
@@ -0,0 +1,378 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Module CLI Base Class for CloudOps Runbooks - Enterprise Standardization
|
4
|
+
|
5
|
+
Provides consistent CLI implementation patterns across all runbooks modules.
|
6
|
+
Eliminates code duplication and ensures uniform UX across inventory, operate,
|
7
|
+
security, cfat, vpc, remediation, and sre modules.
|
8
|
+
|
9
|
+
Author: CloudOps Runbooks Team
|
10
|
+
Version: 1.0.0 - CLI Standardization Framework
|
11
|
+
"""
|
12
|
+
|
13
|
+
import abc
|
14
|
+
import sys
|
15
|
+
from typing import Dict, Any, Optional, List, Callable
|
16
|
+
from dataclasses import dataclass
|
17
|
+
|
18
|
+
import click
|
19
|
+
from rich.console import Console
|
20
|
+
from rich.panel import Panel
|
21
|
+
from rich.table import Table
|
22
|
+
|
23
|
+
from runbooks.common.rich_utils import (
|
24
|
+
console, print_header, print_success, print_error, print_warning, print_info,
|
25
|
+
create_table, create_progress_bar, format_cost, STATUS_INDICATORS
|
26
|
+
)
|
27
|
+
from runbooks.common.cli_decorators import (
|
28
|
+
common_aws_options, rich_output_options, enterprise_safety_options
|
29
|
+
)
|
30
|
+
from runbooks.common.error_handling import handle_aws_errors, handle_validation_errors
|
31
|
+
from runbooks.common.profile_utils import get_profile_for_operation, validate_profile_access_decorator
|
32
|
+
from runbooks.common.business_logic import BusinessMetrics, OptimizationResult, UniversalBusinessLogic
|
33
|
+
|
34
|
+
|
35
|
+
@dataclass
|
36
|
+
class ModuleConfig:
|
37
|
+
"""Configuration for runbooks module CLI."""
|
38
|
+
name: str
|
39
|
+
version: str
|
40
|
+
description: str
|
41
|
+
primary_operation_type: str # For profile selection: "management", "operational", "cost"
|
42
|
+
performance_target_seconds: int = 30 # Module-specific performance target
|
43
|
+
supports_multi_account: bool = True
|
44
|
+
supports_export: bool = True
|
45
|
+
default_region: str = "us-east-1"
|
46
|
+
|
47
|
+
|
48
|
+
class ModuleCLIBase(abc.ABC):
|
49
|
+
"""
|
50
|
+
Base class for standardized runbooks module CLI implementation.
|
51
|
+
|
52
|
+
Provides consistent patterns for:
|
53
|
+
- CLI option handling with enterprise decorators
|
54
|
+
- AWS profile management with 3-tier priority
|
55
|
+
- Rich CLI output with enterprise UX standards
|
56
|
+
- Error handling with user-friendly guidance
|
57
|
+
- Performance monitoring with module targets
|
58
|
+
- Business logic integration with standardized metrics
|
59
|
+
"""
|
60
|
+
|
61
|
+
def __init__(self, config: ModuleConfig):
|
62
|
+
"""Initialize module CLI with configuration."""
|
63
|
+
self.config = config
|
64
|
+
self.console = console
|
65
|
+
self.business_logic = UniversalBusinessLogic()
|
66
|
+
self._session_data = {}
|
67
|
+
|
68
|
+
def print_module_header(self):
|
69
|
+
"""Print standardized module header with Rich formatting."""
|
70
|
+
print_header(self.config.name, self.config.version)
|
71
|
+
self.console.print(
|
72
|
+
Panel(
|
73
|
+
f"[cyan]{self.config.description}[/cyan]",
|
74
|
+
title=f"🚀 {self.config.name} Module",
|
75
|
+
border_style="blue"
|
76
|
+
)
|
77
|
+
)
|
78
|
+
|
79
|
+
def validate_prerequisites(self, profile: Optional[str] = None) -> bool:
|
80
|
+
"""
|
81
|
+
Validate module prerequisites before execution.
|
82
|
+
|
83
|
+
Args:
|
84
|
+
profile: AWS profile to validate
|
85
|
+
|
86
|
+
Returns:
|
87
|
+
True if all prerequisites are met
|
88
|
+
"""
|
89
|
+
try:
|
90
|
+
# Validate AWS profile access
|
91
|
+
selected_profile = get_profile_for_operation(
|
92
|
+
self.config.primary_operation_type,
|
93
|
+
profile
|
94
|
+
)
|
95
|
+
|
96
|
+
if not validate_profile_access(selected_profile, self.config.name):
|
97
|
+
return False
|
98
|
+
|
99
|
+
self._session_data['validated_profile'] = selected_profile
|
100
|
+
return True
|
101
|
+
|
102
|
+
except Exception as e:
|
103
|
+
print_error(f"Prerequisites validation failed: {str(e)}")
|
104
|
+
return False
|
105
|
+
|
106
|
+
@abc.abstractmethod
|
107
|
+
def execute_primary_operation(self, **kwargs) -> Dict[str, Any]:
|
108
|
+
"""
|
109
|
+
Execute the primary module operation.
|
110
|
+
|
111
|
+
Must be implemented by each module to provide core functionality.
|
112
|
+
|
113
|
+
Returns:
|
114
|
+
Dictionary containing operation results
|
115
|
+
"""
|
116
|
+
pass
|
117
|
+
|
118
|
+
@abc.abstractmethod
|
119
|
+
def format_results_for_display(self, results: Dict[str, Any]) -> None:
|
120
|
+
"""
|
121
|
+
Format and display results using Rich CLI standards.
|
122
|
+
|
123
|
+
Args:
|
124
|
+
results: Operation results from execute_primary_operation
|
125
|
+
"""
|
126
|
+
pass
|
127
|
+
|
128
|
+
def create_results_table(self, title: str, data: List[Dict[str, Any]],
|
129
|
+
columns: List[str]) -> Table:
|
130
|
+
"""
|
131
|
+
Create standardized results table with Rich formatting.
|
132
|
+
|
133
|
+
Args:
|
134
|
+
title: Table title
|
135
|
+
data: List of dictionaries containing row data
|
136
|
+
columns: List of column names
|
137
|
+
|
138
|
+
Returns:
|
139
|
+
Configured Rich Table object
|
140
|
+
"""
|
141
|
+
table = create_table(title=title, columns=[
|
142
|
+
{"name": col, "style": "cyan" if "name" in col.lower() else "default"}
|
143
|
+
for col in columns
|
144
|
+
])
|
145
|
+
|
146
|
+
for row in data:
|
147
|
+
table.add_row(*[str(row.get(col, "N/A")) for col in columns])
|
148
|
+
|
149
|
+
return table
|
150
|
+
|
151
|
+
def export_results(self, results: Dict[str, Any], format_type: str = "json",
|
152
|
+
output_path: Optional[str] = None) -> bool:
|
153
|
+
"""
|
154
|
+
Export results in specified format.
|
155
|
+
|
156
|
+
Args:
|
157
|
+
results: Results to export
|
158
|
+
format_type: Export format (json, csv, markdown, pdf)
|
159
|
+
output_path: Optional custom output path
|
160
|
+
|
161
|
+
Returns:
|
162
|
+
True if export successful
|
163
|
+
"""
|
164
|
+
if not self.config.supports_export:
|
165
|
+
print_warning(f"{self.config.name} module does not support export")
|
166
|
+
return False
|
167
|
+
|
168
|
+
try:
|
169
|
+
export_handler = self.business_logic.create_export_handler(
|
170
|
+
format_type, output_path
|
171
|
+
)
|
172
|
+
|
173
|
+
success = export_handler.export_data(results)
|
174
|
+
|
175
|
+
if success:
|
176
|
+
print_success(f"Results exported to {export_handler.output_path}")
|
177
|
+
else:
|
178
|
+
print_error(f"Failed to export results in {format_type} format")
|
179
|
+
|
180
|
+
return success
|
181
|
+
|
182
|
+
except Exception as e:
|
183
|
+
print_error(f"Export failed: {str(e)}")
|
184
|
+
return False
|
185
|
+
|
186
|
+
def calculate_business_metrics(self, results: Dict[str, Any]) -> BusinessMetrics:
|
187
|
+
"""
|
188
|
+
Calculate business metrics from operation results.
|
189
|
+
|
190
|
+
Args:
|
191
|
+
results: Operation results
|
192
|
+
|
193
|
+
Returns:
|
194
|
+
BusinessMetrics with calculated values
|
195
|
+
"""
|
196
|
+
return self.business_logic.calculate_business_impact(results)
|
197
|
+
|
198
|
+
def create_standard_cli_command(self, command_name: str) -> Callable:
|
199
|
+
"""
|
200
|
+
Create standardized CLI command with common options.
|
201
|
+
|
202
|
+
Args:
|
203
|
+
command_name: Name of the CLI command
|
204
|
+
|
205
|
+
Returns:
|
206
|
+
Decorated click command function
|
207
|
+
"""
|
208
|
+
@click.command(name=command_name)
|
209
|
+
@common_aws_options
|
210
|
+
@rich_output_options
|
211
|
+
@enterprise_safety_options
|
212
|
+
@handle_aws_errors(module_name=self.config.name)
|
213
|
+
@handle_validation_errors
|
214
|
+
@validate_profile_access_decorator(operation_type=self.config.primary_operation_type)
|
215
|
+
def standardized_command(profile=None, region=None, dry_run=True,
|
216
|
+
export_format=None, quiet=False, **kwargs):
|
217
|
+
"""Execute standardized module operation with enterprise safety."""
|
218
|
+
|
219
|
+
# Module header
|
220
|
+
if not quiet:
|
221
|
+
self.print_module_header()
|
222
|
+
|
223
|
+
# Validate prerequisites
|
224
|
+
if not self.validate_prerequisites(profile):
|
225
|
+
sys.exit(1)
|
226
|
+
|
227
|
+
# Execute with performance monitoring
|
228
|
+
try:
|
229
|
+
with create_progress_bar() as progress:
|
230
|
+
task = progress.add_task(
|
231
|
+
f"[cyan]Executing {self.config.name} operation...",
|
232
|
+
total=100
|
233
|
+
)
|
234
|
+
|
235
|
+
# Execute primary operation
|
236
|
+
results = self.execute_primary_operation(
|
237
|
+
profile=profile,
|
238
|
+
region=region,
|
239
|
+
dry_run=dry_run,
|
240
|
+
progress=progress,
|
241
|
+
task=task,
|
242
|
+
**kwargs
|
243
|
+
)
|
244
|
+
|
245
|
+
progress.update(task, completed=100)
|
246
|
+
|
247
|
+
# Display results
|
248
|
+
if not quiet:
|
249
|
+
self.format_results_for_display(results)
|
250
|
+
|
251
|
+
# Calculate business metrics
|
252
|
+
metrics = self.calculate_business_metrics(results)
|
253
|
+
|
254
|
+
if not quiet and metrics.annual_savings > 0:
|
255
|
+
print_success(
|
256
|
+
f"💰 Potential annual savings: {format_cost(metrics.annual_savings)}"
|
257
|
+
)
|
258
|
+
|
259
|
+
# Export if requested
|
260
|
+
if export_format:
|
261
|
+
self.export_results(results, export_format)
|
262
|
+
|
263
|
+
return results
|
264
|
+
|
265
|
+
except Exception as e:
|
266
|
+
print_error(f"Operation failed: {str(e)}")
|
267
|
+
if not quiet:
|
268
|
+
console.print_exception()
|
269
|
+
sys.exit(1)
|
270
|
+
|
271
|
+
return standardized_command
|
272
|
+
|
273
|
+
|
274
|
+
class AnalysisModuleCLI(ModuleCLIBase):
|
275
|
+
"""
|
276
|
+
Specialized base class for analysis modules (finops, inventory, cfat).
|
277
|
+
|
278
|
+
Provides additional patterns for:
|
279
|
+
- Multi-account analysis operations
|
280
|
+
- Cost calculation and savings projections
|
281
|
+
- Executive reporting capabilities
|
282
|
+
- Trend analysis and recommendations
|
283
|
+
"""
|
284
|
+
|
285
|
+
def create_executive_summary(self, results: Dict[str, Any]) -> Panel:
|
286
|
+
"""Create executive summary panel for business stakeholders."""
|
287
|
+
metrics = self.calculate_business_metrics(results)
|
288
|
+
|
289
|
+
summary_content = []
|
290
|
+
|
291
|
+
if metrics.annual_savings > 0:
|
292
|
+
summary_content.append(f"💰 Annual Savings Potential: {format_cost(metrics.annual_savings)}")
|
293
|
+
|
294
|
+
if metrics.roi_percentage > 0:
|
295
|
+
summary_content.append(f"📊 ROI: {metrics.roi_percentage:.1f}%")
|
296
|
+
|
297
|
+
if hasattr(metrics, 'resources_analyzed'):
|
298
|
+
summary_content.append(f"🔍 Resources Analyzed: {metrics.resources_analyzed:,}")
|
299
|
+
|
300
|
+
summary_content.append(f"⏱️ Execution Time: <{self.config.performance_target_seconds}s target")
|
301
|
+
summary_content.append(f"✅ Confidence: {metrics.confidence_level:.1f}%")
|
302
|
+
|
303
|
+
return Panel(
|
304
|
+
"\n".join(summary_content),
|
305
|
+
title="📈 Executive Summary",
|
306
|
+
border_style="green"
|
307
|
+
)
|
308
|
+
|
309
|
+
|
310
|
+
class OperationsModuleCLI(ModuleCLIBase):
|
311
|
+
"""
|
312
|
+
Specialized base class for operations modules (operate, security, remediation).
|
313
|
+
|
314
|
+
Provides additional patterns for:
|
315
|
+
- Resource modification operations with safety controls
|
316
|
+
- Multi-level approval workflows
|
317
|
+
- Rollback capabilities and audit trails
|
318
|
+
- Compliance validation and reporting
|
319
|
+
"""
|
320
|
+
|
321
|
+
def validate_operation_safety(self, operation: str, resources: List[str]) -> bool:
|
322
|
+
"""
|
323
|
+
Validate operation safety before execution.
|
324
|
+
|
325
|
+
Args:
|
326
|
+
operation: Operation type (start, stop, delete, modify)
|
327
|
+
resources: List of resource identifiers
|
328
|
+
|
329
|
+
Returns:
|
330
|
+
True if operation is safe to proceed
|
331
|
+
"""
|
332
|
+
# Always require explicit approval for destructive operations
|
333
|
+
destructive_operations = ['delete', 'terminate', 'remove', 'destroy']
|
334
|
+
|
335
|
+
if any(op in operation.lower() for op in destructive_operations):
|
336
|
+
print_warning(f"⚠️ Destructive operation requested: {operation}")
|
337
|
+
print_info(f"Resources affected: {len(resources)}")
|
338
|
+
|
339
|
+
if not click.confirm("Are you sure you want to proceed?"):
|
340
|
+
print_info("Operation cancelled by user")
|
341
|
+
return False
|
342
|
+
|
343
|
+
return True
|
344
|
+
|
345
|
+
def create_audit_trail(self, operation: str, results: Dict[str, Any]) -> str:
|
346
|
+
"""
|
347
|
+
Create audit trail entry for operations.
|
348
|
+
|
349
|
+
Args:
|
350
|
+
operation: Operation performed
|
351
|
+
results: Operation results
|
352
|
+
|
353
|
+
Returns:
|
354
|
+
Audit trail entry as JSON string
|
355
|
+
"""
|
356
|
+
import json
|
357
|
+
from datetime import datetime
|
358
|
+
|
359
|
+
audit_entry = {
|
360
|
+
"timestamp": datetime.now().isoformat(),
|
361
|
+
"module": self.config.name,
|
362
|
+
"operation": operation,
|
363
|
+
"user": self._session_data.get('validated_profile', 'unknown'),
|
364
|
+
"resources_affected": results.get('resources_affected', []),
|
365
|
+
"success": results.get('success', False),
|
366
|
+
"execution_time": results.get('execution_time_seconds', 0)
|
367
|
+
}
|
368
|
+
|
369
|
+
return json.dumps(audit_entry, indent=2)
|
370
|
+
|
371
|
+
|
372
|
+
# Export standardized classes for module implementations
|
373
|
+
__all__ = [
|
374
|
+
"ModuleConfig",
|
375
|
+
"ModuleCLIBase",
|
376
|
+
"AnalysisModuleCLI",
|
377
|
+
"OperationsModuleCLI"
|
378
|
+
]
|