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

[WIP] Support special characters in reporting of Custom Attributes #11112

Closed
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
21 changes: 19 additions & 2 deletions app/models/miq_report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,31 @@ def self.get_col_info(path)
}
end

# example
# converts "Host.hardware.disks-filename" to
# ["Host", "hardware", "disk", "filename"]
def self.parse_column(column)
prefix = CustomAttributeMixin::CUSTOM_ATTRIBUTES_PREFIX
column, column_without_relations = column.split("-#{prefix}") if column.include?(prefix)

parts = column.split(".")

if column.include?(prefix)
parts.push("#{prefix}#{column_without_relations}")
end

parts[-1] = parts.last.split("-").first # Strip off field name from last element

parts
end

def self.display_filter_details(cols, mode)
# mode => :field || :tag
# Only return cols from has_many sub-tables
return [] if cols.nil?
cols.inject([]) do |r, c|
# eg. c = ["Host.Hardware.Disks : File Name", "Host.hardware.disks-filename"]
parts = c.last.split(".")
parts[-1] = parts.last.split("-").first # Strip off field name from last element
parts = parse_column(c.last)

if parts.last == "managed"
next(r) unless mode == :tag
Expand Down
5 changes: 5 additions & 0 deletions lib/extensions/ar_taggable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,13 @@ def tag_list(options = {})

def vtag_list(options = {})
ns = Tag.get_namespace(options)
origin_ns = ns

prefix = CustomAttributeMixin::CUSTOM_ATTRIBUTES_PREFIX
ns, attribute_without_prefix = ns.split(prefix) if origin_ns.include?(prefix)

predicate = ns.split("/")[2..-1] # throw away /virtual
predicate.push("#{prefix}#{attribute_without_prefix}") if origin_ns.include?(prefix)

# p "ns: [#{ns}]"
# p "predicate: [#{predicate.inspect}]"
Expand Down
26 changes: 24 additions & 2 deletions lib/miq_expression.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1001,8 +1001,30 @@ def self.preprocess_managed_tag(tag)
tag
end

def self.value2tag(tag, val = nil)
model, *values = tag.to_s.gsub(/[\.-]/, "/").split("/") # replace model path ".", column name "-" with "/"
# example:
# convert "MiqGroup.vms.host-disconnected" to
# ["MiqGroup", ["vms", "host", "disconnected"]]
def self.model_and_attributes_from(attribute_with_model)
origin_attribute_with_model = attribute_with_model

prefix = CustomAttributeMixin::CUSTOM_ATTRIBUTES_PREFIX

if origin_attribute_with_model.include?(prefix)
attribute_with_model, virtual_custom_attribute_without_prefix = attribute_with_model.split(prefix)
end

# replace model path ".", column name "-" with "/"
model, *values = attribute_with_model.to_s.gsub(/[\.-]/, "/").split("/")

if origin_attribute_with_model.include?(prefix)
values.push("#{prefix}#{virtual_custom_attribute_without_prefix}")
end

[model, values]
end

def self.value2tag(attribute_with_model, val = nil)
model, values = model_and_attributes_from(attribute_with_model)

case values.first
when "user_tag"
Expand Down
9 changes: 7 additions & 2 deletions lib/miq_expression/field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@ class MiqExpression::Field
FIELD_REGEX = /
(?<model_name>([[:upper:]][[:alnum:]]*(::)?)+)
\.?(?<associations>[a-z_\.]+)*
-(?<column>[a-z]+(_[[:alnum:]]+)*)
-
Copy link
Contributor

Choose a reason for hiding this comment

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

Not necessary for this PR, but if this regex is getting increasingly complex it may be better to implement a bona fide parser. User feedback is valuable - LMK if you have any opinions either way

(?:
(?<virtual_custom_column>#{CustomAttributeMixin::CUSTOM_ATTRIBUTES_PREFIX}.*)|
(?<column>[a-z]+(_[[:alnum:]]+)*)
)
/x

ParseError = Class.new(StandardError)

def self.parse(field)
match = FIELD_REGEX.match(field) or raise ParseError, field
new(match[:model_name].constantize, match[:associations].to_s.split("."), match[:column])
column = match[:virtual_custom_column] || match[:column]
new(match[:model_name].constantize, match[:associations].to_s.split("."), column)
end

attr_reader :model, :associations, :column
Expand Down
13 changes: 7 additions & 6 deletions spec/models/miq_report_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,16 @@

context "report with virtual dynamic custom attributes" do
let(:options) { {:targets_hash => true, :userid => "admin"} }
let(:custom_column_key_1) { 'ATTR_Name_1' }
let(:custom_column_key_2) { 'ATTR_Name_2' }
let(:custom_column_key_1) { 'kubernetes.io/hostname' }
let(:custom_column_key_2) { 'manageiq.org' }
let(:custom_column_key_3) { 'ATTR_Name_3' }
let(:custom_column_value) { 'value1' }
let(:user) { FactoryGirl.create(:user_with_group) }
let(:ems) { FactoryGirl.create(:ems_vmware) }
let!(:vm_1) { FactoryGirl.create(:vm_vmware) }
let!(:vm_2) { FactoryGirl.create(:vm_vmware, :retired => false, :ext_management_system => ems) }
let(:virtual_column_key_1) { "#{CustomAttributeMixin::CUSTOM_ATTRIBUTES_PREFIX}ATTR_Name_1" }
let(:virtual_column_key_2) { "#{CustomAttributeMixin::CUSTOM_ATTRIBUTES_PREFIX}ATTR_Name_2" }
let(:virtual_column_key_1) { "#{CustomAttributeMixin::CUSTOM_ATTRIBUTES_PREFIX}kubernetes.io/hostname" }
let(:virtual_column_key_2) { "#{CustomAttributeMixin::CUSTOM_ATTRIBUTES_PREFIX}manageiq.org" }
let(:virtual_column_key_3) { "#{CustomAttributeMixin::CUSTOM_ATTRIBUTES_PREFIX}ATTR_Name_3" }
let(:miq_task) { FactoryGirl.create(:miq_task) }

Expand All @@ -111,9 +111,9 @@
MiqReport.new(
:name => "Custom VM report", :title => "Custom VM report", :rpt_group => "Custom", :rpt_type => "Custom",
:db => "ManageIQ::Providers::InfraManager::Vm",
:cols => %w(name virtual_custom_attribute_ATTR_Name_1 virtual_custom_attribute_ATTR_Name_2),
:cols => %w(name virtual_custom_attribute_kubernetes.io/hostname virtual_custom_attribute_manageiq.org),
:include => {:custom_attributes => {}},
:col_order => %w(name virtual_custom_attribute_ATTR_Name_1 virtual_custom_attribute_ATTR_Name_2),
:col_order => %w(name virtual_custom_attribute_kubernetes.io/hostname virtual_custom_attribute_manageiq.org),
:headers => ["Name", custom_column_key_1, custom_column_key_1],
:order => "Ascending"
)
Expand Down Expand Up @@ -151,6 +151,7 @@
end

expected_results = ["name" => vm_1.name, virtual_column_key_1 => custom_column_value, virtual_column_key_2 => nil]

expect(report_result).to match_array(expected_results)
end

Expand Down