rrdreel

Check-in [eab80cd79f]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:initial import
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | descendants | trunk
Files: files | file ages | folders
SHA3-256:eab80cd79f395b96a9e9d9ff09d5abf76be5564f0f8495841c157a4b334ceff4
User & Date: jef@foutaise.org 2016-11-27 18:38:20
Context
2016-11-27
19:00
resize image check-in: 8e49de86e6 user: jef@foutaise.org tags: trunk
18:38
initial import check-in: eab80cd79f user: jef@foutaise.org tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace

Added LICENSE.











































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
The MIT License (MIT)

Copyright (c) 2012-2016 Gerome Fournier

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Added README.md.







>
>
>
1
2
3
# rrdreel

Live data visualisation framework

Added backends/base.pm.



















































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
package backends::base;

=head1 NAME

backends::base - base component for monitoring backends

=head1 OBJECT METHODS

=over 4

=item rrd_file

Return the path of the RRD database

=item graph_file

Return the path of the PNG file

=item cleanup

Perform backend cleanup, removing RDD and PNG file

=item timeslots

Return an array reference of timeslots supported by the
backend. Each timeslot is an hash reference made of a
C<label> string value and a C<range> integer value
representing an amount of seconds.

=item timeslot_set ( TS )

Set the current timeslot to TS

=item step

Return the backend's RRD step value

=item rras

Return a list of RRAs for RRD creation

=back

=cut

use strict;
use warnings;
use 5.010;
use File::Path qw(rmtree);
use File::Temp qw();
use POSIX qw(ceil);

sub tempdir {
        state $dir = File::Temp::tempdir();
        return $dir;
}

sub new {
        my ($class, @args) = @_;

        my %args = @args;

        my $this = {};
        my $defaults;
        {
                no strict 'refs';
                $defaults = ${$class . "::defaults"};
        }

        foreach my $key (keys %{ $defaults }) {
                $this->{$key} = $args{$key} // $defaults->{$key};
        }
        bless($this, $class);
        $this->timeslot_set($this->timeslots()->[0]);

        return $this;
}

sub rrd_file {
        my ($this) = @_;

        return tempdir() . "/db.rrd";
}

sub graph_file {
        my ($this) = @_;

        return tempdir() . "/graph.png";
}

sub cleanup {
        my ($this) = @_;

        rmtree(tempdir());
}

sub timeslots {
        my ($this) = @_;

        return [
                {
                        label => "last 5 mns",
                        range => 300
                },
                {
                        label => "last 15 mns",
                        range => 900
                },
                {
                        label => "last 30 mns",
                        range => 1800
                },
                {
                        label => "last hour",
                        range => 3600
                },
                {
                        label => "last 12 hours",
                        range => 42300
                },
                {
                        label => "last day",
                        range => 86400
                }
        ];
}

sub timeslot_set {
        my ($this, $ts) = @_;

        $this->{ts} = $ts;
}

sub step {
        my ($this) = @_;

        return $this->{step};
}

sub rras {
        my ($this) = @_;

        my $max = sub {
                $_[0] < $_[1] ? $_[1] : $_[0];
        };

        my %rras = ();
        my $steps;
        my $rows;
        foreach my $ts (@{ $this->timeslots() }) {
                # compute the right amount of steps / rows for this timeslot
                my $ndata = ceil($ts->{range} / $this->{step});
                if ($ndata <= $this->{graph_width}) {
                        ($steps, $rows) = (1, $ndata);
                } else {
                        ($steps, $rows) = (ceil($ndata / $this->{graph_width}), $this->{graph_width});
                }

                # store new RRA / merge if another RRA is already using a same amount of steps
                if (exists $rras{$steps}) {
                        $rras{$steps} = $max->($rows, $rras{$steps});
                } else {
                        $rras{$steps} = $rows;
                }
        }

        return map { sprintf("RRA:AVERAGE:0.5:%d:%d", $_, $rras{$_}) } keys %rras;
}
1;

Added backends/gauge.pm.



















































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
package backends::gauge;

=head1 NAME

backends::value - Monitor backend for a single gauge value

=head1 CONSTRUCTOR

=over 4

=item new ( [ARGS] )

Creates a c<backends::gauge> object.

