#!/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
#
# LVM script for testing PV expand and shrink
#
# Setup: Create two 100MB disk segments, and create an LVM container from
#        these two segments.
# Test1: Expand the second PV in the container by 100MB. Check the resulting
#        size of the container and the freespace region.
# Test2: Shrink the first PV in the container by 50MB.
# Test3: Create a 200MB region in the container. This will span all of the
#        first segment (50MB) and 75% of the second segment (150MB). Attempt
#        to shrink the first PV, which should fail, since it has no free PEs.
# Test4: Shrink the second PV by 25MB.
# Test5: Attempt to shrink the second PV by 100MB. The second PV does not have
#        this much freespace, so the shrink amount should be rounded down.
#        There should be no freespace left in the container now.
# Test6: Expand the first PV by 50MB.

use strict;
use warnings;

use Evms::Common;
use Evms::Log;
use Evms::Object;
use Evms::Dos;
use Evms::Lvm;

my $container_name = "Fellowship";
my $container_pe_size = "1MB";
my @pvs;

# Setup
# Create an LVM container with two DOS segments.
sub Setup
{
	my $disk = $_[0];
	my %options;
	my $rc;

	log_info("Creating 2 DOS segments.\n");

	$rc = create_dos_segments($disk, 2, "100MB");
	if ($rc) {
		log_error("Error creating DOS segments.\n");
		goto out;
	}

	# The CLI reverses the order of the objects when creating the container.
	@pvs = ($disk."6", $disk."5");
	$options{"name"} = $container_name;
	$options{"pe_size"} = $container_pe_size;

	log_info("Creating LVM container $container_name\n");

	$rc = create_lvm_container(\%options, @pvs);
	if ($rc) {
		log_error("Error creating LVM container.\n");
		goto out;
	}

	@pvs = ($disk."5", $disk."6");

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

# Test1
# Expand the second PV in the container by 100MB. Check the resulting
# size of the container's freespace region.
sub Test1
{
	my $disk = $_[0];
	my ($freespace_size_before, $freespace_size_after);
	my $expand_size = "100MB";
	my $freespace_segment = $disk . "_freespace2";
	my $rc;

	log_info("Test expanding an LVM PV.\n");

	log_info("1. Getting size of container' freespace " .
		 "before the expand.\n");

	$freespace_size_before = get_object_size("lvm/".$container_name."/Freespace");

	log_info("2. Expanding PV $pvs[1] in container $container_name.\n");

	$rc = expand_dos_segment($pvs[1], $freespace_segment, $expand_size);
	if ($rc) {
		log_error("Error expanding PV $pvs[1].\n");
		goto out;
	}

	log_info("3. Getting size of container and freespace " .
		 "after the expand.\n");

	$freespace_size_after = get_object_size("lvm/".$container_name."/Freespace");

	log_info("4. Verifying container's freespace expanded by the " .
		 "correct amount.\n");

	$rc = compare_sizes($freespace_size_after,
			    add_sizes($freespace_size_before, $expand_size), 10);
	if ($rc) {
		log_error("Error: freespace did not expand by the correct amount.\n");
		goto out;
	}

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

# Test2
# Shrink the first PV in the container by 50MB.
sub Test2
{
	my ($freespace_size_before, $freespace_size_after);
	my $shrink_size = "50MB";
	my $rc;

	log_info("Test shrinking an LVM PV.\n");

	log_info("1. Getting size of container's freespace " .
		 "before the shrink.\n");

	$freespace_size_before = get_object_size("lvm/".$container_name."/Freespace");

	log_info("2. Shrinking PV $pvs[0] in container " .
		 "$container_name by $shrink_size.\n");

	$rc = shrink_dos_segment($pvs[0], $shrink_size);
	if ($rc) {
		log_error("Error shrinking PV $pvs[0].\n");
		goto out;
	}

	log_info("3. Getting size of container and freespace " .
		 "after the shrink.\n");

	$freespace_size_after = get_object_size("lvm/".$container_name."/Freespace");

	log_info("4. Verifying container's freespace shrunk by the " .
		 "correct amount.\n");

	$rc = compare_sizes($freespace_size_before,
			    add_sizes($freespace_size_after, $shrink_size), 10);
	if ($rc) {
		log_error("Error: freespace did not shrink by the correct amount.\n");
		goto out;
	}

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

# Test3
# Create a 150MB region in the container. This will span all of the
# first segment (50MB) and half of the second segment (100MB). Attempt
# to shrink the first PV, which should fail, since it has no free PEs.
sub Test3
{
	my $region_name = "Sam";
	my $region_size = "150MB";
	my $shrink_size = "25MB";
	my ($freespace_size_before, $freespace_size_after);
	my %options;
	my $rc;

	$options{"name"} = $region_name;
	$options{"size"} = $region_size;

	log_info("Test shrinking an LVM PV which has no freespace.\n");

	log_info("1. Creating an LVM region $region_name to consume all\n");
	log_info("   of $pvs[0] and part of $pvs[1].\n");

	$rc = create_lvm_region($container_name, \%options);
	if ($rc) {
		log_error("Error creating region $region_name.\n");
		goto out;
	}

	log_info("2. Getting size of container's freespace " .
		 "before the shrink.\n");

	$freespace_size_before = get_object_size("lvm/".$container_name."/Freespace");

	log_info("3. Trying to shrink PV $pvs[0], which has no free extents.\n");

	$rc = shrink_dos_segment($pvs[0], $shrink_size);
	if (!$rc) {
		log_error("Error: PV $pvs[0] was allowed to shrink.\n");
		$rc = 1;
		goto out;
	}

	log_info("4. Verifying that PV $pvs[0] did *not* shrink.\n");

	$freespace_size_after = get_object_size("lvm/".$container_name."/Freespace");

	$rc = compare_sizes($freespace_size_before, $freespace_size_after, 0);
	if ($rc) {
		log_error("Error: Size of freespace in container " .
			  "$container_name has changed.\n");
		goto out;
	}

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

# Test4
# Shrink the second PV by 25MB.
sub Test4
{
	my ($freespace_size_before, $freespace_size_after);
	my $shrink_size = "25MB";
	my $rc;

	log_info("Test shrinking an LVM PV.\n");

	log_info("1. Getting size of container's freespace " .
		 "before the shrink.\n");

	$freespace_size_before = get_object_size("lvm/".$container_name."/Freespace");

	log_info("2. Shrinking PV $pvs[1] by $shrink_size.\n");

	$rc = shrink_dos_segment($pvs[1], $shrink_size);
	if ($rc) {
		log_error("Error shrinking PV $pvs[1].\n");
		goto out;
	}

	log_info("3. Getting size of container and freespace " .
		 "after the shrink.\n");

	$freespace_size_after = get_object_size("lvm/".$container_name."/Freespace");

	log_info("4. Verifying container's freespace shrunk by the " .
		 "correct amount.\n");

	$rc = compare_sizes($freespace_size_before,
			    add_sizes($freespace_size_after, $shrink_size), 10);
	if ($rc) {
		log_error("Error: freespace did not shrink by the correct amount.\n");
		goto out;
	}

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

# Test5
# Attempt to shrink the second PV by 100MB. The second PV does not have
# this much freespace, so the shrink should fail.
sub Test5
{
	my ($freespace_size_before, $freespace_size_after);
	my $shrink_size = "100MB";
	my $rc;

	log_info("Test shrinking an LVM PV which can't " .
		 "be shrunk by the desired size.\n");

	log_info("1. Getting size of container's freespace " .
		 "before the shrink.\n");

	$freespace_size_before = get_object_size("lvm/".$container_name."/Freespace");

	log_info("2. Trying to shrink PV $pvs[1] by $shrink_size, which\n");
	log_info("   has less than that amount in free extents.\n");

	$rc = shrink_dos_segment($pvs[1], $shrink_size);
	if (!$rc) {
		log_error("Error: PV $pvs[0] was allowed to shrink.\n");
		$rc = 1;
		goto out;
	}

	log_info("3. Verifying that PV $pvs[0] did *not* shrink.\n");

	$freespace_size_after = get_object_size("lvm/".$container_name."/Freespace");

	$rc = compare_sizes($freespace_size_before, $freespace_size_after, 0);
	if ($rc) {
		log_error("Error: Size of freespace in container " .
			  "$container_name has changed.\n");
		goto out;
	}

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

# Test6
# Expand the first PV by 25MB.
sub Test6
{
	my $disk = $_[0];
	my ($freespace_size_before, $freespace_size_after);
	my $expand_size = "25MB";
	my $freespace_segment = $disk . "_freespace2";
	my $rc;

	log_info("Test expanding an LVM PV.\n");

	log_info("1. Getting size of container' freespace " .
		 "before the expand.\n");

	$freespace_size_before = get_object_size("lvm/".$container_name."/Freespace");

	log_info("2. Expanding PV $pvs[0] in container $container_name.\n");

	$rc = expand_dos_segment($pvs[0], $freespace_segment, $expand_size);
	if ($rc) {
		log_error("Error expanding PV $pvs[0].\n");
		goto out;
	}

	log_info("3. Getting size of container and freespace " .
		 "after the expand.\n");

	$freespace_size_after = get_object_size("lvm/".$container_name."/Freespace");

	log_info("4. Verifying container's freespace expanded by the " .
		 "correct amount.\n");

	$rc = compare_sizes($freespace_size_after,
			    add_sizes($freespace_size_before, $expand_size), 10);
	if ($rc) {
		log_error("Error: freespace did not expand by the correct amount.\n");
		goto out;
	}

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

MAIN:
{
	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;
	}

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

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

	$rc = Test2();
	if ($rc) {
		goto finish;
	}

	$rc = Test3();
	if ($rc) {
		goto finish;
	}

	$rc = Test4();
	if ($rc) {
		goto finish;
	}

	$rc = Test5();
	if ($rc) {
		goto finish;
	}

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

finish:
}

