#!/usr/bin/perl

use strict;
use warnings;

use Evms::Common;
use Evms::Log;
use Evms::Object;
use Evms::Dos;
use Evms::DM;
use Evms::MD;

my $pers = "RAID5";
my $region = $pers . " region";
my $RegMgr = "MDRaid5RegMgr";

my $EINVAL = 22;

sub check_disk_size
{
	my $disk = $_[0];
	my $size = $_[1];
	my $real_size;
	my $rc;

	$real_size = get_object_size($disk);

	$rc = compare_sizes($real_size, $size, 0);
	if ($rc < 0) {
		log_error("Disk $disk smaller than required size ($size).\n");
	} else {
		$rc = 0;
	}

	return $rc;
}

# Setup
#	This function assigns DOS SegMgr to the logical disk
#
#	PARAMETERS (1)
#	- logical disk
#	RETURN:
#	- error code if any
sub Setup
{
	my $disk = $_[0];
	my @query_output;
	my $mbr = $disk . "_mbr";
	my $rc;

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

	@query_output = query_object($mbr);
	if (@query_output == 0) {
		log_error("Error getting details for MBR on disk $disk.\n");
		$rc = 1;
		goto out;
	}

out:
	return $rc;
}

# Cleanup
#
sub Cleanup
{
	my $disk = $_[0];
	unassign_dos_plugin($disk);
}


# Create_DOS_Primary_Segments
#	This subroutine creates 1 or more (max 4) primary segments
#
#	PARAMETERS:
#	- logical disk
#	- 1 to 4 strings of desired sizes
#	(e.g. Create_DOS_Primary_Segments("hdc", "50MB", "100MB")
#	(I know that that passing references is better :)
#
#	RETURN:
#	- error code if any
sub Create_DOS_Primary_Segments
{
	my $disk = shift;
	my @sizes = @_;
	my $freespace = $disk . "_freespace1";
	my ($count, $rc);

	$count = scalar(@sizes);
	if (($count == 0) || ($count > 4)) {
		$rc = $EINVAL;
		goto out;
	}

	foreach (@sizes) {
		log_info("Creating a $_ primary segment...\n");
		$rc = create_dos_primary_segment($freespace, $_);
		if ($rc) {
			log_error("Error creating primary segment.\n");
			goto out;
		}
	}
out:
	return $rc;
}


# Query_RAID5_Region
#	PARAMETERS:
#	- region name
#
#	RETURN:
#	error code if any
sub Query_RAID5_Region
{
	my $region_name = shift;
	my ($rc, @output);

	$rc = query_md_region($region_name, \@output);
	if ($rc) {
		log_error("Error querying $region $region_name.\n");
		goto out;
	} else {
#		foreach (@output) {
#			chomp($_);
#			next if (length($_) == 0);
#			log_info("$_\n");
#		}
	}
out:
	return $rc;
}

# Test_Lookup_Children
#	PARAMETERS (2)
#	- region name
#	- array of children
#
#	RETURN:
#	return 1 if FOUND all children, 0 if any of children is NOT FOUND.
sub Test_Lookup_Children
{
	my $region_name = shift;
	my $children = shift;
	my ($rc, $child, @output);

	$rc =  query_md_region_children($region_name, \@output);
	goto not_found unless !$rc;

	foreach $child (@{$children}) {
		foreach (@output) {
			if ($_ =~ / $child/) {
#				log_info("FOUND $child in $region_name.\n");
				goto found;
			}
		}
		# We could not find $child in @output
#		log_info("Could not find $child in $region_name.\n");
		goto not_found;
found:
	}
	return 1;

not_found:
	return 0;
}


