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
@@ -5,6 +5,7 @@ Strategic Achievement: Replace hardcoded JIRA references with dynamic business c
5
5
  - Enterprise naming conventions with configurable business scenarios
6
6
  - Dynamic financial targets and achievement tracking
7
7
  - Reusable template system for unlimited business case scaling
8
+ - Business Scenario Matrix with intelligent parameter defaults (Phase 1 Priority 2)
8
9
 
9
10
  This module provides configurable business case templates following enterprise standards:
10
11
  - "Do one thing and do it well": Centralized configuration management
@@ -13,7 +14,7 @@ This module provides configurable business case templates following enterprise s
13
14
 
14
15
  import os
15
16
  from dataclasses import dataclass
16
- from typing import Dict, List, Optional, Any, Union
17
+ from typing import Dict, List, Optional, Any, Union, NamedTuple
17
18
  from enum import Enum
18
19
 
19
20
 
@@ -148,31 +149,125 @@ class BusinessCaseConfigManager:
148
149
  }
149
150
 
150
151
  def _load_environment_overrides(self) -> None:
151
- """Load configuration overrides from environment variables."""
152
+ """
153
+ Load configuration overrides and discover new scenarios from environment variables.
154
+
155
+ Implements Unlimited Scenario Expansion Framework:
156
+ - Overrides existing scenarios with environment variables
157
+ - Auto-discovers new scenarios via RUNBOOKS_BUSINESS_CASE_[SCENARIO]_* pattern
158
+ - Creates BusinessScenario objects for new scenarios dynamically
159
+ """
152
160
  prefix = f"{self.config_source}_"
153
-
161
+
162
+ # Phase 1: Override existing scenarios
154
163
  for scenario_key, scenario in self.scenarios.items():
155
164
  # Check for scenario-specific overrides
156
165
  env_key = f"{prefix}{scenario_key.upper().replace('-', '_')}"
157
-
166
+
158
167
  # Override target savings if specified
159
168
  min_savings = os.getenv(f"{env_key}_MIN_SAVINGS")
160
169
  max_savings = os.getenv(f"{env_key}_MAX_SAVINGS")
161
-
170
+
162
171
  if min_savings:
163
172
  scenario.target_savings_min = float(min_savings)
164
173
  if max_savings:
165
174
  scenario.target_savings_max = float(max_savings)
166
-
175
+
167
176
  # Override display name if specified
168
177
  display_name = os.getenv(f"{env_key}_DISPLAY_NAME")
169
178
  if display_name:
170
179
  scenario.display_name = display_name
171
-
172
- # Override business description if specified
180
+
181
+ # Override business description if specified
173
182
  description = os.getenv(f"{env_key}_DESCRIPTION")
174
183
  if description:
175
184
  scenario.business_description = description
