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;
use PVE::Network::SDN::Vnets;
use PVE::Network::SDN::Zones::VlanPlugin;
PVE::Network::SDN::Zones::FaucetPlugin->register();
PVE::Network::SDN::Zones::Plugin->init();
+my $local_network_sdn_file = "/etc/network/interfaces.d/sdn";
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);
+ die "sdn '$id' does not exist\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}};
+ my $config = cfs_read_file("sdn/zones.cfg");
return $config;
}
-sub write_config {
- my ($cfg) = @_;
-
- cfs_write_file("sdn/zones.cfg.new", $cfg);
+sub get_plugin_config {
+ my ($vnet) = @_;
+ my $zoneid = $vnet->{zone};
+ my $zone_cfg = PVE::Network::SDN::Zones::config();
+ return $zone_cfg->{ids}->{$zoneid};
}
-sub lock_sdn_zones_config {
- my ($code, $errmsg) = @_;
+sub write_config {
+ my ($cfg) = @_;
- cfs_lock_file("sdn/zones.cfg.new", undef, $code);
- if (my $err = $@) {
- $errmsg ? die "$errmsg: $err" : die $err;
- }
+ cfs_write_file("sdn/zones.cfg", $cfg);
}
sub sdn_zones_ids {
sub generate_etc_network_config {
+ my $version = PVE::Cluster::cfs_read_file('sdn/.version');
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;
- }
- }
+ my $zone_cfg = PVE::Cluster::cfs_read_file('sdn/zones.cfg');
+ my $controller_cfg = PVE::Cluster::cfs_read_file('sdn/controllers.cfg');
+ return if !$vnet_cfg && !$zone_cfg;
+
+ my $interfaces_config = PVE::INotify::read_file('interfaces');
#generate configuration
my $config = {};
- foreach my $id (keys %{$vnet_cfg->{ids}}) {
+ my $nodename = PVE::INotify::nodename();
+
+ for my $id (sort keys %{$vnet_cfg->{ids}}) {
my $vnet = $vnet_cfg->{ids}->{$id};
my $zone = $vnet->{zone};
- if(!$zone) {
- warn "can't generate vnet $vnet : zone $zone don't exist";
+ if (!$zone) {
+ warn "can't generate vnet '$id': no zone assigned!\n";
next;
}
- my $plugin_config = $transport_cfg->{ids}->{$zone};
+ my $plugin_config = $zone_cfg->{ids}->{$zone};
if (!defined($plugin_config)) {
- warn "can't generate vnet $vnet : zone $zone don't exist";
+ warn "can't generate vnet '$id': zone $zone don't exist\n";
next;
}
+ next if defined($plugin_config->{nodes}) && !$plugin_config->{nodes}->{$nodename};
+
+ my $controller;
+ if (my $controllerid = $plugin_config->{controller}) {
+ $controller = $controller_cfg->{ids}->{$controllerid};
+ }
+
my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
- $plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $uplinks, $config);
+ $plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $controller, $interfaces_config, $config);
}
- my $raw_network_config = "";
- foreach my $iface (keys %$config) {
+ my $raw_network_config = "\#version:$version\n";
+ foreach my $iface (sort keys %$config) {
$raw_network_config .= "\n";
$raw_network_config .= "auto $iface\n";
$raw_network_config .= "iface $iface\n";
my ($rawconfig) = @_;
return if !$rawconfig;
- my $sdn_interfaces_file = "/etc/network/interfaces.d/sdn";
- my $writefh = IO::File->new($sdn_interfaces_file,">");
+ my $writefh = IO::File->new($local_network_sdn_file,">");
print $writefh $rawconfig;
$writefh->close();
}
+sub read_etc_network_config_version {
+ my $versionstr = PVE::Tools::file_read_firstline($local_network_sdn_file);
+
+ return if !defined($versionstr);
+
+ if ($versionstr =~ m/^\#version:(\d+)$/) {
+ return $1;
+ }
+}
+
sub ifquery_check {
my $cmd = ['ifquery', '-a', '-c', '-o','json'];
# 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;
+ my $local_version = PVE::Network::SDN::Zones::read_etc_network_config_version();
+ my $sdn_version = PVE::Cluster::cfs_read_file('sdn/.version');
- if (!-e $local_sdn_file) {
+ return if !$sdn_version;
+ if (!$local_version) {
$err_config = "local sdn network configuration is not yet generated, please reload";
- warn $err_config;
- } 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) {
- $err_config = "local sdn network configuration is too old, please reload";
- warn $err_config;
- }
+ warn "$err_config\n";
+ } elsif ($local_version < $sdn_version) {
+ $err_config = "local sdn network configuration is too old, please reload";
+ warn "$err_config\n";
}
my $status = ifquery_check();
my $vnet_cfg = PVE::Cluster::cfs_read_file('sdn/vnets.cfg');
+ my $zone_cfg = PVE::Cluster::cfs_read_file('sdn/zones.cfg');
+ my $nodename = PVE::INotify::nodename();
+
my $vnet_status = {};
- my $transport_status = {};
-
- foreach my $id (keys %{$vnet_cfg->{ids}}) {
- my $zone = $vnet_cfg->{ids}->{$id}->{zone};
- $vnet_status->{$id}->{zone} = $zone;
- $transport_status->{$zone}->{status} = 'available' if !defined($transport_status->{$zone}->{status});
-
- if($err_config) {
- $vnet_status->{$id}->{status} = 'pending';
- $vnet_status->{$id}->{statusmsg} = $err_config;
- $transport_status->{$zone}->{status} = 'pending';
- } 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';
- $vnet_status->{$id}->{statusmsg} = 'configuration not fully applied';
- $transport_status->{$zone}->{status} = 'error';
- }
- } else {
- $vnet_status->{$id}->{status} = 'error';
- $vnet_status->{$id}->{statusmsg} = 'missing';
- $transport_status->{$zone}->{status} = 'error';
- }
+ my $zone_status = {};
+
+ foreach my $id (sort keys %{$vnet_cfg->{ids}}) {
+ my $vnet = $vnet_cfg->{ids}->{$id};
+ my $zone = $vnet->{zone};
+ next if !$zone;
+
+ my $plugin_config = $zone_cfg->{ids}->{$zone};
+ next if defined($plugin_config->{nodes}) && !$plugin_config->{nodes}->{$nodename};
+
+ my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
+ $plugin->status($plugin_config, $zone, $id, $vnet, $err_config, $status, $vnet_status, $zone_status);
+ }
+
+ return($zone_status, $vnet_status);
+}
+
+sub get_bridge_vlan {
+ my ($vnetid) = @_;
+
+ my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
+
+ return ($vnetid, undef) if !$vnet; # fallback for classic bridge
+
+ my $plugin_config = get_plugin_config($vnet);
+ my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
+ return $plugin->get_bridge_vlan($plugin_config, $vnetid, $vnet->{tag});
+}
+
+sub tap_create {
+ my ($iface, $bridge) = @_;
+
+ my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge);
+ if (!$vnet) { # fallback for classic bridge
+ PVE::Network::tap_create($iface, $bridge);
+ return;
+ }
+
+ my $plugin_config = get_plugin_config($vnet);
+ my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
+ $plugin->tap_create($plugin_config, $vnet, $iface, $bridge);
+}
+
+sub veth_create {
+ my ($veth, $vethpeer, $bridge, $hwaddr) = @_;
+
+ my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge);
+ if (!$vnet) { # fallback for classic bridge
+ PVE::Network::veth_create($veth, $vethpeer, $bridge, $hwaddr);
+ return;
+ }
+
+ my $plugin_config = get_plugin_config($vnet);
+ my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
+ $plugin->veth_create($plugin_config, $vnet, $veth, $vethpeer, $bridge, $hwaddr);
+}
+
+sub tap_plug {
+ my ($iface, $bridge, $tag, $firewall, $trunks, $rate) = @_;
+
+ my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge);
+ if (!$vnet) { # fallback for classic bridge
+ PVE::Network::tap_plug($iface, $bridge, $tag, $firewall, $trunks, $rate);
+ return;
}
- return($transport_status, $vnet_status);
+
+ my $plugin_config = get_plugin_config($vnet);
+ my $nodename = PVE::INotify::nodename();
+
+ die "vnet $bridge is not allowed on this node\n"
+ if $plugin_config->{nodes} && !defined($plugin_config->{nodes}->{$nodename});
+
+ my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
+ $plugin->tap_plug($plugin_config, $vnet, $iface, $bridge, $firewall, $rate);
}
1;