diff --git a/app/controllers/setup_controller.rb b/app/controllers/setup_controller.rb
index 4f4742d..5a658e1 100644
--- a/app/controllers/setup_controller.rb
+++ b/app/controllers/setup_controller.rb
@@ -5,6 +5,7 @@ class SetupController < ApplicationController
def index
@regions = Setup.get_regions
@minutes = Setup.get_minutes
+ @minutesrefresh = Setup.get_minutesrefresh
@importdbr = Setup.get_importdbr
@s3bucket = Setup.get_s3bucket
@processed = Setup.get_processed
@@ -18,6 +19,9 @@ def change
if !params[:minutes].blank?
Setup.put_minutes params[:minutes] if params[:minutes].to_i >= 30 || params[:minutes].to_i == 0
end
+ if !params[:minutesrefresh].blank?
+ Setup.put_minutesrefresh params[:minutesrefresh] if params[:minutesrefresh].to_i >= 5
+ end
Setup.put_password params[:password] if !params[:password].blank?
Setup.put_importdbr !params[:importdbr].blank?
Setup.put_affinity !params[:affinity].blank?
diff --git a/app/controllers/summary_controller.rb b/app/controllers/summary_controller.rb
index 3600882..7eb486b 100644
--- a/app/controllers/summary_controller.rb
+++ b/app/controllers/summary_controller.rb
@@ -7,7 +7,7 @@ class SummaryController < ApplicationController
def index
instances = Instance.all
reserved_instances = ReservedInstance.all
- @summary = get_summary(instances,reserved_instances)
+ @summary = Summary.all
end
def health
@@ -15,27 +15,9 @@ def health
end
def recommendations
- instances2 = Instance.all
- reserved_instances2 = ReservedInstance.all
- summary = get_summary(instances2,reserved_instances2)
-
- instances = {}
- instances2.each do |instance|
- instances[instance.instanceid] = {:account_id => instance.accountid, :type => instance.instancetype, :az => instance.az, :platform => instance.platform, :tenancy => instance.tenancy, :vpc => instance.network}
- end
-
- reserved_instances = {}
- reserved_instances2.each do |ri|
- reserved_instances[ri.reservationid] = {:account_id => ri.accountid, :type => ri.instancetype, :az => ri.az, :tenancy => ri.tenancy, :platform => ri.platform, :vpc => ri.network, :count => ri.count, :end => ri.enddate, :status => ri.status, :offering => ri.offering, :duration => ri.duration}
- end
-
- continue_iteration = true
@recommendations = []
- while continue_iteration do
- excess = {}
- # Excess of Instances and Reserved Instances per set of interchangable types
- calculate_excess(summary, excess)
- continue_iteration = iterate_recommendation(excess, instances, summary, reserved_instances, @recommendations)
+ RecommendationCache.all.each do |recommenation|
+ @recommendations << Marshal.load(recommenation.object)
end
end
@@ -50,7 +32,10 @@ def apply_recommendations
end
def periodic_worker
- populatedb_data()
+ if Setup.now_after_nextrefresh
+ Setup.update_nextrefresh
+ populatedb_data()
+ end
if Setup.now_after_next
Setup.update_next
@@ -127,265 +112,6 @@ def populatedb
private
- def iterate_recommendation(excess, instances, summary, reserved_instances, recommendations)
- 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)
- end
- return true if calculate_recommendation(instances, family, region, platform, tenancy, summary, reserved_instances, recommendations, false)
- end
- end
- end
- end
- end
- return false
- end
-
- def calculate_recommendation(instances, family, region, platform, tenancy, summary, reserved_instances, recommendations, affinity)
- excess_instance = []
-
- instances.each do |instance_id, instance|
- if instance[:type].split(".")[0] == family && instance[:az][0..-2] == region && instance[:platform] == platform && instance[:tenancy] == tenancy
- # This instance is of the usable type
- if summary[instance[:type]][instance[:az]][instance[:platform]][instance[:tenancy]][0] > summary[instance[:type]][instance[:az]][instance[:platform]][instance[:tenancy]][1]
- # If for this instance type we have excess of instances
- excess_instance << instance_id
- end
- end
- end
-
- # First look for AZ changes
- reserved_instances.each do |ri_id, ri|
- if !ri.nil? && ri[:type].split(".")[0] == family && ri[:az][0..-2] == region && ri[:platform] == platform && ri[:tenancy] == tenancy && ri[:status] == 'active'
- # This reserved instance is of the usable type
- if summary[ri[:type]][ri[:az]][ri[:platform]][ri[:tenancy]][1] > summary[ri[:type]][ri[:az]][ri[:platform]][ri[:tenancy]][0]
- # 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|
- # Change with the same type
- if instances[instance_id][:type] == ri[:type] && (!affinity || instances[instance_id][:account_id] == ri[:account_id])
- 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
- 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
- reserved_instances[ri_id] = nil if reserved_instances[ri_id][:count] == 0
- recommendations << [recommendation]
- return true
- end
- end
- end
- end
- end
-
- # Now I look for type changes
- # Only for Linux instances
- if platform == 'Linux/UNIX'
- reserved_instances.each do |ri_id, ri|
- if !ri.nil? && ri[:type].split(".")[0] == family && ri[:az][0..-2] == region && ri[:platform] == platform && ri[:tenancy] == tenancy && ri[:status] == 'active'
- # This reserved instance is of the usable type
- if summary[ri[:type]][ri[:az]][ri[:platform]][ri[:tenancy]][1] > summary[ri[:type]][ri[:az]][ri[:platform]][ri[:tenancy]][0]
- # 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])
- factor_instance = get_factor(instances[instance_id][:type])
- factor_ri = get_factor(ri[:type])
- recommendation = {rid: ri_id}
- recommendation[:type] = instances[instance_id][:type]
- recommendation[:az] = instances[instance_id][:az] if instances[instance_id][:az] != ri[:az]
- #recommendation[:vpc] = instances[instance_id][:vpc] if instances[instance_id][:vpc] != ri[:vpc]
- if factor_ri > factor_instance
- # Split the RI
- new_instances = factor_ri / factor_instance
- recommendation[:count] = new_instances.to_i
- recommendation[:orig_count] = 1
- #Rails.logger.debug("Change in the RI #{ri_id}, split in #{new_instances} to type #{instances[instance_id][:type]}")
-
- summary[ri[:type]][ri[:az]][ri[:platform]][ri[:tenancy]][1] -= 1
- summary[instances[instance_id][:type]][instances[instance_id][:az]][ri[:platform]][ri[:tenancy]][1] += new_instances
- reserved_instances[ri_id][:count] -= 1
- reserved_instances[ri_id] = nil if reserved_instances[ri_id][:count] == 0
- recommendations << [recommendation]
- return true
- else
- # Join the RI, I need more RIs to complete the needed factor of the instance
- ri_needed = factor_instance / factor_ri
- if (ri[:count] > ri_needed) && (summary[ri[:type]][ri[:az]][ri[:platform]][ri[:tenancy]][1]-ri_needed) >= summary[ri[:type]][ri[:az]][ri[:platform]][ri[:tenancy]][0]
- # We only need join part of this RI to reach to the needed number of instances
- recommendation[:count] = 1
- recommendation[:orig_count] = ri_needed
- summary[ri[:type]][ri[:az]][ri[:platform]][ri[:tenancy]][1] -= ri_needed
- summary[instances[instance_id][:type]][instances[instance_id][:az]][ri[:platform]][ri[:tenancy]][1] += 1
- reserved_instances[ri_id][:count] -= ri_needed
- reserved_instances[ri_id] = nil if reserved_instances[ri_id][:count] == 0
- recommendations << [recommendation]
- return true
- else
- # We need to find more RIs to join with this one
- list_ris = [ri]
- list_ri_ids = [ri_id]
- count_ri = [(summary[ri[:type]][ri[:az]][ri[:platform]][ri[:tenancy]][1]-summary[ri[:type]][ri[:az]][ri[:platform]][ri[:tenancy]][0]), ri[:count]].min
- list_ri_counts = [count_ri]
- factor_ri_needed = factor_instance - (factor_ri*count_ri)
-
- reserved_instances.each do |ri_id2, ri2|
- if !ri2.nil? && ri2[:type].split(".")[0] == family && ri2[:az][0..-2] == region && ri2[:platform] == platform && ri2[:tenancy] == tenancy && ri2[:status] == 'active' && ri2[:account_id] == ri[:account_id] && !list_ri_ids.include?(ri_id2) && ri[:end].change(:min => 0) == ri2[:end].change(:min => 0) && ri[:offering] == ri2[:offering] && ri[:duration] == ri2[:duration]
- if summary[ri2[:type]][ri2[:az]][ri2[:platform]][ri2[:tenancy]][1] > summary[ri2[:type]][ri2[:az]][ri2[:platform]][ri2[:tenancy]][0]
- factor_ri2 = get_factor(ri2[:type])
- if factor_ri2 < factor_instance
- list_ris << ri2
- list_ri_ids << ri_id2
- count_ri = [(summary[ri2[:type]][ri2[:az]][ri2[:platform]][ri2[:tenancy]][1]-summary[ri2[:type]][ri2[:az]][ri2[:platform]][ri2[:tenancy]][0]), ri2[:count]].min
- if (factor_ri2*count_ri) > factor_ri_needed
- count_ri = factor_ri_needed/factor_ri2
- list_ri_counts << count_ri
- factor_ri_needed -= factor_ri2*count_ri
- break
- else
- list_ri_counts << count_ri
- factor_ri_needed -= factor_ri2*count_ri
- end
- end
- end
- end
- end
- if factor_ri_needed == 0
- recommendation_complex = []
- summary[instances[instance_id][:type]][instances[instance_id][:az]][instances[instance_id][:platform]][instances[instance_id][:tenancy]][1] += 1
- list_ris.each_index do |i|
- recommendation = {rid: list_ri_ids[i]}
- recommendation[:type] = instances[instance_id][:type]
- recommendation[:az] = instances[instance_id][:az] if instances[instance_id][:az] != list_ris[i][:az]
- recommendation[:count] = 1
- recommendation[:orig_count] = list_ri_counts[i]
- summary[list_ris[i][:type]][list_ris[i][:az]][list_ris[i][:platform]][list_ris[i][:tenancy]][1] -= list_ri_counts[i]
- reserved_instances[list_ri_ids[i]][:count] -= list_ri_counts[i]
- reserved_instances[list_ri_ids[i]] = nil if reserved_instances[list_ri_ids[i]][:count] == 0
- recommendation_complex << recommendation
- end
- recommendations << recommendation_complex
- return true
- end
- end
- end
- end
- end
- end
- end
- end
- end
-
- return false
-
- end
-
- def get_total_compatible_ri_factor(ri, reserved_instances, summary)
- # Return the total execess of ri with the same end date, compatible to join
- total_number = 0
- reserved_instances.each do |ri2_id, ri2|
- if !ri2.nil? && ri[:type].split(".")[0] == ri2[:type].split(".")[0] && ri[:az][0..-2] == ri2[:az][0..-2] && ri[:platform] == ri2[:platform] && ri[:tenancy] == ri2[:tenancy] && ri2[:status] == 'active' && ri[:end] == ri2[:end]
- # This reserved instance is of the same type
- if summary[ri2[:type]][ri2[:az]][ri2[:platform]][ri2[:tenancy]][1] > summary[ri2[:type]][ri2[:az]][ri2[:platform]][ri2[:tenancy]][0]
- # If for this reservation type we have excess of RIs
- total_number += get_factor(ri2[:type]) * (summary[ri2[:type]][ri2[:az]][ri2[:platform]][ri2[:tenancy]][1] - summary[ri2[:type]][ri2[:az]][ri2[:platform]][ri2[:tenancy]][0])
- end
- end
- end
- return total_number
- end
-
- def get_combination_ris(list_ris, value_expected)
- list_ris.each_index do |i|
- ri = list_ris[i]
- ri_factor = get_factor(ri[:type])
- return [ri] if value_expected == ri_factor
- if value_expected > ri_factor
- new_list = Array.new(list_ris)
- new_list = list_ris.delete_at(i)
- new_combination = get_combination_ris(new_list, value_expected-ri_factor)
- return new_combination << ri if !new_combination.nil?
- end
- end
- return nil
- end
-
- def get_list_possible_ris(ri, reserved_instances, summary)
- # Return the list of ri with the same end date, compatible to join
- possible_ris = []
- reserved_instances.each do |ri2_id, ri2|
- if !ri2.nil? && ri[:type].split(".")[0] == ri2[:type].split(".")[0] && ri[:az][0..-2] == ri2[:az][0..-2] && ri[:platform] == ri2[:platform] && ri[:tenancy] == ri2[:tenancy] && ri2[:status] == 'active' && ri[:end] == ri2[:end]
- # This reserved instance is of the same type
- if summary[ri2[:type]][ri2[:az]][ri2[:platform]][ri2[:tenancy]][1] > summary[ri2[:type]][ri2[:az]][ri2[:platform]][ri2[:tenancy]][0]
- # If for this reservation type we have excess of RIs
- possible_ris << ri2
- end
- end
- end
- return possible_ris
- end
-
- def calculate_excess(summary, excess)
- # Group the excess of RIs and instances per family and region
- # For example, for m3 in eu-west-1, it calculate the total RIs not used and the total instances not assigned to an RI (in any family type and AZ)
- summary.each do |type, elem1|
- elem1.each do |az, elem2|
- elem2.each do |platform, elem3|
- elem3.each do |tenancy, total|
- if total[0] != total[1]
- family = type.split(".")[0]
- region = az[0..-2]
- excess[family] = {} if excess[family].nil?
- excess[family][region] = {} if excess[family][region].nil?
- excess[family][region][platform] = {} if excess[family][region][platform].nil?
- excess[family][region][platform][tenancy] = [0,0] if excess[family][region][platform][tenancy].nil?
- factor = get_factor(type)
- if total[0] > total[1]
- # [0] -> Total of instances without a reserved instance
- excess[family][region][platform][tenancy][0] += (total[0]-total[1])*factor
- else
- # [1] -> Total of reserved instances not used
- excess[family][region][platform][tenancy][1] += (total[1]-total[0])*factor
- end
- end
- end
- end
- end
- end
- end
-
- def get_summary(instances, reserved_instances)
- summary = {}
-
- instances.each do |instance|
- summary[instance.instancetype] = {} if summary[instance.instancetype].nil?
- summary[instance.instancetype][instance.az] = {} if summary[instance.instancetype][instance.az].nil?
- summary[instance.instancetype][instance.az][instance.platform] = {} if summary[instance.instancetype][instance.az][instance.platform].nil?
- summary[instance.instancetype][instance.az][instance.platform][instance.tenancy] = [0,0] if summary[instance.instancetype][instance.az][instance.platform][instance.tenancy].nil?
- summary[instance.instancetype][instance.az][instance.platform][instance.tenancy][0] += 1
- end
-
- reserved_instances.each do |ri|
- if ri.status == 'active'
- summary[ri.instancetype] = {} if summary[ri.instancetype].nil?
- summary[ri.instancetype][ri.az] = {} if summary[ri.instancetype][ri.az].nil?
- summary[ri.instancetype][ri.az][ri.platform] = {} if summary[ri.instancetype][ri.az][ri.platform].nil?
- summary[ri.instancetype][ri.az][ri.platform][ri.tenancy] = [0,0] if summary[ri.instancetype][ri.az][ri.platform][ri.tenancy].nil?
- summary[ri.instancetype][ri.az][ri.platform][ri.tenancy][1] += ri.count
- end
- end
-
- return summary
- end
-
def authenticate_local
render :nothing => true, :status => :unauthorized if !Socket.ip_address_list.map {|x| x.ip_address}.include?(request.remote_ip)
end
diff --git a/app/models/recommendation_cache.rb b/app/models/recommendation_cache.rb
new file mode 100644
index 0000000..f30284c
--- /dev/null
+++ b/app/models/recommendation_cache.rb
@@ -0,0 +1,2 @@
+class RecommendationCache < ActiveRecord::Base
+end
diff --git a/app/models/setup.rb b/app/models/setup.rb
index 5a75845..d9761e3 100644
--- a/app/models/setup.rb
+++ b/app/models/setup.rb
@@ -19,6 +19,24 @@ def self.put_minutes(minutes)
update_next
end
+ def self.get_minutesrefresh
+ minutesrefresh = 5
+ setup = Setup.first
+ if !setup.nil?
+ minutesrefresh = setup.minutesrefresh if !setup.minutesrefresh.nil?
+ end
+ return minutesrefresh
+ end
+
+ def self.put_minutesrefresh(minutes)
+ setup = Setup.first
+ setup = Setup.new if setup.nil?
+
+ setup.minutesrefresh = minutes
+ setup.save
+ update_nextrefresh
+ end
+
def self.get_password
password = BCrypt::Password.create(ENV['DEFAULT_PASSWORD'])
setup = Setup.first
@@ -131,6 +149,30 @@ def self.now_after_next
return after_next
end
+ def self.update_nextrefresh
+ minutesrefresh = 5
+ setup = Setup.first
+ if !setup.nil?
+ minutesrefresh = setup.minutesrefresh if !setup.minutesrefresh.nil?
+ end
+
+ setup.nextrefresh = Time.current + minutesrefresh.minutes
+ setup.save
+ end
+
+ def self.now_after_nextrefresh
+ ###### DELETE THIS #######
+ return true
+ ###### DELETE THIS #######
+ after_nextrefresh = true
+ setup = Setup.first
+ if !setup.nil? && !setup.nextrefresh.nil?
+ after_nextrefresh = (setup.nextrefresh < Time.current)
+ end
+ return after_nextrefresh
+ end
+
+
def self.get_regions
regions = {"eu-west-1"=> false, "us-east-1"=> false, "eu-central-1"=> false, "us-west-1"=> false, "us-west-2"=> false, "ap-southeast-1"=> false, "ap-southeast-2"=> false, "ap-northeast-1"=> false, "sa-east-1"=> false}
setup = Setup.first
diff --git a/app/models/summary.rb b/app/models/summary.rb
new file mode 100644
index 0000000..f990a32
--- /dev/null
+++ b/app/models/summary.rb
@@ -0,0 +1,2 @@
+class Summary < ActiveRecord::Base
+end
diff --git a/app/views/setup/index.erb b/app/views/setup/index.erb
index be40b48..f570467 100644
--- a/app/views/setup/index.erb
+++ b/app/views/setup/index.erb
@@ -14,6 +14,10 @@
<%= number_field_tag "minutes", @minutes.to_s, min:0, id: 'minutestext', class: 'form-control' %>
minutes (0 to stop, minimum 30)
+