vga => {
optional => 1,
type => 'string',
- description => "Select VGA type. If you want to use high resolution modes (>= 1280x1024x16) then you should use option 'std' or 'vmware'. Default is 'std' for win8/win7/w2k8, and 'cirrur' for other OS types",
- enum => [qw(std cirrus vmware)],
+ description => "Select VGA type. If you want to use high resolution modes (>= 1280x1024x16) then you should use option 'std' or 'vmware'. Default is 'std' for win8/win7/w2k8, and 'cirrur' for other OS types. Option 'qxl' enables the SPICE display sever.",
+ enum => [qw(std cirrus vmware qxl)],
},
watchdog => {
optional => 1,
optional => 1,
type => 'boolean',
default => 1,
- description => "Enable/disable the usb tablet device. 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 disabling this to save some context switches.",
+ description => "Enable/disable the usb tablet device. This device is usually needed to allow absolute mouse positioning with VNC. 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 disabling this to save some context switches. This is turned of by default if you use spice (vga=qxl).",
},
migrate_speed => {
optional => 1,
my $usbdesc = {
optional => 1,
type => 'string', format => 'pve-qm-usb-device',
- typetext => 'host=HOSTUSBDEVICE',
+ typetext => 'host=HOSTUSBDEVICE|spice',
description => <<EODESCR,
Configure an USB device (n is 0 to 4). This can be used to
pass-through usb devices to the guest. HOSTUSBDEVICE syntax is:
Note: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
+The value 'spice' can be used to add a usb redirection devices for spice.
+
EODESCR
};
PVE::JSONSchema::register_standard_option("pve-qm-usb", $usbdesc);
return undef if $res->{backup} && $res->{backup} !~ m/^(yes|no)$/;
return undef if $res->{aio} && $res->{aio} !~ m/^(native|threads)$/;
-
+
return undef if $res->{mbps_rd} && $res->{mbps};
return undef if $res->{mbps_wr} && $res->{mbps};
if ($res->{size}) {
- return undef if !defined($res->{size} = &$parse_size($res->{size}));
+ return undef if !defined($res->{size} = &$parse_size($res->{size}));
}
if ($res->{media} && ($res->{media} eq 'cdrom')) {
my $buf = "\x00" x 36;
my $sensebuf = "\x00" x 8;
- my $cmd = pack("C x3 C x11", 0x12, 36);
+ my $cmd = pack("C x3 C x1", 0x12, 36);
# see /usr/include/scsi/sg.h
my $sg_io_hdr_t = "i i C C s I P P P I I i P C C C C S S i I I";
}
my $res = {};
- ($res->{device}, $res->{removable}, $res->{venodor},
+ (my $byte0, my $byte1, $res->{vendor},
$res->{product}, $res->{revision}) = unpack("C C x6 A8 A16 A4", $buf);
+ $res->{removable} = $byte1 & 128 ? 1 : 0;
+ $res->{type} = $byte0 & 31;
+
return $res;
}
my $path = '';
if (drive_is_cdrom($drive)) {
$devicetype = 'cd';
- } else {
+ } else {
if ($drive->{file} =~ m|^/|) {
$path = $drive->{file};
} else {
}
if($path =~ m/^iscsi\:\/\//){
- $devicetype = 'generic';
- }
- else {
- $devicetype = 'block' if path_is_scsi($path);
+ $devicetype = 'generic';
+ } else {
+ if (my $info = path_is_scsi($path)) {
+ if ($info->{type} == 0) {
+ $devicetype = 'block';
+ } elsif ($info->{type} == 1) { # tape
+ $devicetype = 'generic';
+ }
+ }
}
}
$found = 1;
$res->{hostbus} = $1;
$res->{hostport} = $2;
+ } elsif ($v =~ m/^spice$/) {
+ $found = 1;
+ $res->{spice} = 1;
} else {
return undef;
}
my @lines = split(/\n/, $raw);
foreach my $line (@lines) {
next if $line =~ m/^\s*$/;
-
+
if ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
my $snapname = $1;
$conf->{description} = $descr if $descr;
$descr = '';
- $conf = $res->{snapshots}->{$snapname} = {};
+ $conf = $res->{snapshots}->{$snapname} = {};
next;
}
delete $conf->{$key};
}
}
-
+
my $generate_raw_config = sub {
my ($conf) = @_;
my $nodelist = PVE::Cluster::get_nodelist();
my $nodehash = { map { $_ => 1 } @$nodelist };
my $nodename = PVE::INotify::nodename();
-
+
foreach_drive($conf, sub {
my ($ds, $drive) = @_;
my $info = $resp->{'return'};
return if !$info->{max_mem};
-
+
my $d = $res->{$vmid};
# use memory assigned to VM
$d->{maxmem} = $info->{max_mem};
$d->{balloon} = $info->{actual};
-
+
if (defined($info->{total_mem}) && defined($info->{free_mem})) {
$d->{mem} = $info->{total_mem} - $info->{free_mem};
$d->{freemem} = $info->{free_mem};
sub foreach_volid {
my ($conf, $func) = @_;
-
+
my $volhash = {};
my $test_volid = sub {
my ($volid, $is_cdrom) = @_;
return if !$volid;
-
+
$volhash->{$volid} = $is_cdrom || 0;
};
}
foreach my $volid (keys %$volhash) {
- &$func($volid, $volhash->{$volid});
+ &$func($volid, $volhash->{$volid});
}
}
my $globalFlags = [];
my $machineFlags = [];
my $rtcFlags = [];
+ my $cpuFlags = [];
my $devices = [];
my $pciaddr = '';
my $bridges = {};
# include usb device config
push @$devices, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg' if $use_usb2;
+ my $vga = $conf->{vga};
+ if (!$vga) {
+ if ($conf->{ostype} && ($conf->{ostype} eq 'win8' ||
+ $conf->{ostype} eq 'win7' ||
+ $conf->{ostype} eq 'w2k8')) {
+ $vga = 'std';
+ } else {
+ $vga = 'cirrus';
+ }
+ }
+
# enable absolute mouse coordinates (needed by vnc)
- my $tablet = defined($conf->{tablet}) ? $conf->{tablet} : $defaults->{tablet};
+ my $tablet;
+ if (defined($conf->{tablet})) {
+ $tablet = $conf->{tablet};
+ } else {
+ $tablet = $defaults->{tablet};
+ $tablet = 0 if $vga eq 'qxl'; # disable for spice because it is not needed
+ }
+
push @$devices, '-device', 'usb-tablet,id=tablet,bus=uhci.0,port=1' if $tablet;
# host pci devices
push @$devices, '-device', "usb-host,vendorid=0x$d->{vendorid},productid=0x$d->{productid}";
} elsif (defined($d->{hostbus}) && defined($d->{hostport})) {
push @$devices, '-device', "usb-host,hostbus=$d->{hostbus},hostport=$d->{hostport}";
+ } elsif ($d->{spice}) {
+ # usb redir support for spice
+ push @$devices, '-chardev', "spicevmc,id=usbredirchardev$i,name=usbredir";
+ push @$devices, '-device', "usb-redir,chardev=usbredirchardev$i,id=usbredirdev$i,bus=ehci.0";
}
}
$sockets = $conf->{sockets} if $conf->{sockets};
my $cores = $conf->{cores} || 1;
-
push @$cmd, '-smp', "sockets=$sockets,cores=$cores";
- push @$cmd, '-cpu', $conf->{cpu} if $conf->{cpu};
-
push @$cmd, '-nodefaults';
my $bootorder = $conf->{boot} || $confdesc->{boot}->{default};
push @$cmd, '-no-reboot' if defined($conf->{reboot}) && $conf->{reboot} == 0;
- my $vga = $conf->{vga};
- if (!$vga) {
- if ($conf->{ostype} && ($conf->{ostype} eq 'win8' || $conf->{ostype} eq 'win7' || $conf->{ostype} eq 'w2k8')) {
- $vga = 'std';
- } else {
- $vga = 'cirrus';
- }
- }
-
push @$cmd, '-vga', $vga if $vga; # for kvm 77 and later
# time drift fix
}
}
- if ($ost eq 'win7' || $ost eq 'win8' || $ost eq 'w2k8' ||
+ if ($ost eq 'win7' || $ost eq 'win8' || $ost eq 'w2k8' ||
$ost eq 'wvista') {
push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';
push @$cmd, '-no-hpet';
+ #push @$cpuFlags , 'hv_vapic" if !$nokvm; #fixme, my win2008R2 hang at boot with this
+ push @$cpuFlags , 'hv_spinlocks=0xffff' if !$nokvm;
+ }
+
+ if ($ost eq 'win7' || $ost eq 'win8') {
+ push @$cpuFlags , 'hv_relaxed' if !$nokvm;
}
}
push @$rtcFlags, 'base=localtime';
}
+ my $cpu = $nokvm ? "qemu64" : "kvm64";
+ $cpu = $conf->{cpu} if $conf->{cpu};
+
+ push @$cpuFlags , '+x2apic' if !$nokvm;
+
+ push @$cpuFlags, '+sep' if $cpu eq 'kvm64' || $cpu eq 'kvm32';
+
+ $cpu .= "," . join(',', @$cpuFlags) if scalar(@$cpuFlags);
+
+ push @$cmd, '-cpu', $cpu;
+
push @$cmd, '-S' if $conf->{freeze};
# set keyboard layout
push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
}
+ if ($vga eq 'qxl') {
+ my $pciaddr = print_pci_addr("spice", $bridges);
+
+ my $port = PVE::Tools::next_unused_port(61000, 61099);
+
+ push @$cmd, '-spice', "tls-port=$port,addr=127.0.0.1,tls-ciphers=DES-CBC3-SHA,seamless-migration=on";
+
+ push @$cmd, '-device', "virtio-serial,id=spice$pciaddr";
+ push @$cmd, '-chardev', "spicevmc,id=vdagent,name=vdagent";
+ push @$cmd, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0";
+ }
+
# enable balloon by default, unless explicitly disabled
if (!defined($conf->{balloon}) || $conf->{balloon}) {
$pciaddr = print_pci_addr("balloon0", $bridges);
}
push @$cmd, @$devices;
- push @$cmd, '-rtc', join(',', @$rtcFlags)
+ push @$cmd, '-rtc', join(',', @$rtcFlags)
if scalar(@$rtcFlags);
- push @$cmd, '-machine', join(',', @$machineFlags)
+ push @$cmd, '-machine', join(',', @$machineFlags)
if scalar(@$machineFlags);
push @$cmd, '-global', join(',', @$globalFlags)
if scalar(@$globalFlags);
return "${var_run_tmpdir}/$vmid.vnc";
}
+sub spice_port {
+ my ($vmid) = @_;
+
+ my $res = vm_mon_cmd_nocheck($vmid, 'query-spice');
+
+ return $res->{'tls-port'} || $res->{'port'} || die "no spice port\n";
+}
+
sub qmp_socket {
my ($vmid) = @_;
return "${var_run_tmpdir}/$vmid.qmp";
$capabilities->{capability} = "xbzrle";
$capabilities->{state} = JSON::true;
eval { vm_mon_cmd_nocheck($vmid, "migrate-set-capabilities", capabilities => [$capabilities]); };
+ if($conf->{vga} eq 'qxl'){
+ my $spice_port = PVE::QemuServer::spice_port($vmid);
+ print "spice listens on port $spice_port\n" if $spice_port;
+ if($spiceticket){
+ PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "set_password", protocol => 'spice', password => $spiceticket);
+ PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "expire_password", protocol => 'spice', time => "+5");
+ }
+ }
+
}
else{
if (!$statefile && (!defined($conf->{balloon}) || $conf->{balloon})) {
- vm_mon_cmd_nocheck($vmid, "balloon", value => $conf->{balloon}*1024*1024)
+ vm_mon_cmd_nocheck($vmid, "balloon", value => $conf->{balloon}*1024*1024)
if $conf->{balloon};
- vm_mon_cmd_nocheck($vmid, 'qom-set',
- path => "machine/peripheral/balloon0",
- property => "guest-stats-polling-interval",
+ vm_mon_cmd_nocheck($vmid, 'qom-set',
+ path => "machine/peripheral/balloon0",
+ property => "guest-stats-polling-interval",
value => 2);
}
}
$timeout = $cmd->{arguments}->{timeout};
delete $cmd->{arguments}->{timeout};
}
-
+
eval {
die "VM $vmid not running\n" if !check_running($vmid, $nocheck);
my $sname = qmp_socket($vmid);
scsihw1 => { bus => 0, addr => 6 },
ahci0 => { bus => 0, addr => 7 },
qga0 => { bus => 0, addr => 8 },
+ spice => { bus => 0, addr => 9 },
virtio0 => { bus => 0, addr => 10 },
virtio1 => { bus => 0, addr => 11 },
virtio2 => { bus => 0, addr => 12 },
sub update_disksize {
my ($vmid, $conf, $volid_hash) = @_;
-
+
my $changes;
my $used = {};
# to the same path).
my $usedpath = {};
-
+
# update size info
foreach my $opt (keys %$conf) {
if (valid_drivename($opt)) {
next if !$volid;
$used->{$volid} = 1;
- if ($volid_hash->{$volid} &&
+ if ($volid_hash->{$volid} &&
(my $path = $volid_hash->{$volid}->{path})) {
$usedpath->{$path} = 1;
}
next if $opt !~ m/^unused\d+$/;
my $volid = $conf->{$opt};
my $path = $volid_hash->{$volid}->{path} if $volid_hash->{$volid};
- if ($used->{$volid} || ($path && $usedpath->{$path})) {
+ if ($used->{$volid} || ($path && $usedpath->{$path})) {
$changes = 1;
delete $conf->{$opt};
}
my ($vmid) = @_;
my $conf = load_config($vmid);
-
+
check_lock($conf);
my $vm_volids = {};
&$updatefn($vmid);
} else {
lock_config($vmid, $updatefn, $vmid);
- }
+ }
}
}
}
} else {
die "unknown compression method '$comp'\n";
}
-
+
}
my $tmpdir = "/var/tmp/vzdumptmp$$";
$devinfo->{$devname}->{format} = $format;
$devinfo->{$devname}->{storeid} = $storeid;
- # check permission on storage
+ # check permission on storage
my $pool = $opts->{pool}; # todo: do we need that?
if ($user ne 'root@pam') {
$rpcenv->check($user, "/storage/$storeid", ['Datastore.AllocateSpace']);
}
foreach my $devname (keys %$devinfo) {
- die "found no device mapping information for device '$devname'\n"
- if !$devinfo->{$devname}->{virtdev};
+ die "found no device mapping information for device '$devname'\n"
+ if !$devinfo->{$devname}->{virtdev};
}
my $cfg = cfs_read_file('storage.cfg');
# create empty/temp config
- if ($oldconf) {
+ if ($oldconf) {
PVE::Tools::file_set_contents($conffile, "memory: 128\n");
foreach_drive($oldconf, sub {
my ($ds, $drive) = @_;
my $write_zeros = 1;
# fixme: what other storages types initialize volumes with zero?
- if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs' ||
+ if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs' ||
$scfg->{type} eq 'sheepdog' || $scfg->{type} eq 'rbd') {
$write_zeros = 0;
}
my $cookie = { netcount => 0 };
while (defined(my $line = <$fh>)) {
- restore_update_config_line($outfd, $cookie, $vmid, $map, $line, $opts->{unique});
+ restore_update_config_line($outfd, $cookie, $vmid, $map, $line, $opts->{unique});
}
$fh->close();
close($fifofh);
}
};
-
+
print "restore vma archive: $cmd\n";
run_command($cmd, input => $input, outfunc => $parser, afterfork => $openfifo);
};
my $cookie = { netcount => 0 };
while (defined (my $line = <$srcfd>)) {
- restore_update_config_line($outfd, $cookie, $vmid, $map, $line, $opts->{unique});
+ restore_update_config_line($outfd, $cookie, $vmid, $map, $line, $opts->{unique});
}
$srcfd->close();
next if $k eq 'digest';
next if $k eq 'description';
next if $k =~ m/^unused\d+$/;
-
+
$dest->{$k} = $source->{$k};
}
};
my $volid = $drive->{file};
my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
- $sidhash->{$sid} = $sid if $sid;
+ $sidhash->{$sid} = $sid if $sid;
}
foreach my $sid (sort keys %$sidhash) {
my $alloc_vmstate_volid = sub {
my ($storecfg, $vmid, $conf, $snapname) = @_;
-
+
# Note: we try to be smart when selecting a $target storage
my $target;
my $conf = load_config($vmid);
- die "you can't take a snapshot if it's a template\n"
+ die "you can't take a snapshot if it's a template\n"
if is_template($conf);
check_lock($conf);
$conf->{lock} = 'snapshot';
- die "snapshot name '$snapname' already used\n"
- if defined($conf->{snapshots}->{$snapname});
+ die "snapshot name '$snapname' already used\n"
+ if defined($conf->{snapshots}->{$snapname});
my $storecfg = PVE::Storage::config();
die "snapshot feature is not available" if !has_feature('snapshot', $conf, $storecfg);
my $conf = load_config($vmid);
- die "missing snapshot lock\n"
- if !($conf->{lock} && $conf->{lock} eq 'snapshot');
+ die "missing snapshot lock\n"
+ if !($conf->{lock} && $conf->{lock} eq 'snapshot');
my $snap = $conf->{snapshots}->{$snapname};
- die "snapshot '$snapname' does not exist\n" if !defined($snap);
+ die "snapshot '$snapname' does not exist\n" if !defined($snap);
+
+ die "wrong snapshot state\n"
+ if !($snap->{snapstate} && $snap->{snapstate} eq "prepare");
- die "wrong snapshot state\n"
- if !($snap->{snapstate} && $snap->{snapstate} eq "prepare");
-
delete $snap->{snapstate};
delete $conf->{lock};
my $prepare = 1;
my $storecfg = PVE::Storage::config();
-
+
my $updatefn = sub {
my $conf = load_config($vmid);
$snap = $conf->{snapshots}->{$snapname};
- die "snapshot '$snapname' does not exist\n" if !defined($snap);
+ die "snapshot '$snapname' does not exist\n" if !defined($snap);
- die "unable to rollback to incomplete snapshot (snapstate = $snap->{snapstate})\n"
+ die "unable to rollback to incomplete snapshot (snapstate = $snap->{snapstate})\n"
if $snap->{snapstate};
if ($prepare) {
# Note: old code did not store 'machine', so we try to be smart
# and guess the snapshot was generated with kvm 1.4 (pc-i440fx-1.4).
$forcemachine = $conf->{machine} || 'pc-i440fx-1.4';
- # we remove the 'machine' configuration if not explicitly specified
+ # we remove the 'machine' configuration if not explicitly specified
# in the original config.
delete $conf->{machine} if $snap->{vmstate} && !$has_machine_config;
}
};
lock_config($vmid, $updatefn);
-
+
foreach_drive($snap, sub {
my ($ds, $drive) = @_;
if ($running) {
if ($snap->{vmstate}) {
- my $path = PVE::Storage::path($storecfg, $snap->{vmstate});
+ my $path = PVE::Storage::path($storecfg, $snap->{vmstate});
vm_mon_cmd($vmid, "savevm-start", statefile => $path);
&$savevm_wait($vmid);
} else {
};
qga_freezefs($vmid) if $running && $freezefs;
-
+
foreach_drive($snap, sub {
my ($ds, $drive) = @_;
}
}
};
-
+
my $updatefn = sub {
my ($remove_drive) = @_;
if (!$drivehash) {
check_lock($conf);
- die "you can't delete a snapshot if vm is a template\n"
+ die "you can't delete a snapshot if vm is a template\n"
if is_template($conf);
}
$snap = $conf->{snapshots}->{$snapname};
- die "snapshot '$snapname' does not exist\n" if !defined($snap);
+ die "snapshot '$snapname' does not exist\n" if !defined($snap);
# remove parent refs
&$unlink_parent($conf, $snap->{parent});
return $1;
} elsif ($scfg->{type} eq 'iscsi') {
return "host_device";
- } else {
+ } else {
return "raw";
}
}
my $dst_path = PVE::Storage::path($storecfg, $dst_volid);
if ($format) {
- #fixme : sometime drive-mirror timeout, but works fine after.
+ #fixme : sometime drive-mirror timeout, but works fine after.
# (I have see the problem with big volume > 200GB), so we need to eval
- eval { vm_mon_cmd($vmid, "drive-mirror", timeout => 10, device => "drive-$drive", mode => "existing",
+ eval { vm_mon_cmd($vmid, "drive-mirror", timeout => 10, device => "drive-$drive", mode => "existing",
sync => "full", target => $dst_path, format => $format); };
} else {
- eval { vm_mon_cmd($vmid, "drive-mirror", timeout => 10, device => "drive-$drive", mode => "existing",
+ eval { vm_mon_cmd($vmid, "drive-mirror", timeout => 10, device => "drive-$drive", mode => "existing",
sync => "full", target => $dst_path); };
}
$old_len = $stat->{offset};
sleep 1;
}
-
+
if ($vmiddst == $vmid) {
- # switch the disk if source and destination are on the same guest
+ # switch the disk if source and destination are on the same guest
vm_mon_cmd($vmid, "block-job-complete", device => "drive-$drive");
}
};
}
sub clone_disk {
- my ($storecfg, $vmid, $running, $drivename, $drive, $snapname,
+ my ($storecfg, $vmid, $running, $drivename, $drive, $snapname,
$newvmid, $storage, $format, $full, $newvollist) = @_;
my $newvolid;
qemu_img_convert($drive->{file}, $newvolid, $size, $snapname);
} else {
qemu_drive_mirror($vmid, $drivename, $newvolid, $newvmid);
- }
+ }
}
my ($size) = PVE::Storage::volume_size_info($storecfg, $newvolid, 3);
my ($vmid) = @_;
my $cmd = { execute => 'query-machines', arguments => {} };
- my $res = PVE::QemuServer::vm_qmp_command($vmid, $cmd);
+ my $res = PVE::QemuServer::vm_qmp_command($vmid, $cmd);
my ($current, $default);
foreach my $e (@$res) {
return $current || $default || 'pc';
}
+sub read_x509_subject_spice {
+ my ($filename) = @_;
+
+ # read x509 subject
+ my $bio = Net::SSLeay::BIO_new_file($filename, 'r');
+ my $x509 = Net::SSLeay::PEM_read_bio_X509($bio);
+ Net::SSLeay::BIO_free($bio);
+ my $nameobj = Net::SSLeay::X509_get_subject_name($x509);
+ my $subject = Net::SSLeay::X509_NAME_oneline($nameobj);
+ Net::SSLeay::X509_free($x509);
+
+ # remote-viewer wants comma as seperator (not '/')
+ $subject =~ s!^/!!;
+ $subject =~ s!/(\w+=)!,$1!g;
+
+ return $subject;
+}
1;