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 centreon::plugins::http;
use centreon::plugins::statefile;
use centreon::plugins::misc;
use JSON::XS;
use Digest::MD5 qw(md5_hex);
@ -149,7 +150,7 @@ sub get_ticket {
my $post_data = 'username=' . $self->{api_username} .
'&password=' . $self->{api_password} .
'&realm=' . $self->{realm};
$self->settings();
my $content = $self->{http}->request(
@ -186,20 +187,21 @@ sub request_api {
if (!defined($self->{ticket})) {
$self->{ticket} = $self->get_ticket(statefile => $self->{cache});
}
$self->settings();
my $content = $self->{http}->request(%options);
my $decoded;
eval {
$decoded = JSON::XS->new->utf8->decode($content);
};
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}->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}->option_exit();
}
@ -216,7 +218,7 @@ sub get_version {
sub internal_api_list_vms {
my ($self, %options) = @_;
my $vm = $self->request_api(method => 'GET', url_path =>'/api2/json/cluster/resources?type=vm');
return $vm;
}
@ -242,7 +244,7 @@ sub api_list_vms {
sub internal_api_list_nodes {
my ($self, %options) = @_;
my $nodes = $self->request_api(method => 'GET', url_path =>'/api2/json/cluster/resources?type=node');
return $nodes;
}
@ -350,6 +352,93 @@ sub cache_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 {
my ($self, %options) = @_;
@ -501,7 +590,7 @@ Proxmox VE Rest API
=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
@ -523,7 +612,7 @@ Specify https if needed (default: 'https').
Set Proxmox VE Username
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>
@ -531,7 +620,7 @@ Set Proxmox VE Password
=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>

View File

@ -65,6 +65,36 @@ sub discovery_vm {
$vm->{state} = $vms->{$vm_id}->{State};
$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;
}
@ -140,7 +170,7 @@ Resources discovery.
=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

View File

@ -530,7 +530,7 @@ sub request {
$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} . ' ' .
(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} })) {
$status = 'unknown';
}
if (!$self->{output}->is_status(value => $status, compare => 'ok', litteral => 1)) {
if (!$request_options->{silently_fail} && !$self->{output}->is_status(value => $status, compare => 'ok', litteral => 1)) {
my $short_msg = $self->{response}->status_line;
if ($short_msg =~ /^401/) {
$short_msg .= ' (' . $1 . ' authentication expected)' if (defined($self->{response}->www_authenticate) &&

View File

@ -810,6 +810,30 @@ sub json_encode {
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
sub is_excluded {
my ($string, $include_regexp, $exclude_regexp) = @_;
@ -1339,6 +1363,31 @@ Encodes an object to a JSON string.
=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
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
not match the string. The string will also be excluded if it is undefined.
=cut
=head1 AUTHOR
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
Cyberoam
Datacore
Datastore
datasource
datastore
datastores
@ -128,6 +129,7 @@ in-ucast
io-cards
iops
IpAddr
IPs
ip-label
ipsec
ipv4
@ -238,6 +240,7 @@ PPID
prct
Primera
Procurve
Proxmox
proto
--proxyurl
psu
@ -298,6 +301,7 @@ total-online-prct
total-oper-down
total-oper-up
tower-cli
TLSv1
TrendMicro
tsdb-version
UCD
@ -323,6 +327,7 @@ vdomain
VDSL2
Veeam
VeloCloud
VE
vhost
VM
VMs