Skip to content

Commit

Permalink
Add rate assignment by docker labels to Chargeback for container images
Browse files Browse the repository at this point in the history
  • Loading branch information
Ari Zellner committed Dec 6, 2016
1 parent 6f4f7c6 commit 31fac86
Show file tree
Hide file tree
Showing 10 changed files with 190 additions and 20 deletions.
56 changes: 54 additions & 2 deletions app/controllers/chargeback_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ def cb_assign_field_changed
render :update do |page|
page << javascript_prologue
changed = (@edit[:new] != @edit[:current])
page.replace("cb_assignment_div", :partial => "cb_assignments") if params[:cbshow_typ] || params[:cbtag_cat] # only replace if cbshow_typ or cbtag_cat has changed
page.replace("cb_assignment_div", :partial => "cb_assignments") if params[:cbshow_typ] || params[:cbtag_cat] || params[:cblabel_key]
page << javascript_for_miq_button_visibility(changed)
end
end
Expand Down Expand Up @@ -623,6 +623,19 @@ def cb_assign_set_record_vars
@edit[:set_assignments].push(temp)
end
end
elsif @edit[:new][:cbshow_typ].ends_with?("-labels")
@edit[:set_assignments] = []
@edit[:cb_assign][:docker_label_values].each do |id, _value|
key = "#{@edit[:new][:cbshow_typ]}__#{id}"
if !@edit[:new][key].nil? && @edit[:new][key] != "nil"
temp = {
:cb_rate => ChargebackRate.find(@edit[:new][key]),
:label => [CustomAttribute.find(id)]
}
temp[:label].push(@edit[:new][:cbshow_typ].split("-").first)
@edit[:set_assignments].push(temp)
end
end
else
@edit[:set_assignments] = []
@edit[:cb_assign][:cis].each do |id, _ci|
Expand Down Expand Up @@ -670,7 +683,11 @@ def cb_assign_set_form_vars
when MiqEnterprise
"enterprise"
when NilClass
"#{@edit[:current_assignment][0][:tag][1]}-tags"
if @edit[:current_assignment][0][:tag]
"#{@edit[:current_assignment][0][:tag][1]}-tags"
else
"#{@edit[:current_assignment][0][:label][1]}-labels"
end
else
@edit[:current_assignment][0][:object].class.name.downcase
end
Expand All @@ -684,6 +701,16 @@ def cb_assign_set_form_vars
else
@edit[:current_assignment] = []
end
elsif @edit[:new][:cbshow_typ] && @edit[:new][:cbshow_typ].ends_with?("-labels")
get_docker_labels_all_keys
label = @edit[:current_assignment][0][:label][0]
if label
label = @edit[:cb_assign][:docker_label_keys].select{ |_key, value| value == label.name }.first
@edit[:new][:cblabel_key] = label.first.to_s
get_docker_labels_all_values(label.first)
else
@edit[:current_assignment] = []
end
elsif @edit[:new][:cbshow_typ]
get_cis_all
end
Expand All @@ -693,6 +720,9 @@ def cb_assign_set_form_vars
@edit[:new]["#{@edit[:new][:cbshow_typ]}__#{el[:object]["id"]}"] = el[:cb_rate]["id"].to_s
elsif el[:tag]
@edit[:new]["#{@edit[:new][:cbshow_typ]}__#{el[:tag][0]["id"]}"] = el[:cb_rate]["id"].to_s
elsif el[:label]
label = @edit[:cb_assign][:docker_label_values].select{ |_key, value| value == el[:label][0].value }.first
@edit[:new]["#{@edit[:new][:cbshow_typ]}__#{label.first}"] = el[:cb_rate]["id"].to_s
end
end

Expand All @@ -718,6 +748,22 @@ def get_tags_all(category)
classification.entries.each { |e| @edit[:cb_assign][:tags][e.id.to_s] = e.description } if classification
end

def get_docker_labels_all_keys
@edit[:cb_assign][:docker_label_keys] = {}
CustomAttribute.where(:section => "docker_labels").pluck(:id, :name).uniq { |x| x.second }.each do |label|
@edit[:cb_assign][:docker_label_keys][label.first.to_s] = label.second
end
end

def get_docker_labels_all_values(label_id)
@edit[:cb_assign][:docker_label_values] = {}
label_name = CustomAttribute.find(label_id).name

