runbooks 1.0.3__py3-none-any.whl → 1.1.1__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.
Files changed (48) hide show
  1. runbooks/__init__.py +10 -5
  2. runbooks/__init__.py.backup +134 -0
  3. runbooks/__init___optimized.py +110 -0
  4. runbooks/cloudops/base.py +56 -3
  5. runbooks/cloudops/cost_optimizer.py +496 -42
  6. runbooks/common/aws_pricing.py +236 -80
  7. runbooks/common/business_logic.py +485 -0
  8. runbooks/common/cli_decorators.py +219 -0
  9. runbooks/common/error_handling.py +424 -0
  10. runbooks/common/lazy_loader.py +186 -0
  11. runbooks/common/module_cli_base.py +378 -0
  12. runbooks/common/performance_monitoring.py +512 -0
  13. runbooks/common/profile_utils.py +133 -6
  14. runbooks/enterprise/logging.py +30 -2
  15. runbooks/enterprise/validation.py +177 -0
  16. runbooks/finops/README.md +311 -236
  17. runbooks/finops/aws_client.py +1 -1
  18. runbooks/finops/business_case_config.py +723 -19
  19. runbooks/finops/cli.py +136 -0
  20. runbooks/finops/commvault_ec2_analysis.py +25 -9
  21. runbooks/finops/config.py +272 -0
  22. runbooks/finops/dashboard_runner.py +136 -23
  23. runbooks/finops/ebs_cost_optimizer.py +39 -40
  24. runbooks/finops/enhanced_trend_visualization.py +7 -2
  25. runbooks/finops/enterprise_wrappers.py +45 -18
  26. runbooks/finops/finops_dashboard.py +50 -25
  27. runbooks/finops/finops_scenarios.py +22 -7
  28. runbooks/finops/helpers.py +115 -2
  29. runbooks/finops/multi_dashboard.py +7 -5
  30. runbooks/finops/optimizer.py +97 -6
  31. runbooks/finops/scenario_cli_integration.py +247 -0
  32. runbooks/finops/scenarios.py +12 -1
  33. runbooks/finops/unlimited_scenarios.py +393 -0
  34. runbooks/finops/validation_framework.py +19 -7
  35. runbooks/finops/workspaces_analyzer.py +1 -5
  36. runbooks/inventory/mcp_inventory_validator.py +2 -1
  37. runbooks/main.py +132 -94
  38. runbooks/main_final.py +358 -0
  39. runbooks/main_minimal.py +84 -0
  40. runbooks/main_optimized.py +493 -0
  41. runbooks/main_ultra_minimal.py +47 -0
  42. runbooks/utils/version_validator.py +1 -1
  43. {runbooks-1.0.3.dist-info → runbooks-1.1.1.dist-info}/METADATA +1 -1
  44. {runbooks-1.0.3.dist-info → runbooks-1.1.1.dist-info}/RECORD +48 -32
  45. {runbooks-1.0.3.dist-info → runbooks-1.1.1.dist-info}/WHEEL +0 -0
  46. {runbooks-1.0.3.dist-info → runbooks-1.1.1.dist-info}/entry_points.txt +0 -0
  47. {runbooks-1.0.3.dist-info → runbooks-1.1.1.dist-info}/licenses/LICENSE +0 -0
  48. {runbooks-1.0.3.dist-info → runbooks-1.1.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,493 @@
1
+ """
2
+ PERFORMANCE OPTIMIZED CloudOps Runbooks - Enterprise CLI Interface
3
+
4
+ ## Performance Optimizations Applied
5
+
6
+ ### 1. Lazy Loading Architecture
7
+ - Defer AWS session initialization until needed
8
+ - Defer MCP validator loading until actual validation
9
+ - Defer pricing API calls until cost analysis operations
10
+ - Keep basic CLI operations (--help, --version) lightning fast
11
+
12
+ ### 2. Startup Experience Optimization
13
+ - Move enterprise validation to on-demand loading
14
+ - Eliminate pricing API failures during basic operations
15
+ - Clean up warning pollution for simple commands
16
+ - Progressive disclosure of enterprise features
17
+
18
+ ### 3. Import Optimization
19
+ - Critical imports only at module level
20
+ - Expensive imports deferred using lazy_loader
21
+ - Basic CLI functionality remains fast
22
+
23
+ ## Performance Targets:
24
+ - Basic CLI operations < 0.5s
25
+ - Zero warnings for --help, --version
26
+ - Clean startup without overhead
27
+ """
28
+
29
+ import sys
30
+ from datetime import datetime
31
+ from pathlib import Path
32
+ from typing import Optional
33
+ import os
34
+
35
+ import click
36
+ from loguru import logger
37
+
38
+ # Import lazy loading architecture FIRST
39
+ from runbooks.common.lazy_loader import (
40
+ lazy_rich_console,
41
+ lazy_aws_session,
42
+ lazy_mcp_validator,
43
+ lazy_performance_monitor,
44
+ lazy_pricing_api,
45
+ lazy_inventory_collector,
46
+ fast_startup_mode,
47
+ defer_expensive_imports,
48
+ requires_aws,
49
+ requires_mcp
50
+ )
51
+
52
+ # Enable deferred imports for startup optimization
53
+ defer_expensive_imports()
54
+
55
+ # Fast Rich console loading
56
+ try:
57
+ from rich.console import Console
58
+ from rich.table import Table
59
+ from rich.markup import escape
60
+ _HAS_RICH = True
61
+ except ImportError:
62
+ _HAS_RICH = False
63
+ # Fallback console implementation
64
+ class Console:
65
+ def print(self, *args, **kwargs):
66
+ output = " ".join(str(arg) for arg in args)
67
+ print(output)
68
+
69
+ # Basic imports only - no heavy AWS/MCP initialization
70
+ from runbooks import __version__
71
+
72
+ # Lazy imports for performance-critical operations
73
+ def get_assessment_runner():
74
+ """Lazy load CFAT assessment runner."""
75
+ from runbooks.cfat.runner import AssessmentRunner
76
+ return AssessmentRunner
77
+
78
+ def get_profile_utils():
79
+ """Lazy load profile utilities."""
80
+ from runbooks.common.profile_utils import (
81
+ create_management_session,
82
+ create_operational_session,
83
+ get_profile_for_operation,
84
+ )
85
+ return {
86
+ 'create_management_session': create_management_session,
87
+ 'create_operational_session': create_operational_session,
88
+ 'get_profile_for_operation': get_profile_for_operation,
89
+ }
90
+
91
+ def get_rich_utils():
92
+ """Lazy load Rich utilities."""
93
+ from runbooks.common.rich_utils import (
94
+ console, create_table, print_banner, print_header, print_status
95
+ )
96
+ return {
97
+ 'console': console,
98
+ 'create_table': create_table,
99
+ 'print_banner': print_banner,
100
+ 'print_header': print_header,
101
+ 'print_status': print_status,
102
+ }
103
+
104
+ def get_config_utils():
105
+ """Lazy load configuration utilities."""
106
+ from runbooks.config import load_config, save_config
107
+ return {'load_config': load_config, 'save_config': save_config}
108
+
109
+ def get_logging_utils():
110
+ """Lazy load logging utilities."""
111
+ from runbooks.utils import setup_logging, setup_enhanced_logging
112
+ return {'setup_logging': setup_logging, 'setup_enhanced_logging': setup_enhanced_logging}
113
+
114
+ def get_business_case_utils():
115
+ """Lazy load business case utilities."""
116
+ from runbooks.finops.business_case_config import get_business_case_config, format_business_achievement
117
+ return {
118
+ 'get_business_case_config': get_business_case_config,
119
+ 'format_business_achievement': format_business_achievement
120
+ }
121
+
122
+ # Global console for basic operations
123
+ console = Console()
124
+
125
+ # ============================================================================
126
+ # CLI ARGUMENT FIXES - Handle Profile Tuples and Export Format Issues
127
+ # ============================================================================
128
+
129
+ def normalize_profile_parameter(profile_param):
130
+ """
131
+ Normalize profile parameter from Click multiple=True tuple to string.
132
+
133
+ Args:
134
+ profile_param: Profile parameter from Click (could be tuple, list, or string)
135
+
136
+ Returns:
137
+ str: Single profile name for AWS operations
138
+ """
139
+ if profile_param is None:
140
+ return None
141
+
142
+ # Handle tuple/list from Click multiple=True
143
+ if isinstance(profile_param, (tuple, list)):
144
+ # Take the first profile if multiple provided
145
+ return profile_param[0] if len(profile_param) > 0 else None
146
+
147
+ # Handle string directly
148
+ return profile_param
149
+
150
+ # Performance monitoring decorator
151
+ def track_performance(operation_name: str):
152
+ """Decorator to track CLI operation performance."""
153
+ def decorator(func):
154
+ def wrapper(*args, **kwargs):
155
+ if fast_startup_mode():
156
+ # Skip performance tracking for fast operations
157
+ return func(*args, **kwargs)
158
+
159
+ start_time = datetime.now()
160
+ try:
161
+ result = func(*args, **kwargs)
162
+ end_time = datetime.now()
163
+ duration = (end_time - start_time).total_seconds()
164
+
165
+ # Only log if operation takes >0.1s to avoid spam
166
+ if duration > 0.1:
167
+ logger.debug(f"Operation '{operation_name}' completed in {duration:.3f}s")
168
+
169
+ return result
170
+ except Exception as e:
171
+ end_time = datetime.now()
172
+ duration = (end_time - start_time).total_seconds()
173
+ logger.error(f"Operation '{operation_name}' failed after {duration:.3f}s: {e}")
174
+ raise
175
+ return wrapper
176
+ return decorator
177
+
178
+ # ============================================================================
179
+ # CLI MAIN ENTRY POINT - OPTIMIZED FOR PERFORMANCE
180
+ # ============================================================================
181
+
182
+ @click.group()
183
+ @click.version_option(version=__version__, prog_name="runbooks")
184
+ @click.option('--debug', is_flag=True, help='Enable debug logging')
185
+ @click.option('--profile', help='AWS profile to use')
186
+ @click.pass_context
187
+ def cli(ctx: click.Context, debug: bool, profile: str):
188
+ """
189
+ CloudOps Runbooks - Enterprise AWS Automation Platform
190
+
191
+ Fast, enterprise-grade automation for CloudOps, DevOps, and SRE teams.
192
+
193
+ Performance optimized for sub-second response times.
194
+ """
195
+ # Fast context setup
196
+ ctx.ensure_object(dict)
197
+ ctx.obj['profile'] = profile
198
+ ctx.obj['debug'] = debug
199
+
200
+ # Only setup logging if not a fast operation
201
+ if not fast_startup_mode() and debug:
202
+ setup_logging = get_logging_utils()['setup_logging']
203
+ setup_logging(debug=debug)
204
+
205
+ @cli.command()
206
+ @track_performance("version")
207
+ def version():
208
+ """Show version information (fast operation)."""
209
+ console.print(f"CloudOps Runbooks v{__version__}")
210
+ console.print("Enterprise AWS Automation Platform")
211
+
212
+ @cli.command()
213
+ @track_performance("status")
214
+ def status():
215
+ """Show system status (fast operation)."""
216
+ console.print("🚀 CloudOps Runbooks Status")
217
+ console.print(f"Version: {__version__}")
218
+ console.print("Status: Ready")
219
+
220
+ # Only check AWS if explicitly requested (not for basic status)
221
+ import sys
222
+ if '--aws' in sys.argv:
223
+ check_aws_status()
224
+
225
+ def check_aws_status():
226
+ """Check AWS connectivity (lazy loaded)."""
227
+ try:
228
+ session = lazy_aws_session()
229
+ console.print("AWS: Connected")
230
+ except Exception as e:
231
+ console.print(f"AWS: Error - {e}")
232
+
233
+ # ============================================================================
234
+ # FINOPS COMMANDS - WITH LAZY LOADING
235
+ # ============================================================================
236
+
237
+ @cli.group()
238
+ def finops():
239
+ """Financial Operations and Cost Analysis"""
240
+ pass
241
+
242
+ @finops.command()
243
+ @click.option('--profile', help='AWS profile to use')
244
+ @click.option('--export', type=click.Choice(['csv', 'json', 'html', 'pdf']), help='Export format')
245
+ @click.option('--output-file', type=click.Path(), help='Output file path')
246
+ @click.option('--mcp-validation', is_flag=True, help='Enable MCP validation')
247
+ @track_performance("finops_dashboard")
248
+ @requires_aws
249
+ def dashboard(profile: str, export: str, output_file: str, mcp_validation: bool):
250
+ """Run FinOps cost analysis dashboard (lazy loaded)."""
251
+ # Lazy load FinOps components only when needed
252
+ from runbooks.finops.dashboard_runner import run_dashboard
253
+
254
+ profile_utils = get_profile_utils()
255
+ rich_utils = get_rich_utils()
256
+
257
+ # Normalize profile parameter
258
+ normalized_profile = normalize_profile_parameter(profile)
259
+
260
+ console.print("🚀 Starting FinOps Dashboard Analysis...")
261
+
262
+ # Optional MCP validation
263
+ if mcp_validation:
264
+ validator = lazy_mcp_validator()
265
+ console.print("📊 MCP validation enabled")
266
+
267
+ # Run dashboard with lazy-loaded components
268
+ try:
269
+ result = run_dashboard(
270
+ profile=normalized_profile,
271
+ export_format=export,
272
+ output_file=output_file
273
+ )
274
+ console.print("✅ FinOps analysis completed successfully")
275
+ return result
276
+ except Exception as e:
277
+ console.print(f"❌ FinOps analysis failed: {e}")
278
+ raise
279
+
280
+ # ============================================================================
281
+ # INVENTORY COMMANDS - WITH LAZY LOADING
282
+ # ============================================================================
283
+
284
+ @cli.group()
285
+ def inventory():
286
+ """Resource Discovery and Inventory Management"""
287
+ pass
288
+
289
+ @inventory.command()
290
+ @click.option('--profile', help='AWS profile to use')
291
+ @click.option('--regions', multiple=True, help='AWS regions to scan')
292
+ @click.option('--services', multiple=True, help='AWS services to include')
293
+ @click.option('--export', type=click.Choice(['csv', 'json', 'yaml']), help='Export format')
294
+ @track_performance("inventory_collect")
295
+ @requires_aws
296
+ def collect(profile: str, regions: tuple, services: tuple, export: str):
297
+ """Collect comprehensive inventory across AWS accounts (lazy loaded)."""
298
+ # Lazy load inventory collector only when needed
299
+ InventoryCollector = lazy_inventory_collector()
300
+
301
+ profile_utils = get_profile_utils()
302
+
303
+ # Normalize profile parameter
304
+ normalized_profile = normalize_profile_parameter(profile)
305
+
306
+ console.print("🔍 Starting inventory collection...")
307
+
308
+ # Create collector with lazy-loaded session
309
+ session = lazy_aws_session()
310
+ collector = InventoryCollector(session=session)
311
+
312
+ try:
313
+ result = collector.collect_all(
314
+ profile=normalized_profile,
315
+ regions=list(regions) if regions else None,
316
+ services=list(services) if services else None,
317
+ export_format=export
318
+ )
319
+ console.print("✅ Inventory collection completed successfully")
320
+ return result
321
+ except Exception as e:
322
+ console.print(f"❌ Inventory collection failed: {e}")
323
+ raise
324
+
325
+ # ============================================================================
326
+ # SECURITY COMMANDS - WITH LAZY LOADING
327
+ # ============================================================================
328
+
329
+ @cli.group()
330
+ def security():
331
+ """Security Assessment and Compliance"""
332
+ pass
333
+
334
+ @security.command()
335
+ @click.option('--profile', help='AWS profile to use')
336
+ @click.option('--frameworks', multiple=True, help='Compliance frameworks to check')
337
+ @click.option('--export', type=click.Choice(['csv', 'json', 'html']), help='Export format')
338
+ @track_performance("security_assess")
339
+ @requires_aws
340
+ def assess(profile: str, frameworks: tuple, export: str):
341
+ """Run security baseline assessment (lazy loaded)."""
342
+ # Lazy load security components only when needed
343
+ from runbooks.security.security_baseline_tester import SecurityBaselineTester
344
+
345
+ profile_utils = get_profile_utils()
346
+
347
+ # Normalize profile parameter
348
+ normalized_profile = normalize_profile_parameter(profile)
349
+
350
+ console.print("🔒 Starting security assessment...")
351
+
352
+ # Create tester with lazy-loaded session
353
+ session = lazy_aws_session()
354
+ tester = SecurityBaselineTester(session=session)
355
+
356
+ try:
357
+ result = tester.run_assessment(
358
+ profile=normalized_profile,
359
+ frameworks=list(frameworks) if frameworks else None,
360
+ export_format=export
361
+ )
362
+ console.print("✅ Security assessment completed successfully")
363
+ return result
364
+ except Exception as e:
365
+ console.print(f"❌ Security assessment failed: {e}")
366
+ raise
367
+
368
+ # ============================================================================
369
+ # OPERATE COMMANDS - WITH LAZY LOADING
370
+ # ============================================================================
371
+
372
+ @cli.group()
373
+ def operate():
374
+ """AWS Resource Operations and Automation"""
375
+ pass
376
+
377
+ @operate.command()
378
+ @click.option('--profile', help='AWS profile to use')
379
+ @click.option('--dry-run', is_flag=True, default=True, help='Dry run mode (default: enabled)')
380
+ @track_performance("operate_ec2")
381
+ @requires_aws
382
+ def ec2(profile: str, dry_run: bool):
383
+ """EC2 resource operations (lazy loaded)."""
384
+ # Lazy load EC2 operations only when needed
385
+ from runbooks.operate.ec2_operations import EC2Operations
386
+
387
+ # Normalize profile parameter
388
+ normalized_profile = normalize_profile_parameter(profile)
389
+
390
+ console.print("⚡ Starting EC2 operations...")
391
+
392
+ if dry_run:
393
+ console.print("🔒 Running in dry-run mode (no changes will be made)")
394
+
395
+ # Create operations with lazy-loaded session
396
+ session = lazy_aws_session()
397
+ ec2_ops = EC2Operations(session=session)
398
+
399
+ try:
400
+ result = ec2_ops.list_instances(
401
+ profile=normalized_profile,
402
+ dry_run=dry_run
403
+ )
404
+ console.print("✅ EC2 operations completed successfully")
405
+ return result
406
+ except Exception as e:
407
+ console.print(f"❌ EC2 operations failed: {e}")
408
+ raise
409
+
410
+ # ============================================================================
411
+ # CFAT COMMANDS - WITH LAZY LOADING
412
+ # ============================================================================
413
+
414
+ @cli.group()
415
+ def cfat():
416
+ """Cloud Foundations Assessment Tool"""
417
+ pass
418
+
419
+ @cfat.command()
420
+ @click.option('--profile', help='AWS profile to use')
421
+ @click.option('--output-file', type=click.Path(), help='Assessment report output file')
422
+ @track_performance("cfat_assess")
423
+ @requires_aws
424
+ def assess(profile: str, output_file: str):
425
+ """Run Cloud Foundations Assessment (lazy loaded)."""
426
+ # Lazy load CFAT components only when needed
427
+ AssessmentRunner = get_assessment_runner()
428
+
429
+ # Normalize profile parameter
430
+ normalized_profile = normalize_profile_parameter(profile)
431
+
432
+ console.print("🏛️ Starting Cloud Foundations Assessment...")
433
+
434
+ # Create runner with lazy-loaded session
435
+ session = lazy_aws_session()
436
+ runner = AssessmentRunner(session=session)
437
+
438
+ try:
439
+ result = runner.run_assessment(
440
+ profile=normalized_profile,
441
+ output_file=output_file
442
+ )
443
+ console.print("✅ CFAT assessment completed successfully")
444
+ return result
445
+ except Exception as e:
446
+ console.print(f"❌ CFAT assessment failed: {e}")
447
+ raise
448
+
449
+ # ============================================================================
450
+ # PERFORMANCE DIAGNOSTICS
451
+ # ============================================================================
452
+
453
+ @cli.command()
454
+ @track_performance("perf_test")
455
+ def perf():
456
+ """Performance diagnostics and benchmarking."""
457
+ start_time = datetime.now()
458
+
459
+ console.print("🚀 Performance Diagnostics")
460
+ console.print(f"Startup Time: {(datetime.now() - start_time).total_seconds():.3f}s")
461
+
462
+ # Test lazy loading performance
463
+ console.print("\n📊 Component Loading Times:")
464
+
465
+ # Test Rich loading
466
+ rich_start = datetime.now()
467
+ lazy_rich_console()
468
+ rich_time = (datetime.now() - rich_start).total_seconds()
469
+ console.print(f"Rich Console: {rich_time:.3f}s")
470
+
471
+ # Test AWS session (only if credentials available)
472
+ try:
473
+ aws_start = datetime.now()
474
+ lazy_aws_session()
475
+ aws_time = (datetime.now() - aws_start).total_seconds()
476
+ console.print(f"AWS Session: {aws_time:.3f}s")
477
+ except Exception:
478
+ console.print("AWS Session: Not available (no credentials)")
479
+
480
+ # Test MCP validator
481
+ try:
482
+ mcp_start = datetime.now()
483
+ lazy_mcp_validator()
484
+ mcp_time = (datetime.now() - mcp_start).total_seconds()
485
+ console.print(f"MCP Validator: {mcp_time:.3f}s")
486
+ except Exception:
487
+ console.print("MCP Validator: Not available")
488
+
489
+ total_time = (datetime.now() - start_time).total_seconds()
490
+ console.print(f"\n⏱️ Total Diagnostic Time: {total_time:.3f}s")
491
+
492
+ if __name__ == "__main__":
493
+ cli()
@@ -0,0 +1,47 @@
1
+ """
2
+ ULTRA MINIMAL CloudOps Runbooks CLI - Performance Baseline Test
3
+
4
+ This version avoids ALL runbooks imports to isolate the performance issue.
5
+ """
6
+
7
+ import sys
8
+ import click
9
+ from datetime import datetime
10
+
11
+ # Hard-code version to avoid import chain
12
+ __version__ = "1.0.0"
13
+
14
+ @click.group()
15
+ @click.version_option(version=__version__, prog_name="runbooks")
16
+ @click.pass_context
17
+ def cli(ctx: click.Context):
18
+ """
19
+ CloudOps Runbooks - Ultra Minimal Test Version
20
+
21
+ Testing CLI performance without any runbooks imports.
22
+ """
23
+ ctx.ensure_object(dict)
24
+
25
+ @cli.command()
26
+ def version():
27
+ """Show version information."""
28
+ print(f"CloudOps Runbooks v{__version__}")
29
+
30
+ @cli.command()
31
+ def status():
32
+ """Show basic status."""
33
+ print("🚀 CloudOps Runbooks Status")
34
+ print(f"Version: {__version__}")
35
+ print("Status: Ready")
36
+
37
+ @cli.command()
38
+ def perf():
39
+ """Test CLI performance baseline."""
40
+ start_time = datetime.now()
41
+ print("🚀 Performance Test")
42
+ end_time = datetime.now()
43
+ duration = (end_time - start_time).total_seconds()
44
+ print(f"Command execution: {duration:.3f}s")
45
+
46
+ if __name__ == "__main__":
47
+ cli()
@@ -12,7 +12,7 @@ from pathlib import Path
12
12
  from typing import Dict, List, Optional, Tuple
13
13
 
14
14
  # Avoid circular imports by defining central version directly
15
- CENTRAL_VERSION = "0.9.3" # Must match runbooks.__init__.__version__
15
+ CENTRAL_VERSION = "1.1.1" # Must match runbooks.__init__.__version__
16
16
 
17
17
 
18
18
  class VersionDriftError(Exception):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runbooks
3
- Version: 1.0.3
3
+ Version: 1.1.1
4
4
  Summary: CloudOps Automation Toolkit with Enhanced Cloud Foundations Assessment for DevOps and SRE teams.
5
5
  Author-email: Maintainers <nnthanh101@gmail.com>
6
6
  License-Expression: Apache-2.0