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

Chargeback for container images #10677

Merged
merged 2 commits into from
Oct 10, 2016
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
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)
Copy link
Member

@gtanzillo gtanzillo Oct 6, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should add this method to the base Chargeback class and have it raise an error indicating that get_rate_parents must be implemented in child class.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

# 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
Loading