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

Add metering reports #16342

Merged
merged 14 commits into from
Oct 30, 2017
4 changes: 4 additions & 0 deletions app/models/chargeable_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ def showback_dimension
"fixed_storage_2" => ['fixed_storage_2', '', 'occurrence']}[metric_index]
end

def measure_metering(consumption, options, sub_metric = nil)
used? ? consumption.sum(metric) : measure(consumption, options, sub_metric)
end

def measure(consumption, options, sub_metric = nil)
return consumption.consumed_hours_in_interval if metering?
return 1.0 if fixed?
Expand Down
4 changes: 1 addition & 3 deletions app/models/chargeback.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ class Chargeback < ActsAsArModel
:tag_name => :string,
:label_name => :string,
:fixed_compute_metric => :integer,
:metering_used_metric => :integer,
:metering_used_cost => :float
)

def self.build_results_for_report_chargeback(options)
Expand Down Expand Up @@ -145,7 +143,7 @@ def calculate_costs(consumption, rates)
end

def self.report_cb_model(model)
model.gsub(/^Chargeback/, "")
model.gsub(/^(Chargeback|Metering)/, "")
end

def self.db_is_chargeback?(db)
Expand Down
7 changes: 6 additions & 1 deletion app/models/chargeback/consumption_with_rollups.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ def tag_list_with_prefix
@tag_list_with_prefix ||= @rollups.map(&:tag_list_with_prefix).flatten.uniq
end

def sum(metric, sub_metric = nil)
metric = ChargeableField::VIRTUAL_COL_USES[metric] || metric
values(metric, sub_metric).sum
end

def max(metric, sub_metric = nil)
values(metric, sub_metric).max
end
Expand All @@ -42,7 +47,7 @@ def avg(metric, sub_metric = nil)

def current_value(metric, _sub_metric) # used for containers allocated metrics
case metric
when 'derived_vm_numvcpu_cores' # Allocated CPU count
when 'derived_vm_numvcpu_cores', 'derived_vm_numvcpus_cores' # Allocated CPU count
resource.try(:limit_cpu_cores).to_f
when 'derived_memory_available'
resource.try(:limit_memory_bytes).to_f / 1.megabytes # bytes to megabytes
Expand Down
37 changes: 37 additions & 0 deletions app/models/metering.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module Metering
def calculate_costs(consumption, _)
self.fixed_compute_metric = consumption.chargeback_fields_present if consumption.chargeback_fields_present
self.metering_used_metric = fixed_compute_metric
self.existence_hours_metric = consumption.consumed_hours_in_interval

relevant_fields.each do |field|
next unless self.class.report_col_options.include?(field)
group, source, * = field.split('_')

if field == 'net_io_used_metric'
group = 'net_io'
source = 'used'
end

if field == 'disk_io_used_metric'
group = 'disk_io'
source = 'used'
end

if field == 'cpu_cores_used_metric'
group = 'cpu_cores'
source = 'used'
end

if field == 'cpu_cores_allocated_metric'
group = 'cpu_cores'
source = 'allocated'
end

chargable_field = ChargeableField.find_by(:group => group, :source => source)
next if field == "existence_hours_metric" || field == "fixed_compute_metric" || chargable_field && chargable_field.metering?
value = chargable_field.measure_metering(consumption, @options) if chargable_field
self[field] = (value || 0)
end
end
end
24 changes: 24 additions & 0 deletions app/models/metering_container_image.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class MeteringContainerImage < ChargebackContainerImage
set_columns_hash(
:metering_used_metric => :integer,
:existence_hours_metric => :integer
)

include Metering

def self.report_col_options
{
"cpu_cores_allocated_metric" => {:grouping => [:total]},
"cpu_cores_used_metric" => {:grouping => [:total]},
"fixed_compute_metric" => {:grouping => [:total]},
"memory_allocated_metric" => {:grouping => [:total]},
"memory_used_metric" => {:grouping => [:total]},
"metering_used_metric" => {:grouping => [:total]},
"net_io_used_metric" => {:grouping => [:total]},
}
end

def self.build_results_for_report_MeteringContainerImage(options)
build_results_for_report_ChargebackContainerImage(options)
end
end
22 changes: 22 additions & 0 deletions app/models/metering_container_project.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class MeteringContainerProject < ChargebackContainerProject
set_columns_hash(
:metering_used_metric => :integer,
:existence_hours_metric => :integer
)

include Metering

def self.report_col_options
{
"cpu_cores_used_metric" => {:grouping => [:total]},
"fixed_compute_metric" => {:grouping => [:total]},
"memory_used_metric" => {:grouping => [:total]},
"metering_used_metric" => {:grouping => [:total]},
"net_io_used_metric" => {:grouping => [:total]},
}
end

