From 8fb1ee7f422c9e30f4f09efc6637946dbc4a48ae Mon Sep 17 00:00:00 2001 From: Alexandre Derumier Date: Mon, 30 Sep 2019 11:03:32 +0200 Subject: [PATCH] make sdn controller plugin generic move all code for frr to frrplugin, rename router option to controller. This will allow to manage more controller in the future (ovn, faucet,....) Signed-off-by: Alexandre Derumier --- PVE/Network/SDN.pm | 148 +++++++-------------------------- PVE/Network/SDN/FrrPlugin.pm | 114 +++++++++++++++++++++++-- PVE/Network/SDN/Plugin.pm | 8 +- PVE/Network/SDN/VlanPlugin.pm | 6 ++ PVE/Network/SDN/VnetPlugin.pm | 6 ++ PVE/Network/SDN/VxlanPlugin.pm | 24 ++++-- test/documentation.txt | 6 +- test/generateconfig.pl | 10 +-- 8 files changed, 179 insertions(+), 143 deletions(-) diff --git a/PVE/Network/SDN.pm b/PVE/Network/SDN.pm index 101464b..1e5ba67 100644 --- a/PVE/Network/SDN.pm +++ b/PVE/Network/SDN.pm @@ -162,7 +162,7 @@ sub generate_etc_network_config { return $raw_network_config; } -sub generate_frr_config { +sub generate_controller_config { my $sdn_cfg = PVE::Cluster::cfs_read_file('sdn.cfg'); return if !$sdn_cfg; @@ -184,124 +184,31 @@ sub generate_frr_config { } } - my $frr_cfg = undef; - my $transport_cfg = undef; - - foreach my $id (keys %{$sdn_cfg->{ids}}) { - if ($sdn_cfg->{ids}->{$id}->{type} eq 'frr') { - $frr_cfg->{ids}->{$id} = $sdn_cfg->{ids}->{$id}; - } elsif ($sdn_cfg->{ids}->{$id}->{type} ne 'vnet') { - $transport_cfg->{ids}->{$id} = $sdn_cfg->{ids}->{$id}; - } - } - - return undef if !$frr_cfg; - #generate configuration my $config = {}; - foreach my $id (sort keys %{$frr_cfg->{ids}}) { - my $plugin_config = $frr_cfg->{ids}->{$id}; + foreach my $id (keys %{$sdn_cfg->{ids}}) { + my $plugin_config = $sdn_cfg->{ids}->{$id}; my $plugin = PVE::Network::SDN::Plugin->lookup($plugin_config->{type}); - $plugin->generate_frr_config($plugin_config, $plugin_config, $id, $uplinks, $config); - } - - foreach my $id (sort keys %{$transport_cfg->{ids}}) { - my $plugin_config = $transport_cfg->{ids}->{$id}; - my $routerid = $plugin_config->{router}; - if ($routerid) { - my $router = $frr_cfg->{ids}->{$routerid}; - if ($router) { - my $plugin = PVE::Network::SDN::Plugin->lookup($plugin_config->{type}); - $plugin->generate_frr_config($plugin_config, $router, $id, $uplinks, $config); + 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($plugin_config->{type}); + $controller_plugin->generate_controller_config($plugin_config, $controller, $id, $uplinks, $config); + } } } } - my $final_config = []; - push @{$final_config}, "log syslog informational"; - push @{$final_config}, "!"; - - generate_frr_recurse($final_config, $config, undef, 0); - - push @{$final_config}, "!"; - push @{$final_config}, "line vty"; - push @{$final_config}, "!"; - - my $raw_frr_config = join("\n", @{$final_config}); - return $raw_frr_config; -} - -sub sort_frr_config { - my $order = {}; - $order->{''} = 0; - $order->{'vrf'} = 1; - $order->{'ipv4 unicast'} = 1; - $order->{'ipv6 unicast'} = 2; - $order->{'l2vpn evpn'} = 3; - - my $a_val = 100; - my $b_val = 100; - - $a_val = $order->{$a} if defined($order->{$a}); - $b_val = $order->{$b} if defined($order->{$b}); - - if($a =~ /bgp (\d+)$/) { - $a_val = 2; - } - - if($b =~ /bgp (\d+)$/) { - $b_val = 2; - } - - return $a_val <=> $b_val; + return $config; } -sub generate_frr_recurse{ - my ($final_config, $content, $parentkey, $level) = @_; - - my $keylist = {}; - $keylist->{vrf} = 1; - $keylist->{'address-family'} = 1; - $keylist->{router} = 1; - - my $exitkeylist = {}; - $exitkeylist->{vrf} = 1; - $exitkeylist->{'address-family'} = 1; - - #fix me, make this generic - my $paddinglevel = undef; - if($level == 1 || $level == 2) { - $paddinglevel = $level - 1; - } elsif ($level == 3 || $level == 4) { - $paddinglevel = $level - 2; - } - - my $padding = ""; - $padding = ' ' x ($paddinglevel) if $paddinglevel; - - if (ref $content eq ref {}) { - foreach my $key (sort sort_frr_config keys %$content) { - if ($parentkey && defined($keylist->{$parentkey})) { - push @{$final_config}, $padding."!"; - push @{$final_config}, $padding."$parentkey $key"; - } else { - push @{$final_config}, $padding."$key" if $key ne '' && !defined($keylist->{$key}); - } - - my $option = $content->{$key}; - generate_frr_recurse($final_config, $option, $key, $level+1); - - push @{$final_config}, $padding."exit-$parentkey" if $parentkey && defined($exitkeylist->{$parentkey}); - } - } - - if (ref $content eq 'ARRAY') { - foreach my $value (@$content) { - push @{$final_config}, $padding."$value"; - } - } -} sub write_etc_network_config { my ($rawconfig) = @_; @@ -313,20 +220,23 @@ sub write_etc_network_config { $writefh->close(); } -sub write_frr_config { - my ($rawconfig) = @_; +sub write_controller_config { + my ($config) = @_; - return if !$rawconfig; - return if !-d "/etc/frr"; - - my $frr_config_file = "/etc/frr/frr.conf"; + my $sdn_cfg = PVE::Cluster::cfs_read_file('sdn.cfg'); + return if !$sdn_cfg; - my $writefh = IO::File->new($frr_config_file,">"); - print $writefh $rawconfig; - $writefh->close(); + 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"; diff --git a/PVE/Network/SDN/FrrPlugin.pm b/PVE/Network/SDN/FrrPlugin.pm index b0e36fa..455b185 100644 --- a/PVE/Network/SDN/FrrPlugin.pm +++ b/PVE/Network/SDN/FrrPlugin.pm @@ -13,6 +13,12 @@ sub type { return 'frr'; } +sub plugindata { + return { + role => 'controller', + }; +} + sub properties { return { 'asn' => { @@ -43,7 +49,7 @@ sub options { } # Plugin implementation -sub generate_frr_config { +sub generate_controller_config { my ($class, $plugin_config, $router, $id, $uplinks, $config) = @_; my @peers = split(',', $plugin_config->{'peers'}) if $plugin_config->{'peers'}; @@ -86,7 +92,7 @@ sub generate_frr_config { push @router_config, "neighbor $address remote-as external"; } } - push(@{$config->{router}->{"bgp $asn"}->{""}}, @router_config); + push(@{$config->{frr}->{router}->{"bgp $asn"}->{""}}, @router_config); @router_config = (); foreach my $address (@peers) { @@ -94,7 +100,7 @@ sub generate_frr_config { push @router_config, "neighbor $address activate"; } push @router_config, "advertise-all-vni"; - push(@{$config->{router}->{"bgp $asn"}->{"address-family"}->{"l2vpn evpn"}}, @router_config); + push(@{$config->{frr}->{router}->{"bgp $asn"}->{"address-family"}->{"l2vpn evpn"}}, @router_config); if ($is_gateway) { @@ -105,8 +111,8 @@ sub generate_frr_config { foreach my $address (@gatewaypeers) { push @router_config, "neighbor $address activate"; } - push(@{$config->{router}->{"bgp $asn"}->{"address-family"}->{"ipv4 unicast"}}, @router_config); - push(@{$config->{router}->{"bgp $asn"}->{"address-family"}->{"ipv6 unicast"}}, @router_config); + push(@{$config->{frr}->{router}->{"bgp $asn"}->{"address-family"}->{"ipv4 unicast"}}, @router_config); + push(@{$config->{frr}->{router}->{"bgp $asn"}->{"address-family"}->{"ipv6 unicast"}}, @router_config); } @@ -137,6 +143,102 @@ sub on_update_hook { } } -1; +sub sort_frr_config { + my $order = {}; + $order->{''} = 0; + $order->{'vrf'} = 1; + $order->{'ipv4 unicast'} = 1; + $order->{'ipv6 unicast'} = 2; + $order->{'l2vpn evpn'} = 3; + + my $a_val = 100; + my $b_val = 100; + + $a_val = $order->{$a} if defined($order->{$a}); + $b_val = $order->{$b} if defined($order->{$b}); + + if($a =~ /bgp (\d+)$/) { + $a_val = 2; + } + + if($b =~ /bgp (\d+)$/) { + $b_val = 2; + } + + return $a_val <=> $b_val; +} + +sub generate_frr_recurse{ + my ($final_config, $content, $parentkey, $level) = @_; + + my $keylist = {}; + $keylist->{vrf} = 1; + $keylist->{'address-family'} = 1; + $keylist->{router} = 1; + + my $exitkeylist = {}; + $exitkeylist->{vrf} = 1; + $exitkeylist->{'address-family'} = 1; + + #fix me, make this generic + my $paddinglevel = undef; + if($level == 1 || $level == 2) { + $paddinglevel = $level - 1; + } elsif ($level == 3 || $level == 4) { + $paddinglevel = $level - 2; + } + + my $padding = ""; + $padding = ' ' x ($paddinglevel) if $paddinglevel; + + if (ref $content eq ref {}) { + foreach my $key (sort sort_frr_config keys %$content) { + if ($parentkey && defined($keylist->{$parentkey})) { + push @{$final_config}, $padding."!"; + push @{$final_config}, $padding."$parentkey $key"; + } else { + push @{$final_config}, $padding."$key" if $key ne '' && !defined($keylist->{$key}); + } + + my $option = $content->{$key}; + generate_frr_recurse($final_config, $option, $key, $level+1); + + push @{$final_config}, $padding."exit-$parentkey" if $parentkey && defined($exitkeylist->{$parentkey}); + } + } + if (ref $content eq 'ARRAY') { + foreach my $value (@$content) { + push @{$final_config}, $padding."$value"; + } + } +} + +sub write_controller_config { + my ($class, $plugin_config, $config) = @_; + + my $final_config = []; + push @{$final_config}, "log syslog informational"; + push @{$final_config}, "!"; + + generate_frr_recurse($final_config, $config->{frr}, undef, 0); + + push @{$final_config}, "!"; + push @{$final_config}, "line vty"; + push @{$final_config}, "!"; + + my $rawconfig = join("\n", @{$final_config}); + + + return if !$rawconfig; + return if !-d "/etc/frr"; + + my $frr_config_file = "/etc/frr/frr.conf"; + + my $writefh = IO::File->new($frr_config_file,">"); + print $writefh $rawconfig; + $writefh->close(); +} + +1; diff --git a/PVE/Network/SDN/Plugin.pm b/PVE/Network/SDN/Plugin.pm index 1c58049..9428aea 100644 --- a/PVE/Network/SDN/Plugin.pm +++ b/PVE/Network/SDN/Plugin.pm @@ -71,12 +71,18 @@ sub generate_sdn_config { die "please implement inside plugin"; } -sub generate_frr_config { +sub generate_controller_config { my ($class, $plugin_config, $router, $id, $uplinks, $config) = @_; die "please implement inside plugin"; } +sub write_controller_config { + my ($class, $plugin_config, $config) = @_; + + die "please implement inside plugin"; +} + sub on_delete_hook { my ($class, $sndid, $scfg) = @_; diff --git a/PVE/Network/SDN/VlanPlugin.pm b/PVE/Network/SDN/VlanPlugin.pm index 2af24b7..6078206 100644 --- a/PVE/Network/SDN/VlanPlugin.pm +++ b/PVE/Network/SDN/VlanPlugin.pm @@ -10,6 +10,12 @@ 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) = @_; diff --git a/PVE/Network/SDN/VnetPlugin.pm b/PVE/Network/SDN/VnetPlugin.pm index e8aa204..e918564 100644 --- a/PVE/Network/SDN/VnetPlugin.pm +++ b/PVE/Network/SDN/VnetPlugin.pm @@ -12,6 +12,12 @@ sub type { return 'vnet'; } +sub plugindata { + return { + role => 'vnet', + }; +} + sub properties { return { transportzone => { diff --git a/PVE/Network/SDN/VxlanPlugin.pm b/PVE/Network/SDN/VxlanPlugin.pm index 1860490..986a250 100644 --- a/PVE/Network/SDN/VxlanPlugin.pm +++ b/PVE/Network/SDN/VxlanPlugin.pm @@ -40,6 +40,12 @@ sub type { return 'vxlan'; } +sub plugindata { + return { + role => 'transport', + }; +} + sub properties { return { 'vxlan-allowed' => { @@ -62,7 +68,7 @@ sub properties { type => 'integer', description => "l3vni.", }, - 'router' => { + 'controller' => { type => 'string', description => "Frr router name", }, @@ -78,7 +84,7 @@ sub options { 'vxlan-allowed' => { optional => 1 }, 'vrf' => { optional => 1 }, 'vrf-vxlan' => { optional => 1 }, - 'router' => { optional => 1 }, + 'controller' => { optional => 1 }, }; } @@ -182,7 +188,7 @@ sub generate_sdn_config { return $config; } -sub generate_frr_config { +sub generate_controller_config { my ($class, $plugin_config, $router, $id, $uplinks, $config) = @_; my $vrf = $plugin_config->{'vrf'}; @@ -195,7 +201,7 @@ sub generate_frr_config { #vrf my @router_config = (); push @router_config, "vni $vrfvxlan"; - push(@{$config->{vrf}->{"$vrf"}}, @router_config); + push(@{$config->{frr}->{vrf}->{"$vrf"}}, @router_config); @router_config = (); @@ -213,20 +219,20 @@ sub generate_frr_config { #frr 7.1 tag is bugged -> works fine with 7.1 stable branch(20190829-02-g6ba76bbc1) #https://github.com/FRRouting/frr/issues/4905 push @router_config, "import vrf $vrf"; - push(@{$config->{router}->{"bgp $asn"}->{"address-family"}->{"ipv4 unicast"}}, @router_config); - push(@{$config->{router}->{"bgp $asn"}->{"address-family"}->{"ipv6 unicast"}}, @router_config); + push(@{$config->{frr}->{router}->{"bgp $asn"}->{"address-family"}->{"ipv4 unicast"}}, @router_config); + push(@{$config->{frr}->{router}->{"bgp $asn"}->{"address-family"}->{"ipv6 unicast"}}, @router_config); @router_config = (); #redistribute connected to be able to route to local vms on the gateway push @router_config, "redistribute connected"; - push(@{$config->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"ipv4 unicast"}}, @router_config); - push(@{$config->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"ipv6 unicast"}}, @router_config); + push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"ipv4 unicast"}}, @router_config); + push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"ipv6 unicast"}}, @router_config); @router_config = (); #add default originate to announce 0.0.0.0/0 type5 route in evpn push @router_config, "default-originate ipv4"; push @router_config, "default-originate ipv6"; - push(@{$config->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"l2vpn evpn"}}, @router_config); + push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"l2vpn evpn"}}, @router_config); } return $config; diff --git a/test/documentation.txt b/test/documentation.txt index ecaf4bd..785b21c 100644 --- a/test/documentation.txt +++ b/test/documentation.txt @@ -11,14 +11,14 @@ pvesh create /cluster/sdn/ --sdn vxlanmulticastzone --type vxlan --uplink-id 1 - #create a layer2 vxlan unicast transportzone pvesh create /cluster/sdn/ --sdn vxlanunicastzone --type vxlan --uplink-id 1 --unicast-address 192.168.0.1,192.168.0.2,192.168.0.3 -#create a frr router +#create a frr controller pvesh create /cluster/sdn/ --sdn frrrouter1 --type frr --uplink-id 1 --peers 192.168.0.1,192.168.0.2,192.168.0.3 --asn 1234 --gateway-nodes pxnode1,pxnode2 --gateway-external-peers 192.168.0.253,192.168.0.254 #create a layer2 vxlan bgpevpn transportzone -pvesh create /cluster/sdn/ --sdn layer2evpnzone --type vxlan --uplink-id 1 --router frrrouter1 +pvesh create /cluster/sdn/ --sdn layer2evpnzone --type vxlan --uplink-id 1 --controller frrrouter1 #create a layer3 routable vxlan bgpevpn transportzone -pvesh create /cluster/sdn/ --sdn layer3evpnzone --type vxlan --uplink-id 1 --router frrrouter1 --vrf vrf1 --vrf-vxlan 4000 +pvesh create /cluster/sdn/ --sdn layer3evpnzone --type vxlan --uplink-id 1 --controller frrrouter1 --vrf vrf1 --vrf-vxlan 4000 #create a vnet in the transportzone diff --git a/test/generateconfig.pl b/test/generateconfig.pl index dda9b8e..da82672 100644 --- a/test/generateconfig.pl +++ b/test/generateconfig.pl @@ -4,7 +4,7 @@ use File::Copy; use PVE::Cluster qw(cfs_read_file); use PVE::Network::SDN; - +use Data::Dumper; my $network_config = PVE::Network::SDN::generate_etc_network_config(); @@ -14,9 +14,9 @@ print $network_config; print "\n"; -my $frr_config = PVE::Network::SDN::generate_frr_config(); -if ($frr_config) { - PVE::Network::SDN::write_frr_config($frr_config); +my $controller_config = PVE::Network::SDN::generate_controller_config(); +if ($controller_config) { + print Dumper($controller_config); + PVE::Network::SDN::write_controller_config($controller_config); print "/etc/frr/frr.conf\n"; - print $frr_config; } -- 2.39.5