From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id EF6747B364 for ; Wed, 12 May 2021 08:56:30 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id D7C6592C4 for ; Wed, 12 May 2021 08:56:00 +0200 (CEST) Received: from kvmformation3.odiso.net (globalOdiso.M6Lille.odiso.net [89.248.211.242]) by firstgate.proxmox.com (Proxmox) with ESMTP id 0160792AF for ; Wed, 12 May 2021 08:55:58 +0200 (CEST) Received: by kvmformation3.odiso.net (Postfix, from userid 0) id AAB18A0B37; Wed, 12 May 2021 08:55:50 +0200 (CEST) From: Alexandre Derumier To: pve-devel@lists.proxmox.com Date: Wed, 12 May 2021 08:55:49 +0200 Message-Id: <20210512065549.830358-1-aderumier@odiso.com> X-Mailer: git-send-email 2.20.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 1 AWL -0.159 Adjusted score from AWL reputation of From: address HEADER_FROM_DIFFERENT_DOMAINS 0.248 From and EnvelopeFrom 2nd level mail domains are different KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment KAM_LAZY_DOMAIN_SECURITY 1 Sending domain does not have any anti-forgery methods KHOP_HELO_FCRDNS 0.398 Relay HELO differs from its IP's reverse DNS NO_DNS_FOR_FROM 0.379 Envelope sender has no MX or A DNS records SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_NONE 0.001 SPF: sender does not publish an SPF Record Subject: [pve-devel] [PATCH pve-network] add vnets test + ipam fixes X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 12 May 2021 06:56:31 -0000 - 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 Signed-off-by: Alexandre Derumier --- 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