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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
# included so we can test object types
require 'puppet'
# A class for handling metrics. This is currently ridiculously hackish.
class Puppet::Util::Metric
# Load the library as a feature, so we can test its presence.
Puppet.features.add :rrd, :libs => 'RRDtool'
attr_accessor :type, :name, :value, :label
attr_writer :values
attr_writer :basedir
def basedir
if defined? @basedir
@basedir
else
Puppet[:rrddir]
end
end
def create(start = nil)
Puppet.settings.use(:metrics)
start ||= Time.now.to_i - 5
@rrd = RRDtool.new(self.path)
args = []
values.each { |value|
# the 7200 is the heartbeat -- this means that any data that isn't
# more frequently than every two hours gets thrown away
args.push "DS:%s:GAUGE:7200:U:U" % [value[0]]
}
args.push "RRA:AVERAGE:0.5:1:300"
begin
@rrd.create( Puppet[:rrdinterval].to_i, start, args)
rescue => detail
raise "Could not create RRD file %s: %s" % [path,detail]
end
end
def dump
puts @rrd.info
end
def graph(range = nil)
unless Puppet.features.rrd?
Puppet.warning "RRD library is missing; cannot graph metrics"
return
end
unit = 60 * 60 * 24
colorstack = %w{#00ff00 #ff0000 #0000ff #ffff00 #ff99ff #ff9966 #66ffff #990000 #099000 #000990 #f00990 #0f0f0f #555555 #333333 #ffffff}
{:daily => unit, :weekly => unit * 7, :monthly => unit * 30, :yearly => unit * 365}.each do |name, time|
file = self.path.sub(/\.rrd$/, "-%s.png" % name)
args = [file]
args.push("--title",self.label)
args.push("--imgformat","PNG")
args.push("--interlace")
i = 0
defs = []
lines = []
#p @values.collect { |s,l| s }
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("LINE2:%s%s:%s" % [value[0],color,value[1]])
}
args << defs
args << lines
args.flatten!
if range
args.push("--start",range[0],"--end",range[1])
else
args.push("--start", Time.now.to_i - time, "--end", Time.now.to_i)
end
begin
#Puppet.warning "args = #{args}"
RRDtool.graph( args )
rescue => detail
Puppet.err "Failed to graph %s: %s" % [self.name,detail]
end
end
end
def initialize(name,label = nil)
@name = name.to_s
if label
@label = label
else
@label = name.to_s.capitalize.gsub("_", " ")
end
@values = []
end
def path
return File.join(self.basedir, @name + ".rrd")
end
def newvalue(name,value,label = nil)
unless label
label = name.to_s.capitalize.gsub("_", " ")
end
@values.push [name,label,value]
end
def store(time)
unless Puppet.features.rrd?
Puppet.warning "RRD library is missing; cannot store metrics"
return
end
unless FileTest.exists?(self.path)
self.create(time - 5)
end
@rrd ||= RRDtool.new(self.path)
# XXX this is not terribly error-resistant
args = [time]
temps = []
values.each { |value|
#Puppet.warning "value[0]: #{value[0]}; value[1]: #{value[1]}; value[2]: #{value[2]}; "
args.push value[2]
temps.push value[0]
}
arg = args.join(":")
template = temps.join(":")
begin
@rrd.update( template, [ arg ] )
#system("rrdtool updatev %s '%s'" % [self.path, arg])
rescue => detail
raise Puppet::Error, "Failed to update %s: %s" % [self.name,detail]
end
end
def values
@values.sort { |a, b| a[1] <=> b[1] }
end
end
Puppet::Metric = Puppet::Util::Metric
|