]>
Commit | Line | Data |
---|---|---|
b94e047a EK |
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 | # $optional->{drive_name} may be used to specify ide0, scsi1, etc ... | |
13 | # $optional->{format} may be used to specify qcow2, raw, etc ... | |
14 | sub do_import { | |
15 | my ($src_path, $vmid, $storage_id, $optional) = @_; | |
16 | ||
17 | my $drive_name = extract_param($optional, 'drive_name'); | |
18 | my $format = extract_param($optional, 'format'); | |
19 | my $debug = extract_param($optional, 'debug'); | |
20 | if ($drive_name && !(PVE::QemuServer::is_valid_drivename($drive_name))) { | |
21 | die "invalid drive name: $drive_name\n"; | |
22 | } | |
23 | ||
24 | # get the needed size from source disk | |
25 | my $src_size = PVE::Storage::file_size_info($src_path); | |
26 | ||
27 | # get target format, target image's path, and whether it's possible to sparseinit | |
28 | my $storecfg = PVE::Storage::config(); | |
29 | my $dst_format = PVE::QemuServer::resolve_dst_disk_format($storecfg, | |
30 | $storage_id, undef, $format); | |
31 | warn "format : $dst_format\n" if $debug; | |
32 | ||
33 | my $dst_volid = PVE::Storage::vdisk_alloc($storecfg, $storage_id, $vmid, | |
34 | $dst_format, undef, $src_size / 1024); | |
35 | my $dst_path = PVE::Storage::path($storecfg, $dst_volid); | |
36 | ||
37 | warn "args: $src_path, $vmid, $storage_id, $optional\n", | |
38 | "\$dst_volid: $dst_volid\n", if $debug; | |
39 | ||
40 | # qemu-img convert does the hard job | |
41 | # we don't attempt to guess filetypes ourselves | |
42 | my $convert_command = ['qemu-img', 'convert', $src_path, '-p', '-n', '-O', $dst_format]; | |
43 | if (PVE::Storage::volume_has_feature($storecfg, 'sparseinit', $dst_volid)) { | |
44 | push @$convert_command, "zeroinit:$dst_path"; | |
45 | } else { | |
46 | push @$convert_command, $dst_path; | |
47 | } | |
48 | ||
49 | my $create_drive = sub { | |
50 | my $vm_conf = PVE::QemuConfig->load_config($vmid); | |
51 | PVE::QemuConfig->check_lock($vm_conf); | |
52 | ||
53 | if ($drive_name) { | |
54 | # should never happen as setting $drive_name is not exposed to public interface | |
21463b9a | 55 | die "cowardly refusing to overwrite existing entry: $drive_name\n" if $vm_conf->{$drive_name}; |
b94e047a EK |
56 | |
57 | my $modified = {}; # record what $option we modify | |
58 | $modified->{$drive_name} = 1; | |
59 | $vm_conf->{pending}->{$drive_name} = $dst_volid; | |
60 | PVE::QemuConfig->write_config($vmid, $vm_conf); | |
61 | ||
62 | my $running = PVE::QemuServer::check_running($vmid); | |
63 | if ($running) { | |
64 | my $errors = {}; | |
65 | PVE::QemuServer::vmconfig_hotplug_pending($vmid, $vm_conf, $storecfg, $modified, $errors); | |
0f0aa6b7 FG |
66 | if (scalar(keys %$errors)) { |
67 | foreach my $k (keys %$errors) { | |
68 | warn "$k: $errors->{$k}\n" if $debug; | |
69 | warn "hotplugging imported disk failed\n"; | |
70 | } | |
71 | } | |
b94e047a EK |
72 | } else { |
73 | PVE::QemuServer::vmconfig_apply_pending($vmid, $vm_conf, $storecfg); | |
74 | } | |
75 | ||
76 | } else { | |
77 | PVE::QemuConfig->add_unused_volume($vm_conf, $dst_volid); | |
78 | PVE::QemuConfig->write_config($vmid, $vm_conf); | |
79 | } | |
80 | ||
81 | }; | |
82 | ||
83 | eval { | |
84 | # trap interrupts so we have a chance to clean up | |
6cb0144a EK |
85 | local $SIG{INT} = |
86 | local $SIG{TERM} = | |
87 | local $SIG{QUIT} = | |
88 | local $SIG{HUP} = | |
89 | local $SIG{PIPE} = sub { die "interrupted by signal\n"; }; | |
b94e047a EK |
90 | PVE::Storage::activate_volumes($storecfg, [$dst_volid]); |
91 | run_command($convert_command); | |
92 | PVE::Storage::deactivate_volumes($storecfg, [$dst_volid]); | |
93 | PVE::QemuConfig->lock_config($vmid, $create_drive); | |
94 | }; | |
95 | ||
96 | my $err = $@; | |
97 | if ($err) { | |
98 | eval { # do not die before we returned $err | |
99 | PVE::Storage::vdisk_free($storecfg, $dst_volid); | |
100 | }; | |
101 | die $err; | |
102 | } | |
103 | } | |
104 | ||
105 | 1; |