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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
module Puppet
class Server
# A simple server for triggering a new run on a Puppet client.
class Report < Handler
@interface = XMLRPC::Service::Interface.new("puppetreports") { |iface|
iface.add_method("string report(array)")
}
Puppet.setdefaults(:reporting,
:reportdirectory => {:default => "$vardir/reports",
:mode => 0750,
:owner => "$user",
:group => "$group",
:desc => "The directory in which to store reports received from the
client. Each client gets a separate subdirectory."},
:reports => ["none",
"The list of reports to generate. All reports are looked for
in puppet/reports/<name>.rb, and multiple report names should be
comma-separated (whitespace is okay)."
]
)
@reports = {}
@reportloader = Puppet::Autoload.new(self, "puppet/reports")
class << self
attr_reader :hooks
end
def self.reportmethod(report)
"report_" + report.to_s
end
# Add a hook for processing reports.
def self.newreport(name, &block)
name = name.intern if name.is_a? String
method = reportmethod(name)
# We want to define a method so that reports can use 'return'.
define_method(method, &block)
@reports[name] = method
end
# Load a report.
def self.report(name)
name = name.intern if name.is_a? String
unless @reports.include? reportmethod(name)
@reportloader.load(name)
unless @reports.include? name
Puppet.warning(
"Loaded report file for %s but report was not defined" %
name
)
return nil
end
end
@reports[name]
end
def initialize(*args)
super
Puppet.config.use(:reporting)
Puppet.config.use(:metrics)
end
# Dynamically create the report methods as necessary.
def method_missing(name, *args)
if name.to_s =~ /^report_(.+)$/
if self.class.report($1)
send(name, *args)
else
super
end
else
super
end
end
# Accept a report from a client.
def report(report, client = nil, clientip = nil)
# We need the client name for storing files.
client ||= Facter["hostname"].value
# Unescape the report
unless @local
report = CGI.unescape(report)
end
process(report)
# We don't want any tracking back in the fs. Unlikely, but there
# you go.
client.gsub("..",".")
dir = File.join(Puppet[:reportdirectory], client)
unless FileTest.exists?(dir)
mkclientdir(client, dir)
end
# Now store the report.
now = Time.now.gmtime
name = %w{year month day hour min}.collect do |method|
# Make sure we're at least two digits everywhere
"%02d" % now.send(method).to_s
end.join("") + ".yaml"
file = File.join(dir, name)
begin
File.open(file, "w", 0640) do |f|
f.puts report
end
rescue => detail
if Puppet[:debug]
puts detail.backtrace
end
Puppet.warning "Could not write report for %s at %s: %s" %
[client, file, detail]
end
# Our report is in YAML
return file
end
private
def mkclientdir(client, dir)
Puppet.config.setdefaults("reportclient-#{client}",
"clientdir-#{client}" => { :default => dir,
:mode => 0750,
:owner => "$user",
:group => "$group"
}
)
Puppet.config.use("reportclient-#{client}")
end
# Process the report using all of the existing hooks.
def process(report)
return if Puppet[:reports] == "none"
# First convert the report to real objects
begin
report = YAML.load(report)
rescue => detail
Puppet.warning "Could not load report: %s" % detail
return
end
Puppet[:reports].split(/\s*,\s*/).each do |name|
method = self.class.report(name)
if respond_to? method
Puppet.info "Processing report %s" % name
begin
send(method, report)
rescue NoMethodError => detail
Puppet.warning "No report named '%s'" % name
rescue => detail
if Puppet[:debug]
puts detail.backtrace
end
Puppet.err "Report %s failed: %s" %
[name, detail]
end
else
Puppet.warning "No report named '%s'" % name
end
end
end
end
end
end
# $Id$
|