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();
29 sub sdn_zones_config
{
30 my ($cfg, $id, $noerr) = @_;
32 die "no sdn zone ID specified\n" if !$id;
34 my $scfg = $cfg->{ids
}->{$id};
35 die "sdn '$id' does not exist\n" if (!$noerr && !$scfg);
41 my $config = cfs_read_file
("sdn/zones.cfg.new");
42 $config = cfs_read_file
("sdn/zones.cfg") if !keys %{$config->{ids
}};
49 cfs_write_file
("sdn/zones.cfg.new", $cfg);
52 sub lock_sdn_zones_config
{
53 my ($code, $errmsg) = @_;
55 cfs_lock_file
("sdn/zones.cfg.new", undef, $code);
57 $errmsg ?
die "$errmsg: $err" : die $err;
64 return keys %{$cfg->{ids
}};
67 sub complete_sdn_zone
{
68 my ($cmdname, $pname, $cvalue) = @_;
70 my $cfg = PVE
::Network
::SDN
::config
();
72 return $cmdname eq 'add' ?
[] : [ PVE
::Network
::SDN
::sdn_zones_ids
($cfg) ];
76 sub generate_etc_network_config
{
78 my $vnet_cfg = PVE
::Cluster
::cfs_read_file
('sdn/vnets.cfg');
79 my $zone_cfg = PVE
::Cluster
::cfs_read_file
('sdn/zones.cfg');
80 my $controller_cfg = PVE
::Cluster
::cfs_read_file
('sdn/controllers.cfg');
81 return if !$vnet_cfg && !$zone_cfg;
83 my $interfaces_config = PVE
::INotify
::read_file
('interfaces');
85 #generate configuration
87 my $nodename = PVE
::INotify
::nodename
();
89 foreach my $id (keys %{$vnet_cfg->{ids
}}) {
90 my $vnet = $vnet_cfg->{ids
}->{$id};
91 my $zone = $vnet->{zone
};
94 warn "can't generate vnet $vnet : zone $zone don't exist";
98 my $plugin_config = $zone_cfg->{ids
}->{$zone};
100 if (!defined($plugin_config)) {
101 warn "can't generate vnet $vnet : zone $zone don't exist";
105 next if defined($plugin_config->{nodes
}) && !$plugin_config->{nodes
}->{$nodename};
107 my $controller = undef;
108 if($plugin_config->{controller
}) {
109 my $controllerid = $plugin_config->{controller
};
110 $controller = $controller_cfg->{ids
}->{$controllerid};
113 my $plugin = PVE
::Network
::SDN
::Zones
::Plugin-
>lookup($plugin_config->{type
});
114 $plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $controller, $interfaces_config, $config);
117 my $raw_network_config = "";
118 foreach my $iface (keys %$config) {
119 $raw_network_config .= "\n";
120 $raw_network_config .= "auto $iface\n";
121 $raw_network_config .= "iface $iface\n";
122 foreach my $option (@{$config->{$iface}}) {
123 $raw_network_config .= "\t$option\n";
127 return $raw_network_config;
130 sub write_etc_network_config
{
131 my ($rawconfig) = @_;
133 return if !$rawconfig;
134 my $sdn_interfaces_file = "/etc/network/interfaces.d/sdn";
136 my $writefh = IO
::File-
>new($sdn_interfaces_file,">");
137 print $writefh $rawconfig;
143 my $cmd = ['ifquery', '-a', '-c', '-o','json'];
146 my $reader = sub { $result .= shift };
149 run_command
($cmd, outfunc
=> $reader);
152 my $resultjson = decode_json
($result);
155 foreach my $interface (@$resultjson) {
156 my $name = $interface->{name
};
157 $interfaces->{$name} = {
158 status
=> $interface->{status
},
159 config
=> $interface->{config
},
160 config_status
=> $interface->{config_status
},
167 # improve me : move status code inside plugins ?
170 my $cluster_vnet_file = "/etc/pve/sdn/vnets.cfg";
171 my $cluster_zone_file = "/etc/pve/sdn/zones.cfg";
172 my $local_sdn_file = "/etc/network/interfaces.d/sdn";
173 my $err_config = undef;
175 return if !-e
$cluster_vnet_file && !-e
$cluster_zone_file;
177 if (!-e
$local_sdn_file) {
179 $err_config = "local sdn network configuration is not yet generated, please reload";
180 warn "$err_config\n";
182 # fixme : use some kind of versioning info?
183 my $cluster_vnet_timestamp = (stat($cluster_vnet_file))[9];
184 my $cluster_zone_timestamp = (stat($cluster_zone_file))[9];
185 my $local_sdn_timestamp = (stat($local_sdn_file))[9];
187 if ($local_sdn_timestamp < $cluster_vnet_timestamp || $local_sdn_timestamp < $cluster_zone_timestamp) {
188 $err_config = "local sdn network configuration is too old, please reload";
189 warn "$err_config\n";
193 my $status = ifquery_check
();
195 my $vnet_cfg = PVE
::Cluster
::cfs_read_file
('sdn/vnets.cfg');
196 my $zone_cfg = PVE
::Cluster
::cfs_read_file
('sdn/zones.cfg');
197 my $nodename = PVE
::INotify
::nodename
();
200 my $vnet_status = {};
201 my $zone_status = {};
203 foreach my $id (sort keys %{$vnet_cfg->{ids
}}) {
204 my $vnet = $vnet_cfg->{ids
}->{$id};
205 my $zone = $vnet->{zone
};
208 my $plugin_config = $zone_cfg->{ids
}->{$zone};
209 next if defined($plugin_config->{nodes
}) && !$plugin_config->{nodes
}->{$nodename};
211 my $plugin = PVE
::Network
::SDN
::Zones
::Plugin-
>lookup($plugin_config->{type
});
212 $plugin->status($plugin_config, $zone, $id, $vnet, $err_config, $status, $vnet_status, $zone_status);
215 return($zone_status, $vnet_status);
218 sub get_bridge_vlan
{
221 my $vnet = PVE
::Network
::SDN
::Vnets
::get_vnet
($vnetid);
223 #fallback if classic bridge
224 return ($vnetid, undef) if !$vnet;
226 my $zone_cfg = PVE
::Network
::SDN
::Zones
::config
();
227 my $zoneid = $vnet->{zone
};
228 my $tag = $vnet->{tag
};
230 my $plugin_config = $zone_cfg->{ids
}->{$zoneid};
231 my $plugin = PVE
::Network
::SDN
::Zones
::Plugin-
>lookup($plugin_config->{type
});
232 return $plugin->get_bridge_vlan($plugin_config, $vnetid, $tag);
236 my ($iface, $bridge) = @_;
238 my $vnet = PVE
::Network
::SDN
::Vnets
::get_vnet
($bridge);
240 #fallback if classic bridge
242 PVE
::Network
::tap_create
($iface, $bridge);
246 my $zone_cfg = PVE
::Network
::SDN
::Zones
::config
();
247 my $zoneid = $vnet->{zone
};
249 my $plugin_config = $zone_cfg->{ids
}->{$zoneid};
250 my $plugin = PVE
::Network
::SDN
::Zones
::Plugin-
>lookup($plugin_config->{type
});
251 $plugin->tap_create($plugin_config, $vnet, $iface, $bridge);
255 my ($veth, $vethpeer, $bridge, $hwaddr) = @_;
257 my $vnet = PVE
::Network
::SDN
::Vnets
::get_vnet
($bridge);
259 #fallback if classic bridge
261 PVE
::Network
::veth_create
($veth, $vethpeer, $bridge, $hwaddr);
265 my $zone_cfg = PVE
::Network
::SDN
::Zones
::config
();
266 my $zoneid = $vnet->{zone
};
268 my $plugin_config = $zone_cfg->{ids
}->{$zoneid};
269 my $plugin = PVE
::Network
::SDN
::Zones
::Plugin-
>lookup($plugin_config->{type
});
270 $plugin->veth_create($plugin_config, $vnet, $veth, $vethpeer, $bridge, $hwaddr);
274 my ($iface, $bridge, $tag, $firewall, $trunks, $rate) = @_;
276 my $vnet = PVE
::Network
::SDN
::Vnets
::get_vnet
($bridge);
278 #fallback if classic bridge
280 PVE
::Network
::tap_plug
($iface, $bridge, $tag, $firewall, $trunks, $rate);
284 my $zone_cfg = PVE
::Network
::SDN
::Zones
::config
();
285 my $nodename = PVE
::INotify
::nodename
();
287 my $zoneid = $vnet->{zone
};
290 die "vnet $bridge is not allowed on this node" if defined($zone_cfg->{ids
}->{$zoneid}->{nodes
}) && !$zone_cfg->{ids
}->{$zoneid}->{nodes
}->{$nodename};
292 my $plugin_config = $zone_cfg->{ids
}->{$zoneid};
293 my $plugin = PVE
::Network
::SDN
::Zones
::Plugin-
>lookup($plugin_config->{type
});
294 $plugin->tap_plug($plugin_config, $vnet, $iface, $bridge, $firewall, $rate);