# Test1
#	This subroutine tests creation and deletion of a simple RAID5 region.
#
sub Test1
{
	my @raid_disks = @{$_[0]}; # Have our own array of segments
	my ($rc, $msg);
	my $region_name = "";
	my $err = 0;
	my @spare_disks;

	log_info("Test creation and deletion of a simple 3-disk $region.\n");

	# We need only 3 segments for this test.
	@raid_disks = @raid_disks[0..2];
 
	$msg = "1. Creating $region with: ";
	$msg .= join ",",@raid_disks;
	$msg .= "...\n";
	log_info($msg);

	# Create MD region
	$rc = create_md_region($RegMgr,\@raid_disks,\@spare_disks, $region_name);
	if ($rc) {
		$err = $rc;
		log_error("Error creating $region.\n");
		goto out;
	}

	if ($region_name eq "") {
		$err = $EINVAL;
		log_error("New $region has no name!!!.\n");
		goto out;
	} else {
		log_info("$region $region_name has been created.\n");
	}

	# Make sure that we can query the RAID5 object
	log_info("2. Making sure that $region $region_name exists.\n");
	$rc = Query_RAID5_Region($region_name);
	if ($rc) {
		$err = $rc;
		goto out;
	}

	# RAID5 will start a resync.
	# wait for 10 seconds (just a guess)
	sleep 10;
	
out:
	# delete MD region (needs region name)
	if ($region_name ne "") {
		log_info("3. Deleting $region $region_name.\n");
		$rc = delete_md_region($region_name);
		if ($rc) {
			$err ? ($err = $err) : ($err = $rc);
			log_error("Error deleting $region $region_name.\n");
		} else {
			log_info("$region $region_name has been deleted.\n");
		}
	}

	log_result($err);
	return $err;
}


# Test2
#	Create with spare option.
#
sub Test2
{
	my @raid_disks = @{$_[0]}; # have our own array of raid disks
	my ($rc, $msg);
	my @spare_disks;
	my $region_name = "";
	my $err = 0;

	log_info("Test creation of $region with spare option.\n");
	# We need only 4 segments for this test
	@raid_disks = @raid_disks[0..3];

	# Use the last element of @raid_disks as spare disk
	push @spare_disks, pop @raid_disks;

	$msg = "1. Creating $region with active disks: ";
	$msg .= join ",",@raid_disks;
	$msg .= " and spare disk: " . join ",",@spare_disks;
	$msg .= "...\n";
	log_info($msg);

	# Create the MD region
	$rc = create_md_region($RegMgr,\@raid_disks,\@spare_disks, $region_name);
	if ($rc) {
		$err = $rc;
		log_error("Error creating $region.\n");
		goto out;
	}

	if ($region_name eq "") {
		$err = $EINVAL;
		log_error("New $region has no name!!!.\n");
		goto out;
	} else {
		log_info("$region $region_name has been created.\n");
	}

	# Make sure that we can query the RAID5 object
	log_info("2. Making sure that $region $region_name exists.\n");
	$rc = Query_RAID5_Region($region_name);
	if ($rc) {
		$err = $rc;
		goto out;
	}
	
	# RAID5 will start a resync.
	# wait for 10 seconds (just a guess)
	sleep 10;

out:
	# delete MD region (needs region name)
	if ($region_name ne "") {
		log_info("3. Deleting $region $region_name.\n");
		$rc = delete_md_region($region_name);
		if ($rc) {
			$err ? ($err = $err) : ($err = $rc);
			log_error("Error deleting $region $region_name.\n");
		} else {
			log_info("$region $region_name has been deleted.\n");
		}
	}
	log_result($err);
	return $err;
}

# Test3
#	This subroutine tests adding spare to a RAID5 region
#
sub Test3
{
	my @raid_disks = @{$_[0]}; # Have our own array of segments
	my @none;
	my @spare_disks;
	my ($rc, $msg);
	my $region_name = "";
	my $err = 0;

	log_info("Test adding spare to $region.\n");

	# We need only 4 disks for this test.
	@raid_disks = @raid_disks[0..3];
 
	# Use the last element of @raid_disks as spare disk
	push @spare_disks, pop @raid_disks;

	$msg = "1. Creating $region with: ";
	$msg .= join ",",@raid_disks;
	$msg .= "...\n";
	log_info($msg);

	# Create MD region
	$rc = create_md_region($RegMgr,\@raid_disks,\@none, $region_name);
	if ($rc) {
		$err = $rc;
		log_error("Error creating $region.\n");
		goto out;
	}

	if ($region_name eq "") {
		$err = $EINVAL;
		log_error("New $region has no name!!!.\n");
		goto out;
	} else {
		log_info("$region $region_name has been created.\n");
	}

	# Make sure that we can query the RAID5 object
	$rc = Query_RAID5_Region($region_name);
	if ($rc) {
		$err = $rc;
		goto out;
	}

	# RAID5 will start a resync.
	# wait for 10 seconds (just a guess)
	sleep 10;
	
	# Add a spare to MD region
	log_info("2. Adding spare @spare_disks to $region_name.\n");
	$rc = add_spare_to_md_region($region_name,\@spare_disks);
	if ($rc) {
		$err = $rc;
		log_error("Error adding spare to $region_name.\n");
		goto out;
	}

	# Make sure that the spare is in the children list
	log_info("3. Making sure that @spare_disks is in $region $region_name.\n");
	if (!Test_Lookup_Children($region_name, \@spare_disks)) {
		$err = $EINVAL;
		goto out;
	}

out:
	# delete MD region (region name is required)
	if ($region_name ne "") {
		log_info("4. Deleting $region $region_name.\n");
		$rc = delete_md_region($region_name);
		if ($rc) {
			$err ? ($err = $err) : ($err = $rc);
			log_error("Error deleting $region $region_name.\n");
		} else {
			log_info("$region $region_name has been deleted.\n");
		}
	}

	log_result($err);
	return $err;
}

