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