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.

Files changed (185) hide show
  1. data/CHANGELOG +98 -0
  2. data/Rakefile +5 -1
  3. data/bin/puppet +1 -1
  4. data/bin/puppetca +25 -11
  5. data/bin/puppetd +189 -66
  6. data/bin/puppetdoc +79 -62
  7. data/bin/puppetmasterd +93 -49
  8. data/bin/puppetrun +385 -0
  9. data/conf/redhat/client.init +5 -2
  10. data/conf/redhat/fileserver.conf +1 -1
  11. data/conf/redhat/lsb-config.patch +51 -0
  12. data/conf/redhat/puppet.spec +45 -18
  13. data/conf/redhat/puppetd.conf +32 -4
  14. data/conf/redhat/server.init +5 -2
  15. data/conf/solaris/pkginfo +7 -0
  16. data/conf/solaris/smf/puppetd.xml +77 -0
  17. data/conf/solaris/smf/puppetmasterd.xml +77 -0
  18. data/conf/solaris/smf/svc-puppetd +66 -0
  19. data/conf/solaris/smf/svc-puppetmasterd +62 -0
  20. data/examples/code/failers/noobjectrvalue +1 -0
  21. data/examples/code/snippets/deepclassheirarchy.pp +23 -0
  22. data/examples/code/snippets/defineoverrides.pp +17 -0
  23. data/examples/code/snippets/emptyexec.pp +3 -0
  24. data/examples/code/snippets/selectorvalues.pp +6 -1
  25. data/examples/code/snippets/tagged.pp +35 -0
  26. data/ext/ldap/puppet.schema +2 -2
  27. data/install.rb +4 -2
  28. data/lib/puppet.rb +206 -15
  29. data/lib/puppet/client.rb +30 -20
  30. data/lib/puppet/client/ca.rb +2 -2
  31. data/lib/puppet/client/dipper.rb +5 -9
  32. data/lib/puppet/client/master.rb +224 -44
  33. data/lib/puppet/client/pelement.rb +54 -9
  34. data/lib/puppet/client/proxy.rb +3 -2
  35. data/lib/puppet/client/reporter.rb +34 -0
  36. data/lib/puppet/client/runner.rb +17 -0
  37. data/lib/puppet/config.rb +136 -55
  38. data/lib/puppet/daemon.rb +59 -37
  39. data/lib/puppet/element.rb +2 -1
  40. data/lib/puppet/event.rb +14 -3
  41. data/lib/puppet/filetype.rb +28 -19
  42. data/lib/puppet/log.rb +297 -132
  43. data/lib/puppet/metric.rb +31 -131
  44. data/lib/puppet/networkclient.rb +73 -46
  45. data/lib/puppet/parameter.rb +49 -1
  46. data/lib/puppet/parsedfile.rb +32 -12
  47. data/lib/puppet/parser/ast.rb +6 -1
  48. data/lib/puppet/parser/ast/astarray.rb +32 -6
  49. data/lib/puppet/parser/ast/collection.rb +91 -0
  50. data/lib/puppet/parser/ast/compdef.rb +2 -2
  51. data/lib/puppet/parser/ast/component.rb +24 -11
  52. data/lib/puppet/parser/ast/function.rb +50 -0
  53. data/lib/puppet/parser/ast/hostclass.rb +70 -22
  54. data/lib/puppet/parser/ast/node.rb +17 -8
  55. data/lib/puppet/parser/ast/nodedef.rb +1 -1
  56. data/lib/puppet/parser/ast/objectdef.rb +28 -10
  57. data/lib/puppet/parser/ast/selector.rb +4 -1
  58. data/lib/puppet/parser/functions.rb +145 -0
  59. data/lib/puppet/parser/interpreter.rb +243 -86
  60. data/lib/puppet/parser/lexer.rb +5 -4
  61. data/lib/puppet/parser/parser.rb +586 -505
  62. data/lib/puppet/parser/scope.rb +337 -187
  63. data/lib/puppet/rails.rb +115 -0
  64. data/lib/puppet/rails/database.rb +40 -0
  65. data/lib/puppet/rails/host.rb +83 -0
  66. data/lib/puppet/rails/rails_object.rb +42 -0
  67. data/lib/puppet/rails/rails_parameter.rb +5 -0
  68. data/lib/puppet/reports/rrdgraph.rb +20 -0
  69. data/lib/puppet/reports/tagmail.rb +94 -0
  70. data/lib/puppet/server.rb +20 -4
  71. data/lib/puppet/server/authconfig.rb +14 -3
  72. data/lib/puppet/server/authstore.rb +2 -2
  73. data/lib/puppet/server/ca.rb +23 -11
  74. data/lib/puppet/server/filebucket.rb +10 -10
  75. data/lib/puppet/server/fileserver.rb +4 -8
  76. data/lib/puppet/server/master.rb +19 -22
  77. data/lib/puppet/server/pelement.rb +28 -16
  78. data/lib/puppet/server/report.rb +184 -0
  79. data/lib/puppet/server/runner.rb +62 -0
  80. data/lib/puppet/server/servlet.rb +23 -9
  81. data/lib/puppet/sslcertificates/ca.rb +25 -1
  82. data/lib/puppet/statechange.rb +34 -53
  83. data/lib/puppet/storage.rb +1 -2
  84. data/lib/puppet/transaction.rb +305 -133
  85. data/lib/puppet/transaction/report.rb +42 -0
  86. data/lib/puppet/transportable.rb +57 -33
  87. data/lib/puppet/type.rb +260 -127
  88. data/lib/puppet/type/component.rb +9 -21
  89. data/lib/puppet/type/cron.rb +367 -116
  90. data/lib/puppet/type/exec.rb +15 -16
  91. data/lib/puppet/type/group.rb +9 -1
  92. data/lib/puppet/type/nameservice.rb +2 -5
  93. data/lib/puppet/type/nameservice/netinfo.rb +3 -0
  94. data/lib/puppet/type/nameservice/objectadd.rb +23 -10
  95. data/lib/puppet/type/nameservice/pw.rb +16 -3
  96. data/lib/puppet/type/package.rb +25 -75
  97. data/lib/puppet/type/package/apple.rb +15 -1
  98. data/lib/puppet/type/package/apt.rb +37 -2
  99. data/lib/puppet/type/package/blastwave.rb +136 -0
  100. data/lib/puppet/type/package/dpkg.rb +4 -4
  101. data/lib/puppet/type/package/gem.rb +119 -0
  102. data/lib/puppet/type/package/openbsd.rb +7 -6
  103. data/lib/puppet/type/package/ports.rb +7 -2
  104. data/lib/puppet/type/package/rpm.rb +1 -1
  105. data/lib/puppet/type/package/sun.rb +23 -9
  106. data/lib/puppet/type/package/sunfreeware.rb +7 -0
  107. data/lib/puppet/type/package/yum.rb +16 -9
  108. data/lib/puppet/type/parsedtype.rb +7 -5
  109. data/lib/puppet/type/parsedtype/mount.rb +55 -34
  110. data/lib/puppet/type/parsedtype/port.rb +7 -1
  111. data/lib/puppet/type/parsedtype/sshkey.rb +6 -16
  112. data/lib/puppet/type/pfile.rb +115 -23
  113. data/lib/puppet/type/pfile/checksum.rb +18 -5
  114. data/lib/puppet/type/pfile/content.rb +2 -2
  115. data/lib/puppet/type/pfile/ensure.rb +3 -3
  116. data/lib/puppet/type/pfile/group.rb +2 -2
  117. data/lib/puppet/type/pfile/source.rb +28 -17
  118. data/lib/puppet/type/pfile/target.rb +25 -17
  119. data/lib/puppet/type/pfilebucket.rb +25 -6
  120. data/lib/puppet/type/schedule.rb +6 -6
  121. data/lib/puppet/type/service.rb +24 -14
  122. data/lib/puppet/type/service/debian.rb +1 -1
  123. data/lib/puppet/type/service/redhat.rb +13 -10
  124. data/lib/puppet/type/service/smf.rb +3 -3
  125. data/lib/puppet/type/state.rb +1 -2
  126. data/lib/puppet/type/symlink.rb +3 -4
  127. data/lib/puppet/type/user.rb +22 -10
  128. data/lib/puppet/type/yumrepo.rb +6 -1
  129. data/lib/puppet/type/zone.rb +595 -0
  130. data/lib/puppet/util.rb +58 -12
  131. data/test/client/client.rb +2 -2
  132. data/test/client/master.rb +92 -3
  133. data/test/client/pelement.rb +99 -0
  134. data/test/executables/puppetbin.rb +3 -4
  135. data/test/executables/puppetca.rb +3 -3
  136. data/test/executables/puppetd.rb +3 -3
  137. data/test/executables/puppetmasterd.rb +1 -5
  138. data/test/executables/puppetmodule.rb +2 -2
  139. data/test/language/ast.rb +200 -11
  140. data/test/language/functions.rb +245 -0
  141. data/test/language/interpreter.rb +155 -6
  142. data/test/language/lexer.rb +35 -2
  143. data/test/language/node.rb +48 -1
  144. data/test/language/parser.rb +250 -1
  145. data/test/language/rails.rb +105 -0
  146. data/test/language/scope.rb +304 -10
  147. data/test/language/snippets.rb +54 -5
  148. data/test/language/transportable.rb +60 -28
  149. data/test/other/config.rb +214 -1
  150. data/test/other/events.rb +67 -9
  151. data/test/other/log.rb +31 -5
  152. data/test/other/metrics.rb +23 -21
  153. data/test/other/parsedfile.rb +29 -2
  154. data/test/other/puppet.rb +79 -0
  155. data/test/other/report.rb +106 -0
  156. data/test/other/storage.rb +2 -2
  157. data/test/other/transactions.rb +128 -2
  158. data/test/puppet/utiltest.rb +10 -5
  159. data/test/puppettest.rb +193 -21
  160. data/test/server/authstore.rb +13 -4
  161. data/test/server/bucket.rb +33 -8
  162. data/test/server/ca.rb +44 -6
  163. data/test/server/master.rb +6 -7
  164. data/test/server/pelement.rb +15 -5
  165. data/test/server/report.rb +93 -0
  166. data/test/server/runner.rb +107 -0
  167. data/test/server/server.rb +28 -1
  168. data/test/types/cron.rb +339 -31
  169. data/test/types/file.rb +256 -24
  170. data/test/types/filebucket.rb +6 -2
  171. data/test/types/filesources.rb +41 -92
  172. data/test/types/group.rb +31 -1
  173. data/test/types/host.rb +2 -1
  174. data/test/types/mount.rb +18 -1
  175. data/test/types/package.rb +200 -18
  176. data/test/types/service.rb +5 -1
  177. data/test/types/sshkey.rb +2 -1
  178. data/test/types/symlink.rb +3 -2
  179. data/test/types/type.rb +180 -1
  180. data/test/types/user.rb +65 -27
  181. data/test/types/yumrepo.rb +15 -0
  182. data/test/types/zone.rb +437 -0
  183. metadata +43 -4
  184. data/bin/cf2puppet +0 -186
  185. 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