Allowed arguments:

=over 8

=item graph_width

PNG graph width (default 600)

=item graph_height

PNG graph height (default 120)

=item step

delay in seconds between each values (default 1)

=item title

main title of the graph (default "")

=item vlabel

vertical left label (default "")

=item legend

legend of the monitored gauge (default "")

=item unit

unit of the monitored gauge (default "")

=back

=back

=head1 OBJECT METHODS

=over 4

=item rrd_create

Create the RRD file.

=item rrd_update

Update the RRD file, reading a gauge value from STDIN.
This backend expect to recieve a single value per line.

=item graph_update

Update the graph for a specific RANGE.

=back

=cut

use strict;
use warnings;
use 5.010;
use RRDs;
use List::MoreUtils qw(uniq);
use base ("backends::base");

our $defaults = {
        graph_width => 600,
        graph_height => 120,
        step => 1,
        title => "",
        vlabel => "",
        legend => "",
        unit => ""
};

sub rrd_create {
        my ($this) = @_;

        my $heartbeat = $this->{step} * 2;
        RRDs::create $this->rrd_file(),
                  "-bN",
                  "-s$this->{step}",
                  "DS:gauge:GAUGE:$heartbeat:U:U",
                  $this->rras();
        die RRDs::error if RRDs::error;
}

sub rrd_update {
        my ($this) = @_;

        my $value = <STDIN>;
        chomp $value;
        RRDs::update $this->rrd_file(), "-t", "gauge", "N:$value";
        die RRDs::error if RRDs::error;
}

sub graph_update {
        my ($this) = @_;
        state $lastrange = -1;

        my @args = (
                $this->graph_file(),
                "-s -$this->{ts}->{range}",
                "-t $this->{title}",
                "-h", $this->{graph_height}, "-w", $this->{graph_width},
                "-l 0",
                "-a", "PNG",
                "-v $this->{vlabel}",
                sprintf("DEF:gauge=%s:gauge:AVERAGE", $this->rrd_file()),
                "AREA:gauge#32CD32:$this->{legend}",
                "LINE1:gauge#336600",
                "GPRINT:gauge:MAX: Max\\: %5.1lf %s",
                "GPRINT:gauge:AVERAGE: Avg\\: %5.1lf %S",
                "GPRINT:gauge:LAST: Current\\: %5.1lf %S$this->{unit}\\n",
                "HRULE:0#000000");

        push @args, "--lazy" if $lastrange == $this->{ts}->{range};
        RRDs::graph(@args);
        die RRDs::error if RRDs::error;

        $lastrange = $this->{ts}->{range};
}

1;

Added backends/interface.pm.





















































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
package backends::interface;

=head1 NAME

backends::interface - Monitor backend for network interfaces

=head1 CONSTRUCTOR

=over 4

=item new ( [ARGS] )

Creates a c<backends::interface> object.

Allowed arguments:

=over 8

=item graph_width

PNG graph width (default 600)

=item graph_height

PNG graph height (default 120)

=item step

delay in seconds between each values (default 1)

=item title

main title of the graph (default "Traffic on interface")

=item vlabel

vertical left label (default "Bits per second")

=item in_legend

legend for the input values (default "In")

=item in_unit

unit of the input values (default "bits")

=item out_legend

legend for the output values (default "Out")

=item out_unit

unit of the output values (default "bits")

=back

=back

=head1 OBJECT METHODS

=over 4

=item rrd_create

Create the RRD file.

=item rrd_update

Update the RRD file, reading a counter value from STDIN.
This backend expect to recieve a single value per line.

=item graph_update

Update the graph for a specific RANGE.

=back

=cut

use strict;
use warnings;
use 5.010;
use RRDs;
use List::MoreUtils qw(uniq);
use base ("backends::base");

our $defaults = {
        graph_width => 600,
        graph_height => 120,
        step => 1,
        title => "Traffic on interface",
        vlabel => "Bits per second",
        in_legend => "In",
        in_unit => "bits",
        out_legend => "Out",
        out_unit => "bits"
};

