Skip to content

Commit

Permalink
Merge pull request ManageIQ#10677 from zeari/chargeback_container_image
Browse files Browse the repository at this point in the history
Chargeback for container images
  • Loading branch information
gtanzillo committed Oct 10, 2016
2 parents 2d0963a + 6cdb5ce commit 3b2860f
Show file tree
Hide file tree
Showing 9 changed files with 341 additions and 47 deletions.
43 changes: 5 additions & 38 deletions app/controllers/report_controller/reports/editor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,7 @@ def set_record_vars(rpt)
options[:provider_id] = @edit[:new][:cb_provider_id]
options[:entity_id] = @edit[:new][:cb_entity_id]
options[:groupby_tag] = @edit[:new][:cb_groupby_tag]
options[:groupby] = @edit[:new][:cb_groupby]
end

rpt.db_options[:options] = options
Expand Down Expand Up @@ -1028,37 +1029,8 @@ def set_record_vars(rpt)
rpt.sortby = @edit[:new][:sortby1] == NOTHING_STRING ? nil : [] # Clear sortby if sortby1 not present, else set up array

# Add in the chargeback static fields
if Chargeback.db_is_chargeback?(rpt.db) # For chargeback, add in static fields
rpt.cols = %w(start_date display_range)
name_col = @edit[:new][:model].constantize.report_name_field
tag_col = @edit[:new][:model].constantize.report_tag_field
if @edit[:new][:cb_groupby] == "date"
rpt.cols += [name_col]
rpt.col_order = ["display_range", name_col]
rpt.sortby = ["start_date", name_col]
elsif @edit[:new][:cb_groupby] == "vm"
rpt.cols += [name_col]
rpt.col_order = [name_col, "display_range"]
rpt.sortby = [name_col, "start_date"]
elsif @edit[:new][:cb_groupby] == "tag"
rpt.cols += [tag_col]
rpt.col_order = [tag_col, "display_range"]
rpt.sortby = [tag_col, "start_date"]
end
rpt.col_order.each do |c|
if c == tag_col
header = @edit[:cb_cats][@edit[:new][:cb_groupby_tag]]
rpt.headers.push(Dictionary.gettext(header, :type => :column, :notfound => :titleize))
else
rpt.headers.push(Dictionary.gettext(c, :type => :column, :notfound => :titleize))
end

rpt.col_formats.push(nil) # No formatting needed on the static cols
end
rpt.col_options = @edit[:new][:model].constantize.report_col_options
rpt.order = "Ascending"
rpt.group = "y"
rpt.tz = @edit[:new][:tz]
if Chargeback.db_is_chargeback?(rpt.db) # For chargeback, add in specific chargeback report options
rpt = @edit[:new][:model].constantize.set_chargeback_report_options(rpt, @edit)
end

# Remove when we support user sorting of trend reports
Expand Down Expand Up @@ -1284,20 +1256,15 @@ def set_form_vars
@edit[:new][:cb_show_typ] = "entity"
@edit[:new][:cb_entity_id] = options[:entity_id]
@edit[:new][:cb_provider_id] = options[:provider_id]
@edit[:new][:cb_groupby] = options[:groupby]
@edit[:new][:cb_groupby_tag] = options[:groupby_tag]
end

@edit[:new][:cb_model] = Chargeback.report_cb_model(@rpt.db)
@edit[:new][:cb_interval] = options[:interval]
@edit[:new][:cb_interval_size] = options[:interval_size]
@edit[:new][:cb_end_interval_offset] = options[:end_interval_offset]
@edit[:new][:cb_groupby] = if @rpt.sortby.nil? || @rpt.sortby.first == "start_date"
"date"
elsif @edit[:new][:cb_groupby_tag].present?
"tag"
else
"vm"
end
@edit[:new][:cb_groupby] = options[:groupby]
end

# Only show chargeback users choice if an admin
Expand Down
58 changes: 54 additions & 4 deletions app/models/chargeback.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def self.build_results_for_report_chargeback(options)
end

