X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=PVE%2FCLI%2Fqm.pm;h=5dce10fa163863c47c91351adb0e18d91aa2dc31;hb=1d5aaa1db57c6854d240dbdd8e870119f972cace;hp=0dd81590d6bacb5b4aee45c002d24087d611734e;hpb=12612b09ae4cadae5b654892fefd20ad24c5f5e3;p=qemu-server.git diff --git a/PVE/CLI/qm.pm b/PVE/CLI/qm.pm index 0dd8159..5dce10f 100755 --- a/PVE/CLI/qm.pm +++ b/PVE/CLI/qm.pm @@ -17,7 +17,9 @@ use PVE::SafeSyslog; use PVE::INotify; use PVE::RPCEnvironment; use PVE::QemuServer; +use PVE::QemuServer::ImportDisk; use PVE::API2::Qemu; +use JSON; use PVE::JSONSchema qw(get_standard_option); use Term::ReadLine; @@ -33,6 +35,10 @@ my $upid_exit = sub { my $nodename = PVE::INotify::nodename(); +sub setup_environment { + PVE::RPCEnvironment->setup_default_cli_env(); +} + sub run_vnc_proxy { my ($path) = @_; @@ -75,6 +81,32 @@ sub run_vnc_proxy { exit(0); } +sub print_recursive_hash { + my ($prefix, $hash, $key) = @_; + + if (ref($hash) eq 'HASH') { + if (defined($key)) { + print "$prefix$key:\n"; + } + foreach my $itemkey (keys %$hash) { + print_recursive_hash("\t$prefix", $hash->{$itemkey}, $itemkey); + } + } elsif (ref($hash) eq 'ARRAY') { + if (defined($key)) { + print "$prefix$key:\n"; + } + foreach my $item (@$hash) { + print_recursive_hash("\t$prefix", $item); + } + } elsif (!ref($hash) && defined($hash)) { + if (defined($key)) { + print "$prefix$key: $hash\n"; + } else { + print "$prefix$hash\n"; + } + } +} + __PACKAGE__->register_method ({ name => 'showcmd', path => 'showcmd', @@ -117,7 +149,7 @@ __PACKAGE__->register_method ({ my ($param) = @_; # test if VM exists - my $conf = PVE::QemuServer::load_config ($param->{vmid}); + my $conf = PVE::QemuConfig->load_config ($param->{vmid}); my $vmstatus = PVE::QemuServer::vmstatus($param->{vmid}, 1); my $stat = $vmstatus->{$param->{vmid}}; @@ -125,8 +157,7 @@ __PACKAGE__->register_method ({ foreach my $k (sort (keys %$stat)) { next if $k eq 'cpu' || $k eq 'relcpu'; # always 0 my $v = $stat->{$k}; - next if !defined($v); - print "$k: $v\n"; + print_recursive_hash("", $v, $k); } } else { my $status = $stat->{qmpstatus} || 'unknown'; @@ -184,16 +215,38 @@ __PACKAGE__->register_method ({ my $vmid = $param->{vmid}; - PVE::QemuServer::lock_config ($vmid, sub { - my $conf = PVE::QemuServer::load_config($vmid); + PVE::QemuConfig->lock_config ($vmid, sub { + my $conf = PVE::QemuConfig->load_config($vmid); delete $conf->{lock}; delete $conf->{pending}->{lock} if $conf->{pending}; # just to be sure - PVE::QemuServer::write_config($vmid, $conf); + PVE::QemuConfig->write_config($vmid, $conf); }); return undef; }}); +__PACKAGE__->register_method ({ + name => 'nbdstop', + path => 'nbdstop', + method => 'PUT', + description => "Stop embedded nbd server.", + parameters => { + additionalProperties => 0, + properties => { + vmid => get_standard_option('pve-vmid', { completion => \&PVE::QemuServer::complete_vmid }), + }, + }, + returns => { type => 'null'}, + code => sub { + my ($param) = @_; + + my $vmid = $param->{vmid}; + + PVE::QemuServer::nbd_stop($vmid); + + return undef; + }}); + __PACKAGE__->register_method ({ name => 'mtunnel', path => 'mtunnel', @@ -212,12 +265,34 @@ __PACKAGE__->register_method ({ return undef; } - print "tunnel online\n"; - *STDOUT->flush(); + my $tunnel_write = sub { + my $text = shift; + chomp $text; + print "$text\n"; + *STDOUT->flush(); + }; + + $tunnel_write->("tunnel online"); + $tunnel_write->("ver 1"); while (my $line = <>) { chomp $line; - last if $line =~ m/^quit$/; + if ($line =~ /^quit$/) { + $tunnel_write->("OK"); + last; + } elsif ($line =~ /^resume (\d+)$/) { + my $vmid = $1; + if (PVE::QemuServer::check_running($vmid, 1)) { + eval { PVE::QemuServer::vm_resume($vmid, 1, 1); }; + if ($@) { + $tunnel_write->("ERR: resume failed - $@"); + } else { + $tunnel_write->("OK"); + } + } else { + $tunnel_write->("ERR: resume failed - VM $vmid not running"); + } + } } return undef; @@ -280,7 +355,7 @@ __PACKAGE__->register_method ({ my $vmid = $param->{vmid}; - my $conf = PVE::QemuServer::load_config ($vmid); # check if VM exists + my $conf = PVE::QemuConfig->load_config ($vmid); # check if VM exists print "Entering Qemu Monitor for VM $vmid - type 'help' for help\n"; @@ -327,6 +402,60 @@ __PACKAGE__->register_method ({ return undef; }}); +__PACKAGE__->register_method ({ + name => 'importdisk', + path => 'importdisk', + method => 'POST', + description => "Import an external disk image as an unused disk in a VM. The + image format has to be supported by qemu-img(1).", + parameters => { + additionalProperties => 0, + properties => { + vmid => get_standard_option('pve-vmid', {completion => \&PVE::QemuServer::complete_vmid}), + source => { + description => 'Path to the disk image to import', + type => 'string', + optional => 0, + }, + storage => get_standard_option('pve-storage-id', { + description => 'Target storage ID', + completion => \&PVE::QemuServer::complete_storage, + optional => 0, + }), + format => { + type => 'string', + description => 'Target format', + enum => [ 'raw', 'qcow2', 'vmdk' ], + optional => 1, + }, + }, + }, + returns => { type => 'null'}, + code => sub { + my ($param) = @_; + + my $vmid = extract_param($param, 'vmid'); + my $source = extract_param($param, 'source'); + my $storeid = extract_param($param, 'storage'); + my $format = extract_param($param, 'format'); + + my $vm_conf = PVE::QemuConfig->load_config($vmid); + PVE::QemuConfig->check_lock($vm_conf); + die "$source: non-existent or non-regular file\n" if (! -f $source); + + my $storecfg = PVE::Storage::config(); + PVE::Storage::storage_check_enabled($storecfg, $storeid); + + my $target_storage_config = + PVE::Storage::storage_config($storecfg, $storeid); + die "storage $storeid does not support vm images\n" + if !$target_storage_config->{content}->{images}; + + PVE::QemuServer::ImportDisk::do_import($source, $vmid, $storeid, { format => $format }); + + return undef; + }}); + __PACKAGE__->register_method ({ name => 'terminal', path => 'terminal', @@ -350,7 +479,7 @@ __PACKAGE__->register_method ({ my $vmid = $param->{vmid}; - my $conf = PVE::QemuServer::load_config ($vmid); # check if VM exists + my $conf = PVE::QemuConfig->load_config ($vmid); # check if VM exists my $iface = $param->{iface}; @@ -380,6 +509,29 @@ __PACKAGE__->register_method ({ return undef; }}); + +my $print_agent_result = sub { + my ($data) = @_; + + my $result = $data->{result}; + return if !defined($result); + + my $class = ref($result); + + if (!$class) { + chomp $result; + return if $result =~ m/^\s*$/; + print "$result\n"; + return; + } + + if (($class eq 'HASH') && !scalar(keys %$result)) { # empty hash + return; + } + + print to_json($result, { pretty => 1, canonical => 1}); +}; + our $cmddef = { list => [ "PVE::API2::Qemu", 'vmlist', [], { node => $nodename }, sub { @@ -465,6 +617,17 @@ our $cmddef = { delsnapshot => [ "PVE::API2::Qemu", 'delsnapshot', ['vmid', 'snapname'], { node => $nodename } , $upid_exit ], + listsnapshot => [ "PVE::API2::Qemu", 'snapshot_list', ['vmid'], { node => $nodename }, + sub { + my $res = shift; + foreach my $e (@$res) { + my $headline = $e->{description} || 'no-description'; + $headline =~ s/\n.*//sg; + my $parent = $e->{parent} // 'no-parent'; + printf("%-20s %-20s %s\n", $e->{name}, $parent, $headline); + } + }], + rollback => [ "PVE::API2::Qemu", 'rollback', ['vmid', 'snapname'], { node => $nodename } , $upid_exit ], template => [ "PVE::API2::Qemu", 'template', ['vmid'], { node => $nodename }], @@ -493,65 +656,16 @@ our $cmddef = { monitor => [ __PACKAGE__, 'monitor', ['vmid']], + agent => [ "PVE::API2::Qemu", 'agent', ['vmid', 'command'], + { node => $nodename }, $print_agent_result ], + mtunnel => [ __PACKAGE__, 'mtunnel', []], + nbdstop => [ __PACKAGE__, 'nbdstop', ['vmid']], + terminal => [ __PACKAGE__, 'terminal', ['vmid']], + + importdisk => [ __PACKAGE__, 'importdisk', ['vmid', 'source', 'storage']], }; 1; - -__END__ - -=head1 NAME - -qm - qemu/kvm virtual machine manager - -=head1 SYNOPSIS - -=include synopsis - -=head1 DESCRIPTION - -qm is a script to manage virtual machines with qemu/kvm. You can -create and destroy virtual machines, and control execution -(start/stop/suspend/resume). Besides that, you can use qm to set -parameters in the associated config file. It is also possible to -create and delete virtual disks. - -=head1 CONFIGURATION - -All configuration files consists of lines in the form - - PARAMETER: value - -See L for a complete list of options. - -Configuration files are stored inside the Proxmox configuration file system, and can be access at F.conf>. - -The default for option 'keyboard' is read from -F. - -=head1 Locks - -Online migration and backups (vzdump) set a lock to prevent -unintentional action on such VMs. Sometimes you need remove such lock -manually (power failure). - - qm unlock - -=head1 EXAMPLES - - # create a new VM with 4 GB ide disk - qm create 300 -ide0 4 -net0 e1000 -cdrom proxmox-mailgateway_2.1.iso - - # start the new VM - qm start 300 - - # send shutdown, then wait until VM is stopped - qm shutdown 300 && qm wait 300 - - # same as above, but only wait for 40 seconds - qm shutdown 300 && qm wait 300 -timeout 40 - - -=include pve_copyright