require 'puppet/util/instance_loader'

# A simple server for triggering a new run on a Puppet client.
class Puppet::Network::Handler
    class Report < Handler
        desc "Accepts a Puppet transaction report and processes it."

        extend Puppet::Util::ClassGen
        extend Puppet::Util::InstanceLoader

        module ReportBase
            include Puppet::Util::Docs
            attr_writer :useyaml

            def useyaml?
                if defined? @useyaml
                    @useyaml
                else
                    false
                end
            end
        end

        @interface = XMLRPC::Service::Interface.new("puppetreports") { |iface|
            iface.add_method("string report(array)")
        }

        # Set up autoloading and retrieving of reports.
        autoload :report, 'puppet/reports'

        class << self
            attr_reader :hooks
        end

        # Add a new report type.
        def self.newreport(name, options = {}, &block)
            name = symbolize(name)

            mod = genmodule(name, :extend => ReportBase, :hash => instance_hash(:report), :block => block)

            if options[:useyaml]
                mod.useyaml = true
            end

            mod.send(:define_method, :report_name) do
                name
            end
        end

        # Collect the docs for all of our reports.
        def self.reportdocs
            docs = ""

            # Use this method so they all get loaded
            instance_loader(:report).loadall
            loaded_instances(:report).sort { |a,b| a.to_s <=> b.to_s }.each do |name|
                mod = self.report(name)
                docs += "%s\n%s\n" % [name, "-" * name.to_s.length]

                docs += Puppet::Util::Docs.scrub(mod.doc) + "\n\n"
            end

            docs
        end

        # List each of the reports.
        def self.reports
            instance_loader(:report).loadall
            loaded_instances(:report)
        end

        def initialize(*args)
            super
            Puppet.config.use(:reporting)
            Puppet.config.use(:metrics)
        end

        # Accept a report from a client.
        def report(report, client = nil, clientip = nil)
            # Unescape the report
            unless @local
                report = CGI.unescape(report)
            end

            Puppet.info "Processing reports %s for %s" % [reports().join(", "), client]
            begin
                process(report)
            rescue => detail
                Puppet.err "Could not process report for %s: %s" % [client, detail]
                if Puppet[:trace]
                    puts detail.backtrace
                end
            end
        end

        private

        # Process the report using all of the existing hooks.
        def process(yaml)
            return if Puppet[:reports] == "none"

            # First convert the report to real objects
            begin
                report = YAML.load(yaml)
            rescue => detail
                Puppet.warning "Could not load report: %s" % detail
                return
            end

            # Used for those reports that accept yaml
            client = report.host

            reports().each do |name|
                if mod = self.class.report(name)
                    # We have to use a dup because we're including a module in the
                    # report.
                    newrep = report.dup
                    begin
                        newrep.extend(mod)
                        if mod.useyaml?
                            newrep.process(yaml)
                        else
                            newrep.process
                        end
                    rescue => detail
                        if Puppet[:trace]
                            puts detail.backtrace
                        end
                        Puppet.err "Report %s failed: %s" %
                            [name, detail]
                    end
                else
                    Puppet.warning "No report named '%s'" % name
                end
            end
        end

        # Handle the parsing of the reports attribute.
        def reports
            Puppet[:reports].gsub(/(^\s+)|(\s+$)/, '').split(/\s*,\s*/)
        end
    end
end

# $Id: report.rb 2637 2007-06-20 00:16:41Z luke $


syntax highlighted by Code2HTML, v. 0.9.1