perl plugin: Added support to dispatch notifications to Perl plugins.
[collectd.git] / bindings / perl / Collectd.pm
1 # collectd - Collectd.pm
2 # Copyright (C) 2007  Sebastian Harl
3 #
4 # This program is free software; you can redistribute it and/or modify it
5 # under the terms of the GNU General Public License as published by the
6 # Free Software Foundation; only version 2 of the License is applicable.
7 #
8 # This program is distributed in the hope that it will be useful, but
9 # WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 # General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License along
14 # with this program; if not, write to the Free Software Foundation, Inc.,
15 # 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
16 #
17 # Author:
18 #   Sebastian Harl <sh at tokkee.org>
19
20 package Collectd;
21
22 use strict;
23 use warnings;
24
25 use Config;
26
27 use threads;
28 use threads::shared;
29
30 BEGIN {
31         if (! $Config{'useithreads'}) {
32                 die "Perl does not support ithreads!";
33         }
34 }
35
36 require Exporter;
37
38 our @ISA = qw( Exporter );
39
40 our %EXPORT_TAGS = (
41         'plugin' => [ qw(
42                         plugin_register
43                         plugin_unregister
44                         plugin_dispatch_values
45                         plugin_log
46         ) ],
47         'types' => [ qw(
48                         TYPE_INIT
49                         TYPE_READ
50                         TYPE_WRITE
51                         TYPE_SHUTDOWN
52                         TYPE_LOG
53                         TYPE_NOTIF
54                         TYPE_DATASET
55         ) ],
56         'ds_types' => [ qw(
57                         DS_TYPE_COUNTER
58                         DS_TYPE_GAUGE
59         ) ],
60         'log' => [ qw(
61                         ERROR
62                         WARNING
63                         NOTICE
64                         INFO
65                         DEBUG
66                         LOG_ERR
67                         LOG_WARNING
68                         LOG_NOTICE
69                         LOG_INFO
70                         LOG_DEBUG
71         ) ],
72         'notif' => [ qw(
73                         NOTIF_FAILURE
74                         NOTIF_WARNING
75                         NOTIF_OKAY
76         ) ],
77         'globals' => [ qw(
78                         $hostname_g
79                         $interval_g
80         ) ],
81 );
82
83 {
84         my %seen;
85         push @{$EXPORT_TAGS{'all'}}, grep {! $seen{$_}++ } @{$EXPORT_TAGS{$_}}
86                 foreach keys %EXPORT_TAGS;
87 }
88
89 Exporter::export_ok_tags ('all');
90
91 my @plugins : shared = ();
92
93 my %types = (
94         TYPE_INIT,     "init",
95         TYPE_READ,     "read",
96         TYPE_WRITE,    "write",
97         TYPE_SHUTDOWN, "shutdown",
98         TYPE_LOG,      "log",
99         TYPE_NOTIF,    "notify"
100 );
101
102 foreach my $type (keys %types) {
103         $plugins[$type] = &share ({});
104 }
105
106 sub _log {
107         my $caller = shift;
108         my $lvl    = shift;
109         my $msg    = shift;
110
111         if ("Collectd" eq $caller) {
112                 $msg = "perl: $msg";
113         }
114         return plugin_log ($lvl, $msg);
115 }
116
117 sub ERROR   { _log (scalar caller, LOG_ERR,     shift); }
118 sub WARNING { _log (scalar caller, LOG_WARNING, shift); }
119 sub NOTICE  { _log (scalar caller, LOG_NOTICE,  shift); }
120 sub INFO    { _log (scalar caller, LOG_INFO,    shift); }
121 sub DEBUG   { _log (scalar caller, LOG_DEBUG,   shift); }
122
123 sub plugin_call_all {
124         my $type = shift;
125
126         our $cb_name = undef;
127
128         if (! defined $type) {
129                 return;
130         }
131
132         if (TYPE_LOG != $type) {
133                 DEBUG ("Collectd::plugin_call: type = \"$type\", args=\"@_\"");
134         }
135
136         if (! defined $plugins[$type]) {
137                 ERROR ("Collectd::plugin_call: unknown type \"$type\"");
138                 return;
139         }
140
141         lock @plugins;
142         foreach my $plugin (keys %{$plugins[$type]}) {
143                 my $p = $plugins[$type]->{$plugin};
144
145                 my $status = 0;
146
147                 if ($p->{'wait_left'} > 0) {
148                         # TODO: use interval_g
149                         $p->{'wait_left'} -= 10;
150                 }
151
152                 next if ($p->{'wait_left'} > 0);
153
154                 $cb_name = $p->{'cb_name'};
155                 $status = call_by_name (@_);
156
157                 if (! $status) {
158                         my $err = undef;
159
160                         if ($@) {
161                                 $err = $@;
162                         }
163                         else {
164                                 $err = "callback returned false";
165                         }
166
167                         if (TYPE_LOG != $type) {
168                                 ERROR ("Execution of callback \"$cb_name\" failed: $err");
169                         }
170
171                         $status = 0;
172                 }
173
174                 if ($status) {
175                         $p->{'wait_left'} = 0;
176                         $p->{'wait_time'} = 10;
177                 }
178                 elsif (TYPE_READ == $type) {
179                         WARNING ("${plugin}->read() failed with status $status. "
180                                 . "Will suspend it for $p->{'wait_left'} seconds.");
181
182                         $p->{'wait_left'} = $p->{'wait_time'};
183                         $p->{'wait_time'} *= 2;
184
185                         if ($p->{'wait_time'} > 86400) {
186                                 $p->{'wait_time'} = 86400;
187                         }
188                 }
189                 elsif (TYPE_INIT == $type) {
190                         ERROR ("${plugin}->init() failed with status $status. "
191                                 . "Plugin will be disabled.");
192
193                         foreach my $type (keys %types) {
194                                 plugin_unregister ($type, $plugin);
195                         }
196                 }
197                 elsif (TYPE_LOG != $type) {
198                         WARNING ("${plugin}->$types{$type}() failed with status $status.");
199                 }
200         }
201         return 1;
202 }
203
204 # Collectd::plugin_register (type, name, data).
205 #
206 # type:
207 #   init, read, write, shutdown, data set
208 #
209 # name:
210 #   name of the plugin
211 #
212 # data:
213 #   reference to the plugin's subroutine that does the work or the data set
214 #   definition
215 sub plugin_register {
216         my $type = shift;
217         my $name = shift;
218         my $data = shift;
219
220         DEBUG ("Collectd::plugin_register: "
221                 . "type = \"$type\", name = \"$name\", data = \"$data\"");
222
223         if (! ((defined $type) && (defined $name) && (defined $data))) {
224                 ERROR ("Usage: Collectd::plugin_register (type, name, data)");
225                 return;
226         }
227
228         if ((! defined $plugins[$type]) && (TYPE_DATASET != $type)) {
229                 ERROR ("Collectd::plugin_register: Invalid type \"$type\"");
230                 return;
231         }
232
233         if ((TYPE_DATASET == $type) && ("ARRAY" eq ref $data)) {
234                 return plugin_register_data_set ($name, $data);
235         }
236         elsif ((TYPE_DATASET != $type) && (! ref $data)) {
237                 my $pkg = scalar caller;
238
239                 my %p : shared;
240
241                 if ($data !~ m/^$pkg/) {
242                         $data = $pkg . "::" . $data;
243                 }
244
245                 # TODO: make interval_g available at configuration time
246                 %p = (
247                         wait_time => 10,
248                         wait_left => 0,
249                         cb_name   => $data,
250                 );
251
252                 lock @plugins;
253                 $plugins[$type]->{$name} = \%p;
254         }
255         else {
256                 ERROR ("Collectd::plugin_register: Invalid data.");
257                 return;
258         }
259         return 1;
260 }
261
262 sub plugin_unregister {
263         my $type = shift;
264         my $name = shift;
265
266         DEBUG ("Collectd::plugin_unregister: type = \"$type\", name = \"$name\"");
267
268         if (! ((defined $type) && (defined $name))) {
269                 ERROR ("Usage: Collectd::plugin_unregister (type, name)");
270                 return;
271         }
272
273         if (TYPE_DATASET == $type) {
274                 return plugin_unregister_data_set ($name);
275         }
276         elsif (defined $plugins[$type]) {
277                 lock @plugins;
278                 delete $plugins[$type]->{$name};
279         }
280         else {
281                 ERROR ("Collectd::plugin_unregister: Invalid type.");
282                 return;
283         }
284 }
285
286 1;
287
288 # vim: set sw=4 ts=4 tw=78 noexpandtab :
289