diff options
Diffstat (limited to 'lib/puppet/server/fileserver.rb')
-rwxr-xr-x | lib/puppet/server/fileserver.rb | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/lib/puppet/server/fileserver.rb b/lib/puppet/server/fileserver.rb new file mode 100755 index 000000000..43e08655f --- /dev/null +++ b/lib/puppet/server/fileserver.rb @@ -0,0 +1,264 @@ +require 'puppet' +require 'cgi' + +module Puppet +class Server + class FileServerError < Puppet::Error; end + class FileServer + attr_accessor :local + + #CHECKPARAMS = %w{checksum type mode owner group} + CHECKPARAMS = [:mode, :type, :owner, :group, :checksum] + + def self.interface + XMLRPC::Service::Interface.new("fileserver") { |iface| + iface.add_method("string describe(string)") + iface.add_method("string list(string, boolean)") + iface.add_method("string retrieve(string)") + } + end + + Puppet::Server.addhandler(:FileServer, self) + + def check(dir) + unless FileTest.exists?(dir) + Puppet.notice "File source %s does not exist" % dir + return nil + end + + obj = nil + unless obj = Puppet::Type::PFile[dir] + obj = Puppet::Type::PFile.new( + :name => dir, + :check => CHECKPARAMS + ) + end + # we should really have a timeout here -- we don't + # want to actually check on every connection, maybe no more + # than every 60 seconds or something + #@files[mount].evaluate + obj.evaluate + + return obj + end + + def describe(file) + mount, path = splitpath(file) + + subdir = nil + unless subdir = subdir(mount, path) + Puppet.notice "Could not find subdirectory %s" % + "//%s/%s" % [mount, path] + return "" + end + + obj = nil + unless obj = self.check(subdir) + return "" + end + + desc = [] + CHECKPARAMS.each { |check| + if state = obj.state(check) + unless state.is + Puppet.notice "Manually retrieving info for %s" % check + state.retrieve + end + desc << state.is + else + if check == "checksum" and obj.state(:type).is == "file" + Puppet.notice "File %s does not have data for %s" % + [obj.name, check] + end + desc << nil + end + } + + return desc.join("\t") + end + + def initialize(hash = {}) + @mounts = {} + @files = {} + + if hash[:Local] + @local = hash[:Local] + else + @local = false + end + end + + def list(dir, recurse = false, sum = "md5") + mount, path = splitpath(dir) + + subdir = nil + unless subdir = subdir(mount, path) + Puppet.notice "Could not find subdirectory %s" % + "//%s/%s" % [mount, path] + return "" + end + + obj = nil + unless FileTest.exists?(subdir) + return "" + end + + #rmdir = File.dirname(File.join(@mounts[mount], path)) + rmdir = nameswap(dir, mount) + desc = self.reclist(rmdir, subdir, recurse) + + if desc.length == 0 + Puppet.notice "Got no information on //%s/%s" % + [mount, path] + return "" + end + + desc.collect { |sub| + sub.join("\t") + }.join("\n") + end + + def mount(dir, name) + if @mounts.include?(name) + if @mounts[name] != dir + raise FileServerError, "%s is already mounted at %s" % + [@mounts[name], name] + else + # it's already mounted; no problem + return + end + end + + unless name =~ %r{^\w+$} + raise FileServerError, "Invalid name format '%s'" % name + end + + unless FileTest.exists?(dir) + raise FileServerError, "%s does not exist" % dir + end + + if FileTest.directory?(dir) + if FileTest.readable?(dir) + Puppet.info "Mounting %s at %s" % [dir, name] + @mounts[name] = dir + else + raise FileServerError, "%s is not readable" % dir + end + else + raise FileServerError, "%s is not a directory" % dir + end + end + + # recursive listing function + def reclist(root, path, recurse) + #desc = [obj.name.sub(%r{#{root}/?}, '')] + name = path.sub(root, '') + if name == "" + name = "/" + end + + if name == path + raise Puppet::FileServerError, "Could not match %s in %s" % + [root, path] + end + + desc = [name] + ftype = File.stat(path).ftype + + desc << ftype + if recurse.is_a?(Integer) + recurse -= 1 + end + + ary = [desc] + if recurse == true or (recurse.is_a?(Integer) and recurse > -1) + if ftype == "directory" + Dir.entries(path).each { |child| + next if child =~ /^\.\.?$/ + self.reclist(root, File.join(path, child), recurse).each { |cobj| + ary << cobj + } + } + end + end + + return ary.reject { |c| c.nil? } + end + + def retrieve(file) + mount, path = splitpath(file) + + unless (@mounts.include?(mount)) + # FIXME I really need some better way to pass and handle xmlrpc errors + raise FileServerError, "%s not mounted" % mount + end + + fpath = nil + if path + fpath = File.join(@mounts[mount], path) + else + fpath = @mounts[mount] + end + + unless FileTest.exists?(fpath) + return "" + end + + str = File.read(fpath) + + if @local + return str + else + return CGI.escape(str) + end + end + + private + + def nameswap(name, mount) + name.sub(/\/#{mount}/, @mounts[mount]).gsub(%r{//}, '/').sub( + %r{/$}, '' + ) + #Puppet.info "Swapped %s to %s" % [name, newname] + #newname + end + + def splitpath(dir) + # the dir is based on one of the mounts + # so first retrieve the mount path + mount = nil + path = nil + if dir =~ %r{/(\w+)/?} + mount = $1 + path = dir.sub(%r{/#{mount}/?}, '') + + unless @mounts.include?(mount) + raise FileServerError, "%s not mounted" % mount + end + else + raise FileServerError, "Invalid path '%s'" % dir + end + + if path == "" + path = nil + end + return mount, path + end + + def subdir(mount, dir) + basedir = @mounts[mount] + + dirname = nil + if dir + dirname = File.join(basedir, dir.split("/").join(File::SEPARATOR)) + else + dirname = basedir + end + + return dirname + end + end +end +end + +# $Id$ |