diff --git a/cloud/aws/cloudwatch/mode/discovery.pm b/cloud/aws/cloudwatch/mode/discovery.pm new file mode 100644 index 000000000..ba0eb03f7 --- /dev/null +++ b/cloud/aws/cloudwatch/mode/discovery.pm @@ -0,0 +1,243 @@ +# +# Copyright 2019 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 cloud::aws::cloudwatch::mode::discovery; + +use base qw(centreon::plugins::mode); + +use strict; +use warnings; +use JSON::XS; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => { + "region:s" => { name => 'region' }, + "service:s@" => { name => 'service' }, + "prettify" => { name => 'prettify' }, + }); + + $self->{services} = { + VPC => $self->can('discover_vpc'), + EC2 => $self->can('discover_ec2'), + RDS => $self->can('discover_rds'), + ELB => $self->can('discover_elb'), + VPN => $self->can('discover_vpn'), + }; + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); + + if (!defined($self->{option_results}->{region}) || $self->{option_results}->{region} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --region option."); + $self->{output}->option_exit(); + } + + if (!defined($self->{option_results}->{service}) || $self->{option_results}->{service} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --service option."); + $self->{output}->option_exit(); + } +} + +sub discover_vpc { + my (%options) = @_; + + my @disco_data; + + my $vpcs = $options{custom}->discovery(region => $options{region}, + service => 'ec2', command => 'describe-vpcs'); + foreach my $vpc (@{$vpcs->{Vpcs}}) { + next if (!defined($vpc->{VpcId})); + my %vpc; + $vpc{type} = "vpc"; + $vpc{id} = $vpc->{VpcId}; + $vpc{state} = $vpc->{State}; + $vpc{cidr} = $vpc->{CidrBlock}; + foreach my $tag (@{$vpc->{Tags}}) { + if ($tag->{Key} eq "Name" && defined($tag->{Value})) { + $vpc{name} = $tag->{Value}; + } + push @{$vpc{tags}}, { key => $tag->{Key}, value => $tag->{Value} }; + } + push @disco_data, \%vpc; + } + return @disco_data; +} + +sub discover_ec2 { + my (%options) = @_; + + my @disco_data; + + my $instances = $options{custom}->discovery(region => $options{region}, + service => 'ec2', command => 'describe-instances'); + foreach my $reservation (@{$instances->{Reservations}}) { + foreach my $instance (@{$reservation->{Instances}}) { + next if (!defined($instance->{InstanceId})); + my %ec2; + $ec2{type} = "ec2"; + $ec2{id} = $instance->{InstanceId}; + $ec2{state} = $instance->{State}->{Name}; + $ec2{key_name} = $instance->{KeyName}; + $ec2{private_ip} = $instance->{PrivateIpAddress}; + $ec2{private_dns_name} = $instance->{PrivateDnsName}; + $ec2{public_dns_name} = $instance->{PublicDnsName}; + $ec2{instance_type} = $instance->{InstanceType}; + $ec2{vpc_id} = $instance->{VpcId}; + foreach my $tag (@{$instance->{Tags}}) { + if ($tag->{Key} eq "aws:autoscaling:groupName" && defined($tag->{Value})) { + $ec2{asg} = $tag->{Value}; + } + if ($tag->{Key} eq "Name" && defined($tag->{Value})) { + $ec2{name} = $tag->{Value}; + } + push @{$ec2{tags}}, { key => $tag->{Key}, value => $tag->{Value} }; + } + push @disco_data, \%ec2; + } + } + return @disco_data; +} + +sub discover_rds { + my (%options) = @_; + + my @disco_data; + + my $db_instances = $options{custom}->discovery(region => $options{region}, + service => 'rds', command => 'describe-db-instances'); + foreach my $db_instance (@{$db_instances->{DBInstances}}) { + next if (!defined($db_instance->{DbiResourceId})); + my %rds; + $rds{type} = "rds"; + $rds{id} = $db_instance->{DbiResourceId}; + $rds{name} = $db_instance->{DBInstanceIdentifier}; + $rds{status} = $db_instance->{DBInstanceStatus}; + $rds{storage_type} = $db_instance->{StorageType}; + $rds{instance_class} = $db_instance->{DBInstanceClass}; + $rds{availability_zone} = $db_instance->{AvailabilityZone}; + $rds{vpc_id} = $db_instance->{DBSubnetGroup}->{VpcId}; + push @disco_data, \%rds; + } + return @disco_data; +} + +sub discover_elb { + my (%options) = @_; + + my @disco_data; + + my $load_balancers = $options{custom}->discovery(region => $options{region}, + service => 'elb', command => 'describe-load-balancers'); + foreach my $load_balancer (@{$load_balancers->{LoadBalancerDescriptions}}) { + next if (!defined($load_balancer->{LoadBalancerName})); + my %elb; + $elb{type} = "elb"; + $elb{name} = $load_balancer->{LoadBalancerName}; + $elb{dns_name} = $load_balancer->{DNSName}; + $elb{availability_zones} = $load_balancer->{AvailabilityZones}; + $elb{vpc_id} = $load_balancer->{VPCId}; + push @disco_data, \%elb; + } + return @disco_data; +} + +sub discover_vpn { + my (%options) = @_; + + my @disco_data; + + my $vpns = $options{custom}->discovery(region => $options{region}, + service => 'ec2', command => 'describe-vpn-connections'); + foreach my $connection (@{$vpns->{VpnConnections}}) { + next if (!defined($connection->{VpnConnectionId})); + my %vpn; + $vpn{type} = "vpn"; + $vpn{id} = $connection->{VpnConnectionId}; + $vpn{connection_type} = $connection->{Type}; + $vpn{state} = $connection->{State}; + $vpn{category} = $connection->{Category}; + foreach my $tag (@{$connection->{Tags}}) { + if ($tag->{Key} eq "Name" && defined($tag->{Value})) { + $vpn{name} = $tag->{Value}; + } + push @{$vpn{tags}}, { key => $tag->{Key}, value => $tag->{Value} }; + } + push @disco_data, \%vpn; + } + return @disco_data; +} + +sub run { + my ($self, %options) = @_; + + my @disco_data; + my $disco_stats; + + $disco_stats->{start_time} = time(); + + foreach my $service (@{$self->{option_results}->{service}}) { + push @disco_data, $self->{services}->{uc($service)}->(custom => $options{custom}, + region => $self->{option_results}->{region}) if (defined($self->{services}->{uc($service)})); + } + + $disco_stats->{end_time} = time(); + $disco_stats->{duration} = $disco_stats->{end_time} - $disco_stats->{start_time}; + $disco_stats->{discovered_items} = @disco_data; + $disco_stats->{results} = \@disco_data; + + my $encoded_data; + eval { + if (defined($self->{option_results}->{prettify})) { + $encoded_data = JSON::XS->new->utf8->pretty->encode($disco_stats); + } else { + $encoded_data = JSON::XS->new->utf8->encode($disco_stats); + } + }; + if ($@) { + $encoded_data = '{"code":"encode_error","message":"Cannot encode discovered data into JSON format"}'; + } + + $self->{output}->output_add(short_msg => $encoded_data); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1); + $self->{output}->exit(); +} + +1; + +__END__ + +=head1 MODE + +Resources discovery. + +=over 8 + +=back + +=cut diff --git a/cloud/aws/cloudwatch/plugin.pm b/cloud/aws/cloudwatch/plugin.pm index 630e8141d..1efbb1b21 100644 --- a/cloud/aws/cloudwatch/plugin.pm +++ b/cloud/aws/cloudwatch/plugin.pm @@ -31,9 +31,10 @@ sub new { $self->{version} = '0.1'; %{ $self->{modes} } = ( - 'get-alarms' => 'cloud::aws::cloudwatch::mode::getalarms', - 'get-metrics' => 'cloud::aws::cloudwatch::mode::getmetrics', - 'list-metrics' => 'cloud::aws::cloudwatch::mode::listmetrics', + 'discovery' => 'cloud::aws::cloudwatch::mode::discovery', + 'get-alarms' => 'cloud::aws::cloudwatch::mode::getalarms', + 'get-metrics' => 'cloud::aws::cloudwatch::mode::getmetrics', + 'list-metrics' => 'cloud::aws::cloudwatch::mode::listmetrics', ); $self->{custom_modes}{paws} = 'cloud::aws::custom::paws'; diff --git a/cloud/aws/custom/awscli.pm b/cloud/aws/custom/awscli.pm index 1d5ec9f50..f69e69edb 100644 --- a/cloud/aws/custom/awscli.pm +++ b/cloud/aws/custom/awscli.pm @@ -194,6 +194,39 @@ sub cloudwatch_get_metrics { return $metric_results; } +sub discovery_set_cmd { + my ($self, %options) = @_; + + return if (defined($self->{option_results}->{command_options}) && $self->{option_results}->{command_options} ne ''); + my $cmd_options = $options{service} . " " . $options{command} . " --region $options{region} --output json"; + $cmd_options .= " --endpoint-url $self->{endpoint_url}" if (defined($self->{endpoint_url}) && $self->{endpoint_url} ne ''); + return $cmd_options; +} + +sub discovery { + my ($self, %options) = @_; + + my $cmd_options = $self->discovery_set_cmd(%options); + my ($response) = centreon::plugins::misc::execute( + output => $self->{output}, + options => $self->{option_results}, + sudo => $self->{option_results}->{sudo}, + command => $self->{option_results}->{command}, + command_path => $self->{option_results}->{command_path}, + command_options => $cmd_options + ); + my $result; + eval { + $result = JSON::XS->new->utf8->decode($response); + }; + if ($@) { + $self->{output}->add_option_msg(short_msg => "Cannot decode json response: $@"); + $self->{output}->option_exit(); + } + + return $result; +} + sub cloudwatch_get_alarms_set_cmd { my ($self, %options) = @_;