<commit_before>################################################################################
# Copyright 2005-2013 MERETHIS
# Centreon is developped by : <NAME> and <NAME> 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.
# 
#
####################################################################################

package centreon::centstorage::CentstoragePool;

use strict;
use warnings;
use centreon::common::db;
use centreon::common::misc;
use centreon::centstorage::CentstorageLib;
use centreon::centstorage::CentstorageRebuild;

my %handlers = (TERM => {}, CHLD => {}, HUP => {});
my %rrd_trans = (g => 0, c => 1, d => 2, a => 3);

sub new {
    my $class = shift;
    my $self  = {};
    $self->{logger} = shift;
    $self->{rrd} = shift;
    $self->{rebuild_progress} = shift;
    $self->{dbcentreon} = undef;
    $self->{dbcentstorage} = undef;

    # skip if we don't find IDS in config
    $self->{skip_if_no_ids} = 1;
    
    $self->{len_storage_rrd} = undef;
    $self->{rrd_metrics_path} = undef;
    $self->{rrd_status_path} = undef;
    $self->{main_perfdata_file} = undef;
    $self->{interval_time} = undef;
    $self->{do_rrd_status} = 1;
    $self->{TIMEOUT} = 30;
    $self->{num_pool} = undef;
    # If rebuild in progress, we don't try to insert in DATA_BIN

    $self->{storage_type} = undef;
    # { 
    #    'metrics' => {'name1' => {}, 'name2' => {} }
    #    'service_id' => 
    #    'host_id' => 
    #    'storage_type' =>
    #    'check_interval' =>
    #    'index_id' =>
    #    'rebuild' =>
    #    'rrd_retention' => 
    # }
    $self->{cache_service} = {};
    
    # Perfdata parsing vars
    $self->{perfdata_pos} = undef;
    $self->{perfdata_size} = undef;
    $self->{perfdata_chars} = undef;

    $self->{perfdata_parser_stop} = 0;

    $self->{service_perfdata} = undef;
    $self->{metric_name} = undef;
    $self->{metric_value} = undef;
    $self->{metric_unit} = undef;
    $self->{metric_warn} = undef;
    $self->{warn_low} = undef;
    $self->{warn_threshold_mode} = undef;
    $self->{metric_crit} = undef;
    $self->{crit_low} = undef;
    $self->{crit_threshold_mode} = undef;
    $self->{metric_min} = undef;
    $self->{metric_max} = undef;

    # By service
    $self->{cache_services_failed} = {};
    $self->{last_check_failed} = time();
    $self->{check_failed_every} = 60 * 10; # 20 minutes

    # Rename
    $self->{cache_services_rename} = {};
    $self->{rename_rebuild_wait} = 0;
    $self->{rename_old_new} = {};

    $self->{group_databin_stmt} = "";
    $self->{group_databin_total} = 0;
    $self->{group_databin_append} = "";
    $self->{group_databin_max} = 500;

    $self->{rebuild_index_id} = undef;
    $self->{rebuild_key} = undef;
    $self->{current_pid} = undef;

    # reload flag
    $self->{reload} = 1;
    $self->{config_file} = undef;
    
    $self->{save_read} = [];
    $self->{read_select} = undef;
    $self->{pipe_write} = undef;
    bless $self, $class;
    $self->set_signal_handlers;
    return $self;
}

sub set_signal_handlers {
    my $self = shift;

    $SIG{TERM} = \&class_handle_TERM;
    $handlers{TERM}->{$self} = sub { $self->handle_TERM() };
    $SIG{CHLD} = \&class_handle_CHLD;
    $handlers{CHLD}->{$self} = sub { $self->handle_CHLD() };
    $SIG{HUP} = \&class_handle_HUP;
    $handlers{HUP}->{$self} = sub { $self->handle_HUP() };
}

sub handle_HUP {
    my $self = shift;
    $self->{reload} = 0;
}

sub handle_TERM {
    my $self = shift;
    $self->{logger}->writeLogInfo("$$ Receiving order to stop...");

    if (defined($self->{current_pid})) {
        $self->{logger}->writeLogInfo("Send -TERM signal to rebuild process..");
        kill('TERM', $self->{current_pid});
    }

    ###
    # Flush Data_bin
    ###
    eval {
        local $SIG{ALRM} = sub { die "alarm\n" };
        alarm 5;
        $self->flush_mysql(1);
        alarm 0;
    };
    if ($@) {
        $self->{dbcentstorage}->kill();
    }
    $self->{dbcentreon}->disconnect() if (defined($self->{dbcentreon}));
    $self->{dbcentstorage}->disconnect() if (defined($self->{dbcentstorage}));

    ###
    # Flush RRD
    ###
    $self->{'rrd'}->flush_all(1);

    ###
    # Write In File
    ###
    if (open(FILE, '>> ' . $self->{main_perfdata_file} . "_" . $self->{num_pool} . ".bckp")) {
        foreach my $id (keys %{$self->{cache_services_failed}}) {
            foreach (@{$self->{cache_services_failed}->{$id}}) {
                print FILE join("\t", @$_) . "\n";
            }
        }

        # Rename
        foreach my $id (keys %{$self->{cache_services_rename}}) {
            foreach (@{$self->{cache_services_rename}->{$id}}) {
                print FILE join("\t", @$_) . "\n";
            }
        }
        

        ### Try to read pipe
        my @rh_set = $self->{read_select}->can_read(1);
        if (scalar(@rh_set) > 0) {
            foreach my $rh (@rh_set) {
                my $read_done = 0;
                while ((my ($status_line, $readline) = centreon::common::misc::get_line_pipe($rh, \@{$self->{save_read}}, \$read_done))) {
                    last if ($status_line <= 0);
                    if ($readline =~ /^UPDATE/) {
                        $readline =~ s/^UPDATE\t//;
                        print FILE $readline . "\n";
                    }
                }
            }
        }
        close FILE;
    } else {
        $self->{"logger"}->writeLogError("Cannot open " . $self->{main_perfdata_file} . "_" . $self->{num_pool} . ".bckp file : $!");
    }

    ###
    # Check Child
    ###
    my $kill_or_not = 1;
    for (my $i = 0; $i < $self->{TIMEOUT}; $i++) {
        if (!defined($self->{current_pid})) {
            $kill_or_not = 0;
            last;
        }
        sleep(1);
    }

    if ($kill_or_not == 1) {
        $self->{logger}->writeLogInfo("Send -KILL signal to rebuild process..");
        kill('KILL', $self->{current_pid});
    }
}

sub handle_CHLD {
    my $self = shift;
    my $child_pid;
    my $exit_code;

    $self->{logger}->writeLogInfo("Received SIGCHLD...");
    $self->{current_pid} = undef;
    if ($self->{rename_rebuild_wait} == 1) {
        my ($new_host_name, $new_service_description) = split(';', $self->{rename_old_new}->{$self->{rebuild_key}});
        $self->force_flush_rrd($self->{rebuild_key});
        delete $self->{cache_service}->{$self->{rebuild_key}};
        delete $self->{cache_services_failed}->{$self->{rebuild_key}};
        delete $self->{rename_old_new}->{$self->{rebuild_key}};
        $self->send_rename_finish($new_host_name, $new_service_description);
    }
    $self->rebuild_finish();
    while (($child_pid = waitpid(-1, &POSIX::WNOHANG)) > 0) {
        $exit_code = $? >> 8;
    }
    $SIG{CHLD} = \&class_handle_CHLD;
}

sub class_handle_TERM {
    foreach (keys %{$handlers{TERM}}) {
        &{$handlers{TERM}->{$_}}();
    }
    exit(0);
}

sub class_handle_CHLD {
    foreach (keys %{$handlers{CHLD}}) {
        &{$handlers{CHLD}->{$_}}();
    }
}

sub class_handle_HUP {
    foreach (keys %{$handlers{HUP}}) {
        &{$handlers{HUP}->{$_}}();
    }
}