base_rollup = MetricRollup.includes(
:resource => [:hardware, :tenant],
:resource => [:hardware, :tenant, :tags, :vim_performance_states],
:parent_host => :tags,
:parent_ems_cluster => :tags,
:parent_storage => :tags,
Expand Down Expand Up @@ -99,15 +99,20 @@ def get_rates(perf)

tags = perf.tag_names.split("|").reject { |n| n.starts_with?("folder_path_") }.sort.join("|")
keys = [tags, perf.parent_host_id, perf.parent_ems_cluster_id, perf.parent_storage_id, perf.parent_ems_id]
keys += [perf.resource.container_image, perf.timestamp] if perf.resource_type == Container.name
tenant_resource = perf.resource.try(:tenant)
keys.push(tenant_resource.id) unless tenant_resource.nil?
key = keys.join("_")
return @rates[key] if @rates.key?(key)

tag_list = perf.tag_names.split("|").inject([]) { |arr, t| arr << "vm/tag/managed/#{t}"; arr }
tag_list = perf.tag_names.split("|").inject([]) { |arr, t| arr << "#{Chargeback.report_cb_model(self.class.name).underscore}/tag/managed/#{t}" }

parents = [perf.parent_host, perf.parent_ems_cluster, perf.parent_storage, perf.parent_ems, @enterprise].compact
parents.push(tenant_resource) unless tenant_resource.nil?
if perf.resource_type == Container.name
state = perf.resource.vim_performance_state_for_ts(perf.timestamp.to_s)
tag_list += state.image_tag_names.split("|").inject([]) { |arr, t| arr << "container_image/tag/managed/#{t}" } if state.present?
end

parents = get_rate_parents(perf).compact

@rates[key] = ChargebackRate.get_assigned_for_target(perf.resource, :tag_list => tag_list, :parents => parents)
end
Expand Down Expand Up @@ -240,4 +245,49 @@ def self.db_is_chargeback?(db)
def self.report_tag_field
"tag_name"
end

def self.get_rate_parents
raise "Chargeback: get_rate_parents must be implemented in child class."
end

def self.set_chargeback_report_options(rpt, edit)
rpt.cols = %w(start_date display_range)

static_cols = report_static_cols
if edit[:new][:cb_groupby] == "date"
rpt.cols += static_cols
rpt.col_order = ["display_range"] + static_cols
rpt.sortby = ["start_date"] + static_cols
elsif edit[:new][:cb_groupby] == "vm"
rpt.cols += static_cols
rpt.col_order = static_cols + ["display_range"]
rpt.sortby = static_cols + ["start_date"]
elsif edit[:new][:cb_groupby] == "tag"
tag_col = report_tag_field
rpt.cols += tag_col
rpt.col_order = [tag_col, "display_range"]
rpt.sortby = [tag_col, "start_date"]
elsif edit[:new][:cb_groupby] == "project"
static_cols -= ["image_name"]
rpt.cols += static_cols
rpt.col_order = static_cols + ["display_range"]
rpt.sortby = static_cols + ["start_date"]
end
rpt.col_order.each do |c|
if c == tag_col
header = edit[:cb_cats][edit[:new][:cb_groupby_tag]]
rpt.headers.push(Dictionary.gettext(header, :type => :column, :notfound => :titleize))
else
rpt.headers.push(Dictionary.gettext(c, :type => :column, :notfound => :titleize))
end

rpt.col_formats.push(nil) # No formatting needed on the static cols
end

rpt.col_options = report_col_options
rpt.order = "Ascending"
rpt.group = "y"
rpt.tz = edit[:new][:tz]
rpt
end
end # class Chargeback
114 changes: 114 additions & 0 deletions app/models/chargeback_container_image.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
class ChargebackContainerImage < Chargeback
set_columns_hash(
:start_date => :datetime,
:end_date => :datetime,
:interval_name => :string,
:display_range => :string,
:chargeback_rates => :string,
:project_name => :string,
:image_name => :string,
:tag_name => :string,
:project_uid => :string,
:provider_name => :string,
:provider_uid => :string,
:archived => :string,
:cpu_cores_used_cost => :float,
:cpu_cores_used_metric => :float,
:fixed_compute_metric => :integer,
:fixed_compute_1_cost => :float,
:fixed_compute_2_cost => :float,
:fixed_2_cost => :float,
:fixed_cost => :float,
:memory_used_cost => :float,
:memory_used_metric => :float,
:net_io_used_cost => :float,
:net_io_used_metric => :float,
:total_cost => :float
)

def self.build_results_for_report_ChargebackContainerImage(options)
# Options:
# :rpt_type => chargeback
# :interval => daily | weekly | monthly
# :start_time
# :end_time
# :end_interval_offset
# :interval_size
# :owner => <userid>
# :tag => /managed/environment/prod (Mutually exclusive with :user)
# :chargeback_type => detail | summary
# :entity_id => 1/2/3.../all rails id of entity

# Find Project by id or get all projects
@options = options
provider_id = options[:provider_id]
id = options[:entity_id]
raise "must provide option :entity_id and provider_id" if id.nil? && provider_id.nil?

@containers = if provider_id == "all"
Container.all
elsif id == "all"
Container.where('ems_id = ? or old_ems_id = ?', provider_id, provider_id)
else
Container.joins(:container_group).where('container_groups.container_project_id = ? or container_groups.old_container_project_id = ?', id, id)
end

@containers = @containers.includes(:container_project, :old_container_project, :container_image)
return [[]] if @containers.empty?

@data_index = {}
@containers.each do |c|
@data_index.store_path(:container_project, :by_container_id, c.id, c.container_project || c.old_container_project)
@data_index.store_path(:container_image, :by_container_id, c.id, c.container_image)
end

build_results_for_report_chargeback(options)
end

def self.get_keys_and_extra_fields(perf, ts_key)
project = @data_index.fetch_path(:container_project, :by_container_id, perf.resource_id)
image = @data_index.fetch_path(:container_image, :by_container_id, perf.resource_id)

key = @options[:groupby] == 'project' ? "#{project.id}_#{ts_key}" : "#{project.id}_#{image.id}_#{ts_key}"

extra_fields = {
"project_name" => project.name,
"image_name" => image.try(:full_name) || _("Deleted"), # until image archiving is implemented
"project_uid" => project.ems_ref,
"provider_name" => perf.parent_ems.try(:name),
"provider_uid" => perf.parent_ems.try(:name),
"archived" => project.archived? ? _("Yes") : _("No")
}

[key, extra_fields]
end

def self.where_clause(records, _options)
records.where(:resource_type => Container.name, :resource_id => @containers.pluck(:id))
end

def self.report_static_cols
%w(project_name image_name)
end

def self.report_col_options
{
"cpu_cores_used_cost" => {:grouping => [:total]},
"cpu_cores_used_metric" => {:grouping => [:total]},
"fixed_compute_metric" => {:grouping => [:total]},
"fixed_compute_1_cost" => {:grouping => [:total]},
"fixed_compute_2_cost" => {:grouping => [:total]},
"fixed_cost" => {:grouping => [:total]},
"memory_used_cost" => {:grouping => [:total]},
"memory_used_metric" => {:grouping => [:total]},
"net_io_used_cost" => {:grouping => [:total]},
"net_io_used_metric" => {:grouping => [:total]},
"total_cost" => {:grouping => [:total]}
}
end

def get_rate_parents(_perf)
# get rates from image tags only
[]
end
end # class ChargebackContainerImage
9 changes: 7 additions & 2 deletions app/models/chargeback_container_project.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ def self.where_clause(records, _options)
records.where(:resource_type => ContainerProject.name, :resource_id => @projects.select(:id))
end

def self.report_name_field
"project_name"
def self.report_static_cols
%w(project_name)
end

def self.report_col_options
Expand All @@ -102,4 +102,9 @@ def self.report_col_options
def tags
ContainerProject.includes(:tags).find_by_ems_ref(project_uid).try(:tags).to_a
end

def get_rate_parents(perf)
# Get rate from assigned containers providers only
[perf.parent_ems]
end
end # class Chargeback
9 changes: 7 additions & 2 deletions app/models/chargeback_vm.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ def self.where_clause(records, options)
end
end

def self.report_name_field
"vm_name"
def self.report_static_cols
%w(vm_name)
end

def self.report_col_options
Expand Down Expand Up @@ -166,4 +166,9 @@ def self.report_col_options
def tags
Vm.includes(:tags).find_by_ems_ref(vm_uid).try(:tags).to_a
end

def get_rate_parents(perf)
@enterprise ||= MiqEnterprise.my_enterprise
[perf.parent_host, perf.parent_ems_cluster, perf.parent_storage, perf.parent_ems, @enterprise, perf.resource.try(:tenant)]
end
end # class Chargeback
7 changes: 6 additions & 1 deletion app/views/report/_form_filter_chargeback.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
- opts += [[ui_lookup(:model => @edit[:new][:cb_model]), "entity"], ["%s Tag" % current_tenant.name, "tag"]]
- elsif @edit[:new][:model] == "ChargebackVm"
- opts += [[_('Owner'), "owner"], ["%{tenant_name} Tag" % {:tenant_name => current_tenant.name}, "tag"], [_('Tenant'), "tenant"]]
- elsif @edit[:new][:model] == "ChargebackContainerImage"
- opts += [[ui_lookup(:model => @edit[:new][:cb_model]), "entity"]]
- else
- opts += [[_('Owner'), "owner"], ["%{tenant_name} Tag" % {:tenant_name => current_tenant.name}, "tag"], [_(@edit[:new][:cb_model].to_s), "entity"]]
= select_tag("cb_show_typ",
Expand Down Expand Up @@ -108,8 +110,11 @@
%label.control-label.col-md-2
= _('Group by')
.col-md-8
- opts = [["#{_('Date')}", "date"], ["#{_(@edit[:new][:cb_model])}", "vm"]]
- opts += [["#{_('Tag')}", "tag"]] unless @edit[:new][:model] == "ChargebackContainerImage"
- opts += [["#{_('Project')}", "project"]] if @edit[:new][:model] == "ChargebackContainerImage"
= select_tag("cb_groupby",
options_for_select([["#{_('Date')}", "date"], ["#{_('VM/Instance/Project')}", "vm"], ["#{_('Tag')}", "tag"]], @edit[:new][:cb_groupby]),
options_for_select(opts, @edit[:new][:cb_groupby]),
:class => "selectpicker")
:javascript
miqInitSelectPicker();
Expand Down
1 change: 1 addition & 0 deletions lib/miq_expression.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class MiqExpression
BottleneckEvent
ChargebackVm
ChargebackContainerProject
ChargebackContainerImage
CloudResourceQuota
CloudTenant
CloudVolume
Expand Down
6 changes: 6 additions & 0 deletions spec/factories/vim_performance_state.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FactoryGirl.define do
factory :vim_performance_state, :class => :VimPerformanceState do
timestamp { Time.now.utc }
state_data {{}}
end
end

0 comments on commit 3b2860f

Please sign in to comment.