sub rrd_create {
        my ($this) = @_;

        my $heartbeat = $this->{step} * 2;
        RRDs::create $this->rrd_file(),
                  "-bN",
                  "-s$this->{step}",
                  "DS:in:DERIVE:$heartbeat:0:U",
                  "DS:out:DERIVE:$heartbeat:0:U",
                  $this->rras();
        die RRDs::error if RRDs::error;
}

sub rrd_update {
        my ($this) = @_;

        my ($in, $out) = split(/\s+/, <STDIN>, 3);
        RRDs::update $this->rrd_file(), "-t", "in:out", "N:$in:$out";
        die RRDs::error if RRDs::error;
}

sub graph_update {
        my ($this) = @_;
        state $lastrange = -1;

        my @args = (
                $this->graph_file(),
                "-s -$this->{ts}->{range}",
                "-t $this->{title}",
                "-h", $this->{graph_height}, "-w", $this->{graph_width},
                "-l 0",
                "-a", "PNG",
                "-v $this->{vlabel}",
                sprintf("DEF:in=%s:in:AVERAGE", $this->rrd_file()),
                sprintf("DEF:out=%s:out:AVERAGE", $this->rrd_file()),
                "CDEF:out_neg=out,-1,*",
                "AREA:in#32CD32:$this->{in_legend}",
                "LINE1:in#336600",
                "GPRINT:in:MAX:  Max\\: %.1lf %s",
                "GPRINT:in:AVERAGE: Avg\\: %.1lf %S",
                "GPRINT:in:LAST: Current\\: %.1lf %S$this->{in_unit}\\n",
                "AREA:out_neg#4169E1:$this->{out_legend}",
                "LINE1:out_neg#0033CC",
                "GPRINT:out:MAX:  Max\\: %.1lf %S",
                "GPRINT:out:AVERAGE: Avg\\: %.1lf %S",
                "GPRINT:out:LAST: Current\\: %.1lf %S$this->{out_unit}\\n",
                "HRULE:0#000000");

        push @args, "--lazy" if $lastrange == $this->{ts}->{range};
        RRDs::graph(@args);
        die RRDs::error if RRDs::error;

        $lastrange = $this->{ts}->{range};
}

1;

Added examples/cpu-load.

























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
#!/usr/bin/env bash
#
# Monitor CPU load

export LC_ALL=C

function usage() {
	cat <<-EOF
	Monitor CPU load
	Usage:
		$0
	EOF
}

if [ "$1" == "-h" ]; then
	usage
	exit
fi

step=1
while true; do
	echo $(uptime | awk '{load=$(NF-2); sub(",", "", load); print load}')
	sleep $step
done | $(dirname $0)/../rrdreel gauge \
	            step $step \
	            title "CPU load" \
	            legend "load" \
	            unit ""

Added examples/cpu-load-snmp.



































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
#!/usr/bin/env bash
#
# Monitor CPU load through snmp

function usage() {
	cat <<-EOF
	Monitor CPU load through snmp
	Usage:
		$0 <host> <community>
	EOF
}

if [ "$1" == "-h" ]; then
	usage
	exit
fi

