2
require 'puppet/util/log'
3
require 'puppet/util/metric'
4
require 'puppet/property'
5
require 'puppet/parameter'
7
require 'puppet/util/autoload'
8
require 'puppet/metatype/manager'
9
require 'puppet/util/errors'
10
require 'puppet/util/log_paths'
11
require 'puppet/util/logging'
12
require 'puppet/util/cacher'
13
require 'puppet/file_collection/lookup'
14
require 'puppet/util/tagging'
16
# see the bottom of the file for the rest of the inclusions
21
include Puppet::Util::Errors
22
include Puppet::Util::LogPaths
23
include Puppet::Util::Logging
24
include Puppet::Util::Cacher
25
include Puppet::FileCollection::Lookup
26
include Puppet::Util::Tagging
28
###############################
29
# Code related to resource type attributes.
31
include Puppet::Util::ClassGen
32
include Puppet::Util::Warnings
33
attr_reader :properties
37
warnonce "The states method is deprecated; use properties"
41
# All parameters, in the appropriate order. The key_attributes come first, then
42
# the provider, then the properties, and finally the params and metaparams
43
# in the order they were specified in the files.
45
key_attributes | (parameters & [:provider]) | properties.collect { |property| property.name } | parameters | metaparams
48
# Retrieve an attribute alias, if there is one.
49
def self.attr_alias(param)
50
@attr_aliases[symbolize(param)]
53
# Create an alias to an existing attribute. This will cause the aliased
54
# attribute to be valid when setting and retrieving values on the instance.
55
def self.set_attr_alias(hash)
56
hash.each do |new, old|
57
@attr_aliases[symbolize(new)] = symbolize(old)
61
# Find the class associated with any given attribute.
62
def self.attrclass(name)
65
# We cache the value, since this method gets called such a huge number
66
# of times (as in, hundreds of thousands in a given run).
67
unless @attrclasses.include?(name)
68
@attrclasses[name] = case self.attrtype(name)
69
when :property; @validproperties[name]
70
when :meta; @@metaparamhash[name]
71
when :param; @paramhash[name]
77
# What type of parameter are we dealing with? Cache the results, because
78
# this method gets called so many times.
79
def self.attrtype(attr)
81
unless @attrtypes.include?(attr)
82
@attrtypes[attr] = case
83
when @validproperties.include?(attr); :property
84
when @paramhash.include?(attr); :param
85
when @@metaparamhash.include?(attr); :meta
92
def self.eachmetaparam
93
@@metaparams.each { |p| yield p.name }
96
# Create the 'ensure' class. This is a separate method so other types
97
# can easily call it and create their own 'ensure' values.
98
def self.ensurable(&block)
100
self.newproperty(:ensure, :parent => Puppet::Property::Ensure, &block)
102
self.newproperty(:ensure, :parent => Puppet::Property::Ensure) do
108
# Should we add the 'ensure' property to this class?
110
# If the class has all three of these methods defined, then it's
112
ens = [:exists?, :create, :destroy].inject { |set, method|
113
set &&= self.public_method_defined?(method)
119
# Deal with any options passed into parameters.
120
def self.handle_param_options(name, options)
121
# If it's a boolean parameter, create a method to test the value easily
123
define_method(name.to_s + "?") do
125
if val == :true or val == true
132
# Is the parameter in question a meta-parameter?
133
def self.metaparam?(param)
134
@@metaparamhash.include?(symbolize(param))
137
# Find the metaparameter class associated with a given metaparameter name.
138
def self.metaparamclass(name)
139
@@metaparamhash[symbolize(name)]
143
@@metaparams.collect { |param| param.name }
146
def self.metaparamdoc(metaparam)
147
@@metaparamhash[metaparam].doc
150
# Create a new metaparam. Requires a block and a name, stores it in the
151
# @parameters array, and does some basic checking on it.
152
def self.newmetaparam(name, options = {}, &block)
154
@@metaparamhash ||= {}
155
name = symbolize(name)
160
:parent => options[:parent] || Puppet::Parameter,
161
:prefix => "MetaParam",
162
:hash => @@metaparamhash,
163
:array => @@metaparams,
164
:attributes => options[:attributes],
170
param.required_features = options[:required_features] if options[:required_features]
172
handle_param_options(name, options)
174
param.metaparam = true
179
def self.key_attribute_parameters
180
@key_attribute_parameters ||= (
181
params = @parameters.find_all { |param|
182
param.isnamevar? or param.name == :name
187
def self.key_attributes
188
key_attribute_parameters.collect { |p| p.name }
191
def self.title_patterns
192
case key_attributes.length
195
identity = lambda {|x| x}
196
[ [ /(.*)/m, [ [key_attributes.first, identity ] ] ] ]
198
raise Puppet::DevError,"you must specify title patterns when there are two or more key attributes"
203
to_resource.uniqueness_key
206
# Create a new parameter. Requires a block and a name, stores it in the
207
# @parameters array, and does some basic checking on it.
208
def self.newparam(name, options = {}, &block)
209
options[:attributes] ||= {}
213
:parent => options[:parent] || Puppet::Parameter,
214
:attributes => options[:attributes],
216
:prefix => "Parameter",
217
:array => @parameters,
222
handle_param_options(name, options)
225
param.required_features = options[:required_features] if options[:required_features]
227
param.isnamevar if options[:namevar]
232
def self.newstate(name, options = {}, &block)
233
Puppet.warning "newstate() has been deprecrated; use newproperty(#{name})"
234
newproperty(name, options, &block)
237
# Create a new property. The first parameter must be the name of the property;
238
# this is how users will refer to the property when creating new instances.
239
# The second parameter is a hash of options; the options are:
240
# * <tt>:parent</tt>: The parent class for the property. Defaults to Puppet::Property.
241
# * <tt>:retrieve</tt>: The method to call on the provider or @parent object (if
242
# the provider is not set) to retrieve the current value.
243
def self.newproperty(name, options = {}, &block)
244
name = symbolize(name)
246
# This is here for types that might still have the old method of defining
248
unless options.is_a? Hash
249
raise Puppet::DevError,
250
"Options must be a hash, not #{options.inspect}"
253
raise Puppet::DevError, "Class #{self.name} already has a property named #{name}" if @validproperties.include?(name)
255
if parent = options[:parent]
256
options.delete(:parent)
258
parent = Puppet::Property
261
# We have to create our own, new block here because we want to define
262
# an initial :retrieve method, if told to, and then eval the passed
263
# block if available.
264
prop = genclass(name, :parent => parent, :hash => @validproperties, :attributes => options) do
265
# If they've passed a retrieve method, then override the retrieve
266
# method on the class.
267
if options[:retrieve]
268
define_method(:retrieve) do
269
provider.send(options[:retrieve])
273
class_eval(&block) if block
276
# If it's the 'ensure' property, always put it first.
278
@properties.unshift prop
286
def self.paramdoc(param)
287
@paramhash[param].doc
290
# Return the parameter names
292
return [] unless defined?(@parameters)
293
@parameters.collect { |klass| klass.name }
296
# Find the parameter class associated with a given parameter name.
297
def self.paramclass(name)
301
# Return the property class associated with a name
302
def self.propertybyname(name)
303
@validproperties[name]
306
def self.validattr?(name)
307
name = symbolize(name)
308
return true if name == :name
311
unless @validattrs.include?(name)
312
@validattrs[name] = !!(self.validproperty?(name) or self.validparameter?(name) or self.metaparam?(name))
318
# does the name reflect a valid property?
319
def self.validproperty?(name)
320
name = symbolize(name)
321
@validproperties.include?(name) && @validproperties[name]
324
# Return the list of validproperties
325
def self.validproperties
326
return {} unless defined?(@parameters)
328
@validproperties.keys
331
# does the name reflect a valid parameter?
332
def self.validparameter?(name)
333
raise Puppet::DevError, "Class #{self} has not defined parameters" unless defined?(@parameters)
334
!!(@paramhash.include?(name) or @@metaparamhash.include?(name))
337
# This is a forward-compatibility method - it's the validity interface we'll use in Puppet::Resource.
338
def self.valid_parameter?(name)
342
# Return either the attribute alias or the attribute.
344
name = symbolize(name)
345
if synonym = self.class.attr_alias(name)
352
# Are we deleting this resource?
354
obj = @parameters[:ensure] and obj.should == :absent
357
# Create a new property if it is valid but doesn't exist
358
# Returns: true if a new parameter was added, false otherwise
359
def add_property_parameter(prop_name)
360
if self.class.validproperty?(prop_name) && !@parameters[prop_name]
361
self.newattr(prop_name)
368
# The name_var is the key_attribute in the case that there is only one.
371
key_attributes = self.class.key_attributes
372
(key_attributes.length == 1) && key_attributes.first
375
# abstract accessing parameters and properties, and normalize
376
# access to always be symbols, not strings
377
# This returns a value, not an object. It returns the 'is'
378
# value, but you can also specifically return 'is' and 'should'
379
# values using 'object.is(:property)' or 'object.should(:property)'.
381
name = attr_alias(name)
383
fail("Invalid parameter #{name}(#{name.inspect})") unless self.class.validattr?(name)
389
if obj = @parameters[name]
390
# Note that if this is a property, then the value is the "should" value,
391
# not the current value.
398
# Abstract setting parameters and properties, and normalize
399
# access to always be symbols, not strings. This sets the 'should'
400
# value on properties, and otherwise just sets the appropriate parameter.
402
name = attr_alias(name)
404
fail("Invalid parameter #{name}") unless self.class.validattr?(name)
409
raise Puppet::Error.new("Got nil value for #{name}") if value.nil?
411
property = self.newattr(name)
414
# make sure the parameter doesn't have any errors
415
property.value = value
417
error = Puppet::Error.new("Parameter #{name} failed: #{detail}")
418
error.set_backtrace(detail.backtrace)
425
# remove a property from the object; useful in testing or in cleanup
426
# when an error has been encountered
428
attr = symbolize(attr)
429
if @parameters.has_key?(attr)
430
@parameters.delete(attr)
432
raise Puppet::DevError.new("Undefined attribute '#{attr}' in #{self}")
436
# iterate across the existing properties
438
# properties is a private method
439
properties.each { |property|
444
# Create a transaction event. Called by Transaction or by
446
def event(options = {})
447
Puppet::Transaction::Event.new({:resource => self, :file => file, :line => line, :tags => tags, :version => version}.merge(options))
450
# Let the catalog determine whether a given cached value is
451
# still valid or has expired.
456
# retrieve the 'should' value for a specified property
458
name = attr_alias(name)
459
(prop = @parameters[name] and prop.is_a?(Puppet::Property)) ? prop.should : nil
462
# Create the actual attribute instance. Requires either the attribute
463
# name or class as the first argument, then an optional hash of
464
# attributes to set during initialization.
471
unless klass = self.class.attrclass(name)
472
raise Puppet::Error, "Resource type #{self.class.name} does not support parameter #{name}"
475
return @parameters[name] if @parameters.include?(name)
477
@parameters[name] = klass.new(:resource => self)
480
# return the value of a parameter
482
@parameters[name.to_sym]
489
# Is the named property defined?
490
def propertydefined?(name)
491
name = name.intern unless name.is_a? Symbol
492
@parameters.include?(name)
495
# Return an actual property instance by name; to return the value, use 'resource[param]'
496
# LAK:NOTE(20081028) Since the 'parameter' method is now a superset of this method,
497
# this one should probably go away at some point.
499
(obj = @parameters[symbolize(name)] and obj.is_a?(Puppet::Property)) ? obj : nil
502
# For any parameters or properties that have defaults and have not yet been
503
# set, set them now. This method can be handed a list of attributes,
504
# and if so it will only set defaults for those attributes.
505
def set_default(attr)
506
return unless klass = self.class.attrclass(attr)
507
return unless klass.method_defined?(:default)
508
return if @parameters.include?(klass.name)
510
return unless parameter = newattr(klass.name)
512
if value = parameter.default and ! value.nil?
513
parameter.value = value
515
@parameters.delete(parameter.name)
519
# Convert our object to a hash. This just includes properties.
523
@parameters.each do |name, obj|
524
rethash[name] = obj.value
534
# Return a specific value for an attribute.
536
name = attr_alias(name)
538
(obj = @parameters[name] and obj.respond_to?(:value)) ? obj.value : nil
542
return 0 unless catalog
546
# Return all of the property objects, in the order specified in the
549
self.class.properties.collect { |prop| @parameters[prop.name] }.compact
552
# Is this type's name isomorphic with the object? That is, if the
553
# name conflicts, does it necessarily mean that the objects conflict?
556
if defined?(@isomorphic)
564
self.class.isomorphic?
567
# is the instance a managed instance? A 'yes' here means that
568
# the instance was created from the language, vs. being created
569
# in order resolve other questions, such as finding a package
572
# Once an object is managed, it always stays managed; but an object
573
# that is listed as unmanaged might become managed later in the process,
574
# so we have to check that every time
579
properties.each { |property|
581
if s and ! property.class.unmanaged
590
###############################
591
# Code related to the container behaviour.
593
# this is a retarded hack method to get around the difference between
594
# component children and file children
600
self.class.depthfirst?
603
# Remove an object. The argument determines whether the object's
604
# subscriptions get eliminated, too.
605
def remove(rmdeps = true)
606
# This is hackish (mmm, cut and paste), but it works for now, and it's
607
# better than warnings.
608
@parameters.each do |name, obj|
615
# Remove the reference to the provider.
622
###############################
623
# Code related to evaluating the resources.
625
# Flush the provider, if it supports it. This is called by the
628
self.provider.flush if self.provider and self.provider.respond_to?(:flush)
631
# if all contained objects are in sync, then we're in sync
632
# FIXME I don't think this is used on the type instances any more,
633
# it's really only used for testing
637
if property = @parameters[:ensure]
638
unless is.include? property
639
raise Puppet::DevError,
640
"The is value is not in the is array for '#{property.name}'"
642
ensureis = is[property]
643
if property.insync?(ensureis) and property.should == :absent
648
properties.each { |property|
649
unless is.include? property
650
raise Puppet::DevError,
651
"The is value is not in the is array for '#{property.name}'"
654
propis = is[property]
655
unless property.insync?(propis)
656
property.debug("Not in sync: #{propis.inspect} vs #{property.should.inspect}")
659
# property.debug("In sync")
663
#self.debug("#{self} sync status is #{insync}")
667
# retrieve the current value of all contained properties
669
fail "Provider #{provider.class.name} is not functional on this host" if self.provider.is_a?(Puppet::Provider) and ! provider.class.suitable?
671
result = Puppet::Resource.new(type, title)
673
# Provide the name, so we know we'll always refer to a real thing
674
result[:name] = self[:name] unless self[:name] == title
676
if ensure_prop = property(:ensure) or (self.class.validattr?(:ensure) and ensure_prop = newattr(:ensure))
677
result[:ensure] = ensure_state = ensure_prop.retrieve
682
properties.each do |property|
683
next if property.name == :ensure
684
if ensure_state == :absent
685
result[property] = :absent
687
result[property] = property.retrieve
694
def retrieve_resource
696
resource = Resource.new(type, title, :parameters => resource) if resource.is_a? Hash
700
# Get a hash of the current properties. Returns a hash with
701
# the actual property instance as the key and the current value
703
def currentpropvalues
704
# It's important to use the 'properties' method here, as it follows the order
705
# in which they're defined in the class. It also guarantees that 'ensure'
706
# is the first property, which is important for skipping 'retrieve' on
707
# all the properties if the resource is absent.
709
return properties.inject({}) do | prophash, property|
710
if property.name == :ensure
711
ensure_state = property.retrieve
712
prophash[property] = ensure_state
714
if ensure_state == :absent
715
prophash[property] = :absent
717
prophash[property] = property.retrieve
724
# Are we running in noop mode?
726
# If we're not a host_config, we're almost certainly part of
727
# Settings, and we want to ignore 'noop'
728
return false if catalog and ! catalog.host_config?
741
###############################
742
# Code related to managing resource instances.
743
require 'puppet/transportable'
745
# retrieve a named instance of the current type
747
raise "Global resource access is deprecated"
748
@objects[name] || @aliases[name]
751
# add an instance by name to the class list of instances
752
def self.[]=(name,object)
753
raise "Global resource storage is deprecated"
755
if object.is_a?(Puppet::Type)
758
raise Puppet::DevError, "must pass a Puppet::Type object"
761
if exobj = @objects[name] and self.isomorphic?
762
msg = "Object '#{newobj.class.name}[#{name}]' already exists"
764
msg += ("in file #{object.file} at line #{object.line}") if exobj.file and exobj.line
765
msg += ("and cannot be redefined in file #{object.file} at line #{object.line}") if object.file and object.line
766
error = Puppet::Error.new(msg)
769
#Puppet.info("adding %s of type %s to class list" %
770
# [name,object.class])
771
@objects[name] = newobj
775
# Create an alias. We keep these in a separate hash so that we don't encounter
776
# the objects multiple times when iterating over them.
777
def self.alias(name, obj)
778
raise "Global resource aliasing is deprecated"
779
if @objects.include?(name)
780
unless @objects[name] == obj
781
raise Puppet::Error.new(
782
"Cannot create alias #{name}: object already exists"
787
if @aliases.include?(name)
788
unless @aliases[name] == obj
789
raise Puppet::Error.new(
790
"Object #{@aliases[name].name} already has alias #{name}"
798
# remove all of the instances of a single type
800
raise "Global resource removal is deprecated"
801
if defined?(@objects)
802
@objects.each do |name, obj|
807
@aliases.clear if defined?(@aliases)
810
# Force users to call this, so that we can merge objects if
812
def self.create(args)
813
# LAK:DEP Deprecation notice added 12/17/2008
814
Puppet.warning "Puppet::Type.create is deprecated; use Puppet::Type.new"
818
# remove a specified object
819
def self.delete(resource)
820
raise "Global resource removal is deprecated"
821
return unless defined?(@objects)
822
@objects.delete(resource.title) if @objects.include?(resource.title)
823
@aliases.delete(resource.title) if @aliases.include?(resource.title)
824
if @aliases.has_value?(resource)
826
@aliases.each do |name, otherres|
827
if otherres == resource
831
names.each { |name| @aliases.delete(name) }
835
# iterate across each of the type's instances
837
raise "Global resource iteration is deprecated"
838
return unless defined?(@objects)
839
@objects.each { |name,instance|
844
# does the type have an object with the given name?
845
def self.has_key?(name)
846
raise "Global resource access is deprecated"
847
@objects.has_key?(name)
850
# Retrieve all known instances. Either requires providers or must be overridden.
852
raise Puppet::DevError, "#{self.name} has no providers and has not overridden 'instances'" if provider_hash.empty?
854
# Put the default provider first, then the rest of the suitable providers.
855
provider_instances = {}
856
providers_by_source.collect do |provider|
857
provider.instances.collect do |instance|
858
# We always want to use the "first" provider instance we find, unless the resource
859
# is already managed and has a different provider set
860
if other = provider_instances[instance.name]
861
Puppet.warning "%s %s found in both %s and %s; skipping the %s version" %
862
[self.name.to_s.capitalize, instance.name, other.class.name, instance.class.name, instance.class.name]
865
provider_instances[instance.name] = instance
867
new(:name => instance.name, :provider => instance, :audit => :all)
872
# Return a list of one suitable provider per source, with the default provider first.
873
def self.providers_by_source
874
# Put the default provider first, then the rest of the suitable providers.
876
[defaultprovider, suitableprovider].flatten.uniq.collect do |provider|
877
next if sources.include?(provider.source)
879
sources << provider.source
884
# Convert a simple hash into a Resource instance.
885
def self.hash2resource(hash)
886
hash = hash.inject({}) { |result, ary| result[ary[0].to_sym] = ary[1]; result }
888
title = hash.delete(:title)
889
title ||= hash[:name]
890
title ||= hash[key_attributes.first] if key_attributes.length == 1
892
raise Puppet::Error, "Title or name must be provided" unless title
894
# Now create our resource.
895
resource = Puppet::Resource.new(self.name, title)
896
[:catalog].each do |attribute|
897
if value = hash[attribute]
898
hash.delete(attribute)
899
resource.send(attribute.to_s + "=", value)
903
hash.each do |param, value|
904
resource[param] = value
909
# Create the path for logging and such.
912
[p.pathbuilder, self.ref].flatten
918
###############################
919
# Add all of the meta parameters.
920
newmetaparam(:noop) do
921
desc "Boolean flag indicating whether work should actually
924
newvalues(:true, :false)
927
when true, :true, "true"; @resource.noop = true
928
when false, :false, "false"; @resource.noop = false
933
newmetaparam(:schedule) do
934
desc "On what schedule the object should be managed. You must create a
935
schedule object, and then reference the name of that object to use
936
that for your schedule:
943
exec { \"/usr/bin/apt-get update\":
947
The creation of the schedule object does not need to appear in the
948
configuration before objects that use it."
951
newmetaparam(:audit) do
952
desc "Audit specified attributes of resources over time, and report if any have changed.
953
This attribute can be used to track changes to any resource over time, and can
954
provide an audit trail of every change that happens on any given machine.
956
Note that you cannot both audit and manage an attribute - managing it guarantees
957
the value, and any changes already get logged."
961
unless list == [:all]
963
next if @resource.class.validattr?(param)
964
fail "Cannot audit #{param}: not a valid attribute for #{resource}"
970
properties_to_audit(args).each do |param|
971
next unless resource.class.validproperty?(param)
972
resource.newattr(param)
977
resource.class.properties.find_all do |property|
978
resource.provider.nil? or resource.provider.class.supports_parameter?(property)
979
end.collect do |property|
984
def properties_to_audit(list)
986
list = all_properties if list == :all
988
list = Array(list).collect { |p| p.to_sym }
993
newmetaparam(:check) do
994
desc "Audit specified attributes of resources over time, and report if any have changed.
995
This parameter has been deprecated in favor of 'audit'."
998
resource.warning "'check' attribute is deprecated; use 'audit' instead"
999
resource[:audit] = args
1003
newmetaparam(:loglevel) do
1004
desc "Sets the level that information will be logged.
1005
The log levels have the biggest impact when logs are sent to
1006
syslog (which is currently the default)."
1009
newvalues(*Puppet::Util::Log.levels)
1013
val = super(loglevel)
1021
newmetaparam(:alias) do
1022
desc "Creates an alias for the object. Puppet uses this internally when you
1023
provide a symbolic name:
1026
path => $operatingsystem ? {
1027
solaris => \"/usr/local/etc/ssh/sshd_config\",
1028
default => \"/etc/ssh/sshd_config\"
1034
subscribe => File[sshdconfig]
1037
When you use this feature, the parser sets `sshdconfig` as the name,
1038
and the library sets that as an alias for the file so the dependency
1039
lookup for `sshd` works. You can use this parameter yourself,
1040
but note that only the library can use these aliases; for instance,
1041
the following code will not work:
1043
file { \"/etc/ssh/sshd_config\":
1053
There's no way here for the Puppet parser to know that these two stanzas
1054
should be affecting the same file.
1056
See the [Language Tutorial](http://docs.puppetlabs.com/guides/language_tutorial.html) for more information.
1061
aliases = [aliases] unless aliases.is_a?(Array)
1063
raise(ArgumentError, "Cannot add aliases without a catalog") unless @resource.catalog
1065
aliases.each do |other|
1066
if obj = @resource.catalog.resource(@resource.class.name, other)
1067
unless obj.object_id == @resource.object_id
1068
self.fail("#{@resource.title} can not create alias #{other}: object already exists")
1073
# Newschool, add it to the catalog.
1074
@resource.catalog.alias(@resource, other)
1079
newmetaparam(:tag) do
1080
desc "Add the specified tags to the associated resource. While all resources
1081
are automatically tagged with as much information as possible
1082
(e.g., each class and definition containing the resource), it can
1083
be useful to add your own tags to a given resource.
1085
Tags are currently useful for things like applying a subset of a
1086
host's configuration:
1088
puppet agent --test --tags mytag
1090
This way, when you're testing a configuration you can run just the
1091
portion you're testing."
1094
tags = [tags] unless tags.is_a? Array
1102
class RelationshipMetaparam < Puppet::Parameter
1104
attr_accessor :direction, :events, :callback, :subclasses
1109
def self.inherited(sub)
1113
def munge(references)
1114
references = [references] unless references.is_a?(Array)
1115
references.collect do |ref|
1116
if ref.is_a?(Puppet::Resource)
1119
Puppet::Resource.new(ref)
1124
def validate_relationship
1125
@value.each do |ref|
1126
unless @resource.catalog.resource(ref.to_s)
1127
description = self.class.direction == :in ? "dependency" : "dependent"
1128
fail "Could not find #{description} #{ref} for #{resource.ref}"
1133
# Create edges from each of our relationships. :in
1134
# relationships are specified by the event-receivers, and :out
1135
# relationships are specified by the event generator. This
1136
# way 'source' and 'target' are consistent terms in both edges
1137
# and events -- that is, an event targets edges whose source matches
1138
# the event's source. The direction of the relationship determines
1139
# which resource is applied first and which resource is considered
1140
# to be the event generator.
1142
@value.collect do |reference|
1143
reference.catalog = resource.catalog
1145
# Either of the two retrieval attempts could have returned
1147
unless related_resource = reference.resolve
1148
self.fail "Could not retrieve dependency '#{reference}' of #{@resource.ref}"
1151
# Are we requiring them, or vice versa? See the method docs
1152
# for futher info on this.
1153
if self.class.direction == :in
1154
source = related_resource
1158
target = related_resource
1161
if method = self.class.callback
1163
:event => self.class.events,
1166
self.debug("subscribes to #{related_resource.ref}")
1168
# If there's no callback, there's no point in even adding
1171
self.debug("requires #{related_resource.ref}")
1174
rel = Puppet::Relationship.new(source, target, subargs)
1179
def self.relationship_params
1180
RelationshipMetaparam.subclasses
1184
# Note that the order in which the relationships params is defined
1185
# matters. The labelled params (notify and subcribe) must be later,
1186
# so that if both params are used, those ones win. It's a hackish
1187
# solution, but it works.
1189
newmetaparam(:require, :parent => RelationshipMetaparam, :attributes => {:direction => :in, :events => :NONE}) do
1190
desc "One or more objects that this object depends on.
1191
This is used purely for guaranteeing that changes to required objects
1192
happen before the dependent object. For instance:
1194
# Create the destination directory before you copy things down
1195
file { \"/usr/local/scripts\":
1199
file { \"/usr/local/scripts/myscript\":
1200
source => \"puppet://server/module/myscript\",
1202
require => File[\"/usr/local/scripts\"]
1205
Multiple dependencies can be specified by providing a comma-seperated list
1206
of resources, enclosed in square brackets:
1208
require => [ File[\"/usr/local\"], File[\"/usr/local/scripts\"] ]
1210
Note that Puppet will autorequire everything that it can, and
1211
there are hooks in place so that it's easy for resources to add new
1212
ways to autorequire objects, so if you think Puppet could be
1213
smarter here, let us know.
1215
In fact, the above code was redundant -- Puppet will autorequire
1216
any parent directories that are being managed; it will
1217
automatically realize that the parent directory should be created
1218
before the script is pulled down.
1220
Currently, exec resources will autorequire their CWD (if it is
1221
specified) plus any fully qualified paths that appear in the
1222
command. For instance, if you had an `exec` command that ran
1223
the `myscript` mentioned above, the above code that pulls the
1224
file down would be automatically listed as a requirement to the
1225
`exec` code, so that you would always be running againts the
1226
most recent version.
1230
newmetaparam(:subscribe, :parent => RelationshipMetaparam, :attributes => {:direction => :in, :events => :ALL_EVENTS, :callback => :refresh}) do
1231
desc "One or more objects that this object depends on. Changes in the
1232
subscribed to objects result in the dependent objects being
1233
refreshed (e.g., a service will get restarted). For instance:
1236
file { \"/etc/nagios/nagios.conf\":
1237
source => \"puppet://server/module/nagios.conf\",
1238
alias => nagconf # just to make things easier for me
1242
subscribe => File[nagconf]
1246
Currently the `exec`, `mount` and `service` type support
1251
newmetaparam(:before, :parent => RelationshipMetaparam, :attributes => {:direction => :out, :events => :NONE}) do
1252
desc %{This parameter is the opposite of **require** -- it guarantees
1253
that the specified object is applied later than the specifying
1256
file { "/var/nagios/configuration":
1259
before => Exec["nagios-rebuid"]
1262
exec { "nagios-rebuild":
1263
command => "/usr/bin/make",
1264
cwd => "/var/nagios/configuration"
1267
This will make sure all of the files are up to date before the
1268
make command is run.}
1271
newmetaparam(:notify, :parent => RelationshipMetaparam, :attributes => {:direction => :out, :events => :ALL_EVENTS, :callback => :refresh}) do
1272
desc %{This parameter is the opposite of **subscribe** -- it sends events
1273
to the specified object:
1275
file { "/etc/sshd_config":
1277
notify => Service[sshd]
1284
This will restart the sshd service if the sshd config file changes.}
1287
newmetaparam(:stage) do
1288
desc %{Which run stage a given resource should reside in. This just creates
1289
a dependency on or from the named milestone. For instance, saying that
1290
this is in the 'bootstrap' stage creates a dependency on the 'bootstrap'
1293
By default, all classes get directly added to the
1294
'main' stage. You can create new stages as resources:
1296
stage { [pre, post]: }
1298
To order stages, use standard relationships:
1300
stage { pre: before => Stage[main] }
1302
Or use the new relationship syntax:
1304
Stage[pre] -> Stage[main] -> Stage[post]
1306
Then use the new class parameters to specify a stage:
1308
class { foo: stage => pre }
1310
Stages can only be set on classes, not individual resources. This will
1313
file { '/foo': stage => pre, ensure => file }
1317
###############################
1318
# All of the provider plumbing for the resource types.
1319
require 'puppet/provider'
1320
require 'puppet/util/provider_features'
1322
# Add the feature handling module.
1323
extend Puppet::Util::ProviderFeatures
1325
attr_reader :provider
1327
# the Type class attribute accessors
1329
attr_accessor :providerloader
1330
attr_writer :defaultprovider
1333
# Find the default provider.
1334
def self.defaultprovider
1335
unless @defaultprovider
1336
suitable = suitableprovider
1338
# Find which providers are a default for this system.
1339
defaults = suitable.find_all { |provider| provider.default? }
1341
# If we don't have any default we use suitable providers
1342
defaults = suitable if defaults.empty?
1343
max = defaults.collect { |provider| provider.specificity }.max
1344
defaults = defaults.find_all { |provider| provider.specificity == max }
1347
if defaults.length > 1
1349
"Found multiple default providers for #{self.name}: #{defaults.collect { |i| i.name.to_s }.join(", ")}; using #{defaults[0].name}"
1351
retval = defaults.shift
1352
elsif defaults.length == 1
1353
retval = defaults.shift
1355
raise Puppet::DevError, "Could not find a default provider for #{self.name}"
1358
@defaultprovider = retval
1364
def self.provider_hash_by_type(type)
1365
@provider_hashes ||= {}
1366
@provider_hashes[type] ||= {}
1369
def self.provider_hash
1370
Puppet::Type.provider_hash_by_type(self.name)
1373
# Retrieve a provider by name.
1374
def self.provider(name)
1375
name = Puppet::Util.symbolize(name)
1377
# If we don't have it yet, try loading it.
1378
@providerloader.load(name) unless provider_hash.has_key?(name)
1382
# Just list all of the providers.
1387
def self.validprovider?(name)
1388
name = Puppet::Util.symbolize(name)
1390
(provider_hash.has_key?(name) && provider_hash[name].suitable?)
1393
# Create a new provider of a type. This method must be called
1394
# directly on the type that it's implementing.
1395
def self.provide(name, options = {}, &block)
1396
name = Puppet::Util.symbolize(name)
1398
if obj = provider_hash[name]
1399
Puppet.debug "Reloading #{name} #{self.name} provider"
1403
parent = if pname = options[:parent]
1404
options.delete(:parent)
1405
if pname.is_a? Class
1408
if provider = self.provider(pname)
1411
raise Puppet::DevError,
1412
"Could not find parent provider #{pname} of #{name}"
1419
options[:resource_type] ||= self
1424
provider = genclass(
1427
:hash => provider_hash,
1428
:prefix => "Provider",
1430
:include => feature_module,
1431
:extend => feature_module,
1433
:attributes => options
1439
# Make sure we have a :provider parameter defined. Only gets called if there
1442
return if @paramhash.has_key? :provider
1444
newparam(:provider) do
1445
desc "The specific backend for #{self.name.to_s} to use. You will
1446
seldom need to specify this -- Puppet will usually discover the
1447
appropriate provider for your platform."
1449
# This is so we can refer back to the type to get a list of
1450
# providers for documentation.
1452
attr_accessor :parenttype
1455
# We need to add documentation for each provider.
1457
@doc + " Available providers are:\n\n" + parenttype.providers.sort { |a,b|
1460
"* **#{i}**: #{parenttype().provider(i).doc}"
1465
@resource.class.defaultprovider.name
1468
validate do |provider_class|
1469
provider_class = provider_class[0] if provider_class.is_a? Array
1470
provider_class = provider_class.class.name if provider_class.is_a?(Puppet::Provider)
1472
unless provider = @resource.class.provider(provider_class)
1473
raise ArgumentError, "Invalid #{@resource.class.name} provider '#{provider_class}'"
1478
provider = provider[0] if provider.is_a? Array
1479
provider = provider.intern if provider.is_a? String
1480
@resource.provider = provider
1482
if provider.is_a?(Puppet::Provider)
1488
end.parenttype = self
1491
def self.unprovide(name)
1492
if provider_hash.has_key? name
1496
:hash => provider_hash,
1498
:prefix => "Provider"
1500
if @defaultprovider and @defaultprovider.name == name
1501
@defaultprovider = nil
1506
# Return an array of all of the suitable providers.
1507
def self.suitableprovider
1508
providerloader.loadall if provider_hash.empty?
1509
provider_hash.find_all { |name, provider|
1511
}.collect { |name, provider|
1513
}.reject { |p| p.name == :fake } # For testing
1517
if name.is_a?(Puppet::Provider)
1519
@provider.resource = self
1520
elsif klass = self.class.provider(name)
1521
@provider = klass.new(self)
1523
raise ArgumentError, "Could not find #{name} provider of #{self.class.name}"
1527
###############################
1528
# All of the relationship code.
1530
# Specify a block for generating a list of objects to autorequire. This
1531
# makes it so that you don't have to manually specify things that you clearly
1533
def self.autorequire(name, &block)
1534
@autorequires ||= {}
1535
@autorequires[name] = block
1538
# Yield each of those autorequires in turn, yo.
1539
def self.eachautorequire
1540
@autorequires ||= {}
1541
@autorequires.each { |type, block|
1546
# Figure out of there are any objects we can automatically add as
1548
def autorequire(rel_catalog = nil)
1549
rel_catalog ||= catalog
1550
raise(Puppet::DevError, "You cannot add relationships without a catalog") unless rel_catalog
1553
self.class.eachautorequire { |type, block|
1554
# Ignore any types we can't find, although that would be a bit odd.
1555
next unless typeobj = Puppet::Type.type(type)
1557
# Retrieve the list of names from the block.
1558
next unless list = self.instance_eval(&block)
1559
list = [list] unless list.is_a?(Array)
1561
# Collect the current prereqs
1564
# Support them passing objects directly, to save some effort.
1565
unless dep.is_a? Puppet::Type
1566
# Skip autorequires that we aren't managing
1567
unless dep = rel_catalog.resource(type, dep)
1572
reqs << Puppet::Relationship.new(dep, self)
1579
# Build the dependencies associated with an individual object.
1581
# Handle the requires
1582
self.class.relationship_params.collect do |klass|
1583
if param = @parameters[klass.name]
1586
end.flatten.reject { |r| r.nil? }
1589
# Define the initial list of tags.
1591
tag(self.class.name)
1595
# Types (which map to resources in the languages) are entirely composed of
1596
# attribute value pairs. Generally, Puppet calls any of these things an
1597
# 'attribute', but these attributes always take one of three specific
1598
# forms: parameters, metaparams, or properties.
1600
# In naming methods, I have tried to consistently name the method so
1601
# that it is clear whether it operates on all attributes (thus has 'attr' in
1602
# the method name, or whether it operates on a specific type of attributes.
1608
# class methods dealing with Type management
1612
# the Type class attribute accessors
1615
attr_accessor :self_refresh
1616
include Enumerable, Puppet::Util::ClassGen
1617
include Puppet::MetaType::Manager
1619
include Puppet::Util
1620
include Puppet::Util::Logging
1623
# all of the variables that must be initialized for each subclass
1625
# all of the instances of this class
1633
@validproperties = {}
1640
@paramdoc = Hash.new { |hash,key|
1641
key = key.intern if key.is_a?(String)
1642
if hash.include?(key)
1645
"Param Documentation for #{key} not found"
1655
"Puppet::Type::#{@name.to_s.capitalize}"
1661
# Create a block to validate that our object is set up entirely. This will
1662
# be run before the object is operated on.
1663
def self.validate(&block)
1664
define_method(:validate, &block)
1668
# The catalog that this resource is stored in.
1669
attr_accessor :catalog
1671
# is the resource exported
1672
attr_accessor :exported
1674
# is the resource virtual (it should not :-))
1675
attr_accessor :virtual
1677
# create a log at specified level
1680
Puppet::Util::Log.create(
1682
:level => @parameters[:loglevel].value,
1690
# instance methods related to instance intrinsics
1691
# e.g., initialize and name
1695
attr_reader :original_parameters
1697
# initialize the type instance
1698
def initialize(resource)
1699
raise Puppet::DevError, "Got TransObject instead of Resource or hash" if resource.is_a?(Puppet::TransObject)
1700
resource = self.class.hash2resource(resource) unless resource.is_a?(Puppet::Resource)
1702
# The list of parameter/property instances.
1705
# Set the title first, so any failures print correctly.
1706
if resource.type.to_s.downcase.to_sym == self.class.name
1707
self.title = resource.title
1709
# This should only ever happen for components
1710
self.title = resource.ref
1713
[:file, :line, :catalog, :exported, :virtual].each do |getter|
1714
setter = getter.to_s + "="
1715
if val = resource.send(getter)
1716
self.send(setter, val)
1720
@tags = resource.tags
1722
@original_parameters = resource.to_hash
1724
set_name(@original_parameters)
1726
set_default(:provider)
1728
set_parameters(@original_parameters)
1730
self.validate if self.respond_to?(:validate)
1735
# Set our resource's name.
1737
self[name_var] = hash.delete(name_var) if name_var
1740
# Set all of the parameters from a hash, in the appropriate order.
1741
def set_parameters(hash)
1742
# Use the order provided by allattrs, but add in any
1743
# extra attributes from the resource so we get failures
1744
# on invalid attributes.
1746
(self.class.allattrs + hash.keys).uniq.each do |attr|
1748
# Set any defaults immediately. This is mostly done so
1749
# that the default provider is available for any other
1750
# property validation.
1751
if hash.has_key?(attr)
1752
self[attr] = hash[attr]
1756
rescue ArgumentError, Puppet::Error, TypeError
1759
error = Puppet::DevError.new( "Could not set #{attr} on #{self.class.name}: #{detail}")
1760
error.set_backtrace(detail.backtrace)
1764
no_values.each do |attr|
1771
# Set up all of our autorequires.
1773
# Make sure all of our relationships are valid. Again, must be done
1774
# when the entire catalog is instantiated.
1775
self.class.relationship_params.collect do |klass|
1776
if param = @parameters[klass.name]
1777
param.validate_relationship
1779
end.flatten.reject { |r| r.nil? }
1782
# For now, leave the 'name' method functioning like it used to. Once 'title'
1783
# works everywhere, I'll switch it.
1788
# Look up our parent in the catalog, if we have one.
1790
return nil unless catalog
1792
unless defined?(@parent)
1793
if parents = catalog.adjacent(self, :direction => :in)
1794
# We should never have more than one parent, so let's just ignore
1795
# it if we happen to.
1796
@parent = parents.shift
1804
# Return the "type[name]" style reference.
1806
"#{self.class.name.to_s.capitalize}[#{self.title}]"
1810
self.class.self_refresh
1813
# Mark that we're purging.
1818
# Is this resource being purged? Used by transactions to forbid
1819
# deletion when there are dependencies.
1821
if defined?(@purging)
1828
# Retrieve the title of an object. If no title was set separately,
1829
# then use the object's name.
1832
if self.class.validparameter?(name_var)
1833
@title = self[:name]
1834
elsif self.class.validproperty?(name_var)
1835
@title = self.should(name_var)
1837
self.devfail "Could not find namevar #{name_var} for #{self.class.name}"
1844
# convert to a string
1849
# Convert to a transportable object
1850
def to_trans(ret = true)
1851
trans = TransObject.new(self.title, self.class.name)
1853
values = retrieve_resource
1854
values.each do |name, value|
1855
name = name.name if name.respond_to? :name
1859
@parameters.each do |name, param|
1860
# Avoid adding each instance name twice
1861
next if param.class.isnamevar? and param.value == self.title
1863
# We've already got property values
1864
next if param.is_a?(Puppet::Property)
1865
trans[name] = param.value
1868
trans.tags = self.tags
1870
# FIXME I'm currently ignoring 'parent' and 'path'
1876
# this 'type instance' versus 'resource' distinction seems artificial
1877
# I'd like to see it collapsed someday ~JW
1878
self.to_trans.to_resource
1881
def virtual?; !!@virtual; end
1882
def exported?; !!@exported; end
1886
require 'puppet/provider'
1888
# Always load these types.
1889
require 'puppet/type/component'