bump version to 7.3-1
[qemu-server.git] / PVE / QemuServer / ImportDisk.pm
1 package PVE::QemuServer::ImportDisk;
2
3 use strict;
4 use warnings;
5
6 use PVE::Storage;
7 use PVE::QemuServer;
8 use PVE::Tools qw(run_command extract_param);
9
10 # imports an external disk image to an existing VM
11 # and creates by default a drive entry unused[n] pointing to the created volume
12 # $params->{drive_name} may be used to specify ide0, scsi1, etc ...
13 # $params->{format} may be used to specify qcow2, raw, etc ...
14 sub do_import {
15 my ($src_path, $vmid, $storage_id, $params) = @_;
16
17 my $drive_name = extract_param($params, 'drive_name');
18 my $format = extract_param($params, 'format');
19 if ($drive_name && !(PVE::QemuServer::is_valid_drivename($drive_name))) {
20 die "invalid drive name: $drive_name\n";
21 }
22
23 # get the needed size from source disk
24 my $src_size = PVE::Storage::file_size_info($src_path);
25
26 # get target format, target image's path, and whether it's possible to sparseinit
27 my $storecfg = PVE::Storage::config();
28 my $dst_format = PVE::QemuServer::resolve_dst_disk_format($storecfg, $storage_id, undef, $format);
29
30 my $dst_volid = PVE::Storage::vdisk_alloc($storecfg, $storage_id, $vmid, $dst_format, undef, $src_size / 1024);
31
32 my $zeroinit = PVE::Storage::volume_has_feature($storecfg, 'sparseinit', $dst_volid);
33
34 my $create_drive = sub {
35 my $vm_conf = PVE::QemuConfig->load_config($vmid);
36 if (!$params->{skiplock}) {
37 PVE::QemuConfig->check_lock($vm_conf);
38 }
39
40 if ($drive_name) {
41 # should never happen as setting $drive_name is not exposed to public interface
42 die "cowardly refusing to overwrite existing entry: $drive_name\n" if $vm_conf->{$drive_name};
43
44 my $modified = {}; # record what $option we modify
45 $modified->{$drive_name} = 1;
46 $vm_conf->{pending}->{$drive_name} = $dst_volid;
47 PVE::QemuConfig->write_config($vmid, $vm_conf);
48
49 my $running = PVE::QemuServer::check_running($vmid);
50 if ($running) {
51 my $errors = {};
52 PVE::QemuServer::vmconfig_hotplug_pending($vmid, $vm_conf, $storecfg, $modified, $errors);
53 warn "hotplugging imported disk '$_' failed: $errors->{$_}\n" for keys %$errors;
54 } else {
55 PVE::QemuServer::vmconfig_apply_pending($vmid, $vm_conf, $storecfg);
56 }
57 } else {
58 $drive_name = PVE::QemuConfig->add_unused_volume($vm_conf, $dst_volid);
59 PVE::QemuConfig->write_config($vmid, $vm_conf);
60 }
61 };
62
63 eval {
64 # trap interrupts so we have a chance to clean up
65 local $SIG{INT} =
66 local $SIG{TERM} =
67 local $SIG{QUIT} =
68 local $SIG{HUP} =
69 local $SIG{PIPE} = sub { die "interrupted by signal $!\n"; };
70
71 PVE::Storage::activate_volumes($storecfg, [$dst_volid]);
72 PVE::QemuServer::qemu_img_convert($src_path, $dst_volid, $src_size, undef, $zeroinit);
73 PVE::Storage::deactivate_volumes($storecfg, [$dst_volid]);
74 PVE::QemuConfig->lock_config($vmid, $create_drive);
75 };
76 if (my $err = $@) {
77 eval { PVE::Storage::vdisk_free($storecfg, $dst_volid) };
78 warn "cleanup of $dst_volid failed: $@\n" if $@;
79 die $err;
80 }
81
82 return ($drive_name, $dst_volid);
83 }
84
85 1;