mirror of
https://git.proxmox.com/git/qemu-server
synced 2025-08-06 11:08:34 +00:00
1074 lines
26 KiB
Perl
Executable File
1074 lines
26 KiB
Perl
Executable File
#!/usr/bin/perl -w
|
|
|
|
use strict;
|
|
|
|
use Term::ReadLine;
|
|
use PVE::QemuServer;
|
|
use IO::Socket::INET;
|
|
use File::Path;
|
|
use Sys::Syslog;
|
|
use PVE::Storage;
|
|
|
|
# VNC proxy example:
|
|
# nc -l -p 5900 -w 20 -c "/usr/sbin/qm vncproxy 200 <ticket>"
|
|
# where ticket(password) is an arbitrary string containing
|
|
# [A-Za-z0-9\+\/] (base64 charset)
|
|
# simple vncproxy example: nc -l -p 5900 -c "qm vncproxy 200 test"
|
|
|
|
|
|
# command used by the vnc option
|
|
my $vnccmd = "echo -e '__TICKET__\\n__TICKET__' |vncpasswd __TMPFILE__;vncviewer localhost:__DISPLAY__ -passwd __TMPFILE__";
|
|
|
|
openlog ('qm', 'cons,pid', 'daemon');
|
|
|
|
sub print_usage {
|
|
my ($msg) = @_;
|
|
|
|
if ($msg) {
|
|
print STDERR "ERROR: $msg\n";
|
|
}
|
|
print STDERR "qm <command> <vmid> [OPTIONS]\n";
|
|
print STDERR "qm [create|set] <vmid>\n";
|
|
print STDERR "\t--memory <MBYTES> memory in MB (64 - 8192)\n";
|
|
print STDERR "\t--sockets <N> set number of CPU sockets <N>\n";
|
|
print STDERR "\t--cores <N> set cores per socket to <N>\n";
|
|
print STDERR "\t--ostype NAME specify OS type\n";
|
|
print STDERR "\t--onboot [yes|no] start at boot\n";
|
|
print STDERR "\t--keyboard XX set vnc keyboard layout\n";
|
|
print STDERR "\t--cpuunits <num> CPU weight for a VM\n";
|
|
print STDERR "\t--name <text> set a name for the VM\n";
|
|
print STDERR "\t--description <text> set VM description\n";
|
|
print STDERR "\t--boot [a|c|d|n] specify boot order\n";
|
|
print STDERR "\t--bootdisk <disk> enable booting from <disk>\n";
|
|
print STDERR "\t--acpi (yes|no) enable/disable ACPI\n";
|
|
print STDERR "\t--kvm (yes|no) enable/disable KVM\n";
|
|
print STDERR "\t--tdf (yes|no) enable/disable time drift fix\n";
|
|
print STDERR "\t--localtime (yes|no) set the RTC to local time\n";
|
|
print STDERR "\t--vga (gd5446|vesa) specify VGA type\n";
|
|
|
|
print STDERR "\n";
|
|
print STDERR "\t--vlan[0-9u] MODEL=XX:XX:XX:XX:XX:XX[,MODEL=YY:YY:YY:YY:YY:YY]\n";
|
|
|
|
print STDERR "\n";
|
|
print STDERR "\t--ide<N> [volume=]volume,[,media=cdrom|disk]\n";
|
|
print STDERR "\t [,cyls=c,heads=h,secs=s[,trans=t]]\n";
|
|
print STDERR "\t [,cache=none|writethrough|writeback]\n";
|
|
print STDERR "\t [,snapshot=on|off][,cache=on|off][,format=f]\n";
|
|
print STDERR "\t [,werror=enospc|ignore|report|stop]\n";
|
|
print STDERR "\t [,rerror=ignore|report|stop]\n";
|
|
print STDERR "\t--ide<N> <GBYTES> create new disk\n";
|
|
print STDERR "\t--ide<N> delete remove drive - destroy image\n";
|
|
print STDERR "\t--ide<N> undef remove drive - keep image\n";
|
|
|
|
print STDERR "\t--cdrom <file> is an alias for --ide2 <file>,media=cdrom\n";
|
|
|
|
print STDERR "\n";
|
|
print STDERR "\t--scsi<N> [volume=]volume,[,media=cdrom|disk]\n";
|
|
print STDERR "\t [,cyls=c,heads=h,secs=s[,trans=t]]\n";
|
|
print STDERR "\t [,snapshot=on|off][,format=f]\n";
|
|
print STDERR "\t [,cache=none|writethrough|writeback]\n";
|
|
print STDERR "\t [,werror=enospc|ignore|report|stop]\n";
|
|
print STDERR "\t--scsi<N> <GBYTES> create new disk\n";
|
|
print STDERR "\t--scsi<N> delete remove drive - destroy image\n";
|
|
print STDERR "\t--scsi<N> undef remove drive - keep image\n";
|
|
|
|
print STDERR "\n";
|
|
print STDERR "\t--virtio<N> [volume=]volume,[,media=cdrom|disk]\n";
|
|
print STDERR "\t [,cyls=c,heads=h,secs=s[,trans=t]]\n";
|
|
print STDERR "\t [,snapshot=on|off][,format=f]\n";
|
|
print STDERR "\t [,cache=none|writethrough|writeback]\n";
|
|
print STDERR "\t [,werror=enospc|ignore|report|stop]\n";
|
|
print STDERR "\t [,rerror=ignore|report|stop]\n";
|
|
print STDERR "\t--virtio<N> <GBYTES> create new disk\n";
|
|
print STDERR "\t--virtio<N> delete remove drive - destroy image\n";
|
|
print STDERR "\t--virtio<N> undef remove drive - keep image\n";
|
|
|
|
print STDERR "\n";
|
|
|
|
print STDERR "qm monitor <vmid> connect to vm control monitor\n";
|
|
print STDERR "qm start <vmid> start vm\n";
|
|
print STDERR "qm shutdown <vmid> gracefully stop vm (send poweroff)\n";
|
|
print STDERR "qm wait <vmid> [time] wait until vm is stopped\n";
|
|
print STDERR "qm stop <vmid> kill vm (immediate stop)\n";
|
|
print STDERR "qm reset <vmid> reset vm (stop, start)\n";
|
|
print STDERR "qm suspend <vmid> suspend vm\n";
|
|
print STDERR "qm resume <vmid> resume vm\n";
|
|
print STDERR "qm cad <vmid> sendkey ctrl-alt-delete\n";
|
|
print STDERR "qm destroy <vmid> destroy vm (delete all used/owned volumes)\n";
|
|
print STDERR "qm unlock <vmid> clear migrate/backup lock\n";
|
|
print STDERR "qm status <vmid> shows the container status\n";
|
|
|
|
print STDERR "\n";
|
|
print STDERR "qm cdrom <vmid> [<device>] <path> set cdrom path. <device is ide2 by default>\n";
|
|
print STDERR "qm cdrom <vmid> [<device>] eject eject cdrom\n";
|
|
|
|
print STDERR "\n";
|
|
print STDERR "qm unlink <vmid> <volume> delete unused disk images\n";
|
|
|
|
print STDERR "qm vncproxy <vmid> <ticket> open vnc proxy\n";
|
|
print STDERR "qm vnc <vmid> start (X11) vncviewer (experimental)\n";
|
|
print STDERR "qm showcmd <vmid> show command line (debug info)\n";
|
|
print STDERR "qm list list all virtual machines\n";
|
|
|
|
print STDERR "\n";
|
|
print STDERR "qm startall start all virtual machines (when onboot=1)\n";
|
|
print STDERR "qm stopall [timeout] stop all virtual machines (default timeout is 3 minutes)
|
|
\n";
|
|
|
|
}
|
|
|
|
sub __next_vnc_port {
|
|
|
|
for (my $p = 5900; $p < 6000; $p++) {
|
|
|
|
my $sock = IO::Socket::INET->new (Listen => 5,
|
|
LocalAddr => 'localhost',
|
|
LocalPort => $p,
|
|
ReuseAddr => 1,
|
|
Proto => 0);
|
|
|
|
if ($sock) {
|
|
close ($sock);
|
|
return $p;
|
|
}
|
|
}
|
|
|
|
die "unable to find free vnc port";
|
|
}
|
|
|
|
sub run_vnc_proxy {
|
|
my ($vmid) = @_;
|
|
|
|
my $path = PVE::QemuServer::vnc_socket ($vmid);
|
|
|
|
my $s = IO::Socket::UNIX->new (Peer => $path, Timeout => 120);
|
|
|
|
die "unable to connect to socket '$path' - $!" if !$s;
|
|
|
|
my $select = new IO::Select;
|
|
|
|
$select->add (\*STDIN);
|
|
$select->add ($s);
|
|
|
|
my $timeout = 60*15; # 15 minutes
|
|
|
|
my @handles;
|
|
while ($select->count &&
|
|
scalar (@handles = $select->can_read ($timeout))) {
|
|
foreach my $h (@handles) {
|
|
my $buf;
|
|
my $n = $h->sysread ($buf, 4096);
|
|
|
|
if ($h == \*STDIN) {
|
|
if ($n) {
|
|
syswrite ($s, $buf);
|
|
} else {
|
|
exit (0);
|
|
}
|
|
} elsif ($h == $s) {
|
|
if ($n) {
|
|
syswrite (\*STDOUT, $buf);
|
|
} else {
|
|
exit (0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
exit (0);
|
|
}
|
|
|
|
|
|
if (scalar (@ARGV) == 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
my $cmd = shift @ARGV;
|
|
|
|
my $skiplock;
|
|
if ($cmd =~ m/^--?skiplock$/) {
|
|
$skiplock = 1;
|
|
$cmd = shift @ARGV;
|
|
}
|
|
|
|
my $shortcmds = {
|
|
list => 1,
|
|
startall => 1,
|
|
stopall => 1,
|
|
mtunnel => 1,
|
|
};
|
|
|
|
my $vmid;
|
|
|
|
my @disks = PVE::QemuServer::disknames();
|
|
|
|
my $qm = PVE::QemuServer->new();
|
|
|
|
if (!defined ($shortcmds->{$cmd})) {
|
|
if (scalar (@ARGV) == 0) {
|
|
print_usage ("no <vmid>");
|
|
exit (-1);
|
|
}
|
|
$vmid = shift @ARGV;
|
|
|
|
if ($vmid !~ m/^\d+$/) {
|
|
print_usage ("unable to parse <vmid>");
|
|
exit (-1);
|
|
}
|
|
}
|
|
|
|
sub add_random_macs {
|
|
my ($settings) = @_;
|
|
|
|
foreach my $opt (keys %$settings) {
|
|
next if $opt !~ m/^vlan(\d+|u)$/;
|
|
my $vlan = PVE::QemuServer::parse_vlan ($settings->{$opt});
|
|
next if !$vlan;
|
|
$settings->{$opt} = PVE::QemuServer::print_vlan ($vlan);
|
|
}
|
|
}
|
|
|
|
sub create_disks {
|
|
my ($storecfg, $vmid, $settings) = @_;
|
|
|
|
my $vollist = [];
|
|
|
|
eval {
|
|
foreach my $ds (@disks) {
|
|
next if !$settings->{$ds};
|
|
|
|
my $disk = PVE::QemuServer::parse_drive ($ds, $settings->{$ds});
|
|
|
|
next if PVE::QemuServer::drive_is_cdrom ($disk);
|
|
|
|
my $file = $disk->{file};
|
|
|
|
if ($file =~ m/^(([^:\s]+):)?(\d+(\.\d+)?)$/) {
|
|
my $storeid = $2 || 'local';
|
|
my $size = $3;
|
|
my $defformat = PVE::Storage::storage_default_format ($storecfg, $storeid);
|
|
my $fmt = $disk->{format} || $defformat;
|
|
syslog ('info', "VM $vmid creating new disk - size is $size GB");
|
|
|
|
my $volid = PVE::Storage::vdisk_alloc ($storecfg, $storeid, $vmid,
|
|
$fmt, undef, $size*1024*1024);
|
|
|
|
$disk->{file} = $volid;
|
|
delete ($disk->{format}); # no longer needed
|
|
push @$vollist, $volid;
|
|
$settings->{$ds} = PVE::QemuServer::print_drive ($vmid, $disk);
|
|
} else {
|
|
my $path;
|
|
if ($disk->{file} =~ m|^/dev/.+|) {
|
|
$path = $disk->{file};
|
|
} else {
|
|
$path = PVE::Storage::path ($storecfg, $disk->{file});
|
|
}
|
|
if (!(-f $path || -b $path)) {
|
|
die "image '$path' does not exists\n";
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
my $err = $@;
|
|
|
|
if ($err) {
|
|
syslog ('err', "VM $vmid create disk failed - $err");
|
|
foreach my $volid (@$vollist) {
|
|
PVE::Storage::vdisk_free ($storecfg, $volid);
|
|
}
|
|
die $err;
|
|
}
|
|
|
|
return $vollist;
|
|
}
|
|
|
|
if ($cmd eq 'set') {
|
|
|
|
my $settings;
|
|
if (!($settings = $qm->parse_options ($vmid, 0))) {
|
|
exit (-1);
|
|
}
|
|
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
PVE::QemuServer::lock_config ($vmid, sub {
|
|
|
|
my $conf = PVE::QemuServer::load_config ($vmid);
|
|
|
|
PVE::QemuServer::check_lock ($conf) if !$skiplock;
|
|
|
|
my $unset = {};
|
|
foreach my $opt (keys %$settings) {
|
|
my $value = $settings->{$opt};
|
|
next if !defined ($value);
|
|
if ($value eq 'delete') {
|
|
foreach my $ds (@disks) {
|
|
next if $ds ne $opt;
|
|
my $disk = $conf->{diskinfo}->{$opt};
|
|
next if !$disk;
|
|
if (!PVE::QemuServer::drive_is_cdrom ($disk)) {
|
|
PVE::QemuServer::unlink_image ($qm->{storecfg}, $vmid, $disk->{file});
|
|
}
|
|
last;
|
|
}
|
|
$unset->{$opt} = 1;
|
|
delete $settings->{$opt};
|
|
} elsif ($value eq '' || $value eq 'undef') {
|
|
$unset->{$opt} = 1;
|
|
delete $settings->{$opt};
|
|
}
|
|
}
|
|
|
|
add_random_macs ($settings);
|
|
|
|
create_disks ($qm->{storecfg}, $vmid, $settings);
|
|
|
|
PVE::QemuServer::change_config_nolock ($vmid, $settings, $unset, 1);
|
|
|
|
});
|
|
|
|
my $err = $@;
|
|
|
|
die "setting parameters failed - $err" if $err;
|
|
|
|
exit (0);
|
|
|
|
} elsif ($cmd eq 'create') {
|
|
|
|
my $vollist = [];
|
|
|
|
my $filename = PVE::QemuServer::config_file ($vmid);
|
|
|
|
my $settings;
|
|
|
|
# first test (befor locking)
|
|
die "unable to create vm $vmid: config file already exists\n"
|
|
if -f $filename;
|
|
|
|
if (!($settings = $qm->parse_options ($vmid, 1))) {
|
|
exit (-1);
|
|
}
|
|
|
|
add_random_macs ($settings);
|
|
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
syslog ('info', "VM $vmid creating new virtual machine");
|
|
|
|
PVE::QemuServer::lock_config ($vmid, sub {
|
|
|
|
# second test (after locking test is accurate)
|
|
die "unable to create vm $vmid: config file already exists\n"
|
|
if -f $filename;
|
|
|
|
$vollist = create_disks ($qm->{storecfg}, $vmid, $settings);
|
|
|
|
# try to be smart about bootdisk
|
|
my @disks = PVE::QemuServer::disknames();
|
|
my $firstdisk;
|
|
foreach my $ds (reverse @disks) {
|
|
next if !$settings->{$ds};
|
|
my $disk = PVE::QemuServer::parse_drive ($ds, $settings->{$ds});
|
|
next if PVE::QemuServer::drive_is_cdrom ($disk);
|
|
$firstdisk = $ds;
|
|
}
|
|
|
|
if (!$settings->{bootdisk} && $firstdisk) {
|
|
$settings->{bootdisk} = $firstdisk;
|
|
}
|
|
|
|
PVE::QemuServer::create_conf_nolock ($vmid, $settings);
|
|
});
|
|
|
|
my $err = $@;
|
|
|
|
if ($err) {
|
|
syslog ('err', "VM $vmid create failed - $err");
|
|
foreach my $volid (@$vollist) {
|
|
eval { PVE::Storage::vdisk_free ($qm->{storecfg}, $volid); };
|
|
warn $@ if $@;
|
|
}
|
|
die "create failed - $err";
|
|
}
|
|
|
|
exit (0);
|
|
|
|
} elsif ($cmd eq 'unlink') {
|
|
|
|
if (scalar (@ARGV) == 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
PVE::QemuServer::lock_config ($vmid, sub {
|
|
|
|
my $conf = PVE::QemuServer::load_config ($vmid);
|
|
|
|
PVE::QemuServer::check_lock ($conf) if !$skiplock;
|
|
|
|
my $di = $conf->{diskinfo} || {};
|
|
|
|
foreach my $file (@ARGV) {
|
|
my $found;
|
|
foreach my $ds (keys %$di) {
|
|
if ($di->{$ds}->{file} eq $file) {
|
|
$found = 1;
|
|
last;
|
|
}
|
|
}
|
|
die "disk image '$file' is used - unable to unlink\n" if $found;
|
|
|
|
PVE::QemuServer::unlink_image ($qm->{storecfg}, $vmid, $file);
|
|
}
|
|
});
|
|
|
|
die $@ if $@;
|
|
|
|
exit 0;
|
|
}
|
|
|
|
if ($cmd eq 'monitor') {
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
print "Entering Qemu Monitor for VM $vmid - type 'help' for help\n";
|
|
|
|
my $term = new Term::ReadLine ('qm');
|
|
|
|
my $input;
|
|
while (defined ($input = $term->readline('qm> '))) {
|
|
chomp $input;
|
|
|
|
next if $input =~ m/^\s*$/;
|
|
|
|
if ($input =~ m/^\s*q(uit)?\s*$/) {
|
|
exit (0);
|
|
}
|
|
eval {
|
|
print PVE::QemuServer::vm_monitor_command ($vmid, $input);
|
|
};
|
|
print "ERROR: $@" if $@;
|
|
}
|
|
|
|
} elsif ($cmd eq 'showcmd') {
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
print vm_commandline ($qm->{storecfg}, $vmid) . "\n";
|
|
|
|
} elsif ($cmd eq 'start') {
|
|
my $statefile;
|
|
if (scalar (@ARGV) == 2 && ($ARGV[0] =~ m/^\-\-?i(ncoming)?$/)) {
|
|
$statefile = $ARGV[1];
|
|
} elsif (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
PVE::QemuServer::vm_start ($qm->{storecfg}, $vmid, $statefile, $skiplock);
|
|
|
|
} elsif ($cmd eq 'startall') {
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
$qm->vm_startall_old ();
|
|
|
|
} elsif ($cmd eq 'reset') {
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
PVE::QemuServer::vm_reset ($vmid, $skiplock);
|
|
|
|
} elsif ($cmd eq 'shutdown') {
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
vm_shutdown ($vmid, $skiplock);
|
|
|
|
} elsif ($cmd eq 'wait') {
|
|
my $timeout = shift || 0;
|
|
|
|
if ($timeout !~ m/^\d+$/) {
|
|
print_usage ("timeout must be numeric");
|
|
exit (-1);
|
|
}
|
|
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
$qm->vm_wait_old ($vmid, $timeout);
|
|
|
|
} elsif ($cmd eq 'stopall') {
|
|
my $timeout = shift || 0;
|
|
|
|
if ($timeout !~ m/^\d+$/) {
|
|
print_usage ("timeout must be numeric");
|
|
exit (-1);
|
|
}
|
|
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
PVE::QemuServer::vm_stopall($timeout);
|
|
|
|
} elsif ($cmd eq 'stop') {
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
vm_stop ($vmid, $skiplock);
|
|
|
|
} elsif ($cmd eq 'destroy') {
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
PVE::QemuServer::vm_destroy ($qm->{storecfg}, $vmid, $skiplock);
|
|
|
|
} elsif ($cmd eq 'suspend') {
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
PVE::QemuServer::vm_suspend ($vmid, $skiplock);
|
|
|
|
} elsif ($cmd eq 'resume') {
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
PVE::QemuServer::vm_resume ($vmid, $skiplock);
|
|
|
|
} elsif ($cmd eq 'cad') {
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
PVE::QemuServer::vm_cad ($vmid, $skiplock);
|
|
|
|
} elsif ($cmd eq 'vncticket') {
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
my $ticket = PVE::QemuServer::vm_vncticket ($vmid);
|
|
print "TICKET:$ticket\n";
|
|
|
|
} elsif ($cmd eq 'cdrom') {
|
|
|
|
my ($device, $path);
|
|
|
|
if (scalar (@ARGV) == 1) {
|
|
$path = shift @ARGV;
|
|
$device = 'ide2';
|
|
} elsif (scalar (@ARGV) == 2) {
|
|
my $dev;
|
|
($dev, $path) = @ARGV;
|
|
foreach my $ds (@disks) {
|
|
if (($ds eq $dev) || ("-$ds" eq $dev) || ("--$ds" eq $dev)) {
|
|
$device = $ds;
|
|
last;
|
|
}
|
|
}
|
|
} else {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
if (!$path || !$device) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
$path = 'none' if $path eq 'eject';
|
|
|
|
if (!($path eq 'none' || $path eq 'cdrom')) {
|
|
|
|
my ($vtype, $volid) = PVE::Storage::path_to_volume_id ($qm->{storecfg}, $path);
|
|
|
|
if (!($vtype eq 'iso')) {
|
|
die "path '$path' does not point to a valid cdrom image\n";
|
|
}
|
|
|
|
$path = $volid;
|
|
}
|
|
|
|
$qm->vm_cdrom ($vmid, $device, $path);
|
|
|
|
} elsif ($cmd eq 'vncproxy') {
|
|
my $ticket = shift @ARGV;
|
|
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
if ($ticket) {
|
|
die "illegal characters in ticket\n" if $ticket =~ m/[^A-Za-z0-9\+\/]/;
|
|
$qm->vm_vncticket ($vmid, $ticket);
|
|
}
|
|
|
|
run_vnc_proxy ($vmid, $ticket);
|
|
|
|
} elsif ($cmd eq 'vnc') {
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
my $port = __next_vnc_port();
|
|
my $display = $port - 5900;
|
|
|
|
my $ticket = $qm->vm_vncticket ($vmid);
|
|
$ticket = substr ($ticket, 0, 8);
|
|
my $mpf = "/tmp/.qmvncpw$$";
|
|
|
|
$vnccmd =~ s/__TICKET__/$ticket/g;
|
|
$vnccmd =~ s/__TMPFILE__/$mpf/g;
|
|
$vnccmd =~ s/__DISPLAY__/$display/g;
|
|
$vnccmd =~ s/__PORT__/$port/g;
|
|
|
|
system ("$vnccmd &");
|
|
system ("nc -l -p $port -c 'qm vncproxy $vmid'");
|
|
unlink $mpf;
|
|
|
|
} elsif ($cmd eq 'list') {
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
my $vzlist = PVE::QemuServer::vzlist();
|
|
|
|
exit 0 if (!scalar(keys %$vzlist));
|
|
|
|
printf "%10s %-20s %-10s %-10s %12s %-10s\n",
|
|
qw(VMID NAME STATUS MEM(MB) BOOTDISK(GB) PID);
|
|
|
|
foreach my $vmid (sort keys %$vzlist) {
|
|
my $conf = PVE::QemuServer::load_config ($vmid);
|
|
my $name = $conf->{name} || '-';
|
|
|
|
my $status = $vzlist->{$vmid}->{pid} ? 'running' : 'stopped';
|
|
my $pid = $vzlist->{$vmid}->{pid} || 0;
|
|
printf "%10s %-20s %-10s %-10s %12.2f %-10s\n", $vmid, $name, $status,
|
|
$conf->{memory}||0, $conf->{disksize}||0, $pid;
|
|
}
|
|
|
|
} elsif ($cmd eq 'unlock') {
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
PVE::QemuServer::lock_config ($vmid, sub {
|
|
PVE::QemuServer::change_config_nolock ($vmid, {}, { lock => 1 }, 1);
|
|
});
|
|
|
|
die $@ if $@;
|
|
|
|
} elsif ($cmd eq 'status') {
|
|
if (scalar (@ARGV) != 0) {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
my $status = 'unknown';
|
|
|
|
eval {
|
|
if (PVE::QemuServer::check_running($vmid)) {
|
|
$status = 'running';
|
|
} else {
|
|
$status = 'stopped';
|
|
}
|
|
};
|
|
|
|
print "$status\n";
|
|
|
|
} elsif ($cmd eq 'mtunnel') {
|
|
|
|
print "tunnel online\n";
|
|
*STDOUT->flush();
|
|
|
|
while (my $line = <>) {
|
|
chomp $line;
|
|
last if $line =~ m/^quit$/;
|
|
}
|
|
|
|
exit (0);
|
|
|
|
} else {
|
|
print_usage ();
|
|
exit (-1);
|
|
}
|
|
|
|
exit (0);
|
|
|
|
__END__
|
|
|
|
=head1 NAME
|
|
|
|
qm - qemu/kvm manager
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
qm [--skiplock] <command> <vmid> [OPTIONS]
|
|
|
|
type qm to see a list of valid commands
|
|
|
|
=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
|
|
|
|
Global server configuration is stored in /etc/pve/qemu-server.cfg.
|
|
|
|
All configuration files consists of lines in the form
|
|
|
|
PARAMETER: value
|
|
|
|
The following defaults can be specified:
|
|
|
|
=over 1
|
|
|
|
=item keyboard: XX
|
|
|
|
Keybord layout used for vnc server (for example C<fr> for French)
|
|
|
|
=item onboot: (yes|no)
|
|
|
|
Specifies whether a VM will be started during system bootup.
|
|
Default is no, meaning the VM will not be started if ONBOOT
|
|
parameter is omitted.
|
|
|
|
=item autostart: (yes|no)
|
|
|
|
Automatic restart after crash. Default is no. This value is
|
|
currently ignored (because we have no monitor).
|
|
|
|
=item memory: num
|
|
|
|
Amount of RAM for the VM in MB. Default value is 512.
|
|
|
|
=item cpuunits: num
|
|
|
|
CPU weight for a VM. Argument is positive non-zero number, passed to
|
|
and used in the kernel fair scheduler. The larger the number is,
|
|
the more CPU time this VM gets. Maximum value is 500000, minimal
|
|
is 8. Number is relative to weights of all the other running VMs.
|
|
If cpuunits are not specified, default value of 1000 is used.
|
|
|
|
NOTE: You can disable fair-scheduler configuration by setting this to 0.
|
|
|
|
=item vga: (std|cirrus)
|
|
|
|
Select VGA type. Default is a Cirrus Logic GD5446 PCI VGA card (cirrus).
|
|
If you want to use high resolution modes (>= 1280x1024x16) then you should
|
|
use option C<std>.
|
|
|
|
=item tdf: (yes|no)
|
|
|
|
enable/disable time drift fix [default=yes]
|
|
|
|
=item tablet: (yes|no)
|
|
|
|
enable/disable the usb tablet device [default=yes]. This device is usually
|
|
needed to allow absolute mouse positioning. Else the mouse runs out of
|
|
sync with normal vnc clients. If you're running lots of console-only guests
|
|
on one host, you may consider setting "tablet: no" to save some context switches.
|
|
|
|
=item migrate_downtime: num
|
|
|
|
set maximum tolerated downtime (in seconds) for migrations. [default=1]
|
|
|
|
=item migrate_speed: num
|
|
|
|
set maximum speed (in MB/s) for migrations. [default=0 (no limit)]
|
|
|
|
=back
|
|
|
|
=head1 VM CONFIGURATION
|
|
|
|
Each VM is identified by an unique ID (integer). Configuration for
|
|
a VM is stored at C</etc/qemu-server/ID.conf>
|
|
|
|
Currently, the following parameters are supported:
|
|
|
|
=over 1
|
|
|
|
=item keyboard: XX
|
|
|
|
Default is read from global configuration file.
|
|
|
|
=item onboot: (yes|no)
|
|
|
|
Default is read from global configuration file.
|
|
|
|
=item autostart: (yes|no)
|
|
|
|
Default is read from global configuration file.
|
|
|
|
=item memory: num
|
|
|
|
Default is read from global configuration file.
|
|
|
|
=item cpuunits: num
|
|
|
|
Default is read from global configuration file.
|
|
|
|
=item migrate_downtime: num
|
|
|
|
Default is read from global configuration file.
|
|
|
|
=item migrate_speed: num
|
|
|
|
Default is read from global configuration file.
|
|
|
|
=item reboot: (yes|no)
|
|
|
|
Exit instead of rebooting.
|
|
|
|
=item name: text
|
|
|
|
Set a name for the VM. Only used on the configuration
|
|
web interface.
|
|
|
|
=item description: text
|
|
|
|
Description for the VM. Only used on the configuration
|
|
web interface.
|
|
|
|
=item boot: [a|c|d|n]
|
|
|
|
boot on floppy (a), hard disk (c), CD-ROM (d), or network (n)
|
|
|
|
default is C<cad> (disk, floppy, cdrom)
|
|
|
|
=item bootdisk: <disk>
|
|
|
|
enable booting from <disk>
|
|
|
|
<disk> = <ide0|ide1|ide2|ide3|scsi0|scsi1|scsi2|...
|
|
|
|
=item smp: num (please use C<sockets> instead)
|
|
|
|
set the number of CPUs to C<num> [default=1]
|
|
|
|
=item sockets: num
|
|
|
|
set the number of CPU sockets to C<num> [default=1]
|
|
|
|
=item cores: num
|
|
|
|
set the number of cores per socket to C<num> [default=1]
|
|
|
|
=item acpi: (yes|no)
|
|
|
|
enable/disable ACPI [default=yes]
|
|
|
|
=item kvm: (yes|no)
|
|
|
|
enable/disable KVM hardware virtualization [default=yes]
|
|
|
|
=item tdf: (yes|no)
|
|
|
|
Default is read from global configuration file.
|
|
|
|
=item tablet: (yes|no)
|
|
|
|
Default is read from global configuration file.
|
|
|
|
=item localtime: (yes|no)
|
|
|
|
set the real time clock to local time
|
|
|
|
=item startdate: (now|YYYY-MM-DD|YYYY-MM-DDTHH-MM-SS)
|
|
|
|
Set the initial date of the real time clock. Valid format
|
|
for date are: C<now> or C<2006-06-17T16:01:21> or C<2006-06-17>.
|
|
The default value is C<now>.
|
|
|
|
=item freeze: (yes|no)
|
|
|
|
freeze CPU at startup (use C<c> monitor command to start execution)
|
|
|
|
=item vlan0: MODEL=XX:XX:XX:XX:XX:XX[,MODEL=YY:YY:YY:YY:YY:YY]
|
|
|
|
Specify network devices connected to vlan0. Currently, vlan0 is
|
|
connected to vmbr0.
|
|
|
|
MODEL is one of: ne2k_pci e1000 rtl8139 pcnet virtio
|
|
ne2k_isa i82551 i82557b i82559er
|
|
|
|
XX:XX:XX:XX:XX:XX should be an unique MAC address
|
|
|
|
=item vlan[1-4094]: MODEL=XX:XX:XX:XX:XX:XX[,MODEL=YY:YY:YY:YY:YY:YY]
|
|
|
|
Same as vlan0, but vlanX is bridged to vmbrX
|
|
|
|
=item vlanu: MODEL=XX:XX:XX:XX:XX:XX[,MODEL=YY:YY:YY:YY:YY:YY]
|
|
|
|
Same as vlan0, but vlanu uses user mode network stack (NAT).
|
|
Provides DHCP and DNS services. The following addresses are used:
|
|
|
|
10.0.2.2 Gateway
|
|
10.0.2.3 DNS Server
|
|
10.0.2.4 SMB Server
|
|
|
|
The DHCP server assign addresses to the hosts starting from 10.0.2.15.
|
|
|
|
=item ostype: (other|wxp|w2k|w2k3|w2k8|wvista|l24|l26)
|
|
|
|
Used to enable special optimization/features for specific
|
|
operating systems:
|
|
|
|
other => unspecified OS
|
|
wxp => Microsoft Windows XP
|
|
w2k => Microsoft Windows 2000
|
|
w2k3 => Microsoft Windows 2003
|
|
w2k8 => Microsoft Windows 2008
|
|
wvista => Microsoft Windows Vista
|
|
l24 => Linux 2.4 Kernel
|
|
l26 => Linux 2.6 Kernel
|
|
|
|
other|l24|l26 ... no special behaviour
|
|
wxp|w2k|w2k3|w2k8|wvista ... use --localtime switch
|
|
|
|
=item vga: (std|cirrus)
|
|
|
|
Default is read from global configuration file.
|
|
|
|
=item ide0 - ide3: [volume=]volume,][,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough"|writeback] [,format=f]
|
|
|
|
Use volume as IDE hard disk or CD-ROM.
|
|
|
|
=item scsi0 - scsi15: [volume=]volume,][,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough"|writeback] [,format=f]
|
|
|
|
Use volume as SCSI hard disk or CD-ROM.
|
|
|
|
=item virtio0 - virtio15: [volume=]volume,][,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough"|writeback] [,format=f]
|
|
|
|
Use file as VIRTIO hard disk.
|
|
|
|
=item hostpci: [HOSTPCIDEVICE][,HOSTPCIDEVICE]*
|
|
|
|
Map host pci devices. HOSTPCIDEVICE syntax is:
|
|
|
|
C<bus:dev.func> (hexadecimal numbers)
|
|
|
|
You can us the C<lspci> command to list existing pci devices.
|
|
|
|
Note: This option allows direct access to host hardware. So it is no
|
|
longer possible to migrate such machines - use with special care.
|
|
|
|
Experimental: user reported problems with this option
|
|
|
|
=item hostusb: [HOSTUSBDEVICE][,HOSTUSBDEVICE]*
|
|
|
|
Map host usb devices. HOSTUSBDEVICE syntax is:
|
|
|
|
C<bus.addr> (decimal numbers) or C<vendor_id:product_id> (hexadeciaml numbers)
|
|
|
|
You can us the C<lsusb> command to list existing usb devices (or take a
|
|
look at C</proc/bus/usb/devices>).
|
|
|
|
Note: This option allows direct access to host hardware. So it is no
|
|
longer possible to migrate such machines - use with special care.
|
|
|
|
=item serial: [SERIALDEVICE][,SERIALDEVICE]*
|
|
|
|
Experimental: user reported problems with this option
|
|
|
|
Map host serial devices. SERIALDEVICE syntax is /dev/ttyS*
|
|
|
|
Note: This option allows direct access to host hardware. So it is no
|
|
longer possible to migrate such machines - use with special care.
|
|
|
|
=item parallel: [PARALLELDEVICE][,PARALLELDEVICE]*
|
|
|
|
Experimental: user reported problems with this option
|
|
|
|
Map host parallel devices. PARALLELDEVICE syntax is /dev/parport*
|
|
|
|
Note: This option allows direct access to host hardware. So it is no
|
|
longer possible to migrate such machines - use with special care.
|
|
|
|
=item args: ...
|
|
|
|
Note: this option is for experts only. It allows you to pass arbitrary
|
|
arguments to kvm, for example:
|
|
|
|
args: -no-reboot -no-hpet
|
|
|
|
=item lock: (migrate|backup)
|
|
|
|
This value is set during online migration (migrate) and vzdump
|
|
(backup).
|
|
|
|
=back
|
|
|
|
=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 <vmid>
|
|
|
|
=head1 EXAMPLES
|
|
|
|
# create a new VM with 4 GB ide disk
|
|
|
|
qm create 300 -ide0 4 -vlan0 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 40
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|