app/models/chargeable_field.rb
class ChargeableField < ApplicationRecord
VIRTUAL_COL_USES = {
'v_derived_cpu_total_cores_used' => 'cpu_usage_rate_average',
'derived_vm_numvcpu_cores' => 'derived_vm_numvcpu_cores'
}.freeze
# The following chargeable fields are stored in following units
UNITS = {
'cpu_usagemhz_rate_average' => 'megahertz',
'derived_memory_used' => 'megabytes',
'derived_memory_available' => 'megabytes',
'net_usage_rate_average' => 'kbps',
'disk_usage_rate_average' => 'kbps',
'derived_vm_allocated_disk_storage' => 'bytes',
'derived_vm_used_disk_storage' => 'bytes'
}.freeze
belongs_to :detail_measure, :class_name => 'ChargebackRateDetailMeasure', :foreign_key => :chargeback_rate_detail_measure_id
validates :metric, :uniqueness_when_changed => true, :presence => true
validates :group, :source, :presence => true
def showback_measure
group
end
def showback_dimension
metric_index = VIRTUAL_COL_USES.invert[metric] || metric
{'cpu_usagemhz_rate_average' => ['cpu_usagemhz_rate_average', '', 'duration'],
"v_derived_cpu_total_cores_used" => ['v_derived_cpu_total_cores_used', 'THz', 'duration'],
"derived_vm_numvcpus" => ['derived_vm_numvcpus', '', 'duration'],
"derived_memory_used" => ['derived_memory_used', 'Gi', 'duration'],
"derived_memory_available" => ['derived_memory_available', 'B', 'duration'],
"metering_used_hours" => ['metering_used_hours', '', 'quantity'],
"net_usage_rate_average" => ['net_usage_rate_average', '', 'duration'],
"disk_usage_rate_average" => ['disk_usage_rate_average', '', 'duration'],
"fixed_compute_1" => ['fixed_compute_1', '', 'occurrence'],
"fixed_compute_2" => ['fixed_compute_2', '', 'occurrence'],
"derived_vm_allocated_disk_storage" => ['derived_vm_allocated_disk_storage', 'Gi', 'duration'],
"derived_vm_used_disk_storage" => ['derived_vm_used_disk_storage', 'Gi', 'duration'],
"fixed_storage_1" => ['fixed_storage_1', '', 'occurrence'],
"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?
return 0 if options.method_for_allocated_metrics != :current_value && consumption.none?(metric, sub_metric)
return consumption.send(options.method_for_allocated_metrics, metric, sub_metric) if allocated?
consumption.avg(metric) if used?
end
def fixed?
group == 'fixed'
end
def adjustment_to(target_unit)
# return multiplicator, that would bring UNITS[metric] 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
# Fixed metric has _1 or _2 in name but column
# fixed_compute_metric is used in report and calculations
# TODO: remove and unify with metric_key
def metric_column_key
fixed? ? metric_key.gsub(/_1|_2/, '') : metric_key
end
def cost_keys(sub_metric = nil)
keys = ["#{rate_name}_#{sub_metric ? sub_metric + '_' : ''}cost", # cost associated with metric (e.g. Storage [Used|Allocated|Fixed] Cost)
'total_cost']
sub_metric ? keys : keys + ["#{group}_cost"] # cost associated with metric's group (e.g. Storage Total Cost)
end
def metering?
group == 'metering' && source == 'used'
end
def rate_name
"#{group}_#{source}"
end
def self.cols_on_metric_rollup
(%w[id tag_names resource_id] + chargeable_cols_on_metric_rollup).uniq
end
def self.col_index(column)
@rate_cols ||= {}
column = VIRTUAL_COL_USES[column] || column
@rate_cols[column] ||= cols_on_metric_rollup.index(column.to_s)
end
private
def used?
source == 'used'
end
def allocated?
source == 'allocated'
end
def self.seed
measures = ChargebackRateDetailMeasure.all.index_by(&:name)
existing = ChargeableField.all.index_by(&:metric)
seed_data.each do |f|
measure = f.delete(:measure)
if measure
f[:chargeback_rate_detail_measure_id] = measures[measure].id
end
rec = existing[f[:metric]]
if rec.nil?
create(f)
else
rec.attributes = f
rec.save! if rec.changed?
end
end
end
def self.seed_data
fixture_file = File.join(FIXTURE_DIR, 'chargeable_fields.yml')
File.exist?(fixture_file) ? YAML.load_file(fixture_file) : []
end
private_class_method :seed_data
def self.chargeable_cols_on_metric_rollup
existing_cols = MetricRollup.attribute_names
chargeable_cols = pluck(:metric) & existing_cols
chargeable_cols.map! { |x| VIRTUAL_COL_USES[x] || x }.sort
end
private_class_method :chargeable_cols_on_metric_rollup
end