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