]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer.pm
set template flag earlier
[qemu-server.git] / PVE / QemuServer.pm
index a7ffb8a19c51e8138359062d0ea26f53a45a0c5e..194b678d12f1edfa5ceb5f64a319c3b6b57889d7 100644 (file)
@@ -166,7 +166,7 @@ my $confdesc = {
     hotplug => {
         optional => 1,
         type => 'boolean',
-        description => "Activate hotplug for disk and network device",
+        description => "Allow hotplug for disk and network device",
         default => 0,
     },
     reboot => {
@@ -358,6 +358,12 @@ EODESC
        typetext => '[[order=]\d+] [,up=\d+] [,down=\d+] ',
        description => "Startup and shutdown behavior. Order is a non-negative number defining the general startup order. Shutdown in done with reverse ordering. Additionally you can set the 'up' or 'down' delay in seconds, which specifies a delay to wait before the next VM is started or stopped.",
     },
+    template => {
+       optional => 1,
+       type => 'boolean',
+       description => "Enable/disable Template.",
+       default => 0,
+    },
     args => {
        optional => 1,
        type => 'string',
@@ -398,7 +404,7 @@ EODESCR
        description => "Emulated CPU type.",
        type => 'string',
        enum => [ qw(486 athlon pentium pentium2 pentium3 coreduo core2duo kvm32 kvm64 qemu32 qemu64 phenom Conroe Penryn Nehalem Westmere SandyBridge Haswell Opteron_G1 Opteron_G2 Opteron_G3 Opteron_G4 Opteron_G5 host) ],
-       default => 'qemu64',
+       default => 'kvm64',
     },
     parent => get_standard_option('pve-snapshot-name', {
        optional => 1,
@@ -1104,11 +1110,10 @@ sub print_drive_full {
        } else {
            $path = PVE::Storage::path($storecfg, $volid);
        }
-       if (!$drive->{cache} && ($path =~ m|^/dev/| || $path =~ m|\.raw$|)) {
-           $opts .= ",cache=none";
-       }
     }
 
+    $opts .= ",cache=none" if !$drive->{cache} && !drive_is_cdrom($drive);
+
     my $pathinfo = $path ? "file=$path," : '';
 
     return "${pathinfo}if=none,id=drive-$drive->{interface}$drive->{index}$opts";
@@ -1194,7 +1199,7 @@ sub parse_net {
 
        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) || PVE::Tools::random_ether_addr();
+           my $mac = defined($3) ? uc($3) : PVE::Tools::random_ether_addr();
            $res->{model} = $model;
            $res->{macaddr} = $mac;
        } elsif ($kvp =~ m/^bridge=(\S+)$/) {
@@ -1836,7 +1841,7 @@ sub check_cmdline {
        my @param = split(/\0/, $line);
 
        my $cmd = $param[0];
-       return if !$cmd || ($cmd !~ m|kvm$|);
+       return if !$cmd || ($cmd !~ m|kvm$| && $cmd !~ m|qemu-system-x86_64$|);
 
        for (my $i = 0; $i < scalar (@param); $i++) {
            my $p = $param[$i];
@@ -1981,6 +1986,8 @@ sub vmstatus {
        $d->{diskread} = 0;
        $d->{diskwrite} = 0;
 
+        $d->{template} = is_template($conf);
+
        $res->{$vmid} = $d;
     }
 
@@ -2193,6 +2200,9 @@ sub config_to_command {
 
     push @$cmd, '-daemonize';
 
+    $pciaddr = print_pci_addr("piix3", $bridges);
+    push @$devices, '-device', "piix3-usb-uhci,id=uhci$pciaddr.0x2";
+
     my $use_usb2 = 0;
     for (my $i = 0; $i < $MAX_USB_DEVICES; $i++)  {
        next if !$conf->{"usb$i"};
@@ -2203,13 +2213,7 @@ sub config_to_command {
 
     # enable absolute mouse coordinates (needed by vnc)
     my $tablet = defined($conf->{tablet}) ? $conf->{tablet} : $defaults->{tablet};
-    if ($tablet) {
-       if ($use_usb2) {
-           push @$devices, '-device', 'usb-tablet,bus=ehci.0,port=6';
-       } else {
-           push @$devices, '-usbdevice', 'tablet';
-       }
-    }
+    push @$devices, '-device', 'usb-tablet,id=tablet,bus=uhci.0,port=1' if $tablet;
 
     # host pci devices
     for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++)  {
@@ -2524,7 +2528,15 @@ sub vm_devices_list {
 sub vm_deviceplug {
     my ($storecfg, $conf, $vmid, $deviceid, $device) = @_;
 
-    return 1 if !check_running($vmid) || !$conf->{hotplug};
+    return 1 if !check_running($vmid);
+
+    if ($deviceid eq 'tablet') {
+       my $devicefull = "usb-tablet,id=tablet,bus=uhci.0,port=1";
+       qemu_deviceadd($vmid, $devicefull);
+       return 1;
+    }
+
+    return 1 if !$conf->{hotplug};
 
     my $devices_list = vm_devices_list($vmid);
     return 1 if defined($devices_list->{$deviceid});
@@ -2584,7 +2596,14 @@ sub vm_deviceplug {
 sub vm_deviceunplug {
     my ($vmid, $conf, $deviceid) = @_;
 
-    return 1 if !check_running ($vmid) || !$conf->{hotplug};
+    return 1 if !check_running ($vmid);
+
+    if ($deviceid eq 'tablet') {
+       qemu_devicedel($vmid, $deviceid);
+       return 1;
+    }
+
+    return 1 if !$conf->{hotplug};
 
     my $devices_list = vm_devices_list($vmid);
     return 1 if !defined($devices_list->{$deviceid});
@@ -2592,9 +2611,9 @@ sub vm_deviceunplug {
     die "can't unplug bootdisk" if $conf->{bootdisk} && $conf->{bootdisk} eq $deviceid;
 
     if ($deviceid =~ m/^(virtio)(\d+)$/) {
-        return undef if !qemu_drivedel($vmid, $deviceid);
         qemu_devicedel($vmid, $deviceid);
         return undef if !qemu_devicedelverify($vmid, $deviceid);
+        return undef if !qemu_drivedel($vmid, $deviceid);
     }
 
     if ($deviceid =~ m/^(lsi)(\d+)$/) {
@@ -2607,9 +2626,9 @@ sub vm_deviceunplug {
     }
 
     if ($deviceid =~ m/^(net)(\d+)$/) {
-        return undef if !qemu_netdevdel($vmid, $deviceid);
         qemu_devicedel($vmid, $deviceid);
         return undef if !qemu_devicedelverify($vmid, $deviceid);
+        return undef if !qemu_netdevdel($vmid, $deviceid);
     }
 
     return 1;
@@ -2618,23 +2637,17 @@ sub vm_deviceunplug {
 sub qemu_deviceadd {
     my ($vmid, $devicefull) = @_;
 
-    my $ret = vm_human_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
-    return 1 if $ret eq "";
-    syslog("err", "error on hotplug device : $ret");
-    return undef;
+    $devicefull = "driver=".$devicefull;
+    my %options =  split(/[=,]/, $devicefull);
 
+    vm_mon_cmd($vmid, "device_add" , %options);
+    return 1;
 }
 
 sub qemu_devicedel {
     my($vmid, $deviceid) = @_;
-
-    my $ret = vm_human_monitor_command($vmid, "device_del $deviceid");
-    $ret =~ s/^\s+//;
-    return 1 if $ret eq "";
-    syslog("err", "detaching device $deviceid failed : $ret");
-    return undef;
+    my $ret = vm_mon_cmd($vmid, "device_del", id => $deviceid);
+    return 1;
 }
 
 sub qemu_driveadd {
@@ -2729,24 +2742,17 @@ sub qemu_netdevadd {
     my ($vmid, $conf, $device, $deviceid) = @_;
 
     my $netdev = print_netdev_full($vmid, $conf, $device, $deviceid);
-    my $ret = vm_human_monitor_command($vmid, "netdev_add $netdev");
-    $ret =~ s/^\s+//;
+    my %options =  split(/[=,]/, $netdev);
 
-    #if the command succeeds, no output is sent. So any non-empty string shows an error
-    return 1 if $ret eq "";
-    syslog("err", "adding netdev failed: $ret");
-    return undef;
+    vm_mon_cmd($vmid, "netdev_add",  %options);
+    return 1;
 }
 
 sub qemu_netdevdel {
     my ($vmid, $deviceid) = @_;
 
-    my $ret = vm_human_monitor_command($vmid, "netdev_del $deviceid");
-    $ret =~ s/^\s+//;
-    #if the command succeeds, no output is sent. So any non-empty string shows an error
-    return 1 if $ret eq "";
-    syslog("err", "deleting netdev failed: $ret");
-    return undef;
+    vm_mon_cmd($vmid, "netdev_del", id => $deviceid);
+    return 1;
 }
 
 sub qemu_block_set_io_throttle {
@@ -2929,6 +2935,8 @@ sub vm_start {
     lock_config($vmid, sub {
        my $conf = load_config($vmid, $migratedfrom);
 
+       die "you can't start a vm if it's a template\n" if is_template($conf);
+
        check_lock($conf) if !$skiplock;
 
        die "VM $vmid already running\n" if check_running($vmid, undef, $migratedfrom);
@@ -2993,7 +3001,7 @@ sub vm_start {
                    if $conf->{balloon};
                vm_mon_cmd_nocheck($vmid, 'qom-set', 
                            path => "machine/peripheral/balloon0", 
-                           property => "stats-polling-interval", 
+                           property => "guest-stats-polling-interval", 
                            value => 2);
            }
        }
@@ -3218,7 +3226,7 @@ sub vm_suspend {
 
        my $conf = load_config($vmid);
 
-       check_lock($conf) if !$skiplock;
+       check_lock($conf) if !($skiplock || ($conf->{lock} && $conf->{lock} eq 'backup'));
 
        vm_mon_cmd($vmid, "stop");
     });
@@ -3231,7 +3239,7 @@ sub vm_resume {
 
        my $conf = load_config($vmid);
 
-       check_lock($conf) if !$skiplock;
+       check_lock($conf) if !($skiplock || ($conf->{lock} && $conf->{lock} eq 'backup'));
 
        vm_mon_cmd($vmid, "cont");
     });
@@ -3353,7 +3361,7 @@ sub print_pci_addr {
 
     my $res = '';
     my $devices = {
-       #addr1 : ide,parallel,serial (motherboard)
+       piix3 => { bus => 0, addr => 1 },
        #addr2 : first videocard
        balloon0 => { bus => 0, addr => 3 },
        watchdog => { bus => 0, addr => 4 },
@@ -3539,7 +3547,7 @@ sub restore_update_config_line {
        print $outfd "$id: $netstr\n";
     } elsif ($line =~ m/^((ide|scsi|virtio|sata)\d+):\s*(\S+)\s*$/) {
        my $virtdev = $1;
-       my $value = $2;
+       my $value = $3;
        if ($line =~ m/backup=no/) {
            print $outfd "#$line";
        } elsif ($virtdev && $map->{$virtdev}) {
@@ -3620,7 +3628,13 @@ sub rescan {
            
        check_lock($conf);
 
-       my $changes = update_disksize($vmid, $conf, $volid_hash);
+       my $vm_volids = {};
+       foreach my $volid (keys %$volid_hash) {
+           my $info = $volid_hash->{$volid};
+           $vm_volids->{$volid} = $info if $info->{vmid} && $info->{vmid} == $vmid;
+       }
+
+       my $changes = update_disksize($vmid, $conf, $vm_volids);
 
        update_config_nolock($vmid, $conf, 1) if $changes;
     };
@@ -3772,7 +3786,8 @@ sub restore_vma_archive {
 
            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;
            }
 
@@ -3874,8 +3889,10 @@ sub restore_tar_archive {
     }
 
     my $storecfg = cfs_read_file('storage.cfg');
+
     # destroy existing data - keep empty config
-    destroy_vm($storecfg, $vmid, 1);
+    my $vmcfgfn = PVE::QemuServer::config_file($vmid);
+    destroy_vm($storecfg, $vmid, 1) if -f $vmcfgfn;
 
     my $tocmd = "/usr/lib/qemu-server/qmextract";
 
@@ -4089,6 +4106,9 @@ my $snapshot_prepare = sub {
 
        my $conf = load_config($vmid);
 
+       die "you can't take a snapshot if it's a template\n" 
+           if is_template($conf);
+
        check_lock($conf);
 
        $conf->{lock} = 'snapshot';
@@ -4162,6 +4182,8 @@ sub snapshot_rollback {
 
        my $conf = load_config($vmid);
 
+       die "you can't rollback if vm is a template\n" if is_template($conf);
+
        $snap = $conf->{snapshots}->{$snapname};
 
        die "snapshot '$snapname' does not exist\n" if !defined($snap); 
@@ -4317,7 +4339,11 @@ sub snapshot_delete {
 
        my $conf = load_config($vmid);
 
-       check_lock($conf) if !$drivehash;
+       if (!$drivehash) {
+           check_lock($conf);
+           die "you can't delete a snapshot if vm is a template\n" 
+               if is_template($conf);
+       }
 
        $snap = $conf->{snapshots}->{$snapname};
 
@@ -4397,7 +4423,7 @@ sub snapshot_delete {
     lock_config($vmid, $updatefn);
 }
 
-sub has_feature{
+sub has_feature {
     my ($feature, $conf, $storecfg, $snapname, $running) = @_;
 
     my $err = undef;
@@ -4411,4 +4437,35 @@ sub has_feature{
 
     return 1 if !$err;
 }
+
+sub template_create {
+    my ($vmid, $conf, $disk) = @_;
+
+    my $running = check_running($vmid);
+    die "you can't convert a vm to template if vm is running vm\n" if $running;
+
+    my $storecfg = PVE::Storage::config();
+
+    foreach_drive($conf, sub {
+       my ($ds, $drive) = @_;
+
+       return if drive_is_cdrom($drive);
+       return if $disk && $ds ne $disk;
+
+       my $volid = $drive->{file};
+       return if !PVE::Storage::volume_has_feature($storecfg, 'template', $volid);
+
+       my $voliddst = PVE::Storage::vdisk_create_base($storecfg, $volid);
+       $drive->{file} = $voliddst;
+       $conf->{$ds} = PVE::QemuServer::print_drive($vmid, $drive);
+       PVE::QemuServer::update_config_nolock($vmid, $conf, 1);
+    });
+}
+
+sub is_template {
+    my ($conf) = @_;
+
+    return 1 if defined $conf->{template} && $conf->{template} == 1;
+}
+
 1;