#!/usr/bin/perl
#
# (C) Copyright IBM Corp. 2004
#
# This program is free software;  you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY;  without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
# the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program;  if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Test that each filesystem can correctly handle the snapshot filling up.

use strict;
use warnings;

use Evms::Common;
use Evms::Log;
use Evms::DM;
use Evms::Dos;
use Evms::Object;
use Evms::Volume;
use Evms::Snapshot;

my @segments;
my $tmp_dir = "/tmp";
my $dev_dir = "/dev/evms/";
my $mount_dir = "/mnt/evms/";
my $org_vol_name = "Org1";
my $snap_vol_name = "Snap1";
my $snap_object = "snap1";
my $org_volume = $dev_dir . $org_vol_name;
my $org_mount = $mount_dir . $org_vol_name;
my $snap_volume = $dev_dir . $snap_vol_name;
my $snap_mount = $mount_dir . $snap_vol_name;
my $kernel1 = "2.4.0";
my $kernel2 = "2.4.1";

sub download_kernel($ $)
{
	my $version = $_[0];
	my $dest_dir = $_[1];
	my $short_version;
	my $server = "www.kernel.org";
	my $base_dir = "pub/linux/kernel";
	my $command;

	if (-e $dest_dir . "/linux-$version.tar.bz2") {
		return 0;
	}

	if (!($version =~ /(\d+\.\d+)\.\d+/)) {
		return 1;
	}
	$short_version = $1;

	$command = "wget -q -T 15 -P $dest_dir http://$server/$base_dir/v$short_version/linux-$version.tar.bz2";

	log_debug("$command\n");
	`$command`;

	return ($? >> 8);
}

sub untar_kernel($ $)
{
	my $version = $_[0];
	my $dest_dir = $_[1];
	my ($major, $minor, $patch);
	my $command;
	my $rc;

	if (-d $dest_dir . "/linux-$version") {
		return 0;
	}

	if (!($version =~ /(\d+)\.(\d+)\.(\d+)/)) {
		return 1;
	}
	$major = $1;
	$minor = $2;
	$patch = $3;

	$command = "tar -xj --file $dest_dir/linux-$version.tar.bz2 --directory $dest_dir";

	log_debug("$command\n");
	`$command`;
	$rc = $? >> 8;
	if ($rc) {
		return $rc
	}

	if ($major < 2 || $minor < 4 || ($minor == 4 && $patch < 19)) {
		`mv $dest_dir/linux $dest_dir/linux-$version`;
		$rc = $? >> 8;
	}

	return $rc;
}

sub copy_kernel_tree($ $ $)
{
	my $version = $_[0];
	my $src_dir = $_[1];
	my $dest_dir = $_[2];

	log_debug("cp -a $src_dir/linux-$version $dest_dir\n");
	`cp -a $src_dir/linux-$version $dest_dir`;

	return ($? >> 8);
}

sub remove_kernel_tree($ $)
{
	my $version = $_[0];
	my $dest_dir = $_[1];

	log_debug("rm -rf $dest_dir/linux-$version\n");
	`rm -rf $dest_dir/linux-$version`;

	return ($? >> 8);
}

sub verify_kernel_tree($ $ $)
{
	my $version = $_[0];
	my $src_dir = $_[1];
	my $dest_dir = $_[2];

	`diff -Naur --brief $src_dir/linux-$version $dest_dir/linux-$version 2>&1`;

	return ($? >> 8);
}

sub make_snapshot()
{
	my %options;
	my $rc;

	$options{"original"} = $org_volume;
	$options{"snapshot"} = $snap_object;
	$rc = create_snapshot($segments[1], \%options);
	if (!$rc) {
		$rc = create_evms_volume($snap_object, $snap_vol_name);
	}

	return $rc;
}

sub remove_snapshot()
{
	delete_thing($snap_volume);
	delete_thing($snap_object);
}

# Setup.
# Create two DOS segments on the disk.
sub Setup
{
	my $disk = $_[0];
	my $freespace = $disk . "_freespace1";
	my $rc;

	log_info("1. Creating test directories.\n");

	log_debug("mkdir -p $mount_dir\n");
	log_debug("mkdir -p $tmp_dir\n");
	`mkdir -p $mount_dir`;
	`mkdir -p $tmp_dir`;

	log_info("2. Getting kernel source trees in $tmp_dir.\n");

	$rc = download_kernel($kernel1, $tmp_dir);
	if ($rc) {
		log_error("Error downloading kernel $kernel1 to $tmp_dir\n");
		goto out;
	}

	$rc = download_kernel($kernel2, $tmp_dir);
	if ($rc) {
		log_error("Error downloading kernel $kernel2 to $tmp_dir\n");
		goto out;
	}

	$rc = untar_kernel($kernel1, $tmp_dir);
	if ($rc) {
		log_error("Error untarring kernel $kernel1 in $tmp_dir\n");
		goto out;
	}

	$rc = untar_kernel($kernel2, $tmp_dir);
	if ($rc) {
		log_error("Error untarring kernel $kernel2 in $tmp_dir\n");
		goto out;
	}

	log_info("3. Assigning DOS to disk $disk.\n");

	$rc = assign_dos_plugin($disk);
	if ($rc) {
		log_error("Error assigning DOS to disk $disk.\n");
		goto out;
	}

	log_info("4. Creating two DOS primary segments on disk $disk.\n");

	$rc = create_dos_primary_segment($freespace, "300MB");
	if ($rc) {
		log_error("Error creating first DOS segment on disk $disk.\n");
		goto out;
	}

	$rc = create_dos_primary_segment($freespace, "50MB");
	if ($rc) {
		log_error("Error creating second DOS segment on disk $disk.\n");
		goto out;
	}

	$segments[0] = $disk . "1";
	$segments[1] = $disk . "2";

	log_info("5. Creating origin volume $org_vol_name on " .
		 "segment $segments[0].\n");

	$rc = create_evms_volume($segments[0], $org_vol_name);
	if ($rc) {
		log_error("Error creating EVMS volume $org_vol_name " .
			  "on $segments[0].\n");
		goto out;
	}

out:
	log_result($rc);
	return $rc;
}