185
+
186
+ # Phase 2: Auto-discovery of new scenarios from environment variables
187
+ self._discover_new_scenarios_from_environment(prefix)
188
+
189
+ def _discover_new_scenarios_from_environment(self, prefix: str) -> None:
190
+ """
191
+ Discover and create new business scenarios from environment variables.
192
+
193
+ Environment Variable Pattern:
194
+ RUNBOOKS_BUSINESS_CASE_[SCENARIO]_DISPLAY_NAME=... (required)
195
+ RUNBOOKS_BUSINESS_CASE_[SCENARIO]_MIN_SAVINGS=... (optional)
196
+ RUNBOOKS_BUSINESS_CASE_[SCENARIO]_MAX_SAVINGS=... (optional)
197
+ RUNBOOKS_BUSINESS_CASE_[SCENARIO]_DESCRIPTION=... (optional)
198
+ RUNBOOKS_BUSINESS_CASE_[SCENARIO]_CLI_SUFFIX=... (optional, defaults to scenario key)
199
+ RUNBOOKS_BUSINESS_CASE_[SCENARIO]_TYPE=... (optional, defaults to cost_optimization)
200
+ RUNBOOKS_BUSINESS_CASE_[SCENARIO]_RISK_LEVEL=... (optional, defaults to Medium)
201
+ """
202
+ # Scan all environment variables for new scenario patterns
203
+ discovered_scenarios = set()
204
+
205
+ for env_var in os.environ:
206
+ if env_var.startswith(prefix) and "_DISPLAY_NAME" in env_var:
207
+ # Extract scenario key from pattern: RUNBOOKS_BUSINESS_CASE_[SCENARIO]_DISPLAY_NAME
208
+ scenario_part = env_var.replace(prefix, "").replace("_DISPLAY_NAME", "")
209
+ scenario_key = scenario_part.lower().replace('_', '-')
210
+ discovered_scenarios.add((scenario_key, scenario_part))
211
+
212
+ # Create new scenarios for discovered patterns
213
+ for scenario_key, env_scenario_key in discovered_scenarios:
214
+ if scenario_key not in self.scenarios: # Don't override existing scenarios
215
+ new_scenario = self._create_scenario_from_environment(scenario_key, env_scenario_key, prefix)
216
+ if new_scenario:
217
+ self.scenarios[scenario_key] = new_scenario
218
+
219
+ def _create_scenario_from_environment(self, scenario_key: str, env_scenario_key: str, prefix: str) -> Optional[BusinessScenario]:
220
+ """
221
+ Create a new BusinessScenario from environment variables.
222
+
223
+ Args:
224
+ scenario_key: The normalized scenario key (kebab-case)
225
+ env_scenario_key: The environment variable scenario key (UPPER_CASE)
226
+ prefix: Environment variable prefix
227
+
228
+ Returns:
229
+ New BusinessScenario object or None if required fields missing
230
+ """
231
+ env_base = f"{prefix}{env_scenario_key}"
232
+
233
+ # Required field: display name
234
+ display_name = os.getenv(f"{env_base}_DISPLAY_NAME")
235
+ if not display_name:
236
+ return None
237
+
238
+ # Optional fields with smart defaults
239
+ business_type_str = os.getenv(f"{env_base}_TYPE", "cost_optimization")
240
+ try:
241
+ business_case_type = BusinessCaseType(business_type_str)
242
+ except ValueError:
243
+ business_case_type = BusinessCaseType.COST_OPTIMIZATION
244
+
245
+ # Financial targets
246
+ min_savings_str = os.getenv(f"{env_base}_MIN_SAVINGS")
247
+ max_savings_str = os.getenv(f"{env_base}_MAX_SAVINGS")
248
+
249
+ min_savings = float(min_savings_str) if min_savings_str else None
250
+ max_savings = float(max_savings_str) if max_savings_str else None
251
+
252
+ # Business context
253
+ description = os.getenv(f"{env_base}_DESCRIPTION", f"Business optimization scenario for {display_name}")
254
+ technical_focus = os.getenv(f"{env_base}_TECHNICAL_FOCUS", f"{display_name} analysis and optimization")
255
+ risk_level = os.getenv(f"{env_base}_RISK_LEVEL", "Medium")
256
+ implementation_status = os.getenv(f"{env_base}_STATUS", "Analysis")
257
+ cli_suffix = os.getenv(f"{env_base}_CLI_SUFFIX", scenario_key)
258
+
259
+ return BusinessScenario(
260
+ scenario_id=scenario_key,
261
+ display_name=display_name,
262
+ business_case_type=business_case_type,
263
+ target_savings_min=min_savings,
264
+ target_savings_max=max_savings,
265
+ business_description=description,
266
+ technical_focus=technical_focus,
267
+ risk_level=risk_level,
268
+ implementation_status=implementation_status,
269
+ cli_command_suffix=cli_suffix
270
+ )
176
271
 
177
272
  def get_scenario(self, scenario_key: str) -> Optional[BusinessScenario]:
178
273
  """Get business scenario by key."""
@@ -212,19 +307,155 @@ class BusinessCaseConfigManager:
212
307
 
213
308
  return base_info
214
309
 
