Project

General

Profile

Bug #405 » 0001-Fixes-405-and-Fixes-349-Adds-support-to-2.6.x-report.patch

Ohad Levy, 10/20/2010 05:09 PM

View differences:

app/models/log.rb
class Log < ActiveRecord::Base
belongs_to :message
belongs_to :source
belongs_to :report
validates_presence_of :message_id, :source_id, :report_id, :level_id
LEVELS = [:debug, :info, :notice, :warning, :err, :alert, :emerg, :crit]
def to_s
"#{source} #{message}"
end
def level= l
write_attribute(:level_id, LEVELS.index(l))
end
def level
LEVELS[level_id]
end
end
app/models/message.rb
class Message < ActiveRecord::Base
has_many :reports, :through => :logs
has_many :logs
validates_presence_of :value
def to_s
value
end
end
app/models/report.rb
class Report < ActiveRecord::Base
belongs_to :host
serialize :log, Puppet::Transaction::Report
validates_presence_of :log, :host_id, :reported_at, :status
has_many :messages, :through => :logs, :dependent => :destroy
has_many :sources, :through => :logs, :dependent => :destroy
has_many :logs, :dependent => :destroy
validates_presence_of :host_id, :reported_at, :status
validates_uniqueness_of :reported_at, :scope => :host_id
METRIC = %w[applied restarted failed failed_restarts skipped]
......
return type.nil? ? h : h[type]
end
# extracts serialized metrics and keep them as a hash_with_indifferent_access
def metrics
YAML.load(read_attribute(:metrics)).with_indifferent_access
end
# serialize metrics as YAML
def metrics= m
write_attribute(:metrics,m.to_yaml) unless m.nil?
end
# generate dynamically methods for all metrics
# e.g. Report.last.applied
METRIC.each do |method|
......
end
def config_retrieval
t = validate_meteric("time", :config_retrieval)
t.round_with_precision(2) if t
metrics[:time][:config_retrieval].round_with_precision(2)
end
def runtime
t = validate_meteric("time", :total)
t.round_with_precision(2) if t
(metrics[:time][:total] || metrics[:time].values.sum).round_with_precision(2)
end
#imports a YAML report into database
......
# parse report metrics
raise "Invalid report: can't find metrics information for #{report.host} at #{report.id}" if report.metrics.nil?
# Is this a pre 2.6.x report format?
@pre26 = !report.instance_variables.include?("@resource_statuses")
# convert report status to bit field
st = calc_status(metrics_to_hash(report))
......
host.save_with_validation(false)
# and save our report
self.create! :host => host, :reported_at => report.time.utc, :log => report, :status => st
r = self.create!(:host => host, :reported_at => report.time.utc, :status => st, :metrics => self.m2h(report.metrics))
# Store all Puppet message logs
r.import_log_messages report
return r
rescue Exception => e
logger.warn "Failed to process report for #{report.host} due to:#{e}"
false
......
status = conditions[:status]
cond = "created_at < \'#{(Time.now.utc - timerange).to_formatted_s(:db)}\'"
cond += " and status = #{status}" unless status.nil?
# delete the reports
count = Report.delete_all(cond)
# delete the reports, must use destroy_all vs. delete_all because of assoicated logs and METRIC
count = Report.destroy_all(cond)
logger.info Time.now.to_s + ": Expired #{count} Reports"
return count
end
......
counter
end
protected
def import_log_messages report
report.logs.each do |r|
message = Message.find_or_create_by_value r.message
source = Source.find_or_create_by_value r.source
log = Log.create :message_id => message.id, :source_id => source.id, :report_id => self.id, :level => r.level
log.errors.empty?
end
end
private
# Converts metrics form Puppet report into a hash
# this hash is required by the calc_status method
def self.metrics_to_hash(report)
resources = report.metrics["resources"]
report_status = {}
metrics = report.metrics.with_indifferent_access
# find our metric values
METRIC.each { |m| report_status[m] = resources[m.to_sym] }
METRIC.each do |m|
if @pre26
report_status[m] = metrics["resources"][m]
else
h=translate_metrics_to26(m)
report_status[m] = metrics[h[:type]][h[:name]]
end
report_status[m] ||= 0
end
# special fix for false warning about skips
# sometimes there are skip values, but there are no error messages, we ignore them.
if report_status["skipped"] > 0 and ((report_status.values.sum) - report_status["skipped"] == report.logs.size)
report_status["skipped"] = 0
end
end unless report_status["skipped"].nil?
return report_status
end
# return all metrics as a hash
def self.m2h metrics
h = {}
metrics.each do |title, mtype|
h[mtype.name] ||= {}
mtype.values.each{|m| h[mtype.name].merge!({m[0] => m[2]})}
end
return h
end
# converts a hash into a bit field
# expects a metrics_to_hash kind of hash
def self.calc_status (hash = {})
......
nil
end
# The metrics layout has changed in Puppet 2.6.x release,
# this method attempts to align the bit value metrics and the new name scheme in 2.6.x
# returns a hash of { :type => "metric type", :name => "metric_name"}
def self.translate_metrics_to26 metric
case metric
when "applied"
{ :type => "changes", :name => :total}
else
{ :type => "resources", :name => metric.to_sym}
end
end
end
app/models/source.rb
class Source < ActiveRecord::Base
has_many :reports, :through => :logs
has_many :logs
validates_presence_of :value
def to_s
value
end
end
app/views/host_mailer/error_state.text.html.erb
<title>Puppet error from <%= @host.to_label %></title>
</head>
<body style="background-color: #ffffff;">
<%= render :partial => 'reports/output', :locals => {:logs => @report.log.logs } %>
<%= render :partial => 'reports/output', :locals => {:logs => @report.logs } %>
</body>
</html>
app/views/reports/show.rhtml
<% title @report.host.name %>
<% title @report.host.name -%>
Reported at <%= @report.reported_at.getlocal %>, which is <b><%= time_ago_in_words(@report.reported_at) %> ago</b>
<% if @offset > 100 -%>
<div class="flash error">
......
</div>
<% end -%>
<% if @report.log.logs.size > 0 -%>
<%= render :partial => 'output', :locals => { :logs => @report.log.logs} %>
<% if @report.logs.size > 0 -%>
<%= render :partial => 'output', :locals => { :logs => @report.logs} %>
<% end -%>
<div class="flash">
......
<td> <b>Metrics</b></td>
<td>
<table style="width:100%;">
<% @report.log.metrics["time"].values.each do |name, label, value|-%>
<% if label == 'Total' then -%>
<% @report.metrics["time"].each do |label, value|-%>
<% if label.to_s =~ /total/i then -%>
<% @totaltime = value -%>
<% next -%>
<% end -%>
......
<td> <%= value.round_with_precision(4) rescue "N/A"%> </td>
</tr>
<% end %>
<tr><td class="last_row">Total</td><td class="last_row"><%= h @totaltime.round_with_precision(4) rescue "N/A"%></td></tr>
<tr><td class="last_row">Total</td><td class="last_row"><%= h (@totaltime || @report.runtime).round_with_precision(4) rescue "N/A"%></td></tr>
</table>
</td>
</tr>
db/migrate/20101018120548_create_messages.rb
class CreateMessages < ActiveRecord::Migration
def self.up
create_table :messages do |t|
t.text :value
end
if ActiveRecord::Base.connection.instance_values["config"][:adapter] == "mysql"
execute "ALTER TABLE messages ENGINE = MYISAM"
execute "ALTER TABLE messages ADD FULLTEXT (value)"
else
add_index :messages, :value
end
end
def self.down
remove_index :messages, :value
drop_table :messages
end
end
db/migrate/20101018120603_create_sources.rb
class CreateSources < ActiveRecord::Migration
def self.up
create_table :sources do |t|
t.text :value
end
if ActiveRecord::Base.connection.instance_values["config"][:adapter] == "mysql"
execute "ALTER TABLE sources ENGINE = MYISAM"
execute "ALTER TABLE sources ADD FULLTEXT (value)"
else
add_index :sources, :value
end
end
def self.down
remove_index :sources, :value
drop_table :sources
end
end
db/migrate/20101018120621_create_logs.rb
class CreateLogs < ActiveRecord::Migration
def self.up
create_table :logs do |t|
t.integer :source_id
t.integer :message_id
t.integer :report_id
t.integer :level_id
t.timestamps
end
add_index :logs, :report_id
add_index :logs, :message_id
add_index :logs, :level_id
end
def self.down
remove_index :logs, :level_id
remove_index :logs, :report_id
remove_index :logs, :message_id
drop_table :logs
end
end
db/migrate/20101019122857_add_metrics_to_report.rb
class AddMetricsToReport < ActiveRecord::Migration
def self.up
add_column :reports, :metrics, :text
end
def self.down
remove_column :reports, :metrics
end
end
db/migrate/20101019183859_convert_reports.rb
class ConvertReports < ActiveRecord::Migration
def self.up
say "About to convert all of the #{Report.count} reports log field into a more DB optimized way... this might take a while....."
Report.all.each do |report|
case report.log.class.to_s
when "Puppet::Transaction::Report"
log = report.log
when "String"
log = YAML.load(report.log)
else
# this report might have been processed already, skipping
next
end
# Is this a pre 2.6.x report format?
pre26 = !report.instance_variables.include?("@resource_statuses")
# Recalcuate the status field if this report is from a 2.6.x puppet client
report.status = Report.calc_status(Report.metrics_to_hash(log)) unless pre26
report.metrics = Report.m2h(log.metrics).with_indifferent_access
report.import_log_messages(log)
report.log = "" # not really needed, but this way the db can reuse some space instead of claim new one.
report.save
end
remove_column :reports, :log
end
def self.down
add_column :reports, :log, :text
say "cant recreate the data, import it again"
end
end
test/fixtures/logs.yml
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
one:
source_id: 1
message_id: 1
report_id: 1
two:
source_id: 1
message_id: 1
report_id: 1
test/fixtures/messages.yml
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
one:
value: MyText
two:
value: MyText
test/fixtures/sources.yml
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
one:
value: MyText
two:
value: MyText
test/unit/log_test.rb
require 'test_helper'
class LogTest < ActiveSupport::TestCase
# Replace this with your real tests.
test "the truth" do
assert true
end
end
test/unit/message_test.rb
require 'test_helper'
class MessageTest < ActiveSupport::TestCase
# Replace this with your real tests.
test "the truth" do
assert true
end
end
test/unit/source_test.rb
require 'test_helper'
class SourceTest < ActiveSupport::TestCase
# Replace this with your real tests.
test "the truth" do
assert true
end
end
(2-2/2)