Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
fce29b6
Create plugin.pm
psamecentreon Jun 16, 2026
5616fa3
Create xmlapi.pm
psamecentreon Jun 16, 2026
4a1ba80
Create resources.pm
psamecentreon Jun 16, 2026
56716b6
Create blade.pm
psamecentreon Jun 16, 2026
3473e98
Create chassis.pm
psamecentreon Jun 16, 2026
0d58850
Create cpu.pm
psamecentreon Jun 16, 2026
f38d32b
Create fan.pm
psamecentreon Jun 16, 2026
c3e5127
Create fex.pm
psamecentreon Jun 16, 2026
6ff5d69
Create iocard.pm
psamecentreon Jun 16, 2026
242e5ab
Create localdisk.pm
psamecentreon Jun 16, 2026
35dab80
Create memory.pm
psamecentreon Jun 16, 2026
42fbbf2
Create psu.pm
psamecentreon Jun 16, 2026
6dc990b
Create equipment.pm
psamecentreon Jun 16, 2026
2326887
Create faults.pm
psamecentreon Jun 16, 2026
7152c12
Create auditlogs.pm
psamecentreon Jun 16, 2026
38f89b8
Create mgmtentities.pm
psamecentreon Jun 16, 2026
db219ff
Update faults.pm
psamecentreon Jun 16, 2026
9084abf
Update auditlogs.pm
psamecentreon Jun 16, 2026
79d8f9f
Create serviceprofile.pm
psamecentreon Jun 16, 2026
402a295
Create plugin.pm
psamecentreon Jun 16, 2026
5245462
Create equipment.pm
psamecentreon Jun 16, 2026
9fc060d
Add files via upload
psamecentreon Jun 16, 2026
028ccf2
Create api.pm
psamecentreon Jun 16, 2026
a94f668
Create cpu.pm
psamecentreon Jun 16, 2026
1efc677
Commit components
psamecentreon Jun 16, 2026
6bd7138
Rename src/hardware/server/cisco/ucs/redfish/components/chassis.pm to…
psamecentreon Jun 16, 2026
cc3f30f
Rename src/hardware/server/cisco/ucs/redfish/components/cpu.pm to src…
psamecentreon Jun 16, 2026
43dd3ea
Rename src/hardware/server/cisco/ucs/redfish/components/fan.pm to src…
psamecentreon Jun 16, 2026
274ce8c
Rename src/hardware/server/cisco/ucs/redfish/components/localdisk.pm …
psamecentreon Jun 16, 2026
9a8e70e
Rename src/hardware/server/cisco/ucs/redfish/components/memory.pm to …
psamecentreon Jun 16, 2026
962a6b1
Rename src/hardware/server/cisco/ucs/redfish/components/psu.pm to src…
psamecentreon Jun 16, 2026
a30178d
Rename src/hardware/server/cisco/ucs/redfish/components/resources.pm …
psamecentreon Jun 16, 2026
893791f
Add files via upload
psamecentreon Jun 17, 2026
f49114b
Update psu.pm
psamecentreon Jun 17, 2026
921c9b8
Update resources.pm
psamecentreon Jun 17, 2026
77fab26
Update memory.pm
psamecentreon Jun 17, 2026
58334a3
Update localdisk.pm
psamecentreon Jun 17, 2026
8bc9a7d
Update fan.pm
psamecentreon Jun 17, 2026
2c5b1bd
Update chassis.pm
psamecentreon Jun 17, 2026
e02a436
Update plugin.pm
psamecentreon Jun 17, 2026
e1d94c6
Create firmware.pm
psamecentreon Jun 17, 2026
21a097b
Create networkinterfaces.pm
psamecentreon Jun 17, 2026
b54d3e7
Update equipment.pm
psamecentreon Jun 17, 2026
6c2e3e4
Create temperature.pm
psamecentreon Jun 17, 2026
e565842
Update plugin.pm
psamecentreon Jun 17, 2026
0c726cb
Create fabricinterconnect.pm
psamecentreon Jun 17, 2026
ed28508
Create firmware.pm
psamecentreon Jun 17, 2026
9679da5
Create ports.pm
psamecentreon Jun 17, 2026
e756c21
Create temperature.pm
psamecentreon Jun 17, 2026
d11bd75
Create vhba.pm
psamecentreon Jun 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
306 changes: 306 additions & 0 deletions src/hardware/server/cisco/ucs/redfish/custom/api.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,306 @@
#
# Copyright 2026 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 hardware::server::cisco::ucs::redfish::custom::api;