310
+ def add_dynamic_scenario(self, scenario: BusinessScenario) -> None:
311
+ """
312
+ Add a new business scenario dynamically.
313
+
314
+ Args:
315
+ scenario: BusinessScenario object to add to the configuration
316
+ """
317
+ self.scenarios[scenario.scenario_id] = scenario
318
+
319
+ def create_scenario_from_template(self, scenario_id: str, template_type: str = "aws_resource_optimization") -> BusinessScenario:
320
+ """
321
+ Create a business scenario from predefined templates.
322
+
323
+ Templates available:
324
+ - aws_resource_optimization: Generic AWS resource optimization
325
+ - lambda_rightsizing: AWS Lambda function rightsizing
326
+ - s3_storage_optimization: S3 storage class optimization
327
+ - healthcare_compliance: Healthcare-specific compliance scenarios
328
+ - finance_cost_governance: Financial industry cost governance
329
+ - manufacturing_automation: Manufacturing automation scenarios
330
+
331
+ Args:
332
+ scenario_id: Unique identifier for the scenario
333
+ template_type: Type of template to use
334
+
335
+ Returns:
336
+ Pre-configured BusinessScenario object
337
+ """
338
+ templates = {
339
+ "aws_resource_optimization": BusinessScenario(
340
+ scenario_id=scenario_id,
341
+ display_name=f"{scenario_id.title().replace('-', ' ')} Resource Optimization",
342
+ business_case_type=BusinessCaseType.COST_OPTIMIZATION,
343
+ business_description=f"Optimize {scenario_id.replace('-', ' ')} resources for cost efficiency and performance",
344
+ technical_focus=f"{scenario_id.title().replace('-', ' ')} resource analysis with automated recommendations",
345
+ risk_level="Medium",
346
+ implementation_status="Template Ready",
347
+ cli_command_suffix=scenario_id
348
+ ),
349
+ "lambda_rightsizing": BusinessScenario(
350
+ scenario_id=scenario_id,
351
+ display_name="Lambda Function Rightsizing",
352
+ business_case_type=BusinessCaseType.COST_OPTIMIZATION,
353
+ business_description="Optimize Lambda function memory allocation and timeout settings",
354
+ technical_focus="CloudWatch metrics analysis for memory and duration optimization",
355
+ risk_level="Low",
356
+ cli_command_suffix=scenario_id
357
+ ),
358
+ "s3_storage_optimization": BusinessScenario(
359
+ scenario_id=scenario_id,
360
+ display_name="S3 Storage Class Optimization",
361
+ business_case_type=BusinessCaseType.COST_OPTIMIZATION,
362
+ business_description="Optimize S3 storage classes based on access patterns",
363
+ technical_focus="S3 access pattern analysis with intelligent storage class recommendations",
364
+ risk_level="Low",
365
+ cli_command_suffix=scenario_id
366
+ ),
367
+ "healthcare_compliance": BusinessScenario(
368
+ scenario_id=scenario_id,
369
+ display_name="Healthcare Compliance Optimization",
370
+ business_case_type=BusinessCaseType.COMPLIANCE_FRAMEWORK,
371
+ business_description="HIPAA compliance optimization with cost considerations",
372
+ technical_focus="Healthcare data security analysis with cost-effective compliance solutions",
373
+ risk_level="High",
374
+ cli_command_suffix=scenario_id
375
+ ),
376
+ "finance_cost_governance": BusinessScenario(
377
+ scenario_id=scenario_id,
378
+ display_name="Financial Cost Governance",
379
+ business_case_type=BusinessCaseType.COMPLIANCE_FRAMEWORK,
380
+ business_description="Financial industry cost governance and audit readiness",
381
+ technical_focus="SOX compliance cost optimization with audit trail requirements",
382
+ risk_level="High",
383
+ cli_command_suffix=scenario_id
384
+ ),
385
+ "manufacturing_automation": BusinessScenario(
386
+ scenario_id=scenario_id,
387
+ display_name="Manufacturing Process Automation",
388
+ business_case_type=BusinessCaseType.AUTOMATION_DEPLOYMENT,
389
+ business_description="Manufacturing workflow automation with cost optimization",
390
+ technical_focus="IoT and automation pipeline cost optimization analysis",
391
+ risk_level="Medium",
392
+ cli_command_suffix=scenario_id
393
+ )
394
+ }
395
+
396
+ template = templates.get(template_type, templates["aws_resource_optimization"])
397
+ # Create a copy and update the scenario_id
398
+ return BusinessScenario(
399
+ scenario_id=scenario_id,
400
+ display_name=template.display_name,
401
+ business_case_type=template.business_case_type,
402
+ target_savings_min=template.target_savings_min,
403
+ target_savings_max=template.target_savings_max,
404
+ business_description=template.business_description,
405
+ technical_focus=template.technical_focus,
406
+ risk_level=template.risk_level,
407
+ implementation_status=template.implementation_status,
408
+ cli_command_suffix=template.cli_command_suffix
409
+ )
410
+
411
+ def get_template_types(self) -> List[str]:
412
+ """Get list of available template types for scenario creation."""
413
+ return [
414
+ "aws_resource_optimization",
415
+ "lambda_rightsizing",
416
+ "s3_storage_optimization",
417
+ "healthcare_compliance",
418
+ "finance_cost_governance",
419
+ "manufacturing_automation"
420
+ ]
421
+
422
+ def calculate_roi_projection(self, scenario_key: str, current_monthly_cost: float,
423
+ optimization_percentage: float = 0.20) -> Dict[str, float]:
424
+ """
425
+ Calculate ROI projection for a business scenario.
426
+
427
+ Args:
428
+ scenario_key: Business scenario identifier
429
+ current_monthly_cost: Current monthly cost for the resource
430
+ optimization_percentage: Expected optimization percentage (default 20%)
431
+
432
+ Returns:
433
+ Dictionary with ROI calculations
434
+ """
435
+ monthly_savings = current_monthly_cost * optimization_percentage
436
+ annual_savings = monthly_savings * 12
437
+
438
+ return {
439
+ "current_monthly_cost": current_monthly_cost,
440
+ "monthly_savings": monthly_savings,
441
+ "annual_savings": annual_savings,
442
+ "optimization_percentage": optimization_percentage * 100,
443
+ "roi_12_month": annual_savings # Assuming minimal implementation cost for analysis
444
+ }
445
+
215
446
  def create_business_case_summary(self) -> Dict[str, Any]:
216
447
  """Create executive summary of all business cases."""
217
448
  total_min_savings = sum(
218
- scenario.target_savings_min or 0
449
+ scenario.target_savings_min or 0
219
450
  for scenario in self.scenarios.values()
220
451
  )
