1 package PVE
::API2
::Qemu
;
7 use PVE
::Cluster qw
(cfs_read_file cfs_write_file
);;
9 use PVE
::Tools
qw(extract_param);
10 use PVE
::Exception
qw(raise raise_param_exc);
12 use PVE
::JSONSchema
qw(get_standard_option);
16 use PVE
::RPCEnvironment
;
17 use PVE
::AccessControl
;
20 use Data
::Dumper
; # fixme: remove
22 use base
qw(PVE::RESTHandler);
24 my $opt_force_description = "Force physical removal. Without this, we simple remove the disk from the config file and create an additional configuration entry called 'unused[n]', which contains the volume ID. Unlink of unused[n] always cause physical removal.";
26 my $resolve_cdrom_alias = sub {
29 if (my $value = $param->{cdrom
}) {
30 $value .= ",media=cdrom" if $value !~ m/media=/;
31 $param->{ide2
} = $value;
32 delete $param->{cdrom
};
37 my $check_storage_access = sub {
38 my ($rpcenv, $authuser, $storecfg, $vmid, $settings, $default_storage) = @_;
40 PVE
::QemuServer
::foreach_drive
($settings, sub {
41 my ($ds, $drive) = @_;
43 my $isCDROM = PVE
::QemuServer
::drive_is_cdrom
($drive);
45 my $volid = $drive->{file
};
47 if (!$volid || $volid eq 'none') {
49 } elsif ($isCDROM && ($volid eq 'cdrom')) {
50 $rpcenv->check($authuser, "/", ['Sys.Console']);
51 } elsif (!$isCDROM && ($volid =~ m/^(([^:\s]+):)?(\d+(\.\d+)?)$/)) {
52 my ($storeid, $size) = ($2 || $default_storage, $3);
53 die "no storage ID specified (and no default storage)\n" if !$storeid;
54 $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
56 $rpcenv->check_volume_access($authuser, $storecfg, $vmid, $volid);
61 # Note: $pool is only needed when creating a VM, because pool permissions
62 # are automatically inherited if VM already exists inside a pool.
63 my $create_disks = sub {
64 my ($rpcenv, $authuser, $conf, $storecfg, $vmid, $pool, $settings, $default_storage) = @_;
69 PVE
::QemuServer
::foreach_drive
($settings, sub {
72 my $volid = $disk->{file
};
74 if (!$volid || $volid eq 'none' || $volid eq 'cdrom') {
75 $res->{$ds} = $settings->{$ds};
76 } elsif ($volid =~ m/^(([^:\s]+):)?(\d+(\.\d+)?)$/) {
77 my ($storeid, $size) = ($2 || $default_storage, $3);
78 die "no storage ID specified (and no default storage)\n" if !$storeid;
79 my $defformat = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
80 my $fmt = $disk->{format
} || $defformat;
81 my $volid = PVE
::Storage
::vdisk_alloc
($storecfg, $storeid, $vmid,
82 $fmt, undef, $size*1024*1024);
83 $disk->{file
} = $volid;
84 $disk->{size
} = $size*1024*1024*1024;
85 push @$vollist, $volid;
86 delete $disk->{format
}; # no longer needed
87 $res->{$ds} = PVE
::QemuServer
::print_drive
($vmid, $disk);
90 my $path = $rpcenv->check_volume_access($authuser, $storecfg, $vmid, $volid);
92 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
94 my $foundvolid = undef;
97 PVE
::Storage
::activate_volumes
($storecfg, [ $volid ]);
98 my $dl = PVE
::Storage
::vdisk_list
($storecfg, $storeid, undef);
100 PVE
::Storage
::foreach_volid
($dl, sub {
102 if($volumeid eq $volid) {
109 die "image '$path' does not exists\n" if (!(-f
$path || -b
$path || $foundvolid));
111 my ($size) = PVE
::Storage
::volume_size_info
($storecfg, $volid, 1);
112 $disk->{size
} = $size;
113 $res->{$ds} = PVE
::QemuServer
::print_drive
($vmid, $disk);
117 # free allocated images on error
119 syslog
('err', "VM $vmid creating disks failed");
120 foreach my $volid (@$vollist) {
121 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid); };
127 # modify vm config if everything went well
128 foreach my $ds (keys %$res) {
129 $conf->{$ds} = $res->{$ds};
135 my $check_vm_modify_config_perm = sub {
136 my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_;
138 return 1 if $authuser eq 'root@pam';
140 foreach my $opt (@$key_list) {
141 # disk checks need to be done somewhere else
142 next if PVE
::QemuServer
::valid_drivename
($opt);
144 if ($opt eq 'sockets' || $opt eq 'cores' ||
145 $opt eq 'cpu' || $opt eq 'smp' ||
146 $opt eq 'cpulimit' || $opt eq 'cpuunits') {
147 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.CPU']);
148 } elsif ($opt eq 'boot' || $opt eq 'bootdisk') {
149 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk']);
150 } elsif ($opt eq 'memory' || $opt eq 'balloon') {
151 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Memory']);
152 } elsif ($opt eq 'args' || $opt eq 'lock') {
153 die "only root can set '$opt' config\n";
154 } elsif ($opt eq 'cpu' || $opt eq 'kvm' || $opt eq 'acpi' ||
155 $opt eq 'vga' || $opt eq 'watchdog' || $opt eq 'tablet') {
156 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.HWType']);
157 } elsif ($opt =~ m/^net\d+$/) {
158 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
160 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Options']);
167 __PACKAGE__-
>register_method({
171 description
=> "Virtual machine index (per node).",
173 description
=> "Only list VMs where you have VM.Audit permissons on /vms/<vmid>.",
177 protected
=> 1, # qemu pid files are only readable by root
179 additionalProperties
=> 0,
181 node
=> get_standard_option
('pve-node'),
190 links
=> [ { rel
=> 'child', href
=> "{vmid}" } ],
195 my $rpcenv = PVE
::RPCEnvironment
::get
();
196 my $authuser = $rpcenv->get_user();
198 my $vmstatus = PVE
::QemuServer
::vmstatus
();
201 foreach my $vmid (keys %$vmstatus) {
202 next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1);
204 my $data = $vmstatus->{$vmid};
205 $data->{vmid
} = $vmid;
212 __PACKAGE__-
>register_method({
216 description
=> "Create or restore a virtual machine.",
218 description
=> "You need 'VM.Allocate' permissions on /vms/{vmid} or on the VM pool /pool/{pool}. If you create disks you need 'Datastore.AllocateSpace' on any used storage.",
220 [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
221 [ 'perm', '/pool/{pool}', ['VM.Allocate'], require_param
=> 'pool'],
227 additionalProperties
=> 0,
228 properties
=> PVE
::QemuServer
::json_config_properties
(
230 node
=> get_standard_option
('pve-node'),
231 vmid
=> get_standard_option
('pve-vmid'),
233 description
=> "The backup file.",
238 storage
=> get_standard_option
('pve-storage-id', {
239 description
=> "Default storage.",
245 description
=> "Allow to overwrite existing VM.",
246 requires
=> 'archive',
251 description
=> "Assign a unique random ethernet address.",
252 requires
=> 'archive',
256 type
=> 'string', format
=> 'pve-poolid',
257 description
=> "Add the VM to the specified pool.",
267 my $rpcenv = PVE
::RPCEnvironment
::get
();
269 my $authuser = $rpcenv->get_user();
271 my $node = extract_param
($param, 'node');
273 my $vmid = extract_param
($param, 'vmid');
275 my $archive = extract_param
($param, 'archive');
277 my $storage = extract_param
($param, 'storage');
279 my $force = extract_param
($param, 'force');
281 my $unique = extract_param
($param, 'unique');
283 my $pool = extract_param
($param, 'pool');
285 my $filename = PVE
::QemuServer
::config_file
($vmid);
287 my $storecfg = PVE
::Storage
::config
();
289 PVE
::Cluster
::check_cfs_quorum
();
291 if (defined($pool)) {
292 $rpcenv->check_pool_exist($pool);
295 $rpcenv->check($authuser, "/storage/$storage", ['Datastore.AllocateSpace'])
296 if defined($storage);
299 &$resolve_cdrom_alias($param);
301 &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param, $storage);
303 &$check_vm_modify_config_perm($rpcenv, $authuser, $vmid, $pool, [ keys %$param]);
305 foreach my $opt (keys %$param) {
306 if (PVE
::QemuServer
::valid_drivename
($opt)) {
307 my $drive = PVE
::QemuServer
::parse_drive
($opt, $param->{$opt});
308 raise_param_exc
({ $opt => "unable to parse drive options" }) if !$drive;
310 PVE
::QemuServer
::cleanup_drive_path
($opt, $storecfg, $drive);
311 $param->{$opt} = PVE
::QemuServer
::print_drive
($vmid, $drive);
315 PVE
::QemuServer
::add_random_macs
($param);
317 my $keystr = join(' ', keys %$param);
318 raise_param_exc
({ archive
=> "option conflicts with other options ($keystr)"}) if $keystr;
320 if ($archive eq '-') {
321 die "pipe requires cli environment\n"
322 if $rpcenv->{type
} ne 'cli';
324 my $path = $rpcenv->check_volume_access($authuser, $storecfg, $vmid, $archive);
326 PVE
::Storage
::activate_volumes
($storecfg, [ $archive ])
327 if PVE
::Storage
::parse_volume_id
($archive, 1);
329 die "can't find archive file '$archive'\n" if !($path && -f
$path);
334 my $addVMtoPoolFn = sub {
335 my $usercfg = cfs_read_file
("user.cfg");
336 if (my $data = $usercfg->{pools
}->{$pool}) {
337 $data->{vms
}->{$vmid} = 1;
338 $usercfg->{vms
}->{$vmid} = $pool;
339 cfs_write_file
("user.cfg", $usercfg);
343 my $restorefn = sub {
346 die "unable to restore vm $vmid: config file already exists\n"
349 die "unable to restore vm $vmid: vm is running\n"
350 if PVE
::QemuServer
::check_running
($vmid);
352 # destroy existing data - keep empty config
353 PVE
::QemuServer
::destroy_vm
($storecfg, $vmid, 1);
357 PVE
::QemuServer
::restore_archive
($archive, $vmid, $authuser, {
360 unique
=> $unique });
362 PVE
::AccessControl
::lock_user_config
($addVMtoPoolFn, "can't add VM to pool") if $pool;
365 return $rpcenv->fork_worker('qmrestore', $vmid, $authuser, $realcmd);
371 die "unable to create vm $vmid: config file already exists\n"
382 $vollist = &$create_disks($rpcenv, $authuser, $conf, $storecfg, $vmid, $pool, $param, $storage);
384 # try to be smart about bootdisk
385 my @disks = PVE
::QemuServer
::disknames
();
387 foreach my $ds (reverse @disks) {
388 next if !$conf->{$ds};
389 my $disk = PVE
::QemuServer
::parse_drive
($ds, $conf->{$ds});
390 next if PVE
::QemuServer
::drive_is_cdrom
($disk);
394 if (!$conf->{bootdisk
} && $firstdisk) {
395 $conf->{bootdisk
} = $firstdisk;
398 PVE
::QemuServer
::update_config_nolock
($vmid, $conf);
404 foreach my $volid (@$vollist) {
405 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid); };
408 die "create failed - $err";
411 PVE
::AccessControl
::lock_user_config
($addVMtoPoolFn, "can't add VM to pool") if $pool;
414 return $rpcenv->fork_worker('qmcreate', $vmid, $authuser, $realcmd);
417 return PVE
::QemuServer
::lock_config_full
($vmid, 1, $archive ?
$restorefn : $createfn);
420 __PACKAGE__-
>register_method({
425 description
=> "Directory index",
430 additionalProperties
=> 0,
432 node
=> get_standard_option
('pve-node'),
433 vmid
=> get_standard_option
('pve-vmid'),
441 subdir
=> { type
=> 'string' },
444 links
=> [ { rel
=> 'child', href
=> "{subdir}" } ],
450 { subdir
=> 'config' },
451 { subdir
=> 'status' },
452 { subdir
=> 'unlink' },
453 { subdir
=> 'vncproxy' },
454 { subdir
=> 'migrate' },
455 { subdir
=> 'resize' },
457 { subdir
=> 'rrddata' },
458 { subdir
=> 'monitor' },
464 __PACKAGE__-
>register_method({
466 path
=> '{vmid}/rrd',
468 protected
=> 1, # fixme: can we avoid that?
470 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
472 description
=> "Read VM RRD statistics (returns PNG)",
474 additionalProperties
=> 0,
476 node
=> get_standard_option
('pve-node'),
477 vmid
=> get_standard_option
('pve-vmid'),
479 description
=> "Specify the time frame you are interested in.",
481 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
484 description
=> "The list of datasources you want to display.",
485 type
=> 'string', format
=> 'pve-configid-list',
488 description
=> "The RRD consolidation function",
490 enum
=> [ 'AVERAGE', 'MAX' ],
498 filename
=> { type
=> 'string' },
504 return PVE
::Cluster
::create_rrd_graph
(
505 "pve2-vm/$param->{vmid}", $param->{timeframe
},
506 $param->{ds
}, $param->{cf
});
510 __PACKAGE__-
>register_method({
512 path
=> '{vmid}/rrddata',
514 protected
=> 1, # fixme: can we avoid that?
516 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
518 description
=> "Read VM RRD statistics",
520 additionalProperties
=> 0,
522 node
=> get_standard_option
('pve-node'),
523 vmid
=> get_standard_option
('pve-vmid'),
525 description
=> "Specify the time frame you are interested in.",
527 enum
=> [ 'hour', 'day', 'week', 'month', 'year' ],
530 description
=> "The RRD consolidation function",
532 enum
=> [ 'AVERAGE', 'MAX' ],
547 return PVE
::Cluster
::create_rrd_data
(
548 "pve2-vm/$param->{vmid}", $param->{timeframe
}, $param->{cf
});
552 __PACKAGE__-
>register_method({
554 path
=> '{vmid}/config',
557 description
=> "Get virtual machine configuration.",
559 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
562 additionalProperties
=> 0,
564 node
=> get_standard_option
('pve-node'),
565 vmid
=> get_standard_option
('pve-vmid'),
573 description
=> 'SHA1 digest of configuration file. This can be used to prevent concurrent modifications.',
580 my $conf = PVE
::QemuServer
::load_config
($param->{vmid
});
585 my $vm_is_volid_owner = sub {
586 my ($storecfg, $vmid, $volid) =@_;
588 if ($volid !~ m
|^/|) {
590 eval { ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid); };
591 if ($owner && ($owner == $vmid)) {
599 my $test_deallocate_drive = sub {
600 my ($storecfg, $vmid, $key, $drive, $force) = @_;
602 if (!PVE
::QemuServer
::drive_is_cdrom
($drive)) {
603 my $volid = $drive->{file
};
604 if (&$vm_is_volid_owner($storecfg, $vmid, $volid)) {
605 if ($force || $key =~ m/^unused/) {
606 my $sid = PVE
::Storage
::parse_volume_id
($volid);
615 my $delete_drive = sub {
616 my ($conf, $storecfg, $vmid, $key, $drive, $force) = @_;
618 if (!PVE
::QemuServer
::drive_is_cdrom
($drive)) {
619 my $volid = $drive->{file
};
620 if (&$vm_is_volid_owner($storecfg, $vmid, $volid)) {
621 if ($force || $key =~ m/^unused/) {
622 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid); };
625 PVE
::QemuServer
::add_unused_volume
($conf, $volid, $vmid);
630 delete $conf->{$key};
633 my $vmconfig_delete_option = sub {
634 my ($rpcenv, $authuser, $conf, $storecfg, $vmid, $opt, $force) = @_;
636 return if !defined($conf->{$opt});
638 my $isDisk = PVE
::QemuServer
::valid_drivename
($opt)|| ($opt =~ m/^unused/);
641 $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']);
643 my $drive = PVE
::QemuServer
::parse_drive
($opt, $conf->{$opt});
644 if (my $sid = &$test_deallocate_drive($storecfg, $vmid, $opt, $drive, $force)) {
645 $rpcenv->check($authuser, "/storage/$sid", ['Datastore.Allocate']);
649 die "error hot-unplug $opt" if !PVE
::QemuServer
::vm_deviceunplug
($vmid, $conf, $opt);
652 my $drive = PVE
::QemuServer
::parse_drive
($opt, $conf->{$opt});
653 &$delete_drive($conf, $storecfg, $vmid, $opt, $drive, $force);
655 delete $conf->{$opt};
658 PVE
::QemuServer
::update_config_nolock
($vmid, $conf, 1);
661 my $safe_num_ne = sub {
664 return 0 if !defined($a) && !defined($b);
665 return 1 if !defined($a);
666 return 1 if !defined($b);
671 my $vmconfig_update_disk = sub {
672 my ($rpcenv, $authuser, $conf, $storecfg, $vmid, $opt, $value, $force) = @_;
674 my $drive = PVE
::QemuServer
::parse_drive
($opt, $value);
676 if (PVE
::QemuServer
::drive_is_cdrom
($drive)) { #cdrom
677 $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.CDROM']);
679 $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']);
684 if (my $old_drive = PVE
::QemuServer
::parse_drive
($opt, $conf->{$opt})) {
686 my $media = $drive->{media
} || 'disk';
687 my $oldmedia = $old_drive->{media
} || 'disk';
688 die "unable to change media type\n" if $media ne $oldmedia;
690 if (!PVE
::QemuServer
::drive_is_cdrom
($old_drive) &&
691 ($drive->{file
} ne $old_drive->{file
})) { # delete old disks
693 &$vmconfig_delete_option($rpcenv, $authuser, $conf, $storecfg, $vmid, $opt, $force);
694 $conf = PVE
::QemuServer
::load_config
($vmid); # update/reload
697 if(&$safe_num_ne($drive->{mbps
}, $old_drive->{mbps
}) ||
698 &$safe_num_ne($drive->{mbps_rd
}, $old_drive->{mbps_rd
}) ||
699 &$safe_num_ne($drive->{mbps_wr
}, $old_drive->{mbps_wr
}) ||
700 &$safe_num_ne($drive->{iops
}, $old_drive->{iops
}) ||
701 &$safe_num_ne($drive->{iops_rd
}, $old_drive->{iops_rd
}) ||
702 &$safe_num_ne($drive->{iops_wr
}, $old_drive->{iops_wr
})) {
703 PVE
::QemuServer
::qemu_block_set_io_throttle
($vmid,"drive-$opt", $drive->{mbps
}*1024*1024,
704 $drive->{mbps_rd
}*1024*1024, $drive->{mbps_wr
}*1024*1024,
705 $drive->{iops
}, $drive->{iops_rd
}, $drive->{iops_wr
})
706 if !PVE
::QemuServer
::drive_is_cdrom
($drive);
711 &$create_disks($rpcenv, $authuser, $conf, $storecfg, $vmid, undef, {$opt => $value});
712 PVE
::QemuServer
::update_config_nolock
($vmid, $conf, 1);
714 $conf = PVE
::QemuServer
::load_config
($vmid); # update/reload
715 $drive = PVE
::QemuServer
::parse_drive
($opt, $conf->{$opt});
717 if (PVE
::QemuServer
::drive_is_cdrom
($drive)) { # cdrom
719 if (PVE
::QemuServer
::check_running
($vmid)) {
720 if ($drive->{file
} eq 'none') {
721 PVE
::QemuServer
::vm_mon_cmd
($vmid, "eject",force
=> JSON
::true
,device
=> "drive-$opt");
723 my $path = PVE
::QemuServer
::get_iso_path
($storecfg, $vmid, $drive->{file
});
724 PVE
::QemuServer
::vm_mon_cmd
($vmid, "eject",force
=> JSON
::true
,device
=> "drive-$opt"); #force eject if locked
725 PVE
::QemuServer
::vm_mon_cmd
($vmid, "change",device
=> "drive-$opt",target
=> "$path") if $path;
729 } else { # hotplug new disks
731 die "error hotplug $opt" if !PVE
::QemuServer
::vm_deviceplug
($storecfg, $conf, $vmid, $opt, $drive);
735 my $vmconfig_update_net = sub {
736 my ($rpcenv, $authuser, $conf, $storecfg, $vmid, $opt, $value) = @_;
739 #if online update, then unplug first
740 die "error hot-unplug $opt for update" if !PVE
::QemuServer
::vm_deviceunplug
($vmid, $conf, $opt);
743 $conf->{$opt} = $value;
744 PVE
::QemuServer
::update_config_nolock
($vmid, $conf, 1);
745 $conf = PVE
::QemuServer
::load_config
($vmid); # update/reload
747 my $net = PVE
::QemuServer
::parse_net
($conf->{$opt});
749 die "error hotplug $opt" if !PVE
::QemuServer
::vm_deviceplug
($storecfg, $conf, $vmid, $opt, $net);
752 my $vm_config_perm_list = [
762 __PACKAGE__-
>register_method({
764 path
=> '{vmid}/config',
768 description
=> "Set virtual machine options.",
770 check
=> ['perm', '/vms/{vmid}', $vm_config_perm_list, any
=> 1],
773 additionalProperties
=> 0,
774 properties
=> PVE
::QemuServer
::json_config_properties
(
776 node
=> get_standard_option
('pve-node'),
777 vmid
=> get_standard_option
('pve-vmid'),
778 skiplock
=> get_standard_option
('skiplock'),
780 type
=> 'string', format
=> 'pve-configid-list',
781 description
=> "A list of settings you want to delete.",
786 description
=> $opt_force_description,
788 requires
=> 'delete',
792 description
=> 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.',
798 returns
=> { type
=> 'null'},
802 my $rpcenv = PVE
::RPCEnvironment
::get
();
804 my $authuser = $rpcenv->get_user();
806 my $node = extract_param
($param, 'node');
808 my $vmid = extract_param
($param, 'vmid');
810 my $digest = extract_param
($param, 'digest');
812 my @paramarr = (); # used for log message
813 foreach my $key (keys %$param) {
814 push @paramarr, "-$key", $param->{$key};
817 my $skiplock = extract_param
($param, 'skiplock');
818 raise_param_exc
({ skiplock
=> "Only root may use this option." })
819 if $skiplock && $authuser ne 'root@pam';
821 my $delete_str = extract_param
($param, 'delete');
823 my $force = extract_param
($param, 'force');
825 die "no options specified\n" if !$delete_str && !scalar(keys %$param);
827 my $storecfg = PVE
::Storage
::config
();
829 &$resolve_cdrom_alias($param);
831 # now try to verify all parameters
834 foreach my $opt (PVE
::Tools
::split_list
($delete_str)) {
835 $opt = 'ide2' if $opt eq 'cdrom';
836 raise_param_exc
({ delete => "you can't use '-$opt' and " .
837 "-delete $opt' at the same time" })
838 if defined($param->{$opt});
840 if (!PVE
::QemuServer
::option_exists
($opt)) {
841 raise_param_exc
({ delete => "unknown option '$opt'" });
847 foreach my $opt (keys %$param) {
848 if (PVE
::QemuServer
::valid_drivename
($opt)) {
850 my $drive = PVE
::QemuServer
::parse_drive
($opt, $param->{$opt});
851 PVE
::QemuServer
::cleanup_drive_path
($opt, $storecfg, $drive);
852 $param->{$opt} = PVE
::QemuServer
::print_drive
($vmid, $drive);
853 } elsif ($opt =~ m/^net(\d+)$/) {
855 my $net = PVE
::QemuServer
::parse_net
($param->{$opt});
856 $param->{$opt} = PVE
::QemuServer
::print_net
($net);
860 &$check_vm_modify_config_perm($rpcenv, $authuser, $vmid, undef, [@delete]);
862 &$check_vm_modify_config_perm($rpcenv, $authuser, $vmid, undef, [keys %$param]);
864 &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param);
868 my $conf = PVE
::QemuServer
::load_config
($vmid);
870 die "checksum missmatch (file change by other user?)\n"
871 if $digest && $digest ne $conf->{digest
};
873 PVE
::QemuServer
::check_lock
($conf) if !$skiplock;
875 PVE
::Cluster
::log_msg
('info', $authuser, "update VM $vmid: " . join (' ', @paramarr));
877 foreach my $opt (@delete) { # delete
878 $conf = PVE
::QemuServer
::load_config
($vmid); # update/reload
879 &$vmconfig_delete_option($rpcenv, $authuser, $conf, $storecfg, $vmid, $opt, $force);
882 foreach my $opt (keys %$param) { # add/change
884 $conf = PVE
::QemuServer
::load_config
($vmid); # update/reload
886 next if $conf->{$opt} && ($param->{$opt} eq $conf->{$opt}); # skip if nothing changed
888 if (PVE
::QemuServer
::valid_drivename
($opt)) {
890 &$vmconfig_update_disk($rpcenv, $authuser, $conf, $storecfg, $vmid,
891 $opt, $param->{$opt}, $force);
893 } elsif ($opt =~ m/^net(\d+)$/) { #nics
895 &$vmconfig_update_net($rpcenv, $authuser, $conf, $storecfg, $vmid,
896 $opt, $param->{$opt});
900 $conf->{$opt} = $param->{$opt};
901 PVE
::QemuServer
::update_config_nolock
($vmid, $conf, 1);
906 PVE
::QemuServer
::lock_config
($vmid, $updatefn);
912 __PACKAGE__-
>register_method({
913 name
=> 'destroy_vm',
918 description
=> "Destroy the vm (also delete all used/owned volumes).",
920 check
=> [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
923 additionalProperties
=> 0,
925 node
=> get_standard_option
('pve-node'),
926 vmid
=> get_standard_option
('pve-vmid'),
927 skiplock
=> get_standard_option
('skiplock'),
936 my $rpcenv = PVE
::RPCEnvironment
::get
();
938 my $authuser = $rpcenv->get_user();
940 my $vmid = $param->{vmid
};
942 my $skiplock = $param->{skiplock
};
943 raise_param_exc
({ skiplock
=> "Only root may use this option." })
944 if $skiplock && $authuser ne 'root@pam';
947 my $conf = PVE
::QemuServer
::load_config
($vmid);
949 my $storecfg = PVE
::Storage
::config
();
951 my $delVMfromPoolFn = sub {
952 my $usercfg = cfs_read_file
("user.cfg");
953 if (my $pool = $usercfg->{vms
}->{$vmid}) {
954 if (my $data = $usercfg->{pools
}->{$pool}) {
955 delete $data->{vms
}->{$vmid};
956 delete $usercfg->{vms
}->{$vmid};
957 cfs_write_file
("user.cfg", $usercfg);
965 syslog
('info', "destroy VM $vmid: $upid\n");
967 PVE
::QemuServer
::vm_destroy
($storecfg, $vmid, $skiplock);
969 PVE
::AccessControl
::lock_user_config
($delVMfromPoolFn, "pool cleanup failed");
972 return $rpcenv->fork_worker('qmdestroy', $vmid, $authuser, $realcmd);
975 __PACKAGE__-
>register_method({
977 path
=> '{vmid}/unlink',
981 description
=> "Unlink/delete disk images.",
983 check
=> [ 'perm', '/vms/{vmid}', ['VM.Config.Disk']],
986 additionalProperties
=> 0,
988 node
=> get_standard_option
('pve-node'),
989 vmid
=> get_standard_option
('pve-vmid'),
991 type
=> 'string', format
=> 'pve-configid-list',
992 description
=> "A list of disk IDs you want to delete.",
996 description
=> $opt_force_description,
1001 returns
=> { type
=> 'null'},
1005 $param->{delete} = extract_param
($param, 'idlist');
1007 __PACKAGE__-
>update_vm($param);
1014 __PACKAGE__-
>register_method({
1016 path
=> '{vmid}/vncproxy',
1020 check
=> ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
1022 description
=> "Creates a TCP VNC proxy connections.",
1024 additionalProperties
=> 0,
1026 node
=> get_standard_option
('pve-node'),
1027 vmid
=> get_standard_option
('pve-vmid'),
1031 additionalProperties
=> 0,
1033 user
=> { type
=> 'string' },
1034 ticket
=> { type
=> 'string' },
1035 cert
=> { type
=> 'string' },
1036 port
=> { type
=> 'integer' },
1037 upid
=> { type
=> 'string' },
1043 my $rpcenv = PVE
::RPCEnvironment
::get
();
1045 my $authuser = $rpcenv->get_user();
1047 my $vmid = $param->{vmid
};
1048 my $node = $param->{node
};
1050 my $authpath = "/vms/$vmid";
1052 my $ticket = PVE
::AccessControl
::assemble_vnc_ticket
($authuser, $authpath);
1054 $sslcert = PVE
::Tools
::file_get_contents
("/etc/pve/pve-root-ca.pem", 8192)
1057 my $port = PVE
::Tools
::next_vnc_port
();
1061 if ($node ne 'localhost' && $node ne PVE
::INotify
::nodename
()) {
1062 $remip = PVE
::Cluster
::remote_node_ip
($node);
1065 # NOTE: kvm VNC traffic is already TLS encrypted,
1066 # so we select the fastest chipher here (or 'none'?)
1067 my $remcmd = $remip ?
['/usr/bin/ssh', '-T', '-o', 'BatchMode=yes',
1068 '-c', 'blowfish-cbc', $remip] : [];
1075 syslog
('info', "starting vnc proxy $upid\n");
1077 my $qmcmd = [@$remcmd, "/usr/sbin/qm", 'vncproxy', $vmid];
1079 my $qmstr = join(' ', @$qmcmd);
1081 # also redirect stderr (else we get RFB protocol errors)
1082 my $cmd = ['/bin/nc', '-l', '-p', $port, '-w', $timeout, '-c', "$qmstr 2>/dev/null"];
1084 PVE
::Tools
::run_command
($cmd);
1089 my $upid = $rpcenv->fork_worker('vncproxy', $vmid, $authuser, $realcmd);
1100 __PACKAGE__-
>register_method({
1102 path
=> '{vmid}/status',
1105 description
=> "Directory index",
1110 additionalProperties
=> 0,
1112 node
=> get_standard_option
('pve-node'),
1113 vmid
=> get_standard_option
('pve-vmid'),
1121 subdir
=> { type
=> 'string' },
1124 links
=> [ { rel
=> 'child', href
=> "{subdir}" } ],
1130 my $conf = PVE
::QemuServer
::load_config
($param->{vmid
});
1133 { subdir
=> 'current' },
1134 { subdir
=> 'start' },
1135 { subdir
=> 'stop' },
1141 my $vm_is_ha_managed = sub {
1144 my $cc = PVE
::Cluster
::cfs_read_file
('cluster.conf');
1145 if (PVE
::Cluster
::cluster_conf_lookup_pvevm
($cc, 0, $vmid, 1)) {
1151 __PACKAGE__-
>register_method({
1152 name
=> 'vm_status',
1153 path
=> '{vmid}/status/current',
1156 protected
=> 1, # qemu pid files are only readable by root
1157 description
=> "Get virtual machine status.",
1159 check
=> ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
1162 additionalProperties
=> 0,
1164 node
=> get_standard_option
('pve-node'),
1165 vmid
=> get_standard_option
('pve-vmid'),
1168 returns
=> { type
=> 'object' },
1173 my $conf = PVE
::QemuServer
::load_config
($param->{vmid
});
1175 my $vmstatus = PVE
::QemuServer
::vmstatus
($param->{vmid
}, 1);
1176 my $status = $vmstatus->{$param->{vmid
}};
1178 $status->{ha
} = &$vm_is_ha_managed($param->{vmid
});
1183 __PACKAGE__-
>register_method({
1185 path
=> '{vmid}/status/start',
1189 description
=> "Start virtual machine.",
1191 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1194 additionalProperties
=> 0,
1196 node
=> get_standard_option
('pve-node'),
1197 vmid
=> get_standard_option
('pve-vmid'),
1198 skiplock
=> get_standard_option
('skiplock'),
1199 stateuri
=> get_standard_option
('pve-qm-stateuri'),
1200 migratedfrom
=> get_standard_option
('pve-node',{ optional
=> 1 }),
1210 my $rpcenv = PVE
::RPCEnvironment
::get
();
1212 my $authuser = $rpcenv->get_user();
1214 my $node = extract_param
($param, 'node');
1216 my $vmid = extract_param
($param, 'vmid');
1218 my $stateuri = extract_param
($param, 'stateuri');
1219 raise_param_exc
({ stateuri
=> "Only root may use this option." })
1220 if $stateuri && $authuser ne 'root@pam';
1222 my $skiplock = extract_param
($param, 'skiplock');
1223 raise_param_exc
({ skiplock
=> "Only root may use this option." })
1224 if $skiplock && $authuser ne 'root@pam';
1226 my $migratedfrom = extract_param
($param, 'migratedfrom');
1227 raise_param_exc
({ migratedfrom
=> "Only root may use this option." })
1228 if $migratedfrom && $authuser ne 'root@pam';
1230 my $storecfg = PVE
::Storage
::config
();
1232 if (&$vm_is_ha_managed($vmid) && !$stateuri &&
1233 $rpcenv->{type
} ne 'ha') {
1238 my $service = "pvevm:$vmid";
1240 my $cmd = ['clusvcadm', '-e', $service, '-m', $node];
1242 print "Executing HA start for VM $vmid\n";
1244 PVE
::Tools
::run_command
($cmd);
1249 return $rpcenv->fork_worker('hastart', $vmid, $authuser, $hacmd);
1256 syslog
('info', "start VM $vmid: $upid\n");
1258 PVE
::QemuServer
::vm_start
($storecfg, $vmid, $stateuri, $skiplock, $migratedfrom);
1263 return $rpcenv->fork_worker('qmstart', $vmid, $authuser, $realcmd);
1267 __PACKAGE__-
>register_method({
1269 path
=> '{vmid}/status/stop',
1273 description
=> "Stop virtual machine.",
1275 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1278 additionalProperties
=> 0,
1280 node
=> get_standard_option
('pve-node'),
1281 vmid
=> get_standard_option
('pve-vmid'),
1282 skiplock
=> get_standard_option
('skiplock'),
1283 migratedfrom
=> get_standard_option
('pve-node',{ optional
=> 1 }),
1285 description
=> "Wait maximal timeout seconds.",
1291 description
=> "Do not decativate storage volumes.",
1304 my $rpcenv = PVE
::RPCEnvironment
::get
();
1306 my $authuser = $rpcenv->get_user();
1308 my $node = extract_param
($param, 'node');
1310 my $vmid = extract_param
($param, 'vmid');
1312 my $skiplock = extract_param
($param, 'skiplock');
1313 raise_param_exc
({ skiplock
=> "Only root may use this option." })
1314 if $skiplock && $authuser ne 'root@pam';
1316 my $keepActive = extract_param
($param, 'keepActive');
1317 raise_param_exc
({ keepActive
=> "Only root may use this option." })
1318 if $keepActive && $authuser ne 'root@pam';
1320 my $migratedfrom = extract_param
($param, 'migratedfrom');
1321 raise_param_exc
({ migratedfrom
=> "Only root may use this option." })
1322 if $migratedfrom && $authuser ne 'root@pam';
1325 my $storecfg = PVE
::Storage
::config
();
1327 if (&$vm_is_ha_managed($vmid) && $rpcenv->{type
} ne 'ha') {
1332 my $service = "pvevm:$vmid";
1334 my $cmd = ['clusvcadm', '-d', $service];
1336 print "Executing HA stop for VM $vmid\n";
1338 PVE
::Tools
::run_command
($cmd);
1343 return $rpcenv->fork_worker('hastop', $vmid, $authuser, $hacmd);
1349 syslog
('info', "stop VM $vmid: $upid\n");
1351 PVE
::QemuServer
::vm_stop
($storecfg, $vmid, $skiplock, 0,
1352 $param->{timeout
}, 0, 1, $keepActive, $migratedfrom);
1357 return $rpcenv->fork_worker('qmstop', $vmid, $authuser, $realcmd);
1361 __PACKAGE__-
>register_method({
1363 path
=> '{vmid}/status/reset',
1367 description
=> "Reset virtual machine.",
1369 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1372 additionalProperties
=> 0,
1374 node
=> get_standard_option
('pve-node'),
1375 vmid
=> get_standard_option
('pve-vmid'),
1376 skiplock
=> get_standard_option
('skiplock'),
1385 my $rpcenv = PVE
::RPCEnvironment
::get
();
1387 my $authuser = $rpcenv->get_user();
1389 my $node = extract_param
($param, 'node');
1391 my $vmid = extract_param
($param, 'vmid');
1393 my $skiplock = extract_param
($param, 'skiplock');
1394 raise_param_exc
({ skiplock
=> "Only root may use this option." })
1395 if $skiplock && $authuser ne 'root@pam';
1397 die "VM $vmid not running\n" if !PVE
::QemuServer
::check_running
($vmid);
1402 PVE
::QemuServer
::vm_reset
($vmid, $skiplock);
1407 return $rpcenv->fork_worker('qmreset', $vmid, $authuser, $realcmd);
1410 __PACKAGE__-
>register_method({
1411 name
=> 'vm_shutdown',
1412 path
=> '{vmid}/status/shutdown',
1416 description
=> "Shutdown virtual machine.",
1418 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1421 additionalProperties
=> 0,
1423 node
=> get_standard_option
('pve-node'),
1424 vmid
=> get_standard_option
('pve-vmid'),
1425 skiplock
=> get_standard_option
('skiplock'),
1427 description
=> "Wait maximal timeout seconds.",
1433 description
=> "Make sure the VM stops.",
1439 description
=> "Do not decativate storage volumes.",
1452 my $rpcenv = PVE
::RPCEnvironment
::get
();
1454 my $authuser = $rpcenv->get_user();
1456 my $node = extract_param
($param, 'node');
1458 my $vmid = extract_param
($param, 'vmid');
1460 my $skiplock = extract_param
($param, 'skiplock');
1461 raise_param_exc
({ skiplock
=> "Only root may use this option." })
1462 if $skiplock && $authuser ne 'root@pam';
1464 my $keepActive = extract_param
($param, 'keepActive');
1465 raise_param_exc
({ keepActive
=> "Only root may use this option." })
1466 if $keepActive && $authuser ne 'root@pam';
1468 my $storecfg = PVE
::Storage
::config
();
1473 syslog
('info', "shutdown VM $vmid: $upid\n");
1475 PVE
::QemuServer
::vm_stop
($storecfg, $vmid, $skiplock, 0, $param->{timeout
},
1476 1, $param->{forceStop
}, $keepActive);
1481 return $rpcenv->fork_worker('qmshutdown', $vmid, $authuser, $realcmd);
1484 __PACKAGE__-
>register_method({
1485 name
=> 'vm_suspend',
1486 path
=> '{vmid}/status/suspend',
1490 description
=> "Suspend virtual machine.",
1492 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1495 additionalProperties
=> 0,
1497 node
=> get_standard_option
('pve-node'),
1498 vmid
=> get_standard_option
('pve-vmid'),
1499 skiplock
=> get_standard_option
('skiplock'),
1508 my $rpcenv = PVE
::RPCEnvironment
::get
();
1510 my $authuser = $rpcenv->get_user();
1512 my $node = extract_param
($param, 'node');
1514 my $vmid = extract_param
($param, 'vmid');
1516 my $skiplock = extract_param
($param, 'skiplock');
1517 raise_param_exc
({ skiplock
=> "Only root may use this option." })
1518 if $skiplock && $authuser ne 'root@pam';
1520 die "VM $vmid not running\n" if !PVE
::QemuServer
::check_running
($vmid);
1525 syslog
('info', "suspend VM $vmid: $upid\n");
1527 PVE
::QemuServer
::vm_suspend
($vmid, $skiplock);
1532 return $rpcenv->fork_worker('qmsuspend', $vmid, $authuser, $realcmd);
1535 __PACKAGE__-
>register_method({
1536 name
=> 'vm_resume',
1537 path
=> '{vmid}/status/resume',
1541 description
=> "Resume virtual machine.",
1543 check
=> ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
1546 additionalProperties
=> 0,
1548 node
=> get_standard_option
('pve-node'),
1549 vmid
=> get_standard_option
('pve-vmid'),
1550 skiplock
=> get_standard_option
('skiplock'),
1559 my $rpcenv = PVE
::RPCEnvironment
::get
();
1561 my $authuser = $rpcenv->get_user();
1563 my $node = extract_param
($param, 'node');
1565 my $vmid = extract_param
($param, 'vmid');
1567 my $skiplock = extract_param
($param, 'skiplock');
1568 raise_param_exc
({ skiplock
=> "Only root may use this option." })
1569 if $skiplock && $authuser ne 'root@pam';
1571 die "VM $vmid not running\n" if !PVE
::QemuServer
::check_running
($vmid);
1576 syslog
('info', "resume VM $vmid: $upid\n");
1578 PVE
::QemuServer
::vm_resume
($vmid, $skiplock);
1583 return $rpcenv->fork_worker('qmresume', $vmid, $authuser, $realcmd);
1586 __PACKAGE__-
>register_method({
1587 name
=> 'vm_sendkey',
1588 path
=> '{vmid}/sendkey',
1592 description
=> "Send key event to virtual machine.",
1594 check
=> ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
1597 additionalProperties
=> 0,
1599 node
=> get_standard_option
('pve-node'),
1600 vmid
=> get_standard_option
('pve-vmid'),
1601 skiplock
=> get_standard_option
('skiplock'),
1603 description
=> "The key (qemu monitor encoding).",
1608 returns
=> { type
=> 'null'},
1612 my $rpcenv = PVE
::RPCEnvironment
::get
();
1614 my $authuser = $rpcenv->get_user();
1616 my $node = extract_param
($param, 'node');
1618 my $vmid = extract_param
($param, 'vmid');
1620 my $skiplock = extract_param
($param, 'skiplock');
1621 raise_param_exc
({ skiplock
=> "Only root may use this option." })
1622 if $skiplock && $authuser ne 'root@pam';
1624 PVE
::QemuServer
::vm_sendkey
($vmid, $skiplock, $param->{key
});
1629 __PACKAGE__-
>register_method({
1630 name
=> 'migrate_vm',
1631 path
=> '{vmid}/migrate',
1635 description
=> "Migrate virtual machine. Creates a new migration task.",
1637 check
=> ['perm', '/vms/{vmid}', [ 'VM.Migrate' ]],
1640 additionalProperties
=> 0,
1642 node
=> get_standard_option
('pve-node'),
1643 vmid
=> get_standard_option
('pve-vmid'),
1644 target
=> get_standard_option
('pve-node', { description
=> "Target node." }),
1647 description
=> "Use online/live migration.",
1652 description
=> "Allow to migrate VMs which use local devices. Only root may use this option.",
1659 description
=> "the task ID.",
1664 my $rpcenv = PVE
::RPCEnvironment
::get
();
1666 my $authuser = $rpcenv->get_user();
1668 my $target = extract_param
($param, 'target');
1670 my $localnode = PVE
::INotify
::nodename
();
1671 raise_param_exc
({ target
=> "target is local node."}) if $target eq $localnode;
1673 PVE
::Cluster
::check_cfs_quorum
();
1675 PVE
::Cluster
::check_node_exists
($target);
1677 my $targetip = PVE
::Cluster
::remote_node_ip
($target);
1679 my $vmid = extract_param
($param, 'vmid');
1681 raise_param_exc
({ force
=> "Only root may use this option." })
1682 if $param->{force
} && $authuser ne 'root@pam';
1685 my $conf = PVE
::QemuServer
::load_config
($vmid);
1687 # try to detect errors early
1689 PVE
::QemuServer
::check_lock
($conf);
1691 if (PVE
::QemuServer
::check_running
($vmid)) {
1692 die "cant migrate running VM without --online\n"
1693 if !$param->{online
};
1696 my $storecfg = PVE
::Storage
::config
();
1697 PVE
::QemuServer
::check_storage_availability
($storecfg, $conf, $target);
1699 if (&$vm_is_ha_managed($vmid) && $rpcenv->{type
} ne 'ha') {
1704 my $service = "pvevm:$vmid";
1706 my $cmd = ['clusvcadm', '-M', $service, '-m', $target];
1708 print "Executing HA migrate for VM $vmid to node $target\n";
1710 PVE
::Tools
::run_command
($cmd);
1715 return $rpcenv->fork_worker('hamigrate', $vmid, $authuser, $hacmd);
1722 PVE
::QemuMigrate-
>migrate($target, $targetip, $vmid, $param);
1725 return $rpcenv->fork_worker('qmigrate', $vmid, $authuser, $realcmd);
1730 __PACKAGE__-
>register_method({
1732 path
=> '{vmid}/monitor',
1736 description
=> "Execute Qemu monitor commands.",
1738 check
=> ['perm', '/vms/{vmid}', [ 'VM.Monitor' ]],
1741 additionalProperties
=> 0,
1743 node
=> get_standard_option
('pve-node'),
1744 vmid
=> get_standard_option
('pve-vmid'),
1747 description
=> "The monitor command.",
1751 returns
=> { type
=> 'string'},
1755 my $vmid = $param->{vmid
};
1757 my $conf = PVE
::QemuServer
::load_config
($vmid); # check if VM exists
1761 $res = PVE
::QemuServer
::vm_human_monitor_command
($vmid, $param->{command
});
1763 $res = "ERROR: $@" if $@;
1768 __PACKAGE__-
>register_method({
1769 name
=> 'resize_vm',
1770 path
=> '{vmid}/resize',
1774 description
=> "Extend volume size.",
1776 check
=> ['perm', '/vms/{vmid}', [ 'VM.Config.Disk' ]],
1779 additionalProperties
=> 0,
1781 node
=> get_standard_option
('pve-node'),
1782 vmid
=> get_standard_option
('pve-vmid'),
1783 skiplock
=> get_standard_option
('skiplock'),
1786 description
=> "The disk you want to resize.",
1787 enum
=> [PVE
::QemuServer
::disknames
()],
1791 pattern
=> '\+?\d+(\.\d+)?[KMGT]?',
1792 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.",
1796 description
=> 'Prevent changes if current configuration file has different SHA1 digest. This can be used to prevent concurrent modifications.',
1802 returns
=> { type
=> 'null'},
1806 my $rpcenv = PVE
::RPCEnvironment
::get
();
1808 my $authuser = $rpcenv->get_user();
1810 my $node = extract_param
($param, 'node');
1812 my $vmid = extract_param
($param, 'vmid');
1814 my $digest = extract_param
($param, 'digest');
1816 my $disk = extract_param
($param, 'disk');
1818 my $sizestr = extract_param
($param, 'size');
1820 my $skiplock = extract_param
($param, 'skiplock');
1821 raise_param_exc
({ skiplock
=> "Only root may use this option." })
1822 if $skiplock && $authuser ne 'root@pam';
1824 my $storecfg = PVE
::Storage
::config
();
1826 my $updatefn = sub {
1828 my $conf = PVE
::QemuServer
::load_config
($vmid);
1830 die "checksum missmatch (file change by other user?)\n"
1831 if $digest && $digest ne $conf->{digest
};
1832 PVE
::QemuServer
::check_lock
($conf) if !$skiplock;
1834 die "disk '$disk' does not exist\n" if !$conf->{$disk};
1836 my $drive = PVE
::QemuServer
::parse_drive
($disk, $conf->{$disk});
1838 my $volid = $drive->{file
};
1840 die "disk '$disk' has no associated volume\n" if !$volid;
1842 die "you can't resize a cdrom\n" if PVE
::QemuServer
::drive_is_cdrom
($drive);
1844 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid);
1846 $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
1848 my $size = PVE
::Storage
::volume_size_info
($storecfg, $volid, 5);
1850 die "internal error" if $sizestr !~ m/^(\+)?(\d+(\.\d+)?)([KMGT])?$/;
1851 my ($ext, $newsize, $unit) = ($1, $2, $4);
1854 $newsize = $newsize * 1024;
1855 } elsif ($unit eq 'M') {
1856 $newsize = $newsize * 1024 * 1024;
1857 } elsif ($unit eq 'G') {
1858 $newsize = $newsize * 1024 * 1024 * 1024;
1859 } elsif ($unit eq 'T') {
1860 $newsize = $newsize * 1024 * 1024 * 1024 * 1024;
1863 $newsize += $size if $ext;
1864 $newsize = int($newsize);
1866 die "unable to skrink disk size\n" if $newsize < $size;
1868 return if $size == $newsize;
1870 PVE
::Cluster
::log_msg
('info', $authuser, "update VM $vmid: resize --disk $disk --size $sizestr");
1872 PVE
::QemuServer
::qemu_block_resize
($vmid, "drive-$disk", $storecfg, $volid, $newsize);
1874 $drive->{size
} = $newsize;
1875 $conf->{$disk} = PVE
::QemuServer
::print_drive
($vmid, $drive);
1877 PVE
::QemuServer
::update_config_nolock
($vmid, $conf, 1);
1880 PVE
::QemuServer
::lock_config
($vmid, $updatefn);