From 5fdbe4f02322a38943cb1b80a774c738662e1bdb Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Mon, 10 Oct 2011 13:17:40 +0200 Subject: [PATCH] create background tasks --- PVE/API2/Qemu.pm | 440 +++++++++++++++++++++++++++++++++++++++------- PVE/QemuServer.pm | 12 +- qm | 52 ++---- 3 files changed, 400 insertions(+), 104 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 324900b..eba65b5 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -77,10 +77,16 @@ __PACKAGE__->register_method({ vmid => get_standard_option('pve-vmid'), }), }, - returns => { type => 'null'}, + returns => { + type => 'string', + }, code => sub { my ($param) = @_; + my $rpcenv = PVE::RPCEnvironment::get(); + + my $user = $rpcenv->get_user(); + my $node = extract_param($param, 'node'); # fixme: fork worker? @@ -108,47 +114,50 @@ __PACKAGE__->register_method({ PVE::QemuServer::add_random_macs($param); - #fixme: ? syslog ('info', "VM $vmid creating new virtual machine"); - - my $vollist = []; - my $createfn = sub { # second test (after locking test is accurate) die "unable to create vm $vmid: config file already exists\n" if -f $filename; - $vollist = PVE::QemuServer::create_disks($storecfg, $vmid, $param); + my $realcmd = sub { - # try to be smart about bootdisk - my @disks = PVE::QemuServer::disknames(); - my $firstdisk; - foreach my $ds (reverse @disks) { - next if !$param->{$ds}; - my $disk = PVE::QemuServer::parse_drive($ds, $param->{$ds}); - next if PVE::QemuServer::drive_is_cdrom($disk); - $firstdisk = $ds; - } + my $vollist = []; - if (!$param->{bootdisk} && $firstdisk) { - $param->{bootdisk} = $firstdisk; - } + eval { + $vollist = PVE::QemuServer::create_disks($storecfg, $vmid, $param); - PVE::QemuServer::create_conf_nolock($vmid, $param); - }; + # try to be smart about bootdisk + my @disks = PVE::QemuServer::disknames(); + my $firstdisk; + foreach my $ds (reverse @disks) { + next if !$param->{$ds}; + my $disk = PVE::QemuServer::parse_drive($ds, $param->{$ds}); + next if PVE::QemuServer::drive_is_cdrom($disk); + $firstdisk = $ds; + } - eval { PVE::QemuServer::lock_config($vmid, $createfn); }; - my $err = $@; + if (!$param->{bootdisk} && $firstdisk) { + $param->{bootdisk} = $firstdisk; + } - if ($err) { - foreach my $volid (@$vollist) { - eval { PVE::Storage::vdisk_free($storecfg, $volid); }; - warn $@ if $@; - } - die "create failed - $err"; - } + PVE::QemuServer::create_conf_nolock($vmid, $param); + }; + my $err = $@; - return undef; + if ($err) { + foreach my $volid (@$vollist) { + eval { PVE::Storage::vdisk_free($storecfg, $volid); }; + warn $@ if $@; + } + die "create failed - $err"; + } + }; + + return $rpcenv->fork_worker('qmcreate', $vmid, $user, $realcmd); + }; + + return PVE::QemuServer::lock_config($vmid, $createfn); }}); __PACKAGE__->register_method({ @@ -355,6 +364,13 @@ __PACKAGE__->register_method({ my $vmid = extract_param($param, 'vmid'); + my $digest = extract_param($param, 'digest'); + + my @paramarr = (); # used for log message + foreach my $key (keys %$param) { + push @paramarr, "-$key", $param->{$key}; + } + my $skiplock = extract_param($param, 'skiplock'); raise_param_exc({ skiplock => "Only root may use this option." }) if $skiplock && $user ne 'root@pam'; @@ -364,8 +380,6 @@ __PACKAGE__->register_method({ die "no options specified\n" if !$delete && !scalar(keys %$param); - my $digest = extract_param($param, 'digest'); - my $storecfg = PVE::Storage::config(); &$resolve_cdrom_alias($param); @@ -411,6 +425,8 @@ __PACKAGE__->register_method({ PVE::QemuServer::check_lock($conf) if !$skiplock; + PVE::Cluster::log_msg('info', $user, "update VM $vmid: " . join (' ', @paramarr)); + foreach my $opt (keys %$eject) { if ($conf->{$opt}) { my $drive = PVE::QemuServer::parse_drive($opt, $conf->{$opt}); @@ -510,7 +526,9 @@ __PACKAGE__->register_method({ skiplock => get_standard_option('skiplock'), }, }, - returns => { type => 'null' }, + returns => { + type => 'string', + }, code => sub { my ($param) = @_; @@ -524,11 +542,16 @@ __PACKAGE__->register_method({ raise_param_exc({ skiplock => "Only root may use this option." }) if $skiplock && $user ne 'root@pam'; + # test if VM exists + my $conf = PVE::QemuServer::load_config($vmid); + my $storecfg = PVE::Storage::config(); - PVE::QemuServer::vm_destroy($storecfg, $vmid, $skiplock); + my $realcmd = sub { + PVE::QemuServer::vm_destroy($storecfg, $vmid, $skiplock); + }; - return undef; + return $rpcenv->fork_worker('qmdestroy', $vmid, $user, $realcmd); }}); __PACKAGE__->register_method({ @@ -651,9 +674,47 @@ __PACKAGE__->register_method({ }; }}); +__PACKAGE__->register_method({ + name => 'vmcmdidx', + path => '{vmid}/status', + method => 'GET', + proxyto => 'node', + description => "Directory index", + parameters => { + additionalProperties => 0, + properties => { + node => get_standard_option('pve-node'), + vmid => get_standard_option('pve-vmid'), + }, + }, + returns => { + type => 'array', + items => { + type => "object", + properties => { + subdir => { type => 'string' }, + }, + }, + links => [ { rel => 'child', href => "{subdir}" } ], + }, + code => sub { + my ($param) = @_; + + # test if VM exists + my $conf = PVE::QemuServer::load_config($param->{vmid}); + + my $res = [ + { subdir => 'current' }, + { subdir => 'start' }, + { subdir => 'stop' }, + ]; + + return $res; + }}); + __PACKAGE__->register_method({ name => 'vm_status', - path => '{vmid}/status', + path => '{vmid}/status/current', method => 'GET', proxyto => 'node', protected => 1, # qemu pid files are only readable by root @@ -678,12 +739,12 @@ __PACKAGE__->register_method({ }}); __PACKAGE__->register_method({ - name => 'vm_command', - path => '{vmid}/status', - method => 'PUT', + name => 'vm_start', + path => '{vmid}/status/start', + method => 'POST', protected => 1, proxyto => 'node', - description => "Set virtual machine status (execute vm commands).", + description => "Start virtual machine.", parameters => { additionalProperties => 0, properties => { @@ -691,14 +752,11 @@ __PACKAGE__->register_method({ vmid => get_standard_option('pve-vmid'), skiplock => get_standard_option('skiplock'), stateuri => get_standard_option('pve-qm-stateuri'), - command => { - description => "The command to execute.", - type => 'string', - enum => [qw(start stop reset shutdown cad suspend resume) ], - }, }, }, - returns => { type => 'null'}, + returns => { + type => 'string', + }, code => sub { my ($param) = @_; @@ -718,29 +776,289 @@ __PACKAGE__->register_method({ raise_param_exc({ skiplock => "Only root may use this option." }) if $skiplock && $user ne 'root@pam'; - my $command = $param->{command}; - my $storecfg = PVE::Storage::config(); - - if ($command eq 'start') { + + my $realcmd = sub { + my $upid = shift; + + syslog('info', "start VM $vmid: $upid\n"); + PVE::QemuServer::vm_start($storecfg, $vmid, $stateuri, $skiplock); - } elsif ($command eq 'stop') { + + return; + }; + + return $rpcenv->fork_worker('qmstart', $vmid, $user, $realcmd); + }}); + +__PACKAGE__->register_method({ + name => 'vm_stop', + path => '{vmid}/status/stop', + method => 'POST', + protected => 1, + proxyto => 'node', + description => "Stop virtual machine.", + parameters => { + additionalProperties => 0, + properties => { + node => get_standard_option('pve-node'), + vmid => get_standard_option('pve-vmid'), + skiplock => get_standard_option('skiplock'), + }, + }, + returns => { + type => 'string', + }, + code => sub { + my ($param) = @_; + + my $rpcenv = PVE::RPCEnvironment::get(); + + my $user = $rpcenv->get_user(); + + my $node = extract_param($param, 'node'); + + my $vmid = extract_param($param, 'vmid'); + + my $skiplock = extract_param($param, 'skiplock'); + raise_param_exc({ skiplock => "Only root may use this option." }) + if $skiplock && $user ne 'root@pam'; + + my $realcmd = sub { + my $upid = shift; + + syslog('info', "stop VM $vmid: $upid\n"); + PVE::QemuServer::vm_stop($vmid, $skiplock); - } elsif ($command eq 'reset') { + + return; + }; + + return $rpcenv->fork_worker('qmstop', $vmid, $user, $realcmd); + }}); + +__PACKAGE__->register_method({ + name => 'vm_reset', + path => '{vmid}/status/reset', + method => 'POST', + protected => 1, + proxyto => 'node', + description => "Reset virtual machine.", + parameters => { + additionalProperties => 0, + properties => { + node => get_standard_option('pve-node'), + vmid => get_standard_option('pve-vmid'), + skiplock => get_standard_option('skiplock'), + }, + }, + returns => { + type => 'string', + }, + code => sub { + my ($param) = @_; + + my $rpcenv = PVE::RPCEnvironment::get(); + + my $user = $rpcenv->get_user(); + + my $node = extract_param($param, 'node'); + + my $vmid = extract_param($param, 'vmid'); + + my $skiplock = extract_param($param, 'skiplock'); + raise_param_exc({ skiplock => "Only root may use this option." }) + if $skiplock && $user ne 'root@pam'; + + my $realcmd = sub { + my $upid = shift; + + syslog('info', "reset VM $vmid: $upid\n"); + PVE::QemuServer::vm_reset($vmid, $skiplock); - } elsif ($command eq 'shutdown') { + + return; + }; + + return $rpcenv->fork_worker('qmreset', $vmid, $user, $realcmd); + }}); + +__PACKAGE__->register_method({ + name => 'vm_shutdown', + path => '{vmid}/status/shutdown', + method => 'POST', + protected => 1, + proxyto => 'node', + description => "Shutdown virtual machine.", + parameters => { + additionalProperties => 0, + properties => { + node => get_standard_option('pve-node'), + vmid => get_standard_option('pve-vmid'), + skiplock => get_standard_option('skiplock'), + }, + }, + returns => { + type => 'string', + }, + code => sub { + my ($param) = @_; + + my $rpcenv = PVE::RPCEnvironment::get(); + + my $user = $rpcenv->get_user(); + + my $node = extract_param($param, 'node'); + + my $vmid = extract_param($param, 'vmid'); + + my $skiplock = extract_param($param, 'skiplock'); + raise_param_exc({ skiplock => "Only root may use this option." }) + if $skiplock && $user ne 'root@pam'; + + my $realcmd = sub { + my $upid = shift; + + syslog('info', "shutdown VM $vmid: $upid\n"); + PVE::QemuServer::vm_shutdown($vmid, $skiplock); - } elsif ($command eq 'suspend') { + + return; + }; + + return $rpcenv->fork_worker('qmshutdown', $vmid, $user, $realcmd); + }}); + +__PACKAGE__->register_method({ + name => 'vm_suspend', + path => '{vmid}/status/suspend', + method => 'POST', + protected => 1, + proxyto => 'node', + description => "Suspend virtual machine.", + parameters => { + additionalProperties => 0, + properties => { + node => get_standard_option('pve-node'), + vmid => get_standard_option('pve-vmid'), + skiplock => get_standard_option('skiplock'), + }, + }, + returns => { + type => 'string', + }, + code => sub { + my ($param) = @_; + + my $rpcenv = PVE::RPCEnvironment::get(); + + my $user = $rpcenv->get_user(); + + my $node = extract_param($param, 'node'); + + my $vmid = extract_param($param, 'vmid'); + + my $skiplock = extract_param($param, 'skiplock'); + raise_param_exc({ skiplock => "Only root may use this option." }) + if $skiplock && $user ne 'root@pam'; + + my $realcmd = sub { + my $upid = shift; + + syslog('info', "suspend VM $vmid: $upid\n"); + PVE::QemuServer::vm_suspend($vmid, $skiplock); - } elsif ($command eq 'resume') { + + return; + }; + + return $rpcenv->fork_worker('qmsuspend', $vmid, $user, $realcmd); + }}); + +__PACKAGE__->register_method({ + name => 'vm_resume', + path => '{vmid}/status/resume', + method => 'POST', + protected => 1, + proxyto => 'node', + description => "Resume virtual machine.", + parameters => { + additionalProperties => 0, + properties => { + node => get_standard_option('pve-node'), + vmid => get_standard_option('pve-vmid'), + skiplock => get_standard_option('skiplock'), + }, + }, + returns => { + type => 'string', + }, + code => sub { + my ($param) = @_; + + my $rpcenv = PVE::RPCEnvironment::get(); + + my $user = $rpcenv->get_user(); + + my $node = extract_param($param, 'node'); + + my $vmid = extract_param($param, 'vmid'); + + my $skiplock = extract_param($param, 'skiplock'); + raise_param_exc({ skiplock => "Only root may use this option." }) + if $skiplock && $user ne 'root@pam'; + + my $realcmd = sub { + my $upid = shift; + + syslog('info', "resume VM $vmid: $upid\n"); + PVE::QemuServer::vm_resume($vmid, $skiplock); - } elsif ($command eq 'cad') { - PVE::QemuServer::vm_cad($vmid, $skiplock); - } else { - raise_param_exc({ command => "unknown command '$command'" }) - } - return undef; + return; + }; + + return $rpcenv->fork_worker('qmresume', $vmid, $user, $realcmd); + }}); + +__PACKAGE__->register_method({ + name => 'vm_sendkey', + path => '{vmid}/sendkey', + method => 'PUT', + protected => 1, + proxyto => 'node', + description => "Send key event to virtual machine.", + parameters => { + additionalProperties => 0, + properties => { + node => get_standard_option('pve-node'), + vmid => get_standard_option('pve-vmid'), + skiplock => get_standard_option('skiplock'), + key => { + description => "The key (qemu monitor encoding).", + type => 'string' + } + }, + }, + returns => { type => 'null'}, + code => sub { + my ($param) = @_; + + my $rpcenv = PVE::RPCEnvironment::get(); + + my $user = $rpcenv->get_user(); + + my $node = extract_param($param, 'node'); + + my $vmid = extract_param($param, 'vmid'); + + my $skiplock = extract_param($param, 'skiplock'); + raise_param_exc({ skiplock => "Only root may use this option." }) + if $skiplock && $user ne 'root@pam'; + + PVE::QemuServer::vm_sendkey($vmid, $skiplock, $param->{key}); + + return; }}); __PACKAGE__->register_method({ diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 2e70d80..82cadf5 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -1215,9 +1215,11 @@ sub lock_config { my $filename = config_file_lock($vmid); - lock_file($filename, 10, $code, @param); + my $res = lock_file($filename, 10, $code, @param); die $@ if $@; + + return $res; } sub cfs_config_path { @@ -2561,8 +2563,8 @@ sub vm_resume { }); } -sub vm_cad { - my ($vmid, $skiplock) = @_; +sub vm_sendkey { + my ($vmid, $skiplock, $key) = @_; lock_config($vmid, sub { @@ -2570,9 +2572,9 @@ sub vm_cad { check_lock($conf) if !$skiplock; - syslog("info", "VM $vmid sending cntl-alt-delete"); + syslog("info", "VM $vmid sending key $key"); - vm_monitor_command($vmid, "sendkey ctrl-alt-delete", 1); + vm_monitor_command($vmid, "sendkey $key", 1); }); } diff --git a/qm b/qm index 6cb0dd2..a03c0df 100755 --- a/qm +++ b/qm @@ -415,6 +415,20 @@ my $cmddef = { status => [ __PACKAGE__, 'status', ['vmid']], + start => [ "PVE::API2::Qemu", 'vm_start', ['vmid'], { node => $nodename } ], + + stop => [ "PVE::API2::Qemu", 'vm_stop', ['vmid'], { node => $nodename } ], + + reset => [ "PVE::API2::Qemu", 'vm_reset', ['vmid'], { node => $nodename } ], + + shutdown => [ "PVE::API2::Qemu", 'vm_shutdown', ['vmid'], { node => $nodename } ], + + suspend => [ "PVE::API2::Qemu", 'vm_suspend', ['vmid'], { node => $nodename } ], + + resume => [ "PVE::API2::Qemu", 'vm_resume', ['vmid'], { node => $nodename } ], + + sendkey => [ "PVE::API2::Qemu", 'vm_sendkey', ['vmid', 'key'], { node => $nodename } ], + vncproxy => [ __PACKAGE__, 'vncproxy', ['vmid']], wait => [ __PACKAGE__, 'wait', ['vmid']], @@ -430,44 +444,6 @@ my $cmddef = { mtunnel => [ __PACKAGE__, 'mtunnel', []], }; -sub register_vm_command { - my ($cmd, $descr) = @_; - - # we create a wrapper, because we want one 'description' per command - __PACKAGE__->register_method ({ - name => $cmd, - path => $cmd, - method => 'PUT', - description => $descr, - parameters => { - additionalProperties => 0, - properties => { - vmid => get_standard_option('pve-vmid'), - skiplock => get_standard_option('skiplock'), - stateuri => get_standard_option('pve-qm-stateuri'), - }, - }, - returns => { type => 'null'}, - code => sub { - my ($param) = @_; - - $param->{command} = $cmd; - $param->{node} = $nodename; - - return PVE::API2::Qemu->vm_command($param); - }}); - - $cmddef->{$cmd} = [ __PACKAGE__, $cmd, ['vmid']]; -} - -register_vm_command('start', "Start virtual machine."); -register_vm_command('stop', "Stop virtual machine."); -register_vm_command('reset', "Reset virtual machine."); -register_vm_command('shutdown', "Shutdown virtual machine (send ACPI showdown request)"); -register_vm_command('suspend', "Suspend virtual machine."); -register_vm_command('resume', "Resume virtual machine."); -register_vm_command('cad', "Send CTRL-ALT-DELETE keys."); - my $cmd = shift; PVE::CLIHandler::handle_cmd($cmddef, "qm", $cmd, \@ARGV, undef, $0); -- 2.39.2