]> git.proxmox.com Git - pve-manager.git/blob - PVE/API2/Network.pm
35b077b97baf6495f9cbb05d5a61cefda6967f33
[pve-manager.git] / PVE / API2 / Network.pm
1 package PVE::API2::Network;
2
3 use strict;
4 use warnings;
5
6 use Net::IP qw(:PROC);
7 use PVE::Tools qw(extract_param);
8 use PVE::SafeSyslog;
9 use PVE::INotify;
10 use PVE::Exception qw(raise_param_exc);
11 use PVE::RESTHandler;
12 use PVE::RPCEnvironment;
13 use PVE::JSONSchema qw(get_standard_option);
14 use PVE::AccessControl;
15 use IO::File;
16
17 use base qw(PVE::RESTHandler);
18
19 my $iflockfn = "/etc/network/.pve-interfaces.lock";
20
21 my $bond_mode_enum = [
22 'balance-rr',
23 'active-backup',
24 'balance-xor',
25 'broadcast',
26 '802.3ad',
27 'balance-tlb',
28 'balance-alb'
29 ];
30
31 my $confdesc = {
32 autostart => {
33 description => "Automatically start interface on boot.",
34 type => 'boolean',
35 optional => 1,
36 },
37 bridge_ports => {
38 description => "Specify the iterfaces you want to add to your bridge.",
39 optional => 1,
40 type => 'string', format => 'pve-iface-list',
41 },
42 slaves => {
43 description => "Specify the interfaces used by the bonding device.",
44 optional => 1,
45 type => 'string', format => 'pve-iface-list',
46 },
47 bond_mode => {
48 description => "Bonding mode.",
49 optional => 1,
50 type => 'string', enum => $bond_mode_enum,
51 },
52 gateway => {
53 description => 'Default gateway address.',
54 type => 'string', format => 'ipv4',
55 optional => 1,
56 },
57 netmask => {
58 description => 'Network mask.',
59 type => 'string', format => 'ipv4mask',
60 optional => 1,
61 requires => 'address',
62 },
63 address => {
64 description => 'IP address.',
65 type => 'string', format => 'ipv4',
66 optional => 1,
67 requires => 'netmask',
68 }
69 };
70
71 sub json_config_properties {
72 my $prop = shift;
73
74 foreach my $opt (keys %$confdesc) {
75 $prop->{$opt} = $confdesc->{$opt};
76 }
77
78 return $prop;
79 }
80
81 __PACKAGE__->register_method({
82 name => 'index',
83 path => '',
84 method => 'GET',
85 permissions => { user => 'all' },
86 description => "List available networks",
87 proxyto => 'node',
88 parameters => {
89 additionalProperties => 0,
90 properties => {
91 node => get_standard_option('pve-node'),
92 type => {
93 description => "Only list specific interface types.",
94 type => 'string',
95 enum => ['bond', 'bridge', 'alias', 'eth'],
96 optional => 1,
97 },
98 },
99 },
100 returns => {
101 type => "array",
102 items => {
103 type => "object",
104 properties => {},
105 },
106 links => [ { rel => 'child', href => "{iface}" } ],
107 },
108 code => sub {
109 my ($param) = @_;
110
111 my $rpcenv = PVE::RPCEnvironment::get();
112
113 my $tmp = PVE::INotify::read_file('interfaces', 1);
114 my $config = $tmp->{data};
115 my $changes = $tmp->{changes};
116
117 $rpcenv->set_result_attrib('changes', $changes) if $changes;
118
119 delete $config->{lo}; # do not list the loopback device
120
121 if ($param->{type}) {
122 foreach my $k (keys %$config) {
123 delete $config->{$k} if $param->{type} ne $config->{$k}->{type};
124 }
125 }
126
127 return PVE::RESTHandler::hash_to_array($config, 'iface');
128 }});
129
130 __PACKAGE__->register_method({
131 name => 'revert_network_changes',
132 path => '',
133 method => 'DELETE',
134 permissions => {
135 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
136 },
137 protected => 1,
138 description => "Revert network configuration changes.",
139 proxyto => 'node',
140 parameters => {
141 additionalProperties => 0,
142 properties => {
143 node => get_standard_option('pve-node'),
144 },
145 },
146 returns => { type => "null" },
147 code => sub {
148 my ($param) = @_;
149
150 unlink "/etc/network/interfaces.new";
151
152 return undef;
153 }});
154
155 my $check_duplicate_gateway = sub {
156 my ($config, $newiface) = @_;
157
158 foreach my $iface (keys %$config) {
159 raise_param_exc({ gateway => "Default gateway already exists on interface '$iface'." })
160 if ($newiface ne $iface) && $config->{$iface}->{gateway};
161 }
162 };
163
164 my $check_ipv4_settings = sub {
165 my ($address, $netmask) = @_;
166
167 my $binip = Net::IP::ip_iptobin($address, 4);
168 my $binmask = Net::IP::ip_iptobin($netmask, 4);
169 my $broadcast = Net::IP::ip_iptobin('255.255.255.255', 4);
170 my $binhost = $binip | $binmask;
171
172 raise_param_exc({ address => "$address is not a valid host ip address." })
173 if ($binhost eq $binmask) || ($binhost eq $broadcast);
174 };
175
176 __PACKAGE__->register_method({
177 name => 'create_network',
178 path => '',
179 method => 'POST',
180 permissions => {
181 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
182 },
183 description => "Create network device configuration",
184 protected => 1,
185 proxyto => 'node',
186 parameters => {
187 additionalProperties => 0,
188 properties => json_config_properties({
189 node => get_standard_option('pve-node'),
190 iface => get_standard_option('pve-iface')}),
191 },
192 returns => { type => 'null' },
193 code => sub {
194 my ($param) = @_;
195
196 my $node = extract_param($param, 'node');
197 my $iface = extract_param($param, 'iface');
198
199 my $code = sub {
200 my $config = PVE::INotify::read_file('interfaces');
201
202 raise_param_exc({ iface => "interface already exists" })
203 if $config->{$iface};
204
205 &$check_duplicate_gateway($config, $iface)
206 if $param->{gateway};
207
208 &$check_ipv4_settings($param->{address}, $param->{netmask})
209 if $param->{address};
210
211 $param->{method} = $param->{address} ? 'static' : 'manual';
212
213 $config->{$iface} = $param;
214
215 PVE::INotify::write_file('interfaces', $config);
216 };
217
218 PVE::Tools::lock_file($iflockfn, 10, $code);
219 die $@ if $@;
220
221 return undef;
222 }});
223
224 __PACKAGE__->register_method({
225 name => 'update_network',
226 path => '{iface}',
227 method => 'PUT',
228 permissions => {
229 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
230 },
231 description => "Update network device configuration",
232 protected => 1,
233 proxyto => 'node',
234 parameters => {
235 additionalProperties => 0,
236 properties => json_config_properties({
237 node => get_standard_option('pve-node'),
238 iface => get_standard_option('pve-iface'),
239 delete => {
240 type => 'string', format => 'pve-configid-list',
241 description => "A list of settings you want to delete.",
242 optional => 1,
243 }}),
244 },
245 returns => { type => 'null' },
246 code => sub {
247 my ($param) = @_;
248
249 my $node = extract_param($param, 'node');
250 my $iface = extract_param($param, 'iface');
251 my $delete = extract_param($param, 'delete');
252
253 my $code = sub {
254 my $config = PVE::INotify::read_file('interfaces');
255
256 raise_param_exc({ iface => "interface does not exist" })
257 if !$config->{$iface};
258
259 foreach my $k (PVE::Tools::split_list($delete)) {
260 delete $config->{$iface}->{$k};
261 }
262
263 &$check_duplicate_gateway($config, $iface)
264 if $param->{gateway};
265
266 &$check_ipv4_settings($param->{address}, $param->{netmask})
267 if $param->{address};
268
269 $param->{method} = $param->{address} ? 'static' : 'manual';
270
271 foreach my $k (keys %$param) {
272 $config->{$iface}->{$k} = $param->{$k};
273 }
274
275 PVE::INotify::write_file('interfaces', $config);
276 };
277
278 PVE::Tools::lock_file($iflockfn, 10, $code);
279 die $@ if $@;
280
281 return undef;
282 }});
283
284 __PACKAGE__->register_method({
285 name => 'network_config',
286 path => '{iface}',
287 method => 'GET',
288 permissions => {
289 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
290 },
291 description => "Read network device configuration",
292 proxyto => 'node',
293 parameters => {
294 additionalProperties => 0,
295 properties => {
296 node => get_standard_option('pve-node'),
297 iface => get_standard_option('pve-iface'),
298 },
299 },
300 returns => {
301 type => "object",
302 properties => {
303 type => {
304 type => 'string',
305 },
306 method => {
307 type => 'string',
308 },
309 },
310 },
311 code => sub {
312 my ($param) = @_;
313
314 my $config = PVE::INotify::read_file('interfaces');
315
316 raise_param_exc({ iface => "interface does not exist" })
317 if !$config->{$param->{iface}};
318
319 return $config->{$param->{iface}};
320 }});
321
322 __PACKAGE__->register_method({
323 name => 'delete_network',
324 path => '{iface}',
325 method => 'DELETE',
326 permissions => {
327 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
328 },
329 description => "Delete network device configuration",
330 protected => 1,
331 proxyto => 'node',
332 parameters => {
333 additionalProperties => 0,
334 properties => {
335 node => get_standard_option('pve-node'),
336 iface => get_standard_option('pve-iface'),
337 },
338 },
339 returns => { type => 'null' },
340 code => sub {
341 my ($param) = @_;
342
343 my $code = sub {
344 my $config = PVE::INotify::read_file('interfaces');
345
346 raise_param_exc({ iface => "interface does not exist" })
347 if !$config->{$param->{iface}};
348
349 delete $config->{$param->{iface}};
350
351 PVE::INotify::write_file('interfaces', $config);
352 };
353
354 PVE::Tools::lock_file($iflockfn, 10, $code);
355 die $@ if $@;
356
357 return undef;
358 }});