[pve-devel] [PATCH v10 pve-network 31/35] subnets/ipam: allow same subnet on different zones

Alexandre Derumier aderumier at odiso.com
Mon Oct 5 17:09:08 CEST 2020


Signed-off-by: Alexandre Derumier <aderumier at odiso.com>
---
 PVE/API2/Network/SDN/Subnets.pm        |  13 ++--
 PVE/API2/Network/SDN/Vnets.pm          |  14 ++--
 PVE/API2/Network/SDN/Zones.pm          |  11 +++
 PVE/Network/SDN/Dns/PowerdnsPlugin.pm  |   6 +-
 PVE/Network/SDN/Ipams/NetboxPlugin.pm  |  18 ++---
 PVE/Network/SDN/Ipams/PVEPlugin.pm     | 102 ++++++++++++++++---------
 PVE/Network/SDN/Ipams/PhpIpamPlugin.pm |  20 ++---
 PVE/Network/SDN/Ipams/Plugin.pm        |   6 +-
 PVE/Network/SDN/SubnetPlugin.pm        |  26 +++++--
 PVE/Network/SDN/Subnets.pm             |  28 ++++---
 PVE/Network/SDN/VnetPlugin.pm          |  31 ++++----
 PVE/Network/SDN/Vnets.pm               |  18 +++--
 PVE/Network/SDN/Zones/EvpnPlugin.pm    |   3 +-
 PVE/Network/SDN/Zones/SimplePlugin.pm  |   5 +-
 14 files changed, 188 insertions(+), 113 deletions(-)

diff --git a/PVE/API2/Network/SDN/Subnets.pm b/PVE/API2/Network/SDN/Subnets.pm
index 5ea4fc4..0fb9420 100644
--- a/PVE/API2/Network/SDN/Subnets.pm
+++ b/PVE/API2/Network/SDN/Subnets.pm
@@ -28,7 +28,6 @@ my $api_sdn_subnets_config = sub {
 
     my $scfg = dclone(PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id));
     $scfg->{subnet} = $id;
-    $scfg->{cidr} = $id =~ s/-/\//r;
     $scfg->{digest} = $cfg->{digest};
 
     return $scfg;
