1 package PVE
::API2
::LXC
;
7 use PVE
::Tools
qw(extract_param run_command);
8 use PVE
::Exception
qw(raise raise_param_exc raise_perm_exc);
10 use PVE
::Cluster
qw(cfs_read_file);
12 use PVE
::DataCenterConfig
;
13 use PVE
::AccessControl
;
17 use PVE
::RPCEnvironment
;
18 use PVE
::ReplicationConfig
;
21 use PVE
::LXC
::Migrate
;
22 use PVE
::GuestHelpers
;
23 use PVE
::VZDump
::Plugin
;
24 use PVE
::API2
::LXC
::Config
;
25 use PVE
::API2
::LXC
::Status
;
26 use PVE
::API2
::LXC
::Snapshot
;
27 use PVE
::JSONSchema
qw(get_standard_option);
28 use base
qw(PVE::RESTHandler);
31 if (!$ENV{PVE_GENERATING_DOCS
}) {
32 require PVE
::HA
::Env
::PVE2
;
33 import PVE
::HA
::Env
::PVE2
;
34 require PVE
::HA
::Config
;
35 import PVE
::HA
::Config
;
39 __PACKAGE__-
>register_method ({
40 subclass
=> "PVE::API2::LXC::Config",
41 path
=> '{vmid}/config',
44 __PACKAGE__-
>register_method ({
45 subclass
=> "PVE::API2::LXC::Status",
46 path
=> '{vmid}/status',
49 __PACKAGE__-
>register_method ({
50 subclass
=> "PVE::API2::LXC::Snapshot",
51 path
=> '{vmid}/snapshot',
54 __PACKAGE__-
>register_method ({
55 subclass
=> "PVE::API2::Firewall::CT",
56 path
=> '{vmid}/firewall',
59 __PACKAGE__-
>register_method({
63 description
=> "LXC container index (per node).",
65 description
=> "Only list CTs where you have VM.Audit permissons on /vms/<vmid>.",
69 protected
=> 1, # /proc files are only readable by root
71 additionalProperties
=> 0,
73 node
=> get_standard_option
('pve-node'),
80 properties
=> $PVE::LXC
::vmstatus_return_properties
,
82 links
=> [ { rel
=> 'child', href
=> "{vmid}" } ],
87 my $rpcenv = PVE
::RPCEnvironment
::get
();
88 my $authuser = $rpcenv->get_user();
90 my $vmstatus = PVE
::LXC
::vmstatus
();
93 foreach my $vmid (keys %$vmstatus) {
94 next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1);
96 my $data = $vmstatus->{$vmid};
104 __PACKAGE__-
>register_method({
108 description
=> "Create or restore a container.",
110 user
=> 'all', # check inside
111 description
=> "You need 'VM.Allocate' permissions on /vms/{vmid} or on the VM pool /pool/{pool}. " .
112 "For restore, it is enough if the user has 'VM.Backup' permission and the VM already exists. " .
113 "You also need 'Datastore.AllocateSpace' permissions on the storage.",
118 additionalProperties
=> 0,
119 properties
=> PVE
::LXC
::Config-
>json_config_properties({
120 node
=> get_standard_option
('pve-node'),
121 vmid
=> get_standard_option
('pve-vmid', { completion
=> \
&PVE
::Cluster
::complete_next_vmid
}),
123 description
=> "The OS template or backup file.",
126 completion
=> \
&PVE
::LXC
::complete_os_templates
,
131 description
=> "Sets root password inside container.",
134 storage
=> get_standard_option
('pve-storage-id', {
135 description
=> "Default Storage.",
138 completion
=> \
&PVE
::Storage
::complete_storage_enabled
,
143 description
=> "Allow to overwrite existing container.",
148 description
=> "Mark this as restore task.",
153 description
=> "Assign a unique random ethernet address.",
154 requires
=> 'restore',
158 type
=> 'string', format
=> 'pve-poolid',
159 description
=> "Add the VM to the specified pool.",
161 'ignore-unpack-errors' => {
164 description
=> "Ignore errors when extracting the template.",
166 'ssh-public-keys' => {
169 description
=> "Setup public SSH keys (one key per line, " .
173 description
=> "Override I/O bandwidth limit (in KiB/s).",
177 default => 'restore limit from datacenter or storage config',
183 description
=> "Start the CT after its creation finished successfully.",
193 PVE
::Cluster
::check_cfs_quorum
();
195 my $rpcenv = PVE
::RPCEnvironment
::get
();
196 my $authuser = $rpcenv->get_user();
198 my $node = extract_param
($param, 'node');
199 my $vmid = extract_param
($param, 'vmid');
200 my $ignore_unpack_errors = extract_param
($param, 'ignore-unpack-errors');
201 my $bwlimit = extract_param
($param, 'bwlimit');
202 my $start_after_create = extract_param
($param, 'start');
204 my $basecfg_fn = PVE
::LXC
::Config-
>config_file($vmid);
205 my $same_container_exists = -f
$basecfg_fn;
207 # 'unprivileged' is read-only, so we can't pass it to update_pct_config
208 my $unprivileged = extract_param
($param, 'unprivileged');
209 my $restore = extract_param
($param, 'restore');
210 my $unique = extract_param
($param, 'unique');
212 # used to skip firewall config restore if user lacks permission
213 my $skip_fw_config_restore = 0;
216 # fixme: limit allowed parameters
219 my $force = extract_param
($param, 'force');
221 if (!($same_container_exists && $restore && $force)) {
222 PVE
::Cluster
::check_vmid_unused
($vmid);
224 die "can't overwrite running container\n" if PVE
::LXC
::check_running
($vmid);
225 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
226 PVE
::LXC
::Config-
>check_protection($conf, "unable to restore CT $vmid");
229 my $password = extract_param
($param, 'password');
230 my $ssh_keys = extract_param
($param, 'ssh-public-keys');
231 PVE
::Tools
::validate_ssh_public_keys
($ssh_keys) if defined($ssh_keys);
233 my $pool = extract_param
($param, 'pool');
234 if (defined($pool)) {
235 $rpcenv->check_pool_exist($pool);
236 $rpcenv->check_perm_modify($authuser, "/pool/$pool");
239 if ($rpcenv->check($authuser, "/vms/$vmid", ['VM.Allocate'], 1)) {
241 } elsif ($pool && $rpcenv->check($authuser, "/pool/$pool", ['VM.Allocate'], 1)) {
243 } elsif ($restore && $force && $same_container_exists &&
244 $rpcenv->check($authuser, "/vms/$vmid", ['VM.Backup'], 1)) {
245 # OK: user has VM.Backup permissions, and want to restore an existing VM
247 # we don't want to restore a container-provided FW conf in this case
248 # since the user is lacking permission to configure the container's FW
249 $skip_fw_config_restore = 1;
254 my $ostemplate = extract_param
($param, 'ostemplate');
255 my $storage = extract_param
($param, 'storage') // 'local';
257 PVE
::LXC
::check_ct_modify_config_perm
($rpcenv, $authuser, $vmid, $pool, $param, []);
259 my $storage_cfg = cfs_read_file
("storage.cfg");
262 if ($ostemplate eq '-') {
263 die "pipe requires cli environment\n"
264 if $rpcenv->{type
} ne 'cli';
265 die "pipe can only be used with restore tasks\n"
268 die "restore from pipe requires rootfs parameter\n" if !defined($param->{rootfs
});
270 PVE
::Storage
::check_volume_access
($rpcenv, $authuser, $storage_cfg, $vmid, $ostemplate);
271 $archive = $ostemplate;
275 my $check_and_activate_storage = sub {
278 my $scfg = PVE
::Storage
::storage_check_node
($storage_cfg, $sid, $node);
280 raise_param_exc
({ storage
=> "storage '$sid' does not support container directories"})
281 if !$scfg->{content
}->{rootdir
};
283 $rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);
285 PVE
::Storage
::activate_storage
($storage_cfg, $sid);
286 $used_storages{$sid} = 1;
291 my $is_root = $authuser eq 'root@pam';
293 my $no_disk_param = {};
295 my $storage_only_mode = 1;
296 foreach my $opt (keys %$param) {
297 my $value = $param->{$opt};
298 if ($opt eq 'rootfs' || $opt =~ m/^mp\d+$/) {
299 # allow to use simple numbers (add default storage in that case)
300 if ($value =~ m/^\d+(\.\d+)?$/) {
301 $mp_param->{$opt} = "$storage:$value";
303 $mp_param->{$opt} = $value;
305 $storage_only_mode = 0;
306 } elsif ($opt =~ m/^unused\d+$/) {
307 warn "ignoring '$opt', cannot create/restore with unused volume\n";
308 delete $param->{$opt};
310 $no_disk_param->{$opt} = $value;
314 die "mount points configured, but 'rootfs' not set - aborting\n"
315 if !$storage_only_mode && !defined($mp_param->{rootfs
});
317 # check storage access, activate storage
318 my $delayed_mp_param = {};
319 PVE
::LXC
::Config-
>foreach_volume($mp_param, sub {
320 my ($ms, $mountpoint) = @_;
322 my $volid = $mountpoint->{volume
};
323 my $mp = $mountpoint->{mp
};
325 if ($mountpoint->{type
} ne 'volume') { # bind or device
326 die "Only root can pass arbitrary filesystem paths.\n"
329 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid);
330 &$check_and_activate_storage($sid);
334 # check/activate default storage
335 &$check_and_activate_storage($storage) if !defined($mp_param->{rootfs
});
337 PVE
::LXC
::Config-
>update_pct_config($vmid, $conf, 0, $no_disk_param);
339 $conf->{unprivileged
} = 1 if $unprivileged;
341 my $emsg = $restore ?
"unable to restore CT $vmid -" : "unable to create CT $vmid -";
343 eval { PVE
::LXC
::Config-
>create_and_lock_config($vmid, $force) };
344 die "$emsg $@" if $@;
347 my $old_conf = PVE
::LXC
::Config-
>load_config($vmid);
352 my $orig_mp_param; # only used if $restore
354 die "can't overwrite running container\n" if PVE
::LXC
::check_running
($vmid);
355 if ($archive ne '-') {
357 print "recovering backed-up configuration from '$archive'\n";
358 ($orig_conf, $orig_mp_param) = PVE
::LXC
::Create
::recover_config
($storage_cfg, $archive, $vmid);
359 $was_template = delete $orig_conf->{template
};
360 # When we're root call 'restore_configuration' with restricted=0,
361 # causing it to restore the raw lxc entries, among which there may be
362 # 'lxc.idmap' entries. We need to make sure that the extracted contents
363 # of the container match up with the restored configuration afterwards:
364 $conf->{lxc
} = $orig_conf->{lxc
} if $is_root;
367 if ($storage_only_mode) {
369 if (!defined($orig_mp_param)) {
370 print "recovering backed-up configuration from '$archive'\n";
371 (undef, $orig_mp_param) = PVE
::LXC
::Create
::recover_config
($storage_cfg, $archive, $vmid);
373 $mp_param = $orig_mp_param;
374 die "rootfs configuration could not be recovered, please check and specify manually!\n"
375 if !defined($mp_param->{rootfs
});
376 PVE
::LXC
::Config-
>foreach_volume($mp_param, sub {
377 my ($ms, $mountpoint) = @_;
378 my $type = $mountpoint->{type
};
379 if ($type eq 'volume') {
380 die "unable to detect disk size - please specify $ms (size)\n"
381 if !defined($mountpoint->{size
});
382 my $disksize = $mountpoint->{size
} / (1024 * 1024 * 1024); # create_disks expects GB as unit size
383 delete $mountpoint->{size
};
384 $mountpoint->{volume
} = "$storage:$disksize";
385 $mp_param->{$ms} = PVE
::LXC
::Config-
>print_ct_mountpoint($mountpoint, $ms eq 'rootfs');
387 my $type = $mountpoint->{type
};
388 die "restoring rootfs to $type mount is only possible by specifying -rootfs manually!\n"
389 if ($ms eq 'rootfs');
390 die "restoring '$ms' to $type mount is only possible for root\n"
393 if ($mountpoint->{backup
}) {
394 warn "WARNING - unsupported configuration!\n";
395 warn "backup was enabled for $type mount point $ms ('$mountpoint->{mp}')\n";
396 warn "mount point configuration will be restored after archive extraction!\n";
397 warn "contained files will be restored to wrong directory!\n";
399 delete $mp_param->{$ms}; # actually delay bind/dev mps
400 $delayed_mp_param->{$ms} = PVE
::LXC
::Config-
>print_ct_mountpoint($mountpoint, $ms eq 'rootfs');
404 $mp_param->{rootfs
} = "$storage:4"; # defaults to 4GB
408 $vollist = PVE
::LXC
::create_disks
($storage_cfg, $vmid, $mp_param, $conf);
410 # we always have the 'create' lock so check for more than 1 entry
411 if (scalar(keys %$old_conf) > 1) {
412 # destroy old container volumes
413 PVE
::LXC
::destroy_lxc_container
($storage_cfg, $vmid, $old_conf, { lock => 'create' });
417 my $rootdir = PVE
::LXC
::mount_all
($vmid, $storage_cfg, $conf, 1);
418 $bwlimit = PVE
::Storage
::get_bandwidth_limit
('restore', [keys %used_storages], $bwlimit);
419 print "restoring '$archive' now..\n"
420 if $restore && $archive ne '-';
421 PVE
::LXC
::Create
::restore_archive
($storage_cfg, $archive, $rootdir, $conf, $ignore_unpack_errors, $bwlimit);
424 print "merging backed-up and given configuration..\n";
425 PVE
::LXC
::Create
::restore_configuration
($vmid, $storage_cfg, $archive, $rootdir, $conf, !$is_root, $unique, $skip_fw_config_restore);
426 my $lxc_setup = PVE
::LXC
::Setup-
>new($conf, $rootdir);
427 $lxc_setup->template_fixup($conf);
429 my $lxc_setup = PVE
::LXC
::Setup-
>new($conf, $rootdir); # detect OS
430 PVE
::LXC
::Config-
>write_config($vmid, $conf); # safe config (after OS detection)
431 $lxc_setup->post_create_hook($password, $ssh_keys);
435 PVE
::LXC
::umount_all
($vmid, $storage_cfg, $conf, $err ?
1 : 0);
436 PVE
::Storage
::deactivate_volumes
($storage_cfg, PVE
::LXC
::Config-
>get_vm_volumes($conf));
439 $conf->{hostname
} ||= "CT$vmid";
440 $conf->{memory
} ||= 512;
441 $conf->{swap
} //= 512;
442 foreach my $mp (keys %$delayed_mp_param) {
443 $conf->{$mp} = $delayed_mp_param->{$mp};
445 # If the template flag was set, we try to convert again to template after restore
447 print STDERR
"Convert restored container to template...\n";
448 PVE
::LXC
::template_create
($vmid, $conf);
449 $conf->{template
} = 1;
451 PVE
::LXC
::Config-
>write_config($vmid, $conf);
454 PVE
::LXC
::destroy_disks
($storage_cfg, $vollist);
455 eval { PVE
::LXC
::Config-
>destroy_config($vmid) };
459 PVE
::AccessControl
::add_vm_to_pool
($vmid, $pool) if $pool;
461 PVE
::API2
::LXC
::Status-
>vm_start({ vmid
=> $vmid, node
=> $node })
462 if $start_after_create;
465 my $workername = $restore ?
'vzrestore' : 'vzcreate';
466 my $realcmd = sub { PVE
::LXC
::Config-
>lock_config($vmid, $code); };
468 return $rpcenv->fork_worker($workername, $vmid, $authuser, $realcmd);
471 __PACKAGE__-
>register_method({
476 description
=> "Directory index",
481 additionalProperties
=> 0,
483 node
=> get_standard_option
('pve-node'),
484 vmid
=> get_standard_option
('pve-vmid'),
492 subdir
=> { type
=> 'string' },
495 links
=> [ { rel
=> 'child', href
=> "{subdir}" } ],
501 my $conf = PVE
::LXC
::Config-
>load_config($param->{vmid
});
504 { subdir
=> 'config' },
505 { subdir
=> 'pending' },
506 { subdir
=> 'status' },
507 { subdir
=> 'vncproxy' },
508 { subdir
=> 'termproxy' },
509 { subdir
=> 'vncwebsocket' },
510 { subdir
=> 'spiceproxy' },
511 { subdir
=> 'migrate' },
512 { subdir
=> 'clone' },
513 # { subdir => 'initlog' },
515 { subdir
=> 'rrddata' },
516 { subdir
=> 'firewall' },
517 { subdir
=> 'snapshot' },
518 { subdir
=> 'resize' },
525 __PACKAGE__-
>register_method({
527 path
=> '{vmid}/rrd',
529 protected
=> 1, # fixme: can we avoid that?
531 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
533 description
=> "Read VM RRD statistics (returns PNG)",
535 additionalProperties
=> 0,
537 node
=> get_standard_option
('pve-node'),
538 vmid
=> get_standard_option
('pve-vmid'),
540 description
=> "Specify the time frame you are interested in.",
542 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
545 description
=> "The list of datasources you want to display.",
546 type
=> 'string', format
=> 'pve-configid-list',
549 description
=> "The RRD consolidation function",
551 enum
=> [ 'AVERAGE', 'MAX' ],
559 filename
=> { type
=> 'string' },
565 return PVE
::RRD
::create_rrd_graph
(
566 "pve2-vm/$param->{vmid}", $param->{timeframe
},
567 $param->{ds
}, $param->{cf
});
571 __PACKAGE__-
>register_method({
573 path
=> '{vmid}/rrddata',
575 protected
=> 1, # fixme: can we avoid that?
577 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
579 description
=> "Read VM RRD statistics",
581 additionalProperties
=> 0,
583 node
=> get_standard_option
('pve-node'),
584 vmid
=> get_standard_option
('pve-vmid'),
586 description
=> "Specify the time frame you are interested in.",
588 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
591 description
=> "The RRD consolidation function",
593 enum
=> [ 'AVERAGE', 'MAX' ],
608 return PVE
::RRD
::create_rrd_data
(
609 "pve2-vm/$param->{vmid}", $param->{timeframe
}, $param->{cf
});
612 __PACKAGE__-
>register_method({
613 name
=> 'destroy_vm',
618 description
=> "Destroy the container (also delete all uses files).",
620 check
=> [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
623 additionalProperties
=> 0,
625 node
=> get_standard_option
('pve-node'),
626 vmid
=> get_standard_option
('pve-vmid', { completion
=> \
&PVE
::LXC
::complete_ctid_stopped
}),
629 description
=> "Force destroy, even if running.",
635 description
=> "Remove container from all related configurations."
636 ." For example, backup jobs, replication jobs or HA."
637 ." Related ACLs and Firewall entries will *always* be removed.",
641 'destroy-unreferenced-disks' => {
643 description
=> "If set, destroy additionally all disks with the VMID from all"
644 ." enabled storages which are not referenced in the config.",
655 my $rpcenv = PVE
::RPCEnvironment
::get
();
656 my $authuser = $rpcenv->get_user();
657 my $vmid = $param->{vmid
};
659 # test if container exists
661 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
662 my $early_checks = sub {
664 PVE
::LXC
::Config-
>check_protection($conf, "can't remove CT $vmid");
665 PVE
::LXC
::Config-
>check_lock($conf);
667 my $ha_managed = PVE
::HA
::Config
::service_is_configured
("ct:$vmid");
669 if (!$param->{purge
}) {
670 die "unable to remove CT $vmid - used in HA resources and purge parameter not set.\n"
673 # do not allow destroy if there are replication jobs without purge
674 my $repl_conf = PVE
::ReplicationConfig-
>new();
675 $repl_conf->check_for_existing_jobs($vmid);
681 $early_checks->($conf);
683 my $running_error_msg = "unable to destroy CT $vmid - container is running\n";
684 die $running_error_msg if !$param->{force
} && PVE
::LXC
::check_running
($vmid); # check early
687 # reload config after lock
688 $conf = PVE
::LXC
::Config-
>load_config($vmid);
689 my $ha_managed = $early_checks->($conf);
691 if (PVE
::LXC
::check_running
($vmid)) {
692 die $running_error_msg if !$param->{force
};
693 warn "forced to stop CT $vmid before destroying!\n";
695 PVE
::LXC
::vm_stop
($vmid, 1);
697 run_command
(['ha-manager', 'crm-command', 'stop', "ct:$vmid", '120']);
701 my $storage_cfg = cfs_read_file
("storage.cfg");
702 PVE
::LXC
::destroy_lxc_container
(
706 { lock => 'destroyed' },
707 $param->{'destroy-unreferenced-disks'},
710 PVE
::AccessControl
::remove_vm_access
($vmid);
711 PVE
::Firewall
::remove_vmfw_conf
($vmid);
712 if ($param->{purge
}) {
713 print "purging CT $vmid from related configurations..\n";
714 PVE
::ReplicationConfig
::remove_vmid_jobs
($vmid);
715 PVE
::VZDump
::Plugin
::remove_vmid_from_backup_jobs
($vmid);
718 PVE
::HA
::Config
::delete_service_from_config
("ct:$vmid");
719 print "NOTE: removed CT $vmid from HA resource configuration.\n";
723 # only now remove the zombie config, else we can have reuse race
724 PVE
::LXC
::Config-
>destroy_config($vmid);
727 my $realcmd = sub { PVE
::LXC
::Config-
>lock_config($vmid, $code); };
729 return $rpcenv->fork_worker('vzdestroy', $vmid, $authuser, $realcmd);
734 __PACKAGE__-
>register_method ({
736 path
=> '{vmid}/vncproxy',
740 check
=> ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
742 description
=> "Creates a TCP VNC proxy connections.",
744 additionalProperties
=> 0,
746 node
=> get_standard_option
('pve-node'),
747 vmid
=> get_standard_option
('pve-vmid'),
751 description
=> "use websocket instead of standard VNC.",
755 description
=> "sets the width of the console in pixels.",
762 description
=> "sets the height of the console in pixels.",
770 additionalProperties
=> 0,
772 user
=> { type
=> 'string' },
773 ticket
=> { type
=> 'string' },
774 cert
=> { type
=> 'string' },
775 port
=> { type
=> 'integer' },
776 upid
=> { type
=> 'string' },
782 my $rpcenv = PVE
::RPCEnvironment
::get
();
784 my $authuser = $rpcenv->get_user();
786 my $vmid = $param->{vmid
};
787 my $node = $param->{node
};
789 my $authpath = "/vms/$vmid";
791 my $ticket = PVE
::AccessControl
::assemble_vnc_ticket
($authuser, $authpath);
793 $sslcert = PVE
::Tools
::file_get_contents
("/etc/pve/pve-root-ca.pem", 8192)
796 my ($remip, $family);
798 if ($node ne PVE
::INotify
::nodename
()) {
799 ($remip, $family) = PVE
::Cluster
::remote_node_ip
($node);
801 $family = PVE
::Tools
::get_host_address_family
($node);
804 my $port = PVE
::Tools
::next_vnc_port
($family);
806 # NOTE: vncterm VNC traffic is already TLS encrypted,
807 # so we select the fastest chipher here (or 'none'?)
808 my $remcmd = $remip ?
809 ['/usr/bin/ssh', '-e', 'none', '-t', $remip] : [];
811 my $conf = PVE
::LXC
::Config-
>load_config($vmid, $node);
812 my $concmd = PVE
::LXC
::get_console_command
($vmid, $conf, -1);
814 my $shcmd = [ '/usr/bin/dtach', '-A',
815 "/var/run/dtach/vzctlconsole$vmid",
816 '-r', 'winch', '-z', @$concmd];
821 syslog
('info', "starting lxc vnc proxy $upid\n");
825 my $cmd = ['/usr/bin/vncterm', '-rfbport', $port,
826 '-timeout', $timeout, '-authpath', $authpath,
827 '-perm', 'VM.Console'];
829 if ($param->{width
}) {
830 push @$cmd, '-width', $param->{width
};
833 if ($param->{height
}) {
834 push @$cmd, '-height', $param->{height
};
837 if ($param->{websocket
}) {
838 $ENV{PVE_VNC_TICKET
} = $ticket; # pass ticket to vncterm
839 push @$cmd, '-notls', '-listen', 'localhost';
842 push @$cmd, '-c', @$remcmd, @$shcmd;
844 run_command
($cmd, keeplocale
=> 1);
849 my $upid = $rpcenv->fork_worker('vncproxy', $vmid, $authuser, $realcmd);
851 PVE
::Tools
::wait_for_vnc_port
($port);
862 __PACKAGE__-
>register_method ({
864 path
=> '{vmid}/termproxy',
868 check
=> ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
870 description
=> "Creates a TCP proxy connection.",
872 additionalProperties
=> 0,
874 node
=> get_standard_option
('pve-node'),
875 vmid
=> get_standard_option
('pve-vmid'),
879 additionalProperties
=> 0,
881 user
=> { type
=> 'string' },
882 ticket
=> { type
=> 'string' },
883 port
=> { type
=> 'integer' },
884 upid
=> { type
=> 'string' },
890 my $rpcenv = PVE
::RPCEnvironment
::get
();
892 my $authuser = $rpcenv->get_user();
894 my $vmid = $param->{vmid
};
895 my $node = $param->{node
};
897 my $authpath = "/vms/$vmid";
899 my $ticket = PVE
::AccessControl
::assemble_vnc_ticket
($authuser, $authpath);
901 my ($remip, $family);
903 if ($node ne 'localhost' && $node ne PVE
::INotify
::nodename
()) {
904 ($remip, $family) = PVE
::Cluster
::remote_node_ip
($node);
906 $family = PVE
::Tools
::get_host_address_family
($node);
909 my $port = PVE
::Tools
::next_vnc_port
($family);
911 my $remcmd = $remip ?
912 ['/usr/bin/ssh', '-e', 'none', '-t', $remip, '--'] : [];
914 my $conf = PVE
::LXC
::Config-
>load_config($vmid, $node);
915 my $concmd = PVE
::LXC
::get_console_command
($vmid, $conf, -1);
917 my $shcmd = [ '/usr/bin/dtach', '-A',
918 "/var/run/dtach/vzctlconsole$vmid",
919 '-r', 'winch', '-z', @$concmd];
924 syslog
('info', "starting lxc termproxy $upid\n");
926 my $cmd = ['/usr/bin/termproxy', $port, '--path', $authpath,
927 '--perm', 'VM.Console', '--'];
928 push @$cmd, @$remcmd, @$shcmd;
930 PVE
::Tools
::run_command
($cmd);
933 my $upid = $rpcenv->fork_worker('vncproxy', $vmid, $authuser, $realcmd, 1);
935 PVE
::Tools
::wait_for_vnc_port
($port);
945 __PACKAGE__-
>register_method({
946 name
=> 'vncwebsocket',
947 path
=> '{vmid}/vncwebsocket',
950 description
=> "You also need to pass a valid ticket (vncticket).",
951 check
=> ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
953 description
=> "Opens a weksocket for VNC traffic.",
955 additionalProperties
=> 0,
957 node
=> get_standard_option
('pve-node'),
958 vmid
=> get_standard_option
('pve-vmid'),
960 description
=> "Ticket from previous call to vncproxy.",
965 description
=> "Port number returned by previous vncproxy call.",
975 port
=> { type
=> 'string' },
981 my $rpcenv = PVE
::RPCEnvironment
::get
();
983 my $authuser = $rpcenv->get_user();
985 my $authpath = "/vms/$param->{vmid}";
987 PVE
::AccessControl
::verify_vnc_ticket
($param->{vncticket
}, $authuser, $authpath);
989 my $port = $param->{port
};
991 return { port
=> $port };
994 __PACKAGE__-
>register_method ({
995 name
=> 'spiceproxy',
996 path
=> '{vmid}/spiceproxy',
1001 check
=> ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
1003 description
=> "Returns a SPICE configuration to connect to the CT.",
1005 additionalProperties
=> 0,
1007 node
=> get_standard_option
('pve-node'),
1008 vmid
=> get_standard_option
('pve-vmid'),
1009 proxy
=> get_standard_option
('spice-proxy', { optional
=> 1 }),
1012 returns
=> get_standard_option
('remote-viewer-config'),
1016 my $vmid = $param->{vmid
};
1017 my $node = $param->{node
};
1018 my $proxy = $param->{proxy
};
1020 my $authpath = "/vms/$vmid";
1021 my $permissions = 'VM.Console';
1023 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
1025 die "CT $vmid not running\n" if !PVE
::LXC
::check_running
($vmid);
1027 my $concmd = PVE
::LXC
::get_console_command
($vmid, $conf);
1029 my $shcmd = ['/usr/bin/dtach', '-A',
1030 "/var/run/dtach/vzctlconsole$vmid",
1031 '-r', 'winch', '-z', @$concmd];
1033 my $title = "CT $vmid";
1035 return PVE
::API2Tools
::run_spiceterm
($authpath, $permissions, $vmid, $node, $proxy, $title, $shcmd);
1039 __PACKAGE__-
>register_method({
1040 name
=> 'migrate_vm',
1041 path
=> '{vmid}/migrate',
1045 description
=> "Migrate the container to another node. Creates a new migration task.",
1047 check
=> ['perm', '/vms/{vmid}', [ 'VM.Migrate' ]],
1050 additionalProperties
=> 0,
1052 node
=> get_standard_option
('pve-node'),
1053 vmid
=> get_standard_option
('pve-vmid', { completion
=> \
&PVE
::LXC
::complete_ctid
}),
1054 target
=> get_standard_option
('pve-node', {
1055 description
=> "Target node.",
1056 completion
=> \
&PVE
::Cluster
::complete_migration_target
,
1060 description
=> "Use online/live migration.",
1065 description
=> "Use restart migration",
1070 description
=> "Timeout in seconds for shutdown for restart migration",
1076 description
=> "Force migration despite local bind / device" .
1077 " mounts. NOTE: deprecated, use 'shared' property of mount point instead.",
1081 description
=> "Override I/O bandwidth limit (in KiB/s).",
1085 default => 'migrate limit from datacenter or storage config',
1091 description
=> "the task ID.",
1096 my $rpcenv = PVE
::RPCEnvironment
::get
();
1098 my $authuser = $rpcenv->get_user();
1100 my $target = extract_param
($param, 'target');
1102 my $localnode = PVE
::INotify
::nodename
();
1103 raise_param_exc
({ target
=> "target is local node."}) if $target eq $localnode;
1105 PVE
::Cluster
::check_cfs_quorum
();
1107 PVE
::Cluster
::check_node_exists
($target);
1109 my $targetip = PVE
::Cluster
::remote_node_ip
($target);
1111 my $vmid = extract_param
($param, 'vmid');
1114 PVE
::LXC
::Config-
>load_config($vmid);
1116 # try to detect errors early
1117 if (PVE
::LXC
::check_running
($vmid)) {
1118 die "can't migrate running container without --online or --restart\n"
1119 if !$param->{online
} && !$param->{restart
};
1122 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid) && $rpcenv->{type
} ne 'ha') {
1127 my $service = "ct:$vmid";
1129 my $cmd = ['ha-manager', 'migrate', $service, $target];
1131 print "Requesting HA migration for CT $vmid to node $target\n";
1133 PVE
::Tools
::run_command
($cmd);
1138 return $rpcenv->fork_worker('hamigrate', $vmid, $authuser, $hacmd);
1143 PVE
::LXC
::Migrate-
>migrate($target, $targetip, $vmid, $param);
1147 return PVE
::GuestHelpers
::guest_migration_lock
($vmid, 10, $realcmd);
1150 return $rpcenv->fork_worker('vzmigrate', $vmid, $authuser, $worker);
1154 __PACKAGE__-
>register_method({
1155 name
=> 'vm_feature',
1156 path
=> '{vmid}/feature',
1160 description
=> "Check if feature for virtual machine is available.",
1162 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
1165 additionalProperties
=> 0,
1167 node
=> get_standard_option
('pve-node'),
1168 vmid
=> get_standard_option
('pve-vmid'),
1170 description
=> "Feature to check.",
1172 enum
=> [ 'snapshot', 'clone', 'copy' ],
1174 snapname
=> get_standard_option
('pve-snapshot-name', {
1182 hasFeature
=> { type
=> 'boolean' },
1185 #items => { type => 'string' },
1192 my $node = extract_param
($param, 'node');
1194 my $vmid = extract_param
($param, 'vmid');
1196 my $snapname = extract_param
($param, 'snapname');
1198 my $feature = extract_param
($param, 'feature');
1200 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
1203 my $snap = $conf->{snapshots
}->{$snapname};
1204 die "snapshot '$snapname' does not exist\n" if !defined($snap);
1207 my $storage_cfg = PVE
::Storage
::config
();
1208 #Maybe include later
1209 #my $nodelist = PVE::LXC::shared_nodes($conf, $storage_cfg);
1210 my $hasFeature = PVE
::LXC
::Config-
>has_feature($feature, $conf, $storage_cfg, $snapname);
1213 hasFeature
=> $hasFeature,
1214 #nodes => [ keys %$nodelist ],
1218 __PACKAGE__-
>register_method({
1220 path
=> '{vmid}/template',
1224 description
=> "Create a Template.",
1226 description
=> "You need 'VM.Allocate' permissions on /vms/{vmid}",
1227 check
=> [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
1230 additionalProperties
=> 0,
1232 node
=> get_standard_option
('pve-node'),
1233 vmid
=> get_standard_option
('pve-vmid', { completion
=> \
&PVE
::LXC
::complete_ctid_stopped
}),
1236 returns
=> { type
=> 'null'},
1240 my $rpcenv = PVE
::RPCEnvironment
::get
();
1242 my $authuser = $rpcenv->get_user();
1244 my $node = extract_param
($param, 'node');
1246 my $vmid = extract_param
($param, 'vmid');
1248 my $updatefn = sub {
1250 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
1251 PVE
::LXC
::Config-
>check_lock($conf);
1253 die "unable to create template, because CT contains snapshots\n"
1254 if $conf->{snapshots
} && scalar(keys %{$conf->{snapshots
}});
1256 die "you can't convert a template to a template\n"
1257 if PVE
::LXC
::Config-
>is_template($conf);
1259 die "you can't convert a CT to template if the CT is running\n"
1260 if PVE
::LXC
::check_running
($vmid);
1263 PVE
::LXC
::template_create
($vmid, $conf);
1265 $conf->{template
} = 1;
1267 PVE
::LXC
::Config-
>write_config($vmid, $conf);
1268 # and remove lxc config
1269 PVE
::LXC
::update_lxc_config
($vmid, $conf);
1272 return $rpcenv->fork_worker('vztemplate', $vmid, $authuser, $realcmd);
1275 PVE
::LXC
::Config-
>lock_config($vmid, $updatefn);
1280 __PACKAGE__-
>register_method({
1282 path
=> '{vmid}/clone',
1286 description
=> "Create a container clone/copy",
1288 description
=> "You need 'VM.Clone' permissions on /vms/{vmid}, " .
1289 "and 'VM.Allocate' permissions " .
1290 "on /vms/{newid} (or on the VM pool /pool/{pool}). You also need " .
1291 "'Datastore.AllocateSpace' on any used storage.",
1294 ['perm', '/vms/{vmid}', [ 'VM.Clone' ]],
1296 [ 'perm', '/vms/{newid}', ['VM.Allocate']],
1297 [ 'perm', '/pool/{pool}', ['VM.Allocate'], require_param
=> 'pool'],
1302 additionalProperties
=> 0,
1304 node
=> get_standard_option
('pve-node'),
1305 vmid
=> get_standard_option
('pve-vmid', { completion
=> \
&PVE
::LXC
::complete_ctid
}),
1306 newid
=> get_standard_option
('pve-vmid', {
1307 completion
=> \
&PVE
::Cluster
::complete_next_vmid
,
1308 description
=> 'VMID for the clone.' }),
1311 type
=> 'string', format
=> 'dns-name',
1312 description
=> "Set a hostname for the new CT.",
1317 description
=> "Description for the new CT.",
1321 type
=> 'string', format
=> 'pve-poolid',
1322 description
=> "Add the new CT to the specified pool.",
1324 snapname
=> get_standard_option
('pve-snapshot-name', {
1327 storage
=> get_standard_option
('pve-storage-id', {
1328 description
=> "Target storage for full clone.",
1334 description
=> "Create a full copy of all disks. This is always done when " .
1335 "you clone a normal CT. For CT templates, we try to create a linked clone by default.",
1337 target
=> get_standard_option
('pve-node', {
1338 description
=> "Target node. Only allowed if the original VM is on shared storage.",
1342 description
=> "Override I/O bandwidth limit (in KiB/s).",
1346 default => 'clone limit from datacenter or storage config',
1356 my $rpcenv = PVE
::RPCEnvironment
::get
();
1358 my $authuser = $rpcenv->get_user();
1360 my $node = extract_param
($param, 'node');
1362 my $vmid = extract_param
($param, 'vmid');
1364 my $newid = extract_param
($param, 'newid');
1366 my $pool = extract_param
($param, 'pool');
1368 if (defined($pool)) {
1369 $rpcenv->check_pool_exist($pool);
1372 my $snapname = extract_param
($param, 'snapname');
1374 my $storage = extract_param
($param, 'storage');
1376 my $target = extract_param
($param, 'target');
1378 my $localnode = PVE
::INotify
::nodename
();
1380 undef $target if $target && ($target eq $localnode || $target eq 'localhost');
1382 PVE
::Cluster
::check_node_exists
($target) if $target;
1384 my $storecfg = PVE
::Storage
::config
();
1387 # check if storage is enabled on local node
1388 PVE
::Storage
::storage_check_enabled
($storecfg, $storage);
1390 # check if storage is available on target node
1391 PVE
::Storage
::storage_check_node
($storecfg, $storage, $target);
1392 # clone only works if target storage is shared
1393 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storage);
1394 die "can't clone to non-shared storage '$storage'\n" if !$scfg->{shared
};
1398 PVE
::Cluster
::check_cfs_quorum
();
1402 my $mountpoints = {};
1407 PVE
::LXC
::Config-
>lock_config($vmid, sub {
1408 my $src_conf = PVE
::LXC
::Config-
>set_lock($vmid, 'disk');
1410 $running = PVE
::LXC
::check_running
($vmid) || 0;
1412 my $full = extract_param
($param, 'full');
1413 if (!defined($full)) {
1414 $full = !PVE
::LXC
::Config-
>is_template($src_conf);
1416 die "parameter 'storage' not allowed for linked clones\n" if defined($storage) && !$full;
1419 die "snapshot '$snapname' does not exist\n"
1420 if $snapname && !defined($src_conf->{snapshots
}->{$snapname});
1423 my $src_conf = $snapname ?
$src_conf->{snapshots
}->{$snapname} : $src_conf;
1425 $conffile = PVE
::LXC
::Config-
>config_file($newid);
1426 die "unable to create CT $newid: config file already exists\n"
1430 foreach my $opt (keys %$src_conf) {
1431 next if $opt =~ m/^unused\d+$/;
1433 my $value = $src_conf->{$opt};
1435 if (($opt eq 'rootfs') || ($opt =~ m/^mp\d+$/)) {
1436 my $mp = PVE
::LXC
::Config-
>parse_volume($opt, $value);
1438 if ($mp->{type
} eq 'volume') {
1439 my $volid = $mp->{volume
};
1441 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid);
1442 $sid = $storage if defined($storage);
1443 my $scfg = PVE
::Storage
::storage_config
($storecfg, $sid);
1444 if (!$scfg->{shared
}) {
1446 warn "found non-shared volume: $volid\n" if $target;
1449 $rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);
1452 die "Cannot do full clones on a running container without snapshots\n"
1453 if $running && !defined($snapname);
1454 $fullclone->{$opt} = 1;
1456 # not full means clone instead of copy
1457 die "Linked clone feature for '$volid' is not available\n"
1458 if !PVE
::Storage
::volume_has_feature
($storecfg, 'clone', $volid, $snapname, $running, {'valid_target_formats' => ['raw', 'subvol']});
1461 $mountpoints->{$opt} = $mp;
1462 push @$vollist, $volid;
1465 # TODO: allow bind mounts?
1466 die "unable to clone mountpint '$opt' (type $mp->{type})\n";
1468 } elsif ($opt =~ m/^net(\d+)$/) {
1469 # always change MAC! address
1470 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
1471 my $net = PVE
::LXC
::Config-
>parse_lxc_network($value);
1472 $net->{hwaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
});
1473 $newconf->{$opt} = PVE
::LXC
::Config-
>print_lxc_network($net);
1475 # copy everything else
1476 $newconf->{$opt} = $value;
1479 die "can't clone CT to node '$target' (CT uses local storage)\n"
1480 if $target && !$sharedvm;
1482 # Replace the 'disk' lock with a 'create' lock.
1483 $newconf->{lock} = 'create';
1485 delete $newconf->{snapshots
};
1486 delete $newconf->{pending
};
1487 delete $newconf->{template
};
1488 if ($param->{hostname
}) {
1489 $newconf->{hostname
} = $param->{hostname
};
1492 if ($param->{description
}) {
1493 $newconf->{description
} = $param->{description
};
1496 # create empty/temp config - this fails if CT already exists on other node
1497 PVE
::LXC
::Config-
>write_config($newid, $newconf);
1500 eval { PVE
::LXC
::Config-
>remove_lock($vmid, 'disk') };
1506 my $update_conf = sub {
1507 my ($key, $value) = @_;
1508 return PVE
::LXC
::Config-
>lock_config($newid, sub {
1509 my $conf = PVE
::LXC
::Config-
>load_config($newid);
1510 die "Lost 'create' config lock, aborting.\n"
1511 if !PVE
::LXC
::Config-
>has_lock($conf, 'create');
1512 $conf->{$key} = $value;
1513 PVE
::LXC
::Config-
>write_config($newid, $conf);
1520 my $newvollist = [];
1522 my $verify_running = PVE
::LXC
::check_running
($vmid) || 0;
1523 die "unexpected state change\n" if $verify_running != $running;
1529 local $SIG{HUP
} = sub { die "interrupted by signal\n"; };
1531 PVE
::Storage
::activate_volumes
($storecfg, $vollist, $snapname);
1532 my $bwlimit = extract_param
($param, 'bwlimit');
1534 foreach my $opt (keys %$mountpoints) {
1535 my $mp = $mountpoints->{$opt};
1536 my $volid = $mp->{volume
};
1539 if ($fullclone->{$opt}) {
1540 print "create full clone of mountpoint $opt ($volid)\n";
1541 my $source_storage = PVE
::Storage
::parse_volume_id
($volid);
1542 my $target_storage = $storage // $source_storage;
1543 my $clonelimit = PVE
::Storage
::get_bandwidth_limit
('clone', [$source_storage, $target_storage], $bwlimit);
1544 $newvolid = PVE
::LXC
::copy_volume
($mp, $newid, $target_storage, $storecfg, $newconf, $snapname, $clonelimit);
1546 print "create linked clone of mount point $opt ($volid)\n";
1547 $newvolid = PVE
::Storage
::vdisk_clone
($storecfg, $volid, $newid, $snapname);
1550 push @$newvollist, $newvolid;
1551 $mp->{volume
} = $newvolid;
1553 $update_conf->($opt, PVE
::LXC
::Config-
>print_ct_mountpoint($mp, $opt eq 'rootfs'));
1556 PVE
::AccessControl
::add_vm_to_pool
($newid, $pool) if $pool;
1557 PVE
::LXC
::Config-
>remove_lock($newid, 'create');
1560 # always deactivate volumes - avoid lvm LVs to be active on several nodes
1561 PVE
::Storage
::deactivate_volumes
($storecfg, $vollist, $snapname) if !$running;
1562 PVE
::Storage
::deactivate_volumes
($storecfg, $newvollist);
1564 my $newconffile = PVE
::LXC
::Config-
>config_file($newid, $target);
1565 die "Failed to move config to node '$target' - rename failed: $!\n"
1566 if !rename($conffile, $newconffile);
1571 # Unlock the source config in any case:
1572 eval { PVE
::LXC
::Config-
>remove_lock($vmid, 'disk') };
1576 # Now cleanup the config & disks:
1579 sleep 1; # some storages like rbd need to wait before release volume - really?
1581 foreach my $volid (@$newvollist) {
1582 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid); };
1585 die "clone failed: $err";
1591 PVE
::Firewall
::clone_vmfw_conf
($vmid, $newid);
1592 return $rpcenv->fork_worker('vzclone', $vmid, $authuser, $realcmd);
1596 __PACKAGE__-
>register_method({
1597 name
=> 'resize_vm',
1598 path
=> '{vmid}/resize',
1602 description
=> "Resize a container mount point.",
1604 check
=> ['perm', '/vms/{vmid}', ['VM.Config.Disk'], any
=> 1],
1607 additionalProperties
=> 0,
1609 node
=> get_standard_option
('pve-node'),
1610 vmid
=> get_standard_option
('pve-vmid', { completion
=> \
&PVE
::LXC
::complete_ctid
}),
1613 description
=> "The disk you want to resize.",
1614 enum
=> [PVE
::LXC
::Config-
>valid_volume_keys()],
1618 pattern
=> '\+?\d+(\.\d+)?[KMGT]?',
1619 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.",
1623 description
=> 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.',
1631 description
=> "the task ID.",
1636 my $rpcenv = PVE
::RPCEnvironment
::get
();
1638 my $authuser = $rpcenv->get_user();
1640 my $node = extract_param
($param, 'node');
1642 my $vmid = extract_param
($param, 'vmid');
1644 my $digest = extract_param
($param, 'digest');
1646 my $sizestr = extract_param
($param, 'size');
1647 my $ext = ($sizestr =~ s/^\+//);
1648 my $newsize = PVE
::JSONSchema
::parse_size
($sizestr);
1649 die "invalid size string" if !defined($newsize);
1651 die "no options specified\n" if !scalar(keys %$param);
1653 PVE
::LXC
::check_ct_modify_config_perm
($rpcenv, $authuser, $vmid, undef, $param, []);
1655 my $storage_cfg = cfs_read_file
("storage.cfg");
1659 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
1660 PVE
::LXC
::Config-
>check_lock($conf);
1662 PVE
::Tools
::assert_if_modified
($digest, $conf->{digest
});
1664 my $running = PVE
::LXC
::check_running
($vmid);
1666 my $disk = $param->{disk
};
1667 my $mp = PVE
::LXC
::Config-
>parse_volume($disk, $conf->{$disk});
1669 my $volid = $mp->{volume
};
1671 my (undef, undef, $owner, undef, undef, undef, $format) =
1672 PVE
::Storage
::parse_volname
($storage_cfg, $volid);
1674 die "can't resize mount point owned by another container ($owner)"
1677 die "can't resize volume: $disk if snapshot exists\n"
1678 if %{$conf->{snapshots
}} && $format eq 'qcow2';
1680 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid);
1682 $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
1684 PVE
::Storage
::activate_volumes
($storage_cfg, [$volid]);
1686 my $size = PVE
::Storage
::volume_size_info
($storage_cfg, $volid, 5);
1688 die "Could not determine current size of volume '$volid'\n" if !defined($size);
1690 $newsize += $size if $ext;
1691 $newsize = int($newsize);
1693 die "unable to shrink disk size\n" if $newsize < $size;
1695 die "disk is already at specified size\n" if $size == $newsize;
1697 PVE
::Cluster
::log_msg
('info', $authuser, "update CT $vmid: resize --disk $disk --size $sizestr");
1699 # Note: PVE::Storage::volume_resize doesn't do anything if $running=1, so
1700 # we pass 0 here (parameter only makes sense for qemu)
1701 PVE
::Storage
::volume_resize
($storage_cfg, $volid, $newsize, 0);
1703 $mp->{size
} = $newsize;
1704 $conf->{$disk} = PVE
::LXC
::Config-
>print_ct_mountpoint($mp, $disk eq 'rootfs');
1706 PVE
::LXC
::Config-
>write_config($vmid, $conf);
1708 if ($format eq 'raw') {
1709 # we need to ensure that the volume is mapped, if not needed this is a NOP
1710 my $path = PVE
::Storage
::map_volume
($storage_cfg, $volid);
1711 $path = PVE
::Storage
::path
($storage_cfg, $volid) if !defined($path);
1715 my $use_loopdev = (PVE
::LXC
::mountpoint_mount_path
($mp, $storage_cfg))[1];
1716 $path = PVE
::LXC
::query_loopdev
($path) if $use_loopdev;
1717 die "internal error: CT running but mount point not attached to a loop device"
1719 PVE
::Tools
::run_command
(['losetup', '--set-capacity', $path]) if $use_loopdev;
1721 # In order for resize2fs to know that we need online-resizing a mountpoint needs
1722 # to be visible to it in its namespace.
1723 # To not interfere with the rest of the system we unshare the current mount namespace,
1724 # mount over /tmp and then run resize2fs.
1726 # interestingly we don't need to e2fsck on mounted systems...
1727 my $quoted = PVE
::Tools
::shellquote
($path);
1728 my $cmd = "mount --make-rprivate / && mount $quoted /tmp && resize2fs $quoted";
1730 PVE
::Tools
::run_command
(['unshare', '-m', '--', 'sh', '-c', $cmd]);
1732 warn "Failed to update the container's filesystem: $@\n" if $@;
1735 PVE
::Tools
::run_command
(['e2fsck', '-f', '-y', $path]);
1736 PVE
::Tools
::run_command
(['resize2fs', $path]);
1738 warn "Failed to update the container's filesystem: $@\n" if $@;
1740 # always un-map if not running, this is a NOP if not needed
1741 PVE
::Storage
::unmap_volume
($storage_cfg, $volid);
1746 return $rpcenv->fork_worker('resize', $vmid, $authuser, $realcmd);
1749 return PVE
::LXC
::Config-
>lock_config($vmid, $code);;
1752 __PACKAGE__-
>register_method({
1753 name
=> 'move_volume',
1754 path
=> '{vmid}/move_volume',
1758 description
=> "Move a rootfs-/mp-volume to a different storage",
1760 description
=> "You need 'VM.Config.Disk' permissions on /vms/{vmid}, " .
1761 "and 'Datastore.AllocateSpace' permissions on the storage.",
1764 ['perm', '/vms/{vmid}', [ 'VM.Config.Disk' ]],
1765 ['perm', '/storage/{storage}', [ 'Datastore.AllocateSpace' ]],
1769 additionalProperties
=> 0,
1771 node
=> get_standard_option
('pve-node'),
1772 vmid
=> get_standard_option
('pve-vmid', { completion
=> \
&PVE
::LXC
::complete_ctid
}),
1775 enum
=> [ PVE
::LXC
::Config-
>valid_volume_keys() ],
1776 description
=> "Volume which will be moved.",
1778 storage
=> get_standard_option
('pve-storage-id', {
1779 description
=> "Target Storage.",
1780 completion
=> \
&PVE
::Storage
::complete_storage_enabled
,
1784 description
=> "Delete the original volume after successful copy. By default the original is kept as an unused volume entry.",
1790 description
=> 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.',
1795 description
=> "Override I/O bandwidth limit (in KiB/s).",
1799 default => 'clone limit from datacenter or storage config',
1809 my $rpcenv = PVE
::RPCEnvironment
::get
();
1811 my $authuser = $rpcenv->get_user();
1813 my $vmid = extract_param
($param, 'vmid');
1815 my $storage = extract_param
($param, 'storage');
1817 my $mpkey = extract_param
($param, 'volume');
1819 my $lockname = 'disk';
1821 my ($mpdata, $old_volid);
1823 PVE
::LXC
::Config-
>lock_config($vmid, sub {
1824 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
1825 PVE
::LXC
::Config-
>check_lock($conf);
1827 die "cannot move volumes of a running container\n" if PVE
::LXC
::check_running
($vmid);
1829 $mpdata = PVE
::LXC
::Config-
>parse_volume($mpkey, $conf->{$mpkey});
1830 $old_volid = $mpdata->{volume
};
1832 die "you can't move a volume with snapshots and delete the source\n"
1833 if $param->{delete} && PVE
::LXC
::Config-
>is_volume_in_use_by_snapshots($conf, $old_volid);
1835 PVE
::Tools
::assert_if_modified
($param->{digest
}, $conf->{digest
});
1837 PVE
::LXC
::Config-
>set_lock($vmid, $lockname);
1842 PVE
::Cluster
::log_msg
('info', $authuser, "move volume CT $vmid: move --volume $mpkey --storage $storage");
1844 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
1845 my $storage_cfg = PVE
::Storage
::config
();
1850 PVE
::Storage
::activate_volumes
($storage_cfg, [ $old_volid ]);
1851 my $bwlimit = extract_param
($param, 'bwlimit');
1852 my $source_storage = PVE
::Storage
::parse_volume_id
($old_volid);
1853 my $movelimit = PVE
::Storage
::get_bandwidth_limit
('move', [$source_storage, $storage], $bwlimit);
1854 $new_volid = PVE
::LXC
::copy_volume
($mpdata, $vmid, $storage, $storage_cfg, $conf, undef, $movelimit);
1855 if (PVE
::LXC
::Config-
>is_template($conf)) {
1856 PVE
::Storage
::activate_volumes
($storage_cfg, [ $new_volid ]);
1857 my $template_volid = PVE
::Storage
::vdisk_create_base
($storage_cfg, $new_volid);
1858 $mpdata->{volume
} = $template_volid;
1860 $mpdata->{volume
} = $new_volid;
1863 PVE
::LXC
::Config-
>lock_config($vmid, sub {
1864 my $digest = $conf->{digest
};
1865 $conf = PVE
::LXC
::Config-
>load_config($vmid);
1866 PVE
::Tools
::assert_if_modified
($digest, $conf->{digest
});
1868 $conf->{$mpkey} = PVE
::LXC
::Config-
>print_ct_mountpoint($mpdata, $mpkey eq 'rootfs');
1870 PVE
::LXC
::Config-
>add_unused_volume($conf, $old_volid) if !$param->{delete};
1872 PVE
::LXC
::Config-
>write_config($vmid, $conf);
1876 # try to deactivate volumes - avoid lvm LVs to be active on several nodes
1877 PVE
::Storage
::deactivate_volumes
($storage_cfg, [ $new_volid ])
1883 PVE
::Storage
::vdisk_free
($storage_cfg, $new_volid)
1884 if defined($new_volid);
1890 if ($param->{delete}) {
1892 PVE
::Storage
::deactivate_volumes
($storage_cfg, [ $old_volid ]);
1893 PVE
::Storage
::vdisk_free
($storage_cfg, $old_volid);
1897 PVE
::LXC
::Config-
>lock_config($vmid, sub {
1898 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
1899 PVE
::LXC
::Config-
>add_unused_volume($conf, $old_volid);
1900 PVE
::LXC
::Config-
>write_config($vmid, $conf);
1906 eval { PVE
::LXC
::Config-
>remove_lock($vmid, $lockname) };
1911 $rpcenv->fork_worker('move_volume', $vmid, $authuser, $realcmd);
1914 eval { PVE
::LXC
::Config-
>remove_lock($vmid, $lockname) };
1921 __PACKAGE__-
>register_method({
1922 name
=> 'vm_pending',
1923 path
=> '{vmid}/pending',
1926 description
=> 'Get container configuration, including pending changes.',
1928 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
1931 additionalProperties
=> 0,
1933 node
=> get_standard_option
('pve-node'),
1934 vmid
=> get_standard_option
('pve-vmid', { completion
=> \
&PVE
::LXC
::complete_ctid
}),
1943 description
=> 'Configuration option name.',
1947 description
=> 'Current value.',
1952 description
=> 'Pending value.',
1957 description
=> "Indicates a pending delete request if present and not 0.",
1969 my $conf = PVE
::LXC
::Config-
>load_config($param->{vmid
});
1971 my $pending_delete_hash = PVE
::LXC
::Config-
>parse_pending_delete($conf->{pending
}->{delete});
1973 return PVE
::GuestHelpers
::config_with_pending_array
($conf, $pending_delete_hash);