package PVE::LXC::Migrate;

use strict;
use warnings;
use PVE::AbstractMigrate;
use File::Basename;
use File::Copy; # fixme: remove
use PVE::Tools;
use PVE::INotify;
use PVE::Cluster;
use PVE::Storage;
use PVE::LXC;

use base qw(PVE::AbstractMigrate);

sub lock_vm {
    my ($self, $vmid, $code, @param) = @_;

    return PVE::LXC::lock_container($vmid, undef, $code, @param);
}

sub prepare {
    my ($self, $vmid) = @_;

    my $online = $self->{opts}->{online};

    $self->{storecfg} = PVE::Storage::config();

    # test is VM exist
    my $conf = $self->{vmconf} = PVE::LXC::load_config($vmid);

    PVE::LXC::check_lock($conf);

    my $running = 0;
    if (PVE::LXC::check_running($vmid)) {
	die "lxc live migration is currently not implemented\n";

	die "cant migrate running container without --online\n" if !$online;
	$running = 1;
    }

    PVE::LXC::foreach_mountpoint($conf, sub {
	my ($ms, $mountpoint) = @_;

	my $volid = $mountpoint->{volume};
	my ($storage, $volname) = PVE::Storage::parse_volume_id($volid, 1) if $volid;
	die "can't determine assigned storage for mountpoint '$ms'\n" if !$storage;

	# check if storage is available on both nodes
	my $scfg = PVE::Storage::storage_check_node($self->{storecfg}, $storage);
	PVE::Storage::storage_check_node($self->{storecfg}, $storage, $self->{node});

	die "unable to migrate local mountpoint '$volid' while CT is running"
	    if !$scfg->{shared};

    });

    my $volid_list = PVE::LXC::get_vm_volumes($conf);
    PVE::Storage::activate_volumes($self->{storecfg}, $volid_list);

    # todo: test if VM uses local resources

    # test ssh connection
    my $cmd = [ @{$self->{rem_ssh}}, '/bin/true' ];
    eval { $self->cmd_quiet($cmd); };
    die "Can't connect to destination address using public key\n" if $@;

    return $running;
}

sub phase1 {
    my ($self, $vmid) = @_;

    $self->log('info', "starting migration of CT $self->{vmid} to node '$self->{node}' ($self->{nodeip})");

    my $conf = $self->{vmconf};
    $conf->{lock} = 'migrate';
    PVE::LXC::write_config($vmid, $conf);

    if ($self->{running}) {
	$self->log('info', "container is running - using online migration");
    }

    $self->{volumes} = [];

    PVE::LXC::foreach_mountpoint($conf, sub {
	my ($ms, $mountpoint) = @_;

	my $volid = $mountpoint->{volume};
	my ($storage, $volname) = PVE::Storage::parse_volume_id($volid);
	my $scfg = PVE::Storage::storage_check_node($self->{storecfg}, $storage);

	if (!$scfg->{shared}) {

	    $self->log('info', "copy mointpoint '$ms' ($volid) to node ' $self->{node}'");
	    PVE::Storage::storage_migrate($self->{storecfg}, $volid, $self->{nodeip}, $storage);
	    push @{$self->{volumes}}, $volid;
	} else {
	    $self->log('info', "mointpoint '$ms' is on shared storage '$storage'");
	}
    });

    my $conffile = PVE::OpenVZ::config_file($vmid);
    my $newconffile = PVE::OpenVZ::config_file($vmid, $self->{node});

    if ($self->{running}) {
	die "implement me";
    }

    # make sure everything on (shared) storage is unmounted
    # Note: we must be 100% sure, else we get data corruption because
    # non-shared file system could be mounted twice (on shared storage)

    PVE::LXC::umount_all($vmid, $self->{storecfg}, $conf);

    # move config
    die "Failed to move config to node '$self->{node}' - rename failed: $!\n"
	if !rename($conffile, $newconffile);
}

sub phase1_cleanup {
    my ($self, $vmid, $err) = @_;

    $self->log('info', "aborting phase 1 - cleanup resources");

    if ($self->{volumes}) {
	foreach my $volid (@{$self->{volumes}}) {
	    $self->log('err', "found stale volume copy '$volid' on node '$self->{node}'");
	    # fixme: try to remove ?
	}
    }
}

sub phase3 {
    my ($self, $vmid) = @_;

    my $volids = $self->{volumes};

    # destroy local copies
    foreach my $volid (@$volids) {
	eval { PVE::Storage::vdisk_free($self->{storecfg}, $volid); };
	if (my $err = $@) {
	    $self->log('err', "removing local copy of '$volid' failed - $err");
	    $self->{errors} = 1;
	    last if $err =~ /^interrupted by signal$/;
	}
    }
}

sub final_cleanup {
    my ($self, $vmid) = @_;

    $self->log('info', "start final cleanup");

    my $conf = $self->{vmconf};
    delete $conf->{lock};

    eval { PVE::LXC::write_config($vmid, $conf); };
    if (my $err = $@) {
	$self->log('err', $err);
    }
}

1;
