]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer.pm
config: NIC macaddr: enforce unicast MAC addresses
[qemu-server.git] / PVE / QemuServer.pm
index 2d3bf58075126409987102eeb9cbf7090ff1ff59..deac8e2d0f11d4c48d252671fed4b923af6e56f9 100644 (file)
@@ -256,6 +256,21 @@ my $vga_fmt = {
     },
 };
 
+my $ivshmem_fmt = {
+    size => {
+       type => 'integer',
+       minimum => 1,
+       description => "The size of the file in MB.",
+    },
+    name => {
+       type => 'string',
+       pattern => '[a-zA-Z0-9\-]+',
+       optional => 1,
+       format_description => 'string',
+       description => "The name of the file. Will be prefixed with 'pve-shm-'. Default is the VMID. Will be deleted when the VM is stopped.",
+    },
+};
+
 my $confdesc = {
     onboot => {
        optional => 1,
@@ -621,7 +636,38 @@ EODESCR
        optional => 1,
        description => "Script that will be executed during various steps in the vms lifetime.",
     },
+    ivshmem => {
+       type => 'string',
+       format => $ivshmem_fmt,
+       description => "Inter-VM shared memory. Useful for direct communication between VMs, or to the host.",
+       optional => 1,
+    }
+};
+
+my $cicustom_fmt = {
+    meta => {
+       type => 'string',
+       optional => 1,
+       description => 'Specify a custom file containing all meta data passed to the VM via cloud-init. This is provider specific meaning configdrive2 and nocloud differ.',
+       format => 'pve-volume-id',
+       format_description => 'volume',
+    },
+    network => {
+       type => 'string',
+       optional => 1,
+       description => 'Specify a custom file containing all network data passed to the VM via cloud-init.',
+       format => 'pve-volume-id',
+       format_description => 'volume',
+    },
+    user => {
+       type => 'string',
+       optional => 1,
+       description => 'Specify a custom file containing all user data passed to the VM via cloud-init.',
+       format => 'pve-volume-id',
+       format_description => 'volume',
+    },
 };
