2020-10-05 17:08:47 +02:00
|
|
|
package PVE::Network::SDN::Ipams::NetboxPlugin;
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
use PVE::INotify;
|
|
|
|
use PVE::Cluster;
|
|
|
|
use PVE::Tools;
|
|
|
|
|
|
|
|
use base('PVE::Network::SDN::Ipams::Plugin');
|
|
|
|
|
|
|
|
sub type {
|
|
|
|
return 'netbox';
|
|
|
|
}
|
|
|
|
|
|
|
|
sub properties {
|
|
|
|
return {
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
sub options {
|
|
|
|
|
|
|
|
return {
|
|
|
|
url => { optional => 0},
|
|
|
|
token => { optional => 0 },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
# Plugin implementation
|
|
|
|
|
|
|
|
sub add_subnet {
|
|
|
|
my ($class, $plugin_config, $subnetid, $subnet) = @_;
|
|
|
|
|
2020-10-05 17:09:08 +02:00
|
|
|
my $cidr = $subnet->{cidr};
|
2020-10-05 17:08:47 +02:00
|
|
|
my $gateway = $subnet->{gateway};
|
|
|
|
my $url = $plugin_config->{url};
|
|
|
|
my $token = $plugin_config->{token};
|
|
|
|
my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
|
|
|
|
|
|
|
|
my $internalid = get_prefix_id($url, $cidr, $headers);
|
|
|
|
|
|
|
|
#create subnet
|
|
|
|
if (!$internalid) {
|
|
|
|
|
|
|
|
my $params = { prefix => $cidr };
|
|
|
|
|
|
|
|
eval {
|
2021-01-05 10:35:25 +01:00
|
|
|
my $result = PVE::Network::SDN::api_request("POST", "$url/ipam/prefixes/", $headers, $params);
|
2020-10-05 17:08:47 +02:00
|
|
|
};
|
|
|
|
if ($@) {
|
|
|
|
die "error add subnet to ipam: $@";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
sub del_subnet {
|
|
|
|
my ($class, $plugin_config, $subnetid, $subnet) = @_;
|
|
|
|
|
2020-10-05 17:09:08 +02:00
|
|
|
my $cidr = $subnet->{cidr};
|
2020-10-05 17:08:47 +02:00
|
|
|
my $url = $plugin_config->{url};
|
|
|
|
my $token = $plugin_config->{token};
|
|
|
|
my $gateway = $subnet->{gateway};
|
|
|
|
my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
|
|
|
|
|
|
|
|
my $internalid = get_prefix_id($url, $cidr, $headers);
|
|
|
|
return if !$internalid;
|
|
|
|
|
2020-10-05 17:09:08 +02:00
|
|
|
return; #fixme: check that prefix is empty exluding gateway, before delete
|
2020-10-05 17:08:47 +02:00
|
|
|
|
|
|
|
eval {
|
2021-01-05 10:35:25 +01:00
|
|
|
PVE::Network::SDN::api_request("DELETE", "$url/ipam/prefixes/$internalid/", $headers);
|
2020-10-05 17:08:47 +02:00
|
|
|
};
|
|
|
|
if ($@) {
|
|
|
|
die "error deleting subnet from ipam: $@";
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
sub add_ip {
|
2021-01-05 10:35:23 +01:00
|
|
|
my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway) = @_;
|
2020-10-05 17:08:47 +02:00
|
|
|
|
2020-10-05 17:09:08 +02:00
|
|
|
my $mask = $subnet->{mask};
|
2020-10-05 17:08:47 +02:00
|
|
|
my $url = $plugin_config->{url};
|
|
|
|
my $token = $plugin_config->{token};
|
|
|
|
my $section = $plugin_config->{section};
|
|
|
|
my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
|
2021-01-05 10:35:23 +01:00
|
|
|
$description .= " mac:$mac" if $mac && $description;
|
2020-10-05 17:08:47 +02:00
|
|
|
|
2020-10-05 17:09:11 +02:00
|
|
|
my $params = { address => "$ip/$mask", dns_name => $hostname, description => $description };
|
2020-10-05 17:08:47 +02:00
|
|
|
|
|
|
|
eval {
|
2021-01-05 10:35:25 +01:00
|
|
|
PVE::Network::SDN::api_request("POST", "$url/ipam/ip-addresses/", $headers, $params);
|
2020-10-05 17:08:47 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
if ($@) {
|
|
|
|
die "error add subnet ip to ipam: ip already exist: $@";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-05 10:35:24 +01:00
|
|
|
sub update_ip {
|
|
|
|
my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway) = @_;
|
|
|
|
|
|
|
|
my $mask = $subnet->{mask};
|
|
|
|
my $url = $plugin_config->{url};
|
|
|
|
my $token = $plugin_config->{token};
|
|
|
|
my $section = $plugin_config->{section};
|
|
|
|
my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
|
|
|
|
$description .= " mac:$mac" if $mac && $description;
|
|
|
|
|
|
|
|
my $params = { address => "$ip/$mask", dns_name => $hostname, description => $description };
|
|
|
|
|
|
|
|
my $ip_id = get_ip_id($url, $ip, $headers);
|
|
|
|
die "can't find ip $ip in ipam" if !$ip_id;
|
|
|
|
|
|
|
|
eval {
|
2021-01-05 10:35:25 +01:00
|
|
|
PVE::Network::SDN::api_request("PATCH", "$url/ipam/ip-addresses/$ip_id/", $headers, $params);
|
2021-01-05 10:35:24 +01:00
|
|
|
};
|
|
|
|
if ($@) {
|
|
|
|
die "error update ip $ip : $@";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-05 17:08:47 +02:00
|
|
|
sub add_next_freeip {
|
2021-01-05 10:35:23 +01:00
|
|
|
my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $description) = @_;
|
2020-10-05 17:08:47 +02:00
|
|
|
|
2020-10-05 17:09:08 +02:00
|
|
|
my $cidr = $subnet->{cidr};
|
|
|
|
|
2020-10-05 17:08:47 +02:00
|
|
|
my $url = $plugin_config->{url};
|
|
|
|
my $token = $plugin_config->{token};
|
|
|
|
my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
|
|
|
|
|
|
|
|
my $internalid = get_prefix_id($url, $cidr, $headers);
|
2021-01-05 10:35:23 +01:00
|
|
|
$description .= " mac:$mac" if $mac && $description;
|
2020-10-05 17:08:47 +02:00
|
|
|
|
2020-10-05 17:09:11 +02:00
|
|
|
my $params = { dns_name => $hostname, description => $description };
|
2020-10-05 17:08:47 +02:00
|
|
|
|
|
|
|
my $ip = undef;
|
|
|
|
eval {
|
2021-01-05 10:35:25 +01:00
|
|
|
my $result = PVE::Network::SDN::api_request("POST", "$url/ipam/prefixes/$internalid/available-ips/", $headers, $params);
|
2020-10-05 17:08:47 +02:00
|
|
|
$ip = $result->{address};
|
|
|
|
};
|
|
|
|
|
|
|
|
if ($@) {
|
|
|
|
die "can't find free ip in subnet $cidr: $@";
|
|
|
|
}
|
|
|
|
|
|
|
|
return $ip;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub del_ip {
|
2020-10-05 17:09:08 +02:00
|
|
|
my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
|
2020-10-05 17:08:47 +02:00
|
|
|
|
|
|
|
return if !$ip;
|
|
|
|
|
|
|
|
my $url = $plugin_config->{url};
|
|
|
|
my $token = $plugin_config->{token};
|
|
|
|
my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
|
|
|
|
|
|
|
|
my $ip_id = get_ip_id($url, $ip, $headers);
|
|
|
|
die "can't find ip $ip in ipam" if !$ip_id;
|
|
|
|
|
|
|
|
eval {
|
2021-01-05 10:35:25 +01:00
|
|
|
PVE::Network::SDN::api_request("DELETE", "$url/ipam/ip-addresses/$ip_id/", $headers);
|
2020-10-05 17:08:47 +02:00
|
|
|
};
|
|
|
|
if ($@) {
|
|
|
|
die "error delete ip $ip : $@";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub verify_api {
|
|
|
|
my ($class, $plugin_config) = @_;
|
|
|
|
|
|
|
|
my $url = $plugin_config->{url};
|
|
|
|
my $token = $plugin_config->{token};
|
|
|
|
my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
|
|
|
|
|
|
|
|
|
|
|
|
eval {
|
2021-01-05 10:35:25 +01:00
|
|
|
PVE::Network::SDN::api_request("GET", "$url/ipam/aggregates/", $headers);
|
2020-10-05 17:08:47 +02:00
|
|
|
};
|
|
|
|
if ($@) {
|
|
|
|
die "Can't connect to netbox api: $@";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub on_update_hook {
|
|
|
|
my ($class, $plugin_config) = @_;
|
|
|
|
|
|
|
|
PVE::Network::SDN::Ipams::NetboxPlugin::verify_api($class, $plugin_config);
|
|
|
|
}
|
|
|
|
|
|
|
|
#helpers
|
|
|
|
|
|
|
|
sub get_prefix_id {
|
|
|
|
my ($url, $cidr, $headers) = @_;
|
|
|
|
|
2021-01-05 10:35:25 +01:00
|
|
|
my $result = PVE::Network::SDN::api_request("GET", "$url/ipam/prefixes/?q=$cidr", $headers);
|
2020-10-05 17:08:47 +02:00
|
|
|
my $data = @{$result->{results}}[0];
|
|
|
|
my $internalid = $data->{id};
|
|
|
|
return $internalid;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub get_ip_id {
|
|
|
|
my ($url, $ip, $headers) = @_;
|
2021-01-05 10:35:25 +01:00
|
|
|
my $result = PVE::Network::SDN::api_request("GET", "$url/ipam/ip-addresses/?q=$ip", $headers);
|
2020-10-05 17:08:47 +02:00
|
|
|
my $data = @{$result->{results}}[0];
|
|
|
|
my $ip_id = $data->{id};
|
|
|
|
return $ip_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
1;
|
|
|
|
|
|
|
|
|