-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathawsEC2pricing.py
195 lines (159 loc) · 5.85 KB
/
awsEC2pricing.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
"""
AWS EC2 Price Finder - A tool to find and compare EC2 instance prices including spot instances.
Provides both on-demand and spot pricing information along with interruption rates.
"""
import sys
from typing import Tuple, List, Optional, Union
from colorama import Fore, Style
from includes import (
list_regions, list_os, find_ec2, get_ec2_spot_price,
get_ec2_spot_interruption, print_help, region_map,
P_VCPU, P_RAM, P_OS, P_REGION, REGION_NVIRGINIA
)
# Constants
MAX_EC2_RESULTS = 10
HOURS_PER_DAY = 24
DAYS_PER_MONTH = 30
MONTHLY_HOURS = HOURS_PER_DAY * DAYS_PER_MONTH
# Output format templates
HEADER_FORMAT = "{:<15} {:<6} {:<6} {:<10} {:<8} {:<11} {:<8} {:<10} {:<8}"
INSTANCE_FORMAT = "{:<15} {:<6.2f} {:<6.2f} {:<10} {:.5f} {:<10.5f} {:.5f} {:<10.5f} {:<3}"
SUMMARY_FORMAT = (
Style.RESET_ALL + "--------------------------\n" +
Fore.GREEN + " vCPU: {0:.2f}\n RAM: {1:.2f}\n OS: {2}\n Region: {3}\n" +
Style.RESET_ALL + "--------------------------"
)
def get_sanitized_args(testing: bool) -> List[str]:
"""
Get and sanitize command line arguments.
Args:
testing: Boolean indicating if in test mode
Returns:
List of sanitized command line arguments
"""
if testing:
return ['', '-t', '8', '16', 'Linux', REGION_NVIRGINIA]
# Create a copy of sys.argv to avoid modifying the original
args = sys.argv.copy()
# Sanitize and validate each argument
sanitized_args = []
for arg in args:
# Remove any potentially dangerous characters
cleaned_arg = ''.join(c for c in str(arg) if c.isalnum() or c in '.-_')
sanitized_args.append(cleaned_arg)
return sanitized_args
def get_sys_argv(pp_args: List[str]) -> Tuple[bool, bool, float, float, str, str]:
"""
Parse and validate command line arguments.
Args:
pp_args: List of command line arguments
Returns:
Tuple containing:
- success: Boolean indicating if parsing was successful
- text_only: Boolean for text-only output
- vcpu: Number of virtual CPUs
- ram: Amount of RAM in GB
- os: Operating system
- region: AWS region
Raises:
ValueError: If numeric arguments cannot be parsed
"""
if len(pp_args) == 1:
print('no parameters. Check help with -h')
return False, False, 0, 0, '', ''
if pp_args[1] == '-h':
print_help()
return False, False, 0, 0, '', ''
text_only = pp_args[1] == '-t'
if not text_only and pp_args[1] != '-h':
print('incorrect parameter check help with -h')
return False, False, 0, 0, '', ''
# Default values
vcpu, ram = P_VCPU, P_RAM
os_type, region = P_OS, P_REGION
# Parse vCPU
if len(pp_args) > 2:
try:
vcpu = float(pp_args[2])
except ValueError:
print('Please use an integer or floating number for vCPU')
return False, False, 0, 0, '', ''
# Parse RAM
if len(pp_args) > 3:
try:
ram = float(pp_args[3])
except ValueError:
print('Please use an integer or floating number for RAM')
return False, False, 0, 0, '', ''
# Parse OS
if len(pp_args) > 4:
os_type = pp_args[4]
if os_type not in list_os:
print("Enter one of the values for os:", list_os)
return False, False, 0, 0, '', ''
# Parse Region
if len(pp_args) > 5:
region = pp_args[5]
if region not in list_regions:
print("Enter one of the values for regions. Check help with -h")
return False, False, 0, 0, '', ''
return True, text_only, vcpu, ram, os_type, region
def print_instance_details(
result_row: tuple,
spot_price: float,
kill_rate: str
) -> None:
"""
Print formatted instance details including pricing information.
Args:
result_row: Tuple containing instance information from find_ec2
spot_price: Hourly spot price
kill_rate: Instance interruption rate
"""
instance = result_row[1] # Instance name is at index 1
vcpu = result_row[2] # vCPU is at index 2
ram = result_row[3] # RAM is at index 3
os_type = result_row[4] # OS is at index 4
price = result_row[5] # Price is at index 5
spot_price_monthly = spot_price * MONTHLY_HOURS
price_monthly = price * MONTHLY_HOURS
print(Fore.GREEN + INSTANCE_FORMAT.format(
instance, vcpu, ram, os_type, price, price_monthly,
spot_price, spot_price_monthly, kill_rate
))
def main(testing: bool = False) -> Optional[bool]:
"""
Main function to process EC2 instance pricing information.
Args:
testing: Boolean flag for test mode
Returns:
Boolean indicating success in test mode, None otherwise
"""
pp_args = get_sanitized_args(testing)
success, text_only, vcpu, ram, os_type, region = get_sys_argv(pp_args)
if not success:
sys.exit()
if text_only:
result = find_ec2(cpu=vcpu, ram=ram, os=os_type, region=region, limit=MAX_EC2_RESULTS)
print(Fore.GREEN + SUMMARY_FORMAT.format(vcpu, ram, os_type, region))
print(Fore.LIGHTGREEN_EX + HEADER_FORMAT.format(
"Instance", "vCPU", "RAM", "OS", "PriceH", "PriceM", "SpotH", "SpotM", "KillRate"
))
instances = [r[1] for r in result]
spot_prices = get_ec2_spot_price(instances=instances, os=os_type, region=region)
spot_interrupt_rates = get_ec2_spot_interruption(
instances=instances,
os=os_type,
region=region_map[region]
)
for row in result:
print_instance_details(
row,
spot_prices[row[1]],
spot_interrupt_rates[row[1]]
)
print(Style.RESET_ALL)
if testing:
return True
if __name__ == '__main__':
main()