~ubuntu-branches/ubuntu/vivid/zentyal-core/vivid-proposed

« back to all changes in this revision

Viewing changes to src/EBox/EventDaemon.pm

  • Committer: Package Import Robot
  • Author(s): Jorge Salamero Sanz
  • Date: 2012-08-28 10:03:33 UTC
  • Revision ID: package-import@ubuntu.com-20120828100333-oz3n5kpav4z0tl27
Tags: 2.3.21+quantal1
New upstream release for Quantal

Show diffs side-by-side

added added

removed removed

Lines of Context:
44
44
use EBox::DBEngineFactory;
45
45
 
46
46
# Core modules
47
 
use Data::Dumper;
48
 
use File::stat;
 
47
use JSON::XS;
49
48
use File::Slurp;
50
49
use IO::Handle;
51
50
use IO::Select;
52
51
use Error qw(:try);
53
52
use POSIX;
54
 
use UNIVERSAL;
55
53
use Time::Local qw(timelocal);
56
54
 
57
55
# Constants:
63
61
#      SCANNING_INTERVAL - Integer interval between scannings
64
62
#
65
63
use constant LOG_TABLE => 'events';
66
 
use constant WATCHERS_DIR      => EBox::Config::conf() . 'events/WatcherEnabled/';
67
 
use constant DISPATCHERS_DIR   => EBox::Config::conf() . 'events/DispatcherEnabled/';
68
 
use constant EVENTS_FIFO       => EBox::Config::tmp() . 'events-fifo';
 
64
use constant EVENTS_FIFO => EBox::Config::tmp() . 'events-fifo';
69
65
use constant SCANNING_INTERVAL => 60;
70
66
use constant EVENT_FOLDING_INTERVAL => 30 * 60; # half hour
71
67
use constant MAX_MSG_LENGTH => 256;
96
92
        # class name with two fields: deadOut (the number of
97
93
        # seconds till next run) and instance (with an
98
94
        # instance of the method).
99
 
        registeredEvents => {},
100
 
        registeredDispatchers => {},
101
 
        lastWatcherScan => time(),
102
 
        lastDispatcherScan => time(),
 
95
        watchers => {},
 
96
        dispatchers => {},
 
97
        json => JSON::XS->new()->allow_blessed(1)->convert_blessed(1),
103
98
    };
104
99
    bless ($self, $class);
105
100
 
170
165
    my ($self, $eventPipe) = @_;
171
166
 
172
167
    # Load watchers classes
173
 
    $self->_loadModules('Watcher');
