]> git.proxmox.com Git - pve-network.git/blame - PVE/Network/SDN/Zones.pm
/etc/network/interfaces.d/sdn : add version
[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;
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();
26PVE::Network::SDN::Zones::Plugin->init();
27
0814c9a9 28my $local_network_sdn_file = "/etc/network/interfaces.d/sdn";
f5eabba0
AD
29
30sub sdn_zones_config {
31 my ($cfg, $id, $noerr) = @_;
32
33 die "no sdn zone ID specified\n" if !$id;
34
35 my $scfg = $cfg->{ids}->{$id};
b2d83056 36 die "sdn '$id' does not exist\n" if (!$noerr && !$scfg);
f5eabba0
AD
37
38 return $scfg;
39}
40
41sub config {
f703d2ae 42 my $config = cfs_read_file("sdn/zones.cfg");
f5eabba0
AD
43 return $config;
44}
45
41d40fb1
TL
46sub get_plugin_config {
47 my ($vnet) = @_;
48 my $zoneid = $vnet->{zone};
49 my $zone_cfg = PVE::Network::SDN::Zones::config();
50 return $zone_cfg->{ids}->{$zoneid};
51}
52
f5eabba0
AD
53sub write_config {
54 my ($cfg) = @_;
55
f703d2ae 56 cfs_write_file("sdn/zones.cfg", $cfg);
f5eabba0
AD
57}
58
f5eabba0
AD
59sub sdn_zones_ids {
60 my ($cfg) = @_;
61
62 return keys %{$cfg->{ids}};
63}
64
65sub complete_sdn_zone {
66 my ($cmdname, $pname, $cvalue) = @_;
67
68 my $cfg = PVE::Network::SDN::config();
69
70 return $cmdname eq 'add' ? [] : [ PVE::Network::SDN::sdn_zones_ids($cfg) ];
71}
72
73
74sub generate_etc_network_config {
75
0814c9a9 76 my $version = PVE::Cluster::cfs_read_file('sdn/.version');
f5eabba0 77 my $vnet_cfg = PVE::Cluster::cfs_read_file('sdn/vnets.cfg');
56cdcac9 78 my $zone_cfg = PVE::Cluster::cfs_read_file('sdn/zones.cfg');
4405f2de 79 my $controller_cfg = PVE::Cluster::cfs_read_file('sdn/controllers.cfg');
56cdcac9 80 return if !$vnet_cfg && !$zone_cfg;
f5eabba0 81
ba7ac021 82 my $interfaces_config = PVE::INotify::read_file('interfaces');
f5eabba0
AD
83
84 #generate configuration
85 my $config = {};
c2b9c173
AD
86 my $nodename = PVE::INotify::nodename();
87
f5eabba0
AD
88 foreach my $id (keys %{$vnet_cfg->{ids}}) {
89 my $vnet = $vnet_cfg->{ids}->{$id};
3fd3e917 90 my $zone = $vnet->{zone};
f5eabba0
AD
91
92 if(!$zone) {
93 warn "can't generate vnet $vnet : zone $zone don't exist";
94 next;
95 }
96
56cdcac9 97 my $plugin_config = $zone_cfg->{ids}->{$zone};
f5eabba0
AD
98
99 if (!defined($plugin_config)) {
100 warn "can't generate vnet $vnet : zone $zone don't exist";
101 next;
102 }
103
c2b9c173
AD
104 next if defined($plugin_config->{nodes}) && !$plugin_config->{nodes}->{$nodename};
105
4405f2de
AD
106 my $controller = undef;
107 if($plugin_config->{controller}) {
108 my $controllerid = $plugin_config->{controller};
109 $controller = $controller_cfg->{ids}->{$controllerid};
110 }
111
f5eabba0 112 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
ba7ac021 113 $plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $controller, $interfaces_config, $config);
f5eabba0
AD
114 }
115
0814c9a9 116 my $raw_network_config = "\#version:$version\n";
2d4c068e 117 foreach my $iface (sort keys %$config) {
f5eabba0
AD
118 $raw_network_config .= "\n";
119 $raw_network_config .= "auto $iface\n";
120 $raw_network_config .= "iface $iface\n";
121 foreach my $option (@{$config->{$iface}}) {
122 $raw_network_config .= "\t$option\n";
123 }
124 }
125
126 return $raw_network_config;
127}
128
129sub write_etc_network_config {
130 my ($rawconfig) = @_;
131
132 return if !$rawconfig;
f5eabba0 133
0814c9a9 134 my $writefh = IO::File->new($local_network_sdn_file,">");
f5eabba0
AD
135 print $writefh $rawconfig;
136 $writefh->close();
137}
138
0814c9a9
AD
139sub read_etc_network_config_version {
140 my $versionstr = PVE::Tools::file_read_firstline($local_network_sdn_file);
141 if ($versionstr =~ m/^\#version:(\d+)$/) {
142 return $1;
143 }
144}
145
f5eabba0
AD
146sub ifquery_check {
147
148 my $cmd = ['ifquery', '-a', '-c', '-o','json'];
149
150 my $result = '';
151 my $reader = sub { $result .= shift };
152
153 eval {
154 run_command($cmd, outfunc => $reader);
155 };
156
157 my $resultjson = decode_json($result);
158 my $interfaces = {};
159
160 foreach my $interface (@$resultjson) {
161 my $name = $interface->{name};
162 $interfaces->{$name} = {
163 status => $interface->{status},
164 config => $interface->{config},
165 config_status => $interface->{config_status},
166 };
167 }
168
169 return $interfaces;
170}
171
172# improve me : move status code inside plugins ?
173sub status {
174
175 my $cluster_vnet_file = "/etc/pve/sdn/vnets.cfg";
56cdcac9 176 my $cluster_zone_file = "/etc/pve/sdn/zones.cfg";
f5eabba0
AD
177 my $local_sdn_file = "/etc/network/interfaces.d/sdn";
178 my $err_config = undef;
179
56cdcac9 180 return if !-e $cluster_vnet_file && !-e $cluster_zone_file;
f5eabba0
AD
181
182 if (!-e $local_sdn_file) {
3709a203
AD
183
184 $err_config = "local sdn network configuration is not yet generated, please reload";
6933ca89 185 warn "$err_config\n";
f5eabba0
AD
186 } else {
187 # fixme : use some kind of versioning info?
188 my $cluster_vnet_timestamp = (stat($cluster_vnet_file))[9];
56cdcac9 189 my $cluster_zone_timestamp = (stat($cluster_zone_file))[9];
f5eabba0
AD
190 my $local_sdn_timestamp = (stat($local_sdn_file))[9];
191
56cdcac9 192 if ($local_sdn_timestamp < $cluster_vnet_timestamp || $local_sdn_timestamp < $cluster_zone_timestamp) {
3709a203 193 $err_config = "local sdn network configuration is too old, please reload";
6933ca89
TL
194 warn "$err_config\n";
195 }
f5eabba0
AD
196 }
197
198 my $status = ifquery_check();
199
200 my $vnet_cfg = PVE::Cluster::cfs_read_file('sdn/vnets.cfg');
c2b9c173
AD
201 my $zone_cfg = PVE::Cluster::cfs_read_file('sdn/zones.cfg');
202 my $nodename = PVE::INotify::nodename();
f5eabba0 203
627b1694 204
f5eabba0 205 my $vnet_status = {};
56cdcac9 206 my $zone_status = {};
f5eabba0 207
70da0442
TL
208 foreach my $id (sort keys %{$vnet_cfg->{ids}}) {
209 my $vnet = $vnet_cfg->{ids}->{$id};
210 my $zone = $vnet->{zone};
627b1694 211 next if !$zone;
627b1694 212
70da0442
TL
213 my $plugin_config = $zone_cfg->{ids}->{$zone};
214 next if defined($plugin_config->{nodes}) && !$plugin_config->{nodes}->{$nodename};
215
216 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
217 $plugin->status($plugin_config, $zone, $id, $vnet, $err_config, $status, $vnet_status, $zone_status);
f5eabba0 218 }
627b1694 219
56cdcac9 220 return($zone_status, $vnet_status);
f5eabba0
AD
221}
222
2ba9613b
AD
223sub get_bridge_vlan {
224 my ($vnetid) = @_;
225
eb1549e7 226 my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
2ba9613b 227
a1ef0eb9 228 return ($vnetid, undef) if !$vnet; # fallback for classic bridge
2ba9613b 229
41d40fb1 230 my $plugin_config = get_plugin_config($vnet);
eb1549e7 231 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
41d40fb1 232 return $plugin->get_bridge_vlan($plugin_config, $vnetid, $vnet->{tag});
eb1549e7
AD
233}
234
235sub tap_create {
236 my ($iface, $bridge) = @_;
237
238 my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge);
a1ef0eb9 239 if (!$vnet) { # fallback for classic bridge
eb1549e7 240 PVE::Network::tap_create($iface, $bridge);
a1ef0eb9 241 return;
eb1549e7
AD
242 }
243
41d40fb1 244 my $plugin_config = get_plugin_config($vnet);
eb1549e7
AD
245 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
246 $plugin->tap_create($plugin_config, $vnet, $iface, $bridge);
247}
248
249sub veth_create {
250 my ($veth, $vethpeer, $bridge, $hwaddr) = @_;
251
252 my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge);
a1ef0eb9 253 if (!$vnet) { # fallback for classic bridge
eb1549e7 254 PVE::Network::veth_create($veth, $vethpeer, $bridge, $hwaddr);
a1ef0eb9 255 return;
eb1549e7
AD
256 }
257
41d40fb1 258 my $plugin_config = get_plugin_config($vnet);
eb1549e7
AD
259 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
260 $plugin->veth_create($plugin_config, $vnet, $veth, $vethpeer, $bridge, $hwaddr);
261}
262
263sub tap_plug {
264 my ($iface, $bridge, $tag, $firewall, $trunks, $rate) = @_;
265
266 my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge);
a1ef0eb9 267 if (!$vnet) { # fallback for classic bridge
eb1549e7
AD
268 PVE::Network::tap_plug($iface, $bridge, $tag, $firewall, $trunks, $rate);
269 return;
270 }
271
41d40fb1 272 my $plugin_config = get_plugin_config($vnet);
eb1549e7
AD
273 my $nodename = PVE::INotify::nodename();
274
41d40fb1
TL
275 die "vnet $bridge is not allowed on this node\n"
276 if $plugin_config->{nodes} && !defined($plugin_config->{nodes}->{$nodename});
2ba9613b 277
2ba9613b 278 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
eb1549e7 279 $plugin->tap_plug($plugin_config, $vnet, $iface, $bridge, $firewall, $rate);
2ba9613b
AD
280}
281
f5eabba0
AD
2821;
283