]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/API2/Qemu.pm
use new PVE::Storage::check_volume_access()
[qemu-server.git] / PVE / API2 / Qemu.pm
index 902f028f3746f30371b8875abbc93ef536840cad..2c1e2c991e427f061a41b1b6f5772cc4105649ee 100644 (file)
@@ -60,7 +60,7 @@ my $check_storage_access = sub {
            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);
        }
     });
 };
@@ -157,7 +157,7 @@ my $create_disks = sub {
            $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;
 
@@ -474,7 +474,7 @@ __PACKAGE__->register_method({
                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);
            }
        }
@@ -614,6 +614,7 @@ __PACKAGE__->register_method({
            { subdir => 'rrd' },
            { subdir => 'rrddata' },
            { subdir => 'monitor' },
+           { subdir => 'agent' },
            { subdir => 'snapshot' },
            { subdir => 'spiceproxy' },
            { subdir => 'sendkey' },
@@ -1641,6 +1642,11 @@ __PACKAGE__->register_method({
                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 => {
@@ -1679,6 +1685,13 @@ __PACKAGE__->register_method({
        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')) {
@@ -1719,7 +1732,7 @@ __PACKAGE__->register_method({
                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;
            };
@@ -2442,21 +2455,28 @@ __PACKAGE__->register_method({
                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};
@@ -2477,6 +2497,8 @@ __PACKAGE__->register_method({
                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) {
@@ -2509,17 +2531,15 @@ __PACKAGE__->register_method({
     proxyto => 'node',
     description => "Move volume to different storage.",
     permissions => {
-       description => "You need 'VM.Config.Disk' permissions on /vms/{vmid}, " .
-           "and 'Datastore.AllocateSpace' permissions on the storage.",
-       check =>
-       [ 'and',
-         ['perm', '/vms/{vmid}', [ 'VM.Config.Disk' ]],
-         ['perm', '/storage/{storage}', [ 'Datastore.AllocateSpace' ]],
-       ],
+       description => "You need 'VM.Config.Disk' permissions on /vms/{vmid}, and 'Datastore.AllocateSpace' permissions on the storage.",
+       check => [ 'and',
+                  ['perm', '/vms/{vmid}', [ 'VM.Config.Disk' ]],
+                  ['perm', '/storage/{storage}', [ 'Datastore.AllocateSpace' ]],
+           ],
     },
     parameters => {
         additionalProperties => 0,
-        properties => {
+       properties => {
            node => get_standard_option('pve-node'),
            vmid => get_standard_option('pve-vmid', { completion => \&PVE::QemuServer::complete_vmid }),
            disk => {
@@ -2695,17 +2715,24 @@ __PACKAGE__->register_method({
            migration_type => {
                type => 'string',
                enum => ['secure', 'insecure'],
-               description => "Migration traffic is encrypted using an SSH " .
-                 "tunnel by default. On secure, completely private networks " .
-                 "this can be disabled to increase performance.",
+               description => "Migration traffic is encrypted using an SSH tunnel by default. On secure, completely private networks this can be disabled to increase performance.",
                optional => 1,
            },
            migration_network => {
-               type => 'string',
-               format => 'CIDR',
+               type => 'string', format => 'CIDR',
                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 => {
@@ -2732,6 +2759,9 @@ __PACKAGE__->register_method({
 
        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';
 
@@ -2797,7 +2827,7 @@ __PACKAGE__->register_method({
     description => "Execute Qemu monitor commands.",
     permissions => {
        description => "Sys.Modify is required for (sub)commands which are not read-only ('info *' and 'help')",
-       check => ['perm', '/vms/{vmid}', [ 'VM.Monitor' ]],
+        check => ['perm', '/vms/{vmid}', [ 'VM.Monitor' ]],
     },
     parameters => {
        additionalProperties => 0,
@@ -2839,6 +2869,70 @@ __PACKAGE__->register_method({
        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',
+    method => 'POST',
+    protected => 1,
+    proxyto => 'node',
+    description => "Execute Qemu Guest Agent commands.",
+    permissions => {
+       check => ['perm', '/vms/{vmid}', [ 'VM.Monitor' ]],
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+           vmid => get_standard_option('pve-vmid', {
+                   completion => \&PVE::QemuServer::complete_vmid_running }),
+           command => {
+               type => 'string',
+               description => "The QGA command.",
+               enum => $guest_agent_commands,
+           },
+       },
+    },
+    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 $vmid = $param->{vmid};
+
+       my $conf = PVE::QemuConfig->load_config ($vmid); # check if VM exists
+
+       die "No Qemu Guest Agent\n" if !defined($conf->{agent});
+       die "VM $vmid is not running\n" if !PVE::QemuServer::check_running($vmid);
+
+       my $cmd = $param->{command};
+
+       my $res = PVE::QemuServer::vm_mon_cmd($vmid, "guest-$cmd");
+
+       return { result => $res };
+    }});
+
 __PACKAGE__->register_method({
     name => 'resize_vm',
     path => '{vmid}/resize',