diff options
Diffstat (limited to 'lib/puppet')
-rw-r--r-- | lib/puppet/client.rb | 7 | ||||
-rw-r--r-- | lib/puppet/event.rb | 2 | ||||
-rw-r--r-- | lib/puppet/log.rb | 10 | ||||
-rw-r--r-- | lib/puppet/metric.rb | 253 | ||||
-rw-r--r-- | lib/puppet/transaction.rb | 1 | ||||
-rw-r--r-- | lib/puppet/type.rb | 36 |
6 files changed, 303 insertions, 6 deletions
diff --git a/lib/puppet/client.rb b/lib/puppet/client.rb index f1140ea4d..06808a4b4 100644 --- a/lib/puppet/client.rb +++ b/lib/puppet/client.rb @@ -10,6 +10,7 @@ require 'puppet/type' require 'puppet/fact' require 'puppet/transaction' require 'puppet/transportable' +require 'puppet/metric' require 'http-access2' require 'soap/rpc/driver' require 'soap/rpc/httpserver' @@ -80,6 +81,12 @@ module Puppet #transaction = Puppet::Transaction.new(objects) transaction.toplevel = true transaction.evaluate + Puppet::Metric.gather + Puppet::Metric.tally + Metric.store + if @@config[:rrdgraph] == true + #Metric.store + end self.shutdown end diff --git a/lib/puppet/event.rb b/lib/puppet/event.rb index a8d818b39..86205591d 100644 --- a/lib/puppet/event.rb +++ b/lib/puppet/event.rb @@ -107,7 +107,7 @@ module Puppet @object = args[:object] @transaction = args[:transaction] - Puppet.warning "New Event: '%s' => '%s'" % + Puppet.info "%s: %s" % [@object,@event] # initially, just stuff all instances into a central bucket diff --git a/lib/puppet/log.rb b/lib/puppet/log.rb index 9cfb80193..5f77e623c 100644 --- a/lib/puppet/log.rb +++ b/lib/puppet/log.rb @@ -47,12 +47,14 @@ module Puppet def Log.create(level,*ary) msg = ary.join(" ") - if @@levels.index(@@loglevel) >= @@levels.index(level) - Puppet::Log.new( + if @@levels.index(level) >= @@loglevel + return Puppet::Log.new( :level => level, :source => "Puppet", :message => msg ) + else + return nil end end @@ -86,11 +88,11 @@ module Puppet level = level.intern end - unless @@loglevels.include?(level) + unless @@levels.include?(level) raise "Invalid loglevel %s" % level end - @@loglevel = @@loglevels.index(level) + @@loglevel = @@levels.index(level) end def Log.newmessage(msg) diff --git a/lib/puppet/metric.rb b/lib/puppet/metric.rb new file mode 100644 index 000000000..d9696b601 --- /dev/null +++ b/lib/puppet/metric.rb @@ -0,0 +1,253 @@ +#!/usr/local/bin/ruby -w + +# $Id$ + +# included so we can test object types +require 'puppet' + +module Puppet + class Metric + def Metric.init + @@typemetrics = Hash.new { |typehash,type| + typehash[type] = Hash.new(0) + } + + @@eventmetrics = Hash.new(0) + + @@metrics = {} + end + + def Metric.clear + @@metrics = {} + @@eventmetrics = nil + @@typemetrics = nil + end + + def Metric.gather + metrics = Metric.init + + # first gather stats about all of the types + Puppet::Type.eachtype { |type| + type.each { |instance| + metrics[type][:total] += 1 + if instance.managed + metrics[type][:managed] += 1 + end + } + } + + # the rest of the metrics are injected directly by type.rb + end + + def Metric.add(type,instance,metric,count) + return unless defined? @@typemetrics + case metric + when :outofsync: + @@typemetrics[type][metric] += count + when :changes: + @@typemetrics[type][:changed] += 1 + @@typemetrics[type][:totalchanges] += count + else + raise "Unknown metric %s" % metric + end + end + + # we're currently throwing away the type and instance information + def Metric.addevents(type,instance,events) + return unless defined? @@eventmetrics + events.each { |event| + @@eventmetrics[event] += 1 + } + end + + def Metric.each + @@metrics.each { |name,metric| + yield metric + } + end + + def Metric.load(ary) + @@typemetrics = ary[0] + @@eventmetrics = ary[1] + end + + def Metric.graph(range = nil) + @@metrics.each { |name,metric| + metric.graph(range) + } + end + + def Metric.store(time = nil) + require 'RRD' + unless time + time = Time.now.to_i + end + @@metrics.each { |name,metric| + metric.store(time) + } + end + + def Metric.tally + type = Metric.new("typecount","Types") + type.newvalue("Number",@@typemetrics.length) + + metrics = { + :total => "Instances", + :managed => "Managed Instances", + :outofsync => "Out of Sync Instances", + :changed => "Changed Instances", + :totalchanges => "Total Number of Changes", + } + total = Hash.new(0) + @@typemetrics.each { |type,instancehash| + name = type.name.to_s + instmet = Metric.new("type-" + name,name.capitalize) + metrics.each { |symbol,label| + instmet.newvalue(symbol.to_s,instancehash[symbol],label) + total[symbol] += instancehash[symbol] + } + } + + totalmet = Metric.new("typetotals","Type Totals") + metrics.each { |symbol,label| + totalmet.newvalue(symbol.to_s,total[symbol],label) + } + + eventmet = Metric.new("events") + total = 0 + @@eventmetrics.each { |event,count| + event = event.to_s + # add the specific event as a value, with the label being a + # capitalized version with s/_/ /g + eventmet.newvalue( + event, + count, + event.capitalize.gsub(/_/,' ') + ) + + total += count + } + eventmet.newvalue("total",total,"Event Total") + end + + attr_accessor :type, :name, :value, :label + + + def create + dir = Puppet[:rrddir] + unless dir + raise "Cannot store metrics unless RRDDir is set" + end + + unless FileTest.exist?(dir) + tmp = dir.sub(/^\//,'') + path = [File::SEPARATOR] + tmp.split(File::SEPARATOR).each { |dir| + path.push dir + unless FileTest.exist?(File.join(path)) + Dir.mkdir(File.join(path)) + end + } + end + + unless FileTest.directory?(dir) + raise "%s must be a directory" % dir + end + + path = self.path + args = [ + path, + "--start", Time.now.to_i - 5, + "--step", "300", # XXX Defaulting to every five minutes, but prob bad + ] + + @values.each { |value| + args.push "DS:%s:GAUGE:600:U:U" % value[0] + } + args.push "RRA:AVERAGE:0.5:1:300" + + begin + RRD.create(*args) + rescue => detail + raise "Could not create RRD file %s: %s" % [path,detail] + end + end + + def initialize(name,label = nil) + @name = name + if label + @label = label + else + @label = name.capitalize + end + + @values = [] + if @@metrics.include?(self.name) + raise "Somehow created two metrics with name %s" % self.name + else + @@metrics[self.name] = self + end + end + + def newvalue(name,value,label = nil) + unless label + label = name.capitalize + end + @values.push [name,label,value] + end + + def path + return File.join(Puppet[:rrddir],@name + ".rrd") + end + + def graph(range = nil) + args = [self.path.sub(/rrd$/,"png")] + args.push("--title",self.label) + args.push("--imgformat","PNG") + args.push("--interlace") + colorstack = %w{#ff0000 #00ff00 #0000ff #099000 #000990 #f00990} + i = 0 + defs = [] + lines = [] + @values.zip(colorstack).each { |value,color| + next if value.nil? + # this actually uses the data label + defs.push("DEF:%s=%s:%s:AVERAGE" % [value[0],self.path,value[0]]) + lines.push("LINE3:%s%s:%s" % [value[0],color,value[1]]) + } + args << defs + args << lines + args.flatten! + if range + args.push("--start",range[0],"--end",range[1]) + end + + begin + RRD.graph(*args) + rescue => detail + Puppet.err "Failed to graph %s: %s" % [self.name,detail] + exit + end + end + + def store(time) + unless FileTest.exists?(File.join(Puppet[:rrddir],@name + ".rrd")) + self.create + end + + # XXX this is not terribly error-resistant + args = [time] + @values.each { |value| + args.push value[2] + } + arg = args.join(":") + Puppet.debug "Updating %s with %s" % [self.name,arg] + begin + RRD.update(self.path,args.join(":")) + rescue => detail + Puppet.err "Failed to update %s: %s" % [self.name,detail] + exit + end + end + end +end diff --git a/lib/puppet/transaction.rb b/lib/puppet/transaction.rb index 1a3a7361a..03f965a76 100644 --- a/lib/puppet/transaction.rb +++ b/lib/puppet/transaction.rb @@ -23,6 +23,7 @@ class Transaction # failed def Transaction.init @@failures = Hash.new(0) + Puppet::Metric.init @@changed = [] end #--------------------------------------------------------------- diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index 81c20ceec..6d74e4ef3 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -6,6 +6,7 @@ require 'puppet' require 'puppet/element' require 'puppet/event' +require 'puppet/metric' require 'puppet/type/state' @@ -273,6 +274,15 @@ class Type < Puppet::Element #--------------------------------------------------------------- #--------------------------------------------------------------- + def Type.each + return unless defined? @objects + @objects.each { |instance| + yield instance + } + end + #--------------------------------------------------------------- + + #--------------------------------------------------------------- # all objects total def Type.push(object) @@allobjects.push object @@ -581,11 +591,13 @@ class Type < Puppet::Element #--------------------------------------------------------------- def sync - self.collect { |child| + events = self.collect { |child| child.sync }.reject { |event| ! (event.is_a?(Symbol) or event.is_a?(String)) }.flatten + + Puppet::Metric.addevents(self.class,self,events) end #--------------------------------------------------------------- @@ -602,6 +614,23 @@ class Type < Puppet::Element #--------------------------------------------------------------- #--------------------------------------------------------------- + def managed + if defined? @managed + return @managed + else + self.states.each { |state| + if state.should + @managed = true + else + @managed = false + end + } + end + return @managed + end + #--------------------------------------------------------------- + + #--------------------------------------------------------------- def states Puppet.debug "%s has %s states" % [self,@states.length] tmpstates = [] @@ -686,6 +715,8 @@ class Type < Puppet::Element # this only operates on states, not states + children self.retrieve unless self.insync? + # add one to the number of out-of-sync instances + Puppet::Metric.add(self.class,self,:outofsync,1) changes << self.states.find_all { |state| ! state.insync? }.collect { |state| @@ -697,6 +728,9 @@ class Type < Puppet::Element #self.collect { |child| # child.evaluate #} + + # now record how many changes we've resulted in + Puppet::Metric.add(self.class,self,:changes,changes.length) return changes end #--------------------------------------------------------------- |