Skip to content

Commit

Permalink
Fixes #11150 - Allow searching of facts as types other than string
Browse files Browse the repository at this point in the history
  • Loading branch information
imriz authored and Dominic Cleal committed Jul 24, 2015
1 parent baddeae commit 3f8e6c3
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 3 deletions.
9 changes: 8 additions & 1 deletion app/models/concerns/hostext/search.rb
Expand Up @@ -53,7 +53,7 @@ module Search
scoped_search :in => :interfaces, :on => :mac, :complete_value => true, :rename => :has_mac

scoped_search :in => :puppetclasses, :on => :name, :complete_value => true, :rename => :class, :only_explicit => true, :operators => ['= ', '~ '], :ext_method => :search_by_puppetclass
scoped_search :in => :fact_values, :on => :value, :in_key=> :fact_names, :on_key=> :name, :rename => :facts, :complete_value => true, :only_explicit => true
scoped_search :in => :fact_values, :on => :value, :in_key=> :fact_names, :on_key=> :name, :rename => :facts, :complete_value => true, :only_explicit => true, :ext_method => :search_cast_facts
scoped_search :in => :search_parameters, :on => :value, :on_key=> :name, :complete_value => true, :rename => :params, :ext_method => :search_by_params, :only_explicit => true

if SETTINGS[:locations_enabled]
Expand Down Expand Up @@ -171,6 +171,13 @@ def search_by_config_group(key, operator, value)
{:conditions => opts, :include => :hostgroup}
end

def search_cast_facts(key, operator, value)
{
:conditions => "fact_names.name = '#{key.split('.')[1]}' AND #{cast_facts(key,operator,value)}",
:include => :fact_names,
}
end

private

def param_conditions(p)
Expand Down
14 changes: 14 additions & 0 deletions app/models/concerns/scoped_search_extensions.rb
Expand Up @@ -7,5 +7,19 @@ def value_to_sql(operator, value)
return value.tr_s('%*', '%') if (value =~ /%|\*/)
"%#{value}%"
end

def cast_facts(key, operator, value)
is_int = (value =~ /\A[-+]?\d+\z/ ) || (value.is_a?(Integer))
is_pg = ActiveRecord::Base.connection.adapter_name.downcase.starts_with? 'postgresql'
# Once Postgresql 8 support is removed (used in CentOS 6), this could be replaced to only keep the first form (working well with PG 9)
if (is_int && !is_pg)
casted = "CAST(fact_values.value AS DECIMAL) #{operator} #{value}"
elsif (is_int && is_pg)
casted = "fact_values.value ~ E'^\\\\d+$' AND CAST(fact_values.value AS DECIMAL) #{operator} #{value}"
else
casted = "fact_values.value #{operator} '#{value}'"
end
casted
end
end
end
17 changes: 15 additions & 2 deletions app/models/fact_value.rb
Expand Up @@ -9,8 +9,8 @@ class FactValue < ActiveRecord::Base

has_one :parent_fact_name, :through => :fact_name, :source => :parent

scoped_search :on => :value, :in_key=> :fact_name, :on_key=> :name, :rename => :facts, :complete_value => true, :only_explicit => true
scoped_search :on => :value, :default_order => true
scoped_search :on => :value, :in_key=> :fact_name, :on_key=> :name, :rename => :facts, :complete_value => true, :only_explicit => true, :ext_method => :search_cast_facts
scoped_search :on => :value, :default_order => true, :ext_method => :search_value_cast_facts
scoped_search :in => :fact_name, :on => :name, :complete_value => true, :alias => "fact"
scoped_search :in => :host, :on => :name, :complete_value => true, :rename => :host, :ext_method => :search_by_host, :only_explicit => true
scoped_search :in => :hostgroup, :on => :name, :complete_value => true, :rename => :"host.hostgroup", :only_explicit => true
Expand Down Expand Up @@ -102,4 +102,17 @@ def self.to_gb(fact)
end
[ values.sum, values.size ]
end

def self.search_cast_facts(key, operator, value)
{
:conditions => "fact_names.name = '#{key.split('.')[1]}' AND #{cast_facts(key,operator,value)}",
:include => :fact_name,
}
end

def self.search_value_cast_facts(key, operator, value)
{
:conditions => cast_facts(key,operator,value)
}
end
end
16 changes: 16 additions & 0 deletions test/unit/fact_value_test.rb
Expand Up @@ -69,6 +69,22 @@ def setup
assert_empty results
end

test 'numeric searches should use numeric comparsion' do
host = FactoryGirl.create(:host)
FactoryGirl.create(:fact_value, :value => '64498',:host => host,
:fact_name => FactoryGirl.create(:fact_name, :name => 'memory_mb'))
results = FactValue.search_for("facts.memory_mb > 112889")
assert_empty results
results = FactValue.search_for("facts.memory_mb > 6544")
refute_empty results
results = FactValue.search_for("value > 112889")
assert_empty results
results = FactValue.search_for("value > 6544")
refute_empty results
results = FactValue.search_for("name = memory_mb AND value > 6544")
refute_empty results
end

describe '.my_facts' do
let(:target_host) { FactoryGirl.create(:host, :with_hostgroup, :with_facts) }
let(:other_host) { FactoryGirl.create(:host, :with_hostgroup, :with_facts) }
Expand Down
16 changes: 16 additions & 0 deletions test/unit/host_test.rb
Expand Up @@ -1358,6 +1358,22 @@ def teardown
assert_equal ["Common", "Common/db"].sort, hosts.map { |h| h.hostgroup.title }.sort
end

test "can search hosts by numeric and string facts" do
host = FactoryGirl.create(:host, :hostname => 'num001.example.com')
host.import_facts({:architecture => "x86_64", :interfaces => 'eth0', :operatingsystem => 'RedHat-test', :operatingsystemrelease => '6.2',:memory_mb => "64498",:custom_fact => "find_me"})

hosts = Host::Managed.search_for("facts.memory_mb > 112889")
assert_equal hosts.count, 0

hosts = Host::Managed.search_for("facts.memory_mb > 6544")
assert_equal hosts.count, 1
assert_equal ["num001.example.com"], hosts.map { |h| h.name }.sort

hosts = Host::Managed.search_for("facts.custom_fact = find_me")
assert_equal hosts.count, 1
assert_equal ["num001.example.com"], hosts.map { |h| h.name }.sort
end

test "non-admin user with edit_hosts permission can update interface" do
@one = users(:one)
# add permission for user :one
Expand Down

0 comments on commit 3f8e6c3

Please sign in to comment.