- return @puppetserver.authconfig.allowed?(method, client, ip)
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
- Servlet.log "Allowing %s(%s) trusted access to %s" %
65
+ Puppet.info "Allowing %s(%s) trusted access to %s" %
52
66
  [client, ip, method]
53
67
  return true
54
68
  else
55
- Servlet.log "Denying %s(%s) trusted access to %s on %s" %
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 1117 2006-04-19 15:35:04Z luke $
357
+ # $Id: ca.rb 1145 2006-04-28 04:08:38Z luke $
@@ -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
- begin
45
- events = @state.sync
46
- if events.nil?
47
- return nil
48
- end
47
+ # The transaction catches any exceptions here.
48
+ events = @state.sync
49
+ if events.nil?
50
+ return nil
51
+ end
49
52
 
50
- if events.is_a?(Array)
51
- if events.empty?
52
- return nil
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
- return events.collect { |event|
59
- # default to a simple event type
60
- if ! event.is_a?(Symbol)
61
- @state.warning("State '%s' returned invalid event '%s'; resetting to default" %
62
- [@state.class,event])
63
-
64
- event = @state.parent.class.name.id2name + "_changed"
65
- end
66
-
67
- # i should maybe include object type, but the event type
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
- raise
86
- # there should be a way to ask the state what type of event
87
- # it would have generated, but...
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
- @state.log "Failed: " + @state.change_to_s
94
- return Puppet::Event.new(
95
- :event => pname + "_failed",
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
- :message => "Failed: " + self.to_s
79
+ :source => @state.parent,
80
+ :message => self.to_s
100
81
  )
101
- end
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 966 2006-03-02 17:12:26Z luke $
129
+ # $Id: statechange.rb 1336 2006-06-29 16:05:52Z luke $
@@ -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 1098 2006-04-10 22:13:10Z luke $
98
+ # $Id: storage.rb 1408 2006-07-20 19:01:58Z luke $
@@ -6,195 +6,365 @@ require 'puppet/statechange'
6
6
 
7
7
  module Puppet
8
8
  class Transaction
9
- attr_accessor :toplevel, :component, :objects
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. Values must
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
- # a bit of a gross hack; a global list of objects that have failed to sync,
19
- # so that we can verify during later syncs that our dependencies haven't
20
- # failed
21
- def Transaction.init
22
- @@failures = Hash.new(0)
23
- Puppet::Metric.init
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
- # for now, just store the changes for executing linearly
27
- # later, we might execute them as we receive them
28
- def change(change)
29
- @changes.push change
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
- # okay, here's the deal:
33
- # a given transaction maps directly to a component, and each transaction
34
- # will only ever receive changes from its respective component
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
- #Puppet.debug "Beginning transaction %s with %s changes" %
41
- # [self.object_id, @changes.length]
136
+ @count = 0
42
137
 
43
- count = 0
44
- now = Time.now
45
- tags = Puppet[: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.split(/\s*,\s*/)
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
- events = @objects.find_all { |child|
52
- if tags
53
- child.tagged?(tags)
54
- else
55
- true # match everything when there are no tags
56
- end
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
- 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
- #Puppet.err("%s failed: %s" % [change.to_s,detail])
82
- if Puppet[:debug]
83
- puts detail.backtrace
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
- next
86
- # FIXME this should support using onerror to determine
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
- # This is kinda lame, because it can result in the same
92
- # object being modified multiple times, but that's difficult
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
- unless events.nil? or (events.is_a?(Array) and events.empty?)
97
- change.changed = true
98
- end
179
+ # And return the events for collection
99
180
  events
100
- }
101
- }.flatten.reject { |child|
102
- child.nil? # remove empties
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
- self.propagate(events)
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 course, this won't work on the second run
122
- unless defined? @@failures
123
- @toplevel = true
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
- # Respond to each of the events. This method walks up the parent tree,
131
- # triggering each parent in turn. It's important that the transaction
132
- # itself know whether a given subscription fails, so that it can respond
133
- # appropriately (when we get to the point where we're responding to events).
134
- def propagate(events)
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
- while source
139
- Puppet::Event::Subscription.trigger(source, event) do |sub|
140
- begin
141
- sub.trigger(self)
142
- rescue => detail
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
- # Reset the source if there's a parent obj
151
- source = source.parent
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
- events = @changes.reverse.collect { |change|
160
- if change.is_a?(Puppet::StateChange)
161
- # skip changes that were never actually run
162
- unless change.changed
163
- Puppet.debug "%s was not changed" % change.to_s
164
- next
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
- #change.transaction = self
167
- begin
168
- change.backward
169
- rescue => detail
170
- Puppet.err("%s rollback failed: %s" % [change,detail])
171
- if Puppet[:debug]
172
- puts detail.backtrace
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
- next
175
- # at this point, we would normally do error handling
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
- self.propagate(events)
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
- # $Id: transaction.rb 1005 2006-03-11 22:18:59Z luke $
376
+ require 'puppet/transaction/report'
377
+
378
+ # $Id: transaction.rb 1351 2006-07-01 17:27:54Z luke $