sub reload {
    my $self = shift;
    
    $self->{logger}->writeLogInfo("Reload in progress for pool process " . $self->{num_pool} . "...");
    # reopen file
    if ($self->{logger}->is_file_mode()) {
        $self->{logger}->file_mode($self->{logger}->{file_name});
    }
    $self->{logger}->redirect_output();
    
    my ($status, $status_cdb, $status_csdb) = centreon::common::misc::reload_db_config($self->{logger}, $self->{config_file},
                                                                                       $self->{dbcentreon}, $self->{dbcentstorage});
    if ($status_cdb == 1) {
        $self->{dbcentreon}->disconnect();
        $self->{dbcentreon}->connect();
    }
    if ($status_csdb == 1) {
        $self->{dbcentstorage}->disconnect();
        $self->{dbcentstorage}->connect();
    }
    centreon::common::misc::check_debug($self->{logger}, "debug_centstorage", $self->{dbcentreon}, "centstorage pool process " . $self->{num_pool});

    $self->{reload} = 1;
}

sub add_data_mysql {
    my $self = shift;
    my ($metric_id, $ctime, $value) = @_;
    
    $self->{group_databin_stmt} .= $self->{group_databin_append} . "('$metric_id', '$ctime', '$value')";
    $self->{group_databin_append} = ", ";
    $self->{group_databin_total}++;
}

sub force_flush_rrd {
    my $self = shift;
    my ($key) = @_;

    if (defined($self->{cache_service}->{$key})) {
        foreach (keys %{$self->{cache_service}->{$key}->{metrics}}) {
            $self->{rrd}->flush_metric($self->{cache_service}->{$key}->{metrics}->{$_}->{metric_id});
        }

        $self->{rrd}->flush_status($self->{cache_service}->{$key}->{index_id});
    }
}

sub flush_mysql {
    my $self = shift;
    my ($force) = @_;
    
    return 0 if ($self->{rebuild_progress} == 1 && (!defined($force) || $force == 0));
    if ((defined($force) && $force == 1 && $self->{group_databin_total} > 0) || $self->{group_databin_total} > $self->{group_databin_max}) {
        my $rq = "INSERT INTO `data_bin` (`id_metric`, `ctime`, `value`) VALUES " . $self->{group_databin_stmt};
        my ($status, $stmt) = $self->{dbcentstorage}->query($rq);
        $self->{group_databin_total} = 0;
        $self->{group_databin_append} = "";
        $self->{group_databin_stmt} = "";
    }
}

sub flush_failed {
    my $self = shift;

    if (time() > ($self->{last_check_failed} + $self->{check_failed_every})) {
        # Need to reconnect (maybe a gone away. So we try)
        $self->{dbcentreon}->disconnect();
        $self->{dbcentreon}->connect();

        $self->{logger}->writeLogInfo("Begin Cache Services Failed");
        foreach my $id (keys %{$self->{cache_services_failed}}) {
            next if ($self->{rebuild_progress} == 1 && $id == $self->{rebuild_key});
            my @tmp_ar = ();
            my $lerror = 0;            
            foreach (@{$self->{cache_services_failed}->{$id}}) {
                if ($lerror == 0) {
                    if ($self->update(1, @$_) != 0) {
                        push @tmp_ar, \@$_;
                        $lerror = 1;
                    }
                } else {
                    push @tmp_ar, \@$_;
                }
            }
            if (scalar(@tmp_ar) != 0) {
                @{$self->{cache_services_failed}->{$id}} = @tmp_ar;
            } else {
                delete $self->{cache_services_failed}->{$id};
            }
        }
        $self->{last_check_failed} = time();
    }
}

sub remove_special_char_metric {
    my $self = shift;
    my $remove_special_char = $_[0];

    $remove_special_char =~ s/\./\-/g;
    $remove_special_char =~ s/\,/\-/g;
    $remove_special_char =~ s/\:/\-/g;
    $remove_special_char =~ s/\ /\-/g;
    return $remove_special_char;
}

sub create_metric {
    my $self = shift;
    my ($index_id, $cache_metrics, $metric_name) = @_;

    # Check if exists already
    my ($status, $stmt) = $self->{dbcentstorage}->query("SELECT * FROM `metrics` WHERE `index_id` = '" . $index_id . "' AND `metric_name` = " . $self->{dbcentstorage}->quote($metric_name) . " LIMIT 1");
    return -1 if ($status == -1);
    my $data = $stmt->fetchrow_hashref();
    # move part for compat with old centstorage name
    if (!defined($data)) {
        ($status, $stmt) = $self->{dbcentstorage}->query("SELECT * FROM `metrics` WHERE `index_id` = '" . $index_id . "' AND `metric_name` = " . $self->{dbcentstorage}->quote($self->remove_special_char_metric($metric_name)) . " LIMIT 1");
        return -1 if ($status == -1);
        $data = $stmt->fetchrow_hashref();
        if (defined($data)) {
            ($status) = $self->{dbcentstorage}->query("UPDATE `metrics` SET `metric_name` = " . $self->{dbcentstorage}->quote($metric_name) . " WHERE `index_id` = '" . $index_id . "' AND `metric_name` = " . $self->{dbcentstorage}->quote($self->remove_special_char_metric($metric_name)) . " LIMIT 1");
            return -1 if ($status == -1);
        } else {
            # Insert
            ($status, $stmt) = $self->{dbcentstorage}->query("INSERT INTO `metrics` (`index_id`, `metric_name`, `unit_name`, `warn`, `warn_low`, `warn_threshold_mode`, `crit`, `crit_low`, `crit_threshold_mode`, `min`, `max`, `data_source_type`) VALUES ('" . $index_id . "', " . $self->{dbcentstorage}->quote($metric_name) . ", '" . $self->{metric_unit} . "', " . 
                                    $self->{dbcentstorage}->quote($self->{metric_warn}) . ", " . $self->{dbcentstorage}->quote($self->{warn_low}) . ", " . $self->{dbcentstorage}->quote($self->{warn_threshold_mode}) . ", " . 
                                    $self->{dbcentstorage}->quote($self->{metric_crit}) . ", " . $self->{dbcentstorage}->quote($self->{crit_low}) . ", " . $self->{dbcentstorage}->quote($self->{crit_threshold_mode}) . ", '" .
                                    $self->{metric_min} . "', '" . $self->{metric_max} . "', '" . $self->{metric_type} . "')");    
            return -1 if ($status);
            my $last_insert_id = $self->{dbcentstorage}->last_insert_id();
            $$cache_metrics->{$metric_name} = {metric_id => $last_insert_id, metric_unit => $self->{metric_unit}, 
                                               metric_warn => $self->{metric_warn}, warn_low => $self->{warn_low}, warn_threshold_mode => $self->{warn_threshold_mode}, 
                                               metric_crit => $self->{metric_crit}, crit_low => $self->{crit_low}, crit_threshold_mode => $self->{crit_threshold_mode},
                                               metric_min => $self->{metric_min}, metric_max => $self->{metric_max}, data_source_type => $self->{metric_type}};
            
            return 0;
        }
    }
    # We get
    $$cache_metrics->{$metric_name} = {metric_id => $data->{metric_id}, metric_unit => defined($data->{unit_name}) ? $data->{unit_name} : "", 
                                       metric_warn => defined($data->{warn}) ? $data->{warn} : "", 
                                       warn_low => defined($data->{warn_low}) ? $data->{warn_low} : "", 
                                       warn_threshold_mode => defined($data->{warn_threshold_mode}) ? $data->{warn_threshold_mode} : "",
                                       metric_crit => defined($data->{crit}) ? $data->{crit} : "", 
                                       crit_low => defined($data->{crit_low}) ? $data->{crit_low} : "", 
                                       crit_threshold_mode => defined($data->{crit_threshold_mode}) ? $data->{crit_threshold_mode} : "",
                                       metric_min => defined($data->{min}) ? $data->{min} : "", 
                                       metric_max => defined($data->{max}) ? $data->{max} : "",
                                       data_source_type => $data->{data_source_type}};
    return 0;
}

