puppet 0.9.2
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 +0 -0
- data/COPYING +340 -0
- data/LICENSE +17 -0
- data/README +24 -0
- data/Rakefile +294 -0
- data/TODO +4 -0
- data/bin/cf2puppet +186 -0
- data/bin/puppet +176 -0
- data/bin/puppetca +213 -0
- data/bin/puppetd +246 -0
- data/bin/puppetdoc +184 -0
- data/bin/puppetmasterd +258 -0
- data/examples/code/allatonce +13 -0
- data/examples/code/assignments +11 -0
- data/examples/code/classing +35 -0
- data/examples/code/components +73 -0
- data/examples/code/execs +16 -0
- data/examples/code/failers/badclassnoparam +10 -0
- data/examples/code/failers/badclassparam +10 -0
- data/examples/code/failers/badcompnoparam +9 -0
- data/examples/code/failers/badcompparam +9 -0
- data/examples/code/failers/badtypeparam +3 -0
- data/examples/code/file.bl +11 -0
- data/examples/code/filedefaults +10 -0
- data/examples/code/fileparsing +116 -0
- data/examples/code/filerecursion +15 -0
- data/examples/code/functions +3 -0
- data/examples/code/groups +7 -0
- data/examples/code/head +30 -0
- data/examples/code/importing +8 -0
- data/examples/code/nodes +20 -0
- data/examples/code/one +8 -0
- data/examples/code/relationships +34 -0
- data/examples/code/selectors +28 -0
- data/examples/code/simpletests +11 -0
- data/examples/code/snippets/argumentdefaults +14 -0
- data/examples/code/snippets/casestatement +39 -0
- data/examples/code/snippets/classheirarchy.pp +15 -0
- data/examples/code/snippets/classincludes.pp +17 -0
- data/examples/code/snippets/classpathtest +11 -0
- data/examples/code/snippets/dirchmod +19 -0
- data/examples/code/snippets/failmissingexecpath.pp +13 -0
- data/examples/code/snippets/falsevalues.pp +3 -0
- data/examples/code/snippets/filecreate +11 -0
- data/examples/code/snippets/implicititeration +15 -0
- data/examples/code/snippets/multipleinstances +7 -0
- data/examples/code/snippets/namevartest +9 -0
- data/examples/code/snippets/scopetest +13 -0
- data/examples/code/snippets/selectorvalues.pp +22 -0
- data/examples/code/snippets/simpledefaults +5 -0
- data/examples/code/snippets/simpleselector +38 -0
- data/examples/code/svncommit +13 -0
- data/examples/root/bin/sleeper +69 -0
- data/examples/root/etc/configfile +0 -0
- data/examples/root/etc/debian-passwd +29 -0
- data/examples/root/etc/debian-syslog.conf +71 -0
- data/examples/root/etc/init.d/sleeper +65 -0
- data/examples/root/etc/otherfile +0 -0
- data/examples/root/etc/puppet/fileserver.conf +3 -0
- data/examples/root/etc/puppet/puppetmasterd.conf +10 -0
- data/ext/module:puppet +195 -0
- data/install.rb +270 -0
- data/lib/puppet.rb +249 -0
- data/lib/puppet/base64.rb +19 -0
- data/lib/puppet/client.rb +519 -0
- data/lib/puppet/config.rb +49 -0
- data/lib/puppet/daemon.rb +208 -0
- data/lib/puppet/element.rb +71 -0
- data/lib/puppet/event.rb +259 -0
- data/lib/puppet/log.rb +321 -0
- data/lib/puppet/metric.rb +250 -0
- data/lib/puppet/parsedfile.rb +38 -0
- data/lib/puppet/parser/ast.rb +1560 -0
- data/lib/puppet/parser/interpreter.rb +150 -0
- data/lib/puppet/parser/lexer.rb +226 -0
- data/lib/puppet/parser/parser.rb +1354 -0
- data/lib/puppet/parser/scope.rb +755 -0
- data/lib/puppet/server.rb +170 -0
- data/lib/puppet/server/authstore.rb +227 -0
- data/lib/puppet/server/ca.rb +140 -0
- data/lib/puppet/server/filebucket.rb +147 -0
- data/lib/puppet/server/fileserver.rb +477 -0
- data/lib/puppet/server/logger.rb +43 -0
- data/lib/puppet/server/master.rb +103 -0
- data/lib/puppet/server/servlet.rb +247 -0
- data/lib/puppet/sslcertificates.rb +737 -0
- data/lib/puppet/statechange.rb +150 -0
- data/lib/puppet/storage.rb +95 -0
- data/lib/puppet/transaction.rb +179 -0
- data/lib/puppet/transportable.rb +151 -0
- data/lib/puppet/type.rb +1354 -0
- data/lib/puppet/type/component.rb +141 -0
- data/lib/puppet/type/cron.rb +543 -0
- data/lib/puppet/type/exec.rb +316 -0
- data/lib/puppet/type/group.rb +152 -0
- data/lib/puppet/type/nameservice.rb +3 -0
- data/lib/puppet/type/nameservice/netinfo.rb +173 -0
- data/lib/puppet/type/nameservice/objectadd.rb +146 -0
- data/lib/puppet/type/nameservice/posix.rb +200 -0
- data/lib/puppet/type/package.rb +420 -0
- data/lib/puppet/type/package/apt.rb +70 -0
- data/lib/puppet/type/package/dpkg.rb +108 -0
- data/lib/puppet/type/package/rpm.rb +81 -0
- data/lib/puppet/type/package/sun.rb +117 -0
- data/lib/puppet/type/package/yum.rb +58 -0
- data/lib/puppet/type/pfile.rb +569 -0
- data/lib/puppet/type/pfile/checksum.rb +219 -0
- data/lib/puppet/type/pfile/create.rb +108 -0
- data/lib/puppet/type/pfile/group.rb +129 -0
- data/lib/puppet/type/pfile/mode.rb +131 -0
- data/lib/puppet/type/pfile/source.rb +264 -0
- data/lib/puppet/type/pfile/type.rb +31 -0
- data/lib/puppet/type/pfile/uid.rb +166 -0
- data/lib/puppet/type/pfilebucket.rb +80 -0
- data/lib/puppet/type/pprocess.rb +97 -0
- data/lib/puppet/type/service.rb +347 -0
- data/lib/puppet/type/service/base.rb +17 -0
- data/lib/puppet/type/service/debian.rb +50 -0
- data/lib/puppet/type/service/init.rb +145 -0
- data/lib/puppet/type/service/smf.rb +29 -0
- data/lib/puppet/type/state.rb +182 -0
- data/lib/puppet/type/symlink.rb +183 -0
- data/lib/puppet/type/tidy.rb +183 -0
- data/lib/puppet/type/typegen.rb +149 -0
- data/lib/puppet/type/typegen/filerecord.rb +243 -0
- data/lib/puppet/type/typegen/filetype.rb +316 -0
- data/lib/puppet/type/user.rb +290 -0
- data/lib/puppet/util.rb +138 -0
- data/test/certmgr/certmgr.rb +265 -0
- data/test/client/client.rb +203 -0
- data/test/executables/puppetbin.rb +53 -0
- data/test/executables/puppetca.rb +79 -0
- data/test/executables/puppetd.rb +71 -0
- data/test/executables/puppetmasterd.rb +153 -0
- data/test/executables/puppetmodule.rb +60 -0
- data/test/language/ast.rb +412 -0
- data/test/language/interpreter.rb +71 -0
- data/test/language/scope.rb +412 -0
- data/test/language/snippets.rb +445 -0
- data/test/other/events.rb +111 -0
- data/test/other/log.rb +195 -0
- data/test/other/metrics.rb +92 -0
- data/test/other/overrides.rb +115 -0
- data/test/other/parsedfile.rb +31 -0
- data/test/other/relationships.rb +113 -0
- data/test/other/state.rb +106 -0
- data/test/other/storage.rb +39 -0
- data/test/other/transactions.rb +235 -0
- data/test/parser/lexer.rb +120 -0
- data/test/parser/parser.rb +180 -0
- data/test/puppet/conffiles.rb +104 -0
- data/test/puppet/defaults.rb +100 -0
- data/test/puppet/error.rb +23 -0
- data/test/puppet/utiltest.rb +120 -0
- data/test/puppettest.rb +774 -0
- data/test/server/authstore.rb +209 -0
- data/test/server/bucket.rb +227 -0
- data/test/server/ca.rb +201 -0
- data/test/server/fileserver.rb +710 -0
- data/test/server/logger.rb +175 -0
- data/test/server/master.rb +150 -0
- data/test/server/server.rb +130 -0
- data/test/tagging/tagging.rb +80 -0
- data/test/test +51 -0
- data/test/types/basic.rb +119 -0
- data/test/types/component.rb +272 -0
- data/test/types/cron.rb +261 -0
- data/test/types/exec.rb +273 -0
- data/test/types/file.rb +616 -0
- data/test/types/filebucket.rb +167 -0
- data/test/types/fileignoresource.rb +287 -0
- data/test/types/filesources.rb +587 -0
- data/test/types/filetype.rb +162 -0
- data/test/types/group.rb +271 -0
- data/test/types/package.rb +205 -0
- data/test/types/query.rb +101 -0
- data/test/types/service.rb +100 -0
- data/test/types/symlink.rb +93 -0
- data/test/types/tidy.rb +124 -0
- data/test/types/type.rb +135 -0
- data/test/types/user.rb +371 -0
- metadata +243 -0
@@ -0,0 +1,141 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
# $Id: component.rb 740 2005-11-01 20:22:19Z luke $
|
4
|
+
|
5
|
+
# the object allowing us to build complex structures
|
6
|
+
# this thing contains everything else, including itself
|
7
|
+
|
8
|
+
require 'puppet'
|
9
|
+
require 'puppet/type'
|
10
|
+
require 'puppet/transaction'
|
11
|
+
|
12
|
+
module Puppet
|
13
|
+
class Type
|
14
|
+
class Component < Puppet::Type
|
15
|
+
include Enumerable
|
16
|
+
|
17
|
+
@name = :component
|
18
|
+
@namevar = :name
|
19
|
+
|
20
|
+
@states = []
|
21
|
+
@parameters = [:name,:type]
|
22
|
+
|
23
|
+
# topo sort functions
|
24
|
+
def self.sort(objects)
|
25
|
+
list = []
|
26
|
+
tmplist = {}
|
27
|
+
|
28
|
+
objects.each { |obj|
|
29
|
+
self.recurse(obj, tmplist, list)
|
30
|
+
}
|
31
|
+
|
32
|
+
return list.flatten
|
33
|
+
end
|
34
|
+
|
35
|
+
# FIXME this method assumes that dependencies themselves
|
36
|
+
# are never components
|
37
|
+
def self.recurse(obj, inlist, list)
|
38
|
+
if inlist.include?(obj.object_id)
|
39
|
+
return
|
40
|
+
end
|
41
|
+
inlist[obj.object_id] = true
|
42
|
+
obj.eachdependency { |req|
|
43
|
+
self.recurse(req, inlist, list)
|
44
|
+
}
|
45
|
+
|
46
|
+
if obj.is_a?(Puppet::Type::Component)
|
47
|
+
obj.each { |child|
|
48
|
+
self.recurse(child, inlist, list)
|
49
|
+
}
|
50
|
+
else
|
51
|
+
list << obj
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Remove a child from the component.
|
56
|
+
def delete(child)
|
57
|
+
if @children.include?(child)
|
58
|
+
@children.delete(child)
|
59
|
+
return true
|
60
|
+
else
|
61
|
+
return false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Return each child in turn.
|
66
|
+
def each
|
67
|
+
@children.each { |child| yield child }
|
68
|
+
end
|
69
|
+
|
70
|
+
# Return a flattened array containing all of the children
|
71
|
+
# and all child components' children, sorted in order of dependencies.
|
72
|
+
def flatten
|
73
|
+
self.class.sort(@children).flatten
|
74
|
+
end
|
75
|
+
|
76
|
+
# Initialize a new component
|
77
|
+
def initialize(args)
|
78
|
+
@children = []
|
79
|
+
|
80
|
+
# it makes sense to have a more reasonable default here than 'false'
|
81
|
+
unless args.include?(:type) or args.include?("type")
|
82
|
+
args[:type] = "component"
|
83
|
+
end
|
84
|
+
super(args)
|
85
|
+
end
|
86
|
+
|
87
|
+
# flatten all children, sort them, and evaluate them in order
|
88
|
+
# this is only called on one component over the whole system
|
89
|
+
# this also won't work with scheduling, but eh
|
90
|
+
def evaluate
|
91
|
+
transaction = Puppet::Transaction.new(self.flatten)
|
92
|
+
transaction.component = self
|
93
|
+
return transaction
|
94
|
+
end
|
95
|
+
|
96
|
+
def name
|
97
|
+
#return self[:name]
|
98
|
+
unless defined? @name
|
99
|
+
if self[:type] == self[:name]
|
100
|
+
@name = self[:type]
|
101
|
+
else
|
102
|
+
@name = "%s[%s]" % [self[:type],self[:name]]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
return @name
|
106
|
+
end
|
107
|
+
|
108
|
+
def push(*ary)
|
109
|
+
ary.each { |child|
|
110
|
+
unless child.is_a?(Puppet::Element)
|
111
|
+
self.debug "Got object of type %s" % child.class
|
112
|
+
raise Puppet::DevError.new(
|
113
|
+
"Containers can only contain Puppet::Elements, not %s" %
|
114
|
+
child.class
|
115
|
+
)
|
116
|
+
end
|
117
|
+
@children.push child
|
118
|
+
child.parent = self
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
def refresh
|
123
|
+
@children.collect { |child|
|
124
|
+
if child.respond_to?(:refresh)
|
125
|
+
child.refresh
|
126
|
+
end
|
127
|
+
}
|
128
|
+
end
|
129
|
+
|
130
|
+
#def retrieve
|
131
|
+
# self.collect { |child|
|
132
|
+
# child.retrieve
|
133
|
+
# }
|
134
|
+
#end
|
135
|
+
|
136
|
+
def to_s
|
137
|
+
return "component(%s)" % self.name
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,543 @@
|
|
1
|
+
# A Puppet::Type class to manage cron jobs. Some abstraction is done,
|
2
|
+
# so that different platforms' versions of +crontab+ all work equivalently.
|
3
|
+
|
4
|
+
require 'etc'
|
5
|
+
require 'facter'
|
6
|
+
require 'puppet/type/state'
|
7
|
+
|
8
|
+
module Puppet
|
9
|
+
# The Puppet::CronType modules are responsible for the actual abstraction.
|
10
|
+
# They must implement three module functions: +read+, +write+, and +remove+,
|
11
|
+
# analogous to the three flags accepted by most implementations of +crontab+.
|
12
|
+
# All of these methods require the user name to be passed in.
|
13
|
+
#
|
14
|
+
# These modules operate on the strings that are ore become the cron tabs --
|
15
|
+
# they do not have any semantic understanding of what they are reading or
|
16
|
+
# writing.
|
17
|
+
module CronType
|
18
|
+
# Retrieve the uid of a user. This is duplication of code, but the unless
|
19
|
+
# I start using Puppet::Type::User objects here, it's a much better design.
|
20
|
+
def self.uid(user)
|
21
|
+
begin
|
22
|
+
return Etc.getpwnam(user).uid
|
23
|
+
rescue ArgumentError
|
24
|
+
raise Puppet::Error, "User %s not found" % user
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# This module covers nearly everyone; SunOS is only known exception so far.
|
29
|
+
module Default
|
30
|
+
# Only add the -u flag when the user is different. Fedora apparently
|
31
|
+
# does not think I should be allowed to set the user to myself.
|
32
|
+
def self.cmdbase(user)
|
33
|
+
uid = CronType.uid(user)
|
34
|
+
cmd = nil
|
35
|
+
if uid == Process.uid
|
36
|
+
return "crontab"
|
37
|
+
else
|
38
|
+
return "crontab -u #{user}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Read a specific user's cron tab.
|
43
|
+
def self.read(user)
|
44
|
+
tab = %x{#{self.cmdbase(user)} -l 2>/dev/null}
|
45
|
+
end
|
46
|
+
|
47
|
+
# Remove a specific user's cron tab.
|
48
|
+
def self.remove(user)
|
49
|
+
%x{#{self.cmdbase(user)} -r 2>/dev/null}
|
50
|
+
end
|
51
|
+
|
52
|
+
# Overwrite a specific user's cron tab; must be passed the user name
|
53
|
+
# and the text with which to create the cron tab.
|
54
|
+
def self.write(user, text)
|
55
|
+
IO.popen("#{self.cmdbase(user)} -", "w") { |p|
|
56
|
+
p.print text
|
57
|
+
}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# SunOS has completely different cron commands; this module implements
|
62
|
+
# its versions.
|
63
|
+
module SunOS
|
64
|
+
# Read a specific user's cron tab.
|
65
|
+
def self.read(user)
|
66
|
+
Puppet::Util.asuser(user) {
|
67
|
+
%x{crontab -l 2>/dev/null}
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
# Remove a specific user's cron tab.
|
72
|
+
def self.remove(user)
|
73
|
+
Puppet.asuser(user) {
|
74
|
+
%x{crontab -r 2>/dev/null}
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
# Overwrite a specific user's cron tab; must be passed the user name
|
79
|
+
# and the text with which to create the cron tab.
|
80
|
+
def self.write(user, text)
|
81
|
+
Puppet.asuser(user) {
|
82
|
+
IO.popen("crontab", "w") { |p|
|
83
|
+
p.print text
|
84
|
+
}
|
85
|
+
}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class State
|
91
|
+
# This is Cron's single state. Somewhat uniquely, this state does not
|
92
|
+
# actually change anything -- it just calls +@parent.sync+, which writes
|
93
|
+
# out the whole cron tab for the user in question. There is no real way
|
94
|
+
# to change individual cron jobs without rewriting the entire cron file.
|
95
|
+
#
|
96
|
+
# Note that this means that managing many cron jobs for a given user
|
97
|
+
# could currently result in multiple write sessions for that user.
|
98
|
+
class CronCommand < Puppet::State
|
99
|
+
@name = :command
|
100
|
+
@doc = "The command to execute in the cron job. The environment
|
101
|
+
provided to the command varies by local system rules, and it is
|
102
|
+
best to always provide a fully qualified command. The user's
|
103
|
+
profile is not sourced when the command is run, so if the
|
104
|
+
user's environment is desired it should be sourced manually."
|
105
|
+
|
106
|
+
# Normally this would retrieve the current value, but our state is not
|
107
|
+
# actually capable of doing so. The Cron class does the actual tab
|
108
|
+
# retrieval, so all this method does is default to :notfound for @is.
|
109
|
+
def retrieve
|
110
|
+
unless defined? @is and ! @is.nil?
|
111
|
+
@is = :notfound
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Determine whether the cron job should be destroyed, and figure
|
116
|
+
# out which event to return. Finally, call @parent.sync to write the
|
117
|
+
# cron tab.
|
118
|
+
def sync
|
119
|
+
@parent.store
|
120
|
+
|
121
|
+
event = nil
|
122
|
+
if @is == :notfound
|
123
|
+
#@is = @should
|
124
|
+
event = :cron_created
|
125
|
+
elsif self.should == :notfound
|
126
|
+
@parent.remove(true)
|
127
|
+
event = :cron_deleted
|
128
|
+
elsif self.should == @is
|
129
|
+
self.err "Uh, they're both %s" % self.should
|
130
|
+
return nil
|
131
|
+
else
|
132
|
+
#@is = @should
|
133
|
+
self.err "@is is %s" % @is
|
134
|
+
event = :cron_changed
|
135
|
+
end
|
136
|
+
|
137
|
+
@parent.store
|
138
|
+
|
139
|
+
return event
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
class Type
|
145
|
+
# Model the actual cron jobs. Supports all of the normal cron job fields
|
146
|
+
# as parameters, with the 'command' as the single state. Also requires a
|
147
|
+
# completely symbolic 'name' paremeter, which gets written to the file
|
148
|
+
# and is used to manage the job.
|
149
|
+
class Cron < Type
|
150
|
+
@states = [
|
151
|
+
Puppet::State::CronCommand
|
152
|
+
]
|
153
|
+
|
154
|
+
@parameters = [
|
155
|
+
:name,
|
156
|
+
:user,
|
157
|
+
:minute,
|
158
|
+
:hour,
|
159
|
+
:weekday,
|
160
|
+
:month,
|
161
|
+
:monthday
|
162
|
+
]
|
163
|
+
|
164
|
+
@paramdoc[:name] = "The symbolic name of the cron job. This name
|
165
|
+
is used for human reference only."
|
166
|
+
@paramdoc[:user] = "The user to run the command as. This user must
|
167
|
+
be allowed to run cron jobs, which is not currently checked by
|
168
|
+
Puppet."
|
169
|
+
@paramdoc[:minute] = "The minute at which to run the cron job.
|
170
|
+
Optional; if specified, must be between 0 and 59, inclusive."
|
171
|
+
@paramdoc[:hour] = "The hour at which to run the cron job. Optional;
|
172
|
+
if specified, must be between 0 and 23, inclusive."
|
173
|
+
@paramdoc[:weekday] = "The weekday on which to run the command.
|
174
|
+
Optional; if specified, must be between 0 and 6, inclusive, with
|
175
|
+
0 being Sunday, or must be the name of the day (e.g., Tuesday)."
|
176
|
+
@paramdoc[:month] = "The month of the year. Optional; if specified
|
177
|
+
must be between 1 and 12 or the month name (e.g., December)."
|
178
|
+
@paramdoc[:monthday] = "The day of the month on which to run the
|
179
|
+
command. Optional; if specified, must be between 1 and 31."
|
180
|
+
|
181
|
+
@doc = "Installs and manages cron jobs. All fields except the command
|
182
|
+
and the user are optional, although specifying no periodic
|
183
|
+
fields would result in the command being executed every
|
184
|
+
minute. While the name of the cron job is not part of the actual
|
185
|
+
job, it is used by Puppet to store and retrieve it. If you specify
|
186
|
+
a cron job that matches an existing job in every way except name,
|
187
|
+
then the jobs will be considered equivalent and the new name will
|
188
|
+
be permanently associated with that job. Once this association is
|
189
|
+
made and synced to disk, you can then manage the job normally."
|
190
|
+
@name = :cron
|
191
|
+
@namevar = :name
|
192
|
+
|
193
|
+
@loaded = {}
|
194
|
+
|
195
|
+
@synced = {}
|
196
|
+
|
197
|
+
@instances = {}
|
198
|
+
|
199
|
+
@@weekdays = %w{sunday monday tuesday wednesday thursday friday saturday}
|
200
|
+
|
201
|
+
@@months = %w{january february march april may june july
|
202
|
+
august september october november december}
|
203
|
+
|
204
|
+
case Facter["operatingsystem"].value
|
205
|
+
when "SunOS":
|
206
|
+
@crontype = Puppet::CronType::SunOS
|
207
|
+
else
|
208
|
+
@crontype = Puppet::CronType::Default
|
209
|
+
end
|
210
|
+
|
211
|
+
class << self
|
212
|
+
attr_accessor :crontype
|
213
|
+
end
|
214
|
+
|
215
|
+
attr_accessor :uid
|
216
|
+
|
217
|
+
# Override the Puppet::Type#[]= method so that we can store the instances
|
218
|
+
# in per-user arrays. Then just call +super+.
|
219
|
+
def self.[]=(name, object)
|
220
|
+
self.instance(object)
|
221
|
+
super
|
222
|
+
end
|
223
|
+
|
224
|
+
# In addition to removing the instances in @objects, Cron has to remove
|
225
|
+
# per-user cron tab information.
|
226
|
+
def self.clear
|
227
|
+
@instances = {}
|
228
|
+
@loaded = {}
|
229
|
+
@synced = {}
|
230
|
+
super
|
231
|
+
end
|
232
|
+
|
233
|
+
# Override the default Puppet::Type method, because instances
|
234
|
+
# also need to be deleted from the @instances hash
|
235
|
+
def self.delete(child)
|
236
|
+
if @instances.include?(child[:user])
|
237
|
+
if @instances[child[:user]].include?(child)
|
238
|
+
@instances[child[:user]].delete(child)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
super
|
242
|
+
end
|
243
|
+
|
244
|
+
# Return the fields found in the cron tab.
|
245
|
+
def self.fields
|
246
|
+
return [:minute, :hour, :monthday, :month, :weekday, :command]
|
247
|
+
end
|
248
|
+
|
249
|
+
# Return the header placed at the top of each generated file, warning
|
250
|
+
# users that modifying this file manually is probably a bad idea.
|
251
|
+
def self.header
|
252
|
+
%{#This file was autogenerated at #{Time.now} by puppet. While it
|
253
|
+
# can still be managed manually, it is definitely not recommended.
|
254
|
+
# Note particularly that the comments starting with 'Puppet Name' should
|
255
|
+
# not be deleted, as doing so could cause duplicate cron jobs.\n}
|
256
|
+
end
|
257
|
+
|
258
|
+
# Store a new instance of a cron job. Called from Cron#initialize.
|
259
|
+
def self.instance(obj)
|
260
|
+
user = obj[:user]
|
261
|
+
if @instances.include?(user)
|
262
|
+
unless @instances[obj[:user]].include?(obj)
|
263
|
+
@instances[obj[:user]] << obj
|
264
|
+
end
|
265
|
+
else
|
266
|
+
@instances[obj[:user]] = [obj]
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
# Parse a user's cron job into individual cron objects.
|
271
|
+
#
|
272
|
+
# Autogenerates names for any jobs that don't already have one; these
|
273
|
+
# names will get written back to the file.
|
274
|
+
#
|
275
|
+
# This method also stores existing comments, and it stores all cron
|
276
|
+
# jobs in order, mostly so that comments are retained in the order
|
277
|
+
# they were written and in proximity to the same jobs.
|
278
|
+
def self.parse(user, text)
|
279
|
+
count = 0
|
280
|
+
hash = {}
|
281
|
+
name = nil
|
282
|
+
unless @instances.include?(user)
|
283
|
+
@instances[user] = []
|
284
|
+
end
|
285
|
+
text.chomp.split("\n").each { |line|
|
286
|
+
case line
|
287
|
+
when /^# Puppet Name: (\w+)$/: name = $1
|
288
|
+
when /^#/:
|
289
|
+
# add other comments to the list as they are
|
290
|
+
@instances[user] << line
|
291
|
+
else
|
292
|
+
ary = line.split(" ")
|
293
|
+
fields().each { |param|
|
294
|
+
value = ary.shift
|
295
|
+
unless value == "*"
|
296
|
+
hash[param] = value
|
297
|
+
end
|
298
|
+
}
|
299
|
+
|
300
|
+
if ary.length > 0
|
301
|
+
hash[:command] += " " + ary.join(" ")
|
302
|
+
end
|
303
|
+
cron = nil
|
304
|
+
unless name
|
305
|
+
Puppet.info "Autogenerating name for %s" % hash[:command]
|
306
|
+
name = "cron-%s" % hash.object_id
|
307
|
+
end
|
308
|
+
|
309
|
+
unless hash.include?(:command)
|
310
|
+
raise Puppet::DevError, "No command for %s" % name
|
311
|
+
end
|
312
|
+
# if the cron already exists with that name...
|
313
|
+
if cron = Puppet::Type::Cron[name]
|
314
|
+
# do nothing...
|
315
|
+
elsif tmp = @instances[user].reject { |obj|
|
316
|
+
! obj.is_a?(Cron)
|
317
|
+
}.find { |obj|
|
318
|
+
obj.should(:command) == hash[:command]
|
319
|
+
}
|
320
|
+
# if we can find a cron whose spec exactly matches
|
321
|
+
|
322
|
+
# we now have a cron job whose command exactly matches
|
323
|
+
# let's see if the other fields match
|
324
|
+
txt = tmp.to_cron.sub(/#.+\n/,'')
|
325
|
+
|
326
|
+
if txt == line
|
327
|
+
cron = tmp
|
328
|
+
end
|
329
|
+
else
|
330
|
+
# create a new cron job, since no existing one
|
331
|
+
# seems to match
|
332
|
+
cron = Puppet::Type::Cron.create(
|
333
|
+
:name => name
|
334
|
+
)
|
335
|
+
end
|
336
|
+
|
337
|
+
hash.each { |param, value|
|
338
|
+
cron.is = [param, value]
|
339
|
+
}
|
340
|
+
hash.clear
|
341
|
+
name = nil
|
342
|
+
count += 1
|
343
|
+
end
|
344
|
+
}
|
345
|
+
end
|
346
|
+
|
347
|
+
# Retrieve a given user's cron job, using the @crontype's +retrieve+
|
348
|
+
# method. Returns nil if there was no cron job; else, returns the
|
349
|
+
# number of cron instances found.
|
350
|
+
def self.retrieve(user)
|
351
|
+
#%x{crontab -u #{user} -l 2>/dev/null}.split("\n").each { |line|
|
352
|
+
text = @crontype.read(user)
|
353
|
+
if $? != 0
|
354
|
+
# there is no cron file
|
355
|
+
return nil
|
356
|
+
else
|
357
|
+
self.parse(user, text)
|
358
|
+
end
|
359
|
+
|
360
|
+
@loaded[user] = Time.now
|
361
|
+
end
|
362
|
+
|
363
|
+
# Store the user's cron tab. Collects the text of the new tab and
|
364
|
+
# sends it to the +@crontype+ module's +write+ function. Also adds
|
365
|
+
# header warning users not to modify the file directly.
|
366
|
+
def self.store(user)
|
367
|
+
if @instances.include?(user)
|
368
|
+
@crontype.write(user, self.header + self.tab(user))
|
369
|
+
@synced[user] = Time.now
|
370
|
+
else
|
371
|
+
Puppet.notice "No cron instances for %s" % user
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
# Collect all Cron instances for a given user and convert them
|
376
|
+
# into literal text.
|
377
|
+
def self.tab(user)
|
378
|
+
if @instances.include?(user)
|
379
|
+
return @instances[user].collect { |obj|
|
380
|
+
if obj.is_a?(Cron)
|
381
|
+
obj.to_cron
|
382
|
+
else
|
383
|
+
obj.to_s
|
384
|
+
end
|
385
|
+
}.join("\n") + "\n"
|
386
|
+
else
|
387
|
+
Puppet.notice "No cron instances for %s" % user
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
# Return the last time a given user's cron tab was loaded. Could
|
392
|
+
# be used for reducing writes, but currently is not.
|
393
|
+
def self.loaded?(user)
|
394
|
+
if @loaded.include?(user)
|
395
|
+
return @loaded[user]
|
396
|
+
else
|
397
|
+
return nil
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
# A method used to do parameter input handling. Converts integers
|
402
|
+
# in string form to actual integers, and returns the value if it's
|
403
|
+
# an integer or false if it's just a normal string.
|
404
|
+
def numfix(num)
|
405
|
+
if num =~ /^\d+$/
|
406
|
+
return num.to_i
|
407
|
+
elsif num.is_a?(Integer)
|
408
|
+
return num
|
409
|
+
else
|
410
|
+
return false
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
# Verify that a number is within the specified limits. Return the
|
415
|
+
# number if it is, or false if it is not.
|
416
|
+
def limitcheck(num, lower, upper)
|
417
|
+
if num >= lower and num <= upper
|
418
|
+
return num
|
419
|
+
else
|
420
|
+
return false
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
# Verify that a value falls within the specified array. Does case
|
425
|
+
# insensitive matching, and supports matching either the entire word
|
426
|
+
# or the first three letters of the word.
|
427
|
+
def alphacheck(value, ary)
|
428
|
+
tmp = value.downcase
|
429
|
+
if tmp.length == 3
|
430
|
+
ary.each_with_index { |name, index|
|
431
|
+
if name =~ /#{tmp}/i
|
432
|
+
return index
|
433
|
+
end
|
434
|
+
}
|
435
|
+
else
|
436
|
+
if ary.include?(tmp)
|
437
|
+
return ary.index(tmp)
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
return false
|
442
|
+
end
|
443
|
+
|
444
|
+
# The method that does all of the actual parameter value checking; called
|
445
|
+
# by all of the +param<name>=+ methods. Requires the value, type, and
|
446
|
+
# bounds, and optionally supports a boolean of whether to do alpha
|
447
|
+
# checking, and if so requires the ary against which to do the checking.
|
448
|
+
def parameter(values, type, lower, upper, alpha = nil, ary = nil)
|
449
|
+
unless values.is_a?(Array)
|
450
|
+
if values =~ /,/
|
451
|
+
values = values.split(/,/)
|
452
|
+
else
|
453
|
+
values = [values]
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
@parameters[type] = values.collect { |value|
|
458
|
+
retval = nil
|
459
|
+
if num = numfix(value)
|
460
|
+
retval = limitcheck(num, lower, upper)
|
461
|
+
elsif alpha
|
462
|
+
retval = alphacheck(value, ary)
|
463
|
+
end
|
464
|
+
|
465
|
+
if retval
|
466
|
+
@parameters[type] = retval
|
467
|
+
else
|
468
|
+
raise Puppet::Error, "%s is not a valid %s" %
|
469
|
+
[value, type]
|
470
|
+
end
|
471
|
+
}
|
472
|
+
end
|
473
|
+
|
474
|
+
def paramminute=(value)
|
475
|
+
parameter(value, :minute, 0, 59)
|
476
|
+
end
|
477
|
+
|
478
|
+
def paramhour=(value)
|
479
|
+
parameter(value, :hour, 0, 23)
|
480
|
+
end
|
481
|
+
|
482
|
+
def paramweekday=(value)
|
483
|
+
parameter(value, :weekday, 0, 6, true, @@weekdays)
|
484
|
+
end
|
485
|
+
|
486
|
+
def parammonth=(value)
|
487
|
+
parameter(value, :month, 1, 12, true, @@months)
|
488
|
+
end
|
489
|
+
|
490
|
+
def parammonthday=(value)
|
491
|
+
parameter(value, :monthday, 1, 31)
|
492
|
+
end
|
493
|
+
|
494
|
+
def paramuser=(user)
|
495
|
+
require 'etc'
|
496
|
+
|
497
|
+
begin
|
498
|
+
obj = Etc.getpwnam(user)
|
499
|
+
@uid = obj.uid
|
500
|
+
rescue ArgumentError
|
501
|
+
raise Puppet::Error, "User %s not found" % user
|
502
|
+
end
|
503
|
+
@parameters[:user] = user
|
504
|
+
end
|
505
|
+
|
506
|
+
# Override the default Puppet::Type method because we need to call
|
507
|
+
# the +@crontype+ retrieve method.
|
508
|
+
def retrieve
|
509
|
+
unless @parameters.include?(:user)
|
510
|
+
raise Puppet::Error, "You must specify the cron user"
|
511
|
+
end
|
512
|
+
|
513
|
+
self.class.retrieve(@parameters[:user])
|
514
|
+
@states[:command].retrieve
|
515
|
+
end
|
516
|
+
|
517
|
+
# Write the entire user's cron tab out.
|
518
|
+
def store
|
519
|
+
self.class.store(@parameters[:user])
|
520
|
+
end
|
521
|
+
|
522
|
+
# Convert the current object a cron-style string. Adds the cron name
|
523
|
+
# as a comment above the cron job, in the form '# Puppet Name: <name>'.
|
524
|
+
def to_cron
|
525
|
+
hash = {:command => @states[:command].should || @states[:command].is }
|
526
|
+
self.class.fields().reject { |f| f == :command }.each { |param|
|
527
|
+
hash[param] = @parameters[param] || "*"
|
528
|
+
}
|
529
|
+
|
530
|
+
return "# Puppet Name: %s\n" % self.name +
|
531
|
+
self.class.fields.collect { |f|
|
532
|
+
if hash[f].is_a?(Array)
|
533
|
+
hash[f].join(",")
|
534
|
+
else
|
535
|
+
hash[f]
|
536
|
+
end
|
537
|
+
}.join(" ")
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
# $Id: cron.rb 737 2005-10-30 04:07:52Z luke $
|