def self.build_results_for_report_MeteringContainerProject(options)
build_results_for_report_ChargebackContainerProject(options)
end
end
27 changes: 27 additions & 0 deletions app/models/metering_vm.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class MeteringVm < ChargebackVm
set_columns_hash(
:metering_used_metric => :integer,
:existence_hours_metric => :integer
)

include Metering

def self.report_col_options
{
"cpu_allocated_metric" => {:grouping => [:total]},
"cpu_used_metric" => {:grouping => [:total]},
"disk_io_used_metric" => {:grouping => [:total]},
"fixed_compute_metric" => {:grouping => [:total]},
"memory_allocated_metric" => {:grouping => [:total]},
"memory_used_metric" => {:grouping => [:total]},
"metering_used_metric" => {:grouping => [:total]},
"net_io_used_metric" => {:grouping => [:total]},
"storage_allocated_metric" => {:grouping => [:total]},
"storage_used_metric" => {:grouping => [:total]},
}
end

def self.build_results_for_report_MeteringVm(options)
build_results_for_report_ChargebackVm(options)
end
end
3 changes: 3 additions & 0 deletions config/miq_expression.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@
- Switch
- ManageIQ::Providers::CloudManager::Template
- ManageIQ::Providers::InfraManager::Template
- MeteringContainerProject
- MeteringContainerImage
- MeteringVm
- Tenant
- User
- VimPerformanceTrend
Expand Down
10 changes: 0 additions & 10 deletions db/fixtures/chargeback_rates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,16 +106,6 @@
:finish: Infinity
:fixed_rate: 0.0
:variable_rate: 0.0
- :description: Metering - Hours Used
:metric: metering_used_hours
:per_time: hourly
:per_unit: hours
:type_currency: Dollars
:tiers:
- :start: 0
:finish: Infinity
:fixed_rate: 0.0
:variable_rate: 1
- :description: Default
:guid: 7d7aaf20-5214-11df-a888-001d09066d98
:rate_type: Storage
Expand Down
4 changes: 3 additions & 1 deletion lib/miq_expression.rb
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,9 @@ def self.reporting_available_fields(model, interval = nil)
cb_model = Chargeback.report_cb_model(model)
model.constantize.try(:refresh_dynamic_metric_columns)
md = model_details(model, :include_model => false, :include_tags => true).select do |c|
c.last.ends_with?(*ReportController::Reports::Editor::CHARGEBACK_ALLOWED_FIELD_SUFFIXES)
allowed_suffixes = ReportController::Reports::Editor::CHARGEBACK_ALLOWED_FIELD_SUFFIXES
allowed_suffixes -= ['_cost'] if model.starts_with?('Metering')
c.last.ends_with?(*allowed_suffixes)
end
td = if TAG_CLASSES.include?(cb_model)
tag_details(model, {}) + _custom_details_for(cb_model, {})
Expand Down
3 changes: 3 additions & 0 deletions spec/models/chargeback_container_image_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
let(:metric_rollup_params) { {:parent_ems_id => ems.id, :tag_names => ""} }

before do
# TODO: remove metering columns form specs
described_class.set_columns_hash(:metering_used_metric => :integer, :metering_used_cost => :float)

MiqRegion.seed
ChargebackRateDetailMeasure.seed
ChargeableField.seed
Expand Down
3 changes: 3 additions & 0 deletions spec/models/chargeback_container_project_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
let(:metric_rollup_params) { {:parent_ems_id => ems.id, :tag_names => ""} }

before do
# TODO: remove metering columns form specs
described_class.set_columns_hash(:metering_used_metric => :integer, :metering_used_cost => :float)

MiqRegion.seed
ChargebackRateDetailMeasure.seed
ChargeableField.seed
Expand Down
1 change: 0 additions & 1 deletion spec/models/chargeback_rate_detail_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
fixed_compute_2
derived_memory_available
derived_memory_used
metering_used_hours
net_usage_rate_average
)

Expand Down
3 changes: 3 additions & 0 deletions spec/models/chargeback_vm_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@
end

before do
# TODO: remove metering columns form specs
described_class.set_columns_hash(:metering_used_metric => :integer, :metering_used_cost => :float)

MiqRegion.seed
ChargebackRateDetailMeasure.seed
ChargeableField.seed
Expand Down
62 changes: 62 additions & 0 deletions spec/models/metering_container_image_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
describe MeteringContainerImage do
include Spec::Support::ChargebackHelper
let(:base_options) { {:interval_size => 2, :end_interval_offset => 0, :ext_options => {:tz => 'UTC'} } }
let(:hourly_rate) { 0.01 }
let(:count_hourly_rate) { 1.00 }
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 }
let(:ems) { FactoryGirl.create(:ems_openshift) }
let(:metric_rollup_params) { {:parent_ems_id => ems.id, :tag_names => ""} }