221
-
452
+
222
453
  total_max_savings = sum(
223
- scenario.target_savings_max or 0
224
- for scenario in self.scenarios.values()
454
+ scenario.target_savings_max or 0
455
+ for scenario in self.scenarios.values()
225
456
  if scenario.target_savings_max
226
457
  )
227
-
458
+
228
459
  return {
229
460
  "total_scenarios": len(self.scenarios),
230
461
  "total_potential_min": total_min_savings,
@@ -232,10 +463,16 @@ class BusinessCaseConfigManager:
232
463
  "potential_range": f"${total_min_savings:,.0f}-${total_max_savings:,.0f}",
233
464
  "scenarios_by_type": {
234
465
  case_type.value: [
235
- s.display_name for s in self.scenarios.values()
466
+ s.display_name for s in self.scenarios.values()
236
467
  if s.business_case_type == case_type
237
468
  ]
238
469
  for case_type in BusinessCaseType
470
+ },
471
+ "scenario_discovery": {
472
+ "default_scenarios": 7,
473
+ "environment_discovered": len(self.scenarios) - 7,
474
+ "total_active": len(self.scenarios),
475
+ "unlimited_expansion": True
239
476
  }
240
477
  }
241
478
 
@@ -283,20 +520,119 @@ def format_business_achievement(scenario_key: str, achieved_savings: float) -> s
283
520
  return achievement_text
284
521
 
285
522
 
523
+ # Unlimited Scenario Expansion Framework - Helper Functions
524
+ def create_scenario_from_environment_variables(scenario_id: str) -> Optional[BusinessScenario]:
525
+ """
526
+ Create a business scenario from environment variables.
527
+
528
+ Environment Variable Pattern:
529
+ RUNBOOKS_BUSINESS_CASE_[SCENARIO_ID]_DISPLAY_NAME=... (required)
530
+ RUNBOOKS_BUSINESS_CASE_[SCENARIO_ID]_MIN_SAVINGS=... (optional)
531
+ RUNBOOKS_BUSINESS_CASE_[SCENARIO_ID]_MAX_SAVINGS=... (optional)
532
+ RUNBOOKS_BUSINESS_CASE_[SCENARIO_ID]_DESCRIPTION=... (optional)
533
+ RUNBOOKS_BUSINESS_CASE_[SCENARIO_ID]_TYPE=... (optional)
534
+
535
+ Args:
536
+ scenario_id: Scenario identifier (will be normalized to kebab-case)
537
+
538
+ Returns:
539
+ BusinessScenario object or None if required fields missing
540
+ """
541
+ config_manager = get_business_case_config()
542
+ env_key = scenario_id.upper().replace('-', '_')
543
+
544
+ return config_manager._create_scenario_from_environment(
545
+ scenario_id.lower().replace('_', '-'),
546
+ env_key,
547
+ "RUNBOOKS_BUSINESS_CASE_"
548
+ )
549
+
550
+
551
+ def add_scenario_from_template(scenario_id: str, template_type: str = "aws_resource_optimization") -> BusinessScenario:
552
+ """
553
+ Add a new business scenario from a template.
554
+
555
+ Args:
556
+ scenario_id: Unique identifier for the new scenario
557
+ template_type: Template type to use (see get_available_templates())
558
+
559
+ Returns:
560
+ Created BusinessScenario object
561
+ """
562
+ config_manager = get_business_case_config()
563
+ scenario = config_manager.create_scenario_from_template(scenario_id, template_type)
564
+ config_manager.add_dynamic_scenario(scenario)
565
+ return scenario
566
+
567
+
568
+ def get_available_templates() -> List[str]:
569
+ """Get list of available business scenario templates."""
570
+ config_manager = get_business_case_config()
571
+ return config_manager.get_template_types()
572
+
573
+
574
+ def calculate_scenario_roi(scenario_id: str, current_monthly_cost: float,
575
+ optimization_percentage: float = 0.20) -> Dict[str, float]:
576
+ """
577
+ Calculate ROI projection for any business scenario.
578
+
579
+ Args:
580
+ scenario_id: Business scenario identifier
581
+ current_monthly_cost: Current monthly AWS spend
582
+ optimization_percentage: Expected optimization (default 20%)
583
+
584
+ Returns:
585
+ ROI calculation dictionary
586
+ """
587
+ config_manager = get_business_case_config()
588
+ return config_manager.calculate_roi_projection(scenario_id, current_monthly_cost, optimization_percentage)
589
+
590
+
591
+ def get_unlimited_scenario_choices() -> List[str]:
592
+ """
593
+ Get unlimited scenario choices including environment-discovered scenarios.
594
+
595
+ This function enables unlimited scenario expansion without hardcoded lists.
596
+ """
597
+ config_manager = get_business_case_config()
598
+ return config_manager.get_scenario_choices()
599
+
600
+
601
+ def get_unlimited_scenario_help() -> str:
602
+ """
603
+ Get unlimited scenario help text including dynamically discovered scenarios.
604
+
605
+ Returns comprehensive help covering all available scenarios.
606
+ """
607
+ config_manager = get_business_case_config()
608
+ return config_manager.get_scenario_help_text()
609
+
610
+
611
+ def discover_scenarios_summary() -> Dict[str, Any]:
612
+ """
613
+ Get summary of scenario discovery including environment-based scenarios.
614
+
615
+ Returns:
616
+ Discovery summary with counts and expansion capabilities
617
+ """
618
+ config_manager = get_business_case_config()
619
+ return config_manager.create_business_case_summary()
620
+
621
+
286
622
  # Migration helper functions for existing hardcoded patterns
