summaryrefslogtreecommitdiffstats
path: root/lib/puppet/server/resource.rb
blob: d2bad52f3c2df1b5458c9ebd55fd05f0b73dc11b (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
require 'puppet'
require 'puppet/server'

module Puppet

# Serve Puppet elements.  Useful for querying, copying, and, um, other stuff.
class Server::Resource < Server::Handler
    attr_accessor :local

    @interface = XMLRPC::Service::Interface.new("resource") { |iface|
        iface.add_method("string apply(string, string)")
        iface.add_method("string describe(string, string, array, array)")
        iface.add_method("string list(string, array, string)")
    }

    # Apply a TransBucket as a transaction.
    def apply(bucket, format = "yaml", client = nil, clientip = nil)
        unless @local
            begin
                case format
                when "yaml":
                    bucket = YAML::load(Base64.decode64(bucket))
                else
                    raise Puppet::Error, "Unsupported format '%s'" % format
                end
            rescue => detail
                raise Puppet::Error, "Could not load YAML TransBucket: %s" % detail
            end
        end

        component = bucket.to_type

        # Create a client, but specify the remote machine as the server
        # because the class requires it, even though it's unused
        client = Puppet::Client::MasterClient.new(:Server => client||"localhost")

        # Set the objects
        client.objects = component

        # And then apply the configuration.  This way we're reusing all
        # the code in there.  It should probably just be separated out, though.
        transaction = client.apply
        
        # And then clean up
        component.remove

        # It'd be nice to return some kind of report, but... at this point
        # we have no such facility.
        return "success"
    end

    # Describe a given object.  This returns the 'is' values for every property
    # available on the object type.
    def describe(type, name, retrieve = nil, ignore = [], format = "yaml", client = nil, clientip = nil)
        Puppet.info "Describing %s[%s]" % [type.to_s.capitalize, name]
        @local = true unless client
        typeklass = nil
        unless typeklass = Puppet.type(type)
            raise Puppet::Error, "Puppet type %s is unsupported" % type
        end

        obj = nil

        retrieve ||= :all
        ignore ||= []

        if obj = typeklass[name]
            obj[:check] = retrieve
        else
            begin
                obj = typeklass.create(:name => name, :check => retrieve)
            rescue Puppet::Error => detail
                raise Puppet::Error, "%s[%s] could not be created: %s" %
                    [type, name, detail]
            end
        end

        unless obj
            raise XMLRPC::FaultException.new(
                1, "Could not create %s[%s]" % [type, name]
            )
        end

        trans = obj.to_trans

        # Now get rid of any attributes they specifically don't want
        ignore.each do |st|
            if trans.include? st
                trans.delete(st)
            end
        end

        # And get rid of any attributes that are nil
        trans.each do |attr, value|
            if value.nil?
                trans.delete(attr)
            end
        end

        unless @local
            case format
            when "yaml":
                trans = Base64.encode64(YAML::dump(trans))
            else
                raise XMLRPC::FaultException.new(
                    1, "Unavailable config format %s" % format
                )
            end
        end

        return trans
    end

    # Create a new fileserving module.
    def initialize(hash = {})
        if hash[:Local]
            @local = hash[:Local]
        else
            @local = false
        end
    end

    # List all of the elements of a given type.
    def list(type, ignore = [], base = nil, format = "yaml", client = nil, clientip = nil)
        @local = true unless client
        typeklass = nil
        unless typeklass = Puppet.type(type)
            raise Puppet::Error, "Puppet type %s is unsupported" % type
        end

        # They can pass in false
        ignore ||= []
        ignore = [ignore] unless ignore.is_a? Array
        bucket = TransBucket.new
        bucket.type = typeklass.name

        typeklass.list.each do |obj|
            next if ignore.include? obj.name

            object = TransObject.new(obj.name, typeklass.name)
            bucket << object
        end

        unless @local
            case format
            when "yaml":
                begin
                bucket = Base64.encode64(YAML::dump(bucket))
                rescue => detail
                    Puppet.err detail
                    raise XMLRPC::FaultException.new(
                        1, detail.to_s
                    )
                end
            else
                raise XMLRPC::FaultException.new(
                    1, "Unavailable config format %s" % format
                )
            end
        end

        return bucket
    end

    private

    def authcheck(file, mount, client, clientip)
        unless mount.allowed?(client, clientip)
            mount.warning "%s cannot access %s" %
                [client, file]
            raise Puppet::Server::AuthorizationError, "Cannot access %s" % mount
        end
    end

    # Deal with ignore parameters.
    def handleignore(children, path, ignore)            
        ignore.each { |ignore|                
            Dir.glob(File.join(path,ignore), File::FNM_DOTMATCH) { |match|
                children.delete(File.basename(match))
            }                
        }
        return children
    end  

    def to_s
        "resource"
    end
end
end

# $Id$