]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer.pm
fix #2043: vm start: always stop existing systemd scopes
[qemu-server.git] / PVE / QemuServer.pm
index a162db95c59bc6be82b6a25a61f67938c5f873e4..6dc68a40665abe44e311f204ae39db37460b0019 100644 (file)
@@ -30,6 +30,7 @@ use PVE::ProcFSTools;
 use PVE::QemuConfig;
 use PVE::QMPClient;
 use PVE::RPCEnvironment;
+use PVE::GuestHelpers;
 use PVE::QemuServer::PCI qw(print_pci_addr print_pcie_addr);
 use PVE::QemuServer::Memory;
 use PVE::QemuServer::USB qw(parse_usb_device);
@@ -186,6 +187,13 @@ my $cpu_fmt = {
        optional => 1,
        default => 0
     },
+    'hv-vendor-id' => {
+       type => 'string',
+       pattern => qr/[a-zA-Z0-9]{1,12}/,
+       format_description => 'vendor-id',
+       description => 'The Hyper-V vendor ID. Some drivers or programs inside Windows guests need a specific ID.',
+       optional => 1,
+    },
     flags => {
        description => "List of additional CPU flags separated by ';'."
                     . " Use '+FLAG' to enable, '-FLAG' to disable a flag."
@@ -237,7 +245,7 @@ my $vga_fmt = {
        default => 'std',
        optional => 1,
        default_key => 1,
-       enum => [qw(cirrus qxl qxl2 qxl3 qxl4 serial0 serial1 serial2 serial3 std virtio vmware)],
+       enum => [qw(cirrus qxl qxl2 qxl3 qxl4 none serial0 serial1 serial2 serial3 std virtio vmware)],
     },
     memory => {
        description => "Sets the VGA memory (in MiB). Has no effect with serial display.",
@@ -277,7 +285,7 @@ my $confdesc = {
        optional => 1,
        type => 'string',
        description => "Lock/unlock the VM.",
-       enum => [qw(migrate backup snapshot rollback)],
+       enum => [qw(backup clone create migrate rollback snapshot snapshot-delete)],
     },
     cpulimit => {
        optional => 1,
@@ -607,6 +615,12 @@ EODESCR
        default => "1 (autogenerated)",
        optional => 1,
     },
+    hookscript => {
+       type => 'string',
+       format => 'pve-volume-id',
+       optional => 1,
+       description => "Script that will be executed during various steps in the vms lifetime.",
+    },
 };
 
 my $confdesc_cloudinit = {
@@ -1387,7 +1401,9 @@ sub kvm_user_version {
 
 }
 
-my $kernel_has_vhost_net = -c '/dev/vhost-net';
+sub kernel_has_vhost_net {
+    return -c '/dev/vhost-net';
+}
 
 sub valid_drive_names {
     # order is important - used to autoselect boot disk
@@ -1973,7 +1989,7 @@ sub print_netdev_full {
 
     my $vhostparam = '';
     if (is_native($arch)) {
-       $vhostparam = ',vhost=on' if $kernel_has_vhost_net && $net->{model} eq 'virtio';
+       $vhostparam = ',vhost=on' if kernel_has_vhost_net() && $net->{model} eq 'virtio';
     }
 
     my $vmname = $conf->{name} || "vm$vmid";
@@ -2023,7 +2039,7 @@ sub print_vga_device {
     my ($conf, $vga, $arch, $machine, $id, $qxlnum, $bridges) = @_;
 
     my $type = $vga_map->{$vga->{type}};
-    if ($type eq 'virtio-vga' && $arch eq 'aarch64') {
+    if ($arch eq 'aarch64' && defined($type) && $type eq 'virtio-vga') {
        $type = 'virtio-gpu';
     }
     my $vgamem_mb = $vga->{memory};
@@ -3329,11 +3345,13 @@ sub get_cpu_options {
     if ($arch eq 'aarch64') {
        $cpu = 'cortex-a57';
     }
+    my $hv_vendor_id;
     if (my $cputype = $conf->{cpu}) {
        my $cpuconf = PVE::JSONSchema::parse_property_string($cpu_fmt, $cputype)
            or die "Cannot parse cpu description: $cputype\n";
        $cpu = $cpuconf->{cputype};
        $kvm_off = 1 if $cpuconf->{hidden};
+       $hv_vendor_id = $cpuconf->{'hv-vendor-id'};
 
        if (defined(my $flags = $cpuconf->{flags})) {
            push @$cpuFlags, split(";", $flags);
@@ -3355,7 +3373,7 @@ sub get_cpu_options {
        push @$cpuFlags , '+kvm_pv_eoi' if $kvm;
     }
 
-    add_hyperv_enlightenments($cpuFlags, $winversion, $machine_type, $kvmver, $conf->{bios}, $gpu_passthrough) if $kvm;
+    add_hyperv_enlightenments($cpuFlags, $winversion, $machine_type, $kvmver, $conf->{bios}, $gpu_passthrough, $hv_vendor_id) if $kvm;
 
     push @$cpuFlags, 'enforce' if $cpu ne 'host' && $kvm && $arch eq 'x86_64';
 
@@ -3524,7 +3542,13 @@ sub config_to_command {
        my $pcie = $d->{pcie};
        if($pcie){
            die "q35 machine model is not enabled" if !$q35;
-           $pciaddr = print_pcie_addr("hostpci$i");
+           # win7 wants to have the pcie devices directly on the pcie bus
+           # instead of in the root port
+           if ($winversion == 7) {
+               $pciaddr = print_pcie_addr("hostpci${i}bus0");
+           } else {
+               $pciaddr = print_pcie_addr("hostpci$i");
+           }
        }else{
            $pciaddr = print_pci_addr("hostpci$i", $bridges, $arch, $machine_type);
        }
@@ -3536,7 +3560,7 @@ sub config_to_command {
        if ($d->{'x-vga'}) {
            $xvga = ',x-vga=on';
            $kvm_off = 1;
-           $vga->{type} = 'none';
+           $vga->{type} = 'none' if !defined($conf->{vga});
            $gpu_passthrough = 1;
 
            if ($conf->{bios} && $conf->{bios} eq 'ovmf') {
@@ -3883,12 +3907,6 @@ sub config_to_command {
        }
     }
 
-    # add custom args
-    if ($conf->{args}) {
-       my $aa = PVE::Tools::split_args($conf->{args});
-       push @$cmd, @$aa;
-    }
-
     push @$cmd, @$devices;
     push @$cmd, '-rtc', join(',', @$rtcFlags)
        if scalar(@$rtcFlags);
@@ -3897,6 +3915,12 @@ sub config_to_command {
     push @$cmd, '-global', join(',', @$globalFlags)
        if scalar(@$globalFlags);
 
+    # add custom args
+    if ($conf->{args}) {
+       my $aa = PVE::Tools::split_args($conf->{args});
+       push @$cmd, @$aa;
+    }
+
     return wantarray ? ($cmd, $vollist, $spice_port) : $cmd;
 }
 
@@ -4579,6 +4603,7 @@ my $fast_plug_option = {
     'description' => 1,
     'protection' => 1,
     'vmstatestorage' => 1,
+    'hookscript' => 1,
 };
 
 # hotplug changes in [PENDING]
@@ -5097,6 +5122,8 @@ sub vm_start {
            }
        }
 
+       PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'pre-start', 1);
+
        my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine);
 
        my $migrate_port = 0;
@@ -5177,7 +5204,7 @@ sub vm_start {
 
        PVE::Storage::activate_volumes($storecfg, $vollist);
 
-       if (!check_running($vmid, 1)) {
+       if (-d "/sys/fs/cgroup/systemd/qemu.slice/$vmid.scope") {
            eval {
                run_command(['/bin/systemctl', 'stop', "$vmid.scope"],
                    outfunc => sub {}, errfunc => sub {});
@@ -5294,6 +5321,7 @@ sub vm_start {
                    property => "guest-stats-polling-interval",
                    value => 2) if (!defined($conf->{balloon}) || $conf->{balloon});
 
+       PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'post-start');
     });
 }
 
@@ -5355,10 +5383,19 @@ sub vm_human_monitor_command {
 }
 
 sub vm_commandline {
-    my ($storecfg, $vmid) = @_;
+    my ($storecfg, $vmid, $snapname) = @_;
 
     my $conf = PVE::QemuConfig->load_config($vmid);
 
+    if ($snapname) {
+       my $snapshot = $conf->{snapshots}->{$snapname};
+       die "snapshot '$snapname' does not exist\n" if !defined($snapshot);
+
+       $snapshot->{digest} = $conf->{digest}; # keep file digest for API
+
+       $conf = $snapshot;
+    }
+
     my $defaults = load_defaults();
 
     my $cmd = config_to_command($storecfg, $vmid, $conf, $defaults);
@@ -5457,6 +5494,7 @@ sub vm_stop {
                my $opts = PVE::JSONSchema::pve_parse_startup_order($conf->{startup});
                $timeout = $opts->{down} if $opts->{down};
            }
+           PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'pre-stop');
        }
 
        $timeout = 60 if !defined($timeout);
@@ -6655,6 +6693,7 @@ sub clone_disk {
        my $name = undef;
        if (drive_is_cloudinit($drive)) {
            $name = "vm-$newvmid-cloudinit";
+           $snapname = undef;
            # cloudinit only supports raw and qcow2 atm:
            if ($dst_format eq 'qcow2') {
                $name .= '.qcow2';
@@ -6824,12 +6863,15 @@ sub scsihw_infos {
 }
 
 sub add_hyperv_enlightenments {
-    my ($cpuFlags, $winversion, $machine_type, $kvmver, $bios, $gpu_passthrough) = @_;
+    my ($cpuFlags, $winversion, $machine_type, $kvmver, $bios, $gpu_passthrough, $hv_vendor_id) = @_;
 
     return if $winversion < 6;
     return if $bios && $bios eq 'ovmf' && $winversion < 8;
 
-    push @$cpuFlags , 'hv_vendor_id=proxmox' if $gpu_passthrough;
+    if ($gpu_passthrough || defined($hv_vendor_id)) {
+       $hv_vendor_id //= 'proxmox';
+       push @$cpuFlags , "hv_vendor_id=$hv_vendor_id";
+    }
 
     if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)) {
        push @$cpuFlags , 'hv_spinlocks=0x1fff';