|
module Classification
|
|
module ValuesHashQuery
|
|
class << self
|
|
# Calculates values for a given set of keys and host, but returns only values that were not set
|
|
# on the host itself (inherited from different properties like hostgroup, interface e.t.c.).
|
|
# See values_hash for more info.
|
|
def inherited_values(host, keys)
|
|
values_hash host, keys, :skip_fqdn => true
|
|
end
|
|
|
|
# Calculates values for a given set of keys and a host supplied in the constructor.
|
|
# Inputs:
|
|
# keys: an ActiveRecord::Scope with a set of LookupKey instances
|
|
# options:
|
|
# :skip_fqdn => true, if there is no need to calculate values specified directly for the host.
|
|
# returns: ClassificationResult instance.
|
|
def values_hash(host, keys, options = {})
|
|
lookup_values_cache = lookup_values(host, keys)
|
|
values = Hash.new { |h, k| h[k] = {} }
|
|
keys.each do |key|
|
|
value = calculate_value(key, lookup_values_cache, options)
|
|
values[key.id][key.key] = value if value.present?
|
|
end
|
|
Classification::ClassificationResult.new(values, host)
|
|
end
|
|
|
|
private
|
|
|
|
def lookup_values(host, keys)
|
|
LookupValue.where(:match => Classification::MatchesGenerator.matches(host, keys)).where(:lookup_key_id => keys).includes(:lookup_key).to_a
|
|
end
|
|
|
|
def calculate_value(key, lookup_values_cache, options)
|
|
lookup_values_for_key = lookup_values_cache.select{|i| i.lookup_key_id == key.id}
|
|
sorted_lookup_values = sort_lookup_values(key, lookup_values_for_key)
|
|
value = nil
|
|
if key.merge_overrides
|
|
value = merged_value(key, sorted_lookup_values, options)
|
|
else
|
|
value = update_generic_matcher(sorted_lookup_values, options)
|
|
end
|
|
|
|
value
|
|
end
|
|
|
|
def merged_value(key, sorted_lookup_values, options)
|
|
default = key.merge_default ? key.default_value : nil
|
|
case key.key_type
|
|
when "array"
|
|
value = update_array_matcher(default, key.avoid_duplicates, sorted_lookup_values, options)
|
|
when "hash"
|
|
value = update_hash_matcher(default, sorted_lookup_values, options)
|
|
else
|
|
raise "merging enabled for non mergeable key #{key.key}"
|
|
end
|
|
|
|
value
|
|
end
|
|
|
|
def sort_lookup_values(key, lookup_values)
|
|
lookup_values.sort_by do |lv|
|
|
matcher_key, matcher_value = split_matcher(lv)
|
|
# prefer matchers in order of the path, then more specific matches (i.e. hostgroup children)
|
|
[key.path.index(matcher_key.chomp(',')), -1 * matcher_value.length]
|
|
end
|
|
end
|
|
|
|
def split_matcher(lookup_value)
|
|
matcher_key = ''
|
|
matcher_value = ''
|
|
|
|
lookup_value.match.split(LookupKey::KEY_DELM).each do |match_key|
|
|
element = match_key.split(LookupKey::EQ_DELM).first
|
|
matcher_key += element + ','
|
|
if element == 'hostgroup'
|
|
matcher_value = match_key.split(LookupKey::EQ_DELM).last
|
|
end
|
|
end
|
|
|
|
[matcher_key, matcher_value]
|
|
end
|
|
|
|
def get_element_and_element_name(lookup_value)
|
|
element = ''
|
|
element_name = ''
|
|
lookup_value.match.split(LookupKey::KEY_DELM).each do |match_key|
|
|
lv_element, lv_element_name = match_key.split(LookupKey::EQ_DELM)
|
|
element += lv_element + ','
|
|
element_name += lv_element_name + ','
|
|
end
|
|
[element.chomp(','), element_name.chomp(',')]
|
|
end
|
|
|
|
def update_generic_matcher(lookup_values, options)
|
|
computed_lookup_value = nil
|
|
lookup_values.each do |lookup_value|
|
|
element, element_name = get_element_and_element_name(lookup_value)
|
|
next if (options[:skip_fqdn] && element == "fqdn")
|
|
computed_lookup_value = compute_lookup_value(lookup_value, element, element_name)
|
|
computed_lookup_value[:managed] = lookup_value.omit if lookup_value.lookup_key.puppet?
|
|
break
|
|
end
|
|
computed_lookup_value
|
|
end
|
|
|
|
def compute_lookup_value(lookup_value, element, element_name)
|
|
value_method = %w(yaml json).include?(lookup_value.lookup_key.key_type) ? :value_before_type_cast : :value
|
|
{
|
|
:value => lookup_value.send(value_method),
|
|
:element => element,
|
|
:element_name => element_name
|
|
}
|
|
end
|
|
|
|
def update_array_matcher(default, should_avoid_duplicates, lookup_values, options)
|
|
values, elements, element_names = set_defaults(default, [])
|
|
|
|
lookup_values.each do |lookup_value|
|
|
element, element_name = get_element_and_element_name(lookup_value)
|
|
next if skip_value?(element, lookup_value, options)
|
|
elements << element
|
|
element_names << element_name
|
|
|
|
values = accumulate_value(values, lookup_value, should_avoid_duplicates)
|
|
end
|
|
|
|
return nil unless values.present?
|
|
{:value => values, :element => elements, :element_name => element_names}
|
|
end
|
|
|
|
def skip_value?(element, lookup_value, options)
|
|
((options[:skip_fqdn] && element == "fqdn") || lookup_value.omit)
|
|
end
|
|
|
|
def accumulate_value(values, lookup_value, should_avoid_duplicates)
|
|
if should_avoid_duplicates
|
|
values |= lookup_value.value
|
|
else
|
|
values += lookup_value.value
|
|
end
|
|
values
|
|
end
|
|
|
|
def set_defaults(default, empty_value)
|
|
return [empty_value, [], []] if default.nil?
|
|
|
|
[default, [s_("LookupKey|Default value")], [s_("LookupKey|Default value")]]
|
|
end
|
|
|
|
def update_hash_matcher(default, lookup_values, options)
|
|
values, elements, element_names = set_defaults(default, {})
|
|
|
|
# to make sure seep merge overrides by priority, putting in the values with the lower priority first
|
|
# and then merging with higher priority
|
|
lookup_values.reverse_each do |lookup_value|
|
|
element, element_name = get_element_and_element_name(lookup_value)
|
|
next if skip_value?(element, lookup_value, options)
|
|
elements << element
|
|
element_names << element_name
|
|
values.deep_merge!(lookup_value.value)
|
|
end
|
|
|
|
return nil unless values.present?
|
|
{:value => values, :element => elements, :element_name => element_names}
|
|
end
|
|
end
|
|
end
|
|
end
|