sub check_update_extra_metric {
    my $self = shift;
    my $cache_metric = $_[0];

    if ($$cache_metric->{metric_unit} ne $self->{metric_unit} ||
        $$cache_metric->{metric_warn} ne $self->{metric_warn} ||
        $$cache_metric->{warn_low} ne $self->{warn_low} ||
        $$cache_metric->{warn_threshold_mode} ne $self->{warn_threshold_mode} ||
        $$cache_metric->{metric_crit} ne $self->{metric_crit} ||
        $$cache_metric->{crit_low} ne $self->{crit_low} ||
        $$cache_metric->{crit_threshold_mode} ne $self->{crit_threshold_mode} ||
        $$cache_metric->{metric_min} ne $self->{metric_min} ||
        $$cache_metric->{metric_max} ne $self->{metric_max}) {
        $self->{dbcentstorage}->query("UPDATE `metrics` SET `unit_name` = " . $self->{dbcentstorage}->quote($self->{metric_unit}) . ", " . 
                                            "`warn` = " . $self->{dbcentstorage}->quote($self->{metric_warn}) . ", `warn_low` = " . $self->{dbcentstorage}->quote($self->{warn_low}) . ", `warn_threshold_mode` = " . $self->{dbcentstorage}->quote($self->{warn_threshold_mode}) . ", " .
                                            "`crit` = " . $self->{dbcentstorage}->quote($self->{metric_crit}) . ", `crit_low` = " . $self->{dbcentstorage}->quote($self->{crit_low}) . ", `crit_threshold_mode` = " . $self->{dbcentstorage}->quote($self->{crit_threshold_mode}) . ", " .
                                            "`min` = '" . $self->{metric_min} . "', `max` = '" . $self->{metric_max} . "' WHERE `metric_id` = " . $$cache_metric->{metric_id});
        $$cache_metric->{metric_unit} = $self->{metric_unit};
        $$cache_metric->{metric_warn} = $self->{metric_warn};
        $$cache_metric->{warn_low} = $self->{warn_low};
        $$cache_metric->{warn_threshold_mode} = $self->{warn_threshold_mode};
        $$cache_metric->{metric_crit} = $self->{metric_crit};
        $$cache_metric->{crit_low} = $self->{crit_low};
        $$cache_metric->{crit_threshold_mode} = $self->{crit_threshold_mode};
        $$cache_metric->{metric_min} = $self->{metric_min};
        $$cache_metric->{metric_max} = $self->{metric_max};
    }
}

sub send_rename_command {
    my $self = shift;
    my ($old_host_name, $old_service_description, $new_host_name, $new_service_description) = @_;
    
    $self->{logger}->writeLogInfo("Hostname/Servicename changed had been detected " . $old_host_name . "/" . $old_service_description);
    my $fh = $self->{pipe_write};
    print $fh "RENAMECLEAN\t" . $old_host_name . "\t" . $old_service_description . "\t" . $new_host_name . "\t" . $new_service_description . "\n";
}

sub create_service {
    my $self = shift;
    my ($host_id, $service_id, $interval, $host_name, $service_description) = @_;
    my ($status, $stmt);

    if ($host_name =~ /_Module_([a-zA-Z0-9]*)/) {
        ($status, $stmt) = $self->{dbcentstorage}->query("SELECT `id`, `storage_type`, `host_name`, `service_description`, `rrd_retention` FROM `index_data` WHERE `host_name` = " . $self->{dbcentstorage}->quote($host_name) . " AND `service_description` = " . $self->{dbcentstorage}->quote($service_description)  . " LIMIT 1");
    } else {
        ($status, $stmt) = $self->{dbcentstorage}->query("SELECT `id`, `storage_type`, `host_name`, `service_description`, `rrd_retention` FROM `index_data` WHERE `host_id` = " . $host_id . " AND `service_id` = " . $service_id . " LIMIT 1");
    }    
    return -1 if ($status == -1);
    my $data = $stmt->fetchrow_hashref();
    if (defined($data) && ($data->{host_name} ne $host_name || $data->{service_description} ne $service_description)) {
        ($status, $stmt) = $self->{dbcentstorage}->query("UPDATE `index_data` SET `host_name` = " . $self->{dbcentstorage}->quote($host_name) . ", `service_description` = " . $self->{dbcentreon}->quote($service_description) . " WHERE id = " . $data->{'id'});
        if ($status != -1) {
            $self->{cache_service}->{$host_name . ";" . $service_description} = {};
            $self->{cache_service}->{$host_name . ";" . $service_description}->{metrics} = {};
            $self->{cache_service}->{$host_name . ";" . $service_description}->{service_id} = $service_id;
            $self->{cache_service}->{$host_name . ";" . $service_description}->{host_id} = $host_id;
            $self->{cache_service}->{$host_name . ";" . $service_description}->{storage_type} = int($data->{storage_type});
            $self->{cache_service}->{$host_name . ";" . $service_description}->{index_id} = $data->{id};
            $self->{cache_service}->{$host_name . ";" . $service_description}->{check_interval} = $interval;
            $self->{cache_service}->{$host_name . ";" . $service_description}->{rebuild} = 0;
            $self->{cache_service}->{$host_name . ";" . $service_description}->{rrd_retention} = (defined($data->{rrd_retention})) ? $data->{rrd_retention} : -1;
            # Send command to clean cache
            $self->send_rename_command($data->{host_name}, $data->{service_description}, $host_name, $service_description);
            return -2;
        }
    } elsif (defined($data) && ($data->{host_name} eq $host_name || $data->{service_description} eq $service_description)) {
        # same name but exist already
        $self->{cache_service}->{$host_name . ";" . $service_description} = {};
        $self->{cache_service}->{$host_name . ";" . $service_description}->{metrics} = {};
        $self->{cache_service}->{$host_name . ";" . $service_description}->{service_id} = $service_id;
        $self->{cache_service}->{$host_name . ";" . $service_description}->{host_id} = $host_id;
        $self->{cache_service}->{$host_name . ";" . $service_description}->{storage_type} = int($data->{storage_type});
        $self->{cache_service}->{$host_name . ";" . $service_description}->{index_id} = $data->{id};
        $self->{cache_service}->{$host_name . ";" . $service_description}->{check_interval} = $interval;
        $self->{cache_service}->{$host_name . ";" . $service_description}->{rebuild} = 0;
        $self->{cache_service}->{$host_name . ";" . $service_description}->{rrd_retention} = (defined($data->{rrd_retention})) ? $data->{rrd_retention} : -1;
    } else {
        # create
        if ($host_name =~ /_Module_([a-zA-Z0-9]*)/) {
            ($status, $stmt) = $self->{dbcentstorage}->query("INSERT INTO `index_data` (`host_name`, `service_description`, `host_id`, `service_id`, `special`, `storage_type`) VALUES (" . $self->{dbcentstorage}->quote($host_name) . ", " . $self->{dbcentstorage}->quote($service_description) . ", " . $host_id . ", " . $service_id . ", '1', '" . $self->{storage_type} . "')");
        } else {
            ($status, $stmt) = $self->{dbcentstorage}->query("INSERT INTO `index_data` (`host_name`, `service_description`, `host_id`, `service_id`, `storage_type`) VALUES (" . $self->{dbcentstorage}->quote($host_name) . ", " . $self->{dbcentstorage}->quote($service_description) . ", " . $host_id . ", " . $service_id . ", '" . $self->{storage_type} . "')");
        }
        if ($status != -1) {
            $self->{cache_service}->{$host_name . ";" . $service_description} = {};
            $self->{cache_service}->{$host_name . ";" . $service_description}->{metrics} = {};
            $self->{cache_service}->{$host_name . ";" . $service_description}->{service_id} = $service_id;
            $self->{cache_service}->{$host_name . ";" . $service_description}->{host_id} = $host_id;
            $self->{cache_service}->{$host_name . ";" . $service_description}->{storage_type} = $self->{storage_type};
            $self->{cache_service}->{$host_name . ";" . $service_description}->{index_id} = $self->{dbcentstorage}->last_insert_id();
            $self->{cache_service}->{$host_name . ";" . $service_description}->{check_interval} = $interval;
            $self->{cache_service}->{$host_name . ";" . $service_description}->{rebuild} = 0;
            $self->{cache_service}->{$host_name . ";" . $service_description}->{rrd_retention} = -1;
        }
    }

    return $status;
}

