[pve-devel] [PATCH v2 pve-network 01/15] split transport/controllers/vnet to separate plugins

Alexandre Derumier aderumier at odiso.com
Tue Nov 26 10:00:16 CET 2019


Signed-off-by: Alexandre Derumier <aderumier at odiso.com>
---
 PVE/Network/SDN.pm                            | 297 +-----------------
 PVE/Network/SDN/Controllers.pm                | 158 ++++++++++
 .../FaucetPlugin.pm}                          |  14 +-
 .../FrrEvpnPlugin.pm}                         |  21 +-
 PVE/Network/SDN/Controllers/Makefile          |   8 +
 PVE/Network/SDN/Controllers/Plugin.pm         | 133 ++++++++
 PVE/Network/SDN/Makefile                      |   4 +-
 PVE/Network/SDN/VnetPlugin.pm                 |  52 ++-
 PVE/Network/SDN/Vnets.pm                      |  59 ++++
 PVE/Network/SDN/Zones.pm                      | 227 +++++++++++++
 PVE/Network/SDN/{ => Zones}/EvpnPlugin.pm     |  10 +-
 PVE/Network/SDN/{ => Zones}/FaucetPlugin.pm   |   6 +-
 PVE/Network/SDN/Zones/Makefile                |   8 +
 PVE/Network/SDN/{ => Zones}/Plugin.pm         |  32 +-
 PVE/Network/SDN/{ => Zones}/QinQPlugin.pm     |  16 +-
 PVE/Network/SDN/{ => Zones}/VlanPlugin.pm     |  16 +-
 PVE/Network/SDN/{ => Zones}/VxlanPlugin.pm    |  18 +-
 test/generateconfig.pl                        |  15 +-
 18 files changed, 705 insertions(+), 389 deletions(-)
 create mode 100644 PVE/Network/SDN/Controllers.pm
 rename PVE/Network/SDN/{FaucetControllerPlugin.pm => Controllers/FaucetPlugin.pm} (90%)
 rename PVE/Network/SDN/{EvpnControllerPlugin.pm => Controllers/FrrEvpnPlugin.pm} (95%)
 create mode 100644 PVE/Network/SDN/Controllers/Makefile
 create mode 100644 PVE/Network/SDN/Controllers/Plugin.pm
 create mode 100644 PVE/Network/SDN/Vnets.pm
 create mode 100644 PVE/Network/SDN/Zones.pm
 rename PVE/Network/SDN/{ => Zones}/EvpnPlugin.pm (94%)
 rename PVE/Network/SDN/{ => Zones}/FaucetPlugin.pm (92%)
 create mode 100644 PVE/Network/SDN/Zones/Makefile
 rename PVE/Network/SDN/{ => Zones}/Plugin.pm (79%)
 rename PVE/Network/SDN/{ => Zones}/QinQPlugin.pm (88%)
 rename PVE/Network/SDN/{ => Zones}/VlanPlugin.pm (89%)
 rename PVE/Network/SDN/{ => Zones}/VxlanPlugin.pm (93%)

diff --git a/PVE/Network/SDN.pm b/PVE/Network/SDN.pm
index 96f76d1..4088221 100644
--- a/PVE/Network/SDN.pm
+++ b/PVE/Network/SDN.pm
@@ -6,74 +6,12 @@ use warnings;
 use Data::Dumper;
 use JSON;
 
+use PVE::Network::SDN::Zones;
+
 use PVE::Tools qw(extract_param dir_glob_regex run_command);
 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
-use PVE::Network::SDN::Plugin;
-use PVE::Network::SDN::VnetPlugin;
-use PVE::Network::SDN::VlanPlugin;
-use PVE::Network::SDN::VxlanPlugin;
-use PVE::Network::SDN::FaucetPlugin;
-use PVE::Network::SDN::FaucetControllerPlugin;
-use PVE::Network::SDN::EvpnPlugin;
-use PVE::Network::SDN::EvpnControllerPlugin;
-use PVE::Network::SDN::QinQPlugin;
-
-PVE::Network::SDN::VnetPlugin->register();
-PVE::Network::SDN::VlanPlugin->register();
-PVE::Network::SDN::VxlanPlugin->register();
-PVE::Network::SDN::FaucetControllerPlugin->register();
-PVE::Network::SDN::FaucetPlugin->register();
-PVE::Network::SDN::EvpnPlugin->register();
-PVE::Network::SDN::EvpnControllerPlugin->register();
-PVE::Network::SDN::QinQPlugin->register();
-PVE::Network::SDN::Plugin->init();
-
-
-sub sdn_config {
-    my ($cfg, $sdnid, $noerr) = @_;
-
-    die "no sdn ID specified\n" if !$sdnid;
-
-    my $scfg = $cfg->{ids}->{$sdnid};
-    die "sdn '$sdnid' does not exists\n" if (!$noerr && !$scfg);
-
-    return $scfg;
-}
-
-sub config {
-    my $config = cfs_read_file("sdn.cfg.new");
-    $config = cfs_read_file("sdn.cfg") if !keys %{$config->{ids}};
-    return $config;
-}
-
-sub write_config {
-    my ($cfg) = @_;
-
-    cfs_write_file("sdn.cfg.new", $cfg);
-}
-
-sub lock_sdn_config {
-    my ($code, $errmsg) = @_;
-
-    cfs_lock_file("sdn.cfg.new", undef, $code);
-    if (my $err = $@) {
-        $errmsg ? die "$errmsg: $err" : die $err;
-    }
-}
-
-sub sdn_ids {
-    my ($cfg) = @_;
-
-    return keys %{$cfg->{ids}};
-}
 