use strict;
use warnings;
use centreon::plugins::http;
use centreon::plugins::statefile;
use JSON::XS;
use Digest::MD5 qw(md5_hex);
use MIME::Base64;

sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;

if (!defined($options{output})) {
print "Class Custom: Need to specify 'output' argument.\n";
exit 3;
}
if (!defined($options{options})) {
print "Class Custom: Need to specify 'options' argument.\n";
exit 3;
}

$self->{output} = $options{output};
$self->{options} = $options{options};
$self->{http} = centreon::plugins::http->new(%options);
$self->{cache} = centreon::plugins::statefile->new(%options);
$self->{token} = undef;

$options{options}->add_options(arguments => {
'hostname:s' => { name => 'hostname' },
'port:s' => { name => 'port' },
'proto:s' => { name => 'proto' },
'username:s' => { name => 'username' },
'password:s' => { name => 'password' },
'timeout:s' => { name => 'timeout' },
'api-path:s' => { name => 'api_path', default => '/redfish/v1' },
'ssl-opt:s@' => { name => 'ssl_opt' },
});

return $self;
}

sub set_options {
my ($self, %options) = @_;
$self->{option_results} = $options{option_results};
}

sub set_defaults {}

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

$self->{hostname} = $self->{option_results}->{hostname} // '';
$self->{port} = $self->{option_results}->{port} // 443;
$self->{proto} = $self->{option_results}->{proto} // 'https';
$self->{username} = $self->{option_results}->{username} // '';
$self->{password} = $self->{option_results}->{password} // '';
$self->{timeout} = $self->{option_results}->{timeout} // 30;
$self->{api_path} = $self->{option_results}->{api_path} // '/redfish/v1';

if ($self->{hostname} eq '') {
$self->{output}->add_option_msg(short_msg => 'Need to specify --hostname option.');
$self->{output}->option_exit();
}
if ($self->{username} eq '') {
$self->{output}->add_option_msg(short_msg => 'Need to specify --username option.');
$self->{output}->option_exit();
}

$self->{cache}->check_options(option_results => $self->{option_results});
return 0;
}

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

my $json_body = JSON::XS->new->encode({
UserName => $self->{username},
Password => $self->{password},
});

my $content = $self->{http}->request(
method => 'POST',
hostname => $self->{hostname},
port => $self->{port},
proto => $self->{proto},
url_path => $self->{api_path} . '/SessionService/Sessions',
timeout => $self->{timeout},
header => ['Content-Type: application/json', 'Accept: application/json'],
query_form_post => $json_body,
unknown_status => '',
warning_status => '',
critical_status => '',
);

if (!defined($content) || $content eq '') {
$self->{output}->add_option_msg(short_msg => 'Redfish: no response during authentication.');
$self->{output}->option_exit();
}

# X-Auth-Token is normally returned as a response header
my $token = $self->{http}->get_header(name => 'X-Auth-Token');

# Fallback: some UCS implementations return the token in the JSON body
if (!defined($token) || $token eq '') {
eval {
my $data = JSON::XS->new->decode($content);
$token = $data->{'Token'} // $data->{'X-Auth-Token'};
};
}

if (!defined($token) || $token eq '') {
# Last resort: fall back to HTTP Basic Auth (no session tokens)
$self->{use_basic_auth} = 1;
return undef;
}

$self->{cache}->write(data => {
token => $token,
expires_in => time() + 1740, # 29 min
});

return $token;
}

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

return undef if $self->{use_basic_auth};

my $has_cache = $self->{cache}->read(statefile => 'cisco_ucs_redfish_' . md5_hex($self->{hostname} . $self->{username}));
my $token = $self->{cache}->get(name => 'token');
my $expires_in = $self->{cache}->get(name => 'expires_in');

if ($has_cache == 0 || !defined($token) || $token eq '' || (defined($expires_in) && time() > $expires_in)) {
$token = $self->_authenticate();
}

return $token;
}