+PVE::JSONSchema::register_format('pve-qm-cicustom', $cicustom_fmt);
 
 my $confdesc_cloudinit = {
     citype => {
@@ -640,6 +686,12 @@ my $confdesc_cloudinit = {
        type => 'string',
        description => 'cloud-init: Password to assign the user. Using this is generally not recommended. Use ssh keys instead. Also note that older cloud-init versions do not support hashed passwords.',
     },
+    cicustom => {
+       optional => 1,
+       type => 'string',
+       description => 'cloud-init: Specify custom files to replace the automatically generated ones at start.',
+       format => 'pve-qm-cicustom',
+    },
     searchdomain => {
        optional => 1,
        type => 'string',
@@ -750,13 +802,9 @@ The DHCP server assign addresses to the guest starting from 10.0.2.15.
 __EOD__
 
 my $net_fmt = {
-    macaddr => {
-       type => 'string',
-       pattern => qr/[0-9a-f]{2}(?::[0-9a-f]{2}){5}/i,
+    macaddr  => get_standard_option('mac-addr', {
        description => "MAC address. That address must be unique withing your network. This is automatically generated if not specified.",
-       format_description => "XX:XX:XX:XX:XX:XX",
-       optional => 1,
-    },
+    }),
     model => {
        type => 'string',
        description => "Network Card Model. The 'virtio' model provides the best performance with very low CPU overhead. If your guest does not support this driver, it is usually best to use 'e1000'.",
@@ -2808,6 +2856,8 @@ sub check_local_resources {
     $loc_res = 1 if $conf->{hostusb}; # old syntax
     $loc_res = 1 if $conf->{hostpci}; # old syntax
 
+    $loc_res = 1 if $conf->{ivshmem};
+
     foreach my $k (keys %$conf) {
        next if $k =~ m/^usb/ && ($conf->{$k} eq 'spice');
        # sockets are safe: they will recreated be on the target side post-migrate
@@ -3556,7 +3606,7 @@ sub config_to_command {
        next if !$d;
 
        my $pcie = $d->{pcie};
-       if($pcie){
+       if ($pcie) {
            die "q35 machine model is not enabled" if !$q35;
            # win7 wants to have the pcie devices directly on the pcie bus
            # instead of in the root port
@@ -3565,7 +3615,7 @@ sub config_to_command {
            } else {
                $pciaddr = print_pcie_addr("hostpci$i");
            }
-       }else{
+       } else {
            $pciaddr = print_pci_addr("hostpci$i", $bridges, $arch, $machine_type);
        }
 
@@ -3908,6 +3958,23 @@ sub config_to_command {
          push @$devices, '-device', $netdevicefull;
     }
 
+    if ($conf->{ivshmem}) {
+       my $ivshmem = PVE::JSONSchema::parse_property_string($ivshmem_fmt, $conf->{ivshmem});
+
+       my $bus;
+       if ($q35) {
+           $bus = print_pcie_addr("ivshmem");
+       } else {
+           $bus = print_pci_addr("ivshmem", $bridges, $arch, $machine_type);
+       }
+
+       my $ivshmem_name = $ivshmem->{name} // $vmid;
+       my $path = '/dev/shm/pve-shm-' . $ivshmem_name;
+
+       push @$devices, '-device', "ivshmem-plain,memdev=ivshmem$bus,";
+       push @$devices, '-object', "memory-backend-file,id=ivshmem,share=on,mem-path=$path,size=$ivshmem->{size}M";
+    }
+
     if (!$q35) {
        # add pci bridges
         if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)) {
@@ -4092,20 +4159,23 @@ sub vm_deviceplug {
 
     } elsif ($deviceid =~ m/^(net)(\d+)$/) {
 
-        return undef if !qemu_netdevadd($vmid, $conf, $arch, $device, $deviceid);
+       return undef if !qemu_netdevadd($vmid, $conf, $arch, $device, $deviceid);
 
-        my $machine_type = PVE::QemuServer::qemu_machine_pxe($vmid, $conf); 
-        my $use_old_bios_files = undef;
-        ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files($machine_type);
+       my $machine_type = PVE::QemuServer::qemu_machine_pxe($vmid, $conf);
+       my $use_old_bios_files = undef;
+       ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files($machine_type);
 
-        my $netdevicefull = print_netdevice_full($vmid, $conf, $device, $deviceid, undef, $use_old_bios_files, $arch, $machine_type);
-        qemu_deviceadd($vmid, $netdevicefull);
-        eval { qemu_deviceaddverify($vmid, $deviceid); };
+       my $netdevicefull = print_netdevice_full($vmid, $conf, $device, $deviceid, undef, $use_old_bios_files, $arch, $machine_type);
+       qemu_deviceadd($vmid, $netdevicefull);
+       eval {
+           qemu_deviceaddverify($vmid, $deviceid);
+           qemu_set_link_status($vmid, $deviceid, !$device->{link_down});
+       };
        if (my $err = $@) {
            eval { qemu_netdevdel($vmid, $deviceid); };
            warn $@ if $@;
            die $err;
-        }
+       }
 
     } elsif (!$q35 && $deviceid =~ m/^(pci\.)(\d+)$/) {
 
@@ -4204,7 +4274,11 @@ sub qemu_iothread_add {
 sub qemu_iothread_del {
     my($conf, $vmid, $deviceid) = @_;
 
-    my $device = parse_drive($deviceid, $conf->{$deviceid});
+    my $confid = $deviceid;
+    if ($deviceid =~ m/^(?:virtioscsi|scsihw)(\d+)$/) {
+       $confid = 'scsi' . $1;
+    }
+    my $device = parse_drive($confid, $conf->{$confid});
     if ($device->{iothread}) {
        my $iothreads = vm_iothreads_list($vmid);
        qemu_objectdel($vmid, "iothread-$deviceid") if $iothreads->{"iothread-$deviceid"};
@@ -5464,6 +5538,15 @@ sub vm_stop_cleanup {
            unlink "/var/run/qemu-server/${vmid}.$ext";
        }
 
+       if ($conf->{ivshmem}) {
+           my $ivshmem = PVE::JSONSchema::parse_property_string($ivshmem_fmt, $conf->{ivshmem});
+           # just delete it for now, VMs which have this already open do not
+           # are affected, but new VMs will get a separated one. If this
+           # becomes an issue we either add some sort of ref-counting or just
+           # add a "don't delete on stop" flag to the ivshmem format.
+           unlink '/dev/shm/pve-shm-' . ($ivshmem->{name} // $vmid);
+       }
+
        foreach my $key (keys %$conf) {
            next if $key !~ m/^hostpci(\d+)$/;
            my $hostpciindex = $1;
@@ -6457,6 +6540,23 @@ sub template_create {
     });
 }
 
+sub convert_iscsi_path {
+    my ($path) = @_;
+
+    if ($path =~ m|^iscsi://([^/]+)/([^/]+)/(.+)$|) {
+       my $portal = $1;
+       my $target = $2;
+       my $lun = $3;
+
+       my $initiator_name = get_initiator_name();
+
+       return "file.driver=iscsi,file.transport=tcp,file.initiator-name=$initiator_name,".
+              "file.portal=$portal,file.target=$target,file.lun=$lun,driver=raw";
+    }
+
+    die "cannot convert iscsi path '$path', unkown format\n";
+}
+
 sub qemu_img_convert {
     my ($src_volid, $dst_volid, $size, $snapname, $is_zero_initialized) = @_;
 
@@ -6477,13 +6577,32 @@ sub qemu_img_convert {
        my $src_path = PVE::Storage::path($storecfg, $src_volid, $snapname);
        my $dst_path = PVE::Storage::path($storecfg, $dst_volid);
 
+       my $src_is_iscsi = ($src_path =~ m|^iscsi://|);
+       my $dst_is_iscsi = ($dst_path =~ m|^iscsi://|);
+
        my $cmd = [];
        push @$cmd, '/usr/bin/qemu-img', 'convert', '-p', '-n';
        push @$cmd, '-l', "snapshot.name=$snapname" if($snapname && $src_format eq "qcow2");
        push @$cmd, '-t', 'none' if $dst_scfg->{type} eq 'zfspool';
        push @$cmd, '-T', 'none' if $src_scfg->{type} eq 'zfspool';
-       push @$cmd, '-f', $src_format, '-O', $dst_format, $src_path;
-       if ($is_zero_initialized) {
+
+       if ($src_is_iscsi) {
+           push @$cmd, '--image-opts';
+           $src_path = convert_iscsi_path($src_path);
+       } else {
+           push @$cmd, '-f', $src_format;
+       }
+
+       if ($dst_is_iscsi) {
+           push @$cmd, '--target-image-opts';
+           $dst_path = convert_iscsi_path($dst_path);
+       } else {
+           push @$cmd, '-O', $dst_format;
+       }
+
+       push @$cmd, $src_path;
+
+       if (!$dst_is_iscsi && $is_zero_initialized) {
            push @$cmd, "zeroinit:$dst_path";
        } else {
            push @$cmd, $dst_path;