[pve-devel] [RFC pve-network 3/9] vnet|subnet: add_next_free_ip : implement dhcprange ipam search

Alexandre Derumier aderumier at odiso.com
Mon Nov 13 11:04:11 CET 2023


Signed-off-by: Alexandre Derumier <aderumier at odiso.com>
---
 src/PVE/Network/SDN/Ipams/NetboxPlugin.pm | 36 ++++++++++++++++++++
 src/PVE/Network/SDN/Ipams/PVEPlugin.pm    | 12 +++----
 src/PVE/Network/SDN/Ipams/Plugin.pm       |  7 ++++
 src/PVE/Network/SDN/Subnets.pm            | 21 +++++++++---
 src/PVE/Network/SDN/Vnets.pm              | 40 +++++++++++------------
 src/test/run_test_subnets.pl              |  2 +-
 src/test/run_test_vnets.pl                |  4 +--
 7 files changed, 88 insertions(+), 34 deletions(-)

diff --git a/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm b/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm
index f0e7168..2099a7f 100644
--- a/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm
+++ b/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm
@@ -151,6 +151,33 @@ sub add_next_freeip {
     return $ip;
 }
 
+sub add_range_next_freeip {
+    my ($class, $plugin_config, $subnet, $range, $data, $noerr) = @_;
+
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
+
+    my $internalid = get_iprange_id($url, $range, $headers);
+    my $description = "mac:$data->{mac}" if $data->{mac};
+
+    my $params = { dns_name => $data->{hostname}, description => $description };
+
+    my $ip = undef;
+    eval {
+	my $result = PVE::Network::SDN::api_request("POST", "$url/ipam/ip-ranges/$internalid/available-ips/", $headers, $params);
+	$ip = $result->{address};
+	print "found ip free $ip in range $range->{'start-address'}-$range->{'end-address'}\n" if $ip;
+    };
+
+    if ($@) {
+	die "can't find free ip in range $range->{'start-address'}-$range->{'end-address'}: $@" if !$noerr;
+    }
+
+    return $ip;
+
+}
+
 sub del_ip {
     my ($class, $plugin_config, $subnetid, $subnet, $ip, $noerr) = @_;
 
@@ -204,6 +231,15 @@ sub get_prefix_id {
     return $internalid;
 }
 
+sub get_iprange_id {
+    my ($url, $range, $headers) = @_;
+
+    my $result = PVE::Network::SDN::api_request("GET", "$url/ipam/ip-ranges/?start_address=$range->{'start-address'}&end_address=$range->{'end-address'}", $headers);
+    my $data = @{$result->{results}}[0];
+    my $internalid = $data->{id};
+    return $internalid;
+}
+
 sub get_ip_id {
     my ($url, $ip, $headers) = @_;
     my $result = PVE::Network::SDN::api_request("GET", "$url/ipam/ip-addresses/?q=$ip", $headers);
diff --git a/src/PVE/Network/SDN/Ipams/PVEPlugin.pm b/src/PVE/Network/SDN/Ipams/PVEPlugin.pm
index fcc8282..37b47e4 100644
--- a/src/PVE/Network/SDN/Ipams/PVEPlugin.pm
+++ b/src/PVE/Network/SDN/Ipams/PVEPlugin.pm
@@ -110,7 +110,7 @@ sub update_ip {
 }
 
 sub add_next_freeip {
-    my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $description) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $description, $noerr) = @_;
 
     my $cidr = $subnet->{cidr};
     my $network = $subnet->{network};
@@ -156,8 +156,8 @@ sub add_next_freeip {
     return "$freeip/$mask";
 }
 
-sub add_dhcp_ip {
-    my ($class, $subnet, $dhcp_range, $data) = @_;
+sub add_range_next_freeip {
+    my ($class, $plugin_config, $subnet, $range, $data, $noerr) = @_;
 
     my $cidr = $subnet->{cidr};
     my $zone = $subnet->{zone};
@@ -171,8 +171,8 @@ sub add_dhcp_ip {
 	my $dbsubnet = $dbzone->{subnets}->{$cidr};
 	die "subnet '$cidr' doesn't exist in IPAM DB\n" if !$dbsubnet;
 
-	my $ip = new Net::IP ("$dhcp_range->{'start-address'} - $dhcp_range->{'end-address'}")
-	    or die "Invalid IP address(es) in DHCP Range!\n";
+	my $ip = new Net::IP ("$range->{'start-address'} - $range->{'end-address'}")
+	    or die "Invalid IP address(es) in Range!\n";
 
 	do {
 	    my $ip_address = $ip->ip();
@@ -184,7 +184,7 @@ sub add_dhcp_ip {
 	    }
 	} while (++$ip);
 
-	die "No free IP left in DHCP Range $dhcp_range->{'start-address'}:$dhcp_range->{'end-address'}}\n";
+	die "No free IP left in Range $range->{'start-address'}:$range->{'end-address'}}\n";
     });
 }
 
diff --git a/src/PVE/Network/SDN/Ipams/Plugin.pm b/src/PVE/Network/SDN/Ipams/Plugin.pm
index c96eeda..4d85b81 100644
--- a/src/PVE/Network/SDN/Ipams/Plugin.pm
+++ b/src/PVE/Network/SDN/Ipams/Plugin.pm
@@ -98,6 +98,13 @@ sub add_next_freeip {
     die "please implement inside plugin";
 }
 
+
+sub add_range_next_freeip {
+    my ($class, $plugin_config, $subnet, $range, $data, $noerr) = @_;
+
+    die "please implement inside plugin";
+}
+
 sub del_ip {
     my ($class, $plugin_config, $subnetid, $subnet, $ip, $noerr) = @_;
 
diff --git a/src/PVE/Network/SDN/Subnets.pm b/src/PVE/Network/SDN/Subnets.pm
index dd9e697..9f953a6 100644
--- a/src/PVE/Network/SDN/Subnets.pm
+++ b/src/PVE/Network/SDN/Subnets.pm
@@ -202,8 +202,8 @@ sub del_subnet {
     $plugin->del_subnet($plugin_config, $subnetid, $subnet);
 }
 
-sub next_free_ip {
-    my ($zone, $subnetid, $subnet, $hostname, $mac, $description, $skipdns) = @_;
+sub add_next_free_ip {
+    my ($zone, $subnetid, $subnet, $hostname, $mac, $description, $skipdns, $dhcprange) = @_;
 
     my $cidr = undef;
     my $ip = undef;
@@ -225,9 +225,20 @@ sub next_free_ip {
 	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);
+	    if ($dhcprange) {
+		my $data = {
+		    mac => $mac,
+		    hostname => $hostname,
+		};
+		foreach my $range (@{$subnet->{'dhcp-range'}}) { 
+		    $ip = $plugin->add_range_next_freeip($plugin_config, $subnet, $range, $data);
+	            next if !$ip;
+		}
+	    } else {
+		$ip = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet, $hostname, $mac, $description);
+	    }
 	};
+
 	die $@ if $@;
     }
 
@@ -249,7 +260,7 @@ sub next_free_ip {
 	};
 	die $err;
     }
