Signed-off-by: Alexandre Derumier <aderum...@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                 |  92 ------
 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, 663 insertions(+), 471 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
 delete mode 100644 PVE/Network/SDN/VnetPlugin.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
deleted file mode 100644
index e918564..0000000
--- a/PVE/Network/SDN/VnetPlugin.pm
+++ /dev/null
@@ -1,92 +0,0 @@
-package PVE::Network::SDN::VnetPlugin;
-
-use strict;
-use warnings;
-use PVE::Network::SDN::Plugin;
-
-use base('PVE::Network::SDN::Plugin');
-
-use PVE::Cluster;
-
-sub type {
-    return 'vnet';
-}
-
-sub plugindata {
-    return {
-        role => 'vnet',
-    };
-}
-
-sub properties {
-    return {
-       transportzone => {
-            type => 'string',
-            description => "transportzone id",
-       },
-       tag => {
-            type => 'integer',
-            description => "vlan or vxlan id",
-       },
-        alias => {
-            type => 'string',
-            description => "alias name of the vnet",
-           optional => 1,
-        },
-        mtu => {
-            type => 'integer',
-            description => "mtu",
-           optional => 1,
-        },
-        ipv4 => {
-            description => "Anycast router ipv4 address.",
-            type => 'string', format => 'CIDRv4',
-            optional => 1,
-        },
-       ipv6 => {
-           description => "Anycast router ipv6 address.",
-           type => 'string', format => 'CIDRv6',
-           optional => 1,
-       },
-        mac => {
-            type => 'string',
-            description => "Anycast router mac address",
-           optional => 1, format => 'mac-addr'
-        }
-    };
-}
-
-sub options {
-    return {
-        transportzone => { optional => 0},
-        tag => { optional => 0},
-        alias => { optional => 1 },
-        ipv4 => { optional => 1 },
-        ipv6 => { optional => 1 },
-        mtu => { optional => 1 },
-        mac => { optional => 1 },
-    };
-}
-
-sub on_delete_hook {
-    my ($class, $sdnid, $sdn_cfg) = @_;
-
-    return;
-}
-
-sub on_update_hook {
-    my ($class, $sdnid, $sdn_cfg) = @_;
-    # verify that tag is not already defined in another vnet
-    if (defined($sdn_cfg->{ids}->{$sdnid}->{tag})) {
-       my $tag = $sdn_cfg->{ids}->{$sdnid}->{tag};
-       foreach my $id (keys %{$sdn_cfg->{ids}}) {
-           next if $id eq $sdnid;
-           my $sdn = $sdn_cfg->{ids}->{$id};
-           if ($sdn->{type} eq 'vnet' && defined($sdn->{tag})) {
-               die "tag $tag already exist in vnet $id" if $tag eq $sdn->{tag};
-           }
-       }
-    }
-}
-
-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..a3634ac
--- /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 = 'unknown';
+        }
+    }
+
+    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

_______________________________________________
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

Reply via email to