before do
# TODO: remove metering columns form specs
described_class.set_columns_hash(:metering_used_metric => :integer, :metering_used_cost => :float)

MiqRegion.seed
ChargebackRateDetailMeasure.seed
ChargeableField.seed
ChargebackRate.seed

MiqEnterprise.seed

EvmSpecHelper.create_guid_miq_server_zone
@node = FactoryGirl.create(:container_node, :name => "node")
@image = FactoryGirl.create(:container_image, :ext_management_system => ems)
@label = FactoryGirl.build(:custom_attribute, :name => "version/1.2/_label-1", :value => "test/1.0.0 rc_2", :section => 'docker_labels')
@project = FactoryGirl.create(:container_project, :name => "my project", :ext_management_system => ems)
@group = FactoryGirl.create(:container_group, :ext_management_system => ems, :container_project => @project,
:container_node => @node)
@container = FactoryGirl.create(:kubernetes_container, :container_group => @group, :container_image => @image,
:limit_memory_bytes => 1.megabytes, :limit_cpu_cores => 1.0)

Timecop.travel(report_run_time)
end

after do
Timecop.return
end

context "Monthly" do
let(:options) { base_options.merge(:interval => 'monthly', :entity_id => @project.id, :tag => nil) }

before do
add_metric_rollups_for(@container, month_beginning...month_end, 12.hours, metric_rollup_params)

Range.new(month_beginning, month_end, true).step_value(12.hours).each do |time|
@container.vim_performance_states << FactoryGirl.create(:vim_performance_state, :timestamp => time, :image_tag_names => "environment/prod")
end
end

subject { MeteringContainerImage.build_results_for_report_MeteringContainerImage(options).first.first }

it "allocated fields" do
expect(subject.memory_allocated_metric).to eq(@container.limit_memory_bytes / 1.megabytes)
expect(subject.cpu_cores_allocated_metric).to eq(@container.limit_cpu_cores)
expect(subject.cpu_cores_allocated_metric).to eq(@container.limit_memory_bytes / 1.megabytes)
end
end
end
67 changes: 67 additions & 0 deletions spec/models/metering_container_project_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
describe MeteringContainerProject do
include Spec::Support::ChargebackHelper

let(:admin) { FactoryGirl.create(:user_admin) }
let(:base_options) do
{:interval_size => 2,
:end_interval_offset => 0,
:ext_options => {:tz => 'UTC'},
:userid => admin.userid}
end

let(:cpu_usage_rate_average) { 50.0 }
let(:derived_memory_used) { 100.0 }
let(:net_usage_rate_average) { 25.0 }

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(:count_of_metric_rollup) { MetricRollup.where(:timestamp => month_beginning...month_end).count }
let(:ems) { FactoryGirl.create(:ems_vmware) }
let(:project) { FactoryGirl.create(:container_project, :name => "my project", :ext_management_system => ems, :created_on => month_beginning) }
let(:hardware) { FactoryGirl.create(:hardware, :memory_mb => 8124, :cpu_total_cores => 1, :cpu_speed => 9576) }
let(:host) { FactoryGirl.create(:host, :storages => [storage], :hardware => hardware, :vms => [vm]) }
let(:storage) { FactoryGirl.create(:storage_target_vmware) }
let(:ems_cluster) { FactoryGirl.create(:ems_cluster, :ext_management_system => ems, :hosts => [host]) }

before do
MiqRegion.seed
ChargebackRateDetailMeasure.seed
ChargeableField.seed
MiqEnterprise.seed
EvmSpecHelper.create_guid_miq_server_zone
Timecop.travel(report_run_time)
end

after do
Timecop.return
end

let(:metric_rollup_params) do
{
:parent_ems_id => ems.id,
:tag_names => "",
}
end

context 'monthly' do
subject { MeteringContainerProject.build_results_for_report_MeteringContainerProject(options).first.first }

let(:options) { base_options.merge(:interval => 'monthly', :interval_size => 4, :entity_id => project.id) }

before do
add_metric_rollups_for(project, month_beginning...month_end, 12.hours, metric_rollup_params)
end

it 'calculates metering values' do
expect(subject.cpu_cores_used_metric).to eq(cpu_usage_rate_average * count_of_metric_rollup)
expect(subject.fixed_compute_metric).to eq(count_of_metric_rollup)
expect(subject.memory_used_metric).to eq(derived_memory_used * count_of_metric_rollup)
expect(subject.metering_used_metric).to eq(count_of_metric_rollup)
expect(subject.existence_hours_metric).to eq(month_beginning.end_of_month.day * 24)
expect(subject.net_io_used_metric).to eq(net_usage_rate_average * count_of_metric_rollup)
end
end
end
Loading