1 package PVE
::Network
::SDN
::Zones
;
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);
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
;
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();
28 my $local_network_sdn_file = "/etc/network/interfaces.d/sdn";
30 sub sdn_zones_config
{
31 my ($cfg, $id, $noerr) = @_;
33 die "no sdn zone ID specified\n" if !$id;
35 my $scfg = $cfg->{ids
}->{$id};
36 die "sdn '$id' does not exist\n" if (!$noerr && !$scfg);
42 my $config = cfs_read_file
("sdn/zones.cfg");
46 sub get_plugin_config
{
48 my $zoneid = $vnet->{zone
};
49 my $zone_cfg = PVE
::Network
::SDN
::Zones
::config
();
50 return $zone_cfg->{ids
}->{$zoneid};
56 cfs_write_file
("sdn/zones.cfg", $cfg);
62 return keys %{$cfg->{ids
}};
65 sub complete_sdn_zone
{
66 my ($cmdname, $pname, $cvalue) = @_;
68 my $cfg = PVE
::Network
::SDN
::config
();
70 return $cmdname eq 'add' ?
[] : [ PVE
::Network
::SDN
::sdn_zones_ids
($cfg) ];
74 sub generate_etc_network_config
{
76 my $version = PVE
::Cluster
::cfs_read_file
('sdn/.version');
77 my $vnet_cfg = PVE
::Cluster
::cfs_read_file
('sdn/vnets.cfg');
78 my $zone_cfg = PVE
::Cluster
::cfs_read_file
('sdn/zones.cfg');
79 my $controller_cfg = PVE
::Cluster
::cfs_read_file
('sdn/controllers.cfg');
80 return if !$vnet_cfg && !$zone_cfg;
82 my $interfaces_config = PVE
::INotify
::read_file
('interfaces');
84 #generate configuration
86 my $nodename = PVE
::INotify
::nodename
();
88 for my $id (sort keys %{$vnet_cfg->{ids
}}) {
89 my $vnet = $vnet_cfg->{ids
}->{$id};
90 my $zone = $vnet->{zone
};
93 warn "can't generate vnet '$id': no zone assigned!\n";
97 my $plugin_config = $zone_cfg->{ids
}->{$zone};
99 if (!defined($plugin_config)) {
100 warn "can't generate vnet '$id': zone $zone don't exist\n";
104 next if defined($plugin_config->{nodes
}) && !$plugin_config->{nodes
}->{$nodename};
107 if (my $controllerid = $plugin_config->{controller
}) {
108 $controller = $controller_cfg->{ids
}->{$controllerid};
111 my $plugin = PVE
::Network
::SDN
::Zones
::Plugin-
>lookup($plugin_config->{type
});
112 $plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $controller, $interfaces_config, $config);
115 my $raw_network_config = "\#version:$version\n";
116 foreach my $iface (sort keys %$config) {
117 $raw_network_config .= "\n";
118 $raw_network_config .= "auto $iface\n";
119 $raw_network_config .= "iface $iface\n";
120 foreach my $option (@{$config->{$iface}}) {
121 $raw_network_config .= "\t$option\n";
125 return $raw_network_config;
128 sub write_etc_network_config
{
129 my ($rawconfig) = @_;
131 return if !$rawconfig;
133 my $writefh = IO
::File-
>new($local_network_sdn_file,">");
134 print $writefh $rawconfig;
138 sub read_etc_network_config_version
{
139 my $versionstr = PVE
::Tools
::file_read_firstline
($local_network_sdn_file);
141 return if !defined($versionstr);
143 if ($versionstr =~ m/^\#version:(\d+)$/) {
150 my $cmd = ['ifquery', '-a', '-c', '-o','json'];
153 my $reader = sub { $result .= shift };
156 run_command
($cmd, outfunc
=> $reader);
159 my $resultjson = decode_json
($result);
162 foreach my $interface (@$resultjson) {
163 my $name = $interface->{name
};
164 $interfaces->{$name} = {
165 status
=> $interface->{status
},
166 config
=> $interface->{config
},
167 config_status
=> $interface->{config_status
},
174 # improve me : move status code inside plugins ?
177 my $err_config = undef;
179 my $local_version = PVE
::Network
::SDN
::Zones
::read_etc_network_config_version
();
180 my $sdn_version = PVE
::Cluster
::cfs_read_file
('sdn/.version');
182 return if !$sdn_version;
184 if (!$local_version) {
185 $err_config = "local sdn network configuration is not yet generated, please reload";
186 warn "$err_config\n";
187 } elsif ($local_version < $sdn_version) {
188 $err_config = "local sdn network configuration is too old, please reload";
189 warn "$err_config\n";
192 my $status = ifquery_check
();
194 my $vnet_cfg = PVE
::Cluster
::cfs_read_file
('sdn/vnets.cfg');
195 my $zone_cfg = PVE
::Cluster
::cfs_read_file
('sdn/zones.cfg');
196 my $nodename = PVE
::INotify
::nodename
();
199 my $vnet_status = {};
200 my $zone_status = {};
202 foreach my $id (sort keys %{$vnet_cfg->{ids
}}) {
203 my $vnet = $vnet_cfg->{ids
}->{$id};
204 my $zone = $vnet->{zone
};
207 my $plugin_config = $zone_cfg->{ids
}->{$zone};
208 next if defined($plugin_config->{nodes
}) && !$plugin_config->{nodes
}->{$nodename};
210 my $plugin = PVE
::Network
::SDN
::Zones
::Plugin-
>lookup($plugin_config->{type
});
211 $plugin->status($plugin_config, $zone, $id, $vnet, $err_config, $status, $vnet_status, $zone_status);
214 return($zone_status, $vnet_status);
217 sub get_bridge_vlan
{
220 my $vnet = PVE
::Network
::SDN
::Vnets
::get_vnet
($vnetid);
222 return ($vnetid, undef) if !$vnet; # fallback for classic bridge
224 my $plugin_config = get_plugin_config
($vnet);
225 my $plugin = PVE
::Network
::SDN
::Zones
::Plugin-
>lookup($plugin_config->{type
});
226 return $plugin->get_bridge_vlan($plugin_config, $vnetid, $vnet->{tag
});
230 my ($iface, $bridge) = @_;
232 my $vnet = PVE
::Network
::SDN
::Vnets
::get_vnet
($bridge);
233 if (!$vnet) { # fallback for classic bridge
234 PVE
::Network
::tap_create
($iface, $bridge);
238 my $plugin_config = get_plugin_config
($vnet);
239 my $plugin = PVE
::Network
::SDN
::Zones
::Plugin-
>lookup($plugin_config->{type
});
240 $plugin->tap_create($plugin_config, $vnet, $iface, $bridge);
244 my ($veth, $vethpeer, $bridge, $hwaddr) = @_;
246 my $vnet = PVE
::Network
::SDN
::Vnets
::get_vnet
($bridge);
247 if (!$vnet) { # fallback for classic bridge
248 PVE
::Network
::veth_create
($veth, $vethpeer, $bridge, $hwaddr);
252 my $plugin_config = get_plugin_config
($vnet);
253 my $plugin = PVE
::Network
::SDN
::Zones
::Plugin-
>lookup($plugin_config->{type
});
254 $plugin->veth_create($plugin_config, $vnet, $veth, $vethpeer, $bridge, $hwaddr);
258 my ($iface, $bridge, $tag, $firewall, $trunks, $rate) = @_;
260 my $vnet = PVE
::Network
::SDN
::Vnets
::get_vnet
($bridge);
261 if (!$vnet) { # fallback for classic bridge
262 PVE
::Network
::tap_plug
($iface, $bridge, $tag, $firewall, $trunks, $rate);
266 my $plugin_config = get_plugin_config
($vnet);
267 my $nodename = PVE
::INotify
::nodename
();
269 die "vnet $bridge is not allowed on this node\n"
270 if $plugin_config->{nodes
} && !defined($plugin_config->{nodes
}->{$nodename});
272 my $plugin = PVE
::Network
::SDN
::Zones
::Plugin-
>lookup($plugin_config->{type
});
273 $plugin->tap_plug($plugin_config, $vnet, $iface, $bridge, $firewall, $rate);