optional => 1,
type => 'string',
description => "scsi controller model",
- enum => [qw(lsi virtio-scsi-pci)],
+ enum => [qw(lsi virtio-scsi-pci megasas)],
default => 'lsi',
},
description => {
optional => 1,
type => 'string', format => 'pve-qm-bootdisk',
description => "Enable booting from specified disk.",
- pattern => '(ide|scsi|virtio)\d+',
+ pattern => '(ide|sata|scsi|virtio)\d+',
},
smp => {
optional => 1,
description => "Enable/disable ACPI.",
default => 1,
},
+ agent => {
+ optional => 1,
+ type => 'boolean',
+ description => "Enable/disable Qemu GuestAgent.",
+ default => 0,
+ },
kvm => {
optional => 1,
type => 'boolean',
my $MAX_IDE_DISKS = 4;
my $MAX_SCSI_DISKS = 14;
-my $MAX_VIRTIO_DISKS = 32;
+my $MAX_VIRTIO_DISKS = 16;
my $MAX_SATA_DISKS = 6;
my $MAX_USB_DEVICES = 5;
my $MAX_NETS = 32;
optional => 1,
type => 'string', format => 'pve-qm-drive',
typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads]',
- description => "Use volume as IDE hard disk or CD-ROM (n is 0 to 3).",
+ description => "Use volume as IDE hard disk or CD-ROM (n is 0 to " .($MAX_IDE_DISKS -1) . ").",
};
PVE::JSONSchema::register_standard_option("pve-qm-ide", $idedesc);
optional => 1,
type => 'string', format => 'pve-qm-drive',
typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads]',
- description => "Use volume as SCSI hard disk or CD-ROM (n is 0 to 13).",
+ description => "Use volume as SCSI hard disk or CD-ROM (n is 0 to " . ($MAX_SCSI_DISKS - 1) . ").",
};
PVE::JSONSchema::register_standard_option("pve-qm-scsi", $scsidesc);
optional => 1,
type => 'string', format => 'pve-qm-drive',
typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads]',
- description => "Use volume as SATA hard disk or CD-ROM (n is 0 to 5).",
+ description => "Use volume as SATA hard disk or CD-ROM (n is 0 to " . ($MAX_SATA_DISKS - 1). ").",
};
PVE::JSONSchema::register_standard_option("pve-qm-sata", $satadesc);
optional => 1,
type => 'string', format => 'pve-qm-drive',
typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads]',
- description => "Use volume as VIRTIO hard disk (n is 0 to 5).",
+ description => "Use volume as VIRTIO hard disk (n is 0 to " . ($MAX_VIRTIO_DISKS - 1) . ").",
};
PVE::JSONSchema::register_standard_option("pve-qm-virtio", $virtiodesc);
PVE::Tools::file_set_contents($filename, $data);
}
+my $parse_size = sub {
+ my ($value) = @_;
+
+ return undef if $value !~ m/^(\d+(\.\d+)?)([KMG])?$/;
+ my ($size, $unit) = ($1, $3);
+ if ($unit) {
+ if ($unit eq 'K') {
+ $size = $size * 1024;
+ } elsif ($unit eq 'M') {
+ $size = $size * 1024 * 1024;
+ } elsif ($unit eq 'G') {
+ $size = $size * 1024 * 1024 * 1024;
+ }
+ }
+ return int($size);
+};
+
+my $format_size = sub {
+ my ($size) = @_;
+
+ $size = int($size);
+
+ my $kb = int($size/1024);
+ return $size if $kb*1024 != $size;
+
+ my $mb = int($kb/1024);
+ return "${kb}K" if $mb*1024 != $kb;
+
+ my $gb = int($mb/1024);
+ return "${mb}M" if $gb*1024 != $mb;
+
+ return "${gb}G";
+};
+
# ideX = [volume=]volume-id[,media=d][,cyls=c,heads=h,secs=s[,trans=t]]
# [,snapshot=on|off][,cache=on|off][,format=f][,backup=yes|no]
# [,rerror=ignore|report|stop][,werror=enospc|ignore|report|stop]
foreach my $p (split (/,/, $data)) {
next if $p =~ m/^\s*$/;
- if ($p =~ m/^(file|volume|cyls|heads|secs|trans|media|snapshot|cache|format|rerror|werror|backup|aio|bps|bps_rd|bps_wr|iops|iops_rd|iops_wr|size)=(.+)$/) {
+ if ($p =~ m/^(file|volume|cyls|heads|secs|trans|media|snapshot|cache|format|rerror|werror|backup|aio|bps|mbps|bps_rd|mbps_rd|bps_wr|mbps_wr|iops|iops_rd|iops_wr|size)=(.+)$/) {
my ($k, $v) = ($1, $2);
$k = 'file' if $k eq 'volume';
return undef if defined $res->{$k};
+ if ($k eq 'bps' || $k eq 'bps_rd' || $k eq 'bps_wr') {
+ return undef if !$v || $v !~ m/^\d+/;
+ $k = "m$k";
+ $v = sprintf("%.3f", $v / (1024*1024));
+ }
$res->{$k} = $v;
} else {
if (!$res->{file} && $p !~ m/=/) {
return undef if $res->{backup} && $res->{backup} !~ m/^(yes|no)$/;
return undef if $res->{aio} && $res->{aio} !~ m/^(native|threads)$/;
- return undef if $res->{bps_rd} && $res->{bps};
- return undef if $res->{bps_wr} && $res->{bps};
+
+ return undef if $res->{mbps_rd} && $res->{mbps};
+ return undef if $res->{mbps_wr} && $res->{mbps};
+
+ return undef if $res->{mbps} && $res->{mbps} !~ m/^\d+(\.\d+)?$/;
+ return undef if $res->{mbps_rd} && $res->{mbps_rd} !~ m/^\d+(\.\d+)?$/;
+ return undef if $res->{mbps_wr} && $res->{mbps_wr} !~ m/^\d+(\.\d+)?$/;
+
return undef if $res->{iops_rd} && $res->{iops};
return undef if $res->{iops_wr} && $res->{iops};
-
- return undef if $res->{bps} && $res->{bps} !~ m/^\d+$/;
- return undef if $res->{bps_rd} && $res->{bps_rd} !~ m/^\d+$/;
- return undef if $res->{bps_wr} && $res->{bps_wr} !~ m/^\d+$/;
return undef if $res->{iops} && $res->{iops} !~ m/^\d+$/;
return undef if $res->{iops_rd} && $res->{iops_rd} !~ m/^\d+$/;
return undef if $res->{iops_wr} && $res->{iops_wr} !~ m/^\d+$/;
if ($res->{size}) {
- return undef if $res->{size} !~ m/^([1-9]\d*(\.\d+)?)([KMG])?$/;
- my ($size, $unit) = ($1, $3);
- if ($unit) {
- if ($unit eq 'K') {
- $size = $size * 1024;
- } elsif ($unit eq 'M') {
- $size = $size * 1024 * 1024;
- } elsif ($unit eq 'G') {
- $size = $size * 1024 * 1024 * 1024;
- }
- }
- $res->{size} = int($size);
+ return undef if !defined($res->{size} = &$parse_size($res->{size}));
}
if ($res->{media} && ($res->{media} eq 'cdrom')) {
return $res;
}
-my @qemu_drive_options = qw(heads secs cyls trans media format cache snapshot rerror werror aio bps bps_rd bps_wr iops iops_rd iops_wr);
-
-my $format_size = sub {
- my ($size) = @_;
-
- $size = int($size);
-
- my $kb = int($size/1024);
- return $size if $kb*1024 != $size;
-
- my $mb = int($kb/1024);
- return "${kb}K" if $mb*1024 != $kb;
-
- my $gb = int($mb/1024);
- return "${mb}M" if $gb*1024 != $mb;
-
- return "${gb}G";
-};
+my @qemu_drive_options = qw(heads secs cyls trans media format cache snapshot rerror werror aio iops iops_rd iops_wr);
sub print_drive {
my ($vmid, $drive) = @_;
my $opts = '';
- foreach my $o (@qemu_drive_options, 'backup') {
+ foreach my $o (@qemu_drive_options, 'mbps', 'mbps_rd', 'mbps_wr', 'backup') {
$opts .= ",$o=$drive->{$o}" if $drive->{$o};
}
} else {
$path = PVE::Storage::path($storecfg, $drive->{file});
}
- $devicetype = 'block' if path_is_scsi($path);
+
+ if($path =~ m/^iscsi\:\/\//){
+ $devicetype = 'generic';
+ }
+ else {
+ $devicetype = 'block' if path_is_scsi($path);
+ }
}
if (!$conf->{scsihw} || $conf->{scsihw} eq 'lsi'){
$opts .= ",$o=$drive->{$o}" if $drive->{$o};
}
+ foreach my $o (qw(bps bps_rd bps_wr)) {
+ my $v = $drive->{"m$o"};
+ $opts .= ",$o=" . int($v*1024*1024) if $v;
+ }
+
# use linux-aio by default (qemu default is threads)
$opts .= ",aio=native" if !$drive->{aio};
}
sub load_config {
- my ($vmid) = @_;
+ my ($vmid, $node) = @_;
- my $cfspath = cfs_config_path($vmid);
+ my $cfspath = cfs_config_path($vmid, $node);
my $conf = PVE::Cluster::cfs_read_file($cfspath);
}
sub check_running {
- my ($vmid, $nocheck) = @_;
+ my ($vmid, $nocheck, $node) = @_;
- my $filename = config_file($vmid);
+ my $filename = config_file($vmid, $node);
die "unable to find configuration file for VM $vmid - no such machine\n"
if !$nocheck && ! -f $filename;
}
}
- return $res if !$full;
+ return $res if !$full;
my $qmpclient = PVE::QMPClient->new();
push @$cmd, '-incoming', $migrate_uri if $migrate_uri;
+ push @$cmd, '-S' if $migrate_uri;
+
my $use_usb2 = 0;
for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
next if !$conf->{"usb$i"};
#my $soundhw = $conf->{soundhw} || $defaults->{soundhw};
#push @$cmd, '-soundhw', 'es1370';
#push @$cmd, '-soundhw', $soundhw if $soundhw;
+
+ if($conf->{agent}) {
+ my $qgasocket = qga_socket($vmid);
+ my $pciaddr = print_pci_addr("qga0", $bridges);
+ push @$devices, '-chardev', "socket,path=$qgasocket,server,nowait,id=qga0";
+ push @$devices, '-device', "virtio-serial,id=qga0$pciaddr";
+ push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
+ }
+
$pciaddr = print_pci_addr("balloon0", $bridges);
push @$devices, '-device', "virtio-balloon-pci,id=balloon0$pciaddr" if $conf->{balloon};
return "${var_run_tmpdir}/$vmid.qmp";
}
+sub qga_socket {
+ my ($vmid) = @_;
+ return "${var_run_tmpdir}/$vmid.qga";
+}
+
sub pidfile_name {
my ($vmid) = @_;
return "${var_run_tmpdir}/$vmid.pid";
my $devices_list = vm_devices_list($vmid);
return 1 if defined($devices_list->{$deviceid});
+ qemu_bridgeadd($storecfg, $conf, $vmid, $deviceid); #add bridge if we need it for the device
+
if ($deviceid =~ m/^(virtio)(\d+)$/) {
return undef if !qemu_driveadd($storecfg, $vmid, $device);
my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device);
}
}
+ if ($deviceid =~ m/^(pci\.)(\d+)$/) {
+ my $bridgeid = $2;
+ my $pciaddr = print_pci_addr($deviceid);
+ my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr";
+ qemu_deviceadd($vmid, $devicefull);
+ return undef if !qemu_deviceaddverify($vmid, $deviceid);
+ }
+
return 1;
}
return 1;
}
+sub qemu_bridgeadd {
+ my ($storecfg, $conf, $vmid, $device) = @_;
+
+ my $bridges = {};
+ my $bridgeid = undef;
+ print_pci_addr($device, $bridges);
+
+ while (my ($k, $v) = each %$bridges) {
+ $bridgeid = $k;
+ }
+ return if $bridgeid < 1;
+ my $bridge = "pci.$bridgeid";
+ my $devices_list = vm_devices_list($vmid);
+
+ if(!defined($devices_list->{$bridge})) {
+ return undef if !vm_deviceplug($storecfg, $conf, $vmid, $bridge);
+ }
+ return 1;
+}
+
sub qemu_netdevadd {
my ($vmid, $conf, $device, $deviceid) = @_;
}
-# old code, only used to shutdown old VM after update
+# old code, only used to shutdown old VM after update
sub __read_avail {
my ($fh, $timeout) = @_;
}
die "monitor read timeout\n" if !scalar(@ready);
-
+
return $res;
}
-# old code, only used to shutdown old VM after update
+# old code, only used to shutdown old VM after update
sub vm_monitor_command {
my ($vmid, $cmdstr, $nocheck) = @_;
-
+
my $res;
eval {
if ($res = __read_avail($sock, $timeout)) {
my @lines = split("\r?\n", $res);
-
+
shift @lines if $lines[0] !~ m/^unknown command/; # skip echo
-
+
$res = join("\n", @lines);
$res .= "\n";
}
syslog("err", "VM $vmid monitor command failed - $err");
die $err;
}
-
+
return $res;
}
}
+sub qemu_volume_snapshot {
+ my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
+
+ my $running = PVE::QemuServer::check_running($vmid);
+
+ return if !PVE::Storage::volume_snapshot($storecfg, $volid, $snap, $running);
+
+ return if !$running;
+
+ vm_mon_cmd($vmid, "snapshot-drive", device => $deviceid, name => $snap);
+
+}
+
+sub qemu_volume_snapshot_delete {
+ my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
+
+ #need to implement statefile location
+ my $statefile="/tmp/$vmid-$snap";
+
+ unlink $statefile if -e $statefile;
+
+ my $running = PVE::QemuServer::check_running($vmid);
+
+ return if !PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
+
+ return if !$running;
+
+ #need to split delvm monitor command like savevm
+
+}
+
+sub qemu_snapshot_start {
+ my ($vmid, $snap) = @_;
+
+ #need to implement statefile location
+ my $statefile="/tmp/$vmid-$snap";
+
+ vm_mon_cmd($vmid, "snapshot-start", statefile => $statefile);
+
+}
+
+sub qemu_snapshot_end {
+ my ($vmid) = @_;
+
+ vm_mon_cmd($vmid, "snapshot-end");
+
+}
+
+sub qga_freezefs {
+ my ($vmid) = @_;
+
+ #need to impplement call to qemu-ga
+}
+
+sub qga_unfreezefs {
+ my ($vmid) = @_;
+
+ #need to impplement call to qemu-ga
+}
+
sub vm_start {
- my ($storecfg, $vmid, $statefile, $skiplock) = @_;
+ my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom) = @_;
lock_config($vmid, sub {
- my $conf = load_config($vmid);
+ my $conf = load_config($vmid, $migratedfrom);
check_lock($conf) if !$skiplock;
- die "VM $vmid already running\n" if check_running($vmid);
+ die "VM $vmid already running\n" if check_running($vmid, undef, $migratedfrom);
my $migrate_uri;
my $migrate_port = 0;
my $defaults = load_defaults();
+ # set environment variable useful inside network script
+ $ENV{PVE_MIGRATED_FROM} = $migratedfrom if $migratedfrom;
+
my ($cmd, $vollist) = config_to_command($storecfg, $vmid, $conf, $defaults, $migrate_uri);
# host pci devices
for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
eval { vm_mon_cmd($vmid, "migrate_set_downtime", value => $migrate_downtime); };
}
+ if($migratedfrom) {
+ my $capabilities = {};
+ $capabilities->{capability} = "xbzrle";
+ $capabilities->{state} = JSON::true;
+ eval { PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "migrate-set-capabilities", capabilities => [$capabilities]); };
+ }
+
vm_balloonset($vmid, $conf->{balloon}) if $conf->{balloon};
});
my $res;
+ my $timeout;
+ if ($cmd->{arguments} && $cmd->{arguments}->{timeout}) {
+ $timeout = $cmd->{arguments}->{timeout};
+ delete $cmd->{arguments}->{timeout};
+ }
+
eval {
die "VM $vmid not running\n" if !check_running($vmid, $nocheck);
my $sname = PVE::QemuServer::qmp_socket($vmid);
- if (-e $sname) {
+ if (-e $sname) {
my $qmpclient = PVE::QMPClient->new();
- $res = $qmpclient->cmd($vmid, $cmd);
+ $res = $qmpclient->cmd($vmid, $cmd, $timeout);
} elsif (-e "${var_run_tmpdir}/$vmid.mon") {
die "can't execute complex command on old monitor - stop/start your vm to fix the problem\n"
if scalar(%{$cmd->{arguments}});
my $res;
- my $cmd = {
+ my $cmd = {
execute => 'human-monitor-command',
arguments => { 'command-line' => $cmdline},
};
PVE::Storage::deactivate_volumes($storecfg, $vollist);
}
- foreach my $ext (qw(mon pid vnc)) {
+ foreach my $ext (qw(mon qmp pid vnc qga)) {
unlink "/var/run/qemu-server/${vmid}.$ext";
}
};
# We need that when migration VMs to other nodes (files already moved)
# Note: we set $keepActive in vzdump stop mode - volumes need to stay active
sub vm_stop {
- my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive) = @_;
-
- $timeout = 60 if !defined($timeout);
+ my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive, $migratedfrom) = @_;
$force = 1 if !defined($force) && !$shutdown;
+ if ($migratedfrom){
+ my $pid = check_running($vmid, $nocheck, $migratedfrom);
+ kill 15, $pid if $pid;
+ my $conf = load_config($vmid, $migratedfrom);
+ vm_stop_cleanup($storecfg, $vmid, $conf, $keepActive);
+ return;
+ }
+
lock_config($vmid, sub {
my $pid = check_running($vmid, $nocheck);
if (!$nocheck) {
$conf = load_config($vmid);
check_lock($conf) if !$skiplock;
+ if (!defined($timeout) && $shutdown && $conf->{startup}) {
+ my $opts = parse_startup($conf->{startup});
+ $timeout = $opts->{down} if $opts->{down};
+ }
}
+ $timeout = 60 if !defined($timeout);
+
eval {
if ($shutdown) {
$nocheck ? vm_mon_cmd_nocheck($vmid, "system_powerdown") : vm_mon_cmd($vmid, "system_powerdown");
lock_config($vmid, sub {
my $conf = load_config($vmid);
-
+
# there is no qmp command, so we use the human monitor command
vm_human_monitor_command($vmid, "sendkey $key");
});
scsihw0 => { bus => 0, addr => 5 },
scsihw1 => { bus => 0, addr => 6 },
ahci0 => { bus => 0, addr => 7 },
+ qga0 => { bus => 0, addr => 8 },
virtio0 => { bus => 0, addr => 10 },
virtio1 => { bus => 0, addr => 11 },
virtio2 => { bus => 0, addr => 12 },
'virtio13' => { bus => 2, addr => 8 },
'virtio14' => { bus => 2, addr => 9 },
'virtio15' => { bus => 2, addr => 10 },
- 'virtio16' => { bus => 2, addr => 11 },
- 'virtio17' => { bus => 2, addr => 12 },
- 'virtio18' => { bus => 2, addr => 13 },
- 'virtio19' => { bus => 2, addr => 14 },
- 'virtio20' => { bus => 2, addr => 15 },
- 'virtio21' => { bus => 2, addr => 16 },
- 'virtio22' => { bus => 2, addr => 17 },
- 'virtio23' => { bus => 2, addr => 18 },
- 'virtio24' => { bus => 2, addr => 19 },
- 'virtio25' => { bus => 2, addr => 20 },
- 'virtio26' => { bus => 2, addr => 21 },
- 'virtio27' => { bus => 2, addr => 22 },
- 'virtio28' => { bus => 2, addr => 23 },
- 'virtio29' => { bus => 2, addr => 24 },
- 'virtio30' => { bus => 2, addr => 25 },
- 'virtio31' => { bus => 2, addr => 26 },
-
};
if (defined($devices->{$id}->{bus}) && defined($devices->{$id}->{addr})) {
my $addr = sprintf("0x%x", $devices->{$id}->{addr});
my $bus = $devices->{$id}->{bus};
$res = ",bus=pci.$bus,addr=$addr";
- $bridges->{$bus} = 1;
+ $bridges->{$bus} = 1 if $bridges;
}
return $res;