enh(apps::proxmox::ve::restapi): Add OS and IPs information to autodiscovery module (#5639)

Refs: CTOR-1200

Co-authored-by: Evan-Adam <152897682+Evan-Adam@users.noreply.github.com>
This commit is contained in:
Sylvain Cresto 2025-08-08 09:26:36 +02:00 committed by GitHub
parent af72ad6cb0
commit d4f9625b19
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 788 additions and 15 deletions

View File

@ -26,6 +26,7 @@ use strict;
use warnings; use warnings;
use centreon::plugins::http; use centreon::plugins::http;
use centreon::plugins::statefile; use centreon::plugins::statefile;
use centreon::plugins::misc;
use JSON::XS; use JSON::XS;
use Digest::MD5 qw(md5_hex); use Digest::MD5 qw(md5_hex);
@ -186,20 +187,21 @@ sub request_api {
if (!defined($self->{ticket})) { if (!defined($self->{ticket})) {
$self->{ticket} = $self->get_ticket(statefile => $self->{cache}); $self->{ticket} = $self->get_ticket(statefile => $self->{cache});
} }
$self->settings(); $self->settings();
my $content = $self->{http}->request(%options); my $content = $self->{http}->request(%options);
my $decoded; my $decoded;
eval { eval {
$decoded = JSON::XS->new->utf8->decode($content); $decoded = JSON::XS->new->utf8->decode($content);
}; };
if ($@) { if ($@) {
# When silently_fail is set we ignore errors
return undef if $options{silently_fail};
$self->{output}->add_option_msg(short_msg => "Cannot decode json response: $@"); $self->{output}->add_option_msg(short_msg => "Cannot decode json response: $@");
$self->{output}->option_exit(); $self->{output}->option_exit();
} }
if (!defined($decoded->{data})) { if (!exists($decoded->{data})) {
$self->{output}->add_option_msg(short_msg => "Error while retrieving data (add --debug option for detailed message)"); $self->{output}->add_option_msg(short_msg => "Error while retrieving data (add --debug option for detailed message)");
$self->{output}->option_exit(); $self->{output}->option_exit();
} }
@ -350,6 +352,93 @@ sub cache_storages {
return $storages; return $storages;
} }
sub internal_api_get_network_interfaces {
my ($self, %options) = @_;
# We use the silently_fail option because we don't want to crash the module when QEMU agent is not running
# In this case we simply ignore the IP retrieval
my $vm_network = $self->request_api(method => 'GET',
url_path => '/api2/json/nodes/' . $options{node_id} . '/qemu/' . $options{vm_id} . '/agent/network-get-interfaces',
silently_fail => 1);
return $vm_network;
}
sub internal_api_get_osinfo {
my ($self, %options) = @_;
# We use the silently_fail option because we don't want to crash the module when QEMU agent is not running
# In this case we simply ignore the osinfo retrieval
my $vm_osinfo = $self->request_api(method => 'GET',
url_path => '/api2/json/nodes/' . $options{node_id} . '/qemu/' . $options{vm_id} . '/agent/get-osinfo',
silently_fail => 1);
return $vm_osinfo;
}
sub api_get_osinfo {
my ($self, %options) = @_;
my $vm_osinfo = $self->internal_api_get_osinfo(node_id => $options{Node}, vm_id => $options{Vmid});
# Retrieve the os info HASH from result HASH
return { PrettyName => '', Name => '', Version => '', Machine => '', Kernel => ''}
unless $vm_osinfo && ref $vm_osinfo eq 'HASH' && exists $vm_osinfo->{result};
$vm_osinfo = $vm_osinfo->{result};
# Defined values depend on the guest OS
return { PrettyName => $vm_osinfo->{'pretty-name'} // $vm_osinfo->{name} // '',
Name => $vm_osinfo->{name} // '',
Version => $vm_osinfo->{'version-id'} // $vm_osinfo->{'version'} // '',
Machine => $vm_osinfo->{'machine'} // '',
Kernel => $vm_osinfo->{'kernel-release'} // $vm_osinfo->{'build-id'} // '',
}
}
sub api_get_network_interfaces {
my ($self, %options) = @_;
my $vm_network = $self->internal_api_get_network_interfaces(node_id => $options{Node}, vm_id => $options{Vmid});
# Retrieve the network interfaces ARRAY from result HASH
return unless $vm_network && ref $vm_network eq 'HASH' && exists $vm_network->{'result'} && ref $vm_network->{'result'} eq 'ARRAY';
$vm_network=$vm_network->{'result'};
# We returns only IPv4 addresses
# We also sort IPs to return public IPs first, then local IPs, and loopback addresses last
my (@ips_loop, @ips_local, @ips_public, @ips_interface);
my %hash_ips_by_interface;
foreach my $interface (@$vm_network) {
next unless $interface->{'ip-addresses'} && ref $interface->{'ip-addresses'} eq 'ARRAY';
foreach my $ip (@{$interface->{'ip-addresses'}}) {
next unless $ip->{'ip-address'};
next if $ip->{'ip-address-type'} && $ip->{'ip-address-type'} !~ /ipv4/i;
my $name = $interface->{'name'} // '';
my $ip_loopback = ($ip->{'ip-address'} =~ /^127\./ || $name =~ /^lo$/i) ? 1 : 0;
my $ip_local = $ip_loopback || centreon::plugins::misc::is_local_ip($ip->{'ip-address'});
$hash_ips_by_interface{$name} = { address => $ip->{'ip-address'}, local => $ip_local, loopback => $ip_loopback }
unless $hash_ips_by_interface{$name};
if ($ip_loopback) {
push @ips_loop, $ip->{'ip-address'};
} elsif ($ip_local) {
push @ips_local, $ip->{'ip-address'};
} else {
push @ips_public, $ip->{'ip-address'};
}
}
}
@ips_loop = sort centreon::plugins::misc::sort_ips @ips_loop;
@ips_local = sort centreon::plugins::misc::sort_ips @ips_local;
@ips_public = sort centreon::plugins::misc::sort_ips @ips_public;
@ips_interface = map { { interface => $_, %{$hash_ips_by_interface{$_}} } } sort keys %hash_ips_by_interface;
return [ @ips_public, @ips_local, @ips_loop ], \@ips_interface;
}
sub internal_api_get_vm_stats { sub internal_api_get_vm_stats {
my ($self, %options) = @_; my ($self, %options) = @_;
@ -501,7 +590,7 @@ Proxmox VE Rest API
=head1 REST API OPTIONS =head1 REST API OPTIONS
Proxmox Rest API Proxmox VE (Virtual Environment) Rest API
More Info about Proxmox VE API on https://pve.proxmox.com/wiki/Proxmox_VE_API More Info about Proxmox VE API on https://pve.proxmox.com/wiki/Proxmox_VE_API
@ -523,7 +612,7 @@ Specify https if needed (default: 'https').
Set Proxmox VE Username Set Proxmox VE Username
API user need to have this privileges API user need to have this privileges
'VM.Monitor, VM.Audit, Datastore.Audit, Sys.Audit, Sys.Syslog' C<VM.Monitor>, C<VM.Audit>, C<Datastore.Audit>, C<Sys.Audit>, C<Sys.Syslog>
=item B<--api-password> =item B<--api-password>
@ -531,7 +620,7 @@ Set Proxmox VE Password
=item B<--realm> =item B<--realm>
Set Proxmox VE Realm (pam, pve or custom) (default: 'pam'). Set Proxmox VE Realm (C<pam>, C<pve> or C<custom>) (default: C<pam>).
=item B<--timeout> =item B<--timeout>

View File

@ -65,6 +65,36 @@ sub discovery_vm {
$vm->{state} = $vms->{$vm_id}->{State}; $vm->{state} = $vms->{$vm_id}->{State};
$vm->{node_name} = $vms->{$vm_id}->{Node}; $vm->{node_name} = $vms->{$vm_id}->{Node};
my ($network_ips, $network_interfaces, $osinfo);
# OSInfo and IPs are retrieved only with a QEMU VM
if ($vms->{$vm_id}->{Type} eq 'qemu') {
$osinfo = $options{custom}->api_get_osinfo( Node => $vms->{$vm_id}->{Node}, Vmid => $vms->{$vm_id}->{Vmid});
$vm->{os_info_name} = $osinfo->{Name};
$vm->{os_info_prettyname} = $osinfo->{PrettyName};
$vm->{os_info_version} = $osinfo->{Version};
$vm->{os_info_machine} = $osinfo->{Machine};
$vm->{os_info_kernel} = $osinfo->{Kernel};
($network_ips, $network_interfaces) = $options{custom}->api_get_network_interfaces( Node => $vms->{$vm_id}->{Node}, Vmid => $vms->{$vm_id}->{Vmid});
} else {
$vm->{$_} = '' foreach (qw/os_info_name os_info_prettyname os_info_version os_info_machine os_info_kernel/);
}
# provide the list of IP addresses if available or else provide the VM's name as only address
if ($network_ips && ref $network_ips eq 'ARRAY' && @{$network_ips} > 0) {
$vm->{ip_addresses} = $network_ips;
} else {
$vm->{ip_addresses} = [ $vms->{$vm_id}->{Name}];
}
# provide the list of interfaces if available or else provide the VM's name as default address
if ($network_interfaces && ref $network_interfaces eq 'ARRAY' && @{$network_interfaces} > 0) {
$vm->{iface_addresses} = $network_interfaces;
} else {
$vm->{iface_addresses} = [ { iface => 'default', ip => $vms->{$vm_id}->{Name} } ];
}
push @$disco_data, $vm; push @$disco_data, $vm;
} }
@ -140,7 +170,7 @@ Resources discovery.
=item B<--resource-type> =item B<--resource-type>
Choose the type of resources to discover (can be: 'vm', 'node'). Choose the type of resources to discover (can be: C<vm>, C<node>).
=back =back

View File

@ -530,7 +530,7 @@ sub request {
$status = 'unknown'; $status = 'unknown';
} }
if (!$self->{output}->is_status(value => $status, compare => 'ok', litteral => 1)) { if (!$options{request}->{silently_fail} && !$self->{output}->is_status(value => $status, compare => 'ok', litteral => 1)) {
my $short_msg = $self->{response_code} . ' ' . my $short_msg = $self->{response_code} . ' ' .
(defined($http_code_explained->{$self->{response_code}}) ? $http_code_explained->{$self->{response_code}} : 'unknown'); (defined($http_code_explained->{$self->{response_code}}) ? $http_code_explained->{$self->{response_code}} : 'unknown');

View File

@ -279,8 +279,7 @@ sub request {
$self->{output}->test_eval(test => $request_options->{unknown_status}, values => { code => $self->{response_code} })) { $self->{output}->test_eval(test => $request_options->{unknown_status}, values => { code => $self->{response_code} })) {
$status = 'unknown'; $status = 'unknown';
} }
if (!$request_options->{silently_fail} && !$self->{output}->is_status(value => $status, compare => 'ok', litteral => 1)) {
if (!$self->{output}->is_status(value => $status, compare => 'ok', litteral => 1)) {
my $short_msg = $self->{response}->status_line; my $short_msg = $self->{response}->status_line;
if ($short_msg =~ /^401/) { if ($short_msg =~ /^401/) {
$short_msg .= ' (' . $1 . ' authentication expected)' if (defined($self->{response}->www_authenticate) && $short_msg .= ' (' . $1 . ' authentication expected)' if (defined($self->{response}->www_authenticate) &&

View File

@ -810,6 +810,30 @@ sub json_encode {
return $encoded; return $encoded;
} }
sub is_local_ip($) {
my ($ip) = @_;
return 0 unless $ip;
return 1 if $ip =~ /^127\./;
return 1 if $ip =~ /^10\./;
return 1 if $ip =~ /^192\.168\./;
return 1 if $ip =~ /^172\.(1[6-9]|2[0-9]|3[0-1])\./;
return 1 if $ip =~ /^169\.254\./;
return 1 if $ip eq '0.0.0.0';
return 0;
}
# This function is used with "sort", it sorts an array of IP addresses.
# $_[0] and $_[1] correspond to Perl's special variables $a and $b used by sort.
# I can't use $a and $b directly here, otherwise Perl generates a warning: "uninitialized value".
sub sort_ips($$) {
my @a = split /\./, $_[0];
my @b = split /\./, $_[1];
return $a[0] <=> $b[0] || $a[1] <=> $b[1] || $a[2] <=> $b[2] || $a[3] <=> $b[3]
}
# function to assess if a string has to be excluded given an include regexp and an exclude regexp # function to assess if a string has to be excluded given an include regexp and an exclude regexp
sub is_excluded { sub is_excluded {
my ($string, $include_regexp, $exclude_regexp) = @_; my ($string, $include_regexp, $exclude_regexp) = @_;
@ -1339,6 +1363,31 @@ Encodes an object to a JSON string.
=back =back
=head2 is_local_ip
my $is_local = centreon::plugins::misc::is_local_ip($ip);
Returns 1 if an IPv4 IP is within a local address range.
=over 4
=item * C<$ip> - IP to test.
=back
=head2 sort_ips
my @array = ( '192.168.0.3', '127.0.0.1' );
@array = sort centreon::plugins::misc::sort_ips @array;
Returns a sorted array.
=over 4
=item * C<@array> - An array containing IPs to be sorted.
=back
=head2 is_excluded =head2 is_excluded
my $excluded = is_excluded($string, $include_regexp, $exclude_regexp); my $excluded = is_excluded($string, $include_regexp, $exclude_regexp);
@ -1359,8 +1408,6 @@ Returns 1 if the string is excluded, 0 if it is included.
The string is excluded if $exclude_regexp is defined and matches the string, or if $include_regexp is defined and does The string is excluded if $exclude_regexp is defined and matches the string, or if $include_regexp is defined and does
not match the string. The string will also be excluded if it is undefined. not match the string. The string will also be excluded if it is undefined.
=cut
=head1 AUTHOR =head1 AUTHOR
Centreon Centreon

View File

@ -0,0 +1,274 @@
{
"uuid": "7c3f15b2-59b9-4121-a4f0-fe988d60ed57",
"lastMigration": 33,
"name": "Proxmox restapi.mockoon",
"endpointPrefix": "",
"latency": 0,
"port": 3000,
"hostname": "",
"folders": [],
"routes": [
{
"uuid": "6a778c11-99ff-4a50-8004-97b6aabff215",
"type": "http",
"documentation": "Proxmox restapi ve",
"method": "get",
"endpoint": "",
"responses": [
{
"uuid": "5c0324bb-6a0b-45f6-a01f-29fb6decc85b",
"body": "{\n \"return\": [\n {\n \"name\": \"eth0\",\n \"hardware-address\": \"52:54:00:12:34:56\",\n \"ip-addresses\": [\n {\n \"ip-address\": \"192.168.1.42\",\n \"prefix\": 24,\n \"ip-address-type\": \"ipv4\"\n }\n ],\n \"link-status\": \"up\"\n }\n ]\n}\n{\n \"return\": [\n {\n \"name\": \"eth0\",\n \"hardware-address\": \"52:54:00:12:34:56\",\n \"ip-addresses\": [\n {\n \"ip-address\": \"192.168.1.42\",\n \"prefix\": 24,\n \"ip-address-type\": \"ipv4\"\n }\n ],\n \"link-status\": \"up\"\n }\n ]\n}\n{\n \"data\": [\n {\n \"name\": \"eth0\",\n \"hardware-address\": \"12:54:00:32:24:56\",\n \"ip-addresses\": [\n {\n \"ip-address\": \"192.168.1.10\",\n \"prefix\": 24,\n \"ip-address-type\": \"ipv4\"\n },\n {\n \"ip-address\": \"fe80::5114:ff:ee12:1456\",\n \"prefix\": 64,\n \"ip-address-type\": \"ipv6\"\n }\n ],\n \"link-status\": \"up\",\n \"ipv4\": [\n \"192.168.1.42/24\"\n ],\n \"ipv6\": [\n \"fe80::5054:ff:fe12:3456/64\"\n ],\n \"flags\": [\"up\", \"broadcast\", \"multicast\"]\n },\n {\n \"name\": \"lo\",\n \"hardware-address\": \"00:00:00:00:00:00\",\n \"ip-addresses\": [\n {\n \"ip-address\": \"127.0.0.1\",\n \"prefix\": 8,\n \"ip-address-type\": \"ipv4\"\n },\n {\n \"ip-address\": \"::1\",\n \"prefix\": 128,\n \"ip-address-type\": \"ipv6\"\n }\n ],\n \"link-status\": \"up\",\n \"ipv4\": [\n \"127.0.0.1/8\"\n ],\n \"ipv6\": [\n \"::1/128\"\n ],\n \"flags\": [\"up\", \"loopback\"]\n }\n ]\n}",
"latency": 0,
"statusCode": 200,
"label": "",
"headers": [],
"bodyType": "INLINE",
"filePath": "",
"databucketID": "",
"sendFileAsBody": false,
"rules": [],
"rulesOperator": "OR",
"disableTemplating": false,
"fallbackTo404": false,
"default": true,
"crudKey": "id",
"callbacks": []
}
],
"responseMode": null,
"streamingMode": null,
"streamingInterval": 0
},
{
"uuid": "d71c2a8a-90af-40cc-8580-a7c42143e803",
"type": "http",
"documentation": "",
"method": "post",
"endpoint": "api2/json/access/ticket",
"responses": [
{
"uuid": "440d1923-ff1f-4e23-9ac2-8ba9ca6584d1",
"body": "{\n \"data\": {\n \"username\": \"root@pam\",\n \"ticket\": \"PVE:root@pam:65b884a0::g2bBeFuM\",\n \"CSRFPreventionToken\": \"65b884a0-3d3b-4e91-bf79-b1a2c18e1bfc\",\n \"cap\": {\n \"access\": {\n \"audit\": 1,\n \"groups\": 1,\n \"permissions\": 1,\n \"realm\": 1,\n \"roles\": 1,\n \"user\": 1\n },\n \"nodes\": {\n \"Sys.Audit\": 1,\n \"Sys.Modify\": 1\n },\n \"storage\": {\n \"Datastore.Allocate\": 1,\n \"Datastore.AllocateSpace\": 1,\n \"Datastore.Audit\": 1\n },\n \"vms\": {\n \"VM.Allocate\": 1,\n \"VM.Console\": 1,\n \"VM.Migrate\": 1,\n \"VM.PowerMgmt\": 1,\n \"VM.Audit\": 1\n }\n },\n \"clustername\": \"proxmox-cluster\",\n \"hostname\": \"proxmox1.local\"\n }\n}",
"latency": 0,
"statusCode": 200,
"label": "",
"headers": [],
"bodyType": "INLINE",
"filePath": "",
"databucketID": "",
"sendFileAsBody": false,
"rules": [],
"rulesOperator": "OR",
"disableTemplating": false,
"fallbackTo404": false,
"default": false,
"crudKey": "id",
"callbacks": []
}
],
"responseMode": null,
"streamingMode": null,
"streamingInterval": 0
},
{
"uuid": "84416441-bdf8-48be-ad8c-dbfd56e99895",
"type": "http",
"documentation": "",
"method": "get",
"endpoint": "api2/json/cluster/resources",
"responses": [
{
"uuid": "3ca59867-a6c9-42c0-9365-6aee513e8338",
"body": "{\n \"data\": [\n {\n \"id\": \"node/proxmox1\",\n \"type\": \"node\",\n \"node\": \"proxmox1\",\n \"status\": \"online\",\n \"maxcpu\": 8,\n \"cpu\": 0.15,\n \"maxmem\": 34359738368,\n \"mem\": 4294967296,\n \"maxdisk\": 500000000000,\n \"disk\": 200000000000,\n \"uptime\": 86400,\n \"level\": \"\",\n \"ssl_fingerprint\": \"AA:BB:CC:...\",\n \"version\": \"8.1-2\",\n \"name\": \"rien0\"\n },\n {\n \"id\": \"qemu/101\",\n \"type\": \"qemu\",\n \"vmid\": 101,\n \"name\": \"debian-vm2\",\n \"node\": \"proxmox1\",\n \"status\": \"running\",\n \"maxcpu\": 2,\n \"cpu\": 0.20,\n \"maxmem\": 2147483648,\n \"mem\": 1073741824,\n \"maxdisk\": 34359738368,\n \"disk\": 8589934592,\n \"uptime\": 3600,\n \"template\": 0\n },\n {\n \"id\": \"storage/local\",\n \"type\": \"storage\",\n \"storage\": \"local\",\n \"node\": \"proxmox1\",\n \"status\": \"available\",\n \"enabled\": 1,\n \"content\": \"iso,vztmpl,backup\",\n \"shared\": 0,\n \"maxdisk\": 250000000000,\n \"disk\": 150000000000,\n \"name\": \"rien0\"\n }\n ]\n}",
"latency": 0,
"statusCode": 200,
"label": "",
"headers": [],
"bodyType": "INLINE",
"filePath": "",
"databucketID": "",
"sendFileAsBody": false,
"rules": [
{
"target": "query",
"modifier": "type",
"value": "node",
"invert": false,
"operator": "equals"
},
{
"target": "body",
"modifier": "",
"value": "",
"invert": false,
"operator": "equals"
}
],
"rulesOperator": "OR",
"disableTemplating": false,
"fallbackTo404": false,
"default": false,
"crudKey": "id",
"callbacks": []
}
],
"responseMode": null,
"streamingMode": null,
"streamingInterval": 0
},
{
"uuid": "c0cdebe1-88ef-4fd8-8074-b4ebc77b5e9f",
"type": "http",
"documentation": "",
"method": "get",
"endpoint": "api2/json/nodes/([a-z0-9]+)/([a-z0-9]+)/agent/get-osinfo",
"responses": [
{
"uuid": "3b682249-4d8b-4c5e-91cc-272832002e77",
"body": "{\n \"data\": {\n \"id\": \"Linux\",\n \"name\": \"Debian GNU/Linux\",\n \"version-id\": \"11\",\n \"version\": \"11 (bullseye)\",\n \"pretty-name\": \"Debian GNU/Linux 11 (bullseye)\",\n \"kernel-release\": \"5.10.0-21-amd64\",\n \"kernel-version\": \"#1 SMP Debian 5.10.162-1 (2023-01-21)\",\n \"machine\": \"x86_64\"\n }\n}",
"latency": 0,
"statusCode": 500,
"label": "",
"headers": [
{
"key": "access-control-allow-headers",
"value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With"
},
{
"key": "access-control-allow-methods",
"value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS"
},
{
"key": "access-control-allow-origin",
"value": "*"
},
{
"key": "content-security-policy",
"value": "default-src 'none'"
},
{
"key": "content-type",
"value": "text/html; charset=utf-8"
},
{
"key": "x-content-type-options",
"value": "nosniff"
}
],
"bodyType": "INLINE",
"filePath": "",
"databucketID": "",
"sendFileAsBody": false,
"rules": [],
"rulesOperator": "OR",
"disableTemplating": false,
"fallbackTo404": false,
"default": false,
"crudKey": "id",
"callbacks": []
}
],
"responseMode": null,
"streamingMode": null,
"streamingInterval": 0
},
{
"uuid": "78d64704-80ab-4420-aa6b-2c807930f0e6",
"type": "http",
"documentation": "",
"method": "get",
"endpoint": "api2/json/nodes/([a-z0-9]+)/([a-z0-9]+)/agent/network-get-interfaces",
"responses": [
{
"uuid": "62d7f560-f89c-41ca-8a36-46da872671b0",
"body": "{\n \"data\": [\n {\n \"name\": \"lo\",\n \"hardware-address\": null,\n \"ip-addresses\": [\n {\n \"ip-address-type\": \"ipv4\",\n \"ip-address\": \"127.0.0.1\",\n \"prefix\": 8\n }\n ],\n \"mtu\": 65536,\n \"state\": \"UNKNOWN\",\n \"type\": \"loopback\",\n \"flags\": [\"up\", \"loopback\", \"multicast\"]\n },\n {\n \"name\": \"eth0\",\n \"hardware-address\": \"52:54:00:12:34:56\",\n \"ip-addresses\": [\n {\n \"ip-address-type\": \"ipv4\",\n \"ip-address\": \"192.168.1.101\",\n \"prefix\": 24\n },\n {\n \"ip-address-type\": \"ipv6\",\n \"ip-address\": \"fe80::5054:ff:fe12:3456\",\n \"prefix\": 64\n }\n ],\n \"mtu\": 1500,\n \"state\": \"UP\",\n \"type\": \"ethernet\",\n \"flags\": [\"up\", \"broadcast\", \"multicast\"]\n },\n {\n \"name\": \"eth1\",\n \"hardware-address\": \"52:54:00:12:34:56\",\n \"ip-addresses\": [\n {\n \"ip-address-type\": \"ipv4\",\n \"ip-address\": \"212.168.1.101\",\n \"prefix\": 24\n },\n {\n \"ip-address-type\": \"ipv6\",\n \"ip-address\": \"fe80::5054:ff:fe12:3456\",\n \"prefix\": 64\n }\n ],\n \"mtu\": 1500,\n \"state\": \"UP\",\n \"type\": \"ethernet\",\n \"flags\": [\"up\", \"broadcast\", \"multicast\"]\n }\n ]\n}",
"latency": 0,
"statusCode": 200,
"label": "",
"headers": [],
"bodyType": "INLINE",
"filePath": "",
"databucketID": "",
"sendFileAsBody": false,
"rules": [],
"rulesOperator": "OR",
"disableTemplating": false,
"fallbackTo404": false,
"default": false,
"crudKey": "id",
"callbacks": []
}
],
"responseMode": null,
"streamingMode": null,
"streamingInterval": 0
}
],
"rootChildren": [
{
"type": "route",
"uuid": "6a778c11-99ff-4a50-8004-97b6aabff215"
},
{
"type": "route",
"uuid": "d71c2a8a-90af-40cc-8580-a7c42143e803"
},
{
"type": "route",
"uuid": "84416441-bdf8-48be-ad8c-dbfd56e99895"
},
{
"type": "route",
"uuid": "c0cdebe1-88ef-4fd8-8074-b4ebc77b5e9f"
},
{
"type": "route",
"uuid": "78d64704-80ab-4420-aa6b-2c807930f0e6"
}
],
"proxyMode": false,
"proxyHost": "",
"proxyRemovePrefix": false,
"tlsOptions": {
"enabled": false,
"type": "CERT",
"pfxPath": "",
"certPath": "",
"keyPath": "",
"caPath": "",
"passphrase": ""
},
"cors": true,
"headers": [
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "Access-Control-Allow-Origin",
"value": "*"
},
{
"key": "Access-Control-Allow-Methods",
"value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS"
},
{
"key": "Access-Control-Allow-Headers",
"value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With"
}
],
"proxyReqHeaders": [
{
"key": "",
"value": ""
}
],
"proxyResHeaders": [
{
"key": "",
"value": ""
}
],
"data": [],
"callbacks": []
}

View File

@ -0,0 +1,36 @@
*** Settings ***
Documentation Proxmox VE REST API Mode Discovery
Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource
Suite Setup Start Mockoon ${MOCKOON_JSON}
Suite Teardown Stop Mockoon
Test Timeout 120s
*** Variables ***
${MOCKOON_JSON} ${CURDIR}${/}proxmox.mockoon.json
${HOSTNAME} 127.0.0.1
${APIPORT} 3000
${CMD} ${CENTREON_PLUGINS}
... --plugin=apps::proxmox::ve::restapi::plugin
... --mode discovery
... --hostname=${HOSTNAME}
... --api-username=xx
... --api-password=xx
... --proto=http
... --port=${APIPORT}
*** Test Cases ***
Discovery ${tc}
[Tags] storage api hpe hp
${command} Catenate
... ${CMD}
... ${extra_options}
Ctn Run Command And Check Result As Regexp ${command} ${expected_regexp}
Examples: tc extraoptions expected_regexp --
... 1 ${EMPTY} "discovered_items":3
... 2 --resource-type=vm (?=.*"ip_addresses":\\\\["123.321.123.321","127.0.0.1"\\\\],)(?=.*"os_info_name":"XxXxXx GNU/Linux")
... 3 --resource-type=node ^(?!.*(ip_addresses|os_info_name)).*$

View File

@ -0,0 +1,293 @@
{
"uuid": "7c3f15b2-59b9-4121-a4f0-fe988d60ed57",
"lastMigration": 33,
"name": "Proxmox restapi.mockoon",
"endpointPrefix": "",
"latency": 0,
"port": 3000,
"hostname": "",
"folders": [],
"routes": [
{
"uuid": "6a778c11-99ff-4a50-8004-97b6aabff215",
"type": "http",
"documentation": "Proxmox restapi ve",
"method": "get",
"endpoint": "",
"responses": [
{
"uuid": "5c0324bb-6a0b-45f6-a01f-29fb6decc85b",
"body": "{\n \"return\": [\n {\n \"name\": \"eth0\",\n \"hardware-address\": \"52:54:00:12:34:56\",\n \"ip-addresses\": [\n {\n \"ip-address\": \"192.168.1.42\",\n \"prefix\": 24,\n \"ip-address-type\": \"ipv4\"\n }\n ],\n \"link-status\": \"up\"\n }\n ]\n}\n{\n \"return\": [\n {\n \"name\": \"eth0\",\n \"hardware-address\": \"52:54:00:12:34:56\",\n \"ip-addresses\": [\n {\n \"ip-address\": \"192.168.1.42\",\n \"prefix\": 24,\n \"ip-address-type\": \"ipv4\"\n }\n ],\n \"link-status\": \"up\"\n }\n ]\n}\n{\n \"data\": [\n {\n \"name\": \"eth0\",\n \"hardware-address\": \"12:54:00:32:24:56\",\n \"ip-addresses\": [\n {\n \"ip-address\": \"192.168.1.10\",\n \"prefix\": 24,\n \"ip-address-type\": \"ipv4\"\n },\n {\n \"ip-address\": \"fe80::5114:ff:ee12:1456\",\n \"prefix\": 64,\n \"ip-address-type\": \"ipv6\"\n }\n ],\n \"link-status\": \"up\",\n \"ipv4\": [\n \"192.168.1.42/24\"\n ],\n \"ipv6\": [\n \"fe80::5054:ff:fe12:3456/64\"\n ],\n \"flags\": [\"up\", \"broadcast\", \"multicast\"]\n },\n {\n \"name\": \"lo\",\n \"hardware-address\": \"00:00:00:00:00:00\",\n \"ip-addresses\": [\n {\n \"ip-address\": \"127.0.0.1\",\n \"prefix\": 8,\n \"ip-address-type\": \"ipv4\"\n },\n {\n \"ip-address\": \"::1\",\n \"prefix\": 128,\n \"ip-address-type\": \"ipv6\"\n }\n ],\n \"link-status\": \"up\",\n \"ipv4\": [\n \"127.0.0.1/8\"\n ],\n \"ipv6\": [\n \"::1/128\"\n ],\n \"flags\": [\"up\", \"loopback\"]\n }\n ]\n}",
"latency": 0,
"statusCode": 200,
"label": "",
"headers": [],
"bodyType": "INLINE",
"filePath": "",
"databucketID": "",
"sendFileAsBody": false,
"rules": [],
"rulesOperator": "OR",
"disableTemplating": false,
"fallbackTo404": false,
"default": true,
"crudKey": "id",
"callbacks": []
}
],
"responseMode": null,
"streamingMode": null,
"streamingInterval": 0
},
{
"uuid": "d71c2a8a-90af-40cc-8580-a7c42143e803",
"type": "http",
"documentation": "",
"method": "post",
"endpoint": "api2/json/access/ticket",
"responses": [
{
"uuid": "440d1923-ff1f-4e23-9ac2-8ba9ca6584d1",
"body": "{\n \"data\": {\n \"username\": \"root@pam\",\n \"ticket\": \"PVE:root@pam:ccccccc::g2bBfFuM\",\n \"CSRFPreventionToken\": \"aaaaaaa-3d3b-4e91-bf79-b1a2c18eabfc\",\n \"cap\": {\n \"access\": {\n \"audit\": 1,\n \"groups\": 1,\n \"permissions\": 1,\n \"realm\": 1,\n \"roles\": 1,\n \"user\": 1\n },\n \"nodes\": {\n \"Sys.Audit\": 1,\n \"Sys.Modify\": 1\n },\n \"storage\": {\n \"Datastore.Allocate\": 1,\n \"Datastore.AllocateSpace\": 1,\n \"Datastore.Audit\": 1\n },\n \"vms\": {\n \"VM.Allocate\": 1,\n \"VM.Console\": 1,\n \"VM.Migrate\": 1,\n \"VM.PowerMgmt\": 1,\n \"VM.Audit\": 1\n }\n },\n \"clustername\": \"WwWwW-cluster\",\n \"hostname\": \"WwWwW1.local\"\n }\n}",
"latency": 0,
"statusCode": 200,
"label": "",
"headers": [],
"bodyType": "INLINE",
"filePath": "",
"databucketID": "",
"sendFileAsBody": false,
"rules": [],
"rulesOperator": "OR",
"disableTemplating": false,
"fallbackTo404": false,
"default": false,
"crudKey": "id",
"callbacks": []
}
],
"responseMode": null,
"streamingMode": null,
"streamingInterval": 0
},
{
"uuid": "84416441-bdf8-48be-ad8c-dbfd56e99895",
"type": "http",
"documentation": "",
"method": "get",
"endpoint": "api2/json/cluster/resources",
"responses": [
{
"uuid": "3ca59867-a6c9-42c0-9365-6aee513e8338",
"body": "{\n \"data\": [\n {\n \"id\": \"node/www1\",\n \"type\": \"node\",\n \"node\": \"www1\",\n \"status\": \"online\",\n \"maxcpu\": 8,\n \"cpu\": 0.15,\n \"maxmem\": 34359738368,\n \"mem\": 4294967296,\n \"maxdisk\": 500000000000,\n \"disk\": 200000000000,\n \"uptime\": 86400,\n \"level\": \"\",\n \"ssl_fingerprint\": \"AA:BB:CC:...\",\n \"version\": \"1.1-2\",\n \"name\": \"rien0\"\n },\n {\n \"id\": \"qemu/101\",\n \"type\": \"qemu\",\n \"vmid\": 101,\n \"name\": \"cccccc-vm2\",\n \"node\": \"dddddd1\",\n \"status\": \"running\",\n \"maxcpu\": 2,\n \"cpu\": 0.20,\n \"maxmem\": 2147483648,\n \"mem\": 1073741824,\n \"maxdisk\": 34359738368,\n \"disk\": 8589934592,\n \"uptime\": 3600,\n \"template\": 0\n },\n {\n \"id\": \"storage/local\",\n \"type\": \"storage\",\n \"storage\": \"local\",\n \"node\": \"cccccc1\",\n \"status\": \"available\",\n \"enabled\": 1,\n \"content\": \"iso,vztmpl,backup\",\n \"shared\": 0,\n \"maxdisk\": 250000000000,\n \"disk\": 150000000000,\n \"name\": \"rien0\"\n }\n ]\n}",
"latency": 0,
"statusCode": 200,
"label": "",
"headers": [],
"bodyType": "INLINE",
"filePath": "",
"databucketID": "",
"sendFileAsBody": false,
"rules": [
{
"target": "query",
"modifier": "type",
"value": "node",
"invert": false,
"operator": "equals"
},
{
"target": "body",
"modifier": "",
"value": "",
"invert": false,
"operator": "equals"
}
],
"rulesOperator": "OR",
"disableTemplating": false,
"fallbackTo404": false,
"default": false,
"crudKey": "id",
"callbacks": []
}
],
"responseMode": null,
"streamingMode": null,
"streamingInterval": 0
},
{
"uuid": "c0cdebe1-88ef-4fd8-8074-b4ebc77b5e9f",
"type": "http",
"documentation": "",
"method": "get",
"endpoint": "api2/json/nodes/([a-z0-9]+)/qemu/([a-z0-9]+)/agent/get-osinfo",
"responses": [
{
"uuid": "3b682249-4d8b-4c5e-91cc-272832002e77",
"body": "{\"data\":{\"result\":{\"machine\":\"x86_64\",\"version\":\"111 (XxXxXx)\",\"version-id\":\"111\",\"pretty-name\":\"XXXXX GNU/Linux 111 (XxXxXx)\",\"id\":\"xXxXx\",\"name\":\"XxXxXx GNU/Linux\",\"kernel-version\":\"#1 SMP PREEMPT_DYNAMIC XXXXX 6.0.0-1 (2025-01-01)\",\"kernel-release\":\"6.0.0-32-amd64\"}}}",
"latency": 0,
"statusCode": 500,
"label": "",
"headers": [
{
"key": "access-control-allow-headers",
"value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With"
},
{
"key": "access-control-allow-methods",
"value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS"
},
{
"key": "access-control-allow-origin",
"value": "*"
},
{
"key": "content-security-policy",
"value": "default-src 'none'"
},
{
"key": "content-type",
"value": "text/html; charset=utf-8"
},
{
"key": "x-content-type-options",
"value": "nosniff"
}
],
"bodyType": "INLINE",
"filePath": "",
"databucketID": "",
"sendFileAsBody": false,
"rules": [],
"rulesOperator": "OR",
"disableTemplating": false,
"fallbackTo404": false,
"default": false,
"crudKey": "id",
"callbacks": []
},
{
"uuid": "34ffe7cc-4968-43e7-b8c4-b57c248f20ef",
"body": "{\"message\":\"QEMU guest agent is not running\\n\",\"data\":null}",
"latency": 0,
"statusCode": 200,
"label": "",
"headers": [],
"bodyType": "INLINE",
"filePath": "",
"databucketID": "",
"sendFileAsBody": false,
"rules": [],
"rulesOperator": "OR",
"disableTemplating": false,
"fallbackTo404": false,
"default": false,
"crudKey": "id",
"callbacks": []
}
],
"responseMode": null,
"streamingMode": null,
"streamingInterval": 0
},
{
"uuid": "78d64704-80ab-4420-aa6b-2c807930f0e6",
"type": "http",
"documentation": "",
"method": "get",
"endpoint": "api2/json/nodes/([a-z0-9]+)/qemu/([a-z0-9]+)/agent/network-get-interfaces",
"responses": [
{
"uuid": "62d7f560-f89c-41ca-8a36-46da872671b0",
"body": "{\"data\":{\"result\":[{\"ip-addresses\":[{\"ip-address\":\"127.0.0.1\",\"prefix\":8,\"ip-address-type\":\"ipv4\"},{\"prefix\":128,\"ip-address-type\":\"ipv6\",\"ip-address\":\"::1\"}],\"hardware-address\":\"00:00:00:00:00:00\",\"name\":\"lo\",\"statistics\":{\"tx-packets\":332,\"rx-errs\":0,\"tx-errs\":0,\"rx-packets\":332,\"rx-dropped\":0,\"tx-bytes\":56962,\"rx-bytes\":56962,\"tx-dropped\":0}},{\"statistics\":{\"tx-errs\":0,\"rx-packets\":682355687,\"tx-packets\":492437157,\"rx-errs\":0,\"rx-bytes\":172127793,\"tx-dropped\":0,\"rx-dropped\":0,\"tx-bytes\":917205385},\"name\":\"eth0\",\"hardware-address\":\"aa:bb:cc:dd:ee:ff\",\"ip-addresses\":[{\"ip-address-type\":\"ipv4\",\"prefix\":24,\"ip-address\":\"123.321.123.321\"},{\"ip-address\":\"aaaa::bbbb:cccc:dddd:eeee\",\"ip-address-type\":\"ipv6\",\"prefix\":64}]}]}}",
"latency": 0,
"statusCode": 200,
"label": "",
"headers": [],
"bodyType": "INLINE",
"filePath": "",
"databucketID": "",
"sendFileAsBody": false,
"rules": [],
"rulesOperator": "OR",
"disableTemplating": false,
"fallbackTo404": false,
"default": false,
"crudKey": "id",
"callbacks": []
}
],
"responseMode": null,
"streamingMode": null,
"streamingInterval": 0
}
],
"rootChildren": [
{
"type": "route",
"uuid": "6a778c11-99ff-4a50-8004-97b6aabff215"
},
{
"type": "route",
"uuid": "d71c2a8a-90af-40cc-8580-a7c42143e803"
},
{
"type": "route",
"uuid": "84416441-bdf8-48be-ad8c-dbfd56e99895"
},
{
"type": "route",
"uuid": "c0cdebe1-88ef-4fd8-8074-b4ebc77b5e9f"
},
{
"type": "route",
"uuid": "78d64704-80ab-4420-aa6b-2c807930f0e6"
}
],
"proxyMode": false,
"proxyHost": "",
"proxyRemovePrefix": false,
"tlsOptions": {
"enabled": false,
"type": "CERT",
"pfxPath": "",
"certPath": "",
"keyPath": "",
"caPath": "",
"passphrase": ""
},
"cors": true,
"headers": [
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "Access-Control-Allow-Origin",
"value": "*"
},
{
"key": "Access-Control-Allow-Methods",
"value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS"
},
{
"key": "Access-Control-Allow-Headers",
"value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With"
}
],
"proxyReqHeaders": [
{
"key": "",
"value": ""
}
],
"proxyResHeaders": [
{
"key": "",
"value": ""
}
],
"data": [],
"callbacks": []
}

View File

@ -58,6 +58,7 @@ cpu-utilization-5s
CX CX
Cyberoam Cyberoam
Datacore Datacore
Datastore
datasource datasource
datastore datastore
datastores datastores
@ -128,6 +129,7 @@ in-ucast
io-cards io-cards
iops iops
IpAddr IpAddr
IPs
ip-label ip-label
ipsec ipsec
ipv4 ipv4
@ -238,6 +240,7 @@ PPID
prct prct
Primera Primera
Procurve Procurve
Proxmox
proto proto
--proxyurl --proxyurl
psu psu
@ -298,6 +301,7 @@ total-online-prct
total-oper-down total-oper-down
total-oper-up total-oper-up
tower-cli tower-cli
TLSv1
TrendMicro TrendMicro
tsdb-version tsdb-version
UCD UCD
@ -323,6 +327,7 @@ vdomain
VDSL2 VDSL2
Veeam Veeam
VeloCloud VeloCloud
VE
vhost vhost
VM VM
VMs VMs