description => "Enable/disable ACPI.",
default => 1,
},
+ agent => {
+ optional => 1,
+ type => 'boolean',
+ description => "Enable/disable Qemu GuestAgent.",
+ default => 0,
+ },
kvm => {
optional => 1,
type => 'boolean',
my $parse_size = sub {
my ($value) = @_;
- return undef if $value !~ m/^([1-9]\d*(\.\d+)?)([KMG])?$/;
+ return undef if $value !~ m/^(\d+(\.\d+)?)([KMG])?$/;
my ($size, $unit) = ($1, $3);
if ($unit) {
if ($unit eq 'K') {
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+$/;
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 @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};
}
$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};
#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";
}
+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, $migratedfrom) = @_;
PVE::Storage::deactivate_volumes($storecfg, $vollist);
}
- foreach my $ext (qw(mon qmp pid vnc)) {
+ foreach my $ext (qw(mon qmp pid vnc qga)) {
unlink "/var/run/qemu-server/${vmid}.$ext";
}
};
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 },