]>
git.proxmox.com Git - pve-container.git/blob - src/PVE/API2/LXC.pm
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 if (my $volid = $lxc_conf->{'pve.volid'}) {
27 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid);
28 return wantarray ?
($sid, $volname) : $sid;
30 my $path = $lxc_conf->{'lxc.rootfs'};
31 my ($vtype, $volid) = PVE
::Storage
::path_to_volume_id
($stcfg, $path);
32 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1) if $volid;
33 return wantarray ?
($sid, $volname, $path) : $sid;
37 my $check_ct_modify_config_perm = sub {
38 my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_;
40 return 1 if $authuser ne 'root@pam';
42 foreach my $opt (@$key_list) {
44 if ($opt eq 'cpus' || $opt eq 'cpuunits' || $opt eq 'cpulimit') {
45 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.CPU']);
46 } elsif ($opt eq 'disk') {
47 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk']);
48 } elsif ($opt eq 'memory' || $opt eq 'swap') {
49 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Memory']);
50 } elsif ($opt =~ m/^net\d+$/ || $opt eq 'nameserver' ||
51 $opt eq 'searchdomain' || $opt eq 'hostname') {
52 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
54 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Options']);
61 PVE
::JSONSchema
::register_standard_option
('pve-lxc-snapshot-name', {
62 description
=> "The name of the snapshot.",
63 type
=> 'string', format
=> 'pve-configid',
67 __PACKAGE__-
>register_method({
71 description
=> "LXC container index (per node).",
73 description
=> "Only list CTs where you have VM.Audit permissons on /vms/<vmid>.",
77 protected
=> 1, # /proc files are only readable by root
79 additionalProperties
=> 0,
81 node
=> get_standard_option
('pve-node'),
90 links
=> [ { rel
=> 'child', href
=> "{vmid}" } ],
95 my $rpcenv = PVE
::RPCEnvironment
::get
();
96 my $authuser = $rpcenv->get_user();
98 my $vmstatus = PVE
::LXC
::vmstatus
();
101 foreach my $vmid (keys %$vmstatus) {
102 next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1);
104 my $data = $vmstatus->{$vmid};
105 $data->{vmid
} = $vmid;
113 __PACKAGE__-
>register_method({
117 description
=> "Create or restore a container.",
119 user
=> 'all', # check inside
120 description
=> "You need 'VM.Allocate' permissions on /vms/{vmid} or on the VM pool /pool/{pool}. " .
121 "For restore, it is enough if the user has 'VM.Backup' permission and the VM already exists. " .
122 "You also need 'Datastore.AllocateSpace' permissions on the storage.",
127 additionalProperties
=> 0,
128 properties
=> PVE
::LXC
::json_config_properties
({
129 node
=> get_standard_option
('pve-node'),
130 vmid
=> get_standard_option
('pve-vmid'),
132 description
=> "The OS template or backup file.",
139 description
=> "Sets root password inside container.",
142 storage
=> get_standard_option
('pve-storage-id', {
143 description
=> "Target storage.",
150 description
=> "Allow to overwrite existing container.",
155 description
=> "Mark this as restore task.",
159 type
=> 'string', format
=> 'pve-poolid',
160 description
=> "Add the VM to the specified pool.",
170 my $rpcenv = PVE
::RPCEnvironment
::get
();
172 my $authuser = $rpcenv->get_user();
174 my $node = extract_param
($param, 'node');
176 my $vmid = extract_param
($param, 'vmid');
178 my $basecfg_fn = PVE
::LXC
::config_file
($vmid);
180 my $same_container_exists = -f
$basecfg_fn;
182 my $restore = extract_param
($param, 'restore');
185 # fixme: limit allowed parameters
189 my $force = extract_param
($param, 'force');
191 if (!($same_container_exists && $restore && $force)) {
192 PVE
::Cluster
::check_vmid_unused
($vmid);
195 my $password = extract_param
($param, 'password');
197 my $storage = extract_param
($param, 'storage') || 'local';
199 my $pool = extract_param
($param, 'pool');
201 my $storage_cfg = cfs_read_file
("storage.cfg");
203 my $scfg = PVE
::Storage
::storage_check_node
($storage_cfg, $storage, $node);
205 raise_param_exc
({ storage
=> "storage '$storage' does not support container root directories"})
206 if !$scfg->{content
}->{rootdir
};
208 if (defined($pool)) {
209 $rpcenv->check_pool_exist($pool);
210 $rpcenv->check_perm_modify($authuser, "/pool/$pool");
213 if ($rpcenv->check($authuser, "/vms/$vmid", ['VM.Allocate'], 1)) {
215 } elsif ($pool && $rpcenv->check($authuser, "/pool/$pool", ['VM.Allocate'], 1)) {
217 } elsif ($restore && $force && $same_container_exists &&
218 $rpcenv->check($authuser, "/vms/$vmid", ['VM.Backup'], 1)) {
219 # OK: user has VM.Backup permissions, and want to restore an existing VM
224 &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, $pool, [ keys %$param]);
226 PVE
::Storage
::activate_storage
($storage_cfg, $storage);
228 my $ostemplate = extract_param
($param, 'ostemplate');
232 if ($ostemplate eq '-') {
233 die "pipe requires cli environment\n"
234 if $rpcenv->{type
} ne 'cli';
235 die "pipe can only be used with restore tasks\n"
239 $rpcenv->check_volume_access($authuser, $storage_cfg, $vmid, $ostemplate);
240 $archive = PVE
::Storage
::abs_filesystem_path
($storage_cfg, $ostemplate);
245 $conf = PVE
::LXCCreate
::recover_config
($archive, $conf);
247 foreach my $item ( %{$conf}) {
249 if ($item =~ m/(net\d+)/) {
251 my $pair = $conf->{$net}->{'veth.pair'};
252 $pair =~ s/\d+/$vmid/;
253 $conf->{$net}->{'veth.pair'} = $pair;
258 $param->{hostname
} ||= "CT$vmid" if !defined($conf->{'lxc.utsname'});
259 $param->{memory
} ||= 512 if !defined($conf->{'lxc.cgroup.memory.limit_in_bytes'});
260 $param->{swap
} = 512 if (!defined($param->{swap
}) && !defined($conf->{'lxc.cgroup.memory.memsw.limit_in_bytes'}));
262 PVE
::LXC
::update_lxc_config
($vmid, $conf, 0, $param);
264 # assigng default names, so that we can configure network with LXCSetup
265 foreach my $k (keys %$conf) {
266 next if $k !~ m/^net(\d+)$/;
269 $d->{name
} = "eth$ind"; # fixme: do not overwrite settings!
272 # use user namespace ?
273 # disable for now, because kernel 3.10.0 does not support it
274 #$conf->{'lxc.id_map'} = ["u 0 100000 65536", "g 0 100000 65536"];
276 my $check_vmid_usage = sub {
278 die "cant overwrite running container\n"
279 if PVE
::LXC
::check_running
($vmid);
281 PVE
::Cluster
::check_vmid_unused
($vmid);
286 if ($restore && ($ostemplate =~ m/openvz/) ) {
287 print "###########################################################\n";
288 print "Restore from OpenVZ please check the config and add network\n";
289 print "###########################################################\n";
292 &$check_vmid_usage(); # final check after locking
294 PVE
::Cluster
::check_cfs_quorum
();
296 PVE
::LXCCreate
::create_rootfs
($storage_cfg, $storage, $param->{disk
}, $vmid, $conf,
297 $archive, $password, $restore);
300 my $realcmd = sub { PVE
::LXC
::lock_container
($vmid, 1, $code); };
302 &$check_vmid_usage(); # first check before locking
304 return $rpcenv->fork_worker($restore ?
'vzrestore' : 'vzcreate',
305 $vmid, $authuser, $realcmd);
309 my $vm_config_perm_list = [
317 __PACKAGE__-
>register_method({
319 path
=> '{vmid}/config',
323 description
=> "Set container options.",
325 check
=> ['perm', '/vms/{vmid}', $vm_config_perm_list, any
=> 1],
328 additionalProperties
=> 0,
329 properties
=> PVE
::LXC
::json_config_properties
(
331 node
=> get_standard_option
('pve-node'),
332 vmid
=> get_standard_option
('pve-vmid'),
334 type
=> 'string', format
=> 'pve-configid-list',
335 description
=> "A list of settings you want to delete.",
340 description
=> 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.',
346 returns
=> { type
=> 'null'},
350 my $rpcenv = PVE
::RPCEnvironment
::get
();
352 my $authuser = $rpcenv->get_user();
354 my $node = extract_param
($param, 'node');
356 my $vmid = extract_param
($param, 'vmid');
358 my $digest = extract_param
($param, 'digest');
360 die "no options specified\n" if !scalar(keys %$param);
362 my $delete_str = extract_param
($param, 'delete');
363 my @delete = PVE
::Tools
::split_list
($delete_str);
365 &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, [@delete]);
367 foreach my $opt (@delete) {
368 raise_param_exc
({ delete => "you can't use '-$opt' and " .
369 "-delete $opt' at the same time" })
370 if defined($param->{$opt});
372 if (!PVE
::LXC
::option_exists
($opt)) {
373 raise_param_exc
({ delete => "unknown option '$opt'" });
377 &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, [keys %$param]);
381 my $conf = PVE
::LXC
::load_config
($vmid);
382 PVE
::LXC
::check_lock
($conf);
384 PVE
::Tools
::assert_if_modified
($digest, $conf->{digest
});
386 my $running = PVE
::LXC
::check_running
($vmid);
388 PVE
::LXC
::update_lxc_config
($vmid, $conf, $running, $param, \
@delete);
390 PVE
::LXC
::write_config
($vmid, $conf);
393 PVE
::LXC
::lock_container
($vmid, undef, $code);
398 __PACKAGE__-
>register_method ({
399 subclass
=> "PVE::API2::Firewall::CT",
400 path
=> '{vmid}/firewall',
403 __PACKAGE__-
>register_method({
408 description
=> "Directory index",
413 additionalProperties
=> 0,
415 node
=> get_standard_option
('pve-node'),
416 vmid
=> get_standard_option
('pve-vmid'),
424 subdir
=> { type
=> 'string' },
427 links
=> [ { rel
=> 'child', href
=> "{subdir}" } ],
433 my $conf = PVE
::LXC
::load_config
($param->{vmid
});
436 { subdir
=> 'config' },
437 { subdir
=> 'status' },
438 { subdir
=> 'vncproxy' },
439 { subdir
=> 'vncwebsocket' },
440 { subdir
=> 'spiceproxy' },
441 { subdir
=> 'migrate' },
442 # { subdir => 'initlog' },
444 { subdir
=> 'rrddata' },
445 { subdir
=> 'firewall' },
446 { subdir
=> 'snapshot' },
452 __PACKAGE__-
>register_method({
454 path
=> '{vmid}/rrd',
456 protected
=> 1, # fixme: can we avoid that?
458 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
460 description
=> "Read VM RRD statistics (returns PNG)",
462 additionalProperties
=> 0,
464 node
=> get_standard_option
('pve-node'),
465 vmid
=> get_standard_option
('pve-vmid'),
467 description
=> "Specify the time frame you are interested in.",
469 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
472 description
=> "The list of datasources you want to display.",
473 type
=> 'string', format
=> 'pve-configid-list',
476 description
=> "The RRD consolidation function",
478 enum
=> [ 'AVERAGE', 'MAX' ],
486 filename
=> { type
=> 'string' },
492 return PVE
::Cluster
::create_rrd_graph
(
493 "pve2-vm/$param->{vmid}", $param->{timeframe
},
494 $param->{ds
}, $param->{cf
});
498 __PACKAGE__-
>register_method({
500 path
=> '{vmid}/rrddata',
502 protected
=> 1, # fixme: can we avoid that?
504 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
506 description
=> "Read VM RRD statistics",
508 additionalProperties
=> 0,
510 node
=> get_standard_option
('pve-node'),
511 vmid
=> get_standard_option
('pve-vmid'),
513 description
=> "Specify the time frame you are interested in.",
515 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
518 description
=> "The RRD consolidation function",
520 enum
=> [ 'AVERAGE', 'MAX' ],
535 return PVE
::Cluster
::create_rrd_data
(
536 "pve2-vm/$param->{vmid}", $param->{timeframe
}, $param->{cf
});
540 __PACKAGE__-
>register_method({
542 path
=> '{vmid}/config',
545 description
=> "Get container configuration.",
547 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
550 additionalProperties
=> 0,
552 node
=> get_standard_option
('pve-node'),
553 vmid
=> get_standard_option
('pve-vmid'),
561 description
=> 'SHA1 digest of configuration file. This can be used to prevent concurrent modifications.',
568 my $lxc_conf = PVE
::LXC
::load_config
($param->{vmid
});
570 # NOTE: we only return selected/converted values
572 my $conf = PVE
::LXC
::lxc_conf_to_pve
($param->{vmid
}, $lxc_conf);
574 my $stcfg = PVE
::Cluster
::cfs_read_file
("storage.cfg");
576 my ($sid, undef, $path) = &$get_container_storage($stcfg, $param->{vmid
}, $lxc_conf);
577 $conf->{storage
} = $sid || $path;
582 __PACKAGE__-
>register_method({
583 name
=> 'destroy_vm',
588 description
=> "Destroy the container (also delete all uses files).",
590 check
=> [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
593 additionalProperties
=> 0,
595 node
=> get_standard_option
('pve-node'),
596 vmid
=> get_standard_option
('pve-vmid'),
605 my $rpcenv = PVE
::RPCEnvironment
::get
();
607 my $authuser = $rpcenv->get_user();
609 my $vmid = $param->{vmid
};
611 # test if container exists
612 my $conf = PVE
::LXC
::load_config
($vmid);
614 my $storage_cfg = cfs_read_file
("storage.cfg");
617 # reload config after lock
618 $conf = PVE
::LXC
::load_config
($vmid);
619 PVE
::LXC
::check_lock
($conf);
621 PVE
::LXC
::destory_lxc_container
($storage_cfg, $vmid, $conf);
622 PVE
::AccessControl
::remove_vm_from_pool
($vmid);
625 my $realcmd = sub { PVE
::LXC
::lock_container
($vmid, 1, $code); };
627 return $rpcenv->fork_worker('vzdestroy', $vmid, $authuser, $realcmd);
632 __PACKAGE__-
>register_method ({
634 path
=> '{vmid}/vncproxy',
638 check
=> ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
640 description
=> "Creates a TCP VNC proxy connections.",
642 additionalProperties
=> 0,
644 node
=> get_standard_option
('pve-node'),
645 vmid
=> get_standard_option
('pve-vmid'),
649 description
=> "use websocket instead of standard VNC.",
654 additionalProperties
=> 0,
656 user
=> { type
=> 'string' },
657 ticket
=> { type
=> 'string' },
658 cert
=> { type
=> 'string' },
659 port
=> { type
=> 'integer' },
660 upid
=> { type
=> 'string' },
666 my $rpcenv = PVE
::RPCEnvironment
::get
();
668 my $authuser = $rpcenv->get_user();
670 my $vmid = $param->{vmid
};
671 my $node = $param->{node
};
673 my $authpath = "/vms/$vmid";
675 my $ticket = PVE
::AccessControl
::assemble_vnc_ticket
($authuser, $authpath);
677 $sslcert = PVE
::Tools
::file_get_contents
("/etc/pve/pve-root-ca.pem", 8192)
680 my ($remip, $family);
682 if ($node ne PVE
::INotify
::nodename
()) {
683 ($remip, $family) = PVE
::Cluster
::remote_node_ip
($node);
685 $family = PVE
::Tools
::get_host_address_family
($node);
688 my $port = PVE
::Tools
::next_vnc_port
($family);
690 # NOTE: vncterm VNC traffic is already TLS encrypted,
691 # so we select the fastest chipher here (or 'none'?)
692 my $remcmd = $remip ?
693 ['/usr/bin/ssh', '-t', $remip] : [];
695 my $shcmd = [ '/usr/bin/dtach', '-A',
696 "/var/run/dtach/vzctlconsole$vmid",
698 'lxc-console', '-n', $vmid ];
703 syslog
('info', "starting lxc vnc proxy $upid\n");
707 my $cmd = ['/usr/bin/vncterm', '-rfbport', $port,
708 '-timeout', $timeout, '-authpath', $authpath,
709 '-perm', 'VM.Console'];
711 if ($param->{websocket
}) {
712 $ENV{PVE_VNC_TICKET
} = $ticket; # pass ticket to vncterm
713 push @$cmd, '-notls', '-listen', 'localhost';
716 push @$cmd, '-c', @$remcmd, @$shcmd;
723 my $upid = $rpcenv->fork_worker('vncproxy', $vmid, $authuser, $realcmd);
725 PVE
::Tools
::wait_for_vnc_port
($port);
736 __PACKAGE__-
>register_method({
737 name
=> 'vncwebsocket',
738 path
=> '{vmid}/vncwebsocket',
741 description
=> "You also need to pass a valid ticket (vncticket).",
742 check
=> ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
744 description
=> "Opens a weksocket for VNC traffic.",
746 additionalProperties
=> 0,
748 node
=> get_standard_option
('pve-node'),
749 vmid
=> get_standard_option
('pve-vmid'),
751 description
=> "Ticket from previous call to vncproxy.",
756 description
=> "Port number returned by previous vncproxy call.",
766 port
=> { type
=> 'string' },
772 my $rpcenv = PVE
::RPCEnvironment
::get
();
774 my $authuser = $rpcenv->get_user();
776 my $authpath = "/vms/$param->{vmid}";
778 PVE
::AccessControl
::verify_vnc_ticket
($param->{vncticket
}, $authuser, $authpath);
780 my $port = $param->{port
};
782 return { port
=> $port };
785 __PACKAGE__-
>register_method ({
786 name
=> 'spiceproxy',
787 path
=> '{vmid}/spiceproxy',
792 check
=> ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
794 description
=> "Returns a SPICE configuration to connect to the CT.",
796 additionalProperties
=> 0,
798 node
=> get_standard_option
('pve-node'),
799 vmid
=> get_standard_option
('pve-vmid'),
800 proxy
=> get_standard_option
('spice-proxy', { optional
=> 1 }),
803 returns
=> get_standard_option
('remote-viewer-config'),
807 my $vmid = $param->{vmid
};
808 my $node = $param->{node
};
809 my $proxy = $param->{proxy
};
811 my $authpath = "/vms/$vmid";
812 my $permissions = 'VM.Console';
814 my $shcmd = ['/usr/bin/dtach', '-A',
815 "/var/run/dtach/vzctlconsole$vmid",
817 'lxc-console', '-n', $vmid];
819 my $title = "CT $vmid";
821 return PVE
::API2Tools
::run_spiceterm
($authpath, $permissions, $vmid, $node, $proxy, $title, $shcmd);
824 __PACKAGE__-
>register_method({
826 path
=> '{vmid}/status',
829 description
=> "Directory index",
834 additionalProperties
=> 0,
836 node
=> get_standard_option
('pve-node'),
837 vmid
=> get_standard_option
('pve-vmid'),
845 subdir
=> { type
=> 'string' },
848 links
=> [ { rel
=> 'child', href
=> "{subdir}" } ],
854 my $conf = PVE
::LXC
::load_config
($param->{vmid
});
857 { subdir
=> 'current' },
858 { subdir
=> 'start' },
859 { subdir
=> 'stop' },
860 { subdir
=> 'shutdown' },
861 { subdir
=> 'migrate' },
867 __PACKAGE__-
>register_method({
869 path
=> '{vmid}/status/current',
872 protected
=> 1, # openvz /proc entries are only readable by root
873 description
=> "Get virtual machine status.",
875 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
878 additionalProperties
=> 0,
880 node
=> get_standard_option
('pve-node'),
881 vmid
=> get_standard_option
('pve-vmid'),
884 returns
=> { type
=> 'object' },
889 my $conf = PVE
::LXC
::load_config
($param->{vmid
});
891 my $vmstatus = PVE
::LXC
::vmstatus
($param->{vmid
});
892 my $status = $vmstatus->{$param->{vmid
}};
894 $status->{ha
} = PVE
::HA
::Config
::vm_is_ha_managed
($param->{vmid
}) ?
1 : 0;
899 __PACKAGE__-
>register_method({
901 path
=> '{vmid}/status/start',
905 description
=> "Start the container.",
907 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
910 additionalProperties
=> 0,
912 node
=> get_standard_option
('pve-node'),
913 vmid
=> get_standard_option
('pve-vmid'),
922 my $rpcenv = PVE
::RPCEnvironment
::get
();
924 my $authuser = $rpcenv->get_user();
926 my $node = extract_param
($param, 'node');
928 my $vmid = extract_param
($param, 'vmid');
930 die "CT $vmid already running\n" if PVE
::LXC
::check_running
($vmid);
932 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid) && $rpcenv->{type
} ne 'ha') {
937 my $service = "ct:$vmid";
939 my $cmd = ['ha-manager', 'enable', $service];
941 print "Executing HA start for CT $vmid\n";
943 PVE
::Tools
::run_command
($cmd);
948 return $rpcenv->fork_worker('hastart', $vmid, $authuser, $hacmd);
955 syslog
('info', "starting CT $vmid: $upid\n");
957 my $conf = PVE
::LXC
::load_config
($vmid);
958 my $stcfg = cfs_read_file
("storage.cfg");
959 if (my $sid = &$get_container_storage($stcfg, $vmid, $conf)) {
960 PVE
::Storage
::activate_storage
($stcfg, $sid);
963 my $cmd = ['lxc-start', '-n', $vmid];
970 return $rpcenv->fork_worker('vzstart', $vmid, $authuser, $realcmd);
974 __PACKAGE__-
>register_method({
976 path
=> '{vmid}/status/stop',
980 description
=> "Stop the container.",
982 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
985 additionalProperties
=> 0,
987 node
=> get_standard_option
('pve-node'),
988 vmid
=> get_standard_option
('pve-vmid'),
997 my $rpcenv = PVE
::RPCEnvironment
::get
();
999 my $authuser = $rpcenv->get_user();
1001 my $node = extract_param
($param, 'node');
1003 my $vmid = extract_param
($param, 'vmid');
1005 die "CT $vmid not running\n" if !PVE
::LXC
::check_running
($vmid);
1007 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid) && $rpcenv->{type
} ne 'ha') {
1012 my $service = "ct:$vmid";
1014 my $cmd = ['ha-manager', 'disable', $service];
1016 print "Executing HA stop for CT $vmid\n";
1018 PVE
::Tools
::run_command
($cmd);
1023 return $rpcenv->fork_worker('hastop', $vmid, $authuser, $hacmd);
1030 syslog
('info', "stoping CT $vmid: $upid\n");
1032 my $cmd = ['lxc-stop', '-n', $vmid, '--kill'];
1039 return $rpcenv->fork_worker('vzstop', $vmid, $authuser, $realcmd);
1043 __PACKAGE__-
>register_method({
1044 name
=> 'vm_shutdown',
1045 path
=> '{vmid}/status/shutdown',
1049 description
=> "Shutdown the container.",
1051 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1054 additionalProperties
=> 0,
1056 node
=> get_standard_option
('pve-node'),
1057 vmid
=> get_standard_option
('pve-vmid'),
1059 description
=> "Wait maximal timeout seconds.",
1066 description
=> "Make sure the Container stops.",
1079 my $rpcenv = PVE
::RPCEnvironment
::get
();
1081 my $authuser = $rpcenv->get_user();
1083 my $node = extract_param
($param, 'node');
1085 my $vmid = extract_param
($param, 'vmid');
1087 my $timeout = extract_param
($param, 'timeout');
1089 die "CT $vmid not running\n" if !PVE
::LXC
::check_running
($vmid);
1094 syslog
('info', "shutdown CT $vmid: $upid\n");
1096 my $cmd = ['lxc-stop', '-n', $vmid];
1098 $timeout = 60 if !defined($timeout);
1100 push @$cmd, '--timeout', $timeout;
1102 eval { run_command
($cmd, timeout
=> $timeout+5); };
1106 die $err if !$param->{forceStop
};
1108 warn "shutdown failed - forcing stop now\n";
1110 push @$cmd, '--kill';
1116 my $upid = $rpcenv->fork_worker('vzshutdown', $vmid, $authuser, $realcmd);
1121 __PACKAGE__-
>register_method({
1122 name
=> 'vm_suspend',
1123 path
=> '{vmid}/status/suspend',
1127 description
=> "Suspend the container.",
1129 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1132 additionalProperties
=> 0,
1134 node
=> get_standard_option
('pve-node'),
1135 vmid
=> get_standard_option
('pve-vmid'),
1144 my $rpcenv = PVE
::RPCEnvironment
::get
();
1146 my $authuser = $rpcenv->get_user();
1148 my $node = extract_param
($param, 'node');
1150 my $vmid = extract_param
($param, 'vmid');
1152 die "CT $vmid not running\n" if !PVE
::LXC
::check_running
($vmid);
1157 syslog
('info', "suspend CT $vmid: $upid\n");
1159 my $cmd = ['lxc-checkpoint', '-n', $vmid, '-s', '-D', '/var/liv/vz/dump'];
1166 my $upid = $rpcenv->fork_worker('vzsuspend', $vmid, $authuser, $realcmd);
1171 __PACKAGE__-
>register_method({
1172 name
=> 'vm_resume',
1173 path
=> '{vmid}/status/resume',
1177 description
=> "Resume the container.",
1179 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1182 additionalProperties
=> 0,
1184 node
=> get_standard_option
('pve-node'),
1185 vmid
=> get_standard_option
('pve-vmid'),
1194 my $rpcenv = PVE
::RPCEnvironment
::get
();
1196 my $authuser = $rpcenv->get_user();
1198 my $node = extract_param
($param, 'node');
1200 my $vmid = extract_param
($param, 'vmid');
1202 die "CT $vmid already running\n" if PVE
::LXC
::check_running
($vmid);
1207 syslog
('info', "resume CT $vmid: $upid\n");
1209 my $cmd = ['lxc-checkpoint', '-n', $vmid, '-r', '--foreground',
1210 '-D', '/var/liv/vz/dump'];
1217 my $upid = $rpcenv->fork_worker('vzresume', $vmid, $authuser, $realcmd);
1222 __PACKAGE__-
>register_method({
1223 name
=> 'migrate_vm',
1224 path
=> '{vmid}/migrate',
1228 description
=> "Migrate the container to another node. Creates a new migration task.",
1230 check
=> ['perm', '/vms/{vmid}', [ 'VM.Migrate' ]],
1233 additionalProperties
=> 0,
1235 node
=> get_standard_option
('pve-node'),
1236 vmid
=> get_standard_option
('pve-vmid'),
1237 target
=> get_standard_option
('pve-node', { description
=> "Target node." }),
1240 description
=> "Use online/live migration.",
1247 description
=> "the task ID.",
1252 my $rpcenv = PVE
::RPCEnvironment
::get
();
1254 my $authuser = $rpcenv->get_user();
1256 my $target = extract_param
($param, 'target');
1258 my $localnode = PVE
::INotify
::nodename
();
1259 raise_param_exc
({ target
=> "target is local node."}) if $target eq $localnode;
1261 PVE
::Cluster
::check_cfs_quorum
();
1263 PVE
::Cluster
::check_node_exists
($target);
1265 my $targetip = PVE
::Cluster
::remote_node_ip
($target);
1267 my $vmid = extract_param
($param, 'vmid');
1270 PVE
::LXC
::load_config
($vmid);
1272 # try to detect errors early
1273 if (PVE
::LXC
::check_running
($vmid)) {
1274 die "cant migrate running container without --online\n"
1275 if !$param->{online
};
1278 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid) && $rpcenv->{type
} ne 'ha') {
1283 my $service = "ct:$vmid";
1285 my $cmd = ['ha-manager', 'migrate', $service, $target];
1287 print "Executing HA migrate for CT $vmid to node $target\n";
1289 PVE
::Tools
::run_command
($cmd);
1294 return $rpcenv->fork_worker('hamigrate', $vmid, $authuser, $hacmd);
1301 # fixme: implement lxc container migration
1302 die "lxc container migration not implemented\n";
1307 return $rpcenv->fork_worker('vzmigrate', $vmid, $authuser, $realcmd);
1311 __PACKAGE__-
>register_method({
1313 path
=> '{vmid}/snapshot',
1317 description
=> "Snapshot a container.",
1319 check
=> ['perm', '/vms/{vmid}', [ 'VM.Snapshot' ]],
1322 additionalProperties
=> 0,
1324 node
=> get_standard_option
('pve-node'),
1325 vmid
=> get_standard_option
('pve-vmid'),
1326 snapname
=> get_standard_option
('pve-lxc-snapshot-name'),
1330 description
=> "Save the vmstate",
1335 description
=> "A textual description or comment.",
1341 description
=> "the task ID.",
1346 my $rpcenv = PVE
::RPCEnvironment
::get
();
1348 my $authuser = $rpcenv->get_user();
1350 my $node = extract_param
($param, 'node');
1352 my $vmid = extract_param
($param, 'vmid');
1354 my $snapname = extract_param
($param, 'snapname');
1356 die "unable to use snapshot name 'current' (reserved name)\n"
1357 if $snapname eq 'current';
1360 PVE
::Cluster
::log_msg
('info', $authuser, "snapshot container $vmid: $snapname");
1361 PVE
::LXC
::snapshot_create
($vmid, $snapname, $param->{description
});
1364 return $rpcenv->fork_worker('pctsnapshot', $vmid, $authuser, $realcmd);
1367 __PACKAGE__-
>register_method({
1368 name
=> 'delsnapshot',
1369 path
=> '{vmid}/snapshot/{snapname}',
1373 description
=> "Delete a LXC snapshot.",
1375 check
=> ['perm', '/vms/{vmid}', [ 'VM.Snapshot' ]],
1378 additionalProperties
=> 0,
1380 node
=> get_standard_option
('pve-node'),
1381 vmid
=> get_standard_option
('pve-vmid'),
1382 snapname
=> get_standard_option
('pve-lxc-snapshot-name'),
1386 description
=> "For removal from config file, even if removing disk snapshots fails.",
1392 description
=> "the task ID.",
1397 my $rpcenv = PVE
::RPCEnvironment
::get
();
1399 my $authuser = $rpcenv->get_user();
1401 my $node = extract_param
($param, 'node');
1403 my $vmid = extract_param
($param, 'vmid');
1405 my $snapname = extract_param
($param, 'snapname');
1408 PVE
::Cluster
::log_msg
('info', $authuser, "delete snapshot VM $vmid: $snapname");
1409 PVE
::LXC
::snapshot_delete
($vmid, $snapname, $param->{force
});
1412 return $rpcenv->fork_worker('lxcdelsnapshot', $vmid, $authuser, $realcmd);
1415 __PACKAGE__-
>register_method({
1417 path
=> '{vmid}/snapshot/{snapname}/rollback',
1421 description
=> "Rollback LXC state to specified snapshot.",
1423 check
=> ['perm', '/vms/{vmid}', [ 'VM.Snapshot' ]],
1426 additionalProperties
=> 0,
1428 node
=> get_standard_option
('pve-node'),
1429 vmid
=> get_standard_option
('pve-vmid'),
1430 snapname
=> get_standard_option
('pve-lxc-snapshot-name'),
1435 description
=> "the task ID.",
1440 my $rpcenv = PVE
::RPCEnvironment
::get
();
1442 my $authuser = $rpcenv->get_user();
1444 my $node = extract_param
($param, 'node');
1446 my $vmid = extract_param
($param, 'vmid');
1448 my $snapname = extract_param
($param, 'snapname');
1451 PVE
::Cluster
::log_msg
('info', $authuser, "rollback snapshot LXC $vmid: $snapname");
1452 PVE
::LXC
::snapshot_rollback
($vmid, $snapname);
1455 return $rpcenv->fork_worker('lxcrollback', $vmid, $authuser, $realcmd);
1458 __PACKAGE__-
>register_method({
1459 name
=> 'snapshot_list',
1460 path
=> '{vmid}/snapshot',
1462 description
=> "List all snapshots.",
1464 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
1467 protected
=> 1, # lxc pid files are only readable by root
1469 additionalProperties
=> 0,
1471 vmid
=> get_standard_option
('pve-vmid'),
1472 node
=> get_standard_option
('pve-node'),
1481 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
1486 my $vmid = $param->{vmid
};
1488 my $conf = PVE
::LXC
::load_config
($vmid);
1489 my $snaphash = $conf->{snapshots
} || {};
1493 foreach my $name (keys %$snaphash) {
1494 my $d = $snaphash->{$name};
1497 snaptime
=> $d->{'pve.snaptime'} || 0,
1498 description
=> $d->{'pve.snapcomment'} || '',
1500 $item->{parent
} = $d->{'pve.parent'} if $d->{'pve.parent'};
1501 $item->{snapstate
} = $d->{'pve.snapstate'} if $d->{'pve.snapstate'};
1505 my $running = PVE
::LXC
::check_running
($vmid) ?
1 : 0;
1506 my $current = { name
=> 'current', digest
=> $conf->{digest
}, running
=> $running };
1507 $current->{parent
} = $conf->{'pve.parent'} if $conf->{'pve.parent'};
1509 push @$res, $current;
1514 __PACKAGE__-
>register_method({
1515 name
=> 'snapshot_cmd_idx',
1516 path
=> '{vmid}/snapshot/{snapname}',
1523 additionalProperties
=> 0,
1525 vmid
=> get_standard_option
('pve-vmid'),
1526 node
=> get_standard_option
('pve-node'),
1527 snapname
=> get_standard_option
('pve-lxc-snapshot-name'),
1536 links
=> [ { rel
=> 'child', href
=> "{cmd}" } ],
1543 push @$res, { cmd
=> 'rollback' };
1544 push @$res, { cmd
=> 'config' };
1549 __PACKAGE__-
>register_method({
1550 name
=> 'update_snapshot_config',
1551 path
=> '{vmid}/snapshot/{snapname}/config',
1555 description
=> "Update snapshot metadata.",
1557 check
=> ['perm', '/vms/{vmid}', [ 'VM.Snapshot' ]],
1560 additionalProperties
=> 0,
1562 node
=> get_standard_option
('pve-node'),
1563 vmid
=> get_standard_option
('pve-vmid'),
1564 snapname
=> get_standard_option
('pve-lxc-snapshot-name'),
1568 description
=> "A textual description or comment.",
1572 returns
=> { type
=> 'null' },
1576 my $rpcenv = PVE
::RPCEnvironment
::get
();
1578 my $authuser = $rpcenv->get_user();
1580 my $vmid = extract_param
($param, 'vmid');
1582 my $snapname = extract_param
($param, 'snapname');
1584 return undef if !defined($param->{description
});
1586 my $updatefn = sub {
1588 my $conf = PVE
::LXC
::load_config
($vmid);
1589 PVE
::LXC
::check_lock
($conf);
1591 my $snap = $conf->{snapshots
}->{$snapname};
1593 die "snapshot '$snapname' does not exist\n" if !defined($snap);
1595 $snap->{'pve.snapcomment'} = $param->{description
} if defined($param->{description
});
1597 PVE
::LXC
::write_config
($vmid, $conf, 1);
1600 PVE
::LXC
::lock_container
($vmid, 10, $updatefn);
1605 __PACKAGE__-
>register_method({
1606 name
=> 'get_snapshot_config',
1607 path
=> '{vmid}/snapshot/{snapname}/config',
1610 description
=> "Get snapshot configuration",
1612 check
=> ['perm', '/vms/{vmid}', [ 'VM.Snapshot' ]],
1615 additionalProperties
=> 0,
1617 node
=> get_standard_option
('pve-node'),
1618 vmid
=> get_standard_option
('pve-vmid'),
1619 snapname
=> get_standard_option
('pve-lxc-snapshot-name'),
1622 returns
=> { type
=> "object" },
1626 my $rpcenv = PVE
::RPCEnvironment
::get
();
1628 my $authuser = $rpcenv->get_user();
1630 my $vmid = extract_param
($param, 'vmid');
1632 my $snapname = extract_param
($param, 'snapname');
1634 my $lxc_conf = PVE
::LXC
::load_config
($vmid);
1636 my $snap = $lxc_conf->{snapshots
}->{$snapname};
1638 die "snapshot '$snapname' does not exist\n" if !defined($snap);
1640 my $conf = PVE
::LXC
::lxc_conf_to_pve
($param->{vmid
}, $snap);
1645 __PACKAGE__-
>register_method({
1646 name
=> 'vm_feature',
1647 path
=> '{vmid}/feature',
1651 description
=> "Check if feature for virtual machine is available.",
1653 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
1656 additionalProperties
=> 0,
1658 node
=> get_standard_option
('pve-node'),
1659 vmid
=> get_standard_option
('pve-vmid'),
1661 description
=> "Feature to check.",
1663 enum
=> [ 'snapshot' ],
1665 snapname
=> get_standard_option
('pve-lxc-snapshot-name', {
1673 hasFeature
=> { type
=> 'boolean' },
1676 #items => { type => 'string' },
1683 my $node = extract_param
($param, 'node');
1685 my $vmid = extract_param
($param, 'vmid');
1687 my $snapname = extract_param
($param, 'snapname');
1689 my $feature = extract_param
($param, 'feature');
1691 my $conf = PVE
::LXC
::load_config
($vmid);
1694 my $snap = $conf->{snapshots
}->{$snapname};
1695 die "snapshot '$snapname' does not exist\n" if !defined($snap);
1698 my $storecfg = PVE
::Storage
::config
();
1699 #Maybe include later
1700 #my $nodelist = PVE::LXC::shared_nodes($conf, $storecfg);
1701 my $hasFeature = PVE
::LXC
::has_feature
($feature, $conf, $storecfg, $snapname);
1704 hasFeature
=> $hasFeature,
1705 #nodes => [ keys %$nodelist ],