]> git.proxmox.com Git - pve-manager.git/blame - PVE/API2/OpenVZ.pm
create /var/run/dtach at pvedaemon startup
[pve-manager.git] / PVE / API2 / OpenVZ.pm
CommitLineData
339e4159
DM
1package PVE::API2::OpenVZ;
2
3use strict;
4use warnings;
5use File::Basename;
aabf3add 6use File::Path;
c3163e37 7use POSIX qw (LONG_MAX);
339e4159
DM
8
9use PVE::SafeSyslog;
aabf3add 10use PVE::Tools qw(extract_param run_command);
7e79e293
DM
11use PVE::Exception qw(raise raise_param_exc);
12use PVE::INotify;
8993f2db
DM
13use PVE::Cluster qw(cfs_lock_file cfs_read_file cfs_write_file);
14use PVE::AccessControl;
339e4159
DM
15use PVE::Storage;
16use PVE::RESTHandler;
17use PVE::RPCEnvironment;
18use PVE::OpenVZ;
0618d446 19use PVE::OpenVZMigrate;
339e4159
DM
20use PVE::JSONSchema qw(get_standard_option);
21
22use base qw(PVE::RESTHandler);
23
24use Data::Dumper; # fixme: remove
25
26my $pve_base_ovz_config = <<__EOD;
27ONBOOT="no"
28
29PHYSPAGES="0:256M"
30SWAPPAGES="0:256M"
31KMEMSIZE="116M:128M"
32DCACHESIZE="58M:64M"
33LOCKEDPAGES="128M"
34PRIVVMPAGES="unlimited"
35SHMPAGES="unlimited"
36NUMPROC="unlimited"
37VMGUARPAGES="0:unlimited"
38OOMGUARPAGES="0:unlimited"
39NUMTCPSOCK="unlimited"
40NUMFLOCK="unlimited"
41NUMPTY="unlimited"
42NUMSIGINFO="unlimited"
43TCPSNDBUF="unlimited"
44TCPRCVBUF="unlimited"
45OTHERSOCKBUF="unlimited"
46DGRAMRCVBUF="unlimited"
47NUMOTHERSOCK="unlimited"
48NUMFILE="unlimited"
49NUMIPTENT="unlimited"
50
51# Disk quota parameters (in form of softlimit:hardlimit)
52DISKSPACE="unlimited:unlimited"
53DISKINODES="unlimited:unlimited"
54QUOTATIME="0"
55QUOTAUGIDLIMIT="0"
56
57# CPU fair scheduler parameter
58CPUUNITS="1000"
59CPUS="1"
60__EOD
61
bc8f054e
DM
62my $get_container_storage = sub {
63 my ($stcfg, $vmid, $veconf) = @_;
64
65 my $path = PVE::OpenVZ::get_privatedir($veconf, $vmid);
66 my ($vtype, $volid) = PVE::Storage::path_to_volume_id($stcfg, $path);
67 my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1) if $volid;
68 return wantarray ? ($sid, $volname, $path) : $sid;
69};
70
8993f2db
DM
71my $check_ct_modify_config_perm = sub {
72 my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_;
73
74 return 1 if $authuser ne 'root@pam';
75
76 foreach my $opt (@$key_list) {
77
78 if ($opt eq 'cpus' || $opt eq 'cpuunits' || $opt eq 'cpulimit') {
79 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.CPU']);
80 } elsif ($opt eq 'disk' || $opt eq 'quotatime' || $opt eq 'quotaugidlimit') {
81 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk']);
82 } elsif ($opt eq 'memory' || $opt eq 'swap') {
83 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Memory']);
84 } elsif ($opt eq 'netif' || $opt eq 'ip_address' || $opt eq 'nameserver' ||
85 $opt eq 'searchdomain' || $opt eq 'hostname') {
86 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
87 } else {
88 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Options']);
89 }
90 }
91
92 return 1;
93};
94
339e4159
DM
95__PACKAGE__->register_method({
96 name => 'vmlist',
97 path => '',
98 method => 'GET',
99 description => "OpenVZ container index (per node).",
85dc4bf7
DM
100 permissions => {
101 description => "Only list VMs where you have VM.Audit permissons on /vms/<vmid>.",
102 user => 'all',
103 },
339e4159
DM
104 proxyto => 'node',
105 protected => 1, # openvz proc files are only readable by root
106 parameters => {
107 additionalProperties => 0,
108 properties => {
109 node => get_standard_option('pve-node'),
110 },
111 },
112 returns => {
113 type => 'array',
114 items => {
115 type => "object",
116 properties => {},
117 },
118 links => [ { rel => 'child', href => "{vmid}" } ],
119 },
120 code => sub {
121 my ($param) = @_;
122
85dc4bf7
DM
123 my $rpcenv = PVE::RPCEnvironment::get();
124 my $authuser = $rpcenv->get_user();
125
339e4159
DM
126 my $vmstatus = PVE::OpenVZ::vmstatus();
127
85dc4bf7
DM
128 my $res = [];
129 foreach my $vmid (keys %$vmstatus) {
130 next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1);
339e4159 131
85dc4bf7
DM
132 my $data = $vmstatus->{$vmid};
133 $data->{vmid} = $vmid;
134 push @$res, $data;
135 }
136
137 return $res;
138
339e4159
DM
139 }});
140
aabf3add 141my $restore_openvz = sub {
9f767883 142 my ($private, $archive, $vmid, $force) = @_;
aabf3add
DM
143
144 my $vzconf = PVE::OpenVZ::read_global_vz_config ();
145 my $conffile = PVE::OpenVZ::config_file($vmid);
146 my $cfgdir = dirname($conffile);
147
aabf3add
DM
148 my $root = $vzconf->{rootdir};
149 $root =~ s/\$VEID/$vmid/;
150
151 print "you choose to force overwriting VPS config file, private and root directories.\n" if $force;
152
153 die "unable to create CT $vmid - container already exists\n"
154 if !$force && -f $conffile;
155
156 die "unable to create CT $vmid - directory '$private' already exists\n"
157 if !$force && -d $private;
158
159 die "unable to create CT $vmid - directory '$root' already exists\n"
160 if !$force && -d $root;
161
162 my $conf;
163
164 eval {
9f767883
DM
165 if ($force && -f $conffile) {
166 my $conf = PVE::OpenVZ::load_config($vmid);
167
2d590018 168 my $oldprivate = PVE::OpenVZ::get_privatedir($conf, $vmid);
9f767883
DM
169 rmtree $oldprivate if -d $oldprivate;
170
171 my $oldroot = $conf->{ve_root} ? $conf->{ve_root}->{value} : $root;
172 rmtree $oldroot if -d $oldroot;
173 };
aabf3add
DM
174
175 mkpath $private || die "unable to create private dir '$private'";
176 mkpath $root || die "unable to create private dir '$private'";
177
178 my $cmd = ['tar', 'xpf', $archive, '--totals', '--sparse', '-C', $private];
179
180 if ($archive eq '-') {
181 print "extracting archive from STDIN\n";
182 run_command($cmd, input => "<&STDIN");
183 } else {
184 print "extracting archive '$archive'\n";
185 run_command($cmd);
186 }
187
188 my $backup_cfg = "$private/etc/vzdump/vps.conf";
189 if (-f $backup_cfg) {
190 print "restore configuration to '$conffile'\n";
191
0618d446 192 my $conf = PVE::Tools::file_get_contents($backup_cfg);
aabf3add
DM
193
194 $conf =~ s/VE_ROOT=.*/VE_ROOT=\"$root\"/;
195 $conf =~ s/VE_PRIVATE=.*/VE_PRIVATE=\"$private\"/;
196 $conf =~ s/host_ifname=veth[0-9]+\./host_ifname=veth${vmid}\./g;
197
198 PVE::Tools::file_set_contents($conffile, $conf);
199
200 foreach my $s (PVE::OpenVZ::SCRIPT_EXT) {
201 my $tfn = "$cfgdir/${vmid}.$s";
202 my $sfn = "$private/etc/vzdump/vps.$s";
203 if (-f $sfn) {
204 my $sc = PVE::Tools::file_get_contents($sfn);
205 PVE::Tools::file_set_contents($tfn, $sc);
206 }
207 }
208 }
209
210 rmtree "$private/etc/vzdump";
211 };
212
213 my $err = $@;
214
215 if ($err) {
216 rmtree $private;
217 rmtree $root;
218 unlink $conffile;
219 foreach my $s (PVE::OpenVZ::SCRIPT_EXT) {
220 unlink "$cfgdir/${vmid}.$s";
221 }
222 die $err;
223 }
224
225 return $conf;
226};
227
228# create_vm is also used by vzrestore
339e4159
DM
229__PACKAGE__->register_method({
230 name => 'create_vm',
231 path => '',
232 method => 'POST',
aabf3add 233 description => "Create or restore a container.",
8993f2db
DM
234 permissions => {
235 description => "You need 'VM.Allocate' permissions on /vms/{vmid} or on the VM pool /pool/{pool}, and 'Datastore.AllocateSpace' on the storage.",
236 check => [ 'or',
237 [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
238 [ 'perm', '/pool/{pool}', ['VM.Allocate'], require_param => 'pool'],
239 ],
240 },
339e4159
DM
241 protected => 1,
242 proxyto => 'node',
243 parameters => {
244 additionalProperties => 0,
245 properties => PVE::OpenVZ::json_config_properties({
246 node => get_standard_option('pve-node'),
247 vmid => get_standard_option('pve-vmid'),
248 ostemplate => {
aabf3add 249 description => "The OS template or backup file.",
339e4159
DM
250 type => 'string',
251 maxLength => 255,
252 },
253 password => {
254 optional => 1,
255 type => 'string',
256 description => "Sets root password inside container.",
257 },
9f767883
DM
258 storage => get_standard_option('pve-storage-id', {
259 description => "Target storage.",
260 default => 'local',
261 optional => 1,
262 }),
aabf3add
DM
263 force => {
264 optional => 1,
265 type => 'boolean',
266 description => "Allow to overwrite existing container.",
267 },
268 restore => {
269 optional => 1,
270 type => 'boolean',
271 description => "Mark this as restore task.",
272 },
8993f2db
DM
273 pool => {
274 optional => 1,
275 type => 'string', format => 'pve-poolid',
276 description => "Add the VM to the specified pool.",
277 },
339e4159
DM
278 }),
279 },
7ca813e6
DM
280 returns => {
281 type => 'string',
282 },
339e4159
DM
283 code => sub {
284 my ($param) = @_;
285
7ca813e6
DM
286 my $rpcenv = PVE::RPCEnvironment::get();
287
8993f2db 288 my $authuser = $rpcenv->get_user();
7ca813e6 289
339e4159
DM
290 my $node = extract_param($param, 'node');
291
339e4159
DM
292 my $vmid = extract_param($param, 'vmid');
293
294 my $password = extract_param($param, 'password');
295
9f767883
DM
296 my $storage = extract_param($param, 'storage') || 'local';
297
8993f2db
DM
298 my $pool = extract_param($param, 'pool');
299
9f767883
DM
300 my $storage_cfg = cfs_read_file("storage.cfg");
301
302 my $scfg = PVE::Storage::storage_check_node($storage_cfg, $storage, $node);
303
304 raise_param_exc({ storage => "storage '$storage' does not support openvz root directories"})
305 if !$scfg->{content}->{rootdir};
306
307 my $private = PVE::Storage::get_private_dir($storage_cfg, $storage, $vmid);
308
8993f2db
DM
309 if (defined($pool)) {
310 $rpcenv->check_pool_exist($pool);
311 $rpcenv->check_perm_modify($authuser, "/pool/$pool");
312 }
313
314 $rpcenv->check($authuser, "/storage/$storage", ['Datastore.AllocateSpace']);
315
316 &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, $pool, [ keys %$param]);
317
9f767883 318 PVE::Storage::activate_storage($storage_cfg, $storage);
339e4159
DM
319
320 my $conf = PVE::OpenVZ::parse_ovz_config("/tmp/openvz/$vmid.conf", $pve_base_ovz_config);
321
8993f2db
DM
322 my $addVMtoPoolFn = sub {
323 my $usercfg = cfs_read_file("user.cfg");
324 if (my $data = $usercfg->{pools}->{$pool}) {
325 $data->{vms}->{$vmid} = 1;
326 $usercfg->{vms}->{$vmid} = $pool;
327 cfs_write_file("user.cfg", $usercfg);
328 }
329 };
330
92480040 331 my $ostemplate = extract_param($param, 'ostemplate');
339e4159 332
92480040 333 my $archive;
339e4159 334
92480040
DM
335 if ($ostemplate eq '-') {
336 die "pipe requires cli environment\n"
337 if $rpcenv->{type} ne 'cli';
338 die "pipe can only be used with restore tasks\n"
339 if !$param->{restore};
340 $archive = '-';
341 } else {
342 $archive = $rpcenv->check_volume_access($authuser, $storage_cfg, $vmid, $ostemplate);
343 die "can't find file '$archive'\n" if ! -f $archive;
344 }
339e4159 345
92480040
DM
346 if (!defined($param->{searchdomain}) &&
347 !defined($param->{nameserver})) {
348
349 my $resolv = PVE::INotify::read_file('resolvconf');
aabf3add 350
92480040 351 $param->{searchdomain} = $resolv->{search} if $resolv->{search};
339e4159 352
92480040
DM
353 my @ns = ();
354 push @ns, $resolv->{dns1} if $resolv->{dns1};
355 push @ns, $resolv->{dns2} if $resolv->{dns2};
356 push @ns, $resolv->{dns3} if $resolv->{dns3};
4223bcba 357
92480040
DM
358 $param->{nameserver} = join(' ', @ns) if scalar(@ns);
359 }
4223bcba 360
cd73c63b
DM
361 # try to append domain to hostmane
362 if ($param->{hostname} && $param->{hostname} !~ m/\./ &&
363 $param->{searchdomain}) {
364
365 $param->{hostname} .= ".$param->{searchdomain}";
366 }
367
92480040 368 my $basecfg_fn = PVE::OpenVZ::config_file($vmid);
4223bcba 369
92480040
DM
370 my $check_vmid_usage = sub {
371 if ($param->{force}) {
372 die "cant overwrite mounted container\n"
373 if PVE::OpenVZ::check_mounted($conf, $vmid);
374 } else {
375 die "CT $vmid already exists\n" if -f $basecfg_fn;
4223bcba 376 }
92480040
DM
377 };
378
379 my $code = sub {
380
381 &$check_vmid_usage(); # final check after locking
4223bcba 382
3736f16b 383 PVE::OpenVZ::update_ovz_config($vmid, $conf, $param);
339e4159
DM
384
385 my $rawconf = PVE::OpenVZ::generate_raw_config($pve_base_ovz_config, $conf);
386
aabf3add 387 PVE::Cluster::check_cfs_quorum();
339e4159 388
92480040
DM
389 if ($param->{restore}) {
390 &$restore_openvz($private, $archive, $vmid, $param->{force});
0618d446 391
92480040
DM
392 # is this really needed?
393 my $cmd = ['vzctl', '--skiplock', '--quiet', 'set', $vmid,
394 '--applyconfig_map', 'name', '--save'];
395 run_command($cmd);
45116ffb 396
92480040
DM
397 # reload config
398 $conf = PVE::OpenVZ::load_config($vmid);
45116ffb 399
92480040
DM
400 # and initialize quota
401 my $disk_quota = $conf->{disk_quota}->{value};
402 if (!defined($disk_quota) || ($disk_quota != 0)) {
403 $cmd = ['vzctl', '--skiplock', 'quotainit', $vmid];
45116ffb
DM
404 run_command($cmd);
405 }
8993f2db 406
92480040
DM
407 } else {
408 PVE::Tools::file_set_contents($basecfg_fn, $rawconf);
409 my $cmd = ['vzctl', '--skiplock', 'create', $vmid,
410 '--ostemplate', $archive, '--private', $private];
411 run_command($cmd);
412
413 # hack: vzctl '--userpasswd' starts the CT, but we want
414 # to avoid that for create
415 PVE::OpenVZ::set_rootpasswd($private, $password)
416 if defined($password);
417 }
9020f201 418
92480040 419 PVE::AccessControl::lock_user_config($addVMtoPoolFn, "can't add VM to pool") if $pool;
7ca813e6 420 };
9020f201 421
92480040
DM
422 my $realcmd = sub { PVE::OpenVZ::lock_container($vmid, 1, $code); };
423
424 &$check_vmid_usage(); # first check before locking
425
426 return $rpcenv->fork_worker($param->{restore} ? 'vzrestore' : 'vzcreate',
427 $vmid, $authuser, $realcmd);
9020f201
DM
428 }});
429
8993f2db
DM
430my $vm_config_perm_list = [
431 'VM.Config.Disk',
432 'VM.Config.CPU',
433 'VM.Config.Memory',
434 'VM.Config.Network',
435 'VM.Config.Options',
436 ];
437
9020f201
DM
438__PACKAGE__->register_method({
439 name => 'update_vm',
440 path => '{vmid}/config',
441 method => 'PUT',
442 protected => 1,
443 proxyto => 'node',
444 description => "Set virtual machine options.",
8993f2db
DM
445 permissions => {
446 check => ['perm', '/vms/{vmid}', $vm_config_perm_list, any => 1],
447 },
9020f201
DM
448 parameters => {
449 additionalProperties => 0,
450 properties => PVE::OpenVZ::json_config_properties(
451 {
452 node => get_standard_option('pve-node'),
453 vmid => get_standard_option('pve-vmid'),
454 digest => {
455 type => 'string',
456 description => 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.',
457 maxLength => 40,
458 optional => 1,
459 }
460 }),
461 },
462 returns => { type => 'null'},
463 code => sub {
464 my ($param) = @_;
465
466 my $rpcenv = PVE::RPCEnvironment::get();
467
8993f2db 468 my $authuser = $rpcenv->get_user();
9020f201
DM
469
470 my $node = extract_param($param, 'node');
471
472 my $vmid = extract_param($param, 'vmid');
339e4159 473
9020f201
DM
474 my $digest = extract_param($param, 'digest');
475
476 die "no options specified\n" if !scalar(keys %$param);
477
8993f2db
DM
478 &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, [keys %$param]);
479
9020f201
DM
480 my $code = sub {
481
c3163e37 482 my $conf = PVE::OpenVZ::load_config($vmid);
9020f201
DM
483 die "checksum missmatch (file change by other user?)\n"
484 if $digest && $digest ne $conf->{digest};
485
3736f16b 486 my $changes = PVE::OpenVZ::update_ovz_config($vmid, $conf, $param);
9020f201
DM
487
488 return if scalar (@$changes) <= 0;
489
490 my $cmd = ['vzctl', '--skiplock', 'set', $vmid, @$changes, '--save'];
491
8993f2db 492 PVE::Cluster::log_msg('info', $authuser, "update CT $vmid: " . join(' ', @$changes));
96e1743e 493
7e79e293 494 run_command($cmd);
339e4159
DM
495 };
496
92480040 497 PVE::OpenVZ::lock_container($vmid, undef, $code);
9020f201
DM
498
499 return undef;
339e4159
DM
500 }});
501
07151796
DM
502__PACKAGE__->register_method({
503 name => 'vmdiridx',
504 path => '{vmid}',
505 method => 'GET',
506 proxyto => 'node',
507 description => "Directory index",
85dc4bf7
DM
508 permissions => {
509 user => 'all',
510 },
07151796
DM
511 parameters => {
512 additionalProperties => 0,
513 properties => {
514 node => get_standard_option('pve-node'),
515 vmid => get_standard_option('pve-vmid'),
516 },
517 },
518 returns => {
519 type => 'array',
520 items => {
521 type => "object",
522 properties => {
523 subdir => { type => 'string' },
524 },
525 },
526 links => [ { rel => 'child', href => "{subdir}" } ],
527 },
528 code => sub {
529 my ($param) = @_;
530
531 # test if VM exists
532 my $conf = PVE::OpenVZ::load_config($param->{vmid});
533
534 my $res = [
535 { subdir => 'config' },
536 { subdir => 'status' },
537 { subdir => 'vncproxy' },
538 { subdir => 'migrate' },
2d590018 539 { subdir => 'initlog' },
07151796
DM
540 { subdir => 'rrd' },
541 { subdir => 'rrddata' },
542 ];
543
544 return $res;
545 }});
546
d9c18330
DM
547__PACKAGE__->register_method({
548 name => 'rrd',
549 path => '{vmid}/rrd',
550 method => 'GET',
551 protected => 1, # fixme: can we avoid that?
552 permissions => {
7d020b42 553 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
d9c18330
DM
554 },
555 description => "Read VM RRD statistics (returns PNG)",
556 parameters => {
557 additionalProperties => 0,
558 properties => {
559 node => get_standard_option('pve-node'),
560 vmid => get_standard_option('pve-vmid'),
561 timeframe => {
562 description => "Specify the time frame you are interested in.",
563 type => 'string',
564 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
565 },
566 ds => {
567 description => "The list of datasources you want to display.",
568 type => 'string', format => 'pve-configid-list',
569 },
570 cf => {
571 description => "The RRD consolidation function",
572 type => 'string',
573 enum => [ 'AVERAGE', 'MAX' ],
574 optional => 1,
575 },
576 },
577 },
578 returns => {
579 type => "object",
580 properties => {
581 filename => { type => 'string' },
582 },
583 },
584 code => sub {
585 my ($param) = @_;
586
587 return PVE::Cluster::create_rrd_graph(
588 "pve2-vm/$param->{vmid}", $param->{timeframe},
589 $param->{ds}, $param->{cf});
590
591 }});
592
593__PACKAGE__->register_method({
594 name => 'rrddata',
595 path => '{vmid}/rrddata',
596 method => 'GET',
597 protected => 1, # fixme: can we avoid that?
598 permissions => {
7d020b42 599 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
d9c18330
DM
600 },
601 description => "Read VM RRD statistics",
602 parameters => {
603 additionalProperties => 0,
604 properties => {
605 node => get_standard_option('pve-node'),
606 vmid => get_standard_option('pve-vmid'),
607 timeframe => {
608 description => "Specify the time frame you are interested in.",
609 type => 'string',
610 enum => [ 'hour', 'day', 'week', 'month', 'year' ],
611 },
612 cf => {
613 description => "The RRD consolidation function",
614 type => 'string',
615 enum => [ 'AVERAGE', 'MAX' ],
616 optional => 1,
617 },
618 },
619 },
620 returns => {
621 type => "array",
622 items => {
623 type => "object",
624 properties => {},
625 },
626 },
627 code => sub {
628 my ($param) = @_;
629
630 return PVE::Cluster::create_rrd_data(
631 "pve2-vm/$param->{vmid}", $param->{timeframe}, $param->{cf});
632 }});
633
2d590018
DM
634__PACKAGE__->register_method({
635 name => 'initlog',
636 path => '{vmid}/initlog',
637 method => 'GET',
638 protected => 1,
7b8e4045 639 proxyto => 'node',
2d590018 640 permissions => {
7d020b42 641 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
2d590018
DM
642 },
643 description => "Read init log.",
644 parameters => {
645 additionalProperties => 0,
646 properties => {
647 node => get_standard_option('pve-node'),
648 vmid => get_standard_option('pve-vmid'),
649 start => {
650 type => 'integer',
651 minimum => 0,
652 optional => 1,
653 },
654 limit => {
655 type => 'integer',
656 minimum => 0,
657 optional => 1,
658 },
659 },
660 },
661 returns => {
662 type => 'array',
663 items => {
664 type => "object",
665 properties => {
666 n => {
667 description=> "Line number",
668 type=> 'integer',
669 },
670 t => {
671 description=> "Line text",
672 type => 'string',
673 }
674 }
675 }
676 },
677 code => sub {
678 my ($param) = @_;
679
680 my $rpcenv = PVE::RPCEnvironment::get();
8993f2db 681 my $authuser = $rpcenv->get_user();
2d590018
DM
682
683 my $vmid = $param->{vmid};
684
685 my $conf = PVE::OpenVZ::load_config($vmid);
686
687 my $privatedir = PVE::OpenVZ::get_privatedir($conf, $vmid);
688
689 my $logfn = "$privatedir/var/log/init.log";
690
691 my ($count, $lines) = PVE::Tools::dump_logfile($logfn, $param->{start}, $param->{limit});
692
e09058af 693 $rpcenv->set_result_attrib('total', $count);
2d590018
DM
694
695 return $lines;
696 }});
697
c3163e37
DM
698__PACKAGE__->register_method({
699 name => 'vm_config',
700 path => '{vmid}/config',
701 method => 'GET',
702 proxyto => 'node',
337dca7c 703 description => "Get container configuration.",
85dc4bf7
DM
704 permissions => {
705 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
706 },
c3163e37
DM
707 parameters => {
708 additionalProperties => 0,
709 properties => {
710 node => get_standard_option('pve-node'),
711 vmid => get_standard_option('pve-vmid'),
712 },
713 },
714 returns => {
715 type => "object",
716 properties => {
717 digest => {
718 type => 'string',
719 description => 'SHA1 digest of configuration file. This can be used to prevent concurrent modifications.',
720 }
721 },
722 },
723 code => sub {
724 my ($param) = @_;
725
726 my $veconf = PVE::OpenVZ::load_config($param->{vmid});
727
728 # we only return selected/converted values
729 my $conf = { digest => $veconf->{digest} };
730
337dca7c
DM
731 if ($veconf->{ostemplate} && $veconf->{ostemplate}->{value}) {
732 $conf->{ostemplate} = $veconf->{ostemplate}->{value};
733 }
734
2c8e0b15
DM
735 my $stcfg = cfs_read_file("storage.cfg");
736
bc8f054e
DM
737 my ($sid, undef, $path) = &$get_container_storage($stcfg, $param->{vmid}, $veconf);
738 $conf->{storage} = $sid || $path;
2c8e0b15 739
c3163e37
DM
740 my $properties = PVE::OpenVZ::json_config_properties();
741
742 foreach my $k (keys %$properties) {
743 next if $k eq 'memory';
744 next if $k eq 'swap';
745 next if $k eq 'disk';
88a6668d
DM
746
747 next if !$veconf->{$k};
748 next if !defined($veconf->{$k}->{value});
749
750 if ($k eq 'description') {
751 $conf->{$k} = PVE::Tools::decode_text($veconf->{$k}->{value});
752 } else {
753 $conf->{$k} = $veconf->{$k}->{value};
754 }
c3163e37
DM
755 }
756
493a4387 757 ($conf->{memory}, $conf->{swap}) = PVE::OpenVZ::ovz_config_extract_mem_swap($veconf, 1024*1024);
d9f0ffa9 758
c3163e37
DM
759 my $diskspace = $veconf->{diskspace}->{bar} || LONG_MAX;
760 if ($diskspace == LONG_MAX) {
761 $conf->{disk} = 0;
762 } else {
d9f0ffa9 763 $conf->{disk} = $diskspace/(1024*1024);
c3163e37
DM
764 }
765 return $conf;
766 }});
767
339e4159
DM
768__PACKAGE__->register_method({
769 name => 'destroy_vm',
770 path => '{vmid}',
771 method => 'DELETE',
772 protected => 1,
773 proxyto => 'node',
774 description => "Destroy the container (also delete all uses files).",
85dc4bf7
DM
775 permissions => {
776 check => [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
777 },
339e4159
DM
778 parameters => {
779 additionalProperties => 0,
780 properties => {
781 node => get_standard_option('pve-node'),
782 vmid => get_standard_option('pve-vmid'),
783 },
784 },
a42f6acb
DM
785 returns => {
786 type => 'string',
787 },
339e4159
DM
788 code => sub {
789 my ($param) = @_;
790
791 my $rpcenv = PVE::RPCEnvironment::get();
792
8993f2db 793 my $authuser = $rpcenv->get_user();
339e4159
DM
794
795 my $vmid = $param->{vmid};
796
07151796
DM
797 # test if VM exists
798 my $conf = PVE::OpenVZ::load_config($param->{vmid});
799
a42f6acb
DM
800 my $realcmd = sub {
801 my $cmd = ['vzctl', 'destroy', $vmid ];
339e4159 802
7e79e293 803 run_command($cmd);
a42f6acb 804 };
339e4159 805
8993f2db 806 return $rpcenv->fork_worker('vzdestroy', $vmid, $authuser, $realcmd);
339e4159
DM
807 }});
808
b1b121d5
DM
809my $sslcert;
810
811__PACKAGE__->register_method ({
812 name => 'vncproxy',
813 path => '{vmid}/vncproxy',
814 method => 'POST',
815 protected => 1,
816 permissions => {
7d020b42 817 check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
b1b121d5
DM
818 },
819 description => "Creates a TCP VNC proxy connections.",
820 parameters => {
821 additionalProperties => 0,
822 properties => {
823 node => get_standard_option('pve-node'),
824 vmid => get_standard_option('pve-vmid'),
825 },
826 },
827 returns => {
828 additionalProperties => 0,
829 properties => {
830 user => { type => 'string' },
831 ticket => { type => 'string' },
832 cert => { type => 'string' },
833 port => { type => 'integer' },
834 upid => { type => 'string' },
835 },
836 },
837 code => sub {
838 my ($param) = @_;
839
840 my $rpcenv = PVE::RPCEnvironment::get();
841
8993f2db 842 my $authuser = $rpcenv->get_user();
b1b121d5
DM
843
844 my $vmid = $param->{vmid};
845 my $node = $param->{node};
846
57ebda08
DM
847 my $authpath = "/vms/$vmid";
848
8993f2db 849 my $ticket = PVE::AccessControl::assemble_vnc_ticket($authuser, $authpath);
57ebda08 850
b1b121d5
DM
851 $sslcert = PVE::Tools::file_get_contents("/etc/pve/pve-root-ca.pem", 8192)
852 if !$sslcert;
853
854 my $port = PVE::Tools::next_vnc_port();
855
856 my $remip;
857
858 if ($node ne PVE::INotify::nodename()) {
859 $remip = PVE::Cluster::remote_node_ip($node);
860 }
861
862 # NOTE: vncterm VNC traffic is already TLS encrypted,
863 # so we select the fastest chipher here (or 'none'?)
864 my $remcmd = $remip ?
865 ['/usr/bin/ssh', '-c', 'blowfish-cbc', '-t', $remip] : [];
866
f9d4fc64
DM
867 my $shcmd = [ '/usr/bin/dtach', '-A',
868 "/var/run/dtach/vzctlconsole$vmid",
869 '-r', 'winch', '-z',
870 '/usr/sbin/vzctl', 'console', $vmid ];
b1b121d5
DM
871
872 my $realcmd = sub {
873 my $upid = shift;
874
875 syslog ('info', "starting openvz vnc proxy $upid\n");
876
877 my $timeout = 10;
878
879 my $cmd = ['/usr/bin/vncterm', '-rfbport', $port,
57ebda08 880 '-timeout', $timeout, '-authpath', $authpath,
b1b121d5
DM
881 '-perm', 'VM.Console', '-c', @$remcmd, @$shcmd];
882
7e79e293 883 run_command($cmd);
b1b121d5
DM
884
885 return;
886 };
887
8993f2db 888 my $upid = $rpcenv->fork_worker('vncproxy', $vmid, $authuser, $realcmd);
b1b121d5
DM
889
890 return {
8993f2db 891 user => $authuser,
b1b121d5
DM
892 ticket => $ticket,
893 port => $port,
894 upid => $upid,
895 cert => $sslcert,
896 };
897 }});
898
07151796
DM
899__PACKAGE__->register_method({
900 name => 'vmcmdidx',
901 path => '{vmid}/status',
902 method => 'GET',
903 proxyto => 'node',
904 description => "Directory index",
85dc4bf7
DM
905 permissions => {
906 user => 'all',
907 },
07151796
DM
908 parameters => {
909 additionalProperties => 0,
910 properties => {
911 node => get_standard_option('pve-node'),
912 vmid => get_standard_option('pve-vmid'),
913 },
914 },
915 returns => {
916 type => 'array',
917 items => {
918 type => "object",
919 properties => {
920 subdir => { type => 'string' },
921 },
922 },
923 links => [ { rel => 'child', href => "{subdir}" } ],
924 },
925 code => sub {
926 my ($param) = @_;
927
928 # test if VM exists
929 my $conf = PVE::OpenVZ::load_config($param->{vmid});
930
931 my $res = [
932 { subdir => 'current' },
56203bf1 933 { subdir => 'ubc' },
07151796
DM
934 { subdir => 'start' },
935 { subdir => 'stop' },
936 ];
937
938 return $res;
939 }});
940
60a452f2
DM
941my $vm_is_ha_managed = sub {
942 my ($vmid) = @_;
943
944 my $cc = PVE::Cluster::cfs_read_file('cluster.conf');
945 if (PVE::Cluster::cluster_conf_lookup_pvevm($cc, 0, $vmid, 1)) {
946 return 1;
947 }
948 return 0;
949};
950
07151796
DM
951__PACKAGE__->register_method({
952 name => 'vm_status',
953 path => '{vmid}/status/current',
954 method => 'GET',
955 proxyto => 'node',
956 protected => 1, # openvz /proc entries are only readable by root
957 description => "Get virtual machine status.",
85dc4bf7
DM
958 permissions => {
959 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
960 },
07151796
DM
961 parameters => {
962 additionalProperties => 0,
963 properties => {
964 node => get_standard_option('pve-node'),
965 vmid => get_standard_option('pve-vmid'),
966 },
967 },
968 returns => { type => 'object' },
969 code => sub {
970 my ($param) = @_;
971
972 # test if VM exists
973 my $conf = PVE::OpenVZ::load_config($param->{vmid});
974
975 my $vmstatus = PVE::OpenVZ::vmstatus($param->{vmid});
58bd7194 976 my $status = $vmstatus->{$param->{vmid}};
07151796 977
60a452f2 978 $status->{ha} = &$vm_is_ha_managed($param->{vmid});
58bd7194
DM
979
980 return $status;
07151796
DM
981 }});
982
56203bf1
DM
983__PACKAGE__->register_method({
984 name => 'vm_user_beancounters',
985 path => '{vmid}/status/ubc',
986 method => 'GET',
987 proxyto => 'node',
988 protected => 1, # openvz /proc entries are only readable by root
989 description => "Get container user_beancounters.",
85dc4bf7
DM
990 permissions => {
991 check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
992 },
56203bf1
DM
993 parameters => {
994 additionalProperties => 0,
995 properties => {
996 node => get_standard_option('pve-node'),
997 vmid => get_standard_option('pve-vmid'),
998 },
999 },
1000 returns => {
1001 type => 'array',
1002 items => {
1003 type => "object",
1004 properties => {
1005 id => { type => 'string' },
1006 held => { type => 'number' },
1007 maxheld => { type => 'number' },
1008 bar => { type => 'number' },
1009 lim => { type => 'number' },
1010 failcnt => { type => 'number' },
1011 },
1012 },
1013 },
1014 code => sub {
1015 my ($param) = @_;
1016
1017 # test if VM exists
1018 my $conf = PVE::OpenVZ::load_config($param->{vmid});
1019
9056e748
DM
1020 my $ubchash = PVE::OpenVZ::read_user_beancounters();
1021 my $ubc = $ubchash->{$param->{vmid}} || {};
1022 delete $ubc->{failcntsum};
56203bf1
DM
1023
1024 return PVE::RESTHandler::hash_to_array($ubc, 'id');
1025 }});
1026
07151796
DM
1027__PACKAGE__->register_method({
1028 name => 'vm_start',
1029 path => '{vmid}/status/start',
1030 method => 'POST',
1031 protected => 1,
1032 proxyto => 'node',
1033 description => "Start the container.",
85dc4bf7
DM
1034 permissions => {
1035 check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1036 },
07151796
DM
1037 parameters => {
1038 additionalProperties => 0,
1039 properties => {
1040 node => get_standard_option('pve-node'),
1041 vmid => get_standard_option('pve-vmid'),
1042 },
1043 },
1044 returns => {
1045 type => 'string',
1046 },
1047 code => sub {
1048 my ($param) = @_;
1049
1050 my $rpcenv = PVE::RPCEnvironment::get();
1051
8993f2db 1052 my $authuser = $rpcenv->get_user();
07151796
DM
1053
1054 my $node = extract_param($param, 'node');
1055
1056 my $vmid = extract_param($param, 'vmid');
1057
51ed1415
DM
1058 die "CT $vmid already running\n" if PVE::OpenVZ::check_running($vmid);
1059
60a452f2
DM
1060 if (&$vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') {
1061
1062 my $hacmd = sub {
1063 my $upid = shift;
1064
1065 my $service = "pvevm:$vmid";
1066
1067 my $cmd = ['clusvcadm', '-e', $service, '-m', $node];
1068
1069 print "Executing HA start for CT $vmid\n";
1070
1071 PVE::Tools::run_command($cmd);
1072
1073 return;
1074 };
1075
1076 return $rpcenv->fork_worker('hastart', $vmid, $authuser, $hacmd);
1077
1078 } else {
1079
1080 my $realcmd = sub {
1081 my $upid = shift;
07151796 1082
60a452f2 1083 syslog('info', "starting CT $vmid: $upid\n");
07151796 1084
bc8f054e
DM
1085 my $veconf = PVE::OpenVZ::load_config($vmid);
1086 my $stcfg = cfs_read_file("storage.cfg");
1087 if (my $sid = &$get_container_storage($stcfg, $vmid, $veconf)) {
1088 PVE::Storage::activate_storage($stcfg, $sid);
1089 }
1090
60a452f2 1091 my $cmd = ['vzctl', 'start', $vmid];
07151796 1092
60a452f2 1093 run_command($cmd);
7e79e293 1094
60a452f2
DM
1095 return;
1096 };
07151796 1097
60a452f2
DM
1098 return $rpcenv->fork_worker('vzstart', $vmid, $authuser, $realcmd);
1099 }
07151796
DM
1100 }});
1101
1102__PACKAGE__->register_method({
1103 name => 'vm_stop',
1104 path => '{vmid}/status/stop',
1105 method => 'POST',
1106 protected => 1,
1107 proxyto => 'node',
1108 description => "Stop the container.",
85dc4bf7
DM
1109 permissions => {
1110 check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1111 },
07151796
DM
1112 parameters => {
1113 additionalProperties => 0,
1114 properties => {
1115 node => get_standard_option('pve-node'),
1116 vmid => get_standard_option('pve-vmid'),
51ed1415
DM
1117 },
1118 },
1119 returns => {
1120 type => 'string',
1121 },
1122 code => sub {
1123 my ($param) = @_;
1124
1125 my $rpcenv = PVE::RPCEnvironment::get();
1126
8993f2db 1127 my $authuser = $rpcenv->get_user();
51ed1415
DM
1128
1129 my $node = extract_param($param, 'node');
1130
1131 my $vmid = extract_param($param, 'vmid');
1132
1133 die "CT $vmid not running\n" if !PVE::OpenVZ::check_running($vmid);
1134
60a452f2 1135 if (&$vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') {
51ed1415 1136
60a452f2
DM
1137 my $hacmd = sub {
1138 my $upid = shift;
51ed1415 1139
60a452f2 1140 my $service = "pvevm:$vmid";
51ed1415 1141
60a452f2 1142 my $cmd = ['clusvcadm', '-d', $service];
51ed1415 1143
60a452f2
DM
1144 print "Executing HA stop for CT $vmid\n";
1145
1146 PVE::Tools::run_command($cmd);
1147
1148 return;
1149 };
1150
1151 return $rpcenv->fork_worker('hastop', $vmid, $authuser, $hacmd);
1152
1153 } else {
1154
1155 my $realcmd = sub {
1156 my $upid = shift;
1157
1158 syslog('info', "stoping CT $vmid: $upid\n");
1159
1160 my $cmd = ['vzctl', 'stop', $vmid, '--fast'];
1161 run_command($cmd);
1162
1163 return;
1164 };
1165
1166 return $rpcenv->fork_worker('vzstop', $vmid, $authuser, $realcmd);
1167 }
51ed1415
DM
1168 }});
1169
8710f280
DM
1170__PACKAGE__->register_method({
1171 name => 'vm_mount',
1172 path => '{vmid}/status/mount',
1173 method => 'POST',
1174 protected => 1,
1175 proxyto => 'node',
1176 description => "Mounts container private area.",
1177 permissions => {
1178 check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1179 },
1180 parameters => {
1181 additionalProperties => 0,
1182 properties => {
1183 node => get_standard_option('pve-node'),
1184 vmid => get_standard_option('pve-vmid'),
1185 },
1186 },
1187 returns => {
1188 type => 'string',
1189 },
1190 code => sub {
1191 my ($param) = @_;
1192
1193 my $rpcenv = PVE::RPCEnvironment::get();
1194
1195 my $authuser = $rpcenv->get_user();
1196
1197 my $node = extract_param($param, 'node');
1198
1199 my $vmid = extract_param($param, 'vmid');
1200
1201 die "CT $vmid is running\n" if PVE::OpenVZ::check_running($vmid);
1202
1203 my $realcmd = sub {
1204 my $upid = shift;
1205
1206 syslog('info', "mount CT $vmid: $upid\n");
1207
1208 my $cmd = ['vzctl', 'mount', $vmid];
1209
1210 run_command($cmd);
1211
1212 return;
1213 };
1214
1215 return $rpcenv->fork_worker('vzmount', $vmid, $authuser, $realcmd);
1216 }});
1217
1218__PACKAGE__->register_method({
1219 name => 'vm_umount',
1220 path => '{vmid}/status/umount',
1221 method => 'POST',
1222 protected => 1,
1223 proxyto => 'node',
1224 description => "Unmounts container private area.",
1225 permissions => {
1226 check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1227 },
1228 parameters => {
1229 additionalProperties => 0,
1230 properties => {
1231 node => get_standard_option('pve-node'),
1232 vmid => get_standard_option('pve-vmid'),
1233 },
1234 },
1235 returns => {
1236 type => 'string',
1237 },
1238 code => sub {
1239 my ($param) = @_;
1240
1241 my $rpcenv = PVE::RPCEnvironment::get();
1242
1243 my $authuser = $rpcenv->get_user();
1244
1245 my $node = extract_param($param, 'node');
1246
1247 my $vmid = extract_param($param, 'vmid');
1248
1249 die "CT $vmid is running\n" if PVE::OpenVZ::check_running($vmid);
1250
1251 my $realcmd = sub {
1252 my $upid = shift;
1253
1254 syslog('info', "umount CT $vmid: $upid\n");
1255
1256 my $cmd = ['vzctl', 'umount', $vmid];
1257
1258 run_command($cmd);
1259
1260 return;
1261 };
1262
1263 return $rpcenv->fork_worker('vzumount', $vmid, $authuser, $realcmd);
1264 }});
1265
51ed1415
DM
1266__PACKAGE__->register_method({
1267 name => 'vm_shutdown',
1268 path => '{vmid}/status/shutdown',
1269 method => 'POST',
1270 protected => 1,
1271 proxyto => 'node',
1272 description => "Shutdown the container.",
85dc4bf7
DM
1273 permissions => {
1274 check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1275 },
51ed1415
DM
1276 parameters => {
1277 additionalProperties => 0,
1278 properties => {
1279 node => get_standard_option('pve-node'),
1280 vmid => get_standard_option('pve-vmid'),
1281 timeout => {
1282 description => "Wait maximal timeout seconds.",
1283 type => 'integer',
1284 minimum => 0,
1285 optional => 1,
1286 default => 60,
1287 },
1288 forceStop => {
1289 description => "Make sure the Container stops.",
07151796 1290 type => 'boolean',
07151796 1291 optional => 1,
51ed1415 1292 default => 0,
07151796
DM
1293 }
1294 },
1295 },
1296 returns => {
1297 type => 'string',
1298 },
1299 code => sub {
1300 my ($param) = @_;
1301
1302 my $rpcenv = PVE::RPCEnvironment::get();
1303
8993f2db 1304 my $authuser = $rpcenv->get_user();
07151796
DM
1305
1306 my $node = extract_param($param, 'node');
1307
1308 my $vmid = extract_param($param, 'vmid');
1309
51ed1415
DM
1310 my $timeout = extract_param($param, 'timeout');
1311
1312 die "CT $vmid not running\n" if !PVE::OpenVZ::check_running($vmid);
1313
07151796
DM
1314 my $realcmd = sub {
1315 my $upid = shift;
1316
51ed1415 1317 syslog('info', "shutdown CT $vmid: $upid\n");
07151796
DM
1318
1319 my $cmd = ['vzctl', 'stop', $vmid];
1320
51ed1415
DM
1321 $timeout = 60 if !defined($timeout);
1322
1323 eval { run_command($cmd, timeout => $timeout); };
1324 my $err = $@;
1325 return if !$err;
1326
1327 die $err if !$param->{forceStop};
1328
1329 warn "shutdown failed - forcing stop now\n";
1330
1331 push @$cmd, '--fast';
7e79e293 1332 run_command($cmd);
07151796
DM
1333
1334 return;
1335 };
1336
8993f2db 1337 my $upid = $rpcenv->fork_worker('vzshutdown', $vmid, $authuser, $realcmd);
07151796
DM
1338
1339 return $upid;
1340 }});
1341
7e79e293
DM
1342__PACKAGE__->register_method({
1343 name => 'migrate_vm',
1344 path => '{vmid}/migrate',
1345 method => 'POST',
1346 protected => 1,
1347 proxyto => 'node',
1348 description => "Migrate the container to another node. Creates a new migration task.",
85dc4bf7
DM
1349 permissions => {
1350 check => ['perm', '/vms/{vmid}', [ 'VM.Migrate' ]],
1351 },
7e79e293
DM
1352 parameters => {
1353 additionalProperties => 0,
1354 properties => {
1355 node => get_standard_option('pve-node'),
1356 vmid => get_standard_option('pve-vmid'),
1357 target => get_standard_option('pve-node', { description => "Target node." }),
1358 online => {
1359 type => 'boolean',
1360 description => "Use online/live migration.",
1361 optional => 1,
1362 },
1363 },
1364 },
1365 returns => {
1366 type => 'string',
1367 description => "the task ID.",
1368 },
1369 code => sub {
1370 my ($param) = @_;
1371
1372 my $rpcenv = PVE::RPCEnvironment::get();
1373
8993f2db 1374 my $authuser = $rpcenv->get_user();
7e79e293
DM
1375
1376 my $target = extract_param($param, 'target');
1377
1378 my $localnode = PVE::INotify::nodename();
1379 raise_param_exc({ target => "target is local node."}) if $target eq $localnode;
1380
1381 PVE::Cluster::check_cfs_quorum();
1382
1383 PVE::Cluster::check_node_exists($target);
1384
1385 my $targetip = PVE::Cluster::remote_node_ip($target);
1386
1387 my $vmid = extract_param($param, 'vmid');
1388
1389 # test if VM exists
1390 PVE::OpenVZ::load_config($vmid);
1391
1392 # try to detect errors early
1393 if (PVE::OpenVZ::check_running($vmid)) {
1394 die "cant migrate running container without --online\n"
1395 if !$param->{online};
1396 }
1397
60a452f2 1398 if (&$vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') {
45116ffb 1399
60a452f2
DM
1400 my $hacmd = sub {
1401 my $upid = shift;
45116ffb 1402
60a452f2 1403 my $service = "pvevm:$vmid";
7e79e293 1404
60a452f2 1405 my $cmd = ['clusvcadm', '-M', $service, '-m', $target];
7e79e293 1406
60a452f2
DM
1407 print "Executing HA migrate for CT $vmid to node $target\n";
1408
1409 PVE::Tools::run_command($cmd);
1410
1411 return;
1412 };
1413
1414 return $rpcenv->fork_worker('hamigrate', $vmid, $authuser, $hacmd);
1415
1416 } else {
1417
1418 my $realcmd = sub {
1419 my $upid = shift;
1420
1421 PVE::OpenVZMigrate->migrate($target, $targetip, $vmid, $param);
1422
1423 return;
1424 };
1425
1426 return $rpcenv->fork_worker('vzmigrate', $vmid, $authuser, $realcmd);
1427 }
7e79e293
DM
1428 }});
1429
339e4159 14301;