]> git.proxmox.com Git - pve-network.git/blame - PVE/Network/SDN.pm
controllers: evpn : use frr restart if reload fail
[pve-network.git] / PVE / Network / SDN.pm
CommitLineData
86d22462 1package PVE::Network::SDN;
92b6f291
AD
2
3use strict;
4use warnings;
434125ce 5
92b6f291 6use Data::Dumper;
c665cefc 7use JSON;
434125ce 8
7129a3ca 9use PVE::Network::SDN::Vnets;
f5eabba0 10use PVE::Network::SDN::Zones;
5d3e0248
AD
11use PVE::Network::SDN::Controllers;
12use PVE::Network::SDN::Subnets;
f5eabba0 13
434125ce 14use PVE::Tools qw(extract_param dir_glob_regex run_command);
92b6f291 15use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
92b6f291 16
f9bc9640 17
5d3e0248 18my $running_cfg = "sdn/.running-config";
f9bc9640 19
5d3e0248 20my $parse_running_cfg = sub {
f9bc9640
AD
21 my ($filename, $raw) = @_;
22
5d3e0248 23 my $cfg = {};
28664c9b 24
5d3e0248 25 return $cfg if !defined($raw) || $raw eq '';
f9bc9640 26
5d3e0248
AD
27 eval {
28 $cfg = from_json($raw);
29 };
30 return {} if $@;
31
32 return $cfg;
f9bc9640
AD
33};
34
5d3e0248
AD
35my $write_running_cfg = sub {
36 my ($filename, $cfg) = @_;
f9bc9640 37
5d3e0248 38 my $json = to_json($cfg);
f9bc9640 39
5d3e0248 40 return $json;
f9bc9640
AD
41};
42
5d3e0248 43PVE::Cluster::cfs_register_file($running_cfg, $parse_running_cfg, $write_running_cfg);
f9bc9640
AD
44
45
f5eabba0 46# improve me : move status code inside plugins ?
92b6f291 47
e424c7ac 48sub ifquery_check {
c665cefc
AD
49
50 my $cmd = ['ifquery', '-a', '-c', '-o','json'];
c665cefc 51
0c5021ad
TL
52 my $result = '';
53 my $reader = sub { $result .= shift };
c665cefc
AD
54
55 eval {
6e9fff39 56 run_command($cmd, outfunc => $reader);
c665cefc
AD
57 };
58
6e9fff39 59 my $resultjson = decode_json($result);
c665cefc
AD
60 my $interfaces = {};
61
62 foreach my $interface (@$resultjson) {
6e9fff39
AD
63 my $name = $interface->{name};
64 $interfaces->{$name} = {
65 status => $interface->{status},
66 config => $interface->{config},
67 config_status => $interface->{config_status},
68 };
c665cefc
AD
69 }
70
71 return $interfaces;
72}
73
e424c7ac
AD
74sub status {
75
56cdcac9
AD
76 my ($zone_status, $vnet_status) = PVE::Network::SDN::Zones::status();
77 return($zone_status, $vnet_status);
e424c7ac
AD
78}
79
d73c7c36 80sub running_config {
5d3e0248
AD
81 return cfs_read_file($running_cfg);
82}
83
6f5f42e4
AD
84sub pending_config {
85 my ($running_cfg, $cfg, $type) = @_;
86
87 my $pending = {};
88
89 my $running_objects = $running_cfg->{$type}->{ids};
90 my $config_objects = $cfg->{ids};
91
92 foreach my $id (sort keys %{$running_objects}) {
93 my $running_object = $running_objects->{$id};
94 my $config_object = $config_objects->{$id};
95 foreach my $key (sort keys %{$running_object}) {
96 $pending->{$id}->{$key} = $running_object->{$key};
97 if(!keys %{$config_object}) {
98 $pending->{$id}->{state} = "deleted";
e382bf71
AD
99 } elsif (!defined($config_object->{$key})) {
100 $pending->{$id}->{"pending"}->{$key} = 'deleted';
101 $pending->{$id}->{state} = "changed";
102 } elsif (PVE::Network::SDN::encode_value(undef, $key, $running_object->{$key})
103 ne PVE::Network::SDN::encode_value(undef, $key, $config_object->{$key})) {
6f5f42e4
AD
104 $pending->{$id}->{state} = "changed";
105 }
106 }
36fecf55 107 $pending->{$id}->{"pending"} = {} if $pending->{$id}->{state} && !defined($pending->{$id}->{"pending"});
6f5f42e4
AD
108 }
109
110 foreach my $id (sort keys %{$config_objects}) {
111 my $running_object = $running_objects->{$id};
112 my $config_object = $config_objects->{$id};
113
114 foreach my $key (sort keys %{$config_object}) {
e382bf71
AD
115 my $config_value = PVE::Network::SDN::encode_value(undef, $key, $config_object->{$key}) if $config_object->{$key};
116 my $running_value = PVE::Network::SDN::encode_value(undef, $key, $running_object->{$key}) if $running_object->{$key};
6f5f42e4
AD
117 if($key eq 'type' || $key eq 'vnet') {
118 $pending->{$id}->{$key} = $config_value;
119 } else {
120 $pending->{$id}->{"pending"}->{$key} = $config_value if !defined($running_value) || ($config_value ne $running_value);
121 }
122 if(!keys %{$running_object}) {
123 $pending->{$id}->{state} = "new";
124 } elsif (!defined($running_value) && defined($config_value)) {
125 $pending->{$id}->{state} = "changed";
126 }
127 }
36fecf55 128 $pending->{$id}->{"pending"} = {} if $pending->{$id}->{state} && !defined($pending->{$id}->{"pending"});
6f5f42e4
AD
129 }
130
131 return {ids => $pending};
132
133}
134
5d3e0248 135sub commit_config {
f9bc9640 136
5d3e0248
AD
137 my $cfg = cfs_read_file($running_cfg);
138 my $version = $cfg->{version};
f9bc9640 139
22b24447 140 if ($version) {
f9bc9640
AD
141 $version++;
142 } else {
143 $version = 1;
144 }
145
5d3e0248
AD
146 my $vnets_cfg = PVE::Network::SDN::Vnets::config();
147 my $zones_cfg = PVE::Network::SDN::Zones::config();
148 my $controllers_cfg = PVE::Network::SDN::Controllers::config();
149 my $subnets_cfg = PVE::Network::SDN::Subnets::config();
150
151 my $vnets = { ids => $vnets_cfg->{ids} };
152 my $zones = { ids => $zones_cfg->{ids} };
153 my $controllers = { ids => $controllers_cfg->{ids} };
154 my $subnets = { ids => $subnets_cfg->{ids} };
155
156 $cfg = { version => $version, vnets => $vnets, zones => $zones, controllers => $controllers, subnets => $subnets };
157
158 cfs_write_file($running_cfg, $cfg);
f9bc9640
AD
159}
160
161sub lock_sdn_config {
162 my ($code, $errmsg) = @_;
163
5d3e0248 164 cfs_lock_file($running_cfg, undef, $code);
f9bc9640
AD
165
166 if (my $err = $@) {
167 $errmsg ? die "$errmsg: $err" : die $err;
168 }
169}
170
7129a3ca
AD
171sub get_local_vnets {
172
173 my $rpcenv = PVE::RPCEnvironment::get();
174
175 my $authuser = $rpcenv->get_user();
176
177 my $nodename = PVE::INotify::nodename();
178
d73c7c36 179 my $cfg = PVE::Network::SDN::running_config();
5d3e0248
AD
180 my $vnets_cfg = $cfg->{vnets};
181 my $zones_cfg = $cfg->{zones};
7129a3ca
AD
182
183 my @vnetids = PVE::Network::SDN::Vnets::sdn_vnets_ids($vnets_cfg);
184
185 my $vnets = {};
186
187 foreach my $vnetid (@vnetids) {
188
189 my $vnet = PVE::Network::SDN::Vnets::sdn_vnets_config($vnets_cfg, $vnetid);
190 my $zoneid = $vnet->{zone};
c1dff7b2
AD
191 my $comments = $vnet->{alias};
192
7129a3ca
AD
193 my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
194
195 next if !$zoneid;
bb95abb0 196 next if !$rpcenv->check_any($authuser, "/sdn/zones/$zoneid", $privs, 1) && !$rpcenv->check_any($authuser, "/sdn/vnets/$vnetid", $privs, 1);
7129a3ca
AD
197
198 my $zone_config = PVE::Network::SDN::Zones::sdn_zones_config($zones_cfg, $zoneid);
199
200 next if defined($zone_config->{nodes}) && !$zone_config->{nodes}->{$nodename};
3d8799db
AD
201 my $ipam = $zone_config->{ipam} ? 1 : 0;
202 my $vlanaware = $vnet->{vlanaware} ? 1 : 0;
203 $vnets->{$vnetid} = { type => 'vnet', active => '1', ipam => $ipam, vlanaware => $vlanaware, comments => $comments };
7129a3ca
AD
204 }
205
206 return $vnets;
207}
208
22b24447
TL
209sub generate_zone_config {
210 my $raw_config = PVE::Network::SDN::Zones::generate_etc_network_config();
211 PVE::Network::SDN::Zones::write_etc_network_config($raw_config);
212}
213
214sub generate_controller_config {
215 my ($reload) = @_;
216
217 my $raw_config = PVE::Network::SDN::Controllers::generate_controller_config();
218 PVE::Network::SDN::Controllers::write_controller_config($raw_config);
219
220 PVE::Network::SDN::Controllers::reload_controller() if $reload;
221}
222
e382bf71
AD
223sub encode_value {
224 my ($type, $key, $value) = @_;
225
226 if ($key eq 'nodes' || $key eq 'exitnodes') {
227 if(ref($value) eq 'HASH') {
228 return join(',', sort keys(%$value));
229 } else {
230 return $value;
231 }
232 }
233
234 return $value;
235}
236
80348b2d 237
167dc03f
AD
238#helpers
239sub api_request {
240 my ($method, $url, $headers, $data) = @_;
241
242 my $encoded_data = to_json($data) if $data;
243
244 my $req = HTTP::Request->new($method,$url, $headers, $encoded_data);
245
246 my $ua = LWP::UserAgent->new(protocols_allowed => ['http', 'https'], timeout => 30);
247 my $proxy = undef;
248
249 if ($proxy) {
250 $ua->proxy(['http', 'https'], $proxy);
251 } else {
252 $ua->env_proxy;
253 }
254
255 $ua->ssl_opts(verify_hostname => 0, SSL_verify_mode => 0x00);
256
257 my $response = $ua->request($req);
258 my $code = $response->code;
259
260 if ($code !~ /^2(\d+)$/) {
261 my $msg = $response->message || 'unknown';
262 die "Invalid response from server: $code $msg\n";
263 }
264
265 my $raw = '';
266 if (defined($response->decoded_content)) {
267 $raw = $response->decoded_content;
268 } else {
269 $raw = $response->content;
270 }
271
272 return if $raw eq '';
273
274 my $json = '';
275 eval {
276 $json = from_json($raw);
277 };
278 die "api response is not a json" if $@;
279
280 return $json;
281}
282
2831;