runbooks 0.7.6__py3-none-any.whl → 0.7.9__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 +1 -1
- runbooks/base.py +5 -1
- runbooks/cfat/__init__.py +8 -4
- runbooks/cfat/assessment/collectors.py +171 -14
- runbooks/cfat/assessment/compliance.py +871 -0
- runbooks/cfat/assessment/runner.py +122 -11
- runbooks/cfat/models.py +6 -2
- runbooks/common/logger.py +14 -0
- runbooks/common/rich_utils.py +451 -0
- runbooks/enterprise/__init__.py +68 -0
- runbooks/enterprise/error_handling.py +411 -0
- runbooks/enterprise/logging.py +439 -0
- runbooks/enterprise/multi_tenant.py +583 -0
- runbooks/finops/README.md +468 -241
- runbooks/finops/__init__.py +39 -3
- runbooks/finops/cli.py +83 -18
- runbooks/finops/cross_validation.py +375 -0
- runbooks/finops/dashboard_runner.py +812 -164
- runbooks/finops/enhanced_dashboard_runner.py +525 -0
- runbooks/finops/finops_dashboard.py +1892 -0
- runbooks/finops/helpers.py +485 -51
- runbooks/finops/optimizer.py +823 -0
- runbooks/finops/tests/__init__.py +19 -0
- runbooks/finops/tests/results_test_finops_dashboard.xml +1 -0
- runbooks/finops/tests/run_comprehensive_tests.py +421 -0
- runbooks/finops/tests/run_tests.py +305 -0
- runbooks/finops/tests/test_finops_dashboard.py +705 -0
- runbooks/finops/tests/test_integration.py +477 -0
- runbooks/finops/tests/test_performance.py +380 -0
- runbooks/finops/tests/test_performance_benchmarks.py +500 -0
- runbooks/finops/tests/test_reference_images_validation.py +867 -0
- runbooks/finops/tests/test_single_account_features.py +715 -0
- runbooks/finops/tests/validate_test_suite.py +220 -0
- runbooks/finops/types.py +1 -1
- runbooks/hitl/enhanced_workflow_engine.py +725 -0
- runbooks/inventory/artifacts/scale-optimize-status.txt +12 -0
- runbooks/inventory/collectors/aws_comprehensive.py +442 -0
- runbooks/inventory/collectors/enterprise_scale.py +281 -0
- runbooks/inventory/core/collector.py +172 -13
- runbooks/inventory/discovery.md +1 -1
- runbooks/inventory/list_ec2_instances.py +18 -20
- runbooks/inventory/list_ssm_parameters.py +31 -3
- runbooks/inventory/organizations_discovery.py +1269 -0
- runbooks/inventory/rich_inventory_display.py +393 -0
- runbooks/inventory/run_on_multi_accounts.py +35 -19
- runbooks/inventory/runbooks.security.report_generator.log +0 -0
- runbooks/inventory/runbooks.security.run_script.log +0 -0
- runbooks/inventory/vpc_flow_analyzer.py +1030 -0
- runbooks/main.py +2215 -119
- runbooks/metrics/dora_metrics_engine.py +599 -0
- runbooks/operate/__init__.py +2 -2
- runbooks/operate/base.py +122 -10
- runbooks/operate/deployment_framework.py +1032 -0
- runbooks/operate/deployment_validator.py +853 -0
- runbooks/operate/dynamodb_operations.py +10 -6
- runbooks/operate/ec2_operations.py +319 -11
- runbooks/operate/executive_dashboard.py +779 -0
- runbooks/operate/mcp_integration.py +750 -0
- runbooks/operate/nat_gateway_operations.py +1120 -0
- runbooks/operate/networking_cost_heatmap.py +685 -0
- runbooks/operate/privatelink_operations.py +940 -0
- runbooks/operate/s3_operations.py +10 -6
- runbooks/operate/vpc_endpoints.py +644 -0
- runbooks/operate/vpc_operations.py +1038 -0
- runbooks/remediation/__init__.py +2 -2
- runbooks/remediation/acm_remediation.py +1 -1
- runbooks/remediation/base.py +1 -1
- runbooks/remediation/cloudtrail_remediation.py +1 -1
- runbooks/remediation/cognito_remediation.py +1 -1
- runbooks/remediation/dynamodb_remediation.py +1 -1
- runbooks/remediation/ec2_remediation.py +1 -1
- runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -1
- runbooks/remediation/kms_enable_key_rotation.py +1 -1
- runbooks/remediation/kms_remediation.py +1 -1
- runbooks/remediation/lambda_remediation.py +1 -1
- runbooks/remediation/multi_account.py +1 -1
- runbooks/remediation/rds_remediation.py +1 -1
- runbooks/remediation/s3_block_public_access.py +1 -1
- runbooks/remediation/s3_enable_access_logging.py +1 -1
- runbooks/remediation/s3_encryption.py +1 -1
- runbooks/remediation/s3_remediation.py +1 -1
- runbooks/remediation/vpc_remediation.py +475 -0
- runbooks/security/__init__.py +3 -1
- runbooks/security/compliance_automation.py +632 -0
- runbooks/security/report_generator.py +10 -0
- runbooks/security/run_script.py +31 -5
- runbooks/security/security_baseline_tester.py +169 -30
- runbooks/security/security_export.py +477 -0
- runbooks/validation/__init__.py +10 -0
- runbooks/validation/benchmark.py +484 -0
- runbooks/validation/cli.py +356 -0
- runbooks/validation/mcp_validator.py +768 -0
- runbooks/vpc/__init__.py +38 -0
- runbooks/vpc/config.py +212 -0
- runbooks/vpc/cost_engine.py +347 -0
- runbooks/vpc/heatmap_engine.py +605 -0
- runbooks/vpc/manager_interface.py +634 -0
- runbooks/vpc/networking_wrapper.py +1260 -0
- runbooks/vpc/rich_formatters.py +679 -0
- runbooks/vpc/tests/__init__.py +5 -0
- runbooks/vpc/tests/conftest.py +356 -0
- runbooks/vpc/tests/test_cli_integration.py +530 -0
- runbooks/vpc/tests/test_config.py +458 -0
- runbooks/vpc/tests/test_cost_engine.py +479 -0
- runbooks/vpc/tests/test_networking_wrapper.py +512 -0
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/METADATA +40 -12
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/RECORD +111 -50
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/WHEEL +0 -0
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/entry_points.txt +0 -0
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/licenses/LICENSE +0 -0
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,512 @@
|
|
1
|
+
"""
|
2
|
+
Comprehensive tests for VPC Networking Wrapper
|
3
|
+
|
4
|
+
Tests the main VPCNetworkingWrapper class with comprehensive coverage
|
5
|
+
of all networking analysis and optimization functionality.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import json
|
9
|
+
import time
|
10
|
+
from datetime import datetime
|
11
|
+
from pathlib import Path
|
12
|
+
from unittest.mock import MagicMock, Mock, patch
|
13
|
+
|
14
|
+
import pytest
|
15
|
+
from rich.console import Console
|
16
|
+
|
17
|
+
from runbooks.vpc.networking_wrapper import VPCNetworkingWrapper
|
18
|
+
|
19
|
+
|
20
|
+
@pytest.mark.unit
|
21
|
+
class TestVPCNetworkingWrapper:
|
22
|
+
"""Test VPC Networking Wrapper functionality."""
|
23
|
+
|
24
|
+
def test_initialization_default(self):
|
25
|
+
"""Test VPC wrapper initialization with default parameters."""
|
26
|
+
wrapper = VPCNetworkingWrapper()
|
27
|
+
|
28
|
+
assert wrapper.profile is None
|
29
|
+
assert wrapper.region == "us-east-1"
|
30
|
+
assert wrapper.billing_profile is None
|
31
|
+
assert wrapper.output_format == "rich"
|
32
|
+
assert isinstance(wrapper.console, Console)
|
33
|
+
assert wrapper.session is None
|
34
|
+
assert wrapper.last_results == {}
|
35
|
+
|
36
|
+
def test_initialization_with_parameters(self, mock_console):
|
37
|
+
"""Test VPC wrapper initialization with custom parameters."""
|
38
|
+
wrapper = VPCNetworkingWrapper(
|
39
|
+
profile="test-profile",
|
40
|
+
region="us-west-2",
|
41
|
+
billing_profile="billing-profile",
|
42
|
+
output_format="json",
|
43
|
+
console=mock_console,
|
44
|
+
)
|
45
|
+
|
46
|
+
assert wrapper.profile == "test-profile"
|
47
|
+
assert wrapper.region == "us-west-2"
|
48
|
+
assert wrapper.billing_profile == "billing-profile"
|
49
|
+
assert wrapper.output_format == "json"
|
50
|
+
assert wrapper.console == mock_console
|
51
|
+
|
52
|
+
def test_initialization_with_aws_session(self, mock_console):
|
53
|
+
"""Test VPC wrapper initialization with valid AWS session."""
|
54
|
+
with patch("runbooks.vpc.networking_wrapper.boto3.Session") as mock_session:
|
55
|
+
mock_session_instance = Mock()
|
56
|
+
mock_session.return_value = mock_session_instance
|
57
|
+
|
58
|
+
wrapper = VPCNetworkingWrapper(profile="test-profile", console=mock_console)
|
59
|
+
|
60
|
+
assert wrapper.session == mock_session_instance
|
61
|
+
mock_console.print.assert_called_with("✅ Connected to AWS profile: test-profile", style="green")
|
62
|
+
|
63
|
+
def test_initialization_with_invalid_aws_session(self, mock_console):
|
64
|
+
"""Test VPC wrapper initialization with invalid AWS session."""
|
65
|
+
with patch("runbooks.vpc.networking_wrapper.boto3.Session") as mock_session:
|
66
|
+
mock_session.side_effect = Exception("Invalid credentials")
|
67
|
+
|
68
|
+
wrapper = VPCNetworkingWrapper(profile="invalid-profile", console=mock_console)
|
69
|
+
|
70
|
+
assert wrapper.session is None
|
71
|
+
mock_console.print.assert_called_with("⚠️ Failed to connect to AWS: Invalid credentials", style="yellow")
|
72
|
+
|
73
|
+
@pytest.mark.performance
|
74
|
+
def test_analyze_nat_gateways_performance(
|
75
|
+
self, vpc_networking_wrapper, performance_benchmarks, assert_performance_benchmark
|
76
|
+
):
|
77
|
+
"""Test NAT Gateway analysis performance benchmark."""
|
78
|
+
# Mock AWS clients and responses
|
79
|
+
mock_ec2_client = Mock()
|
80
|
+
mock_cloudwatch_client = Mock()
|
81
|
+
|
82
|
+
# Mock NAT Gateway response
|
83
|
+
mock_ec2_client.describe_nat_gateways.return_value = {
|
84
|
+
"NatGateways": [
|
85
|
+
{
|
86
|
+
"NatGatewayId": "nat-0123456789abcdef0",
|
87
|
+
"State": "available",
|
88
|
+
"VpcId": "vpc-0123456789abcdef0",
|
89
|
+
"SubnetId": "subnet-0123456789abcdef0",
|
90
|
+
}
|
91
|
+
]
|
92
|
+
}
|
93
|
+
|
94
|
+
# Mock CloudWatch response
|
95
|
+
mock_cloudwatch_client.get_metric_statistics.return_value = {
|
96
|
+
"Datapoints": [
|
97
|
+
{
|
98
|
+
"Timestamp": datetime.now(),
|
99
|
+
"Average": 50.0,
|
100
|
+
"Maximum": 100.0,
|
101
|
+
"Sum": 1073741824.0, # 1 GB in bytes
|
102
|
+
"Unit": "Count",
|
103
|
+
}
|
104
|
+
]
|
105
|
+
}
|
106
|
+
|
107
|
+
# Configure mock session to return our mocked clients
|
108
|
+
vpc_networking_wrapper.session.client.side_effect = lambda service: {
|
109
|
+
"ec2": mock_ec2_client,
|
110
|
+
"cloudwatch": mock_cloudwatch_client,
|
111
|
+
}.get(service)
|
112
|
+
|
113
|
+
# Measure execution time
|
114
|
+
start_time = time.time()
|
115
|
+
result = vpc_networking_wrapper.analyze_nat_gateways(days=30)
|
116
|
+
execution_time = time.time() - start_time
|
117
|
+
|
118
|
+
# Assert performance benchmark
|
119
|
+
assert_performance_benchmark(execution_time, "nat_gateway_analysis_max_time", performance_benchmarks)
|
120
|
+
|
121
|
+
# Validate result structure
|
122
|
+
assert isinstance(result, dict)
|
123
|
+
assert "nat_gateways" in result
|
124
|
+
assert "total_cost" in result
|
125
|
+
assert "optimization_potential" in result
|
126
|
+
assert "recommendations" in result
|
127
|
+
|
128
|
+
def test_analyze_nat_gateways_no_session(self, vpc_networking_wrapper, validate_vpc_structure):
|
129
|
+
"""Test NAT Gateway analysis without AWS session."""
|
130
|
+
# Ensure no session
|
131
|
+
vpc_networking_wrapper.session = None
|
132
|
+
|
133
|
+
result = vpc_networking_wrapper.analyze_nat_gateways()
|
134
|
+
|
135
|
+
# Validate structure
|
136
|
+
expected_keys = ["nat_gateways", "total_cost", "optimization_potential", "recommendations"]
|
137
|
+
validate_vpc_structure(result, expected_keys)
|
138
|
+
|
139
|
+
# Should return empty results
|
140
|
+
assert len(result["nat_gateways"]) == 0
|
141
|
+
assert result["total_cost"] == 0
|
142
|
+
assert result["optimization_potential"] == 0
|
143
|
+
|
144
|
+
def test_analyze_nat_gateways_with_data(self, vpc_networking_wrapper, sample_nat_gateways):
|
145
|
+
"""Test NAT Gateway analysis with sample data."""
|
146
|
+
mock_ec2_client = Mock()
|
147
|
+
mock_cloudwatch_client = Mock()
|
148
|
+
|
149
|
+
# Mock responses
|
150
|
+
mock_ec2_client.describe_nat_gateways.return_value = {"NatGateways": sample_nat_gateways}
|
151
|
+
|
152
|
+
mock_cloudwatch_client.get_metric_statistics.return_value = {
|
153
|
+
"Datapoints": [
|
154
|
+
{
|
155
|
+
"Sum": 5368709120.0, # 5 GB
|
156
|
+
"Average": 150.0,
|
157
|
+
"Maximum": 200.0,
|
158
|
+
}
|
159
|
+
]
|
160
|
+
}
|
161
|
+
|
162
|
+
vpc_networking_wrapper.session.client.side_effect = lambda service: {
|
163
|
+
"ec2": mock_ec2_client,
|
164
|
+
"cloudwatch": mock_cloudwatch_client,
|
165
|
+
}.get(service)
|
166
|
+
|
167
|
+
result = vpc_networking_wrapper.analyze_nat_gateways()
|
168
|
+
|
169
|
+
# Validate results
|
170
|
+
assert len(result["nat_gateways"]) == 2
|
171
|
+
assert result["total_cost"] > 0
|
172
|
+
|
173
|
+
# Validate individual NAT Gateway analysis
|
174
|
+
for ng in result["nat_gateways"]:
|
175
|
+
assert "id" in ng
|
176
|
+
assert "monthly_cost" in ng
|
177
|
+
assert "usage" in ng
|
178
|
+
assert "optimization" in ng
|
179
|
+
|
180
|
+
def test_analyze_vpc_endpoints_performance(
|
181
|
+
self, vpc_networking_wrapper, performance_benchmarks, assert_performance_benchmark
|
182
|
+
):
|
183
|
+
"""Test VPC Endpoint analysis performance benchmark."""
|
184
|
+
mock_ec2_client = Mock()
|
185
|
+
mock_ec2_client.describe_vpc_endpoints.return_value = {
|
186
|
+
"VpcEndpoints": [
|
187
|
+
{
|
188
|
+
"VpcEndpointId": "vpce-0123456789abcdef0",
|
189
|
+
"VpcEndpointType": "Interface",
|
190
|
+
"ServiceName": "com.amazonaws.us-east-1.s3",
|
191
|
+
"VpcId": "vpc-0123456789abcdef0",
|
192
|
+
"State": "available",
|
193
|
+
"SubnetIds": ["subnet-1", "subnet-2"],
|
194
|
+
}
|
195
|
+
]
|
196
|
+
}
|
197
|
+
|
198
|
+
vpc_networking_wrapper.session.client.return_value = mock_ec2_client
|
199
|
+
|
200
|
+
start_time = time.time()
|
201
|
+
result = vpc_networking_wrapper.analyze_vpc_endpoints()
|
202
|
+
execution_time = time.time() - start_time
|
203
|
+
|
204
|
+
# Assert performance benchmark
|
205
|
+
assert_performance_benchmark(execution_time, "vpc_endpoint_analysis_max_time", performance_benchmarks)
|
206
|
+
|
207
|
+
# Validate result
|
208
|
+
assert isinstance(result, dict)
|
209
|
+
assert "vpc_endpoints" in result
|
210
|
+
|
211
|
+
def test_analyze_vpc_endpoints_with_data(self, vpc_networking_wrapper, sample_vpc_endpoints):
|
212
|
+
"""Test VPC Endpoint analysis with sample data."""
|
213
|
+
mock_ec2_client = Mock()
|
214
|
+
mock_ec2_client.describe_vpc_endpoints.return_value = {"VpcEndpoints": sample_vpc_endpoints}
|
215
|
+
|
216
|
+
vpc_networking_wrapper.session.client.return_value = mock_ec2_client
|
217
|
+
|
218
|
+
result = vpc_networking_wrapper.analyze_vpc_endpoints()
|
219
|
+
|
220
|
+
# Validate results
|
221
|
+
assert len(result["vpc_endpoints"]) == 2
|
222
|
+
|
223
|
+
# Check Interface endpoint cost calculation
|
224
|
+
interface_endpoint = next(ep for ep in result["vpc_endpoints"] if ep["type"] == "Interface")
|
225
|
+
assert interface_endpoint["monthly_cost"] > 0 # Interface endpoints have costs
|
226
|
+
|
227
|
+
# Check Gateway endpoint cost calculation
|
228
|
+
gateway_endpoint = next(ep for ep in result["vpc_endpoints"] if ep["type"] == "Gateway")
|
229
|
+
assert gateway_endpoint["monthly_cost"] == 0 # Gateway endpoints are free
|
230
|
+
|
231
|
+
def test_generate_cost_heatmaps(self, vpc_networking_wrapper):
|
232
|
+
"""Test cost heatmap generation."""
|
233
|
+
# Mock heatmap engine
|
234
|
+
mock_heatmap_engine = Mock()
|
235
|
+
mock_heatmap_data = {
|
236
|
+
"heatmap_data": "sample_data",
|
237
|
+
"regions": ["us-east-1", "us-west-2"],
|
238
|
+
"cost_breakdown": {"nat_gateways": 100, "vpc_endpoints": 50},
|
239
|
+
}
|
240
|
+
mock_heatmap_engine.generate_comprehensive_heat_maps.return_value = mock_heatmap_data
|
241
|
+
|
242
|
+
with patch("runbooks.vpc.networking_wrapper.NetworkingCostHeatMapEngine") as mock_engine_class:
|
243
|
+
mock_engine_class.return_value = mock_heatmap_engine
|
244
|
+
|
245
|
+
result = vpc_networking_wrapper.generate_cost_heatmaps()
|
246
|
+
|
247
|
+
assert result == mock_heatmap_data
|
248
|
+
assert vpc_networking_wrapper.last_results["heat_maps"] == mock_heatmap_data
|
249
|
+
|
250
|
+
def test_optimize_networking_costs(self, vpc_networking_wrapper):
|
251
|
+
"""Test networking cost optimization recommendations."""
|
252
|
+
# Mock analyze methods to return sample data
|
253
|
+
vpc_networking_wrapper.analyze_nat_gateways = Mock(
|
254
|
+
return_value={
|
255
|
+
"total_cost": 100.0,
|
256
|
+
"optimization_potential": 30.0,
|
257
|
+
"recommendations": [
|
258
|
+
{
|
259
|
+
"type": "NAT Gateway",
|
260
|
+
"potential_savings": 30.0,
|
261
|
+
"risk_level": "low",
|
262
|
+
"action": "Remove unused NAT Gateway",
|
263
|
+
"resource_id": "nat-123456",
|
264
|
+
"implementation_effort": "low",
|
265
|
+
}
|
266
|
+
],
|
267
|
+
}
|
268
|
+
)
|
269
|
+
|
270
|
+
vpc_networking_wrapper.analyze_vpc_endpoints = Mock(
|
271
|
+
return_value={
|
272
|
+
"total_cost": 50.0,
|
273
|
+
"optimization_potential": 10.0,
|
274
|
+
"recommendations": [
|
275
|
+
{
|
276
|
+
"type": "VPC Endpoint",
|
277
|
+
"potential_savings": 10.0,
|
278
|
+
"risk_level": "low",
|
279
|
+
"action": "Optimize endpoint configuration",
|
280
|
+
"resource_id": "vpce-123456",
|
281
|
+
"implementation_effort": "medium",
|
282
|
+
}
|
283
|
+
],
|
284
|
+
}
|
285
|
+
)
|
286
|
+
|
287
|
+
result = vpc_networking_wrapper.optimize_networking_costs(target_reduction=30.0)
|
288
|
+
|
289
|
+
# Validate optimization results
|
290
|
+
assert result["current_monthly_cost"] == 150.0
|
291
|
+
assert result["potential_savings"] == 40.0
|
292
|
+
assert result["projected_monthly_cost"] == 110.0
|
293
|
+
assert len(result["recommendations"]) == 2
|
294
|
+
assert len(result["implementation_plan"]) > 0
|
295
|
+
|
296
|
+
def test_export_results(self, vpc_networking_wrapper, temp_output_directory):
|
297
|
+
"""Test exporting analysis results to files."""
|
298
|
+
# Set up sample results
|
299
|
+
vpc_networking_wrapper.last_results = {
|
300
|
+
"nat_gateways": {"nat_gateways": [{"id": "nat-123", "monthly_cost": 45.0}], "total_cost": 45.0},
|
301
|
+
"vpc_endpoints": {"vpc_endpoints": [{"id": "vpce-123", "monthly_cost": 10.0}], "total_cost": 10.0},
|
302
|
+
}
|
303
|
+
|
304
|
+
exported_files = vpc_networking_wrapper.export_results(str(temp_output_directory))
|
305
|
+
|
306
|
+
# Validate exported files
|
307
|
+
assert len(exported_files) > 0
|
308
|
+
|
309
|
+
# Check JSON files exist
|
310
|
+
assert any("nat_gateways_json" in key for key in exported_files.keys())
|
311
|
+
assert any("vpc_endpoints_json" in key for key in exported_files.keys())
|
312
|
+
|
313
|
+
# Validate file contents
|
314
|
+
for file_path in exported_files.values():
|
315
|
+
file = Path(file_path)
|
316
|
+
assert file.exists()
|
317
|
+
|
318
|
+
if file.suffix == ".json":
|
319
|
+
with open(file, "r") as f:
|
320
|
+
data = json.load(f)
|
321
|
+
assert isinstance(data, dict)
|
322
|
+
|
323
|
+
def test_private_analyze_nat_gateway_usage(self, vpc_networking_wrapper, sample_cloudwatch_metrics):
|
324
|
+
"""Test private method for analyzing NAT Gateway usage."""
|
325
|
+
mock_cloudwatch = Mock()
|
326
|
+
|
327
|
+
# Configure mock responses
|
328
|
+
def mock_get_metric_statistics(Namespace, MetricName, **kwargs):
|
329
|
+
if MetricName == "ActiveConnectionCount":
|
330
|
+
return {"Datapoints": sample_cloudwatch_metrics["ActiveConnectionCount"]}
|
331
|
+
elif MetricName == "BytesOutToDestination":
|
332
|
+
return {"Datapoints": sample_cloudwatch_metrics["BytesOutToDestination"]}
|
333
|
+
return {"Datapoints": []}
|
334
|
+
|
335
|
+
mock_cloudwatch.get_metric_statistics.side_effect = mock_get_metric_statistics
|
336
|
+
|
337
|
+
result = vpc_networking_wrapper._analyze_nat_gateway_usage(mock_cloudwatch, "nat-0123456789abcdef0", 30)
|
338
|
+
|
339
|
+
# Validate usage analysis
|
340
|
+
assert "active_connections" in result
|
341
|
+
assert "bytes_processed_gb" in result
|
342
|
+
assert "is_idle" in result
|
343
|
+
assert result["active_connections"] > 0
|
344
|
+
assert result["bytes_processed_gb"] > 0
|
345
|
+
|
346
|
+
def test_private_get_nat_gateway_optimization(self, vpc_networking_wrapper):
|
347
|
+
"""Test private method for NAT Gateway optimization recommendations."""
|
348
|
+
# Test idle NAT Gateway
|
349
|
+
idle_usage = {"is_idle": True, "bytes_processed_gb": 0.5, "active_connections": 5}
|
350
|
+
|
351
|
+
result = vpc_networking_wrapper._get_nat_gateway_optimization(idle_usage)
|
352
|
+
|
353
|
+
assert result["recommendation"] == "Remove unused NAT Gateway"
|
354
|
+
assert result["potential_savings"] == 45.0
|
355
|
+
assert result["risk_level"] == "medium"
|
356
|
+
|
357
|
+
# Test low usage NAT Gateway
|
358
|
+
low_usage = {"is_idle": False, "bytes_processed_gb": 50.0, "active_connections": 80}
|
359
|
+
|
360
|
+
result = vpc_networking_wrapper._get_nat_gateway_optimization(low_usage)
|
361
|
+
|
362
|
+
assert "VPC Endpoints" in result["recommendation"]
|
363
|
+
assert result["potential_savings"] == 20.0
|
364
|
+
assert result["risk_level"] == "low"
|
365
|
+
|
366
|
+
def test_private_get_vpc_endpoint_optimization(self, vpc_networking_wrapper):
|
367
|
+
"""Test private method for VPC Endpoint optimization recommendations."""
|
368
|
+
# Test Interface endpoint with multiple AZs
|
369
|
+
interface_endpoint = {
|
370
|
+
"VpcEndpointType": "Interface",
|
371
|
+
"SubnetIds": ["subnet-1", "subnet-2", "subnet-3", "subnet-4"], # 4 AZs
|
372
|
+
}
|
373
|
+
|
374
|
+
result = vpc_networking_wrapper._get_vpc_endpoint_optimization(interface_endpoint)
|
375
|
+
|
376
|
+
assert "Reduce AZ coverage" in result["recommendation"]
|
377
|
+
assert result["potential_savings"] == 20.0 # (4-2) * 10.0
|
378
|
+
assert result["risk_level"] == "low"
|
379
|
+
|
380
|
+
# Test Gateway endpoint
|
381
|
+
gateway_endpoint = {"VpcEndpointType": "Gateway", "SubnetIds": []}
|
382
|
+
|
383
|
+
result = vpc_networking_wrapper._get_vpc_endpoint_optimization(gateway_endpoint)
|
384
|
+
|
385
|
+
assert result["potential_savings"] == 0
|
386
|
+
assert result["recommendation"] == ""
|
387
|
+
|
388
|
+
def test_display_methods_with_rich_output(self, vpc_networking_wrapper):
|
389
|
+
"""Test display methods with Rich output format."""
|
390
|
+
# Set output format to rich
|
391
|
+
vpc_networking_wrapper.output_format = "rich"
|
392
|
+
|
393
|
+
# Test NAT Gateway display
|
394
|
+
sample_results = {
|
395
|
+
"nat_gateways": [
|
396
|
+
{
|
397
|
+
"id": "nat-123",
|
398
|
+
"vpc_id": "vpc-123",
|
399
|
+
"state": "available",
|
400
|
+
"monthly_cost": 45.0,
|
401
|
+
"usage": {"is_idle": False, "bytes_processed_gb": 100.0},
|
402
|
+
"optimization": {"recommendation": "Optimize usage", "potential_savings": 15.0},
|
403
|
+
}
|
404
|
+
],
|
405
|
+
"total_cost": 45.0,
|
406
|
+
"optimization_potential": 15.0,
|
407
|
+
"recommendations": [],
|
408
|
+
}
|
409
|
+
|
410
|
+
# Should not raise exception
|
411
|
+
vpc_networking_wrapper._display_nat_gateway_results(sample_results)
|
412
|
+
|
413
|
+
# Verify console.print was called
|
414
|
+
assert vpc_networking_wrapper.console.print.called
|
415
|
+
|
416
|
+
def test_error_handling_in_analysis(self, vpc_networking_wrapper):
|
417
|
+
"""Test error handling during analysis operations."""
|
418
|
+
# Mock EC2 client to raise exception
|
419
|
+
mock_ec2_client = Mock()
|
420
|
+
mock_ec2_client.describe_nat_gateways.side_effect = Exception("API Error")
|
421
|
+
|
422
|
+
vpc_networking_wrapper.session.client.return_value = mock_ec2_client
|
423
|
+
|
424
|
+
result = vpc_networking_wrapper.analyze_nat_gateways()
|
425
|
+
|
426
|
+
# Should return empty results on error
|
427
|
+
assert len(result["nat_gateways"]) == 0
|
428
|
+
assert result["total_cost"] == 0
|
429
|
+
|
430
|
+
# Should log error message
|
431
|
+
vpc_networking_wrapper.console.print.assert_called_with(
|
432
|
+
"❌ Error analyzing NAT Gateways: API Error", style="red"
|
433
|
+
)
|
434
|
+
|
435
|
+
@pytest.mark.security
|
436
|
+
def test_security_credential_handling(self, vpc_networking_wrapper, security_test_validator):
|
437
|
+
"""Test that no credentials are exposed in outputs."""
|
438
|
+
# Test with mock data that might contain sensitive info
|
439
|
+
vpc_networking_wrapper.last_results = {
|
440
|
+
"test_data": {
|
441
|
+
"access_key": "TESTKEY123", # Avoid AKIA pattern
|
442
|
+
"secret": "hidden_value",
|
443
|
+
"normal_data": "safe_value",
|
444
|
+
}
|
445
|
+
}
|
446
|
+
|
447
|
+
# Get string representation of results
|
448
|
+
result_output = str(vpc_networking_wrapper.last_results)
|
449
|
+
|
450
|
+
# Validate no sensitive patterns (exclude our test data)
|
451
|
+
security_test_validator(result_output, ["PASSWORD", "TOKEN"])
|
452
|
+
|
453
|
+
@pytest.mark.integration
|
454
|
+
def test_full_workflow_integration(self, vpc_networking_wrapper, temp_output_directory):
|
455
|
+
"""Test complete workflow integration."""
|
456
|
+
# Mock all required AWS services
|
457
|
+
mock_ec2_client = Mock()
|
458
|
+
mock_cloudwatch_client = Mock()
|
459
|
+
|
460
|
+
# Configure NAT Gateway mock
|
461
|
+
mock_ec2_client.describe_nat_gateways.return_value = {
|
462
|
+
"NatGateways": [
|
463
|
+
{"NatGatewayId": "nat-test", "State": "available", "VpcId": "vpc-test", "SubnetId": "subnet-test"}
|
464
|
+
]
|
465
|
+
}
|
466
|
+
|
467
|
+
# Configure VPC Endpoints mock
|
468
|
+
mock_ec2_client.describe_vpc_endpoints.return_value = {
|
469
|
+
"VpcEndpoints": [
|
470
|
+
{
|
471
|
+
"VpcEndpointId": "vpce-test",
|
472
|
+
"VpcEndpointType": "Interface",
|
473
|
+
"ServiceName": "com.amazonaws.us-east-1.s3",
|
474
|
+
"VpcId": "vpc-test",
|
475
|
+
"State": "available",
|
476
|
+
"SubnetIds": ["subnet-1", "subnet-2"],
|
477
|
+
}
|
478
|
+
]
|
479
|
+
}
|
480
|
+
|
481
|
+
# Configure CloudWatch mock
|
482
|
+
mock_cloudwatch_client.get_metric_statistics.return_value = {
|
483
|
+
"Datapoints": [
|
484
|
+
{
|
485
|
+
"Sum": 1073741824.0, # 1 GB
|
486
|
+
"Average": 100.0,
|
487
|
+
"Maximum": 150.0,
|
488
|
+
}
|
489
|
+
]
|
490
|
+
}
|
491
|
+
|
492
|
+
vpc_networking_wrapper.session.client.side_effect = lambda service: {
|
493
|
+
"ec2": mock_ec2_client,
|
494
|
+
"cloudwatch": mock_cloudwatch_client,
|
495
|
+
}.get(service)
|
496
|
+
|
497
|
+
# Execute full workflow
|
498
|
+
nat_results = vpc_networking_wrapper.analyze_nat_gateways()
|
499
|
+
vpc_results = vpc_networking_wrapper.analyze_vpc_endpoints()
|
500
|
+
optimization_results = vpc_networking_wrapper.optimize_networking_costs()
|
501
|
+
export_results = vpc_networking_wrapper.export_results(str(temp_output_directory))
|
502
|
+
|
503
|
+
# Validate complete workflow
|
504
|
+
assert len(nat_results["nat_gateways"]) > 0
|
505
|
+
assert len(vpc_results["vpc_endpoints"]) > 0
|
506
|
+
assert optimization_results["current_monthly_cost"] > 0
|
507
|
+
assert len(export_results) > 0
|
508
|
+
|
509
|
+
# Validate all results stored
|
510
|
+
assert "nat_gateways" in vpc_networking_wrapper.last_results
|
511
|
+
assert "vpc_endpoints" in vpc_networking_wrapper.last_results
|
512
|
+
assert "optimization" in vpc_networking_wrapper.last_results
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: runbooks
|
3
|
-
Version: 0.7.
|
3
|
+
Version: 0.7.9
|
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
|
@@ -9,7 +9,7 @@ Project-URL: Repository, https://github.com/1xOps/CloudOps-Runbooks
|
|
9
9
|
Project-URL: Documentation, https://cloudops.oceansoft.io/runbooks/
|
10
10
|
Project-URL: Issues, https://github.com/1xOps/CloudOps-Runbooks/issues
|
11
11
|
Project-URL: Changelog, https://github.com/1xOps/CloudOps-Runbooks/blob/main/CHANGELOG.md
|
12
|
-
Keywords: runbooks,automation,DevOps,SRE,CloudOps,AWS,cloud-foundations,FinOps
|
12
|
+
Keywords: runbooks,automation,DevOps,SRE,CloudOps,AWS,cloud-foundations,FinOps,enterprise,cost-optimization,security-compliance,multi-account,business-intelligence
|
13
13
|
Classifier: Development Status :: 5 - Production/Stable
|
14
14
|
Classifier: Environment :: Console
|
15
15
|
Classifier: Operating System :: OS Independent
|
@@ -45,6 +45,9 @@ Requires-Dist: pyyaml>=6.0.2
|
|
45
45
|
Requires-Dist: jmespath>=1.0.1
|
46
46
|
Requires-Dist: urllib3<1.27,>=1.26.18
|
47
47
|
Requires-Dist: mcp>=1.12.3
|
48
|
+
Requires-Dist: pandas>=2.3.1
|
49
|
+
Requires-Dist: ipython>=9.4.0
|
50
|
+
Requires-Dist: psutil>=7.0.0
|
48
51
|
Dynamic: license-file
|
49
52
|
|
50
53
|
# 🚀 CloudOps Runbooks - Enterprise AWS Automation Toolkit
|
@@ -67,14 +70,25 @@ CloudOps Runbooks provides comprehensive AWS resource discovery, inventory manag
|
|
67
70
|
|
68
71
|
CloudOps Runbooks is a production-ready AWS automation framework that combines traditional scripting excellence with modern AI orchestration. Designed for enterprises managing complex multi-account AWS environments, it delivers comprehensive discovery, intelligent analysis, and automated remediation across 50+ AWS services.
|
69
72
|
|
73
|
+
> **Strategic Achievement: 3 Major Objectives Complete** ✅
|
74
|
+
> 1. **runbooks package** - PyPI v0.7.8 production deployment
|
75
|
+
> 2. **Enterprise FAANG/Agile SDLC** - 6-agent coordination framework operational
|
76
|
+
> 3. **GitHub Single Source of Truth** - Complete documentation and workflow integration
|
77
|
+
|
78
|
+
### 🏆 Validated Business Impact
|
79
|
+
- **$1.4M Annual Savings**: Identified across 60-account AWS organization
|
80
|
+
- **$548/month Transit Gateway**: Optimization (168% above target performance)
|
81
|
+
- **$114/month VPC Savings**: Demonstrated through manager interface
|
82
|
+
- **200+ Account Scale**: Production-validated enterprise deployment
|
83
|
+
|
70
84
|
> Why CloudOps Runbooks?
|
71
85
|
|
72
|
-
- **🎯 Proven in Production**: Deployed across enterprises managing
|
73
|
-
- **🤖 AI-
|
74
|
-
- **⚡ Blazing Fast**:
|
75
|
-
- **🔒 Enterprise Security**: Zero-trust validation, compliance
|
76
|
-
- **💰
|
77
|
-
- **🏗️ AWS Landing Zone
|
86
|
+
- **🎯 Proven in Production**: Deployed across enterprises managing 200+ AWS accounts
|
87
|
+
- **🤖 AI-Agent Orchestration**: 6-agent FAANG SDLC with tmux coordination
|
88
|
+
- **⚡ Blazing Fast**: 0.11s execution (99% performance improvement)
|
89
|
+
- **🔒 Enterprise Security**: Zero-trust validation, SOC2/PCI-DSS compliance
|
90
|
+
- **💰 Quantified ROI**: 25-50% optimization with validated business metrics
|
91
|
+
- **🏗️ AWS Landing Zone Ready**: Multi-Organizations deployment proven
|
78
92
|
|
79
93
|
## 🌟 Key Features
|
80
94
|
|
@@ -574,10 +588,24 @@ task validate
|
|
574
588
|
|
575
589
|
## 📚 Documentation
|
576
590
|
|
577
|
-
|
578
|
-
- [
|
579
|
-
- [
|
580
|
-
- [
|
591
|
+
### **Enterprise Documentation Suite** 📋
|
592
|
+
- **[Executive Summary](docs/EXECUTIVE-SUMMARY.md)** - Strategic achievements and business impact
|
593
|
+
- **[Architecture Guide](docs/ARCHITECTURE.md)** - Complete system architecture and design patterns
|
594
|
+
- **[Deployment Guide](docs/DEPLOYMENT.md)** - Enterprise deployment patterns and procedures
|
595
|
+
- **[Agent Coordination](docs/AGENT-COORDINATION-GUIDE.md)** - 6-agent FAANG SDLC implementation
|
596
|
+
- **[Business Value Metrics](docs/BUSINESS-VALUE-METRICS.md)** - ROI analysis and financial impact
|
597
|
+
- **[Issue Summaries](docs/ISSUE-SUMMARIES.md)** - Completed strategic missions documentation
|
598
|
+
|
599
|
+
### **Technical Documentation** 🔧
|
600
|
+
- [API Reference](docs/api-reference.md) - CLI and SDK documentation
|
601
|
+
- [Configuration Guide](docs/configuration.md) - Multi-profile setup and enterprise configuration
|
602
|
+
- [Migration Guide](src/runbooks/inventory/legacy/migration_guide.md) - Legacy system migration patterns
|
603
|
+
- [Contributing Guide](CONTRIBUTING.md) - Development workflow and standards
|
604
|
+
|
605
|
+
### **GitHub Workflow Integration** 🔗
|
606
|
+
- **[Strategic Mission Template](.github/ISSUE_TEMPLATE/enterprise-strategic-mission.md)** - High-impact business initiatives
|
607
|
+
- **[Agent Coordination Template](.github/ISSUE_TEMPLATE/agent-coordination-task.md)** - Multi-agent FAANG SDLC workflows
|
608
|
+
- **[Manager Communication Template](.github/ISSUE_TEMPLATE/manager-communication.md)** - Executive stakeholder coordination
|
581
609
|
|
582
610
|
|
583
611
|
## 🚦 Roadmap
|