sub Cleanup
{
}

# Test_Snapshot
# Routine to run for each filesystem.
sub Test_Snapshot($)
{
	my $fsim = $_[0];
	my $mount_opts;
	my $rc;

	if ($fsim eq "XFS") {
		$mount_opts = "nouuid";
	}

	log_info("Test whether $fsim correctly handles a snapshot filling up.\n");

	log_info("1.  Adding $fsim filesystem to origin volume $org_volume.\n");

	unmkfs_volume($org_volume);
	$rc = mkfs_volume($org_volume, $fsim);
	if ($rc) {
		log_error("Error adding $fsim to $org_volume.\n");
		goto out;
	}

	log_info("2.  Mounting origin volume $org_volume at $org_mount.\n");

	$rc = mount_volume($org_volume, $org_mount);
	if ($rc) {
		log_error("Error mounting origin volume.\n");
		goto out;
	}

	log_info("3.  Copying $kernel1 kernel tree to origin.\n");

	$rc = copy_kernel_tree($kernel1, $tmp_dir, $org_mount);
	if ($rc) {
		log_error("Error copying kernel tree to origin.\n");
		goto out;
	}
	`sync`;

	log_info("4.  Creating snapshot of $org_volume.\n");

	$rc = make_snapshot();
	if ($rc) {
		log_error("Error creating snapshot.\n");
		goto out;
	}

	log_info("5.  Mounting snapshot volume $snap_volume at $snap_mount.\n");

	$rc = mount_volume($snap_volume, $snap_mount, $mount_opts);
	if ($rc) {
		log_error("Error mounting snapshot volume.\n");
		goto out;
	}

	log_info("6.  Removing $kernel1 kernel tree from origin.\n");
	
	$rc = remove_kernel_tree($kernel1, $org_mount);
	if ($rc) {
		log_error("Error removing kernel tree from origin.\n");
		goto out;
	}

	log_info("7.  Copying $kernel2 kernel tree to origin, which will fill\n");
	log_info("    and invalidate the snapshot.\n");

	$rc = copy_kernel_tree($kernel2, $tmp_dir, $org_mount);
	if ($rc) {
		log_error("Error copying kernel tree to origin.\n");
		goto out;
	}
	`sync`;

	log_info("8.  Testing that files on the snapshot cannot be opened.\n");

	$rc = open(SNAP_FILE, "$snap_mount/linux-$kernel1/init/main.c");
	if ($rc) {
		log_error("Error: still able to open files on the snapshot.\n");
		close(SNAP_FILE);
		goto out;
	}

	log_info("9.  Testing that the snapshot cannot be re-mounted.\n");

	unmount_volume($snap_mount);

	$rc = mount_volume($snap_volume, $snap_mount, $mount_opts);
	if (!$rc) {
		log_error("Error: still able to mount the snapshot volume.\n");
		$rc = 1;
		goto out;
	}

	log_info("10. Verifying $kernel2 kernel tree on origin.\n");

	$rc = verify_kernel_tree($kernel2, $tmp_dir, $org_mount);
	if ($rc) {
		log_error("Error verifying kernel tree on origin.\n");
		goto out;
	}

	log_info("11. Resetting the snapshot volume.\n");

	$rc = reset_snapshot($snap_object);
	if ($rc) {
		log_error("Error resetting snapshot.\n");
		goto out;
	}

	$rc = mount_volume($snap_volume, $snap_mount, $mount_opts);
	if ($rc) {
		log_error("Error mounting snapshot volume.\n");
		goto out;
	}

	log_info("12. Verifying $kernel2 kernel tree on snapshot.\n");

	$rc = verify_kernel_tree($kernel2, $tmp_dir, $snap_mount);
	if ($rc) {
		log_error("Error verifying kernel tree on snapshot.\n");
		goto out;
	}

out:
	unmount_volume($snap_mount);
	unmount_volume($org_mount);
	remove_snapshot();
	log_result($rc);
	return $rc;
}

MAIN:
{
	#my @fsims = ("Ext2/3", "ReiserFS", "JFS", "XFS");
	my @fsims = ("Ext2/3", "XFS");
	my $fsim;
	my $disk;
	my $rc;

	# Only use the first disk specified.
	$disk = $ARGV[0];
	$disk || die("USAGE: $0 <disk>\n");

	# Check for minimum-sized disk.
	$rc = check_minimum_object_size($disk, "500MB");
	if ($rc) {
		goto finish;
	}

	# See if the user has set a different temp directory.
	if (defined($ENV{"EVMS_TMP"})) {
		$tmp_dir = $ENV{"EVMS_TMP"};
		$tmp_dir =~ s/^(.*)\/$/$1/;
	}

	$rc = Setup($disk);
	if ($rc) {
		goto finish;
	}

	# Run the snapshot test for each fsim.
	foreach $fsim (@fsims) {
		$rc = Test_Snapshot($fsim);
		if ($rc) {
			goto finish;
		}
	}

	Cleanup();

finish:
}

