]>
Commit | Line | Data |
---|---|---|
f5eabba0 | 1 | package PVE::Network::SDN::Zones::QinQPlugin; |
20e19696 AD |
2 | |
3 | use strict; | |
4 | use warnings; | |
3dfdc684 | 5 | use PVE::Network::SDN::Zones::Plugin; |
1d44ce70 | 6 | use PVE::Exception qw(raise raise_param_exc); |
20e19696 | 7 | |
3dfdc684 | 8 | use base('PVE::Network::SDN::Zones::Plugin'); |
20e19696 AD |
9 | |
10 | sub type { | |
11 | return 'qinq'; | |
12 | } | |
13 | ||
20e19696 AD |
14 | sub properties { |
15 | return { | |
1c95991c TL |
16 | tag => { |
17 | type => 'integer', | |
18 | minimum => 0, | |
19 | description => "Service-VLAN Tag", | |
20 | }, | |
823f2e2a AD |
21 | mtu => { |
22 | type => 'integer', | |
1c95991c | 23 | description => "MTU", |
823f2e2a AD |
24 | optional => 1, |
25 | }, | |
3dfdc684 AD |
26 | 'vlan-protocol' => { |
27 | type => 'string', | |
28 | enum => ['802.1q', '802.1ad'], | |
29 | default => '802.1q', | |
30 | optional => 1, | |
31 | } | |
20e19696 AD |
32 | }; |
33 | } | |
34 | ||
35 | sub options { | |
36 | ||
37 | return { | |
c2b9c173 | 38 | nodes => { optional => 1}, |
20e19696 | 39 | 'tag' => { optional => 0 }, |
938ebef7 | 40 | 'bridge' => { optional => 0 }, |
823f2e2a | 41 | 'mtu' => { optional => 1 }, |
3dfdc684 | 42 | 'vlan-protocol' => { optional => 1 }, |
4ad78442 AD |
43 | dns => { optional => 1 }, |
44 | reversedns => { optional => 1 }, | |
45 | dnszone => { optional => 1 }, | |
57a335c4 | 46 | ipam => { optional => 1 }, |
20e19696 AD |
47 | }; |
48 | } | |
49 | ||
50 | # Plugin implementation | |
51 | sub generate_sdn_config { | |
efffa0ff | 52 | my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_; |
20e19696 | 53 | |
3dfdc684 | 54 | my $stag = $plugin_config->{tag}; |
938ebef7 AD |
55 | my $mtu = $plugin_config->{mtu}; |
56 | my $bridge = $plugin_config->{'bridge'}; | |
3dfdc684 AD |
57 | my $vlanprotocol = $plugin_config->{'vlan-protocol'}; |
58 | my $ctag = $vnet->{tag}; | |
59 | my $alias = $vnet->{alias}; | |
20e19696 | 60 | |
fdf22d5f AD |
61 | PVE::Network::SDN::Zones::Plugin::find_bridge($bridge); |
62 | ||
63 | my $vlan_aware = PVE::Network::SDN::Zones::Plugin::is_vlanaware($bridge); | |
64 | my $is_ovs = PVE::Network::SDN::Zones::Plugin::is_ovs($bridge); | |
20e19696 | 65 | |
3dfdc684 AD |
66 | my @iface_config = (); |
67 | my $vnet_bridge_ports = ""; | |
403b05e2 AD |
68 | my $zone_bridge_ports = ""; |
69 | my $zone_notag_uplink = "ln_".$zoneid; | |
70 | my $zone_notag_uplinkpeer = "pr_".$zoneid; | |
71 | my $zone = "z_$zoneid"; | |
72 | ||
73 | if($ctag) { | |
74 | $vnet_bridge_ports = "$zone.$ctag"; | |
75 | } else { | |
76 | $vnet_bridge_ports = $zone_notag_uplinkpeer; | |
77 | } | |
3dfdc684 AD |
78 | |
79 | if($is_ovs) { | |
80 | ||
81 | #ovs--->ovsintport(dot1q-tunnel tag)------->vlanawarebrige-----(tag)--->vnet | |
82 | ||
83 | $vlanprotocol = "802.1q" if !$vlanprotocol; | |
84 | my $svlan_iface = "sv_".$zoneid; | |
3dfdc684 AD |
85 | |
86 | #ovs dot1q-tunnel port | |
87 | @iface_config = (); | |
88 | push @iface_config, "ovs_type OVSIntPort"; | |
89 | push @iface_config, "ovs_bridge $bridge"; | |
82159388 | 90 | push @iface_config, "ovs_mtu $mtu" if $mtu; |
3dfdc684 AD |
91 | push @iface_config, "ovs_options vlan_mode=dot1q-tunnel tag=$stag other_config:qinq-ethtype=$vlanprotocol"; |
92 | push(@{$config->{$svlan_iface}}, @iface_config) if !$config->{$svlan_iface}; | |
93 | ||
d8c88170 AD |
94 | #redefine main ovs bridge, ifupdown2 will merge ovs_ports |
95 | @{$config->{$bridge}}[0] = "ovs_ports" if !@{$config->{$bridge}}[0]; | |
96 | my @ovs_ports = split / / , @{$config->{$bridge}}[0]; | |
97 | @{$config->{$bridge}}[0] .= " $svlan_iface" if !grep( $_ eq $svlan_iface, @ovs_ports ); | |
3dfdc684 | 98 | |
403b05e2 | 99 | $zone_bridge_ports = $svlan_iface; |
3dfdc684 AD |
100 | |
101 | } elsif ($vlan_aware) { | |
102 | ||
103 | #vlanawarebrige-(tag)----->vlanwarebridge-(tag)----->vnet | |
938ebef7 | 104 | |
3dfdc684 AD |
105 | if($vlanprotocol) { |
106 | @iface_config = (); | |
107 | push @iface_config, "bridge-vlan-protocol $vlanprotocol"; | |
108 | push(@{$config->{$bridge}}, @iface_config) if !$config->{$bridge}; | |
109 | } | |
110 | ||
403b05e2 | 111 | $zone_bridge_ports = "$bridge.$stag"; |
3dfdc684 AD |
112 | |
113 | } else { | |
114 | ||
403b05e2 | 115 | #eth--->eth.x(svlan)----->vlanwarebridge-(tag)----->vnet---->vnet |
3dfdc684 | 116 | |
fdf22d5f | 117 | my @bridge_ifaces = PVE::Network::SDN::Zones::Plugin::get_bridge_ifaces($bridge); |
3dfdc684 AD |
118 | |
119 | foreach my $bridge_iface (@bridge_ifaces) { | |
120 | ||
121 | # use named vlan interface to avoid too long names | |
2fa5d392 | 122 | my $svlan_iface = "sv_$zoneid"; |
3dfdc684 AD |
123 | |
124 | #svlan | |
125 | @iface_config = (); | |
126 | push @iface_config, "vlan-raw-device $bridge_iface"; | |
127 | push @iface_config, "vlan-id $stag"; | |
128 | push @iface_config, "vlan-protocol $vlanprotocol" if $vlanprotocol; | |
129 | push(@{$config->{$svlan_iface}}, @iface_config) if !$config->{$svlan_iface}; | |
130 | ||
403b05e2 AD |
131 | $zone_bridge_ports = $svlan_iface; |
132 | last; | |
3dfdc684 AD |
133 | } |
134 | } | |
135 | ||
403b05e2 AD |
136 | #veth peer for notag vnet |
137 | @iface_config = (); | |
138 | push @iface_config, "link-type veth"; | |
139 | push @iface_config, "veth-peer-name $zone_notag_uplinkpeer"; | |
140 | push(@{$config->{$zone_notag_uplink}}, @iface_config) if !$config->{$zone_notag_uplink}; | |
141 | ||
142 | @iface_config = (); | |
143 | push @iface_config, "link-type veth"; | |
144 | push @iface_config, "veth-peer-name $zone_notag_uplink"; | |
145 | push(@{$config->{$zone_notag_uplinkpeer}}, @iface_config) if !$config->{$zone_notag_uplinkpeer}; | |
146 | ||
147 | #zone vlan aware bridge | |
148 | @iface_config = (); | |
149 | push @iface_config, "mtu $mtu" if $mtu; | |
150 | push @iface_config, "bridge-stp off"; | |
151 | push @iface_config, "bridge-ports $zone_bridge_ports $zone_notag_uplink"; | |
152 | push @iface_config, "bridge-fd 0"; | |
153 | push @iface_config, "bridge-vlan-aware yes"; | |
154 | push @iface_config, "bridge-vids 2-4094"; | |
155 | push(@{$config->{$zone}}, @iface_config) if !$config->{$zone}; | |
156 | ||
3dfdc684 AD |
157 | #vnet bridge |
158 | @iface_config = (); | |
159 | push @iface_config, "bridge_ports $vnet_bridge_ports"; | |
160 | push @iface_config, "bridge_stp off"; | |
161 | push @iface_config, "bridge_fd 0"; | |
912fb443 AD |
162 | if($vnet->{vlanaware}) { |
163 | push @iface_config, "bridge-vlan-aware yes"; | |
164 | push @iface_config, "bridge-vids 2-4094"; | |
165 | } | |
3dfdc684 AD |
166 | push @iface_config, "mtu $mtu" if $mtu; |
167 | push @iface_config, "alias $alias" if $alias; | |
168 | push(@{$config->{$vnetid}}, @iface_config) if !$config->{$vnetid}; | |
169 | ||
20e19696 AD |
170 | } |
171 | ||
58433186 | 172 | sub status { |
4d7cc94f | 173 | my ($class, $plugin_config, $zone, $vnetid, $vnet, $status) = @_; |
58433186 AD |
174 | |
175 | my $bridge = $plugin_config->{bridge}; | |
4d7cc94f AD |
176 | my $err_msg = []; |
177 | ||
178 | if (!-d "/sys/class/net/$bridge") { | |
179 | push @$err_msg, "missing $bridge"; | |
180 | return $err_msg; | |
181 | } | |
182 | ||
fdf22d5f | 183 | my $vlan_aware = PVE::Network::SDN::Zones::Plugin::is_vlanaware($bridge); |
4d7cc94f AD |
184 | |
185 | my $tag = $vnet->{tag}; | |
186 | my $vnet_uplink = "ln_".$vnetid; | |
187 | my $vnet_uplinkpeer = "pr_".$vnetid; | |
403b05e2 AD |
188 | my $zone_notag_uplink = "ln_".$zone; |
189 | my $zone_notag_uplinkpeer = "pr_".$zone; | |
190 | my $zonebridge = "z_$zone"; | |
4d7cc94f AD |
191 | |
192 | # ifaces to check | |
193 | my $ifaces = [ $vnetid, $bridge ]; | |
403b05e2 AD |
194 | |
195 | push @$ifaces, $zonebridge; | |
196 | push @$ifaces, $zone_notag_uplink; | |
197 | push @$ifaces, $zone_notag_uplinkpeer; | |
198 | ||
199 | if (!$vlan_aware) { | |
200 | my $svlan_iface = "sv_$zone"; | |
4d7cc94f | 201 | push @$ifaces, $svlan_iface; |
4d7cc94f AD |
202 | } |
203 | ||
204 | foreach my $iface (@{$ifaces}) { | |
205 | if (!$status->{$iface}->{status}) { | |
206 | push @$err_msg, "missing $iface"; | |
207 | } elsif ($status->{$iface}->{status} ne 'pass') { | |
208 | push @$err_msg, "error $iface"; | |
209 | } | |
58433186 | 210 | } |
4d7cc94f | 211 | return $err_msg; |
58433186 AD |
212 | } |
213 | ||
5ca07ed9 | 214 | sub vnet_update_hook { |
88d9562b AD |
215 | my ($class, $vnet_cfg, $vnetid, $zone_cfg) = @_; |
216 | ||
217 | my $vnet = $vnet_cfg->{ids}->{$vnetid}; | |
218 | my $tag = $vnet->{tag}; | |
1d44ce70 | 219 | |
403b05e2 | 220 | raise_param_exc({ tag => "vlan tag max value is 4096"}) if $tag && $tag > 4096; |
88d9562b AD |
221 | |
222 | # verify that tag is not already defined in another vnet on same zone | |
223 | foreach my $id (keys %{$vnet_cfg->{ids}}) { | |
224 | next if $id eq $vnetid; | |
225 | my $othervnet = $vnet_cfg->{ids}->{$id}; | |
226 | my $other_tag = $othervnet->{tag}; | |
227 | next if $vnet->{zone} ne $othervnet->{zone}; | |
228 | raise_param_exc({ tag => "tag $tag already exist in vnet $id"}) if $other_tag && $tag eq $other_tag; | |
403b05e2 | 229 | raise_param_exc({ tag => "vnet $id without tag already exist in this zone"}) if !$other_tag && !$tag; |
88d9562b | 230 | } |
1d44ce70 AD |
231 | } |
232 | ||
20e19696 AD |
233 | 1; |
234 | ||
235 |