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 $check_ct_modify_config_perm = sub {
24 my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_;
26 return 1 if $authuser ne 'root@pam';
28 foreach my $opt (@$key_list) {
30 if ($opt eq 'cpus' || $opt eq 'cpuunits' || $opt eq 'cpulimit') {
31 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.CPU']);
32 } elsif ($opt eq 'disk') {
33 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk']);
34 } elsif ($opt eq 'memory' || $opt eq 'swap') {
35 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Memory']);
36 } elsif ($opt =~ m/^net\d+$/ || $opt eq 'nameserver' ||
37 $opt eq 'searchdomain' || $opt eq 'hostname') {
38 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
40 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Options']);
47 my $alloc_rootfs = sub {
48 my ($storage_conf, $storage, $disk_size_gb, $vmid) = @_;
52 my $size = 4*1024*1024; # defaults to 4G
54 $size = int($disk_size_gb*1024) * 1024 if defined($disk_size_gb);
57 my $scfg = PVE
::Storage
::storage_config
($storage_conf, $storage);
58 # fixme: use better naming ct-$vmid-disk-X.raw?
60 if ($scfg->{type
} eq 'dir' || $scfg->{type
} eq 'nfs') {
62 $volid = PVE
::Storage
::vdisk_alloc
($storage_conf, $storage, $vmid, 'raw',
63 "vm-$vmid-rootfs.raw", $size);
65 $volid = PVE
::Storage
::vdisk_alloc
($storage_conf, $storage, $vmid, 'subvol',
66 "subvol-$vmid-rootfs", 0);
68 } elsif ($scfg->{type
} eq 'zfspool') {
70 $volid = PVE
::Storage
::vdisk_alloc
($storage_conf, $storage, $vmid, 'subvol',
71 "subvol-$vmid-rootfs", $size);
72 } elsif ($scfg->{type
} eq 'drbd') {
74 $volid = PVE
::Storage
::vdisk_alloc
($storage_conf, $storage, $vmid, 'raw', undef, $size);
77 die "unable to create containers on storage type '$scfg->{type}'\n";
82 eval { PVE
::Storage
::vdisk_free
($storage_conf, $volid) if $volid; };
90 PVE
::JSONSchema
::register_standard_option
('pve-lxc-snapshot-name', {
91 description
=> "The name of the snapshot.",
92 type
=> 'string', format
=> 'pve-configid',
96 __PACKAGE__-
>register_method({
100 description
=> "LXC container index (per node).",
102 description
=> "Only list CTs where you have VM.Audit permissons on /vms/<vmid>.",
106 protected
=> 1, # /proc files are only readable by root
108 additionalProperties
=> 0,
110 node
=> get_standard_option
('pve-node'),
119 links
=> [ { rel
=> 'child', href
=> "{vmid}" } ],
124 my $rpcenv = PVE
::RPCEnvironment
::get
();
125 my $authuser = $rpcenv->get_user();
127 my $vmstatus = PVE
::LXC
::vmstatus
();
130 foreach my $vmid (keys %$vmstatus) {
131 next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1);
133 my $data = $vmstatus->{$vmid};
134 $data->{vmid
} = $vmid;
142 __PACKAGE__-
>register_method({
146 description
=> "Create or restore a container.",
148 user
=> 'all', # check inside
149 description
=> "You need 'VM.Allocate' permissions on /vms/{vmid} or on the VM pool /pool/{pool}. " .
150 "For restore, it is enough if the user has 'VM.Backup' permission and the VM already exists. " .
151 "You also need 'Datastore.AllocateSpace' permissions on the storage.",
156 additionalProperties
=> 0,
157 properties
=> PVE
::LXC
::json_config_properties_no_rootfs
({
158 node
=> get_standard_option
('pve-node'),
159 vmid
=> get_standard_option
('pve-vmid'),
161 description
=> "The OS template or backup file.",
168 description
=> "Sets root password inside container.",
171 storage
=> get_standard_option
('pve-storage-id', {
172 description
=> "Target storage.",
179 description
=> "Amount of disk space for the VM in GB. A zero indicates no limits.",
186 description
=> "Allow to overwrite existing container.",
191 description
=> "Mark this as restore task.",
195 type
=> 'string', format
=> 'pve-poolid',
196 description
=> "Add the VM to the specified pool.",
206 my $rpcenv = PVE
::RPCEnvironment
::get
();
208 my $authuser = $rpcenv->get_user();
210 my $node = extract_param
($param, 'node');
212 my $vmid = extract_param
($param, 'vmid');
214 my $basecfg_fn = PVE
::LXC
::config_file
($vmid);
216 my $same_container_exists = -f
$basecfg_fn;
218 my $restore = extract_param
($param, 'restore');
221 # fixme: limit allowed parameters
225 my $force = extract_param
($param, 'force');
227 if (!($same_container_exists && $restore && $force)) {
228 PVE
::Cluster
::check_vmid_unused
($vmid);
231 my $password = extract_param
($param, 'password');
233 my $disksize = extract_param
($param, 'size');
235 my $storage = extract_param
($param, 'storage') // 'local';
237 my $storage_cfg = cfs_read_file
("storage.cfg");
239 my $scfg = PVE
::Storage
::storage_check_node
($storage_cfg, $storage, $node);
241 raise_param_exc
({ storage
=> "storage '$storage' does not support container root directories"})
242 if !($scfg->{content
}->{images
} || $scfg->{content
}->{rootdir
});
244 my $pool = extract_param
($param, 'pool');
246 if (defined($pool)) {
247 $rpcenv->check_pool_exist($pool);
248 $rpcenv->check_perm_modify($authuser, "/pool/$pool");
251 $rpcenv->check($authuser, "/storage/$storage", ['Datastore.AllocateSpace']);
253 if ($rpcenv->check($authuser, "/vms/$vmid", ['VM.Allocate'], 1)) {
255 } elsif ($pool && $rpcenv->check($authuser, "/pool/$pool", ['VM.Allocate'], 1)) {
257 } elsif ($restore && $force && $same_container_exists &&
258 $rpcenv->check($authuser, "/vms/$vmid", ['VM.Backup'], 1)) {
259 # OK: user has VM.Backup permissions, and want to restore an existing VM
264 &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, $pool, [ keys %$param]);
266 PVE
::Storage
::activate_storage
($storage_cfg, $storage);
268 my $ostemplate = extract_param
($param, 'ostemplate');
272 if ($ostemplate eq '-') {
273 die "pipe requires cli environment\n"
274 if $rpcenv->{type
} ne 'cli';
275 die "pipe can only be used with restore tasks\n"
277 die "pipe requires --size parameter\n"
278 if !defined($disksize);
281 $rpcenv->check_volume_access($authuser, $storage_cfg, $vmid, $ostemplate);
282 $archive = PVE
::Storage
::abs_filesystem_path
($storage_cfg, $ostemplate);
287 PVE
::LXC
::update_pct_config
($vmid, $conf, 0, $param);
289 my $check_vmid_usage = sub {
291 die "cant overwrite running container\n"
292 if PVE
::LXC
::check_running
($vmid);
294 PVE
::Cluster
::check_vmid_unused
($vmid);
299 &$check_vmid_usage(); # final check after locking
301 PVE
::Cluster
::check_cfs_quorum
();
306 if (!defined($disksize)) {
308 (undef, $disksize) = PVE
::LXCCreate
::recover_config
($archive);
309 die "unable to detect disk size - please specify with --size\n"
315 $volid = &$alloc_rootfs($storage_cfg, $storage, $disksize, $vmid);
317 PVE
::LXCCreate
::create_rootfs
($storage_cfg, $storage, $volid, $vmid, $conf,
318 $archive, $password, $restore);
320 $conf->{rootfs
} = "$volid,size=$disksize";
323 $conf->{hostname
} ||= "CT$vmid";
324 $conf->{memory
} ||= 512;
325 $conf->{swap
} //= 512;
327 PVE
::LXC
::create_config
($vmid, $conf);
330 eval { PVE
::Storage
::vdisk_free
($storage_cfg, $volid) if $volid; };
332 PVE
::LXC
::destroy_config
($vmid);
337 my $realcmd = sub { PVE
::LXC
::lock_container
($vmid, 1, $code); };
339 &$check_vmid_usage(); # first check before locking
341 return $rpcenv->fork_worker($restore ?
'vzrestore' : 'vzcreate',
342 $vmid, $authuser, $realcmd);
346 my $vm_config_perm_list = [
354 __PACKAGE__-
>register_method({
356 path
=> '{vmid}/config',
360 description
=> "Set container options.",
362 check
=> ['perm', '/vms/{vmid}', $vm_config_perm_list, any
=> 1],
365 additionalProperties
=> 0,
366 properties
=> PVE
::LXC
::json_config_properties
(
368 node
=> get_standard_option
('pve-node'),
369 vmid
=> get_standard_option
('pve-vmid'),
371 type
=> 'string', format
=> 'pve-configid-list',
372 description
=> "A list of settings you want to delete.",
377 description
=> 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.',
383 returns
=> { type
=> 'null'},
387 my $rpcenv = PVE
::RPCEnvironment
::get
();
389 my $authuser = $rpcenv->get_user();
391 my $node = extract_param
($param, 'node');
393 my $vmid = extract_param
($param, 'vmid');
395 my $digest = extract_param
($param, 'digest');
397 die "no options specified\n" if !scalar(keys %$param);
399 my $delete_str = extract_param
($param, 'delete');
400 my @delete = PVE
::Tools
::split_list
($delete_str);
402 &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, [@delete]);
404 foreach my $opt (@delete) {
405 raise_param_exc
({ delete => "you can't use '-$opt' and " .
406 "-delete $opt' at the same time" })
407 if defined($param->{$opt});
409 if (!PVE
::LXC
::option_exists
($opt)) {
410 raise_param_exc
({ delete => "unknown option '$opt'" });
414 &$check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, [keys %$param]);
416 my $storage_cfg = cfs_read_file
("storage.cfg");
420 my $conf = PVE
::LXC
::load_config
($vmid);
421 PVE
::LXC
::check_lock
($conf);
423 PVE
::Tools
::assert_if_modified
($digest, $conf->{digest
});
425 my $running = PVE
::LXC
::check_running
($vmid);
427 PVE
::LXC
::update_pct_config
($vmid, $conf, $running, $param, \
@delete);
429 PVE
::LXC
::write_config
($vmid, $conf);
430 PVE
::LXC
::update_lxc_config
($storage_cfg, $vmid, $conf);
433 PVE
::LXC
::lock_container
($vmid, undef, $code);
438 __PACKAGE__-
>register_method ({
439 subclass
=> "PVE::API2::Firewall::CT",
440 path
=> '{vmid}/firewall',
443 __PACKAGE__-
>register_method({
448 description
=> "Directory index",
453 additionalProperties
=> 0,
455 node
=> get_standard_option
('pve-node'),
456 vmid
=> get_standard_option
('pve-vmid'),
464 subdir
=> { type
=> 'string' },
467 links
=> [ { rel
=> 'child', href
=> "{subdir}" } ],
473 my $conf = PVE
::LXC
::load_config
($param->{vmid
});
476 { subdir
=> 'config' },
477 { subdir
=> 'status' },
478 { subdir
=> 'vncproxy' },
479 { subdir
=> 'vncwebsocket' },
480 { subdir
=> 'spiceproxy' },
481 { subdir
=> 'migrate' },
482 # { subdir => 'initlog' },
484 { subdir
=> 'rrddata' },
485 { subdir
=> 'firewall' },
486 { subdir
=> 'snapshot' },
492 __PACKAGE__-
>register_method({
494 path
=> '{vmid}/rrd',
496 protected
=> 1, # fixme: can we avoid that?
498 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
500 description
=> "Read VM RRD statistics (returns PNG)",
502 additionalProperties
=> 0,
504 node
=> get_standard_option
('pve-node'),
505 vmid
=> get_standard_option
('pve-vmid'),
507 description
=> "Specify the time frame you are interested in.",
509 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
512 description
=> "The list of datasources you want to display.",
513 type
=> 'string', format
=> 'pve-configid-list',
516 description
=> "The RRD consolidation function",
518 enum
=> [ 'AVERAGE', 'MAX' ],
526 filename
=> { type
=> 'string' },
532 return PVE
::Cluster
::create_rrd_graph
(
533 "pve2-vm/$param->{vmid}", $param->{timeframe
},
534 $param->{ds
}, $param->{cf
});
538 __PACKAGE__-
>register_method({
540 path
=> '{vmid}/rrddata',
542 protected
=> 1, # fixme: can we avoid that?
544 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
546 description
=> "Read VM RRD statistics",
548 additionalProperties
=> 0,
550 node
=> get_standard_option
('pve-node'),
551 vmid
=> get_standard_option
('pve-vmid'),
553 description
=> "Specify the time frame you are interested in.",
555 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
558 description
=> "The RRD consolidation function",
560 enum
=> [ 'AVERAGE', 'MAX' ],
575 return PVE
::Cluster
::create_rrd_data
(
576 "pve2-vm/$param->{vmid}", $param->{timeframe
}, $param->{cf
});
580 __PACKAGE__-
>register_method({
582 path
=> '{vmid}/config',
585 description
=> "Get container configuration.",
587 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
590 additionalProperties
=> 0,
592 node
=> get_standard_option
('pve-node'),
593 vmid
=> get_standard_option
('pve-vmid'),
601 description
=> 'SHA1 digest of configuration file. This can be used to prevent concurrent modifications.',
608 my $conf = PVE
::LXC
::load_config
($param->{vmid
});
610 delete $conf->{snapshots
};
616 __PACKAGE__-
>register_method({
617 name
=> 'destroy_vm',
622 description
=> "Destroy the container (also delete all uses files).",
624 check
=> [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
627 additionalProperties
=> 0,
629 node
=> get_standard_option
('pve-node'),
630 vmid
=> get_standard_option
('pve-vmid'),
639 my $rpcenv = PVE
::RPCEnvironment
::get
();
641 my $authuser = $rpcenv->get_user();
643 my $vmid = $param->{vmid
};
645 # test if container exists
646 my $conf = PVE
::LXC
::load_config
($vmid);
648 my $storage_cfg = cfs_read_file
("storage.cfg");
651 # reload config after lock
652 $conf = PVE
::LXC
::load_config
($vmid);
653 PVE
::LXC
::check_lock
($conf);
655 PVE
::LXC
::destroy_lxc_container
($storage_cfg, $vmid, $conf);
656 PVE
::AccessControl
::remove_vm_from_pool
($vmid);
659 my $realcmd = sub { PVE
::LXC
::lock_container
($vmid, 1, $code); };
661 return $rpcenv->fork_worker('vzdestroy', $vmid, $authuser, $realcmd);
666 __PACKAGE__-
>register_method ({
668 path
=> '{vmid}/vncproxy',
672 check
=> ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
674 description
=> "Creates a TCP VNC proxy connections.",
676 additionalProperties
=> 0,
678 node
=> get_standard_option
('pve-node'),
679 vmid
=> get_standard_option
('pve-vmid'),
683 description
=> "use websocket instead of standard VNC.",
688 additionalProperties
=> 0,
690 user
=> { type
=> 'string' },
691 ticket
=> { type
=> 'string' },
692 cert
=> { type
=> 'string' },
693 port
=> { type
=> 'integer' },
694 upid
=> { type
=> 'string' },
700 my $rpcenv = PVE
::RPCEnvironment
::get
();
702 my $authuser = $rpcenv->get_user();
704 my $vmid = $param->{vmid
};
705 my $node = $param->{node
};
707 my $authpath = "/vms/$vmid";
709 my $ticket = PVE
::AccessControl
::assemble_vnc_ticket
($authuser, $authpath);
711 $sslcert = PVE
::Tools
::file_get_contents
("/etc/pve/pve-root-ca.pem", 8192)
714 my ($remip, $family);
716 if ($node ne PVE
::INotify
::nodename
()) {
717 ($remip, $family) = PVE
::Cluster
::remote_node_ip
($node);
719 $family = PVE
::Tools
::get_host_address_family
($node);
722 my $port = PVE
::Tools
::next_vnc_port
($family);
724 # NOTE: vncterm VNC traffic is already TLS encrypted,
725 # so we select the fastest chipher here (or 'none'?)
726 my $remcmd = $remip ?
727 ['/usr/bin/ssh', '-t', $remip] : [];
729 my $shcmd = [ '/usr/bin/dtach', '-A',
730 "/var/run/dtach/vzctlconsole$vmid",
732 'lxc-console', '-n', $vmid ];
737 syslog
('info', "starting lxc vnc proxy $upid\n");
741 my $cmd = ['/usr/bin/vncterm', '-rfbport', $port,
742 '-timeout', $timeout, '-authpath', $authpath,
743 '-perm', 'VM.Console'];
745 if ($param->{websocket
}) {
746 $ENV{PVE_VNC_TICKET
} = $ticket; # pass ticket to vncterm
747 push @$cmd, '-notls', '-listen', 'localhost';
750 push @$cmd, '-c', @$remcmd, @$shcmd;
757 my $upid = $rpcenv->fork_worker('vncproxy', $vmid, $authuser, $realcmd);
759 PVE
::Tools
::wait_for_vnc_port
($port);
770 __PACKAGE__-
>register_method({
771 name
=> 'vncwebsocket',
772 path
=> '{vmid}/vncwebsocket',
775 description
=> "You also need to pass a valid ticket (vncticket).",
776 check
=> ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
778 description
=> "Opens a weksocket for VNC traffic.",
780 additionalProperties
=> 0,
782 node
=> get_standard_option
('pve-node'),
783 vmid
=> get_standard_option
('pve-vmid'),
785 description
=> "Ticket from previous call to vncproxy.",
790 description
=> "Port number returned by previous vncproxy call.",
800 port
=> { type
=> 'string' },
806 my $rpcenv = PVE
::RPCEnvironment
::get
();
808 my $authuser = $rpcenv->get_user();
810 my $authpath = "/vms/$param->{vmid}";
812 PVE
::AccessControl
::verify_vnc_ticket
($param->{vncticket
}, $authuser, $authpath);
814 my $port = $param->{port
};
816 return { port
=> $port };
819 __PACKAGE__-
>register_method ({
820 name
=> 'spiceproxy',
821 path
=> '{vmid}/spiceproxy',
826 check
=> ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
828 description
=> "Returns a SPICE configuration to connect to the CT.",
830 additionalProperties
=> 0,
832 node
=> get_standard_option
('pve-node'),
833 vmid
=> get_standard_option
('pve-vmid'),
834 proxy
=> get_standard_option
('spice-proxy', { optional
=> 1 }),
837 returns
=> get_standard_option
('remote-viewer-config'),
841 my $vmid = $param->{vmid
};
842 my $node = $param->{node
};
843 my $proxy = $param->{proxy
};
845 my $authpath = "/vms/$vmid";
846 my $permissions = 'VM.Console';
848 my $shcmd = ['/usr/bin/dtach', '-A',
849 "/var/run/dtach/vzctlconsole$vmid",
851 'lxc-console', '-n', $vmid];
853 my $title = "CT $vmid";
855 return PVE
::API2Tools
::run_spiceterm
($authpath, $permissions, $vmid, $node, $proxy, $title, $shcmd);
858 __PACKAGE__-
>register_method({
860 path
=> '{vmid}/status',
863 description
=> "Directory index",
868 additionalProperties
=> 0,
870 node
=> get_standard_option
('pve-node'),
871 vmid
=> get_standard_option
('pve-vmid'),
879 subdir
=> { type
=> 'string' },
882 links
=> [ { rel
=> 'child', href
=> "{subdir}" } ],
888 my $conf = PVE
::LXC
::load_config
($param->{vmid
});
891 { subdir
=> 'current' },
892 { subdir
=> 'start' },
893 { subdir
=> 'stop' },
894 { subdir
=> 'shutdown' },
895 { subdir
=> 'migrate' },
901 __PACKAGE__-
>register_method({
903 path
=> '{vmid}/status/current',
906 protected
=> 1, # openvz /proc entries are only readable by root
907 description
=> "Get virtual machine status.",
909 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
912 additionalProperties
=> 0,
914 node
=> get_standard_option
('pve-node'),
915 vmid
=> get_standard_option
('pve-vmid'),
918 returns
=> { type
=> 'object' },
923 my $conf = PVE
::LXC
::load_config
($param->{vmid
});
925 my $vmstatus = PVE
::LXC
::vmstatus
($param->{vmid
});
926 my $status = $vmstatus->{$param->{vmid
}};
928 $status->{ha
} = PVE
::HA
::Config
::vm_is_ha_managed
($param->{vmid
}) ?
1 : 0;
933 __PACKAGE__-
>register_method({
935 path
=> '{vmid}/status/start',
939 description
=> "Start the container.",
941 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
944 additionalProperties
=> 0,
946 node
=> get_standard_option
('pve-node'),
947 vmid
=> get_standard_option
('pve-vmid'),
956 my $rpcenv = PVE
::RPCEnvironment
::get
();
958 my $authuser = $rpcenv->get_user();
960 my $node = extract_param
($param, 'node');
962 my $vmid = extract_param
($param, 'vmid');
964 die "CT $vmid already running\n" if PVE
::LXC
::check_running
($vmid);
966 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid) && $rpcenv->{type
} ne 'ha') {
971 my $service = "ct:$vmid";
973 my $cmd = ['ha-manager', 'enable', $service];
975 print "Executing HA start for CT $vmid\n";
977 PVE
::Tools
::run_command
($cmd);
982 return $rpcenv->fork_worker('hastart', $vmid, $authuser, $hacmd);
989 syslog
('info', "starting CT $vmid: $upid\n");
991 my $conf = PVE
::LXC
::load_config
($vmid);
992 my $storage_cfg = cfs_read_file
("storage.cfg");
994 my $rootinfo = PVE
::LXC
::parse_ct_mountpoint
($conf->{rootfs
});
995 PVE
::Storage
::activate_volumes
($storage_cfg, [$rootinfo->{volume
}]);
997 PVE
::LXC
::update_lxc_config
($storage_cfg, $vmid, $conf);
999 my $cmd = ['lxc-start', '-n', $vmid];
1006 return $rpcenv->fork_worker('vzstart', $vmid, $authuser, $realcmd);
1010 __PACKAGE__-
>register_method({
1012 path
=> '{vmid}/status/stop',
1016 description
=> "Stop the container.",
1018 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1021 additionalProperties
=> 0,
1023 node
=> get_standard_option
('pve-node'),
1024 vmid
=> get_standard_option
('pve-vmid'),
1033 my $rpcenv = PVE
::RPCEnvironment
::get
();
1035 my $authuser = $rpcenv->get_user();
1037 my $node = extract_param
($param, 'node');
1039 my $vmid = extract_param
($param, 'vmid');
1041 die "CT $vmid not running\n" if !PVE
::LXC
::check_running
($vmid);
1043 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid) && $rpcenv->{type
} ne 'ha') {
1048 my $service = "ct:$vmid";
1050 my $cmd = ['ha-manager', 'disable', $service];
1052 print "Executing HA stop for CT $vmid\n";
1054 PVE
::Tools
::run_command
($cmd);
1059 return $rpcenv->fork_worker('hastop', $vmid, $authuser, $hacmd);
1066 syslog
('info', "stoping CT $vmid: $upid\n");
1068 my $conf = PVE
::LXC
::load_config
($vmid);
1070 my $storage_cfg = PVE
::Storage
::config
();
1072 my $cmd = ['lxc-stop', '-n', $vmid, '--kill'];
1076 PVE
::LXC
::vm_stop_cleanup
($storage_cfg, $vmid, $conf);
1081 return $rpcenv->fork_worker('vzstop', $vmid, $authuser, $realcmd);
1085 __PACKAGE__-
>register_method({
1086 name
=> 'vm_shutdown',
1087 path
=> '{vmid}/status/shutdown',
1091 description
=> "Shutdown the container.",
1093 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1096 additionalProperties
=> 0,
1098 node
=> get_standard_option
('pve-node'),
1099 vmid
=> get_standard_option
('pve-vmid'),
1101 description
=> "Wait maximal timeout seconds.",
1108 description
=> "Make sure the Container stops.",
1121 my $rpcenv = PVE
::RPCEnvironment
::get
();
1123 my $authuser = $rpcenv->get_user();
1125 my $node = extract_param
($param, 'node');
1127 my $vmid = extract_param
($param, 'vmid');
1129 my $timeout = extract_param
($param, 'timeout');
1131 die "CT $vmid not running\n" if !PVE
::LXC
::check_running
($vmid);
1136 syslog
('info', "shutdown CT $vmid: $upid\n");
1138 my $cmd = ['lxc-stop', '-n', $vmid];
1140 $timeout = 60 if !defined($timeout);
1142 my $conf = PVE
::LXC
::load_config
($vmid);
1144 my $storage_cfg = PVE
::Storage
::config
();
1146 push @$cmd, '--timeout', $timeout;
1148 eval { run_command
($cmd, timeout
=> $timeout+5); };
1150 if ($err && $param->{forceStop
}) {
1152 warn "shutdown failed - forcing stop now\n";
1154 push @$cmd, '--kill';
1159 PVE
::LXC
::vm_stop_cleanup
($storage_cfg, $vmid, $conf);
1166 my $upid = $rpcenv->fork_worker('vzshutdown', $vmid, $authuser, $realcmd);
1171 __PACKAGE__-
>register_method({
1172 name
=> 'vm_suspend',
1173 path
=> '{vmid}/status/suspend',
1177 description
=> "Suspend 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 not running\n" if !PVE
::LXC
::check_running
($vmid);
1207 syslog
('info', "suspend CT $vmid: $upid\n");
1209 my $cmd = ['lxc-checkpoint', '-n', $vmid, '-s', '-D', '/var/liv/vz/dump'];
1216 my $upid = $rpcenv->fork_worker('vzsuspend', $vmid, $authuser, $realcmd);
1221 __PACKAGE__-
>register_method({
1222 name
=> 'vm_resume',
1223 path
=> '{vmid}/status/resume',
1227 description
=> "Resume the container.",
1229 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1232 additionalProperties
=> 0,
1234 node
=> get_standard_option
('pve-node'),
1235 vmid
=> get_standard_option
('pve-vmid'),
1244 my $rpcenv = PVE
::RPCEnvironment
::get
();
1246 my $authuser = $rpcenv->get_user();
1248 my $node = extract_param
($param, 'node');
1250 my $vmid = extract_param
($param, 'vmid');
1252 die "CT $vmid already running\n" if PVE
::LXC
::check_running
($vmid);
1257 syslog
('info', "resume CT $vmid: $upid\n");
1259 my $cmd = ['lxc-checkpoint', '-n', $vmid, '-r', '--foreground',
1260 '-D', '/var/liv/vz/dump'];
1267 my $upid = $rpcenv->fork_worker('vzresume', $vmid, $authuser, $realcmd);
1272 __PACKAGE__-
>register_method({
1273 name
=> 'migrate_vm',
1274 path
=> '{vmid}/migrate',
1278 description
=> "Migrate the container to another node. Creates a new migration task.",
1280 check
=> ['perm', '/vms/{vmid}', [ 'VM.Migrate' ]],
1283 additionalProperties
=> 0,
1285 node
=> get_standard_option
('pve-node'),
1286 vmid
=> get_standard_option
('pve-vmid'),
1287 target
=> get_standard_option
('pve-node', { description
=> "Target node." }),
1290 description
=> "Use online/live migration.",
1297 description
=> "the task ID.",
1302 my $rpcenv = PVE
::RPCEnvironment
::get
();
1304 my $authuser = $rpcenv->get_user();
1306 my $target = extract_param
($param, 'target');
1308 my $localnode = PVE
::INotify
::nodename
();
1309 raise_param_exc
({ target
=> "target is local node."}) if $target eq $localnode;
1311 PVE
::Cluster
::check_cfs_quorum
();
1313 PVE
::Cluster
::check_node_exists
($target);
1315 my $targetip = PVE
::Cluster
::remote_node_ip
($target);
1317 my $vmid = extract_param
($param, 'vmid');
1320 PVE
::LXC
::load_config
($vmid);
1322 # try to detect errors early
1323 if (PVE
::LXC
::check_running
($vmid)) {
1324 die "cant migrate running container without --online\n"
1325 if !$param->{online
};
1328 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid) && $rpcenv->{type
} ne 'ha') {
1333 my $service = "ct:$vmid";
1335 my $cmd = ['ha-manager', 'migrate', $service, $target];
1337 print "Executing HA migrate for CT $vmid to node $target\n";
1339 PVE
::Tools
::run_command
($cmd);
1344 return $rpcenv->fork_worker('hamigrate', $vmid, $authuser, $hacmd);
1351 # fixme: implement lxc container migration
1352 die "lxc container migration not implemented\n";
1357 return $rpcenv->fork_worker('vzmigrate', $vmid, $authuser, $realcmd);
1361 __PACKAGE__-
>register_method({
1363 path
=> '{vmid}/snapshot',
1367 description
=> "Snapshot a container.",
1369 check
=> ['perm', '/vms/{vmid}', [ 'VM.Snapshot' ]],
1372 additionalProperties
=> 0,
1374 node
=> get_standard_option
('pve-node'),
1375 vmid
=> get_standard_option
('pve-vmid'),
1376 snapname
=> get_standard_option
('pve-lxc-snapshot-name'),
1379 # type => 'boolean',
1380 # description => "Save the vmstate",
1385 description
=> "A textual description or comment.",
1391 description
=> "the task ID.",
1396 my $rpcenv = PVE
::RPCEnvironment
::get
();
1398 my $authuser = $rpcenv->get_user();
1400 my $node = extract_param
($param, 'node');
1402 my $vmid = extract_param
($param, 'vmid');
1404 my $snapname = extract_param
($param, 'snapname');
1406 die "unable to use snapshot name 'current' (reserved name)\n"
1407 if $snapname eq 'current';
1410 PVE
::Cluster
::log_msg
('info', $authuser, "snapshot container $vmid: $snapname");
1411 PVE
::LXC
::snapshot_create
($vmid, $snapname, $param->{description
});
1414 return $rpcenv->fork_worker('pctsnapshot', $vmid, $authuser, $realcmd);
1417 __PACKAGE__-
>register_method({
1418 name
=> 'delsnapshot',
1419 path
=> '{vmid}/snapshot/{snapname}',
1423 description
=> "Delete a LXC snapshot.",
1425 check
=> ['perm', '/vms/{vmid}', [ 'VM.Snapshot' ]],
1428 additionalProperties
=> 0,
1430 node
=> get_standard_option
('pve-node'),
1431 vmid
=> get_standard_option
('pve-vmid'),
1432 snapname
=> get_standard_option
('pve-lxc-snapshot-name'),
1436 description
=> "For removal from config file, even if removing disk snapshots fails.",
1442 description
=> "the task ID.",
1447 my $rpcenv = PVE
::RPCEnvironment
::get
();
1449 my $authuser = $rpcenv->get_user();
1451 my $node = extract_param
($param, 'node');
1453 my $vmid = extract_param
($param, 'vmid');
1455 my $snapname = extract_param
($param, 'snapname');
1458 PVE
::Cluster
::log_msg
('info', $authuser, "delete snapshot VM $vmid: $snapname");
1459 PVE
::LXC
::snapshot_delete
($vmid, $snapname, $param->{force
});
1462 return $rpcenv->fork_worker('lxcdelsnapshot', $vmid, $authuser, $realcmd);
1465 __PACKAGE__-
>register_method({
1467 path
=> '{vmid}/snapshot/{snapname}/rollback',
1471 description
=> "Rollback LXC state to specified snapshot.",
1473 check
=> ['perm', '/vms/{vmid}', [ 'VM.Snapshot' ]],
1476 additionalProperties
=> 0,
1478 node
=> get_standard_option
('pve-node'),
1479 vmid
=> get_standard_option
('pve-vmid'),
1480 snapname
=> get_standard_option
('pve-lxc-snapshot-name'),
1485 description
=> "the task ID.",
1490 my $rpcenv = PVE
::RPCEnvironment
::get
();
1492 my $authuser = $rpcenv->get_user();
1494 my $node = extract_param
($param, 'node');
1496 my $vmid = extract_param
($param, 'vmid');
1498 my $snapname = extract_param
($param, 'snapname');
1501 PVE
::Cluster
::log_msg
('info', $authuser, "rollback snapshot LXC $vmid: $snapname");
1502 PVE
::LXC
::snapshot_rollback
($vmid, $snapname);
1505 return $rpcenv->fork_worker('lxcrollback', $vmid, $authuser, $realcmd);
1508 __PACKAGE__-
>register_method({
1509 name
=> 'snapshot_list',
1510 path
=> '{vmid}/snapshot',
1512 description
=> "List all snapshots.",
1514 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
1517 protected
=> 1, # lxc pid files are only readable by root
1519 additionalProperties
=> 0,
1521 vmid
=> get_standard_option
('pve-vmid'),
1522 node
=> get_standard_option
('pve-node'),
1531 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
1536 my $vmid = $param->{vmid
};
1538 my $conf = PVE
::LXC
::load_config
($vmid);
1539 my $snaphash = $conf->{snapshots
} || {};
1543 foreach my $name (keys %$snaphash) {
1544 my $d = $snaphash->{$name};
1547 snaptime
=> $d->{snaptime
} || 0,
1548 description
=> $d->{description
} || '',
1550 $item->{parent
} = $d->{parent
} if defined($d->{parent
});
1551 $item->{snapstate
} = $d->{snapstate
} if $d->{snapstate
};
1555 my $running = PVE
::LXC
::check_running
($vmid) ?
1 : 0;
1556 my $current = { name
=> 'current', digest
=> $conf->{digest
}, running
=> $running };
1557 $current->{parent
} = $conf->{parent
} if defined($conf->{parent
});
1559 push @$res, $current;
1564 __PACKAGE__-
>register_method({
1565 name
=> 'snapshot_cmd_idx',
1566 path
=> '{vmid}/snapshot/{snapname}',
1573 additionalProperties
=> 0,
1575 vmid
=> get_standard_option
('pve-vmid'),
1576 node
=> get_standard_option
('pve-node'),
1577 snapname
=> get_standard_option
('pve-lxc-snapshot-name'),
1586 links
=> [ { rel
=> 'child', href
=> "{cmd}" } ],
1593 push @$res, { cmd
=> 'rollback' };
1594 push @$res, { cmd
=> 'config' };
1599 __PACKAGE__-
>register_method({
1600 name
=> 'update_snapshot_config',
1601 path
=> '{vmid}/snapshot/{snapname}/config',
1605 description
=> "Update snapshot metadata.",
1607 check
=> ['perm', '/vms/{vmid}', [ 'VM.Snapshot' ]],
1610 additionalProperties
=> 0,
1612 node
=> get_standard_option
('pve-node'),
1613 vmid
=> get_standard_option
('pve-vmid'),
1614 snapname
=> get_standard_option
('pve-lxc-snapshot-name'),
1618 description
=> "A textual description or comment.",
1622 returns
=> { type
=> 'null' },
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 return undef if !defined($param->{description
});
1636 my $updatefn = sub {
1638 my $conf = PVE
::LXC
::load_config
($vmid);
1639 PVE
::LXC
::check_lock
($conf);
1641 my $snap = $conf->{snapshots
}->{$snapname};
1643 die "snapshot '$snapname' does not exist\n" if !defined($snap);
1645 $snap->{description
} = $param->{description
} if defined($param->{description
});
1647 PVE
::LXC
::write_config
($vmid, $conf, 1);
1650 PVE
::LXC
::lock_container
($vmid, 10, $updatefn);
1655 __PACKAGE__-
>register_method({
1656 name
=> 'get_snapshot_config',
1657 path
=> '{vmid}/snapshot/{snapname}/config',
1660 description
=> "Get snapshot configuration",
1662 check
=> ['perm', '/vms/{vmid}', [ 'VM.Snapshot' ]],
1665 additionalProperties
=> 0,
1667 node
=> get_standard_option
('pve-node'),
1668 vmid
=> get_standard_option
('pve-vmid'),
1669 snapname
=> get_standard_option
('pve-lxc-snapshot-name'),
1672 returns
=> { type
=> "object" },
1676 my $rpcenv = PVE
::RPCEnvironment
::get
();
1678 my $authuser = $rpcenv->get_user();
1680 my $vmid = extract_param
($param, 'vmid');
1682 my $snapname = extract_param
($param, 'snapname');
1684 my $conf = PVE
::LXC
::load_config
($vmid);
1686 my $snap = $conf->{snapshots
}->{$snapname};
1688 die "snapshot '$snapname' does not exist\n" if !defined($snap);
1690 delete $snap->{lxc
};
1695 __PACKAGE__-
>register_method({
1696 name
=> 'vm_feature',
1697 path
=> '{vmid}/feature',
1701 description
=> "Check if feature for virtual machine is available.",
1703 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
1706 additionalProperties
=> 0,
1708 node
=> get_standard_option
('pve-node'),
1709 vmid
=> get_standard_option
('pve-vmid'),
1711 description
=> "Feature to check.",
1713 enum
=> [ 'snapshot' ],
1715 snapname
=> get_standard_option
('pve-lxc-snapshot-name', {
1723 hasFeature
=> { type
=> 'boolean' },
1726 #items => { type => 'string' },
1733 my $node = extract_param
($param, 'node');
1735 my $vmid = extract_param
($param, 'vmid');
1737 my $snapname = extract_param
($param, 'snapname');
1739 my $feature = extract_param
($param, 'feature');
1741 my $conf = PVE
::LXC
::load_config
($vmid);
1744 my $snap = $conf->{snapshots
}->{$snapname};
1745 die "snapshot '$snapname' does not exist\n" if !defined($snap);
1748 my $storage_cfg = PVE
::Storage
::config
();
1749 #Maybe include later
1750 #my $nodelist = PVE::LXC::shared_nodes($conf, $storage_cfg);
1751 my $hasFeature = PVE
::LXC
::has_feature
($feature, $conf, $storage_cfg, $snapname);
1754 hasFeature
=> $hasFeature,
1755 #nodes => [ keys %$nodelist ],