-sub complete_sdn {
-    my ($cmdname, $pname, $cvalue) = @_;
-
-    my $cfg = PVE::Network::SDN::config();
-
-    return  $cmdname eq 'add' ? [] : [ PVE::Network::SDN::sdn_ids($cfg) ];
-}
+# improve me : move status code inside plugins ?
 
 sub ifquery_check {
 
@@ -101,236 +39,9 @@ sub ifquery_check {
     return $interfaces;
 }
 
-
-sub generate_etc_network_config {
-
-    my $sdn_cfg = PVE::Cluster::cfs_read_file('sdn.cfg');
-    return if !$sdn_cfg;
-
-    #read main config for physical interfaces
-    my $current_config_file = "/etc/network/interfaces";
-    my $fh = IO::File->new($current_config_file);
-    my $interfaces_config = PVE::INotify::read_etc_network_interfaces(1,$fh);
-    $fh->close();
-
-    #check uplinks
-    my $uplinks = {};
-    foreach my $id (keys %{$interfaces_config->{ifaces}}) {
-	my $interface = $interfaces_config->{ifaces}->{$id};
-	if (my $uplink = $interface->{'uplink-id'}) {
-	    die "uplink-id $uplink is already defined on $uplinks->{$uplink}" if $uplinks->{$uplink};
-	    $interface->{name} = $id;
-	    $uplinks->{$interface->{'uplink-id'}} = $interface;
-	}
-    }
-
-    my $vnet_cfg = undef;
-    my $transport_cfg = undef;
-
-    foreach my $id (keys %{$sdn_cfg->{ids}}) {
-	if ($sdn_cfg->{ids}->{$id}->{type} eq 'vnet') {
-	    $vnet_cfg->{ids}->{$id} = $sdn_cfg->{ids}->{$id};
-	} else {
-	    $transport_cfg->{ids}->{$id} = $sdn_cfg->{ids}->{$id};
-	}
-    }
-
-    #generate configuration
-    my $config = {};
-    foreach my $id (keys %{$vnet_cfg->{ids}}) {
-	my $vnet = $vnet_cfg->{ids}->{$id};
-	my $zone = $vnet->{transportzone};
-
-	if(!$zone) {
-	    warn "can't generate vnet $vnet : zone $zone don't exist";
-	    next;
-	}
-
-	my $plugin_config = $transport_cfg->{ids}->{$zone};
-
-	if (!defined($plugin_config)) {
-	    warn "can't generate vnet $vnet : zone $zone don't exist";
-	    next;
-	}
-
-	my $plugin = PVE::Network::SDN::Plugin->lookup($plugin_config->{type});
-	$plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $uplinks, $config);
-    }
-
-    my $raw_network_config = "";
-    foreach my $iface (keys %$config) {
-	$raw_network_config .= "\n";
-	$raw_network_config .= "auto $iface\n";
-	$raw_network_config .= "iface $iface\n";
-	foreach my $option (@{$config->{$iface}}) {
-	    $raw_network_config .= "\t$option\n";
-	}
-    }
-
-    return $raw_network_config;
-}
-
-sub generate_controller_config {
-
-    my $sdn_cfg = PVE::Cluster::cfs_read_file('sdn.cfg');
-    return if !$sdn_cfg;
-
-    #read main config for physical interfaces
-    my $current_config_file = "/etc/network/interfaces";
-    my $fh = IO::File->new($current_config_file);
-    my $interfaces_config = PVE::INotify::read_etc_network_interfaces(1,$fh);
-    $fh->close();
-
-    #check uplinks
-    my $uplinks = {};
-    foreach my $id (keys %{$interfaces_config->{ifaces}}) {
-	my $interface = $interfaces_config->{ifaces}->{$id};
-	if (my $uplink = $interface->{'uplink-id'}) {
-	    die "uplink-id $uplink is already defined on $uplinks->{$uplink}" if $uplinks->{$uplink};
-	    $interface->{name} = $id;
-	    $uplinks->{$interface->{'uplink-id'}} = $interface;
-	}
-    }
-
-    #generate configuration
-    my $config = {};
-
-    foreach my $id (keys %{$sdn_cfg->{ids}}) {
-	my $plugin_config = $sdn_cfg->{ids}->{$id};
-	my $plugin = PVE::Network::SDN::Plugin->lookup($plugin_config->{type});
-	my $pd = $plugin->plugindata();
-	my $role = $pd->{role};
-	if ($role eq 'controller') {
-	    $plugin->generate_controller_config($plugin_config, $plugin_config, $id, $uplinks, $config);
-	} elsif ($role eq 'transport') {
-	    my $controllerid = $plugin_config->{controller};
-	    if ($controllerid) {
-		my $controller = $sdn_cfg->{ids}->{$controllerid};
-		if ($controller) {
-		    my $controller_plugin = PVE::Network::SDN::Plugin->lookup($controller->{type});
-		    $controller_plugin->generate_controller_transport_config($plugin_config, $controller, $id, $uplinks, $config);
-		}
-	    }
-	} elsif ($role eq 'vnet') {
-	    my $transportid = $plugin_config->{transportzone};
-	    if ($transportid) {
-		my $transport = $sdn_cfg->{ids}->{$transportid};
-		if ($transport) {
-		    my $controllerid = $transport->{controller};
-		    if ($controllerid) {
-			my $controller = $sdn_cfg->{ids}->{$controllerid};
-			if ($controller) {
-			    my $controller_plugin = PVE::Network::SDN::Plugin->lookup($controller->{type});
-			    $controller_plugin->generate_controller_vnet_config($plugin_config, $controller, $transportid, $id, $config);
-			}
-		    }
-		}
-	    }
-	}
-    }
-
-    return $config;
-}
-
-
-sub reload_controller {
-
-    my $sdn_cfg = PVE::Cluster::cfs_read_file('sdn.cfg');
-    return if !$sdn_cfg;
-
-    foreach my $id (keys %{$sdn_cfg->{ids}}) {
-	my $plugin_config = $sdn_cfg->{ids}->{$id};
-	my $plugin = PVE::Network::SDN::Plugin->lookup($plugin_config->{type});
-	my $pd = $plugin->plugindata();
-	my $role = $pd->{role};
-	if ($role eq 'controller') {
-	    $plugin->reload_controller();
-	}
-    }
-}
-
-sub write_etc_network_config {
-    my ($rawconfig) = @_;
-
-    return if !$rawconfig;
-    my $sdn_interfaces_file = "/etc/network/interfaces.d/sdn";
-
-    my $writefh = IO::File->new($sdn_interfaces_file,">");
-    print $writefh $rawconfig;
-    $writefh->close();
-}
-
-sub write_controller_config {
-    my ($config) = @_;
-
-    my $sdn_cfg = PVE::Cluster::cfs_read_file('sdn.cfg');
-    return if !$sdn_cfg;
-
-    foreach my $id (keys %{$sdn_cfg->{ids}}) {
-	my $plugin_config = $sdn_cfg->{ids}->{$id};
-	my $plugin = PVE::Network::SDN::Plugin->lookup($plugin_config->{type});
-	my $pd = $plugin->plugindata();
-	my $role = $pd->{role};
-	if ($role eq 'controller') {
-		$plugin->write_controller_config($plugin_config, $config);
-	}
-    }
-}
-
 sub status {
 
-    my $cluster_sdn_file = "/etc/pve/sdn.cfg";
-    my $local_sdn_file = "/etc/network/interfaces.d/sdn";
-    my $err_config = undef;
-
-    return if !-e $cluster_sdn_file;
-
-    if (!-e $local_sdn_file) {
-	warn "local sdn network configuration is not yet generated, please reload";
-	$err_config = 'pending';
-    } else {
-	# fixme : use some kind of versioning info?
-	my $cluster_sdn_timestamp = (stat($cluster_sdn_file))[9];
-	my $local_sdn_timestamp = (stat($local_sdn_file))[9];
-
-	if ($local_sdn_timestamp < $cluster_sdn_timestamp) {
-	    warn "local sdn network configuration is too old, please reload";
-	    $err_config = 'unknown';
-        }
-    }
-
-    my $status = ifquery_check();
-
-    my $network_cfg = PVE::Cluster::cfs_read_file('sdn.cfg');
-    my $vnet_cfg = undef;
-    my $transport_cfg = undef;
-
-    my $vnet_status = {};
-    my $transport_status = {};
-
-    foreach my $id (keys %{$network_cfg->{ids}}) {
-	if ($network_cfg->{ids}->{$id}->{type} eq 'vnet') {
-	    my $transportzone = $network_cfg->{ids}->{$id}->{transportzone};
-	    $vnet_status->{$id}->{transportzone} = $transportzone;
-	    $transport_status->{$transportzone}->{status} = 'available' if !defined($transport_status->{$transportzone}->{status});
-
-	    if($err_config) {
-		$vnet_status->{$id}->{status} = $err_config;
-		$transport_status->{$transportzone}->{status} = $err_config;
-	    } elsif ($status->{$id}->{status} && $status->{$id}->{status} eq 'pass') {
-		$vnet_status->{$id}->{status} = 'available';
-		my $bridgeport = $status->{$id}->{config}->{'bridge-ports'};
-
-		if ($status->{$bridgeport}->{status} && $status->{$bridgeport}->{status} ne 'pass') {
-		     $vnet_status->{$id}->{status} = 'error';
-		     $transport_status->{$transportzone}->{status} = 'error';
-		}
-	    } else {
-		$vnet_status->{$id}->{status} = 'error';
-		$transport_status->{$transportzone}->{status} = 'error';
-	    }
-	}
-    }
+    my ($transport_status, $vnet_status) = PVE::Network::SDN::Zones::status();
     return($transport_status, $vnet_status);
 }
 
