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