runbooks 1.1.4__py3-none-any.whl → 1.1.6__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 +31 -2
- runbooks/__init___optimized.py +18 -4
- runbooks/_platform/__init__.py +1 -5
- runbooks/_platform/core/runbooks_wrapper.py +141 -138
- runbooks/aws2/accuracy_validator.py +812 -0
- runbooks/base.py +7 -0
- runbooks/cfat/assessment/compliance.py +1 -1
- runbooks/cfat/assessment/runner.py +1 -0
- runbooks/cfat/cloud_foundations_assessment.py +227 -239
- runbooks/cli/__init__.py +1 -1
- runbooks/cli/commands/cfat.py +64 -23
- runbooks/cli/commands/finops.py +1005 -54
- runbooks/cli/commands/inventory.py +135 -91
- runbooks/cli/commands/operate.py +9 -36
- runbooks/cli/commands/security.py +42 -18
- runbooks/cli/commands/validation.py +432 -18
- runbooks/cli/commands/vpc.py +81 -17
- runbooks/cli/registry.py +22 -10
- runbooks/cloudops/__init__.py +20 -27
- runbooks/cloudops/base.py +96 -107
- runbooks/cloudops/cost_optimizer.py +544 -542
- runbooks/cloudops/infrastructure_optimizer.py +5 -4
- runbooks/cloudops/interfaces.py +224 -225
- runbooks/cloudops/lifecycle_manager.py +5 -4
- runbooks/cloudops/mcp_cost_validation.py +252 -235
- runbooks/cloudops/models.py +78 -53
- runbooks/cloudops/monitoring_automation.py +5 -4
- runbooks/cloudops/notebook_framework.py +177 -213
- runbooks/cloudops/security_enforcer.py +125 -159
- runbooks/common/accuracy_validator.py +17 -12
- runbooks/common/aws_pricing.py +349 -326
- runbooks/common/aws_pricing_api.py +211 -212
- runbooks/common/aws_profile_manager.py +40 -36
- runbooks/common/aws_utils.py +74 -79
- runbooks/common/business_logic.py +126 -104
- runbooks/common/cli_decorators.py +36 -60
- runbooks/common/comprehensive_cost_explorer_integration.py +455 -463
- runbooks/common/cross_account_manager.py +197 -204
- runbooks/common/date_utils.py +27 -39
- runbooks/common/decorators.py +29 -19
- runbooks/common/dry_run_examples.py +173 -208
- runbooks/common/dry_run_framework.py +157 -155
- runbooks/common/enhanced_exception_handler.py +15 -4
- runbooks/common/enhanced_logging_example.py +50 -64
- runbooks/common/enhanced_logging_integration_example.py +65 -37
- runbooks/common/env_utils.py +16 -16
- runbooks/common/error_handling.py +40 -38
- runbooks/common/lazy_loader.py +41 -23
- runbooks/common/logging_integration_helper.py +79 -86
- runbooks/common/mcp_cost_explorer_integration.py +476 -493
- runbooks/common/mcp_integration.py +99 -79
- runbooks/common/memory_optimization.py +140 -118
- runbooks/common/module_cli_base.py +37 -58
- runbooks/common/organizations_client.py +175 -193
- runbooks/common/patterns.py +23 -25
- runbooks/common/performance_monitoring.py +67 -71
- runbooks/common/performance_optimization_engine.py +283 -274
- runbooks/common/profile_utils.py +111 -37
- runbooks/common/rich_utils.py +315 -141
- runbooks/common/sre_performance_suite.py +177 -186
- runbooks/enterprise/__init__.py +1 -1
- runbooks/enterprise/logging.py +144 -106
- runbooks/enterprise/security.py +187 -204
- runbooks/enterprise/validation.py +43 -56
- runbooks/finops/__init__.py +26 -30
- runbooks/finops/account_resolver.py +1 -1
- runbooks/finops/advanced_optimization_engine.py +980 -0
- runbooks/finops/automation_core.py +268 -231
- runbooks/finops/business_case_config.py +184 -179
- runbooks/finops/cli.py +660 -139
- runbooks/finops/commvault_ec2_analysis.py +157 -164
- runbooks/finops/compute_cost_optimizer.py +336 -320
- runbooks/finops/config.py +20 -20
- runbooks/finops/cost_optimizer.py +484 -618
- runbooks/finops/cost_processor.py +332 -214
- runbooks/finops/dashboard_runner.py +1006 -172
- runbooks/finops/ebs_cost_optimizer.py +991 -657
- runbooks/finops/elastic_ip_optimizer.py +317 -257
- runbooks/finops/enhanced_mcp_integration.py +340 -0
- runbooks/finops/enhanced_progress.py +32 -29
- runbooks/finops/enhanced_trend_visualization.py +3 -2
- runbooks/finops/enterprise_wrappers.py +223 -285
- runbooks/finops/executive_export.py +203 -160
- runbooks/finops/helpers.py +130 -288
- runbooks/finops/iam_guidance.py +1 -1
- runbooks/finops/infrastructure/__init__.py +80 -0
- runbooks/finops/infrastructure/commands.py +506 -0
- runbooks/finops/infrastructure/load_balancer_optimizer.py +866 -0
- runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +832 -0
- runbooks/finops/markdown_exporter.py +337 -174
- runbooks/finops/mcp_validator.py +1952 -0
- runbooks/finops/nat_gateway_optimizer.py +1512 -481
- runbooks/finops/network_cost_optimizer.py +657 -587
- runbooks/finops/notebook_utils.py +226 -188
- runbooks/finops/optimization_engine.py +1136 -0
- runbooks/finops/optimizer.py +19 -23
- runbooks/finops/rds_snapshot_optimizer.py +367 -411
- runbooks/finops/reservation_optimizer.py +427 -363
- runbooks/finops/scenario_cli_integration.py +64 -65
- runbooks/finops/scenarios.py +1277 -438
- runbooks/finops/schemas.py +218 -182
- runbooks/finops/snapshot_manager.py +2289 -0
- runbooks/finops/types.py +3 -3
- runbooks/finops/validation_framework.py +259 -265
- runbooks/finops/vpc_cleanup_exporter.py +189 -144
- runbooks/finops/vpc_cleanup_optimizer.py +591 -573
- runbooks/finops/workspaces_analyzer.py +171 -182
- runbooks/integration/__init__.py +89 -0
- runbooks/integration/mcp_integration.py +1920 -0
- runbooks/inventory/CLAUDE.md +816 -0
- runbooks/inventory/__init__.py +2 -2
- runbooks/inventory/aws_decorators.py +2 -3
- runbooks/inventory/check_cloudtrail_compliance.py +2 -4
- runbooks/inventory/check_controltower_readiness.py +152 -151
- runbooks/inventory/check_landingzone_readiness.py +85 -84
- runbooks/inventory/cloud_foundations_integration.py +144 -149
- runbooks/inventory/collectors/aws_comprehensive.py +1 -1
- runbooks/inventory/collectors/aws_networking.py +109 -99
- runbooks/inventory/collectors/base.py +4 -0
- runbooks/inventory/core/collector.py +495 -313
- runbooks/inventory/core/formatter.py +11 -0
- runbooks/inventory/draw_org_structure.py +8 -9
- runbooks/inventory/drift_detection_cli.py +69 -96
- runbooks/inventory/ec2_vpc_utils.py +2 -2
- runbooks/inventory/find_cfn_drift_detection.py +5 -7
- runbooks/inventory/find_cfn_orphaned_stacks.py +7 -9
- runbooks/inventory/find_cfn_stackset_drift.py +5 -6
- runbooks/inventory/find_ec2_security_groups.py +48 -42
- runbooks/inventory/find_landingzone_versions.py +4 -6
- runbooks/inventory/find_vpc_flow_logs.py +7 -9
- runbooks/inventory/inventory_mcp_cli.py +48 -46
- runbooks/inventory/inventory_modules.py +103 -91
- runbooks/inventory/list_cfn_stacks.py +9 -10
- runbooks/inventory/list_cfn_stackset_operation_results.py +1 -3
- runbooks/inventory/list_cfn_stackset_operations.py +79 -57
- runbooks/inventory/list_cfn_stacksets.py +8 -10
- runbooks/inventory/list_config_recorders_delivery_channels.py +49 -39
- runbooks/inventory/list_ds_directories.py +65 -53
- runbooks/inventory/list_ec2_availability_zones.py +2 -4
- runbooks/inventory/list_ec2_ebs_volumes.py +32 -35
- runbooks/inventory/list_ec2_instances.py +23 -28
- runbooks/inventory/list_ecs_clusters_and_tasks.py +26 -34
- runbooks/inventory/list_elbs_load_balancers.py +22 -20
- runbooks/inventory/list_enis_network_interfaces.py +26 -33
- runbooks/inventory/list_guardduty_detectors.py +2 -4
- runbooks/inventory/list_iam_policies.py +2 -4
- runbooks/inventory/list_iam_roles.py +5 -7
- runbooks/inventory/list_iam_saml_providers.py +4 -6
- runbooks/inventory/list_lambda_functions.py +38 -38
- runbooks/inventory/list_org_accounts.py +6 -8
- runbooks/inventory/list_org_accounts_users.py +55 -44
- runbooks/inventory/list_rds_db_instances.py +31 -33
- runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
- runbooks/inventory/list_route53_hosted_zones.py +3 -5
- runbooks/inventory/list_servicecatalog_provisioned_products.py +37 -41
- runbooks/inventory/list_sns_topics.py +2 -4
- runbooks/inventory/list_ssm_parameters.py +4 -7
- runbooks/inventory/list_vpc_subnets.py +2 -4
- runbooks/inventory/list_vpcs.py +7 -10
- runbooks/inventory/mcp_inventory_validator.py +554 -468
- runbooks/inventory/mcp_vpc_validator.py +359 -442
- runbooks/inventory/organizations_discovery.py +63 -55
- runbooks/inventory/recover_cfn_stack_ids.py +7 -8
- runbooks/inventory/requirements.txt +0 -1
- runbooks/inventory/rich_inventory_display.py +35 -34
- runbooks/inventory/run_on_multi_accounts.py +3 -5
- runbooks/inventory/unified_validation_engine.py +281 -253
- runbooks/inventory/verify_ec2_security_groups.py +1 -1
- runbooks/inventory/vpc_analyzer.py +735 -697
- runbooks/inventory/vpc_architecture_validator.py +293 -348
- runbooks/inventory/vpc_dependency_analyzer.py +384 -380
- runbooks/inventory/vpc_flow_analyzer.py +1 -1
- runbooks/main.py +49 -34
- runbooks/main_final.py +91 -60
- runbooks/main_minimal.py +22 -10
- runbooks/main_optimized.py +131 -100
- runbooks/main_ultra_minimal.py +7 -2
- runbooks/mcp/__init__.py +36 -0
- runbooks/mcp/integration.py +679 -0
- runbooks/monitoring/performance_monitor.py +9 -4
- runbooks/operate/dynamodb_operations.py +3 -1
- runbooks/operate/ec2_operations.py +145 -137
- runbooks/operate/iam_operations.py +146 -152
- runbooks/operate/networking_cost_heatmap.py +29 -8
- runbooks/operate/rds_operations.py +223 -254
- runbooks/operate/s3_operations.py +107 -118
- runbooks/operate/vpc_operations.py +646 -616
- runbooks/remediation/base.py +1 -1
- runbooks/remediation/commons.py +10 -7
- runbooks/remediation/commvault_ec2_analysis.py +70 -66
- runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
- runbooks/remediation/multi_account.py +24 -21
- runbooks/remediation/rds_snapshot_list.py +86 -60
- runbooks/remediation/remediation_cli.py +92 -146
- runbooks/remediation/universal_account_discovery.py +83 -79
- runbooks/remediation/workspaces_list.py +46 -41
- runbooks/security/__init__.py +19 -0
- runbooks/security/assessment_runner.py +1150 -0
- runbooks/security/baseline_checker.py +812 -0
- runbooks/security/cloudops_automation_security_validator.py +509 -535
- runbooks/security/compliance_automation_engine.py +17 -17
- runbooks/security/config/__init__.py +2 -2
- runbooks/security/config/compliance_config.py +50 -50
- runbooks/security/config_template_generator.py +63 -76
- runbooks/security/enterprise_security_framework.py +1 -1
- runbooks/security/executive_security_dashboard.py +519 -508
- runbooks/security/multi_account_security_controls.py +959 -1210
- runbooks/security/real_time_security_monitor.py +422 -444
- runbooks/security/security_baseline_tester.py +1 -1
- runbooks/security/security_cli.py +143 -112
- runbooks/security/test_2way_validation.py +439 -0
- runbooks/security/two_way_validation_framework.py +852 -0
- runbooks/sre/production_monitoring_framework.py +167 -177
- runbooks/tdd/__init__.py +15 -0
- runbooks/tdd/cli.py +1071 -0
- runbooks/utils/__init__.py +14 -17
- runbooks/utils/logger.py +7 -2
- runbooks/utils/version_validator.py +50 -47
- runbooks/validation/__init__.py +6 -6
- runbooks/validation/cli.py +9 -3
- runbooks/validation/comprehensive_2way_validator.py +745 -704
- runbooks/validation/mcp_validator.py +906 -228
- runbooks/validation/terraform_citations_validator.py +104 -115
- runbooks/validation/terraform_drift_detector.py +461 -454
- runbooks/vpc/README.md +617 -0
- runbooks/vpc/__init__.py +8 -1
- runbooks/vpc/analyzer.py +577 -0
- runbooks/vpc/cleanup_wrapper.py +476 -413
- runbooks/vpc/cli_cloudtrail_commands.py +339 -0
- runbooks/vpc/cli_mcp_validation_commands.py +480 -0
- runbooks/vpc/cloudtrail_audit_integration.py +717 -0
- runbooks/vpc/config.py +92 -97
- runbooks/vpc/cost_engine.py +411 -148
- runbooks/vpc/cost_explorer_integration.py +553 -0
- runbooks/vpc/cross_account_session.py +101 -106
- runbooks/vpc/enhanced_mcp_validation.py +917 -0
- runbooks/vpc/eni_gate_validator.py +961 -0
- runbooks/vpc/heatmap_engine.py +185 -160
- runbooks/vpc/mcp_no_eni_validator.py +680 -639
- runbooks/vpc/nat_gateway_optimizer.py +358 -0
- runbooks/vpc/networking_wrapper.py +15 -8
- runbooks/vpc/pdca_remediation_planner.py +528 -0
- runbooks/vpc/performance_optimized_analyzer.py +219 -231
- runbooks/vpc/runbooks_adapter.py +1167 -241
- runbooks/vpc/tdd_red_phase_stubs.py +601 -0
- runbooks/vpc/test_data_loader.py +358 -0
- runbooks/vpc/tests/conftest.py +314 -4
- runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
- runbooks/vpc/tests/test_cost_engine.py +0 -2
- runbooks/vpc/topology_generator.py +326 -0
- runbooks/vpc/unified_scenarios.py +1297 -1124
- runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
- runbooks-1.1.6.dist-info/METADATA +327 -0
- runbooks-1.1.6.dist-info/RECORD +489 -0
- runbooks/finops/README.md +0 -414
- runbooks/finops/accuracy_cross_validator.py +0 -647
- runbooks/finops/business_cases.py +0 -950
- runbooks/finops/dashboard_router.py +0 -922
- runbooks/finops/ebs_optimizer.py +0 -973
- runbooks/finops/embedded_mcp_validator.py +0 -1629
- runbooks/finops/enhanced_dashboard_runner.py +0 -527
- runbooks/finops/finops_dashboard.py +0 -584
- runbooks/finops/finops_scenarios.py +0 -1218
- runbooks/finops/legacy_migration.py +0 -730
- runbooks/finops/multi_dashboard.py +0 -1519
- runbooks/finops/single_dashboard.py +0 -1113
- runbooks/finops/unlimited_scenarios.py +0 -393
- runbooks-1.1.4.dist-info/METADATA +0 -800
- runbooks-1.1.4.dist-info/RECORD +0 -468
- {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/WHEEL +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/entry_points.txt +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/licenses/LICENSE +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/top_level.txt +0 -0
@@ -2,18 +2,18 @@
|
|
2
2
|
"""
|
3
3
|
CloudOps Notebook Framework - Enterprise Consolidation Infrastructure
|
4
4
|
|
5
|
-
Provides reusable components for consolidating 64 individual notebooks into
|
5
|
+
Provides reusable components for consolidating 64 individual notebooks into
|
6
6
|
12-15 production scenarios with enterprise-grade functionality.
|
7
7
|
|
8
8
|
Strategic Alignment:
|
9
|
-
- Follows Rich CLI standards from rich_utils.py
|
9
|
+
- Follows Rich CLI standards from rich_utils.py
|
10
10
|
- Handles authentication failures gracefully (no hardcoding/assumptions)
|
11
11
|
- Dual-purpose interface: executive summary + technical details
|
12
12
|
- Type-safe validation with Pydantic v2
|
13
13
|
|
14
14
|
Key Features:
|
15
15
|
- Authentication flow management with comprehensive error handling
|
16
|
-
- Executive and technical reporting modes
|
16
|
+
- Executive and technical reporting modes
|
17
17
|
- Multi-scenario consolidation support
|
18
18
|
- MCP integration readiness
|
19
19
|
- Performance monitoring and benchmarking
|
@@ -31,36 +31,60 @@ from enum import Enum
|
|
31
31
|
import boto3
|
32
32
|
import pandas as pd
|
33
33
|
from botocore.exceptions import (
|
34
|
-
ClientError,
|
35
|
-
|
34
|
+
ClientError,
|
35
|
+
NoCredentialsError,
|
36
|
+
ProfileNotFound,
|
37
|
+
TokenRetrievalError,
|
38
|
+
UnauthorizedSSOTokenError,
|
36
39
|
)
|
37
40
|
from pydantic import BaseModel, Field, ValidationError
|
38
41
|
|
39
42
|
from runbooks.common.rich_utils import (
|
40
|
-
console,
|
41
|
-
|
42
|
-
|
43
|
+
console,
|
44
|
+
print_header,
|
45
|
+
print_success,
|
46
|
+
print_error,
|
47
|
+
print_warning,
|
48
|
+
print_info,
|
49
|
+
create_table,
|
50
|
+
create_progress_bar,
|
51
|
+
format_cost,
|
52
|
+
create_panel,
|
53
|
+
STATUS_INDICATORS,
|
54
|
+
print_json,
|
55
|
+
create_columns,
|
56
|
+
confirm_action,
|
43
57
|
)
|
44
58
|
from runbooks.common.profile_utils import (
|
45
|
-
get_profile_for_operation,
|
46
|
-
|
59
|
+
get_profile_for_operation,
|
60
|
+
create_cost_session,
|
61
|
+
create_management_session,
|
62
|
+
create_operational_session,
|
47
63
|
)
|
48
64
|
from .models import (
|
49
|
-
BusinessScenario,
|
50
|
-
|
65
|
+
BusinessScenario,
|
66
|
+
ExecutionMode,
|
67
|
+
RiskLevel,
|
68
|
+
ProfileConfiguration,
|
69
|
+
CloudOpsExecutionResult,
|
70
|
+
BusinessMetrics,
|
71
|
+
ResourceImpact,
|
72
|
+
CostOptimizationResult,
|
51
73
|
)
|
52
74
|
from .base import CloudOpsBase, PerformanceBenchmark
|
53
75
|
|
54
76
|
|
55
77
|
class NotebookMode(str, Enum):
|
56
78
|
"""Execution modes for notebook interface."""
|
79
|
+
|
57
80
|
EXECUTIVE = "executive" # Executive summary for business stakeholders
|
58
|
-
TECHNICAL = "technical" # Technical details for engineering teams
|
81
|
+
TECHNICAL = "technical" # Technical details for engineering teams
|
59
82
|
COMPREHENSIVE = "comprehensive" # Both executive and technical views
|
60
83
|
|
61
84
|
|
62
85
|
class AuthenticationStatus(str, Enum):
|
63
86
|
"""AWS authentication status tracking."""
|
87
|
+
|
64
88
|
SUCCESS = "success"
|
65
89
|
EXPIRED_TOKEN = "expired_token"
|
66
90
|
INVALID_PROFILE = "invalid_profile"
|
@@ -72,12 +96,13 @@ class AuthenticationStatus(str, Enum):
|
|
72
96
|
@dataclass
|
73
97
|
class AuthenticationResult:
|
74
98
|
"""Results of AWS authentication validation."""
|
99
|
+
|
75
100
|
status: AuthenticationStatus
|
76
101
|
profile_name: str
|
77
102
|
account_id: Optional[str] = None
|
78
103
|
error_message: Optional[str] = None
|
79
104
|
remediation_steps: List[str] = None
|
80
|
-
|
105
|
+
|
81
106
|
def __post_init__(self):
|
82
107
|
if self.remediation_steps is None:
|
83
108
|
self.remediation_steps = []
|
@@ -85,17 +110,18 @@ class AuthenticationResult:
|
|
85
110
|
|
86
111
|
class ScenarioMetadata(BaseModel):
|
87
112
|
"""Metadata for consolidated notebook scenarios."""
|
113
|
+
|
88
114
|
scenario_id: str = Field(description="Unique scenario identifier")
|
89
115
|
scenario_name: str = Field(description="Human-readable scenario name")
|
90
116
|
scenario_type: BusinessScenario = Field(description="Business scenario category")
|
91
117
|
consolidated_notebooks: List[str] = Field(description="List of individual notebooks consolidated")
|
92
|
-
|
118
|
+
|
93
119
|
# Executive Information
|
94
120
|
business_objective: str = Field(description="High-level business objective")
|
95
121
|
expected_outcomes: List[str] = Field(description="Expected business outcomes")
|
96
122
|
stakeholders: List[str] = Field(description="Key stakeholders", default=[])
|
97
|
-
|
98
|
-
# Technical Information
|
123
|
+
|
124
|
+
# Technical Information
|
99
125
|
aws_services: List[str] = Field(description="AWS services utilized")
|
100
126
|
estimated_execution_time: int = Field(description="Estimated execution time in minutes")
|
101
127
|
prerequisites: List[str] = Field(description="Technical prerequisites", default=[])
|
@@ -104,21 +130,21 @@ class ScenarioMetadata(BaseModel):
|
|
104
130
|
class NotebookFramework(CloudOpsBase):
|
105
131
|
"""
|
106
132
|
Enterprise notebook framework for consolidated CloudOps scenarios.
|
107
|
-
|
133
|
+
|
108
134
|
Provides comprehensive infrastructure for transforming individual notebooks
|
109
135
|
into enterprise-grade consolidated scenarios with dual executive/technical interfaces.
|
110
136
|
"""
|
111
|
-
|
137
|
+
|
112
138
|
def __init__(
|
113
|
-
self,
|
139
|
+
self,
|
114
140
|
profile: str = "default",
|
115
141
|
mode: NotebookMode = NotebookMode.COMPREHENSIVE,
|
116
142
|
dry_run: bool = True,
|
117
|
-
validate_auth: bool = True
|
143
|
+
validate_auth: bool = True,
|
118
144
|
):
|
119
145
|
"""
|
120
146
|
Initialize notebook framework.
|
121
|
-
|
147
|
+
|
122
148
|
Args:
|
123
149
|
profile: AWS profile for authentication
|
124
150
|
mode: Notebook execution mode (executive/technical/comprehensive)
|
@@ -128,7 +154,7 @@ class NotebookFramework(CloudOpsBase):
|
|
128
154
|
self.mode = mode
|
129
155
|
self.validate_auth = validate_auth
|
130
156
|
self.auth_status: Optional[AuthenticationResult] = None
|
131
|
-
|
157
|
+
|
132
158
|
# Initialize base class (handles AWS session setup)
|
133
159
|
# Note: This may raise exceptions for authentication issues
|
134
160
|
try:
|
@@ -141,14 +167,14 @@ class NotebookFramework(CloudOpsBase):
|
|
141
167
|
status=AuthenticationStatus.UNKNOWN_ERROR,
|
142
168
|
profile_name=profile,
|
143
169
|
error_message=str(e),
|
144
|
-
remediation_steps=self._get_generic_remediation_steps()
|
170
|
+
remediation_steps=self._get_generic_remediation_steps(),
|
145
171
|
)
|
146
172
|
self.session = None # Ensure session is None on failure
|
147
|
-
|
173
|
+
|
148
174
|
def _validate_authentication(self) -> AuthenticationResult:
|
149
175
|
"""
|
150
176
|
Comprehensive AWS authentication validation with detailed error handling.
|
151
|
-
|
177
|
+
|
152
178
|
Returns:
|
153
179
|
AuthenticationResult with status and remediation guidance
|
154
180
|
"""
|
@@ -158,25 +184,23 @@ class NotebookFramework(CloudOpsBase):
|
|
158
184
|
status=AuthenticationStatus.NO_CREDENTIALS,
|
159
185
|
profile_name=self.profile,
|
160
186
|
error_message="No AWS session available",
|
161
|
-
remediation_steps=self._get_generic_remediation_steps()
|
187
|
+
remediation_steps=self._get_generic_remediation_steps(),
|
162
188
|
)
|
163
|
-
|
189
|
+
|
164
190
|
# Test authentication by calling STS
|
165
|
-
sts_client = self.session.client(
|
191
|
+
sts_client = self.session.client("sts")
|
166
192
|
identity = sts_client.get_caller_identity()
|
167
|
-
|
168
|
-
account_id = identity.get(
|
169
|
-
user_arn = identity.get(
|
170
|
-
|
193
|
+
|
194
|
+
account_id = identity.get("Account")
|
195
|
+
user_arn = identity.get("Arn")
|
196
|
+
|
171
197
|
print_success(f"Authentication successful for profile: {self.profile}")
|
172
198
|
print_info(f"Account: {account_id}, Identity: {user_arn}")
|
173
|
-
|
199
|
+
|
174
200
|
return AuthenticationResult(
|
175
|
-
status=AuthenticationStatus.SUCCESS,
|
176
|
-
profile_name=self.profile,
|
177
|
-
account_id=account_id
|
201
|
+
status=AuthenticationStatus.SUCCESS, profile_name=self.profile, account_id=account_id
|
178
202
|
)
|
179
|
-
|
203
|
+
|
180
204
|
except UnauthorizedSSOTokenError:
|
181
205
|
return AuthenticationResult(
|
182
206
|
status=AuthenticationStatus.EXPIRED_TOKEN,
|
@@ -185,10 +209,10 @@ class NotebookFramework(CloudOpsBase):
|
|
185
209
|
remediation_steps=[
|
186
210
|
"Run: aws sso login",
|
187
211
|
"Ensure your AWS SSO session is active",
|
188
|
-
f"Verify profile '{self.profile}' is configured for SSO"
|
189
|
-
]
|
212
|
+
f"Verify profile '{self.profile}' is configured for SSO",
|
213
|
+
],
|
190
214
|
)
|
191
|
-
|
215
|
+
|
192
216
|
except TokenRetrievalError as e:
|
193
217
|
return AuthenticationResult(
|
194
218
|
status=AuthenticationStatus.EXPIRED_TOKEN,
|
@@ -197,10 +221,10 @@ class NotebookFramework(CloudOpsBase):
|
|
197
221
|
remediation_steps=[
|
198
222
|
"Run: aws sso login",
|
199
223
|
"Check your internet connection",
|
200
|
-
"Verify AWS SSO configuration"
|
201
|
-
]
|
224
|
+
"Verify AWS SSO configuration",
|
225
|
+
],
|
202
226
|
)
|
203
|
-
|
227
|
+
|
204
228
|
except ProfileNotFound:
|
205
229
|
return AuthenticationResult(
|
206
230
|
status=AuthenticationStatus.INVALID_PROFILE,
|
@@ -209,10 +233,10 @@ class NotebookFramework(CloudOpsBase):
|
|
209
233
|
remediation_steps=[
|
210
234
|
f"Check if profile '{self.profile}' exists in ~/.aws/config",
|
211
235
|
"Run: aws configure list-profiles",
|
212
|
-
"Configure the profile using: aws configure sso"
|
213
|
-
]
|
236
|
+
"Configure the profile using: aws configure sso",
|
237
|
+
],
|
214
238
|
)
|
215
|
-
|
239
|
+
|
216
240
|
except NoCredentialsError:
|
217
241
|
return AuthenticationResult(
|
218
242
|
status=AuthenticationStatus.NO_CREDENTIALS,
|
@@ -221,14 +245,14 @@ class NotebookFramework(CloudOpsBase):
|
|
221
245
|
remediation_steps=[
|
222
246
|
"Configure AWS credentials using: aws configure",
|
223
247
|
"Or set up SSO using: aws configure sso",
|
224
|
-
"Verify AWS credentials are properly configured"
|
225
|
-
]
|
248
|
+
"Verify AWS credentials are properly configured",
|
249
|
+
],
|
226
250
|
)
|
227
|
-
|
251
|
+
|
228
252
|
except ClientError as e:
|
229
|
-
error_code = e.response.get(
|
230
|
-
|
231
|
-
if error_code in [
|
253
|
+
error_code = e.response.get("Error", {}).get("Code", "Unknown")
|
254
|
+
|
255
|
+
if error_code in ["AccessDenied", "UnauthorizedOperation"]:
|
232
256
|
return AuthenticationResult(
|
233
257
|
status=AuthenticationStatus.PERMISSION_DENIED,
|
234
258
|
profile_name=self.profile,
|
@@ -236,113 +260,97 @@ class NotebookFramework(CloudOpsBase):
|
|
236
260
|
remediation_steps=[
|
237
261
|
"Verify your AWS profile has sufficient permissions",
|
238
262
|
"Contact your AWS administrator for access",
|
239
|
-
"Check IAM policies attached to your role/user"
|
240
|
-
]
|
263
|
+
"Check IAM policies attached to your role/user",
|
264
|
+
],
|
241
265
|
)
|
242
266
|
else:
|
243
267
|
return AuthenticationResult(
|
244
268
|
status=AuthenticationStatus.UNKNOWN_ERROR,
|
245
269
|
profile_name=self.profile,
|
246
270
|
error_message=f"AWS API error: {str(e)}",
|
247
|
-
remediation_steps=self._get_generic_remediation_steps()
|
271
|
+
remediation_steps=self._get_generic_remediation_steps(),
|
248
272
|
)
|
249
|
-
|
273
|
+
|
250
274
|
except Exception as e:
|
251
275
|
return AuthenticationResult(
|
252
276
|
status=AuthenticationStatus.UNKNOWN_ERROR,
|
253
277
|
profile_name=self.profile,
|
254
278
|
error_message=f"Unexpected error: {str(e)}",
|
255
|
-
remediation_steps=self._get_generic_remediation_steps()
|
279
|
+
remediation_steps=self._get_generic_remediation_steps(),
|
256
280
|
)
|
257
|
-
|
281
|
+
|
258
282
|
def _get_generic_remediation_steps(self) -> List[str]:
|
259
283
|
"""Get generic remediation steps for authentication issues."""
|
260
284
|
return [
|
261
285
|
"Check AWS profile configuration: aws configure list-profiles",
|
262
286
|
"Verify credentials: aws sts get-caller-identity",
|
263
|
-
"For SSO profiles, login again: aws sso login",
|
264
|
-
"Contact your AWS administrator if issues persist"
|
287
|
+
"For SSO profiles, login again: aws sso login",
|
288
|
+
"Contact your AWS administrator if issues persist",
|
265
289
|
]
|
266
|
-
|
290
|
+
|
267
291
|
def display_authentication_status(self) -> None:
|
268
292
|
"""Display authentication status with Rich CLI formatting."""
|
269
293
|
if not self.auth_status:
|
270
294
|
print_warning("Authentication status not available")
|
271
295
|
return
|
272
|
-
|
296
|
+
|
273
297
|
status_colors = {
|
274
298
|
AuthenticationStatus.SUCCESS: "green",
|
275
299
|
AuthenticationStatus.EXPIRED_TOKEN: "yellow",
|
276
300
|
AuthenticationStatus.INVALID_PROFILE: "red",
|
277
|
-
AuthenticationStatus.NO_CREDENTIALS: "red",
|
301
|
+
AuthenticationStatus.NO_CREDENTIALS: "red",
|
278
302
|
AuthenticationStatus.PERMISSION_DENIED: "red",
|
279
|
-
AuthenticationStatus.UNKNOWN_ERROR: "red"
|
303
|
+
AuthenticationStatus.UNKNOWN_ERROR: "red",
|
280
304
|
}
|
281
|
-
|
305
|
+
|
282
306
|
status_icons = {
|
283
307
|
AuthenticationStatus.SUCCESS: "✅",
|
284
308
|
AuthenticationStatus.EXPIRED_TOKEN: "⚠️",
|
285
309
|
AuthenticationStatus.INVALID_PROFILE: "❌",
|
286
310
|
AuthenticationStatus.NO_CREDENTIALS: "❌",
|
287
311
|
AuthenticationStatus.PERMISSION_DENIED: "🔒",
|
288
|
-
AuthenticationStatus.UNKNOWN_ERROR: "❓"
|
312
|
+
AuthenticationStatus.UNKNOWN_ERROR: "❓",
|
289
313
|
}
|
290
|
-
|
314
|
+
|
291
315
|
status_color = status_colors.get(self.auth_status.status, "white")
|
292
316
|
status_icon = status_icons.get(self.auth_status.status, "?")
|
293
|
-
|
317
|
+
|
294
318
|
# Authentication Status Panel
|
295
319
|
status_content = (
|
296
320
|
f"Profile: {self.auth_status.profile_name}\n"
|
297
321
|
f"Status: {status_icon} {self.auth_status.status.value.replace('_', ' ').title()}"
|
298
322
|
)
|
299
|
-
|
323
|
+
|
300
324
|
if self.auth_status.account_id:
|
301
325
|
status_content += f"\nAccount ID: {self.auth_status.account_id}"
|
302
|
-
|
326
|
+
|
303
327
|
if self.auth_status.error_message:
|
304
328
|
status_content += f"\nError: {self.auth_status.error_message}"
|
305
|
-
|
306
|
-
auth_panel = create_panel(
|
307
|
-
status_content,
|
308
|
-
title="AWS Authentication Status",
|
309
|
-
border_style=status_color
|
310
|
-
)
|
329
|
+
|
330
|
+
auth_panel = create_panel(status_content, title="AWS Authentication Status", border_style=status_color)
|
311
331
|
console.print(auth_panel)
|
312
|
-
|
332
|
+
|
313
333
|
# Remediation Steps (if authentication failed)
|
314
|
-
if
|
315
|
-
self.auth_status.remediation_steps):
|
316
|
-
|
334
|
+
if self.auth_status.status != AuthenticationStatus.SUCCESS and self.auth_status.remediation_steps:
|
317
335
|
remediation_text = "\n".join([f"• {step}" for step in self.auth_status.remediation_steps])
|
318
|
-
remediation_panel = create_panel(
|
319
|
-
remediation_text,
|
320
|
-
title="Remediation Steps",
|
321
|
-
border_style="blue"
|
322
|
-
)
|
336
|
+
remediation_panel = create_panel(remediation_text, title="Remediation Steps", border_style="blue")
|
323
337
|
console.print(remediation_panel)
|
324
|
-
|
338
|
+
|
325
339
|
def is_authenticated(self) -> bool:
|
326
340
|
"""Check if AWS authentication is successful."""
|
327
|
-
return
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
def create_scenario_header(
|
332
|
-
self,
|
333
|
-
metadata: ScenarioMetadata,
|
334
|
-
show_consolidated_info: bool = True
|
335
|
-
) -> None:
|
341
|
+
return self.auth_status and self.auth_status.status == AuthenticationStatus.SUCCESS and self.session is not None
|
342
|
+
|
343
|
+
def create_scenario_header(self, metadata: ScenarioMetadata, show_consolidated_info: bool = True) -> None:
|
336
344
|
"""
|
337
345
|
Create rich scenario header for consolidated notebooks.
|
338
|
-
|
346
|
+
|
339
347
|
Args:
|
340
348
|
metadata: Scenario metadata with business and technical information
|
341
349
|
show_consolidated_info: Show information about consolidated notebooks
|
342
350
|
"""
|
343
351
|
# Main scenario header
|
344
352
|
print_header(f"CloudOps Scenario: {metadata.scenario_name}", "latest version")
|
345
|
-
|
353
|
+
|
346
354
|
# Executive Summary (always shown)
|
347
355
|
if self.mode in [NotebookMode.EXECUTIVE, NotebookMode.COMPREHENSIVE]:
|
348
356
|
exec_content = (
|
@@ -351,55 +359,42 @@ class NotebookFramework(CloudOpsBase):
|
|
351
359
|
f"⏱️ Estimated Time: {metadata.estimated_execution_time} minutes\n"
|
352
360
|
f"🛡️ Execution Mode: {'🔍 Analysis Only' if self.dry_run else '⚡ Live Execution'}"
|
353
361
|
)
|
354
|
-
|
362
|
+
|
355
363
|
if metadata.expected_outcomes:
|
356
364
|
exec_content += f"\n\n📈 Expected Outcomes:\n"
|
357
365
|
exec_content += "\n".join([f"• {outcome}" for outcome in metadata.expected_outcomes])
|
358
|
-
|
359
|
-
exec_panel = create_panel(
|
360
|
-
exec_content,
|
361
|
-
title="Executive Summary",
|
362
|
-
border_style="cyan"
|
363
|
-
)
|
366
|
+
|
367
|
+
exec_panel = create_panel(exec_content, title="Executive Summary", border_style="cyan")
|
364
368
|
console.print(exec_panel)
|
365
|
-
|
369
|
+
|
366
370
|
# Technical Details (technical/comprehensive modes)
|
367
371
|
if self.mode in [NotebookMode.TECHNICAL, NotebookMode.COMPREHENSIVE]:
|
368
372
|
tech_content = (
|
369
|
-
f"🔧 AWS Services: {', '.join(metadata.aws_services)}\n"
|
370
|
-
f"📝 Scenario ID: {metadata.scenario_id}"
|
373
|
+
f"🔧 AWS Services: {', '.join(metadata.aws_services)}\n📝 Scenario ID: {metadata.scenario_id}"
|
371
374
|
)
|
372
|
-
|
375
|
+
|
373
376
|
if metadata.prerequisites:
|
374
377
|
tech_content += f"\n\n✅ Prerequisites:\n"
|
375
378
|
tech_content += "\n".join([f"• {prereq}" for prereq in metadata.prerequisites])
|
376
|
-
|
379
|
+
|
377
380
|
if show_consolidated_info and metadata.consolidated_notebooks:
|
378
381
|
tech_content += f"\n\n📚 Consolidated Notebooks ({len(metadata.consolidated_notebooks)}):\n"
|
379
382
|
tech_content += "\n".join([f"• {nb}" for nb in metadata.consolidated_notebooks])
|
380
|
-
|
381
|
-
tech_panel = create_panel(
|
382
|
-
tech_content,
|
383
|
-
title="Technical Information",
|
384
|
-
border_style="blue"
|
385
|
-
)
|
383
|
+
|
384
|
+
tech_panel = create_panel(tech_content, title="Technical Information", border_style="blue")
|
386
385
|
console.print(tech_panel)
|
387
|
-
|
388
|
-
def create_results_summary(
|
389
|
-
self,
|
390
|
-
result: CloudOpsExecutionResult,
|
391
|
-
show_detailed_metrics: bool = None
|
392
|
-
) -> None:
|
386
|
+
|
387
|
+
def create_results_summary(self, result: CloudOpsExecutionResult, show_detailed_metrics: bool = None) -> None:
|
393
388
|
"""
|
394
389
|
Create comprehensive results summary with mode-appropriate detail level.
|
395
|
-
|
390
|
+
|
396
391
|
Args:
|
397
392
|
result: CloudOps execution result
|
398
393
|
show_detailed_metrics: Override detail level (None = use mode default)
|
399
394
|
"""
|
400
395
|
if show_detailed_metrics is None:
|
401
396
|
show_detailed_metrics = self.mode in [NotebookMode.TECHNICAL, NotebookMode.COMPREHENSIVE]
|
402
|
-
|
397
|
+
|
403
398
|
# Executive Summary (always shown)
|
404
399
|
exec_summary = (
|
405
400
|
f"📊 Scenario: {result.scenario_name}\n"
|
@@ -409,20 +404,18 @@ class NotebookFramework(CloudOpsBase):
|
|
409
404
|
f"💰 Monthly Savings: {format_cost(result.business_metrics.total_monthly_savings)}\n"
|
410
405
|
f"⏱️ Execution Time: {result.execution_time:.1f}s"
|
411
406
|
)
|
412
|
-
|
407
|
+
|
413
408
|
if result.business_metrics.roi_percentage:
|
414
409
|
exec_summary += f"\n📈 ROI: {result.business_metrics.roi_percentage:.1f}%"
|
415
|
-
|
410
|
+
|
416
411
|
if not result.success and result.error_message:
|
417
412
|
exec_summary += f"\n❌ Error: {result.error_message}"
|
418
|
-
|
413
|
+
|
419
414
|
exec_panel = create_panel(
|
420
|
-
exec_summary,
|
421
|
-
title="Execution Results Summary",
|
422
|
-
border_style="green" if result.success else "red"
|
415
|
+
exec_summary, title="Execution Results Summary", border_style="green" if result.success else "red"
|
423
416
|
)
|
424
417
|
console.print(exec_panel)
|
425
|
-
|
418
|
+
|
426
419
|
# Detailed Metrics (technical/comprehensive modes)
|
427
420
|
if show_detailed_metrics and result.business_metrics:
|
428
421
|
metrics_table = create_table(
|
@@ -430,72 +423,54 @@ class NotebookFramework(CloudOpsBase):
|
|
430
423
|
columns=[
|
431
424
|
{"name": "Metric", "style": "cyan"},
|
432
425
|
{"name": "Value", "style": "green"},
|
433
|
-
{"name": "Impact", "style": "yellow"}
|
434
|
-
]
|
426
|
+
{"name": "Impact", "style": "yellow"},
|
427
|
+
],
|
435
428
|
)
|
436
|
-
|
429
|
+
|
437
430
|
# Financial Metrics
|
438
431
|
metrics_table.add_row(
|
439
|
-
"Monthly Savings",
|
440
|
-
f"${result.business_metrics.total_monthly_savings:,.2f}",
|
441
|
-
"Cost Reduction"
|
432
|
+
"Monthly Savings", f"${result.business_metrics.total_monthly_savings:,.2f}", "Cost Reduction"
|
442
433
|
)
|
443
|
-
|
434
|
+
|
444
435
|
if result.business_metrics.roi_percentage:
|
445
436
|
metrics_table.add_row(
|
446
|
-
"ROI Percentage",
|
447
|
-
f"{result.business_metrics.roi_percentage:.1f}%",
|
448
|
-
"Investment Return"
|
437
|
+
"ROI Percentage", f"{result.business_metrics.roi_percentage:.1f}%", "Investment Return"
|
449
438
|
)
|
450
|
-
|
439
|
+
|
451
440
|
if result.business_metrics.payback_period_months:
|
452
441
|
metrics_table.add_row(
|
453
|
-
"Payback Period",
|
454
|
-
f"{result.business_metrics.payback_period_months} months",
|
455
|
-
"Investment Recovery"
|
442
|
+
"Payback Period", f"{result.business_metrics.payback_period_months} months", "Investment Recovery"
|
456
443
|
)
|
457
|
-
|
444
|
+
|
458
445
|
# Operational Metrics
|
459
446
|
if result.business_metrics.operational_efficiency_gain:
|
460
447
|
metrics_table.add_row(
|
461
448
|
"Efficiency Gain",
|
462
449
|
f"{result.business_metrics.operational_efficiency_gain:.1f}%",
|
463
|
-
"Operational Improvement"
|
450
|
+
"Operational Improvement",
|
464
451
|
)
|
465
|
-
|
452
|
+
|
466
453
|
metrics_table.add_row(
|
467
|
-
"Risk Level",
|
468
|
-
result.business_metrics.overall_risk_level.value.title(),
|
469
|
-
"Risk Assessment"
|
454
|
+
"Risk Level", result.business_metrics.overall_risk_level.value.title(), "Risk Assessment"
|
470
455
|
)
|
471
|
-
|
456
|
+
|
472
457
|
console.print(metrics_table)
|
473
|
-
|
458
|
+
|
474
459
|
# Recommendations (always shown if present)
|
475
460
|
if result.recommendations:
|
476
461
|
rec_text = "\n".join([f"• {rec}" for rec in result.recommendations])
|
477
|
-
rec_panel = create_panel(
|
478
|
-
rec_text,
|
479
|
-
title="Strategic Recommendations",
|
480
|
-
border_style="blue"
|
481
|
-
)
|
462
|
+
rec_panel = create_panel(rec_text, title="Strategic Recommendations", border_style="blue")
|
482
463
|
console.print(rec_panel)
|
483
|
-
|
484
|
-
async def execute_with_auth_handling(
|
485
|
-
self,
|
486
|
-
operation_name: str,
|
487
|
-
operation_func: Callable,
|
488
|
-
*args,
|
489
|
-
**kwargs
|
490
|
-
) -> Any:
|
464
|
+
|
465
|
+
async def execute_with_auth_handling(self, operation_name: str, operation_func: Callable, *args, **kwargs) -> Any:
|
491
466
|
"""
|
492
467
|
Execute operation with comprehensive authentication error handling.
|
493
|
-
|
468
|
+
|
494
469
|
Args:
|
495
470
|
operation_name: Human-readable operation name
|
496
471
|
operation_func: Function to execute (sync or async)
|
497
472
|
*args, **kwargs: Arguments for operation_func
|
498
|
-
|
473
|
+
|
499
474
|
Returns:
|
500
475
|
Operation result or None if authentication failed
|
501
476
|
"""
|
@@ -504,101 +479,96 @@ class NotebookFramework(CloudOpsBase):
|
|
504
479
|
print_error(f"Cannot execute {operation_name}: Authentication failed")
|
505
480
|
self.display_authentication_status()
|
506
481
|
return None
|
507
|
-
|
482
|
+
|
508
483
|
# Execute with monitoring (from CloudOpsBase)
|
509
484
|
try:
|
510
|
-
return await self.execute_with_monitoring(
|
511
|
-
operation_name, operation_func, *args, **kwargs
|
512
|
-
)
|
485
|
+
return await self.execute_with_monitoring(operation_name, operation_func, *args, **kwargs)
|
513
486
|
except ClientError as e:
|
514
|
-
error_code = e.response.get(
|
515
|
-
|
516
|
-
if error_code in [
|
487
|
+
error_code = e.response.get("Error", {}).get("Code", "Unknown")
|
488
|
+
|
489
|
+
if error_code in ["ExpiredToken", "InvalidToken"]:
|
517
490
|
print_error(f"AWS token expired during {operation_name}")
|
518
491
|
print_warning("Please refresh your AWS credentials and retry")
|
519
492
|
return None
|
520
|
-
elif error_code in [
|
493
|
+
elif error_code in ["AccessDenied", "UnauthorizedOperation"]:
|
521
494
|
print_error(f"Permission denied during {operation_name}")
|
522
495
|
print_info("Contact your AWS administrator for required permissions")
|
523
496
|
return None
|
524
497
|
else:
|
525
498
|
# Re-raise other AWS errors
|
526
499
|
raise
|
527
|
-
|
500
|
+
|
528
501
|
def export_results_to_formats(
|
529
|
-
self,
|
530
|
-
result: CloudOpsExecutionResult,
|
531
|
-
export_dir: Path = None,
|
532
|
-
formats: List[str] = None
|
502
|
+
self, result: CloudOpsExecutionResult, export_dir: Path = None, formats: List[str] = None
|
533
503
|
) -> Dict[str, str]:
|
534
504
|
"""
|
535
505
|
Export results to multiple formats for enterprise reporting.
|
536
|
-
|
506
|
+
|
537
507
|
Args:
|
538
508
|
result: Execution result to export
|
539
509
|
export_dir: Directory for exported files (default: ./exports)
|
540
510
|
formats: List of formats ['json', 'csv', 'html', 'pdf'] (default: all)
|
541
|
-
|
511
|
+
|
542
512
|
Returns:
|
543
513
|
Dictionary mapping format to file path
|
544
514
|
"""
|
545
515
|
if export_dir is None:
|
546
516
|
export_dir = Path("./exports")
|
547
|
-
|
517
|
+
|
548
518
|
if formats is None:
|
549
|
-
formats = [
|
550
|
-
|
519
|
+
formats = ["json", "csv", "html"] # PDF requires additional dependencies
|
520
|
+
|
551
521
|
export_dir.mkdir(exist_ok=True)
|
552
522
|
exported_files = {}
|
553
|
-
|
523
|
+
|
554
524
|
# Base filename
|
555
525
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
556
526
|
base_filename = f"{result.scenario.value}_{timestamp}"
|
557
|
-
|
527
|
+
|
558
528
|
# JSON Export
|
559
|
-
if
|
529
|
+
if "json" in formats:
|
560
530
|
json_file = export_dir / f"{base_filename}.json"
|
561
|
-
with open(json_file,
|
531
|
+
with open(json_file, "w") as f:
|
562
532
|
json.dump(result.dict(), f, indent=2, default=str)
|
563
|
-
exported_files[
|
533
|
+
exported_files["json"] = str(json_file)
|
564
534
|
print_success(f"JSON export: {json_file}")
|
565
|
-
|
535
|
+
|
566
536
|
# CSV Export (summary metrics)
|
567
|
-
if
|
537
|
+
if "csv" in formats:
|
568
538
|
csv_file = export_dir / f"{base_filename}_summary.csv"
|
569
539
|
summary_df = pd.DataFrame([result.summary_metrics])
|
570
540
|
summary_df.to_csv(csv_file, index=False)
|
571
|
-
exported_files[
|
541
|
+
exported_files["csv"] = str(csv_file)
|
572
542
|
print_success(f"CSV export: {csv_file}")
|
573
|
-
|
543
|
+
|
574
544
|
# Resource impacts CSV
|
575
545
|
if result.resources_impacted:
|
576
546
|
impacts_csv = export_dir / f"{base_filename}_impacts.csv"
|
577
547
|
impacts_data = [impact.dict() for impact in result.resources_impacted]
|
578
548
|
impacts_df = pd.DataFrame(impacts_data)
|
579
549
|
impacts_df.to_csv(impacts_csv, index=False)
|
580
|
-
exported_files[
|
550
|
+
exported_files["csv_impacts"] = str(impacts_csv)
|
581
551
|
print_info(f"Resource impacts CSV: {impacts_csv}")
|
582
|
-
|
552
|
+
|
583
553
|
# HTML Export (Rich console output)
|
584
|
-
if
|
554
|
+
if "html" in formats:
|
585
555
|
html_file = export_dir / f"{base_filename}.html"
|
586
556
|
# Create HTML version of the results
|
587
557
|
html_content = self._create_html_report(result)
|
588
|
-
with open(html_file,
|
558
|
+
with open(html_file, "w") as f:
|
589
559
|
f.write(html_content)
|
590
|
-
exported_files[
|
560
|
+
exported_files["html"] = str(html_file)
|
591
561
|
print_success(f"HTML export: {html_file}")
|
592
|
-
|
562
|
+
|
593
563
|
return exported_files
|
594
|
-
|
564
|
+
|
595
565
|
def _create_html_report(self, result: CloudOpsExecutionResult) -> str:
|
596
566
|
"""
|
597
567
|
Create HTML report from execution result.
|
598
|
-
|
568
|
+
|
599
569
|
Args:
|
600
570
|
result: Execution result
|
601
|
-
|
571
|
+
|
602
572
|
Returns:
|
603
573
|
HTML content string
|
604
574
|
"""
|
@@ -626,8 +596,8 @@ class NotebookFramework(CloudOpsBase):
|
|
626
596
|
<p><strong>Scenario:</strong> {result.scenario_name}</p>
|
627
597
|
<p><strong>Execution Time:</strong> {result.execution_time:.1f} seconds</p>
|
628
598
|
<p><strong>Status:</strong>
|
629
|
-
<span class="{
|
630
|
-
{
|
599
|
+
<span class="{"success" if result.success else "error"}">
|
600
|
+
{"✅ Success" if result.success else "❌ Failed"}
|
631
601
|
</span>
|
632
602
|
</p>
|
633
603
|
<p><strong>Resources Analyzed:</strong> {result.resources_analyzed:,}</p>
|
@@ -667,10 +637,4 @@ class NotebookFramework(CloudOpsBase):
|
|
667
637
|
|
668
638
|
|
669
639
|
# Export framework components
|
670
|
-
__all__ = [
|
671
|
-
"NotebookFramework",
|
672
|
-
"NotebookMode",
|
673
|
-
"AuthenticationStatus",
|
674
|
-
"AuthenticationResult",
|
675
|
-
"ScenarioMetadata"
|
676
|
-
]
|
640
|
+
__all__ = ["NotebookFramework", "NotebookMode", "AuthenticationStatus", "AuthenticationResult", "ScenarioMetadata"]
|