-
Notifications
You must be signed in to change notification settings - Fork 109
/
fact_importer.rb
100 lines (88 loc) · 3.46 KB
/
fact_importer.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
module ForemanAnsible
# Override methods from Foreman app/services/fact_importer so that Ansible
# facts are recognized in Foreman as ForemanAnsible facts. It supports
# nested facts.
#
# Only relevant for 1.12 and lower versions
class FactImporter < ::FactImporter
def fact_name_class
ForemanAnsible::FactName
end
def initialize(host, facts = {})
# Try to assign these facts to the correct host as per the facts say
# If that host isn't created yet, the host parameter will contain it
@host = Host.find_by(:name => facts[:ansible_facts][:ansible_fqdn] ||
facts[:ansible_facts][:fqdn]) ||
host
@facts = normalize(facts[:ansible_facts])
@original_facts = FactSparser.unsparse(facts[:ansible_facts])
@counters = {}
end
private
def add_new_facts
@counters[:added] = 0
add_missing_facts(FactSparser.unsparse(@original_facts))
logger.debug(
"Merging facts for '#{host}': added #{@counters[:added]} facts"
)
end
def add_missing_facts(imported_facts, parent = nil, prefix = '')
imported_facts.reject! { |_fact_name, fact_value| fact_value.nil? }
imported_facts.each do |imported_name, imported_value|
fact_fqn = fact_fqn(imported_name, prefix)
fact_name = find_or_create_fact_name(fact_fqn, parent, imported_value)
add_fact_value(imported_value, fact_name)
add_compose_fact(imported_value, fact_name, fact_fqn)
end
end
def add_compose_fact(imported_values, fact_name, fact_fqn)
if imported_values.is_a?(Hash)
add_missing_facts(imported_values, fact_name, fact_fqn)
elsif imported_values.is_a?(Array)
imported_values.each do |imported_value|
next unless imported_value.is_a?(Hash)
add_missing_facts(imported_value, fact_name, fact_fqn)
end
end
end
def missing_facts
db_fact_names = if db_facts.is_a? Hash
db_facts.keys
else
db_facts
end
# In Foreman versions prior to 1.14, the db_facts key
# used to be a hash. Now it's an ActiveRecord::AssociationRelation
@missing_facts ||= facts.keys +
FactSparser.sparse(@original_facts).keys -
db_fact_names
end
# Returns pairs [id, fact_name]
def fact_names
fact_name_class.group('name').maximum(:id)
end
# Fact fully qualified name contains an unambiguous name for a fact
# e.g: ansible_lo::ipv6, ansible_virbr0::active
def fact_fqn(name, prefix)
prefix.empty? ? name : prefix + FactName::SEPARATOR + name
end
def find_or_create_fact_name(name, parent, fact_value)
return fact_name_class.find(fact_names[name]) if fact_names[name].present?
fact_name_class.create!(:name => name,
:parent => parent,
:compose => compose?(fact_value))
end
def add_fact_value(value, fact_name)
return unless missing_facts.include?(fact_name.name)
method = host.new_record? ? :build : :create!
host.fact_values.send(method,
:value => value.to_s,
:fact_name => fact_name)
@counters[:added] += 1
end
def compose?(fact_value)
fact_value.is_a?(Hash) ||
fact_value.is_a?(Array) && fact_value.first.is_a?(Hash)
end
end
end