diff --git a/app/models/chargeback/consumption.rb b/app/models/chargeback/consumption.rb index f724c34e270..ad4ac5d6d8b 100644 --- a/app/models/chargeback/consumption.rb +++ b/app/models/chargeback/consumption.rb @@ -1,48 +1,11 @@ class Chargeback class Consumption - delegate :timestamp, :resource, :resource_id, :resource_name, :resource_type, :parent_ems, - :hash_features_affecting_rate, :tag_list_with_prefix, :parents_determining_rate, - :to => :first_metric_rollup_record - - def initialize(metric_rollup_records, start_time, end_time) - @rollups = metric_rollup_records + def initialize(start_time, end_time) @start_time, @end_time = start_time, end_time end - def tag_names - first_metric_rollup_record.tag_names.split('|') - end - - def max(metric) - values(metric).max - end - - def avg(metric) - metric_sum = values(metric).sum - metric_sum / hours_in_interval - end - - def none?(metric) - values(metric).empty? - end - - def chargeback_fields_present - @chargeback_fields_present ||= @rollups.count(&:chargeback_fields_present?) - end - def hours_in_interval @hours_in_interval ||= (@end_time - @start_time).round / 1.hour end - - private - - def values(metric) - @values ||= {} - @values[metric] ||= @rollups.collect(&metric.to_sym).compact - end - - def first_metric_rollup_record - @fmrr ||= @rollups.first - end end end diff --git a/app/models/chargeback/consumption_history.rb b/app/models/chargeback/consumption_history.rb index 828afbf3148..c3a19326428 100644 --- a/app/models/chargeback/consumption_history.rb +++ b/app/models/chargeback/consumption_history.rb @@ -9,7 +9,12 @@ def self.for_report(cb_class, options) timerange = options.report_time_range interval_duration = options.duration_of_report_step + extra_resources = cb_class.try(:extra_resources_without_rollups) || [] timerange.step_value(interval_duration).each_cons(2) do |query_start_time, query_end_time| + extra_resources.each do |resource| + yield ConsumptionWithoutRollups.new(resource, query_start_time, query_end_time) + end + records = base_rollup.where(:timestamp => query_start_time...query_end_time, :capture_interval_name => 'hourly') records = cb_class.where_clause(records, options) records = Metric::Helper.remove_duplicate_timestamps(records) @@ -20,7 +25,7 @@ def self.for_report(cb_class, options) # values are grouped by resource_id and timestamp (query_start_time...query_end_time) records.group_by(&:resource_id).each do |_, metric_rollup_records| metric_rollup_records = metric_rollup_records.select { |x| x.resource.present? } - consumption = Consumption.new(metric_rollup_records, query_start_time, query_end_time) + consumption = ConsumptionWithRollups.new(metric_rollup_records, query_start_time, query_end_time) next if metric_rollup_records.empty? yield(consumption) end diff --git a/app/models/chargeback/consumption_with_rollups.rb b/app/models/chargeback/consumption_with_rollups.rb new file mode 100644 index 00000000000..85db3ed71e3 --- /dev/null +++ b/app/models/chargeback/consumption_with_rollups.rb @@ -0,0 +1,44 @@ +class Chargeback + class ConsumptionWithRollups < Consumption + delegate :timestamp, :resource, :resource_id, :resource_name, :resource_type, :parent_ems, + :hash_features_affecting_rate, :tag_list_with_prefix, :parents_determining_rate, + :to => :first_metric_rollup_record + + def initialize(metric_rollup_records, start_time, end_time) + super(start_time, end_time) + @rollups = metric_rollup_records + end + + def tag_names + first_metric_rollup_record.tag_names.split('|') + end + + def max(metric) + values(metric).max + end + + def avg(metric) + metric_sum = values(metric).sum + metric_sum / hours_in_interval + end + + def none?(metric) + values(metric).empty? + end + + def chargeback_fields_present + @chargeback_fields_present ||= @rollups.count(&:chargeback_fields_present?) + end + + private + + def values(metric) + @values ||= {} + @values[metric] ||= @rollups.collect(&metric.to_sym).compact + end + + def first_metric_rollup_record + @fmrr ||= @rollups.first + end + end +end diff --git a/app/models/chargeback/consumption_without_rollups.rb b/app/models/chargeback/consumption_without_rollups.rb new file mode 100644 index 00000000000..72952abaff8 --- /dev/null +++ b/app/models/chargeback/consumption_without_rollups.rb @@ -0,0 +1,52 @@ +class Chargeback + class ConsumptionWithoutRollups < Consumption + delegate :id, :name, :type, :to => :resource, :prefix => :resource + attr_reader :resource + + def initialize(resource, start_time, end_time) + super(start_time, end_time) + @resource = resource + end + + def timestamp + @start_time + end + + def parent_ems + resource.ext_management_system + end + + def tag_names + resource.tags.collect(&:name) + end + + def hash_features_affecting_rate + resource.id + end + + def tag_list_with_prefix + tag_names.map { |t| "vm/tag#{t}" } + end + + def parents_determining_rate + [resource.host, resource.ems_cluster, resource.storage, parent_ems, resource.tenant, + MiqEnterprise.my_enterprise].compact + end + + def none?(_metric) + true # No, values except for fixed (RateDetail.fixed?) + end + + def max(_metric) + raise NotImplementedError # Unreachable code since none?==true + end + + def avg(_metric) + raise NotImplementedError # Unreachable code since none?==true + end + + def chargeback_fields_present + 1 # Yes, charge this interval as fixed_compute_*_* + end + end +end diff --git a/app/models/chargeback_vm.rb b/app/models/chargeback_vm.rb index ed51824c346..e26c6791aac 100644 --- a/app/models/chargeback_vm.rb +++ b/app/models/chargeback_vm.rb @@ -54,6 +54,16 @@ def self.where_clause(records, options) end end + def self.extra_resources_without_rollups + # support hyper-v for which we do not collect metrics yet + scope = ManageIQ::Providers::Microsoft::InfraManager::Vm + if @options[:tag] && (@report_user.nil? || !@report_user.self_service?) + scope.find_tagged_with(:any => @options[:tag], :ns => '*') + else + scope.where(:id => vms) + end + end + def self.report_static_cols %w(vm_name) end diff --git a/spec/models/chargeback_vm_spec.rb b/spec/models/chargeback_vm_spec.rb index 1bd4ed91f51..a4258f48046 100644 --- a/spec/models/chargeback_vm_spec.rb +++ b/spec/models/chargeback_vm_spec.rb @@ -728,7 +728,7 @@ def used_average_for(metric, hours_in_interval) :parent_ems_id => ems.id, :parent_storage_id => @storage.id, :resource => @vm1) end - let(:consumption) { Chargeback::Consumption.new([metric_rollup], nil, nil) } + let(:consumption) { Chargeback::ConsumptionWithRollups.new([metric_rollup], nil, nil) } before do ChargebackRate.set_assignments(:compute, [rate_assignment_options]) @@ -747,7 +747,7 @@ def used_average_for(metric, hours_in_interval) let(:timestamp_key) { 'Fri, 13 May 2016 10:40:00 UTC +00:00' } let(:beginning_of_day) { timestamp_key.in_time_zone.beginning_of_day } let(:metric_rollup) { FactoryGirl.build(:metric_rollup_vm_hr, :timestamp => timestamp_key, :resource => @vm1) } - let(:consumption) { Chargeback::Consumption.new([metric_rollup], nil, nil) } + let(:consumption) { Chargeback::ConsumptionWithRollups.new([metric_rollup], nil, nil) } subject { described_class.report_row_key(consumption) } before do described_class.instance_variable_set(:@options, report_options) @@ -759,7 +759,7 @@ def used_average_for(metric, hours_in_interval) describe '#initialize' do let(:report_options) { Chargeback::ReportOptions.new } let(:vm_owners) { {@vm1.id => @vm1.evm_owner_name} } - let(:consumption) { Chargeback::Consumption.new([metric_rollup], nil, nil) } + let(:consumption) { Chargeback::ConsumptionWithRollups.new([metric_rollup], nil, nil) } let(:shared_extra_fields) do {'vm_name' => @vm1.name, 'owner_name' => admin.name, 'vm_uid' => 'ems_ref', 'vm_guid' => @vm1.guid, 'vm_id' => @vm1.id} @@ -815,7 +815,7 @@ def used_average_for(metric, hours_in_interval) :parent_ems_id => ems.id, :parent_storage_id => @storage.id, :resource => @vm1) end - let(:consumption) { Chargeback::Consumption.new([metric_rollup], nil, nil) } + let(:consumption) { Chargeback::ConsumptionWithRollups.new([metric_rollup], nil, nil) } before do @storage.tag_with([classification_1.tag.name, classification_2.tag.name], :ns => '*') @@ -883,4 +883,34 @@ def used_average_for(metric, hours_in_interval) expect(subject.tag_name).to eq('Production') end end + + context 'for SCVMM (hyper-v)' do + let!(:vm1) do + vm = FactoryGirl.create(:vm_microsoft) + vm.tag_with(@tag.name, :ns => '*') + vm + end + let(:options) { base_options.merge(:interval => 'daily') } + let(:tier) do + FactoryGirl.create(:chargeback_tier, :start => 0, + :finish => Float::INFINITY, + :fixed_rate => hourly_rate.to_s, + :variable_rate => 0.0) + end + let!(:rate_detail) do + FactoryGirl.create(:chargeback_rate_detail_fixed_compute_cost, + :chargeback_rate_id => @cbr.id, + :chargeback_tiers => [tier], + :per_time => 'hourly') + end + + subject { ChargebackVm.build_results_for_report_ChargebackVm(options).first.first } + + it 'works' do + expect(subject.chargeback_rates).to eq(@cbr.description) + expect(subject.fixed_compute_metric).to eq(1) # One day of fixed compute metric + expect(subject.fixed_compute_1_cost).to eq(hourly_rate * 24) + expect(subject.total_cost).to eq(hourly_rate * 24) + end + end end