CustomAttribute.where(:section => "docker_labels", :name => label_name).pluck(:id, :value).uniq { |x| x.second }.each do |label|
@edit[:cb_assign][:docker_label_values][label.first.to_s] = label.second
end
end

WHITELIST_INSTANCE_TYPE = %w(enterprise storage ext_management_system ems_cluster tenant ems_container).freeze
NOTHING_FORM_VALUE = "nil".freeze

Expand Down Expand Up @@ -763,16 +809,22 @@ def cb_assign_get_form_vars
@edit[:new][:cbshow_typ] = params[:cbshow_typ] if params[:cbshow_typ]
@edit[:new][:cbtag_cat] = nil if params[:cbshow_typ] # Reset categories pull down if assign to selection is changed
@edit[:new][:cbtag_cat] = params[:cbtag_cat].to_s if params[:cbtag_cat]
@edit[:new][:cblabel_key] = nil if params[:cbshow_typ]
@edit[:new][:cblabel_key] = params[:cblabel_key].to_s if params[:cblabel_key]

if @edit[:new][:cbshow_typ].ends_with?("-tags")
get_categories_all
get_tags_all(params[:cbtag_cat]) if params[:cbtag_cat]
elsif @edit[:new][:cbshow_typ].ends_with?("-labels")
get_docker_labels_all_keys
get_docker_labels_all_values(params[:cblabel_key]) if !params[:cblabel_key].blank?
else
get_cis_all
end

cb_assign_params_to_edit(:cis)
cb_assign_params_to_edit(:tags)
cb_assign_params_to_edit(:docker_label_values)
end

def replace_right_cell(options = {})
Expand Down
3 changes: 3 additions & 0 deletions app/helpers/ui_constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,9 @@ module UiConstants
"container_image-tags" => PostponedTranslation.new(N_("Tagged %{tables}")) do
{:tables => ui_lookup(:tables => "container_image")}
end,
"container_image-labels" => PostponedTranslation.new(N_("Labeled %{tables}")) do
{:tables => ui_lookup(:tables => "container_image")}
end,
"tenant" => N_("Tenants")
}

Expand Down
2 changes: 1 addition & 1 deletion app/models/chargeback.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def self.build_results_for_report_chargeback(options)
rates = RatesCache.new

