Skip to content

Commit

Permalink
Merge pull request ManageIQ#17142 from lpichler/add_rates_chargeback_…
Browse files Browse the repository at this point in the history
…report

Add rates to chargeback report
  • Loading branch information
gtanzillo authored Mar 20, 2018
2 parents d99daaa + b73e266 commit 90b2801
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 14 deletions.
8 changes: 6 additions & 2 deletions app/models/chargeable_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ def adjustment_to(target_unit)
UNITS[metric] ? detail_measure.adjust(target_unit, UNITS[metric]) : 1
end

def rate_key(sub_metric = nil)
"#{rate_name}_#{sub_metric ? sub_metric + '_' : ''}rate" # rate value (e.g. Storage [Used|Allocated|Fixed] Rate)
end

def metric_key(sub_metric = nil)
"#{rate_name}_#{sub_metric ? sub_metric + '_' : ''}metric" # metric value (e.g. Storage [Used|Allocated|Fixed])
end
Expand All @@ -77,12 +81,12 @@ def metering?
group == 'metering' && source == 'used'
end

private

def rate_name
"#{group}_#{source}"
end

private

def used?
source == 'used'
end
Expand Down
17 changes: 16 additions & 1 deletion app/models/chargeback.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ class Chargeback < ActsAsArModel
:fixed_compute_metric => :integer,
)

def self.dynamic_rate_columns
@chargeable_fields = {}
@chargeable_fields[self.class] ||=
begin
ChargeableField.all.each_with_object({}) do |chargeable_field, result|
next unless report_col_options.keys.include?("#{chargeable_field.rate_name}_cost")
result["#{chargeable_field.rate_name}_rate"] = :string
end
end
end

def self.refresh_dynamic_metric_columns
set_columns_hash(dynamic_rate_columns)
end

def self.build_results_for_report_chargeback(options)
_log.info("Calculating chargeback costs...")
@options = options = ReportOptions.new_from_h(options)
Expand Down Expand Up @@ -140,7 +155,7 @@ def calculate_costs(consumption, rates)
rate.rate_details_relevant_to(relevant_fields).each do |r|
r.charge(relevant_fields, consumption, @options).each do |field, value|
next unless self.class.attribute_names.include?(field)
self[field] = (self[field] || 0) + value
self[field] = self[field].kind_of?(Numeric) ? (self[field] || 0) + value : value
end
end
end
Expand Down
33 changes: 24 additions & 9 deletions app/models/chargeback_rate_detail.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class ChargebackRateDetail < ApplicationRecord

delegate :rate_type, :to => :chargeback_rate, :allow_nil => true

delegate :metric_key, :cost_keys, :to => :chargeable_field
delegate :metric_key, :cost_keys, :rate_key, :to => :chargeable_field

