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
;
15 use PVE
::RPCEnvironment
;
16 use PVE
::ReplicationConfig
;
19 use PVE
::LXC
::Migrate
;
20 use PVE
::GuestHelpers
;
21 use PVE
::API2
::LXC
::Config
;
22 use PVE
::API2
::LXC
::Status
;
23 use PVE
::API2
::LXC
::Snapshot
;
24 use PVE
::JSONSchema
qw(get_standard_option);
25 use base
qw(PVE::RESTHandler);
28 if (!$ENV{PVE_GENERATING_DOCS
}) {
29 require PVE
::HA
::Env
::PVE2
;
30 import PVE
::HA
::Env
::PVE2
;
31 require PVE
::HA
::Config
;
32 import PVE
::HA
::Config
;
36 __PACKAGE__-
>register_method ({
37 subclass
=> "PVE::API2::LXC::Config",
38 path
=> '{vmid}/config',
41 __PACKAGE__-
>register_method ({
42 subclass
=> "PVE::API2::LXC::Status",
43 path
=> '{vmid}/status',
46 __PACKAGE__-
>register_method ({
47 subclass
=> "PVE::API2::LXC::Snapshot",
48 path
=> '{vmid}/snapshot',
51 __PACKAGE__-
>register_method ({
52 subclass
=> "PVE::API2::Firewall::CT",
53 path
=> '{vmid}/firewall',
56 __PACKAGE__-
>register_method({
60 description
=> "LXC container index (per node).",
62 description
=> "Only list CTs where you have VM.Audit permissons on /vms/<vmid>.",
66 protected
=> 1, # /proc files are only readable by root
68 additionalProperties
=> 0,
70 node
=> get_standard_option
('pve-node'),
79 links
=> [ { rel
=> 'child', href
=> "{vmid}" } ],
84 my $rpcenv = PVE
::RPCEnvironment
::get
();
85 my $authuser = $rpcenv->get_user();
87 my $vmstatus = PVE
::LXC
::vmstatus
();
90 foreach my $vmid (keys %$vmstatus) {
91 next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1);
93 my $data = $vmstatus->{$vmid};
94 $data->{vmid
} = $vmid;
102 __PACKAGE__-
>register_method({
106 description
=> "Create or restore a container.",
108 user
=> 'all', # check inside
109 description
=> "You need 'VM.Allocate' permissions on /vms/{vmid} or on the VM pool /pool/{pool}. " .
110 "For restore, it is enough if the user has 'VM.Backup' permission and the VM already exists. " .
111 "You also need 'Datastore.AllocateSpace' permissions on the storage.",
116 additionalProperties
=> 0,
117 properties
=> PVE
::LXC
::Config-
>json_config_properties({
118 node
=> get_standard_option
('pve-node'),
119 vmid
=> get_standard_option
('pve-vmid', { completion
=> \
&PVE
::Cluster
::complete_next_vmid
}),
121 description
=> "The OS template or backup file.",
124 completion
=> \
&PVE
::LXC
::complete_os_templates
,
129 description
=> "Sets root password inside container.",
132 storage
=> get_standard_option
('pve-storage-id', {
133 description
=> "Default Storage.",
136 completion
=> \
&PVE
::Storage
::complete_storage_enabled
,
141 description
=> "Allow to overwrite existing container.",
146 description
=> "Mark this as restore task.",
150 type
=> 'string', format
=> 'pve-poolid',
151 description
=> "Add the VM to the specified pool.",
153 'ignore-unpack-errors' => {
156 description
=> "Ignore errors when extracting the template.",
158 'ssh-public-keys' => {
161 description
=> "Setup public SSH keys (one key per line, " .
165 description
=> "Override i/o bandwidth limit (in KiB/s).",
174 description
=> "Start the CT after its creation finished successfully.",
184 my $rpcenv = PVE
::RPCEnvironment
::get
();
186 my $authuser = $rpcenv->get_user();
188 my $node = extract_param
($param, 'node');
190 my $vmid = extract_param
($param, 'vmid');
192 my $ignore_unpack_errors = extract_param
($param, 'ignore-unpack-errors');
194 my $bwlimit = extract_param
($param, 'bwlimit');
196 my $start_after_create = extract_param
($param, 'start');
198 my $basecfg_fn = PVE
::LXC
::Config-
>config_file($vmid);
200 my $same_container_exists = -f
$basecfg_fn;
202 # 'unprivileged' is read-only, so we can't pass it to update_pct_config
203 my $unprivileged = extract_param
($param, 'unprivileged');
205 my $restore = extract_param
($param, 'restore');
208 # fixme: limit allowed parameters
212 my $force = extract_param
($param, 'force');
214 if (!($same_container_exists && $restore && $force)) {
215 PVE
::Cluster
::check_vmid_unused
($vmid);
217 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
218 PVE
::LXC
::Config-
>check_protection($conf, "unable to restore CT $vmid");
221 my $password = extract_param
($param, 'password');
223 my $ssh_keys = extract_param
($param, 'ssh-public-keys');
224 PVE
::Tools
::validate_ssh_public_keys
($ssh_keys) if defined($ssh_keys);
226 my $pool = extract_param
($param, 'pool');
228 if (defined($pool)) {
229 $rpcenv->check_pool_exist($pool);
230 $rpcenv->check_perm_modify($authuser, "/pool/$pool");
233 if ($rpcenv->check($authuser, "/vms/$vmid", ['VM.Allocate'], 1)) {
235 } elsif ($pool && $rpcenv->check($authuser, "/pool/$pool", ['VM.Allocate'], 1)) {
237 } elsif ($restore && $force && $same_container_exists &&
238 $rpcenv->check($authuser, "/vms/$vmid", ['VM.Backup'], 1)) {
239 # OK: user has VM.Backup permissions, and want to restore an existing VM
244 my $ostemplate = extract_param
($param, 'ostemplate');
245 my $storage = extract_param
($param, 'storage') // 'local';
247 PVE
::LXC
::check_ct_modify_config_perm
($rpcenv, $authuser, $vmid, $pool, $param, []);
249 my $storage_cfg = cfs_read_file
("storage.cfg");
254 if ($ostemplate eq '-') {
255 die "pipe requires cli environment\n"
256 if $rpcenv->{type
} ne 'cli';
257 die "pipe can only be used with restore tasks\n"
260 die "restore from pipe requires rootfs parameter\n" if !defined($param->{rootfs
});
262 PVE
::Storage
::check_volume_access
($rpcenv, $authuser, $storage_cfg, $vmid, $ostemplate);
263 $archive = PVE
::Storage
::abs_filesystem_path
($storage_cfg, $ostemplate);
267 my $check_and_activate_storage = sub {
270 my $scfg = PVE
::Storage
::storage_check_node
($storage_cfg, $sid, $node);
272 raise_param_exc
({ storage
=> "storage '$sid' does not support container directories"})
273 if !$scfg->{content
}->{rootdir
};
275 $rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);
277 PVE
::Storage
::activate_storage
($storage_cfg, $sid);
279 $used_storages{$sid} = 1;
284 my $no_disk_param = {};
286 my $storage_only_mode = 1;
287 foreach my $opt (keys %$param) {
288 my $value = $param->{$opt};
289 if ($opt eq 'rootfs' || $opt =~ m/^mp\d+$/) {
290 # allow to use simple numbers (add default storage in that case)
291 if ($value =~ m/^\d+(\.\d+)?$/) {
292 $mp_param->{$opt} = "$storage:$value";
294 $mp_param->{$opt} = $value;
296 $storage_only_mode = 0;
297 } elsif ($opt =~ m/^unused\d+$/) {
298 warn "ignoring '$opt', cannot create/restore with unused volume\n";
299 delete $param->{$opt};
301 $no_disk_param->{$opt} = $value;
305 die "mount points configured, but 'rootfs' not set - aborting\n"
306 if !$storage_only_mode && !defined($mp_param->{rootfs
});
308 # check storage access, activate storage
309 my $delayed_mp_param = {};
310 PVE
::LXC
::Config-
>foreach_mountpoint($mp_param, sub {
311 my ($ms, $mountpoint) = @_;
313 my $volid = $mountpoint->{volume
};
314 my $mp = $mountpoint->{mp
};
316 if ($mountpoint->{type
} ne 'volume') { # bind or device
317 die "Only root can pass arbitrary filesystem paths.\n"
318 if $authuser ne 'root@pam';
320 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid);
321 &$check_and_activate_storage($sid);
325 # check/activate default storage
326 &$check_and_activate_storage($storage) if !defined($mp_param->{rootfs
});
328 PVE
::LXC
::Config-
>update_pct_config($vmid, $conf, 0, $no_disk_param);
330 $conf->{unprivileged
} = 1 if $unprivileged;
332 my $check_vmid_usage = sub {
334 die "can't overwrite running container\n"
335 if PVE
::LXC
::check_running
($vmid);
337 PVE
::Cluster
::check_vmid_unused
($vmid);
342 &$check_vmid_usage(); # final check after locking
345 my $config_fn = PVE
::LXC
::Config-
>config_file($vmid);
347 die "container exists" if !$restore; # just to be sure
348 $old_conf = PVE
::LXC
::Config-
>load_config($vmid);
351 # try to create empty config on local node, we have an flock
352 PVE
::LXC
::Config-
>write_config($vmid, {});
355 # another node was faster, abort
356 die "Could not reserve ID $vmid, already taken\n" if $@;
359 PVE
::Cluster
::check_cfs_quorum
();
363 if ($storage_only_mode) {
365 (undef, $mp_param) = PVE
::LXC
::Create
::recover_config
($archive);
366 die "rootfs configuration could not be recovered, please check and specify manually!\n"
367 if !defined($mp_param->{rootfs
});
368 PVE
::LXC
::Config-
>foreach_mountpoint($mp_param, sub {
369 my ($ms, $mountpoint) = @_;
370 my $type = $mountpoint->{type
};
371 if ($type eq 'volume') {
372 die "unable to detect disk size - please specify $ms (size)\n"
373 if !defined($mountpoint->{size
});
374 my $disksize = $mountpoint->{size
} / (1024 * 1024 * 1024); # create_disks expects GB as unit size
375 delete $mountpoint->{size
};
376 $mountpoint->{volume
} = "$storage:$disksize";
377 $mp_param->{$ms} = PVE
::LXC
::Config-
>print_ct_mountpoint($mountpoint, $ms eq 'rootfs');
379 my $type = $mountpoint->{type
};
380 die "restoring rootfs to $type mount is only possible by specifying -rootfs manually!\n"
381 if ($ms eq 'rootfs');
382 die "restoring '$ms' to $type mount is only possible for root\n"
383 if $authuser ne 'root@pam';
385 if ($mountpoint->{backup
}) {
386 warn "WARNING - unsupported configuration!\n";
387 warn "backup was enabled for $type mount point $ms ('$mountpoint->{mp}')\n";
388 warn "mount point configuration will be restored after archive extraction!\n";
389 warn "contained files will be restored to wrong directory!\n";
391 delete $mp_param->{$ms}; # actually delay bind/dev mps
392 $delayed_mp_param->{$ms} = PVE
::LXC
::Config-
>print_ct_mountpoint($mountpoint, $ms eq 'rootfs');
396 $mp_param->{rootfs
} = "$storage:4"; # defaults to 4GB
400 $vollist = PVE
::LXC
::create_disks
($storage_cfg, $vmid, $mp_param, $conf);
402 if (defined($old_conf)) {
403 # destroy old container volumes
404 PVE
::LXC
::destroy_lxc_container
($storage_cfg, $vmid, $old_conf, {});
408 my $rootdir = PVE
::LXC
::mount_all
($vmid, $storage_cfg, $conf, 1);
409 $bwlimit = PVE
::Storage
::get_bandwidth_limit
('restore', [keys %used_storages], $bwlimit);
410 PVE
::LXC
::Create
::restore_archive
($archive, $rootdir, $conf, $ignore_unpack_errors, $bwlimit);
413 PVE
::LXC
::Create
::restore_configuration
($vmid, $rootdir, $conf, $authuser ne 'root@pam');
415 my $lxc_setup = PVE
::LXC
::Setup-
>new($conf, $rootdir); # detect OS
416 PVE
::LXC
::Config-
>write_config($vmid, $conf); # safe config (after OS detection)
417 $lxc_setup->post_create_hook($password, $ssh_keys);
421 PVE
::LXC
::umount_all
($vmid, $storage_cfg, $conf, $err ?
1 : 0);
422 PVE
::Storage
::deactivate_volumes
($storage_cfg, PVE
::LXC
::Config-
>get_vm_volumes($conf));
425 $conf->{hostname
} ||= "CT$vmid";
426 $conf->{memory
} ||= 512;
427 $conf->{swap
} //= 512;
428 foreach my $mp (keys %$delayed_mp_param) {
429 $conf->{$mp} = $delayed_mp_param->{$mp};
431 PVE
::LXC
::Config-
>write_config($vmid, $conf);
434 PVE
::LXC
::destroy_disks
($storage_cfg, $vollist);
435 PVE
::LXC
::destroy_config
($vmid);
438 PVE
::AccessControl
::add_vm_to_pool
($vmid, $pool) if $pool;
440 PVE
::API2
::LXC
::Status-
>vm_start({ vmid
=> $vmid, node
=> $node })
441 if $start_after_create;
444 my $realcmd = sub { PVE
::LXC
::Config-
>lock_config($vmid, $code); };
446 &$check_vmid_usage(); # first check before locking
448 return $rpcenv->fork_worker($restore ?
'vzrestore' : 'vzcreate',
449 $vmid, $authuser, $realcmd);
453 __PACKAGE__-
>register_method({
458 description
=> "Directory index",
463 additionalProperties
=> 0,
465 node
=> get_standard_option
('pve-node'),
466 vmid
=> get_standard_option
('pve-vmid'),
474 subdir
=> { type
=> 'string' },
477 links
=> [ { rel
=> 'child', href
=> "{subdir}" } ],
483 my $conf = PVE
::LXC
::Config-
>load_config($param->{vmid
});
486 { subdir
=> 'config' },
487 { subdir
=> 'status' },
488 { subdir
=> 'vncproxy' },
489 { subdir
=> 'termproxy' },
490 { subdir
=> 'vncwebsocket' },
491 { subdir
=> 'spiceproxy' },
492 { subdir
=> 'migrate' },
493 { subdir
=> 'clone' },
494 # { subdir => 'initlog' },
496 { subdir
=> 'rrddata' },
497 { subdir
=> 'firewall' },
498 { subdir
=> 'snapshot' },
499 { subdir
=> 'resize' },
506 __PACKAGE__-
>register_method({
508 path
=> '{vmid}/rrd',
510 protected
=> 1, # fixme: can we avoid that?
512 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
514 description
=> "Read VM RRD statistics (returns PNG)",
516 additionalProperties
=> 0,
518 node
=> get_standard_option
('pve-node'),
519 vmid
=> get_standard_option
('pve-vmid'),
521 description
=> "Specify the time frame you are interested in.",
523 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
526 description
=> "The list of datasources you want to display.",
527 type
=> 'string', format
=> 'pve-configid-list',
530 description
=> "The RRD consolidation function",
532 enum
=> [ 'AVERAGE', 'MAX' ],
540 filename
=> { type
=> 'string' },
546 return PVE
::Cluster
::create_rrd_graph
(
547 "pve2-vm/$param->{vmid}", $param->{timeframe
},
548 $param->{ds
}, $param->{cf
});
552 __PACKAGE__-
>register_method({
554 path
=> '{vmid}/rrddata',
556 protected
=> 1, # fixme: can we avoid that?
558 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
560 description
=> "Read VM RRD statistics",
562 additionalProperties
=> 0,
564 node
=> get_standard_option
('pve-node'),
565 vmid
=> get_standard_option
('pve-vmid'),
567 description
=> "Specify the time frame you are interested in.",
569 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
572 description
=> "The RRD consolidation function",
574 enum
=> [ 'AVERAGE', 'MAX' ],
589 return PVE
::Cluster
::create_rrd_data
(
590 "pve2-vm/$param->{vmid}", $param->{timeframe
}, $param->{cf
});
593 __PACKAGE__-
>register_method({
594 name
=> 'destroy_vm',
599 description
=> "Destroy the container (also delete all uses files).",
601 check
=> [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
604 additionalProperties
=> 0,
606 node
=> get_standard_option
('pve-node'),
607 vmid
=> get_standard_option
('pve-vmid', { completion
=> \
&PVE
::LXC
::complete_ctid_stopped
}),
616 my $rpcenv = PVE
::RPCEnvironment
::get
();
618 my $authuser = $rpcenv->get_user();
620 my $vmid = $param->{vmid
};
622 # test if container exists
623 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
625 my $storage_cfg = cfs_read_file
("storage.cfg");
627 PVE
::LXC
::Config-
>check_protection($conf, "can't remove CT $vmid");
629 die "unable to remove CT $vmid - used in HA resources\n"
630 if PVE
::HA
::Config
::vm_is_ha_managed
($vmid);
632 # do not allow destroy if there are replication jobs
633 my $repl_conf = PVE
::ReplicationConfig-
>new();
634 $repl_conf->check_for_existing_jobs($vmid);
636 my $running_error_msg = "unable to destroy CT $vmid - container is running\n";
638 die $running_error_msg if PVE
::LXC
::check_running
($vmid); # check early
641 # reload config after lock
642 $conf = PVE
::LXC
::Config-
>load_config($vmid);
643 PVE
::LXC
::Config-
>check_lock($conf);
645 die $running_error_msg if PVE
::LXC
::check_running
($vmid);
647 PVE
::LXC
::destroy_lxc_container
($storage_cfg, $vmid, $conf);
648 PVE
::AccessControl
::remove_vm_access
($vmid);
649 PVE
::Firewall
::remove_vmfw_conf
($vmid);
652 my $realcmd = sub { PVE
::LXC
::Config-
>lock_config($vmid, $code); };
654 return $rpcenv->fork_worker('vzdestroy', $vmid, $authuser, $realcmd);
659 __PACKAGE__-
>register_method ({
661 path
=> '{vmid}/vncproxy',
665 check
=> ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
667 description
=> "Creates a TCP VNC proxy connections.",
669 additionalProperties
=> 0,
671 node
=> get_standard_option
('pve-node'),
672 vmid
=> get_standard_option
('pve-vmid'),
676 description
=> "use websocket instead of standard VNC.",
680 description
=> "sets the width of the console in pixels.",
687 description
=> "sets the height of the console in pixels.",
695 additionalProperties
=> 0,
697 user
=> { type
=> 'string' },
698 ticket
=> { type
=> 'string' },
699 cert
=> { type
=> 'string' },
700 port
=> { type
=> 'integer' },
701 upid
=> { type
=> 'string' },
707 my $rpcenv = PVE
::RPCEnvironment
::get
();
709 my $authuser = $rpcenv->get_user();
711 my $vmid = $param->{vmid
};
712 my $node = $param->{node
};
714 my $authpath = "/vms/$vmid";
716 my $ticket = PVE
::AccessControl
::assemble_vnc_ticket
($authuser, $authpath);
718 $sslcert = PVE
::Tools
::file_get_contents
("/etc/pve/pve-root-ca.pem", 8192)
721 my ($remip, $family);
723 if ($node ne PVE
::INotify
::nodename
()) {
724 ($remip, $family) = PVE
::Cluster
::remote_node_ip
($node);
726 $family = PVE
::Tools
::get_host_address_family
($node);
729 my $port = PVE
::Tools
::next_vnc_port
($family);
731 # NOTE: vncterm VNC traffic is already TLS encrypted,
732 # so we select the fastest chipher here (or 'none'?)
733 my $remcmd = $remip ?
734 ['/usr/bin/ssh', '-e', 'none', '-t', $remip] : [];
736 my $conf = PVE
::LXC
::Config-
>load_config($vmid, $node);
737 my $concmd = PVE
::LXC
::get_console_command
($vmid, $conf, 1);
739 my $shcmd = [ '/usr/bin/dtach', '-A',
740 "/var/run/dtach/vzctlconsole$vmid",
741 '-r', 'winch', '-z', @$concmd];
746 syslog
('info', "starting lxc vnc proxy $upid\n");
750 my $cmd = ['/usr/bin/vncterm', '-rfbport', $port,
751 '-timeout', $timeout, '-authpath', $authpath,
752 '-perm', 'VM.Console'];
754 if ($param->{width
}) {
755 push @$cmd, '-width', $param->{width
};
758 if ($param->{height
}) {
759 push @$cmd, '-height', $param->{height
};
762 if ($param->{websocket
}) {
763 $ENV{PVE_VNC_TICKET
} = $ticket; # pass ticket to vncterm
764 push @$cmd, '-notls', '-listen', 'localhost';
767 push @$cmd, '-c', @$remcmd, @$shcmd;
769 run_command
($cmd, keeplocale
=> 1);
774 my $upid = $rpcenv->fork_worker('vncproxy', $vmid, $authuser, $realcmd);
776 PVE
::Tools
::wait_for_vnc_port
($port);
787 __PACKAGE__-
>register_method ({
789 path
=> '{vmid}/termproxy',
793 check
=> ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
795 description
=> "Creates a TCP proxy connection.",
797 additionalProperties
=> 0,
799 node
=> get_standard_option
('pve-node'),
800 vmid
=> get_standard_option
('pve-vmid'),
804 additionalProperties
=> 0,
806 user
=> { type
=> 'string' },
807 ticket
=> { type
=> 'string' },
808 port
=> { type
=> 'integer' },
809 upid
=> { type
=> 'string' },
815 my $rpcenv = PVE
::RPCEnvironment
::get
();
817 my $authuser = $rpcenv->get_user();
819 my $vmid = $param->{vmid
};
820 my $node = $param->{node
};
822 my $authpath = "/vms/$vmid";
824 my $ticket = PVE
::AccessControl
::assemble_vnc_ticket
($authuser, $authpath);
826 my ($remip, $family);
828 if ($node ne 'localhost' && $node ne PVE
::INotify
::nodename
()) {
829 ($remip, $family) = PVE
::Cluster
::remote_node_ip
($node);
831 $family = PVE
::Tools
::get_host_address_family
($node);
834 my $port = PVE
::Tools
::next_vnc_port
($family);
836 my $remcmd = $remip ?
837 ['/usr/bin/ssh', '-e', 'none', '-t', $remip, '--'] : [];
839 my $conf = PVE
::LXC
::Config-
>load_config($vmid, $node);
840 my $concmd = PVE
::LXC
::get_console_command
($vmid, $conf, 1);
842 my $shcmd = [ '/usr/bin/dtach', '-A',
843 "/var/run/dtach/vzctlconsole$vmid",
844 '-r', 'winch', '-z', @$concmd];
849 syslog
('info', "starting lxc termproxy $upid\n");
851 my $cmd = ['/usr/bin/termproxy', $port, '--path', $authpath,
852 '--perm', 'VM.Console', '--'];
853 push @$cmd, @$remcmd, @$shcmd;
855 PVE
::Tools
::run_command
($cmd);
858 my $upid = $rpcenv->fork_worker('vncproxy', $vmid, $authuser, $realcmd, 1);
860 PVE
::Tools
::wait_for_vnc_port
($port);
870 __PACKAGE__-
>register_method({
871 name
=> 'vncwebsocket',
872 path
=> '{vmid}/vncwebsocket',
875 description
=> "You also need to pass a valid ticket (vncticket).",
876 check
=> ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
878 description
=> "Opens a weksocket for VNC traffic.",
880 additionalProperties
=> 0,
882 node
=> get_standard_option
('pve-node'),
883 vmid
=> get_standard_option
('pve-vmid'),
885 description
=> "Ticket from previous call to vncproxy.",
890 description
=> "Port number returned by previous vncproxy call.",
900 port
=> { type
=> 'string' },
906 my $rpcenv = PVE
::RPCEnvironment
::get
();
908 my $authuser = $rpcenv->get_user();
910 my $authpath = "/vms/$param->{vmid}";
912 PVE
::AccessControl
::verify_vnc_ticket
($param->{vncticket
}, $authuser, $authpath);
914 my $port = $param->{port
};
916 return { port
=> $port };
919 __PACKAGE__-
>register_method ({
920 name
=> 'spiceproxy',
921 path
=> '{vmid}/spiceproxy',
926 check
=> ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
928 description
=> "Returns a SPICE configuration to connect to the CT.",
930 additionalProperties
=> 0,
932 node
=> get_standard_option
('pve-node'),
933 vmid
=> get_standard_option
('pve-vmid'),
934 proxy
=> get_standard_option
('spice-proxy', { optional
=> 1 }),
937 returns
=> get_standard_option
('remote-viewer-config'),
941 my $vmid = $param->{vmid
};
942 my $node = $param->{node
};
943 my $proxy = $param->{proxy
};
945 my $authpath = "/vms/$vmid";
946 my $permissions = 'VM.Console';
948 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
950 die "CT $vmid not running\n" if !PVE
::LXC
::check_running
($vmid);
952 my $concmd = PVE
::LXC
::get_console_command
($vmid, $conf);
954 my $shcmd = ['/usr/bin/dtach', '-A',
955 "/var/run/dtach/vzctlconsole$vmid",
956 '-r', 'winch', '-z', @$concmd];
958 my $title = "CT $vmid";
960 return PVE
::API2Tools
::run_spiceterm
($authpath, $permissions, $vmid, $node, $proxy, $title, $shcmd);
964 __PACKAGE__-
>register_method({
965 name
=> 'migrate_vm',
966 path
=> '{vmid}/migrate',
970 description
=> "Migrate the container to another node. Creates a new migration task.",
972 check
=> ['perm', '/vms/{vmid}', [ 'VM.Migrate' ]],
975 additionalProperties
=> 0,
977 node
=> get_standard_option
('pve-node'),
978 vmid
=> get_standard_option
('pve-vmid', { completion
=> \
&PVE
::LXC
::complete_ctid
}),
979 target
=> get_standard_option
('pve-node', {
980 description
=> "Target node.",
981 completion
=> \
&PVE
::Cluster
::complete_migration_target
,
985 description
=> "Use online/live migration.",
990 description
=> "Use restart migration",
995 description
=> "Timeout in seconds for shutdown for restart migration",
1001 description
=> "Force migration despite local bind / device" .
1002 " mounts. NOTE: deprecated, use 'shared' property of mount point instead.",
1009 description
=> "the task ID.",
1014 my $rpcenv = PVE
::RPCEnvironment
::get
();
1016 my $authuser = $rpcenv->get_user();
1018 my $target = extract_param
($param, 'target');
1020 my $localnode = PVE
::INotify
::nodename
();
1021 raise_param_exc
({ target
=> "target is local node."}) if $target eq $localnode;
1023 PVE
::Cluster
::check_cfs_quorum
();
1025 PVE
::Cluster
::check_node_exists
($target);
1027 my $targetip = PVE
::Cluster
::remote_node_ip
($target);
1029 my $vmid = extract_param
($param, 'vmid');
1032 PVE
::LXC
::Config-
>load_config($vmid);
1034 # try to detect errors early
1035 if (PVE
::LXC
::check_running
($vmid)) {
1036 die "can't migrate running container without --online or --restart\n"
1037 if !$param->{online
} && !$param->{restart
};
1040 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid) && $rpcenv->{type
} ne 'ha') {
1045 my $service = "ct:$vmid";
1047 my $cmd = ['ha-manager', 'migrate', $service, $target];
1049 print "Requesting HA migration for CT $vmid to node $target\n";
1051 PVE
::Tools
::run_command
($cmd);
1056 return $rpcenv->fork_worker('hamigrate', $vmid, $authuser, $hacmd);
1061 PVE
::LXC
::Migrate-
>migrate($target, $targetip, $vmid, $param);
1065 return PVE
::GuestHelpers
::guest_migration_lock
($vmid, 10, $realcmd);
1068 return $rpcenv->fork_worker('vzmigrate', $vmid, $authuser, $worker);
1072 __PACKAGE__-
>register_method({
1073 name
=> 'vm_feature',
1074 path
=> '{vmid}/feature',
1078 description
=> "Check if feature for virtual machine is available.",
1080 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
1083 additionalProperties
=> 0,
1085 node
=> get_standard_option
('pve-node'),
1086 vmid
=> get_standard_option
('pve-vmid'),
1088 description
=> "Feature to check.",
1090 enum
=> [ 'snapshot', 'clone', 'copy' ],
1092 snapname
=> get_standard_option
('pve-lxc-snapshot-name', {
1100 hasFeature
=> { type
=> 'boolean' },
1103 #items => { type => 'string' },
1110 my $node = extract_param
($param, 'node');
1112 my $vmid = extract_param
($param, 'vmid');
1114 my $snapname = extract_param
($param, 'snapname');
1116 my $feature = extract_param
($param, 'feature');
1118 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
1121 my $snap = $conf->{snapshots
}->{$snapname};
1122 die "snapshot '$snapname' does not exist\n" if !defined($snap);
1125 my $storage_cfg = PVE
::Storage
::config
();
1126 #Maybe include later
1127 #my $nodelist = PVE::LXC::shared_nodes($conf, $storage_cfg);
1128 my $hasFeature = PVE
::LXC
::Config-
>has_feature($feature, $conf, $storage_cfg, $snapname);
1131 hasFeature
=> $hasFeature,
1132 #nodes => [ keys %$nodelist ],
1136 __PACKAGE__-
>register_method({
1138 path
=> '{vmid}/template',
1142 description
=> "Create a Template.",
1144 description
=> "You need 'VM.Allocate' permissions on /vms/{vmid}",
1145 check
=> [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
1148 additionalProperties
=> 0,
1150 node
=> get_standard_option
('pve-node'),
1151 vmid
=> get_standard_option
('pve-vmid', { completion
=> \
&PVE
::LXC
::complete_ctid_stopped
}),
1154 returns
=> { type
=> 'null'},
1158 my $rpcenv = PVE
::RPCEnvironment
::get
();
1160 my $authuser = $rpcenv->get_user();
1162 my $node = extract_param
($param, 'node');
1164 my $vmid = extract_param
($param, 'vmid');
1166 my $updatefn = sub {
1168 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
1169 PVE
::LXC
::Config-
>check_lock($conf);
1171 die "unable to create template, because CT contains snapshots\n"
1172 if $conf->{snapshots
} && scalar(keys %{$conf->{snapshots
}});
1174 die "you can't convert a template to a template\n"
1175 if PVE
::LXC
::Config-
>is_template($conf);
1177 die "you can't convert a CT to template if the CT is running\n"
1178 if PVE
::LXC
::check_running
($vmid);
1180 my $scfg = PVE
::Storage
::config
();
1181 PVE
::LXC
::Config-
>foreach_mountpoint($conf, sub {
1184 my ($sid) =PVE
::Storage
::parse_volume_id
($mp->{volume
}, 0);
1185 die "Directory storage '$sid' does not support container templates!\n"
1186 if $scfg->{ids
}->{$sid}->{path
};
1190 PVE
::LXC
::template_create
($vmid, $conf);
1192 $conf->{template
} = 1;
1194 PVE
::LXC
::Config-
>write_config($vmid, $conf);
1195 # and remove lxc config
1196 PVE
::LXC
::update_lxc_config
($vmid, $conf);
1199 return $rpcenv->fork_worker('vztemplate', $vmid, $authuser, $realcmd);
1202 PVE
::LXC
::Config-
>lock_config($vmid, $updatefn);
1207 __PACKAGE__-
>register_method({
1209 path
=> '{vmid}/clone',
1213 description
=> "Create a container clone/copy",
1215 description
=> "You need 'VM.Clone' permissions on /vms/{vmid}, " .
1216 "and 'VM.Allocate' permissions " .
1217 "on /vms/{newid} (or on the VM pool /pool/{pool}). You also need " .
1218 "'Datastore.AllocateSpace' on any used storage.",
1221 ['perm', '/vms/{vmid}', [ 'VM.Clone' ]],
1223 [ 'perm', '/vms/{newid}', ['VM.Allocate']],
1224 [ 'perm', '/pool/{pool}', ['VM.Allocate'], require_param
=> 'pool'],
1229 additionalProperties
=> 0,
1231 node
=> get_standard_option
('pve-node'),
1232 vmid
=> get_standard_option
('pve-vmid', { completion
=> \
&PVE
::LXC
::complete_ctid
}),
1233 newid
=> get_standard_option
('pve-vmid', {
1234 completion
=> \
&PVE
::Cluster
::complete_next_vmid
,
1235 description
=> 'VMID for the clone.' }),
1238 type
=> 'string', format
=> 'dns-name',
1239 description
=> "Set a hostname for the new CT.",
1244 description
=> "Description for the new CT.",
1248 type
=> 'string', format
=> 'pve-poolid',
1249 description
=> "Add the new CT to the specified pool.",
1251 snapname
=> get_standard_option
('pve-lxc-snapshot-name', {
1254 storage
=> get_standard_option
('pve-storage-id', {
1255 description
=> "Target storage for full clone.",
1261 description
=> "Create a full copy of all disks. This is always done when " .
1262 "you clone a normal CT. For CT templates, we try to create a linked clone by default.",
1264 target
=> get_standard_option
('pve-node', {
1265 description
=> "Target node. Only allowed if the original VM is on shared storage.",
1276 my $rpcenv = PVE
::RPCEnvironment
::get
();
1278 my $authuser = $rpcenv->get_user();
1280 my $node = extract_param
($param, 'node');
1282 my $vmid = extract_param
($param, 'vmid');
1284 my $newid = extract_param
($param, 'newid');
1286 my $pool = extract_param
($param, 'pool');
1288 if (defined($pool)) {
1289 $rpcenv->check_pool_exist($pool);
1292 my $snapname = extract_param
($param, 'snapname');
1294 my $storage = extract_param
($param, 'storage');
1296 my $target = extract_param
($param, 'target');
1298 my $localnode = PVE
::INotify
::nodename
();
1300 undef $target if $target && ($target eq $localnode || $target eq 'localhost');
1302 PVE
::Cluster
::check_node_exists
($target) if $target;
1304 my $storecfg = PVE
::Storage
::config
();
1307 # check if storage is enabled on local node
1308 PVE
::Storage
::storage_check_enabled
($storecfg, $storage);
1310 # check if storage is available on target node
1311 PVE
::Storage
::storage_check_node
($storecfg, $storage, $target);
1312 # clone only works if target storage is shared
1313 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storage);
1314 die "can't clone to non-shared storage '$storage'\n" if !$scfg->{shared
};
1318 PVE
::Cluster
::check_cfs_quorum
();
1322 my $mountpoints = {};
1327 PVE
::LXC
::Config-
>lock_config($vmid, sub {
1328 my $src_conf = PVE
::LXC
::Config-
>set_lock($vmid, 'disk');
1330 $running = PVE
::LXC
::check_running
($vmid) || 0;
1332 my $full = extract_param
($param, 'full');
1333 if (!defined($full)) {
1334 $full = !PVE
::LXC
::Config-
>is_template($src_conf);
1336 die "parameter 'storage' not allowed for linked clones\n" if defined($storage) && !$full;
1339 die "snapshot '$snapname' does not exist\n"
1340 if $snapname && !defined($src_conf->{snapshots
}->{$snapname});
1343 my $src_conf = $snapname ?
$src_conf->{snapshots
}->{$snapname} : $src_conf;
1345 $conffile = PVE
::LXC
::Config-
>config_file($newid);
1346 die "unable to create CT $newid: config file already exists\n"
1350 foreach my $opt (keys %$src_conf) {
1351 next if $opt =~ m/^unused\d+$/;
1353 my $value = $src_conf->{$opt};
1355 if (($opt eq 'rootfs') || ($opt =~ m/^mp\d+$/)) {
1356 my $mp = $opt eq 'rootfs' ?
1357 PVE
::LXC
::Config-
>parse_ct_rootfs($value) :
1358 PVE
::LXC
::Config-
>parse_ct_mountpoint($value);
1360 if ($mp->{type
} eq 'volume') {
1361 my $volid = $mp->{volume
};
1363 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid);
1364 $sid = $storage if defined($storage);
1365 my $scfg = PVE
::Storage
::storage_config
($storecfg, $sid);
1366 if (!$scfg->{shared
}) {
1368 warn "found non-shared volume: $volid\n" if $target;
1371 $rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);
1374 die "Cannot do full clones on a running container without snapshots\n"
1375 if $running && !defined($snapname);
1376 $fullclone->{$opt} = 1;
1378 # not full means clone instead of copy
1379 die "Linked clone feature for '$volid' is not available\n"
1380 if !PVE
::Storage
::volume_has_feature
($storecfg, 'clone', $volid, $snapname, $running);
1383 $mountpoints->{$opt} = $mp;
1384 push @$vollist, $volid;
1387 # TODO: allow bind mounts?
1388 die "unable to clone mountpint '$opt' (type $mp->{type})\n";
1390 } elsif ($opt =~ m/^net(\d+)$/) {
1391 # always change MAC! address
1392 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
1393 my $net = PVE
::LXC
::Config-
>parse_lxc_network($value);
1394 $net->{hwaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
});
1395 $newconf->{$opt} = PVE
::LXC
::Config-
>print_lxc_network($net);
1397 # copy everything else
1398 $newconf->{$opt} = $value;
1401 die "can't clone CT to node '$target' (CT uses local storage)\n"
1402 if $target && !$sharedvm;
1404 # Replace the 'disk' lock with a 'create' lock.
1405 $newconf->{lock} = 'create';
1407 delete $newconf->{template
};
1408 if ($param->{hostname
}) {
1409 $newconf->{hostname
} = $param->{hostname
};
1412 if ($param->{description
}) {
1413 $newconf->{description
} = $param->{description
};
1416 # create empty/temp config - this fails if CT already exists on other node
1417 PVE
::LXC
::Config-
>write_config($newid, $newconf);
1420 eval { PVE
::LXC
::Config-
>remove_lock($vmid, 'disk') };
1426 my $update_conf = sub {
1427 my ($key, $value) = @_;
1428 return PVE
::LXC
::Config-
>lock_config($newid, sub {
1429 my $conf = PVE
::LXC
::Config-
>load_config($newid);
1430 die "Lost 'create' config lock, aborting.\n"
1431 if !PVE
::LXC
::Config-
>has_lock($conf, 'create');
1432 $conf->{$key} = $value;
1433 PVE
::LXC
::Config-
>write_config($newid, $conf);
1440 my $newvollist = [];
1442 my $verify_running = PVE
::LXC
::check_running
($vmid) || 0;
1443 die "unexpected state change\n" if $verify_running != $running;
1449 local $SIG{HUP
} = sub { die "interrupted by signal\n"; };
1451 PVE
::Storage
::activate_volumes
($storecfg, $vollist, $snapname);
1453 foreach my $opt (keys %$mountpoints) {
1454 my $mp = $mountpoints->{$opt};
1455 my $volid = $mp->{volume
};
1458 if ($fullclone->{$opt}) {
1459 print "create full clone of mountpoint $opt ($volid)\n";
1460 my $target_storage = $storage // PVE
::Storage
::parse_volume_id
($volid);
1461 $newvolid = PVE
::LXC
::copy_volume
($mp, $newid, $target_storage, $storecfg, $newconf, $snapname);
1463 print "create linked clone of mount point $opt ($volid)\n";
1464 $newvolid = PVE
::Storage
::vdisk_clone
($storecfg, $volid, $newid, $snapname);
1467 push @$newvollist, $newvolid;
1468 $mp->{volume
} = $newvolid;
1470 $update_conf->($opt, PVE
::LXC
::Config-
>print_ct_mountpoint($mp, $opt eq 'rootfs'));
1473 PVE
::AccessControl
::add_vm_to_pool
($newid, $pool) if $pool;
1474 PVE
::LXC
::Config-
>remove_lock($newid, 'create');
1477 # always deactivate volumes - avoid lvm LVs to be active on several nodes
1478 PVE
::Storage
::deactivate_volumes
($storecfg, $vollist, $snapname) if !$running;
1479 PVE
::Storage
::deactivate_volumes
($storecfg, $newvollist);
1481 my $newconffile = PVE
::LXC
::Config-
>config_file($newid, $target);
1482 die "Failed to move config to node '$target' - rename failed: $!\n"
1483 if !rename($conffile, $newconffile);
1488 # Unlock the source config in any case:
1489 eval { PVE
::LXC
::Config-
>remove_lock($vmid, 'disk') };
1493 # Now cleanup the config & disks:
1496 sleep 1; # some storages like rbd need to wait before release volume - really?
1498 foreach my $volid (@$newvollist) {
1499 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid); };
1502 die "clone failed: $err";
1508 PVE
::Firewall
::clone_vmfw_conf
($vmid, $newid);
1509 return $rpcenv->fork_worker('vzclone', $vmid, $authuser, $realcmd);
1513 __PACKAGE__-
>register_method({
1514 name
=> 'resize_vm',
1515 path
=> '{vmid}/resize',
1519 description
=> "Resize a container mount point.",
1521 check
=> ['perm', '/vms/{vmid}', ['VM.Config.Disk'], any
=> 1],
1524 additionalProperties
=> 0,
1526 node
=> get_standard_option
('pve-node'),
1527 vmid
=> get_standard_option
('pve-vmid', { completion
=> \
&PVE
::LXC
::complete_ctid
}),
1530 description
=> "The disk you want to resize.",
1531 enum
=> [PVE
::LXC
::Config-
>mountpoint_names()],
1535 pattern
=> '\+?\d+(\.\d+)?[KMGT]?',
1536 description
=> "The new size. With the '+' sign the value is added to the actual size of the volume and without it, the value is taken as an absolute one. Shrinking disk size is not supported.",
1540 description
=> 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.',
1548 description
=> "the task ID.",
1553 my $rpcenv = PVE
::RPCEnvironment
::get
();
1555 my $authuser = $rpcenv->get_user();
1557 my $node = extract_param
($param, 'node');
1559 my $vmid = extract_param
($param, 'vmid');
1561 my $digest = extract_param
($param, 'digest');
1563 my $sizestr = extract_param
($param, 'size');
1564 my $ext = ($sizestr =~ s/^\+//);
1565 my $newsize = PVE
::JSONSchema
::parse_size
($sizestr);
1566 die "invalid size string" if !defined($newsize);
1568 die "no options specified\n" if !scalar(keys %$param);
1570 PVE
::LXC
::check_ct_modify_config_perm
($rpcenv, $authuser, $vmid, undef, $param, []);
1572 my $storage_cfg = cfs_read_file
("storage.cfg");
1576 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
1577 PVE
::LXC
::Config-
>check_lock($conf);
1579 PVE
::Tools
::assert_if_modified
($digest, $conf->{digest
});
1581 my $running = PVE
::LXC
::check_running
($vmid);
1583 my $disk = $param->{disk
};
1584 my $mp = $disk eq 'rootfs' ? PVE
::LXC
::Config-
>parse_ct_rootfs($conf->{$disk}) :
1585 PVE
::LXC
::Config-
>parse_ct_mountpoint($conf->{$disk});
1587 my $volid = $mp->{volume
};
1589 my (undef, undef, $owner, undef, undef, undef, $format) =
1590 PVE
::Storage
::parse_volname
($storage_cfg, $volid);
1592 die "can't resize mount point owned by another container ($owner)"
1595 die "can't resize volume: $disk if snapshot exists\n"
1596 if %{$conf->{snapshots
}} && $format eq 'qcow2';
1598 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid);
1600 $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
1602 PVE
::Storage
::activate_volumes
($storage_cfg, [$volid]);
1604 my $size = PVE
::Storage
::volume_size_info
($storage_cfg, $volid, 5);
1605 $newsize += $size if $ext;
1606 $newsize = int($newsize);
1608 die "unable to shrink disk size\n" if $newsize < $size;
1610 return if $size == $newsize;
1612 PVE
::Cluster
::log_msg
('info', $authuser, "update CT $vmid: resize --disk $disk --size $sizestr");
1614 # Note: PVE::Storage::volume_resize doesn't do anything if $running=1, so
1615 # we pass 0 here (parameter only makes sense for qemu)
1616 PVE
::Storage
::volume_resize
($storage_cfg, $volid, $newsize, 0);
1618 $mp->{size
} = $newsize;
1619 $conf->{$disk} = PVE
::LXC
::Config-
>print_ct_mountpoint($mp, $disk eq 'rootfs');
1621 PVE
::LXC
::Config-
>write_config($vmid, $conf);
1623 if ($format eq 'raw') {
1624 my $path = PVE
::Storage
::path
($storage_cfg, $volid, undef);
1628 my $use_loopdev = (PVE
::LXC
::mountpoint_mount_path
($mp, $storage_cfg))[1];
1629 $path = PVE
::LXC
::query_loopdev
($path) if $use_loopdev;
1630 die "internal error: CT running but mount point not attached to a loop device"
1632 PVE
::Tools
::run_command
(['losetup', '--set-capacity', $path]) if $use_loopdev;
1634 # In order for resize2fs to know that we need online-resizing a mountpoint needs
1635 # to be visible to it in its namespace.
1636 # To not interfere with the rest of the system we unshare the current mount namespace,
1637 # mount over /tmp and then run resize2fs.
1639 # interestingly we don't need to e2fsck on mounted systems...
1640 my $quoted = PVE
::Tools
::shellquote
($path);
1641 my $cmd = "mount --make-rprivate / && mount $quoted /tmp && resize2fs $quoted";
1643 PVE
::Tools
::run_command
(['unshare', '-m', '--', 'sh', '-c', $cmd]);
1645 warn "Failed to update the container's filesystem: $@\n" if $@;
1648 PVE
::Tools
::run_command
(['e2fsck', '-f', '-y', $path]);
1649 PVE
::Tools
::run_command
(['resize2fs', $path]);
1651 warn "Failed to update the container's filesystem: $@\n" if $@;
1656 return $rpcenv->fork_worker('resize', $vmid, $authuser, $realcmd);
1659 return PVE
::LXC
::Config-
>lock_config($vmid, $code);;
1662 __PACKAGE__-
>register_method({
1663 name
=> 'move_volume',
1664 path
=> '{vmid}/move_volume',
1668 description
=> "Move a rootfs-/mp-volume to a different storage",
1670 description
=> "You need 'VM.Config.Disk' permissions on /vms/{vmid}, " .
1671 "and 'Datastore.AllocateSpace' permissions on the storage.",
1674 ['perm', '/vms/{vmid}', [ 'VM.Config.Disk' ]],
1675 ['perm', '/storage/{storage}', [ 'Datastore.AllocateSpace' ]],
1679 additionalProperties
=> 0,
1681 node
=> get_standard_option
('pve-node'),
1682 vmid
=> get_standard_option
('pve-vmid', { completion
=> \
&PVE
::LXC
::complete_ctid
}),
1685 enum
=> [ PVE
::LXC
::Config-
>mountpoint_names() ],
1686 description
=> "Volume which will be moved.",
1688 storage
=> get_standard_option
('pve-storage-id', {
1689 description
=> "Target Storage.",
1690 completion
=> \
&PVE
::Storage
::complete_storage_enabled
,
1694 description
=> "Delete the original volume after successful copy. By default the original is kept as an unused volume entry.",
1700 description
=> 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.',
1712 my $rpcenv = PVE
::RPCEnvironment
::get
();
1714 my $authuser = $rpcenv->get_user();
1716 my $vmid = extract_param
($param, 'vmid');
1718 my $storage = extract_param
($param, 'storage');
1720 my $mpkey = extract_param
($param, 'volume');
1722 my $lockname = 'disk';
1724 my ($mpdata, $old_volid);
1726 PVE
::LXC
::Config-
>lock_config($vmid, sub {
1727 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
1728 PVE
::LXC
::Config-
>check_lock($conf);
1730 die "cannot move volumes of a running container\n" if PVE
::LXC
::check_running
($vmid);
1732 if ($mpkey eq 'rootfs') {
1733 $mpdata = PVE
::LXC
::Config-
>parse_ct_rootfs($conf->{$mpkey});
1734 } elsif ($mpkey =~ m/mp\d+/) {
1735 $mpdata = PVE
::LXC
::Config-
>parse_ct_mountpoint($conf->{$mpkey});
1737 die "Can't parse $mpkey\n";
1739 $old_volid = $mpdata->{volume
};
1741 die "you can't move a volume with snapshots and delete the source\n"
1742 if $param->{delete} && PVE
::LXC
::Config-
>is_volume_in_use_by_snapshots($conf, $old_volid);
1744 PVE
::Tools
::assert_if_modified
($param->{digest
}, $conf->{digest
});
1746 PVE
::LXC
::Config-
>set_lock($vmid, $lockname);
1751 PVE
::Cluster
::log_msg
('info', $authuser, "move volume CT $vmid: move --volume $mpkey --storage $storage");
1753 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
1754 my $storage_cfg = PVE
::Storage
::config
();
1759 PVE
::Storage
::activate_volumes
($storage_cfg, [ $old_volid ]);
1760 $new_volid = PVE
::LXC
::copy_volume
($mpdata, $vmid, $storage, $storage_cfg, $conf);
1761 $mpdata->{volume
} = $new_volid;
1763 PVE
::LXC
::Config-
>lock_config($vmid, sub {
1764 my $digest = $conf->{digest
};
1765 $conf = PVE
::LXC
::Config-
>load_config($vmid);
1766 PVE
::Tools
::assert_if_modified
($digest, $conf->{digest
});
1768 $conf->{$mpkey} = PVE
::LXC
::Config-
>print_ct_mountpoint($mpdata, $mpkey eq 'rootfs');
1770 PVE
::LXC
::Config-
>add_unused_volume($conf, $old_volid) if !$param->{delete};
1772 PVE
::LXC
::Config-
>write_config($vmid, $conf);
1776 # try to deactivate volumes - avoid lvm LVs to be active on several nodes
1777 PVE
::Storage
::deactivate_volumes
($storage_cfg, [ $new_volid ])
1783 PVE
::Storage
::vdisk_free
($storage_cfg, $new_volid)
1784 if defined($new_volid);
1790 if ($param->{delete}) {
1792 PVE
::Storage
::deactivate_volumes
($storage_cfg, [ $old_volid ]);
1793 PVE
::Storage
::vdisk_free
($storage_cfg, $old_volid);
1799 eval { PVE
::LXC
::Config-
>remove_lock($vmid, $lockname) };
1804 $rpcenv->fork_worker('move_volume', $vmid, $authuser, $realcmd);
1807 eval { PVE
::LXC
::Config-
>remove_lock($vmid, $lockname) };