base_rollup = MetricRollup.includes(
:resource => [:hardware, :tenant, :tags, :vim_performance_states, :custom_attributes],
:resource => [:hardware, :tenant, :tags, :vim_performance_states, :custom_attributes, {:container_image => :custom_attributes}],
:parent_host => :tags,
:parent_ems_cluster => :tags,
:parent_storage => :tags,
Expand Down
4 changes: 2 additions & 2 deletions app/models/chargeback_container_image.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ def init_extra_fields(perf)
self.image_name = self.class.image(perf).try(:full_name) || _('Deleted') # until image archiving is implemented
self.project_uid = self.class.project(perf).ems_ref
self.provider_name = perf.parent_ems.try(:name)
self.provider_uid = perf.parent_ems.try(:name)
self.archived = self.class.project(perf).archived? ? _('Yes') : _('No'),
self.provider_uid = perf.parent_ems.try(:guid)
self.archived = self.class.project(perf).archived? ? _('Yes') : _('No')
self.entity = self.class.image(perf)
end
end # class ChargebackContainerImage
4 changes: 3 additions & 1 deletion app/models/chargeback_rate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def self.get_assignments(type)
assigned_tos = rate.get_assigned_tos
assigned_tos[:tags].each { |tag| result << {:cb_rate => rate, :tag => tag} }
assigned_tos[:objects].each { |object| result << {:cb_rate => rate, :object => object} }
assigned_tos[:labels].each { |label| result << {:cb_rate => rate, :label => label} }
end
result
end
Expand All @@ -51,6 +52,7 @@ def self.set_assignments(type, cb_rates)
cb_rates.each do |rate|
rate[:cb_rate].assign_to_objects(rate[:object]) if rate.key?(:object)
rate[:cb_rate].assign_to_tags(*rate[:tag]) if rate.key?(:tag)
rate[:cb_rate].assign_to_labels(*rate[:label]) if rate.key?(:label)
end
end

Expand Down Expand Up @@ -159,7 +161,7 @@ def self.seed_chargeback_rate
end

def assigned?
get_assigned_tos != {:objects => [], :tags => []}
get_assigned_tos != {:objects => [], :tags => [], :labels => []}
end

###########################################################
Expand Down
5 changes: 3 additions & 2 deletions app/models/metric/chargeback_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ def tag_list_with_prefix
if resource.kind_of?(Container)
state = resource.vim_performance_state_for_ts(timestamp.to_s)
image_tag_name = "#{state.image_tag_names}|" if state
labels = resource.try(:container_image).try(:docker_labels).try(:collect) {|l| "container_image/label/managed/#{l.name}/#{l.value}"}
end

"#{image_tag_name}#{tag_names}".split("|").reject(&:empty?).map { |x| "#{tag_prefix}#{x}" }
"#{image_tag_name}#{tag_names}".split("|").reject(&:empty?).map { |x| "#{tag_prefix}#{x}" } + (labels || [])
end

def resource_parents
Expand All @@ -43,7 +44,7 @@ def parents_determining_rate
when ContainerProject.name
[parent_ems].compact
when Container.name
[]
[parent_ems]
end
end
end
24 changes: 23 additions & 1 deletion app/models/mixins/assignment_mixin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,28 @@ def assign_to_tags(objects, klass)
reload
end

def assign_to_labels(objects, klass)
# objects => A single item or array of items
# item => A classification entry instance or a classification entry id
# klass => The class of the object that self is to be assigned to - (Takes both forms - Host or host, EmsCluster or ems_cluster)
objects.to_miq_a.each do |obj|
unless obj.kind_of?(ActiveRecord::Base) # obj is the id of a classification entry instance
id = obj
obj = CustomAttribute.find_by_id(id)
if obj.nil?
_log.warn("Unable to find label with id [#{id}], skipping assignment")
next
end
end
tag = "#{klass.underscore}/label/managed/#{obj.name}/#{obj.value}"
tag_add(tag, :ns => namespace)
end
reload
end

def get_assigned_tos
# Returns: {:objects => [obj, obj, ...], :tags => [[Classification.entry_object, klass], ...]}
result = {:objects => [], :tags => []}
result = {:objects => [], :tags => [], :labels => []}
tags = tag_list(:ns => namespace).split
tags.each do |t|
parts = t.split("/")
Expand All @@ -61,6 +80,9 @@ def get_assigned_tos
when :tag
tag = Tag.find_by_name("/" + parts.join("/"))
result[:tags] << [Classification.find_by_tag_id(tag.id), klass] unless tag.nil?
when :label
label = CustomAttribute.find_by(:name => parts[1], :value => parts[2])
result[:labels] << [label, klass] unless label.nil?
end
end

Expand Down
33 changes: 32 additions & 1 deletion app/views/chargeback/_cb_assignments.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,20 @@
:javascript
miqInitSelectPicker();
miqSelectPickerEvent("cbtag_cat", "#{url}")
- if !@edit[:new][:cbshow_typ].blank? && @edit[:new][:cbshow_typ].ends_with?("-labels")
.form-group
%label.col-md-2.control-label
= _('Docker Labels')
.col-md-8
- options = Array(@edit[:cb_assign][:docker_label_keys].invert).sort_by { |a| a.first.downcase }
= select_tag("cblabel_key", options_for_select([["<#{_('Choose a Label')}>", ""]] + options, @edit[:new][:cblabel_key].to_s),
"data-miq_sparkle_on" => true, "data-miq_sparkle_off" => true, :class => "selectpicker")
:javascript
miqInitSelectPicker();
miqSelectPickerEvent("cblabel_key", "#{url}")
- unless @edit[:new][:cbshow_typ].nil? || @edit[:new][:cbshow_typ] == "nil"
- if !@edit[:new][:cbshow_typ].ends_with?("-tags") || @edit[:new][:cbtag_cat].present?
- show_type = @edit[:new][:cbshow_typ]
- if !(show_type.ends_with?("-tags") || show_type.ends_with?("-labels")) || @edit[:new][:cbtag_cat].present? || @edit[:new][:cblabel_key].present?
%hr
%h3
= _('Selections')
Expand Down Expand Up @@ -84,6 +96,25 @@
:javascript
miqInitSelectPicker();
miqSelectPickerEvent("#{@edit[:new][:cbshow_typ]}__#{id}", "#{url}")
- elsif @edit[:new][:cbshow_typ].ends_with?("-labels")
%table.table.table-bordered.table-striped
%thead
%tr
%th= _('Name')
%th= _('Rate')
%tbody
- @edit[:cb_assign][:docker_label_values].invert.sort_by { |a| a.first.downcase }.each do |value, id|
%tr
%td
= h(value)
%td
- options = @edit[:cb_rates].invert.sort
= select_tag("#{@edit[:new][:cbshow_typ]}__#{id}",
options_for_select([[nothing, "nil"]] + options, @edit[:new]["#{@edit[:new][:cbshow_typ]}__#{id}".to_sym].to_s),
"data-miq_sparkle_on" => true, "data-miq_sparkle_off" => true, :class => "selectpicker")
:javascript
miqInitSelectPicker();
miqSelectPickerEvent("#{@edit[:new][:cbshow_typ]}__#{id}", "#{url}")
- else
%table.table.table-bordered.table-striped
%thead
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
78 changes: 68 additions & 10 deletions spec/models/chargeback_container_image_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

@node = FactoryGirl.create(:container_node, :name => "node")
@image = FactoryGirl.create(:container_image, :ext_management_system => @ems)
@label = FactoryGirl.build(:custom_attribute, :name => "version_label-1", :value => "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)
Expand Down Expand Up @@ -104,16 +105,16 @@

while time < end_time
@container.metric_rollups << FactoryGirl.create(:metric_rollup_vm_hr,
:timestamp => time,
:cpu_usage_rate_average => @cpu_usage_rate,
:derived_vm_numvcpus => @cpu_count,
:derived_memory_available => @memory_available,
:derived_memory_used => @memory_used,
:net_usage_rate_average => @net_usage_rate,
:parent_ems_id => @ems.id,
:tag_names => "",
:resource_name => @project.name,
:resource_id => @project.id)
:timestamp => time,
:cpu_usage_rate_average => @cpu_usage_rate,
:derived_vm_numvcpus => @cpu_count,
:derived_memory_available => @memory_available,
:derived_memory_used => @memory_used,
:net_usage_rate_average => @net_usage_rate,
:parent_ems_id => @ems.id,
:tag_names => "",
:resource_name => @project.name,
:resource_id => @project.id)
@container.vim_performance_states << FactoryGirl.create(:vim_performance_state,
:timestamp => time,
:image_tag_names => "environment/prod")
Expand Down Expand Up @@ -143,4 +144,61 @@
expect(subject.fixed_compute_1_cost).to be_within(0.01).of(@hourly_rate * @hours_in_month)
end
end

context "Label" do
before do
@options[:interval] = "monthly"
@options[:entity_id] = @project.id
@options[:tag] = nil
@image.docker_labels << @label
ChargebackRate.set_assignments(:compute, [{ :cb_rate => @cbr, :label => [@label, "container_image"] }])

tz = Metric::Helper.get_time_zone(@options[:ext_options])
ts = Time.now.in_time_zone(tz)
time = ts.beginning_of_month.utc
end_time = ts.end_of_month.utc

@hours_in_month = Time.days_in_month(time.month, time.year) * 24

while time < end_time
@container.metric_rollups << FactoryGirl.create(:metric_rollup_vm_hr,
:timestamp => time,
:cpu_usage_rate_average => @cpu_usage_rate,
:derived_vm_numvcpus => @cpu_count,
:derived_memory_available => @memory_available,
:derived_memory_used => @memory_used,
:net_usage_rate_average => @net_usage_rate,
:parent_ems_id => @ems.id,
:tag_names => "",
:resource_name => @project.name,
:resource_id => @project.id)
@container.vim_performance_states << FactoryGirl.create(:vim_performance_state,
:timestamp => time,
:image_tag_names => "")
time += 12.hours
end
@metric_size = @container.metric_rollups.size
end

subject { ChargebackContainerImage.build_results_for_report_ChargebackContainerImage(@options).first.first }

let(:cbt) {
FactoryGirl.create(:chargeback_tier,
:start => 0,
:finish => Float::INFINITY,
:fixed_rate => 0.0,
:variable_rate => @hourly_rate.to_s)
}
let!(:cbrd) {
FactoryGirl.create(:chargeback_rate_detail_fixed_compute_cost,
:chargeback_rate_id => @cbr.id,
:per_time => "hourly",
:source => "compute_1",
:chargeback_tiers => [cbt])
}
it "fixed_compute" do
# .to be_within(0.01) is used since theres a float error here
expect(subject.fixed_compute_1_cost).to be_within(0.01).of(@hourly_rate * @hours_in_month)
end
end
end

0 comments on commit 31fac86

Please sign in to comment.