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;
}
};
+my $NEW_DISK_RE = qr!^(([^/:\s]+):)?(\d+(\.\d+)?)$!;
my $check_storage_access = sub {
my ($rpcenv, $authuser, $storecfg, $vmid, $settings, $default_storage) = @_;
# 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);
}
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);
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 {
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);
};
# 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 = [];
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};
}
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
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;
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);
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);
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);
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);
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);
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);
} 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);
}
}});
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({