if [ $# -ne 2 ]; then
        echo "wrong number of arguments" >&2
        exit 1
fi

host=$1
community=$2
step=1
while true; do
        echo $(snmpget -v1 -Oqv -c $community $host .1.3.6.1.4.1.2021.10.1.3.1)
	sleep $step
done | $(dirname $0)/../rrdreel gauge \
	            step $step \
	            title "$host / CPU load" \
	            legend "load" \
	            unit ""

Added examples/interface-bsd.











































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
#!/usr/bin/env bash
#
# Monitor local network interface on BSD

# override locales settings
export LC_ALL=C

function usage() {
	cat <<-EOF
	Monitor local network interface on BSD
	Usage:
		$0 <interface>
	EOF
}

function die() {
	echo "$@" >&2
	exit 1
}

function netstat_field_index() {
        local interface=$1
        local field=$2
        local header
        local i=1

        read header < <(netstat -b -n -I $interface)
        set -- $header
        while [ $# -gt 0 ]; do
                if [ "$1" == "$field" ]; then
                        echo "$i"
                        break
                fi
                ((i++))
                shift
        done
}

if [ "$1" == "-h" ]; then
	usage
	exit
fi

[ $# -eq 1 ] || die "wrong number of arguments"

interface=$1
ibytes_idx=$(netstat_field_index $interface "Ibytes")
obytes_idx=$(netstat_field_index $interface "Obytes")
while true; do
	echo $(netstat -b -n -I $interface \
                | awk "NR == 2 {print 8 * \$$ibytes_idx, 8 * \$$obytes_idx}")
	sleep 1
done | $(dirname $0)/../rrdreel interface title "Traffic on $interface"

Added examples/interface-linux.

































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
#!/usr/bin/env bash
#
# Monitor local network interface on linux

function usage() {
	cat <<-EOF
	Monitor local network interface on linux
	Usage:
		$0 <interface>
	EOF
}

function die() {
	echo "$@" >&2
	exit 1
}

if [ "$1" == "-h" ]; then
	usage
	exit
fi

[ $# -eq 1 ] || die "wrong number of arguments"

interface=$1
[ -d "/sys/class/net/$interface" ] || die "unknown interface '$interface'"

while true; do
	echo $((8 * $(cat /sys/class/net/$interface/statistics/rx_bytes))) \
		$((8 * $(cat /sys/class/net/$interface/statistics/tx_bytes)))
	sleep 1
done | $(dirname $0)/../rrdreel interface title "Traffic on $interface"

Added examples/interface-snmp.





















































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
#!/usr/bin/env bash
#
# Monitor network interface through snmp

# Use current iftable cache timeout as a step value.
# For a finer resolution (like every second),
# you'll have to setup the iftable cache timeout
# accordingly:
#
# snmpset -v2c -c <rwcommunity> <host> .1.3.6.1.4.1.8072.1.5.3.1.2.1.3.6.1.2.1.2.2 i 1

function usage() {
        cat <<-EOF
	Monitor network interface through snmp
	Usage:
		$0 <host> <community>
	EOF
}

function die() {
	echo "$@" >&2
	exit 1
}

function bytes2bits() {
	local input
	while read input; do
		echo $(($input * 8))
	done
}

# return current iftable cache timeout value
function iftable_cache_timeout() {
        local host=$1
        local community=$2

        snmpget -v1 -Oqv -c $community $host ".1.3.6.1.4.1.8072.1.5.3.1.2.1.3.6.1.2.1.2.2"
}

# return an interface description
function interface_desc() {
        local host=$1
        local community=$2
        local int_id=$3

        snmpget -v1 -Oqv -c $community $host .1.3.6.1.2.1.2.2.1.2.$int_id
}

# select an interface id
function interface_id_select() {
        local $host=$1
        local $community=$2
        local int_ids
        local int_id
        local i

	int_ids=$(snmpwalk -v1 -Oqv -c $community $host .1.3.6.1.2.1.2.2.1.1)
	[ -n "$int_ids" ] || return

        while true; do
                echo "Select interface id number:" >&2
                for i in $int_ids; do
                        echo "$i)" $(interface_desc $host $community $i) >&2
                done
                read -p "Answer: " int_id
                if grep -q -F "$int_id" <<< "$int_ids"; then
                        echo $int_id
                        break
                fi
        done
}

if [ "$1" == "-h" ]; then
	usage
	exit
fi

if [ $# -ne 2 ]; then
	echo "wrong number of arguments" >&2
	exit 1
fi

host=$1
community=$2

step=$(iftable_cache_timeout $host $community)
[ -n "$step" ] || die "can't get iftable cache timeout"

int_id=$(interface_id_select $host $community)
[ -n "$int_id" ] || die "can't select an interface"

int_desc=$(interface_desc $host $community $int_id)
[ -n "$int_desc" ] || die "can't find interface description"

ifinoctets=".1.3.6.1.2.1.2.2.1.10"
ifoutoctets=".1.3.6.1.2.1.2.2.1.16"
while true; do
	values=$(snmpget -v1 -Oqv -c $community $host $ifinoctets.$int_id $ifoutoctets.$int_id)
	if [ $? -eq 0 ]; then
		echo $(bytes2bits <<< "$values" \
			| paste -d " " - -)
	fi
	sleep $step
done | $(dirname $0)/../rrdreel interface \
	            step $step \
                    title "Traffic on $host/$int_desc" \

Added examples/ping-times.









































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
#!/usr/bin/env bash
#
# Monitor ping response times

export LC_ALL=C

function usage() {
	cat <<-EOF
	Monitor ping response times
	Usage:
		$0 <host>
	EOF
}

if [ "$1" == "-h" ]; then
	usage
	exit
fi

if [ $# -ne 1 ]; then
	echo "wrong number of arguments" >&2
	exit 1
fi

host=$1
step=1
while true; do
	echo $(ping -c1 $host | awk '/icmp_seq/ {time=$(NF-1); sub(".*=", "", time); print time;}') 
	sleep $step
done \
	| $(dirname $0)/../rrdreel gauge \
		step $step \
		title "Ping response time for $host" \
		vlabel "response time (ms)" \
		legend "time" \
		unit "ms"

Added examples/sinusoide.

































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
#!/usr/bin/env bash
#
# Graph a sinusoide

function usage() {
	cat <<-EOF
	Graph a sinusoide
	Usage:
		$0
	EOF
}

function sin() {
	bc -l <<< "scale=5;s($1 * 0.0174533)"
}

if [ "$1" == "-h" ]; then
	usage
	exit
fi

i=0
step=1
while true; do
	echo $(sin $i)
	i=$(($i + 10))
	sleep $step
done | $(dirname $0)/../rrdreel gauge \
	            step $step \
	            title "Sinusoide" \
		    vlabel "sinus value" \
	            legend "sinus"

Added rrdreel.



































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
#!/usr/bin/env perl
#
# Live data visualisation framework
# Copyright (c) 2016 Gerome Fournier

use strict;
use warnings;
use 5.010;

use Tk;
use Tk::Menu;
use Tk::widgets qw(PNG);

use File::Basename qw(dirname);
use lib dirname($0);

our $cleanup;

sub graph {
        my ($backend) = @_;

        $backend->rrd_create();

        my $mw = MainWindow->new;
        $mw->bind($mw, "<Control-w>" => $cleanup);
        my $photo = $mw->Label()->pack();

        # callback for image update
        my $img_update = sub {
                state $lastimage;

                $backend->graph_update();
                my $image = $mw->Photo(-file => $backend->graph_file());
                $photo->configure(-image => $image);
                $mw->update();
                $lastimage->delete() if defined $lastimage;
                $lastimage = $image;
        };

        # callback for RRD update
        my $rrd_update;
        $rrd_update = sub {
                state $running = 0;

                return if $running;
                $running = 1;
                $backend->rrd_update();
                $img_update->();
                $mw->after($backend->step() * 1000, $rrd_update);
                $running = 0;
        };

        # popup menu to select timeslot view
        my $menu = $mw->Menu(-tearoff => 0);
        foreach my $ts (@{ $backend->timeslots() }) {
                $menu->add('command',
			-label => $ts->{label},
			-command => sub {
		                $backend->timeslot_set($ts);
                		$img_update->();
			});
        }

        # callback for menu click
        my $showmenu = sub {
                my ($self, $x, $y, $widget) = @_;
                $menu->post($x, $y);
        };

        # bind callback on left mouse button
        $mw->bind('<3>', [$showmenu, Ev('X'), Ev('Y'), Ev('W')]);

        # launch the gui
        $rrd_update->();
        MainLoop();
}

sub backend_load {
        my ($backend_name, @args) = @_;

        my $backend = "backends::$backend_name";
        eval "use $backend; 1" or die "No such backend '$backend_name'";

        return $backend->new(@args);
}

sub usage {
	my ($progname) = $0 =~ /([^\/]+)$/;

	print <<EOF;
Live monitoring framework
Look into examples directory for usage scenarios

Options:
    -h : display this help and exit
EOF
}

if (@ARGV and $ARGV[0] eq "-h") {
	usage;
	exit;
}

my $backend = backend_load(@ARGV);
$cleanup = sub {
        $backend->cleanup();
        Tk::exit();
};

local $SIG{INT} = $cleanup;
local $SIG{TERM} = $cleanup;

graph($backend);

Added screenshots/interface-snmp.png.

cannot compute difference between binary files

Added screenshots/ping.png.

cannot compute difference between binary files

Added screenshots/sinusoide.png.

cannot compute difference between binary files