sub get_centstorage_information {
    my $self = shift;
    my ($len_storage_rrd, $rrd_metrics_path, $rrd_status_path, $storage_type);

    my ($status, $stmt) = $self->{dbcentstorage}->query("SELECT len_storage_rrd, RRDdatabase_path, RRDdatabase_status_path, storage_type FROM config");
    my $data = $stmt->fetchrow_hashref();
    if (defined($data)) {
        $len_storage_rrd = int($data->{len_storage_rrd});
        $rrd_metrics_path = $data->{RRDdatabase_path};
        $rrd_status_path = $data->{RRDdatabase_status_path};
        $storage_type = int($data->{storage_type});
    }
    return ($status, $len_storage_rrd, $rrd_metrics_path, $rrd_status_path, $storage_type);
}

sub get_centreon_intervaltime {
    my $self = shift;
    my $interval = 60;

    my ($status, $stmt) = $self->{dbcentreon}->query("SELECT `value` AS interval_length FROM options WHERE `key` = 'interval_length'");
    my $data = $stmt->fetchrow_hashref();
    if (defined($data)) {
        $interval = $data->{interval_length};
    }
    return (0, $interval);
}

#############################
### Perfdata Handle
#############################

sub trim {
    my $self = shift;
    $_[0] =~ s/^[ \t]*?//;
    $_[0] =~ s/[ \t]*?$//;
    return $_[0];
}

sub skip_chars {
    my $self = shift;
    $self->{perfdata_pos}++ while ($self->{perfdata_pos} < $self->{perfdata_size} && (${$self->{perfdata_chars}}[$self->{perfdata_pos}] =~ /$_[0]/));
}

sub continue_to {
    my $self = shift;
    my ($forbidden, $stop1, $not_stop_after) = @_;
    my $value = "";

    while ($self->{perfdata_pos} < $self->{perfdata_size}) {
        if (defined($forbidden) && ${$self->{perfdata_chars}}[$self->{perfdata_pos}] =~ /$forbidden/) {
                return undef;
        }
        if (${$self->{perfdata_chars}}[$self->{perfdata_pos}] =~ /$stop1/) {
            if (!defined($not_stop_after)) {
                return $value;
            }
            if (!($self->{perfdata_pos} + 1 < $self->{perfdata_size} && ${$self->{perfdata_chars}}[$self->{perfdata_pos} + 1] =~ /$not_stop_after/)) {
                $self->{perfdata_pos}++;
                return $value;
            }
            $self->{perfdata_pos}++;
        }

        $value .= ${$self->{perfdata_chars}}[$self->{perfdata_pos}];
        $self->{perfdata_pos}++;
    }

    return $value;
}

sub parse_label {
    my $self = shift;
    my $label;

    if (defined(${$self->{perfdata_chars}}[$self->{perfdata_pos}]) && ${$self->{perfdata_chars}}[$self->{perfdata_pos}] eq "'") {
        $self->{perfdata_pos}++;
        $label = $self->continue_to(undef, "'", "'");
    } else {
        $label = $self->continue_to("[ \t]", "=");
    }
    $self->{perfdata_pos}++;

    return $label;
}

sub parse_value {
    my $self = shift;
    my $with_unit = shift;
    my $unit = '';
    my $value = "";
    my $neg = 1;

    if (defined(${$self->{perfdata_chars}}[$self->{perfdata_pos}]) && ${$self->{perfdata_chars}}[$self->{perfdata_pos}] eq "-") {
        $neg = -1;
        $self->{perfdata_pos}++;
    }

    $value = $self->continue_to(undef, "[^0-9\.,]");
    if (defined($value) && $value ne "") {
        $value =~ s/,/./g;
        $value = $value * $neg;
    }
    if (defined($with_unit) && $with_unit == 1) {
        $unit = $self->parse_unit();
    } else {
        $self->skip_chars(";");
    }
    return ($value, $unit);
}

sub parse_unit {
    my $self = shift;
    my $value = "";

    $value = $self->continue_to(undef, "[ \t;]");
    $self->skip_chars(";");
    return $value;
}

sub parse_threshold {
    my $self = shift;
    my $neg = 1;
    my $value_tmp = "";

    my $arobase = 0;
    my $infinite_neg = 0;
    my $infinite_pos = 0;
    my $value_start = "";
    my $value_end = "";
    my $global_status = 1;
    
    if (defined(${$self->{perfdata_chars}}[$self->{perfdata_pos}]) && ${$self->{perfdata_chars}}[$self->{perfdata_pos}] eq "@") {
        $arobase = 1;
        $self->{perfdata_pos}++;
    }

    if (defined(${$self->{perfdata_chars}}[$self->{perfdata_pos}]) && ${$self->{perfdata_chars}}[$self->{perfdata_pos}] eq "~") {
        $infinite_neg = 1;
        $self->{perfdata_pos}++;
    } else {
        if (defined(${$self->{perfdata_chars}}[$self->{perfdata_pos}]) && ${$self->{perfdata_chars}}[$self->{perfdata_pos}] eq "-") {
            $neg = -1;
            $self->{perfdata_pos}++;
        }
        $value_tmp = $self->continue_to(undef, "[^0-9\.,]");
        if (defined($value_tmp) && $value_tmp ne "") {
            $value_tmp =~ s/,/./g;
            $value_tmp = $value_tmp * $neg;
        }
        $neg = 1;
    }

    if (defined(${$self->{perfdata_chars}}[$self->{perfdata_pos}]) && ${$self->{perfdata_chars}}[$self->{perfdata_pos}] eq ":") {
        if ($value_tmp ne "") {
                $value_start = $value_tmp;
        } else {
                $value_start = 0;
        }
        $self->{perfdata_pos}++;

        if (defined(${$self->{perfdata_chars}}[$self->{perfdata_pos}]) && ${$self->{perfdata_chars}}[$self->{perfdata_pos}] eq "-") {
            $neg = -1;
            $self->{perfdata_pos}++;
        }
        $value_end = $self->continue_to(undef, "[^0-9\.,]");
        if (defined($value_tmp) && $value_end ne "") {
            $value_end =~ s/,/./g;
            $value_end = $value_end * $neg;
        } else {
            $infinite_pos = 1;
        }
    } else {
        $value_start = 0;
        $value_end = $value_tmp;
    }
    
    my $value = $self->continue_to(undef, "[ \t;]");
    $self->skip_chars(";");
    if ($value ne '') {
        $self->{logger}->writeLogInfo("Wrong threshold...");
        $global_status = 0;
    }

    if ($infinite_neg == 1) {
        $value_start = '-1e500';
    }
    if ($infinite_pos == 1) {
        $value_end = '1e500';
    }

    return ($global_status, $value_start, $value_end, $arobase);
}

