]> git.proxmox.com Git - pve-network.git/blobdiff - PVE/Network/SDN/Zones/QinQPlugin.pm
zone: qinq: add vnet without tag support
[pve-network.git] / PVE / Network / SDN / Zones / QinQPlugin.pm
index fe43d4232e9052204ce762ab12120dacab2ba776..8282e35efd6c52f6722a1d7aabfd834f805ddc96 100644 (file)
@@ -3,6 +3,7 @@ package PVE::Network::SDN::Zones::QinQPlugin;
 use strict;
 use warnings;
 use PVE::Network::SDN::Zones::Plugin;
+use PVE::Exception qw(raise raise_param_exc);
 
 use base('PVE::Network::SDN::Zones::Plugin');
 
@@ -12,13 +13,14 @@ sub type {
 
 sub properties {
     return {
-        tag => {
-            type => 'integer',
-            description => "vlan tag",
-        },
+       tag => {
+           type => 'integer',
+           minimum => 0,
+           description => "Service-VLAN Tag",
+       },
        mtu => {
            type => 'integer',
-           description => "mtu",
+           description => "MTU",
            optional => 1,
        },
        'vlan-protocol' => {
@@ -38,12 +40,16 @@ sub options {
        'bridge' => { optional => 0 },
        'mtu' => { optional => 1 },
        'vlan-protocol' => { optional => 1 },
+       dns => { optional => 1 },
+       reversedns => { optional => 1 },
+       dnszone => { optional => 1 },
+       ipam => { optional => 1 },
     };
 }
 
 # Plugin implementation
 sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $interfaces_config, $config) = @_;
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_;
 
     my $stag = $plugin_config->{tag};
     my $mtu = $plugin_config->{mtu};
@@ -52,11 +58,23 @@ sub generate_sdn_config {
     my $ctag = $vnet->{tag};
     my $alias = $vnet->{alias};
 
-    my $vlan_aware = PVE::Tools::file_read_firstline("/sys/class/net/$bridge/bridge/vlan_filtering");
-    my $is_ovs = 1 if !-d "/sys/class/net/$bridge/brif";
+    PVE::Network::SDN::Zones::Plugin::find_bridge($bridge);
+
+    my $vlan_aware = PVE::Network::SDN::Zones::Plugin::is_vlanaware($bridge);
+    my $is_ovs = PVE::Network::SDN::Zones::Plugin::is_ovs($bridge);
 
     my @iface_config = ();
     my $vnet_bridge_ports = "";
+    my $zone_bridge_ports = "";
+    my $zone_notag_uplink = "ln_".$zoneid;
+    my $zone_notag_uplinkpeer = "pr_".$zoneid;
+    my $zone = "z_$zoneid";
+
+    if($ctag) {
+       $vnet_bridge_ports = "$zone.$ctag";
+    } else {
+       $vnet_bridge_ports = $zone_notag_uplinkpeer;
+    }
 
     if($is_ovs) {
 
@@ -64,67 +82,44 @@ sub generate_sdn_config {
 
        $vlanprotocol = "802.1q" if !$vlanprotocol;
        my $svlan_iface = "sv_".$zoneid;
-       my $zone = "z_$zoneid";
 
        #ovs dot1q-tunnel port
        @iface_config = ();
        push @iface_config, "ovs_type OVSIntPort";
        push @iface_config, "ovs_bridge $bridge";
+       push @iface_config, "ovs_mtu $mtu" if $mtu;
        push @iface_config, "ovs_options vlan_mode=dot1q-tunnel tag=$stag other_config:qinq-ethtype=$vlanprotocol";
        push(@{$config->{$svlan_iface}}, @iface_config) if !$config->{$svlan_iface};
 
+        #redefine main ovs bridge, ifupdown2 will merge ovs_ports
+       @{$config->{$bridge}}[0] = "ovs_ports" if !@{$config->{$bridge}}[0];
+       my @ovs_ports = split / / , @{$config->{$bridge}}[0];
+       @{$config->{$bridge}}[0] .= " $svlan_iface" if !grep( $_ eq $svlan_iface, @ovs_ports );
 
-       #zone vlan aware bridge
-       @iface_config = ();
-       push @iface_config, "mtu $mtu" if $mtu;
-       push @iface_config, "bridge-stp off";
-       push @iface_config, "bridge-ports $svlan_iface";
-       push @iface_config, "bridge-fd 0";
-       push @iface_config, "bridge-vlan-aware yes";
-       push @iface_config, "bridge-vids 2-4094";
-       push(@{$config->{$zone}}, @iface_config) if !$config->{$zone};
-
-       $vnet_bridge_ports = "$zone.$ctag";
+       $zone_bridge_ports = $svlan_iface;
 
     } elsif ($vlan_aware) {
 
         #vlanawarebrige-(tag)----->vlanwarebridge-(tag)----->vnet
 
-       my $zone = "z_$zoneid";
-
        if($vlanprotocol) {
            @iface_config = ();
            push @iface_config, "bridge-vlan-protocol $vlanprotocol";
            push(@{$config->{$bridge}}, @iface_config) if !$config->{$bridge};
        }
 
-       #zone vlan bridge
-       @iface_config = ();
-       push @iface_config, "mtu $mtu" if $mtu;
-       push @iface_config, "bridge-stp off";
-       push @iface_config, "bridge-ports $bridge.$stag";
-       push @iface_config, "bridge-fd 0";
-       push @iface_config, "bridge-vlan-aware yes";
-       push @iface_config, "bridge-vids 2-4094";
-       push(@{$config->{$zone}}, @iface_config) if !$config->{$zone};
-
-       $vnet_bridge_ports = "$zone.$ctag";
+       $zone_bridge_ports = "$bridge.$stag";
 
     } else {
 
-       #eth--->eth.x(svlan)--->eth.x.y(cvlan)---->vnet
+       #eth--->eth.x(svlan)----->vlanwarebridge-(tag)----->vnet---->vnet
 
-       my @bridge_ifaces = ();
-       my $dir = "/sys/class/net/$bridge/brif";
-       PVE::Tools::dir_glob_foreach($dir, '(((eth|bond)\d+|en[^.]+)(\.\d+)?)', sub {
-           push @bridge_ifaces, $_[0];
-       });
+       my @bridge_ifaces = PVE::Network::SDN::Zones::Plugin::get_bridge_ifaces($bridge);
 
        foreach my $bridge_iface (@bridge_ifaces) {
 
            # use named vlan interface to avoid too long names
-           my $svlan_iface = "sv_$vnetid";
-           my $cvlan_iface = "cv_$vnetid";
+           my $svlan_iface = "sv_$zoneid";
 
            #svlan
            @iface_config = ();
@@ -133,21 +128,41 @@ sub generate_sdn_config {
            push @iface_config, "vlan-protocol $vlanprotocol" if $vlanprotocol;
            push(@{$config->{$svlan_iface}}, @iface_config) if !$config->{$svlan_iface};
 
-           #cvlan
-           @iface_config = ();
-           push @iface_config, "vlan-raw-device $svlan_iface";
-           push @iface_config, "vlan-id $ctag";
-           push(@{$config->{$cvlan_iface}}, @iface_config) if !$config->{$cvlan_iface};
-
-           $vnet_bridge_ports .= " $cvlan_iface";
+           $zone_bridge_ports = $svlan_iface;
+           last;
         }
    }
 
+    #veth peer for notag vnet
+    @iface_config = ();
+    push @iface_config, "link-type veth";
+    push @iface_config, "veth-peer-name $zone_notag_uplinkpeer";
+    push(@{$config->{$zone_notag_uplink}}, @iface_config) if !$config->{$zone_notag_uplink};
+
+    @iface_config = ();
+    push @iface_config, "link-type veth";
+    push @iface_config, "veth-peer-name $zone_notag_uplink";
+    push(@{$config->{$zone_notag_uplinkpeer}}, @iface_config) if !$config->{$zone_notag_uplinkpeer};
+
+    #zone vlan aware bridge
+    @iface_config = ();
+    push @iface_config, "mtu $mtu" if $mtu;
+    push @iface_config, "bridge-stp off";
+    push @iface_config, "bridge-ports $zone_bridge_ports $zone_notag_uplink";
+    push @iface_config, "bridge-fd 0";
+    push @iface_config, "bridge-vlan-aware yes";
+    push @iface_config, "bridge-vids 2-4094";
+    push(@{$config->{$zone}}, @iface_config) if !$config->{$zone};
+
     #vnet bridge
     @iface_config = ();
     push @iface_config, "bridge_ports $vnet_bridge_ports";
     push @iface_config, "bridge_stp off";
     push @iface_config, "bridge_fd 0";
+    if($vnet->{vlanaware}) {
+       push @iface_config, "bridge-vlan-aware yes";
+       push @iface_config, "bridge-vids 2-4094";
+    }
     push @iface_config, "mtu $mtu" if $mtu;
     push @iface_config, "alias $alias" if $alias;
     push(@{$config->{$vnetid}}, @iface_config) if !$config->{$vnetid};
@@ -155,22 +170,63 @@ sub generate_sdn_config {
 }
 
 sub status {
-    my ($class, $plugin_config, $zone, $id, $vnet, $err_config, $status, $vnet_status, $zone_status) = @_;
+    my ($class, $plugin_config, $zone, $vnetid, $vnet, $status) = @_;
 
     my $bridge = $plugin_config->{bridge};
-    $vnet_status->{$id}->{zone} = $zone;
-    $zone_status->{$zone}->{status} = 'available' if !defined($zone_status->{$zone}->{status});
-
-    if($err_config) {
-       $vnet_status->{$id}->{status} = 'pending';
-       $vnet_status->{$id}->{statusmsg} = $err_config;
-       $zone_status->{$zone}->{status} = 'pending';
-    } elsif ($status->{$bridge}->{status} && $status->{$bridge}->{status} eq 'pass') {
-       $vnet_status->{$id}->{status} = 'available';
-    } else {
-       $vnet_status->{$id}->{status} = 'error';
-       $vnet_status->{$id}->{statusmsg} = 'missing bridge';
-       $zone_status->{$zone}->{status} = 'error';
+    my $err_msg = [];
+
+    if (!-d "/sys/class/net/$bridge") {
+        push @$err_msg, "missing $bridge";
+        return $err_msg;
+    }
+
+    my $vlan_aware = PVE::Network::SDN::Zones::Plugin::is_vlanaware($bridge);
+
+    my $tag = $vnet->{tag};
+    my $vnet_uplink = "ln_".$vnetid;
+    my $vnet_uplinkpeer = "pr_".$vnetid;
+    my $zone_notag_uplink = "ln_".$zone;
+    my $zone_notag_uplinkpeer = "pr_".$zone;
+    my $zonebridge = "z_$zone";
+
+    # ifaces to check
+    my $ifaces = [ $vnetid, $bridge ];
+
+    push @$ifaces, $zonebridge;
+    push @$ifaces, $zone_notag_uplink;
+    push @$ifaces, $zone_notag_uplinkpeer;
+
+    if (!$vlan_aware) {
+       my $svlan_iface = "sv_$zone";
+       push @$ifaces, $svlan_iface;
+    }
+
+    foreach my $iface (@{$ifaces}) {
+       if (!$status->{$iface}->{status}) {
+           push @$err_msg, "missing $iface";
+        } elsif ($status->{$iface}->{status} ne 'pass') {
+           push @$err_msg, "error $iface";
+       }
+    }
+    return $err_msg;
+}
+
+sub vnet_update_hook {
+    my ($class, $vnet_cfg, $vnetid, $zone_cfg) = @_;
+
+    my $vnet = $vnet_cfg->{ids}->{$vnetid};
+    my $tag = $vnet->{tag};
+
+    raise_param_exc({ tag => "vlan tag max value is 4096"}) if $tag && $tag > 4096;
+
+    # verify that tag is not already defined in another vnet on same zone
+    foreach my $id (keys %{$vnet_cfg->{ids}}) {
+       next if $id eq $vnetid;
+       my $othervnet = $vnet_cfg->{ids}->{$id};
+       my $other_tag = $othervnet->{tag};
+       next if $vnet->{zone} ne $othervnet->{zone};
+        raise_param_exc({ tag => "tag $tag already exist in vnet $id"}) if $other_tag && $tag eq $other_tag;
+       raise_param_exc({ tag => "vnet $id without tag already exist in this zone"}) if !$other_tag && !$tag;
     }
 }