Renaming 'configuration' to 'catalog', fixing #954.
[vinpup.git] / lib / puppet / metatype / instances.rb
blob3f44413f83728af63b8ad000ff6d38dae3c37118
1 require 'puppet/transportable'
3 class Puppet::Type
4     # Make 'new' private, so people have to use create instead.
5     class << self
6         private :new
7     end
9     # retrieve a named instance of the current type
10     def self.[](name)
11         @objects[name] || @aliases[name]
12     end
14     # add an instance by name to the class list of instances
15     def self.[]=(name,object)
16         newobj = nil
17         if object.is_a?(Puppet::Type)
18             newobj = object
19         else
20             raise Puppet::DevError, "must pass a Puppet::Type object"
21         end
23         if exobj = @objects[name] and self.isomorphic?
24             msg = "Object '%s[%s]' already exists" %
25                 [newobj.class.name, name]
27             if exobj.file and exobj.line
28                 msg += ("in file %s at line %s" %
29                     [object.file, object.line])
30             end
31             if object.file and object.line
32                 msg += ("and cannot be redefined in file %s at line %s" %
33                     [object.file, object.line])
34             end
35             error = Puppet::Error.new(msg)
36             raise error
37         else
38             #Puppet.info("adding %s of type %s to class list" %
39             #    [name,object.class])
40             @objects[name] = newobj
41         end
42     end
44     # Create an alias.  We keep these in a separate hash so that we don't encounter
45     # the objects multiple times when iterating over them.
46     def self.alias(name, obj)
47         if @objects.include?(name)
48             unless @objects[name] == obj
49                 raise Puppet::Error.new(
50                     "Cannot create alias %s: object already exists" %
51                     [name]
52                 )
53             end
54         end
56         if @aliases.include?(name)
57             unless @aliases[name] == obj
58                 raise Puppet::Error.new(
59                     "Object %s already has alias %s" %
60                     [@aliases[name].name, name]
61                 )
62             end
63         end
65         @aliases[name] = obj
66     end
68     # remove all of the instances of a single type
69     def self.clear
70         if defined? @objects
71             @objects.each do |name, obj|
72                 obj.remove(true)
73             end
74             @objects.clear
75         end
76         if defined? @aliases
77             @aliases.clear
78         end
79     end
81     # Force users to call this, so that we can merge objects if
82     # necessary.
83     def self.create(args)
84         # Don't modify the original hash; instead, create a duplicate and modify it.
85         # We have to dup and use the ! so that it stays a TransObject if it is
86         # one.
87         hash = args.dup
88         symbolizehash!(hash)
90         # If we're the base class, then pass the info on appropriately
91         if self == Puppet::Type
92             type = nil
93             if hash.is_a? Puppet::TransObject
94                 type = hash.type
95             else
96                 # If we're using the type to determine object type, then delete it
97                 if type = hash[:type]
98                     hash.delete(:type)
99                 end
100             end
102             # If they've specified a type and called on the base, then
103             # delegate to the subclass.
104             if type
105                 if typeklass = self.type(type)
106                     return typeklass.create(hash)
107                 else
108                     raise Puppet::Error, "Unknown type %s" % type
109                 end
110             else
111                 raise Puppet::Error, "No type found for %s" % hash.inspect
112             end
113         end
115         # Handle this new object being implicit
116         implicit = hash[:implicit] || false
117         if hash.include?(:implicit)
118             hash.delete(:implicit)
119         end
121         name = nil
122         unless hash.is_a? Puppet::TransObject
123             hash = self.hash2trans(hash)
124         end
126         # XXX This will have to change when transobjects change to using titles
127         title = hash.name
129         # if the object already exists
130         if self.isomorphic? and retobj = self[title]
131             # if only one of our objects is implicit, then it's easy to see
132             # who wins -- the non-implicit one.
133             if retobj.implicit? and ! implicit
134                 Puppet.notice "Removing implicit %s" % retobj.title
135                 # Remove all of the objects, but do not remove their subscriptions.
136                 retobj.remove(false)
138                 # now pass through and create the new object
139             elsif implicit
140                 Puppet.debug "Ignoring implicit %s[%s]" % [self.name, title]
141                 return nil
142             else
143                 raise Puppet::Error, "%s is already being managed" % retobj.ref
144             end
145         end
147         # create it anew
148         # if there's a failure, destroy the object if it got that far, but raise
149         # the error.
150         begin
151             obj = new(hash)
152         rescue => detail
153             Puppet.err "Could not create %s: %s" % [title, detail.to_s]
154             if obj
155                 obj.remove(true)
156             elsif obj = self[title]
157                 obj.remove(true)
158             end
159             raise
160         end
162         if implicit
163             obj.implicit = true
164         end
166         # Store the object by title
167         self[obj.title] = obj
169         return obj
170     end
172     # remove a specified object
173     def self.delete(resource)
174         return unless defined? @objects
175         if @objects.include?(resource.title)
176             @objects.delete(resource.title)
177         end
178         if @aliases.include?(resource.title)
179             @aliases.delete(resource.title)
180         end
181         if @aliases.has_value?(resource)
182             names = []
183             @aliases.each do |name, otherres|
184                 if otherres == resource
185                     names << name
186                 end
187             end
188             names.each { |name| @aliases.delete(name) }
189         end
190     end
192     # iterate across each of the type's instances
193     def self.each
194         return unless defined? @objects
195         @objects.each { |name,instance|
196             yield instance
197         }
198     end
200     # does the type have an object with the given name?
201     def self.has_key?(name)
202         return @objects.has_key?(name)
203     end
205     # Convert a hash to a TransObject.
206     def self.hash2trans(hash)
207         title = nil
208         if hash.include? :title
209             title = hash[:title]
210             hash.delete(:title)
211         elsif hash.include? self.namevar
212             title = hash[self.namevar]
213             hash.delete(self.namevar)
215             if hash.include? :name
216                 raise ArgumentError, "Cannot provide both name and %s to %s" %
217                     [self.namevar, self.name]
218             end
219         elsif hash[:name]
220             title = hash[:name]
221             hash.delete :name
222         end
224         if catalog = hash[:catalog]
225             hash.delete(:catalog)
226         end
228         raise(Puppet::Error, "You must specify a title for objects of type %s" % self.to_s) unless title
230         if hash.include? :type
231             unless self.validattr? :type
232                 hash.delete :type
233             end
234         end
236         # okay, now make a transobject out of hash
237         begin
238             trans = Puppet::TransObject.new(title, self.name.to_s)
239             trans.catalog = catalog if catalog
240             hash.each { |param, value|
241                 trans[param] = value
242             }
243         rescue => detail
244             raise Puppet::Error, "Could not create %s: %s" %
245                 [name, detail]
246         end
248         return trans
249     end
251     # Retrieve all known instances.  Either requires providers or must be overridden.
252     def self.instances
253         unless defined?(@providers) and ! @providers.empty?
254             raise Puppet::DevError, "%s has no providers and has not overridden 'instances'" % self.name
255         end
257         # Put the default provider first, then the rest of the suitable providers.
258         provider_instances = {}
259         providers_by_source.collect do |provider|
260             provider.instances.collect do |instance|
261                 # First try to get the resource if it already exists
262                 # Skip instances that map to a managed resource with a different provider
263                 next if resource = self[instance.name] and resource.provider.class != instance.class
265                 # We always want to use the "first" provider instance we find, unless the resource
266                 # is already managed and has a different provider set
267                 if other = provider_instances[instance.name]
268                     Puppet.warning "%s %s found in both %s and %s; skipping the %s version" %
269                         [self.name.to_s.capitalize, instance.name, other.class.name, instance.class.name, instance.class.name]
270                     next
271                 end
272                 provider_instances[instance.name] = instance
274                 if resource
275                     resource.provider = instance
276                     resource
277                 else
278                     create(:name => instance.name, :provider => instance, :check => :all)
279                 end
280             end
281         end.flatten.compact
282     end
284     # Return a list of one suitable provider per source, with the default provider first.
285     def self.providers_by_source
286         # Put the default provider first, then the rest of the suitable providers.
287         sources = []
288         [defaultprovider, suitableprovider].flatten.uniq.collect do |provider|
289             next if sources.include?(provider.source)
291             sources << provider.source
292             provider
293         end.compact
294     end
296     # Create the path for logging and such.
297     def pathbuilder
298         if p = parent
299             [p.pathbuilder, self.ref].flatten
300         else
301             [self.ref]
302         end
303     end