Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Charge only for past hours #13134

Merged
merged 6 commits into from
Jan 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions app/models/chargeback/consumption.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ def initialize(start_time, end_time)
@start_time, @end_time = start_time, end_time
end

def hours_in_interval
@hours_in_interval ||= (@end_time - @start_time).round / 1.hour
def past_hours_in_interval
# We cannot charge for future hours (i.e. weekly report on Monday, should charge just monday)
@past_hours_in_interval ||= begin
past = (Time.current - @start_time).round / 1.hour
[past, hours_in_interval].min
end
end

def hours_in_month
Expand All @@ -16,6 +20,10 @@ def hours_in_month

private

def hours_in_interval
@hours_in_interval ||= (@end_time - @start_time).round / 1.hour
end

def monthly?
# A heuristic. Is the interval lenght about 30 days?
(hours_in_interval * 1.hour - 1.month).abs < 3.days
Expand Down
2 changes: 1 addition & 1 deletion app/models/chargeback/consumption_with_rollups.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def max(metric)

def avg(metric)
metric_sum = values(metric).sum
metric_sum / hours_in_interval
metric_sum / past_hours_in_interval
end

def none?(metric)
Expand Down
4 changes: 2 additions & 2 deletions app/models/chargeback/report_options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ class Chargeback
# ReportOptions are usualy stored in MiqReport.db_options[:options]
ReportOptions = Struct.new(
:interval, # daily | weekly | monthly
:interval_size,
:end_interval_offset,
:interval_size, # number of :intervals in the report (i.e. `12` months, `4` weeks)
:end_interval_offset, # report ends :intervals ago (i.e. `3` months ago, or `2` weeks ago)
:owner, # userid
:tenant_id,
:tag, # like /managed/environment/prod (Mutually exclusive with :user)
Expand Down
2 changes: 1 addition & 1 deletion app/models/chargeback_rate_detail.rb
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ def cost_keys

def metric_and_cost_by(consumption)
metric_value = metric_value_by(consumption)
[metric_value, hourly_cost(metric_value, consumption) * consumption.hours_in_interval]
[metric_value, hourly_cost(metric_value, consumption) * consumption.past_hours_in_interval]
end

def first_tier?(tier,tiers)
Expand Down
12 changes: 7 additions & 5 deletions spec/models/chargeback_container_image_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
describe ChargebackContainerImage do
let(:base_options) { {:interval_size => 1, :end_interval_offset => 0, :ext_options => {:tz => 'UTC'} } }
let(:base_options) { {:interval_size => 2, :end_interval_offset => 0, :ext_options => {:tz => 'UTC'} } }
let(:hourly_rate) { 0.01 }
let(:ts) { Time.now.in_time_zone(Metric::Helper.get_time_zone(options[:ext_options])) }
let(:starting_date) { Time.parse('2012-09-01 23:59:59Z').utc }
let(:ts) { starting_date.in_time_zone(Metric::Helper.get_time_zone(options[:ext_options])) }
let(:report_run_time) { month_end }
let(:month_beginning) { ts.beginning_of_month.utc }
let(:month_end) { ts.end_of_month.utc }
let(:hours_in_month) { Time.days_in_month(month_beginning.month, month_beginning.year) * 24 }
Expand Down Expand Up @@ -38,7 +40,7 @@
@project.tag_with(@tag.name, :ns => '*')
@image.tag_with(@tag.name, :ns => '*')

Timecop.travel(Time.parse('2012-09-01 23:59:59Z').utc)
Timecop.travel(report_run_time)
end

after do
Expand All @@ -48,8 +50,8 @@
context "Daily" do
let(:hours_in_day) { 24 }
let(:options) { base_options.merge(:interval => 'daily', :entity_id => @project.id, :tag => nil) }
let(:start_time) { Time.parse('2012-09-01 07:00:00Z').utc }
let(:finish_time) { Time.parse('2012-09-01 10:00:00Z').utc }
let(:start_time) { report_run_time - 17.hours }
let(:finish_time) { report_run_time - 14.hours }

before do
Range.new(start_time, finish_time, true).step_value(1.hour).each do |t|
Expand Down
12 changes: 7 additions & 5 deletions spec/models/chargeback_container_project_spec.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
describe ChargebackContainerProject do
include Spec::Support::ChargebackHelper

let(:base_options) { {:interval_size => 1, :end_interval_offset => 0, :ext_options => {:tz => 'UTC'} } }
let(:base_options) { {:interval_size => 2, :end_interval_offset => 0, :ext_options => {:tz => 'UTC'} } }
let(:hourly_rate) { 0.01 }
let(:ts) { Time.now.in_time_zone(Metric::Helper.get_time_zone(options[:ext_options])) }
let(:starting_date) { Time.parse('2012-09-01 23:59:59Z').utc }
let(:ts) { starting_date.in_time_zone(Metric::Helper.get_time_zone(options[:ext_options])) }
let(:report_run_time) { month_end }
let(:month_beginning) { ts.beginning_of_month.utc }
let(:month_end) { ts.end_of_month.utc }
let(:hours_in_month) { Time.days_in_month(month_beginning.month, month_beginning.year) * 24 }
Expand Down Expand Up @@ -40,7 +42,7 @@
@tag = c.tag
@project.tag_with(@tag.name, :ns => '*')

Timecop.travel(Time.parse('2012-09-01 23:59:59Z').utc)
Timecop.travel(report_run_time)
end

after do
Expand All @@ -50,8 +52,8 @@
context "Daily" do
let(:hours_in_day) { 24 }
let(:options) { base_options.merge(:interval => 'daily', :entity_id => @project.id, :tag => nil) }
let(:start_time) { Time.parse('2012-09-01 07:00:00Z').utc }
let(:finish_time) { Time.parse('2012-09-01 10:00:00Z').utc }
let(:start_time) { report_run_time - 17.hours }
let(:finish_time) { report_run_time - 14.hours }

before do
Range.new(start_time, finish_time, true).step_value(1.hour).each do |t|
Expand Down
96 changes: 96 additions & 0 deletions spec/models/chargeback_vm/ongoing_time_period_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
describe ChargebackVm do
let(:admin) { FactoryGirl.create(:user_admin) }
let(:start_of_all_intervals) { Time.parse('2007-01-01 00:00:00Z').utc } # 0hours, Monday, 1st of month
let(:consumed_hours) { 17 }
let(:midle_of_the_first_day) { start_of_all_intervals + consumed_hours.hours } # it is a Monday
let(:ts) { midle_of_the_first_day.in_time_zone(Metric::Helper.get_time_zone(opt[:ext_options])) }
let(:report_run_time) { midle_of_the_first_day }

let(:opt) do
{:interval_size => 1,
:end_interval_offset => 0,
:tag => '/managed/environment/prod',
:ext_options => {:tz => 'UTC'},
:userid => admin.userid}
end
let(:tag) { Tag.find_by_name('/managed/environment/prod') }
let(:vm) do
ems = FactoryGirl.create(:ems_vmware)
vm = FactoryGirl.create(:vm_vmware, :name => 'test_vm', :evm_owner => admin, :ems_ref => 'ems_ref')
vm.tag_with(tag.name, :ns => '*')
host = FactoryGirl.create(:host, :hardware => FactoryGirl.create(:hardware,
:memory_mb => 8124, :cpu_total_cores => 1,
:cpu_speed => 9576), :vms => [vm])
ems_cluster = FactoryGirl.create(:ems_cluster, :ext_management_system => ems)
ems_cluster.hosts << host
storage = FactoryGirl.create(:storage_target_vmware)

Range.new(start_of_all_intervals, midle_of_the_first_day, true).step_value(1.hour).each do |time|
vm.metric_rollups << FactoryGirl.create(:metric_rollup_vm_hr,
:derived_vm_numvcpus => number_of_cpus,
:cpu_usagemhz_rate_average => cpu_usagemhz,
:timestamp => time,
:tag_names => 'environment/prod',
:parent_host_id => host.id,
:parent_ems_cluster_id => ems_cluster.id,
:parent_ems_id => ems.id,
:parent_storage_id => storage.id,
:resource_name => vm.name,
)
end
vm
end
let(:hourly_rate) { 0.01 }
let(:count_hourly_rate) { 1.2 }
let(:hourly_variable_tier_rate) { {:variable_rate => hourly_rate.to_s} }
let(:count_hourly_variable_tier_rate) { {:variable_rate => count_hourly_rate.to_s} }
let(:detail_params) do
{
:chargeback_rate_detail_cpu_used => {:tiers => [hourly_variable_tier_rate]},
:chargeback_rate_detail_cpu_allocated => {:tiers => [count_hourly_variable_tier_rate]},
}
end

let!(:chargeback_rate) do
cat = FactoryGirl.create(:classification, :description => 'Environment', :name => 'environment',
:single_value => true, :show => true)
c = FactoryGirl.create(:classification, :name => 'prod', :description => 'Production', :parent_id => cat.id)
chargeback_rate = FactoryGirl.create(:chargeback_rate, :detail_params => detail_params)
temp = { :cb_rate => chargeback_rate, :tag => [c, 'vm'] }
ChargebackRate.set_assignments(:compute, [temp])
end

before do
MiqRegion.seed
ChargebackRate.seed
EvmSpecHelper.create_guid_miq_server_zone
Timecop.travel(report_run_time)
vm
end

after do
Timecop.return
end

let(:daily_cb) { ChargebackVm.build_results_for_report_ChargebackVm(opt.merge(:interval => 'daily')).first.first }
let(:weekly_cb) { ChargebackVm.build_results_for_report_ChargebackVm(opt.merge(:interval => 'weekly')).first.first }
let(:monthly_cb) { ChargebackVm.build_results_for_report_ChargebackVm(opt.merge(:interval => 'monthly')).first.first }

let(:number_of_cpus) { 1 }
let(:cpu_allocated_cost) { number_of_cpus * consumed_hours * count_hourly_rate }
let(:cpu_usagemhz) { 50 }
let(:cpu_usage_cost) { cpu_usagemhz * consumed_hours * hourly_rate }

it 'should calculate the same -- no matter the time range (daily/weekly/monthly)' do
[daily_cb, weekly_cb, monthly_cb].each do |cb|
expect(cb.start_date).to eq(report_run_time.beginning_of_month)
expect(cb.fixed_compute_metric).to eq(consumed_hours)
expect(cb.cpu_allocated_metric).to eq(number_of_cpus)
expect(cb.cpu_allocated_cost).to eq(cpu_allocated_cost)
expect(cb.cpu_used_metric).to eq(cpu_usagemhz)
expect(cb.cpu_used_cost).to eq(cpu_usage_cost)
expect(cb.total_cost).to eq(cpu_allocated_cost + cpu_usage_cost)
expect(cb.total_cost).to eq(28.9) # hardcoded value here keeps us honest
end
end
end
22 changes: 12 additions & 10 deletions spec/models/chargeback_vm_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

let(:admin) { FactoryGirl.create(:user_admin) }
let(:base_options) do
{:interval_size => 1,
{:interval_size => 2,
:end_interval_offset => 0,
:tag => '/managed/environment/prod',
:ext_options => {:tz => 'UTC'},
Expand All @@ -14,8 +14,9 @@
let(:cpu_count) { 1.0 }
let(:memory_available) { 1000.0 }
let(:vm_allocated_disk_storage) { 4.0 }

let(:ts) { Time.now.in_time_zone(Metric::Helper.get_time_zone(options[:ext_options])) }
let(:starting_date) { Time.parse('2012-09-01 23:59:59Z').utc }
let(:ts) { starting_date.in_time_zone(Metric::Helper.get_time_zone(base_options[:ext_options])) }
let(:report_run_time) { month_end }
let(:month_beginning) { ts.beginning_of_month.utc }
let(:month_end) { ts.end_of_month.utc }
let(:hours_in_month) { Time.days_in_month(month_beginning.month, month_beginning.year) * 24 }
Expand Down Expand Up @@ -63,7 +64,7 @@
temp = {:cb_rate => chargeback_rate, :tag => [c, "vm"]}
ChargebackRate.set_assignments(:compute, [temp])

Timecop.travel(Time.parse('2012-09-01 23:59:59Z').utc)
Timecop.travel(report_run_time)
end

after do
Expand Down Expand Up @@ -116,8 +117,8 @@
let(:hours_in_day) { 24 }
let(:options) { base_options.merge(:interval => 'daily') }

let(:start_time) { Time.parse('2012-09-01 07:00:00Z').utc }
let(:finish_time) { Time.parse('2012-09-01 10:00:00Z').utc }
let(:start_time) { report_run_time - 17.hours }
let(:finish_time) { report_run_time - 14.hours }

before do
Range.new(start_time, finish_time, true).step_value(1.hour).each do |t|
Expand Down Expand Up @@ -223,8 +224,8 @@

context "Report a chargeback of a tenant" do
let(:options_tenant) { base_options.merge(:tenant_id => @tenant.id) }
let(:start_time) { Time.parse('2012-09-01 07:00:00Z').utc }
let(:finish_time) { Time.parse('2012-09-01 10:00:00Z').utc }
let(:start_time) { report_run_time - 17.hours }
let(:finish_time) { report_run_time - 14.hours }

before do
@tenant = FactoryGirl.create(:tenant)
Expand Down Expand Up @@ -387,7 +388,8 @@
let(:chargeback_vm) { ChargebackVm.new }
let(:rate_assignment_options) { {:cb_rate => chargeback_rate, :object => Tenant.root_tenant} }
let(:metric_rollup) do
FactoryGirl.create(:metric_rollup_vm_hr, :timestamp => "2012-08-31T07:00:00Z", :tag_names => "environment/prod",
FactoryGirl.create(:metric_rollup_vm_hr, :timestamp => report_run_time - 1.day - 17.hours,
:tag_names => "environment/prod",
:parent_host_id => @host1.id, :parent_ems_cluster_id => @ems_cluster.id,
:parent_ems_id => ems.id, :parent_storage_id => @storage.id,
:resource => @vm1)
Expand Down Expand Up @@ -474,7 +476,7 @@
let(:rate_assignment_options_2) { {:cb_rate => storage_chargeback_rate_2, :tag => [classification_2, "Storage"]} }

let(:metric_rollup) do
FactoryGirl.create(:metric_rollup_vm_hr, :timestamp => "2012-08-31T07:00:00Z",
FactoryGirl.create(:metric_rollup_vm_hr, :timestamp => report_run_time - 1.day - 17.hours,
:parent_host_id => @host1.id, :parent_ems_cluster_id => @ems_cluster.id,
:parent_ems_id => ems.id, :parent_storage_id => @storage.id,
:resource => @vm1)
Expand Down