174
 
    while ('true') {
175
 
        if ( time() - $self->{lastWatcherScan} > SCANNING_INTERVAL) {
176
 
            $self->_loadModules('Watcher');
177
 
        }
178
 
        foreach my $registeredEvent (keys %{$self->{registeredEvents}}) {
179
 
            my $queueElementRef = $self->{registeredEvents}->{$registeredEvent};
 
168
    $self->_loadModules('watcher');
 
169
    while (1) {
 
170
        foreach my $registeredEvent (keys %{$self->{watchers}}) {
 
171
            my $queueElementRef = $self->{watchers}->{$registeredEvent};
180
172
            $queueElementRef->{deadOut} -= $self->{granularity};
181
173
            if ( $queueElementRef->{deadOut} <= 0 ) {
182
174
                my $eventsRef = undef;
187
179
                    my $exception = shift;
188
180
                    EBox::warn("Error executing run from $registeredEvent: $exception");
189
181
                    # Deleting from registered events
190
 
                    delete ($self->{registeredEvents}->{$registeredEvent});
 
182
                    delete ($self->{watchers}->{$registeredEvent});
191
183
                };
192
184
                # An event has happened
193
185
                if ( defined ( $eventsRef )) {
219
211
    }
220
212
 
221
213
    # Load dispatcher classes
222
 
    $self->_loadModules('Dispatcher');
 
214
    $self->_loadModules('dispatcher');
223
215
    # Start main loop with a select
224
216
    open(my $fifo, '+<', EVENTS_FIFO);
225
217
    my $select = new IO::Select();
234
226
                $data = readline($fh);
235
227
            }
236
228
 
237
 
            my $event;
238
 
            {
239
 
                no strict 'vars'; $event = eval $data;
240
 
            }
 
229
            my $event = $self->{json}->decode($data);
 
230
            bless ($event, 'EBox::Event');
241
231
 
242
232
            # log the event if log is enabled
243
233
            if (exists $self->{dbengine}) {
246
236
 
247
237
            # dispatch event to its watchers
248
238
            # skip the given data if it is not a valid EBox::Event object
249
 
            if ( defined($event) and $event->isa('EBox::Event') ) {
 
239
            if (defined($event) and $event->isa('EBox::Event')) {
250
240
                $self->_dispatchEventByDispatcher($event);
251
241
            }
252
242
        }
253
 
        if ( time() - $self->{lastDispatcherScan} > SCANNING_INTERVAL ) {
254
 
            $self->_loadModules('Dispatcher');
255
 
        }
256
243
    }
257
244
}
258
245
 
260
247
 
261
248
# Method: _loadModules
262
249
#
263
 
#       Load dinamically the modules which lays on a directory given a
264
 
#       prefix. This could be: 'Watcher' or 'Dispatcher'.
 
250
#       Load installed watchers or dispatchers.
265
251
#
266
252
# Parameters:
267
253
#
268
 
#       prefix - String the prefix could 'Watcher' or 'Dispatcher'
 
254
#       type - can be 'watcher' or 'dispatcher'
269
255
#
270
256
sub _loadModules
271
257
{
272
 
    my ($self, $prefix) = @_;
273
 
 
274
 
    my ($prefixPath, $registeredField);
275
 
    if ( $prefix eq 'Watcher' ) {
276
 
        $prefixPath = WATCHERS_DIR;
277
 
        $registeredField = 'registeredEvents';
278
 
    } elsif ( $prefix eq 'Dispatcher' ) {
279
 
        $prefixPath = DISPATCHERS_DIR;
280
 
        $registeredField = 'registeredDispatchers';
281
 
    } else {
282
 
        return undef;
283
 
    }
284
 
 
285
 
    opendir ( my $dir, $prefixPath );
286
 
 
287
 
    while ( defined ( my $file = readdir ( $dir ))) {
288
 
        unless ( -e "$prefixPath/$file" ) {
289
 
            if ( -l "$prefixPath/$file" ) {
290
 
                EBox::info("Unlinking broken link $prefixPath/$file");
291
 
                unlink ( "$prefixPath/$file" )
292
 
                    or throw EBox::Exceptions::Internal("Cannot unlink $prefixPath/$file");
293
 
            }
 
258
    my ($self, $type) = @_;
 
259
 
 
260
    my $events = EBox::Global->getInstance(1)->modInstance('events');
 
261
    my $model = $type eq 'watcher' ? $events->model('ConfigureWatchers') : $events->model('ConfigureDispatchers');
 
262
 
 
263
    foreach my $id (@{$model->enabledRows()}) {
 
264
        my $row = $model->row($id);
 
265
        my $className = $row->valueByName($type);
 
266
 
 
267
        eval "use $className";
 
268
        if ($@) {
 
269
            EBox::error("Error loading $type class: $className $@");
294
270
            next;
295
271
        }
296
 
        next unless ( $file =~ m/.*\.pm/g );
297
 
        my ($className) = ($file =~ m/(.*)\.pm/);
298
 
        $className = 'EBox::Event::' . $prefix . '::' . $className;
299
 
        my $instance;
300
 
        # The class may not be included
301
 
        if (not defined ($self->{$registeredField}->{$className})) {
302
 
            eval qq{require "$prefixPath/$file"};
303
 
            if ( $@ ) {
304
 
                EBox::warn("Error loading class: $className $@");
305
 
                next;
306
 
            }
307
 
            EBox::info("$className loaded from $registeredField");
308
 
            if ($prefix eq 'Watcher') {
309
 
                if ($className->isa('EBox::Event::Watcher::Base') and
310
 
                        (not ($className eq 'EBox::Event::Watcher::Base')) ) {
311
 
                    $instance = $className->new();
312
 
                    $self->{$registeredField}->{$className} = {
313
 
                        instance => $instance,
314
 
                        deadOut  => 0,
315
 
                    };
316
 
                } else {
317
 
                    EBox::info("Class $className not derived from EBox::Event::Watcher::Base");
318
 
                }
319
 
            } else {
320
 
                if ($className->isa('EBox::Event::Dispatcher::Abstract') and
321
 
                        (not ($className eq 'EBox::Event::Dispatcher::Abstract')) ) {
322
 
                    $instance = $className->new();
323
 
                    $self->{$registeredField}->{$className} = $instance;
324
 
                } else {
325
 
                    EBox::info("Class $className not derived from EBox::Event::Dispatcher::Abstract");
326
 
                }
327
 
            }
 
272
        my $instance = $className->new();
 
273
        if ($type eq 'watcher') {
 
274
            $self->{watchers}->{$className} = { instance => $instance, deadOut  => 0 };
328
275
        } else {
329
 
            # Check its last modification time in order to reload
330
 
            # the module
331
 
            my $statFile = stat ("$prefixPath/$file");
332
 
            my $lastScan;
333
 
            if (  $prefix eq 'Watcher' ) {
334
 
                $lastScan = $self->{lastWatcherScan};
335
 
            } else {
336
 
                $lastScan = $self->{lastDispatcherScan};
337
 
            }
338
 
            if ( $statFile->mtime() > $lastScan ) {
339
 
                EBox::info("$className reloaded from $registeredField");
340
 
                $self->_deleteFromINC($className);
341
 
                eval qq{require "$prefixPath/$file";};
342
 
                if ( $@ ) {
343
 
                    EBox::warn("Error loading class: $className");
344
 
                    next;
345
 
                }
346
 
                $instance = $className->new();
347
 
                if ( $prefix eq 'Watcher' ) {
348
 
                    my $registeredEvent = $self->{$registeredField}->{$className};
349
 
                    $registeredEvent->{instance} = $instance;
350
 
                    # If the period has plummered to be lower than
351
 
                    # current dead out, set the new period
352
 
                    if ( $registeredEvent->{deadOut} > $registeredEvent->{instance}->period() ) {
353
 
                        $registeredEvent->{deadOut} = $registeredEvent->{instance}->period();
354
 
                    }
355
 
                } elsif ($prefix eq 'Dispatcher') {
356
 
                    $self->{$registeredField}->{$className} = $instance;
357
 
                }
358
 
            }
359
 
        }
360
 
    }
361
 
    closedir ($dir);
362
 
 
363
 
    # Check for deletion
364
 
    foreach my $className ( keys (%{$self->{$registeredField}}) ){
365
 
        my ($fileName) = $className =~ m/.*::(.*)$/g;
366
 
        $fileName .= '.pm';
367
 
        unless ( -e "$prefixPath/$fileName" ) {
368
 
            EBox::info("$className deleted from $registeredField");
369
 
            $self->_deleteFromINC($className);
370
 
            if ( -l "$prefixPath/$fileName" ) {
371
 
                # Delete broken links
372
 
                EBox::info("Unlinking broken link $prefixPath/$fileName");
373
 
                unlink( "$prefixPath/$fileName" )
374
 
                    or throw EBox::Exceptions::Internal("Cannot unlink $prefixPath/$fileName");
375
 
            }
376
 
        }
377
 
 
378
 
#       unless ( -f ( readlink ( "$prefixPath/$fileName" ))) {
379
 
#           EBox::info("$className deleted from $registeredField since the link is broken");
380
 
#           $self->_deleteFromINC($className);
381
 
#           # Remove broken links
382
 
#       }
383
 
    }
384
 
 
385
 
    # Updating timestamp
386
 
    if ($prefix eq 'Watcher') {
387
 
        $self->{lastWatcherScan} = time();
388
 
    } else {
389
 
        $self->{lastDispatcherScan} = time();
390
 
    }
391
 
 
392
 
}
393
 
 
394
 
# Method: _deleteFromINC
395
 
#
396
 
#     Delete a class from the loaded modules
397
 
#
398
 
# Parameters:
399
 
#
400
 
#     className - String the class name in :: format
401
 
#
402
 
sub _deleteFromINC
403
 
{
404
 
    my ($self, $className) = @_;
405
 
 
406
 
    my $pathName = $className;
407
 
    $pathName =~ s/::/\//g;
408
 
    delete $INC{$pathName};
 
276
            $self->{dispatchers}->{$className} = $instance;
 
277
        }
 
278
    }
409
279
}
410
280
 
411
281
# Method: _dispatchEventByDispatcher
426
296
    my $reqByEventRef = $event->dispatchTo();
427
297
 
428
298
    if ( grep { 'any' } @{$reqByEventRef} ) {
429
 
        @requestedDispatchers = values ( %{$self->{registeredDispatchers}} );
 
299
        @requestedDispatchers = values ( %{$self->{dispatchers}} );
430
300
    } else {
431
301
        my @reqByEvent = map { "EBox::Dispatcher::$_" } @{$reqByEventRef};
432
 
        foreach my $dispatcherName (keys (%{$self->{registeredDispatchers}})) {
 
302
        foreach my $dispatcherName (keys (%{$self->{dispatchers}})) {
433
303
            if ( grep { $dispatcherName } @reqByEvent ) {
434
304
                push ( @requestedDispatchers,
435
 
                        $self->{registeredDispatchers}->{$dispatcherName});
 
305
                        $self->{dispatchers}->{$dispatcherName});
436
306
            }
437
307
        }
438
308
    }
441
311
    foreach my $dispatcher (@requestedDispatchers) {
442
312
        try {
443
313
            $dispatcher->enable();
 
314
            EBox::info("Send event to $dispatcher");
444
315
            $dispatcher->send($event);
445
316
        } catch EBox::Exceptions::External with {
446
317
            my ($exc) = @_;
447
318
            EBox::warn($dispatcher->name() . ' is not enabled to send messages');
448
319
            EBox::error($exc->stringify());
449
 
            # Disable dispatcher since it's not enabled to
450
 
            # send events: This is a non-sense after months of testing
451
 
            # eval { require 'EBox::Global'};
452
 
            # my $events = EBox::Global->modInstance('events');
453
 
            # # Disable the model
454
 
            # $events->enableDispatcher( ref ( $dispatcher ), 0);
455
 
            # $events->configureDispatcherModel()->setMessage(
456
 
            #         __x('Dispatcher {name} disabled since it is not able to '
457
 
            #             . 'send events', name => $dispatcher->name()));
458
320
        };
459
321
    }
460
322
}
504
366
        return undef;
505
367
    }
506
368
 
507
 
    my ($year, $mon, $mday, $hour, $min, $sec) = split /[\s\-:]/, $storedEvent->{lasttimestamp};
 
369
    my ($year, $mon, $mday, $hour, $min, $sec) = split /[\s\-:]/, $storedEvent->{lastTimestamp};
508
370
    $year -= 1900;
509
371
    $mon -= 1;
510
 
    my $storedTimestamp =  timelocal($sec,$min,$hour,$mday,$mon,$year);
 
372
    my $storedTimestamp = timelocal($sec,$min,$hour,$mday,$mon,$year);
511
373
 
512
374
    if (($storedTimestamp + EVENT_FOLDING_INTERVAL) >  $event->timestamp()) {
513
375
        # Last event of the same type happened before last
606
468
{
607
469
    my ($self, $eventPipe, $event) = @_;
608
470
 
609
 
    $Data::Dumper::Indent = 0; # turn off pretty print (\n)
610
 
    my $eventStr = Dumper($event);
611
 
 
612
 
    # Remove null characters
613
 
    $eventStr =~ tr/\0//d;
 
471
    my $eventStr = $self->{json}->encode($event);
614
472
 
615
473
    # Sending the dumpered event with a null char
616
474
    print $eventPipe ( $eventStr . "\0" );