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();
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 sdn_subnets_ids {
my ($cfg) = @_;
- return keys %{$cfg->{ids}};
+ return sort keys %{$cfg->{ids}};
}
sub complete_sdn_subnet {
}
sub get_subnet {
- my ($subnetid) = @_;
+ 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 $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;