use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
use PVE::INotify;
use PVE::ProcFSTools;
-use Time::HiRes qw (gettimeofday);
+use Time::HiRes qw(gettimeofday);
-my $clock_ticks = POSIX::sysconf(&POSIX::_SC_CLK_TCK);
+my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
-# Note about locking: we use flock on the config file protect
+# Note about locking: we use flock on the config file protect
# against concurent actions.
# Aditionaly, we have a 'lock' setting in the config file. This
# can be set to 'migrate' or 'backup'. Most actions are not
# allowed when such lock is set. But you can ignore this kind of
# lock with the --skiplock flag.
-cfs_register_file('/qemu-server/', \&parse_vm_config);
+cfs_register_file('/qemu-server/', \&parse_vm_config);
+
+PVE::JSONSchema::register_standard_option('skiplock', {
+ description => "Ignore locks - only root is allowed to use this option.",
+ type => 'boolean',
+ optional => 1,
+});
+
+PVE::JSONSchema::register_standard_option('pve-qm-stateuri', {
+ description => "Some command save/restore state from this location.",
+ type => 'string',
+ maxLength => 128,
+ optional => 1,
+});
#no warnings 'redefine';
sub fairsched_mknod {
my ($parent, $weight, $desired) = @_;
- return syscall(&__NR_fairsched_mknod, int ($parent), int ($weight), int ($desired));
+ return syscall(&__NR_fairsched_mknod, int($parent), int($weight), int($desired));
}
sub fairsched_rmnod {
my ($id) = @_;
- return syscall(&__NR_fairsched_rmnod, int ($id));
+ return syscall(&__NR_fairsched_rmnod, int($id));
}
sub fairsched_mvpr {
my ($pid, $newid) = @_;
- return syscall(&__NR_fairsched_mvpr, int ($pid), int ($newid));
+ return syscall(&__NR_fairsched_mvpr, int($pid), int($newid));
}
sub fairsched_vcpus {
my ($id, $vcpus) = @_;
- return syscall(&__NR_fairsched_vcpus, int ($id), int ($vcpus));
+ return syscall(&__NR_fairsched_vcpus, int($id), int($vcpus));
}
sub fairsched_rate {
my ($id, $op, $rate) = @_;
- return syscall(&__NR_fairsched_rate, int ($id), int ($op), int ($rate));
+ return syscall(&__NR_fairsched_rate, int($id), int($op), int($rate));
}
use constant FAIRSCHED_SET_RATE => 0;
sub fairsched_cpulimit {
my ($id, $limit) = @_;
- my $cpulim1024 = int ($limit * 1024 / 100);
+ my $cpulim1024 = int($limit * 1024 / 100);
my $op = $cpulim1024 ? FAIRSCHED_SET_RATE : FAIRSCHED_DROP_RATE;
- return fairsched_rate ($id, $op, $cpulim1024);
+ return fairsched_rate($id, $op, $cpulim1024);
}
my $nodename = PVE::INotify::nodename();
description => "Automatic restart after crash (currently ignored).",
default => 0,
},
+ hotplug => {
+ optional => 1,
+ type => 'boolean',
+ description => "Activate hotplug for disk and network device",
+ default => 0,
+ },
reboot => {
optional => 1,
type => 'boolean',
memory => {
optional => 1,
type => 'integer',
- description => "Amount of RAM for the VM in MB.",
+ description => "Amount of RAM for the VM in MB. This is the maximum available memory when you use the balloon device.",
minimum => 16,
default => 512,
},
+ balloon => {
+ optional => 1,
+ type => 'integer',
+ description => "Amount of target RAM for the VM in MB.",
+ minimum => 16,
+ },
keyboard => {
optional => 1,
type => 'string',
type => 'string',
description => "Boot on floppy (a), hard disk (c), CD-ROM (d), or network (n).",
pattern => '[acdn]{1,4}',
- default => 'cad',
+ default => 'cdn',
},
bootdisk => {
optional => 1,
description => "Enable/disable time drift fix.",
default => 1,
},
- localtime => {
+ localtime => {
optional => 1,
type => 'boolean',
description => "Set the real time clock to local time. This is enabled by default if ostype indicates a Microsoft OS.",
},
startdate => {
optional => 1,
- type => 'string',
+ type => 'string',
typetext => "(now | YYYY-MM-DD | YYYY-MM-DDTHH:MM:SS)",
description => "Set the initial date of the real time clock. Valid format for date are: 'now' or '2006-06-17T16:01:21' or '2006-06-17'.",
pattern => '(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)',
my $nic_model_list = ['rtl8139', 'ne2k_pci', 'e1000', 'pcnet', 'virtio',
'ne2k_isa', 'i82551', 'i82557b', 'i82559er'];
-my $nic_model_list_txt = join (' ', sort @$nic_model_list);
+my $nic_model_list_txt = join(' ', sort @$nic_model_list);
# fixme:
my $netdesc = {
type => 'string', format => 'pve-qm-net',
typetext => "MODEL=XX:XX:XX:XX:XX:XX [,bridge=<dev>][,rate=<mbps>]",
description => <<EODESCR,
-Specify network devices.
+Specify network devices.
MODEL is one of: $nic_model_list_txt
-XX:XX:XX:XX:XX:XX should be an unique MAC address. This is
+XX:XX:XX:XX:XX:XX should be an unique MAC address. This is
automatically generated if not specified.
The bridge parameter can be used to automatically add the interface to a bridge device. The Proxmox VE standard bridge is called 'vmbr0'.
}
my $drivename_hash;
-
+
my $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] [,format=f] [,backup=yes|no] [,aio=native|threads]',
+ typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe] [,format=f] [,backup=yes|no] [,aio=native|threads]',
description => "Use volume as IDE hard disk or CD-ROM (n is 0 to 3).",
};
PVE::JSONSchema::register_standard_option("pve-qm-ide", $idedesc);
my $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] [,format=f] [,backup=yes|no] [,aio=native|threads]',
+ typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe] [,format=f] [,backup=yes|no] [,aio=native|threads]',
description => "Use volume as SCSI hard disk or CD-ROM (n is 0 to 13).",
};
PVE::JSONSchema::register_standard_option("pve-qm-scsi", $scsidesc);
my $virtiodesc = {
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] [,format=f] [,backup=yes|no] [,aio=native|threads]',
+ typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe] [,format=f] [,backup=yes|no] [,aio=native|threads]',
description => "Use volume as VIRTIO hard disk (n is 0 to 5).",
};
PVE::JSONSchema::register_standard_option("pve-qm-virtio", $virtiodesc);
Configure an USB device (n is 0 to 4). This can be used to
pass-through usb devices to the guest. HOSTUSBDEVICE syntax is:
-'bus-port(.port)*' (decimal numbers) or
+'bus-port(.port)*' (decimal numbers) or
'vendor_id:product_id' (hexadeciaml numbers)
-You can use the 'lsusb -t' command to list existing usb devices.
+You can use the 'lsusb -t' command to list existing 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.
my $serialdesc = {
optional => 1,
- type => 'string', format => 'pve-qm-serial',
+ type => 'string',
pattern => '/dev/ttyS\d+',
description => <<EODESCR,
-Map host serial devices (n is 0 to 3).
+Map host serial devices (n is 0 to 3).
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.
EODESCR
};
-PVE::JSONSchema::register_standard_option("pve-qm-serial", $serialdesc);
my $paralleldesc= {
optional => 1,
- type => 'string', format => 'pve-qm-parallel',
+ type => 'string',
pattern => '/dev/parport\d+',
description => <<EODESCR,
-Map host parallel devices (n is 0 to 2).
+Map host parallel devices (n is 0 to 2).
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.
EODESCR
};
-PVE::JSONSchema::register_standard_option("pve-qm-parallel", $paralleldesc);
for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
$confdesc->{"parallel$i"} = $paralleldesc;
return $kvm_api_version if $kvm_api_version;
- my $fh = IO::File->new ("</dev/kvm") ||
+ my $fh = IO::File->new("</dev/kvm") ||
return 0;
- if (my $v = $fh->ioctl (KVM_GET_API_VERSION(), 0)) {
+ if (my $v = $fh->ioctl(KVM_GET_API_VERSION(), 0)) {
$kvm_api_version = $v;
}
$kvm_user_version = 'unknown';
my $tmp = `kvm -help 2>/dev/null`;
-
+
if ($tmp =~ m/^QEMU( PC)? emulator version (\d+\.\d+\.\d+) /) {
$kvm_user_version = $2;
}
sub disknames {
# order is important - used to autoselect boot disk
- return ((map { "ide$_" } (0 .. ($MAX_IDE_DISKS - 1))),
+ return ((map { "ide$_" } (0 .. ($MAX_IDE_DISKS - 1))),
(map { "scsi$_" } (0 .. ($MAX_SCSI_DISKS - 1))),
(map { "virtio$_" } (0 .. ($MAX_VIRTIO_DISKS - 1))));
}
sub valid_drivename {
my $dev = shift;
- return defined ($drivename_hash->{$dev});
+ return defined($drivename_hash->{$dev});
}
sub option_exists {
my $key = shift;
return defined($confdesc->{$key});
-}
+}
sub nic_models {
return $nic_model_list;
win7 => 'Windows 7',
l24 => 'Linux 2.4',
l26 => 'Linux 2.6',
- };
-}
-
-# a clumsy way to split an argument string into an array,
-# we simply pass it to the cli (exec call)
-# fixme: use Text::ParseWords::shellwords() ?
-sub split_args {
- my ($str) = @_;
-
- my $args = [];
-
- return $args if !$str;
-
- my $cmd = 'perl -e \'foreach my $a (@ARGV) { print "$a\n"; } \' -- ' . $str;
-
- eval {
- run_command ($cmd, outfunc => sub {
- my $data = shift;
- push @$args, $data;
- });
};
-
- my $err = $@;
-
- die "unable to parse args: $str\n" if $err;
-
- return $args;
}
sub disk_devive_info {
$maxdev = 7;
}
- my $controller = int ($index / $maxdev);
+ my $controller = int($index / $maxdev);
my $unit = $index % $maxdev;
}
sub qemu_drive_name {
- my ($dev, $media) = @_;
+ my ($dev, $media) = @_;
- my $info = disk_devive_info ($dev);
+ my $info = disk_devive_info($dev);
my $mediastr = '';
if (($info->{bus} eq 'ide') || ($info->{bus} eq 'scsi')) {
$mediastr = ($media eq 'cdrom') ? "-cd" : "-hd";
- return sprintf("%s%i%s%i", $info->{bus}, $info->{controller},
+ return sprintf("%s%i%s%i", $info->{bus}, $info->{controller},
$mediastr, $info->{unit});
} else {
- return sprintf("%s%i", $info->{bus}, $info->{index});
+ return sprintf("%s%i", $info->{bus}, $info->{index});
}
}
} elsif ($cdrom =~ m|^/|) {
return $cdrom;
} else {
- return PVE::Storage::path ($storecfg, $cdrom);
+ return PVE::Storage::path($storecfg, $cdrom);
}
}
if (!($file eq 'none' || $file eq 'cdrom' ||
$file =~ m|^/dev/.+| || $file =~ m/^([^:]+):(.+)$/)) {
-
+
return undef if $file =~ m|/|;
-
+
if ($media && $media eq 'cdrom') {
$file = "local:iso/$file";
} else {
}
return if ($vtype eq $etype);
-
+
raise_param_exc({ $opt => "unexpected media type ($vtype != $etype)" });
}
if (($drive->{file} !~ m/^(cdrom|none)$/) &&
($drive->{file} !~ m|^/dev/.+|) &&
($drive->{file} !~ m/^([^:]+):(.+)$/) &&
- ($drive->{file} !~ m/^\d+$/)) {
+ ($drive->{file} !~ m/^\d+$/)) {
my ($vtype, $volid) = PVE::Storage::path_to_volume_id($storecfg, $drive->{file});
raise_param_exc({ $opt => "unable to associate path '$drive->{file}' to any storage"}) if !$vtype;
$drive->{media} = 'cdrom' if !$drive->{media} && $vtype eq 'iso';
sub create_conf_nolock {
my ($vmid, $settings) = @_;
- my $filename = config_file ($vmid);
+ my $filename = config_file($vmid);
die "configuration file '$filename' already exists\n" if -f $filename;
-
+
my $defaults = load_defaults();
$settings->{name} = "vm$vmid" if !$settings->{name};
my ($key, $data) = @_;
my $res = {};
-
+
# $key may be undefined - used to verify JSON parameters
if (!defined($key)) {
$res->{interface} = 'unknown'; # should not harm when used to verify parameters
$k = 'file' if $k eq 'volume';
return undef if defined $res->{$k};
-
+
$res->{$k} = $v;
} else {
if (!$res->{file} && $p !~ m/=/) {
return undef if !$res->{file};
- return undef if $res->{cache} &&
- $res->{cache} !~ m/^(off|none|writethrough|writeback)$/;
+ return undef if $res->{cache} &&
+ $res->{cache} !~ m/^(off|none|writethrough|writeback|unsafe)$/;
return undef if $res->{snapshot} && $res->{snapshot} !~ m/^(on|off)$/;
return undef if $res->{cyls} && $res->{cyls} !~ m/^\d+$/;
return undef if $res->{heads} && $res->{heads} !~ m/^\d+$/;
if ($res->{media} && ($res->{media} eq 'cdrom')) {
return undef if $res->{snapshot} || $res->{trans} || $res->{format};
- return undef if $res->{heads} || $res->{secs} || $res->{cyls};
+ return undef if $res->{heads} || $res->{secs} || $res->{cyls};
return undef if $res->{interface} eq 'virtio';
}
my $device = '';
my $maxdev = 0;
-
- if ($drive->{interface} eq 'virtio') {
- $device="virtio-blk-pci,drive=drive-$drive->{interface}$drive->{index},id=device-$drive->{interface}$drive->{index}";
+ if ($drive->{interface} eq 'virtio') {
+ my $pciaddr = print_pci_addr("$drive->{interface}$drive->{index}");
+ $device = "virtio-blk-pci,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}$pciaddr";
}
elsif ($drive->{interface} eq 'scsi') {
$maxdev = 7;
- my $controller = int ($drive->{index} / $maxdev);
+ my $controller = int($drive->{index} / $maxdev);
my $unit = $drive->{index} % $maxdev;
- $device="scsi-disk,bus=scsi$controller.0,scsi-id=$unit,drive=drive-$drive->{interface}$drive->{index},id=device-$drive->{interface}$drive->{index}";
+ $device = "scsi-disk,bus=scsi$controller.0,scsi-id=$unit,drive=drive-$drive->{interface}$drive->{index},id=device-$drive->{interface}$drive->{index}";
}
elsif ($drive->{interface} eq 'ide'){
$maxdev = 2;
- my $controller = int ($drive->{index} / $maxdev);
+ my $controller = int($drive->{index} / $maxdev);
my $unit = $drive->{index} % $maxdev;
- $device="ide-drive,bus=ide.$controller,unit=$unit,drive=drive-$drive->{interface}$drive->{index},id=device-$drive->{interface}$drive->{index}";
+ $device = "ide-drive,bus=ide.$controller,unit=$unit,drive=drive-$drive->{interface}$drive->{index},id=device-$drive->{interface}$drive->{index}";
}
if ($drive->{interface} eq 'usb'){
- # -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0
+ # -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0
}
return $device;
my $opts = '';
foreach my $o (@qemu_drive_options) {
$opts .= ",$o=$drive->{$o}" if $drive->{$o};
- }
+ }
# use linux-aio by default (qemu default is threads)
- $opts .= ",aio=native" if !$drive->{aio};
+ $opts .= ",aio=native" if !$drive->{aio};
my $path;
my $volid = $drive->{file};
- if (drive_is_cdrom ($drive)) {
- $path = get_iso_path ($storecfg, $vmid, $volid);
+ if (drive_is_cdrom($drive)) {
+ $path = get_iso_path($storecfg, $vmid, $volid);
} else {
if ($volid =~ m|^/|) {
$path = $volid;
} else {
- $path = PVE::Storage::path ($storecfg, $volid);
+ $path = PVE::Storage::path($storecfg, $volid);
}
}
my $res = {};
- foreach my $kvp (split (/,/, $data)) {
+ foreach my $kvp (split(/,/, $data)) {
if ($kvp =~ m/^(ne2k_pci|e1000|rtl8139|pcnet|virtio|ne2k_isa|i82551|i82557b|i82559er)(=([0-9a-f]{2}(:[0-9a-f]{2}){5}))?$/i) {
- my $model = lc ($1);
- my $mac = uc($3) || random_ether_addr ();
+ my $model = lc($1);
+ my $mac = uc($3) || PVE::Tools::random_ether_addr();
$res->{model} = $model;
$res->{macaddr} = $mac;
} elsif ($kvp =~ m/^bridge=(\S+)$/) {
} else {
return undef;
}
-
+
}
return undef if !$res->{model};
return if $vid eq $volid; # do not add duplicates
} else {
$key = $test;
- }
+ }
}
die "To many unused volume - please delete them first.\n" if !$key;
sub verify_bootdisk {
my ($value, $noerr) = @_;
- return $value if valid_drivename($value);
+ return $value if valid_drivename($value);
return undef if $noerr;
return $value if parse_net($value);
return undef if $noerr;
-
+
die "unable to parse network options\n";
}
sub verify_drive {
my ($value, $noerr) = @_;
- return $value if parse_drive (undef, $value);
+ return $value if parse_drive(undef, $value);
return undef if $noerr;
-
+
die "unable to parse drive options\n";
}
return $value if parse_watchdog($value);
return undef if $noerr;
-
+
die "unable to parse watchdog options\n";
}
my $res = {};
- foreach my $p (split (/,/, $value)) {
+ foreach my $p (split(/,/, $value)) {
next if $p =~ m/^\s*$/;
if ($p =~ m/^(model=)?(i6300esb|ib700)$/) {
return undef if !$value;
- my @dl = split (/,/, $value);
+ my @dl = split(/,/, $value);
my $found;
my $res = {};
return $res;
}
-
+
PVE::JSONSchema::register_format('pve-qm-usb-device', \&verify_usb_device);
sub verify_usb_device {
my ($value, $noerr) = @_;
return $value if parse_usb_device($value);
return undef if $noerr;
-
- die "unable to parse usb device\n";
-}
-PVE::JSONSchema::register_format('pve-qm-parallel', \&verify_parallel);
-sub verify_parallel {
- my ($value, $noerr) = @_;
-
- return $value if parse_parallel($value);
-
- return undef if $noerr;
-
- die "invalid device name\n";
-}
-
-sub parse_parallel {
- my ($value) = @_;
-
- return undef if !$value;
-
- my $res = {};
- if ($value =~ m|^/dev/parport\d+$|) {
- $res->{dev} = $value;
- } else {
- return undef;
- }
-
- return $res;
-}
-
-PVE::JSONSchema::register_format('pve-qm-serial', \&verify_serial);
-sub verify_serial {
- my ($value, $noerr) = @_;
-
- return $value if parse_serial($value);
-
- return undef if $noerr;
-
- die "invalid device name\n";
-
-}
-
-sub parse_serial {
- my ($value) = @_;
-
- return undef if !$value;
-
- my $res = {};
- if ($value =~ m|^/dev/ttyS\d+$|) {
- $res->{dev} = $value;
- } else {
- return undef;
- }
-
- return $res;
+ die "unable to parse usb device\n";
}
# add JSON properties for create and set function
my $type = $confdesc->{$key}->{type};
- if (!defined ($value)) {
+ if (!defined($value)) {
die "got undefined value\n";
}
}
if ($type eq 'boolean') {
- return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
- return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
- die "type check ('boolean') failed - got '$value'\n";
+ return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
+ return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
+ die "type check ('boolean') failed - got '$value'\n";
} elsif ($type eq 'integer') {
return int($1) if $value =~ m/^(\d+)$/;
die "type check ('integer') failed - got '$value'\n";
if (my $fmt = $confdesc->{$key}->{format}) {
if ($fmt eq 'pve-qm-drive') {
# special case - we need to pass $key to parse_drive()
- my $drive = parse_drive ($key, $value);
+ my $drive = parse_drive($key, $value);
return $value if $drive;
die "unable to parse drive options\n";
}
PVE::JSONSchema::check_format($fmt, $value);
- return $value;
- }
+ return $value;
+ }
$value =~ s/^\"(.*)\"$/$1/;
- return $value;
+ return $value;
} else {
die "internal error"
}
sub lock_config {
my ($vmid, $code, @param) = @_;
- my $filename = config_file_lock ($vmid);
+ my $filename = config_file_lock($vmid);
- lock_file($filename, 10, $code, @param);
+ my $res = lock_file($filename, 10, $code, @param);
die $@ if $@;
+
+ return $res;
}
sub cfs_config_path {
sub touch_config {
my ($vmid) = @_;
- my $conf = config_file ($vmid);
+ my $conf = config_file($vmid);
utime undef, undef, $conf;
}
sub create_disks {
- my ($storecfg, $vmid, $settings) = @_;
+ my ($storecfg, $vmid, $settings, $conf, $default_storage) = @_;
my $vollist = [];
foreach_drive($settings, sub {
my ($ds, $disk) = @_;
- return if drive_is_cdrom ($disk);
+ return if drive_is_cdrom($disk);
my $file = $disk->{file};
if ($file =~ m/^(([^:\s]+):)?(\d+(\.\d+)?)$/) {
- my $storeid = $2 || 'local';
+ my $storeid = $2 || $default_storage;
my $size = $3;
- my $defformat = PVE::Storage::storage_default_format ($storecfg, $storeid);
+ 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");
+ syslog('info', "VM $vmid creating new disk - size is $size GB");
- my $volid = PVE::Storage::vdisk_alloc ($storecfg, $storeid, $vmid,
+ my $volid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid,
$fmt, undef, $size*1024*1024);
$disk->{file} = $volid;
- delete ($disk->{format}); # no longer needed
+ delete $disk->{format}; # no longer needed
push @$vollist, $volid;
- $settings->{$ds} = PVE::QemuServer::print_drive ($vmid, $disk);
+ $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});
+ $path = PVE::Storage::path($storecfg, $disk->{file});
}
if (!(-f $path || -b $path)) {
die "image '$path' does not exists\n";
}
}
+ PVE::QemuServer::vm_deviceadd($storecfg, $conf, $vmid, $ds, $disk) if defined($conf);
});
};
my $err = $@;
if ($err) {
- syslog ('err', "VM $vmid creating disks failed");
+ syslog('err', "VM $vmid creating disks failed");
foreach my $volid (@$vollist) {
- eval { PVE::Storage::vdisk_free ($storecfg, $volid); };
+ eval { PVE::Storage::vdisk_free($storecfg, $volid); };
warn $@ if $@;
}
die $err;
return $vollist;
}
-sub unlink_image {
- my ($storecfg, $vmid, $volid) = @_;
-
- die "reject to unlink absolute path '$volid'"
- if $volid =~ m|^/|;
-
- my ($path, $owner) = PVE::Storage::path ($storecfg, $volid);
-
- die "reject to unlink '$volid' - not owned by this VM"
- if !$owner || ($owner != $vmid);
-
- syslog ('info', "VM $vmid deleting volume '$volid'");
-
- PVE::Storage::vdisk_free ($storecfg, $volid);
-
- touch_config ($vmid);
-}
-
sub destroy_vm {
- my ($storecfg, $vmid) = @_;
+ my ($storecfg, $vmid, $keep_empty_config) = @_;
- my $conffile = config_file ($vmid);
+ my $conffile = config_file($vmid);
- my $conf = load_config ($vmid);
+ my $conf = load_config($vmid);
- check_lock ($conf);
+ check_lock($conf);
- # only remove disks owned by this VM
+ # only remove disks owned by this VM
foreach_drive($conf, sub {
my ($ds, $drive) = @_;
- return if drive_is_cdrom ($drive);
+ return if drive_is_cdrom($drive);
my $volid = $drive->{file};
- next if !$volid || $volid =~ m|^/|;
+ return if !$volid || $volid =~ m|^/|;
- my ($path, $owner) = PVE::Storage::path ($storecfg, $volid);
- next if !$path || !$owner || ($owner != $vmid);
+ my ($path, $owner) = PVE::Storage::path($storecfg, $volid);
+ return if !$path || !$owner || ($owner != $vmid);
- PVE::Storage::vdisk_free ($storecfg, $volid);
+ PVE::Storage::vdisk_free($storecfg, $volid);
});
-
- unlink $conffile;
+
+ if ($keep_empty_config) {
+ PVE::Tools::file_set_contents($conffile, "memory: 128\n");
+ } else {
+ unlink $conffile;
+ }
# also remove unused disk
eval {
- my $dl = PVE::Storage::vdisk_list ($storecfg, undef, $vmid);
+ my $dl = PVE::Storage::vdisk_list($storecfg, undef, $vmid);
eval {
- PVE::Storage::foreach_volid ($dl, sub {
+ PVE::Storage::foreach_volid($dl, sub {
my ($volid, $sid, $volname, $d) = @_;
- PVE::Storage::vdisk_free ($storecfg, $volid);
+ PVE::Storage::vdisk_free($storecfg, $volid);
});
};
warn $@ if $@;
$res->{$ds} = $di;
- return if drive_is_cdrom ($di);
+ return if drive_is_cdrom($di);
if ($di->{file} =~ m|^/dev/.+|) {
- $info->{$di->{file}}->{size} = PVE::Storage::file_size_info ($di->{file});
+ $info->{$di->{file}}->{size} = PVE::Storage::file_size_info($di->{file});
} else {
push @$vollist, $di->{file};
}
});
eval {
- my $dl = PVE::Storage::vdisk_list ($storecfg, undef, $vmid, $vollist);
+ my $dl = PVE::Storage::vdisk_list($storecfg, undef, $vmid, $vollist);
- PVE::Storage::foreach_volid ($dl, sub {
+ PVE::Storage::foreach_volid($dl, sub {
my ($volid, $sid, $volname, $d) = @_;
$info->{$volid} = $d;
});
foreach my $ds (keys %$res) {
my $di = $res->{$ds};
- $res->{$ds}->{disksize} = $info->{$di->{file}} ?
+ $res->{$ds}->{disksize} = $info->{$di->{file}} ?
$info->{$di->{file}}->{size} / (1024*1024) : 0;
}
die "no such VM ('$vmid')\n" if !defined($conf);
return $conf;
-}
+}
sub parse_vm_config {
my ($filename, $raw) = @_;
digest => Digest::SHA1::sha1_hex($raw),
};
- $filename =~ m|/qemu-server/(\d+)\.conf$|
+ $filename =~ m|/qemu-server/(\d+)\.conf$|
|| die "got strange filename '$filename'";
my $vmid = $1;
while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
my $line = $1;
-
+
next if $line =~ m/^\#/;
next if $line =~ m/^\s*$/;
my $v = parse_drive($key, $value);
if (my $volid = filename_to_volume_id($vmid, $v->{file}, $v->{media})) {
$v->{file} = $volid;
- $value = print_drive ($vmid, $v);
+ $value = print_drive($vmid, $v);
} else {
warn "vm $vmid - unable to parse value of '$key'\n";
next;
# convert old smp to sockets
if ($res->{smp} && !$res->{sockets}) {
$res->{sockets} = $res->{smp};
- }
+ }
delete $res->{smp};
return $res;
sub change_config {
my ($vmid, $settings, $unset, $skiplock) = @_;
- lock_config ($vmid, &change_config_nolock, $settings, $unset, $skiplock);
+ lock_config($vmid, &change_config_nolock, $settings, $unset, $skiplock);
}
sub change_config_nolock {
# we do not use 'smp' any longer
if ($settings->{sockets}) {
- $unset->{smp} = 1;
+ $unset->{smp} = 1;
} elsif ($settings->{smp}) {
$settings->{sockets} = $settings->{smp};
$unset->{smp} = 1;
my $filename = config_file($vmid);
my $tmpfn = "$filename.$$.tmp";
- my $fh = new IO::File ($filename, "r") ||
+ my $fh = new IO::File($filename, "r") ||
die "unable to read config for VM $vmid\n";
my $werror = "unable to write config for VM $vmid\n";
- my $out = new IO::File ($tmpfn, "w") || die $werror;
+ my $out = new IO::File($tmpfn, "w") || die $werror;
eval {
my $done;
while (my $line = <$fh>) {
-
+
if (($line =~ m/^\#/) || ($line =~ m/^\s*$/)) {
die $werror unless print $out $line;
next;
next if $done->{$key};
$done->{$key} = 1;
- if (defined ($res->{$key})) {
+ if (defined($res->{$key})) {
$value = $res->{$key};
delete $res->{$key};
}
- if (!defined ($unset->{$key})) {
+ if (!defined($unset->{$key})) {
die $werror unless print $out "$key: $value\n";
- }
+ }
next;
}
foreach my $key (keys %$res) {
- if (!defined ($unset->{$key})) {
+ if (!defined($unset->{$key})) {
die $werror unless print $out "$key: $res->{$key}\n";
}
}
if (!$out->close()) {
$err = "close failed - $!\n";
unlink $tmpfn;
- die $err;
+ die $err;
}
if (!rename($tmpfn, $filename)) {
}
}
-sub load_defaults {
+sub load_defaults {
my $res = {};
$res->{$key} = $default;
}
}
-
+
my $conf = PVE::Cluster::cfs_read_file('datacenter.cfg');
$res->{keyboard} = $conf->{keyboard} if $conf->{keyboard};
foreach my $vmid (keys %$ids) {
my $d = $ids->{$vmid};
next if !$d->{node} || $d->{node} ne $nodename;
+ next if !$d->{type} || $d->{type} ne 'qemu';
$res->{$vmid}->{exists} = 1;
}
return $res;
my ($conf, $noerr) = @_;
my $loc_res = 0;
- # fixme:
- die "implement me";
- $loc_res = 1 if $conf->{hostusb};
- $loc_res = 1 if $conf->{hostpci};
+
+ $loc_res = 1 if $conf->{hostusb}; # old syntax
+ $loc_res = 1 if $conf->{hostpci}; # old syntax
foreach my $k (keys %$conf) {
$loc_res = 1 if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/;
sub check_cmdline {
my ($pidfile, $pid) = @_;
- my $fh = IO::File->new ("/proc/$pid/cmdline", "r");
- if (defined ($fh)) {
+ my $fh = IO::File->new("/proc/$pid/cmdline", "r");
+ if (defined($fh)) {
my $line = <$fh>;
$fh->close;
return undef if !$line;
- my @param = split (/\0/, $line);
+ my @param = split(/\0/, $line);
my $cmd = $param[0];
return if !$cmd || ($cmd !~ m|kvm$|);
}
sub check_running {
- my ($vmid) = @_;
+ my ($vmid, $nocheck) = @_;
- my $filename = config_file ($vmid);
+ my $filename = config_file($vmid);
die "unable to find configuration file for VM $vmid - no such machine\n"
- if ! -f $filename;
+ if !$nocheck && ! -f $filename;
- my $pidfile = pidfile_name ($vmid);
+ my $pidfile = pidfile_name($vmid);
- if (my $fd = IO::File->new ("<$pidfile")) {
- my $st = stat ($fd);
+ if (my $fd = IO::File->new("<$pidfile")) {
+ my $st = stat($fd);
my $line = <$fd>;
- close ($fd);
+ close($fd);
my $mtime = $st->mtime;
if ($mtime > time()) {
if ($line =~ m/^(\d+)$/) {
my $pid = $1;
-
- return $pid if ((-d "/proc/$pid") && check_cmdline ($pidfile, $pid));
+ if (check_cmdline($pidfile, $pid)) {
+ if (my $pinfo = PVE::ProcFSTools::check_process_running($pid)) {
+ return $pid;
+ }
+ }
}
}
}
sub vzlist {
-
+
my $vzlist = config_list();
- my $fd = IO::Dir->new ($var_run_tmpdir) || return $vzlist;
+ my $fd = IO::Dir->new($var_run_tmpdir) || return $vzlist;
- while (defined(my $de = $fd->read)) {
+ while (defined(my $de = $fd->read)) {
next if $de !~ m/^(\d+)\.pid$/;
my $vmid = $1;
- next if !defined ($vzlist->{$vmid});
- if (my $pid = check_running ($vmid)) {
+ next if !defined($vzlist->{$vmid});
+ if (my $pid = check_running($vmid)) {
$vzlist->{$vmid}->{pid} = $pid;
}
}
if ($volid =~ m|^/|) {
$path = $timeoutid = $volid;
} else {
- $storeid = $timeoutid = PVE::Storage::parse_volume_id ($volid);
+ $storeid = $timeoutid = PVE::Storage::parse_volume_id($volid);
$path = PVE::Storage::path($storecfg, $volid);
}
my $res = {};
- my $storecfg = PVE::Storage::config();
+ my $storecfg = PVE::Storage::config();
my $list = vzlist();
- my ($uptime) = PVE::ProcFSTools::read_proc_uptime();
+ my ($uptime) = PVE::ProcFSTools::read_proc_uptime(1);
+
+ my $cpucount = $cpuinfo->{cpus} || 1;
foreach my $vmid (keys %$list) {
next if $opt_vmid && ($vmid ne $opt_vmid);
}
$d->{cpus} = ($conf->{sockets} || 1) * ($conf->{cores} || 1);
- $d->{name} = $conf->{name} || "VM $vmid";
- $d->{maxmem} = $conf->{memory} ? $conf->{memory}*(1024*1024) : 0;
+ $d->{cpus} = $cpucount if $d->{cpus} > $cpucount;
+ $d->{name} = $conf->{name} || "VM $vmid";
+ $d->{maxmem} = $conf->{memory} ? $conf->{memory}*(1024*1024) : 0;
$d->{uptime} = 0;
$d->{cpu} = 0;
- $d->{relcpu} = 0;
$d->{mem} = 0;
$d->{netout} = 0;
my $vmid = $1;
my $d = $res->{$vmid};
next if !$d;
-
+
$d->{netout} += $netdev->{$dev}->{receive};
$d->{netin} += $netdev->{$dev}->{transmit};
}
- my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
- my $cpucount = $cpuinfo->{cpus} || 1;
my $ctime = gettimeofday;
foreach my $vmid (keys %$list) {
if (my $fh = IO::File->new("/proc/$pid/io", "r")) {
my $data = {};
- while (defined (my $line = <$fh>)) {
+ while (defined(my $line = <$fh>)) {
if ($line =~ m/^([rw]char):\s+(\d+)$/) {
$data->{$1} = $2;
}
$d->{diskwrite} = $data->{wchar} || 0;
}
- my $statstr = file_read_firstline("/proc/$pid/stat");
- next if !$statstr;
+ my $pstat = PVE::ProcFSTools::read_proc_pid_stat($pid);
+ next if !$pstat; # not running
- my ($utime, $stime, $vsize, $rss, $starttime);
- if ($statstr =~ m/^$pid \(.*\) \S (-?\d+) -?\d+ -?\d+ -?\d+ -?\d+ \d+ \d+ \d+ \d+ \d+ (\d+) (\d+) (-?\d+) (-?\d+) -?\d+ -?\d+ -?\d+ 0 (\d+) (\d+) (-?\d+) \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ -?\d+ -?\d+ \d+ \d+ \d+/) {
- ($utime, $stime, $vsize, $rss, $starttime) = ($2, $3, $7, $8 * 4096, $6);
- } else {
- next;
- }
-
- my $used = $utime + $stime;
+ my $used = $pstat->{utime} + $pstat->{stime};
- my $vcpus = $d->{cpus} > $cpucount ? $cpucount : $d->{cpus};
+ $d->{uptime} = int(($uptime - $pstat->{starttime})/$cpuinfo->{user_hz});
- $d->{uptime} = int ($uptime - ($starttime/100));
-
- if ($vsize) {
- $d->{mem} = int (($rss/$vsize)*$d->{maxmem});
+ if ($pstat->{vsize}) {
+ $d->{mem} = int(($pstat->{rss}/$pstat->{vsize})*$d->{maxmem});
}
my $old = $last_proc_pid_stat->{$pid};
if (!$old) {
- $last_proc_pid_stat->{$pid} = {
- time => $ctime,
+ $last_proc_pid_stat->{$pid} = {
+ time => $ctime,
used => $used,
cpu => 0,
- relcpu => 0,
};
next;
}
- my $dtime = ($ctime - $old->{time}) * $cpucount * $clock_ticks;
+ my $dtime = ($ctime - $old->{time}) * $cpucount * $cpuinfo->{user_hz};
if ($dtime > 1000) {
my $dutime = $used - $old->{used};
- $d->{cpu} = $dutime/$dtime;
- $d->{relcpu} = ($d->{cpu} * $cpucount) / $vcpus;
+ $d->{cpu} = (($dutime/$dtime)* $cpucount) / $d->{cpus};
$last_proc_pid_stat->{$pid} = {
- time => $ctime,
+ time => $ctime,
used => $used,
cpu => $d->{cpu},
- relcpu => $d->{relcpu},
};
} else {
$d->{cpu} = $old->{cpu};
- $d->{relcpu} = $old->{relcpu};
}
}
foreach my $ds (keys %$conf) {
next if !valid_drivename($ds);
- my $drive = parse_drive ($ds, $conf->{$ds});
+ my $drive = parse_drive($ds, $conf->{$ds});
next if !$drive;
&$func($ds, $drive);
my ($storecfg, $vmid, $conf, $defaults, $migrate_uri) = @_;
my $cmd = [];
-
+ my $pciaddr = '';
my $kvmver = kvm_user_version();
my $vernum = 0; # unknown
if ($kvmver =~ m/^(\d+)\.(\d+)\.(\d+)$/) {
my $use_virtio = 0;
- my $socket = monitor_socket ($vmid);
+ my $socket = monitor_socket($vmid);
push @$cmd, '-chardev', "socket,id=monitor,path=$socket,server,nowait";
push @$cmd, '-mon', "chardev=monitor,mode=readline";
- $socket = vnc_socket ($vmid);
+ $socket = vnc_socket($vmid);
push @$cmd, '-vnc', "unix:$socket,x509,password";
- push @$cmd, '-pidfile' , pidfile_name ($vmid);
-
+ push @$cmd, '-pidfile' , pidfile_name($vmid);
+
push @$cmd, '-daemonize';
push @$cmd, '-incoming', $migrate_uri if $migrate_uri;
# include usb device config
push @$cmd, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg';
-
+
# enable absolute mouse coordinates (needed by vnc)
- my $tablet = defined ($conf->{tablet}) ? $conf->{tablet} : $defaults->{tablet};
+ my $tablet = defined($conf->{tablet}) ? $conf->{tablet} : $defaults->{tablet};
push @$cmd, '-device', 'usb-tablet,bus=ehci.0,port=6' if $tablet;
# host pci devices
for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
my $d = parse_hostpci($conf->{"hostpci$i"});
next if !$d;
- push @$cmd, '-device', "pci-assign,host=$d->{pciid},id=hostpci$i";
+ $pciaddr = print_pci_addr("hostpci$i");
+ push @$cmd, '-device', "pci-assign,host=$d->{pciid},id=hostpci$i$pciaddr";
}
# usb devices
}
}
- if (my $usbdl = $conf->{hostusb}) {
- my @dl = split (/,/, $usbdl);
- foreach my $dev (@dl) {
- push @$cmd, '-usbdevice', "host:$dev" if $dev;
- }
- }
-
# serial devices
for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
- my $d = parse_serial($conf->{"serial$i"});
- next if !$d;
- push @$cmd, '-chardev', "tty,id=serial$i,path=$d->{dev}";
- push @$cmd, '-device', "isa-serial,chardev=serial$i";
+ if (my $path = $conf->{"serial$i"}) {
+ die "no such serial device\n" if ! -c $path;
+ push @$cmd, '-chardev', "tty,id=serial$i,path=$path";
+ push @$cmd, '-device', "isa-serial,chardev=serial$i";
+ }
}
# parallel devices
for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
- my $d = parse_parallel($conf->{"parallel$i"});
- next if !$d;
- push @$cmd, '-chardev', "parport,id=parallel$i,path=$d->{dev}";
- push @$cmd, '-device', "isa-parallel,chardev=parallel$i";
+ if (my $path = $conf->{"parallel$i"}) {
+ die "no such parallel device\n" if ! -c $path;
+ push @$cmd, '-chardev', "parport,id=parallel$i,path=$path";
+ push @$cmd, '-device', "isa-parallel,chardev=parallel$i";
+ }
}
my $vmname = $conf->{name} || "vm$vmid";
push @$cmd, '-name', $vmname;
-
+
my $sockets = 1;
$sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused
$sockets = $conf->{sockets} if $conf->{sockets};
push @$cmd, '-cpu', $conf->{cpu} if $conf->{cpu};
- $boot_opt = "menu=on";
- if ($conf->{boot}) {
- $boot_opt .= ",order=$conf->{boot}";
- }
-
push @$cmd, '-nodefaults';
- push @$cmd, '-boot', $boot_opt if $boot_opt;
+ my $bootorder = $conf->{boot} || $confdesc->{boot}->{default};
+ push @$cmd, '-boot', "menu=on,order=$bootorder";
- push @$cmd, '-no-acpi' if defined ($conf->{acpi}) && $conf->{acpi} == 0;
+ push @$cmd, '-no-acpi' if defined($conf->{acpi}) && $conf->{acpi} == 0;
- push @$cmd, '-no-reboot' if defined ($conf->{reboot}) && $conf->{reboot} == 0;
+ push @$cmd, '-no-reboot' if defined($conf->{reboot}) && $conf->{reboot} == 0;
my $vga = $conf->{vga};
if (!$vga) {
$vga = 'cirrus';
}
}
-
+
push @$cmd, '-vga', $vga if $vga; # for kvm 77 and later
# time drift fix
- my $tdf = defined ($conf->{tdf}) ? $conf->{tdf} : $defaults->{tdf};
+ my $tdf = defined($conf->{tdf}) ? $conf->{tdf} : $defaults->{tdf};
push @$cmd, '-tdf' if $tdf;
- my $nokvm = defined ($conf->{kvm}) && $conf->{kvm} == 0 ? 1 : 0;
+ my $nokvm = defined($conf->{kvm}) && $conf->{kvm} == 0 ? 1 : 0;
if (my $ost = $conf->{ostype}) {
# other, wxp, w2k, w2k3, w2k8, wvista, win7, l24, l26
if ($ost =~ m/^w/) { # windows
- push @$cmd, '-localtime' if !defined ($conf->{localtime});
+ push @$cmd, '-localtime' if !defined($conf->{localtime});
# use rtc-td-hack when acpi is enabled
- if (!(defined ($conf->{acpi}) && $conf->{acpi} == 0)) {
+ if (!(defined($conf->{acpi}) && $conf->{acpi} == 0)) {
push @$cmd, '-rtc-td-hack';
}
}
# -tdf ?
- # -no-acpi
- # -no-kvm
+ # -no-acpi
+ # -no-kvm
# -win2k-hack ?
}
- push @$cmd, '-no-kvm' if $nokvm;
+ if ($nokvm) {
+ push @$cmd, '-no-kvm';
+ } else {
+ die "No accelerator found!\n" if !$cpuinfo->{hvm};
+ }
push @$cmd, '-localtime' if $conf->{localtime};
#my $soundhw = $conf->{soundhw} || $defaults->{soundhw};
#push @$cmd, '-soundhw', 'es1370';
#push @$cmd, '-soundhw', $soundhw if $soundhw;
+ $pciaddr = print_pci_addr("balloon0");
+ push @$cmd, '-device', "virtio-balloon-pci,id=balloon0$pciaddr" if $conf->{balloon};
if ($conf->{watchdog}) {
my $wdopts = parse_watchdog($conf->{watchdog});
- push @$cmd, '-watchdog', $wdopts->{model} || 'i6300esb';
+ $pciaddr = print_pci_addr("watchdog");
+ my $watchdog = $wdopts->{model} || 'i6300esb';
+ push @$cmd, '-device', "$watchdog$pciaddr";
push @$cmd, '-watchdog-action', $wdopts->{action} if $wdopts->{action};
}
foreach_drive($conf, sub {
my ($ds, $drive) = @_;
- eval {
- PVE::Storage::parse_volume_id ($drive->{file});
+ if (PVE::Storage::parse_volume_id($drive->{file}, 1)) {
push @$vollist, $drive->{file};
- }; # ignore errors
-
+ }
+
$use_virtio = 1 if $ds =~ m/^virtio/;
if ($drive->{interface} eq 'scsi') {
my $maxdev = 7;
- my $controller = int ($drive->{index} / $maxdev);
- push @$cmd, '-device', "lsi,id=scsi$controller" if !$scsicontroller->{$controller};
- my $scsicontroller->{$controller}=1;
+ my $controller = int($drive->{index} / $maxdev);
+ $pciaddr = print_pci_addr("scsi$controller");
+ push @$cmd, '-device', "lsi,id=scsi$controller$pciaddr" if !$scsicontroller->{$controller};
+ $scsicontroller->{$controller}=1;
}
- my $tmp = print_drive_full ($storecfg, $vmid, $drive);
+ my $tmp = print_drive_full($storecfg, $vmid, $drive);
$tmp .= ",boot=on" if $conf->{bootdisk} && ($conf->{bootdisk} eq $ds);
push @$cmd, '-drive', $tmp;
- push @$cmd, '-device',print_drivedevice_full ($storecfg,$vmid, $drive);
+ push @$cmd, '-device',print_drivedevice_full($storecfg,$vmid, $drive);
});
push @$cmd, '-m', $conf->{memory} || $defaults->{memory};
-
+
my $foundnet = 0;
foreach my $k (sort keys %$conf) {
next if $k !~ m/^net(\d+)$/;
- my $i = int ($1);
+ my $i = int($1);
die "got strange net id '$i'\n" if $i >= ${MAX_NETS};
my $ifname = "tap${vmid}i$i";
# kvm uses TUNSETIFF ioctl, and that limits ifname length
- die "interface name '$ifname' is too long (max 15 character)\n"
+ die "interface name '$ifname' is too long (max 15 character)\n"
if length($ifname) >= 16;
my $device = $net->{model};
# not loading the pxe rom file
my $extra = (!$conf->{boot} || ($conf->{boot} !~ m/n/)) ?
"romfile=," : '';
- push @$cmd, '-device', "$device,${extra}mac=$net->{macaddr},netdev=${k}";
+ $pciaddr = print_pci_addr("${k}");
+ push @$cmd, '-device', "$device,${extra}mac=$net->{macaddr},netdev=${k}$pciaddr";
}
- }
-
+ }
+
push @$cmd, '-net', 'none' if !$foundnet;
# hack: virtio with fairsched is unreliable, so we do not use fairsched
# when the VM uses virtio devices.
- if (!$use_virtio && $have_ovz) {
-
- my $cpuunits = defined ($conf->{cpuunits}) ?
+ if (!$use_virtio && $have_ovz) {
+
+ my $cpuunits = defined($conf->{cpuunits}) ?
$conf->{cpuunits} : $defaults->{cpuunits};
push @$cmd, '-cpuunits', $cpuunits if $cpuunits;
# add custom args
if ($conf->{args}) {
- my $aa = split_args ($conf->{args});
+ my $aa = PVE::Tools::split_args($conf->{args});
push @$cmd, @$aa;
}
return wantarray ? ($cmd, $vollist) : $cmd;
}
-
+
sub vnc_socket {
my ($vmid) = @_;
return "${var_run_tmpdir}/$vmid.vnc";
return "${var_run_tmpdir}/$vmid.pid";
}
-sub random_ether_addr {
+sub next_migrate_port {
+
+ for (my $p = 60000; $p < 60010; $p++) {
- my $rand = Digest::SHA1::sha1_hex (rand(), time());
+ my $sock = IO::Socket::INET->new(Listen => 5,
+ LocalAddr => 'localhost',
+ LocalPort => $p,
+ ReuseAddr => 1,
+ Proto => 0);
- my $mac = '';
- for (my $i = 0; $i < 6; $i++) {
- my $ss = hex (substr ($rand, $i*2, 2));
- if (!$i) {
- $ss &= 0xfe; # clear multicast
- $ss |= 2; # set local id
+ if ($sock) {
+ close($sock);
+ return $p;
}
- $ss = sprintf ("%02X", $ss);
+ }
- if (!$i) {
- $mac .= "$ss";
- } else {
- $mac .= ":$ss";
+ die "unable to find free migration port";
+}
+
+sub vm_devices_list {
+ my ($vmid) = @_;
+
+ my $res = vm_monitor_command ($vmid, "info pci");
+
+ my @lines = split ("\n", $res);
+ my $devices;
+ my $bus;
+ my $addr;
+ my $id;
+
+ foreach my $line (@lines) {
+ $line =~ s/^\s+//;
+ if ($line =~ m/^Bus (\d+), device (\d+), function (\d+):$/) {
+ $bus=$1;
+ $addr=$2;
+ }
+ if ($line =~ m/^id "([a-z][a-z_\-]*\d*)"$/) {
+ $id=$1;
+ $devices->{$id}->{bus}=$bus;
+ $devices->{$id}->{addr}=$addr;
}
}
+
+ return $devices;
+}
+
+sub vm_deviceadd {
+ my ($storecfg, $conf, $vmid, $deviceid, $device) = @_;
+ return if !check_running($vmid) || !$conf->{hotplug} || $conf->{$deviceid};
- return $mac;
+ if($deviceid =~ m/^(virtio)(\d+)$/) {
+
+ my $drive = print_drive_full($storecfg, $vmid, $device);
+ my $ret = vm_monitor_command($vmid, "drive_add auto $drive");
+ # If the command succeeds qemu prints: "OK"
+ if ($ret !~ m/OK/s) {
+ die "adding drive failed: $ret";
+ }
+
+ my $devicefull = print_drivedevice_full($storecfg, $vmid, $device);
+ $ret = vm_monitor_command($vmid, "device_add $devicefull");
+ $ret =~ s/^\s+//;
+ # Otherwise, if the command succeeds, no output is sent. So any non-empty string shows an error
+ die 'error on hotplug device : $ret' if $ret ne "";
+ }
+
+ for (my $i = 0; $i <= 5; $i++) {
+ my $devices_list = vm_devices_list($vmid);
+ return if defined($devices_list->{$deviceid});
+ sleep 1;
+ }
+
+ die "error on hotplug device $deviceid";
}
-sub next_migrate_port {
+sub vm_devicedel {
+ my ($vmid, $conf, $deviceid) = @_;
- for (my $p = 60000; $p < 60010; $p++) {
+ return if !check_running ($vmid) || !$conf->{hotplug};
- my $sock = IO::Socket::INET->new (Listen => 5,
- LocalAddr => 'localhost',
- LocalPort => $p,
- ReuseAddr => 1,
- Proto => 0);
+ die "can't unplug bootdisk" if $conf->{bootdisk} eq $deviceid;
+
+ if($deviceid =~ m/^(virtio)(\d+)$/){
+
+ my $ret = vm_monitor_command($vmid, "drive_del drive-$deviceid");
+ $ret =~ s/^\s+//;
+ if ($ret =~ m/Device \'.*?\' not found/s) {
+ # NB: device not found errors mean the drive was auto-deleted and we ignore the error
+ }
+ elsif ($ret ne "") {
+ die "deleting drive $deviceid failed : $ret";
+ }
+
+ $ret = vm_monitor_command($vmid, "device_del $deviceid");
+ $ret =~ s/^\s+//;
+ die 'detaching device $deviceid failed : $ret' if $ret ne "";
- if ($sock) {
- close ($sock);
- return $p;
- }
}
- die "unable to find free migration port";
+ #need to verify the device is correctly remove as device_del is async and empty return is not reliable
+ for (my $i = 0; $i <= 5; $i++) {
+ my $devices_list = vm_devices_list($vmid);
+ return if !defined($devices_list->{$deviceid});
+ sleep 1;
+ }
+ die "error on hot-plugging device $deviceid";
+
+
}
sub vm_start {
my ($storecfg, $vmid, $statefile, $skiplock) = @_;
- lock_config ($vmid, sub {
- my $conf = load_config ($vmid);
+ lock_config($vmid, sub {
+ my $conf = load_config($vmid);
- check_lock ($conf) if !$skiplock;
+ check_lock($conf) if !$skiplock;
- if (check_running ($vmid)) {
- my $msg = "VM $vmid already running - start failed\n" ;
- syslog ('err', $msg);
- die $msg;
- } else {
- syslog ('info', "VM $vmid start");
- }
+ die "VM $vmid already running\n" if check_running($vmid);
my $migrate_uri;
my $migrate_port = 0;
my $defaults = load_defaults();
- my ($cmd, $vollist) = config_to_command ($storecfg, $vmid, $conf, $defaults, $migrate_uri);
+ my ($cmd, $vollist) = config_to_command($storecfg, $vmid, $conf, $defaults, $migrate_uri);
# host pci devices
for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
my $d = parse_hostpci($conf->{"hostpci$i"});
PVE::Storage::activate_volumes($storecfg, $vollist);
- eval { run_command ($cmd, timeout => $migrate_uri ? undef : 30); };
-
+ eval { run_command($cmd, timeout => $migrate_uri ? undef : 30); };
my $err = $@;
-
- if ($err) {
- my $msg = "start failed: $err";
- syslog ('err', "VM $vmid $msg");
- die $msg;
- }
+ die "start failed: $err" if $err;
if ($statefile) {
} else {
unlink $statefile;
# fixme: send resume - is that necessary ?
- eval { vm_monitor_command ($vmid, "cont", 1) };
+ eval { vm_monitor_command($vmid, "cont"); };
}
}
-
- if (my $migrate_speed =
- $conf->{migrate_speed} || $defaults->{migrate_speed}) {
+
+ # always set migrate speed (overwrite kvm default of 32m)
+ # we set a very hight default of 8192m which is basically unlimited
+ my $migrate_speed = $defaults->{migrate_speed} || 8192;
+ $migrate_speed = $conf->{migrate_speed} || $migrate_speed;
+ eval {
my $cmd = "migrate_set_speed ${migrate_speed}m";
- eval { vm_monitor_command ($vmid, $cmd, 1); };
- }
+ vm_monitor_command($vmid, $cmd);
+ };
- if (my $migrate_downtime =
+ if (my $migrate_downtime =
$conf->{migrate_downtime} || $defaults->{migrate_downtime}) {
my $cmd = "migrate_set_downtime ${migrate_downtime}";
- eval { vm_monitor_command ($vmid, $cmd, 1); };
+ eval { vm_monitor_command($vmid, $cmd); };
}
+
+ vm_balloonset($vmid, $conf->{balloon}) if $conf->{balloon};
});
}
my ($fh, $timeout) = @_;
my $sel = new IO::Select;
- $sel->add ($fh);
+ $sel->add($fh);
my $res = '';
my $buf;
my @ready;
- while (scalar (@ready = $sel->can_read ($timeout))) {
+ while (scalar (@ready = $sel->can_read($timeout))) {
my $count;
- if ($count = $fh->sysread ($buf, 8192)) {
+ if ($count = $fh->sysread($buf, 8192)) {
if ($buf =~ /^(.*)\(qemu\) $/s) {
$res .= $1;
last;
$res .= $buf;
}
} else {
- if (!defined ($count)) {
+ if (!defined($count)) {
die "$!\n";
}
last;
}
}
- die "monitor read timeout\n" if !scalar (@ready);
+ die "monitor read timeout\n" if !scalar(@ready);
return $res;
}
sub vm_monitor_command {
- my ($vmid, $cmdstr, $nolog) = @_;
+ my ($vmid, $cmdstr, $nocheck) = @_;
my $res;
- syslog ("info", "VM $vmid monitor command '$cmdstr'") if !$nolog;
-
eval {
- die "VM not running\n" if !check_running ($vmid);
+ die "VM $vmid not running\n" if !check_running($vmid, $nocheck);
- my $sname = monitor_socket ($vmid);
+ my $sname = monitor_socket($vmid);
- my $sock = IO::Socket::UNIX->new ( Peer => $sname ) ||
+ my $sock = IO::Socket::UNIX->new( Peer => $sname ) ||
die "unable to connect to VM $vmid socket - $!\n";
my $timeout = 3;
- # hack: migrate sometime blocks the monitor (when migrate_downtime
+ # hack: migrate sometime blocks the monitor (when migrate_downtime
# is set)
if ($cmdstr =~ m/^(info\s+migrate|migrate\s)/) {
$timeout = 60*60; # 1 hour
}
# read banner;
- my $data = __read_avail ($sock, $timeout);
-
+ my $data = __read_avail($sock, $timeout);
+
if ($data !~ m/^QEMU\s+(\S+)\s+monitor\s/) {
die "got unexpected qemu monitor banner\n";
}
my $sel = new IO::Select;
- $sel->add ($sock);
+ $sel->add($sock);
- if (!scalar (my @ready = $sel->can_write ($timeout))) {
+ if (!scalar(my @ready = $sel->can_write($timeout))) {
die "monitor write error - timeout";
}
my $fullcmd = "$cmdstr\r";
my $b;
- if (!($b = $sock->syswrite ($fullcmd)) || ($b != length ($fullcmd))) {
+ if (!($b = $sock->syswrite($fullcmd)) || ($b != length($fullcmd))) {
die "monitor write error - $!";
}
return if ($cmdstr eq 'q') || ($cmdstr eq 'quit');
- $timeout = 20;
+ $timeout = 20;
if ($cmdstr =~ m/^(info\s+migrate|migrate\s)/) {
$timeout = 60*60; # 1 hour
} elsif ($cmdstr =~ m/^(eject|change)/) {
$timeout = 60; # note: cdrom mount command is slow
}
- if ($res = __read_avail ($sock, $timeout)) {
-
- my @lines = split ("\r?\n", $res);
+ 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 = join("\n", @lines);
$res .= "\n";
}
};
my $err = $@;
if ($err) {
- syslog ("err", "VM $vmid monitor command failed - $err");
+ syslog("err", "VM $vmid monitor command failed - $err");
die $err;
}
sub vm_commandline {
my ($storecfg, $vmid) = @_;
- my $conf = load_config ($vmid);
+ my $conf = load_config($vmid);
my $defaults = load_defaults();
- my $cmd = config_to_command ($storecfg, $vmid, $conf, $defaults);
+ my $cmd = config_to_command($storecfg, $vmid, $conf, $defaults);
- return join (' ', @$cmd);
+ return join(' ', @$cmd);
}
sub vm_reset {
my ($vmid, $skiplock) = @_;
- lock_config ($vmid, sub {
+ lock_config($vmid, sub {
- my $conf = load_config ($vmid);
+ my $conf = load_config($vmid);
- check_lock ($conf) if !$skiplock;
+ check_lock($conf) if !$skiplock;
+
+ vm_monitor_command($vmid, "system_reset");
+ });
+}
+
+sub get_vm_volumes {
+ my ($conf) = @_;
+
+ my $vollist = [];
+ foreach_drive($conf, sub {
+ my ($ds, $drive) = @_;
- syslog ("info", "VM $vmid sending 'reset'");
+ my ($sid, $volname) = PVE::Storage::parse_volume_id($drive->{file}, 1);
+ return if !$sid;
- vm_monitor_command ($vmid, "system_reset", 1);
+ my $volid = $drive->{file};
+ return if !$volid || $volid =~ m|^/|;
+
+ push @$vollist, $volid;
});
+
+ return $vollist;
+}
+
+sub vm_stop_cleanup {
+ my ($storecfg, $vmid, $conf) = @_;
+
+ fairsched_rmnod($vmid); # try to destroy group
+
+ my $vollist = get_vm_volumes($conf);
+ PVE::Storage::deactivate_volumes($storecfg, $vollist);
}
sub vm_shutdown {
- my ($vmid, $skiplock) = @_;
+ my ($storecfg, $vmid, $skiplock, $timeout) = @_;
- lock_config ($vmid, sub {
+ $timeout = 60 if !$timeout;
- my $conf = load_config ($vmid);
+ lock_config($vmid, sub {
- check_lock ($conf) if !$skiplock;
+ my $conf = load_config($vmid);
- syslog ("info", "VM $vmid sending 'shutdown'");
+ check_lock($conf) if !$skiplock;
- vm_monitor_command ($vmid, "system_powerdown", 1);
- });
-}
+ vm_monitor_command($vmid, "system_powerdown");
-sub vm_stop {
- my ($vmid, $skiplock) = @_;
+ my $pid = check_running($vmid);
- lock_config ($vmid, sub {
+ if ($pid && $timeout) {
+ print "waiting until VM $vmid stopps (PID $pid)\n";
- my $pid = check_running ($vmid);
+ my $count = 0;
+ while (($count < $timeout) && check_running($vmid)) {
+ $count++;
+ sleep 1;
+ }
- if (!$pid) {
- syslog ('info', "VM $vmid already stopped");
- return;
+ die "shutdown failed - got timeout\n" if check_running($vmid);
}
- my $conf = load_config ($vmid);
+ vm_stop_cleanup($storecfg, $vmid, $conf);
+ });
+}
- check_lock ($conf) if !$skiplock;
-
- syslog ("info", "VM $vmid stopping");
+# Note: use $nockeck to skip tests if VM configuration file exists.
+# We need that when migration VMs to other nodes (files already moved)
+sub vm_stop {
+ my ($storecfg, $vmid, $skiplock, $nocheck, $timeout) = @_;
- eval { vm_monitor_command ($vmid, "quit", 1); };
+ $timeout = 60 if !$timeout;
+ lock_config($vmid, sub {
+
+ my $pid = check_running($vmid, $nocheck);
+ return if !$pid;
+
+ my $conf;
+ if (!$nocheck) {
+ $conf = load_config($vmid);
+ check_lock($conf) if !$skiplock;
+ }
+
+ eval { vm_monitor_command($vmid, "quit", $nocheck); };
my $err = $@;
if (!$err) {
- # wait some time
- my $timeout = 50; # fixme: how long?
-
my $count = 0;
- while (($count < $timeout) && check_running ($vmid)) {
+ while (($count < $timeout) && check_running($vmid, $nocheck)) {
$count++;
sleep 1;
}
if ($count >= $timeout) {
- syslog ('info', "VM $vmid still running - terminating now with SIGTERM");
+ warn "VM still running - terminating now with SIGTERM\n";
kill 15, $pid;
}
} else {
- syslog ('info', "VM $vmid quit failed - terminating now with SIGTERM");
+ warn "VM quit failed - terminating now with SIGTERM\n";
kill 15, $pid;
}
# wait again
- my $timeout = 10;
+ $timeout = 10;
my $count = 0;
- while (($count < $timeout) && check_running ($vmid)) {
+ while (($count < $timeout) && check_running($vmid, $nocheck)) {
$count++;
sleep 1;
}
if ($count >= $timeout) {
- syslog ('info', "VM $vmid still running - terminating now with SIGKILL\n");
+ warn "VM still running - terminating now with SIGKILL\n";
kill 9, $pid;
+ sleep 1;
}
- fairsched_rmnod ($vmid); # try to destroy group
- });
+ vm_stop_cleanup($storecfg, $vmid, $conf) if $conf;
+ });
}
sub vm_suspend {
my ($vmid, $skiplock) = @_;
- lock_config ($vmid, sub {
-
- my $conf = load_config ($vmid);
+ lock_config($vmid, sub {
- check_lock ($conf) if !$skiplock;
+ my $conf = load_config($vmid);
- syslog ("info", "VM $vmid suspend");
+ check_lock($conf) if !$skiplock;
- vm_monitor_command ($vmid, "stop", 1);
+ vm_monitor_command($vmid, "stop");
});
}
sub vm_resume {
my ($vmid, $skiplock) = @_;
- lock_config ($vmid, sub {
-
- my $conf = load_config ($vmid);
+ lock_config($vmid, sub {
- check_lock ($conf) if !$skiplock;
+ my $conf = load_config($vmid);
- syslog ("info", "VM $vmid resume");
+ check_lock($conf) if !$skiplock;
- vm_monitor_command ($vmid, "cont", 1);
+ vm_monitor_command($vmid, "cont");
});
}
-sub vm_cad {
- my ($vmid, $skiplock) = @_;
+sub vm_sendkey {
+ my ($vmid, $skiplock, $key) = @_;
- lock_config ($vmid, sub {
+ lock_config($vmid, sub {
- my $conf = load_config ($vmid);
+ my $conf = load_config($vmid);
- check_lock ($conf) if !$skiplock;
+ check_lock($conf) if !$skiplock;
- syslog ("info", "VM $vmid sending cntl-alt-delete");
-
- vm_monitor_command ($vmid, "sendkey ctrl-alt-delete", 1);
+ vm_monitor_command($vmid, "sendkey $key");
});
}
sub vm_destroy {
my ($storecfg, $vmid, $skiplock) = @_;
- lock_config ($vmid, sub {
-
- my $conf = load_config ($vmid);
+ lock_config($vmid, sub {
- check_lock ($conf) if !$skiplock;
+ my $conf = load_config($vmid);
- syslog ("info", "VM $vmid destroy called (removing all data)");
+ check_lock($conf) if !$skiplock;
- eval {
- if (!check_running($vmid)) {
- fairsched_rmnod($vmid); # try to destroy group
- destroy_vm($storecfg, $vmid);
- } else {
- die "VM is running\n";
- }
- };
-
- my $err = $@;
-
- if ($err) {
- syslog ("err", "VM $vmid destroy failed - $err");
- die $err;
+ if (!check_running($vmid)) {
+ fairsched_rmnod($vmid); # try to destroy group
+ destroy_vm($storecfg, $vmid);
+ } else {
+ die "VM $vmid is running - destroy failed\n";
}
});
}
sub vm_stopall {
- my ($timeout) = @_;
+ my ($storecfg, $timeout) = @_;
$timeout = 3*60 if !$timeout;
+ my $cleanuphash = {};
+
my $vzlist = vzlist();
my $count = 0;
foreach my $vmid (keys %$vzlist) {
next if !$vzlist->{$vmid}->{pid};
$count++;
+ $cleanuphash->{$vmid} = 1;
}
- if ($count) {
+ return if !$count;
- my $msg = "Stopping Qemu Server - sending shutdown requests to all VMs\n";
- syslog ('info', $msg);
- print STDERR $msg;
+ my $msg = "Stopping Qemu Server - sending shutdown requests to all VMs\n";
+ syslog('info', $msg);
+ warn $msg;
- foreach my $vmid (keys %$vzlist) {
- next if !$vzlist->{$vmid}->{pid};
- eval { vm_shutdown ($vmid, 1); };
- print STDERR $@ if $@;
+ foreach my $vmid (keys %$vzlist) {
+ next if !$vzlist->{$vmid}->{pid};
+ eval { vm_shutdown($storecfg, $vmid, 1); };
+ my $err = $@;
+ if ($err) {
+ warn $err;
+ } else {
+ delete $cleanuphash->{$vmid};
}
+ }
- my $wt = 5;
- my $maxtries = int (($timeout + $wt -1)/$wt);
- my $try = 0;
- while (($try < $maxtries) && $count) {
- $try++;
- sleep $wt;
-
- $vzlist = vzlist();
- $count = 0;
- foreach my $vmid (keys %$vzlist) {
- next if !$vzlist->{$vmid}->{pid};
- $count++;
- }
- last if !$count;
+ my $wt = 5;
+ my $maxtries = int(($timeout + $wt -1)/$wt);
+ my $try = 0;
+ while (($try < $maxtries) && $count) {
+ $try++;
+ sleep $wt;
+
+ $vzlist = vzlist();
+ $count = 0;
+ foreach my $vmid (keys %$vzlist) {
+ next if !$vzlist->{$vmid}->{pid};
+ $count++;
}
+ last if !$count;
+ }
- return if !$count;
+ if ($count) {
foreach my $vmid (keys %$vzlist) {
next if !$vzlist->{$vmid}->{pid};
-
- $msg = "VM $vmid still running - sending stop now\n";
- syslog ('info', $msg);
- print $msg;
-
- eval { vm_monitor_command ($vmid, "quit", 1); };
- print STDERR $@ if $@;
+ warn "VM $vmid still running - sending stop now\n";
+ eval { vm_monitor_command($vmid, "quit"); };
+ warn $@ if $@;
}
$timeout = 30;
- $maxtries = int (($timeout + $wt -1)/$wt);
+ $maxtries = int(($timeout + $wt -1)/$wt);
$try = 0;
while (($try < $maxtries) && $count) {
$try++;
sleep $wt;
-
+
$vzlist = vzlist();
$count = 0;
foreach my $vmid (keys %$vzlist) {
last if !$count;
}
- return if !$count;
+ if ($count) {
- foreach my $vmid (keys %$vzlist) {
- next if !$vzlist->{$vmid}->{pid};
-
- $msg = "VM $vmid still running - terminating now with SIGTERM\n";
- syslog ('info', $msg);
- print $msg;
- kill 15, $vzlist->{$vmid}->{pid};
+ foreach my $vmid (keys %$vzlist) {
+ next if !$vzlist->{$vmid}->{pid};
+
+ warn "VM $vmid still running - terminating now with SIGTERM\n";
+ kill 15, $vzlist->{$vmid}->{pid};
+ }
+ sleep 1;
}
# this is called by system shotdown scripts, so remaining
# processes gets killed anyways (no need to send kill -9 here)
+ }
- $msg = "Qemu Server stopped\n";
- syslog ('info', $msg);
- print STDERR $msg;
+ $vzlist = vzlist();
+ foreach my $vmid (keys %$cleanuphash) {
+ next if $vzlist->{$vmid}->{pid};
+ eval {
+ my $conf = load_config($vmid);
+ vm_stop_cleanup($storecfg, $vmid, $conf);
+ };
+ warn $@ if $@;
}
+
+ $msg = "Qemu Server stopped\n";
+ syslog('info', $msg);
+ print $msg;
}
# pci helpers
sub file_write {
my ($filename, $buf) = @_;
- my $fh = IO::File->new ($filename, "w");
+ my $fh = IO::File->new($filename, "w");
return undef if !$fh;
my $res = print $fh $buf;
my $fn = "$pcisysfs/devices/$name/reset";
- return file_write ($fn, "1");
+ return file_write($fn, "1");
}
sub pci_dev_bind_to_stub {
return 1 if -d $testdir;
my $data = "$dev->{vendor} $dev->{product}";
- return undef if !file_write ("$pcisysfs/drivers/pci-stub/new_id", $data);
+ return undef if !file_write("$pcisysfs/drivers/pci-stub/new_id", $data);
my $fn = "$pcisysfs/devices/$name/driver/unbind";
- if (!file_write ($fn, $name)) {
+ if (!file_write($fn, $name)) {
return undef if -f $fn;
}
$fn = "$pcisysfs/drivers/pci-stub/bind";
if (! -d $testdir) {
- return undef if !file_write ($fn, $name);
+ return undef if !file_write($fn, $name);
}
return -d $testdir;
}
+sub print_pci_addr {
+ my ($id) = @_;
+
+ my $res = '';
+ my $devices = {
+ #addr1 : ide,parallel,serial (motherboard)
+ #addr2 : first videocard
+ balloon0 => { bus => 0, addr => 3 },
+ watchdog => { bus => 0, addr => 4 },
+ scsi0 => { bus => 0, addr => 5 },
+ scsi1 => { bus => 0, addr => 6 },
+ virtio0 => { bus => 0, addr => 10 },
+ virtio1 => { bus => 0, addr => 11 },
+ virtio2 => { bus => 0, addr => 12 },
+ virtio3 => { bus => 0, addr => 13 },
+ virtio4 => { bus => 0, addr => 14 },
+ virtio5 => { bus => 0, addr => 15 },
+ hostpci0 => { bus => 0, addr => 16 },
+ hostpci1 => { bus => 0, addr => 17 },
+ net0 => { bus => 0, addr => 18 },
+ net1 => { bus => 0, addr => 19 },
+ net2 => { bus => 0, addr => 20 },
+ net3 => { bus => 0, addr => 21 },
+ net4 => { bus => 0, addr => 22 },
+ net5 => { bus => 0, addr => 23 },
+ #addr29 : usb-host (pve-usb.cfg)
+ };
+
+ if (defined($devices->{$id}->{bus}) && defined($devices->{$id}->{addr})) {
+ my $addr = sprintf("0x%x", $devices->{$id}->{addr});
+ $res = ",bus=pci.$devices->{$id}->{bus},addr=$addr";
+ }
+ return $res;
+
+}
+
+sub vm_balloonset {
+ my ($vmid, $value) = @_;
+
+ vm_monitor_command($vmid, "balloon $value");
+}
+
+# vzdump restore implementaion
+
+sub archive_read_firstfile {
+ my $archive = shift;
+
+ die "ERROR: file '$archive' does not exist\n" if ! -f $archive;
+
+ # try to detect archive type first
+ my $pid = open (TMP, "tar tf '$archive'|") ||
+ die "unable to open file '$archive'\n";
+ my $firstfile = <TMP>;
+ kill 15, $pid;
+ close TMP;
+
+ die "ERROR: archive contaions no data\n" if !$firstfile;
+ chomp $firstfile;
+
+ return $firstfile;
+}
+
+sub restore_cleanup {
+ my $statfile = shift;
+
+ print STDERR "starting cleanup\n";
+
+ if (my $fd = IO::File->new($statfile, "r")) {
+ while (defined(my $line = <$fd>)) {
+ if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
+ my $volid = $2;
+ eval {
+ if ($volid =~ m|^/|) {
+ unlink $volid || die 'unlink failed\n';
+ } else {
+ my $cfg = cfs_read_file('storage.cfg');
+ PVE::Storage::vdisk_free($cfg, $volid);
+ }
+ print STDERR "temporary volume '$volid' sucessfuly removed\n";
+ };
+ print STDERR "unable to cleanup '$volid' - $@" if $@;
+ } else {
+ print STDERR "unable to parse line in statfile - $line";
+ }
+ }
+ $fd->close();
+ }
+}
+
+sub restore_archive {
+ my ($archive, $vmid, $opts) = @_;
+
+ if ($archive ne '-') {
+ my $firstfile = archive_read_firstfile($archive);
+ die "ERROR: file '$archive' dos not lock like a QemuServer vzdump backup\n"
+ if $firstfile ne 'qemu-server.conf';
+ }
+
+ my $tocmd = "/usr/lib/qemu-server/qmextract";
+
+ $tocmd .= " --storage " . PVE::Tools::shellquote($opts->{storage}) if $opts->{storage};
+ $tocmd .= ' --prealloc' if $opts->{prealloc};
+ $tocmd .= ' --info' if $opts->{info};
+
+ # tar option "xf" does not autodetect compression when read fron STDIN,
+ # so we pipe to zcat
+ my $cmd = "zcat -f|tar xf " . PVE::Tools::shellquote($archive) . " " .
+ PVE::Tools::shellquote("--to-command=$tocmd");
+
+ my $tmpdir = "/var/tmp/vzdumptmp$$";
+ mkpath $tmpdir;
+
+ local $ENV{VZDUMP_TMPDIR} = $tmpdir;
+ local $ENV{VZDUMP_VMID} = $vmid;
+
+ my $conffile = PVE::QemuServer::config_file($vmid);
+ my $tmpfn = "$conffile.$$.tmp";
+
+ # disable interrupts (always do cleanups)
+ local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = sub {
+ print STDERR "got interrupt - ignored\n";
+ };
+
+ eval {
+ # enable interrupts
+ local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub {
+ die "interrupted by signal\n";
+ };
+
+ if ($archive eq '-') {
+ print "extracting archive from STDIN\n";
+ run_command($cmd, input => "<&STDIN");
+ } else {
+ print "extracting archive '$archive'\n";
+ run_command($cmd);
+ }
+
+ return if $opts->{info};
+
+ # read new mapping
+ my $map = {};
+ my $statfile = "$tmpdir/qmrestore.stat";
+ if (my $fd = IO::File->new($statfile, "r")) {
+ while (defined (my $line = <$fd>)) {
+ if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
+ $map->{$1} = $2 if $1;
+ } else {
+ print STDERR "unable to parse line in statfile - $line\n";
+ }
+ }
+ $fd->close();
+ }
+
+ my $confsrc = "$tmpdir/qemu-server.conf";
+
+ my $srcfd = new IO::File($confsrc, "r") ||
+ die "unable to open file '$confsrc'\n";
+
+ my $outfd = new IO::File ($tmpfn, "w") ||
+ die "unable to write config for VM $vmid\n";
+
+ my $netcount = 0;
+
+ while (defined (my $line = <$srcfd>)) {
+ next if $line =~ m/^\#vzdump\#/;
+ next if $line =~ m/^lock:/;
+ next if $line =~ m/^unused\d+:/;
+
+ if (($line =~ m/^(vlan(\d+)):\s*(\S+)\s*$/)) {
+ # try to convert old 1.X settings
+ my ($id, $ind, $ethcfg) = ($1, $2, $3);
+ foreach my $devconfig (PVE::Tools::split_list($ethcfg)) {
+ my ($model, $macaddr) = split(/\=/, $devconfig);
+ $macaddr = PVE::Tools::random_ether_addr() if !$macaddr || $opts->{unique};
+ my $net = {
+ model => $model,
+ bridge => "vmbr$ind",
+ macaddr => $macaddr,
+ };
+ my $netstr = print_net($net);
+ print $outfd "net${netcount}: $netstr\n";
+ $netcount++;
+ }
+ } elsif (($line =~ m/^(net\d+):\s*(\S+)\s*$/) && ($opts->{unique})) {
+ my ($id, $netstr) = ($1, $2);
+ my $net = parse_net($netstr);
+ $net->{macaddr} = PVE::Tools::random_ether_addr() if $net->{macaddr};
+ $netstr = print_net($net);
+ print $outfd "$id: $netstr\n";
+ } elsif ($line =~ m/^((ide|scsi|virtio)\d+):\s*(\S+)\s*$/) {
+ my $virtdev = $1;
+ my $value = $2;
+ if ($line =~ m/backup=no/) {
+ print $outfd "#$line";
+ } elsif ($virtdev && $map->{$virtdev}) {
+ my $di = PVE::QemuServer::parse_drive($virtdev, $value);
+ $di->{file} = $map->{$virtdev};
+ $value = PVE::QemuServer::print_drive($vmid, $di);
+ print $outfd "$virtdev: $value\n";
+ } else {
+ print $outfd $line;
+ }
+ } else {
+ print $outfd $line;
+ }
+ }
+
+ $srcfd->close();
+ $outfd->close();
+ };
+ my $err = $@;
+
+ if ($err) {
+
+ unlink $tmpfn;
+
+ restore_cleanup("$tmpdir/qmrestore.stat") if !$opts->{info};
+
+ die $err;
+ }
+
+ rmtree $tmpdir;
+
+ rename $tmpfn, $conffile ||
+ die "unable to commit configuration file '$conffile'\n";
+};
+
1;