]> git.proxmox.com Git - pve-network.git/blob - PVE/Network/SDN/Zones.pm
evpn: remove uplink-id
[pve-network.git] / PVE / Network / SDN / Zones.pm
1 package PVE::Network::SDN::Zones;
2
3 use strict;
4 use warnings;
5
6 use Data::Dumper;
7 use JSON;
8
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
12 use PVE::Network::SDN::Vnets;
13 use PVE::Network::SDN::Zones::VlanPlugin;
14 use PVE::Network::SDN::Zones::QinQPlugin;
15 use PVE::Network::SDN::Zones::VxlanPlugin;
16 use PVE::Network::SDN::Zones::EvpnPlugin;
17 use PVE::Network::SDN::Zones::FaucetPlugin;
18 use PVE::Network::SDN::Zones::Plugin;
19
20 PVE::Network::SDN::Zones::VlanPlugin->register();
21 PVE::Network::SDN::Zones::QinQPlugin->register();
22 PVE::Network::SDN::Zones::VxlanPlugin->register();
23 PVE::Network::SDN::Zones::EvpnPlugin->register();
24 PVE::Network::SDN::Zones::FaucetPlugin->register();
25 PVE::Network::SDN::Zones::Plugin->init();
26
27
28 sub sdn_zones_config {
29 my ($cfg, $id, $noerr) = @_;
30
31 die "no sdn zone ID specified\n" if !$id;
32
33 my $scfg = $cfg->{ids}->{$id};
34 die "sdn '$id' does not exists\n" if (!$noerr && !$scfg);
35
36 return $scfg;
37 }
38
39 sub config {
40 my $config = cfs_read_file("sdn/zones.cfg.new");
41 $config = cfs_read_file("sdn/zones.cfg") if !keys %{$config->{ids}};
42 return $config;
43 }
44
45 sub write_config {
46 my ($cfg) = @_;
47
48 cfs_write_file("sdn/zones.cfg.new", $cfg);
49 }
50
51 sub lock_sdn_zones_config {
52 my ($code, $errmsg) = @_;
53
54 cfs_lock_file("sdn/zones.cfg.new", undef, $code);
55 if (my $err = $@) {
56 $errmsg ? die "$errmsg: $err" : die $err;
57 }
58 }
59
60 sub sdn_zones_ids {
61 my ($cfg) = @_;
62
63 return keys %{$cfg->{ids}};
64 }
65
66 sub complete_sdn_zone {
67 my ($cmdname, $pname, $cvalue) = @_;
68
69 my $cfg = PVE::Network::SDN::config();
70
71 return $cmdname eq 'add' ? [] : [ PVE::Network::SDN::sdn_zones_ids($cfg) ];
72 }
73
74
75 sub generate_etc_network_config {
76
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;
81
82 #read main config for physical interfaces
83 my $current_config_file = "/etc/network/interfaces";
84 my $fh = IO::File->new($current_config_file);
85 my $interfaces_config = PVE::INotify::read_etc_network_interfaces(1,$fh);
86 $fh->close();
87
88 #check uplinks
89 my $uplinks = {};
90 foreach my $id (keys %{$interfaces_config->{ifaces}}) {
91 my $interface = $interfaces_config->{ifaces}->{$id};
92 if (my $uplink = $interface->{'uplink-id'}) {
93 die "uplink-id $uplink is already defined on $uplinks->{$uplink}" if $uplinks->{$uplink};
94 $interface->{name} = $id;
95 $uplinks->{$interface->{'uplink-id'}} = $interface;
96 }
97 }
98
99 #generate configuration
100 my $config = {};
101 my $nodename = PVE::INotify::nodename();
102
103 foreach my $id (keys %{$vnet_cfg->{ids}}) {
104 my $vnet = $vnet_cfg->{ids}->{$id};
105 my $zone = $vnet->{zone};
106
107 if(!$zone) {
108 warn "can't generate vnet $vnet : zone $zone don't exist";
109 next;
110 }
111
112 my $plugin_config = $zone_cfg->{ids}->{$zone};
113
114 if (!defined($plugin_config)) {
115 warn "can't generate vnet $vnet : zone $zone don't exist";
116 next;
117 }
118
119 next if defined($plugin_config->{nodes}) && !$plugin_config->{nodes}->{$nodename};
120
121 my $controller = undef;
122 if($plugin_config->{controller}) {
123 my $controllerid = $plugin_config->{controller};
124 $controller = $controller_cfg->{ids}->{$controllerid};
125 }
126
127 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
128 $plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $uplinks, $controller, $config);
129 }
130
131 my $raw_network_config = "";
132 foreach my $iface (keys %$config) {
133 $raw_network_config .= "\n";
134 $raw_network_config .= "auto $iface\n";
135 $raw_network_config .= "iface $iface\n";
136 foreach my $option (@{$config->{$iface}}) {
137 $raw_network_config .= "\t$option\n";
138 }
139 }
140
141 return $raw_network_config;
142 }
143
144 sub write_etc_network_config {
145 my ($rawconfig) = @_;
146
147 return if !$rawconfig;
148 my $sdn_interfaces_file = "/etc/network/interfaces.d/sdn";
149
150 my $writefh = IO::File->new($sdn_interfaces_file,">");
151 print $writefh $rawconfig;
152 $writefh->close();
153 }
154
155 sub ifquery_check {
156
157 my $cmd = ['ifquery', '-a', '-c', '-o','json'];
158
159 my $result = '';
160 my $reader = sub { $result .= shift };
161
162 eval {
163 run_command($cmd, outfunc => $reader);
164 };
165
166 my $resultjson = decode_json($result);
167 my $interfaces = {};
168
169 foreach my $interface (@$resultjson) {
170 my $name = $interface->{name};
171 $interfaces->{$name} = {
172 status => $interface->{status},
173 config => $interface->{config},
174 config_status => $interface->{config_status},
175 };
176 }
177
178 return $interfaces;
179 }
180
181 # improve me : move status code inside plugins ?
182 sub status {
183
184 my $cluster_vnet_file = "/etc/pve/sdn/vnets.cfg";
185 my $cluster_zone_file = "/etc/pve/sdn/zones.cfg";
186 my $local_sdn_file = "/etc/network/interfaces.d/sdn";
187 my $err_config = undef;
188
189 return if !-e $cluster_vnet_file && !-e $cluster_zone_file;
190
191 if (!-e $local_sdn_file) {
192
193 $err_config = "local sdn network configuration is not yet generated, please reload";
194 warn $err_config;
195 } else {
196 # fixme : use some kind of versioning info?
197 my $cluster_vnet_timestamp = (stat($cluster_vnet_file))[9];
198 my $cluster_zone_timestamp = (stat($cluster_zone_file))[9];
199 my $local_sdn_timestamp = (stat($local_sdn_file))[9];
200
201 if ($local_sdn_timestamp < $cluster_vnet_timestamp || $local_sdn_timestamp < $cluster_zone_timestamp) {
202 $err_config = "local sdn network configuration is too old, please reload";
203 warn $err_config;
204 }
205 }
206
207 my $status = ifquery_check();
208
209 my $vnet_cfg = PVE::Cluster::cfs_read_file('sdn/vnets.cfg');
210 my $zone_cfg = PVE::Cluster::cfs_read_file('sdn/zones.cfg');
211 my $nodename = PVE::INotify::nodename();
212
213 my $vnet_status = {};
214 my $zone_status = {};
215
216 foreach my $id (keys %{$vnet_cfg->{ids}}) {
217 my $zone = $vnet_cfg->{ids}->{$id}->{zone};
218 next if defined($zone_cfg->{ids}->{$zone}->{nodes}) && !$zone_cfg->{ids}->{$zone}->{nodes}->{$nodename};
219
220 $vnet_status->{$id}->{zone} = $zone;
221 $zone_status->{$zone}->{status} = 'available' if !defined($zone_status->{$zone}->{status});
222
223 if($err_config) {
224 $vnet_status->{$id}->{status} = 'pending';
225 $vnet_status->{$id}->{statusmsg} = $err_config;
226 $zone_status->{$zone}->{status} = 'pending';
227 } elsif ($status->{$id}->{status} && $status->{$id}->{status} eq 'pass') {
228 $vnet_status->{$id}->{status} = 'available';
229 my $bridgeport = $status->{$id}->{config}->{'bridge-ports'};
230
231 if ($status->{$bridgeport}->{status} && $status->{$bridgeport}->{status} ne 'pass') {
232 $vnet_status->{$id}->{status} = 'error';
233 $vnet_status->{$id}->{statusmsg} = 'configuration not fully applied';
234 $zone_status->{$zone}->{status} = 'error';
235 }
236 } else {
237 $vnet_status->{$id}->{status} = 'error';
238 $vnet_status->{$id}->{statusmsg} = 'missing';
239 $zone_status->{$zone}->{status} = 'error';
240 }
241 }
242 return($zone_status, $vnet_status);
243 }
244
245 1;
246