287
623
  def migrate_legacy_scenario_reference(legacy_ref: str) -> str:
288
624
  """
289
625
  Migrate legacy JIRA references to dynamic business case keys.
290
-
626
+
291
627
  Args:
292
628
  legacy_ref: Legacy reference like "FinOps-24", "finops-23", etc.
293
-
629
+
294
630
  Returns:
295
631
  Dynamic business case key
296
632
  """
297
633
  legacy_mapping = {
298
634
  "finops-24": "workspaces",
299
- "FinOps-24": "workspaces",
635
+ "FinOps-24": "workspaces",
300
636
  "finops-23": "rds-snapshots",
301
637
  "FinOps-23": "rds-snapshots",
302
638
  "finops-25": "backup-investigation",
@@ -310,5 +646,373 @@ def migrate_legacy_scenario_reference(legacy_ref: str) -> str:
310
646
  "awso-05": "vpc-cleanup",
311
647
  "AWSO-05": "vpc-cleanup"
312
648
  }
313
-
314
- return legacy_mapping.get(legacy_ref, legacy_ref.lower())
649
+
650
+ return legacy_mapping.get(legacy_ref, legacy_ref.lower())
651
+
652
+
653
+ # ============================================================================
654
+ # Business Scenario Matrix - Phase 1 Priority 2 Implementation
655
+ # ============================================================================
656
+
657
+ class ScenarioParameter(NamedTuple):
658
+ """Parameter recommendation for business scenarios."""
659
+ name: str
660
+ optimal_value: Union[str, int, float]
661
+ business_justification: str
662
+ alternative_values: Optional[List[Any]] = None
663
+
664
+
665
+ @dataclass
666
+ class ScenarioParameterMatrix:
667
+ """Intelligent parameter defaults for business scenarios."""
668
+ timerange_days: Optional[int] = None
669
+ regional_scope: Optional[str] = None # single, multi, global
670
+ cost_focus: Optional[str] = None # unblended, amortized, dual-metrics
671
+ export_priority: Optional[str] = None # csv, json, pdf, markdown
672
+ validation_level: Optional[str] = None # basic, enhanced, comprehensive
673
+ business_justification: str = ""
674
+
675
+ @property
676
+ def parameter_recommendations(self) -> Dict[str, ScenarioParameter]:
677
+ """Get structured parameter recommendations."""
678
+ recommendations = {}
679
+
680
+ if self.timerange_days:
681
+ recommendations['timerange'] = ScenarioParameter(
682
+ name='--time-range',
683
+ optimal_value=self.timerange_days,
684
+ business_justification=f"Optimal analysis period: {self.timerange_days} days. {self.business_justification}",
685
+ alternative_values=[7, 30, 60, 90, 180] if self.timerange_days not in [7, 30, 60, 90, 180] else None
686
+ )
687
+
688
+ if self.regional_scope:
689
+ scope_mapping = {
690
+ 'single': ['--region', 'us-east-1'],
691
+ 'multi': ['--all-regions', True],
692
+ 'global': ['--global-scope', True]
693
+ }
694
+ if self.regional_scope in scope_mapping:
695
+ param_name, param_value = scope_mapping[self.regional_scope]
696
+ recommendations['regional_scope'] = ScenarioParameter(
697
+ name=param_name,
698
+ optimal_value=param_value,
699
+ business_justification=f"Regional scope: {self.regional_scope} - {self.business_justification}"
700
+ )
701
+
702
+ if self.cost_focus:
703
+ recommendations['cost_focus'] = ScenarioParameter(
704
+ name=f'--{self.cost_focus}',
705
+ optimal_value=True,
706
+ business_justification=f"Cost perspective: {self.cost_focus} - {self.business_justification}"
707
+ )
708
+
709
+ if self.export_priority:
710
+ recommendations['export_format'] = ScenarioParameter(
711
+ name='--' + self.export_priority.replace('_', '-'),
712
+ optimal_value=True,
713
+ business_justification=f"Export format: {self.export_priority} - {self.business_justification}",
714
+ alternative_values=['csv', 'json', 'pdf', 'markdown'] if self.export_priority else None
715
+ )
716
+
717
+ return recommendations
718
+
719
+
720
+ class BusinessScenarioMatrix:
721
+ """
722
+ Business Scenario Matrix with intelligent parameter defaults.
723
+
724
+ Implements Phase 1 Priority 2: Business scenario intelligence with smart
725
+ parameter recommendations per business case type.
726
+
727
+ Enhanced with Phase 2 Priority 1: Unlimited Scenario Expansion Framework
728
+ supporting dynamic parameter matrix generation for environment-discovered scenarios.
729
+ """
730
+
731
+ def __init__(self):
732
+ """Initialize scenario matrix with Tier 1, 2, 3 configurations and dynamic expansion."""
733
+ self.scenario_matrix = self._build_scenario_matrix()
734
+ self._extend_matrix_with_discovered_scenarios()
735
+
736
+ def _build_scenario_matrix(self) -> Dict[str, ScenarioParameterMatrix]:
737
+ """Build the complete business scenario parameter matrix."""
738
+ return {
739
+ # TIER 1 HIGH-VALUE SCENARIOS
740
+ 'workspaces': ScenarioParameterMatrix(
741
+ timerange_days=90,
742
+ regional_scope='single',
743
+ cost_focus='unblended',
744
+ export_priority='pdf',
745
+ validation_level='comprehensive',
746
+ business_justification="WorkSpaces require quarterly analysis for usage pattern detection. Single-region focus optimizes analysis speed. Unblended costs show true resource utilization. PDF format ideal for management review."
747
+ ),
748
+
749
+ 'nat-gateway': ScenarioParameterMatrix(
750
+ timerange_days=30,
751
+ regional_scope='multi',
752
+ cost_focus='amortized',
753
+ export_priority='json',
754
+ validation_level='enhanced',
755
+ business_justification="NAT Gateways require monthly analysis for traffic optimization. Multi-region analysis essential for comprehensive network cost optimization. Amortized costs account for data transfer pricing. JSON format enables automation integration."
756
+ ),
757
+
758
+ 'rds-snapshots': ScenarioParameterMatrix(
759
+ timerange_days=90,
760
+ regional_scope='multi',
761
+ cost_focus='dual-metrics',
762
+ export_priority='csv',
763
+ validation_level='comprehensive',
764
+ business_justification="RDS snapshots require quarterly analysis for retention policy optimization. Multi-region scope captures all backup strategies. Dual metrics provide complete cost visibility. CSV format enables spreadsheet analysis."
765
+ ),
766
+
767
+ # TIER 2 STRATEGIC SCENARIOS
768
+ 'ebs-optimization': ScenarioParameterMatrix(
769
+ timerange_days=180,
770
+ regional_scope='multi',
771
+ cost_focus='dual-metrics',
772
+ export_priority='pdf',
773
+ validation_level='comprehensive',
774
+ business_justification="EBS optimization requires extended analysis to identify usage patterns. Multi-region scope captures all storage. Dual metrics show both immediate and amortized costs. PDF format suitable for capacity planning presentations."
775
+ ),
776
+
777
+ 'vpc-cleanup': ScenarioParameterMatrix(
778
+ timerange_days=30,
779
+ regional_scope='multi',
780
+ cost_focus='unblended',
781
+ export_priority='csv',
782
+ validation_level='enhanced',
783
+ business_justification="VPC cleanup requires recent data for active resource identification. Multi-region analysis captures all network resources. Unblended costs show direct infrastructure impact. CSV enables detailed resource tracking."
784
+ ),
785
+
786
+ 'elastic-ip': ScenarioParameterMatrix(
787
+ timerange_days=7,
788
+ regional_scope='multi',
789
+ cost_focus='unblended',
790
+ export_priority='json',
791
+ validation_level='basic',
792
+ business_justification="Elastic IP analysis requires recent data for attachment status. Multi-region scope captures all IP allocations. Unblended costs show direct charges. JSON format enables automated cleanup workflows."
793
+ ),
794
+
795
+ # TIER 3 FRAMEWORK SCENARIOS
796
+ 'backup-investigation': ScenarioParameterMatrix(
797
+ timerange_days=None, # Framework-based
798
+ regional_scope='multi',
799
+ cost_focus='amortized',
800
+ export_priority='markdown',
801
+ validation_level='basic',
802
+ business_justification="Backup investigation uses framework-based timerange analysis. Multi-region scope for comprehensive backup strategy. Amortized costs for long-term planning. Markdown format for documentation and reporting."
803
+ )
804
+ }
805
+
806
+ def _extend_matrix_with_discovered_scenarios(self) -> None:
807
+ """
808
+ Extend parameter matrix with environment-discovered scenarios.
809
+
810
+ Creates intelligent parameter defaults for newly discovered scenarios
811
+ based on business case type and heuristics.
812
+ """
813
+ config_manager = get_business_case_config()
814
+ all_scenarios = config_manager.get_all_scenarios()
815
+
816
+ for scenario_key, scenario in all_scenarios.items():
817
+ if scenario_key not in self.scenario_matrix:
818
+ # Generate intelligent defaults based on business case type
819
+ parameter_matrix = self._generate_parameter_matrix_for_scenario(scenario)
820
+ self.scenario_matrix[scenario_key] = parameter_matrix
821
+
822
+ def _generate_parameter_matrix_for_scenario(self, scenario: BusinessScenario) -> ScenarioParameterMatrix:
823
+ """
824
+ Generate intelligent parameter matrix for a discovered scenario.
825
+
826
+ Args:
827
+ scenario: BusinessScenario object to generate parameters for
828
+
829
+ Returns:
830
+ ScenarioParameterMatrix with intelligent defaults
831
+ """
832
+ # Default parameter patterns based on business case type
833
+ type_defaults = {
834
+ BusinessCaseType.COST_OPTIMIZATION: ScenarioParameterMatrix(
835
+ timerange_days=30,
836
+ regional_scope='multi',
837
+ cost_focus='dual-metrics',
838
+ export_priority='pdf',
839
+ validation_level='enhanced',
840
+ business_justification=f"Cost optimization requires comprehensive analysis with dual cost perspectives for {scenario.display_name}"
841
+ ),
842
+ BusinessCaseType.RESOURCE_CLEANUP: ScenarioParameterMatrix(
843
+ timerange_days=7,
844
+ regional_scope='multi',
845
+ cost_focus='unblended',
846
+ export_priority='csv',
847
+ validation_level='basic',
848
+ business_justification=f"Resource cleanup requires recent data for active resource identification in {scenario.display_name}"
849
+ ),
850
+ BusinessCaseType.COMPLIANCE_FRAMEWORK: ScenarioParameterMatrix(
851
+ timerange_days=90,
852
+ regional_scope='global',
853
+ cost_focus='amortized',
854
+ export_priority='pdf',
855
+ validation_level='comprehensive',
856
+ business_justification=f"Compliance frameworks require extended analysis with comprehensive reporting for {scenario.display_name}"
857
+ ),
858
+ BusinessCaseType.SECURITY_ENHANCEMENT: ScenarioParameterMatrix(
859
+ timerange_days=30,
860
+ regional_scope='global',
861
+ cost_focus='unblended',
862
+ export_priority='markdown',
863
+ validation_level='comprehensive',
864
+ business_justification=f"Security enhancements require thorough analysis with documentation focus for {scenario.display_name}"
865
+ ),
866
+ BusinessCaseType.AUTOMATION_DEPLOYMENT: ScenarioParameterMatrix(
867
+ timerange_days=60,
868
+ regional_scope='multi',
869
+ cost_focus='amortized',
870
+ export_priority='json',
871
+ validation_level='enhanced',
872
+ business_justification=f"Automation deployment requires extended analysis with machine-readable output for {scenario.display_name}"
873
+ )
874
+ }
875
+
876
+ # Use type-based defaults or fallback to generic cost optimization pattern
877
+ default_matrix = type_defaults.get(scenario.business_case_type,
878
+ type_defaults[BusinessCaseType.COST_OPTIMIZATION])
879
+
880
+ # Override with scenario-specific intelligence where available
881
+ if scenario.risk_level.lower() == 'high':
882
+ # High risk scenarios need more comprehensive validation
883
+ default_matrix.validation_level = 'comprehensive'
884
+ default_matrix.timerange_days = max(default_matrix.timerange_days or 30, 90)
885
+
886
+ if scenario.risk_level.lower() == 'low':
887
+ # Low risk scenarios can use basic validation
888
+ default_matrix.validation_level = 'basic'
889
+ default_matrix.timerange_days = min(default_matrix.timerange_days or 30, 14)
890
+
891
+ return default_matrix
892
+
893
+ def add_custom_scenario_parameters(self, scenario_key: str, parameters: ScenarioParameterMatrix) -> None:
894
+ """
895
+ Add or override parameter matrix for a specific scenario.
896
+
897
+ Args:
898
+ scenario_key: Scenario identifier
899
+ parameters: Custom parameter matrix configuration
900
+ """
901
+ self.scenario_matrix[scenario_key] = parameters
902
+
903
+ def get_scenario_parameters(self, scenario_key: str) -> Optional[ScenarioParameterMatrix]:
904
+ """Get parameter matrix for specific scenario."""
905
+ return self.scenario_matrix.get(scenario_key)
906
+
907
+ def get_parameter_recommendations(self, scenario_key: str) -> Dict[str, ScenarioParameter]:
908
+ """Get intelligent parameter recommendations for scenario."""
909
+ scenario_params = self.get_scenario_parameters(scenario_key)
910
+ if not scenario_params:
911
+ return {}
912
+ return scenario_params.parameter_recommendations
913
+
914
+ def generate_scenario_help(self, scenario_key: str) -> str:
915
+ """Generate scenario-specific help text with parameter recommendations."""
916
+ scenario_params = self.get_scenario_parameters(scenario_key)
917
+ if not scenario_params:
918
+ return f"No parameter recommendations available for scenario: {scenario_key}"
919
+
920
+ recommendations = self.get_parameter_recommendations(scenario_key)
921
+ if not recommendations:
922
+ return f"Scenario {scenario_key}: Standard parameters apply"
923
+
924
+ help_lines = [f"Scenario '{scenario_key}' - Intelligent Parameter Recommendations:"]
925
+ help_lines.append("")
926
+
927
+ for param_key, param in recommendations.items():
928
+ if isinstance(param.optimal_value, bool) and param.optimal_value:
929
+ help_lines.append(f" {param.name}")
930
+ else:
931
+ help_lines.append(f" {param.name} {param.optimal_value}")
932
+ help_lines.append(f" → {param.business_justification}")
933
+
934
+ if param.alternative_values:
935
+ alternatives = ', '.join(str(v) for v in param.alternative_values)
936
+ help_lines.append(f" Alternatives: {alternatives}")
937
+ help_lines.append("")
938
+
939
+ return "\n".join(help_lines)
940
+
941
+ def validate_parameters_for_scenario(self, scenario_key: str, provided_params: Dict[str, Any]) -> Dict[str, str]:
942
+ """
943
+ Validate provided parameters against scenario recommendations.
944
+
945
+ Returns dict of warnings/suggestions for parameter optimization.
946
+ """
947
+ recommendations = self.get_parameter_recommendations(scenario_key)
948
+ if not recommendations:
949
+ return {}
950
+
951
+ suggestions = {}
952
+
953
+ # Check timerange optimization
954
+ if 'timerange' in recommendations:
955
+ optimal_timerange = recommendations['timerange'].optimal_value
956
+ provided_timerange = provided_params.get('time_range')
957
+
958
+ if provided_timerange and provided_timerange != optimal_timerange:
959
+ suggestions['timerange'] = f"Consider --time-range {optimal_timerange} for optimal {scenario_key} analysis (current: {provided_timerange})"
960
+
961
+ # Check export format optimization
962
+ if 'export_format' in recommendations:
963
+ optimal_export = recommendations['export_format'].name.replace('--', '')
964
+ export_formats = provided_params.get('export_formats', [])
965
+
966
+ if export_formats and optimal_export not in export_formats:
967
+ suggestions['export_format'] = f"Consider {optimal_export} export format for {scenario_key} analysis (optimal for business case)"
968
+
969
+ # Check cost focus optimization
970
+ if 'cost_focus' in recommendations:
971
+ optimal_focus = recommendations['cost_focus'].name.replace('--', '')
972
+ cost_focus_params = ['unblended', 'amortized', 'dual_metrics']
973
+ provided_focus = None
974
+
975
+ for focus_type in cost_focus_params:
976
+ if provided_params.get(focus_type):
977
+ provided_focus = focus_type
978
+ break
979
+
980
+ if not provided_focus:
981
+ suggestions['cost_focus'] = f"Consider {optimal_focus} cost perspective for {scenario_key} analysis"
982
+
983
+ return suggestions
984
+
985
+ def get_all_scenario_summaries(self) -> Dict[str, str]:
986
+ """Get summary of all scenarios with their optimal parameters."""
987
+ summaries = {}
988
+
989
+ for scenario_key in self.scenario_matrix.keys():
990
+ params = self.get_scenario_parameters(scenario_key)
991
+ if params:
992
+ summary_parts = []
993
+
994
+ if params.timerange_days:
995
+ summary_parts.append(f"{params.timerange_days}d analysis")
996
+ if params.regional_scope:
997
+ summary_parts.append(f"{params.regional_scope}-region")
998
+ if params.cost_focus:
999
+ summary_parts.append(f"{params.cost_focus} costs")
1000
+ if params.export_priority:
1001
+ summary_parts.append(f"{params.export_priority} export")
1002
+
1003
+ summaries[scenario_key] = " | ".join(summary_parts)
1004
+ else:
1005
+ summaries[scenario_key] = "Standard analysis"
1006
+
1007
+ return summaries
1008
+
1009
+
1010
+ # Global scenario matrix instance
1011
+ _scenario_matrix = None
1012
+
1013
+ def get_business_scenario_matrix() -> BusinessScenarioMatrix:
1014
+ """Get global business scenario matrix instance."""
1015
+ global _scenario_matrix
1016
+ if _scenario_matrix is None:
1017
+ _scenario_matrix = BusinessScenarioMatrix()
1018
+ return _scenario_matrix