require 'puppet'
require 'puppet/network/handler'
# Serve Puppet elements. Useful for querying, copying, and, um, other stuff.
class Puppet::Network::Handler
class Resource < Handler
desc "An interface for interacting with client-based resources that can
be used for querying or managing remote machines without using Puppet's
central server tools.
The ``describe`` and ``list`` methods return TransBuckets containing
TransObject instances (``describe`` returns a single TransBucket),
and the ``apply`` method accepts a TransBucket of TransObjects and
applies them locally.
"
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)")
}
side :client
# 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::Network::Client.client(:Master).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 = Puppet::TransBucket.new
bucket.type = typeklass.name
typeklass.instances.each do |obj|
next if ignore.include? obj.name
#object = Puppet::TransObject.new(obj.name, typeklass.name)
bucket << obj.to_trans
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::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: resource.rb 2551 2007-06-04 20:37:14Z luke $
syntax highlighted by Code2HTML, v. 0.9.1