summaryrefslogtreecommitdiffstats
path: root/lib/puppet/parser/resource.rb
blob: 31d43bd183df13c53897899c307a65d6b3a75400 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# A resource that we're managing.  This handles making sure that only subclasses
# can set parameters.
class Puppet::Parser::Resource
    require 'puppet/parser/resource/param'
    require 'puppet/parser/resource/reference'
    ResParam = Struct.new :name, :value, :source, :line, :file
    include Puppet::Util
    include Puppet::Util::MethodHelper
    include Puppet::Util::Errors
    include Puppet::Util::Logging

    attr_accessor :source, :line, :file, :scope, :rails_id
    attr_accessor :virtual, :override, :params, :translated

    attr_reader :exported

    attr_writer :tags

    # Proxy a few methods to our @ref object.
    [:builtin?, :type, :title].each do |method|
        define_method(method) do
            @ref.send(method)
        end
    end

    # Set up some boolean test methods
    [:exported, :translated, :override].each do |method|
        newmeth = (method.to_s + "?").intern
        define_method(newmeth) do
            self.send(method)
        end
    end

    def [](param)
        param = symbolize(param)
        if param == :title
            return self.title
        end
        if @params.has_key?(param)
            @params[param].value
        else
            nil
        end
    end

    # Add default values from our definition.
    def adddefaults
        defaults = scope.lookupdefaults(self.type)

        defaults.each do |name, param|
            unless @params.include?(param.name)
                self.debug "Adding default for %s" % param.name

                @params[param.name] = param
            end
        end
    end

    # Add any metaparams defined in our scope.  This actually adds any metaparams
    # from any parent scope, and there's currently no way to turn that off.
    def addmetaparams
        Puppet::Type.eachmetaparam do |name|
            next if self[name]
            if val = scope.lookupvar(name.to_s, false)
                unless val == :undefined
                    set Param.new(:name => name, :value => val, :source => scope.source)
                end
            end
        end
    end

    # Add any overrides for this object.
    def addoverrides
        overrides = scope.lookupoverrides(self)

        overrides.each do |over|
            self.merge(over)
        end

        overrides.clear
    end

    def builtin=(bool)
        @ref.builtin = bool
    end

    # Retrieve the associated definition and evaluate it.
    def evaluate
        if klass = @ref.definedtype
            finish()
            scope.deleteresource(self)
            return klass.evaluate(:scope => scope,
                                  :type => self.type,
                                  :name => self.title,
                                  :arguments => self.to_hash,
                                  :scope => self.scope,
                                  :exported => self.exported
            )
        elsif builtin?
            devfail "Cannot evaluate a builtin type"
        else
            self.fail "Cannot find definition %s" % self.type
        end
    ensure
        @evaluated = true
    end

    def exported=(value)
        if value
            @virtual = true
            @exported = value
        else
            @exported = value
        end
    end

    def evaluated?
        if defined? @evaluated and @evaluated
            true
        else
            false
        end
    end

    # Do any finishing work on this object, called before evaluation or
    # before storage/translation.
    def finish
        addoverrides()
        adddefaults()
        addmetaparams()
    end

    def initialize(options)
        options = symbolize_options(options)

        # Collect the options necessary to make the reference.
        refopts = [:type, :title].inject({}) do |hash, param|
            hash[param] = options[param]
            options.delete(param)
            hash
        end

        @params = {}
        tmpparams = nil
        if tmpparams = options[:params]
            options.delete(:params)
        end

        # Now set the rest of the options.
        set_options(options)

        @ref = Reference.new(refopts)

        requiredopts(:scope, :source)

        @ref.scope = self.scope

        if tmpparams
            tmpparams.each do |param|
                # We use the method here, because it does type-checking.
                set(param)
            end
        end
    end

    # Merge an override resource in.
    def merge(resource)
        # Some of these might fail, but they'll fail in the way we want.
        resource.params.each do |name, param|
            set(param)
        end
    end

    # This *significantly* reduces the number of calls to Puppet.[].
    def paramcheck?
        unless defined? @@paramcheck
            @@paramcheck = Puppet[:paramcheck]
        end
        @@paramcheck
    end

    # Verify that all passed parameters are valid.  This throws an error if there's
    # a problem, so we don't have to worry about the return value.
    def paramcheck(param)
        # Now make sure it's a valid argument to our class.  These checks
        # are organized in order of commonhood -- most types, it's a valid argument
        # and paramcheck is enabled.
        if @ref.typeclass.validattr?(param)
            true
        elsif (param == "name" or param == "title") # always allow these
            true
        elsif paramcheck?
            self.fail Puppet::ParseError, "Invalid parameter '%s' for type '%s'" %
                    [param, @ref.type]
        end
    end

    # A temporary occasion, until I get paths in the scopes figured out.
    def path
        to_s
    end

    # Return the short version of our name.
    def ref
        @ref.to_s
    end

    # You have to pass a Resource::Param to this.
    def set(param, value = nil, source = nil)
        if value and source
            param = Puppet::Parser::Resource::Param.new(
                :name => param, :value => value, :source => source
            )
        elsif ! param.is_a?(Puppet::Parser::Resource::Param)
            raise ArgumentError, "Must pass a parameter or all necessary values"
        end
        # Because definitions are now parse-time, I can paramcheck immediately.
        paramcheck(param.name)

        if current = @params[param.name]
            # XXX Should we ignore any settings that have the same values?
            if param.source.child_of?(current.source)
                # Replace it, keeping all of its info.
                @params[param.name] = param
            else
                if Puppet[:trace]
                    puts caller
                end
                fail Puppet::ParseError, "Parameter %s is already set on %s by %s" %
                    [param.name, self.to_s, param.source]
            end
        else
            if self.source == param.source or param.source.child_of?(self.source)
                @params[param.name] = param
            else
                fail Puppet::ParseError, "Only subclasses can set parameters"
            end
        end
    end

    def tags
        unless defined? @tags
            @tags = scope.tags
            @tags << self.type
        end
        @tags
    end

    def to_hash
        @params.inject({}) do |hash, ary|
            param = ary[1]
            hash[param.name] = param.value
            hash
        end
    end

    # Turn our parser resource into a Rails resource.
    def to_rails(host)
        args = {}
        %w{type title tags file line exported}.each do |param|
            if value = self.send(param)
                args[param] = value
            end
        end

        # 'type' isn't a valid column name, so we have to use something else.
        args = symbolize_options(args)
        args[:restype] = args[:type]
        args.delete(:type)

        # Let's see if the object exists
        if obj = host.resources.find_by_restype_and_title(self.type, self.title)
            # We exist
            args.each do |param, value|
                obj[param] = value
            end
        else
            # Else create it anew
            obj = host.resources.build(args)
        end

        if l = self.line
            obj.line = l
        end

        # Either way, now add our parameters
        obj.collection_merge(:param_names, @params) do |name, param|
            param.to_rails(obj)
        end

        return obj
    end

    def to_s
        self.ref
    end

    # Translate our object to a transportable object.
    def to_trans
        unless builtin?
            devfail "Tried to translate a non-builtin resource"
        end

        return nil if virtual?

        # Now convert to a transobject
        obj = Puppet::TransObject.new(@ref.title, @ref.type)
        to_hash.each do |p, v|
            if v.is_a?(Reference)
                v = v.to_ref
            elsif v.is_a?(Array)
                v = v.collect { |av|
                    if av.is_a?(Reference)
                        av = av.to_ref
                    end
                    av
                }
            end
            obj[p.to_s] = v
        end

        obj.file = self.file
        obj.line = self.line

        obj.tags = self.tags

        return obj
    end

    def virtual?
        self.virtual
    end
end

# $Id$