@@ -169,7 +168,6 @@ __PACKAGE__->register_method ({
 
 	my $type = extract_param($param, 'type');
 	my $cidr = extract_param($param, 'subnet');
-	my $id = $cidr =~ s/\//-/r;
 
         # create /etc/pve/sdn directory
         PVE::Cluster::check_cfs_quorum();
@@ -184,7 +182,9 @@ __PACKAGE__->register_method ({
 		my $vnet = $param->{vnet};
 		my $zoneid = $vnet_cfg->{ids}->{$vnet}->{zone};
 		my $zone = $zone_cfg->{ids}->{$zoneid};      
-
+		my $id = $cidr =~ s/\//-/r;
+		$id = "$zoneid-$id";
+		
 		my $opts = PVE::Network::SDN::SubnetPlugin->check_config($id, $param, 1, 1);
 
 		my $scfg = undef;
@@ -193,7 +193,9 @@ __PACKAGE__->register_method ({
 		}
 
 		$cfg->{ids}->{$id} = $opts;
-		PVE::Network::SDN::SubnetPlugin->on_update_hook($zone, $id, $opts);
+
+		my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id);
+		PVE::Network::SDN::SubnetPlugin->on_update_hook($zone, $id, $subnet);
 
 		PVE::Network::SDN::Subnets::write_config($cfg);
 
@@ -238,7 +240,8 @@ __PACKAGE__->register_method ({
 
 	    raise_param_exc({ ipam => "you can't change ipam"}) if $opts->{ipam} && $scfg->{ipam} && $opts->{ipam} ne $scfg->{ipam};
 
-	    PVE::Network::SDN::SubnetPlugin->on_update_hook($zone, $id, $opts, $scfg);
+	    my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id);
+	    PVE::Network::SDN::SubnetPlugin->on_update_hook($zone, $id, $subnet);
 
 	    PVE::Network::SDN::Subnets::write_config($cfg);
 
diff --git a/PVE/API2/Network/SDN/Vnets.pm b/PVE/API2/Network/SDN/Vnets.pm
index 6ff61c5..3f99f58 100644
--- a/PVE/API2/Network/SDN/Vnets.pm
+++ b/PVE/API2/Network/SDN/Vnets.pm
@@ -19,6 +19,7 @@ use PVE::API2::Network::SDN::Subnets;
 use Storable qw(dclone);
 use PVE::JSONSchema qw(get_standard_option);
 use PVE::RPCEnvironment;
+use PVE::Exception qw(raise raise_param_exc);
 
 use PVE::RESTHandler;
 
@@ -195,9 +196,7 @@ __PACKAGE__->register_method ({
 	    my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
             $plugin->vnet_update_hook($cfg->{ids}->{$id});
 
-	    my $subnet_cfg = PVE::Network::SDN::Subnets::config();
-
-	    PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg, $subnet_cfg);
+	    PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg);
 
 	    PVE::Network::SDN::Vnets::write_config($cfg);
 
@@ -228,7 +227,12 @@ __PACKAGE__->register_method ({
 
 	    PVE::SectionConfig::assert_if_modified($cfg, $digest);
 
+
 	    my $opts = PVE::Network::SDN::VnetPlugin->check_config($id, $param, 0, 1);
+	    raise_param_exc({ zone => "missing zone"}) if !$opts->{zone};
+	    my $subnets = PVE::Network::SDN::Vnets::get_subnets($id);
+	    raise_param_exc({ zone => "can't change zone if subnets exists"}) if($subnets && $opts->{zone} ne $cfg->{ids}->{$id}->{zone});
+
 	    $cfg->{ids}->{$id} = $opts;
 
 	    my $zone_cfg = PVE::Network::SDN::Zones::config();
@@ -237,9 +241,7 @@ __PACKAGE__->register_method ({
 	    my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
 	    $plugin->vnet_update_hook($cfg->{ids}->{$id});
 
-	    my $subnet_cfg = PVE::Network::SDN::Subnets::config();
-
-	    PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg, $subnet_cfg);
+	    PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg);
 
 	    PVE::Network::SDN::Vnets::write_config($cfg);
 
diff --git a/PVE/API2/Network/SDN/Zones.pm b/PVE/API2/Network/SDN/Zones.pm
index 54f087d..5ae577b 100644
--- a/PVE/API2/Network/SDN/Zones.pm
+++ b/PVE/API2/Network/SDN/Zones.pm
@@ -9,6 +9,7 @@ use PVE::Cluster qw(cfs_read_file cfs_write_file);
 use PVE::Network::SDN;
 use PVE::Network::SDN::Vnets;
 use PVE::Network::SDN::Zones;
+use PVE::Network::SDN::Subnets;
 use PVE::Network::SDN::Dns;
 use PVE::Network::SDN::Zones::Plugin;
 use PVE::Network::SDN::Zones::VlanPlugin;
@@ -263,6 +264,16 @@ __PACKAGE__->register_method ({
 	    my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($scfg->{type});
 	    my $opts = $plugin->check_config($id, $param, 0, 1);
 
+	    if($opts->{ipam} ne $scfg->{ipam}) {
+
+		#don't allow ipam change if subnet are defined
+		my $subnets_cfg = PVE::Network::SDN::Subnets::config();
+		foreach my $subnetid (sort keys %{$subnets_cfg->{ids}}) {
+		    my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($subnets_cfg, $subnetid);
+		    raise_param_exc({ ipam => "can't change ipam if subnet if already defined for this zone"}) if $subnet->{zone} eq $id;
+		}
+	    }
+
 	    foreach my $k (%$opts) {
 		$scfg->{$k} = $opts->{$k};
 	    }
diff --git a/PVE/Network/SDN/Dns/PowerdnsPlugin.pm b/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
index 5b98e87..b00432e 100644
--- a/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
+++ b/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
@@ -186,11 +186,11 @@ sub verify_zone {
 }
 
 sub get_reversedns_zone {
-    my ($class, $plugin_config, $subnetid, $ip) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
 
-    my ($network, $mask) = split(/-/, $subnetid);
+    my $cidr = $subnet->{cidr};
+    my $mask = $subnet->{mask};
 
-    my $cidr = "$ip/$mask";
     my $zone = "";
 
     if (Net::IP::ip_is_ipv4($ip)) {
diff --git a/PVE/Network/SDN/Ipams/NetboxPlugin.pm b/PVE/Network/SDN/Ipams/NetboxPlugin.pm
index c25f451..8695b7d 100644
--- a/PVE/Network/SDN/Ipams/NetboxPlugin.pm
+++ b/PVE/Network/SDN/Ipams/NetboxPlugin.pm
@@ -30,7 +30,7 @@ sub options {
 sub add_subnet {
     my ($class, $plugin_config, $subnetid, $subnet) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
     my $gateway = $subnet->{gateway};
     my $url = $plugin_config->{url};
     my $token = $plugin_config->{token};
@@ -40,13 +40,11 @@ sub add_subnet {
 
     #create subnet
     if (!$internalid) {
-	my ($network, $mask) = split(/-/, $subnetid);
 
 	my $params = { prefix => $cidr };
 
 	eval {
 		my $result = PVE::Network::SDN::Ipams::Plugin::api_request("POST", "$url/ipam/prefixes/", $headers, $params);
-		$subnet->{ipamid} = $result->{id} if defined($result->{id});
 	};
 	if ($@) {
 	    die "error add subnet to ipam: $@";
@@ -58,7 +56,7 @@ sub add_subnet {
 sub del_subnet {
     my ($class, $plugin_config, $subnetid, $subnet) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
     my $url = $plugin_config->{url};
     my $token = $plugin_config->{token};
     my $gateway = $subnet->{gateway};
@@ -66,9 +64,8 @@ sub del_subnet {
 
     my $internalid = get_prefix_id($url, $cidr, $headers);
     return if !$internalid;
-    #fixme: check that prefix is empty exluding gateway, before delete
 
-    PVE::Network::SDN::Ipams::NetboxPlugin::del_ip($class, $plugin_config, $subnetid, $gateway) if $gateway;
+    return; #fixme: check that prefix is empty exluding gateway, before delete
 
     eval {
 	PVE::Network::SDN::Ipams::Plugin::api_request("DELETE", "$url/ipam/prefixes/$internalid/", $headers);
@@ -80,9 +77,9 @@ sub del_subnet {
 }
 
 sub add_ip {
-    my ($class, $plugin_config, $subnetid, $ip, $is_gateway) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $is_gateway) = @_;
 
-    my ($network, $mask) = split(/-/, $subnetid);
+    my $mask = $subnet->{mask};
     my $url = $plugin_config->{url};
     my $token = $plugin_config->{token};
     my $section = $plugin_config->{section};
@@ -102,7 +99,8 @@ sub add_ip {
 sub add_next_freeip {
     my ($class, $plugin_config, $subnetid, $subnet) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
+
     my $url = $plugin_config->{url};
     my $token = $plugin_config->{token};
     my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
@@ -125,7 +123,7 @@ sub add_next_freeip {
 }
 
 sub del_ip {
-    my ($class, $plugin_config, $subnetid, $ip) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
 
     return if !$ip;
 
diff --git a/PVE/Network/SDN/Ipams/PVEPlugin.pm b/PVE/Network/SDN/Ipams/PVEPlugin.pm
index a3ad3d6..601ad26 100644
--- a/PVE/Network/SDN/Ipams/PVEPlugin.pm
+++ b/PVE/Network/SDN/Ipams/PVEPlugin.pm
@@ -7,6 +7,7 @@ use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_register_file cfs_lock_file
 use PVE::Tools;
 use JSON;
 use NetAddr::IP;
+use Net::IP;
 use Digest::SHA;
 
 use base('PVE::Network::SDN::Ipams::Plugin');
@@ -33,15 +34,22 @@ sub options {
 sub add_subnet {
     my ($class, $plugin_config, $subnetid, $subnet) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
+    my $zone = $subnet->{zone};
     my $gateway = $subnet->{gateway};
 
+
     cfs_lock_file($ipamdb_file, undef, sub {
-	my $config = read_db();
-	#create subnet
-	if (!defined($config->{subnets}->{$cidr})) {
-	    $config->{subnets}->{$cidr}->{ips} = {};
-	    write_db($config);
+	my $db = {};
+	$db = read_db();
+
+	$db->{zones}->{$zone} = {} if !$db->{zones}->{$zone};
+	my $zonedb = $db->{zones}->{$zone};
+
+	if(!$zonedb->{subnets}->{$cidr}) {
+	    #create subnet
+	    $zonedb->{subnets}->{$cidr}->{ips} = {};
+	    write_db($db);
 	}
     });
     die "$@" if $@;
@@ -50,14 +58,19 @@ sub add_subnet {
 sub del_subnet {
     my ($class, $plugin_config, $subnetid, $subnet) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
+    my $zone = $subnet->{zone};
 
     cfs_lock_file($ipamdb_file, undef, sub {
 
 	my $db = read_db();
-	my $ips = $db->{subnets}->{$cidr}->{ips};
+	die "zone $zone don't exist in ipam db" if !$db->{zones}->{$zone};
+	my $dbzone = $db->{zones}->{$zone};
+	die "subnet $cidr don't exist in ipam db" if !$dbzone->{subnets}->{$cidr};
+	my $dbsubnet = $dbzone->{subnets}->{$cidr};
+	my $ips = $dbsubnet->{ips};
 	die "can't delete subnet $cidr , not empty" if keys %{$ips} > 0;
-	delete $db->{subnets}->{$cidr};
+	delete $dbzone->{subnets}->{$cidr};
 	write_db($db);
     });
     die "$@" if $@;
@@ -65,19 +78,22 @@ sub del_subnet {
 }
 
 sub add_ip {
-    my ($class, $plugin_config, $subnetid, $ip, $is_gateway) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $is_gateway) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
+    my $zone = $subnet->{zone};
 
     cfs_lock_file($ipamdb_file, undef, sub {
 
 	my $db = read_db();
-	my $s = $db->{subnets}->{$cidr};
 
-	die "ip $ip already exist" if defined($s->{ips}->{$ip});
+	die "zone $zone don't exist in ipam db" if !$db->{zones}->{$zone};
+	my $dbzone = $db->{zones}->{$zone};
+	die "subnet $cidr don't exist in ipam db" if !$dbzone->{subnets}->{$cidr};
+	my $dbsubnet = $dbzone->{subnets}->{$cidr};
 
-	#verify that ip is valid for this subnet
-	$s->{ips}->{$ip} = 1;
+	die "ip $ip already exist" if defined($dbsubnet->{ips}->{$ip});
+	$dbsubnet->{ips}->{$ip} = 1;
 	write_db($db);
     });
     die "$@" if $@;
@@ -86,49 +102,65 @@ sub add_ip {
 sub add_next_freeip {
     my ($class, $plugin_config, $subnetid, $subnet) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
+    my $network = $subnet->{network};
+    my $zone = $subnet->{zone};
+    my $mask = $subnet->{mask};
     my $freeip = undef;
 
     cfs_lock_file($ipamdb_file, undef, sub {
 
 	my $db = read_db();
-	my $s = $db->{subnets}->{$cidr};
-	my $iplist = new NetAddr::IP($cidr);
-	my $broadcast = $iplist->broadcast();
-
-	while(1) {
-	    $iplist++;
-	    last if $iplist eq $broadcast;
-	    my $ip = $iplist->addr();
-	    next if defined($s->{ips}->{$ip});
-	    $freeip = $ip;
-	    last;
+	die "zone $zone don't exist in ipam db" if !$db->{zones}->{$zone};
+        my $dbzone = $db->{zones}->{$zone};
+	die "subnet $cidr don't exist in ipam db" if !$dbzone->{subnets}->{$cidr};
+	my $dbsubnet = $dbzone->{subnets}->{$cidr};
+
+	if (Net::IP::ip_is_ipv4($network) && $mask == 32) {
+	    die "can't find free ip in subnet $cidr" if defined($dbsubnet->{ips}->{$network});
+	    $freeip = $network;
+	} else {
+
+	    my $iplist = new NetAddr::IP($cidr);
+	    my $broadcast = $iplist->broadcast();
+
+	    while(1) {
+		$iplist++;
+		last if $iplist eq $broadcast;
+		my $ip = $iplist->addr();
+		next if defined($dbsubnet->{ips}->{$ip});
+		$freeip = $ip;
+		last;
+	    }
 	}
 
 	die "can't find free ip in subnet $cidr" if !$freeip;
   
-	$s->{ips}->{$freeip} = 1;
+	$dbsubnet->{ips}->{$freeip} = 1;
 	write_db($db);
     });
     die "$@" if $@;
 
-    my ($network, $mask) = split(/-/, $subnetid);
     return "$freeip/$mask";
 }
 
 sub del_ip {
-    my ($class, $plugin_config, $subnetid, $ip) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
+    my $zone = $subnet->{zone};
 
     cfs_lock_file($ipamdb_file, undef, sub {
 
 	my $db = read_db();
-	my $s = $db->{subnets}->{$cidr};
-	return if !$ip;
+	die "zone $zone don't exist in ipam db" if !$db->{zones}->{$zone};
+        my $dbzone = $db->{zones}->{$zone};
+	die "subnet $cidr don't exist in ipam db" if !$dbzone->{subnets}->{$cidr};
+	my $dbsubnet = $dbzone->{subnets}->{$cidr};
+
+	die "ip $ip does not exist in pam" if !defined($dbsubnet->{ips}->{$ip});
+	delete $dbsubnet->{ips}->{$ip};
 
-	die "ip $ip does not exist in pam" if !defined($s->{ips}->{$ip});
-	delete $s->{ips}->{$ip};
 	write_db($db);
     });
     die "$@" if $@;
diff --git a/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm b/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
index d7ba3ed..324f1b2 100644
--- a/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
+++ b/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
@@ -40,7 +40,10 @@ sub options {
 sub add_subnet {
     my ($class, $plugin_config, $subnetid, $subnet) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
+    my $network = $subnet->{network};
+    my $mask = $subnet->{mask};
+
     my $gateway = $subnet->{gateway};
     my $url = $plugin_config->{url};
     my $token = $plugin_config->{token};
@@ -52,7 +55,6 @@ sub add_subnet {
 
     #create subnet
     if (!$internalid) {
-	my ($network, $mask) = split(/-/, $subnetid);
 
 	my $params = { subnet => $network,
 		   mask => $mask,
@@ -72,7 +74,7 @@ sub add_subnet {
 sub del_subnet {
     my ($class, $plugin_config, $subnetid, $subnet) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
     my $url = $plugin_config->{url};
     my $token = $plugin_config->{token};
     my $section = $plugin_config->{section};
@@ -81,7 +83,7 @@ sub del_subnet {
     my $internalid = get_internalid($url, $cidr, $headers);
     return if !$internalid;
 
-    #fixme: check that prefix is empty exluding gateway, before delete
+    return; #fixme: check that prefix is empty exluding gateway, before delete
 
     eval {
 	PVE::Network::SDN::Ipams::Plugin::api_request("DELETE", "$url/subnets/$internalid", $headers);
@@ -93,9 +95,9 @@ sub del_subnet {
 }
 
 sub add_ip {
-    my ($class, $plugin_config, $subnetid, $ip, $is_gateway) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $is_gateway) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
     my $url = $plugin_config->{url};
     my $token = $plugin_config->{token};
     my $section = $plugin_config->{section};
@@ -120,7 +122,8 @@ sub add_ip {
 sub add_next_freeip {
     my ($class, $plugin_config, $subnetid, $subnet, $internalid, $hostname) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};  
+    my $mask = $subnet->{mask};  
     my $url = $plugin_config->{url};
     my $token = $plugin_config->{token};
     my $section = $plugin_config->{section};
@@ -140,12 +143,11 @@ sub add_next_freeip {
         die "can't find free ip in subnet $cidr: $@";
     }
 
-    my ($network, $mask) = split(/-/, $subnetid);
     return "$ip/$mask";
 }
 
 sub del_ip {
-    my ($class, $plugin_config, $subnetid, $ip) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
 
     return if !$ip;
 
diff --git a/PVE/Network/SDN/Ipams/Plugin.pm b/PVE/Network/SDN/Ipams/Plugin.pm
index 683346c..a2ade3b 100644
--- a/PVE/Network/SDN/Ipams/Plugin.pm
+++ b/PVE/Network/SDN/Ipams/Plugin.pm
@@ -75,16 +75,16 @@ sub del_subnet {
 }
 
 sub add_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $internalid, $ip, $hostname, $is_gateway) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $is_gateway) = @_;
 
 }
 
 sub add_next_freeip {
-    my ($class, $plugin_config) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet) = @_;
 }
 
 sub del_ip {
-    my ($class, $plugin_config, $subnetid, $ip) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
 }
 
 
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index a5d03f6..1444262 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -10,6 +10,7 @@ use PVE::Exception qw(raise raise_param_exc);
 use Net::Subnet qw(subnet_matcher);
 use PVE::Network::SDN::Vnets;
 use PVE::Network::SDN::Ipams;
+use Net::IP;
 
 PVE::Cluster::cfs_register_file('sdn/subnets.cfg',
                                  sub { __PACKAGE__->parse_config(@_); },
@@ -25,7 +26,13 @@ PVE::JSONSchema::register_format('pve-sdn-subnet-id', \&parse_sdn_subnet_id);
 sub parse_sdn_subnet_id {
     my ($id, $noerr) = @_;
 
-    my $cidr = $id =~ s/-/\//r;
+    my $cidr = "";
+    if($id =~ /\//) {
+	$cidr = $id;
+    } else {
+	my ($zone, $ip, $mask) = split(/-/, $id);
+	$cidr = "$ip/$mask";
+    }
 
     if (!(PVE::JSONSchema::pve_verify_cidrv4($cidr, 1) ||
           PVE::JSONSchema::pve_verify_cidrv6($cidr, 1)))
@@ -91,7 +98,9 @@ sub options {
 sub on_update_hook {
     my ($class, $zone, $subnetid, $subnet, $old_subnet) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
+    my $mask = $subnet->{mask};
+
     my $subnet_matcher = subnet_matcher($cidr);
 
     my $vnetid = $subnet->{vnet};
@@ -109,9 +118,11 @@ sub on_update_hook {
 	raise_param_exc({ vnet => "you can't add a subnet on a vlanaware vnet"}) if $vnet->{vlanaware};
     }
 
-    my ($ip, $mask) = split(/\//, $cidr);
+    my $pointopoint = 1 if Net::IP::ip_is_ipv4($gateway) && $mask == 32;
+
     #for /32 pointopoint, we allow gateway outside the subnet
-    raise_param_exc({ gateway => "$gateway is not in subnet $subnetid"}) if $gateway && !$subnet_matcher->($gateway) && $mask != 32;
+    raise_param_exc({ gateway => "$gateway is not in subnet $cidr"}) if $gateway && !$subnet_matcher->($gateway) && !$pointopoint;
+
 
     if ($ipam) {
 	my $ipam_cfg = PVE::Network::SDN::Ipams::config();
@@ -119,7 +130,10 @@ sub on_update_hook {
 	my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
 	$plugin->add_subnet($plugin_config, $subnetid, $subnet);
 
-	#delete on removal
+	#don't register gateway for pointopoint
+	return if $pointopoint;
+
+	#delete gateway on removal
 	if (!defined($gateway) && $old_gateway) {
 	    eval {
 		PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $old_subnet, $old_gateway);
@@ -130,7 +144,7 @@ sub on_update_hook {
 	    PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $gateway);
 	}
 
-	#delete old ip after update
+	#delete old gateway after update
 	if($gateway && $old_gateway && $gateway ne $old_gateway) {
 	    eval {
 		PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $old_subnet, $old_gateway);
diff --git a/PVE/Network/SDN/Subnets.pm b/PVE/Network/SDN/Subnets.pm
index 50130d5..bd1eb36 100644
--- a/PVE/Network/SDN/Subnets.pm
+++ b/PVE/Network/SDN/Subnets.pm
@@ -21,6 +21,14 @@ sub sdn_subnets_config {
     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;
 }
 
@@ -64,13 +72,15 @@ sub get_subnet {
 }
 
 sub find_ip_subnet {
-    my ($ip, $subnets) = @_;
+    my ($ip, $mask, $subnets) = @_;
 
     my $subnet = undef;
     my $subnetid = undef;
 
     foreach my $id (sort keys %{$subnets}) {
-	my $cidr = $id =~ s/-/\//r;
+
+	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};
@@ -94,14 +104,14 @@ my $verify_dns_zone = sub {
 };
 
 my $get_reversedns_zone = sub {
-    my ($subnetid, $dns, $ip) = @_;
+    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, $ip);
+    $plugin->get_reversedns_zone($plugin_config, $subnetid, $subnet, $ip);
 };
 
 my $add_dns_record = sub {
@@ -181,7 +191,7 @@ sub next_free_ip {
     }
 
     eval {
-	my $reversednszone = &$get_reversedns_zone($subnetid, $reversedns, $ip);
+	my $reversednszone = &$get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
 
 	#add dns
 	&$add_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
@@ -208,7 +218,7 @@ sub add_ip {
     my $dns = $zone->{dns};
     my $dnszone = $zone->{dnszone};
     my $reversedns = $zone->{reversedns};
-    my $reversednszone = &$get_reversedns_zone($subnetid, $reversedns, $ip);
+    my $reversednszone = &$get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
     my $dnszoneprefix = $subnet->{dnszoneprefix};
 
     #verify dns zones before ipam
@@ -220,7 +230,7 @@ sub add_ip {
 	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, $ip);
+	    $plugin->add_ip($plugin_config, $subnetid, $subnet, $ip);
 	};
 	die $@ if $@;
     }
@@ -250,7 +260,7 @@ sub del_ip {
     my $dns = $zone->{dns};
     my $dnszone = $zone->{dnszone};
     my $reversedns = $zone->{reversedns};
-    my $reversednszone = &$get_reversedns_zone($subnetid, $reversedns, $ip);
+    my $reversednszone = &$get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
     my $dnszoneprefix = $subnet->{dnszoneprefix};
 
     &$verify_dns_zone($dnszone, $dns);
@@ -260,7 +270,7 @@ sub del_ip {
 	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, $ip);
+	$plugin->del_ip($plugin_config, $subnetid, $subnet, $ip);
     }
 
     eval {
diff --git a/PVE/Network/SDN/VnetPlugin.pm b/PVE/Network/SDN/VnetPlugin.pm
index 8481f0d..518d2dd 100644
--- a/PVE/Network/SDN/VnetPlugin.pm
+++ b/PVE/Network/SDN/VnetPlugin.pm
@@ -91,29 +91,28 @@ sub on_delete_hook {
 
     #verify if subnets are associated
     my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
-    my @subnetlist = ();
-    foreach my $subnetid (sort keys %{$subnets}) {
-	push @subnetlist, $subnetid;
-    }
-    raise_param_exc({ vnet => "Vnet is attached to following subnets:". join(',', @subnetlist)}) if @subnetlist > 0;
+    raise_param_exc({ vnet => "Can't delete vnet if subnets exists"}) if $subnets;
 }
 
 sub on_update_hook {
-    my ($class, $vnetid, $vnet_cfg, $subnet_cfg) = @_;
+    my ($class, $vnetid, $vnet_cfg) = @_;
+
+    my $vnet = $vnet_cfg->{ids}->{$vnetid};
+    my $tag = $vnet->{tag};
+    my $vlanaware = $vnet->{vlanaware};
+
+    #don't allow vlanaware change if subnets are defined
+    if($vnet->{vlanaware}) {
+	my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
+	raise_param_exc({ vlanaware => "vlanaware vnet is not compatible with subnets"}) if $subnets;
+    }
 
-    #fixme : don't allow change zone if subnets are defined
-    #fixme : don't vlanaware change if subnets are defined
-#    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
-   
     # verify that tag is not already defined in another vnet
-    if (defined($vnet_cfg->{ids}->{$vnetid}->{tag})) {
-	my $tag = $vnet_cfg->{ids}->{$vnetid}->{tag};
+    if (defined($tag)) {
 	foreach my $id (keys %{$vnet_cfg->{ids}}) {
 	    next if $id eq $vnetid;
-	    my $vnet = $vnet_cfg->{ids}->{$id};
-	    if ($vnet->{type} eq 'vnet' && defined($vnet->{tag})) {
-		raise_param_exc({ tag => "tag $tag already exist in vnet $id"}) if $tag eq $vnet->{tag};
-	    }
+	    my $othervnettag = $vnet_cfg->{ids}->{$id}->{tag};
+	    raise_param_exc({ tag => "tag $tag already exist in vnet $id"}) if $othervnettag && $tag eq $othervnettag;
 	}
     }
 }
diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
index d08db51..6d11003 100644
--- a/PVE/Network/SDN/Vnets.pm
+++ b/PVE/Network/SDN/Vnets.pm
@@ -66,10 +66,10 @@ sub get_vnet {
 sub get_subnets {
     my ($vnetid) = @_;
 
-    my $subnets = {};
+    my $subnets = undef;
     my $subnets_cfg = PVE::Network::SDN::Subnets::config();
     foreach my $subnetid (sort keys %{$subnets_cfg->{ids}}) {
-	my $subnet = $subnets_cfg->{ids}->{$subnetid};
+	my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($subnets_cfg, $subnetid);
 	next if !$subnet->{vnet} || $subnet->{vnet} ne $vnetid;
 	$subnets->{$subnetid} = $subnet;
     }
@@ -77,7 +77,7 @@ sub get_subnets {
 
 }
 
-sub get_next_free_ip {
+sub get_next_free_cidr {
     my ($vnetid, $hostname, $ipversion) = @_;
 
     my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
@@ -91,7 +91,7 @@ sub get_next_free_ip {
 
     foreach my $subnetid (sort keys %{$subnets}) {
         my $subnet = $subnets->{$subnetid};
-	my ($network, $mask) = split(/-/, $subnetid);
+	my $network = $subnet->{network};
 
 	next if $ipversion != Net::IP::ip_get_version($network);
 	$subnetcount++;
@@ -108,7 +108,7 @@ sub get_next_free_ip {
     return $ip;
 }
 
-sub add_ip {
+sub add_cidr {
     my ($vnetid, $cidr, $hostname) = @_;
 
     my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
@@ -117,12 +117,13 @@ sub add_ip {
     my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
 
     my ($ip, $mask) = split(/\//, $cidr);
-    my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets);
+    die "ip address is not in cidr format" if !$mask;
+    my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $mask, $subnets);
 
     PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip, $hostname);
 }
 
-sub del_ip {
+sub del_cidr {
     my ($vnetid, $cidr, $hostname) = @_;
 
     my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
@@ -131,7 +132,8 @@ sub del_ip {
     my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
 
     my ($ip, $mask) = split(/\//, $cidr);
-    my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets);
+    die "ip address is not in cidr format" if !$mask;
+    my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $mask, $subnets);
 
     PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname);
 }
diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
index 723b3cc..62ab817 100644
--- a/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -86,7 +86,8 @@ sub generate_sdn_config {
     my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
     foreach my $subnetid (sort keys %{$subnets}) {
 	my $subnet = $subnets->{$subnetid};
-	my $cidr = $subnetid =~ s/-/\//r;
+	my $cidr = $subnet->{cidr};
+
 	my $gateway = $subnet->{gateway};
 	if ($gateway) {
 	    push @iface_config, "address $gateway" if !defined($address->{$gateway});
diff --git a/PVE/Network/SDN/Zones/SimplePlugin.pm b/PVE/Network/SDN/Zones/SimplePlugin.pm
index fe0f20f..5294485 100644
--- a/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -60,14 +60,15 @@ sub generate_sdn_config {
     my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
     foreach my $subnetid (sort keys %{$subnets}) {
 	my $subnet = $subnets->{$subnetid};
-	my $cidr = $subnetid =~ s/-/\//r; 
+	my $cidr = $subnet->{cidr};
+	my $mask = $subnet->{mask};
+
 	my $gateway = $subnet->{gateway};
 	if ($gateway) {
 	    push @iface_config, "address $gateway" if !defined($address->{$gateway});
 	    $address->{$gateway} = 1;
 	}
 	#add route for /32 pointtopoint
-	my ($ip, $mask) = split(/\//, $cidr);
 	push @iface_config, "up ip route add $cidr dev $vnetid" if $mask == 32;
 	if ($subnet->{snat}) {
 	    #find outgoing interface
-- 
2.20.1





More information about the pve-devel mailing list