]> git.proxmox.com Git - pve-network.git/blob - PVE/Network/SDN/Zones.pm
only parse version if local sdn file exists
[pve-network.git] / PVE / Network / SDN / Zones.pm
1 package PVE::Network::SDN::Zones;
2
3 use strict;
4 use warnings;
5
6 use Data::Dumper;
7 use JSON;
8
9 use PVE::Tools qw(extract_param dir_glob_regex run_command);
10 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
11 use PVE::Network;
12
13 use PVE::Network::SDN::Vnets;
14 use PVE::Network::SDN::Zones::VlanPlugin;
15 use PVE::Network::SDN::Zones::QinQPlugin;
16 use PVE::Network::SDN::Zones::VxlanPlugin;
17 use PVE::Network::SDN::Zones::EvpnPlugin;
18 use PVE::Network::SDN::Zones::FaucetPlugin;
19 use PVE::Network::SDN::Zones::Plugin;
20
21 PVE::Network::SDN::Zones::VlanPlugin->register();
22 PVE::Network::SDN::Zones::QinQPlugin->register();
23 PVE::Network::SDN::Zones::VxlanPlugin->register();
24 PVE::Network::SDN::Zones::EvpnPlugin->register();
25 PVE::Network::SDN::Zones::FaucetPlugin->register();
26 PVE::Network::SDN::Zones::Plugin->init();
27
28 my $local_network_sdn_file = "/etc/network/interfaces.d/sdn";
29
30 sub 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};
36 die "sdn '$id' does not exist\n" if (!$noerr && !$scfg);
37
38 return $scfg;
39 }
40
41 sub config {
42 my $config = cfs_read_file("sdn/zones.cfg");
43 return $config;
44 }
45
46 sub 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
53 sub write_config {
54 my ($cfg) = @_;
55
56 cfs_write_file("sdn/zones.cfg", $cfg);
57 }
58
59 sub sdn_zones_ids {
60 my ($cfg) = @_;
61
62 return keys %{$cfg->{ids}};
63 }
64
65 sub 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
74 sub generate_etc_network_config {
75
76 my $version = PVE::Cluster::cfs_read_file('sdn/.version');
77 my $vnet_cfg = PVE::Cluster::cfs_read_file('sdn/vnets.cfg');
78 my $zone_cfg = PVE::Cluster::cfs_read_file('sdn/zones.cfg');
79 my $controller_cfg = PVE::Cluster::cfs_read_file('sdn/controllers.cfg');
80 return if !$vnet_cfg && !$zone_cfg;
81
82 my $interfaces_config = PVE::INotify::read_file('interfaces');
83
84 #generate configuration
85 my $config = {};
86 my $nodename = PVE::INotify::nodename();
87
88 foreach my $id (keys %{$vnet_cfg->{ids}}) {
89 my $vnet = $vnet_cfg->{ids}->{$id};
90 my $zone = $vnet->{zone};
91
92 if(!$zone) {
93 warn "can't generate vnet $vnet : zone $zone don't exist";
94 next;
95 }
96
97 my $plugin_config = $zone_cfg->{ids}->{$zone};
98
99 if (!defined($plugin_config)) {
100 warn "can't generate vnet $vnet : zone $zone don't exist";
101 next;
102 }
103
104 next if defined($plugin_config->{nodes}) && !$plugin_config->{nodes}->{$nodename};
105
106 my $controller = undef;
107 if($plugin_config->{controller}) {
108 my $controllerid = $plugin_config->{controller};
109 $controller = $controller_cfg->{ids}->{$controllerid};
110 }
111
112 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
113 $plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $controller, $interfaces_config, $config);
114 }
115
116 my $raw_network_config = "\#version:$version\n";
117 foreach my $iface (sort keys %$config) {
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
129 sub write_etc_network_config {
130 my ($rawconfig) = @_;
131
132 return if !$rawconfig;
133
134 my $writefh = IO::File->new($local_network_sdn_file,">");
135 print $writefh $rawconfig;
136 $writefh->close();
137 }
138
139 sub read_etc_network_config_version {
140 my $versionstr = PVE::Tools::file_read_firstline($local_network_sdn_file);
141
142 return if !defined($versionstr);
143
144 if ($versionstr =~ m/^\#version:(\d+)$/) {
145 return $1;
146 }
147 }
148
149 sub ifquery_check {
150
151 my $cmd = ['ifquery', '-a', '-c', '-o','json'];
152
153 my $result = '';
154 my $reader = sub { $result .= shift };
155
156 eval {
157 run_command($cmd, outfunc => $reader);
158 };
159
160 my $resultjson = decode_json($result);
161 my $interfaces = {};
162
163 foreach my $interface (@$resultjson) {
164 my $name = $interface->{name};
165 $interfaces->{$name} = {
166 status => $interface->{status},
167 config => $interface->{config},
168 config_status => $interface->{config_status},
169 };
170 }
171
172 return $interfaces;
173 }
174
175 # improve me : move status code inside plugins ?
176 sub status {
177
178 my $err_config = undef;
179
180 my $local_version = PVE::Network::SDN::Zones::read_etc_network_config_version();
181 my $sdn_version = PVE::Cluster::cfs_read_file('sdn/.version');
182
183 return if !$sdn_version;
184
185 if (!$local_version) {
186 $err_config = "local sdn network configuration is not yet generated, please reload";
187 warn "$err_config\n";
188 } elsif ($local_version < $sdn_version) {
189 $err_config = "local sdn network configuration is too old, please reload";
190 warn "$err_config\n";
191 }
192
193 my $status = ifquery_check();
194
195 my $vnet_cfg = PVE::Cluster::cfs_read_file('sdn/vnets.cfg');
196 my $zone_cfg = PVE::Cluster::cfs_read_file('sdn/zones.cfg');
197 my $nodename = PVE::INotify::nodename();
198
199
200 my $vnet_status = {};
201 my $zone_status = {};
202
203 foreach my $id (sort keys %{$vnet_cfg->{ids}}) {
204 my $vnet = $vnet_cfg->{ids}->{$id};
205 my $zone = $vnet->{zone};
206 next if !$zone;
207
208 my $plugin_config = $zone_cfg->{ids}->{$zone};
209 next if defined($plugin_config->{nodes}) && !$plugin_config->{nodes}->{$nodename};
210
211 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
212 $plugin->status($plugin_config, $zone, $id, $vnet, $err_config, $status, $vnet_status, $zone_status);
213 }
214
215 return($zone_status, $vnet_status);
216 }
217
218 sub get_bridge_vlan {
219 my ($vnetid) = @_;
220
221 my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
222
223 return ($vnetid, undef) if !$vnet; # fallback for classic bridge
224
225 my $plugin_config = get_plugin_config($vnet);
226 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
227 return $plugin->get_bridge_vlan($plugin_config, $vnetid, $vnet->{tag});
228 }
229
230 sub tap_create {
231 my ($iface, $bridge) = @_;
232
233 my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge);
234 if (!$vnet) { # fallback for classic bridge
235 PVE::Network::tap_create($iface, $bridge);
236 return;
237 }
238
239 my $plugin_config = get_plugin_config($vnet);
240 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
241 $plugin->tap_create($plugin_config, $vnet, $iface, $bridge);
242 }
243
244 sub veth_create {
245 my ($veth, $vethpeer, $bridge, $hwaddr) = @_;
246
247 my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge);
248 if (!$vnet) { # fallback for classic bridge
249 PVE::Network::veth_create($veth, $vethpeer, $bridge, $hwaddr);
250 return;
251 }
252
253 my $plugin_config = get_plugin_config($vnet);
254 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
255 $plugin->veth_create($plugin_config, $vnet, $veth, $vethpeer, $bridge, $hwaddr);
256 }
257
258 sub tap_plug {
259 my ($iface, $bridge, $tag, $firewall, $trunks, $rate) = @_;
260
261 my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge);
262 if (!$vnet) { # fallback for classic bridge
263 PVE::Network::tap_plug($iface, $bridge, $tag, $firewall, $trunks, $rate);
264 return;
265 }
266
267 my $plugin_config = get_plugin_config($vnet);
268 my $nodename = PVE::INotify::nodename();
269
270 die "vnet $bridge is not allowed on this node\n"
271 if $plugin_config->{nodes} && !defined($plugin_config->{nodes}->{$nodename});
272
273 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
274 $plugin->tap_plug($plugin_config, $vnet, $iface, $bridge, $firewall, $rate);
275 }
276
277 1;
278