use PVE::Network;
use PVE::Firewall;
use PVE::API2::Firewall::VM;
-use PVE::HA::Env::PVE2;
-use PVE::HA::Config;
+
+BEGIN {
+ if (!$ENV{PVE_GENERATING_DOCS}) {
+ require PVE::HA::Env::PVE2;
+ import PVE::HA::Env::PVE2;
+ require PVE::HA::Config;
+ import PVE::HA::Config;
+ }
+}
use Data::Dumper; # fixme: remove
die "no storage ID specified (and no default storage)\n" if !$storeid;
$rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
} else {
- $rpcenv->check_volume_access($authuser, $storecfg, $vmid, $volid);
+ PVE::Storage::check_volume_access($rpcenv, $authuser, $storecfg, $vmid, $volid);
}
});
};
$res->{$ds} = PVE::QemuServer::print_drive($vmid, $disk);
} else {
- $rpcenv->check_volume_access($authuser, $storecfg, $vmid, $volid);
+ PVE::Storage::check_volume_access($rpcenv, $authuser, $storecfg, $vmid, $volid);
my $volid_is_new = 1;
die "pipe requires cli environment\n"
if $rpcenv->{type} ne 'cli';
} else {
- $rpcenv->check_volume_access($authuser, $storecfg, $vmid, $archive);
+ PVE::Storage::check_volume_access($rpcenv, $authuser, $storecfg, $vmid, $archive);
$archive = PVE::Storage::abs_filesystem_path($storecfg, $archive);
}
}
optional => 1,
},
machine => get_standard_option('pve-qm-machine'),
+ targetstorage => {
+ description => "Target storage for the migration. (Can be '1' to use the same storage id as on the source node.)",
+ type => 'string',
+ optional => 1
+ }
},
},
returns => {
raise_param_exc({ migration_network => "Only root may use this option." })
if $migration_network && $authuser ne 'root@pam';
+ my $targetstorage = extract_param($param, 'targetstorage');
+ raise_param_exc({ targetstorage => "Only root may use this option." })
+ if $targetstorage && $authuser ne 'root@pam';
+
+ raise_param_exc({ targetstorage => "targetstorage can only by used with migratedfrom." })
+ if $targetstorage && !$migratedfrom;
+
# read spice ticket from STDIN
my $spice_ticket;
if ($stateuri && ($stateuri eq 'tcp') && $migratedfrom && ($rpcenv->{type} eq 'cli')) {
syslog('info', "start VM $vmid: $upid\n");
PVE::QemuServer::vm_start($storecfg, $vmid, $stateuri, $skiplock, $migratedfrom, undef,
- $machine, $spice_ticket, $migration_network, $migration_type);
+ $machine, $spice_ticket, $migration_network, $migration_type, $targetstorage);
return;
};
my $upid = shift;
my $newvollist = [];
+ my $jobs = {};
eval {
local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = sub { die "interrupted by signal\n"; };
PVE::Storage::activate_volumes($storecfg, $vollist, $snapname);
+ my $total_jobs = scalar(keys %{$drives});
+ my $i = 1;
+
foreach my $opt (keys %$drives) {
my $drive = $drives->{$opt};
+ my $skipcomplete = ($total_jobs != $i); # finish after last drive
my $newdrive = PVE::QemuServer::clone_disk($storecfg, $vmid, $running, $opt, $drive, $snapname,
- $newid, $storage, $format, $fullclone->{$opt}, $newvollist);
+ $newid, $storage, $format, $fullclone->{$opt}, $newvollist,
+ $jobs, $skipcomplete, $oldconf->{agent});
$newconf->{$opt} = PVE::QemuServer::print_drive($vmid, $newdrive);
PVE::QemuConfig->write_config($newid, $newconf);
+ $i++;
}
delete $newconf->{lock};
if (my $err = $@) {
unlink $conffile;
+ eval { PVE::QemuServer::qemu_blockjobs_cancel($vmid, $jobs) };
+
sleep 1; # some storage like rbd need to wait before release volume - really?
foreach my $volid (@$newvollist) {
PVE::QemuConfig->add_unused_volume($conf, $old_volid) if !$param->{delete};
+ # convert moved disk to base if part of template
+ PVE::QemuServer::template_create($vmid, $conf, $disk)
+ if PVE::QemuConfig->is_template($conf);
+
PVE::QemuConfig->write_config($vmid, $conf);
eval {
description => "CIDR of the (sub) network that is used for migration.",
optional => 1,
},
+ "with-local-disks" => {
+ type => 'boolean',
+ description => "Enable live storage migration for local disk",
+ optional => 1,
+ },
+ targetstorage => get_standard_option('pve-storage-id', {
+ description => "Default target storage.",
+ optional => 1,
+ completion => \&PVE::QemuServer::complete_storage,
+ }),
},
},
returns => {
my $vmid = extract_param($param, 'vmid');
+ raise_param_exc({ targetstorage => "Live storage migration can only be done online." })
+ if !$param->{online} && $param->{targetstorage};
+
raise_param_exc({ force => "Only root may use this option." })
if $param->{force} && $authuser ne 'root@pam';
return $res;
}});
+my $guest_agent_commands = [
+ 'ping',
+ 'get-time',
+ 'info',
+ 'fsfreeze-status',
+ 'fsfreeze-freeze',
+ 'fsfreeze-thaw',
+ 'fstrim',
+ 'network-get-interfaces',
+ 'get-vcpus',
+ 'get-fsinfo',
+ 'get-memory-blocks',
+ 'get-memory-block-info',
+ 'suspend-hybrid',
+ 'suspend-ram',
+ 'suspend-disk',
+ 'shutdown',
+ ];
+
__PACKAGE__->register_method({
name => 'agent',
path => '{vmid}/agent',
command => {
type => 'string',
description => "The QGA command.",
+ enum => $guest_agent_commands,
},
},
},
- returns => { type => 'object' },
+ returns => {
+ type => 'object',
+ description => "Returns an object with a single `result` property. The type of that
+property depends on the executed command.",
+ },
code => sub {
my ($param) = @_;
my $conf = PVE::QemuConfig->load_config ($vmid); # check if VM exists
- die "Only qga commands are allowed\n" if $param->{command} !~ m/^guest-.*$/;
die "No Qemu Guest Agent\n" if !defined($conf->{agent});
die "VM $vmid is not running\n" if !PVE::QemuServer::check_running($vmid);
- my $res = '';
- eval {
- $res = PVE::QemuServer::vm_mon_cmd($vmid, $param->{command});
- };
+ my $cmd = $param->{command};
- if (my $err = $@) {
- return {'ERROR:', $err};
- } else {
- return {'OK:', $res};
- }
+ my $res = PVE::QemuServer::vm_mon_cmd($vmid, "guest-$cmd");
+
+ return { result => $res };
}});
__PACKAGE__->register_method({