mirror of
git://git.proxmox.com/git/pve-network.git
synced 2025-04-30 19:27:11 +02:00

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>
376 lines
10 KiB
Perl
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;
|