#!/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 = "RAID1";
my $region = $pers . " region";
my $RegMgr = "MDRaid1RegMgr";

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;
}

# Create_DOS_Logical_Segments
#	This subroutine creates logical segments
#
#	PARAMETERS:
#	- logical disk
#	- strings of desired segment sizes
#
#	RETURN:
#	- error code if any
sub Create_DOS_Logical_Segments
{
    my $disk = shift;
    my $sizes = shift;
    my $freespace = $disk . "_freespace1";
    my ($count, $rc);

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

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

# Query_RAID1_Region
#	PARAMETERS:
#	- region name
#
#	RETURN:
#	error code if any
sub Query_RAID1_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;
}

# 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 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 RAID1 region.
#
sub Test1
{
	my @raid_disks = @{$_[0]}; # Have our own array of segments
	my ($rc, $msg);
	my $region_name = "";
	my $err = 0;
	my @none;

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

	# We need only 2 segments for this test.
	@raid_disks = @raid_disks[0..1];
 
	$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 RAID1 object
	log_info("2. Making sure that $region $region_name exists.\n");
	$rc = Query_RAID1_Region($region_name);
	if ($rc) {
		$err = $rc;
		goto out;
	}

	# RAID1 will start a resync.
	# wait for 10 seconds (just a guess)
	sleep 10;
	
out:
	# delete MD region (region name is required)
	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;

	# We need only 3 segments for this test
	@raid_disks = @raid_disks[0..2];

	# 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 RAID1 object
	log_info("2. Making sure that $region $region_name exists.\n");
	$rc = Query_RAID1_Region($region_name);
	if ($rc) {
		$err = $rc;
		goto out;
	}
	
	# RAID1 will start a resync.
	# wait for 10 seconds (just a guess)
	sleep 10;

out:
	# delete MD region (region name is required)
	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 RAID1 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 3 disks for this test.
	@raid_disks = @raid_disks[0..2];
 
	# 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 RAID1 object
	$rc = Query_RAID1_Region($region_name);
	if ($rc) {
		$err = $rc;
		goto out;
	}

	# RAID1 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 (!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 a $region region.\n");
	# We need only 3 raid disks for this test
	@raid_disks = @raid_disks[0..2];

	# 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 RAID1 object
	$rc = Query_RAID1_Region($region_name);
	if ($rc) {
		$err = $rc;
		goto out;
	}
	
	# RAID1 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;
	}

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

	# Make sure that the raid disks are still in the region
	$msg = "4. Making sure that active disks (";
	$msg .= join ",",@raid_disks;
	$msg .= ") are still in $region_name.\n";
	log_info($msg);	
	if (!Lookup_Children($region_name, \@raid_disks)) {
		$err = $EINVAL;
		goto out;
	}

out:
	# delete MD region (region name is required)
	if ($region_name ne "") {
		log_info("5. 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;
}

# Test5
#	This subroutine tests adding active disk to RAID1 region
#
sub Test5
{
	my @raid_disks = @{$_[0]}; # Have our own array of segments
	my @none;
	my @active_disks;
	my ($rc, $msg);
	my $region_name = "";
	my $err = 0;

	log_info("Test adding active disk to a $region region.\n");
	# We need only 3 disks for this test.
	@raid_disks = @raid_disks[0..2];
 
	# Use the last element of @raid_disks as active disk
	push @active_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 RAID1 object
	log_info("2. Making sure that $region $region_name exists.\n");
	$rc = Query_RAID1_Region($region_name);
	if ($rc) {
		$err = $rc;
		goto out;
	}

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

	# Make sure that the active disk is in the children list
	log_info("4. Making sure that @active_disks exists in $region $region_name.\n");
	if (!Lookup_Children($region_name, \@active_disks)) {
		$err = $EINVAL;
		goto out;
	}

out:
	# delete MD region (region name is required)
	if ($region_name ne "") {
		log_info("5. 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;
}

# Test6
#	Create RAID1 region, then remove an active disk
#
sub Test6
{
	my @raid_disks = @{$_[0]}; # have our own array of raid disks
	my ($rc, $msg);
	my @active_disks;
	my @none;
	my $region_name = "";
	my $err = 0;

	
	log_info("Test Removing an active disk.\n");
	# We need only 3 raid disks for this test
	@raid_disks = @raid_disks[0..2];

	# Use the last element of @raid_disks
	push @active_disks, $raid_disks[-1];

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

	# Create the 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 RAID1 object
	log_info("2. Making sure that $region $region_name exists.\n");
	$rc = Query_RAID1_Region($region_name);
	if ($rc) {
		$err = $rc;
		goto out;
	}
	
	# Wait for the resync to finish.
	log_info("3. Wait for $region $region_name to finish syncing.\n");
	wait_for_md_resync($region_name);

	# Remove active disk from MD region
	log_info("4. Removing active disk @active_disks from $region_name.\n");
	$rc = remove_active_from_md_region($region_name,\@active_disks);
	if ($rc) {
		$err = $rc;
		log_error("Error removing active from $region_name.\n");
		goto out;
	}

	# Make sure that the removed disk is NOT in the children list.
	$msg = "5. Making sure that disks (";
	$msg .= join ",",@active_disks;
	$msg .= ") are no longer in $region $region_name.\n";
	log_info($msg);
	if (Lookup_Children($region_name, \@active_disks)) {
		$err = $EINVAL;
		goto out;
	}

	pop @raid_disks;

	# Make sure that the raid disks are still in the region
	log_info("6. Making sure that @raid_disks are still in $region_name.\n");
	if (!Lookup_Children($region_name, \@raid_disks)) {
		$err = $EINVAL;
		goto out;
	}

out:
	# delete MD region (region name is required)
	if ($region_name ne "") {
		log_info("7. 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;
}

# Test7
#	This subroutine tests creation and deletion of a 27-disk RAID1 region.
#
sub Test7
{
	my @raid_disks = @{$_[0]}; # Have our own array of segments
	my ($rc, $msg);
	my $region_name = "";
	my $err = 0;
	my @none;

	log_info("Test creating 27-disk MD RAID1 region.\n");

	# We need 27 segments for this test.
	@raid_disks = @raid_disks[0..26];
 
	$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");
	}

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

	# RAID1 will start a resync.
	# wait for 10 seconds (just a guess)
	sleep 60;
	
out:
	# delete MD region (region name is required)
	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;
}


MAIN:
{
	my $disk;
	my @segments;
	my @logical_segment_sizes;
	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, "1GB");
	if ($rc) {
		die("Disk $disk isn't large enough for this test.\n");
	}

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

	#Create 3 primary segments
	$rc = Create_DOS_Primary_Segments($disk, "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;

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

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

	#Create lots of logical segments
	my $count = 0;
	while ($count++ < 30) {
	    push @logical_segment_sizes, "50MB";
	}

	$rc = Create_DOS_Logical_Segments($disk, \@logical_segment_sizes);
	goto finish unless !$rc;

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

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

finish:
	Cleanup($disk);
out:
}

