]> git.proxmox.com Git - pve-network.git/blobdiff - PVE/Network/SDN.pm
subnet: disable route option for now and add dns domain format
[pve-network.git] / PVE / Network / SDN.pm
index d72b94aa8cf46a2ef6d07d8d59e0a6df5c119265..85faca06b49ddfff42eff5422881f5e0b95eb285 100644 (file)
@@ -6,66 +6,37 @@ use warnings;
 use Data::Dumper;
 use JSON;
 
+use PVE::Network::SDN::Vnets;
+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::FrrPlugin;
-
-PVE::Network::SDN::VnetPlugin->register();
-PVE::Network::SDN::VlanPlugin->register();
-PVE::Network::SDN::VxlanPlugin->register();
-PVE::Network::SDN::FrrPlugin->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);
+my $version_cfg = "sdn/.version";
 
-    return $scfg;
-}
-
-sub config {
-    my $config = cfs_read_file("sdn.cfg.new");
-    $config = cfs_read_file("sdn.cfg") if !keys %{$config->{ids}};
-    return $config;
-}
+my $parse_version_cfg = sub {
+    my ($filename, $raw) = @_;
 
-sub write_config {
-    my ($cfg) = @_;
+    return 0 if !defined($raw) || $raw eq '';
 
-    cfs_write_file("sdn.cfg.new", $cfg);
-}
+    warn "invalid sdn version '$raw'" if $raw !~ m/\d+$/;
 
-sub lock_sdn_config {
-    my ($code, $errmsg) = @_;
+    return $raw,
+};
 
-    cfs_lock_file("sdn.cfg.new", undef, $code);
-    if (my $err = $@) {
-        $errmsg ? die "$errmsg: $err" : die $err;
-    }
-}
+my $write_version_cfg = sub {
+    my ($filename, $version) = @_;
 
-sub sdn_ids {
-    my ($cfg) = @_;
+    warn "invalid sdn version" if $version !~ m/\d+$/;
 
-    return keys %{$cfg->{ids}};
-}
+    return $version;
+};
 
-sub complete_sdn {
-    my ($cmdname, $pname, $cvalue) = @_;
+PVE::Cluster::cfs_register_file($version_cfg, $parse_version_cfg, $write_version_cfg);
 
-    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 {
 
@@ -93,296 +64,80 @@ sub ifquery_check {
     return $interfaces;
 }
 
+sub status {
 
-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};
+    my ($zone_status, $vnet_status) = PVE::Network::SDN::Zones::status();
+    return($zone_status, $vnet_status);
+}
 
-       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);
-    }
+sub increase_version {
 
-    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";
-       }
+    my $version = cfs_read_file($version_cfg);
+    if ($version) {
+       $version++;
+    } else {
+       $version = 1;
     }
 
-    return $raw_network_config;
+    cfs_write_file($version_cfg, $version);
 }
 
-sub generate_frr_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;
-       }
-    }
+sub lock_sdn_config {
+    my ($code, $errmsg) = @_;
 
-    my $frr_cfg = undef;
-    my $transport_cfg = undef;
+    cfs_lock_file($version_cfg, undef, $code);
 
-    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};
-       }
+    if (my $err = $@) {
+        $errmsg ? die "$errmsg: $err" : die $err;
     }
+}
 
-    return undef if !$frr_cfg;
+sub get_local_vnets {
 
-    #generate configuration
-    my $config = {};
+    my $rpcenv = PVE::RPCEnvironment::get();
 
-    foreach my $id (keys %{$frr_cfg->{ids}}) {
-       my $plugin_config = $frr_cfg->{ids}->{$id};
-       my $asn = $plugin_config->{asn};
-       if ($asn) {
-           my $plugin = PVE::Network::SDN::Plugin->lookup($plugin_config->{type});
-           $plugin->generate_frr_config($plugin_config, $asn, $id, $uplinks, $config);
-       }
-    }
+    my $authuser = $rpcenv->get_user();
 
-    foreach my $id (keys %{$transport_cfg->{ids}}) {
-       my $plugin_config = $transport_cfg->{ids}->{$id};
-       my $router = $plugin_config->{router};
-       if ($router) {
-           my $asn = $frr_cfg->{ids}->{$router}->{asn};
-           if ($asn) {
-               my $plugin = PVE::Network::SDN::Plugin->lookup($plugin_config->{type});
-               $plugin->generate_frr_config($plugin_config, $asn, $id, $uplinks, $config);
-           }
-       }
-    }
+    my $nodename = PVE::INotify::nodename();
 
-    my $final_config = [];
-    push @{$final_config}, "log syslog informational";
+    my $vnets_cfg = PVE::Network::SDN::Vnets::config();
+    my $zones_cfg = PVE::Network::SDN::Zones::config();
 
-    generate_frr_recurse($final_config, $config, undef, 0);
+    my @vnetids = PVE::Network::SDN::Vnets::sdn_vnets_ids($vnets_cfg);
 
-    push @{$final_config}, "!";
-    push @{$final_config}, "line vty";
-    push @{$final_config}, "!";
+    my $vnets = {};
 
-    my $raw_frr_config = join("\n", @{$final_config});
-    return $raw_frr_config;
-}
+    foreach my $vnetid (@vnetids) {
 
-sub sort_frr_config {
-    my $order = {};
-    $order->{''} = 0;
-    $order->{'vrf'} = 1;
-    $order->{'ipv4 unicast'} = 1;
-    $order->{'l2vpn evpn'} = 2;
-
-    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;
-    }
+       my $vnet = PVE::Network::SDN::Vnets::sdn_vnets_config($vnets_cfg, $vnetid);
+       my $zoneid = $vnet->{zone};
+       my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
 
-    return $a_val <=> $b_val;
-}
+       next if !$zoneid;
+       next if !$rpcenv->check_any($authuser, "/sdn/zones/$zoneid", $privs, 1);
 
-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});
-       }
-    }
+       my $zone_config = PVE::Network::SDN::Zones::sdn_zones_config($zones_cfg, $zoneid);
 
-    if (ref $content eq 'ARRAY') {
-       foreach my $value (@$content) {
-           push @{$final_config}, $padding."$value";
-       }
+       next if defined($zone_config->{nodes}) && !$zone_config->{nodes}->{$nodename};
+       $vnets->{$vnetid} = { type => 'vnet', active => '1' };
     }
-}
-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();
+    return $vnets;
 }
 
-sub write_frr_config {
-    my ($rawconfig) = @_;
-
-    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();
+sub generate_zone_config {
+    my $raw_config = PVE::Network::SDN::Zones::generate_etc_network_config();
+    PVE::Network::SDN::Zones::write_etc_network_config($raw_config);
 }
 
+sub generate_controller_config {
+    my ($reload) = @_;
 
-sub status {
-
-    my $cluster_sdn_file = "/etc/pve/sdn.cfg";
-    my $local_sdn_file = "/etc/network/interfaces.d/sdn";
-    my $err_config = undef;
+    my $raw_config = PVE::Network::SDN::Controllers::generate_controller_config();
+    PVE::Network::SDN::Controllers::write_controller_config($raw_config);
 
-    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';
-           }
-       }
-    }
-    return($transport_status, $vnet_status);
+    PVE::Network::SDN::Controllers::reload_controller() if $reload;
 }
 
 1;