sub get_perfdata {
    my $self = shift;
    my ($counter_type, $perf_label, $perf_value, $perf_unit, $perf_warn, $perf_crit, $perf_min, $perf_max);

    if (!defined($self->{service_perfdata}) || $self->{perfdata_pos} >= $self->{perfdata_size}) {
        return 0;
    }

    $self->skip_chars("[ \t]");
    $perf_label = $self->parse_label();
    if (!defined($perf_label) || $perf_label eq '') {
        $self->{logger}->writeLogError("Wrong perfdata format: " . $self->{service_perfdata});
        return -1 if ($self->{perfdata_parser_stop} == 1);
        return 1;
    }

    ($perf_value, $perf_unit) = $self->parse_value(1);
    if (!defined($perf_value) || $perf_value eq '') {
        $self->{logger}->writeLogError("Wrong perfdata format: " . $self->{service_perfdata});
        return -1 if ($self->{perfdata_parser_stop} == 1);
        return 1;
    }

    my ($status_th_warn, $th_warn_start, $th_warn_end, $th_warn_inclusive) = $self->parse_threshold();
    my ($status_th_crit, $th_crit_start, $th_crit_end, $th_crit_inclusive) = $self->parse_threshold();
    $perf_min = $self->parse_value();
    $perf_max = $self->parse_value();

    $perf_label = $self->trim($perf_label);
    $counter_type = 'g';
    if ($perf_label =~ /^([adc])\[(.*?)\]$/) {<commit_msg>Fix #6012<commit_after>################################################################################
# Copyright 2005-2013 MERETHIS
# Centreon is developped by : <NAME> and <NAME> 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.
# 
#
####################################################################################

package centreon::centstorage::CentstoragePool;

use strict;
use warnings;
use centreon::common::db;
use centreon::common::misc;
use centreon::centstorage::CentstorageLib;
use centreon::centstorage::CentstorageRebuild;

my %handlers = (TERM => {}, CHLD => {}, HUP => {});
my %rrd_trans = (g => 0, c => 1, d => 2, a => 3);

sub new {
    my $class = shift;
    my $self  = {};
    $self->{logger} = shift;
    $self->{rrd} = shift;
    $self->{rebuild_progress} = shift;
    $self->{dbcentreon} = undef;
    $self->{dbcentstorage} = undef;

    # skip if we don't find IDS in config
    $self->{skip_if_no_ids} = 1;
    
    $self->{len_storage_rrd} = undef;
    $self->{rrd_metrics_path} = undef;
    $self->{rrd_status_path} = undef;
    $self->{main_perfdata_file} = undef;
    $self->{interval_time} = undef;
    $self->{do_rrd_status} = 1;
    $self->{TIMEOUT} = 30;
    $self->{num_pool} = undef;
    # If rebuild in progress, we don't try to insert in DATA_BIN

    $self->{storage_type} = undef;
    # { 
    #    'metrics' => {'name1' => {}, 'name2' => {} }
    #    'service_id' => 
    #    'host_id' => 
    #    'storage_type' =>
    #    'check_interval' =>
    #    'index_id' =>
    #    'rebuild' =>
    #    'rrd_retention' => 
    # }
    $self->{cache_service} = {};
    
    # Perfdata parsing vars
    $self->{perfdata_pos} = undef;
    $self->{perfdata_size} = undef;
    $self->{perfdata_chars} = undef;

    $self->{perfdata_parser_stop} = 0;

    $self->{service_perfdata} = undef;
    $self->{metric_name} = undef;
    $self->{metric_value} = undef;
    $self->{metric_unit} = undef;
    $self->{metric_warn} = undef;
    $self->{warn_low} = undef;
    $self->{warn_threshold_mode} = undef;
    $self->{metric_crit} = undef;
    $self->{crit_low} = undef;
    $self->{crit_threshold_mode} = undef;
    $self->{metric_min} = undef;
    $self->{metric_max} = undef;

    # By service
    $self->{cache_services_failed} = {};
    $self->{last_check_failed} = time();
    $self->{check_failed_every} = 60 * 10; # 20 minutes

    # Rename
    $self->{cache_services_rename} = {};
    $self->{rename_rebuild_wait} = 0;
    $self->{rename_old_new} = {};

    $self->{group_databin_stmt} = "";
    $self->{group_databin_total} = 0;
    $self->{group_databin_append} = "";
    $self->{group_databin_max} = 500;

    $self->{rebuild_index_id} = undef;
    $self->{rebuild_key} = undef;
    $self->{current_pid} = undef;

    # reload flag
    $self->{reload} = 1;
    $self->{config_file} = undef;
    
    $self->{save_read} = [];
    $self->{read_select} = undef;
    $self->{pipe_write} = undef;
    bless $self, $class;
    $self->set_signal_handlers;
    return $self;
}

sub set_signal_handlers {
    my $self = shift;

    $SIG{TERM} = \&class_handle_TERM;
    $handlers{TERM}->{$self} = sub { $self->handle_TERM() };
    $SIG{CHLD} = \&class_handle_CHLD;
    $handlers{CHLD}->{$self} = sub { $self->handle_CHLD() };
    $SIG{HUP} = \&class_handle_HUP;
    $handlers{HUP}->{$self} = sub { $self->handle_HUP() };
}

sub handle_HUP {
    my $self = shift;
    $self->{reload} = 0;
}

sub handle_TERM {
    my $self = shift;
    $self->{logger}->writeLogInfo("$$ Receiving order to stop...");

    if (defined($self->{current_pid})) {
        $self->{logger}->writeLogInfo("Send -TERM signal to rebuild process..");
        kill('TERM', $self->{current_pid});
    }

    ###
    # Flush Data_bin
    ###
    eval {
        local $SIG{ALRM} = sub { die "alarm\n" };
        alarm 5;
        $self->flush_mysql(1);
        alarm 0;
    };
    if ($@) {
        $self->{dbcentstorage}->kill();
    }
    $self->{dbcentreon}->disconnect() if (defined($self->{dbcentreon}));
    $self->{dbcentstorage}->disconnect() if (defined($self->{dbcentstorage}));

    ###
    # Flush RRD
    ###
    $self->{'rrd'}->flush_all(1);

    ###
    # Write In File
    ###
    if (open(FILE, '>> ' . $self->{main_perfdata_file} . "_" . $self->{num_pool} . ".bckp")) {
        foreach my $id (keys %{$self->{cache_services_failed}}) {
            foreach (@{$self->{cache_services_failed}->{$id}}) {
                print FILE join("\t", @$_) . "\n";
            }
        }

        # Rename
        foreach my $id (keys %{$self->{cache_services_rename}}) {
            foreach (@{$self->{cache_services_rename}->{$id}}) {
                print FILE join("\t", @$_) . "\n";
            }
        }
        

        ### Try to read pipe
        my @rh_set = $self->{read_select}->can_read(1);
        if (scalar(@rh_set) > 0) {
            foreach my $rh (@rh_set) {
                my $read_done = 0;
                while ((my ($status_line, $readline) = centreon::common::misc::get_line_pipe($rh, \@{$self->{save_read}}, \$read_done))) {
                    last if ($status_line <= 0);
                    if ($readline =~ /^UPDATE/) {
                        $readline =~ s/^UPDATE\t//;
                        print FILE $readline . "\n";
                    }
                }
            }
        }
        close FILE;
    } else {
        $self->{"logger"}->writeLogError("Cannot open " . $self->{main_perfdata_file} . "_" . $self->{num_pool} . ".bckp file : $!");
    }

    ###
    # Check Child
    ###
    my $kill_or_not = 1;
    for (my $i = 0; $i < $self->{TIMEOUT}; $i++) {
        if (!defined($self->{current_pid})) {
            $kill_or_not = 0;
            last;
        }
        sleep(1);
    }

    if ($kill_or_not == 1) {
        $self->{logger}->writeLogInfo("Send -KILL signal to rebuild process..");
        kill('KILL', $self->{current_pid});
    }
}

sub handle_CHLD {
    my $self = shift;
    my $child_pid;
    my $exit_code;

    $self->{logger}->writeLogInfo("Received SIGCHLD...");
    $self->{current_pid} = undef;
    if ($self->{rename_rebuild_wait} == 1) {
        my ($new_host_name, $new_service_description) = split(';', $self->{rename_old_new}->{$self->{rebuild_key}});
        $self->force_flush_rrd($self->{rebuild_key});
        delete $self->{cache_service}->{$self->{rebuild_key}};
        delete $self->{cache_services_failed}->{$self->{rebuild_key}};
        delete $self->{rename_old_new}->{$self->{rebuild_key}};
        $self->send_rename_finish($new_host_name, $new_service_description);
    }
    $self->rebuild_finish();
    while (($child_pid = waitpid(-1, &POSIX::WNOHANG)) > 0) {
        $exit_code = $? >> 8;
    }
    $SIG{CHLD} = \&class_handle_CHLD;
}

sub class_handle_TERM {
    foreach (keys %{$handlers{TERM}}) {
        &{$handlers{TERM}->{$_}}();
    }
    exit(0);
}

sub class_handle_CHLD {
    foreach (keys %{$handlers{CHLD}}) {
        &{$handlers{CHLD}->{$_}}();
    }
}

sub class_handle_HUP {
    foreach (keys %{$handlers{HUP}}) {
        &{$handlers{HUP}->{$_}}();
    }
}

sub reload {
    my $self = shift;
    
    $self->{logger}->writeLogInfo("Reload in progress for pool process " . $self->{num_pool} . "...");
    # reopen file
    if ($self->{logger}->is_file_mode()) {
        $self->{logger}->file_mode($self->{logger}->{file_name});
    }
    $self->{logger}->redirect_output();
    
    my ($status, $status_cdb, $status_csdb) = centreon::common::misc::reload_db_config($self->{logger}, $self->{config_file},
                                                                                       $self->{dbcentreon}, $self->{dbcentstorage});
    if ($status_cdb == 1) {
        $self->{dbcentreon}->disconnect();
        $self->{dbcentreon}->connect();
    }
    if ($status_csdb == 1) {
        $self->{dbcentstorage}->disconnect();
        $self->{dbcentstorage}->connect();
    }
    centreon::common::misc::check_debug($self->{logger}, "debug_centstorage", $self->{dbcentreon}, "centstorage pool process " . $self->{num_pool});

    $self->{reload} = 1;
}

sub add_data_mysql {
    my $self = shift;
    my ($metric_id, $ctime, $value) = @_;
    
    $self->{group_databin_stmt} .= $self->{group_databin_append} . "('$metric_id', '$ctime', '$value')";
    $self->{group_databin_append} = ", ";
    $self->{group_databin_total}++;
}

sub force_flush_rrd {
    my $self = shift;
    my ($key) = @_;

    if (defined($self->{cache_service}->{$key})) {
        foreach (keys %{$self->{cache_service}->{$key}->{metrics}}) {
            $self->{rrd}->flush_metric($self->{cache_service}->{$key}->{metrics}->{$_}->{metric_id});
        }

        $self->{rrd}->flush_status($self->{cache_service}->{$key}->{index_id});
    }
}

sub flush_mysql {
    my $self = shift;
    my ($force) = @_;
    
    return 0 if ($self->{rebuild_progress} == 1 && (!defined($force) || $force == 0));
    if ((defined($force) && $force == 1 && $self->{group_databin_total} > 0) || $self->{group_databin_total} > $self->{group_databin_max}) {
        my $rq = "INSERT INTO `data_bin` (`id_metric`, `ctime`, `value`) VALUES " . $self->{group_databin_stmt};
        my ($status, $stmt) = $self->{dbcentstorage}->query($rq);
        $self->{group_databin_total} = 0;
        $self->{group_databin_append} = "";
        $self->{group_databin_stmt} = "";
    }
}

sub flush_failed {
    my $self = shift;

    if (time() > ($self->{last_check_failed} + $self->{check_failed_every})) {
        # Need to reconnect (maybe a gone away. So we try)
        $self->{dbcentreon}->disconnect();
        $self->{dbcentreon}->connect();

        $self->{logger}->writeLogInfo("Begin Cache Services Failed");
        foreach my $id (keys %{$self->{cache_services_failed}}) {
            next if ($self->{rebuild_progress} == 1 && $id == $self->{rebuild_key});
            my @tmp_ar = ();
            my $lerror = 0;            
            foreach (@{$self->{cache_services_failed}->{$id}}) {
                if ($lerror == 0) {
                    if ($self->update(1, @$_) != 0) {
                        push @tmp_ar, \@$_;
                        $lerror = 1;
                    }
                } else {
                    push @tmp_ar, \@$_;
                }
            }
            if (scalar(@tmp_ar) != 0) {
                @{$self->{cache_services_failed}->{$id}} = @tmp_ar;
            } else {
                delete $self->{cache_services_failed}->{$id};
            }
        }
        $self->{last_check_failed} = time();
    }
}

sub remove_special_char_metric {
    my $self = shift;
    my $remove_special_char = $_[0];

    $remove_special_char =~ s/\./\-/g;
    $remove_special_char =~ s/\,/\-/g;
    $remove_special_char =~ s/\:/\-/g;
    $remove_special_char =~ s/\ /\-/g;
    return $remove_special_char;
}

sub create_metric {
    my $self = shift;
    my ($index_id, $cache_metrics, $metric_name) = @_;

    # Check if exists already
    my ($status, $stmt) = $self->{dbcentstorage}->query("SELECT * FROM `metrics` WHERE `index_id` = '" . $index_id . "' AND `metric_name` = " . $self->{dbcentstorage}->quote($metric_name) . " LIMIT 1");
    return -1 if ($status == -1);
    my $data = $stmt->fetchrow_hashref();
    # move part for compat with old centstorage name
    if (!defined($data)) {
        ($status, $stmt) = $self->{dbcentstorage}->query("SELECT * FROM `metrics` WHERE `index_id` = '" . $index_id . "' AND `metric_name` = " . $self->{dbcentstorage}->quote($self->remove_special_char_metric($metric_name)) . " LIMIT 1");
        return -1 if ($status == -1);
        $data = $stmt->fetchrow_hashref();
        if (defined($data)) {
            ($status) = $self->{dbcentstorage}->query("UPDATE `metrics` SET `metric_name` = " . $self->{dbcentstorage}->quote($metric_name) . " WHERE `index_id` = '" . $index_id . "' AND `metric_name` = " . $self->{dbcentstorage}->quote($self->remove_special_char_metric($metric_name)) . " LIMIT 1");
            return -1 if ($status == -1);
        } else {
            # Insert
            ($status, $stmt) = $self->{dbcentstorage}->query("INSERT INTO `metrics` (`index_id`, `metric_name`, `unit_name`, `warn`, `warn_low`, `warn_threshold_mode`, `crit`, `crit_low`, `crit_threshold_mode`, `min`, `max`, `data_source_type`) VALUES ('" . $index_id . "', " . $self->{dbcentstorage}->quote($metric_name) . ", '" . $self->{metric_unit} . "', " . 
                                    $self->{dbcentstorage}->quote($self->{metric_warn}) . ", " . $self->{dbcentstorage}->quote($self->{warn_low}) . ", " . $self->{dbcentstorage}->quote($self->{warn_threshold_mode}) . ", " . 
                                    $self->{dbcentstorage}->quote($self->{metric_crit}) . ", " . $self->{dbcentstorage}->quote($self->{crit_low}) . ", " . $self->{dbcentstorage}->quote($self->{crit_threshold_mode}) . ", '" .
                                    $self->{metric_min} . "', '" . $self->{metric_max} . "', '" . $self->{metric_type} . "')");    
            return -1 if ($status);
            my $last_insert_id = $self->{dbcentstorage}->last_insert_id();
            $$cache_metrics->{$metric_name} = {metric_id => $last_insert_id, metric_unit => $self->{metric_unit}, 
                                               metric_warn => $self->{metric_warn}, warn_low => $self->{warn_low}, warn_threshold_mode => $self->{warn_threshold_mode}, 
                                               metric_crit => $self->{metric_crit}, crit_low => $self->{crit_low}, crit_threshold_mode => $self->{crit_threshold_mode},
                                               metric_min => $self->{metric_min}, metric_max => $self->{metric_max}, data_source_type => $self->{metric_type}};
            
            return 0;
        }
    }
    # We get
    $$cache_metrics->{$metric_name} = {metric_id => $data->{metric_id}, metric_unit => defined($data->{unit_name}) ? $data->{unit_name} : "", 
                                       metric_warn => defined($data->{warn}) ? $data->{warn} : "", 
                                       warn_low => defined($data->{warn_low}) ? $data->{warn_low} : "", 
                                       warn_threshold_mode => defined($data->{warn_threshold_mode}) ? $data->{warn_threshold_mode} : "",
                                       metric_crit => defined($data->{crit}) ? $data->{crit} : "", 
                                       crit_low => defined($data->{crit_low}) ? $data->{crit_low} : "", 
                                       crit_threshold_mode => defined($data->{crit_threshold_mode}) ? $data->{crit_threshold_mode} : "",
                                       metric_min => defined($data->{min}) ? $data->{min} : "", 
                                       metric_max => defined($data->{max}) ? $data->{max} : "",
                                       data_source_type => $data->{data_source_type}};
    return 0;
}

sub check_update_extra_metric {
    my $self = shift;
    my $cache_metric = $_[0];

    if ($$cache_metric->{metric_unit} ne $self->{metric_unit} ||
        $$cache_metric->{metric_warn} ne $self->{metric_warn} ||
        $$cache_metric->{warn_low} ne $self->{warn_low} ||
        $$cache_metric->{warn_threshold_mode} ne $self->{warn_threshold_mode} ||
        $$cache_metric->{metric_crit} ne $self->{metric_crit} ||
        $$cache_metric->{crit_low} ne $self->{crit_low} ||
        $$cache_metric->{crit_threshold_mode} ne $self->{crit_threshold_mode} ||
        $$cache_metric->{metric_min} ne $self->{metric_min} ||
        $$cache_metric->{metric_max} ne $self->{metric_max}) {
        $self->{dbcentstorage}->query("UPDATE `metrics` SET `unit_name` = " . $self->{dbcentstorage}->quote($self->{metric_unit}) . ", " . 
                                            "`warn` = " . $self->{dbcentstorage}->quote($self->{metric_warn}) . ", `warn_low` = " . $self->{dbcentstorage}->quote($self->{warn_low}) . ", `warn_threshold_mode` = " . $self->{dbcentstorage}->quote($self->{warn_threshold_mode}) . ", " .
                                            "`crit` = " . $self->{dbcentstorage}->quote($self->{metric_crit}) . ", `crit_low` = " . $self->{dbcentstorage}->quote($self->{crit_low}) . ", `crit_threshold_mode` = " . $self->{dbcentstorage}->quote($self->{crit_threshold_mode}) . ", " .
                                            "`min` = '" . $self->{metric_min} . "', `max` = '" . $self->{metric_max} . "' WHERE `metric_id` = " . $$cache_metric->{metric_id});
        $$cache_metric->{metric_unit} = $self->{metric_unit};
        $$cache_metric->{metric_warn} = $self->{metric_warn};
        $$cache_metric->{warn_low} = $self->{warn_low};
        $$cache_metric->{warn_threshold_mode} = $self->{warn_threshold_mode};
        $$cache_metric->{metric_crit} = $self->{metric_crit};
        $$cache_metric->{crit_low} = $self->{crit_low};
        $$cache_metric->{crit_threshold_mode} = $self->{crit_threshold_mode};
        $$cache_metric->{metric_min} = $self->{metric_min};
        $$cache_metric->{metric_max} = $self->{metric_max};
    }
}

sub send_rename_command {
    my $self = shift;
    my ($old_host_name, $old_service_description, $new_host_name, $new_service_description) = @_;
    
    $self->{logger}->writeLogInfo("Hostname/Servicename changed had been detected " . $old_host_name . "/" . $old_service_description);
    my $fh = $self->{pipe_write};
    print $fh "RENAMECLEAN\t" . $old_host_name . "\t" . $old_service_description . "\t" . $new_host_name . "\t" . $new_service_description . "\n";
}

sub create_service {
    my $self = shift;
    my ($host_id, $service_id, $interval, $host_name, $service_description) = @_;
    my ($status, $stmt);

    if ($host_name =~ /_Module_([a-zA-Z0-9]*)/) {
        ($status, $stmt) = $self->{dbcentstorage}->query("SELECT `id`, `storage_type`, `host_name`, `service_description`, `rrd_retention` FROM `index_data` WHERE `host_name` = " . $self->{dbcentstorage}->quote($host_name) . " AND `service_description` = " . $self->{dbcentstorage}->quote($service_description)  . " LIMIT 1");
    } else {
        ($status, $stmt) = $self->{dbcentstorage}->query("SELECT `id`, `storage_type`, `host_name`, `service_description`, `rrd_retention` FROM `index_data` WHERE `host_id` = " . $host_id . " AND `service_id` = " . $service_id . " LIMIT 1");
    }    
    return -1 if ($status == -1);
    my $data = $stmt->fetchrow_hashref();
    if (defined($data) && ($data->{host_name} ne $host_name || $data->{service_description} ne $service_description)) {
        ($status, $stmt) = $self->{dbcentstorage}->query("UPDATE `index_data` SET `host_name` = " . $self->{dbcentstorage}->quote($host_name) . ", `service_description` = " . $self->{dbcentreon}->quote($service_description) . " WHERE id = " . $data->{'id'});
        if ($status != -1) {
            $self->{cache_service}->{$host_name . ";" . $service_description} = {};
            $self->{cache_service}->{$host_name . ";" . $service_description}->{metrics} = {};
            $self->{cache_service}->{$host_name . ";" . $service_description}->{service_id} = $service_id;
            $self->{cache_service}->{$host_name . ";" . $service_description}->{host_id} = $host_id;
            $self->{cache_service}->{$host_name . ";" . $service_description}->{storage_type} = int($data->{storage_type});
            $self->{cache_service}->{$host_name . ";" . $service_description}->{index_id} = $data->{id};
            $self->{cache_service}->{$host_name . ";" . $service_description}->{check_interval} = $interval;
            $self->{cache_service}->{$host_name . ";" . $service_description}->{rebuild} = 0;
            $self->{cache_service}->{$host_name . ";" . $service_description}->{rrd_retention} = (defined($data->{rrd_retention})) ? $data->{rrd_retention} : -1;
            # Send command to clean cache
            $self->send_rename_command($data->{host_name}, $data->{service_description}, $host_name, $service_description);
            return -2;
        }
    } elsif (defined($data) && ($data->{host_name} eq $host_name || $data->{service_description} eq $service_description)) {
        # same name but exist already
        $self->{cache_service}->{$host_name . ";" . $service_description} = {};
        $self->{cache_service}->{$host_name . ";" . $service_description}->{metrics} = {};
        $self->{cache_service}->{$host_name . ";" . $service_description}->{service_id} = $service_id;
        $self->{cache_service}->{$host_name . ";" . $service_description}->{host_id} = $host_id;
        $self->{cache_service}->{$host_name . ";" . $service_description}->{storage_type} = int($data->{storage_type});
        $self->{cache_service}->{$host_name . ";" . $service_description}->{index_id} = $data->{id};
        $self->{cache_service}->{$host_name . ";" . $service_description}->{check_interval} = $interval;
        $self->{cache_service}->{$host_name . ";" . $service_description}->{rebuild} = 0;
        $self->{cache_service}->{$host_name . ";" . $service_description}->{rrd_retention} = (defined($data->{rrd_retention})) ? $data->{rrd_retention} : -1;
    } else {
        # create
        if ($host_name =~ /_Module_([a-zA-Z0-9]*)/) {
            ($status, $stmt) = $self->{dbcentstorage}->query("INSERT INTO `index_data` (`host_name`, `service_description`, `host_id`, `service_id`, `special`, `storage_type`) VALUES (" . $self->{dbcentstorage}->quote($host_name) . ", " . $self->{dbcentstorage}->quote($service_description) . ", " . $host_id . ", " . $service_id . ", '1', '" . $self->{storage_type} . "')");
        } else {
            ($status, $stmt) = $self->{dbcentstorage}->query("INSERT INTO `index_data` (`host_name`, `service_description`, `host_id`, `service_id`, `storage_type`) VALUES (" . $self->{dbcentstorage}->quote($host_name) . ", " . $self->{dbcentstorage}->quote($service_description) . ", " . $host_id . ", " . $service_id . ", '" . $self->{storage_type} . "')");
        }
        if ($status != -1) {
            $self->{cache_service}->{$host_name . ";" . $service_description} = {};
            $self->{cache_service}->{$host_name . ";" . $service_description}->{metrics} = {};
            $self->{cache_service}->{$host_name . ";" . $service_description}->{service_id} = $service_id;
            $self->{cache_service}->{$host_name . ";" . $service_description}->{host_id} = $host_id;
            $self->{cache_service}->{$host_name . ";" . $service_description}->{storage_type} = $self->{storage_type};
            $self->{cache_service}->{$host_name . ";" . $service_description}->{index_id} = $self->{dbcentstorage}->last_insert_id();
            $self->{cache_service}->{$host_name . ";" . $service_description}->{check_interval} = $interval;
            $self->{cache_service}->{$host_name . ";" . $service_description}->{rebuild} = 0;
            $self->{cache_service}->{$host_name . ";" . $service_description}->{rrd_retention} = -1;
        }
    }

    return $status;
}

sub get_centstorage_information {
    my $self = shift;
    my ($len_storage_rrd, $rrd_metrics_path, $rrd_status_path, $storage_type);

    my ($status, $stmt) = $self->{dbcentstorage}->query("SELECT len_storage_rrd, RRDdatabase_path, RRDdatabase_status_path, storage_type FROM config");
    my $data = $stmt->fetchrow_hashref();
    if (defined($data)) {
        $len_storage_rrd = int($data->{len_storage_rrd});
        $rrd_metrics_path = $data->{RRDdatabase_path};
        $rrd_status_path = $data->{RRDdatabase_status_path};
        $storage_type = int($data->{storage_type});
    }
    return ($status, $len_storage_rrd, $rrd_metrics_path, $rrd_status_path, $storage_type);
}

sub get_centreon_intervaltime {
    my $self = shift;
    my $interval = 60;

    my ($status, $stmt) = $self->{dbcentreon}->query("SELECT `value` AS interval_length FROM options WHERE `key` = 'interval_length'");
    my $data = $stmt->fetchrow_hashref();
    if (defined($data)) {
        $interval = $data->{interval_length};
    }
    return (0, $interval);
}

#############################
### Perfdata Handle
#############################

sub trim {
    my $self = shift;
    $_[0] =~ s/^[ \t]*?//;
    $_[0] =~ s/[ \t]*?$//;
    return $_[0];
}

sub skip_chars {
    my $self = shift;
    $self->{perfdata_pos}++ while ($self->{perfdata_pos} < $self->{perfdata_size} && (${$self->{perfdata_chars}}[$self->{perfdata_pos}] =~ /$_[0]/));
}

sub continue_to {
    my $self = shift;
    my ($forbidden, $stop1, $not_stop_after) = @_;
    my $value = "";

    while ($self->{perfdata_pos} < $self->{perfdata_size}) {
        if (defined($forbidden) && ${$self->{perfdata_chars}}[$self->{perfdata_pos}] =~ /$forbidden/) {
                return undef;
        }
        if (${$self->{perfdata_chars}}[$self->{perfdata_pos}] =~ /$stop1/) {
            if (!defined($not_stop_after)) {
                return $value;
            }
            if (!($self->{perfdata_pos} + 1 < $self->{perfdata_size} && ${$self->{perfdata_chars}}[$self->{perfdata_pos} + 1] =~ /$not_stop_after/)) {
                $self->{perfdata_pos}++;
                return $value;
            }
            $self->{perfdata_pos}++;
        }

        $value .= ${$self->{perfdata_chars}}[$self->{perfdata_pos}];
        $self->{perfdata_pos}++;
    }

    return $value;
}

sub parse_label {
    my $self = shift;
    my $label;

    if (defined(${$self->{perfdata_chars}}[$self->{perfdata_pos}]) && ${$self->{perfdata_chars}}[$self->{perfdata_pos}] eq "'") {
        $self->{perfdata_pos}++;
        $label = $self->continue_to(undef, "'", "'");
    } else {
        $label = $self->continue_to("[ \t]", "=");
    }
    $self->{perfdata_pos}++;

    return $label;
}

sub parse_value {
    my $self = shift;
    my $with_unit = shift;
    my $unit = '';
    my $value = "";
    my $neg = 1;

    if (defined(${$self->{perfdata_chars}}[$self->{perfdata_pos}]) && ${$self->{perfdata_chars}}[$self->{perfdata_pos}] eq "-") {
        $neg = -1;
        $self->{perfdata_pos}++;
    }

    $value = $self->continue_to(undef, "[^0-9\.,]");
    if (defined($value) && $value ne "") {
        $value =~ s/,/./g;
        $value = $value * $neg;
    }
    if (defined($with_unit) && $with_unit == 1) {
        $unit = $self->parse_unit();
    } else {
        $self->skip_chars(";");
    }
    return ($value, $unit);
}

sub parse_unit {
    my $self = shift;
    my $value = "";

    $value = $self->continue_to(undef, "[ \t;]");
    $self->skip_chars(";");
    return $value;
}

sub parse_threshold {
    my $self = shift;
    my $neg = 1;
    my $value_tmp = "";

    my $arobase = 0;
    my $infinite_neg = 0;
    my $infinite_pos = 0;
    my $value_start = "";
    my $value_end = "";
    my $global_status = 1;
    
    if (defined(${$self->{perfdata_chars}}[$self->{perfdata_pos}]) && ${$self->{perfdata_chars}}[$self->{perfdata_pos}] eq "@") {
        $arobase = 1;
        $self->{perfdata_pos}++;
    }

    if (defined(${$self->{perfdata_chars}}[$self->{perfdata_pos}]) && ${$self->{perfdata_chars}}[$self->{perfdata_pos}] eq "~") {
        $infinite_neg = 1;
        $self->{perfdata_pos}++;
    } else {
        if (defined(${$self->{perfdata_chars}}[$self->{perfdata_pos}]) && ${$self->{perfdata_chars}}[$self->{perfdata_pos}] eq "-") {
            $neg = -1;
            $self->{perfdata_pos}++;
        }
        $value_tmp = $self->continue_to(undef, "[^0-9\.,]");
        if (defined($value_tmp) && $value_tmp ne "") {
            $value_tmp =~ s/,/./g;
            $value_tmp = $value_tmp * $neg;
        }
        $neg = 1;
    }

    if (defined(${$self->{perfdata_chars}}[$self->{perfdata_pos}]) && ${$self->{perfdata_chars}}[$self->{perfdata_pos}] eq ":") {
        if ($value_tmp ne "") {
                $value_start = $value_tmp;
        } else {
                $value_start = 0;
        }
        $self->{perfdata_pos}++;

        if (defined(${$self->{perfdata_chars}}[$self->{perfdata_pos}]) && ${$self->{perfdata_chars}}[$self->{perfdata_pos}] eq "-") {
            $neg = -1;
            $self->{perfdata_pos}++;
        }
        $value_end = $self->continue_to(undef, "[^0-9\.,]");
        if (defined($value_tmp) && $value_end ne "") {
            $value_end =~ s/,/./g;
            $value_end = $value_end * $neg;
        } else {
            $infinite_pos = 1;
        }
    } else {
        $value_start = 0;
        $value_end = $value_tmp;
    }
    
    my $value = $self->continue_to(undef, "[ \t;]");
    $self->skip_chars(";");
    if ($value ne '') {
        $self->{logger}->writeLogInfo("Wrong threshold...");
        $global_status = 0;
    }

    if ($infinite_neg == 1) {
        $value_start = '-1e500';
    }
    if ($infinite_pos == 1) {
        $value_end = '1e500';
    }

    return ($global_status, $value_start, $value_end, $arobase);
}

sub get_perfdata {
    my $self = shift;
    my ($counter_type, $perf_label, $perf_value, $perf_unit, $perf_warn, $perf_crit, $perf_min, $perf_max);

    if (!defined($self->{service_perfdata}) || $self->{perfdata_pos} >= $self->{perfdata_size}) {
        return 0;
    }

    $self->skip_chars("[ \t]");
    $perf_label = $self->parse_label();
    if (!defined($perf_label) || $perf_label eq '') {
        $self->{logger}->writeLogError("Wrong perfdata format: " . $self->{service_perfdata});
        return -1 if ($self->{perfdata_parser_stop} == 1);
        return 1;
    }

    ($perf_value, $perf_unit) = $self->parse_value(1);
    if (!defined($perf_value) || $perf_value eq '') {
        $self->{logger}->writeLogError("Wrong perfdata format: " . $self->{service_perfdata});
        return -1 if ($self->{perfdata_parser_stop} == 1);
        return 1;
    }

    my ($status_th_warn, $th_warn_start, $th_warn_end, $th_warn_inclusive) = $self->parse_threshold();
    my ($status_th_crit, $th_crit_start, $th_crit_end, $th_crit_inclusive) = $self->parse_threshold();
    ($perf_min) = $self->parse_value();
    ($perf_max) = $self->parse_value();

    $perf_label = $self->trim($perf_label);
    $counter_type = 'g';
    if ($perf_label =~ /^([adc])\[(.*?)\]$/) {
{'fraction_non_alphanumeric': 0.20491751369494315, 'fraction_numerical': 0.0049080143124030275, 'mean_word_length': 2.716345022358202, 'pattern_counts': {'":': 2, '<': 19, '<?xml version=': 0, '>': 1233, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 56, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7657083', 'n_tokens_mistral': 21938, 'n_tokens_neox': 21371, 'n_words': 3649}