Project

General

Profile

Download (2.53 KB) Statistics
| Branch: | Tag: | Revision:
class FactImporter
delegate :logger, :to => :Rails
attr_reader :counters

def self.importer_for(type)
importers[type.to_sym] || importers[:puppet]
end

def self.importers
@importers ||= { :puppet => PuppetFactImporter }
end

def self.register_fact_importer(key, klass)
importers[key.to_sym] = klass
end

def initialize(host, facts = {})
@host = host
@facts = normalize(facts)
@counters = {}
end

# expect a facts hash
def import!
delete_removed_facts
add_new_facts
update_facts

logger.info("Import facts for '#{host}' completed. Added: #{counters[:added]}, Updated: #{counters[:updated]}, Deleted #{counters[:deleted]} facts")
end

# to be defined in children
def fact_name_class
raise NotImplementedError
end

private
attr_reader :host, :facts

def delete_removed_facts
to_delete = host.fact_values.joins(:fact_name).where('fact_names.name NOT IN (?)', facts.keys)
# N+1 DELETE SQL, but this would allow us to use callbacks (e.g. auditing) when deleting.
deleted = to_delete.destroy_all
@counters[:deleted] = deleted.size

@db_facts = nil
logger.debug("Merging facts for '#{host}': deleted #{counters[:deleted]} facts")
end

def add_new_facts
facts_to_create = facts.keys - db_facts.keys
# if the host does not exists yet, we don't have an host_id to use the fact_values table.
if facts_to_create.present?
method = host.new_record? ? :build : :create!
fact_names = fact_name_class.maximum(:id, :group => 'name')
facts_to_create.each do |name|
host.fact_values.send(method, :value => facts[name],
:fact_name_id => fact_names[name] || fact_name_class.create!(:name => name).id)
end
end

@counters[:added] = facts_to_create.size
logger.debug("Merging facts for '#{host}': added #{@counters[:added]} facts")
end

def update_facts
facts_to_update = []
db_facts.each { |name, fv| facts_to_update << [facts[name], fv] if fv.value != facts[name] }

@counters[:updated] = facts_to_update.size
return logger.debug("No facts update required for #{host}") if facts_to_update.empty?

logger.debug("Merging facts for '#{host}': updated #{@counters[:updated]} facts")

facts_to_update.each do |new_value, fv|
fv.update_attribute(:value, new_value)
end
end

def normalize(facts)
facts.keep_if { |k, v| v.present? && v.is_a?(String) }
end

def db_facts
@db_facts ||= host.fact_values.includes(:fact_name).index_by(&:name)
end

end
(5-5/15)