X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=PVE%2FNetwork%2FSDN%2FZones.pm;h=32586e7f778268218e944a374285c8865ed3ed00;hb=56a9e2b3f3093a63cba35a00df3c5e5d47a433af;hp=3f2e140a0a64b46c2ee09c1a0eeb53ad1e936c4b;hpb=6933ca89163af74b5a907b75e6112a6e7631af43;p=pve-network.git diff --git a/PVE/Network/SDN/Zones.pm b/PVE/Network/SDN/Zones.pm index 3f2e140..32586e7 100644 --- a/PVE/Network/SDN/Zones.pm +++ b/PVE/Network/SDN/Zones.pm @@ -3,7 +3,6 @@ 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); @@ -16,6 +15,7 @@ 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::SimplePlugin; use PVE::Network::SDN::Zones::Plugin; PVE::Network::SDN::Zones::VlanPlugin->register(); @@ -23,8 +23,10 @@ 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::SimplePlugin->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) = @_; @@ -38,46 +40,63 @@ sub sdn_zones_config { } 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 { my ($cfg) = @_; - return keys %{$cfg->{ids}}; + return sort keys %{$cfg->{ids}}; } sub complete_sdn_zone { my ($cmdname, $pname, $cvalue) = @_; - my $cfg = PVE::Network::SDN::config(); + my $cfg = PVE::Network::SDN::running_config(); return $cmdname eq 'add' ? [] : [ PVE::Network::SDN::sdn_zones_ids($cfg) ]; } +sub get_zone { + my ($zoneid, $running) = @_; + + my $cfg = {}; + if($running) { + my $cfg = PVE::Network::SDN::running_config(); + $cfg = $cfg->{vnets}; + } else { + $cfg = PVE::Network::SDN::Zones::config(); + } + + my $zone = PVE::Network::SDN::Zones::sdn_zones_config($cfg, $zoneid, 1); + + return $zone; +} + sub generate_etc_network_config { - my $vnet_cfg = PVE::Cluster::cfs_read_file('sdn/vnets.cfg'); - my $zone_cfg = PVE::Cluster::cfs_read_file('sdn/zones.cfg'); - my $controller_cfg = PVE::Cluster::cfs_read_file('sdn/controllers.cfg'); + my $cfg = PVE::Network::SDN::running_config(); + + my $version = $cfg->{version}; + my $vnet_cfg = $cfg->{vnets}; + my $zone_cfg = $cfg->{zones}; + my $subnet_cfg = $cfg->{subnets}; + my $controller_cfg = $cfg->{controllers}; return if !$vnet_cfg && !$zone_cfg; my $interfaces_config = PVE::INotify::read_file('interfaces'); @@ -86,36 +105,41 @@ sub generate_etc_network_config { my $config = {}; my $nodename = PVE::INotify::nodename(); - foreach my $id (keys %{$vnet_cfg->{ids}}) { + 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 = $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 = undef; - if($plugin_config->{controller}) { - my $controllerid = $plugin_config->{controller}; - $controller = $controller_cfg->{ids}->{$controllerid}; + 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, $controller, $interfaces_config, $config); + eval { + $plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config); + }; + if (my $err = $@) { + warn "zone $zone : vnet $id : $err\n"; + next; + } } - 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"; @@ -131,13 +155,22 @@ 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,">"); + 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']; @@ -164,89 +197,94 @@ sub ifquery_check { return $interfaces; } -# improve me : move status code inside plugins ? +my $warned_about_reload; + sub status { - my $cluster_vnet_file = "/etc/pve/sdn/vnets.cfg"; - my $cluster_zone_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_zone_file; + my $local_version = PVE::Network::SDN::Zones::read_etc_network_config_version(); + my $cfg = PVE::Network::SDN::running_config(); + my $sdn_version = $cfg->{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\n"; - } else { - # fixme : use some kind of versioning info? - my $cluster_vnet_timestamp = (stat($cluster_vnet_file))[9]; - my $cluster_zone_timestamp = (stat($cluster_zone_file))[9]; - my $local_sdn_timestamp = (stat($local_sdn_file))[9]; - - if ($local_sdn_timestamp < $cluster_vnet_timestamp || $local_sdn_timestamp < $cluster_zone_timestamp) { - $err_config = "local sdn network configuration is too old, please reload"; + if (!$warned_about_reload) { + $warned_about_reload = 1; + warn "$err_config\n"; + } + } elsif ($local_version < $sdn_version) { + $err_config = "local sdn network configuration is too old, please reload"; + if (!$warned_about_reload) { + $warned_about_reload = 1; warn "$err_config\n"; } + } else { + $warned_about_reload = 0; } 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 $vnet_cfg = $cfg->{vnets}; + my $zone_cfg = $cfg->{zones}; my $nodename = PVE::INotify::nodename(); - my $vnet_status = {}; my $zone_status = {}; - foreach my $id (keys %{$vnet_cfg->{ids}}) { - my $vnet = $vnet_cfg->{ids}->{$id}; - my $zone = $vnet->{zone}; + for my $id (sort keys %{$zone_cfg->{ids}}) { + next if defined($zone_cfg->{ids}->{$id}->{nodes}) && !$zone_cfg->{ids}->{$id}->{nodes}->{$nodename}; + $zone_status->{$id}->{status} = $err_config ? 'pending' : 'available'; + } - next if !$zone; - my $plugin_config = $zone_cfg->{ids}->{$zone}; - next if defined($plugin_config->{nodes}) && !$plugin_config->{nodes}->{$nodename}; + foreach my $id (sort keys %{$vnet_cfg->{ids}}) { + my $vnet = $vnet_cfg->{ids}->{$id}; + my $zone = $vnet->{zone}; + next if !defined($zone); - 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); - } + my $plugin_config = $zone_cfg->{ids}->{$zone}; - return($zone_status, $vnet_status); -} + if (!defined($plugin_config)) { + $vnet_status->{$id}->{status} = 'error'; + $vnet_status->{$id}->{statusmsg} = "unknown zone '$zone' configured"; + next; + } -sub get_bridge_vlan { - my ($vnetid) = @_; + next if defined($plugin_config->{nodes}) && !$plugin_config->{nodes}->{$nodename}; - my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid); + $vnet_status->{$id}->{zone} = $zone; + $vnet_status->{$id}->{status} = 'available'; - #fallback if classic bridge - return ($vnetid, undef) if !$vnet; + if ($err_config) { + $vnet_status->{$id}->{status} = 'pending'; + $vnet_status->{$id}->{statusmsg} = $err_config; + next; + } - my $zone_cfg = PVE::Network::SDN::Zones::config(); - my $zoneid = $vnet->{zone}; - my $tag = $vnet->{tag}; + my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type}); + my $err_msg = $plugin->status($plugin_config, $zone, $id, $vnet, $status); + if (@{$err_msg} > 0) { + $vnet_status->{$id}->{status} = 'error'; + $vnet_status->{$id}->{statusmsg} = join(',', @{$err_msg}); + $zone_status->{$id}->{status} = 'error'; + } + } - my $plugin_config = $zone_cfg->{ids}->{$zoneid}; - my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type}); - return $plugin->get_bridge_vlan($plugin_config, $vnetid, $tag); + return ($zone_status, $vnet_status); } sub tap_create { my ($iface, $bridge) = @_; - my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge); - - #fallback if classic bridge - if(!$vnet) { + my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1); + if (!$vnet) { # fallback for classic bridge PVE::Network::tap_create($iface, $bridge); - return; + return; } - my $zone_cfg = PVE::Network::SDN::Zones::config(); - my $zoneid = $vnet->{zone}; - - my $plugin_config = $zone_cfg->{ids}->{$zoneid}; + 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); } @@ -254,18 +292,13 @@ sub tap_create { sub veth_create { my ($veth, $vethpeer, $bridge, $hwaddr) = @_; - my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge); - - #fallback if classic bridge - if(!$vnet) { + my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1); + if (!$vnet) { # fallback for classic bridge PVE::Network::veth_create($veth, $vethpeer, $bridge, $hwaddr); - return; + return; } - my $zone_cfg = PVE::Network::SDN::Zones::config(); - my $zoneid = $vnet->{zone}; - - my $plugin_config = $zone_cfg->{ids}->{$zoneid}; + 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); } @@ -273,25 +306,22 @@ sub veth_create { sub tap_plug { my ($iface, $bridge, $tag, $firewall, $trunks, $rate) = @_; - my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge); - - #fallback if classic bridge - if(!$vnet) { - PVE::Network::tap_plug($iface, $bridge, $tag, $firewall, $trunks, $rate); + my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1); + if (!$vnet) { # fallback for classic bridge + my $interfaces_config = PVE::INotify::read_file('interfaces'); + my $disablelearning = 1 if $interfaces_config->{ifaces}->{$bridge} && $interfaces_config->{ifaces}->{$bridge}->{'bridge-disable-mac-learning'}; + PVE::Network::tap_plug($iface, $bridge, $tag, $firewall, $trunks, $rate, $disablelearning); return; } - my $zone_cfg = PVE::Network::SDN::Zones::config(); + my $plugin_config = get_plugin_config($vnet); my $nodename = PVE::INotify::nodename(); - my $zoneid = $vnet->{zone}; - $tag = $vnet->{tag}; - - die "vnet $bridge is not allowed on this node" if defined($zone_cfg->{ids}->{$zoneid}->{nodes}) && !$zone_cfg->{ids}->{$zoneid}->{nodes}->{$nodename}; + die "vnet $bridge is not allowed on this node\n" + if $plugin_config->{nodes} && !defined($plugin_config->{nodes}->{$nodename}); - my $plugin_config = $zone_cfg->{ids}->{$zoneid}; my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type}); - $plugin->tap_plug($plugin_config, $vnet, $iface, $bridge, $firewall, $rate); + $plugin->tap_plug($plugin_config, $vnet, $tag, $iface, $bridge, $firewall, $trunks, $rate); } 1;