From faf23aa972ac6d8e8c89ceb9f1e82299204b9da9 Mon Sep 17 00:00:00 2001
From: Antoine Nguyen <anguyen@merethis.com>
Date: Mon, 11 Mar 2013 13:07:59 +0100
Subject: [PATCH] Perl code refactoring.

* New centreon perl library
* Rewritten script (purge, logAnalyser)

Conflicts:
	www/install/sql/centstorage/Update-CSTG-2.4.1_to_2.4.2.sql
---
 cron/centstorage_purge                        |   8 +
 cron/logAnalyser                              |  15 +
 cron/nightly_tasks_manager                    |  44 ++
 lib/perl/centreon/db.pm                       | 283 +++++++++++
 lib/perl/centreon/lock.pm                     | 141 ++++++
 lib/perl/centreon/logger.pm                   | 129 +++++
 lib/perl/centreon/script.pm                   |  98 ++++
 lib/perl/centreon/script/centstorage_purge.pm | 106 ++++
 lib/perl/centreon/script/logAnalyser.pm       | 466 ++++++++++++++++++
 www/install/createTablesCentstorage.sql       |   1 +
 .../Update-CSTG-2.4.1_to_2.4.2.sql            |   3 +-
 11 files changed, 1293 insertions(+), 1 deletion(-)
 create mode 100755 cron/centstorage_purge
 create mode 100644 cron/logAnalyser
 create mode 100755 cron/nightly_tasks_manager
 create mode 100644 lib/perl/centreon/db.pm
 create mode 100644 lib/perl/centreon/lock.pm
 create mode 100644 lib/perl/centreon/logger.pm
 create mode 100644 lib/perl/centreon/script.pm
 create mode 100644 lib/perl/centreon/script/centstorage_purge.pm
 create mode 100644 lib/perl/centreon/script/logAnalyser.pm