diff --git a/PVE/Network/SDN/Controllers.pm b/PVE/Network/SDN/Controllers.pm
new file mode 100644
index 0000000..19ad15a
--- /dev/null
+++ b/PVE/Network/SDN/Controllers.pm
@@ -0,0 +1,158 @@
+package PVE::Network::SDN::Controllers;
+
+use strict;
+use warnings;
+
+use Data::Dumper;
+use JSON;
+
+use PVE::Tools qw(extract_param dir_glob_regex run_command);
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+
+use PVE::Network::SDN::Vnets;
+use PVE::Network::SDN::Zones;
+
+use PVE::Network::SDN::Controllers::FrrEvpnPlugin;
+use PVE::Network::SDN::Controllers::FaucetPlugin;
+use PVE::Network::SDN::Controllers::Plugin;
+PVE::Network::SDN::Controllers::FrrEvpnPlugin->register();
+PVE::Network::SDN::Controllers::FaucetPlugin->register();
+PVE::Network::SDN::Controllers::Plugin->init();
+
+
+sub sdn_controllers_config {
+    my ($cfg, $id, $noerr) = @_;
+
+    die "no sdn controller ID specified\n" if !$id;
+
+    my $scfg = $cfg->{ids}->{$id};
+    die "sdn '$id' does not exists\n" if (!$noerr && !$scfg);
+
+    return $scfg;
+}
+
+sub config {
+    my $config = cfs_read_file("sdn/controllers.cfg.new");
+    $config = cfs_read_file("sdn/controllers.cfg") if !keys %{$config->{ids}};
+    return $config;
+}
+
+sub write_config {
+    my ($cfg) = @_;
+
+    cfs_write_file("sdn/controllers.cfg.new", $cfg);
+}
+
+sub lock_sdn_controllers_config {
+    my ($code, $errmsg) = @_;
+
+    cfs_lock_file("sdn/controllers.cfg.new", undef, $code);
+    if (my $err = $@) {
+        $errmsg ? die "$errmsg: $err" : die $err;
+    }
+}
+
+sub sdn_controllers_ids {
+    my ($cfg) = @_;
+
+    return keys %{$cfg->{ids}};
+}
+
+sub complete_sdn_controller {
+    my ($cmdname, $pname, $cvalue) = @_;
+
+    my $cfg = PVE::Network::SDN::config();
+
+    return  $cmdname eq 'add' ? [] : [ PVE::Network::SDN::sdn_controllers_ids($cfg) ];
+}
+
+sub generate_controller_config {
+
+    my $vnet_cfg = PVE::Cluster::cfs_read_file('sdn/vnets.cfg');
+    my $transport_cfg = PVE::Cluster::cfs_read_file('sdn/zones.cfg');
+    my $controller_cfg = PVE::Cluster::cfs_read_file('sdn/controllers.cfg');
+    return if !$vnet_cfg && !$transport_cfg && !$controller_cfg;
+
+    #read main config for physical interfaces
+    my $current_config_file = "/etc/network/interfaces";
+    my $fh = IO::File->new($current_config_file);
+    my $interfaces_config = PVE::INotify::read_etc_network_interfaces(1,$fh);
+    $fh->close();
+
+    #check uplinks
+    my $uplinks = {};
+    foreach my $id (keys %{$interfaces_config->{ifaces}}) {
+	my $interface = $interfaces_config->{ifaces}->{$id};
+	if (my $uplink = $interface->{'uplink-id'}) {
+	    die "uplink-id $uplink is already defined on $uplinks->{$uplink}" if $uplinks->{$uplink};
+	    $interface->{name} = $id;
+	    $uplinks->{$interface->{'uplink-id'}} = $interface;
+	}
+    }
+
+    #generate configuration
+    my $config = {};
+
+    foreach my $id (keys %{$controller_cfg->{ids}}) {
+	my $plugin_config = $controller_cfg->{ids}->{$id};
+	my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($plugin_config->{type});
+	$plugin->generate_controller_config($plugin_config, $plugin_config, $id, $uplinks, $config);
+    }
+
+    foreach my $id (keys %{$transport_cfg->{ids}}) {
+	my $plugin_config = $transport_cfg->{ids}->{$id};
+	my $controllerid = $plugin_config->{controller};
+	next if !$controllerid;
+	my $controller = $transport_cfg->{ids}->{$controllerid};
+	if ($controller) {
+	    my $controller_plugin = PVE::Network::SDN::Controllers::Plugin->lookup($controller->{type});
+	    $controller_plugin->generate_controller_transport_config($plugin_config, $controller, $id, $uplinks, $config);
+	}
+    }
+
+    foreach my $id (keys %{$vnet_cfg->{ids}}) {
+	my $plugin_config = $vnet_cfg->{ids}->{$id};
+	my $transportid = $plugin_config->{transportzone};
+	next if !$transportid;
+	my $transport = $transport_cfg->{ids}->{$transportid};
+	next if !$transport;
+	my $controllerid = $transport->{controller};
+	next if !$controllerid;
+	my $controller = $controller_cfg->{ids}->{$controllerid};
+	if ($controller) {
+	    my $controller_plugin = PVE::Network::SDN::Controllers::Plugin->lookup($controller->{type});
+	    $controller_plugin->generate_controller_vnet_config($plugin_config, $controller, $transportid, $id, $config);
+	}
+    }
+
+    return $config;
+}
+
+
+sub reload_controller {
+
+    my $controller_cfg = PVE::Cluster::cfs_read_file('sdn/controllers.cfg');
+    return if !$controller_cfg;
+
+    foreach my $id (keys %{$controller_cfg->{ids}}) {
+	my $plugin_config = $controller_cfg->{ids}->{$id};
+	my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($plugin_config->{type});
+	$plugin->reload_controller();
+    }
+}
+
+sub write_controller_config {
+    my ($config) = @_;
+
+    my $controller_cfg = PVE::Cluster::cfs_read_file('sdn/controllers.cfg');
+    return if !$controller_cfg;
+
+    foreach my $id (keys %{$controller_cfg->{ids}}) {
+	my $plugin_config = $controller_cfg->{ids}->{$id};
+	my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($plugin_config->{type});
+	$plugin->write_controller_config($plugin_config, $config);
+    }
+}
+
+1;
+
diff --git a/PVE/Network/SDN/FaucetControllerPlugin.pm b/PVE/Network/SDN/Controllers/FaucetPlugin.pm
similarity index 90%
rename from PVE/Network/SDN/FaucetControllerPlugin.pm
rename to PVE/Network/SDN/Controllers/FaucetPlugin.pm
index ee15bdf..38f9abf 100644
--- a/PVE/Network/SDN/FaucetControllerPlugin.pm
+++ b/PVE/Network/SDN/Controllers/FaucetPlugin.pm
@@ -1,24 +1,18 @@
-package PVE::Network::SDN::FaucetControllerPlugin;
+package PVE::Network::SDN::Controllers::FaucetPlugin;
 
 use strict;
 use warnings;
