pve-network/src/PVE/Network/SDN/Subnets.pm
Thomas Lamprecht 6029cbb071 separate packaging and source build system
like almost all of our repos do nowadays, modern git can detect such
things on rebase so in development stuff should be hopefully not too
much affected by this.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2023-05-25 18:18:57 +02:00

376 lines
10 KiB
Perl

package PVE::Network::SDN::Subnets;
use strict;
use warnings;
use Net::Subnet qw(subnet_matcher);
use Net::IP;
use NetAddr::IP qw(:lower);
use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
use PVE::Network::SDN::Dns;
use PVE::Network::SDN::Ipams;
use PVE::Network::SDN::SubnetPlugin;
PVE::Network::SDN::SubnetPlugin->register();
PVE::Network::SDN::SubnetPlugin->init();
sub sdn_subnets_config {
my ($cfg, $id, $noerr) = @_;
die "no sdn subnet ID specified\n" if !$id;
my $scfg = $cfg->{ids}->{$id};
die "sdn subnet '$id' does not exist\n" if (!$noerr && !$scfg);
if($scfg) {
my ($zone, $network, $mask) = split(/-/, $id);
$scfg->{cidr} = "$network/$mask";
$scfg->{zone} = $zone;
$scfg->{network} = $network;
$scfg->{mask} = $mask;
}
return $scfg;
}
sub config {
my $config = cfs_read_file("sdn/subnets.cfg");
}
sub write_config {
my ($cfg) = @_;
cfs_write_file("sdn/subnets.cfg", $cfg);
}
sub sdn_subnets_ids {
my ($cfg) = @_;
return sort keys %{$cfg->{ids}};
}
sub complete_sdn_subnet {
my ($cmdname, $pname, $cvalue) = @_;
my $cfg = PVE::Network::SDN::Subnets::config();
return $cmdname eq 'add' ? [] : [ PVE::Network::SDN::Subnets::sdn_subnets_ids($cfg) ];
}
sub get_subnet {
my ($subnetid, $running) = @_;
my $cfg = {};
if($running) {
my $cfg = PVE::Network::SDN::running_config();
$cfg = $cfg->{subnets};
} else {
$cfg = PVE::Network::SDN::Subnets::config();
}
my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $subnetid, 1);
return $subnet;
}
sub find_ip_subnet {
my ($ip, $mask, $subnets) = @_;
my $subnet = undef;
my $subnetid = undef;
foreach my $id (sort keys %{$subnets}) {
next if $mask ne $subnets->{$id}->{mask};
my $cidr = $subnets->{$id}->{cidr};
my $subnet_matcher = subnet_matcher($cidr);
next if !$subnet_matcher->($ip);
$subnet = $subnets->{$id};
$subnetid = $id;
last;
}
die "can't find any subnet for ip $ip" if !$subnet;
return ($subnetid, $subnet);
}
sub verify_dns_zone {
my ($zone, $dns) = @_;
return if !$zone || !$dns;
my $dns_cfg = PVE::Network::SDN::Dns::config();
my $plugin_config = $dns_cfg->{ids}->{$dns};
my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
$plugin->verify_zone($plugin_config, $zone);
}
sub get_reversedns_zone {
my ($subnetid, $subnet, $dns, $ip) = @_;
return if !$subnetid || !$dns || !$ip;
my $dns_cfg = PVE::Network::SDN::Dns::config();
my $plugin_config = $dns_cfg->{ids}->{$dns};
my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
$plugin->get_reversedns_zone($plugin_config, $subnetid, $subnet, $ip);
}
sub add_dns_record {
my ($zone, $dns, $hostname, $ip) = @_;
return if !$zone || !$dns || !$hostname || !$ip;
my $dns_cfg = PVE::Network::SDN::Dns::config();
my $plugin_config = $dns_cfg->{ids}->{$dns};
my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
$plugin->add_a_record($plugin_config, $zone, $hostname, $ip);
}
sub add_dns_ptr_record {
my ($reversezone, $zone, $dns, $hostname, $ip) = @_;
return if !$zone || !$reversezone || !$dns || !$hostname || !$ip;
$hostname .= ".$zone";
my $dns_cfg = PVE::Network::SDN::Dns::config();
my $plugin_config = $dns_cfg->{ids}->{$dns};
my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
$plugin->add_ptr_record($plugin_config, $reversezone, $hostname, $ip);
}
sub del_dns_record {
my ($zone, $dns, $hostname, $ip) = @_;
return if !$zone || !$dns || !$hostname || !$ip;
my $dns_cfg = PVE::Network::SDN::Dns::config();
my $plugin_config = $dns_cfg->{ids}->{$dns};
my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
$plugin->del_a_record($plugin_config, $zone, $hostname, $ip);
}
sub del_dns_ptr_record {
my ($reversezone, $dns, $ip) = @_;
return if !$reversezone || !$dns || !$ip;
my $dns_cfg = PVE::Network::SDN::Dns::config();
my $plugin_config = $dns_cfg->{ids}->{$dns};
my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
$plugin->del_ptr_record($plugin_config, $reversezone, $ip);
}
sub add_subnet {
my ($zone, $subnetid, $subnet) = @_;
my $ipam = $zone->{ipam};
return if !$ipam;
my $ipam_cfg = PVE::Network::SDN::Ipams::config();
my $plugin_config = $ipam_cfg->{ids}->{$ipam};
my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
$plugin->add_subnet($plugin_config, $subnetid, $subnet);
}
sub del_subnet {
my ($zone, $subnetid, $subnet) = @_;
my $ipam = $zone->{ipam};
return if !$ipam;
my $ipam_cfg = PVE::Network::SDN::Ipams::config();
my $plugin_config = $ipam_cfg->{ids}->{$ipam};
my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
$plugin->del_subnet($plugin_config, $subnetid, $subnet);
}
sub next_free_ip {
my ($zone, $subnetid, $subnet, $hostname, $mac, $description, $skipdns) = @_;
my $cidr = undef;
my $ip = undef;
$description = '' if !$description;
my $ipamid = $zone->{ipam};
my $dns = $zone->{dns};
my $dnszone = $zone->{dnszone};
my $reversedns = $zone->{reversedns};
my $dnszoneprefix = $subnet->{dnszoneprefix};
$hostname .= ".$dnszoneprefix" if $dnszoneprefix;
#verify dns zones before ipam
verify_dns_zone($dnszone, $dns) if !$skipdns;
if($ipamid) {
my $ipam_cfg = PVE::Network::SDN::Ipams::config();
my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
eval {
$cidr = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet, $hostname, $mac, $description);
($ip, undef) = split(/\//, $cidr);
};
die $@ if $@;
}
eval {
my $reversednszone = get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
if(!$skipdns) {
#add dns
add_dns_record($dnszone, $dns, $hostname, $ip);
#add reverse dns
add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $ip);
}
};
if ($@) {
#rollback
my $err = $@;
eval {
PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname)
};
die $err;
}
return $cidr;
}
sub add_ip {
my ($zone, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $skipdns) = @_;
return if !$subnet || !$ip;
my $ipaddr = NetAddr::IP->new($ip);
$ip = $ipaddr->canon();
my $ipamid = $zone->{ipam};
my $dns = $zone->{dns};
my $dnszone = $zone->{dnszone};
my $reversedns = $zone->{reversedns};
my $reversednszone = get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
my $dnszoneprefix = $subnet->{dnszoneprefix};
$hostname .= ".$dnszoneprefix" if $dnszoneprefix;
#verify dns zones before ipam
if(!$skipdns) {
verify_dns_zone($dnszone, $dns);
verify_dns_zone($reversednszone, $reversedns);
}
if ($ipamid) {
my $ipam_cfg = PVE::Network::SDN::Ipams::config();
my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
eval {
$plugin->add_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway);
};
die $@ if $@;
}
eval {
if(!$skipdns) {
#add dns
add_dns_record($dnszone, $dns, $hostname, $ip);
#add reverse dns
add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $ip);
}
};
if ($@) {
#rollback
my $err = $@;
eval {
PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname)
};
die $err;
}
}
sub update_ip {
my ($zone, $subnetid, $subnet, $ip, $hostname, $oldhostname, $mac, $description, $skipdns) = @_;
return if !$subnet || !$ip;
my $ipaddr = NetAddr::IP->new($ip);
$ip = $ipaddr->canon();
my $ipamid = $zone->{ipam};
my $dns = $zone->{dns};
my $dnszone = $zone->{dnszone};
my $reversedns = $zone->{reversedns};
my $reversednszone = get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
my $dnszoneprefix = $subnet->{dnszoneprefix};
$hostname .= ".$dnszoneprefix" if $dnszoneprefix;
#verify dns zones before ipam
if(!$skipdns) {
verify_dns_zone($dnszone, $dns);
verify_dns_zone($reversednszone, $reversedns);
}
if ($ipamid) {
my $ipam_cfg = PVE::Network::SDN::Ipams::config();
my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
eval {
$plugin->update_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description);
};
die $@ if $@;
}
return if $hostname eq $oldhostname;
eval {
if(!$skipdns) {
#add dns
del_dns_record($dnszone, $dns, $oldhostname, $ip);
add_dns_record($dnszone, $dns, $hostname, $ip);
#add reverse dns
del_dns_ptr_record($reversednszone, $reversedns, $ip);
add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $ip);
}
};
}
sub del_ip {
my ($zone, $subnetid, $subnet, $ip, $hostname, $skipdns) = @_;
return if !$subnet || !$ip;
my $ipaddr = NetAddr::IP->new($ip);
$ip = $ipaddr->canon();
my $ipamid = $zone->{ipam};
my $dns = $zone->{dns};
my $dnszone = $zone->{dnszone};
my $reversedns = $zone->{reversedns};
my $reversednszone = get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
my $dnszoneprefix = $subnet->{dnszoneprefix};
$hostname .= ".$dnszoneprefix" if $dnszoneprefix;
if(!$skipdns) {
verify_dns_zone($dnszone, $dns);
verify_dns_zone($reversednszone, $reversedns);
}
if ($ipamid) {
my $ipam_cfg = PVE::Network::SDN::Ipams::config();
my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
$plugin->del_ip($plugin_config, $subnetid, $subnet, $ip);
}
eval {
if(!$skipdns) {
del_dns_record($dnszone, $dns, $hostname, $ip);
del_dns_ptr_record($reversednszone, $reversedns, $ip);
}
};
if ($@) {
warn $@;
}
}
1;