diff --git a/cron/centstorage_purge b/cron/centstorage_purge
new file mode 100755
index 0000000000..7559b9f92c
--- /dev/null
+++ b/cron/centstorage_purge
@@ -0,0 +1,8 @@
+#!/usr/bin/env perl
+
+use warnings;
+use FindBin qw($Bin);
+use lib "$Bin/../lib/perl";
+use centreon::script::centstorage_purge;
+
+centreon::script::centstorage_purge->new()->run();
diff --git a/cron/logAnalyser b/cron/logAnalyser
new file mode 100644
index 0000000000..74b0b072f6
--- /dev/null
+++ b/cron/logAnalyser
@@ -0,0 +1,15 @@
+#!/usr/bin/env perl
+
+use warnings;
+use FindBin qw($Bin);
+use lib "$Bin/../lib/perl";
+use centreon::script::logAnalyser;
+
+$SIG{__DIE__} = sub {
+    my $error = shift;
+
+    print "Error: $error";
+    exit 1;
+};
+
+centreon::script::logAnalyser->new()->run();
diff --git a/cron/nightly_tasks_manager b/cron/nightly_tasks_manager
new file mode 100755
index 0000000000..885a27f210
--- /dev/null
+++ b/cron/nightly_tasks_manager
@@ -0,0 +1,44 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+use Getopt::Long;
+use FindBin qw($Bin);
+use lib "$Bin/../lib/perl";
+use centreon::logger;
+
+use vars qw(@jobs);
+my $logger = centreon::logger->new();
+
+sub run_task {
+    my ($name) = @_;
+
+    if ($name[0] ne '/') {
+        $name = "$Bin/../$name";
+    }
+    $logger->writeLogInfo("$name: start");
+    my $output = qx|$name 2>&1|;
+
+    ($? || !defined $output)
+      ? $logger->writeLogError("Unexpected end!\n$output")
+      : $logger->writeLogInfo($output, withdate => 0);
+    $logger->writeLogInfo("$name: stop");
+}
+
+=head1 main program
+
+Main program starts here.
+
+=cut
+
+my $cfgfile = "/etc/centreon/nightly_tasks.pm";
+my $logfile = "/tmp/$0.log";
+my $result = GetOptions("config=s" => \$cfgfile,
+                        "logfile=s" => \$logfile);
+
+require $cfgfile;
+
+$logger->file_mode($logfile);
+foreach my $task (@jobs) {
+    run_task($task);
+}
diff --git a/lib/perl/centreon/db.pm b/lib/perl/centreon/db.pm
new file mode 100644
index 0000000000..ec5fd17d78
--- /dev/null
+++ b/lib/perl/centreon/db.pm
@@ -0,0 +1,283 @@
+################################################################################
+# Copyright 2005-2011 MERETHIS
+# Centreon is developped by : Julien Mathis and Romain Le Merlus under
+# GPL Licence 2.0.
+# 
+# This program is free software; you can redistribute it and/or modify it under 
+# the terms of the GNU General Public License as published by the Free Software 
+# Foundation ; either version 2 of the License.
+# 
+# This program is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 
+# PARTICULAR PURPOSE. See the GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License along with 
+# this program; if not, see <http://www.gnu.org/licenses>.
+# 
+# Linking this program statically or dynamically with other modules is making a 
+# combined work based on this program. Thus, the terms and conditions of the GNU 
+# General Public License cover the whole combination.
+# 
+# As a special exception, the copyright holders of this program give MERETHIS 
+# permission to link this program with independent modules to produce an executable, 
+# regardless of the license terms of these independent modules, and to copy and 
+# distribute the resulting executable under terms of MERETHIS choice, provided that 
+# MERETHIS also meet, for each linked independent module, the terms  and conditions 
+# of the license of that module. An independent module is a module which is not 
+# derived from this program. If you modify this program, you may extend this 
+# exception to your version of the program, but you are not obliged to do so. If you
+# do not wish to do so, delete this exception statement from your version.
+# 
+# For more information : contact@centreon.com
+# 
+# SVN : $URL
+# SVN : $Id
+#
+####################################################################################
+package centreon::db;
+
+use strict;
+use warnings;
+use DBI;
+
+
+sub new {
+    my ($class, %options) = @_;
+    my %defaults =
+      (
+       logger => undef,
+       db => undef,
+       host => "localhost",
+       user => undef,
+       password => undef,
+       port => 3306,
+       force => 0
+      );
+    my $self = {%defaults, %options};
+
+    $self->{"instance"} = undef;
+    $self->{"type"} = "mysql";
+    $self->{"args"} = [];
+    bless $self, $class;
+    return $self;
+}
+
+# Getter/Setter DB name
+sub db {
+    my $self = shift;
+    if (@_) {
+        $self->{"db"} = shift;
+    }
+    return $self->{"db"};
+}
+
+# Getter/Setter DB host
+sub host {
+    my $self = shift;
+    if (@_) {
+        $self->{"host"} = shift;
+    }
+    return $self->{"host"};
+}
+
+# Getter/Setter DB port
+sub port {
+    my $self = shift;
+    if (@_) {
+        $self->{"port"} = shift;
+    }
+    return $self->{"port"};
+}
+
+# Getter/Setter DB user
+sub user {
+    my $self = shift;
+    if (@_) {
+        $self->{"user"} = shift;
+    }
+    return $self->{"user"};
+}
+
+# Getter/Setter DB force
+sub force {
+    my $self = shift;
+    if (@_) {
+        $self->{"force"} = shift;
+    }
+    return $self->{"force"};
+}
+
+# Getter/Setter DB password
+sub password {
+    my $self = shift;
+    if (@_) {
+        $self->{"password"} = shift;
+    }
+    return $self->{"password"};
+}
+
+sub last_insert_id {
+    my $self = shift;
+    return $self->{instance}->last_insert_id(undef, undef, undef, undef);
+}
+
+sub quote {
+    my $self = shift;
+
+    if (defined($self->{'instance'})) {
+        return $self->{'instance'}->quote($_[0]);
+    }
+    my $num = scalar(@{$self->{"args"}});
+    push @{$self->{"args"}}, $_[0];
+    return "##__ARG__$num##";
+}
+
+sub set_inactive_destroy {
+    my $self = shift;
+
+    if (defined($self->{'instance'})) {
+        $self->{'instance'}->{InactiveDestroy} = 1;
+    }
+}
+
+sub transaction_mode {
+    my ($self, $status) = @_;
+
+    if ($status) {
+        $self->{instance}->begin_work;
+        $self->{instance}->{RaiseError} = 1;
+    } else {
+        $self->{instance}->{AutoCommit} = 1;
+        $self->{instance}->{RaiseError} = 0;
+    }
+}
+
+sub commit { shift->{instance}->commit; }
+
+sub rollback { shift->{instance}->rollback; }
+
+sub kill {
+    my $self = shift;
+
+    if (defined($self->{'instance'})) {
+        $self->{"logger"}->writeLogInfo("KILL QUERY\n");
+        my $rv = $self->{'instance'}->do("KILL QUERY " . $self->{'instance'}->{'mysql_thread_id'});
+        if (!$rv) {
+            my ($package, $filename, $line) = caller;
+            $self->{'logger'}->writeLogError("MySQL error : " . $self->{'instance'}->errstr . " (caller: $package:$filename:$line)\n");
+        }
+    }
+}
+
+# Connection initializer
+sub connect() {
+    my $self = shift;
+    my $logger = $self->{logger};
+    my $status = 0;
+
+    while (1) {
+        $self->{"instance"} = DBI->connect(
+            "DBI:".$self->{"type"} 
+                .":".$self->{"db"}
+                .":".$self->{"host"}
+                .":".$self->{"port"},
+            $self->{"user"},
+            $self->{"password"},
+            { "RaiseError" => 0, "PrintError" => 0, "AutoCommit" => 1 }
+          );
+        if (defined($self->{"instance"})) {
+            last;
+        }
+
+        my ($package, $filename, $line) = caller;
+        $logger->writeLogError("MySQL error : cannot connect to database " . $self->{"db"} . ": " . $DBI::errstr . " (caller: $package:$filename:$line)\n");
+        if ($self->{'force'} == 0) {
+            $status = -1;
+            last;
+        }
+        sleep(5);
+    }
+    return $status;
+}
+
+# Destroy connection
+sub disconnect {
+    my $self = shift;
+    my $instance = $self->{"instance"};
+    if (defined($instance)) {
+        $instance->disconnect;
+        $self->{"instance"} = undef;
+    }
+}
+
+sub do {
+    my ($self, $query) = @_;
+
+    if (!defined $self->{instance}) {
+        if ($self->connect() == -1) {
+            $self->{logger}->writeLogError("Can't connect to the database");
+            return -1;
+        }
+    }
+    my $numrows = $self->{instance}->do($query);
+    die $self->{instance}->errstr if !defined $numrows;
+    return $numrows;
+}
+
+sub error {
+    my ($self, $error, $query) = @_;
+    my ($package, $filename, $line) = caller 1;
+
+    chomp($query);
+    $self->{logger}->writeLogError(<<"EOE");
+MySQL error: $error (caller: $package:$filename:$line)
+Query: $query
+EOE
+    $self->disconnect();
+    $self->{"instance"} = undef;
+}
+
+sub query {
+    my $self = shift;
+    my $query = shift;
+    my $logger = $self->{logger};
+    my $status = 0;
+    my $statement_handle;
+
+    while (1) {
+        if (!defined($self->{"instance"})) {
+            $status = $self->connect();
+            if ($status != -1) {
+                for (my $i = 0; $i < scalar(@{$self->{"args"}}); $i++) {
+                    my $str_quoted = $self->quote(${$self->{"args"}}[0]);
+                    $query =~ s/##__ARG__$i##/$str_quoted/;
+                }
+                $self->{"args"} = [];
+            }
+            if ($status == -1 && $self->{'force'} == 0) {
+                $self->{"args"} = [];
+                last;
+            }
+        }
+
+        $statement_handle = $self->{"instance"}->prepare($query);
+        if (!defined $statement_handle) {
+            $self->error($statement_handle->errstr, $query);
+            $status = -1;
+            last if $self->{'force'} == 0;
+            next;
+        }
+
+        my $rv = $statement_handle->execute;
+        if (!$rv) {
+            $self->error($statement_handle->errstr, $query);
+            $status = -1;
+            last if $self->{force} == 0;
+            next;
+        }
+        last;
+    }
+    return ($status, $statement_handle);
+}
+
+1;
diff --git a/lib/perl/centreon/lock.pm b/lib/perl/centreon/lock.pm
new file mode 100644
index 0000000000..f4fd55369a
--- /dev/null
+++ b/lib/perl/centreon/lock.pm
@@ -0,0 +1,141 @@
+package centreon::lock;
+
+use strict;
+use warnings;
+
+sub new {
+    my ($class, $name, %options) = @_;
+    my %defaults = (name => $name, timeout => 10);
+    my $self = {%defaults, %options};
+
+    bless $self, $class;
+    return $self;
+}
+
+sub is_set {
+    die "Not implemented";
+}
+
+sub set {
+    my $self = shift;
+
+    for (my $i = 0; $self->is_set() && $i < $self->{timeout}; $i++) {
+        sleep 1;
+    }
+    die "Failed to set lock for $self->{name}" if $self->is_set();
+}
+
+package centreon::lock::file;
+
+use base qw(centreon::lock);
+
+sub new {
+    my $class = shift;
+    my $self = $class->SUPER::new(@_);
+
+    if (!defined $self->{pid} || !defined $self->{storagedir}) {
+        die "Can't build lock, required arguments not provided";
+    }
+    bless $self, $class;
+    $self->{pidfile} = "$self->{storagedir}/$self->{name}.lock";
+    return $self;
+}
+
+sub is_set {
+    return -e shift->{pidfile};
+}
+
+sub set {
+    my $self = shift;
+
+    $self->SUPER::set();
+    open LOCK, ">", $self->{pidfile};
+    print LOCK $self->{pid};
+    close LOCK;
+}
+
+sub DESTROY {
+    my $self = shift;
+
+    if (defined $self->{pidfile} && -e $self->{pidfile}) {
+        unlink $self->{pidfile};
+    }
+}
+
+package centreon::lock::sql;
+
+use base qw(centreon::lock);
+
+sub new {
+    my $class = shift;
+    my $self = $class->SUPER::new(@_);
+
+    if (!defined $self->{dbc}) {
+        die "Can't build lock, required arguments not provided";
+    }
+    bless $self, $class;
+    $self->{launch_time} = time();
+    return $self;
+}
+
+sub is_set {
+    my $self = shift;
+    my ($status, $sth) = $self->{dbc}->query(
+        "SELECT id,running,time_launch FROM cron_operation WHERE name LIKE '$self->{name}'"
+    );
+    my $data = $sth->fetchrow_hashref();
+
+    if (!defined $data->{id}) {
+        $self->{not_created_yet} = 1;
+        $self->{previous_launch_time} = 0;
+        return 0;
+    }
+    $self->{id} = $data->{id};
+    $self->{previous_launch_time} = $data->{time_launch};
+    if (defined $data->{running} && $data->{running} == 1) {
+        return 1;
+    }
+    return 0;
+}
+
+sub set {
+    my $self = shift;
+    my $status;
+
+    $self->SUPER::set();
+    if (defined $self->{not_created_yet}) {
+        $status = $self->{dbc}->do(<<"EOQ");
+INSERT INTO cron_operation
+(name, system, activate)
+VALUES ('$self->{name}', '1', '1')
+EOQ
+        goto error if $status == -1;
+        $self->{id} = $self->{dbc}->last_insert_id();
+        return;
+    }
+    $status = $self->{dbc}->do(<<"EOQ");
+UPDATE cron_operation
+SET running = '1', time_launch = '$self->{launch_time}'
+WHERE id = '$self->{id}'
+EOQ
+    goto error if $status == -1;
+    return;
+
+  error:
+    die "Failed to set lock for $self->{name}";
+}
+
+sub DESTROY {
+    my $self = shift;
+
+    if (defined $self->{dbc}) {
+        my $exectime = time() - $self->{launch_time};
+        $self->{dbc}->do(<<"EOQ");
+UPDATE cron_operation
+SET running = '0', last_execution_time = '$exectime'
+WHERE id = '$self->{id}'
+EOQ
+    }
+}
+
+1;
diff --git a/lib/perl/centreon/logger.pm b/lib/perl/centreon/logger.pm
new file mode 100644
index 0000000000..1e53f19a4e
--- /dev/null
+++ b/lib/perl/centreon/logger.pm
@@ -0,0 +1,129 @@
+package centreon::logger;
+
+=head1 NOM
+
+centreon::logger - Simple logging module
+
+=head1 SYNOPSIS
+
+ #!/usr/bin/perl -w
+
+ use strict;
+ use warnings;
+
+ use centreon::polling;
+
+ my $logger = new centreon::logger();
+
+ $logger->writeLogInfo("information");
+
+=head1 DESCRIPTION
+
+This module offers a simple interface to write log messages to various output:
+
+* standard output
+* file
+* syslog
+
+=cut
+
+use strict;
+use warnings;
+use Sys::Syslog qw(:standard :macros);
+use IO::Handle;
+
+my %severities = (1 => LOG_INFO,
+                  2 => LOG_ERR);
+
+sub new {
+    my $class = shift;
+
+    my $self = bless
+      {
+       file => 0,
+       filehandler => undef,
+       # 0 = nothing, 1 = critical, 3 = info
+       severity => 3,
+       # 0 = stdout, 1 = file, 2 = syslog
+       log_mode => 0,
+       # syslog
+       log_facility => undef,
+       log_option => LOG_PID,
+      }, $class;
+    return $self;
+}
+
+sub file_mode($$) {
+    my ($self, $file) = @_;
+
+    if (open($self->{filehandler}, ">>", $file)){
+        $self->{log_mode} = 1;
+        $self->{filehandler}->autoflush(1);
+        return 1;
+    }
+    $self->{filehandler} = undef;
+    print STDERR "Cannot open file $file: $!\n";
+    return 0;
+}
+
+sub syslog_mode($$$) {
+    my ($self, $logopt, $facility) = @_;
+
+    $self->{log_mode} = 2;
+    openlog($0, $logopt, $facility);
+    return 1;
+}
+
+# Getter/Setter Log severity
+sub severity {
+    my $self = shift;
+    if (@_) {
+        $self->{"severity"} = $_[0];
+    }
+    return $self->{"severity"};
+}
+
+sub get_date {
+    my $self = shift;
+    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time());
+    return sprintf("%04d-%02d-%02d %02d:%02d:%02d", 
+                   $year+1900, $mon+1, $mday, $hour, $min, $sec);
+}
+
+sub writeLog($$$%) {
+    my ($self, $severity, $msg, %options) = @_;
+    my $withdate = (defined $options{withdate}) ? $options{withdate} : 1;
+    my $newmsg = ($withdate) 
+      ? $self->get_date . " - $msg" : $msg;
+
+    if (($self->{severity} & $severity) == 0) {
+        return;
+    }
+    if ($self->{log_mode} == 0) {
+        print "$newmsg\n";
+    } elsif ($self->{log_mode} == 1) {
+        if (defined $self->{filehandler}) {
+            print { $self->{filehandler} } "$newmsg\n";
+        }
+    } elsif ($self->{log_mode} == 2) {
+        syslog($severities{$severity}, $msg);
+    }
+}
+
+sub writeLogInfo {
+    shift->writeLog(1, @_);
+}
+
+sub writeLogError {
+    shift->writeLog(2, @_);
+}
+
+sub DESTROY {
+    my $self = shift;
+
+    if (defined $self->{filehandler}) {
+        $self->{filehandler}->close();
+    }
+}
+
+1;
diff --git a/lib/perl/centreon/script.pm b/lib/perl/centreon/script.pm
new file mode 100644
index 0000000000..48edffde99
--- /dev/null
+++ b/lib/perl/centreon/script.pm
@@ -0,0 +1,98 @@
+package centreon::script;
+
+use strict;
+use warnings;
+use Getopt::Long;
+use Pod::Usage;
+use centreon::logger;
+use centreon::db;
+use centreon::lock;
+
+use vars qw($centreon_config);
+
+sub new {
+    my ($class, $name, %options) = @_;
+    my %defaults = 
+      (
+       config_file => "/etc/centreon/centreon-config.pm",
+       log_file => undef,
+       centreon_db_conn => 0,
+       centstorage_db_conn => 0,
+       debug_mode => 0
+      );
+    my $self = {%defaults, %options};
+
+    bless $self, $class;
+    $self->{name} = $name;
+    $self->{logger} = centreon::logger->new();
+    $self->{options} = {
+        "config=s" => \$self->{config_file},
+        "logfile=s" => \$self->{log_file},
+        "debug" => \$self->{debug_mode},
+        "help|?" => \$self->{help}
+    };
+    return $self;
+}
+
+sub init {
+    my $self = shift;
+
+    if (defined $self->{log_file}) {
+        $self->{logger}->file_mode($self->{log_file});
+    }
+
+    if ($self->{centreon_db_conn}) {
+        $self->{cdb} = centreon::db->new
+          (db => $self->{centreon_config}->{centreon_db},
+           host => $self->{centreon_config}->{db_host},
+           user => $self->{centreon_config}->{db_user},
+           password => $self->{centreon_config}->{db_passwd},
+           logger => $self->{logger});
+        $self->{lock} = centreon::lock::sql->new($self->{name}, dbc => $self->{cdb});
+        $self->{lock}->set();
+    }
+    if ($self->{centstorage_db_conn}) {
+        $self->{csdb} = centreon::db->new
+          (db => $self->{centreon_config}->{centstorage_db},
+           host => $self->{centreon_config}->{db_host},
+           user => $self->{centreon_config}->{db_user},
+           password => $self->{centreon_config}->{db_passwd},
+           logger => $self->{logger});
+    }
+}
+
+sub DESTROY {
+    my $self = shift;
+
+    if (defined $self->{cdb}) {
+        $self->{cdb}->disconnect();
+    }
+    if (defined $self->{csdb}) {
+        $self->{csdb}->disconnect();
+    }
+}
+
+sub add_options {
+    my ($self, %options) = @_;
+
+    $self->{options} = {%{$self->{options}}, %options};
+}
+
+sub parse_options {
+    my $self = shift;
+
+    Getopt::Long::Configure('bundling');
+    die "Command line error" if !GetOptions(%{$self->{options}});
+    pod2usage(1) if $self->{help};
+    require $self->{config_file};
+    $self->{centreon_config} = $centreon_config;
+}
+
+sub run {
+    my $self = shift;
+
+    $self->parse_options();
+    $self->init();
+}
+
+1;
diff --git a/lib/perl/centreon/script/centstorage_purge.pm b/lib/perl/centreon/script/centstorage_purge.pm
new file mode 100644
index 0000000000..4136c9cd01
--- /dev/null
+++ b/lib/perl/centreon/script/centstorage_purge.pm
@@ -0,0 +1,106 @@
+package centreon::script::centstorage_purge;
+
+use strict;
+use warnings;
+use centreon::script;
+use centreon::lock;
+
+use base qw(centreon::script);
+
+sub new {
+    my $class = shift;
+    my $self = $class->SUPER::new("centstorage_purge",
+        centreon_db_conn => 1,
+        centstorage_db_conn => 1
+    );
+
+    bless $self, $class;
+    $self->{broker} = "ndo";
+    my ($status, $sth) = $self->{csdb}->query(<<"EOQ");
+SELECT len_storage_mysql,archive_retention,reporting_retention
+FROM config
+EOQ
+    die "Failed to retrieve configuration from database" if $status == -1;
+    $self->{config} = $sth->fetchrow_hashref();
+
+    ($status, $sth) = $self->{cdb}->query(<<"EOQ");
+SELECT `value` FROM `options` WHERE `key` = 'broker'
+EOQ
+    die "Failed to retrieve the broker type from database" if $status == -1;
+    $self->{broker} = $sth->fetchrow_hashref()->{value};
+    return $self;
+}
+
+sub run {
+    my $self = shift;
+
+    $self->SUPER::run();
+    if (defined $self->{config}->{len_storage_mysql} && 
+        $self->{config}->{len_storage_mysql} != 0) {
+        my $delete_limit = time() - 60 * 60 * 24 * $self->{config}->{len_storage_mysql};
+
+        $self->{logger}->writeLogInfo("Purging centstorage.data_bin table...");
+        $self->{csdb}->do("DELETE FROM data_bin WHERE ctime < '$delete_limit'");
+        $self->{logger}->writeLogInfo("Done");
+    }
+
+    if (defined($self->{config}->{archive_retention}) 
+        && $self->{config}->{archive_retention} != 0) {
+        my $last_log = time() - ($self->{config}->{archive_retention} * 24 * 60 * 60);
+        my $table = ($self->{broker} eq "broker") ? "logs" : "log";
+
+        $self->{logger}->writeLogInfo("Purging centstorage.$table table...");
+        eval {
+            my $lock = undef;
+            if ($self->{broker} eq "ndo") {
+                $lock = centreon::lock::sql("logAnalyser", dbc => $self->{cdb});
+                $lock->set();
+            }
+            $self->{csdb}->do("DELETE FROM `$table` WHERE `ctime` < '$last_log'");
+        };
+        if ($@) {
+            $self->{logger}->writeLogError("Failed: $@");
+        } else {
+            $self->{logger}->writeLogInfo("Done");
+        }
+    }
+
+    if (defined($self->{config}->{reporting_retention}) 
+        && $self->{config}->{reporting_retention} != 0) {
+        my $last_log = time() - ($self->{config}->{reporting_retention} * 24 * 60 * 60);
+
+        $self->{logger}->writeLogInfo("Purging log archive tables...");
+        $self->{csdb}->do("DELETE FROM `log_archive_host` WHERE `date_end` < '$last_log'");
+        $self->{csdb}->do("DELETE FROM `log_archive_service` WHERE `date_end` < '$last_log'");
+        $self->{logger}->writeLogInfo("Done");
+    }
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+centstorage_purge - purge centstorage database
+
+=head1 SYNOPSIS
+
+centstorage_purge [options]
+
+=head1 OPTIONS
+
+=over 8
+
+=item B<-help>
+
+Print a brief help message and exits.
+
+=back
+
+=head1 DESCRIPTION
+
+B<This program> will read the given input file(s) and do something
+useful with the contents thereof.
+
+=cut
diff --git a/lib/perl/centreon/script/logAnalyser.pm b/lib/perl/centreon/script/logAnalyser.pm
new file mode 100644
index 0000000000..c4a801a782
--- /dev/null
+++ b/lib/perl/centreon/script/logAnalyser.pm
@@ -0,0 +1,466 @@
+package centreon::script::logAnalyser;
+
+use strict;
+use warnings;
+use File::Path qw(mkpath);
+use centreon::script;
+
+use base qw(centreon::script);
+
+sub new {
+    my $class = shift;
+    my $self = $class->SUPER::new("logAnalyser",
+        centreon_db_conn => 1,
+        centstorage_db_conn => 1
+    );
+
+    bless $self, $class;
+    $self->add_options(
+        "a" => \$self->{opt_a}, "archives" => \$self->{opt_a},
+        "p=s" => \$self->{opt_p}, "poller" => \$self->{opt_p},
+        "s=s" => \$self->{opt_s}, "startdate" => \$self->{opt_s}
+    );
+    $self->{launch_time} = time();
+    $self->{msg_type5_disabled} = 0;
+    $self->{queries_per_transaction} = 500;
+    return $self;
+}
+
+sub read_config {
+    my $self = shift;
+    my ($status, $sth) = $self->{cdb}->query("SELECT `value` FROM `options` WHERE `key` = 'broker'");
+
+    goto error if $status == -1;
+    if ($sth->fetchrow_hashref()->{value} eq "broker") {
+        die "This script is only suitable for NDO";
+    }
+    ($status, $sth) = $self->{csdb}->query(<<"EOQ");
+SELECT archive_log, archive_retention FROM config
+EOQ
+    goto error if $status == -1;
+    $self->{config} = $sth->fetchrow_hashref();
+    die "No configuration found in database" if !defined $self->{config}->{archive_log};
+    return;
+
+  error:
+    die "Failed to read configuration from database"
+}
+
+=head2 date_to_time($date)
+
+Convert $date to a timestamp.
+
+=cut
+sub date_to_time($$) {
+    my ($self, $date) = @_;
+
+    $date =~ s|-|/|g;
+    return int(`date -d $date +%s`);
+}
+
+=head2 time_to_date($timestamp)
+
+Convert $timestamp to a human readable date.
+
+=cut
+sub time_to_date($$) {
+    my ($self, $timestamp) = @_;
+    chomp(my $result = `date -d \@$timestamp +%m-%d-%Y`);
+
+    return $result;
+}
+
+sub reset_position_flag {
+    my ($self, $instance) = @_;
+    my $status = $self->{csdb}->do(<<"EOQ");
+UPDATE instance SET log_flag = '0' WHERE instance_id = '$instance'
+EOQ
+    die "Failed to reset the position flag into database" if $status == -1;
+}
+
+sub commit_to_log {
+    my ($self, $instance, $ctime, $counter) = @_;
+
+    $self->{csdb}->do(<<"EOQ");
+UPDATE instance SET log_flag='$counter', last_ctime='$ctime' WHERE instance_id = '$instance'
+EOQ
+    $self->{csdb}->commit;
+    $self->{csdb}->transaction_mode(1);
+}
+
+=head2 parse_file($logFile, $instance)
+
+Parse a nagios log file.
+
+=cut
+sub parse_file($$$) {
+    my ($self, $logfile, $instance) = @_;
+    my $ctime = 0;
+    my $logdir = "$self->{centreon_config}->{VarLib}/log/$instance";
+    my ($last_position, $nbqueries, $counter) = (0, 0, 0);
+
+    if (!-d $logdir) {
+        mkpath($logdir);
+    }
+    my ($status, $sth) = $self->{csdb}->query(<<"EOQ");
+SELECT `log_flag`,`last_ctime` FROM `instance` WHERE `instance_id`='$instance'
+EOQ
+    die "Cannot read previous run information from database" if $status == -1;
+    my $prev_run_info = $sth->fetchrow_hashref();
+
+    # Get History Flag
+    if (open LOG, $logfile) {
+        my $fline = <LOG>;
+        close LOG;
+        if ($fline =~ m/\[([0-9]*)\]\ /) {
+            chomp($ctime = $1);
+        } else {
+            $self->{logger}->writeLogError("Cannot find ctime in first line for poller $instance");
+        }
+    }
+
+    # Decide if we have to read the nagios.log from the begining
+    if ($ctime && $prev_run_info->{ctime} && $ctime == $prev_run_info->{ctime}) {
+        $last_position = $prev_run_info->{log_flag};
+    }
+
+    # Open Log File for parsing
+    if (!open FILE, $logfile) {
+        $self->{logger}->writeLogError("Cannot open file: $logfile");
+        return;
+    }
+
+    # Skip old lines (already read)
+    if (!$self->{opt_a} && $last_position) {
+        while ($counter < $last_position && <FILE>) {
+            $counter++;
+        }
+    }
+
+    $self->{csdb}->transaction_mode(1);
+    eval {
+        while (<FILE>) {
+            my $cur_ctime;
+
+            if ($_ =~ m/^\[([0-9]*)\]\sSERVICE ALERT\:\s(.*)$/) {
+                my @tab = split(/;/, $2);
+                $cur_ctime = $1;
+                $tab[0] =~ s/\\/\\\\/g;
+                $tab[0] =~ s/\'/\\\'/g;
+                $tab[1] =~ s/\\/\\\\/g;
+                $tab[1] =~ s/\'/\\\'/g;
+                $tab[5] =~ s/\\/\\\\/g; 
+                $tab[5] =~ s/\'/\\\'/g;
+                my $rq = "INSERT INTO `log` (`msg_type`,`ctime`, `host_name` , `service_description`, `status`, `type`, `retry`, `output`, `instance`) VALUES ('0', '$cur_ctime', '".$tab[0]."', '".$tab[1]."', '".$tab[2]."', '".$tab[3]."','".$tab[4]."','".$tab[5]."', '".$instance."')";
+                my $res = $self->{csdb}->do($rq);
+            } elsif ($_ =~ m/^\[([0-9]*)\]\sHOST ALERT\:\s(.*)$/) {
+                my @tab = split(/;/, $2);
+                $cur_ctime = $1;
+                $tab[0] =~ s/\\/\\\\/g;
+                $tab[0] =~ s/\'/\\\'/g;
+                if (defined($tab[4]) && $tab[4]) {
+                    $tab[4] =~ s/\\/\\\\/g;
+                    $tab[4] =~ s/\'/\\\'/g;
+                }
+                my $rq = "INSERT INTO `log` (`msg_type`,`ctime`, `host_name` , `status`,  `type`, `retry`, `output`, `instance`) VALUES ('1', '$cur_ctime', '".$tab[0]."', '".$tab[1]."', '".$tab[2]."','".$tab[3]."','".$tab[4]."', '".$instance."')";
+                my $res = $self->{csdb}->do($rq);
+            } elsif ($_ =~ m/^\[([0-9]*)\]\sSERVICE NOTIFICATION\:\s(.*)$/) {
+                my @tab = split(/;/, $2);
+                $cur_ctime = $1;
+                $tab[2] =~ s/\\/\\\\/g;
+                $tab[2] =~ s/\'/\\\'/g;
+                $tab[1] =~ s/\\/\\\\/g;
+                $tab[1] =~ s/\'/\\\'/g;
+                if (defined($tab[5])) {
+                    $tab[5] =~ s/\\/\\\\/g; 
+                    $tab[5] =~ s/\'/\\\'/g;
+                } else {
+                    $tab[5] = "";
+                }
+                my $rq = "INSERT INTO `log` (`msg_type`,`ctime`, `host_name` , `service_description`, `status`, `notification_cmd`, `notification_contact`, `output`, `instance`) VALUES ('2', '$cur_ctime', '".$tab[1]."', '".$tab[2]."', '".$tab[3]."', '".$tab[4]."','".$tab[0]."','".$tab[5]."', '".$instance."')";
+                my $res = $self->{csdb}->do($rq);
+            } elsif ($_ =~ m/^\[([0-9]*)\]\sHOST NOTIFICATION\:\s(.*)$/) {
+                my @tab = split(/;/, $2);
+                $cur_ctime = $1;
+                if (defined($tab[4])) {
+                    $tab[4] =~ s/\\/\\\\/g; 
+                    $tab[4] =~ s/\'/\\\'/g;
+                } else {
+                    $tab[4] = "";
+                }
+                my $rq = "INSERT INTO `log` (`msg_type`,`ctime`, `notification_contact`, `host_name` , `status`, `notification_cmd`,  `output`, `instance`) VALUES ('3', '$cur_ctime', '".$tab[0]."','".$tab[1]."', '".$tab[2]."', '".$tab[3]."','".$tab[4]."', '".$instance."')";
+                my $res = $self->{csdb}->do($rq);
+            } elsif ($_ =~ m/^\[([0-9]*)\]\sCURRENT\sHOST\sSTATE\:\s(.*)$/) {
+                my @tab = split(/;/, $2);
+                $cur_ctime = $1;
+                $tab[0] =~ s/\\/\\\\/g;
+                $tab[0] =~ s/\'/\\\'/g;
+                my $rq = "INSERT INTO `log` (`msg_type`, `ctime`, `host_name` , `status`, `type`, `instance`) VALUES ('7', '$cur_ctime', '".$tab[0]."', '".$tab[1]."', '".$tab[2]."', '".$instance."')";
+                my $res = $self->{csdb}->do($rq);
+            } elsif ($_ =~ m/^\[([0-9]*)\]\sCURRENT\sSERVICE\sSTATE\:\s(.*)$/) {
+                my @tab = split(/;/, $2);
+                $cur_ctime = $1;
+                $tab[0] =~ s/\\/\\\\/g;
+                $tab[0] =~ s/\'/\\\'/g;
+                $tab[1] =~ s/\\/\\\\/g;
+                $tab[1] =~ s/\'/\\\'/g;
+                my $rq = "INSERT INTO `log` (`msg_type`, `ctime`, `host_name`, `service_description` , `status`, `type`, `instance`) VALUES ('6', '$cur_ctime', '".$tab[0]."', '".$tab[1]."', '".$tab[2]."', '".$tab[3]."', '".$instance."')";
+                my $res = $self->{csdb}->do($rq);
+            } elsif ($_ =~ m/^\[([0-9]*)\]\sINITIAL\sHOST\sSTATE\:\s(.*)$/) {
+                my @tab = split(/;/, $2);
+                $cur_ctime = $1;
+                $tab[0] =~ s/\\/\\\\/g;
+                $tab[0] =~ s/\'/\\\'/g;
+                my $rq = "INSERT INTO `log` (`msg_type`, `ctime`, `host_name` , `status`, `type`, `instance`) VALUES ('9', '$cur_ctime', '".$tab[0]."', '".$tab[1]."', '".$tab[2]."', '".$instance."')";
+                my $res = $self->{csdb}->do($rq);
+            } elsif ($_ =~ m/^\[([0-9]*)\]\sINITIAL\sSERVICE\sSTATE\:\s(.*)$/) {
+                my @tab = split(/;/, $2);
+                $cur_ctime = $1;
+                $tab[0] =~ s/\\/\\\\/g;
+                $tab[0] =~ s/\'/\\\'/g;
+                $tab[1] =~ s/\\/\\\\/g;
+                $tab[1] =~ s/\'/\\\'/g;
+                my $rq = "INSERT INTO `log` (`msg_type`, `ctime`, `host_name`, `service_description` , `status`, `type`, `instance`) VALUES ('8', '$cur_ctime', '".$tab[0]."', '".$tab[1]."', '".$tab[2]."', '".$tab[3]."', '".$instance."')";
+                my $res = $self->{csdb}->do($rq);
+            } elsif ($_ =~ m/^\[([0-9]*)\]\sEXTERNAL\sCOMMAND\:\sACKNOWLEDGE\_SVC\_PROBLEM\;(.*)$/) {
+                $cur_ctime = $1;
+                my @tab = split(/;/, $2);
+                $tab[0] =~ s/\\/\\\\/g;
+                $tab[0] =~ s/\'/\\\'/g;
+                $tab[1] =~ s/\\/\\\\/g;
+                $tab[1] =~ s/\'/\\\'/g;
+                if (!defined($tab[6])) {
+                    $tab[6] = "";
+                }
+                $tab[6] =~ s/\\/\\\\/g;
+                $tab[6] =~ s/\'/\\\'/g;
+                my $rq = "INSERT INTO `log` (`msg_type`, `ctime`, `host_name`, `service_description`, `notification_contact`, `output`, `instance`) VALUES ('10', '$cur_ctime', '".$tab[0]."', '".$tab[1]."', '".$tab[5]."', '".$tab[6]."','".$instance."')";
+                my $res = $self->{csdb}->do($rq);
+            } elsif ($_ =~ m/^\[([0-9]*)\]\sEXTERNAL\sCOMMAND\:\sACKNOWLEDGE\_HOST\_PROBLEM\;(.*)$/) {
+                $cur_ctime = $1;
+                my @tab = split(/;/, $2);
+                $tab[0] =~ s/\\/\\\\/g;
+                $tab[0] =~ s/\'/\\\'/g;
+                $tab[5] =~ s/\\/\\\\/g;
+                $tab[5] =~ s/\'/\\\'/g;
+                my $rq = "INSERT INTO `log` (`msg_type`, `ctime`, `host_name`, `notification_contact`, `output`, `instance`) VALUES ('11', '$cur_ctime', '".$tab[0]."', '".$tab[4]."', '".$tab[5]."','".$instance."')";
+                my $res = $self->{csdb}->do($rq);
+            } elsif ($_ =~ m/^\[([0-9]*)\]\sWarning\:\s(.*)$/) {
+                my $tab = $2;
+                $cur_ctime = $1;
+                $tab =~ s/\\/\\\\/g; 
+                $tab =~ s/\'/\\\'/g;
+                my $rq = "INSERT INTO `log` (`msg_type`,`ctime`, `output`, `instance`) VALUES ('4','$cur_ctime', '".$tab."', '".$instance."')";
+                my $res = $self->{csdb}->do($rq);
+            } elsif ($_ =~ m/^\[([0-9]*)\]\s(.*)$/ && (!$self->{msg_type5_disabled})) {
+                $cur_ctime = $1;
+                my $tab = $2;
+                $tab =~ s/\\/\\\\/g; 
+                $tab =~ s/\'/\\\'/g;
+                my $rq = "INSERT INTO `log` (`msg_type`,`ctime`, `output`, `instance`) VALUES ('5','$cur_ctime', '".$tab."', '".$instance."')";
+                my $res = $self->{csdb}->do($rq);
+            }
+            $counter++;
+            $nbqueries++;
+            if ($nbqueries == $self->{queries_per_transaction}) {
+                $self->commit_to_log($instance, $ctime, $counter);
+                $nbqueries = 0;
+            }
+        }
+        $self->commit_to_log($instance, $ctime, $counter);
+    };
+    close FILE;
+    if ($@) {
+        $self->{csdb}->rollback;
+        die "Database error: $@";
+    }
+    $self->{csdb}->transaction_mode(0);
+}
+
+=head2 parse_archives($instance, $localhost, $startdate)
+
+Parse log file archices for a given poller (B<$instance>). An
+optionnal B<$startdate> can be provided.
+
+=cut
+sub parse_archives {
+    my ($self, $instance, $localhost, $startdate) = @_;
+    my $archives;
+
+    if ($localhost) {
+        my ($status, $sth) = $self->{cdb}->query(<<"EOQ");
+SELECT `log_archive_path` FROM `cfg_nagios`, `nagios_server` 
+WHERE `nagios_server_id` = '$instance' 
+AND `nagios_server`.`id` = `cfg_nagios`.`nagios_server_id` 
+AND `nagios_server`.`ns_activate` = '1' 
+AND `cfg_nagios`.`nagios_activate` = '1'
+EOQ
+        die "Failed to read instance configuration" if $status == -1;
+        $archives = $sth->fetchrow_hashref()->{log_archive_path};
+    } else {
+        $archives = "$self->{centreon_config}->{VarLib}/log/$instance/archives/";
+    }
+
+    $archives .= "/" if (!($archives =~ /\/$/));
+    if (!-d $archives) {
+        $self->{logger}->writeLogError("No archives for poller $instance");
+        return;
+    }
+
+    my @log_files = split /\s/, `ls $archives`;
+    my $last_log = undef;
+
+    if (!defined $startdate) {
+        $last_log = time() - ($self->{config}->{archive_retention} * 24 * 60 * 60);
+    } else {
+        $last_log = $self->date_to_time($startdate);
+    }
+    foreach (@log_files) {
+        $_ =~ /nagios\-([0-9\-]+).log/;
+        my @time = split /\-/, $1;
+        my $temp = "$time[0]/$time[1]/$time[2]";
+        $temp = `date -d $temp +%s`;
+        if ($temp > $last_log) {
+            my $curarchive = "$archives$_";
+
+            $self->{logger}->writeLogInfo("Parsing log file: $curarchive");
+            if (!-r $curarchive) {
+                $self->{logger}->writeLogError("Cannot read file $curarchive");
+                next;
+            }
+            $self->parse_file($curarchive, $instance);
+        }
+    }
+}
+
+=head2 parse_logfile($instance, $localhost, $previous_launch_time)
+
+Parse the current nagios log file for a given poller.
+
+=cut
+sub parse_logfile($$$) {
+    my ($self, $instance, $localhost, $previous_launch_time) = @_;
+    my ($logfile, $archivepath);
+
+    if ($localhost) {
+        my ($status, $sth) = $self->{cdb}->query(<<"EOQ");
+SELECT `log_file`, `log_archive_path` 
+FROM `cfg_nagios`, `nagios_server` 
+WHERE `nagios_server_id` = '$instance' 
+AND `nagios_server`.`id` = `cfg_nagios`.`nagios_server_id` 
+AND `nagios_server`.`ns_activate` = '1' 
+AND `cfg_nagios`.`nagios_activate` = '1'
+EOQ
+        die "Cannot read logfile from database" if $status == -1;
+        my $data = $sth->fetchrow_hashref();
+        $logfile = $data->{log_file};
+        $archivepath = $data->{log_archive_path};
+        $archivepath .= "/" if ($archivepath !~ /\/$/);
+        die "Failed to open $logfile" if !-r $logfile;
+
+        my @now = localtime();
+        my $archname = "$archivepath/nagios-" . $self->time_to_date($self->{launch_time}) . "-$now[2].log";
+        if (-f $archname) {
+            my $st = stat($archname);
+            if ($st->mtime > $previous_launch_time) {
+                $self->{logger}->writeLogInfo("Parsing rotated file for instance $instance");
+                $self->parse_file($archname, $instance);
+            }
+        }
+    } else {
+        $logfile = "$self->{centreon_config}->{VarLib}/log/$instance/nagios.log";
+        my $rotate_file = "$logfile.rotate";
+
+        if (-e $rotate_file) {
+            $self->{logger}->writeLogInfo("Parsing rotated file for instance $instance");
+            $self->parse_file($rotate_file, $instance);
+            unlink $rotate_file;
+        }
+    }
+
+    $self->parse_file($logfile, $instance);
+}
+
+=head2 run()
+
+Main method.
+
+Several working modes:
+
+* Parse the current log file of each poller (default)
+* Parse the archives of each poller (-a)
+* Parse the archives of a given poller (-p)
+
+When parsing the archives, a start date can be specified.
+
+=cut
+sub run {
+    my $self = shift;
+
+    $self->SUPER::run();
+    $self->read_config();
+
+    if (defined $self->{opt_s}) {
+        if ($self->{opt_s} !~ m/\d{2}-\d{2}-\d{4}/) {
+            $self->{logger}->writeLogError("Invalid start date provided");
+            exit 1;
+        }
+    }
+
+    if (defined $self->{opt_p}) {
+        $self->reset_position_flag($self->{opt_p});
+        $self->{csdb}->do("DELETE FROM `log` WHERE instance='$self->{opt_p}'");
+        $self->parse_archives($self->{opt_p}, 0, $self->{opt_s});
+        return;
+    }
+
+    my $flag = 0;
+    my ($status, $list_sth) = $self->{cdb}->query(<<"EOQ");
+SELECT `id`, `name`, `localhost` FROM `nagios_server` WHERE `ns_activate`=1
+EOQ
+    die "Cannot read pollers list from database" if $status == -1;
+
+    while (my $ns_server = $list_sth->fetchrow_hashref()) {
+        my $sth;
+        ($status, $sth) = $self->{csdb}->query(<<"EOQ");
+SELECT `instance_name` FROM `instance` WHERE `instance_id` = '$ns_server->{id}' LIMIT 1
+EOQ
+        die "Cannot read instance name from database" if $status == -1;
+        if (!$sth->rows()) {
+            $status = $self->{csdb}->do(<<"EOQ");
+INSERT INTO `instance` 
+(`instance_id`, `instance_name`, `log_flag`)
+VALUES ('$ns_server->{id}', '$ns_server->{name}', '0')
+EOQ
+            die "Cannot save instance to database" if $status == -1;
+        } else {
+            $status = $self->{csdb}->do(<<"EOQ");
+UPDATE `instance` SET `instance_name` = '$ns_server->{name}' 
+WHERE `instance_id` = '$ns_server->{id}' LIMIT 1
+EOQ
+            die "Cannot update instance from database" if $status == -1;
+        }
+        $self->{logger}->writeLogInfo("Poller: $ns_server->{name}");
+        if ($self->{opt_a}) {
+            if (!$flag) {
+                if (!defined $self->{opt_s}) {
+                    $status = $self->{csdb}->do("TRUNCATE TABLE `log`");
+                    $self->{logger}->writeLogError("Failed to truncate 'log' table") if $status == -1;
+                } else {
+                    my $limit = $self->date_to_time($self->{opt_s});
+                    $status = $self->{csdb}->do("DELETE FROM `log` WHERE `ctime` >= $limit");
+                    $self->{logger}->writeLogError("Failed to purge 'log' table") if $status == -1;
+                }
+                $flag = 1;
+            }
+            $self->reset_position_flag($ns_server->{id});
+            $self->parse_archives($ns_server->{id}, $ns_server->{localhost}, $self->{opt_s});
+        } else {
+            $self->parse_logfile($ns_server->{id}, $ns_server->{localhost}, 
+                                 $self->{lock}->{previous_launch_time});
+        }
+    }
+    $self->{logger}->writeLogInfo("Done");
+}
+
+1;
diff --git a/www/install/createTablesCentstorage.sql b/www/install/createTablesCentstorage.sql
index 42328a33b8..80a5f46198 100644
--- a/www/install/createTablesCentstorage.sql
+++ b/www/install/createTablesCentstorage.sql
@@ -173,6 +173,7 @@ CREATE TABLE `instance` (
   `instance_alias` varchar(254) DEFAULT NULL,
   `log_flag` int(11) DEFAULT NULL,
   `log_md5` varchar(255) DEFAULT NULL,
+  `last_ctime` int(11) DEFAULT 0,
   PRIMARY KEY (`instance_id`),
   UNIQUE KEY `instance_name` (`instance_name`)
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
diff --git a/www/install/sql/centstorage/Update-CSTG-2.4.1_to_2.4.2.sql b/www/install/sql/centstorage/Update-CSTG-2.4.1_to_2.4.2.sql
index 14118bb95f..7d11785866 100644
--- a/www/install/sql/centstorage/Update-CSTG-2.4.1_to_2.4.2.sql
+++ b/www/install/sql/centstorage/Update-CSTG-2.4.1_to_2.4.2.sql
@@ -1 +1,2 @@
-ALTER TABLE `downtimes` ADD INDEX `downtimeManager_hostList` (`host_id`, `start_time`);
\ No newline at end of file
+ALTER TABLE `downtimes` ADD INDEX `downtimeManager_hostList` (`host_id`, `start_time`);
+alter table instance add last_ctime int(11) default 0 after log_md5;
-- 
GitLab