pve-network/src/PVE/Network/SDN/Vnets.pm
Lou Lecrivain 7603197666 vnet: do not skip if no range is defined, ask for allocation inside prefix instead
Signed-off-by: lou lecrivain <lou.lecrivain@wdz.de>
Tested-by: Stefan Hanreich <s.hanreich@proxmox.com>
Reviewed-by: Stefan Hanreich <s.hanreich@proxmox.com>
Tested-by: Hannes Duerr <h.duerr@proxmox.com>
Link: https://lore.proxmox.com/mailman.181.1734119196.332.pve-devel@lists.proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-07 17:58:17 +02:00

217 lines
5.9 KiB
Perl

package PVE::Network::SDN::Vnets;
use strict;
use warnings;
use Net::IP;
use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
use PVE::Network::SDN;
use PVE::Network::SDN::Dhcp;
use PVE::Network::SDN::Subnets;
use PVE::Network::SDN::Zones;
use PVE::Network::SDN::VnetPlugin;
PVE::Network::SDN::VnetPlugin->register();
PVE::Network::SDN::VnetPlugin->init();
sub sdn_vnets_config {
my ($cfg, $id, $noerr) = @_;
die "no sdn vnet ID specified\n" if !$id;
my $scfg = $cfg->{ids}->{$id};
die "sdn vnet '$id' does not exist\n" if (!$noerr && !$scfg);
return $scfg;
}
sub config {
my ($running) = @_;
if ($running) {
my $cfg = PVE::Network::SDN::running_config();
return $cfg->{vnets};
}
return cfs_read_file("sdn/vnets.cfg");
}
sub write_config {
my ($cfg) = @_;
cfs_write_file("sdn/vnets.cfg", $cfg);
}
sub sdn_vnets_ids {
my ($cfg) = @_;
return sort keys %{$cfg->{ids}};
}
sub complete_sdn_vnet {
my ($cmdname, $pname, $cvalue) = @_;
my $cfg = PVE::Network::SDN::Vnets::config();
return $cmdname eq 'add' ? [] : [ PVE::Network::SDN::Vnets::sdn_vnet_ids($cfg) ];
}
sub get_vnet {
my ($vnetid, $running) = @_;
return if !$vnetid;
my $cfg = PVE::Network::SDN::Vnets::config($running);
return PVE::Network::SDN::Vnets::sdn_vnets_config($cfg, $vnetid, 1);
}
sub get_subnets {
my ($vnetid, $running) = @_;
my $subnets = undef;
my $subnets_cfg = PVE::Network::SDN::Subnets::config($running);
foreach my $subnetid (sort keys %{$subnets_cfg->{ids}}) {
my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($subnets_cfg, $subnetid);
next if !$subnet->{vnet} || ($vnetid && $subnet->{vnet} ne $vnetid);
$subnets->{$subnetid} = $subnet;
}
return $subnets;
}
sub get_subnet_from_vnet_ip {
my ($vnetid, $ip) = @_;
my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
my $zoneid = $vnet->{zone};
my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets);
return ($zone, $subnetid, $subnet, $ip);
}
sub add_next_free_cidr {
my ($vnetid, $hostname, $mac, $vmid, $skipdns, $dhcprange, $ipversion) = @_;
my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
return if !$vnet;
my $zoneid = $vnet->{zone};
my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
return if !$zone->{ipam} || !$zone->{dhcp};
my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
my $ips = {};
my @ipversions = defined($ipversion) ? ($ipversion) : qw/ 4 6 /;
for my $ipversion (@ipversions) {
my $ip = undef;
my $subnetcount = 0;
foreach my $subnetid (sort keys %{$subnets}) {
my $subnet = $subnets->{$subnetid};
my $network = $subnet->{network};
next if Net::IP::ip_get_version($network) != $ipversion || $ips->{$ipversion};
$subnetcount++;
eval {
$ip = PVE::Network::SDN::Subnets::add_next_free_ip($zone, $subnetid, $subnet, $hostname, $mac, $vmid, $skipdns, $subnet->{'dhcp-range'});
};
die $@ if $@;
if ($ip) {
$ips->{$ipversion} = $ip;
last;
}
}
if (!$ip && $subnetcount > 0) {
foreach my $version (sort keys %{$ips}) {
my $ip = $ips->{$version};
my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets);
PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname, $mac, $skipdns);
}
die "can't find any free ip in zone $zoneid for IPv$ipversion";
}
}
}
sub add_ip {
my ($vnetid, $ip, $hostname, $mac, $vmid, $skipdns) = @_;
return if !$vnetid;
my ($zone, $subnetid, $subnet) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_ip($vnetid, $ip);
PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, undef, $skipdns);
}
sub update_ip {
my ($vnetid, $ip, $hostname, $oldhostname, $mac, $vmid, $skipdns) = @_;
return if !$vnetid;
my ($zone, $subnetid, $subnet) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_ip($vnetid, $ip);
PVE::Network::SDN::Subnets::update_ip($zone, $subnetid, $subnet, $ip, $hostname, $oldhostname, $mac, $vmid, $skipdns);
}
sub del_ip {
my ($vnetid, $ip, $hostname, $mac, $skipdns) = @_;
return if !$vnetid;
my ($zone, $subnetid, $subnet) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_ip($vnetid, $ip);
PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname, $mac, $skipdns);
}
sub get_ips_from_mac {
my ($vnetid, $mac) = @_;
my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
return if !$vnet;
my $zoneid = $vnet->{zone};
my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
return if !$zone->{ipam} || !$zone->{dhcp};
return PVE::Network::SDN::Ipams::get_ips_from_mac($mac, $zoneid, $zone);
}
sub del_ips_from_mac {
my ($vnetid, $mac, $hostname) = @_;
my ($ip4, $ip6) = PVE::Network::SDN::Vnets::get_ips_from_mac($vnetid, $mac);
PVE::Network::SDN::Vnets::del_ip($vnetid, $ip4, $hostname, $mac) if $ip4;
PVE::Network::SDN::Vnets::del_ip($vnetid, $ip6, $hostname, $mac) if $ip6;
return ($ip4, $ip6);
}
sub add_dhcp_mapping {
my ($vnetid, $mac, $vmid, $name) = @_;
my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
return if !$vnet;
my $zoneid = $vnet->{zone};
my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
return if !$zone->{ipam} || !$zone->{dhcp};
my ($ip4, $ip6) = PVE::Network::SDN::Vnets::get_ips_from_mac($vnetid, $mac);
add_next_free_cidr($vnetid, $name, $mac, "$vmid", undef, 1, 4) if ! $ip4;
add_next_free_cidr($vnetid, $name, $mac, "$vmid", undef, 1, 6) if ! $ip6;
($ip4, $ip6) = PVE::Network::SDN::Vnets::get_ips_from_mac($vnetid, $mac);
PVE::Network::SDN::Dhcp::add_mapping($vnetid, $mac, $ip4, $ip6) if $ip4 || $ip6;
}
1;