#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

package apps::vmware::connector::mode::snapshotvm;

use base qw(centreon::plugins::mode);

use strict;
use warnings;
use Date::Parse;

sub new {
    my ($class, %options) = @_;
    my $self = $class->SUPER::new(package => __PACKAGE__, %options);
    bless $self, $class;
    
    $options{options}->add_options(arguments => {
        'vm-hostname:s'         => { name => 'vm_hostname' },
        'filter'                => { name => 'filter' },
        'scope-datacenter:s'    => { name => 'scope_datacenter' },
        'scope-cluster:s'       => { name => 'scope_cluster' },
        'scope-host:s'          => { name => 'scope_host' },
        'filter-description:s'  => { name => 'filter_description' },
        'filter-os:s'           => { name => 'filter_os' },
        'filter-uuid:s'         => { name => 'filter_uuid' },
        'display-description'   => { name => 'display_description' },
        'check-consolidation'   => { name => 'check_consolidation' },
        'nopoweredon-skip'      => { name => 'nopoweredon_skip' },
        'empty-continue'        => { name => 'empty_continue' },
        'warning:s'             => { name => 'warning' },
        'critical:s'            => { name => 'critical' },
        'disconnect-status:s'   => { name => 'disconnect_status', default => 'unknown' },
        'unit:s'                => { name => 'unit', default => 's' }
    });
    
    return $self;
}

my $unitdiv = { s => ['seconds', 1], w => ['weeks', 604800], d => ['days', 86400], h => ['hours', 3600], m => ['minutes', 60] };

sub check_options {
    my ($self, %options) = @_;
    $self->SUPER::init(%options);
    
    foreach my $label (('warning', 'critical')) {
        if (($self->{perfdata}->threshold_validate(label => $label, value => $self->{option_results}->{$label})) == 0) {
            my ($label_opt) = $label;
            $label_opt =~ tr/_/-/;
            $self->{output}->add_option_msg(short_msg => "Wrong " . $label_opt . " threshold '" . $self->{option_results}->{$label} . "'.");
            $self->{output}->option_exit();
        }
    }

    if ($self->{output}->is_litteral_status(status => $self->{option_results}->{disconnect_status}) == 0) {
        $self->{output}->add_option_msg(short_msg => "Wrong disconnect-status option '" . $self->{option_results}->{disconnect_status} . "'.");
        $self->{output}->option_exit();
    }

    if ($self->{option_results}->{unit} eq '' || !defined($unitdiv->{$self->{option_results}->{unit}})) {
        $self->{option_results}->{unit} = 's';
    }
}

