]> git.proxmox.com Git - pve-network.git/blob - PVE/Network/SDN/Zones.pm
sdn zones: indentation and whitespace cleanup
[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.new");
42 $config = cfs_read_file("sdn/zones.cfg") if !keys %{$config->{ids}};
43 return $config;
44 }
45
46 sub write_config {
47 my ($cfg) = @_;
48
49 cfs_write_file("sdn/zones.cfg.new", $cfg);
50 }
51
52 sub lock_sdn_zones_config {
53 my ($code, $errmsg) = @_;
54
55 cfs_lock_file("sdn/zones.cfg.new", undef, $code);
56 if (my $err = $@) {
57 $errmsg ? die "$errmsg: $err" : die $err;
58 }
59 }
60
61 sub sdn_zones_ids {
62 my ($cfg) = @_;
63
64 return keys %{$cfg->{ids}};
65 }
66
67 sub 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
75
76 sub generate_etc_network_config {
77
78 my $vnet_cfg = PVE::Cluster::cfs_read_file('sdn/vnets.cfg');
79 my $zone_cfg = PVE::Cluster::cfs_read_file('sdn/zones.cfg');
80 my $controller_cfg = PVE::Cluster::cfs_read_file('sdn/controllers.cfg');
81 return if !$vnet_cfg && !$zone_cfg;
82
83 my $interfaces_config = PVE::INotify::read_file('interfaces');
84
85 #generate configuration
86 my $config = {};
87 my $nodename = PVE::INotify::nodename();
88
89 foreach my $id (keys %{$vnet_cfg->{ids}}) {
90 my $vnet = $vnet_cfg->{ids}->{$id};
91 my $zone = $vnet->{zone};
92
93 if(!$zone) {
94 warn "can't generate vnet $vnet : zone $zone don't exist";
95 next;
96 }
97
98 my $plugin_config = $zone_cfg->{ids}->{$zone};
99
100 if (!defined($plugin_config)) {
101 warn "can't generate vnet $vnet : zone $zone don't exist";
102 next;
103 }
104
105 next if defined($plugin_config->{nodes}) && !$plugin_config->{nodes}->{$nodename};
106
107 my $controller = undef;
108 if($plugin_config->{controller}) {
109 my $controllerid = $plugin_config->{controller};
110 $controller = $controller_cfg->{ids}->{$controllerid};
111 }
112
113 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
114 $plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $controller, $interfaces_config, $config);
115 }
116
117 my $raw_network_config = "";
118 foreach my $iface (keys %$config) {
119 $raw_network_config .= "\n";
120 $raw_network_config .= "auto $iface\n";
121 $raw_network_config .= "iface $iface\n";
122 foreach my $option (@{$config->{$iface}}) {
123 $raw_network_config .= "\t$option\n";
124 }
125 }
126
127 return $raw_network_config;
128 }
129
130 sub write_etc_network_config {
131 my ($rawconfig) = @_;
132
133 return if !$rawconfig;
134 my $sdn_interfaces_file = "/etc/network/interfaces.d/sdn";
135
136 my $writefh = IO::File->new($sdn_interfaces_file,">");
137 print $writefh $rawconfig;
138 $writefh->close();
139 }
140
141 sub ifquery_check {
142
143 my $cmd = ['ifquery', '-a', '-c', '-o','json'];
144
145 my $result = '';
146 my $reader = sub { $result .= shift };
147
148 eval {
149 run_command($cmd, outfunc => $reader);
150 };
151
152 my $resultjson = decode_json($result);
153 my $interfaces = {};
154
155 foreach my $interface (@$resultjson) {
156 my $name = $interface->{name};
157 $interfaces->{$name} = {
158 status => $interface->{status},
159 config => $interface->{config},
160 config_status => $interface->{config_status},
161 };
162 }
163
164 return $interfaces;
165 }
166
167 # improve me : move status code inside plugins ?
168 sub status {
169
170 my $cluster_vnet_file = "/etc/pve/sdn/vnets.cfg";
171 my $cluster_zone_file = "/etc/pve/sdn/zones.cfg";
172 my $local_sdn_file = "/etc/network/interfaces.d/sdn";
173 my $err_config = undef;
174
175 return if !-e $cluster_vnet_file && !-e $cluster_zone_file;
176
177 if (!-e $local_sdn_file) {
178
179 $err_config = "local sdn network configuration is not yet generated, please reload";
180 warn "$err_config\n";
181 } else {
182 # fixme : use some kind of versioning info?
183 my $cluster_vnet_timestamp = (stat($cluster_vnet_file))[9];
184 my $cluster_zone_timestamp = (stat($cluster_zone_file))[9];
185 my $local_sdn_timestamp = (stat($local_sdn_file))[9];
186
187 if ($local_sdn_timestamp < $cluster_vnet_timestamp || $local_sdn_timestamp < $cluster_zone_timestamp) {
188 $err_config = "local sdn network configuration is too old, please reload";
189 warn "$err_config\n";
190 }
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 #fallback if classic bridge
224 return ($vnetid, undef) if !$vnet;
225
226 my $zone_cfg = PVE::Network::SDN::Zones::config();
227 my $zoneid = $vnet->{zone};
228 my $tag = $vnet->{tag};
229
230 my $plugin_config = $zone_cfg->{ids}->{$zoneid};
231 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
232 return $plugin->get_bridge_vlan($plugin_config, $vnetid, $tag);
233 }
234
235 sub tap_create {
236 my ($iface, $bridge) = @_;
237
238 my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge);
239
240 #fallback if classic bridge
241 if(!$vnet) {
242 PVE::Network::tap_create($iface, $bridge);
243 return;
244 }
245
246 my $zone_cfg = PVE::Network::SDN::Zones::config();
247 my $zoneid = $vnet->{zone};
248
249 my $plugin_config = $zone_cfg->{ids}->{$zoneid};
250 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
251 $plugin->tap_create($plugin_config, $vnet, $iface, $bridge);
252 }
253
254 sub veth_create {
255 my ($veth, $vethpeer, $bridge, $hwaddr) = @_;
256
257 my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge);
258
259 #fallback if classic bridge
260 if(!$vnet) {
261 PVE::Network::veth_create($veth, $vethpeer, $bridge, $hwaddr);
262 return;
263 }
264
265 my $zone_cfg = PVE::Network::SDN::Zones::config();
266 my $zoneid = $vnet->{zone};
267
268 my $plugin_config = $zone_cfg->{ids}->{$zoneid};
269 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
270 $plugin->veth_create($plugin_config, $vnet, $veth, $vethpeer, $bridge, $hwaddr);
271 }
272
273 sub tap_plug {
274 my ($iface, $bridge, $tag, $firewall, $trunks, $rate) = @_;
275
276 my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge);
277
278 #fallback if classic bridge
279 if(!$vnet) {
280 PVE::Network::tap_plug($iface, $bridge, $tag, $firewall, $trunks, $rate);
281 return;
282 }
283
284 my $zone_cfg = PVE::Network::SDN::Zones::config();
285 my $nodename = PVE::INotify::nodename();
286
287 my $zoneid = $vnet->{zone};
288 $tag = $vnet->{tag};
289
290 die "vnet $bridge is not allowed on this node" if defined($zone_cfg->{ids}->{$zoneid}->{nodes}) && !$zone_cfg->{ids}->{$zoneid}->{nodes}->{$nodename};
291
292 my $plugin_config = $zone_cfg->{ids}->{$zoneid};
293 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
294 $plugin->tap_plug($plugin_config, $vnet, $iface, $bridge, $firewall, $rate);
295 }
296
297 1;
298