]>
Commit | Line | Data |
---|---|---|
86d22462 | 1 | package PVE::Network::SDN; |
92b6f291 AD |
2 | |
3 | use strict; | |
4 | use warnings; | |
434125ce | 5 | |
92b6f291 | 6 | use Data::Dumper; |
c665cefc | 7 | use JSON; |
434125ce TL |
8 | |
9 | use PVE::Tools qw(extract_param dir_glob_regex run_command); | |
92b6f291 | 10 | use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file); |
86d22462 AD |
11 | use PVE::Network::SDN::Plugin; |
12 | use PVE::Network::SDN::VnetPlugin; | |
13 | use PVE::Network::SDN::VlanPlugin; | |
3ee45e4c | 14 | use PVE::Network::SDN::VxlanPlugin; |
32602a38 | 15 | use PVE::Network::SDN::FrrPlugin; |
ad03c543 AD |
16 | use PVE::Network::SDN::OVSFaucetPlugin; |
17 | use PVE::Network::SDN::FaucetPlugin; | |
63586d2f | 18 | use PVE::Network::SDN::EvpnPlugin; |
92b6f291 | 19 | |
86d22462 AD |
20 | PVE::Network::SDN::VnetPlugin->register(); |
21 | PVE::Network::SDN::VlanPlugin->register(); | |
3ee45e4c | 22 | PVE::Network::SDN::VxlanPlugin->register(); |
32602a38 | 23 | PVE::Network::SDN::FrrPlugin->register(); |
ad03c543 AD |
24 | PVE::Network::SDN::OVSFaucetPlugin->register(); |
25 | PVE::Network::SDN::FaucetPlugin->register(); | |
63586d2f | 26 | PVE::Network::SDN::EvpnPlugin->register(); |
86d22462 | 27 | PVE::Network::SDN::Plugin->init(); |
92b6f291 AD |
28 | |
29 | ||
6bffe819 AD |
30 | sub 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 | ||
41 | sub 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 | ||
47 | sub write_config { | |
48 | my ($cfg) = @_; | |
49 | ||
6bffe819 | 50 | cfs_write_file("sdn.cfg.new", $cfg); |
92b6f291 AD |
51 | } |
52 | ||
6bffe819 | 53 | sub 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 | 62 | sub sdn_ids { |
92b6f291 AD |
63 | my ($cfg) = @_; |
64 | ||
65 | return keys %{$cfg->{ids}}; | |
66 | } | |
67 | ||
6bffe819 | 68 | sub 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 | 76 | sub 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 | |
103 | sub 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 | 171 | sub 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 |
233 | sub 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 |
244 | sub 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 |
261 | sub 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 | ||
318 | 1; | |
80348b2d | 319 |