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