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