# Test4
#	Create with spare option, then remove the spare
#
sub Test4
{
	my @raid_disks = @{$_[0]}; # have our own array of raid disks
	my ($rc, $msg);
	my @spare_disks;
	my $region_name = "";
	my $err = 0;

	log_info("Test removing spare from $region.\n");
	# We need only 4 raid disks for this test
	@raid_disks = @raid_disks[0..3];

	# Use the last element of @raid_disks as spare disk
	push @spare_disks, pop @raid_disks;

	$msg = "1. Creating $region with active disks: ";
	$msg .= join ",",@raid_disks;
	$msg .= " and spare disk: " . join ",",@spare_disks;
	$msg .= "...\n";
	log_info($msg);

	# Create the MD region
	$rc = create_md_region($RegMgr,\@raid_disks,\@spare_disks, $region_name);
	if ($rc) {
		$err = $rc;
		log_error("Error creating $region.\n");
		goto out;
	}

	if ($region_name eq "") {
		$err = $EINVAL;
		log_error("New $region has no name!!!.\n");
		goto out;
	} else {
		log_info("$region $region_name has been created.\n");
	}

	# Make sure that we can query the RAID5 object
	$rc = Query_RAID5_Region($region_name);
	if ($rc) {
		$err = $rc;
		goto out;
	}
	
	# RAID5 will start a resync.
	# wait for 10 seconds (just a guess)
	sleep 10;

	# Remove spare from MD region
	log_info("2. Removing spare @spare_disks from $region_name.\n");
	$rc = remove_spare_from_md_region($region_name,\@spare_disks);
	if ($rc) {
		$err = $rc;
		log_error("Error removing spare from $region_name.\n");
		goto out;
	}

	# Make sure that the spare is NOT in the children list.
	log_info("3. Making sure that @spare_disks is not in $region $region_name.\n");
	if (Test_Lookup_Children($region_name, \@spare_disks)) {
		$err = $EINVAL;
		goto out;
	}

	# Make sure that the raid disks are still in the region
	if (!Test_Lookup_Children($region_name, \@raid_disks)) {
		$err = $EINVAL;
		goto out;
	}

out:
	# delete MD region (region name is required)
	if ($region_name ne "") {
		log_info("4. Deleting $region $region_name.\n");
		$rc = delete_md_region($region_name);
		if ($rc) {
			$err ? ($err = $err) : ($err = $rc);
			log_error("Error deleting $region $region_name.\n");
		} else {
			log_info("$region $region_name has been deleted.\n");
		}
	}
	log_info(($err ? "Failed" : "Success") . "\n\n");
	return $err;
}


MAIN:
{
	my $disk;
	my @segments;
	my $rc;

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

	# Check for minimum-sized disk.
	$rc = check_disk_size($disk, "2GB");
	if ($rc) {
		die("Disk $disk isn't large enough for this test.\n");
	}

	$rc = Setup($disk);
	goto out unless !$rc;

	#Create 4 primary segments
	$rc = Create_DOS_Primary_Segments($disk, "50MB", "50MB", "50MB", "50MB");
	goto finish unless !$rc;

	# For segment names, call get_dos_data_segments()
	$rc = get_dos_data_segments($disk, \@segments);
	if ($rc) {
		log_error("Error retrieving name of segments.\n");
		goto finish;
	}

	$rc = Test1(\@segments);
	goto finish unless !$rc;

	$rc = Test2(\@segments);
	goto finish unless !$rc;

	$rc = Test3(\@segments);
	goto finish unless !$rc;

	$rc = Test4(\@segments);
	goto finish unless !$rc;

finish:
	Cleanup($disk);
out:
}

