]>
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 | |
5600c5b2 TL |
12 | # $params->{drive_name} may be used to specify ide0, scsi1, etc ... |
13 | # $params->{format} may be used to specify qcow2, raw, etc ... | |
b94e047a | 14 | sub do_import { |
5600c5b2 | 15 | my ($src_path, $vmid, $storage_id, $params) = @_; |
b94e047a | 16 | |
5600c5b2 TL |
17 | my $drive_name = extract_param($params, 'drive_name'); |
18 | my $format = extract_param($params, 'format'); | |
b94e047a EK |
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(); | |
5600c5b2 | 28 | my $dst_format = PVE::QemuServer::resolve_dst_disk_format($storecfg, $storage_id, undef, $format); |
b94e047a | 29 | |
5600c5b2 | 30 | my $dst_volid = PVE::Storage::vdisk_alloc($storecfg, $storage_id, $vmid, $dst_format, undef, $src_size / 1024); |
b94e047a | 31 | |
af1f1ec0 | 32 | my $zeroinit = PVE::Storage::volume_has_feature($storecfg, 'sparseinit', $dst_volid); |
b94e047a EK |
33 | |
34 | my $create_drive = sub { | |
35 | my $vm_conf = PVE::QemuConfig->load_config($vmid); | |
5600c5b2 | 36 | if (!$params->{skiplock}) { |
7f384190 DJ |
37 | PVE::QemuConfig->check_lock($vm_conf); |
38 | } | |
b94e047a EK |
39 | |
40 | if ($drive_name) { | |
5600c5b2 TL |
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}; | |
b94e047a | 43 | |
5600c5b2 TL |
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); | |
b94e047a | 48 | |
5600c5b2 TL |
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 | } | |
b94e047a EK |
57 | } else { |
58 | PVE::QemuConfig->add_unused_volume($vm_conf, $dst_volid); | |
59 | PVE::QemuConfig->write_config($vmid, $vm_conf); | |
60 | } | |
b94e047a EK |
61 | }; |
62 | ||
63 | eval { | |
64 | # trap interrupts so we have a chance to clean up | |
6cb0144a EK |
65 | local $SIG{INT} = |
66 | local $SIG{TERM} = | |
67 | local $SIG{QUIT} = | |
68 | local $SIG{HUP} = | |
5600c5b2 TL |
69 | local $SIG{PIPE} = sub { die "interrupted by signal $!\n"; }; |
70 | ||
b94e047a | 71 | PVE::Storage::activate_volumes($storecfg, [$dst_volid]); |
af1f1ec0 | 72 | PVE::QemuServer::qemu_img_convert($src_path, $dst_volid, $src_size, undef, $zeroinit); |
b94e047a EK |
73 | PVE::Storage::deactivate_volumes($storecfg, [$dst_volid]); |
74 | PVE::QemuConfig->lock_config($vmid, $create_drive); | |
75 | }; | |
5600c5b2 TL |
76 | if (my $err = $@) { |
77 | eval { PVE::Storage::vdisk_free($storecfg, $dst_volid) }; | |
78 | warn "cleanup of $dst_volid failed: $@\n" if $@; | |
b94e047a EK |
79 | die $err; |
80 | } | |
81 | } | |
82 | ||
83 | 1; |