diff --git a/app/models/tag.rb b/app/models/tag.rb index 008c1735806..3146d720cd2 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -11,7 +11,11 @@ class Tag < ApplicationRecord def self.list(object, options = {}) ns = get_namespace(options) if ns[0..7] == "/virtual" - predicate = ns.split("/")[2..-1] # throw away /virtual + ns.gsub!('/virtual/','') # throw away /virtual + ns, virtual_custom_attribute = MiqExpression.escape_virtual_custom_attribute(ns) + predicate = ns.split("/") + predicate.map!{ |x| URI::RFC2396_Parser.new.unescape(x) } if virtual_custom_attribute + begin predicate.inject(object) { |target, method| target.public_send method } rescue NoMethodError diff --git a/lib/miq_expression.rb b/lib/miq_expression.rb index 2321bd86709..00893dfb9a5 100644 --- a/lib/miq_expression.rb +++ b/lib/miq_expression.rb @@ -1051,8 +1051,19 @@ def self.preprocess_managed_tag(tag) tag end + def self.escape_virtual_custom_attribute(attribute) + if attribute.include?(CustomAttributeMixin::CUSTOM_ATTRIBUTES_PREFIX) + uri_parser = URI::RFC2396_Parser.new + [uri_parser.escape(attribute, /[^A-Za-z0-9:\-_]/), true] + else + [attribute, false] + end + end + def self.value2tag(tag, val = nil) + tag, virtual_custom_attribute = escape_virtual_custom_attribute(tag) model, *values = tag.to_s.gsub(/[\.-]/, "/").split("/") # replace model path ".", column name "-" with "/" + values.map!{ |x| URI::RFC2396_Parser.new.unescape(x) } if virtual_custom_attribute case values.first when "user_tag" diff --git a/lib/miq_expression/field.rb b/lib/miq_expression/field.rb index e9462ea5f9a..5293f1d06ef 100644 --- a/lib/miq_expression/field.rb +++ b/lib/miq_expression/field.rb @@ -3,14 +3,19 @@ class MiqExpression::Field < MiqExpression::Target (?([[:upper:]][[:alnum:]]*(::)?)+) (?!.*\b(managed|user_tag)\b) \.?(?[a-z][0-9a-z_\.]+)? --(?[a-z]+(_[[:alnum:]]+)*) +- +(?: + (?#{CustomAttributeMixin::CUSTOM_ATTRIBUTES_PREFIX}[a-z]+[_\-.\/[:alnum:]]*)| + (?[a-z]+(_[[:alnum:]]+)*) +) /x delegate :eq, :not_eq, :lteq, :gteq, :lt, :gt, :between, :to => :arel_attribute def self.parse(field) parsed_params = parse_params(field) || return - new(parsed_params[:model_name], parsed_params[:associations], parsed_params[:column]) + new(parsed_params[:model_name], parsed_params[:associations], parsed_params[:column] || + parsed_params[:virtual_custom_column]) end def self.is_field?(field) diff --git a/spec/models/miq_report_spec.rb b/spec/models/miq_report_spec.rb index 65f1b92691e..de8027d6307 100644 --- a/spec/models/miq_report_spec.rb +++ b/spec/models/miq_report_spec.rb @@ -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) } @@ -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" ) @@ -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