summaryrefslogtreecommitdiffstats
path: root/lib/puppet/reports/rrdgraph.rb
blob: 5889750dda6b2f273a4611f8fcfa5920b98e9f08 (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
Puppet::Reports.register_report(:rrdgraph) do
    desc "Graph all available data about hosts using the RRD library.  You
        must have the Ruby RRDtool library installed to use this report, which
        you can get from `the RubyRRDTool RubyForge page`_.  This package may also
        be available as ``ruby-rrd`` or ``rrdtool-ruby`` in your distribution's package
        management system.  The library and/or package will both require the binary 
        ``rrdtool`` package from your distribution to be installed.

        .. _the RubyRRDTool RubyForge page: http://rubyforge.org/projects/rubyrrdtool/
        
        This report will create, manage, and graph RRD database files for each
        of the metrics generated during transactions, and it will create a
        few simple html files to display the reporting host's graphs.  At this
        point, it will not create a common index file to display links to
        all hosts.
        
        All RRD files and graphs get created in the ``rrddir`` directory.  If
        you want to serve these publicly, you should be able to just alias that
        directory in a web server.
    
        If you really know what you're doing, you can tune the ``rrdinterval``,
        which defaults to the ``runinterval``."

    def hostdir
        unless defined? @hostdir
            @hostdir = File.join(Puppet[:rrddir], self.host)
        end
        @hostdir
    end

    def htmlfile(type, graphs, field)
        file = File.join(hostdir, "%s.html" % type)
        File.open(file, "w") do |of|
            of.puts "<html><head><title>%s graphs for %s</title></head><body>" %
                [type.capitalize, host]

            graphs.each do |graph|
                if field == :first
                    name = graph.sub(/-\w+.png/, '').capitalize
                else
                    name = graph.sub(/\w+-/, '').sub(".png", '').capitalize
                end
                of.puts "<img src=%s><br>" % graph
            end
            of.puts "</body></html>"
        end

        return file
    end

    def mkhtml
        images = Dir.entries(hostdir).find_all { |d| d =~ /\.png/ }

        periodorder = %w{daily weekly monthly yearly}

        periods = {}
        types = {}
        images.each do |n|
            type, period = n.sub(".png", '').split("-")
            periods[period] ||= []
            types[type] ||= []
            periods[period] << n
            types[type] << n
        end

        files = []
        # Make the period html files
        periodorder.each do |period|
            unless ary = periods[period]
                raise Puppet::Error, "Could not find graphs for %s" % period
            end
            files << htmlfile(period, ary, :first)
        end

        # make the type html files
        types.sort { |a,b| a[0] <=> b[0] }.each do |type, ary|
            newary = []
            periodorder.each do |period|
                if graph = ary.find { |g| g.include?("-%s.png" % period) }
                    newary << graph
                else
                    raise "Could not find %s-%s graph" % [type, period]
                end
            end

            files << htmlfile(type, newary, :second)
        end

        File.open(File.join(hostdir, "index.html"), "w") do |of|
            of.puts "<html><head><title>Report graphs for %s</title></head><body>" %
                host
            files.each do |file|
                of.puts "<a href='%s'>%s</a><br/>" %
                    [File.basename(file),
                     File.basename(file).sub(".html",'').capitalize]
            end
            of.puts "</body></html>"
        end
    end

    def process(time = nil)
        time ||= Time.now.to_i

        unless File.directory?(hostdir) and FileTest.writable?(hostdir)
            # Some hackishness to create the dir with all of the right modes and ownership
            config = Puppet::Util::Settings.new
            config.setdefaults(:reports, :hostdir => {:default => hostdir, :owner => Puppet[:user], :mode => 0755, :group => Puppet[:group], :desc => "eh"})

            # This creates the dir.
            config.use(:reports)
        end

        self.metrics.each do |name, metric|
            metric.basedir = hostdir

            if name == "time"
                timeclean(metric)
            end

            metric.store(time)

            metric.graph
        end

        unless FileTest.exists?(File.join(hostdir, "index.html"))
            mkhtml()
        end
    end

    # Unfortunately, RRD does not deal well with changing lists of values,
    # so we have to pick a list of values and stick with it.  In this case,
    # that means we record the total time, the config time, and that's about
    # it.  We should probably send each type's time as a separate metric.
    def timeclean(metric)
        metric.values = metric.values.find_all { |name, label, value| [:total, :config_retrieval].include?(name) }
    end
end