1 package PVE
::Network
::SDN
;
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
::SDN
::Plugin
;
12 use PVE
::Network
::SDN
::VnetPlugin
;
13 use PVE
::Network
::SDN
::VlanPlugin
;
14 use PVE
::Network
::SDN
::VxlanPlugin
;
15 use PVE
::Network
::SDN
::FaucetPlugin
;
16 use PVE
::Network
::SDN
::FaucetControllerPlugin
;
17 use PVE
::Network
::SDN
::EvpnPlugin
;
18 use PVE
::Network
::SDN
::EvpnControllerPlugin
;
19 use PVE
::Network
::SDN
::QinQPlugin
;
21 PVE
::Network
::SDN
::VnetPlugin-
>register();
22 PVE
::Network
::SDN
::VlanPlugin-
>register();
23 PVE
::Network
::SDN
::VxlanPlugin-
>register();
24 PVE
::Network
::SDN
::FaucetControllerPlugin-
>register();
25 PVE
::Network
::SDN
::FaucetPlugin-
>register();
26 PVE
::Network
::SDN
::EvpnPlugin-
>register();
27 PVE
::Network
::SDN
::EvpnControllerPlugin-
>register();
28 PVE
::Network
::SDN
::QinQPlugin-
>register();
29 PVE
::Network
::SDN
::Plugin-
>init();
33 my ($cfg, $sdnid, $noerr) = @_;
35 die "no sdn ID specified\n" if !$sdnid;
37 my $scfg = $cfg->{ids
}->{$sdnid};
38 die "sdn '$sdnid' does not exists\n" if (!$noerr && !$scfg);
44 my $config = cfs_read_file
("sdn.cfg.new");
45 $config = cfs_read_file
("sdn.cfg") if !keys %{$config->{ids
}};
52 cfs_write_file
("sdn.cfg.new", $cfg);
56 my ($code, $errmsg) = @_;
58 cfs_lock_file
("sdn.cfg.new", undef, $code);
60 $errmsg ?
die "$errmsg: $err" : die $err;
67 return keys %{$cfg->{ids
}};
71 my ($cmdname, $pname, $cvalue) = @_;
73 my $cfg = PVE
::Network
::SDN
::config
();
75 return $cmdname eq 'add' ?
[] : [ PVE
::Network
::SDN
::sdn_ids
($cfg) ];
80 my $cmd = ['ifquery', '-a', '-c', '-o','json'];
83 my $reader = sub { $result .= shift };
86 run_command
($cmd, outfunc
=> $reader);
89 my $resultjson = decode_json
($result);
92 foreach my $interface (@$resultjson) {
93 my $name = $interface->{name
};
94 $interfaces->{$name} = {
95 status
=> $interface->{status
},
96 config
=> $interface->{config
},
97 config_status
=> $interface->{config_status
},
105 sub generate_etc_network_config
{
107 my $sdn_cfg = PVE
::Cluster
::cfs_read_file
('sdn.cfg');
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);
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;
127 my $vnet_cfg = undef;
128 my $transport_cfg = undef;
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};
134 $transport_cfg->{ids
}->{$id} = $sdn_cfg->{ids
}->{$id};
138 #generate configuration
140 foreach my $id (keys %{$vnet_cfg->{ids
}}) {
141 my $vnet = $vnet_cfg->{ids
}->{$id};
142 my $zone = $vnet->{transportzone
};
145 warn "can't generate vnet $vnet : zone $zone don't exist";
149 my $plugin_config = $transport_cfg->{ids
}->{$zone};
151 if (!defined($plugin_config)) {
152 warn "can't generate vnet $vnet : zone $zone don't exist";
156 my $plugin = PVE
::Network
::SDN
::Plugin-
>lookup($plugin_config->{type
});
157 $plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $uplinks, $config);
160 my $raw_network_config = "";
161 foreach my $iface (keys %$config) {
162 $raw_network_config .= "\n";
163 $raw_network_config .= "auto $iface\n";
164 $raw_network_config .= "iface $iface\n";
165 foreach my $option (@{$config->{$iface}}) {
166 $raw_network_config .= "\t$option\n";
170 return $raw_network_config;
173 sub generate_controller_config
{
175 my $sdn_cfg = PVE
::Cluster
::cfs_read_file
('sdn.cfg');
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);
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;
195 #generate configuration
198 foreach my $id (keys %{$sdn_cfg->{ids
}}) {
199 my $plugin_config = $sdn_cfg->{ids
}->{$id};
200 my $plugin = PVE
::Network
::SDN
::Plugin-
>lookup($plugin_config->{type
});
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
};
208 my $controller = $sdn_cfg->{ids
}->{$controllerid};
210 my $controller_plugin = PVE
::Network
::SDN
::Plugin-
>lookup($controller->{type
});
211 $controller_plugin->generate_controller_transport_config($plugin_config, $controller, $id, $uplinks, $config);
214 } elsif ($role eq 'vnet') {
215 my $transportid = $plugin_config->{transportzone
};
217 my $transport = $sdn_cfg->{ids
}->{$transportid};
219 my $controllerid = $transport->{controller
};
221 my $controller = $sdn_cfg->{ids
}->{$controllerid};
223 my $controller_plugin = PVE
::Network
::SDN
::Plugin-
>lookup($controller->{type
});
224 $controller_plugin->generate_controller_vnet_config($plugin_config, $controller, $transportid, $id, $config);
236 sub reload_controller
{
238 my $sdn_cfg = PVE
::Cluster
::cfs_read_file
('sdn.cfg');
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();
252 sub write_etc_network_config
{
253 my ($rawconfig) = @_;
255 return if !$rawconfig;
256 my $sdn_interfaces_file = "/etc/network/interfaces.d/sdn";
258 my $writefh = IO
::File-
>new($sdn_interfaces_file,">");
259 print $writefh $rawconfig;
263 sub write_controller_config
{
266 my $sdn_cfg = PVE
::Cluster
::cfs_read_file
('sdn.cfg');
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);
282 my $cluster_sdn_file = "/etc/pve/sdn.cfg";
283 my $local_sdn_file = "/etc/network/interfaces.d/sdn";
284 my $err_config = undef;
286 return if !-e
$cluster_sdn_file;
288 if (!-e
$local_sdn_file) {
289 warn "local sdn network configuration is not yet generated, please reload";
290 $err_config = 'pending';
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];
296 if ($local_sdn_timestamp < $cluster_sdn_timestamp) {
297 warn "local sdn network configuration is too old, please reload";
298 $err_config = 'unknown';
302 my $status = ifquery_check
();
304 my $network_cfg = PVE
::Cluster
::cfs_read_file
('sdn.cfg');
305 my $vnet_cfg = undef;
306 my $transport_cfg = undef;
308 my $vnet_status = {};
309 my $transport_status = {};
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
});
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'};
324 if ($status->{$bridgeport}->{status
} && $status->{$bridgeport}->{status
} ne 'pass') {
325 $vnet_status->{$id}->{status
} = 'error';
326 $transport_status->{$transportzone}->{status
} = 'error';
329 $vnet_status->{$id}->{status
} = 'error';
330 $transport_status->{$transportzone}->{status
} = 'error';
334 return($transport_status, $vnet_status);