agent: |
O5FtOq5CNw0pQ0RzZs9iFetch Rightsizing Recommendations for AWS EC2 Instances
Fetch Rightsizing Recommendations for AWS EC2 Instances
There was a problem that the LLM was not able to address. Please rephrase your prompt and try again.
This task retrieves AWS EC2 instance rightsizing recommendations using AWS Compute Optimizer, identifying cost-saving and performance-enhancing opportunities by analyzing usage patterns. It suggests optimal instance types or sizes, ensuring efficient resource utilization.
inputs
outputs
import json
import boto3
from botocore.exceptions import BotoCoreError, ClientError
from datetime import datetime
creds = _get_creds(cred_label)['creds']
access_key = creds['username']
secret_key = creds['password']
# Initialize boto3 clients
#compute_optimizer_client = boto3.client('compute-optimizer', region_name='us-west-2')
pricing_client = boto3.client('pricing',aws_access_key_id=access_key,aws_secret_access_key=secret_key, region_name='us-east-1')
def datetime_converter(o):
if isinstance(o, datetime):
return o.__str__()
def get_price_for_instance(instance_type, region):
# Mapping AWS region to the Pricing API format
region_name_map = {
"us-east-1": "US East (N. Virginia)",
"us-east-2": "US East (Ohio)",
"us-west-1": "US West (N. California)",
"us-west-2": "US West (Oregon)",
"af-south-1": "Africa (Cape Town)",
"ap-east-1": "Asia Pacific (Hong Kong)",
"ap-south-1": "Asia Pacific (Mumbai)",
"ap-northeast-3": "Asia Pacific (Osaka)",
"ap-northeast-2": "Asia Pacific (Seoul)",
"ap-southeast-1": "Asia Pacific (Singapore)",
"ap-southeast-2": "Asia Pacific (Sydney)",
"ap-northeast-1": "Asia Pacific (Tokyo)",
"ca-central-1": "Canada (Central)",
"eu-central-1": "EU (Frankfurt)",
"eu-west-1": "EU (Ireland)",
"eu-west-2": "EU (London)",
"eu-south-1": "EU (Milan)",
"eu-west-3": "EU (Paris)",
"eu-north-1": "EU (Stockholm)",
"me-south-1": "Middle East (Bahrain)",
"sa-east-1": "South America (São Paulo)"}
region_name = region_name_map.get(region, region) # Default to using the region code if no mapping found
try:
response = pricing_client.get_products(
ServiceCode='AmazonEC2',
Filters=[
{'Type': 'TERM_MATCH', 'Field': 'instanceType', 'Value': instance_type},
{'Type': 'TERM_MATCH', 'Field': 'location', 'Value': region_name},
{'Type': 'TERM_MATCH', 'Field': 'preInstalledSw', 'Value': 'NA'},
{'Type': 'TERM_MATCH', 'Field': 'operatingSystem', 'Value': 'Linux'},
{'Type': 'TERM_MATCH', 'Field': 'tenancy', 'Value': 'shared'},
{'Type': 'TERM_MATCH', 'Field': 'capacitystatus', 'Value': 'Used'},
],
MaxResults=1
)
price_info = json.loads(response['PriceList'][0])
price_dimensions = next(iter(price_info['terms']['OnDemand'].values()))['priceDimensions']
price_per_unit = next(iter(price_dimensions.values()))['pricePerUnit']['USD']
return float(price_per_unit)
except Exception as e:
print(f"Error fetching price for {instance_type} in {region}: {e}")
return None
def display_instance_recommendations(recommendations):
# Initialize table with the desired structure and headers
table = context.newtable()
table.title = "EC2 Instance Rightsizing Recommendations"
table.num_cols = 10 # Adjust the number of columns to match the number of attributes you want to display
table.num_rows = 1 # Starts with one row for headers
table.has_header_row = True
# Define header names based on the new structure
headers = [
"Instance ID", "Instance Name", "Current Instance Type", "Region",
"Findings", "Recommended Instance Type", "Migration Effort",
"Savings Opportunity (%)", "Estimated Monthly Savings ($)", "Price Difference ($/hr)"
]
# Set headers in the first row
for col_num, header in enumerate(headers):
table.setval(0, col_num, header)
# Populate the table with data from recommendations
for row_num, recommendation in enumerate(recommendations, start=1):
instance_id = recommendation['instanceArn'].split('/')[-1]
instance_name = recommendation.get('instanceName', 'N/A')
current_instance_type = recommendation.get('currentInstanceType', 'N/A')
region = recommendation['instanceArn'].split(':')[3]
findings = recommendation.get('finding', 'N/A')
migration_effort = "N/A"
savings_opportunity_percentage = "N/A"
estimated_monthly_savings_value = "N/A"
price_difference = "N/A"
if recommendation.get('recommendationOptions'):
option = recommendation['recommendationOptions'][0]
recommended_instance_type = option.get('instanceType')
migration_effort = option.get('migrationEffort', 'N/A')
savings_opportunity_percentage = option.get('savingsOpportunity', {}).get('savingsOpportunityPercentage', 'N/A')
estimated_monthly_savings_value = option.get('savingsOpportunity', {}).get('estimatedMonthlySavings', {}).get('value', 'N/A')
current_price = get_price_for_instance(current_instance_type, region)
recommended_price = get_price_for_instance(recommended_instance_type, region)
price_difference = "N/A" if current_price is None or recommended_price is None else f"{current_price - recommended_price:.4f}"
values = [
instance_id,
instance_name,
current_instance_type,
region,
findings,
recommended_instance_type,
migration_effort,
f"{savings_opportunity_percentage}%",
f"${estimated_monthly_savings_value}",
f"${price_difference}/hr"
]
table.num_rows += 1 # Add a row for each recommendation
for col_num, value in enumerate(values):
table.setval(row_num, col_num, value)
def get_ec2_rightsizing_recommendations(region_name_to_search_recommendations=None):
regions_to_search = []
if region_name_to_search_recommendations:
regions_to_search.append(region_name_to_search_recommendations)
else:
# Fetch all regions if none specified
ec2_client = boto3.client('ec2', aws_access_key_id=access_key, aws_secret_access_key=secret_key, region_name='us-east-1')
all_regions_response = ec2_client.describe_regions()
regions_to_search = [region['RegionName'] for region in all_regions_response['Regions']]
all_recommendations = []
for region in regions_to_search:
try:
# Initialize compute-optimizer client with the proper region
local_compute_optimizer_client = boto3.client('compute-optimizer', aws_access_key_id=access_key, aws_secret_access_key=secret_key, region_name=region)
next_token = None
#page_counter = 1 # To count the number of pages fetched
while True:
if next_token:
response = local_compute_optimizer_client.get_ec2_instance_recommendations(NextToken=next_token)
else:
response = local_compute_optimizer_client.get_ec2_instance_recommendations()
recommendations = response.get('instanceRecommendations', [])
if recommendations:
all_recommendations.extend(recommendations)
#print(f"Fetched {len(recommendations)} recommendations for page {page_counter}.")
# Pagination - Check if there's a next page of recommendations
next_token = response.get('NextToken')
if not next_token:
break # Exit loop if there's no more data to fetch
#page_counter += 1
except ClientError as error:
print(f"Client error in region {region}: {error}")
except BotoCoreError as error:
print(f"BotoCore error in region {region}: {error}")
return all_recommendations
def process_recommendations(region_name_to_search_recommendations=None):
# Fetch recommendations once, using the provided region or searching all regions.
recommendations = get_ec2_rightsizing_recommendations(region_name_to_search_recommendations)
display_instance_recommendations(recommendations) # table printing line
# If no recommendations were found after searching, exit the function.
if not recommendations:
print("No recommendations found. Please check if the region is correct or if there are any permissions issues.")
return
data_for_plotting = []
# Iterate through the fetched recommendations for processing.
for recommendation in recommendations:
# Extract details from each recommendation as before...
instance_id = recommendation['instanceArn'].split('/')[-1]
instance_name = recommendation.get('instanceName', 'N/A')
findings = recommendation.get('finding', 'N/A')
finding_reasons = ", ".join(recommendation.get('findingReasonCodes', []))
instance_state = recommendation.get('instanceState', 'N/A')
current_instance_type = recommendation.get('currentInstanceType', 'N/A')
tags = json.dumps(recommendation.get('tags', []), default=datetime_converter)
account_id = recommendation['instanceArn'].split(':')[4]
region = recommendation['instanceArn'].split(':')[3]
# Print details for each recommendation...
print(f"Instance ID: {instance_id}")
print(f"Instance Name: {instance_name}")
print(f"Findings: {findings}")
print(f"Finding Reasons: {finding_reasons}")
print(f"Instance State: {instance_state}")
print(f"Current Instance Type: {current_instance_type}")
print(f"Tags: {tags}")
print(f"Account ID: {account_id}")
print(f"Region: {region}")
print("-" * 50)
for option in recommendation['recommendationOptions']:
recommended_instance_type = option.get('instanceType')
migration_effort = option.get('migrationEffort', 'N/A')
savings_opportunity_percentage = option.get('savingsOpportunity', {}).get('savingsOpportunityPercentage', 'N/A')
estimated_monthly_savings_value = option.get('savingsOpportunity', {}).get('estimatedMonthlySavings', {}).get('value', 'N/A')
current_price = get_price_for_instance(current_instance_type, region)
recommended_price = get_price_for_instance(recommended_instance_type, region)
price_difference = "N/A" if current_price is None or recommended_price is None else current_price - recommended_price
data_for_plotting.append({
"instance_id": instance_id,
"instance_name": instance_name,
"estimated_monthly_savings_value": estimated_monthly_savings_value
})
print(f"\tRecommended Instance Type: {recommended_instance_type}")
print(f"\tMigration Effort: {migration_effort}")
print(f"\tSavings Opportunity (%): {savings_opportunity_percentage}")
print(f"\tEstimated Monthly Savings: USD {estimated_monthly_savings_value}")
print(f"\tCurrent Price: {current_price if current_price is not None else 'N/A'} USD per hour")
print(f"\tRecommended Price: {recommended_price if recommended_price is not None else 'N/A'} USD per hour")
print(f"\tPrice Difference: {price_difference} USD per hour")
print("-" * 25)
return data_for_plotting
#region_name_to_search_recommendations = None
data_for_plotting = process_recommendations(region_name_to_search_recommendations)
copied