use warnings;
use Cwd 'abs_path';
use Net::SSLeay;
+use UUID;
use PVE::Cluster qw (cfs_read_file cfs_write_file);;
use PVE::SafeSyslog;
}
};
+my $test_deallocate_drive = sub {
+ my ($storecfg, $vmid, $key, $drive, $force) = @_;
+
+ if (!PVE::QemuServer::drive_is_cdrom($drive)) {
+ my $volid = $drive->{file};
+ if ( PVE::QemuServer::vm_is_volid_owner($storecfg, $vmid, $volid)) {
+ if ($force || $key =~ m/^unused/) {
+ my $sid = PVE::Storage::parse_volume_id($volid);
+ return $sid;
+ }
+ }
+ }
+
+ return undef;
+};
my $check_storage_access = sub {
my ($rpcenv, $authuser, $storecfg, $vmid, $settings, $default_storage) = @_;
} elsif ($opt eq 'args' || $opt eq 'lock') {
die "only root can set '$opt' config\n";
} elsif ($opt eq 'cpu' || $opt eq 'kvm' || $opt eq 'acpi' || $opt eq 'machine' ||
- $opt eq 'vga' || $opt eq 'watchdog' || $opt eq 'tablet') {
+ $opt eq 'vga' || $opt eq 'watchdog' || $opt eq 'tablet' || $opt eq 'smbios1') {
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.HWType']);
} elsif ($opt =~ m/^net\d+$/) {
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1);
my $data = $vmstatus->{$vmid};
- $data->{vmid} = $vmid;
+ $data->{vmid} = int($vmid);
push @$res, $data;
}
$conf->{bootdisk} = $firstdisk;
}
+ # auto generate uuid if user did not specify smbios1 option
+ if (!$conf->{smbios1}) {
+ my ($uuid, $uuid_str);
+ UUID::generate($uuid);
+ UUID::unparse($uuid, $uuid_str);
+ $conf->{smbios1} = "uuid=$uuid_str";
+ }
+
PVE::QemuServer::update_config_nolock($vmid, $conf);
};
}});
__PACKAGE__->register_method ({
- subclass => "PVE::API2::Firewall::VM",
+ subclass => "PVE::API2::Firewall::VM",
path => '{vmid}/firewall',
});
return $conf;
}});
-my $vm_is_volid_owner = sub {
- my ($storecfg, $vmid, $volid) =@_;
-
- if ($volid !~ m|^/|) {
- my ($path, $owner);
- eval { ($path, $owner) = PVE::Storage::path($storecfg, $volid); };
- if ($owner && ($owner == $vmid)) {
- return 1;
- }
- }
-
- return undef;
-};
-
-my $test_deallocate_drive = sub {
- my ($storecfg, $vmid, $key, $drive, $force) = @_;
-
- if (!PVE::QemuServer::drive_is_cdrom($drive)) {
- my $volid = $drive->{file};
- if (&$vm_is_volid_owner($storecfg, $vmid, $volid)) {
- if ($force || $key =~ m/^unused/) {
- my $sid = PVE::Storage::parse_volume_id($volid);
- return $sid;
- }
- }
- }
-
- return undef;
-};
-
my $delete_drive = sub {
my ($conf, $storecfg, $vmid, $key, $drive, $force) = @_;
if (!PVE::QemuServer::drive_is_cdrom($drive)) {
my $volid = $drive->{file};
- if (&$vm_is_volid_owner($storecfg, $vmid, $volid)) {
+ if (PVE::QemuServer::vm_is_volid_owner($storecfg, $vmid, $volid)) {
if ($force || $key =~ m/^unused/) {
eval {
# check if the disk is really unused
PVE::QemuServer::check_lock($conf) if !$skiplock;
if ($param->{memory} || defined($param->{balloon})) {
- my $maxmem = $param->{memory} || $conf->{memory} || $defaults->{memory};
- my $balloon = defined($param->{balloon}) ? $param->{balloon} : $conf->{balloon};
+ my $maxmem = $param->{memory} || $conf->{pending}->{memory} || $conf->{memory} || $defaults->{memory};
+ my $balloon = defined($param->{balloon}) ? $param->{balloon} : $conf->{pending}->{balloon} || $conf->{balloon};
die "balloon value too large (must be smaller than assigned memory)\n"
if $balloon && $balloon > $maxmem;
print "update VM $vmid: " . join (' ', @paramarr) . "\n";
- foreach my $opt (@delete) { # delete
+ # write updates to pending section
+
+ foreach my $opt (@delete) {
$conf = PVE::QemuServer::load_config($vmid); # update/reload
- &$vmconfig_delete_option($rpcenv, $authuser, $conf, $storecfg, $vmid, $opt, $force);
+ if ($opt =~ m/^unused/) {
+ $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']);
+ my $drive = PVE::QemuServer::parse_drive($opt, $conf->{$opt});
+ if (my $sid = &$test_deallocate_drive($storecfg, $vmid, $opt, $drive, $force)) {
+ $rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);
+ &$delete_drive($conf, $storecfg, $vmid, $opt, $drive);
+ PVE::QemuServer::update_config_nolock($vmid, $conf, 1);
+ }
+ } elsif (PVE::QemuServer::valid_drivename($opt)) {
+ $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']);
+ PVE::QemuServer::vmconfig_register_unused_drive($storecfg, $vmid, $conf, PVE::QemuServer::parse_drive($opt, $conf->{pending}->{$opt}))
+ if defined($conf->{pending}->{$opt});
+ PVE::QemuServer::vmconfig_delete_pending_option($conf, $opt);
+ PVE::QemuServer::update_config_nolock($vmid, $conf, 1);
+ } else {
+ PVE::QemuServer::vmconfig_delete_pending_option($conf, $opt);
+ PVE::QemuServer::update_config_nolock($vmid, $conf, 1);
+ }
+ }
+
+ foreach my $opt (keys %$param) { # add/change
+ $conf = PVE::QemuServer::load_config($vmid); # update/reload
+ next if defined($conf->{pending}->{$opt}) && ($param->{$opt} eq $conf->{pending}->{$opt}); # skip if nothing changed
+
+ if (PVE::QemuServer::valid_drivename($opt)) {
+ my $drive = PVE::QemuServer::parse_drive($opt, $param->{$opt});
+ if (PVE::QemuServer::drive_is_cdrom($drive)) { # CDROM
+ $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.CDROM']);
+ } else {
+ $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']);
+ }
+ PVE::QemuServer::vmconfig_register_unused_drive($storecfg, $vmid, $conf, PVE::QemuServer::parse_drive($opt, $conf->{pending}->{$opt}))
+ if defined($conf->{pending}->{$opt});
+
+ &$create_disks($rpcenv, $authuser, $conf->{pending}, $storecfg, $vmid, undef, {$opt => $param->{$opt}});
+ } else {
+ $conf->{pending}->{$opt} = $param->{$opt};
+ }
+ PVE::QemuServer::vmconfig_undelete_pending_option($conf, $opt);
+ PVE::QemuServer::update_config_nolock($vmid, $conf, 1);
}
+ # remove pending changes when nothing changed
+ $conf = PVE::QemuServer::load_config($vmid); # update/reload
+ my $changes = PVE::QemuServer::vmconfig_cleanup_pending($conf);
+ PVE::QemuServer::update_config_nolock($vmid, $conf, 1) if $changes;
+
+ return if !scalar(keys %{$conf->{pending}});
+
my $running = PVE::QemuServer::check_running($vmid);
+ # apply pending changes
+
+ $conf = PVE::QemuServer::load_config($vmid); # update/reload
+ PVE::QemuServer::vmconfig_apply_pending($vmid, $conf, $storecfg, $running);
+
+ return; # TODO: remove old code below
+
foreach my $opt (keys %$param) { # add/change
$conf = PVE::QemuServer::load_config($vmid); # update/reload
} elsif($opt eq 'tablet' && $param->{$opt} == 0){
PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt);
}
-
+
if($opt eq 'cores' && $conf->{maxcpus}){
PVE::QemuServer::qemu_cpu_hotplug($vmid, $conf, $param->{$opt});
}
path => '{vmid}/vncwebsocket',
method => 'GET',
permissions => {
+ description => "You also need to pass a valid ticket (vncticket).",
check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
},
- description => "Opens a weksocket for VNV traffic.",
+ description => "Opens a weksocket for VNC traffic.",
parameters => {
additionalProperties => 0,
properties => {
node => get_standard_option('pve-node'),
vmid => get_standard_option('pve-vmid'),
+ vncticket => {
+ description => "Ticket from previous call to vncproxy.",
+ type => 'string',
+ maxLength => 512,
+ },
port => {
description => "Port number returned by previous vncproxy call.",
type => 'integer',
my $vmid = $param->{vmid};
my $node = $param->{node};
+ my $authpath = "/vms/$vmid";
+
+ PVE::AccessControl::verify_vnc_ticket($param->{vncticket}, $authuser, $authpath);
+
my $conf = PVE::QemuServer::load_config($vmid, $node); # VM exists ?
# Note: VNC ports are acessible from outside, so we do not gain any
# check is done by verifying the VNC ticket (inside VNC protocol).
my $port = $param->{port};
-
+
return { port => $port };
}});
my $port = PVE::QemuServer::spice_port($vmid);
- my ($ticket, undef, $remote_viewer_config) =
+ my ($ticket, undef, $remote_viewer_config) =
PVE::AccessControl::remote_viewer_config($authuser, $vmid, $node, $proxy, $title, $port);
-
+
PVE::QemuServer::vm_mon_cmd($vmid, "set_password", protocol => 'spice', password => $ticket);
PVE::QemuServer::vm_mon_cmd($vmid, "expire_password", protocol => 'spice', time => "+30");
-
+
return $remote_viewer_config;
}});
node => get_standard_option('pve-node'),
vmid => get_standard_option('pve-vmid'),
skiplock => get_standard_option('skiplock'),
- migratedfrom => get_standard_option('pve-node',{ optional => 1 }),
+ migratedfrom => get_standard_option('pve-node', { optional => 1 }),
timeout => {
description => "Wait maximal timeout seconds.",
type => 'integer',
my $storecfg = PVE::Storage::config();
- if (&$vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') {
+ if (&$vm_is_ha_managed($vmid) && ($rpcenv->{type} ne 'ha') && !defined($migratedfrom)) {
my $hacmd = sub {
my $upid = shift;
description => "Add the new VM to the specified pool.",
},
snapname => get_standard_option('pve-snapshot-name', {
- requires => 'full',
optional => 1,
}),
storage => get_standard_option('pve-storage-id', {
if (PVE::QemuServer::drive_is_cdrom($drive)) {
$newconf->{$opt} = $value; # simply copy configuration
} else {
- if ($param->{full} || !PVE::Storage::volume_is_base($storecfg, $drive->{file})) {
+ if ($param->{full}) {
die "Full clone feature is not available"
if !PVE::Storage::volume_has_feature($storecfg, 'copy', $drive->{file}, $snapname, $running);
$drive->{full} = 1;
+ } else {
+ # not full means clone instead of copy
+ die "Linked clone feature is not available"
+ if !PVE::Storage::volume_has_feature($storecfg, 'clone', $drive->{file}, $snapname, $running);
}
$drives->{$opt} = $drive;
push @$vollist, $drive->{file};
}
}
+ # auto generate a new uuid
+ my ($uuid, $uuid_str);
+ UUID::generate($uuid);
+ UUID::unparse($uuid, $uuid_str);
+ my $smbios1 = PVE::QemuServer::parse_smbios1($newconf->{smbios1} || '');
+ $smbios1->{uuid} = $uuid_str;
+ $newconf->{smbios1} = PVE::QemuServer::print_smbios1($smbios1);
+
delete $newconf->{template};
if ($param->{name}) {
PVE::QemuServer::update_config_nolock($vmid, $conf, 1);
- eval {
+ eval {
# try to deactivate volumes - avoid lvm LVs to be active on several nodes
- PVE::Storage::deactivate_volumes($storecfg, [ $newdrive->{file} ])
+ PVE::Storage::deactivate_volumes($storecfg, [ $newdrive->{file} ])
if !$running;
};
warn $@ if $@;
die "you can't resize a cdrom\n" if PVE::QemuServer::drive_is_cdrom($drive);
- die "you can't online resize a virtio windows bootdisk\n"
- if PVE::QemuServer::check_running($vmid) && $conf->{bootdisk} eq $disk && $conf->{ostype} =~ m/^w/ && $disk =~ m/^virtio/;
-
my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid);
$rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
type => 'boolean',
description => "Save the vmstate",
},
- freezefs => {
- optional => 1,
- type => 'boolean',
- description => "Freeze the filesystem",
- },
description => {
optional => 1,
type => 'string',
my $realcmd = sub {
PVE::Cluster::log_msg('info', $authuser, "snapshot VM $vmid: $snapname");
- PVE::QemuServer::snapshot_create($vmid, $snapname, $param->{vmstate},
- $param->{freezefs}, $param->{description});
+ PVE::QemuServer::snapshot_create($vmid, $snapname, $param->{vmstate},
+ $param->{description});
};
return $rpcenv->fork_worker('qmsnapshot', $vmid, $authuser, $realcmd);