]> git.proxmox.com Git - pve-container.git/blob - src/PVE/API2/LXC/Status.pm
api: status: move config locking from API handler into worker
[pve-container.git] / src / PVE / API2 / LXC / Status.pm
1 package PVE::API2::LXC::Status;
2
3 use strict;
4 use warnings;
5
6 use PVE::SafeSyslog;
7 use PVE::Tools qw(extract_param run_command);
8 use PVE::Exception qw(raise raise_param_exc);
9 use PVE::INotify;
10 use PVE::Cluster qw(cfs_read_file);
11 use PVE::AccessControl;
12 use PVE::Firewall;
13 use PVE::Storage;
14 use PVE::RESTHandler;
15 use PVE::RPCEnvironment;
16 use PVE::LXC;
17 use PVE::LXC::Create;
18 use PVE::JSONSchema qw(get_standard_option);
19
20 use base qw(PVE::RESTHandler);
21
22 BEGIN {
23 if (!$ENV{PVE_GENERATING_DOCS}) {
24 require PVE::HA::Env::PVE2;
25 import PVE::HA::Env::PVE2;
26 require PVE::HA::Config;
27 import PVE::HA::Config;
28 }
29 }
30
31 __PACKAGE__->register_method({
32 name => 'vmcmdidx',
33 path => '',
34 method => 'GET',
35 proxyto => 'node',
36 description => "Directory index",
37 permissions => {
38 user => 'all',
39 },
40 parameters => {
41 additionalProperties => 0,
42 properties => {
43 node => get_standard_option('pve-node'),
44 vmid => get_standard_option('pve-vmid'),
45 },
46 },
47 returns => {
48 type => 'array',
49 items => {
50 type => "object",
51 properties => {
52 subdir => { type => 'string' },
53 },
54 },
55 links => [ { rel => 'child', href => "{subdir}" } ],
56 },
57 code => sub {
58 my ($param) = @_;
59
60 # test if VM exists
61 my $conf = PVE::LXC::Config->load_config($param->{vmid});
62
63 my $res = [
64 { subdir => 'current' },
65 { subdir => 'start' },
66 { subdir => 'stop' },
67 { subdir => 'shutdown' },
68 { subdir => 'reboot' },
69 { subdir => 'migrate' },
70 ];
71
72 return $res;
73 }});
74
75 __PACKAGE__->register_method({
76 name => 'vm_status',
77 path => 'current',
78 method => 'GET',
79 proxyto => 'node',
80 protected => 1, # /proc entries are only readable by root
81 description => "Get virtual machine status.",
82 permissions => {
83 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
84 },
85 parameters => {
86 additionalProperties => 0,
87 properties => {
88 node => get_standard_option('pve-node'),
89 vmid => get_standard_option('pve-vmid'),
90 },
91 },
92 returns => {
93 type => 'object',
94 properties => {
95 %$PVE::LXC::vmstatus_return_properties,
96 ha => {
97 description => "HA manager service status.",
98 type => 'object',
99 },
100 },
101 },
102 code => sub {
103 my ($param) = @_;
104
105 # test if VM exists
106 my $vmid = $param->{vmid};
107 my $conf = PVE::LXC::Config->load_config($vmid);
108
109 my $vmstatus = PVE::LXC::vmstatus($vmid);
110 my $status = $vmstatus->{$vmid};
111
112 $status->{ha} = PVE::HA::Config::get_service_status("ct:$vmid");
113
114 return $status;
115 }});
116
117 __PACKAGE__->register_method({
118 name => 'vm_start',
119 path => 'start',
120 method => 'POST',
121 protected => 1,
122 proxyto => 'node',
123 description => "Start the container.",
124 permissions => {
125 check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
126 },
127 parameters => {
128 additionalProperties => 0,
129 properties => {
130 node => get_standard_option('pve-node'),
131 vmid => get_standard_option('pve-vmid', { completion => \&PVE::LXC::complete_ctid_stopped }),
132 skiplock => get_standard_option('skiplock'),
133 debug => {
134 optional => 1,
135 type => 'boolean',
136 description => "If set, enables very verbose debug log-level on start.",
137 default => 0,
138 },
139 },
140 },
141 returns => {
142 type => 'string',
143 },
144 code => sub {
145 my ($param) = @_;
146
147 my $rpcenv = PVE::RPCEnvironment::get();
148 my $authuser = $rpcenv->get_user();
149
150 my $node = extract_param($param, 'node');
151 my $vmid = extract_param($param, 'vmid');
152
153 my $skiplock = extract_param($param, 'skiplock');
154 raise_param_exc({ skiplock => "Only root may use this option." })
155 if $skiplock && $authuser ne 'root@pam';
156
157 die "CT $vmid already running\n" if PVE::LXC::check_running($vmid);
158
159 PVE::Cluster::check_cfs_quorum();
160
161 if (PVE::HA::Config::vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') {
162
163 my $hacmd = sub {
164 my $upid = shift;
165
166 print "Requesting HA start for CT $vmid\n";
167
168 my $cmd = ['ha-manager', 'set', "ct:$vmid", '--state', 'started'];
169 PVE::Tools::run_command($cmd);
170 };
171
172 return $rpcenv->fork_worker('hastart', $vmid, $authuser, $hacmd);
173
174 } else {
175
176 my $realcmd = sub {
177 my $upid = shift;
178
179 PVE::LXC::Config->lock_config($vmid, sub {
180 syslog('info', "starting CT $vmid: $upid\n");
181
182 my $conf = PVE::LXC::Config->load_config($vmid);
183
184 die "you can't start a CT if it's a template\n"
185 if PVE::LXC::Config->is_template($conf);
186
187 if (!$skiplock && !PVE::LXC::Config->has_lock($conf, 'mounted')) {
188 PVE::LXC::Config->check_lock($conf);
189 }
190
191 if ($conf->{unprivileged}) {
192 PVE::LXC::Config->foreach_volume($conf, sub {
193 my ($ms, $mountpoint) = @_;
194 die "Quotas are not supported by unprivileged containers.\n"
195 if $mountpoint->{quota};
196 });
197 }
198
199 PVE::LXC::vm_start($vmid, $conf, $skiplock, $param->{debug});
200 });
201 };
202
203 return $rpcenv->fork_worker('vzstart', $vmid, $authuser, $realcmd);
204 }
205 }});
206
207 __PACKAGE__->register_method({
208 name => 'vm_stop',
209 path => 'stop',
210 method => 'POST',
211 protected => 1,
212 proxyto => 'node',
213 description => "Stop the container. This will abruptly stop all processes running in the container.",
214 permissions => {
215 check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
216 },
217 parameters => {
218 additionalProperties => 0,
219 properties => {
220 node => get_standard_option('pve-node'),
221 vmid => get_standard_option('pve-vmid', { completion => \&PVE::LXC::complete_ctid_running }),
222 skiplock => get_standard_option('skiplock'),
223 },
224 },
225 returns => {
226 type => 'string',
227 },
228 code => sub {
229 my ($param) = @_;
230
231 my $rpcenv = PVE::RPCEnvironment::get();
232 my $authuser = $rpcenv->get_user();
233 my $node = extract_param($param, 'node');
234 my $vmid = extract_param($param, 'vmid');
235
236 my $skiplock = extract_param($param, 'skiplock');
237 raise_param_exc({ skiplock => "Only root may use this option." })
238 if $skiplock && $authuser ne 'root@pam';
239
240 die "CT $vmid not running\n" if !PVE::LXC::check_running($vmid);
241
242 if (PVE::HA::Config::vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') {
243
244 my $hacmd = sub {
245 my $upid = shift;
246
247 print "Requesting HA stop for CT $vmid\n";
248
249 my $cmd = ['ha-manager', 'crm-command', 'stop', "ct:$vmid", '0'];
250 PVE::Tools::run_command($cmd);
251 };
252
253 return $rpcenv->fork_worker('hastop', $vmid, $authuser, $hacmd);
254
255 } else {
256
257 my $realcmd = sub {
258 my $upid = shift;
259
260 PVE::LXC::Config->lock_config($vmid, sub {
261 syslog('info', "stopping CT $vmid: $upid\n");
262
263 my $conf = PVE::LXC::Config->load_config($vmid);
264 if (!$skiplock && !PVE::LXC::Config->has_lock($conf, 'mounted')) {
265 PVE::LXC::Config->check_lock($conf);
266 }
267
268 PVE::LXC::vm_stop($vmid, 1);
269 });
270 };
271
272 return $rpcenv->fork_worker('vzstop', $vmid, $authuser, $realcmd);
273 }
274 }});
275
276 __PACKAGE__->register_method({
277 name => 'vm_shutdown',
278 path => 'shutdown',
279 method => 'POST',
280 protected => 1,
281 proxyto => 'node',
282 description => "Shutdown the container. This will trigger a clean shutdown " .
283 "of the container, see lxc-stop(1) for details.",
284 permissions => {
285 check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
286 },
287 parameters => {
288 additionalProperties => 0,
289 properties => {
290 node => get_standard_option('pve-node'),
291 vmid => get_standard_option('pve-vmid', { completion => \&PVE::LXC::complete_ctid_running }),
292 timeout => {
293 description => "Wait maximal timeout seconds.",
294 type => 'integer',
295 minimum => 0,
296 optional => 1,
297 default => 60,
298 },
299 forceStop => {
300 description => "Make sure the Container stops.",
301 type => 'boolean',
302 optional => 1,
303 default => 0,
304 }
305 },
306 },
307 returns => {
308 type => 'string',
309 },
310 code => sub {
311 my ($param) = @_;
312
313 my $rpcenv = PVE::RPCEnvironment::get();
314 my $authuser = $rpcenv->get_user();
315
316 my $node = extract_param($param, 'node');
317 my $vmid = extract_param($param, 'vmid');
318
319 my $timeout = extract_param($param, 'timeout') // 60;
320
321 die "CT $vmid not running\n" if !PVE::LXC::check_running($vmid);
322
323 if (PVE::HA::Config::vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') {
324 my $hacmd = sub {
325 my $upid = shift;
326
327 print "Requesting HA stop for CT $vmid\n";
328
329 my $cmd = ['ha-manager', 'crm-command', 'stop', "ct:$vmid", "$timeout"];
330 PVE::Tools::run_command($cmd);
331 };
332
333 return $rpcenv->fork_worker('hastop', $vmid, $authuser, $hacmd);
334 }
335
336 my $realcmd = sub {
337 my $upid = shift;
338
339 PVE::LXC::Config->lock_config($vmid, sub {
340 syslog('info', "shutdown CT $vmid: $upid\n");
341
342 my $conf = PVE::LXC::Config->load_config($vmid);
343 PVE::LXC::Config->check_lock($conf);
344
345 PVE::LXC::vm_stop($vmid, 0, $timeout, $param->{forceStop});
346 });
347 };
348
349 return $rpcenv->fork_worker('vzshutdown', $vmid, $authuser, $realcmd);
350 }});
351
352 __PACKAGE__->register_method({
353 name => 'vm_suspend',
354 path => 'suspend',
355 method => 'POST',
356 protected => 1,
357 proxyto => 'node',
358 description => "Suspend the container. This is experimental.",
359 permissions => {
360 check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
361 },
362 parameters => {
363 additionalProperties => 0,
364 properties => {
365 node => get_standard_option('pve-node'),
366 vmid => get_standard_option('pve-vmid', { completion => \&PVE::LXC::complete_ctid_running }),
367 },
368 },
369 returns => {
370 type => 'string',
371 },
372 code => sub {
373 my ($param) = @_;
374
375 my $rpcenv = PVE::RPCEnvironment::get();
376 my $authuser = $rpcenv->get_user();
377
378 my $node = extract_param($param, 'node');
379 my $vmid = extract_param($param, 'vmid');
380
381 die "CT $vmid not running\n" if !PVE::LXC::check_running($vmid);
382
383 my $realcmd = sub {
384 my $upid = shift;
385
386 PVE::LXC::Config->lock_config($vmid, sub {
387 syslog('info', "suspend CT $vmid: $upid\n");
388
389 my $conf = PVE::LXC::Config->load_config($vmid);
390 PVE::LXC::Config->check_lock($conf);
391
392 my $cmd = ['lxc-checkpoint', '-n', $vmid, '-s', '-D', '/var/lib/vz/dump'];
393 run_command($cmd);
394 });
395 };
396
397 return $rpcenv->fork_worker('vzsuspend', $vmid, $authuser, $realcmd);
398 }});
399
400 __PACKAGE__->register_method({
401 name => 'vm_resume',
402 path => 'resume',
403 method => 'POST',
404 protected => 1,
405 proxyto => 'node',
406 description => "Resume the container.",
407 permissions => {
408 check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
409 },
410 parameters => {
411 additionalProperties => 0,
412 properties => {
413 node => get_standard_option('pve-node'),
414 vmid => get_standard_option('pve-vmid', { completion => \&PVE::LXC::complete_ctid_stopped }),
415 },
416 },
417 returns => {
418 type => 'string',
419 },
420 code => sub {
421 my ($param) = @_;
422
423 my $rpcenv = PVE::RPCEnvironment::get();
424 my $authuser = $rpcenv->get_user();
425
426 my $node = extract_param($param, 'node');
427 my $vmid = extract_param($param, 'vmid');
428
429 die "CT $vmid already running\n" if PVE::LXC::check_running($vmid);
430
431 my $realcmd = sub {
432 my $upid = shift;
433
434 syslog('info', "resume CT $vmid: $upid\n");
435
436 my $cmd = ['lxc-checkpoint', '-n', $vmid, '-r', '--foreground', '-D', '/var/lib/vz/dump'];
437 run_command($cmd);
438 };
439
440 my $upid = $rpcenv->fork_worker('vzresume', $vmid, $authuser, $realcmd);
441
442 return $upid;
443 }});
444
445 __PACKAGE__->register_method({
446 name => 'vm_reboot',
447 path => 'reboot',
448 method => 'POST',
449 protected => 1,
450 proxyto => 'node',
451 description => "Reboot the container by shutting it down, and starting it again. Applies pending changes.",
452 permissions => {
453 check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
454 },
455 parameters => {
456 additionalProperties => 0,
457 properties => {
458 node => get_standard_option('pve-node'),
459 vmid => get_standard_option('pve-vmid',
460 { completion => \&PVE::LXC::complete_ctid_running }),
461 timeout => {
462 description => "Wait maximal timeout seconds for the shutdown.",
463 type => 'integer',
464 minimum => 0,
465 optional => 1,
466 },
467 },
468 },
469 returns => {
470 type => 'string',
471 },
472 code => sub {
473 my ($param) = @_;
474
475 my $rpcenv = PVE::RPCEnvironment::get();
476 my $authuser = $rpcenv->get_user();
477
478 my $node = extract_param($param, 'node');
479 my $vmid = extract_param($param, 'vmid');
480
481 die "VM $vmid not running\n" if !PVE::LXC::check_running($vmid);
482
483 my $realcmd = sub {
484 my $upid = shift;
485
486 syslog('info', "requesting reboot of CT $vmid: $upid\n");
487 PVE::LXC::vm_reboot($vmid, $param->{timeout});
488 return;
489 };
490
491 return $rpcenv->fork_worker('vzreboot', $vmid, $authuser, $realcmd);
492 }});
493
494
495
496 1;