#!/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 region expands and shrinks.
#
# Test1: Create a region using part of the container. Then expand it without
#        arguments, which should use the remaining space in the conatainer.
# Test2: Create a region using part of the container. Then expand it,
#        specifying the number of extents to add.
# Test3: Expand the previous region, specifying the size to add to the region.
# Test4: Create a region using the entire container. Then shrink the region by
#        the default amount, which should be one extent.
# Test5: Shrink the previous region, specifying the number of extents
#        to remove.
# Test6: Shrink the previous region, specifying the size to remove.
# Test7: Create a striped region across all four PVs in the container, using
#        only a portion of the space. Then expand the region.
# Test8: Shrink the previously expanded striped region.

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 four DOS segments.
sub Setup
{
	my $disk = $_[0];
	my %options;
	my $rc;

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

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

	@pvs = ($disk."5", $disk."6", $disk."7", $disk."8");
	$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;
	}

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

# Test1
# Create a region using part of the container. Then expand it without
# arguments, which should use the remaining space in the conatainer.
sub Test1
{
	my $name = "Frodo";
	my $region_name = $container_name . "/" . $name;
	my $size = "200MB";
	my $freespace_size;
	my ($sectors_before, $sectors_after, $sectors_delta);
	my %options;
	my $rc;

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

	log_info("Test expanding an LVM region by default size.\n");

	log_info("1. Creating a $size LVM region $region_name.\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 region $region_name before the expand.\n");

	$sectors_before = get_blkdev_sectors("lvm/" . $region_name);

	log_info("3. Expanding region $region_name.\n");

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

	$sectors_after = get_blkdev_sectors("lvm/" . $region_name);
	$sectors_delta = $sectors_after - $sectors_before;

	log_info("4. Region $region_name expanded by " .
		 sectors_to_size($sectors_delta) . "\n");

	log_info("5. Verifying all freespace used in container " .
		 "$container_name.\n");

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

	$rc = compare_sizes($freespace_size, "0KB", 0);
	if ($rc) {
		log_error("Error: Container $container_name has " .
			  "non-zero freespace.\n");
		goto out;
	}

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

# Test2
# Create a region using part of the container. Then expand it,
# specifying the number of extents to add.
sub Test2
{
	my $name = "Gandalf";
	my $region_name = $container_name . "/" . $name;
	my $size = "100MB";
	my $add_extents = 100;
	my ($sectors_before, $sectors_after, $sectors_delta);
	my %options;
	my $rc;

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

	log_info("Test expanding an LVM region by specifying " .
		 "additional extents.\n");

	log_info("1. Creating a $size LVM region $region_name.\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 region $region_name before the expand.\n");

	$sectors_before = get_blkdev_sectors("lvm/" . $region_name);

	log_info("3. Expanding region $region_name by $add_extents extents.\n");

	$options{"add_extents"} = $add_extents;
	$rc = expand_lvm_region($region_name, \%options);
	if ($rc) {
		log_error("Error expanding region $region_name.\n");
		goto out;
	}

	log_info("4. Verifying region $region_name was expanded " .
		 "by correct amount.\n");

	$sectors_after = get_blkdev_sectors("lvm/" . $region_name);
	$sectors_delta = $sectors_after - $sectors_before;

	$rc = compare_sectors($sectors_delta, $add_extents *
			      size_to_sectors($container_pe_size), 0);
	if ($rc) {
		log_error("Error: region $region_name expanded by wrong size.\n");
		goto out;
	}

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

# Test3
# Expand the previous region, specifying the size to add to the region.
sub Test3
{
	my $name = "Gandalf";
	my $region_name = $container_name . "/" . $name;
	my $add_size = "50MB";
	my ($sectors_before, $sectors_after, $sectors_delta);
	my %options;
	my $rc;

	log_info("Test expanding an LVM region by specifying " .
		 "additional size.\n");

	log_info("1. Getting size of region $region_name before the expand.\n");

	$sectors_before = get_blkdev_sectors("lvm/" . $region_name);

	log_info("2. Expanding region $region_name by $add_size.\n");

	$options{"add_size"} = $add_size;
	$rc = expand_lvm_region($region_name, \%options);
	if ($rc) {
		log_error("Error expanding region $region_name.\n");
		goto out;
	}

	log_info("3. Verifying region $region_name was expanded " .
		 "by correct amount.\n");

	$sectors_after = get_blkdev_sectors("lvm/" . $region_name);
	$sectors_delta = $sectors_after - $sectors_before;

	$rc = compare_sectors($sectors_delta, size_to_sectors($add_size), 0);
	if ($rc) {
		log_error("Error: region $region_name expanded by wrong size.\n");
		goto out;
	}

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

# Test4
# Create a region using the entire container. Then shrink the region by
# the default amount, which should be one extent.
sub Test4
{
	my $name = "Sam";
	my $region_name = $container_name . "/" . $name;
	my ($sectors_before, $sectors_after, $sectors_delta);
	my %options;
	my $rc;

	$options{"name"} = $name;

	log_info("Test shrinking an LVM region by default size.\n");

	log_info("1. Creating an LVM region $region_name.\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 region $region_name before the shrink.\n");

	$sectors_before = get_blkdev_sectors("lvm/" . $region_name);

	log_info("3. Shrinking region $region_name.\n");

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

	$sectors_after = get_blkdev_sectors("lvm/" . $region_name);
	$sectors_delta = $sectors_before - $sectors_after;

	log_info("4. Verifying region $region_name shrunk by one extent.\n");

	$rc = compare_sectors($sectors_delta,
			      size_to_sectors($container_pe_size), 0);
	if ($rc) {
		log_error("Error: region $region_name shrunk by the wrong size.\n");
		log_error("       expected change: " . size_to_sectors($container_pe_size) . " sectors\n");
		log_error("       actual change:   $sectors_delta sectors\n");
		goto out;
	}

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

# Test5
# Shrink the previous region, specifying the number of extents to remove.
sub Test5
{
	my $name = "Sam";
	my $region_name = $container_name . "/" . $name;
	my ($sectors_before, $sectors_after, $sectors_delta);
	my $remove_extents = 75;
	my %options;
	my $rc;

	log_info("Test shrinking an LVM region by specifying " .
		 "extents to remove.\n");

	log_info("1. Getting size of region $region_name before the shrink.\n");

	$sectors_before = get_blkdev_sectors("lvm/" . $region_name);

	log_info("2. Shrinking region $region_name by " .
		 "$remove_extents extents.\n");

	$options{"remove_extents"} = $remove_extents;
	$rc = shrink_lvm_region($region_name, \%options);
	if ($rc) {
		log_error("Error shrinking region $region_name.\n");
		goto out;
	}

	$sectors_after = get_blkdev_sectors("lvm/" . $region_name);
	$sectors_delta = $sectors_before - $sectors_after;

	log_info("3. Verifying region $region_name shrunk by correct size.\n");

	$rc = compare_sectors($sectors_delta,
			      $remove_extents * size_to_sectors($container_pe_size), 0);
	if ($rc) {
		log_error("Error: region $region_name shrunk by the wrong size.\n");
		goto out;
	}

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

# Test6
# Shrink the previous region, specifying the size to remove.
sub Test6
{
	my $name = "Sam";
	my $region_name = $container_name . "/" . $name;
	my ($sectors_before, $sectors_after, $sectors_delta);
	my $remove_size = "25MB";
	my %options;
	my $rc;

	log_info("Test shrinking an LVM region by specifying " .
		 "size to remove.\n");

	log_info("1. Getting size of region $region_name before the shrink.\n");

	$sectors_before = get_blkdev_sectors("lvm/" . $region_name);

	log_info("2. Shrinking region $region_name by $remove_size.\n");

	$options{"remove_size"} = $remove_size;
	$rc = shrink_lvm_region($region_name, \%options);
	if ($rc) {
		log_error("Error shrinking region $region_name.\n");
		goto out;
	}

	$sectors_after = get_blkdev_sectors("lvm/" . $region_name);
	$sectors_delta = $sectors_before - $sectors_after;

	log_info("3. Verifying region $region_name shrunk by correct size.\n");

	$rc = compare_sectors($sectors_delta, size_to_sectors($remove_size), 0);
	if ($rc) {
		log_error("Error: region $region_name shrunk by the wrong size.\n");
		goto out;
	}

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

# Test7
# Create a striped region across all four PVs in the container, using
# only a portion of the space. Then expand the region.
sub Test7
{
	my $name = "Gimli";
	my $region_name = $container_name . "/" . $name;
	my $extents = 12;
	my $stripes = 4;
	my $add_extents = 7;
	my ($sectors_before, $sectors_after, $sectors_delta);
	my %options;
	my $rc;

	$options{"name"} = $name;
	$options{"extents"} = $extents;
	$options{"stripes"} = $stripes;

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

	log_info("1. Creating an LVM striped region $region_name.\n");
	log_info("   $stripes stripes, $extents extents.\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 region $region_name before the expand.\n");

	$sectors_before = get_blkdev_sectors("lvm/" . $region_name);

	log_info("3. Expanding region $region_name by $add_extents extents.\n");

	$options{"add_extents"} = $add_extents;
	$rc = expand_lvm_region($region_name, \%options);
	if ($rc) {
		log_error("Error expanding region $region_name.\n");
		goto out;
	}

	$sectors_after = get_blkdev_sectors("lvm/" . $region_name);
	$sectors_delta = $sectors_after - $sectors_before;

	log_info("4. Verifying that expand-size was rounded up to " .
		 "num-stripes boundary.\n");

	$rc = compare_sectors($sectors_delta,
			      ($add_extents + 1) * size_to_sectors($container_pe_size), 0);
	if ($rc) {
		log_error("Error: region $region_name expanded by wrong amount.\n");
		goto out;
	}

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

# Test8
# Create a striped region across all four PVs in the container, using
# only a portion of the space. Then expand the region.
sub Test8
{
	my $name = "Gimli";
	my $region_name = $container_name . "/" . $name;
	my $remove_extents = 6;
	my ($sectors_before, $sectors_after, $sectors_delta);
	my %options;
	my $rc;

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

	log_info("1. Getting size of region $region_name before the shrink.\n");

	$sectors_before = get_blkdev_sectors("lvm/" . $region_name);

	log_info("2. Shrinking region $region_name by $remove_extents extents.\n");

	$options{"remove_extents"} = $remove_extents;
	$rc = shrink_lvm_region($region_name, \%options);
	if ($rc) {
		log_error("Error shrinking region $region_name.\n");
		goto out;
	}

	$sectors_after = get_blkdev_sectors("lvm/" . $region_name);
	$sectors_delta = $sectors_before - $sectors_after;

	log_info("4. Verifying that shrink-size was rounded down to " .
		 "num-stripes boundary.\n");

	$rc = compare_sectors($sectors_delta,
			      ($remove_extents - 2) * size_to_sectors($container_pe_size), 0);
	if ($rc) {
		log_error("Error: region $region_name shrunk by wrong 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();
	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();
	if ($rc) {
		goto finish;
	}

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

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

finish:
}

