puppet 0.16.0 → 0.18.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puppet might be problematic. Click here for more details.
- data/CHANGELOG +98 -0
- data/Rakefile +5 -1
- data/bin/puppet +1 -1
- data/bin/puppetca +25 -11
- data/bin/puppetd +189 -66
- data/bin/puppetdoc +79 -62
- data/bin/puppetmasterd +93 -49
- data/bin/puppetrun +385 -0
- data/conf/redhat/client.init +5 -2
- data/conf/redhat/fileserver.conf +1 -1
- data/conf/redhat/lsb-config.patch +51 -0
- data/conf/redhat/puppet.spec +45 -18
- data/conf/redhat/puppetd.conf +32 -4
- data/conf/redhat/server.init +5 -2
- data/conf/solaris/pkginfo +7 -0
- data/conf/solaris/smf/puppetd.xml +77 -0
- data/conf/solaris/smf/puppetmasterd.xml +77 -0
- data/conf/solaris/smf/svc-puppetd +66 -0
- data/conf/solaris/smf/svc-puppetmasterd +62 -0
- data/examples/code/failers/noobjectrvalue +1 -0
- data/examples/code/snippets/deepclassheirarchy.pp +23 -0
- data/examples/code/snippets/defineoverrides.pp +17 -0
- data/examples/code/snippets/emptyexec.pp +3 -0
- data/examples/code/snippets/selectorvalues.pp +6 -1
- data/examples/code/snippets/tagged.pp +35 -0
- data/ext/ldap/puppet.schema +2 -2
- data/install.rb +4 -2
- data/lib/puppet.rb +206 -15
- data/lib/puppet/client.rb +30 -20
- data/lib/puppet/client/ca.rb +2 -2
- data/lib/puppet/client/dipper.rb +5 -9
- data/lib/puppet/client/master.rb +224 -44
- data/lib/puppet/client/pelement.rb +54 -9
- data/lib/puppet/client/proxy.rb +3 -2
- data/lib/puppet/client/reporter.rb +34 -0
- data/lib/puppet/client/runner.rb +17 -0
- data/lib/puppet/config.rb +136 -55
- data/lib/puppet/daemon.rb +59 -37
- data/lib/puppet/element.rb +2 -1
- data/lib/puppet/event.rb +14 -3
- data/lib/puppet/filetype.rb +28 -19
- data/lib/puppet/log.rb +297 -132
- data/lib/puppet/metric.rb +31 -131
- data/lib/puppet/networkclient.rb +73 -46
- data/lib/puppet/parameter.rb +49 -1
- data/lib/puppet/parsedfile.rb +32 -12
- data/lib/puppet/parser/ast.rb +6 -1
- data/lib/puppet/parser/ast/astarray.rb +32 -6
- data/lib/puppet/parser/ast/collection.rb +91 -0
- data/lib/puppet/parser/ast/compdef.rb +2 -2
- data/lib/puppet/parser/ast/component.rb +24 -11
- data/lib/puppet/parser/ast/function.rb +50 -0
- data/lib/puppet/parser/ast/hostclass.rb +70 -22
- data/lib/puppet/parser/ast/node.rb +17 -8
- data/lib/puppet/parser/ast/nodedef.rb +1 -1
- data/lib/puppet/parser/ast/objectdef.rb +28 -10
- data/lib/puppet/parser/ast/selector.rb +4 -1
- data/lib/puppet/parser/functions.rb +145 -0
- data/lib/puppet/parser/interpreter.rb +243 -86
- data/lib/puppet/parser/lexer.rb +5 -4
- data/lib/puppet/parser/parser.rb +586 -505
- data/lib/puppet/parser/scope.rb +337 -187
- data/lib/puppet/rails.rb +115 -0
- data/lib/puppet/rails/database.rb +40 -0
- data/lib/puppet/rails/host.rb +83 -0
- data/lib/puppet/rails/rails_object.rb +42 -0
- data/lib/puppet/rails/rails_parameter.rb +5 -0
- data/lib/puppet/reports/rrdgraph.rb +20 -0
- data/lib/puppet/reports/tagmail.rb +94 -0
- data/lib/puppet/server.rb +20 -4
- data/lib/puppet/server/authconfig.rb +14 -3
- data/lib/puppet/server/authstore.rb +2 -2
- data/lib/puppet/server/ca.rb +23 -11
- data/lib/puppet/server/filebucket.rb +10 -10
- data/lib/puppet/server/fileserver.rb +4 -8
- data/lib/puppet/server/master.rb +19 -22
- data/lib/puppet/server/pelement.rb +28 -16
- data/lib/puppet/server/report.rb +184 -0
- data/lib/puppet/server/runner.rb +62 -0
- data/lib/puppet/server/servlet.rb +23 -9
- data/lib/puppet/sslcertificates/ca.rb +25 -1
- data/lib/puppet/statechange.rb +34 -53
- data/lib/puppet/storage.rb +1 -2
- data/lib/puppet/transaction.rb +305 -133
- data/lib/puppet/transaction/report.rb +42 -0
- data/lib/puppet/transportable.rb +57 -33
- data/lib/puppet/type.rb +260 -127
- data/lib/puppet/type/component.rb +9 -21
- data/lib/puppet/type/cron.rb +367 -116
- data/lib/puppet/type/exec.rb +15 -16
- data/lib/puppet/type/group.rb +9 -1
- data/lib/puppet/type/nameservice.rb +2 -5
- data/lib/puppet/type/nameservice/netinfo.rb +3 -0
- data/lib/puppet/type/nameservice/objectadd.rb +23 -10
- data/lib/puppet/type/nameservice/pw.rb +16 -3
- data/lib/puppet/type/package.rb +25 -75
- data/lib/puppet/type/package/apple.rb +15 -1
- data/lib/puppet/type/package/apt.rb +37 -2
- data/lib/puppet/type/package/blastwave.rb +136 -0
- data/lib/puppet/type/package/dpkg.rb +4 -4
- data/lib/puppet/type/package/gem.rb +119 -0
- data/lib/puppet/type/package/openbsd.rb +7 -6
- data/lib/puppet/type/package/ports.rb +7 -2
- data/lib/puppet/type/package/rpm.rb +1 -1
- data/lib/puppet/type/package/sun.rb +23 -9
- data/lib/puppet/type/package/sunfreeware.rb +7 -0
- data/lib/puppet/type/package/yum.rb +16 -9
- data/lib/puppet/type/parsedtype.rb +7 -5
- data/lib/puppet/type/parsedtype/mount.rb +55 -34
- data/lib/puppet/type/parsedtype/port.rb +7 -1
- data/lib/puppet/type/parsedtype/sshkey.rb +6 -16
- data/lib/puppet/type/pfile.rb +115 -23
- data/lib/puppet/type/pfile/checksum.rb +18 -5
- data/lib/puppet/type/pfile/content.rb +2 -2
- data/lib/puppet/type/pfile/ensure.rb +3 -3
- data/lib/puppet/type/pfile/group.rb +2 -2
- data/lib/puppet/type/pfile/source.rb +28 -17
- data/lib/puppet/type/pfile/target.rb +25 -17
- data/lib/puppet/type/pfilebucket.rb +25 -6
- data/lib/puppet/type/schedule.rb +6 -6
- data/lib/puppet/type/service.rb +24 -14
- data/lib/puppet/type/service/debian.rb +1 -1
- data/lib/puppet/type/service/redhat.rb +13 -10
- data/lib/puppet/type/service/smf.rb +3 -3
- data/lib/puppet/type/state.rb +1 -2
- data/lib/puppet/type/symlink.rb +3 -4
- data/lib/puppet/type/user.rb +22 -10
- data/lib/puppet/type/yumrepo.rb +6 -1
- data/lib/puppet/type/zone.rb +595 -0
- data/lib/puppet/util.rb +58 -12
- data/test/client/client.rb +2 -2
- data/test/client/master.rb +92 -3
- data/test/client/pelement.rb +99 -0
- data/test/executables/puppetbin.rb +3 -4
- data/test/executables/puppetca.rb +3 -3
- data/test/executables/puppetd.rb +3 -3
- data/test/executables/puppetmasterd.rb +1 -5
- data/test/executables/puppetmodule.rb +2 -2
- data/test/language/ast.rb +200 -11
- data/test/language/functions.rb +245 -0
- data/test/language/interpreter.rb +155 -6
- data/test/language/lexer.rb +35 -2
- data/test/language/node.rb +48 -1
- data/test/language/parser.rb +250 -1
- data/test/language/rails.rb +105 -0
- data/test/language/scope.rb +304 -10
- data/test/language/snippets.rb +54 -5
- data/test/language/transportable.rb +60 -28
- data/test/other/config.rb +214 -1
- data/test/other/events.rb +67 -9
- data/test/other/log.rb +31 -5
- data/test/other/metrics.rb +23 -21
- data/test/other/parsedfile.rb +29 -2
- data/test/other/puppet.rb +79 -0
- data/test/other/report.rb +106 -0
- data/test/other/storage.rb +2 -2
- data/test/other/transactions.rb +128 -2
- data/test/puppet/utiltest.rb +10 -5
- data/test/puppettest.rb +193 -21
- data/test/server/authstore.rb +13 -4
- data/test/server/bucket.rb +33 -8
- data/test/server/ca.rb +44 -6
- data/test/server/master.rb +6 -7
- data/test/server/pelement.rb +15 -5
- data/test/server/report.rb +93 -0
- data/test/server/runner.rb +107 -0
- data/test/server/server.rb +28 -1
- data/test/types/cron.rb +339 -31
- data/test/types/file.rb +256 -24
- data/test/types/filebucket.rb +6 -2
- data/test/types/filesources.rb +41 -92
- data/test/types/group.rb +31 -1
- data/test/types/host.rb +2 -1
- data/test/types/mount.rb +18 -1
- data/test/types/package.rb +200 -18
- data/test/types/service.rb +5 -1
- data/test/types/sshkey.rb +2 -1
- data/test/types/symlink.rb +3 -2
- data/test/types/type.rb +180 -1
- data/test/types/user.rb +65 -27
- data/test/types/yumrepo.rb +15 -0
- data/test/types/zone.rb +437 -0
- metadata +43 -4
- data/bin/cf2puppet +0 -186
- data/conf/redhat/puppetmasterd.conf +0 -5
@@ -0,0 +1,62 @@
|
|
1
|
+
module Puppet
|
2
|
+
class Server
|
3
|
+
class MissingMasterError < RuntimeError # Cannot find the master client
|
4
|
+
end
|
5
|
+
# A simple server for triggering a new run on a Puppet client.
|
6
|
+
class Runner < Handler
|
7
|
+
@interface = XMLRPC::Service::Interface.new("puppetrunner") { |iface|
|
8
|
+
iface.add_method("string run(string, string)")
|
9
|
+
}
|
10
|
+
|
11
|
+
# Run the client configuration right now, optionally specifying
|
12
|
+
# tags and whether to ignore schedules
|
13
|
+
def run(tags = [], ignoreschedules = false, fg = true, client = nil, clientip = nil)
|
14
|
+
# We need to retrieve the client
|
15
|
+
master = Puppet::Client::MasterClient.instance
|
16
|
+
|
17
|
+
unless master
|
18
|
+
raise MissingMasterError, "Could not find the master client"
|
19
|
+
end
|
20
|
+
|
21
|
+
if master.locked?
|
22
|
+
Puppet.notice "Could not trigger run; already running"
|
23
|
+
return "running"
|
24
|
+
end
|
25
|
+
|
26
|
+
if tags == ""
|
27
|
+
tags = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
if ignoreschedules == ""
|
31
|
+
ignoreschedules == nil
|
32
|
+
end
|
33
|
+
|
34
|
+
if client
|
35
|
+
msg = "%s(%s) triggered run" % [client, clientip]
|
36
|
+
if tags
|
37
|
+
msg += " with tags %s" % tags.join(", ")
|
38
|
+
end
|
39
|
+
|
40
|
+
if ignoreschedules
|
41
|
+
msg += " without schedules"
|
42
|
+
end
|
43
|
+
|
44
|
+
Puppet.notice msg
|
45
|
+
end
|
46
|
+
|
47
|
+
# And then we need to tell it to run, with this extra info.
|
48
|
+
if fg
|
49
|
+
master.run(tags, ignoreschedules)
|
50
|
+
else
|
51
|
+
Puppet.newthread do
|
52
|
+
master.run(tags, ignoreschedules)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
return "success"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# $Id: runner.rb 1325 2006-06-28 15:17:56Z luke $
|
@@ -38,25 +38,44 @@ class Server
|
|
38
38
|
def authorize(request, method)
|
39
39
|
namespace = method.sub(/\..+/, '')
|
40
40
|
client = request.peeraddr[2]
|
41
|
+
if defined? @client and @client
|
42
|
+
client = @client
|
43
|
+
end
|
41
44
|
ip = request.peeraddr[3]
|
42
45
|
if request.client_cert
|
46
|
+
begin
|
43
47
|
if @puppetserver.authconfig.exists?
|
44
|
-
|
48
|
+
allowed = @puppetserver.authconfig.allowed?(method, client, ip)
|
49
|
+
|
50
|
+
if allowed
|
51
|
+
Puppet.info "Allowing %s(%s) trusted access to %s" %
|
52
|
+
[client, ip, method]
|
53
|
+
return true
|
54
|
+
else
|
55
|
+
Puppet.info "Denying %s(%s) trusted access to %s" %
|
56
|
+
[client, ip, method]
|
57
|
+
return false
|
58
|
+
end
|
45
59
|
else
|
46
60
|
# This is pretty hackish, but...
|
47
61
|
# This means we can't actually test this method at this point.
|
48
62
|
# The next release of Puppet will almost definitely require
|
49
63
|
# this file to exist or will default to denying all access.
|
50
64
|
if Puppet.name == "puppetmasterd" or defined? Test::Unit::TestCase
|
51
|
-
|
65
|
+
Puppet.info "Allowing %s(%s) trusted access to %s" %
|
52
66
|
[client, ip, method]
|
53
67
|
return true
|
54
68
|
else
|
55
|
-
|
69
|
+
Puppet.info "Denying %s(%s) trusted access to %s on %s" %
|
56
70
|
[client, ip, method, Puppet.name]
|
57
71
|
return false
|
58
72
|
end
|
59
73
|
end
|
74
|
+
rescue => detail
|
75
|
+
puts detail
|
76
|
+
puts detail.backtrace
|
77
|
+
raise
|
78
|
+
end
|
60
79
|
else
|
61
80
|
if method =~ /^puppetca\./
|
62
81
|
Puppet.notice "Allowing %s(%s) untrusted access to CA methods" %
|
@@ -84,6 +103,7 @@ class Server
|
|
84
103
|
|
85
104
|
def initialize(server, handlers)
|
86
105
|
@puppetserver = server
|
106
|
+
@notified = {}
|
87
107
|
# the servlet base class does not consume any arguments
|
88
108
|
# and its BasicServer base class only accepts a 'class_delim'
|
89
109
|
# option which won't change in Puppet at all
|
@@ -106,10 +126,8 @@ class Server
|
|
106
126
|
@clientip = nil
|
107
127
|
|
108
128
|
self.set_service_hook { |obj, *args|
|
109
|
-
#raise "crap!"
|
110
129
|
if @client and @clientip
|
111
130
|
args.push(@client, @clientip)
|
112
|
-
#obj.call(args, @request)
|
113
131
|
end
|
114
132
|
begin
|
115
133
|
obj.call(*args)
|
@@ -173,10 +191,6 @@ class Server
|
|
173
191
|
end
|
174
192
|
end
|
175
193
|
end
|
176
|
-
#if request.server_cert
|
177
|
-
# Puppet.info "server cert is %s" % @request.server_cert
|
178
|
-
#end
|
179
|
-
#p @request
|
180
194
|
begin
|
181
195
|
super
|
182
196
|
rescue => detail
|
@@ -70,6 +70,30 @@ class Puppet::SSLCertificates::CA
|
|
70
70
|
@config[:cacert]
|
71
71
|
end
|
72
72
|
|
73
|
+
# Remove all traces of a given host. This is kind of hackish, but, eh.
|
74
|
+
def clean(host)
|
75
|
+
[:csrdir, :signeddir, :publickeydir, :privatekeydir, :certdir].each do |name|
|
76
|
+
dir = Puppet[name]
|
77
|
+
|
78
|
+
file = File.join(dir, host + ".pem")
|
79
|
+
|
80
|
+
if FileTest.exists?(file)
|
81
|
+
begin
|
82
|
+
if Puppet.name == "puppetca"
|
83
|
+
puts "Removing %s" % file
|
84
|
+
else
|
85
|
+
Puppet.info "Removing %s" % file
|
86
|
+
end
|
87
|
+
File.unlink(file)
|
88
|
+
rescue => detail
|
89
|
+
raise Puppet::Error, "Could not delete %s: %s" %
|
90
|
+
[file, detail]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
73
97
|
def host2csrfile(hostname)
|
74
98
|
File.join(Puppet[:csrdir], [hostname, "pem"].join("."))
|
75
99
|
end
|
@@ -330,4 +354,4 @@ class Puppet::SSLCertificates::CA
|
|
330
354
|
end
|
331
355
|
end
|
332
356
|
|
333
|
-
# $Id: ca.rb
|
357
|
+
# $Id: ca.rb 1145 2006-04-28 04:08:38Z luke $
|
data/lib/puppet/statechange.rb
CHANGED
@@ -7,6 +7,9 @@ module Puppet
|
|
7
7
|
# including calling 'sync' on the states and producing events.
|
8
8
|
class StateChange
|
9
9
|
attr_accessor :is, :should, :type, :path, :state, :transaction, :changed
|
10
|
+
|
11
|
+
# The log file generated when this object was changed.
|
12
|
+
attr_reader :report
|
10
13
|
|
11
14
|
def initialize(state)
|
12
15
|
@state = state
|
@@ -32,7 +35,7 @@ module Puppet
|
|
32
35
|
end
|
33
36
|
|
34
37
|
if @state.noop
|
35
|
-
@state.log "is %s, should be %s" %
|
38
|
+
@state.log "is %s, should be %s (noop)" %
|
36
39
|
[state.is_to_s, state.should_to_s]
|
37
40
|
#@state.debug "%s is noop" % @state
|
38
41
|
return nil
|
@@ -41,64 +44,42 @@ module Puppet
|
|
41
44
|
#@state.info "Is: %s, Should: %s" %
|
42
45
|
# [@state.is.inspect, @state.should.inspect]
|
43
46
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
47
|
+
# The transaction catches any exceptions here.
|
48
|
+
events = @state.sync
|
49
|
+
if events.nil?
|
50
|
+
return nil
|
51
|
+
end
|
49
52
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
54
|
-
else
|
55
|
-
events = [events]
|
53
|
+
if events.is_a?(Array)
|
54
|
+
if events.empty?
|
55
|
+
return nil
|
56
56
|
end
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
# should basically point to that, right?
|
69
|
-
#:state => @state,
|
70
|
-
#:object => @state.parent,
|
71
|
-
@state.log @state.change_to_s
|
72
|
-
Puppet::Event.new(
|
73
|
-
:event => event,
|
74
|
-
:change => self,
|
75
|
-
:transaction => @transaction,
|
76
|
-
:source => @state.parent,
|
77
|
-
:message => self.to_s
|
78
|
-
)
|
79
|
-
}
|
80
|
-
rescue => detail
|
81
|
-
#@state.err "%s failed: %s" % [self.to_s,detail]
|
82
|
-
if Puppet[:debug]
|
83
|
-
puts detail.backtrace
|
57
|
+
else
|
58
|
+
events = [events]
|
59
|
+
end
|
60
|
+
|
61
|
+
return events.collect { |event|
|
62
|
+
# default to a simple event type
|
63
|
+
if ! event.is_a?(Symbol)
|
64
|
+
@state.warning("State '%s' returned invalid event '%s'; resetting to default" %
|
65
|
+
[@state.class,event])
|
66
|
+
|
67
|
+
event = @state.parent.class.name.id2name + "_changed"
|
84
68
|
end
|
85
|
-
|
86
|
-
#
|
87
|
-
#
|
88
|
-
pname = @state.parent.class.name.id2name
|
89
|
-
#if pname.is_a?(Symbol)
|
90
|
-
# pname = pname.id2name
|
91
|
-
#end
|
69
|
+
|
70
|
+
# i should maybe include object type, but the event type
|
71
|
+
# should basically point to that, right?
|
92
72
|
#:state => @state,
|
93
|
-
|
94
|
-
|
95
|
-
|
73
|
+
#:object => @state.parent,
|
74
|
+
@report = @state.log(@state.change_to_s)
|
75
|
+
Puppet::Event.new(
|
76
|
+
:event => event,
|
96
77
|
:change => self,
|
97
|
-
:source => @state.parent,
|
98
78
|
:transaction => @transaction,
|
99
|
-
:
|
79
|
+
:source => @state.parent,
|
80
|
+
:message => self.to_s
|
100
81
|
)
|
101
|
-
|
82
|
+
}
|
102
83
|
end
|
103
84
|
|
104
85
|
def forward
|
@@ -145,4 +126,4 @@ module Puppet
|
|
145
126
|
end
|
146
127
|
end
|
147
128
|
|
148
|
-
# $Id: statechange.rb
|
129
|
+
# $Id: statechange.rb 1336 2006-06-29 16:05:52Z luke $
|
data/lib/puppet/storage.rb
CHANGED
@@ -42,7 +42,6 @@ module Puppet
|
|
42
42
|
end
|
43
43
|
|
44
44
|
unless File.exists?(Puppet[:statefile])
|
45
|
-
Puppet.info "Statefile %s does not exist" % Puppet[:statefile]
|
46
45
|
unless defined? @@state and ! @@state.nil?
|
47
46
|
self.init
|
48
47
|
end
|
@@ -96,4 +95,4 @@ module Puppet
|
|
96
95
|
end
|
97
96
|
end
|
98
97
|
|
99
|
-
# $Id: storage.rb
|
98
|
+
# $Id: storage.rb 1408 2006-07-20 19:01:58Z luke $
|
data/lib/puppet/transaction.rb
CHANGED
@@ -6,195 +6,365 @@ require 'puppet/statechange'
|
|
6
6
|
|
7
7
|
module Puppet
|
8
8
|
class Transaction
|
9
|
-
attr_accessor :
|
9
|
+
attr_accessor :component, :objects, :tags, :ignoreschedules
|
10
10
|
|
11
|
+
include Puppet::Util
|
11
12
|
|
12
13
|
Puppet.config.setdefaults(:transaction,
|
13
14
|
:tags => ["", "Tags to use to find objects. If this is set, then
|
14
|
-
only objects tagged with the specified tags will be applied.
|
15
|
-
be comma-separated."]
|
15
|
+
only objects tagged with the specified tags will be applied.
|
16
|
+
Values must be comma-separated."]
|
16
17
|
)
|
17
18
|
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
# Add some additional times for reporting
|
20
|
+
def addtimes(hash)
|
21
|
+
hash.each do |name, num|
|
22
|
+
@timemetrics[name] = num
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Apply all changes for a child, returning a list of the events
|
27
|
+
# generated.
|
28
|
+
def apply(child)
|
29
|
+
# First make sure there are no failed dependencies
|
30
|
+
child.eachdependency do |dep|
|
31
|
+
skip = false
|
32
|
+
if @failures[dep] > 0
|
33
|
+
child.notice "Dependency %s[%s] has %s failures" %
|
34
|
+
[dep.class.name, dep.name, @failures[dep]]
|
35
|
+
skip = true
|
36
|
+
end
|
37
|
+
|
38
|
+
if skip
|
39
|
+
child.warning "Skipping because of failed dependencies"
|
40
|
+
@objectmetrics[:skipped] += 1
|
41
|
+
return []
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
begin
|
46
|
+
changes = child.evaluate
|
47
|
+
rescue => detail
|
48
|
+
if Puppet[:debug]
|
49
|
+
puts detail.backtrace
|
50
|
+
end
|
51
|
+
|
52
|
+
child.err "Failed to retrieve current state: %s" % detail
|
53
|
+
|
54
|
+
# Mark that it failed
|
55
|
+
@failures[child] += 1
|
56
|
+
|
57
|
+
# And then return
|
58
|
+
return []
|
59
|
+
end
|
60
|
+
|
61
|
+
unless changes.is_a? Array
|
62
|
+
changes = [changes]
|
63
|
+
end
|
64
|
+
|
65
|
+
if changes.length > 0
|
66
|
+
@objectmetrics[:out_of_sync] += 1
|
67
|
+
end
|
68
|
+
|
69
|
+
childevents = changes.collect { |change|
|
70
|
+
@changes << change
|
71
|
+
@count += 1
|
72
|
+
change.transaction = self
|
73
|
+
events = nil
|
74
|
+
begin
|
75
|
+
# use an array, so that changes can return more than one
|
76
|
+
# event if they want
|
77
|
+
events = [change.forward].flatten.reject { |e| e.nil? }
|
78
|
+
rescue => detail
|
79
|
+
change.state.err "change from %s to %s failed: %s" %
|
80
|
+
[change.state.is_to_s, change.state.should_to_s, detail]
|
81
|
+
@failures[child] += 1
|
82
|
+
next
|
83
|
+
# FIXME this should support using onerror to determine
|
84
|
+
# behaviour; or more likely, the client calling us
|
85
|
+
# should do so
|
86
|
+
end
|
87
|
+
|
88
|
+
# Mark that our change happened, so it can be reversed
|
89
|
+
# if we ever get to that point
|
90
|
+
unless events.nil? or (events.is_a?(Array) and events.empty?)
|
91
|
+
change.changed = true
|
92
|
+
@objectmetrics[:applied] += 1
|
93
|
+
end
|
94
|
+
|
95
|
+
events
|
96
|
+
}.flatten.reject { |e| e.nil? }
|
97
|
+
|
98
|
+
unless changes.empty?
|
99
|
+
# Record when we last synced
|
100
|
+
child.cache(:synced, Time.now)
|
101
|
+
end
|
102
|
+
|
103
|
+
childevents
|
104
|
+
end
|
105
|
+
|
106
|
+
# Find all of the changed objects.
|
107
|
+
def changed?
|
108
|
+
@changes.find_all { |change| change.changed }.collect { |change|
|
109
|
+
change.state.parent
|
110
|
+
}.uniq
|
24
111
|
end
|
25
112
|
|
26
|
-
#
|
27
|
-
#
|
28
|
-
def
|
29
|
-
|
113
|
+
# Collect all of the targets for the list of events. This is an unintuitive
|
114
|
+
# method because it recurses up through the source the event.
|
115
|
+
def collecttargets(events)
|
116
|
+
events.each do |event|
|
117
|
+
source = event.source
|
118
|
+
start = source
|
119
|
+
|
120
|
+
while source
|
121
|
+
Puppet::Event::Subscription.targets_of(event, source) do |sub|
|
122
|
+
start.info "Scheduling %s of %s[%s]" %
|
123
|
+
[sub.callback, sub.target.class.name, sub.target.name]
|
124
|
+
@targets[sub.target][event] = sub
|
125
|
+
end
|
126
|
+
|
127
|
+
source = source.parent
|
128
|
+
end
|
129
|
+
end
|
30
130
|
end
|
31
131
|
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
# so, when looking for subscribers, we need to first see if the object
|
36
|
-
# that actually changed had any direct subscribers
|
37
|
-
# then, we need to pass the event to the object's containing component,
|
38
|
-
# to see if it or any of its parents have subscriptions on the event
|
132
|
+
# This method does all the actual work of running a transaction. It
|
133
|
+
# collects all of the changes, executes them, and responds to any
|
134
|
+
# necessary events.
|
39
135
|
def evaluate
|
40
|
-
|
41
|
-
# [self.object_id, @changes.length]
|
136
|
+
@count = 0
|
42
137
|
|
43
|
-
|
44
|
-
|
45
|
-
tags
|
46
|
-
if tags == ""
|
138
|
+
# Allow the tags to be overriden
|
139
|
+
tags = self.tags || Puppet[:tags]
|
140
|
+
if tags.nil? or tags == ""
|
47
141
|
tags = nil
|
48
142
|
else
|
49
|
-
tags = tags.
|
143
|
+
tags = [tags] unless tags.is_a? Array
|
144
|
+
tags = tags.collect do |tag|
|
145
|
+
tag.split(/\s*,\s*/)
|
146
|
+
end.flatten
|
50
147
|
end
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
}.find_all { |child|
|
58
|
-
child.scheduled?
|
59
|
-
}.collect { |child|
|
60
|
-
# these children are all Puppet::Type instances
|
61
|
-
# not all of the children will return a change, and Containers
|
62
|
-
# return transactions
|
63
|
-
#ary = child.evaluate
|
64
|
-
#ary
|
65
|
-
changes = child.evaluate
|
66
|
-
unless changes.is_a? Array
|
67
|
-
changes = [changes]
|
68
|
-
end
|
69
|
-
changes.collect { |change|
|
70
|
-
@changes << change
|
71
|
-
count += 1
|
72
|
-
change.transaction = self
|
148
|
+
|
149
|
+
# Start logging.
|
150
|
+
Puppet::Log.newdestination(@report)
|
151
|
+
|
152
|
+
begin
|
153
|
+
allevents = @objects.collect { |child|
|
73
154
|
events = nil
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
155
|
+
if (tags.nil? or child.tagged?(tags))
|
156
|
+
if self.ignoreschedules or child.scheduled?
|
157
|
+
@objectmetrics[:scheduled] += 1
|
158
|
+
# Perform the actual changes
|
159
|
+
|
160
|
+
seconds = thinmark do
|
161
|
+
events = apply(child)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Keep track of how long we spend in each type of object
|
165
|
+
@timemetrics[child.class.name] += seconds
|
166
|
+
|
167
|
+
# Collect the targets of any subscriptions to those events
|
168
|
+
collecttargets(events)
|
169
|
+
else
|
170
|
+
child.debug "Not scheduled"
|
84
171
|
end
|
85
|
-
|
86
|
-
|
87
|
-
# behaviour; or more likely, the client calling us
|
88
|
-
# should do so
|
172
|
+
else
|
173
|
+
child.debug "Not tagged with %s" % tags.join(", ")
|
89
174
|
end
|
90
175
|
|
91
|
-
#
|
92
|
-
|
93
|
-
# to avoid as long as we're syncing each state individually.
|
94
|
-
change.state.parent.cache(:synced, now)
|
176
|
+
# Now check to see if there are any events for this child
|
177
|
+
trigger(child)
|
95
178
|
|
96
|
-
|
97
|
-
change.changed = true
|
98
|
-
end
|
179
|
+
# And return the events for collection
|
99
180
|
events
|
100
|
-
}
|
101
|
-
|
102
|
-
|
103
|
-
|
181
|
+
}.flatten.reject { |e| e.nil? }
|
182
|
+
ensure
|
183
|
+
# And then close the transaction log.
|
184
|
+
Puppet::Log.close(@report)
|
185
|
+
end
|
104
186
|
|
105
187
|
Puppet.debug "Finishing transaction %s with %s changes" %
|
106
|
-
[self.object_id, count]
|
188
|
+
[self.object_id, @count]
|
107
189
|
|
108
|
-
|
190
|
+
allevents
|
191
|
+
end
|
192
|
+
|
193
|
+
# Determine whether a given object has failed.
|
194
|
+
def failed?(obj)
|
195
|
+
@failures[obj] > 0
|
109
196
|
end
|
110
197
|
|
111
198
|
# this should only be called by a Puppet::Container object now
|
112
199
|
# and it should only receive an array
|
113
200
|
def initialize(objects)
|
114
201
|
@objects = objects
|
115
|
-
@toplevel = false
|
116
202
|
|
203
|
+
@objectmetrics = {
|
204
|
+
:total => @objects.length,
|
205
|
+
:out_of_sync => 0, # The number of objects that had changes
|
206
|
+
:applied => 0, # The number of objects fixed
|
207
|
+
:skipped => 0, # The number of objects skipped
|
208
|
+
:restarted => 0, # The number of objects triggered
|
209
|
+
:failed_restarts => 0, # The number of objects that fail a trigger
|
210
|
+
:scheduled => 0 # The number of objects scheduled
|
211
|
+
}
|
212
|
+
|
213
|
+
# Metrics for distributing times across the different types.
|
214
|
+
@timemetrics = Hash.new(0)
|
215
|
+
|
216
|
+
# The number of objects that were triggered in this run
|
117
217
|
@triggered = Hash.new { |hash, key|
|
118
218
|
hash[key] = Hash.new(0)
|
119
219
|
}
|
120
220
|
|
121
|
-
# of
|
122
|
-
|
123
|
-
|
124
|
-
self.class.init
|
221
|
+
# Targets of being triggered.
|
222
|
+
@targets = Hash.new do |hash, key|
|
223
|
+
hash[key] = {}
|
125
224
|
end
|
126
225
|
|
226
|
+
# The changes we're performing
|
127
227
|
@changes = []
|
228
|
+
|
229
|
+
# The objects that have failed and the number of failures each. This
|
230
|
+
# is used for skipping objects because of failed dependencies.
|
231
|
+
@failures = Hash.new do |h, key|
|
232
|
+
h[key] = 0
|
233
|
+
end
|
234
|
+
|
235
|
+
@report = Report.new
|
128
236
|
end
|
129
237
|
|
130
|
-
#
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
events.each do |event|
|
136
|
-
source = event.source
|
238
|
+
# Generate a transaction report.
|
239
|
+
def report
|
240
|
+
@objectmetrics[:failed] = @failures.find_all do |name, num|
|
241
|
+
num > 0
|
242
|
+
end.length
|
137
243
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
sub.target.err "Failed to respond to %s: %s" % [event, detail]
|
144
|
-
if Puppet[:debug]
|
145
|
-
puts detail.backtrace
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
244
|
+
# Get the total time spent
|
245
|
+
@timemetrics[:total] = @timemetrics.inject(0) do |total, vals|
|
246
|
+
total += vals[1]
|
247
|
+
total
|
248
|
+
end
|
149
249
|
|
150
|
-
|
151
|
-
|
250
|
+
# Unfortunately, RRD does not deal well with changing lists of values,
|
251
|
+
# so we have to pick a list of values and stick with it. In this case,
|
252
|
+
# that means we record the total time, the config time, and that's about
|
253
|
+
# it. We should probably send each type's time as a separate metric.
|
254
|
+
@timemetrics.dup.each do |name, value|
|
255
|
+
if Puppet::Type.type(name)
|
256
|
+
@timemetrics.delete(name)
|
152
257
|
end
|
153
|
-
#Puppet::Event::Subscriptions.propagate(object, event, self)
|
154
258
|
end
|
259
|
+
|
260
|
+
# Add all of the metrics related to object count and status
|
261
|
+
@report.newmetric(:objects, @objectmetrics)
|
262
|
+
|
263
|
+
# Record the relative time spent in each object.
|
264
|
+
@report.newmetric(:time, @timemetrics)
|
265
|
+
|
266
|
+
# Then all of the change-related metrics
|
267
|
+
@report.newmetric(:changes,
|
268
|
+
:total => @changes.length
|
269
|
+
)
|
270
|
+
|
271
|
+
@report.time = Time.now
|
272
|
+
|
273
|
+
return @report
|
155
274
|
end
|
156
275
|
|
157
276
|
# Roll all completed changes back.
|
158
277
|
def rollback
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
278
|
+
@targets.clear
|
279
|
+
@triggered.clear
|
280
|
+
allevents = @changes.reverse.collect { |change|
|
281
|
+
# skip changes that were never actually run
|
282
|
+
unless change.changed
|
283
|
+
Puppet.debug "%s was not changed" % change.to_s
|
284
|
+
next
|
285
|
+
end
|
286
|
+
begin
|
287
|
+
events = change.backward
|
288
|
+
rescue => detail
|
289
|
+
Puppet.err("%s rollback failed: %s" % [change,detail])
|
290
|
+
if Puppet[:debug]
|
291
|
+
puts detail.backtrace
|
292
|
+
end
|
293
|
+
next
|
294
|
+
# at this point, we would normally do error handling
|
295
|
+
# but i haven't decided what to do for that yet
|
296
|
+
# so just record that a sync failed for a given object
|
297
|
+
#@@failures[change.state.parent] += 1
|
298
|
+
# this still could get hairy; what if file contents changed,
|
299
|
+
# but a chmod failed? how would i handle that error? dern
|
300
|
+
end
|
301
|
+
|
302
|
+
collecttargets(events)
|
303
|
+
|
304
|
+
# Now check to see if there are any events for this child.
|
305
|
+
# Kind of hackish, since going backwards goes a change at a
|
306
|
+
# time, not a child at a time.
|
307
|
+
trigger(change.state.parent)
|
308
|
+
|
309
|
+
# And return the events for collection
|
310
|
+
events
|
311
|
+
}.flatten.reject { |e| e.nil? }
|
312
|
+
end
|
313
|
+
|
314
|
+
# Trigger any subscriptions to a child. This does an upwardly recursive
|
315
|
+
# search -- it triggers the passed object, but also the object's parent
|
316
|
+
# and so on up the tree.
|
317
|
+
def trigger(child)
|
318
|
+
obj = child
|
319
|
+
callbacks = Hash.new { |hash, key| hash[key] = [] }
|
320
|
+
sources = Hash.new { |hash, key| hash[key] = [] }
|
321
|
+
|
322
|
+
while obj
|
323
|
+
if @targets.include?(obj)
|
324
|
+
callbacks.clear
|
325
|
+
sources.clear
|
326
|
+
@targets[obj].each do |event, sub|
|
327
|
+
# Collect all of the subs for each callback
|
328
|
+
callbacks[sub.callback] << sub
|
329
|
+
|
330
|
+
# And collect the sources for logging
|
331
|
+
sources[event.source] << sub.callback
|
332
|
+
end
|
333
|
+
|
334
|
+
sources.each do |source, callbacklist|
|
335
|
+
obj.debug "%s[%s] results in triggering %s" %
|
336
|
+
[source.class.name, source.name, callbacklist.join(", ")]
|
165
337
|
end
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
338
|
+
|
339
|
+
callbacks.each do |callback, subs|
|
340
|
+
obj.info "Triggering '%s' from %s dependencies" %
|
341
|
+
[callback, subs.length]
|
342
|
+
# At this point, just log failures, don't try to react
|
343
|
+
# to them in any way.
|
344
|
+
begin
|
345
|
+
obj.send(callback)
|
346
|
+
@objectmetrics[:restarted] += 1
|
347
|
+
rescue => detail
|
348
|
+
obj.err "Failed to call %s on %s: %s" %
|
349
|
+
[callback, obj, detail]
|
350
|
+
|
351
|
+
@objectmetrics[:failed_restarts] += 1
|
352
|
+
|
353
|
+
if Puppet[:debug]
|
354
|
+
puts detail.backtrace
|
355
|
+
end
|
173
356
|
end
|
174
|
-
|
175
|
-
|
176
|
-
# but i haven't decided what to do for that yet
|
177
|
-
# so just record that a sync failed for a given object
|
178
|
-
#@@failures[change.state.parent] += 1
|
179
|
-
# this still could get hairy; what if file contents changed,
|
180
|
-
# but a chmod failed? how would i handle that error? dern
|
357
|
+
|
358
|
+
triggered(obj, callback)
|
181
359
|
end
|
182
|
-
elsif change.is_a?(Puppet::Transaction)
|
183
|
-
raise Puppet::DevError, "Got a sub-transaction"
|
184
|
-
# yay, recursion
|
185
|
-
change.rollback
|
186
|
-
else
|
187
|
-
raise Puppe::DevError,
|
188
|
-
"Transactions cannot handle objects of type %s" % child.class
|
189
360
|
end
|
190
|
-
}.flatten.reject { |e| e.nil? }
|
191
361
|
|
192
|
-
|
362
|
+
obj = obj.parent
|
363
|
+
end
|
193
364
|
end
|
194
365
|
|
195
366
|
def triggered(object, method)
|
196
367
|
@triggered[object][method] += 1
|
197
|
-
#@triggerevents << ("%s_%sed" % [object.class.name.to_s, method.to_s]).intern
|
198
368
|
end
|
199
369
|
|
200
370
|
def triggered?(object, method)
|
@@ -203,4 +373,6 @@ class Transaction
|
|
203
373
|
end
|
204
374
|
end
|
205
375
|
|
206
|
-
|
376
|
+
require 'puppet/transaction/report'
|
377
|
+
|
378
|
+
# $Id: transaction.rb 1351 2006-07-01 17:27:54Z luke $
|