]> git.proxmox.com Git - pve-network.git/blame - PVE/Network/SDN.pm
add evpnplugin (splitted from vxlanplugin)
[pve-network.git] / PVE / Network / SDN.pm
CommitLineData
86d22462 1package PVE::Network::SDN;
92b6f291
AD
2
3use strict;
4use warnings;
434125ce 5
92b6f291 6use Data::Dumper;
c665cefc 7use JSON;
434125ce
TL
8
9use PVE::Tools qw(extract_param dir_glob_regex run_command);
92b6f291 10use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
86d22462
AD
11use PVE::Network::SDN::Plugin;
12use PVE::Network::SDN::VnetPlugin;
13use PVE::Network::SDN::VlanPlugin;
3ee45e4c 14use PVE::Network::SDN::VxlanPlugin;
32602a38 15use PVE::Network::SDN::FrrPlugin;
ad03c543
AD
16use PVE::Network::SDN::OVSFaucetPlugin;
17use PVE::Network::SDN::FaucetPlugin;
63586d2f 18use PVE::Network::SDN::EvpnPlugin;
92b6f291 19
86d22462
AD
20PVE::Network::SDN::VnetPlugin->register();
21PVE::Network::SDN::VlanPlugin->register();
3ee45e4c 22PVE::Network::SDN::VxlanPlugin->register();
32602a38 23PVE::Network::SDN::FrrPlugin->register();
ad03c543
AD
24PVE::Network::SDN::OVSFaucetPlugin->register();
25PVE::Network::SDN::FaucetPlugin->register();
63586d2f 26PVE::Network::SDN::EvpnPlugin->register();
86d22462 27PVE::Network::SDN::Plugin->init();
92b6f291
AD
28
29
6bffe819
AD
30sub sdn_config {
31 my ($cfg, $sdnid, $noerr) = @_;
92b6f291 32
6bffe819 33 die "no sdn ID specified\n" if !$sdnid;
92b6f291 34
6bffe819
AD
35 my $scfg = $cfg->{ids}->{$sdnid};
36 die "sdn '$sdnid' does not exists\n" if (!$noerr && !$scfg);
92b6f291
AD
37
38 return $scfg;
39}
40
41sub config {
6bffe819
AD
42 my $config = cfs_read_file("sdn.cfg.new");
43 $config = cfs_read_file("sdn.cfg") if !keys %{$config->{ids}};
39d04c82 44 return $config;
92b6f291
AD
45}
46
47sub write_config {
48 my ($cfg) = @_;
49
6bffe819 50 cfs_write_file("sdn.cfg.new", $cfg);
92b6f291
AD
51}
52
6bffe819 53sub lock_sdn_config {
92b6f291
AD
54 my ($code, $errmsg) = @_;
55
6bffe819 56 cfs_lock_file("sdn.cfg.new", undef, $code);
0c5021ad 57 if (my $err = $@) {
92b6f291
AD
58 $errmsg ? die "$errmsg: $err" : die $err;
59 }
60}
61
6bffe819 62sub sdn_ids {
92b6f291
AD
63 my ($cfg) = @_;
64
65 return keys %{$cfg->{ids}};
66}
67
6bffe819 68sub complete_sdn {
92b6f291
AD
69 my ($cmdname, $pname, $cvalue) = @_;
70
86d22462 71 my $cfg = PVE::Network::SDN::config();
92b6f291 72
6bffe819 73 return $cmdname eq 'add' ? [] : [ PVE::Network::SDN::sdn_ids($cfg) ];
92b6f291
AD
74}
75
e424c7ac 76sub ifquery_check {
c665cefc
AD
77
78 my $cmd = ['ifquery', '-a', '-c', '-o','json'];
c665cefc 79
0c5021ad
TL
80 my $result = '';
81 my $reader = sub { $result .= shift };
c665cefc
AD
82
83 eval {
6e9fff39 84 run_command($cmd, outfunc => $reader);
c665cefc
AD
85 };
86
6e9fff39 87 my $resultjson = decode_json($result);
c665cefc
AD
88 my $interfaces = {};
89
90 foreach my $interface (@$resultjson) {
6e9fff39
AD
91 my $name = $interface->{name};
92 $interfaces->{$name} = {
93 status => $interface->{status},
94 config => $interface->{config},
95 config_status => $interface->{config_status},
96 };
c665cefc
AD
97 }
98
99 return $interfaces;
100}
101
80348b2d
AD
102
103sub generate_etc_network_config {
104
105 my $sdn_cfg = PVE::Cluster::cfs_read_file('sdn.cfg');
106 return if !$sdn_cfg;
107
108 #read main config for physical interfaces
109 my $current_config_file = "/etc/network/interfaces";
110 my $fh = IO::File->new($current_config_file);
111 my $interfaces_config = PVE::INotify::read_etc_network_interfaces(1,$fh);
112 $fh->close();
113
114 #check uplinks
115 my $uplinks = {};
116 foreach my $id (keys %{$interfaces_config->{ifaces}}) {
117 my $interface = $interfaces_config->{ifaces}->{$id};
118 if (my $uplink = $interface->{'uplink-id'}) {
119 die "uplink-id $uplink is already defined on $uplinks->{$uplink}" if $uplinks->{$uplink};
120 $interface->{name} = $id;
121 $uplinks->{$interface->{'uplink-id'}} = $interface;
122 }
123 }
124
125 my $vnet_cfg = undef;
126 my $transport_cfg = undef;
127
128 foreach my $id (keys %{$sdn_cfg->{ids}}) {
129 if ($sdn_cfg->{ids}->{$id}->{type} eq 'vnet') {
130 $vnet_cfg->{ids}->{$id} = $sdn_cfg->{ids}->{$id};
131 } else {
132 $transport_cfg->{ids}->{$id} = $sdn_cfg->{ids}->{$id};
133 }
134 }
135
136 #generate configuration
93dea3aa 137 my $config = {};
80348b2d
AD
138 foreach my $id (keys %{$vnet_cfg->{ids}}) {
139 my $vnet = $vnet_cfg->{ids}->{$id};
140 my $zone = $vnet->{transportzone};
141
142 if(!$zone) {
143 warn "can't generate vnet $vnet : zone $zone don't exist";
144 next;
145 }
146
147 my $plugin_config = $transport_cfg->{ids}->{$zone};
148
149 if (!defined($plugin_config)) {
150 warn "can't generate vnet $vnet : zone $zone don't exist";
151 next;
152 }
153
154 my $plugin = PVE::Network::SDN::Plugin->lookup($plugin_config->{type});
93dea3aa 155 $plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $uplinks, $config);
80348b2d
AD
156 }
157
93dea3aa 158 my $raw_network_config = "";
87d8b623 159 foreach my $iface (keys %$config) {
93dea3aa
AD
160 $raw_network_config .= "\n";
161 $raw_network_config .= "auto $iface\n";
162 $raw_network_config .= "iface $iface\n";
87d8b623 163 foreach my $option (@{$config->{$iface}}) {
93dea3aa
AD
164 $raw_network_config .= "\t$option\n";
165 }
166 }
167
87d8b623
AD
168 return $raw_network_config;
169}
170
8fb1ee7f 171sub generate_controller_config {
87d8b623
AD
172
173 my $sdn_cfg = PVE::Cluster::cfs_read_file('sdn.cfg');
174 return if !$sdn_cfg;
175
176 #read main config for physical interfaces
177 my $current_config_file = "/etc/network/interfaces";
178 my $fh = IO::File->new($current_config_file);
179 my $interfaces_config = PVE::INotify::read_etc_network_interfaces(1,$fh);
180 $fh->close();
181
182 #check uplinks
183 my $uplinks = {};
184 foreach my $id (keys %{$interfaces_config->{ifaces}}) {
185 my $interface = $interfaces_config->{ifaces}->{$id};
186 if (my $uplink = $interface->{'uplink-id'}) {
187 die "uplink-id $uplink is already defined on $uplinks->{$uplink}" if $uplinks->{$uplink};
188 $interface->{name} = $id;
189 $uplinks->{$interface->{'uplink-id'}} = $interface;
190 }
191 }
192
87d8b623
AD
193 #generate configuration
194 my $config = {};
195
8fb1ee7f
AD
196 foreach my $id (keys %{$sdn_cfg->{ids}}) {
197 my $plugin_config = $sdn_cfg->{ids}->{$id};
074d270b 198 my $plugin = PVE::Network::SDN::Plugin->lookup($plugin_config->{type});
8fb1ee7f
AD
199 my $pd = $plugin->plugindata();
200 my $role = $pd->{role};
201 if ($role eq 'controller') {
202 $plugin->generate_controller_config($plugin_config, $plugin_config, $id, $uplinks, $config);
203 } elsif ($role eq 'transport') {
204 my $controllerid = $plugin_config->{controller};
205 if ($controllerid) {
206 my $controller = $sdn_cfg->{ids}->{$controllerid};
207 if ($controller) {
0589eb09
AD
208 my $controller_plugin = PVE::Network::SDN::Plugin->lookup($controller->{type});
209 $controller_plugin->generate_controller_transport_config($plugin_config, $controller, $id, $uplinks, $config);
8fb1ee7f 210 }
87d8b623 211 }
ad03c543
AD
212 } elsif ($role eq 'vnet') {
213 my $transportid = $plugin_config->{transportzone};
214 if ($transportid) {
215 my $transport = $sdn_cfg->{ids}->{$transportid};
216 if ($transport) {
217 my $controllerid = $transport->{controller};
218 if ($controllerid) {
219 my $controller = $sdn_cfg->{ids}->{$controllerid};
220 if ($controller) {
221 my $controller_plugin = PVE::Network::SDN::Plugin->lookup($controller->{type});
222 $controller_plugin->generate_controller_vnet_config($plugin_config, $controller, $transportid, $id, $config);
223 }
224 }
225 }
226 }
87d8b623
AD
227 }
228 }
229
8fb1ee7f 230 return $config;
17854295
AD
231}
232
80348b2d
AD
233sub write_etc_network_config {
234 my ($rawconfig) = @_;
235
236 return if !$rawconfig;
237 my $sdn_interfaces_file = "/etc/network/interfaces.d/sdn";
238
239 my $writefh = IO::File->new($sdn_interfaces_file,">");
240 print $writefh $rawconfig;
241 $writefh->close();
242}
243
8fb1ee7f
AD
244sub write_controller_config {
245 my ($config) = @_;
ecdd8c12 246
8fb1ee7f
AD
247 my $sdn_cfg = PVE::Cluster::cfs_read_file('sdn.cfg');
248 return if !$sdn_cfg;
ecdd8c12 249
8fb1ee7f
AD
250 foreach my $id (keys %{$sdn_cfg->{ids}}) {
251 my $plugin_config = $sdn_cfg->{ids}->{$id};
252 my $plugin = PVE::Network::SDN::Plugin->lookup($plugin_config->{type});
253 my $pd = $plugin->plugindata();
254 my $role = $pd->{role};
255 if ($role eq 'controller') {
256 $plugin->write_controller_config($plugin_config, $config);
257 }
258 }
ecdd8c12
AD
259}
260
e424c7ac
AD
261sub status {
262
263 my $cluster_sdn_file = "/etc/pve/sdn.cfg";
264 my $local_sdn_file = "/etc/network/interfaces.d/sdn";
265 my $err_config = undef;
266
267 return if !-e $cluster_sdn_file;
268
269 if (!-e $local_sdn_file) {
270 warn "local sdn network configuration is not yet generated, please reload";
271 $err_config = 'pending';
272 } else {
273 # fixme : use some kind of versioning info?
274 my $cluster_sdn_timestamp = (stat($cluster_sdn_file))[9];
275 my $local_sdn_timestamp = (stat($local_sdn_file))[9];
276
277 if ($local_sdn_timestamp < $cluster_sdn_timestamp) {
278 warn "local sdn network configuration is too old, please reload";
279 $err_config = 'unknown';
280 }
281 }
282
283 my $status = ifquery_check();
80348b2d 284
e424c7ac
AD
285 my $network_cfg = PVE::Cluster::cfs_read_file('sdn.cfg');
286 my $vnet_cfg = undef;
287 my $transport_cfg = undef;
288
289 my $vnet_status = {};
290 my $transport_status = {};
291
292 foreach my $id (keys %{$network_cfg->{ids}}) {
293 if ($network_cfg->{ids}->{$id}->{type} eq 'vnet') {
294 my $transportzone = $network_cfg->{ids}->{$id}->{transportzone};
295 $vnet_status->{$id}->{transportzone} = $transportzone;
296 $transport_status->{$transportzone}->{status} = 'available' if !defined($transport_status->{$transportzone}->{status});
297
298 if($err_config) {
299 $vnet_status->{$id}->{status} = $err_config;
300 $transport_status->{$transportzone}->{status} = $err_config;
301 } elsif ($status->{$id}->{status} && $status->{$id}->{status} eq 'pass') {
302 $vnet_status->{$id}->{status} = 'available';
303 my $bridgeport = $status->{$id}->{config}->{'bridge-ports'};
304
305 if ($status->{$bridgeport}->{status} && $status->{$bridgeport}->{status} ne 'pass') {
306 $vnet_status->{$id}->{status} = 'error';
307 $transport_status->{$transportzone}->{status} = 'error';
308 }
309 } else {
310 $vnet_status->{$id}->{status} = 'error';
311 $transport_status->{$transportzone}->{status} = 'error';
312 }
313 }
314 }
315 return($transport_status, $vnet_status);
316}
317
3181;
80348b2d 319