diff options
Diffstat (limited to 'src/PVE/Network/SDN/Subnets.pm')
| -rw-r--r-- | src/PVE/Network/SDN/Subnets.pm | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/src/PVE/Network/SDN/Subnets.pm b/src/PVE/Network/SDN/Subnets.pm new file mode 100644 index 0000000..6bb42e5 --- /dev/null +++ b/src/PVE/Network/SDN/Subnets.pm @@ -0,0 +1,375 @@ +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; |
