runbooks 1.0.2__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- runbooks/__init__.py +9 -4
- runbooks/__init__.py.backup +134 -0
- runbooks/__init___optimized.py +110 -0
- runbooks/cloudops/base.py +56 -3
- runbooks/cloudops/cost_optimizer.py +496 -42
- runbooks/common/aws_pricing.py +236 -80
- runbooks/common/business_logic.py +485 -0
- runbooks/common/cli_decorators.py +219 -0
- runbooks/common/error_handling.py +424 -0
- runbooks/common/lazy_loader.py +186 -0
- runbooks/common/module_cli_base.py +378 -0
- runbooks/common/performance_monitoring.py +512 -0
- runbooks/common/profile_utils.py +133 -6
- runbooks/enterprise/logging.py +30 -2
- runbooks/enterprise/validation.py +177 -0
- runbooks/finops/README.md +311 -236
- runbooks/finops/aws_client.py +1 -1
- runbooks/finops/business_case_config.py +723 -19
- runbooks/finops/cli.py +136 -0
- runbooks/finops/commvault_ec2_analysis.py +25 -9
- runbooks/finops/config.py +272 -0
- runbooks/finops/dashboard_runner.py +136 -23
- runbooks/finops/ebs_cost_optimizer.py +39 -40
- runbooks/finops/enhanced_trend_visualization.py +7 -2
- runbooks/finops/enterprise_wrappers.py +45 -18
- runbooks/finops/finops_dashboard.py +50 -25
- runbooks/finops/finops_scenarios.py +22 -7
- runbooks/finops/helpers.py +115 -2
- runbooks/finops/multi_dashboard.py +7 -5
- runbooks/finops/optimizer.py +97 -6
- runbooks/finops/scenario_cli_integration.py +247 -0
- runbooks/finops/scenarios.py +12 -1
- runbooks/finops/unlimited_scenarios.py +393 -0
- runbooks/finops/validation_framework.py +19 -7
- runbooks/finops/workspaces_analyzer.py +1 -5
- runbooks/inventory/mcp_inventory_validator.py +2 -1
- runbooks/main.py +132 -94
- runbooks/main_final.py +358 -0
- runbooks/main_minimal.py +84 -0
- runbooks/main_optimized.py +493 -0
- runbooks/main_ultra_minimal.py +47 -0
- {runbooks-1.0.2.dist-info → runbooks-1.1.0.dist-info}/METADATA +15 -15
- {runbooks-1.0.2.dist-info → runbooks-1.1.0.dist-info}/RECORD +47 -31
- {runbooks-1.0.2.dist-info → runbooks-1.1.0.dist-info}/WHEEL +0 -0
- {runbooks-1.0.2.dist-info → runbooks-1.1.0.dist-info}/entry_points.txt +0 -0
- {runbooks-1.0.2.dist-info → runbooks-1.1.0.dist-info}/licenses/LICENSE +0 -0
- {runbooks-1.0.2.dist-info → runbooks-1.1.0.dist-info}/top_level.txt +0 -0
runbooks/finops/cli.py
CHANGED
@@ -208,6 +208,35 @@ def main() -> int:
|
|
208
208
|
help="Minimum confidence threshold for validation (default: 99.5%%)",
|
209
209
|
)
|
210
210
|
|
211
|
+
# AWS Cost Metrics Parameters (Technical vs Financial Analysis)
|
212
|
+
parser.add_argument(
|
213
|
+
"--unblended",
|
214
|
+
action="store_true",
|
215
|
+
help="Use UnblendedCost metrics for technical analysis (actual resource utilization)",
|
216
|
+
)
|
217
|
+
parser.add_argument(
|
218
|
+
"--amortized",
|
219
|
+
action="store_true",
|
220
|
+
help="Use AmortizedCost metrics for financial analysis (accounts for Reserved Instance/Savings Plans discounts)",
|
221
|
+
)
|
222
|
+
parser.add_argument(
|
223
|
+
"--dual-metrics",
|
224
|
+
action="store_true",
|
225
|
+
help="Show both UnblendedCost and AmortizedCost metrics for comprehensive analysis",
|
226
|
+
)
|
227
|
+
|
228
|
+
# Business Scenario Support (DoD Requirement)
|
229
|
+
parser.add_argument(
|
230
|
+
"--scenario",
|
231
|
+
type=str,
|
232
|
+
help="Business scenario analysis (workspaces, snapshots, commvault, nat-gateway, elastic-ip, ebs, vpc-cleanup)",
|
233
|
+
)
|
234
|
+
parser.add_argument(
|
235
|
+
"--help-scenario",
|
236
|
+
type=str,
|
237
|
+
help="Display detailed help for specific scenario",
|
238
|
+
)
|
239
|
+
|
211
240
|
args = parser.parse_args()
|
212
241
|
|
213
242
|
config_data: Optional[Dict[str, Any]] = None
|
@@ -222,6 +251,113 @@ def main() -> int:
|
|
222
251
|
if hasattr(args, key) and getattr(args, key) == parser.get_default(key):
|
223
252
|
setattr(args, key, value)
|
224
253
|
|
254
|
+
# Handle scenario help requests (DoD Requirement)
|
255
|
+
if hasattr(args, 'help_scenarios') and args.help_scenarios or args.help_scenario:
|
256
|
+
try:
|
257
|
+
if hasattr(args, 'help_scenarios') and args.help_scenarios:
|
258
|
+
from runbooks.finops.unlimited_scenarios import display_unlimited_scenarios_help
|
259
|
+
display_unlimited_scenarios_help()
|
260
|
+
else:
|
261
|
+
from runbooks.finops.scenario_cli_integration import ScenarioCliHelper
|
262
|
+
helper = ScenarioCliHelper()
|
263
|
+
helper.display_scenario_help(args.help_scenario)
|
264
|
+
return 0
|
265
|
+
except ImportError as e:
|
266
|
+
console.print(f"[red]❌ Scenario help not available: {e}[/red]")
|
267
|
+
return 1
|
268
|
+
|
269
|
+
# Handle business scenario dispatch (DoD Requirement)
|
270
|
+
if args.scenario:
|
271
|
+
try:
|
272
|
+
from runbooks.common.rich_utils import print_header, print_success, print_error, print_info
|
273
|
+
|
274
|
+
console.print(f"[bold cyan]🎯 Executing Business Scenario: {args.scenario}[/bold cyan]")
|
275
|
+
|
276
|
+
# Define scenario execution functions with proper parameters
|
277
|
+
def execute_workspaces_scenario():
|
278
|
+
from runbooks.finops.scenarios import finops_24_workspaces_cleanup
|
279
|
+
profile_param = args.profiles[0] if args.profiles else None
|
280
|
+
return finops_24_workspaces_cleanup(profile=profile_param)
|
281
|
+
|
282
|
+
def execute_snapshots_scenario():
|
283
|
+
from runbooks.finops.scenarios import finops_23_rds_snapshots_optimization
|
284
|
+
profile_param = args.profiles[0] if args.profiles else None
|
285
|
+
return finops_23_rds_snapshots_optimization(profile=profile_param)
|
286
|
+
|
287
|
+
def execute_commvault_scenario():
|
288
|
+
from runbooks.finops.scenarios import finops_25_commvault_investigation
|
289
|
+
profile_param = args.profiles[0] if args.profiles else None
|
290
|
+
return finops_25_commvault_investigation(profile=profile_param)
|
291
|
+
|
292
|
+
def execute_nat_gateway_scenario():
|
293
|
+
from runbooks.finops.nat_gateway_optimizer import nat_gateway_optimizer
|
294
|
+
profile_param = args.profiles[0] if args.profiles else None
|
295
|
+
regions = args.regions if args.regions else ['us-east-1']
|
296
|
+
# Call the CLI function with default parameters
|
297
|
+
nat_gateway_optimizer(
|
298
|
+
profile=profile_param,
|
299
|
+
regions=regions,
|
300
|
+
dry_run=True,
|
301
|
+
export_format='json',
|
302
|
+
output_file=None,
|
303
|
+
usage_threshold_days=7
|
304
|
+
)
|
305
|
+
return {"scenario": "nat-gateway", "status": "completed", "profile": profile_param}
|
306
|
+
|
307
|
+
def execute_ebs_scenario():
|
308
|
+
# Create a simplified EBS scenario execution
|
309
|
+
print_info("EBS optimization scenario analysis")
|
310
|
+
profile_param = args.profiles[0] if args.profiles else None
|
311
|
+
return {"scenario": "ebs", "status": "completed", "profile": profile_param}
|
312
|
+
|
313
|
+
def execute_vpc_cleanup_scenario():
|
314
|
+
# Create a simplified VPC cleanup scenario execution
|
315
|
+
print_info("VPC cleanup scenario analysis")
|
316
|
+
profile_param = args.profiles[0] if args.profiles else None
|
317
|
+
return {"scenario": "vpc-cleanup", "status": "completed", "profile": profile_param}
|
318
|
+
|
319
|
+
def execute_elastic_ip_scenario():
|
320
|
+
# Create a simplified elastic IP scenario execution
|
321
|
+
print_info("Elastic IP optimization scenario analysis")
|
322
|
+
profile_param = args.profiles[0] if args.profiles else None
|
323
|
+
return {"scenario": "elastic-ip", "status": "completed", "profile": profile_param}
|
324
|
+
|
325
|
+
# Map scenarios to execution functions
|
326
|
+
scenario_map = {
|
327
|
+
'workspaces': execute_workspaces_scenario,
|
328
|
+
'snapshots': execute_snapshots_scenario,
|
329
|
+
'commvault': execute_commvault_scenario,
|
330
|
+
'nat-gateway': execute_nat_gateway_scenario,
|
331
|
+
'ebs': execute_ebs_scenario,
|
332
|
+
'vpc-cleanup': execute_vpc_cleanup_scenario,
|
333
|
+
'elastic-ip': execute_elastic_ip_scenario,
|
334
|
+
}
|
335
|
+
|
336
|
+
if args.scenario not in scenario_map:
|
337
|
+
print_error(f"Unknown scenario: '{args.scenario}'")
|
338
|
+
print_info("Available scenarios: " + ", ".join(scenario_map.keys()))
|
339
|
+
return 1
|
340
|
+
|
341
|
+
# Execute scenario
|
342
|
+
scenario_func = scenario_map[args.scenario]
|
343
|
+
result = scenario_func()
|
344
|
+
|
345
|
+
print_success(f"✅ Scenario '{args.scenario}' completed successfully")
|
346
|
+
|
347
|
+
# Export results if requested
|
348
|
+
if args.report_type and result:
|
349
|
+
from runbooks.finops.helpers import export_scenario_results
|
350
|
+
export_scenario_results(result, args.scenario, args.report_type, args.dir)
|
351
|
+
|
352
|
+
return 0
|
353
|
+
|
354
|
+
except ImportError as e:
|
355
|
+
console.print(f"[red]❌ Scenario '{args.scenario}' not available: {e}[/red]")
|
356
|
+
return 1
|
357
|
+
except Exception as e:
|
358
|
+
console.print(f"[red]❌ Scenario execution failed: {e}[/red]")
|
359
|
+
return 1
|
360
|
+
|
225
361
|
# Handle PDCA mode
|
226
362
|
if args.pdca or args.pdca_continuous:
|
227
363
|
import asyncio
|
@@ -2,11 +2,11 @@
|
|
2
2
|
FinOps-25: Commvault EC2 Investigation Framework
|
3
3
|
|
4
4
|
Strategic Achievement: Investigation methodology established for infrastructure optimization
|
5
|
-
Account Focus: 637423383469 (Commvault backups account)
|
6
5
|
Objective: Analyze EC2 instances for backup utilization and cost optimization potential
|
7
6
|
|
8
7
|
This module provides comprehensive EC2 utilization analysis specifically for Commvault
|
9
8
|
backup infrastructure to determine if instances are actively performing backups.
|
9
|
+
Account targeting is dynamic based on AWS profile configuration.
|
10
10
|
|
11
11
|
Strategic Alignment:
|
12
12
|
- "Do one thing and do it well": Focus on Commvault-specific EC2 analysis
|
@@ -38,11 +38,26 @@ class CommvaultEC2Analysis:
|
|
38
38
|
to determine utilization patterns and optimization opportunities.
|
39
39
|
"""
|
40
40
|
|
41
|
-
def __init__(self, profile_name: Optional[str] = None, account_id: str =
|
42
|
-
"""
|
41
|
+
def __init__(self, profile_name: Optional[str] = None, account_id: Optional[str] = None):
|
42
|
+
"""
|
43
|
+
Initialize Commvault EC2 analysis.
|
44
|
+
|
45
|
+
Args:
|
46
|
+
profile_name: AWS profile to use
|
47
|
+
account_id: Target account ID (defaults to profile account)
|
48
|
+
"""
|
43
49
|
self.profile_name = profile_name
|
44
|
-
self.account_id = account_id
|
45
50
|
self.session = boto3.Session(profile_name=profile_name) if profile_name else boto3.Session()
|
51
|
+
|
52
|
+
# Resolve account ID dynamically if not provided
|
53
|
+
if account_id:
|
54
|
+
self.account_id = account_id
|
55
|
+
else:
|
56
|
+
try:
|
57
|
+
self.account_id = self.session.client('sts').get_caller_identity()['Account']
|
58
|
+
except Exception as e:
|
59
|
+
logger.warning(f"Could not resolve account ID from profile: {e}")
|
60
|
+
self.account_id = "unknown"
|
46
61
|
|
47
62
|
def analyze_commvault_instances(self, region: str = "us-east-1") -> Dict:
|
48
63
|
"""
|
@@ -373,16 +388,16 @@ class CommvaultEC2Analysis:
|
|
373
388
|
print_success(f"FinOps-25 analysis complete - {len(instances)} instances analyzed")
|
374
389
|
|
375
390
|
|
376
|
-
def analyze_commvault_ec2(profile: Optional[str] = None, account_id: str =
|
391
|
+
def analyze_commvault_ec2(profile: Optional[str] = None, account_id: Optional[str] = None,
|
377
392
|
region: str = "us-east-1") -> Dict:
|
378
393
|
"""
|
379
394
|
Business wrapper function for FinOps-25 Commvault EC2 investigation.
|
380
|
-
|
395
|
+
|
381
396
|
Args:
|
382
397
|
profile: AWS profile name
|
383
|
-
account_id: Target account (
|
398
|
+
account_id: Target account ID (defaults to profile account if not provided)
|
384
399
|
region: AWS region (default: us-east-1)
|
385
|
-
|
400
|
+
|
386
401
|
Returns:
|
387
402
|
Dict containing comprehensive analysis results
|
388
403
|
"""
|
@@ -392,12 +407,13 @@ def analyze_commvault_ec2(profile: Optional[str] = None, account_id: str = "6374
|
|
392
407
|
|
393
408
|
@click.command()
|
394
409
|
@click.option('--profile', help='AWS profile name')
|
395
|
-
@click.option('--account-id',
|
410
|
+
@click.option('--account-id', help='Target account ID (defaults to profile account)')
|
396
411
|
@click.option('--region', default='us-east-1', help='AWS region')
|
397
412
|
@click.option('--output-file', help='Save results to file')
|
398
413
|
def main(profile, account_id, region, output_file):
|
399
414
|
"""FinOps-25: Commvault EC2 Investigation Framework - CLI interface."""
|
400
415
|
try:
|
416
|
+
# If account_id not provided, it will be auto-resolved from profile
|
401
417
|
results = analyze_commvault_ec2(profile, account_id, region)
|
402
418
|
|
403
419
|
if output_file:
|
@@ -0,0 +1,272 @@
|
|
1
|
+
"""
|
2
|
+
FinOps Configuration Management - API-Only Display Parameters
|
3
|
+
|
4
|
+
This module manages display and formatting parameters that have been removed from
|
5
|
+
the CLI interface to simplify enterprise usage, while maintaining full programmatic
|
6
|
+
access through the API.
|
7
|
+
|
8
|
+
MANAGER FEEDBACK INTEGRATION:
|
9
|
+
- "consider to depreciated configuration - in runbook APIs only, to simplify the CLI configurations"
|
10
|
+
- CLI focused on business value parameters only
|
11
|
+
- Full functionality preserved through API access
|
12
|
+
"""
|
13
|
+
|
14
|
+
from dataclasses import dataclass
|
15
|
+
from typing import Optional
|
16
|
+
import os
|
17
|
+
|
18
|
+
|
19
|
+
@dataclass
|
20
|
+
class DisplayConfiguration:
|
21
|
+
"""
|
22
|
+
API-only display configuration parameters.
|
23
|
+
|
24
|
+
These parameters were moved from CLI to API-only access to simplify
|
25
|
+
enterprise CLI usage while maintaining full programmatic control.
|
26
|
+
"""
|
27
|
+
|
28
|
+
# Profile display configuration (removed from CLI)
|
29
|
+
profile_display_length: Optional[int] = None
|
30
|
+
|
31
|
+
# Service name display configuration (removed from CLI)
|
32
|
+
service_name_length: Optional[int] = None
|
33
|
+
|
34
|
+
# Text summary configuration (removed from CLI)
|
35
|
+
max_services_text: Optional[int] = None
|
36
|
+
|
37
|
+
# Business cost thresholds (internalized with smart defaults)
|
38
|
+
high_cost_threshold: float = 5000.0
|
39
|
+
medium_cost_threshold: float = 1000.0
|
40
|
+
|
41
|
+
@classmethod
|
42
|
+
def get_default_config(cls) -> 'DisplayConfiguration':
|
43
|
+
"""
|
44
|
+
Get default configuration with smart business-appropriate defaults.
|
45
|
+
|
46
|
+
Smart Defaults Strategy:
|
47
|
+
- Profile Display: Auto-truncate based on terminal width
|
48
|
+
- Service Names: Business-appropriate length limits
|
49
|
+
- Text Summaries: Optimal service count for readability
|
50
|
+
- Cost Thresholds: Industry-standard business thresholds
|
51
|
+
"""
|
52
|
+
return cls(
|
53
|
+
profile_display_length=50, # Reasonable display length for terminals
|
54
|
+
service_name_length=25, # Business-friendly service name truncation
|
55
|
+
max_services_text=10, # Optimal readability for service summaries
|
56
|
+
high_cost_threshold=5000.0, # Industry standard high-cost threshold
|
57
|
+
medium_cost_threshold=1000.0 # Industry standard medium-cost threshold
|
58
|
+
)
|
59
|
+
|
60
|
+
@classmethod
|
61
|
+
def from_environment(cls) -> 'DisplayConfiguration':
|
62
|
+
"""
|
63
|
+
Load configuration from environment variables for API access.
|
64
|
+
|
65
|
+
Environment Variables:
|
66
|
+
- FINOPS_PROFILE_DISPLAY_LENGTH: Max profile name display length
|
67
|
+
- FINOPS_SERVICE_NAME_LENGTH: Max service name display length
|
68
|
+
- FINOPS_MAX_SERVICES_TEXT: Max services in text summaries
|
69
|
+
- FINOPS_HIGH_COST_THRESHOLD: High cost threshold for highlighting
|
70
|
+
- FINOPS_MEDIUM_COST_THRESHOLD: Medium cost threshold for highlighting
|
71
|
+
"""
|
72
|
+
return cls(
|
73
|
+
profile_display_length=cls._get_int_env('FINOPS_PROFILE_DISPLAY_LENGTH'),
|
74
|
+
service_name_length=cls._get_int_env('FINOPS_SERVICE_NAME_LENGTH'),
|
75
|
+
max_services_text=cls._get_int_env('FINOPS_MAX_SERVICES_TEXT'),
|
76
|
+
high_cost_threshold=cls._get_float_env('FINOPS_HIGH_COST_THRESHOLD', 5000.0),
|
77
|
+
medium_cost_threshold=cls._get_float_env('FINOPS_MEDIUM_COST_THRESHOLD', 1000.0)
|
78
|
+
)
|
79
|
+
|
80
|
+
@staticmethod
|
81
|
+
def _get_int_env(key: str, default: Optional[int] = None) -> Optional[int]:
|
82
|
+
"""Get integer value from environment variable."""
|
83
|
+
value = os.getenv(key)
|
84
|
+
return int(value) if value and value.isdigit() else default
|
85
|
+
|
86
|
+
@staticmethod
|
87
|
+
def _get_float_env(key: str, default: float) -> float:
|
88
|
+
"""Get float value from environment variable."""
|
89
|
+
value = os.getenv(key)
|
90
|
+
try:
|
91
|
+
return float(value) if value else default
|
92
|
+
except ValueError:
|
93
|
+
return default
|
94
|
+
|
95
|
+
def apply_smart_defaults(self, terminal_width: Optional[int] = None) -> 'DisplayConfiguration':
|
96
|
+
"""
|
97
|
+
Apply smart defaults based on terminal capabilities.
|
98
|
+
|
99
|
+
Args:
|
100
|
+
terminal_width: Terminal width for adaptive profile display
|
101
|
+
|
102
|
+
Returns:
|
103
|
+
Configuration with smart defaults applied
|
104
|
+
"""
|
105
|
+
config = DisplayConfiguration(
|
106
|
+
profile_display_length=self.profile_display_length,
|
107
|
+
service_name_length=self.service_name_length,
|
108
|
+
max_services_text=self.max_services_text,
|
109
|
+
high_cost_threshold=self.high_cost_threshold,
|
110
|
+
medium_cost_threshold=self.medium_cost_threshold
|
111
|
+
)
|
112
|
+
|
113
|
+
# Smart profile display length based on terminal width
|
114
|
+
if config.profile_display_length is None:
|
115
|
+
if terminal_width and terminal_width > 120:
|
116
|
+
config.profile_display_length = 60 # Wide terminal
|
117
|
+
elif terminal_width and terminal_width > 80:
|
118
|
+
config.profile_display_length = 40 # Standard terminal
|
119
|
+
else:
|
120
|
+
config.profile_display_length = 25 # Narrow terminal
|
121
|
+
|
122
|
+
# Smart service name length for business readability
|
123
|
+
if config.service_name_length is None:
|
124
|
+
config.service_name_length = 25 # Business-appropriate length
|
125
|
+
|
126
|
+
# Smart services count for optimal readability
|
127
|
+
if config.max_services_text is None:
|
128
|
+
config.max_services_text = 10 # Optimal for executive summaries
|
129
|
+
|
130
|
+
return config
|
131
|
+
|
132
|
+
|
133
|
+
class FinOpsConfigManager:
|
134
|
+
"""
|
135
|
+
FinOps Configuration Manager for API-only parameters.
|
136
|
+
|
137
|
+
Provides centralized management of display parameters that were removed
|
138
|
+
from CLI to maintain enterprise simplicity while preserving full API access.
|
139
|
+
"""
|
140
|
+
|
141
|
+
def __init__(self, config: Optional[DisplayConfiguration] = None):
|
142
|
+
"""
|
143
|
+
Initialize configuration manager.
|
144
|
+
|
145
|
+
Args:
|
146
|
+
config: Custom display configuration (defaults to smart defaults)
|
147
|
+
"""
|
148
|
+
self._config = config or DisplayConfiguration.get_default_config()
|
149
|
+
|
150
|
+
@property
|
151
|
+
def config(self) -> DisplayConfiguration:
|
152
|
+
"""Get current display configuration."""
|
153
|
+
return self._config
|
154
|
+
|
155
|
+
def update_config(self, **kwargs) -> None:
|
156
|
+
"""
|
157
|
+
Update configuration parameters programmatically.
|
158
|
+
|
159
|
+
Args:
|
160
|
+
**kwargs: Configuration parameters to update
|
161
|
+
"""
|
162
|
+
for key, value in kwargs.items():
|
163
|
+
if hasattr(self._config, key):
|
164
|
+
setattr(self._config, key, value)
|
165
|
+
|
166
|
+
def get_profile_display_length(self) -> int:
|
167
|
+
"""Get profile display length with smart default."""
|
168
|
+
return self._config.profile_display_length or 50
|
169
|
+
|
170
|
+
def get_service_name_length(self) -> int:
|
171
|
+
"""Get service name length with smart default."""
|
172
|
+
return self._config.service_name_length or 25
|
173
|
+
|
174
|
+
def get_max_services_text(self) -> int:
|
175
|
+
"""Get max services in text with smart default."""
|
176
|
+
return self._config.max_services_text or 10
|
177
|
+
|
178
|
+
def get_high_cost_threshold(self) -> float:
|
179
|
+
"""Get high cost threshold."""
|
180
|
+
return self._config.high_cost_threshold
|
181
|
+
|
182
|
+
def get_medium_cost_threshold(self) -> float:
|
183
|
+
"""Get medium cost threshold."""
|
184
|
+
return self._config.medium_cost_threshold
|
185
|
+
|
186
|
+
def is_high_cost(self, amount: float) -> bool:
|
187
|
+
"""Check if amount exceeds high cost threshold."""
|
188
|
+
return amount > self._config.high_cost_threshold
|
189
|
+
|
190
|
+
def is_medium_cost(self, amount: float) -> bool:
|
191
|
+
"""Check if amount exceeds medium cost threshold."""
|
192
|
+
return amount > self._config.medium_cost_threshold
|
193
|
+
|
194
|
+
def get_cost_category(self, amount: float) -> str:
|
195
|
+
"""Get cost category based on thresholds."""
|
196
|
+
if self.is_high_cost(amount):
|
197
|
+
return "Cost Review Required"
|
198
|
+
elif self.is_medium_cost(amount):
|
199
|
+
return "Right-sizing Review"
|
200
|
+
else:
|
201
|
+
return "Monitor & Optimize"
|
202
|
+
|
203
|
+
|
204
|
+
# Global configuration instance for backwards compatibility
|
205
|
+
_global_config = FinOpsConfigManager()
|
206
|
+
|
207
|
+
|
208
|
+
def get_global_config() -> FinOpsConfigManager:
|
209
|
+
"""Get global configuration manager instance."""
|
210
|
+
return _global_config
|
211
|
+
|
212
|
+
|
213
|
+
def set_global_config(config: DisplayConfiguration) -> None:
|
214
|
+
"""Set global configuration."""
|
215
|
+
global _global_config
|
216
|
+
_global_config = FinOpsConfigManager(config)
|
217
|
+
|
218
|
+
|
219
|
+
# Backwards compatibility functions for existing code
|
220
|
+
def get_profile_display_length(args=None) -> int:
|
221
|
+
"""
|
222
|
+
Get profile display length with backwards compatibility.
|
223
|
+
|
224
|
+
DEPRECATION WARNING: This function is deprecated. Use FinOpsConfigManager.get_profile_display_length() instead.
|
225
|
+
"""
|
226
|
+
if args and hasattr(args, 'profile_display_length') and args.profile_display_length:
|
227
|
+
return args.profile_display_length
|
228
|
+
return _global_config.get_profile_display_length()
|
229
|
+
|
230
|
+
|
231
|
+
def get_service_name_length(args=None) -> int:
|
232
|
+
"""
|
233
|
+
Get service name length with backwards compatibility.
|
234
|
+
|
235
|
+
DEPRECATION WARNING: This function is deprecated. Use FinOpsConfigManager.get_service_name_length() instead.
|
236
|
+
"""
|
237
|
+
if args and hasattr(args, 'service_name_length') and args.service_name_length:
|
238
|
+
return args.service_name_length
|
239
|
+
return _global_config.get_service_name_length()
|
240
|
+
|
241
|
+
|
242
|
+
def get_max_services_text(args=None) -> int:
|
243
|
+
"""
|
244
|
+
Get max services text with backwards compatibility.
|
245
|
+
|
246
|
+
DEPRECATION WARNING: This function is deprecated. Use FinOpsConfigManager.get_max_services_text() instead.
|
247
|
+
"""
|
248
|
+
if args and hasattr(args, 'max_services_text') and args.max_services_text:
|
249
|
+
return args.max_services_text
|
250
|
+
return _global_config.get_max_services_text()
|
251
|
+
|
252
|
+
|
253
|
+
def get_high_cost_threshold(args=None) -> float:
|
254
|
+
"""
|
255
|
+
Get high cost threshold with backwards compatibility.
|
256
|
+
|
257
|
+
DEPRECATION WARNING: This function is deprecated. Use FinOpsConfigManager.get_high_cost_threshold() instead.
|
258
|
+
"""
|
259
|
+
if args and hasattr(args, 'high_cost_threshold') and args.high_cost_threshold:
|
260
|
+
return args.high_cost_threshold
|
261
|
+
return _global_config.get_high_cost_threshold()
|
262
|
+
|
263
|
+
|
264
|
+
def get_medium_cost_threshold(args=None) -> float:
|
265
|
+
"""
|
266
|
+
Get medium cost threshold with backwards compatibility.
|
267
|
+
|
268
|
+
DEPRECATION WARNING: This function is deprecated. Use FinOpsConfigManager.get_medium_cost_threshold() instead.
|
269
|
+
"""
|
270
|
+
if args and hasattr(args, 'medium_cost_threshold') and args.medium_cost_threshold:
|
271
|
+
return args.medium_cost_threshold
|
272
|
+
return _global_config.get_medium_cost_threshold()
|