]> git.proxmox.com Git - pve-network.git/blame - PVE/Network/SDN/Zones.pm
zones: add del_bridge_fdb
[pve-network.git] / PVE / Network / SDN / Zones.pm
CommitLineData
f5eabba0
AD
1package PVE::Network::SDN::Zones;
2
3use strict;
4use warnings;
5
f5eabba0
AD
6use JSON;
7
8use PVE::Tools qw(extract_param dir_glob_regex run_command);
9use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
eb1549e7 10use PVE::Network;
f5eabba0
AD
11
12use PVE::Network::SDN::Vnets;
13use PVE::Network::SDN::Zones::VlanPlugin;
14use PVE::Network::SDN::Zones::QinQPlugin;
15use PVE::Network::SDN::Zones::VxlanPlugin;
16use PVE::Network::SDN::Zones::EvpnPlugin;
17use PVE::Network::SDN::Zones::FaucetPlugin;
880ae857 18use PVE::Network::SDN::Zones::SimplePlugin;
f5eabba0
AD
19use PVE::Network::SDN::Zones::Plugin;
20
21PVE::Network::SDN::Zones::VlanPlugin->register();
22PVE::Network::SDN::Zones::QinQPlugin->register();
23PVE::Network::SDN::Zones::VxlanPlugin->register();
24PVE::Network::SDN::Zones::EvpnPlugin->register();
25PVE::Network::SDN::Zones::FaucetPlugin->register();
880ae857 26PVE::Network::SDN::Zones::SimplePlugin->register();
f5eabba0
AD
27PVE::Network::SDN::Zones::Plugin->init();
28
0814c9a9 29my $local_network_sdn_file = "/etc/network/interfaces.d/sdn";
f5eabba0
AD
30
31sub sdn_zones_config {
32 my ($cfg, $id, $noerr) = @_;
33
34 die "no sdn zone ID specified\n" if !$id;
35
36 my $scfg = $cfg->{ids}->{$id};
b2d83056 37 die "sdn '$id' does not exist\n" if (!$noerr && !$scfg);
f5eabba0
AD
38
39 return $scfg;
40}
41
42sub config {
f703d2ae 43 my $config = cfs_read_file("sdn/zones.cfg");
f5eabba0
AD
44 return $config;
45}
46
41d40fb1
TL
47sub get_plugin_config {
48 my ($vnet) = @_;
49 my $zoneid = $vnet->{zone};
50 my $zone_cfg = PVE::Network::SDN::Zones::config();
51 return $zone_cfg->{ids}->{$zoneid};
52}
53
f5eabba0
AD
54sub write_config {
55 my ($cfg) = @_;
56
f703d2ae 57 cfs_write_file("sdn/zones.cfg", $cfg);
f5eabba0
AD
58}
59
f5eabba0
AD
60sub sdn_zones_ids {
61 my ($cfg) = @_;
62
b184ebc3 63 return sort keys %{$cfg->{ids}};
f5eabba0
AD
64}
65
66sub complete_sdn_zone {
67 my ($cmdname, $pname, $cvalue) = @_;
68
d73c7c36 69 my $cfg = PVE::Network::SDN::running_config();
f5eabba0
AD
70
71 return $cmdname eq 'add' ? [] : [ PVE::Network::SDN::sdn_zones_ids($cfg) ];
72}
73
4ad78442
AD
74sub get_zone {
75 my ($zoneid, $running) = @_;
76
77 my $cfg = {};
78 if($running) {
d73c7c36 79 my $cfg = PVE::Network::SDN::running_config();
4ad78442
AD
80 $cfg = $cfg->{vnets};
81 } else {
82 $cfg = PVE::Network::SDN::Zones::config();
83 }
84
85 my $zone = PVE::Network::SDN::Zones::sdn_zones_config($cfg, $zoneid, 1);
86
87 return $zone;
88}
89
f5eabba0
AD
90
91sub generate_etc_network_config {
92
d73c7c36 93 my $cfg = PVE::Network::SDN::running_config();
5d3e0248
AD
94
95 my $version = $cfg->{version};
96 my $vnet_cfg = $cfg->{vnets};
97 my $zone_cfg = $cfg->{zones};
98 my $subnet_cfg = $cfg->{subnets};
99 my $controller_cfg = $cfg->{controllers};
56cdcac9 100 return if !$vnet_cfg && !$zone_cfg;
f5eabba0 101
ba7ac021 102 my $interfaces_config = PVE::INotify::read_file('interfaces');
f5eabba0
AD
103
104 #generate configuration
105 my $config = {};
c2b9c173
AD
106 my $nodename = PVE::INotify::nodename();
107
e0c19383 108 for my $id (sort keys %{$vnet_cfg->{ids}}) {
f5eabba0 109 my $vnet = $vnet_cfg->{ids}->{$id};
3fd3e917 110 my $zone = $vnet->{zone};
f5eabba0 111
e0c19383
TL
112 if (!$zone) {
113 warn "can't generate vnet '$id': no zone assigned!\n";
f5eabba0
AD
114 next;
115 }
116
56cdcac9 117 my $plugin_config = $zone_cfg->{ids}->{$zone};
f5eabba0
AD
118
119 if (!defined($plugin_config)) {
e0c19383 120 warn "can't generate vnet '$id': zone $zone don't exist\n";
f5eabba0
AD
121 next;
122 }
123
c2b9c173
AD
124 next if defined($plugin_config->{nodes}) && !$plugin_config->{nodes}->{$nodename};
125
e0c19383
TL
126 my $controller;
127 if (my $controllerid = $plugin_config->{controller}) {
128 $controller = $controller_cfg->{ids}->{$controllerid};
4405f2de
AD
129 }
130
f5eabba0 131 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
ae3f4de8 132 eval {
efffa0ff 133 $plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config);
ae3f4de8 134 };
cd01a0c0
TL
135 if (my $err = $@) {
136 warn "zone $zone : vnet $id : $err\n";
ae3f4de8
AD
137 next;
138 }
f5eabba0
AD
139 }
140
0814c9a9 141 my $raw_network_config = "\#version:$version\n";
2d4c068e 142 foreach my $iface (sort keys %$config) {
f5eabba0
AD
143 $raw_network_config .= "\n";
144 $raw_network_config .= "auto $iface\n";
145 $raw_network_config .= "iface $iface\n";
146 foreach my $option (@{$config->{$iface}}) {
147 $raw_network_config .= "\t$option\n";
148 }
149 }
150
151 return $raw_network_config;
152}
153
154sub write_etc_network_config {
155 my ($rawconfig) = @_;
156
157 return if !$rawconfig;
f5eabba0 158
0814c9a9 159 my $writefh = IO::File->new($local_network_sdn_file,">");
f5eabba0
AD
160 print $writefh $rawconfig;
161 $writefh->close();
162}
163
0814c9a9
AD
164sub read_etc_network_config_version {
165 my $versionstr = PVE::Tools::file_read_firstline($local_network_sdn_file);
06c87086
SI
166
167 return if !defined($versionstr);
168
0814c9a9
AD
169 if ($versionstr =~ m/^\#version:(\d+)$/) {
170 return $1;
171 }
172}
173
f5eabba0
AD
174sub ifquery_check {
175
176 my $cmd = ['ifquery', '-a', '-c', '-o','json'];
177
178 my $result = '';
179 my $reader = sub { $result .= shift };
180
181 eval {
182 run_command($cmd, outfunc => $reader);
183 };
184
185 my $resultjson = decode_json($result);
186 my $interfaces = {};
187
188 foreach my $interface (@$resultjson) {
189 my $name = $interface->{name};
190 $interfaces->{$name} = {
191 status => $interface->{status},
192 config => $interface->{config},
193 config_status => $interface->{config_status},
194 };
195 }
196
197 return $interfaces;
198}
199
9ddc4a6f 200my $warned_about_reload;
4d7cc94f 201
f5eabba0
AD
202sub status {
203
f5eabba0
AD
204 my $err_config = undef;
205
6f4d0610 206 my $local_version = PVE::Network::SDN::Zones::read_etc_network_config_version();
d73c7c36 207 my $cfg = PVE::Network::SDN::running_config();
5d3e0248 208 my $sdn_version = $cfg->{version};
f5eabba0 209
6f4d0610 210 return if !$sdn_version;
3709a203 211
6f4d0610 212 if (!$local_version) {
3709a203 213 $err_config = "local sdn network configuration is not yet generated, please reload";
9ddc4a6f
TL
214 if (!$warned_about_reload) {
215 $warned_about_reload = 1;
216 warn "$err_config\n";
217 }
6f4d0610
AD
218 } elsif ($local_version < $sdn_version) {
219 $err_config = "local sdn network configuration is too old, please reload";
9ddc4a6f
TL
220 if (!$warned_about_reload) {
221 $warned_about_reload = 1;
222 warn "$err_config\n";
223 }
224 } else {
225 $warned_about_reload = 0;
f5eabba0
AD
226 }
227
228 my $status = ifquery_check();
229
5d3e0248
AD
230 my $vnet_cfg = $cfg->{vnets};
231 my $zone_cfg = $cfg->{zones};
c2b9c173 232 my $nodename = PVE::INotify::nodename();
f5eabba0 233
0f700635 234 my $vnet_status = {};
56cdcac9 235 my $zone_status = {};
f5eabba0 236
fc197ef2 237 for my $id (sort keys %{$zone_cfg->{ids}}) {
1608c165 238 next if defined($zone_cfg->{ids}->{$id}->{nodes}) && !$zone_cfg->{ids}->{$id}->{nodes}->{$nodename};
fc197ef2 239 $zone_status->{$id}->{status} = $err_config ? 'pending' : 'available';
4d7cc94f
AD
240 }
241
70da0442
TL
242 foreach my $id (sort keys %{$vnet_cfg->{ids}}) {
243 my $vnet = $vnet_cfg->{ids}->{$id};
244 my $zone = $vnet->{zone};
6b0bf99c 245 next if !defined($zone);
627b1694 246
70da0442 247 my $plugin_config = $zone_cfg->{ids}->{$zone};
153cb80d
TL
248
249 if (!defined($plugin_config)) {
250 $vnet_status->{$id}->{status} = 'error';
251 $vnet_status->{$id}->{statusmsg} = "unknown zone '$zone' configured";
252 next;
253 }
254
70da0442
TL
255 next if defined($plugin_config->{nodes}) && !$plugin_config->{nodes}->{$nodename};
256
4d7cc94f
AD
257 $vnet_status->{$id}->{zone} = $zone;
258 $vnet_status->{$id}->{status} = 'available';
259
fc197ef2 260 if ($err_config) {
4d7cc94f
AD
261 $vnet_status->{$id}->{status} = 'pending';
262 $vnet_status->{$id}->{statusmsg} = $err_config;
263 next;
264 }
265
70da0442 266 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
4d7cc94f
AD
267 my $err_msg = $plugin->status($plugin_config, $zone, $id, $vnet, $status);
268 if (@{$err_msg} > 0) {
269 $vnet_status->{$id}->{status} = 'error';
270 $vnet_status->{$id}->{statusmsg} = join(',', @{$err_msg});
271 $zone_status->{$id}->{status} = 'error';
0f700635 272 }
f5eabba0 273 }
627b1694 274
fc197ef2 275 return ($zone_status, $vnet_status);
f5eabba0
AD
276}
277
eb1549e7
AD
278sub tap_create {
279 my ($iface, $bridge) = @_;
280
5d3e0248 281 my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1);
a1ef0eb9 282 if (!$vnet) { # fallback for classic bridge
eb1549e7 283 PVE::Network::tap_create($iface, $bridge);
a1ef0eb9 284 return;
eb1549e7
AD
285 }
286
41d40fb1 287 my $plugin_config = get_plugin_config($vnet);
eb1549e7
AD
288 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
289 $plugin->tap_create($plugin_config, $vnet, $iface, $bridge);
290}
291
292sub veth_create {
293 my ($veth, $vethpeer, $bridge, $hwaddr) = @_;
294
5d3e0248 295 my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1);
a1ef0eb9 296 if (!$vnet) { # fallback for classic bridge
eb1549e7 297 PVE::Network::veth_create($veth, $vethpeer, $bridge, $hwaddr);
a1ef0eb9 298 return;
eb1549e7
AD
299 }
300
41d40fb1 301 my $plugin_config = get_plugin_config($vnet);
eb1549e7
AD
302 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
303 $plugin->veth_create($plugin_config, $vnet, $veth, $vethpeer, $bridge, $hwaddr);
304}
305
306sub tap_plug {
307 my ($iface, $bridge, $tag, $firewall, $trunks, $rate) = @_;
308
5d3e0248 309 my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1);
a1ef0eb9 310 if (!$vnet) { # fallback for classic bridge
56a9e2b3
AD
311 my $interfaces_config = PVE::INotify::read_file('interfaces');
312 my $disablelearning = 1 if $interfaces_config->{ifaces}->{$bridge} && $interfaces_config->{ifaces}->{$bridge}->{'bridge-disable-mac-learning'};
313 PVE::Network::tap_plug($iface, $bridge, $tag, $firewall, $trunks, $rate, $disablelearning);
eb1549e7
AD
314 return;
315 }
316
41d40fb1 317 my $plugin_config = get_plugin_config($vnet);
eb1549e7
AD
318 my $nodename = PVE::INotify::nodename();
319
41d40fb1
TL
320 die "vnet $bridge is not allowed on this node\n"
321 if $plugin_config->{nodes} && !defined($plugin_config->{nodes}->{$nodename});
2ba9613b 322
2ba9613b 323 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
912fb443 324 $plugin->tap_plug($plugin_config, $vnet, $tag, $iface, $bridge, $firewall, $trunks, $rate);
2ba9613b
AD
325}
326
fef63d88
AD
327sub add_bridge_fdb {
328 my ($iface, $macaddr, $bridge, $firewall) = @_;
329
330 my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1);
331 if (!$vnet) { # fallback for classic bridge
332 PVE::Network::add_bridge_fdb($iface, $macaddr, $firewall);
333 return;
334 }
335
336 my $plugin_config = get_plugin_config($vnet);
337 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
338 PVE::Network::add_bridge_fdb($iface, $macaddr, $firewall) if $plugin_config->{'bridge-disable-mac-learning'};
339}
340
824252f6
AD
341sub del_bridge_fdb {
342 my ($iface, $macaddr, $bridge, $firewall) = @_;
343
344 my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1);
345 if (!$vnet) { # fallback for classic bridge
346 PVE::Network::del_bridge_fdb($iface, $macaddr, $firewall);
347 return;
348 }
349
350 my $plugin_config = get_plugin_config($vnet);
351 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
352 PVE::Network::del_bridge_fdb($iface, $macaddr, $firewall) if $plugin_config->{'bridge-disable-mac-learning'};
353}
354
f5eabba0
AD
3551;
356