#! /usr/bin/perl -w
# AJ Lewis
# Copyright (C) Red Hat, Inc 2004   All rights reserved
#
# Build up an n-way persistant mirror and verify it works
#
##########################################################################

use strict;
use warnings;

my $name = "foo";
my $log = "log";
# array of arrays - [device, size, stamp]
my $raiddevs = [["/dev/sda2", 1024000, 89],
                ["/dev/sda7", 1024000, 90],
                ["/dev/sda8", 1024000, 91]];
my $mirstamp = 88;
my $mirstamp2 = 87;
# [device, size]
my $logdev = ["/dev/sda6", 10240];
my $hashdd = "/usr/src/joestests/hashdd/hashdd";
my $dmdir = "/dev/mapper";

# Actually create the mapped device with dmsetup
sub create_dm {
    my ($table, $name) = @_;
    print "* creating $name: $table\n";
    system("echo $table | dmsetup create $name");
    return ($? >> 8);
}


# Reload teh mapped device with dmsetup
sub reload_dm {
    my ($table, $name) = @_;

    print "* reloading $name: $table\n";
    system("echo $table | dmsetup load $name");
    if (($? >> 8) != 0) {
        return 1;
    }
    system("dmsetup resume $name");
    return ($? >> 8);
}

# build a linear target table
sub build_linear {
    my ($devref) = @_;

    my @devs = @$devref;
    my $count = $#devs + 1;
    my $table = "";

    my $dev;
    foreach $dev (@devs) {
        $table = $table . "0 @$dev[1] linear @$dev[0] 8";
    }

    return $table;
}


# build a raid1 target table
sub build_raid1 {
    my ($devref, $logname) = @_;

    my @devs = @$devref;

    my $count = $#devs  + 1;
    my $minsize = 999999999999;
    my $dev;

    # Use the smallest device size for the mirror size
    foreach $dev (@devs) {
        if (@$dev[1] < $minsize) {
            $minsize = @$dev[1];
        }
    }

    my $table = "0 $minsize mirror disk 2 $dmdir/$logname 1024 $count ";
    foreach $dev (@devs) {
        $table = $table . "@$dev[0] 8 ";
    }

    return $table;
}

sub del_dm {
    my ($name) = @_;

    print "* removing $name\n";
    system("dmsetup remove $name");
    return ($? >> 8);
}

sub wait_dm {
    my ($name) = @_;

    print "* waiting on $name\n";
    system("dmsetup wait $name");
    return ($? >> 8);
}

sub init_log {
    my ($dev, $name) = @_;
    my $tab = build_linear [$dev];
    if (create_dm $tab, $name) {
        return 1;
    }
    print "* clearing log: $name\n";
    system("dd if=/dev/zero of=$dmdir/$log");
    return 0;
}

sub stamp {
    my ($name, $num) = @_;
    print "* stamping $name with $num\n";
    system("$hashdd seed=$num of=$dmdir/$name");
    return ($? >> 8);
}

sub verify {
    my ($name, $num) = @_;
    print "* checking stamp on $name with $num\n";
    system("$hashdd seed=$num if=$dmdir/$name");
    return ($? >> 8);
}

# # # Start of real work # # #
# Clear any old cruft
del_dm $name;
del_dm $log;

my $tab;
my $v;


print "** SETUP\n";
my $first = 1;
# Build up the individual devices with different stamps
foreach $v (@$raiddevs) {
    $tab = build_linear [$v];
    if($first) {
        die "Can't create $tab" if create_dm($tab, $name);
        $first = 0;
    }
    else {
        die "Can't reload $tab" if reload_dm($tab, $name);
    }
    stamp "$name", $$v[2];
}

# create the persistant log device
die "Can't initialize log" if init_log $logdev, $log;


print "** Build RAID1\n";
# build a raid1 with all devices
$tab = build_raid1 $raiddevs, $log;
die "Can't reload $tab" if reload_dm $tab, $name;

wait_dm $name;

print "** Verify RAID1\n";
# verify all devices match the first
$first = 1;
foreach $v (@$raiddevs) {
    if ($first != 1) {
        $tab =  build_linear [$v];
        die "Can't reload $tab" if reload_dm $tab, $name;
        die "Can't verify $tab" if verify $name, $$raiddevs[0][2];
    }
    $first = 0;
}

print "** Rebuild RAID1 from log\n";
# rebuild the raid1 and attempt to write to it
$tab = build_raid1 $raiddevs, $log;
die "Can't reload $tab" if reload_dm $tab, $name;

print "Writing 512 bytes to mirror";
system("dd if=/dev/zero of=$dmdir/$name bs=512 count=1");

print "** Verify writing to RAID1\n";
stamp "$name", $mirstamp;
die "Can't verify $name" if verify $name, $mirstamp;

# verify all devices match the mirror
foreach $v (@$raiddevs) {
    $tab =  build_linear [$v];
    die "Can't reload $tab" if reload_dm $tab, $name;
    die "Can't verify $tab" if verify $name, $mirstamp;
}

del_dm $name;
del_dm $log;

print "** Rebuild RAID1 and verify writes work while resyncing\n";
# create the persistant log device
die "Can't initialize log" if init_log $logdev, $log;

# build a raid1 with all devices
$tab = build_raid1 $raiddevs, $log;
die "Can't create $tab" if create_dm $tab, $name;

stamp "$name", $mirstamp2;
die "Can't verify $name" if verify $name, $mirstamp2;

wait_dm $name;

# verify all devices match the mirror
foreach $v (@$raiddevs) {
    $tab =  build_linear [$v];
    die "Can't reload $tab" if reload_dm $tab, $name;
    die "Can't verify $tab" if verify $name, $mirstamp2;
}

del_dm $name;
del_dm $log;
