]> git.proxmox.com Git - pve-network.git/blame - src/PVE/API2/Network/SDN/Zones.pm
api: take partial configs for PUT /cluster/sdn/zones/<id>
[pve-network.git] / src / PVE / API2 / Network / SDN / Zones.pm
CommitLineData
4140be9e
AD
1package PVE::API2::Network::SDN::Zones;
2
3use strict;
4use warnings;
5
7f507618
TL
6use Storable qw(dclone);
7
8use PVE::Cluster qw(cfs_read_file cfs_write_file);
9use PVE::Exception qw(raise raise_param_exc);
10use PVE::JSONSchema qw(get_standard_option);
11use PVE::RPCEnvironment;
4140be9e
AD
12use PVE::SafeSyslog;
13use PVE::Tools qw(extract_param);
7f507618 14
4ad78442 15use PVE::Network::SDN::Dns;
7f507618
TL
16use PVE::Network::SDN::Subnets;
17use PVE::Network::SDN::Vnets;
18use PVE::Network::SDN;
19
4140be9e
AD
20use PVE::Network::SDN::Zones::EvpnPlugin;
21use PVE::Network::SDN::Zones::FaucetPlugin;
7f507618
TL
22use PVE::Network::SDN::Zones::Plugin;
23use PVE::Network::SDN::Zones::QinQPlugin;
880ae857 24use PVE::Network::SDN::Zones::SimplePlugin;
7f507618
TL
25use PVE::Network::SDN::Zones::VlanPlugin;
26use PVE::Network::SDN::Zones::VxlanPlugin;
27use PVE::Network::SDN::Zones;
4140be9e
AD
28
29use PVE::RESTHandler;
4140be9e
AD
30use base qw(PVE::RESTHandler);
31
32my $sdn_zones_type_enum = PVE::Network::SDN::Zones::Plugin->lookup_types();
33
34my $api_sdn_zones_config = sub {
35 my ($cfg, $id) = @_;
36
37 my $scfg = dclone(PVE::Network::SDN::Zones::sdn_zones_config($cfg, $id));
38 $scfg->{zone} = $id;
39 $scfg->{digest} = $cfg->{digest};
40
c2b9c173 41 if ($scfg->{nodes}) {
e382bf71
AD
42 $scfg->{nodes} = PVE::Network::SDN::encode_value($scfg->{type}, 'nodes', $scfg->{nodes});
43 }
44
45 if ($scfg->{exitnodes}) {
46 $scfg->{exitnodes} = PVE::Network::SDN::encode_value($scfg->{type}, 'exitnodes', $scfg->{exitnodes});
c2b9c173
AD
47 }
48
6f5f42e4
AD
49 my $pending = $scfg->{pending};
50 if ($pending->{nodes}) {
e382bf71
AD
51 $pending->{nodes} = PVE::Network::SDN::encode_value($scfg->{type}, 'nodes', $pending->{nodes});
52 }
53
54 if ($pending->{exitnodes}) {
55 $pending->{exitnodes} = PVE::Network::SDN::encode_value($scfg->{type}, 'exitnodes', $pending->{exitnodes});
6f5f42e4
AD
56 }
57
4140be9e
AD
58 return $scfg;
59};
60
61__PACKAGE__->register_method ({
62 name => 'index',
63 path => '',
64 method => 'GET',
65 description => "SDN zones index.",
66 permissions => {
3551b612 67 description => "Only list entries where you have 'SDN.Audit' or 'SDN.Allocate' permissions on '/sdn/zones/<zone>'",
4140be9e
AD
68 user => 'all',
69 },
70 parameters => {
71 additionalProperties => 0,
72 properties => {
73 type => {
7f507618 74 description => "Only list SDN zones of specific type",
4140be9e
AD
75 type => 'string',
76 enum => $sdn_zones_type_enum,
77 optional => 1,
78 },
6f5f42e4
AD
79 running => {
80 type => 'boolean',
81 optional => 1,
82 description => "Display running config.",
83 },
84 pending => {
85 type => 'boolean',
86 optional => 1,
87 description => "Display pending config.",
88 },
4140be9e
AD
89 },
90 },
91 returns => {
92 type => 'array',
93 items => {
94 type => "object",
0f700635 95 properties => { zone => { type => 'string'},
4140be9e 96 type => { type => 'string'},
6f5f42e4 97 mtu => { type => 'integer', optional => 1 },
4ad78442
AD
98 dns => { type => 'string', optional => 1},
99 reversedns => { type => 'string', optional => 1},
100 dnszone => { type => 'string', optional => 1},
331e2330 101 ipam => { type => 'string', optional => 1},
4c89e704 102 dhcp => { type => 'string', optional => 1},
6f5f42e4
AD
103 pending => { optional => 1},
104 state => { type => 'string', optional => 1},
105 nodes => { type => 'string', optional => 1},
4140be9e
AD
106 },
107 },
108 links => [ { rel => 'child', href => "{zone}" } ],
109 },
110 code => sub {
111 my ($param) = @_;
112
113 my $rpcenv = PVE::RPCEnvironment::get();
114 my $authuser = $rpcenv->get_user();
115
6f5f42e4 116 my $cfg = {};
7f507618 117 if ($param->{pending}) {
d73c7c36 118 my $running_cfg = PVE::Network::SDN::running_config();
6f5f42e4
AD
119 my $config = PVE::Network::SDN::Zones::config();
120 $cfg = PVE::Network::SDN::pending_config($running_cfg, $config, 'zones');
121 } elsif ($param->{running}) {
d73c7c36 122 my $running_cfg = PVE::Network::SDN::running_config();
6f5f42e4
AD
123 $cfg = $running_cfg->{zones};
124 } else {
125 $cfg = PVE::Network::SDN::Zones::config();
126 }
4140be9e
AD
127
128 my @sids = PVE::Network::SDN::Zones::sdn_zones_ids($cfg);
129 my $res = [];
7f507618 130 for my $id (@sids) {
3551b612
AD
131 my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
132 next if !$rpcenv->check_any($authuser, "/sdn/zones/$id", $privs, 1);
4140be9e
AD
133
134 my $scfg = &$api_sdn_zones_config($cfg, $id);
135 next if $param->{type} && $param->{type} ne $scfg->{type};
136
137 my $plugin_config = $cfg->{ids}->{$id};
138 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
139 push @$res, $scfg;
140 }
141
142 return $res;
143 }});
144
145__PACKAGE__->register_method ({
146 name => 'read',
147 path => '{zone}',
148 method => 'GET',
149 description => "Read sdn zone configuration.",
3551b612
AD
150 permissions => {
151 check => ['perm', '/sdn/zones/{zone}', ['SDN.Allocate']],
152 },
4140be9e
AD
153
154 parameters => {
155 additionalProperties => 0,
156 properties => {
157 zone => get_standard_option('pve-sdn-zone-id'),
6f5f42e4
AD
158 running => {
159 type => 'boolean',
160 optional => 1,
161 description => "Display running config.",
162 },
163 pending => {
164 type => 'boolean',
165 optional => 1,
166 description => "Display pending config.",
167 }
4140be9e
AD
168 },
169 },
170 returns => { type => 'object' },
171 code => sub {
172 my ($param) = @_;
173
6f5f42e4 174 my $cfg = {};
7f507618 175 if ($param->{pending}) {
d73c7c36 176 my $running_cfg = PVE::Network::SDN::running_config();
6f5f42e4
AD
177 my $config = PVE::Network::SDN::Zones::config();
178 $cfg = PVE::Network::SDN::pending_config($running_cfg, $config, 'zones');
179 } elsif ($param->{running}) {
d73c7c36 180 my $running_cfg = PVE::Network::SDN::running_config();
6f5f42e4
AD
181 $cfg = $running_cfg->{zones};
182 } else {
183 $cfg = PVE::Network::SDN::Zones::config();
184 }
4140be9e
AD
185
186 return &$api_sdn_zones_config($cfg, $param->{zone});
187 }});
188
189__PACKAGE__->register_method ({
190 name => 'create',
191 protected => 1,
192 path => '',
193 method => 'POST',
194 description => "Create a new sdn zone object.",
3551b612
AD
195 permissions => {
196 check => ['perm', '/sdn/zones', ['SDN.Allocate']],
197 },
4140be9e
AD
198 parameters => PVE::Network::SDN::Zones::Plugin->createSchema(),
199 returns => { type => 'null' },
200 code => sub {
201 my ($param) = @_;
202
203 my $type = extract_param($param, 'type');
204 my $id = extract_param($param, 'zone');
205
206 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($type);
207 my $opts = $plugin->check_config($id, $param, 1, 1);
208
7f507618
TL
209 PVE::Cluster::check_cfs_quorum();
210 mkdir("/etc/pve/sdn");
45c3f15c 211
7f507618
TL
212 PVE::Network::SDN::lock_sdn_config(sub {
213 my $zone_cfg = PVE::Network::SDN::Zones::config();
214 my $controller_cfg = PVE::Network::SDN::Controllers::config();
215 my $dns_cfg = PVE::Network::SDN::Dns::config();
4140be9e 216
7f507618
TL
217 my $scfg = undef;
218 if ($scfg = PVE::Network::SDN::Zones::sdn_zones_config($zone_cfg, $id, 1)) {
219 die "sdn zone object ID '$id' already defined\n";
220 }
4140be9e 221
7f507618
TL
222 my $dnsserver = $opts->{dns};
223 raise_param_exc({ dns => "$dnsserver don't exist"})
224 if $dnsserver && !$dns_cfg->{ids}->{$dnsserver};
4140be9e 225
7f507618
TL
226 my $reversednsserver = $opts->{reversedns};
227 raise_param_exc({ reversedns => "$reversednsserver don't exist"})
228 if $reversednsserver && !$dns_cfg->{ids}->{$reversednsserver};
331e2330 229
7f507618
TL
230 my $dnszone = $opts->{dnszone};
231 raise_param_exc({ dnszone => "missing dns server"})
232 if $dnszone && !$dnsserver;
4140be9e 233
7f507618
TL
234 my $ipam = $opts->{ipam};
235 my $ipam_cfg = PVE::Network::SDN::Ipams::config();
236 raise_param_exc({ ipam => "$ipam not existing"}) if $ipam && !$ipam_cfg->{ids}->{$ipam};
4140be9e 237
7f507618
TL
238 $zone_cfg->{ids}->{$id} = $opts;
239 $plugin->on_update_hook($id, $zone_cfg, $controller_cfg);
240
241 PVE::Network::SDN::Zones::write_config($zone_cfg);
4140be9e 242
7f507618
TL
243 }, "create sdn zone object failed");
244
245 return;
4140be9e
AD
246 }});
247
4140be9e
AD
248__PACKAGE__->register_method ({
249 name => 'update',
250 protected => 1,
251 path => '{zone}',
252 method => 'PUT',
253 description => "Update sdn zone object configuration.",
3551b612 254 permissions => {
d7c16200 255 check => ['perm', '/sdn/zones/{zone}', ['SDN.Allocate']],
3551b612 256 },
4140be9e
AD
257 parameters => PVE::Network::SDN::Zones::Plugin->updateSchema(),
258 returns => { type => 'null' },
259 code => sub {
260 my ($param) = @_;
261
262 my $id = extract_param($param, 'zone');
263 my $digest = extract_param($param, 'digest');
3e3cafab
WB
264 my $delete = extract_param($param, 'delete');
265
266 if ($delete) {
267 $delete = [ PVE::Tools::split_list($delete) ];
268 }
4140be9e 269
7f507618 270 PVE::Network::SDN::lock_sdn_config(sub {
a2b32a94
AD
271 my $zone_cfg = PVE::Network::SDN::Zones::config();
272 my $controller_cfg = PVE::Network::SDN::Controllers::config();
4ad78442 273 my $dns_cfg = PVE::Network::SDN::Dns::config();
4140be9e 274
a2b32a94 275 PVE::SectionConfig::assert_if_modified($zone_cfg, $digest);
4140be9e 276
a2b32a94 277 my $scfg = PVE::Network::SDN::Zones::sdn_zones_config($zone_cfg, $id);
4140be9e
AD
278
279 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($scfg->{type});
280 my $opts = $plugin->check_config($id, $param, 0, 1);
281
3e3cafab
WB
282 my $old_ipam = $scfg->{ipam};
283
284 if ($delete) {
285 my $options = $plugin->private()->{options}->{$scfg->{type}};
286 PVE::SectionConfig::delete_from_config($scfg, $options, $opts, $delete);
287 }
e8736dac 288
3e3cafab
WB
289 $scfg->{$_} = $opts->{$_} for keys $opts->%*;
290
291 my $new_ipam = $scfg->{ipam};
292 if (!$new_ipam != !$old_ipam || (($new_ipam//'') ne ($old_ipam//''))) {
7f507618 293 # don't allow ipam change if subnet are defined for now, need to implement resync ipam content
e8736dac 294 my $subnets_cfg = PVE::Network::SDN::Subnets::config();
7f507618 295 for my $subnetid (sort keys %{$subnets_cfg->{ids}}) {
e8736dac 296 my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($subnets_cfg, $subnetid);
7f507618
TL
297 raise_param_exc({ ipam => "can't change ipam if a subnet is already defined in this zone"})
298 if $subnet->{zone} eq $id;
e8736dac
AD
299 }
300 }
301
4ad78442 302 my $dnsserver = $opts->{dns};
4ad78442 303 raise_param_exc({ dns => "$dnsserver don't exist"}) if $dnsserver && !$dns_cfg->{ids}->{$dnsserver};
7f507618
TL
304
305 my $reversednsserver = $opts->{reversedns};
4ad78442 306 raise_param_exc({ reversedns => "$reversednsserver don't exist"}) if $reversednsserver && !$dns_cfg->{ids}->{$reversednsserver};
7f507618
TL
307
308 my $dnszone = $opts->{dnszone};
4ad78442
AD
309 raise_param_exc({ dnszone => "missing dns server"}) if $dnszone && !$dnsserver;
310
331e2330
AD
311 my $ipam = $opts->{ipam};
312 my $ipam_cfg = PVE::Network::SDN::Ipams::config();
313 raise_param_exc({ ipam => "$ipam not existing"}) if $ipam && !$ipam_cfg->{ids}->{$ipam};
314
a2b32a94 315 $plugin->on_update_hook($id, $zone_cfg, $controller_cfg);
4140be9e 316
a2b32a94 317 PVE::Network::SDN::Zones::write_config($zone_cfg);
4140be9e 318
7f507618 319 }, "update sdn zone object failed");
4140be9e 320
7f507618 321 return;
4140be9e
AD
322 }});
323
324__PACKAGE__->register_method ({
325 name => 'delete',
326 protected => 1,
327 path => '{zone}',
328 method => 'DELETE',
329 description => "Delete sdn zone object configuration.",
3551b612 330 permissions => {
d7c16200 331 check => ['perm', '/sdn/zones/{zone}', ['SDN.Allocate']],
3551b612 332 },
4140be9e 333 parameters => {
7f507618 334 additionalProperties => 0,
4140be9e
AD
335 properties => {
336 zone => get_standard_option('pve-sdn-zone-id', {
7f507618
TL
337 completion => \&PVE::Network::SDN::Zones::complete_sdn_zones,
338 }),
4140be9e
AD
339 },
340 },
341 returns => { type => 'null' },
342 code => sub {
343 my ($param) = @_;
344
345 my $id = extract_param($param, 'zone');
346
7f507618
TL
347 PVE::Network::SDN::lock_sdn_config(sub {
348 my $cfg = PVE::Network::SDN::Zones::config();
349 my $scfg = PVE::Network::SDN::Zones::sdn_zones_config($cfg, $id);
4140be9e 350
7f507618
TL
351 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($scfg->{type});
352 my $vnet_cfg = PVE::Network::SDN::Vnets::config();
4140be9e 353
7f507618 354 $plugin->on_delete_hook($id, $vnet_cfg);
4140be9e 355
7f507618 356 delete $cfg->{ids}->{$id};
4140be9e 357
7f507618
TL
358 PVE::Network::SDN::Zones::write_config($cfg);
359 }, "delete sdn zone object failed");
4140be9e 360
7f507618 361 return;
4140be9e
AD
362 }});
363
3641;