# GET a Redfish endpoint, return the decoded JSON as a hashref
sub request {
my ($self, %options) = @_;
# options: endpoint => '/Systems' (relative to api_path)

my $token = $self->_get_token();

my @headers = ('Accept: application/json');
if (defined $token) {
push @headers, "X-Auth-Token: $token";
} else {
my $b64 = encode_base64("$self->{username}:$self->{password}", '');
push @headers, "Authorization: Basic $b64";
}

my $url = $self->{api_path} . $options{endpoint};

my $content = $self->{http}->request(
method => 'GET',
hostname => $self->{hostname},
port => $self->{port},
proto => $self->{proto},
url_path => $url,
timeout => $self->{timeout},
header => \@headers,
unknown_status => '',
warning_status => '',
critical_status => '',
);

my $http_code = $self->{http}->get_code();

# Token expired -> re-authenticate once and retry
if (defined($http_code) && $http_code == 401) {
$self->{cache}->write(data => { token => '', expires_in => 0 });
$token = $self->_authenticate();
@headers = ('Accept: application/json');
if (defined $token) {
push @headers, "X-Auth-Token: $token";
} else {
my $b64 = encode_base64("$self->{username}:$self->{password}", '');
push @headers, "Authorization: Basic $b64";
}

$content = $self->{http}->request(
method => 'GET',
hostname => $self->{hostname},
port => $self->{port},
proto => $self->{proto},
url_path => $url,
timeout => $self->{timeout},
header => \@headers,
unknown_status => '',
warning_status => '',
critical_status => '',
);
}

if (!defined($content) || $content eq '') {
$self->{output}->add_option_msg(short_msg => "Redfish: no response for endpoint '$url'.");
$self->{output}->option_exit();
}

my $data;
eval { $data = JSON::XS->new->decode($content); };
if ($@) {
$self->{output}->add_option_msg(short_msg => "Redfish: cannot parse JSON for '$url': $@");
$self->{output}->option_exit();
}

return $data;
}

# Fetch a collection and return an arrayref of fully resolved member objects
sub get_collection {
my ($self, %options) = @_;
# options: endpoint => '/Systems'

my $collection = $self->request(endpoint => $options{endpoint});
my @members;

for my $member (@{$collection->{'Members'} // []}) {
my $member_url = $member->{'@odata.id'} // '';
next if $member_url eq '';

# Strip api_path prefix if present (request() re-adds it)
$member_url =~ s{^\Q$self->{api_path}\E}{};

my $obj = $self->request(endpoint => $member_url);
push @members, $obj;
}

return \@members;
}

1;

__END__

=head1 NAME

hardware::server::cisco::ucs::redfish::custom::api - Cisco UCS Redfish API custom mode.

=head1 SYNOPSIS

Handles the Redfish session (X-Auth-Token, with HTTP Basic Auth fallback),
GET requests and collection navigation.

=head1 REDFISH API OPTIONS

=over 8

=item B<--hostname>

UCS server / CIMC IP address or FQDN.

=item B<--port>

HTTPS port (default: 443).

=item B<--proto>

Protocol: http or https (default: https).

=item B<--username>

Redfish username.

=item B<--password>

Redfish password.

=item B<--api-path>

Base Redfish path (default: /redfish/v1).

=item B<--timeout>

HTTP request timeout in seconds (default: 30).

=back

=cut
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#
# Copyright 2026 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 hardware::server::cisco::ucs::redfish::mode::components::chassis;

use strict;
use warnings;
use hardware::server::cisco::ucs::redfish::mode::components::resources qw($thresholds_redfish);

sub load {
my ($self) = @_;
# Data is pre-loaded by equipment.pm into $self->{data}->{chassis}
}

sub check {
my ($self) = @_;

$self->{output}->output_add(long_msg => 'Checking chassis');
$self->{components}->{chassis} = { name => 'chassis', total => 0, skip => 0 };
return if $self->check_filter(section => 'chassis');

for my $chassis (@{$self->{data}->{chassis}}) {
my $id = $chassis->{'Id'} // $chassis->{'@odata.id'} // 'unknown';
my $name = $chassis->{'Name'} // $id;
my $health = $chassis->{Status}->{Health} // 'Unknown';
my $state = $chassis->{Status}->{State} // 'Unknown';

next if $self->check_filter(section => 'chassis', instance => $id);
$self->{components}->{chassis}->{total}++;

$self->{output}->output_add(
long_msg => sprintf("Chassis '%s' health is '%s' [state: %s].", $name, $health, $state)
);

my $threshold = $self->get_severity(
section => 'chassis',
threshold => $thresholds_redfish->{health},
value => $health
);
if (!$self->{output}->is_status(value => $threshold, compare => 'ok', litteral => 1)) {
$self->{output}->output_add(
severity => $threshold,
short_msg => sprintf("Chassis '%s' health is '%s'.", $name, $health)
);
}
}
}

1;
Loading
Loading