FORM_ATTRIBUTES = %i(description per_time per_unit metric group source metric chargeable_field_id sub_metric).freeze
PER_TIME_TYPES = {
Expand Down Expand Up @@ -84,13 +84,23 @@ def sub_metric_human
sub_metric.present? ? sub_metric.capitalize : 'All'
end

def rate_values(consumption, options)
fixed_rate, variable_rate = find_rate(chargeable_field.measure(consumption, options, sub_metric))
hourly_fixed_rate = hourly(fixed_rate, consumption)
hourly_variable_rate = hourly(variable_rate, consumption)

"#{hourly_fixed_rate}/#{hourly_variable_rate}"
end

def charge(relevant_fields, consumption, options)
result = {}
if (relevant_fields & [metric_key, cost_keys[0]]).present?
metric_value, cost = metric_and_cost_by(consumption, options)
if !consumption.chargeback_fields_present && chargeable_field.fixed?
cost = 0
end

result[rate_key(sub_metric)] = rate_values(consumption, options)
result[metric_key(sub_metric)] = metric_value
cost_keys(sub_metric).each { |field| result[field] = cost }
end
Expand All @@ -99,14 +109,19 @@ def charge(relevant_fields, consumption, options)

# Set the rates according to the tiers
def find_rate(value)
fixed_rate = 0.0
variable_rate = 0.0
tier_found = chargeback_tiers.detect { |tier| tier.includes?(value * rate_adjustment) }
unless tier_found.nil?
fixed_rate = tier_found.fixed_rate
variable_rate = tier_found.variable_rate
end
return fixed_rate, variable_rate
@found_rates ||= {}
@found_rates[value] ||=
begin
fixed_rate = 0.0
variable_rate = 0.0
tier_found = chargeback_tiers.detect { |tier| tier.includes?(value * rate_adjustment) }
unless tier_found.nil?
fixed_rate = tier_found.fixed_rate
variable_rate = tier_found.variable_rate
end

[fixed_rate, variable_rate]
end
end

PER_TIME_MAP = {
Expand Down
5 changes: 3 additions & 2 deletions app/models/chargeback_vm.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def self.attribute_names
loaded_attribute_names = super
loaded_storage_allocated_attributes = loaded_attribute_names.select { |x| x.starts_with?('storage_allocated_') }
loaded_sub_metric_fields = loaded_storage_allocated_attributes - DEFAULT_STORAGE_METRICS
non_existing_sub_metric_fields = loaded_sub_metric_fields - dynamic_columns_for(:float).keys
non_existing_sub_metric_fields = loaded_sub_metric_fields - dynamic_columns_for(:float).keys - dynamic_rate_columns.keys

loaded_attribute_names - non_existing_sub_metric_fields
end
Expand All @@ -60,14 +60,15 @@ def self.dynamic_columns_for(column_type)
volume_types = CloudVolume.volume_types
volume_types.push(nil) if volume_types.present?
volume_types.each_with_object({}) do |volume_type, result|
[:metric, :cost].collect do |type|
%i(metric cost rate).collect do |type|
result["storage_allocated_#{volume_type || 'unclassified'}_#{type}"] = column_type
end
end
end

def self.refresh_dynamic_metric_columns
set_columns_hash(dynamic_columns_for(:float))
super
end

def self.build_results_for_report_ChargebackVm(options)
Expand Down
25 changes: 25 additions & 0 deletions spec/models/chargeback_vm_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@
expect(subject.storage_allocated_hdd_cost).to eq(state_data[:allocated_disk_types]['hdd'] / 1.gigabytes * count_hourly_rate * hours_in_day)
end

it 'shows rates' do
skip('this feature needs to be added to new chargeback rating') if Settings.new_chargeback
expect(subject.storage_allocated_sdd_rate).to eq("0.0/1.0")
expect(subject.storage_allocated_hdd_rate).to eq("0.0/1.0")
end

it "doesn't return removed cloud volume types fields" do
described_class.refresh_dynamic_metric_columns

Expand Down Expand Up @@ -575,6 +581,25 @@ def result_row_for_vm(vm)
expect(subject.cpu_allocated_cost).to be_within(0.01).of(cpu_count * count_hourly_rate * hours_in_month)
end

context 'with nonzero fixed rate' do
let(:hourly_variable_tier_rate) { {:fixed_rate => 100, :variable_rate => hourly_rate.to_s} }

it 'shows rates' do
skip('this case needs to be added in new chargeback') if Settings.new_chargeback

expect(subject.cpu_allocated_rate).to eq("0.0/1.0")
expect(subject.cpu_used_rate).to eq("100.0/0.01")
expect(subject.disk_io_used_rate).to eq("100.0/0.01")
expect(subject.fixed_compute_1_rate).to eq("100.0/0.01")
expect(subject.memory_allocated_rate).to eq("100.0/0.01")
expect(subject.memory_used_rate).to eq("100.0/0.01")
expect(subject.metering_used_rate).to eq("0.0/1.0")
expect(subject.net_io_used_rate).to eq("100.0/0.01")
expect(subject.storage_allocated_rate).to eq("0.0/1.0")
expect(subject.storage_used_rate).to eq("0.0/1.0")
end
end

let(:fixed_rate) { 10.0 }

context "fixed and variable rate" do
Expand Down

0 comments on commit 90b2801

Please sign in to comment.