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