-use PVE::Network::SDN::Plugin;
+use PVE::Network::SDN::Controllers::Plugin;
 use PVE::Tools;
 use PVE::INotify;
 use PVE::JSONSchema qw(get_standard_option);
 use CPAN::Meta::YAML;
 use Encode;
 
-use base('PVE::Network::SDN::Plugin');
+use base('PVE::Network::SDN::Controllers::Plugin');
 
 sub type {
-    return 'faucetcontroller';
-}
-
-sub plugindata {
-    return {
-        role => 'controller',
-    };
+    return 'faucet';
 }
 
 sub properties {
diff --git a/PVE/Network/SDN/EvpnControllerPlugin.pm b/PVE/Network/SDN/Controllers/FrrEvpnPlugin.pm
similarity index 95%
rename from PVE/Network/SDN/EvpnControllerPlugin.pm
rename to PVE/Network/SDN/Controllers/FrrEvpnPlugin.pm
index b2c9345..052c77e 100644
--- a/PVE/Network/SDN/EvpnControllerPlugin.pm
+++ b/PVE/Network/SDN/Controllers/FrrEvpnPlugin.pm
@@ -1,26 +1,25 @@
-package PVE::Network::SDN::EvpnControllerPlugin;
+package PVE::Network::SDN::Controllers::FrrEvpnPlugin;
 
 use strict;
 use warnings;
-use PVE::Network::SDN::Plugin;
+use PVE::Network::SDN::Controllers::Plugin;
 use PVE::Tools;
 use PVE::INotify;
 use PVE::JSONSchema qw(get_standard_option);
 
-use base('PVE::Network::SDN::Plugin');
+use base('PVE::Network::SDN::Controllers::Plugin');
 
 sub type {
-    return 'evpncontroller';
-}
-
-sub plugindata {
-    return {
-        role => 'controller',
-    };
+    return 'frrevpn';
 }
 
 sub properties {
     return {
+	'uplink-id' => {
+	    type => 'integer',
+	    minimum => 1, maximum => 4096,
+	    description => 'Uplink interface',
+	},
         'asn' => {
             type => 'integer',
             description => "autonomous system number",
@@ -66,7 +65,7 @@ sub generate_controller_config {
 
     if($uplinks->{$uplink}->{name}) {
 	$iface = $uplinks->{$uplink}->{name};
-        $ifaceip = PVE::Network::SDN::Plugin::get_first_local_ipv4_from_interface($iface);
+        $ifaceip = PVE::Network::SDN::Controllers::Plugin::get_first_local_ipv4_from_interface($iface);
     }
 
     my $is_gateway = undef;
diff --git a/PVE/Network/SDN/Controllers/Makefile b/PVE/Network/SDN/Controllers/Makefile
new file mode 100644
index 0000000..73c3b7b
--- /dev/null
+++ b/PVE/Network/SDN/Controllers/Makefile
@@ -0,0 +1,8 @@
+SOURCES=Plugin.pm FaucetPlugin.pm FrrEvpnPlugin.pm
+
+
+PERL5DIR=${DESTDIR}/usr/share/perl5
+
+.PHONY: install
+install:
+	for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/SDN/Controllers/$$i; done
diff --git a/PVE/Network/SDN/Controllers/Plugin.pm b/PVE/Network/SDN/Controllers/Plugin.pm
new file mode 100644
index 0000000..df385f1
--- /dev/null
+++ b/PVE/Network/SDN/Controllers/Plugin.pm
@@ -0,0 +1,133 @@
+package PVE::Network::SDN::Controllers::Plugin;
+
+use strict;
+use warnings;
+
+use PVE::Tools;
+use PVE::JSONSchema;
+use PVE::Cluster;
+
+use Data::Dumper;
+use PVE::JSONSchema qw(get_standard_option);
+use base qw(PVE::SectionConfig);
+
+PVE::Cluster::cfs_register_file('sdn/controllers.cfg',
+				 sub { __PACKAGE__->parse_config(@_); });
+
+PVE::Cluster::cfs_register_file('sdn/controllers.cfg.new',
+				 sub { __PACKAGE__->parse_config(@_); },
+				 sub { __PACKAGE__->write_config(@_); });
+
+PVE::JSONSchema::register_standard_option('pve-sdn-controller-id', {
+    description => "The SDN controller object identifier.",
+    type => 'string', format => 'pve-sdn-controller-id',
+});
+
+PVE::JSONSchema::register_format('pve-sdn-controller-id', \&parse_sdn_controller_id);
+sub parse_sdn_controller_id {
+    my ($id, $noerr) = @_;
+
+    if ($id !~ m/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i) {
+        return undef if $noerr;
+        die "SDN controller object ID '$id' contains illegal characters\n";
+    }
+    return $id;
+}
+
+my $defaultData = {
+
+    propertyList => {
+	type => {
+	    description => "Plugin type.",
+	    type => 'string', format => 'pve-configid',
+	    type => 'string',
+	},
+        controller => get_standard_option('pve-sdn-controller-id',
+            { completion => \&PVE::Network::SDN::complete_sdn_controller }),
+    },
+};
+
+sub private {
+    return $defaultData;
+}
+
+sub parse_section_header {
+    my ($class, $line) = @_;
+
+    if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
+        my ($type, $id) = (lc($1), $2);
+	my $errmsg = undef; # set if you want to skip whole section
+	eval { PVE::JSONSchema::pve_verify_configid($type); };
+	$errmsg = $@ if $@;
+	my $config = {}; # to return additional attributes
+	return ($type, $id, $errmsg, $config);
+    }
+    return undef;
+}
+
+sub generate_sdn_config {
+    my ($class, $plugin_config, $node, $data, $ctime) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub generate_controller_config {
+    my ($class, $plugin_config, $router, $id, $uplinks, $config) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub generate_controller_vnet_config {
+    my ($class, $plugin_config, $controller, $transportid, $vnetid, $config) = @_;
+
+}
+
+sub write_controller_config {
+    my ($class, $plugin_config, $config) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub controller_reload {
+    my ($class) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub on_delete_hook {
+    my ($class, $sndid, $scfg) = @_;
+
+    # do nothing by default
+}
+
+sub on_update_hook {
+    my ($class, $sdnid, $scfg) = @_;
+
+    # do nothing by default
+}
+
+#helpers
+
+#to be move to Network.pm helper
+sub get_first_local_ipv4_from_interface {
+    my ($interface) = @_;
+
+    my $cmd = ['/sbin/ip', 'address', 'show', 'dev', $interface];
+
+    my $IP = "";
+
+    my $code = sub {
+	my $line = shift;
+
+	if ($line =~ m!^\s*inet\s+($PVE::Tools::IPRE)(?:/\d+|\s+peer\s+)!) {
+	    $IP = $1;
+	    return;
+	}
+    };
+
+    PVE::Tools::run_command($cmd, outfunc => $code);
+
+    return $IP;
+}
+
+1;
diff --git a/PVE/Network/SDN/Makefile b/PVE/Network/SDN/Makefile
index 232db52..7622255 100644
--- a/PVE/Network/SDN/Makefile
+++ b/PVE/Network/SDN/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Plugin.pm VnetPlugin.pm VlanPlugin.pm VxlanPlugin.pm FaucetControllerPlugin.pm FaucetPlugin.pm EvpnPlugin.pm EvpnControllerPlugin.pm QinQPlugin.pm
+SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm
 
 
 PERL5DIR=${DESTDIR}/usr/share/perl5
@@ -6,4 +6,6 @@ PERL5DIR=${DESTDIR}/usr/share/perl5
 .PHONY: install
 install:
 	for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/SDN/$$i; done
+	make -C Controllers install
+	make -C Zones install
 
diff --git a/PVE/Network/SDN/VnetPlugin.pm b/PVE/Network/SDN/VnetPlugin.pm
index e918564..28a7b59 100644
--- a/PVE/Network/SDN/VnetPlugin.pm
+++ b/PVE/Network/SDN/VnetPlugin.pm
@@ -2,28 +2,60 @@ package PVE::Network::SDN::VnetPlugin;
 
 use strict;
 use warnings;
-use PVE::Network::SDN::Plugin;
 
-use base('PVE::Network::SDN::Plugin');
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use base qw(PVE::SectionConfig);
+use PVE::JSONSchema qw(get_standard_option);
 
-use PVE::Cluster;
+PVE::Cluster::cfs_register_file('sdn/vnets.cfg',
+                                 sub { __PACKAGE__->parse_config(@_); });
+
+PVE::Cluster::cfs_register_file('sdn/vnets.cfg.new',
+                                 sub { __PACKAGE__->parse_config(@_); },
+                                 sub { __PACKAGE__->write_config(@_); });
+
+PVE::JSONSchema::register_standard_option('pve-sdn-vnet-id', {
+    description => "The SDN vnet object identifier.",
+    type => 'string', format => 'pve-sdn-vnet-id',
+});
+
+PVE::JSONSchema::register_format('pve-sdn-vnet-id', \&parse_sdn_vnet_id);
+sub parse_sdn_vnet_id {
+    my ($id, $noerr) = @_;
+
+    if ($id !~ m/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i) {
+        return undef if $noerr;
+        die "SDN object vnet ID '$id' contains illegal characters\n";
+    }
+    return $id;
+}
+
+my $defaultData = {
+
+    propertyList => {
+        vnet => get_standard_option('pve-sdn-vnet-id',
+            { completion => \&PVE::Network::SDN::Vnets::complete_sdn_vnet }),
+    },
+};
 
 sub type {
     return 'vnet';
 }
 
-sub plugindata {
-    return {
-        role => 'vnet',
-    };
+sub private {
+    return $defaultData;
 }
 
 sub properties {
     return {
-	transportzone => {
+	zone => {
             type => 'string',
-            description => "transportzone id",
+            description => "zone id",
 	},
+        type => {
+            description => "Type",
+            optional => 1,
+        },
 	tag => {
             type => 'integer',
             description => "vlan or vxlan id",
@@ -58,7 +90,7 @@ sub properties {
 
 sub options {
     return {
-        transportzone => { optional => 0},
+        zone => { optional => 0},
         tag => { optional => 0},
         alias => { optional => 1 },
         ipv4 => { optional => 1 },
diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
new file mode 100644
index 0000000..95a74f5
--- /dev/null
+++ b/PVE/Network/SDN/Vnets.pm
@@ -0,0 +1,59 @@
+package PVE::Network::SDN::Vnets;
+
+use strict;
+use warnings;
+
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+
+
+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 exists\n" if (!$noerr && !$scfg);
+
+    return $scfg;
+}
+
+sub config {
+    my $config = cfs_read_file("sdn/vnets.cfg.new");
+    $config = cfs_read_file("sdn/vnets.cfg") if !keys %{$config->{ids}};
+    return $config;
+}
+
+sub write_config {
+    my ($cfg) = @_;
+
+    cfs_write_file("sdn/vnets.cfg.new", $cfg);
+}
+
+sub lock_sdn_vnets_config {
+    my ($code, $errmsg) = @_;
+
+    cfs_lock_file("sdn/vnets.cfg.new", undef, $code);
+    if (my $err = $@) {
+        $errmsg ? die "$errmsg: $err" : die $err;
+    }
+}
+
+sub sdn_vnets_ids {
+    my ($cfg) = @_;
+
+    return 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) ];
+}
+
+1;
diff --git a/PVE/Network/SDN/Zones.pm b/PVE/Network/SDN/Zones.pm
new file mode 100644
index 0000000..6638712
--- /dev/null
+++ b/PVE/Network/SDN/Zones.pm
@@ -0,0 +1,227 @@
+package PVE::Network::SDN::Zones;
+
+use strict;
+use warnings;
+
+use Data::Dumper;
+use JSON;
+
+use PVE::Tools qw(extract_param dir_glob_regex run_command);
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+
+use PVE::Network::SDN::Vnets;
+use PVE::Network::SDN::Zones::VlanPlugin;
+use PVE::Network::SDN::Zones::QinQPlugin;
+use PVE::Network::SDN::Zones::VxlanPlugin;
+use PVE::Network::SDN::Zones::EvpnPlugin;
+use PVE::Network::SDN::Zones::FaucetPlugin;
+use PVE::Network::SDN::Zones::Plugin;
+
+PVE::Network::SDN::Zones::VlanPlugin->register();
+PVE::Network::SDN::Zones::QinQPlugin->register();
+PVE::Network::SDN::Zones::VxlanPlugin->register();
+PVE::Network::SDN::Zones::EvpnPlugin->register();
+PVE::Network::SDN::Zones::FaucetPlugin->register();
+PVE::Network::SDN::Zones::Plugin->init();
+
+
+sub sdn_zones_config {
+    my ($cfg, $id, $noerr) = @_;
+
+    die "no sdn zone ID specified\n" if !$id;
+
+    my $scfg = $cfg->{ids}->{$id};
+    die "sdn '$id' does not exists\n" if (!$noerr && !$scfg);
+
+    return $scfg;
+}
+
+sub config {
+    my $config = cfs_read_file("sdn/zones.cfg.new");
+    $config = cfs_read_file("sdn/zones.cfg") if !keys %{$config->{ids}};
+    return $config;
+}
+
+sub write_config {
+    my ($cfg) = @_;
+
+    cfs_write_file("sdn/zones.cfg.new", $cfg);
+}
+
+sub lock_sdn_zones_config {
+    my ($code, $errmsg) = @_;
+
+    cfs_lock_file("sdn/zones.cfg.new", undef, $code);
+    if (my $err = $@) {
+        $errmsg ? die "$errmsg: $err" : die $err;
+    }
+}
+
+sub sdn_zones_ids {
+    my ($cfg) = @_;
+
+    return keys %{$cfg->{ids}};
+}
+
+sub complete_sdn_zone {
+    my ($cmdname, $pname, $cvalue) = @_;
+
+    my $cfg = PVE::Network::SDN::config();
+
+    return  $cmdname eq 'add' ? [] : [ PVE::Network::SDN::sdn_zones_ids($cfg) ];
+}
+
+
+sub generate_etc_network_config {
+
+    my $vnet_cfg = PVE::Cluster::cfs_read_file('sdn/vnets.cfg');
+    my $transport_cfg = PVE::Cluster::cfs_read_file('sdn/zones.cfg');
+    return if !$vnet_cfg && !$transport_cfg;
+
+    #read main config for physical interfaces
+    my $current_config_file = "/etc/network/interfaces";
+    my $fh = IO::File->new($current_config_file);
+    my $interfaces_config = PVE::INotify::read_etc_network_interfaces(1,$fh);
+    $fh->close();
+
+    #check uplinks
+    my $uplinks = {};
+    foreach my $id (keys %{$interfaces_config->{ifaces}}) {
+	my $interface = $interfaces_config->{ifaces}->{$id};
+	if (my $uplink = $interface->{'uplink-id'}) {
+	    die "uplink-id $uplink is already defined on $uplinks->{$uplink}" if $uplinks->{$uplink};
+	    $interface->{name} = $id;
+	    $uplinks->{$interface->{'uplink-id'}} = $interface;
+	}
+    }
+
+    #generate configuration
+    my $config = {};
+    foreach my $id (keys %{$vnet_cfg->{ids}}) {
+	my $vnet = $vnet_cfg->{ids}->{$id};
+	my $zone = $vnet->{transportzone};
+
+	if(!$zone) {
+	    warn "can't generate vnet $vnet : zone $zone don't exist";
+	    next;
+	}
+
+	my $plugin_config = $transport_cfg->{ids}->{$zone};
+
+	if (!defined($plugin_config)) {
+	    warn "can't generate vnet $vnet : zone $zone don't exist";
+	    next;
+	}
+
+	my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
+	$plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $uplinks, $config);
+    }
+
+    my $raw_network_config = "";
+    foreach my $iface (keys %$config) {
+	$raw_network_config .= "\n";
+	$raw_network_config .= "auto $iface\n";
+	$raw_network_config .= "iface $iface\n";
+	foreach my $option (@{$config->{$iface}}) {
+	    $raw_network_config .= "\t$option\n";
+	}
+    }
+
+    return $raw_network_config;
+}
+
+sub write_etc_network_config {
+    my ($rawconfig) = @_;
+
+    return if !$rawconfig;
+    my $sdn_interfaces_file = "/etc/network/interfaces.d/sdn";
+
+    my $writefh = IO::File->new($sdn_interfaces_file,">");
+    print $writefh $rawconfig;
+    $writefh->close();
+}
+
+sub ifquery_check {
+
+    my $cmd = ['ifquery', '-a', '-c', '-o','json'];
+
+    my $result = '';
+    my $reader = sub { $result .= shift };
+
+    eval {
+	run_command($cmd, outfunc => $reader);
+    };
+
+    my $resultjson = decode_json($result);
+    my $interfaces = {};
+
+    foreach my $interface (@$resultjson) {
+	my $name = $interface->{name};
+	$interfaces->{$name} = {
+	    status => $interface->{status},
+	    config => $interface->{config},
+	    config_status => $interface->{config_status},
+	};
+    }
+
+    return $interfaces;
+}
+
+# improve me : move status code inside plugins ?
+sub status {
+
+    my $cluster_vnet_file = "/etc/pve/sdn/vnets.cfg";
+    my $cluster_transport_file = "/etc/pve/sdn/zones.cfg";
+    my $local_sdn_file = "/etc/network/interfaces.d/sdn";
+    my $err_config = undef;
+
+    return if !-e $cluster_vnet_file && !-e $cluster_transport_file;
+
+    if (!-e $local_sdn_file) {
+	warn "local sdn network configuration is not yet generated, please reload";
+	$err_config = 'pending';
+    } else {
+	# fixme : use some kind of versioning info?
+	my $cluster_vnet_timestamp = (stat($cluster_vnet_file))[9];
+	my $cluster_transport_timestamp = (stat($cluster_transport_file))[9];
+	my $local_sdn_timestamp = (stat($local_sdn_file))[9];
+
+	if ($local_sdn_timestamp < $cluster_vnet_timestamp || $local_sdn_timestamp < $cluster_transport_timestamp) {
+	    warn "local sdn network configuration is too old, please reload";
+	    $err_config = 'pending';
+        }
+    }
+
+    my $status = ifquery_check();
+
+    my $vnet_cfg = PVE::Cluster::cfs_read_file('sdn/vnets.cfg');
+
+    my $vnet_status = {};
+    my $transport_status = {};
+
+    foreach my $id (keys %{$vnet_cfg->{ids}}) {
+	my $transportzone = $vnet_cfg->{ids}->{$id}->{transportzone};
+	$vnet_status->{$id}->{transportzone} = $transportzone;
+	$transport_status->{$transportzone}->{status} = 'available' if !defined($transport_status->{$transportzone}->{status});
+
+	if($err_config) {
+	    $vnet_status->{$id}->{status} = $err_config;
+	    $transport_status->{$transportzone}->{status} = $err_config;
+	} elsif ($status->{$id}->{status} && $status->{$id}->{status} eq 'pass') {
+	    $vnet_status->{$id}->{status} = 'available';
+	    my $bridgeport = $status->{$id}->{config}->{'bridge-ports'};
+
+	    if ($status->{$bridgeport}->{status} && $status->{$bridgeport}->{status} ne 'pass') {
+		$vnet_status->{$id}->{status} = 'error';
+		$transport_status->{$transportzone}->{status} = 'error';
+	    }
+	} else {
+	    $vnet_status->{$id}->{status} = 'error';
+	    $transport_status->{$transportzone}->{status} = 'error';
+	}
+    }
+    return($transport_status, $vnet_status);
+}
+
+1;
+
diff --git a/PVE/Network/SDN/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
similarity index 94%
rename from PVE/Network/SDN/EvpnPlugin.pm
rename to PVE/Network/SDN/Zones/EvpnPlugin.pm
index f570f2f..179ecc1 100644
--- a/PVE/Network/SDN/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -1,12 +1,12 @@
-package PVE::Network::SDN::EvpnPlugin;
+package PVE::Network::SDN::Zones::EvpnPlugin;
 
 use strict;
 use warnings;
-use PVE::Network::SDN::Plugin;
+use PVE::Network::SDN::Zones::VxlanPlugin;
 use PVE::Tools qw($IPV4RE);
 use PVE::INotify;
 
-use base('PVE::Network::SDN::VxlanPlugin');
+use base('PVE::Network::SDN::Zones::VxlanPlugin');
 
 sub type {
     return 'evpn';
@@ -67,7 +67,7 @@ sub generate_sdn_config {
 
     if($uplinks->{$uplink}->{name}) {
 	$iface = $uplinks->{$uplink}->{name};
-	$ifaceip = PVE::Network::SDN::Plugin::get_first_local_ipv4_from_interface($iface);
+	$ifaceip = PVE::Network::SDN::Zones::Plugin::get_first_local_ipv4_from_interface($iface);
     }
 
     my $mtu = 1450;
@@ -149,7 +149,7 @@ sub on_update_hook {
 		if(defined($sdn->{transportzone}) && $sdn->{transportzone} eq $transportid) {
 		    my $tag = $sdn->{tag};
 		    eval {
-			PVE::Network::SDN::Plugin::parse_tag_number_or_range($vxlanallowed, '16777216', $tag);
+			PVE::Network::SDN::Zones::Plugin::parse_tag_number_or_range($vxlanallowed, '16777216', $tag);
 		    };
 		    if($@) {
 			die "vnet $id - vlan $tag is not allowed in transport $transportid";
diff --git a/PVE/Network/SDN/FaucetPlugin.pm b/PVE/Network/SDN/Zones/FaucetPlugin.pm
similarity index 92%
rename from PVE/Network/SDN/FaucetPlugin.pm
rename to PVE/Network/SDN/Zones/FaucetPlugin.pm
index 9422ee7..e914d4d 100644
--- a/PVE/Network/SDN/FaucetPlugin.pm
+++ b/PVE/Network/SDN/Zones/FaucetPlugin.pm
@@ -1,10 +1,10 @@
-package PVE::Network::SDN::FaucetPlugin;
+package PVE::Network::SDN::Zones::FaucetPlugin;
 
 use strict;
 use warnings;
-use PVE::Network::SDN::VlanPlugin;
+use PVE::Network::SDN::Zones::VlanPlugin;
 
-use base('PVE::Network::SDN::VlanPlugin');
+use base('PVE::Network::SDN::Zones::VlanPlugin');
 
 sub type {
     return 'faucet';
diff --git a/PVE/Network/SDN/Zones/Makefile b/PVE/Network/SDN/Zones/Makefile
new file mode 100644
index 0000000..ba9a4b5
--- /dev/null
+++ b/PVE/Network/SDN/Zones/Makefile
@@ -0,0 +1,8 @@
+SOURCES=Plugin.pm VlanPlugin.pm VxlanPlugin.pm FaucetPlugin.pm EvpnPlugin.pm QinQPlugin.pm
+
+
+PERL5DIR=${DESTDIR}/usr/share/perl5
+
+.PHONY: install
+install:
+	for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/SDN/Zones/$$i; done
diff --git a/PVE/Network/SDN/Plugin.pm b/PVE/Network/SDN/Zones/Plugin.pm
similarity index 79%
rename from PVE/Network/SDN/Plugin.pm
rename to PVE/Network/SDN/Zones/Plugin.pm
index 0c6eaf0..7e820cd 100644
--- a/PVE/Network/SDN/Plugin.pm
+++ b/PVE/Network/SDN/Zones/Plugin.pm
@@ -1,4 +1,4 @@
-package PVE::Network::SDN::Plugin;
+package PVE::Network::SDN::Zones::Plugin;
 
 use strict;
 use warnings;
@@ -11,27 +11,27 @@ use Data::Dumper;
 use PVE::JSONSchema qw(get_standard_option);
 use base qw(PVE::SectionConfig);
 
-PVE::Cluster::cfs_register_file('sdn.cfg',
+PVE::Cluster::cfs_register_file('sdn/zones.cfg',
 				 sub { __PACKAGE__->parse_config(@_); });
 
-PVE::Cluster::cfs_register_file('sdn.cfg.new',
+PVE::Cluster::cfs_register_file('sdn/zones.cfg.new',
 				 sub { __PACKAGE__->parse_config(@_); },
 				 sub { __PACKAGE__->write_config(@_); });
 
-PVE::JSONSchema::register_standard_option('pve-sdn-id', {
-    description => "The SDN object identifier.",
-    type => 'string', format => 'pve-sdn-id',
+PVE::JSONSchema::register_standard_option('pve-sdn-zone-id', {
+    description => "The SDN zone object identifier.",
+    type => 'string', format => 'pve-sdn-zone-id',
 });
 
-PVE::JSONSchema::register_format('pve-sdn-id', \&parse_sdn_id);
-sub parse_sdn_id {
-    my ($sdnid, $noerr) = @_;
+PVE::JSONSchema::register_format('pve-sdn-zone-id', \&parse_sdn_zone_id);
+sub parse_sdn_zone_id {
+    my ($id, $noerr) = @_;
 
-    if ($sdnid !~ m/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i) {
+    if ($id !~ m/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i) {
         return undef if $noerr;
-        die "SDN object ID '$sdnid' contains illegal characters\n";
+        die "SDN zone object ID '$id' contains illegal characters\n";
     }
-    return $sdnid;
+    return $id;
 }
 
 my $defaultData = {
@@ -42,8 +42,8 @@ my $defaultData = {
 	    type => 'string', format => 'pve-configid',
 	    type => 'string',
 	},
-        sdn => get_standard_option('pve-sdn-id',
-            { completion => \&PVE::Network::SDN::complete_sdn }),
+        zone => get_standard_option('pve-sdn-zone-id',
+            { completion => \&PVE::Network::SDN::Zones::complete_sdn_zone }),
     },
 };
 
@@ -55,12 +55,12 @@ sub parse_section_header {
     my ($class, $line) = @_;
 
     if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
-        my ($type, $sdnid) = (lc($1), $2);
+        my ($type, $id) = (lc($1), $2);
 	my $errmsg = undef; # set if you want to skip whole section
 	eval { PVE::JSONSchema::pve_verify_configid($type); };
 	$errmsg = $@ if $@;
 	my $config = {}; # to return additional attributes
-	return ($type, $sdnid, $errmsg, $config);
+	return ($type, $id, $errmsg, $config);
     }
     return undef;
 }
diff --git a/PVE/Network/SDN/QinQPlugin.pm b/PVE/Network/SDN/Zones/QinQPlugin.pm
similarity index 88%
rename from PVE/Network/SDN/QinQPlugin.pm
rename to PVE/Network/SDN/Zones/QinQPlugin.pm
index 9f40e84..d90382c 100644
--- a/PVE/Network/SDN/QinQPlugin.pm
+++ b/PVE/Network/SDN/Zones/QinQPlugin.pm
@@ -1,23 +1,21 @@
-package PVE::Network::SDN::QinQPlugin;
+package PVE::Network::SDN::Zones::QinQPlugin;
 
 use strict;
 use warnings;
-use PVE::Network::SDN::VlanPlugin;
+use PVE::Network::SDN::Zones::VlanPlugin;
 
-use base('PVE::Network::SDN::VlanPlugin');
+use base('PVE::Network::SDN::Zones::VlanPlugin');
 
 sub type {
     return 'qinq';
 }
 
-sub plugindata {
-    return {
-	role => 'transport',
-    };
-}
-
 sub properties {
     return {
+        tag => {
+            type => 'integer',
+            description => "vlan tag",
+        },
 	'vlan-protocol' => {
 	    type => 'string',
             enum => ['802.1q', '802.1ad'],
diff --git a/PVE/Network/SDN/VlanPlugin.pm b/PVE/Network/SDN/Zones/VlanPlugin.pm
similarity index 89%
rename from PVE/Network/SDN/VlanPlugin.pm
rename to PVE/Network/SDN/Zones/VlanPlugin.pm
index 5a38f59..ab46d32 100644
--- a/PVE/Network/SDN/VlanPlugin.pm
+++ b/PVE/Network/SDN/Zones/VlanPlugin.pm
@@ -1,26 +1,20 @@
-package PVE::Network::SDN::VlanPlugin;
+package PVE::Network::SDN::Zones::VlanPlugin;
 
 use strict;
 use warnings;
-use PVE::Network::SDN::Plugin;
+use PVE::Network::SDN::Zones::Plugin;
 
-use base('PVE::Network::SDN::Plugin');
+use base('PVE::Network::SDN::Zones::Plugin');
 
 sub type {
     return 'vlan';
 }
 
-sub plugindata {
-    return {
-	role => 'transport',
-    };
-}
-
 PVE::JSONSchema::register_format('pve-sdn-vlanrange', \&pve_verify_sdn_vlanrange);
 sub pve_verify_sdn_vlanrange {
    my ($vlanstr) = @_;
 
-   PVE::Network::SDN::Plugin::parse_tag_number_or_range($vlanstr, '4096');
+   PVE::Network::SDN::Zones::Plugin::parse_tag_number_or_range($vlanstr, '4096');
 
    return $vlanstr;
 }
@@ -106,7 +100,7 @@ sub on_update_hook {
 		if(defined($sdn->{transportzone}) && $sdn->{transportzone} eq $transportid) {
 		    my $tag = $sdn->{tag};
 		    eval {
-			PVE::Network::SDN::Plugin::parse_tag_number_or_range($vlanallowed, '4096', $tag);
+			PVE::Network::SDN::Zones::Plugin::parse_tag_number_or_range($vlanallowed, '4096', $tag);
 		    };
 		    if($@) {
 			die "vlan $tag is not allowed in transport $transportid";
diff --git a/PVE/Network/SDN/VxlanPlugin.pm b/PVE/Network/SDN/Zones/VxlanPlugin.pm
similarity index 93%
rename from PVE/Network/SDN/VxlanPlugin.pm
rename to PVE/Network/SDN/Zones/VxlanPlugin.pm
index 5a259b0..cccf93a 100644
--- a/PVE/Network/SDN/VxlanPlugin.pm
+++ b/PVE/Network/SDN/Zones/VxlanPlugin.pm
@@ -1,18 +1,18 @@
-package PVE::Network::SDN::VxlanPlugin;
+package PVE::Network::SDN::Zones::VxlanPlugin;
 
 use strict;
 use warnings;
-use PVE::Network::SDN::Plugin;
+use PVE::Network::SDN::Zones::Plugin;
 use PVE::Tools qw($IPV4RE);
 use PVE::INotify;
 
-use base('PVE::Network::SDN::Plugin');
+use base('PVE::Network::SDN::Zones::Plugin');
 
 PVE::JSONSchema::register_format('pve-sdn-vxlanrange', \&pve_verify_sdn_vxlanrange);
 sub pve_verify_sdn_vxlanrange {
    my ($vxlanstr) = @_;
 
-   PVE::Network::SDN::Plugin::parse_tag_number_or_range($vxlanstr, '16777216');
+   PVE::Network::SDN::Zones::Plugin::parse_tag_number_or_range($vxlanstr, '16777216');
 
    return $vxlanstr;
 }
@@ -40,12 +40,6 @@ sub type {
     return 'vxlan';
 }
 
-sub plugindata {
-    return {
-        role => 'transport',
-    };
-}
-
 sub properties {
     return {
         'vxlan-allowed' => {
@@ -94,7 +88,7 @@ sub generate_sdn_config {
 
     if($uplinks->{$uplink}->{name}) {
 	$iface = $uplinks->{$uplink}->{name};
-	$ifaceip = PVE::Network::SDN::Plugin::get_first_local_ipv4_from_interface($iface);
+	$ifaceip = PVE::Network::SDN::Zones::Plugin::get_first_local_ipv4_from_interface($iface);
     }
 
     my $mtu = 1450;
@@ -161,7 +155,7 @@ sub on_update_hook {
 		if(defined($sdn->{transportzone}) && $sdn->{transportzone} eq $transportid) {
 		    my $tag = $sdn->{tag};
 		    eval {
-			PVE::Network::SDN::Plugin::parse_tag_number_or_range($vxlanallowed, '16777216', $tag);
+			PVE::Network::SDN::Zones::Plugin::parse_tag_number_or_range($vxlanallowed, '16777216', $tag);
 		    };
 		    if($@) {
 			die "vnet $id - vlan $tag is not allowed in transport $transportid";
diff --git a/test/generateconfig.pl b/test/generateconfig.pl
index da82672..36880ba 100644
--- a/test/generateconfig.pl
+++ b/test/generateconfig.pl
@@ -3,20 +3,19 @@ use warnings;
 use File::Copy;
 use PVE::Cluster qw(cfs_read_file);
 
-use PVE::Network::SDN;
+use PVE::Network::SDN::Zones;
+use PVE::Network::SDN::Controllers;
 use Data::Dumper;
 
-
-my $network_config = PVE::Network::SDN::generate_etc_network_config();
-PVE::Network::SDN::write_etc_network_config($network_config);
-print "/etc/network/interfaces\n";
+my $network_config = PVE::Network::SDN::Zones::generate_etc_network_config();
+PVE::Network::SDN::Zones::write_etc_network_config($network_config);
+print "/etc/network/interfaces.d/sdn\n";
 print $network_config;
 print "\n";
 
 
-my $controller_config = PVE::Network::SDN::generate_controller_config();
+my $controller_config = PVE::Network::SDN::Controllers::generate_controller_config();
 if ($controller_config) {
     print Dumper($controller_config);
-    PVE::Network::SDN::write_controller_config($controller_config);
-    print "/etc/frr/frr.conf\n";
+    PVE::Network::SDN::Controllers::write_controller_config($controller_config);
 }
-- 
2.20.1




More information about the pve-devel mailing list