[pve-devel] [pve-network 2/4] add vnets test + ipam fixes

Alexandre Derumier aderumier at odiso.com
Fri Jun 4 13:24:58 CEST 2021


- add vnets tests with multiple subnets
- fix pve ipam ipv6 with next_free_ip (ipv6 don't have network && broadcast address)
- fix vnet next_free_ip with no ipam
---
 PVE/Network/SDN/Ipams/PVEPlugin.pm |  13 +-
 PVE/Network/SDN/Vnets.pm           |  13 +-
 test/run_test_subnets.pl           |  16 +-
 test/run_test_vnets.pl             | 355 +++++++++++++++++++++++++++++
 test/vnets/ipv4/ipam.db            |  17 ++
 test/vnets/ipv4/ipam_config        |   7 +
 test/vnets/ipv4/sdn_config         |  26 +++
 test/vnets/ipv4noipam/ipam.db      |  17 ++
 test/vnets/ipv4noipam/ipam_config  |   7 +
 test/vnets/ipv4noipam/sdn_config   |  26 +++
 test/vnets/ipv6/ipam.db            |  16 ++
 test/vnets/ipv6/ipam_config        |   7 +
 test/vnets/ipv6/sdn_config         |  26 +++
 13 files changed, 526 insertions(+), 20 deletions(-)
 create mode 100755 test/run_test_vnets.pl
 create mode 100644 test/vnets/ipv4/ipam.db
 create mode 100644 test/vnets/ipv4/ipam_config
 create mode 100644 test/vnets/ipv4/sdn_config
 create mode 100644 test/vnets/ipv4noipam/ipam.db
 create mode 100644 test/vnets/ipv4noipam/ipam_config
 create mode 100644 test/vnets/ipv4noipam/sdn_config
 create mode 100644 test/vnets/ipv6/ipam.db
 create mode 100644 test/vnets/ipv6/ipam_config
 create mode 100644 test/vnets/ipv6/sdn_config

diff --git a/PVE/Network/SDN/Ipams/PVEPlugin.pm b/PVE/Network/SDN/Ipams/PVEPlugin.pm
index 7e2fb77..8fe5bbb 100644
--- a/PVE/Network/SDN/Ipams/PVEPlugin.pm
+++ b/PVE/Network/SDN/Ipams/PVEPlugin.pm
@@ -90,7 +90,6 @@ sub add_ip {
     cfs_lock_file($ipamdb_file, undef, sub {
 
 	my $db = read_db();
-
 	my $dbzone = $db->{zones}->{$zone};
 	die "zone '$zone' doesn't exist in IPAM DB\n" if !$dbzone;
 	my $dbsubnet = $dbzone->{subnets}->{$cidr};
@@ -132,13 +131,15 @@ sub add_next_freeip {
 	    $freeip = $network;
 	} else {
 	    my $iplist = NetAddr::IP->new($cidr);
-	    my $broadcast = $iplist->broadcast();
-
+	    my $lastip = $iplist->last()->canon();
+	    $iplist++ if Net::IP::ip_is_ipv4($network); #skip network address for ipv4
 	    while(1) {
-		$iplist++;
-		last if $iplist eq $broadcast;
 		my $ip = $iplist->canon();
-		next if defined($dbsubnet->{ips}->{$ip});
+		if (defined($dbsubnet->{ips}->{$ip})) {
+		    last if $ip eq $lastip;
+		    $iplist++;
+		    next;
+		} 
 		$freeip = $ip;
 		last;
 	    }
diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
index 9d9b155..8c9629d 100644
--- a/PVE/Network/SDN/Vnets.pm
+++ b/PVE/Network/SDN/Vnets.pm
@@ -102,6 +102,8 @@ sub get_next_free_cidr {
     my $zoneid = $vnet->{zone};
     my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
 
+    return if !$zone->{ipam};
+
     $ipversion = 4 if !$ipversion;
     my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
     my $ip = undef;
@@ -113,12 +115,11 @@ sub get_next_free_cidr {
 
 	next if $ipversion != Net::IP::ip_get_version($network);
 	$subnetcount++;
-	if ($zone->{ipam}) {
-	    eval {
-		$ip = PVE::Network::SDN::Subnets::next_free_ip($zone, $subnetid, $subnet, $hostname, $mac, $description);
-	    };
-	    warn $@ if $@;
-	}
+
+	eval {
+	    $ip = PVE::Network::SDN::Subnets::next_free_ip($zone, $subnetid, $subnet, $hostname, $mac, $description);
+	};
+	warn $@ if $@;
 	last if $ip;
     }
     die "can't find any free ip" if !$ip && $subnetcount > 0;
diff --git a/test/run_test_subnets.pl b/test/run_test_subnets.pl
index 364baa6..9fca202 100755
--- a/test/run_test_subnets.pl
+++ b/test/run_test_subnets.pl
@@ -69,7 +69,7 @@ foreach my $path (@plugins) {
 
     my $subnet_cidr = $subnet->{cidr};
     my $iplist = NetAddr::IP->new($subnet_cidr);
-    $iplist++;
+    $iplist++ if Net::IP::ip_is_ipv4($iplist->canon()); #skip network address for ipv4
     my $ip = $iplist->canon();
     $iplist++;
     my $ipnextfree = $iplist->canon();
@@ -112,7 +112,7 @@ foreach my $path (@plugins) {
     );
 
     ## add_subnet
-    my $test = "add_subnet";
+    my $test = "add_subnet $subnetid";
     my $name = "$testid $test";
     my $result = undef;
     my $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{}}}}}}';
@@ -132,7 +132,7 @@ foreach my $path (@plugins) {
     }
 
     ## add_ip
-    $test = "add_ip";
+    $test = "add_ip $ip";
     $name = "$testid $test";
     $result = undef;
     $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ip.'":{}}}}}}}';
@@ -152,7 +152,7 @@ foreach my $path (@plugins) {
 
     if($ipam) {
 	## add_already_exist_ip
-	$test = "add_already_exist_ip";
+	$test = "add_already_exist_ip $ip";
 	$name = "$testid $test";
 
 	eval {
@@ -167,7 +167,7 @@ foreach my $path (@plugins) {
     }
 
     ## add_second_ip
-    $test = "add_second_ip";
+    $test = "add_second_ip $ip2";
     $name = "$testid $test";
     $result = undef;
     $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ip.'":{},"'.$ip2.'":{}}}}}}}';
@@ -186,7 +186,7 @@ foreach my $path (@plugins) {
     }
 
     ## add_next_free
-    $test = "add_next_freeip";
+    $test = "find_next_freeip ($ipnextfree)";
     $name = "$testid $test";
     $result = undef;
     $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ip.'":{},"'.$ipnextfree.'":{},"'.$ip2.'":{}}}}}}}';
@@ -203,7 +203,7 @@ foreach my $path (@plugins) {
     }
 
     ## del_ip
-    $test = "del_ip";
+    $test = "del_ip $ip";
     $name = "$testid $test";
     $result = undef;
     $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ipnextfree.'":{},"'.$ip2.'":{}}}}}}}';
@@ -223,7 +223,7 @@ foreach my $path (@plugins) {
 
     if($ipam){
 	## del_subnet_not_empty
-	$test = "del_subnet_not_empty";
+	$test = "del_subnet_not_empty $subnetid";
 	$name = "$testid $test";
 	$result = undef;
 	$expected = undef;
diff --git a/test/run_test_vnets.pl b/test/run_test_vnets.pl
new file mode 100755
index 0000000..5aeb676
--- /dev/null
+++ b/test/run_test_vnets.pl
@@ -0,0 +1,355 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use lib qw(..);
+use File::Slurp;
+use NetAddr::IP qw(:lower);
+
+use Test::More;
+use Test::MockModule;
+
+use PVE::Network::SDN;
+use PVE::Network::SDN::Zones;
+use PVE::Network::SDN::Controllers;
+use PVE::INotify;
+use JSON;
+
+use Data::Dumper qw(Dumper);
+$Data::Dumper::Sortkeys = 1;
+
+sub read_sdn_config {
+    my ($file) = @_;
+    # Read structure back in again
+    open my $in, '<', $file or die $!;
+    my $sdn_config;
+    {
+	local $/;    # slurp mode
+	$sdn_config = eval <$in>;
+    }
+    close $in;
+    return $sdn_config;
+}
+
+
+my @plugins = read_dir( './vnets/', prefix => 1 ) ;
+
+foreach my $path (@plugins) {
+
+    my (undef, $testid) = split(/\//, $path);
+
+    print "test: $testid\n";
+    my $sdn_config = read_sdn_config ("$path/sdn_config");
+
+    my $pve_sdn_zones;
+    $pve_sdn_zones = Test::MockModule->new('PVE::Network::SDN::Zones');
+    $pve_sdn_zones->mock(
+	config => sub {
+	    return $sdn_config->{zones};
+	},
+    );
+
+    my $pve_sdn_vnets;
+    $pve_sdn_vnets = Test::MockModule->new('PVE::Network::SDN::Vnets');
+    $pve_sdn_vnets->mock(
+	config => sub {
+	    return $sdn_config->{vnets};
+	},
+    );
+
+    my $pve_sdn_subnets;
+    $pve_sdn_subnets = Test::MockModule->new('PVE::Network::SDN::Subnets');
+    $pve_sdn_subnets->mock(
+	config => sub {
+	    return $sdn_config->{subnets};
+	},
+	verify_dns_zone => sub {
+	    return;
+	},
+	add_dns_record => sub {
+	    return;
+	}
+    );
+
+    my $js = JSON->new;
+    $js->canonical(1);
+  
+    #test params;
+    #test params;
+    my $subnets = $sdn_config->{subnets}->{ids};
+
+    my $subnetid = (sort keys %{$subnets})[0];
+    my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($sdn_config->{subnets}, $subnetid, 1);
+    my $subnet_cidr = $subnet->{cidr};
+    my $iplist = NetAddr::IP->new($subnet_cidr);
+    my $mask = $iplist->masklen();
+    my $ipversion = undef;
+
+    if (Net::IP::ip_is_ipv4($iplist->canon())){
+	$iplist++; #skip network address for ipv4
+	$ipversion = 4;
+    } else { 
+	$ipversion = 6;
+    }
+
+    my $cidr1 = $iplist->canon()."/$mask";
+    $iplist++;
+    my $cidr2 = $iplist->canon()."/$mask";
+    my $cidr_outofrange = '8.8.8.8/8';
+
+    my $subnetid2 = (sort keys %{$subnets})[1];
+    my $subnet2 = PVE::Network::SDN::Subnets::sdn_subnets_config($sdn_config->{subnets}, $subnetid2, 1);
+    my $subnet2_cidr = $subnet2->{cidr};
+    my $iplist2 = NetAddr::IP->new($subnet2_cidr);
+    $iplist2++;
+    my $cidr3 = $iplist2->canon()."/$mask";
+    $iplist2++;
+    my $cidr4 = $iplist2->canon()."/$mask";
+
+    my $hostname = "myhostname";
+    my $mac = "da:65:8f:18:9b:6f";
+    my $description = "mydescription";
+    my $ipamdb = read_sdn_config ("$path/ipam.db");
+
+    my $zone = $sdn_config->{zones}->{ids}->{"myzone"};
+    my $ipam = $zone->{ipam};
+
+    my $plugin;
+    my $sdn_ipam_plugin;
+    if($ipam) {
+	$plugin = PVE::Network::SDN::Ipams::Plugin->lookup($ipam);
+	$sdn_ipam_plugin = Test::MockModule->new($plugin);
+	$sdn_ipam_plugin->mock(
+	    read_db => sub {
+		return $ipamdb;
+	    },
+	    write_db => sub {
+		my ($cfg) = @_;
+		$ipamdb = $cfg;
+	    }
+	);
+    }
+
+    my $pve_sdn_ipams;
+    $pve_sdn_ipams = Test::MockModule->new('PVE::Network::SDN::Ipams');
+    $pve_sdn_ipams->mock(
+	config => sub {
+	    my $ipam_config = read_sdn_config ("$path/ipam_config");
+	    return $ipam_config;
+	},
+    );
+
+    my $vnetid = "myvnet";
+
+    ## add_ip
+    my $test = "add_cidr $cidr1";
+    my $name = "$testid $test";
+    my $result = undef;
+    my $expected = '';
+
+    eval {
+	PVE::Network::SDN::Vnets::add_cidr($vnetid, $cidr1, $hostname, $mac, $description);
+    };
+
+    if ($@) {
+        fail("$name : $@");
+    } else {
+        is (undef, undef, $name);
+    }
+
+    ## add_ip
+    $test = "add_already_exist_cidr $cidr1";
+    $name = "$testid $test";
+    $result = undef;
+    $expected = '';
+
+    eval {
+	PVE::Network::SDN::Vnets::add_cidr($vnetid, $cidr1, $hostname, $mac, $description);
+    };
+
+    if ($@) {
+        is (undef, undef, $name);
+    } elsif($ipam) {
+        fail("$name : $@");
+    } else {
+        is (undef, undef, $name);
+    }
+
+    ## add_ip
+    $test = "add_cidr $cidr2";
+    $name = "$testid $test";
+    $result = undef;
+    $expected = '';
+
+    eval {
+	PVE::Network::SDN::Vnets::add_cidr($vnetid, $cidr2, $hostname, $mac, $description);
+    };
+
+    if ($@) {
+        fail("$name : $@");
+    } else {
+        is (undef, undef, $name);
+    }
+
+    ## add_ip
+    $test = "add_ip_out_of_range_subnets $cidr_outofrange";
+    $name = "$testid $test";
+    $result = undef;
+    $expected = '';
+
+    eval {
+	PVE::Network::SDN::Vnets::add_cidr($vnetid, $cidr_outofrange, $hostname, $mac, $description);
+    };
+
+    if ($@) {
+        is (undef, undef, $name);
+    } else {
+        fail("$name : $@");
+    }
+
+    ## add_ip
+    $test = "add_cidr $cidr4";
+    $name = "$testid $test";
+    $result = undef;
+    $expected = '';
+
+    eval {
+	PVE::Network::SDN::Vnets::add_cidr($vnetid, $cidr4, $hostname, $mac, $description);
+    };
+
+    if ($@) {
+        fail("$name : $@");
+    } else {
+        is (undef, undef, $name);
+    }
+
+
+    $test = "find_next_free_cidr_in_second_subnet ($cidr3)";
+    $name = "$testid $test";
+    $result = undef;
+    $expected = $ipam ? $cidr3 : undef;
+
+    eval {
+	$result = PVE::Network::SDN::Vnets::get_next_free_cidr($vnetid, $hostname, $mac, $description, $ipversion);
+    };
+
+    if ($@) {
+        fail("$name : $@");
+    } else {
+        is ($result, $expected, $name);
+    }
+
+
+    $test = "del_cidr $cidr1";
+    $name = "$testid $test";
+    $result = undef;
+    $expected = undef;
+
+    eval {
+	$result = PVE::Network::SDN::Vnets::del_cidr($vnetid, $cidr1, $hostname);
+    };
+
+    if ($@) {
+        fail("$name : $@");
+    } else {
+        is (undef, undef, $name);
+    }
+
+    $test = "del_cidr $cidr3";
+    $name = "$testid $test";
+    $result = undef;
+    $expected = undef;
+
+    eval {
+	$result = PVE::Network::SDN::Vnets::del_cidr($vnetid, $cidr3, $hostname);
+    };
+
+    if ($@) {
+        fail("$name : $@");
+    } else {
+        is (undef, undef, $name);
+    }
+
+    $test = "del_cidr not exist $cidr1";
+    $name = "$testid $test";
+    $result = undef;
+    $expected = undef;
+
+    eval {
+	$result = PVE::Network::SDN::Vnets::del_cidr($vnetid, $cidr1, $hostname);
+    };
+
+    if ($@) {
+        is (undef, undef, $name);
+    } elsif($ipam) {
+        fail("$name : $@");
+    } else {
+        is (undef, undef, $name);
+    }
+
+    $test = "del_cidr outofrange $cidr_outofrange";
+    $name = "$testid $test";
+    $result = undef;
+    $expected = undef;
+
+    eval {
+	$result = PVE::Network::SDN::Vnets::del_cidr($vnetid, $cidr_outofrange, $hostname);
+    };
+
+    if ($@) {
+        is (undef, undef, $name);
+    } else {
+        fail("$name : $@");
+    }
+
+    $test = "find_next_free_cidr_in_first_subnet ($cidr1)";
+    $name = "$testid $test";
+    $result = undef;
+    $expected = $ipam ? $cidr1 : undef;
+
+    eval {
+	$result = PVE::Network::SDN::Vnets::get_next_free_cidr($vnetid, $hostname, $mac, $description, $ipversion);
+    };
+
+    if ($@) {
+        fail("$name : $@");
+    } else {
+        is ($result, $expected, $name);
+    }
+
+    $test = "update_cidr $cidr1";
+    $name = "$testid $test";
+    $result = undef;
+    $expected = undef;
+
+    eval {
+	$result = PVE::Network::SDN::Vnets::update_cidr($vnetid, $cidr1, $hostname, $hostname, $mac, $description);
+    };
+
+    if ($@) {
+        fail("$name : $@");
+    } else {
+        is (undef, undef, $name);
+    }
+
+    $test = "update_cidr deleted $cidr3";
+    $name = "$testid $test";
+    $result = undef;
+    $expected = undef;
+
+    eval {
+	$result = PVE::Network::SDN::Vnets::update_cidr($vnetid, $cidr1, $hostname, $hostname, $mac, $description);
+    };
+
+    if ($@) {
+        fail("$name : $@");
+    } else {
+        is (undef, undef, $name);
+    }
+
+}
+
+done_testing();
+
+
diff --git a/test/vnets/ipv4/ipam.db b/test/vnets/ipv4/ipam.db
new file mode 100644
index 0000000..ef3fa93
--- /dev/null
+++ b/test/vnets/ipv4/ipam.db
@@ -0,0 +1,17 @@
+{
+   "zones" => {
+	"myzone" => {
+	    "subnets" => {
+		"192.168.0.0/30" => {
+		    "ips" =>{
+		    }
+		},
+		"192.168.1.0/30" => {
+		    "ips" =>{
+		    }
+		},
+	    }
+	}
+    }
+}
+
diff --git a/test/vnets/ipv4/ipam_config b/test/vnets/ipv4/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/test/vnets/ipv4/ipam_config
@@ -0,0 +1,7 @@
+{
+          'ids' => {
+                     'pve' => {
+                                'type' => 'pve'
+                              },
+                   },
+}
diff --git a/test/vnets/ipv4/sdn_config b/test/vnets/ipv4/sdn_config
new file mode 100644
index 0000000..ee11fd1
--- /dev/null
+++ b/test/vnets/ipv4/sdn_config
@@ -0,0 +1,26 @@
+{
+  version => 1,
+  vnets   => {
+               ids => {
+                        myvnet => { type => "vnet", zone => "myzone" },
+                      },
+             },
+
+  zones   => {
+               ids => { myzone => { ipam => "pve", type =>"simple" } },
+             },
+
+  subnets => {
+              ids => { 
+			'myzone-192.168.0.0-30' => {
+                                                        'type' => 'subnet',
+                                                        'vnet' => 'myvnet',
+                                                  },
+			'myzone-192.168.1.0-30' => {
+                                                        'type' => 'subnet',
+                                                        'vnet' => 'myvnet',
+                                                  },
+                     }
+
+             }
+}
diff --git a/test/vnets/ipv4noipam/ipam.db b/test/vnets/ipv4noipam/ipam.db
new file mode 100644
index 0000000..ef3fa93
--- /dev/null
+++ b/test/vnets/ipv4noipam/ipam.db
@@ -0,0 +1,17 @@
+{
+   "zones" => {
+	"myzone" => {
+	    "subnets" => {
+		"192.168.0.0/30" => {
+		    "ips" =>{
+		    }
+		},
+		"192.168.1.0/30" => {
+		    "ips" =>{
+		    }
+		},
+	    }
+	}
+    }
+}
+
diff --git a/test/vnets/ipv4noipam/ipam_config b/test/vnets/ipv4noipam/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/test/vnets/ipv4noipam/ipam_config
@@ -0,0 +1,7 @@
+{
+          'ids' => {
+                     'pve' => {
+                                'type' => 'pve'
+                              },
+                   },
+}
diff --git a/test/vnets/ipv4noipam/sdn_config b/test/vnets/ipv4noipam/sdn_config
new file mode 100644
index 0000000..470c1ae
--- /dev/null
+++ b/test/vnets/ipv4noipam/sdn_config
@@ -0,0 +1,26 @@
+{
+  version => 1,
+  vnets   => {
+               ids => {
+                        myvnet => { type => "vnet", zone => "myzone" },
+                      },
+             },
+
+  zones   => {
+               ids => { myzone => { type =>"simple" } },
+             },
+
+  subnets => {
+              ids => { 
+			'myzone-192.168.0.0-30' => {
+                                                        'type' => 'subnet',
+                                                        'vnet' => 'myvnet',
+                                                  },
+			'myzone-192.168.1.0-30' => {
+                                                        'type' => 'subnet',
+                                                        'vnet' => 'myvnet',
+                                                  },
+                     }
+
+             }
+}
diff --git a/test/vnets/ipv6/ipam.db b/test/vnets/ipv6/ipam.db
new file mode 100644
index 0000000..d3f2ce9
--- /dev/null
+++ b/test/vnets/ipv6/ipam.db
@@ -0,0 +1,16 @@
+{
+   "zones" => {
+	"myzone" => {
+	    "subnets" => {
+		"2001:db8:85a3::8a2e:370:7334/127" => {
+		    "ips" =>{
+		    }
+		},
+		"2001:db8:85a3::8a2e:371:7334/127" => {
+		    "ips" =>{
+		    }
+		},
+	    }
+	}
+    }
+}
diff --git a/test/vnets/ipv6/ipam_config b/test/vnets/ipv6/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/test/vnets/ipv6/ipam_config
@@ -0,0 +1,7 @@
+{
+          'ids' => {
+                     'pve' => {
+                                'type' => 'pve'
+                              },
+                   },
+}
diff --git a/test/vnets/ipv6/sdn_config b/test/vnets/ipv6/sdn_config
new file mode 100644
index 0000000..231ca8a
--- /dev/null
+++ b/test/vnets/ipv6/sdn_config
@@ -0,0 +1,26 @@
+{
+  version => 1,
+  vnets   => {
+               ids => {
+                        myvnet => { type => "vnet", zone => "myzone" },
+                      },
+             },
+
+  zones   => {
+               ids => { myzone => { ipam => "pve", type =>"simple" } },
+             },
+
+  subnets => {
+              ids => { 
+			'myzone-2001:db8:85a3::8a2e:370:7334-127' => {
+                                                        'type' => 'subnet',
+                                                        'vnet' => 'myvnet',
+                                                  },
+			'myzone-2001:db8:85a3::8a2e:371:7334-127' => {
+                                                        'type' => 'subnet',
+                                                        'vnet' => 'myvnet',
+                                                  },
+                     }
+
+             }
+}
-- 
2.20.1





More information about the pve-devel mailing list