Last active
August 29, 2018 14:11
-
-
Save ftfarias/c5419f47c38329a90f924391fb7ece21 to your computer and use it in GitHub Desktop.
Script to list reserved instances in AWS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python | |
from argparse import ArgumentParser | |
import boto3 | |
import pandas as pd | |
CSV_FILENAME = 'ec2_reserves.csv' | |
ec2 = boto3.client('ec2') | |
def get_instance_size(name): | |
size_name = name.split('.')[1] | |
if size_name == 'nano': | |
return 1 / 16 | |
if size_name == 'micro': | |
return 1 / 8 | |
if size_name == 'small': | |
return 1 / 4 | |
if size_name == 'medium': | |
return 1 / 2 | |
if size_name == 'large': | |
return 1 | |
xlarge_size = 1 if size_name == 'xlarge' else int(size_name.replace('xlarge', '')) | |
return 2 * xlarge_size | |
def get_instance_family(name): | |
return name.split('.')[0] | |
def count_dedicated_reserves(df: pd.DataFrame) -> pd.Series: | |
group_columns = ['InstanceType', 'AvailabilityZone'] | |
if 'InstanceCount' in df.columns: | |
sr_count = df.groupby(group_columns)['InstanceCount'].sum() | |
else: | |
sr_count = df.groupby(group_columns).size() | |
sr_count.index = sr_count.index.tolist() | |
return sr_count | |
def count_default_reserves(df: pd.DataFrame) -> pd.Series: | |
group_columns = ['InstanceFamily'] | |
if 'InstanceLargeCount' in df.columns: | |
sr = df.groupby(group_columns)['InstanceLargeCount'].sum() | |
else: | |
sr = df.groupby(group_columns)['InstanceTypeSize'].sum() | |
sr.index = sr.index.values + '.large' | |
return sr | |
def concat_default_dedicated(sr_default: pd.Series, sr_dedicated: pd.Series) -> pd.Series: | |
return pd.concat( | |
[sr_default, sr_dedicated], | |
keys=['default', 'dedicated'], | |
names=['Tenancy', 'InstanceFamily'] | |
) | |
def get_active_reserves() -> pd.Series: | |
response = ec2.describe_reserved_instances() | |
df = pd.DataFrame(response['ReservedInstances']) | |
df['InstanceFamily'] = df['InstanceType'].map(get_instance_family) | |
df['InstanceTypeSize'] = df['InstanceType'].map(get_instance_size) | |
df['InstanceLargeCount'] = df['InstanceTypeSize'] * df['InstanceCount'] | |
loc_active = df['State'] == 'active' | |
loc_dedicated_tenancy = df['InstanceTenancy'] == 'dedicated' | |
loc_default_tenancy = df['InstanceTenancy'] == 'default' | |
sr_dedicated = count_dedicated_reserves(df.loc[loc_active & loc_dedicated_tenancy]) | |
sr_default = count_default_reserves(df.loc[loc_active & loc_default_tenancy]) | |
return concat_default_dedicated(sr_default, sr_dedicated) | |
def get_running_instances() -> pd.Series: | |
response = ec2.describe_instances() | |
df = pd.DataFrame([i for r in response['Reservations'] for i in r['Instances']]) | |
df['AvailabilityZone'] = df['Placement'].map(lambda x: x['AvailabilityZone']) | |
df['Tenancy'] = df['Placement'].map(lambda x: x['Tenancy']) | |
df['InstanceFamily'] = df['InstanceType'].map(get_instance_family) | |
df['InstanceTypeSize'] = df['InstanceType'].map(get_instance_size) | |
loc_running = df['State'].map(lambda x: x.get('Name') == 'running') | |
loc_dedicated_tenancy = df['Tenancy'] == 'dedicated' | |
loc_default_tenancy = df['Tenancy'] == 'default' | |
sr_dedicated = count_dedicated_reserves(df.loc[loc_running & loc_dedicated_tenancy]) | |
sr_default = count_default_reserves(df.loc[loc_running & loc_default_tenancy]) | |
return concat_default_dedicated(sr_default, sr_dedicated) | |
def get_usage(sr_active_reserves: pd.Series, sr_active_instances: pd.Series) -> pd.DataFrame: | |
df_usage = pd.merge( | |
sr_active_reserves.to_frame('num_reserved'), | |
sr_active_instances.to_frame('num_running'), | |
how='outer', | |
right_index=True, | |
left_index=True | |
) | |
df_usage = df_usage.fillna(0) | |
df_usage['num_available'] = df_usage['num_reserved'] - df_usage['num_running'] | |
return df_usage | |
def main(to_csv: bool): | |
sr_active_reserves = get_active_reserves() | |
sr_running_instances = get_running_instances() | |
df = get_usage(sr_active_reserves, sr_running_instances) | |
if to_csv: | |
print('Written to ' + CSV_FILENAME) | |
else: | |
df['running/reserved'] = df[['num_running', 'num_reserved']].apply( | |
lambda x: '{:g}/{:g}'.format(*x), axis=1) | |
df['num_available'] = df['num_available'].map('{:g}'.format) | |
print(df[['running/reserved', 'num_available']]) | |
if __name__ == '__main__': | |
parser = ArgumentParser(description='Prints EC2 reserved instances and usage') | |
parser.add_argument('--to-csv', action='store_true', | |
help='Writes output to csv named ' + CSV_FILENAME) | |
args = parser.parse_args() | |
main(args.to_csv) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment