package PVE::QemuServer::ImportDisk;

use strict;
use warnings;

use PVE::Storage;
use PVE::QemuServer;
use PVE::Tools qw(run_command extract_param);

# imports an external disk image to an existing VM
# and creates by default a drive entry unused[n] pointing to the created volume
# $params->{drive_name} may be used to specify ide0, scsi1, etc ...
# $params->{format} may be used to specify qcow2, raw, etc ...
# $params->{skiplock} may be used to skip checking for a lock in the VM config
# $params->{'skip-config-update'} may be used to import the disk without updating the VM config
sub do_import {
    my ($src_path, $src_size, $vmid, $storage_id, $params) = @_;

    my $drive_name = extract_param($params, 'drive_name');
    my $format = extract_param($params, 'format');
    if ($drive_name && !(PVE::QemuServer::is_valid_drivename($drive_name))) {
        die "invalid drive name: $drive_name\n";
    }

    # get target format, target image's path, and whether it's possible to sparseinit
    my $storecfg = PVE::Storage::config();
    my $dst_format =
        PVE::QemuServer::resolve_dst_disk_format($storecfg, $storage_id, undef, $format);
    warn "format '$format' is not supported by the target storage - using '$dst_format' instead\n"
        if $format && $format ne $dst_format;

    my $dst_volid = PVE::Storage::vdisk_alloc(
        $storecfg,
        $storage_id,
        $vmid,
        $dst_format,
        undef,
        $src_size / 1024,
    );

    my $zeroinit = PVE::Storage::volume_has_feature($storecfg, 'sparseinit', $dst_volid);

    my $create_drive = sub {
        my $vm_conf = PVE::QemuConfig->load_config($vmid);
        if (!$params->{skiplock}) {
            PVE::QemuConfig->check_lock($vm_conf);
        }

        if ($drive_name) {
            # should never happen as setting $drive_name is not exposed to public interface
            die "cowardly refusing to overwrite existing entry: $drive_name\n"
                if $vm_conf->{$drive_name};

            my $modified = {}; # record what $option we modify
            $modified->{$drive_name} = 1;
            $vm_conf->{pending}->{$drive_name} = $dst_volid;
            PVE::QemuConfig->write_config($vmid, $vm_conf);

            my $running = PVE::QemuServer::check_running($vmid);
            if ($running) {
                my $errors = {};
                PVE::QemuServer::vmconfig_hotplug_pending(
                    $vmid,
                    $vm_conf,
                    $storecfg,
                    $modified,
                    $errors,
                );
                warn "hotplugging imported disk '$_' failed: $errors->{$_}\n" for keys %$errors;
            } else {
                PVE::QemuServer::vmconfig_apply_pending($vmid, $vm_conf, $storecfg);
            }
        } else {
            $drive_name = PVE::QemuConfig->add_unused_volume($vm_conf, $dst_volid);
            PVE::QemuConfig->write_config($vmid, $vm_conf);
        }
    };

    eval {
        # trap interrupts so we have a chance to clean up
        local $SIG{INT} = local $SIG{TERM} = local $SIG{QUIT} = local $SIG{HUP} =
            local $SIG{PIPE} = sub { die "interrupted by signal $!\n"; };

        PVE::Storage::activate_volumes($storecfg, [$dst_volid]);
        PVE::QemuServer::qemu_img_convert(
            $src_path,
            $dst_volid,
            $src_size,
            { 'is-zero-initialized' => $zeroinit },
        );
        PVE::Storage::deactivate_volumes($storecfg, [$dst_volid]);
        PVE::QemuConfig->lock_config($vmid, $create_drive) if !$params->{'skip-config-update'};
    };
    if (my $err = $@) {
        eval { PVE::Storage::vdisk_free($storecfg, $dst_volid) };
        warn "cleanup of $dst_volid failed: $@\n" if $@;
        die $err;
    }

    return ($drive_name, $dst_volid);
}

1;