-    return $cidr;
+    return $ip;
 }
 
 sub add_ip {
diff --git a/src/PVE/Network/SDN/Vnets.pm b/src/PVE/Network/SDN/Vnets.pm
index 39bdda0..76a6caf 100644
--- a/src/PVE/Network/SDN/Vnets.pm
+++ b/src/PVE/Network/SDN/Vnets.pm
@@ -96,8 +96,8 @@ sub get_subnet_from_vnet_cidr {
     return ($zone, $subnetid, $subnet, $ip);
 }
 
-sub get_next_free_cidr {
-    my ($vnetid, $hostname, $mac, $description, $ipversion, $skipdns) = @_;
+sub add_next_free_cidr {
+    my ($vnetid, $hostname, $mac, $description, $skipdns, $dhcprange) = @_;
 
     my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
     my $zoneid = $vnet->{zone};
@@ -105,27 +105,27 @@ sub get_next_free_cidr {
 
     return if !$zone->{ipam};
 
-    $ipversion = 4 if !$ipversion;
     my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
-    my $ip = undef;
-    my $subnetcount = 0;
 
-    foreach my $subnetid (sort keys %{$subnets}) {
-        my $subnet = $subnets->{$subnetid};
-	my $network = $subnet->{network};
-
-	next if $ipversion != Net::IP::ip_get_version($network);
-	$subnetcount++;
-
-	eval {
-	    $ip = PVE::Network::SDN::Subnets::next_free_ip($zone, $subnetid, $subnet, $hostname, $mac, $description, $skipdns);
-	};
-	warn $@ if $@;
-	last if $ip;
+    my @ipversions = 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;
+	    $subnetcount++;
+
+	    eval {
+		$ip = PVE::Network::SDN::Subnets::add_next_free_ip($zone, $subnetid, $subnet, $hostname, $mac, $description, $skipdns, $dhcprange);
+	    };
+	    die $@ if $@;
+	    last if $ip;
+	}
+	die "can't find any free ip" if !$ip && $subnetcount > 0;
     }
-    die "can't find any free ip" if !$ip && $subnetcount > 0;
-
-    return $ip;
 }
 
 sub add_cidr {
diff --git a/src/test/run_test_subnets.pl b/src/test/run_test_subnets.pl
index f6564e1..9692f4c 100755
--- a/src/test/run_test_subnets.pl
+++ b/src/test/run_test_subnets.pl
@@ -192,7 +192,7 @@ foreach my $path (@plugins) {
     $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ip.'":{"gateway":1},"'.$ipnextfree.'":{},"'.$ip2.'":{}}}}}}}';
 
     eval {
-	$ip3 = PVE::Network::SDN::Subnets::next_free_ip($zone, $subnetid, $subnet, $hostname, $mac, $description);
+	$ip3 = PVE::Network::SDN::Subnets::add_next_free_ip($zone, $subnetid, $subnet, $hostname, $mac, $description);
     };
 
     if ($@) {
diff --git a/src/test/run_test_vnets.pl b/src/test/run_test_vnets.pl
index 5aeb676..dc9da67 100755
--- a/src/test/run_test_vnets.pl
+++ b/src/test/run_test_vnets.pl
@@ -231,7 +231,7 @@ foreach my $path (@plugins) {
     $expected = $ipam ? $cidr3 : undef;
 
     eval {
-	$result = PVE::Network::SDN::Vnets::get_next_free_cidr($vnetid, $hostname, $mac, $description, $ipversion);
+	$result = PVE::Network::SDN::Vnets::add_next_free_cidr($vnetid, $hostname, $mac, $description);
     };
 
     if ($@) {
@@ -309,7 +309,7 @@ foreach my $path (@plugins) {
     $expected = $ipam ? $cidr1 : undef;
 
     eval {
-	$result = PVE::Network::SDN::Vnets::get_next_free_cidr($vnetid, $hostname, $mac, $description, $ipversion);
+	$result = PVE::Network::SDN::Vnets::add_next_free_cidr($vnetid, $hostname, $mac, $description);
     };
 
     if ($@) {
-- 
2.39.2





More information about the pve-devel mailing list