]>
git.proxmox.com Git - pve-container.git/blob - src/PVE/API2/LXC.pm
b1aaf2213d9d276202ee4c99166bed492968f8b0
1 package PVE
::API2
::LXC
;
7 use PVE
::Tools
qw(extract_param run_command);
8 use PVE
::Exception
qw(raise raise_param_exc);
10 use PVE
::Cluster
qw(cfs_read_file);
11 use PVE
::AccessControl
;
14 use PVE
::RPCEnvironment
;
18 use PVE
::JSONSchema
qw(get_standard_option);
19 use base
qw(PVE::RESTHandler);
21 use Data
::Dumper
; # fixme: remove
23 my $get_container_storage = sub {
24 my ($stcfg, $vmid, $lxc_conf) = @_;
26 my $path = $lxc_conf->{'lxc.rootfs'};
27 my ($vtype, $volid) = PVE
::Storage
::path_to_volume_id
($stcfg, $path);
28 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1) if $volid;
29 return wantarray ?
($sid, $volname, $path) : $sid;
32 my $check_ct_modify_config_perm = sub {
33 my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_;
35 return 1 if $authuser ne 'root@pam';
37 foreach my $opt (@$key_list) {
39 if ($opt eq 'cpus' || $opt eq 'cpuunits' || $opt eq 'cpulimit') {
40 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.CPU']);
41 } elsif ($opt eq 'disk') {
42 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk']);
43 } elsif ($opt eq 'memory' || $opt eq 'swap') {
44 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Memory']);
45 } elsif ($opt =~ m/^net\d+$/ || $opt eq 'nameserver' ||
46 $opt eq 'searchdomain' || $opt eq 'hostname') {
47 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
49 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Options']);
57 __PACKAGE__-
>register_method({
61 description
=> "LXC container index (per node).",
63 description
=> "Only list CTs where you have VM.Audit permissons on /vms/<vmid>.",
67 protected
=> 1, # /proc files are only readable by root
69 additionalProperties
=> 0,
71 node
=> get_standard_option
('pve-node'),
80 links
=> [ { rel
=> 'child', href
=> "{vmid}" } ],
85 my $rpcenv = PVE
::RPCEnvironment
::get
();
86 my $authuser = $rpcenv->get_user();
88 my $vmstatus = PVE
::LXC
::vmstatus
();
91 foreach my $vmid (keys %$vmstatus) {
92 next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1);
94 my $data = $vmstatus->{$vmid};
95 $data->{vmid
} = $vmid;
103 __PACKAGE__-
>register_method({
107 description
=> "Create or restore a container.",
109 user
=> 'all', # check inside
110 description
=> "You need 'VM.Allocate' permissions on /vms/{vmid} or on the VM pool /pool/{pool}. " .
111 "For restore, it is enough if the user has 'VM.Backup' permission and the VM already exists. " .
112 "You also need 'Datastore.AllocateSpace' permissions on the storage.",
117 additionalProperties
=> 0,
118 properties
=> PVE
::LXC
::json_config_properties
({
119 node
=> get_standard_option
('pve-node'),
120 vmid
=> get_standard_option
('pve-vmid'),
122 description
=> "The OS template or backup file.",
129 description
=> "Sets root password inside container.",
132 storage
=> get_standard_option
('pve-storage-id', {
133 description
=> "Target storage.",
140 description
=> "Allow to overwrite existing container.",
145 description
=> "Mark this as restore task.",
149 type
=> 'string', format
=> 'pve-poolid',
150 description
=> "Add the VM to the specified pool.",
160 my $rpcenv = PVE
::RPCEnvironment
::get
();
162 my $authuser = $rpcenv->get_user();
164 my $node = extract_param
($param, 'node');
166 my $vmid = extract_param
($param, 'vmid');
168 my $basecfg_fn = PVE
::LXC
::config_file
($vmid);
170 my $same_container_exists = -f
$basecfg_fn;
172 my $restore = extract_param
($param, 'restore');
174 my $force = extract_param
($param, 'force');
176 if (!($same_container_exists && $restore && $force)) {
177 PVE
::Cluster
::check_vmid_unused
($vmid);
180 my $password = extract_param
($param, 'password');
182 my $storage = extract_param
($param, 'storage') || 'local';
184 my $pool = extract_param
($param, 'pool');
186 my $storage_cfg = cfs_read_file
("storage.cfg");
188 my $scfg = PVE
::Storage
::storage_check_node
($storage_cfg, $storage, $node);
190 raise_param_exc
({ storage
=> "storage '$storage' does not support container root directories"})
191 if !$scfg->{content
}->{rootdir
};
193 my $private = PVE
::Storage
::get_private_dir
($storage_cfg, $storage, $vmid);
195 if (defined($pool)) {
196 $rpcenv->check_pool_exist($pool);
197 $rpcenv->check_perm_modify($authuser, "/pool/$pool");
200 if ($rpcenv->check($authuser, "/vms/$vmid", ['VM.Allocate'], 1)) {
202 } elsif ($pool && $rpcenv->check($authuser, "/pool/$pool", ['VM.Allocate'], 1)) {
204 } elsif ($restore && $force && $same_container_exists &&
205 $rpcenv->check($authuser, "/vms/$vmid", ['VM.Backup'], 1)) {
206 # OK: user has VM.Backup permissions, and want to restore an existing VM
211 &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, $pool, [ keys %$param]);
213 PVE
::Storage
::activate_storage
($storage_cfg, $storage);
215 my $ostemplate = extract_param
($param, 'ostemplate');
219 if ($ostemplate eq '-') {
220 die "archive pipe not implemented\n"
223 $rpcenv->check_volume_access($authuser, $storage_cfg, $vmid, $ostemplate);
224 $archive = PVE
::Storage
::abs_filesystem_path
($storage_cfg, $ostemplate);
229 $param->{hostname
} ||= "CT$vmid";
230 $param->{memory
} ||= 512;
231 $param->{swap
} = 512 if !defined($param->{swap
});
233 PVE
::LXC
::update_lxc_config
($vmid, $conf, 0, $param);
235 # assigng default names, so that we can configure network with LXCSetup
236 foreach my $k (keys %$conf) {
237 next if $k !~ m/^net(\d+)$/;
240 $d->{name
} = "eth$ind"; # fixme: do not overwrite settings!
243 $conf->{'lxc.hook.mount'} = "/usr/share/lxc/hooks/lxc-pve-mount-hook";
245 # use user namespace ?
246 # disable for now, because kernel 3.10.0 does not support it
247 #$conf->{'lxc.id_map'} = ["u 0 100000 65536", "g 0 100000 65536"];
250 my $size = 4*1024*1024; # defaults to 4G
251 $size = int($param->{disk
}*1024) * 1024 if defined($param->{disk
});
253 PVE
::LXCCreate
::create_rootfs
($storage_cfg, $storage, $size, $vmid, $conf, $archive, $password);
256 my $realcmd = sub { PVE
::LXC
::lock_container
($vmid, 1, $code); };
258 return $rpcenv->fork_worker($param->{restore
} ?
'vzrestore' : 'vzcreate',
259 $vmid, $authuser, $realcmd);
263 my $vm_config_perm_list = [
271 __PACKAGE__-
>register_method({
273 path
=> '{vmid}/config',
277 description
=> "Set container options.",
279 check
=> ['perm', '/vms/{vmid}', $vm_config_perm_list, any
=> 1],
282 additionalProperties
=> 0,
283 properties
=> PVE
::LXC
::json_config_properties
(
285 node
=> get_standard_option
('pve-node'),
286 vmid
=> get_standard_option
('pve-vmid'),
288 type
=> 'string', format
=> 'pve-configid-list',
289 description
=> "A list of settings you want to delete.",
294 description
=> 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.',
300 returns
=> { type
=> 'null'},
304 my $rpcenv = PVE
::RPCEnvironment
::get
();
306 my $authuser = $rpcenv->get_user();
308 my $node = extract_param
($param, 'node');
310 my $vmid = extract_param
($param, 'vmid');
312 my $digest = extract_param
($param, 'digest');
314 die "no options specified\n" if !scalar(keys %$param);
316 my $delete_str = extract_param
($param, 'delete');
317 my @delete = PVE
::Tools
::split_list
($delete_str);
319 &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, [@delete]);
321 foreach my $opt (@delete) {
322 raise_param_exc
({ delete => "you can't use '-$opt' and " .
323 "-delete $opt' at the same time" })
324 if defined($param->{$opt});
326 if (!PVE
::LXC
::option_exists
($opt)) {
327 raise_param_exc
({ delete => "unknown option '$opt'" });
331 &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, [keys %$param]);
335 my $conf = PVE
::LXC
::load_config
($vmid);
337 PVE
::Tools
::assert_if_modified
($digest, $conf->{digest
});
339 my $running = PVE
::LXC
::check_running
($vmid);
341 PVE
::LXC
::update_lxc_config
($vmid, $conf, $running, $param, \
@delete);
343 PVE
::LXC
::write_config
($vmid, $conf);
346 PVE
::LXC
::lock_container
($vmid, undef, $code);
351 __PACKAGE__-
>register_method ({
352 subclass
=> "PVE::API2::Firewall::CT",
353 path
=> '{vmid}/firewall',
356 __PACKAGE__-
>register_method({
361 description
=> "Directory index",
366 additionalProperties
=> 0,
368 node
=> get_standard_option
('pve-node'),
369 vmid
=> get_standard_option
('pve-vmid'),
377 subdir
=> { type
=> 'string' },
380 links
=> [ { rel
=> 'child', href
=> "{subdir}" } ],
386 my $conf = PVE
::LXC
::load_config
($param->{vmid
});
389 { subdir
=> 'config' },
390 { subdir
=> 'status' },
391 { subdir
=> 'vncproxy' },
392 { subdir
=> 'vncwebsocket' },
393 { subdir
=> 'spiceproxy' },
394 { subdir
=> 'migrate' },
395 # { subdir => 'initlog' },
397 { subdir
=> 'rrddata' },
398 { subdir
=> 'firewall' },
404 __PACKAGE__-
>register_method({
406 path
=> '{vmid}/rrd',
408 protected
=> 1, # fixme: can we avoid that?
410 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
412 description
=> "Read VM RRD statistics (returns PNG)",
414 additionalProperties
=> 0,
416 node
=> get_standard_option
('pve-node'),
417 vmid
=> get_standard_option
('pve-vmid'),
419 description
=> "Specify the time frame you are interested in.",
421 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
424 description
=> "The list of datasources you want to display.",
425 type
=> 'string', format
=> 'pve-configid-list',
428 description
=> "The RRD consolidation function",
430 enum
=> [ 'AVERAGE', 'MAX' ],
438 filename
=> { type
=> 'string' },
444 return PVE
::Cluster
::create_rrd_graph
(
445 "pve2-vm/$param->{vmid}", $param->{timeframe
},
446 $param->{ds
}, $param->{cf
});
450 __PACKAGE__-
>register_method({
452 path
=> '{vmid}/rrddata',
454 protected
=> 1, # fixme: can we avoid that?
456 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
458 description
=> "Read VM RRD statistics",
460 additionalProperties
=> 0,
462 node
=> get_standard_option
('pve-node'),
463 vmid
=> get_standard_option
('pve-vmid'),
465 description
=> "Specify the time frame you are interested in.",
467 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
470 description
=> "The RRD consolidation function",
472 enum
=> [ 'AVERAGE', 'MAX' ],
487 return PVE
::Cluster
::create_rrd_data
(
488 "pve2-vm/$param->{vmid}", $param->{timeframe
}, $param->{cf
});
492 __PACKAGE__-
>register_method({
494 path
=> '{vmid}/config',
497 description
=> "Get container configuration.",
499 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
502 additionalProperties
=> 0,
504 node
=> get_standard_option
('pve-node'),
505 vmid
=> get_standard_option
('pve-vmid'),
513 description
=> 'SHA1 digest of configuration file. This can be used to prevent concurrent modifications.',
520 my $lxc_conf = PVE
::LXC
::load_config
($param->{vmid
});
522 # NOTE: we only return selected/converted values
524 my $conf = PVE
::LXC
::lxc_conf_to_pve
($param->{vmid
}, $lxc_conf);
526 my $stcfg = PVE
::Cluster
::cfs_read_file
("storage.cfg");
528 my ($sid, undef, $path) = &$get_container_storage($stcfg, $param->{vmid
}, $lxc_conf);
529 $conf->{storage
} = $sid || $path;
534 __PACKAGE__-
>register_method({
535 name
=> 'destroy_vm',
540 description
=> "Destroy the container (also delete all uses files).",
542 check
=> [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
545 additionalProperties
=> 0,
547 node
=> get_standard_option
('pve-node'),
548 vmid
=> get_standard_option
('pve-vmid'),
557 my $rpcenv = PVE
::RPCEnvironment
::get
();
559 my $authuser = $rpcenv->get_user();
561 my $vmid = $param->{vmid
};
563 # test if container exists
564 my $conf = PVE
::LXC
::load_config
($param->{vmid
});
566 my $storage_cfg = cfs_read_file
("storage.cfg");
569 if (my $volid = $conf->{'pve.volid'}) {
571 my ($vtype, $name, $owner) = PVE
::Storage
::parse_volname
($storage_cfg, $volid);
572 die "got strange volid (containe is not owner!)\n" if $vmid != $owner;
574 PVE
::Storage
::vdisk_free
($storage_cfg, $volid);
576 PVE
::LXC
::destroy_config
($vmid);
579 my $cmd = ['lxc-destroy', '-n', $vmid ];
584 PVE
::AccessControl
::remove_vm_from_pool
($vmid);
587 my $realcmd = sub { PVE
::LXC
::lock_container
($vmid, 1, $code); };
589 return $rpcenv->fork_worker('vzdestroy', $vmid, $authuser, $realcmd);
594 __PACKAGE__-
>register_method ({
596 path
=> '{vmid}/vncproxy',
600 check
=> ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
602 description
=> "Creates a TCP VNC proxy connections.",
604 additionalProperties
=> 0,
606 node
=> get_standard_option
('pve-node'),
607 vmid
=> get_standard_option
('pve-vmid'),
611 description
=> "use websocket instead of standard VNC.",
616 additionalProperties
=> 0,
618 user
=> { type
=> 'string' },
619 ticket
=> { type
=> 'string' },
620 cert
=> { type
=> 'string' },
621 port
=> { type
=> 'integer' },
622 upid
=> { type
=> 'string' },
628 my $rpcenv = PVE
::RPCEnvironment
::get
();
630 my $authuser = $rpcenv->get_user();
632 my $vmid = $param->{vmid
};
633 my $node = $param->{node
};
635 my $authpath = "/vms/$vmid";
637 my $ticket = PVE
::AccessControl
::assemble_vnc_ticket
($authuser, $authpath);
639 $sslcert = PVE
::Tools
::file_get_contents
("/etc/pve/pve-root-ca.pem", 8192)
642 my $port = PVE
::Tools
::next_vnc_port
();
646 if ($node ne PVE
::INotify
::nodename
()) {
647 $remip = PVE
::Cluster
::remote_node_ip
($node);
650 # NOTE: vncterm VNC traffic is already TLS encrypted,
651 # so we select the fastest chipher here (or 'none'?)
652 my $remcmd = $remip ?
653 ['/usr/bin/ssh', '-t', $remip] : [];
655 my $shcmd = [ '/usr/bin/dtach', '-A',
656 "/var/run/dtach/vzctlconsole$vmid",
658 'lxc-console', '-n', $vmid ];
663 syslog
('info', "starting lxc vnc proxy $upid\n");
667 my $cmd = ['/usr/bin/vncterm', '-rfbport', $port,
668 '-timeout', $timeout, '-authpath', $authpath,
669 '-perm', 'VM.Console'];
671 if ($param->{websocket
}) {
672 $ENV{PVE_VNC_TICKET
} = $ticket; # pass ticket to vncterm
673 push @$cmd, '-notls', '-listen', 'localhost';
676 push @$cmd, '-c', @$remcmd, @$shcmd;
683 my $upid = $rpcenv->fork_worker('vncproxy', $vmid, $authuser, $realcmd);
685 PVE
::Tools
::wait_for_vnc_port
($port);
696 __PACKAGE__-
>register_method({
697 name
=> 'vncwebsocket',
698 path
=> '{vmid}/vncwebsocket',
701 description
=> "You also need to pass a valid ticket (vncticket).",
702 check
=> ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
704 description
=> "Opens a weksocket for VNC traffic.",
706 additionalProperties
=> 0,
708 node
=> get_standard_option
('pve-node'),
709 vmid
=> get_standard_option
('pve-vmid'),
711 description
=> "Ticket from previous call to vncproxy.",
716 description
=> "Port number returned by previous vncproxy call.",
726 port
=> { type
=> 'string' },
732 my $rpcenv = PVE
::RPCEnvironment
::get
();
734 my $authuser = $rpcenv->get_user();
736 my $authpath = "/vms/$param->{vmid}";
738 PVE
::AccessControl
::verify_vnc_ticket
($param->{vncticket
}, $authuser, $authpath);
740 my $port = $param->{port
};
742 return { port
=> $port };
745 __PACKAGE__-
>register_method ({
746 name
=> 'spiceproxy',
747 path
=> '{vmid}/spiceproxy',
752 check
=> ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
754 description
=> "Returns a SPICE configuration to connect to the CT.",
756 additionalProperties
=> 0,
758 node
=> get_standard_option
('pve-node'),
759 vmid
=> get_standard_option
('pve-vmid'),
760 proxy
=> get_standard_option
('spice-proxy', { optional
=> 1 }),
763 returns
=> get_standard_option
('remote-viewer-config'),
767 my $vmid = $param->{vmid
};
768 my $node = $param->{node
};
769 my $proxy = $param->{proxy
};
771 my $authpath = "/vms/$vmid";
772 my $permissions = 'VM.Console';
774 my $shcmd = ['/usr/bin/dtach', '-A',
775 "/var/run/dtach/vzctlconsole$vmid",
777 'lxc-console', '-n', $vmid];
779 my $title = "CT $vmid";
781 return PVE
::API2Tools
::run_spiceterm
($authpath, $permissions, $vmid, $node, $proxy, $title, $shcmd);
784 __PACKAGE__-
>register_method({
786 path
=> '{vmid}/status',
789 description
=> "Directory index",
794 additionalProperties
=> 0,
796 node
=> get_standard_option
('pve-node'),
797 vmid
=> get_standard_option
('pve-vmid'),
805 subdir
=> { type
=> 'string' },
808 links
=> [ { rel
=> 'child', href
=> "{subdir}" } ],
814 my $conf = PVE
::OpenVZ
::load_config
($param->{vmid
});
817 { subdir
=> 'current' },
818 { subdir
=> 'start' },
819 { subdir
=> 'stop' },
820 { subdir
=> 'shutdown' },
821 { subdir
=> 'migrate' },
827 __PACKAGE__-
>register_method({
829 path
=> '{vmid}/status/current',
832 protected
=> 1, # openvz /proc entries are only readable by root
833 description
=> "Get virtual machine status.",
835 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
838 additionalProperties
=> 0,
840 node
=> get_standard_option
('pve-node'),
841 vmid
=> get_standard_option
('pve-vmid'),
844 returns
=> { type
=> 'object' },
849 my $conf = PVE
::LXC
::load_config
($param->{vmid
});
851 my $vmstatus = PVE
::LXC
::vmstatus
($param->{vmid
});
852 my $status = $vmstatus->{$param->{vmid
}};
854 $status->{ha
} = PVE
::HA
::Config
::vm_is_ha_managed
($param->{vmid
}) ?
1 : 0;
859 __PACKAGE__-
>register_method({
861 path
=> '{vmid}/status/start',
865 description
=> "Start the container.",
867 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
870 additionalProperties
=> 0,
872 node
=> get_standard_option
('pve-node'),
873 vmid
=> get_standard_option
('pve-vmid'),
882 my $rpcenv = PVE
::RPCEnvironment
::get
();
884 my $authuser = $rpcenv->get_user();
886 my $node = extract_param
($param, 'node');
888 my $vmid = extract_param
($param, 'vmid');
890 die "CT $vmid already running\n" if PVE
::LXC
::check_running
($vmid);
892 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid) && $rpcenv->{type
} ne 'ha') {
897 my $service = "ct:$vmid";
899 my $cmd = ['ha-manager', 'enable', $service];
901 print "Executing HA start for CT $vmid\n";
903 PVE
::Tools
::run_command
($cmd);
908 return $rpcenv->fork_worker('hastart', $vmid, $authuser, $hacmd);
915 syslog
('info', "starting CT $vmid: $upid\n");
917 my $conf = PVE
::LXC
::load_config
($vmid);
918 my $stcfg = cfs_read_file
("storage.cfg");
919 if (my $sid = &$get_container_storage($stcfg, $vmid, $conf)) {
920 PVE
::Storage
::activate_storage
($stcfg, $sid);
923 my $cmd = ['lxc-start', '-n', $vmid];
930 return $rpcenv->fork_worker('vzstart', $vmid, $authuser, $realcmd);
934 __PACKAGE__-
>register_method({
936 path
=> '{vmid}/status/stop',
940 description
=> "Stop the container.",
942 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
945 additionalProperties
=> 0,
947 node
=> get_standard_option
('pve-node'),
948 vmid
=> get_standard_option
('pve-vmid'),
957 my $rpcenv = PVE
::RPCEnvironment
::get
();
959 my $authuser = $rpcenv->get_user();
961 my $node = extract_param
($param, 'node');
963 my $vmid = extract_param
($param, 'vmid');
965 die "CT $vmid not running\n" if !PVE
::LXC
::check_running
($vmid);
967 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid) && $rpcenv->{type
} ne 'ha') {
972 my $service = "ct:$vmid";
974 my $cmd = ['ha-manager', 'disable', $service];
976 print "Executing HA stop for CT $vmid\n";
978 PVE
::Tools
::run_command
($cmd);
983 return $rpcenv->fork_worker('hastop', $vmid, $authuser, $hacmd);
990 syslog
('info', "stoping CT $vmid: $upid\n");
992 my $cmd = ['lxc-stop', '-n', $vmid, '--kill'];
999 return $rpcenv->fork_worker('vzstop', $vmid, $authuser, $realcmd);
1003 __PACKAGE__-
>register_method({
1004 name
=> 'vm_shutdown',
1005 path
=> '{vmid}/status/shutdown',
1009 description
=> "Shutdown the container.",
1011 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1014 additionalProperties
=> 0,
1016 node
=> get_standard_option
('pve-node'),
1017 vmid
=> get_standard_option
('pve-vmid'),
1019 description
=> "Wait maximal timeout seconds.",
1026 description
=> "Make sure the Container stops.",
1039 my $rpcenv = PVE
::RPCEnvironment
::get
();
1041 my $authuser = $rpcenv->get_user();
1043 my $node = extract_param
($param, 'node');
1045 my $vmid = extract_param
($param, 'vmid');
1047 my $timeout = extract_param
($param, 'timeout');
1049 die "CT $vmid not running\n" if !PVE
::LXC
::check_running
($vmid);
1054 syslog
('info', "shutdown CT $vmid: $upid\n");
1056 my $cmd = ['lxc-stop', '-n', $vmid];
1058 $timeout = 60 if !defined($timeout);
1060 push @$cmd, '--timeout', $timeout;
1062 eval { run_command
($cmd, timeout
=> $timeout+5); };
1066 die $err if !$param->{forceStop
};
1068 warn "shutdown failed - forcing stop now\n";
1070 push @$cmd, '--kill';
1076 my $upid = $rpcenv->fork_worker('vzshutdown', $vmid, $authuser, $realcmd);
1081 __PACKAGE__-
>register_method({
1082 name
=> 'vm_suspend',
1083 path
=> '{vmid}/status/suspend',
1087 description
=> "Suspend the container.",
1089 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1092 additionalProperties
=> 0,
1094 node
=> get_standard_option
('pve-node'),
1095 vmid
=> get_standard_option
('pve-vmid'),
1104 my $rpcenv = PVE
::RPCEnvironment
::get
();
1106 my $authuser = $rpcenv->get_user();
1108 my $node = extract_param
($param, 'node');
1110 my $vmid = extract_param
($param, 'vmid');
1112 die "CT $vmid not running\n" if !PVE
::LXC
::check_running
($vmid);
1117 syslog
('info', "suspend CT $vmid: $upid\n");
1119 my $cmd = ['lxc-checkpoint', '-n', $vmid, '-s', '-D', '/var/liv/vz/dump'];
1126 my $upid = $rpcenv->fork_worker('vzsuspend', $vmid, $authuser, $realcmd);
1131 __PACKAGE__-
>register_method({
1132 name
=> 'vm_resume',
1133 path
=> '{vmid}/status/resume',
1137 description
=> "Resume the container.",
1139 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1142 additionalProperties
=> 0,
1144 node
=> get_standard_option
('pve-node'),
1145 vmid
=> get_standard_option
('pve-vmid'),
1154 my $rpcenv = PVE
::RPCEnvironment
::get
();
1156 my $authuser = $rpcenv->get_user();
1158 my $node = extract_param
($param, 'node');
1160 my $vmid = extract_param
($param, 'vmid');
1162 die "CT $vmid already running\n" if PVE
::LXC
::check_running
($vmid);
1167 syslog
('info', "resume CT $vmid: $upid\n");
1169 my $cmd = ['lxc-checkpoint', '-n', $vmid, '-r', '--foreground',
1170 '-D', '/var/liv/vz/dump'];
1177 my $upid = $rpcenv->fork_worker('vzresume', $vmid, $authuser, $realcmd);
1182 __PACKAGE__-
>register_method({
1183 name
=> 'migrate_vm',
1184 path
=> '{vmid}/migrate',
1188 description
=> "Migrate the container to another node. Creates a new migration task.",
1190 check
=> ['perm', '/vms/{vmid}', [ 'VM.Migrate' ]],
1193 additionalProperties
=> 0,
1195 node
=> get_standard_option
('pve-node'),
1196 vmid
=> get_standard_option
('pve-vmid'),
1197 target
=> get_standard_option
('pve-node', { description
=> "Target node." }),
1200 description
=> "Use online/live migration.",
1207 description
=> "the task ID.",
1212 my $rpcenv = PVE
::RPCEnvironment
::get
();
1214 my $authuser = $rpcenv->get_user();
1216 my $target = extract_param
($param, 'target');
1218 my $localnode = PVE
::INotify
::nodename
();
1219 raise_param_exc
({ target
=> "target is local node."}) if $target eq $localnode;
1221 PVE
::Cluster
::check_cfs_quorum
();
1223 PVE
::Cluster
::check_node_exists
($target);
1225 my $targetip = PVE
::Cluster
::remote_node_ip
($target);
1227 my $vmid = extract_param
($param, 'vmid');
1230 PVE
::LXC
::load_config
($vmid);
1232 # try to detect errors early
1233 if (PVE
::LXC
::check_running
($vmid)) {
1234 die "cant migrate running container without --online\n"
1235 if !$param->{online
};
1238 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid) && $rpcenv->{type
} ne 'ha') {
1243 my $service = "ct:$vmid";
1245 my $cmd = ['ha-manager', 'migrate', $service, $target];
1247 print "Executing HA migrate for CT $vmid to node $target\n";
1249 PVE
::Tools
::run_command
($cmd);
1254 return $rpcenv->fork_worker('hamigrate', $vmid, $authuser, $hacmd);
1261 # fixme: implement lxc container migration
1262 die "lxc container migration not implemented\n";
1267 return $rpcenv->fork_worker('vzmigrate', $vmid, $authuser, $realcmd);