Skip to content

Commit

Permalink
Now it checks if the account have access to the destination AZ
Browse files Browse the repository at this point in the history
  • Loading branch information
jros2300 committed Jun 16, 2016
1 parent 7119c08 commit 395369c
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 13 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
## Introduction
This tool is to manage your Reserved Instances in AWS across all your linked accounts. The tool reads your live configuration (instances and reserved instances) in all your accounts using the AWS API, and produces a set of recommendations to optimize your RI usage. The tool can apply the modifications in your RIs (changing the availability zone, instance type or network allocation) for you, and you can configure it to apply all the recommendations automatically every so often.

> **Note:** If you were using the version 2.1 or previous you should modify your IAM roles in all the accounts to add the permission "ec2:DescribeAvailabilityZones" to all of them.
> **Note:** Now the tool supports the instance types "Windows with SQL Standard", "Windows with SQL Web", "Windows with SQL Enterprise", "RHEL" and "SLES". If you're using any of these instance types you should configure the DBR file.
## Installation
Expand All @@ -28,7 +30,8 @@ For each account where you're not going to deploy the tool (so for all but accou
"ec2:DescribeReservedInstancesModifications",
"ec2:DescribeReservedInstancesOfferings",
"ec2:DescribeAccountAttributes",
"ec2:ModifyReservedInstances"
"ec2:ModifyReservedInstances",
"ec2:DescribeAvailabilityZones"
],
"Resource": [
"*"
Expand Down
9 changes: 8 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20160616142650) do
ActiveRecord::Schema.define(version: 20160616151823) do

create_table "amis", force: :cascade do |t|
t.string "ami"
Expand Down Expand Up @@ -100,4 +100,11 @@
t.datetime "updated_at", null: false
end

create_table "zones", force: :cascade do |t|
t.string "accountid"
t.string "az"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end

end
52 changes: 41 additions & 11 deletions lib/aws_common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,37 @@ def get_instances(regions, account_ids)
instances
end

def get_zones(regions, account_ids)
return get_mock_zones if !ENV['MOCK_DATA'].blank?
zones = {}
current_account_id = get_current_account_id
account_ids.each do |account_id|
zones[account_id[0]] = []
regions.select {|key, value| value }.keys.each do |region|
if account_id[0] == current_account_id
ec2 = Aws::EC2::Client.new(region: region)
else
role_credentials = Aws::AssumeRoleCredentials.new( client: Aws::STS::Client.new(region: region), role_arn: account_id[1], role_session_name: "reserved_instances" )
ec2 = Aws::EC2::Client.new(region: region, credentials: role_credentials)
end
resp = ec2.describe_availability_zones()
resp.availability_zones.each do |az|
zones[account_id[0]] << az.zone_name if az.state == 'available'
end
end
end
zones
end

def get_mock_zones()
zones = {}
CSV.foreach('/tmp/zones.csv', headers: true) do |row|
zones[row[1]] = [] if !zones[row[1]]
zones[row[1]] << row[2]
end
return zones
end

def get_failed_modifications(regions, account_ids)
return [] if !ENV['MOCK_DATA'].blank?
failed_modifications = []
Expand Down Expand Up @@ -451,6 +482,7 @@ def populatedb_data
reserved_instances = get_reserved_instances(Setup.get_regions, get_account_ids)
failed_modifications = get_failed_modifications(Setup.get_regions, get_account_ids)
summary = get_summary(instances, reserved_instances)
zones = get_zones(Setup.get_regions, get_account_ids)

continue_iteration = true
recommendations = []
Expand All @@ -461,7 +493,7 @@ def populatedb_data
excess = {}
# Excess of Instances and Reserved Instances per set of interchangable types
calculate_excess(summary2, excess)
continue_iteration = iterate_recommendation(excess, instances2, summary2, reserved_instances2, recommendations)
continue_iteration = iterate_recommendation(excess, instances2, summary2, reserved_instances2, recommendations, zones)
end

ActiveRecord::Base.transaction do
Expand Down Expand Up @@ -584,17 +616,17 @@ def calculate_excess(summary, excess)
end
end

def iterate_recommendation(excess, instances, summary, reserved_instances, recommendations)
def iterate_recommendation(excess, instances, summary, reserved_instances, recommendations, zones)
excess.each do |family, elem1|
elem1.each do |region, elem2|
elem2.each do |platform, elem3|
elem3.each do |tenancy, total|
if total[1] > 0 && total[0] > 0
# There are reserved instances not used and instances on-demand
if Setup.get_affinity
return true if calculate_recommendation(instances, family, region, platform, tenancy, summary, reserved_instances, recommendations, true)
return true if calculate_recommendation(instances, family, region, platform, tenancy, summary, reserved_instances, recommendations, true, zones)
end
return true if calculate_recommendation(instances, family, region, platform, tenancy, summary, reserved_instances, recommendations, false)
return true if calculate_recommendation(instances, family, region, platform, tenancy, summary, reserved_instances, recommendations, false, zones)
end
end
end
Expand All @@ -603,7 +635,7 @@ def iterate_recommendation(excess, instances, summary, reserved_instances, recom
return false
end

def calculate_recommendation(instances, family, region, platform, tenancy, summary, reserved_instances, recommendations, affinity)
def calculate_recommendation(instances, family, region, platform, tenancy, summary, reserved_instances, recommendations, affinity, zones)
excess_instance = []

instances.each do |instance_id, instance|
Expand All @@ -625,12 +657,10 @@ def calculate_recommendation(instances, family, region, platform, tenancy, summa
# I'm going to look for an instance which can use this reservation
excess_instance.each do |instance_id|
# Change with the same type
if instances[instance_id][:type] == ri[:type] && (!affinity || instances[instance_id][:account_id] == ri[:account_id])
if instances[instance_id][:type] == ri[:type] && (!affinity || instances[instance_id][:account_id] == ri[:account_id]) && (instances[instance_id][:az] != ri[:az]) && zones[ri[:account_id]].include?(instances[instance_id][:az])
recommendation = {rid: ri_id, count: 1, orig_count: 1}
if instances[instance_id][:az] != ri[:az]
recommendation[:az] = instances[instance_id][:az]
#Rails.logger.debug("Change in the RI #{ri_id}, to az #{instances[instance_id][:az]}")
end
recommendation[:az] = instances[instance_id][:az]
#Rails.logger.debug("Change in the RI #{ri_id}, to az #{instances[instance_id][:az]}")
summary[ri[:type]][ri[:az]][ri[:platform]][ri[:tenancy]][1] -= 1
summary[ri[:type]][instances[instance_id][:az]][ri[:platform]][ri[:tenancy]][1] += 1
reserved_instances[ri_id][:count] -= 1
Expand All @@ -653,7 +683,7 @@ def calculate_recommendation(instances, family, region, platform, tenancy, summa
# If for this reservation type we have excess of RIs
# I'm going to look for an instance which can use this reservation
excess_instance.each do |instance_id|
if instances[instance_id][:type] != ri[:type] && (!affinity || instances[instance_id][:account_id] == ri[:account_id])
if instances[instance_id][:type] != ri[:type] && (!affinity || instances[instance_id][:account_id] == ri[:account_id]) && zones[ri[:account_id]].include?(instances[instance_id][:az])
factor_instance = get_factor(instances[instance_id][:type])
factor_ri = get_factor(ri[:type])
recommendation = {rid: ri_id}
Expand Down

0 comments on commit 395369c

Please sign in to comment.