sub run {
    my ($self, %options) = @_;

    my $response = $options{custom}->execute(
        params => $self->{option_results},
        command => 'snapshotvm'
    );

    my $multiple = 0;
    my %vm_consolidate = ();
    my %vm_errors = (warning => {}, critical => {}); 
    if (scalar(keys %{$response->{data}}) > 1) {
        $multiple = 1;
    }
    if ($multiple == 1) {
        $self->{output}->output_add(
            severity => 'OK',
            short_msg => sprintf("All snapshots are ok")
        );
    } else {
        $self->{output}->output_add(
            severity => 'OK',
            short_msg => sprintf("Snapshot(s) OK")
        );
    }
    foreach my $vm_id (sort keys %{$response->{data}}) {
        my $vm_name = $response->{data}->{$vm_id}->{name};

        if ($options{custom}->entity_is_connected(state => $response->{data}->{$vm_id}->{connection_state}) == 0) {
            my $output = "VM '" . $vm_name . "' not connected. Current Connection State: '$response->{data}->{$vm_id}->{connection_state}'.";
            if ($multiple == 0 ||  
                !$self->{output}->is_status(value => $self->{option_results}->{disconnect_status}, compare => 'ok', litteral => 1)) {
                $self->{output}->output_add(
                    severity => $self->{option_results}->{disconnect_status},
                    short_msg => $output
                );
            }
            next;
        }
    
        next if (defined($self->{option_results}->{nopoweredon_skip}) && 
                 $options{custom}->vm_is_running(power => $response->{data}->{$vm_id}->{power_state}) == 0);

        if (defined($self->{check_consolidation}) && defined($response->{data}->{$vm_id}->{consolidation_needed}) && $response->{data}->{$vm_id}->{consolidation_needed} == 1) {
            $vm_consolidate{$response->{data}->{$vm_id}->{name}} = 1;
        }

        foreach (@{$response->{data}->{$vm_id}->{snapshosts}}) {
            my $create_time = Date::Parse::str2time($_->{create_time});
            if (!defined($create_time)) {
                $self->{output}->output_add(
                    severity => 'UNKNOWN',
                    short_msg => "Can't Parse date '" . $_->{create_time} . "' for vm '" . $vm_name . "'"
                );
                next;
            }

            my $diff_time = time() - $create_time;
            my $days = int($diff_time / 60 / 60 / 24);
            my $exit = $self->{perfdata}->threshold_check(value => ($diff_time / $unitdiv->{$self->{option_results}->{unit}}->[1]), threshold => [ { label => 'critical', exit_litteral => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]);

            my $prefix_msg = "'$vm_name'";
            if (defined($self->{display_description}) && defined($response->{data}->{$vm_id}->{'config.annotation'}) &&
                $response->{data}->{$vm_id}->{'config.annotation'} ne '') {
                $prefix_msg .= ' [' . $options{custom}->strip_cr(value => $response->{data}->{$vm_id}->{'config.annotation'}) . ']';
            }
            my $suffix_msg = '';
            if (defined($_->{description}) && $_->{description} ne '') {
                $suffix_msg = ' [' . $options{custom}->strip_cr(value => $_->{description}) . ']';
            }
            if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) {
                $vm_errors{$exit}->{$vm_name} = 1;
                $self->{output}->output_add(long_msg => "$prefix_msg snapshot create time: " . $_->{create_time} . $suffix_msg);
            }
        }
    }

    $self->{output}->perfdata_add(
        label => 'num_warning',
        nlabel => 'vm.snapshots.warning.current.count',
        value => scalar(keys %{$vm_errors{warning}}),
        min => 0
    );
    $self->{output}->perfdata_add(
        label => 'num_critical',
        nlabel => 'vm.snapshots.critical.current.count',
        value => scalar(keys %{$vm_errors{critical}}),
        min => 0
    );
    if (scalar(keys %{$vm_errors{warning}}) > 0) {
        $self->{output}->output_add(
            severity => 'WARNING',
            short_msg => sprintf(
                'Snapshots for VM older than %d %s: [%s]',
                $self->{option_results}->{warning},
                $unitdiv->{$self->{option_results}->{unit}}->[0],
                join('] [', sort keys %{$vm_errors{warning}})
            )
        );
    }
    if (scalar(keys %{$vm_errors{critical}}) > 0) {
        $self->{output}->output_add(
            severity => 'CRITICAL',
            short_msg => sprintf(
                'Snapshots for VM older than %d %s: [%s]',
                $self->{option_results}->{critical}, 
                $unitdiv->{$self->{option_results}->{unit}}->[0],
                join('] [', sort keys %{$vm_errors{critical}})
            )
        );
    }
    if (scalar(keys %vm_consolidate) > 0) {
         $self->{output}->output_add(
            severity => 'CRITICAL',
            short_msg => sprintf(
                'VMs need consolidation: [%s]',
                join('] [', sort keys %vm_consolidate)
            )
        );
    }

    $self->{output}->display();
    $self->{output}->exit();
}

1;

__END__

=head1 MODE

Check virtual machine snapshots.

=over 8

=item B<--vm-hostname>

VM hostname to check.
If not set, we check all VMs.

=item B<--filter>

VM hostname is a regexp.

=item B<--filter-description>

Filter also virtual machines description (can be a regexp).

=item B<--filter-os>

Filter also virtual machines OS name (can be a regexp).

=item B<--scope-datacenter>

Search in following datacenter(s) (can be a regexp).

=item B<--scope-cluster>

Search in following cluster(s) (can be a regexp).

=item B<--scope-host>

Search in following host(s) (can be a regexp).

=item B<--display-description>

Display virtual machine description.

=item B<--check-consolidation>

Check if VM needs consolidation (since vsphere 5.0).

=item B<--disconnect-status>

Status if VM disconnected (default: 'unknown').

=item B<--nopoweredon-skip>

Skip check if VM is not poweredOn.

=item B<--empty-continue>

Ask to the connector that an empty response is ok. 

=item B<--unit>

Select the time unit for thresholds. May be 's' for seconds, 'm' for minutes, 'h' for hours, 'd' for days, 'w' for weeks. Default is seconds.

=item B<--warning>

Warning threshold for snapshot's age.

=item B<--critical>

Critical threshold for snapshot's age.

=back

=cut
