X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=PVE%2FAPI2%2FQemu.pm;h=8434018992a7c4fa563c9e7db9969faebaa053e1;hb=a1d8c038c58137050c29b697d6d80de902a59892;hp=3342458929cff5176d7ad734854bdd112a94d16f;hpb=3ab7663a918ae831b5ddca068c9f0568a2799947;p=qemu-server.git diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 3342458..8434018 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -15,6 +15,8 @@ use PVE::Exception qw(raise raise_param_exc raise_perm_exc); use PVE::Storage; use PVE::JSONSchema qw(get_standard_option); use PVE::RESTHandler; +use PVE::ReplicationConfig; +use PVE::GuestHelpers; use PVE::QemuConfig; use PVE::QemuServer; use PVE::QemuMigrate; @@ -50,6 +52,7 @@ my $resolve_cdrom_alias = sub { } }; +my $NEW_DISK_RE = qr!^(([^/:\s]+):)?(\d+(\.\d+)?)$!; my $check_storage_access = sub { my ($rpcenv, $authuser, $storecfg, $vmid, $settings, $default_storage) = @_; @@ -64,10 +67,13 @@ my $check_storage_access = sub { # nothing to check } elsif ($isCDROM && ($volid eq 'cdrom')) { $rpcenv->check($authuser, "/", ['Sys.Console']); - } elsif (!$isCDROM && ($volid =~ m/^(([^:\s]+):)?(\d+(\.\d+)?)$/)) { + } elsif (!$isCDROM && ($volid =~ $NEW_DISK_RE)) { my ($storeid, $size) = ($2 || $default_storage, $3); die "no storage ID specified (and no default storage)\n" if !$storeid; $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']); + my $scfg = PVE::Storage::storage_config($storecfg, $storeid); + raise_param_exc({ storage => "storage '$storeid' does not support vm images"}) + if !$scfg->{content}->{images}; } else { PVE::Storage::check_volume_access($rpcenv, $authuser, $storecfg, $vmid, $volid); } @@ -128,21 +134,21 @@ my $create_disks = sub { if (!$volid || $volid eq 'none' || $volid eq 'cdrom') { delete $disk->{size}; $res->{$ds} = PVE::QemuServer::print_drive($vmid, $disk); - } elsif ($volid =~ m!^(([^/:\s]+):)?(\d+(\.\d+)?)$!) { + } elsif ($volid =~ $NEW_DISK_RE) { my ($storeid, $size) = ($2 || $default_storage, $3); die "no storage ID specified (and no default storage)\n" if !$storeid; my $defformat = PVE::Storage::storage_default_format($storecfg, $storeid); my $fmt = $disk->{format} || $defformat; + $size = PVE::Tools::convert_size($size, 'gb' => 'kb'); # vdisk_alloc uses kb + my $volid; if ($ds eq 'efidisk0') { # handle efidisk my $ovmfvars = '/usr/share/kvm/OVMF_VARS-pure-efi.fd'; die "uefi vars image not found\n" if ! -f $ovmfvars; - $volid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, - $fmt, undef, 128); - $disk->{file} = $volid; - $disk->{size} = 128*1024; + $size = PVE::Tools::convert_size(-s $ovmfvars, 'b' => 'kb'); + $volid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, $fmt, undef, $size); my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid); my $scfg = PVE::Storage::storage_config($storecfg, $storeid); my $qemufmt = PVE::QemuServer::qemu_img_format($scfg, $volname); @@ -157,12 +163,11 @@ my $create_disks = sub { my $err = $@; die "Copying of EFI Vars image failed: $err" if $err; } else { - $volid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, - $fmt, undef, $size*1024*1024); - $disk->{file} = $volid; - $disk->{size} = $size*1024*1024*1024; + $volid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, $fmt, undef, $size); } push @$vollist, $volid; + $disk->{file} = $volid; + $disk->{size} = PVE::Tools::convert_size($size, 'kb' => 'b'); delete $disk->{format}; # no longer needed $res->{$ds} = PVE::QemuServer::print_drive($vmid, $disk); } else { @@ -523,6 +528,9 @@ __PACKAGE__->register_method({ PVE::AccessControl::add_vm_to_pool($vmid, $pool) if $pool; }; + # ensure no old replication state are exists + PVE::ReplicationState::delete_guest_states($vmid); + return $rpcenv->fork_worker('qmrestore', $vmid, $authuser, $realcmd); }; @@ -531,6 +539,9 @@ __PACKAGE__->register_method({ # test after locking PVE::Cluster::check_vmid_unused($vmid); + # ensure no old replication state are exists + PVE::ReplicationState::delete_guest_states($vmid); + my $realcmd = sub { my $vollist = []; @@ -896,7 +907,7 @@ my $update_vm_api = sub { my $background_delay = extract_param($param, 'background_delay'); my @paramarr = (); # used for log message - foreach my $key (keys %$param) { + foreach my $key (sort keys %$param) { push @paramarr, "-$key", $param->{$key}; } @@ -952,12 +963,35 @@ my $update_vm_api = sub { push @delete, $opt; } + my $repl_conf = PVE::ReplicationConfig->new(); + my $is_replicated = $repl_conf->check_for_existing_jobs($vmid, 1); + my $check_replication = sub { + my ($drive) = @_; + return if !$is_replicated; + my $volid = $drive->{file}; + return if !$volid || !($drive->{replicate}//1); + return if PVE::QemuServer::drive_is_cdrom($drive); + my ($storeid, $format); + if ($volid =~ $NEW_DISK_RE) { + $storeid = $2; + $format = $drive->{format} || PVE::Storage::storage_default_format($storecfg, $storeid); + } else { + ($storeid, undef) = PVE::Storage::parse_volume_id($volid, 1); + $format = (PVE::Storage::parse_volname($storecfg, $volid))[6]; + } + return if PVE::Storage::storage_can_replicate($storecfg, $storeid, $format); + my $scfg = PVE::Storage::storage_config($storecfg, $storeid); + return if $scfg->{shared}; + die "cannot add non-replicatable volume to a replicated VM\n"; + }; + foreach my $opt (keys %$param) { if (PVE::QemuServer::is_valid_drivename($opt)) { # cleanup drive path my $drive = PVE::QemuServer::parse_drive($opt, $param->{$opt}); raise_param_exc({ $opt => "unable to parse drive options" }) if !$drive; PVE::QemuServer::cleanup_drive_path($opt, $storecfg, $drive); + $check_replication->($drive); $param->{$opt} = PVE::QemuServer::print_drive($vmid, $drive); } elsif ($opt =~ m/^net(\d+)$/) { # add macaddr @@ -1010,7 +1044,7 @@ my $update_vm_api = sub { foreach my $opt (@delete) { $modified->{$opt} = 1; $conf = PVE::QemuConfig->load_config($vmid); # update/reload - if (!defined($conf->{$opt})) { + if (!defined($conf->{$opt}) && !defined($conf->{pending}->{$opt})) { warn "cannot delete '$opt' - not set in current configuration!\n"; $modified->{$opt} = 0; next; @@ -1279,6 +1313,10 @@ __PACKAGE__->register_method({ die "unable to remove VM $vmid - used in HA resources\n" if PVE::HA::Config::vm_is_ha_managed($vmid); + # do not allow destroy if there are replication jobs + my $repl_conf = PVE::ReplicationConfig->new(); + $repl_conf->check_for_existing_jobs($vmid); + # early tests (repeat after locking) die "VM $vmid is running - destroy failed\n" if PVE::QemuServer::check_running($vmid); @@ -1750,7 +1788,7 @@ __PACKAGE__->register_method({ my $cmd = ['ha-manager', 'set', $service, '--state', 'started']; - print "Executing HA start for VM $vmid\n"; + print "Requesting HA start for VM $vmid\n"; PVE::Tools::run_command($cmd); @@ -1847,7 +1885,7 @@ __PACKAGE__->register_method({ my $cmd = ['ha-manager', 'set', $service, '--state', 'stopped']; - print "Executing HA stop for VM $vmid\n"; + print "Requesting HA stop for VM $vmid\n"; PVE::Tools::run_command($cmd); @@ -2017,7 +2055,7 @@ __PACKAGE__->register_method({ my $cmd = ['ha-manager', 'set', $service, '--state', 'stopped']; - print "Executing HA stop for VM $vmid\n"; + print "Requesting HA stop for VM $vmid\n"; PVE::Tools::run_command($cmd); @@ -2493,7 +2531,10 @@ __PACKAGE__->register_method({ my $jobs = {}; eval { - local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = sub { die "interrupted by signal\n"; }; + local $SIG{INT} = + local $SIG{TERM} = + local $SIG{QUIT} = + local $SIG{HUP} = sub { die "interrupted by signal\n"; }; PVE::Storage::activate_volumes($storecfg, $vollist, $snapname); @@ -2840,7 +2881,7 @@ __PACKAGE__->register_method({ my $cmd = ['ha-manager', 'migrate', $service, $target]; - print "Executing HA migrate for VM $vmid to node $target\n"; + print "Requesting HA migration for VM $vmid to node $target\n"; PVE::Tools::run_command($cmd); @@ -2852,12 +2893,14 @@ __PACKAGE__->register_method({ } else { my $realcmd = sub { - my $upid = shift; - PVE::QemuMigrate->migrate($target, $targetip, $vmid, $param); }; - return $rpcenv->fork_worker('qmigrate', $vmid, $authuser, $realcmd); + my $worker = sub { + return PVE::GuestHelpers::guest_migration_lock($vmid, 10, $realcmd); + }; + + return $rpcenv->fork_worker('qmigrate', $vmid, $authuser, $worker); } }}); @@ -3384,7 +3427,12 @@ __PACKAGE__->register_method({ PVE::QemuConfig->snapshot_rollback($vmid, $snapname); }; - return $rpcenv->fork_worker('qmrollback', $vmid, $authuser, $realcmd); + my $worker = sub { + # hold migration lock, this makes sure that nobody create replication snapshots + return PVE::GuestHelpers::guest_migration_lock($vmid, 10, $realcmd); + }; + + return $rpcenv->fork_worker('qmrollback', $vmid, $authuser, $worker); }}); __PACKAGE__->register_method({