Source:SW RAID LVM

From FAIWiki

Jump to: navigation, search

See User:Gervai/Raid and LVM hook for more info.


#!/usr/bin/perl 
#$Id: mountdisks.SW_RAID 1489 2007-02-14 16:05:48Z grin $
# BEGIN LICENCE BLOCK
#
# Copyright (C) 2005 Michal Svamberg <svamberg@civ.zcu.cz>
# Copyright (C) 2007 Peter Gervai <grin * grin dot hu>
#
# 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.
#
# END LICENCE BLOCK

# Usage:
# SW_RAID_CONFIG=mdX[
#           raid_level[,mdadm_option[,mdadm_option]
#          ]=deviceA[,deviceB][;spareX[,spareY]]
#          :mountpoint:[filesystem[,mkfs_options]:mount_options]:[boot]
#
# LVM_VG_CONFIG=vgname[vgoptions]=deviceA[,deviceB[,...]]
#
# LVM_LV_CONFIG=vgname/lvname[lvoption lvoption ...]=size:mountpoint
#          :[filesystem]:[mountoptions]
#
# Now supported this raids:
#   linear, raid0, stripe, raid1, mirror, raid5
# This filesystems are supported:
#   ext2, ext3, reiserfs, xfs, swap
#  
# Example:
# SW_RAID_CONFIG="md0[mirror,--chunk=16]=/dev/sda1,/dev/sdb1;/dev/sdc1:/:ext3,-T news:rw,errors=remount-ro:boot
#                 md1[raid0]=/dev/sda2,/dev/sdb2:/var:ext3:rw:"
#
# LVM_VG_CONFIG="vol0[-l 32]=/dev/md0,/dev/md1"
# LVM_LV_CONFIG="vol0/home[-n home -s]=256m:/home/:xfs:
#                vol0/tmp[-n tmp]=1G:/tmp/:xfs:"
#  
#
# Records in configuration string are breaking by new line.
#
# Version: 0.1.0
#
# ChangeLog:
# 0.1.0: 2007-02-14 grin*grin dot hu; adding LVM support, minor cosmetics fixes
# 0.0.4: mkfs.xfs is now called with -f option. 
#        You can now give mkfs options in SW_RAID_CONFIG, thanks Jonathon Blumenthal <jonblumenthal[at]gmail[dot]com>
#        Fix hash referencies, thanks Radoslav Bodo <bodik[at]civ[dot]zcu[dot]cz>
# 0.0.3: disabled wait_to_init(), thanks Rene Cunningham <rene[at]compounddata[dot]com>
#        set_partition_type() before create_md(), thanks Sebastien Estienne <sebastien[dot]estienne[at]gmail[dot]com>
# 0.0.2: add xfs support 

use strict;
use Data::Dumper;

my %config = ();
my %lvm_config = ();
my %lvm_pvlist = ();
my $swaplist = "$ENV{SWAPLIST}";
# ------------------------------------------------------------------------
sub parse_config {
    my $var = shift;
    my $line;
    my $md;
    my $rest;
    my @opt;
    foreach $line (split /\n/, $var) {
        chomp $line;
        next if $line =~ /^\s*$/;
        next if $line =~ /^\s*#/;
        
        # md device, type
        $line =~ m#^(md[\d+])\[(linear|raid0|stripe|raid1|mirror|raid5),?(.*?)\]=(.*)$#
            || die "__FILE__[__LINE__]: Error in SW_RAID_CONFIG at line: \n$line\n";
        $md = $1;
        $config{$md}{level} = $2;
        $config{$md}{options} = $3;
        $rest = $4;
        
        # options: --chunk=435,--verbose -> --chunk=435 --verbose
        if ($config{$md}{options}) {
            $config{$md}{options} =~ s/,/ /g;
        }
        
        # rest (partitions, mount options)
        @opt = split /:/, $rest;
        # $opt[0]: devices (data & spare)
        ($config{$md}{devices}, $config{$md}{spares}) = split /;/, $opt[0], 2;
        $config{$md}{devices} =~ s/,/ /g;
        $config{$md}{num_devices} = split / /, $config{$md}{devices};
        
        if ($config{$md}{spares}) {
            $config{$md}{spares} =~ s/,/ /g;
            $config{$md}{num_spare} = split / /, $config{$md}{spares};
        }
        
        # $opt[1]: mountpoint
        $config{$md}{mountpoint} = $opt[1];
        
        # $opt[2]: filesystem
        if ($opt[2] !~ /^(ext2|ext3|reiserfs|swap|xfs),?(.*?)$/) {
            die "__FILE__[__LINE__]: Error in SW_RAID_CONFIG at line:\n$line\n  in filesystem: $opt[2]\n";
        }
        $config{$md}{filesystem} = $1;
        $config{$md}{filesystem_opt} = $2;
        
        # $opt[3]: mount options
        $config{$md}{mountoptions} = $opt[3] || 'defaults';
        
        # $opt[4]: boot
        $config{$md}{boot} = $opt[4];
        
        # changes for swap
        if ($config{$md}{filesystem} eq 'swap') {
            $config{$md}{mountoptions} = 'sw';
            $config{$md}{mountpoint} = 'none';
            $swaplist .= "/dev/$md ";
        }
    }
}

# ----------------------------------------------------------------------
sub parse_vg_config {
    # FIXME: duplicate PV check
    my $var = shift;
    my $line;
    my $vg;
    my $rest;
    my @opt;
    foreach $line (split /\n/, $var) {
        next if $line =~ /^\s*(#|$)/;
        chomp $line;
        
        $line =~ m#^([^\[]+)\[([^\]]*)]=(.+)#
            || die __FILE__.'['.__LINE__."]: Error in LVM_VG_CONFIG at line: \n$line\n";
        $vg = $1;
        $lvm_config{$vg}{options}=$2;
        $rest = $3;
        $rest =~ tr/,/ /;
        $lvm_config{$vg}{devices}=$rest;
    }
}

# ----------------------------------------------------------------------
sub parse_lv_config {
    my $var = shift;
    my $line;
    my ($vg,$lv);
    my $rest;
    my @opt;
    foreach $line (split /\n/, $var) {
        next if $line =~ /^\s*(#|$)/;
        chomp $line;
        
        $line =~ m#^([^/]+)/([^/\[]+)\[([^\]]*)]=(.+):([^:]*):([^:]*):([^:]*)#
            || die __FILE__.'['.__LINE__."]: Error in LVM_LV_CONFIG at line: \n$line\n";
        ($vg,$lv) = ($1,$2);
        $lvm_config{$vg}{lv}{$lv}{options}=$3;
        $lvm_config{$vg}{lv}{$lv}{size}=$4;
        $lvm_config{$vg}{lv}{$lv}{mountpoint}=$5 || 'none';
        $lvm_config{$vg}{lv}{$lv}{filesystem}=$6 || 'ext2';
        $lvm_config{$vg}{lv}{$lv}{mountoptions}= $7 || 'defaults';

    
        if( $lvm_config{$vg}{lv}{$lv}{mountpoint} eq '/' ) {
            print "Mounting root on lvm is not really recommended, ";
            print "without initrd your system probably won't start. You've been warned!\n";
        }

    }
}

# ----------------------------------------------------------------------
sub do_command {
    my $command = shift;
    my $result;
    
    print "$command\n";
    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!        
    $result = `sh -c '$command'`;
    (($? >> 8) == 0) || (die "\nERROR:\n $result\n");
    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    print $result;
}


# ----------------------------------------------------------------------
sub create_md {
    my $md;
    my $cmnd;
    foreach $md (sort keys %config) {
        $cmnd = "echo 'y' | mdadm --create $config{$md}{options} --verbose /dev/$md --level=$config{$md}{level} --raid-devices=$config{$md}{num_devices} $config{$md}{devices}";
        if ($config{$md}{spares}) {
            $cmnd .= " --spare-devices=$config{$md}{num_spare} $config{$md}{spares}";
        }
        do_command($cmnd);
    }
}

# ----------------------------------------------------------------------
sub create_lvm {
    my $cmd;
    foreach my $vg (keys %lvm_config) {
        # create physical volumes for vg
        foreach my $pv (split / /, $lvm_config{$vg}{devices}) {
            $cmd = "pvcreate --yes --force $pv";
            &do_command( $cmd );
            # remember used up devices
            $lvm_pvlist{$pv} = 1;
        }
        
        # create volume group
        $cmd = "vgcreate " . $lvm_config{$vg}{options} . " $vg " . $lvm_config{$vg}{devices};
        &do_command( $cmd );
        
        # create logical volumes 
        foreach my $lv (keys %{ $lvm_config{$vg}{lv} }) {
            $cmd = "lvcreate " . $lvm_config{$vg}{lv}{$lv}{options};
            $cmd.= " -L " . $lvm_config{$vg}{lv}{$lv}{size};
            $cmd.= " $vg";
            &do_command( $cmd );
        }
    }
}

# ----------------------------------------------------------------------
sub wait_to_init {
    while (($_ = `cat /proc/mdstat`) && ($_ =~ /resync/s)) {
        print "\nWaiting for initialization of md devices ... ";
        sleep 30;
    }
    print "done.\n";
}

# -----------------------------------------------------------------------
sub set_partition_type {
    my $md;
    my $cmnd;
    my $dev;
    my $devnum;
    
    foreach $md (sort keys %config) {
        foreach $dev (split / /, $config{$md}{devices}) {
            $dev =~ /^(.*?)(\d+)$/;
            $cmnd = "sfdisk --change-id $1 $2 fd";
            do_command($cmnd);
        }
        foreach $dev (split / /, $config{$md}{spares}) {
            $dev =~ /^(.*?)(\d+)$/;
            $cmnd = "sfdisk --change-id $1 $2 fd";
            do_command($cmnd);
        }
    }
}

# ------------------------------------------------------------------------
sub mkfs {
    my $md;
    my %trans = (
                 'reiserfs' => 'mkfs.reiserfs -q',
                 'ext3' => 'mkfs.ext3 -q',
                 'ext2' => 'mkfs.ext2 -q',
                 'swap' => 'mkswap',
                 'xfs' => 'mkfs.xfs -q -f'
                );
    
    foreach $md (sort keys %config) {
        if( !defined( $lvm_pvlist{"/dev/$md"} ) ) {
            # only use if not consumed by lvm
            print "Formatting /dev/$md as $config{$md}{filesystem}\n";
            do_command("$trans{$config{$md}{filesystem}} $config{$md}{filesystem_opt} /dev/$md");
        }
    }
    
    foreach my $vg (keys %lvm_config) {
        foreach my $lv (keys %{ $lvm_config{$vg}{lv} }) {
            print "Formatting /dev/$vg/$lv as $lvm_config{$vg}{lv}{$lv}{filesystem}\n";
            &do_command( $trans{ $lvm_config{$vg}{lv}{$lv}{filesystem} } . " /dev/$vg/$lv" );
        }
    }
}

# ----------------------------------------------------------------------
sub update_fstab() {
    my @lines = ();
    my $i;
    my $md;
    my $nums;
    my $filename = "$ENV{LOGDIR}/fstab";
    my $comments = "";
    
    # read $target/etc/fstab
    open FILE, $filename || die __FILE__.'['.__LINE__."]: Can't open $filename for read (fstab)\n";
    @lines = <FILE>;
    close FILE;
    
    # remove comments
    $i = 0;
    while (($lines[$i] =~ /^#/) && ($i <= $#lines)) {
        $comments .= splice (@lines, $i, 1);
        next;
        $i++;
    }
    
    # remove old records
    print Data::Dumper->Dump([\@lines],['lines']);
    foreach $md (sort keys %config) {
        LINE:          for ($i = 0; $i <= $#lines; $i++) {
            # remove mountpoint
            if (($lines[$i] =~ /\s+$config{$md}{mountpoint}\s+/) && ($lines[$i] !~ /swap/)) {
                print "remove lines (mountpoints):\n '$config{$md}{mountpoint}' \n $lines[$i]\n";
                splice (@lines, $i, 1);
                redo LINE;
            }
            # remove all devices in raid
            foreach (split / /, $config{$md}{devices}) {
                if ($lines[$i] =~ /$_/) {
                    print "remove lines (devices):\n '$_' \n $lines[$i]\n";
                    splice (@lines, $i, 1);
                    redo LINE;
                }
            }
            # remove all devices as spare
            foreach (split / /, $config{$md}{spares}) {
                if ($lines[$i] =~ /$_/) {
                    print "remove lines (spares):\n '$_' \n $lines[$i]\n";
                    splice (@lines, $i, 1);
                    redo LINE;
                }
            }
        }
    }
    
    foreach my $pv (keys %lvm_pvlist) {
        for( my $i=0; $i <= $#lines; $i++ ) {
            while( $lines[$i] =~ /$pv/ ) {
                print "remove line (lvm pv):\n '$pv'\n $lines[$i]\n";
                splice( @lines, $i, 1 );
            }
        }
    }
    
    # append new records
    foreach $md (sort keys %config) {
        next if defined( $lvm_pvlist{ "/dev/$md" } );
        
        $nums = ($config{$md}{mountpoint} eq 'none')?'0 0':'0 2';
        if ($config{$md}{mountpoint} eq '/') {
            $nums = '0 1';
            unshift @lines, "/dev/$md\t$config{$md}{mountpoint}\t$config{$md}{filesystem}\t$config{$md}{mountoptions}\t$nums\n";
        }
        else {
            push @lines, "/dev/$md\t$config{$md}{mountpoint}\t$config{$md}{filesystem}\t$config{$md}{mountoptions}\t$nums\n";
        }
    }
    
    foreach my $vg (keys %lvm_config) {
        foreach my $lv (keys %{ $lvm_config{$vg}{lv} }) {
            # /dev/mapper/ translates '-' to '--'
            my $lv_xlate = $lv;
            $lv_xlate =~ s/-/--/g;
            
            my $mp = $lvm_config{$vg}{lv}{$lv}{mountpoint};
            $nums = ($mp eq 'none')?'0 0':'0 2';
            if( $mp eq '/' ) {
                # you asked for it, you got it...
                $nums = '0 1';
                unshift @lines, 
                    "/dev/mapper/$vg-$lv_xlate\t$lvm_config{$vg}{lv}{$lv}{mountpoint}" .
                    "\t$lvm_config{$vg}{lv}{$lv}{filesystem}" .
                    "\t$lvm_config{$vg}{lv}{$lv}{mountoptions}\t$nums\n";
            } else {
                push @lines, 
                    "/dev/mapper/$vg-$lv_xlate\t$lvm_config{$vg}{lv}{$lv}{mountpoint}" .
                    "\t$lvm_config{$vg}{lv}{$lv}{filesystem}" .
                    "\t$lvm_config{$vg}{lv}{$lv}{mountoptions}\t$nums\n";
            }
        }
    }
    
    
    print "Final $filename:\n";
    print Data::Dumper->Dump([\@lines],['lines_final']);
    
    # save fstab
    open FILE, ">$filename" || die __FILE__."[".__LINE__."]: Can't open $filename for write (fstab)\n";
    print FILE $comments;
    print FILE @lines;
    close FILE;
    
}

# -------------------------------------------------------------------------
sub update_diskvar {
    my $filename = "$ENV{LOGDIR}/disk_var.sh";
    my $lines = "";
    my $md;
    
    open FILE, $filename || die __FILE__."[".__LINE__."]: Can't open $filename for read (disk_var.sh)\n";
    while ($_ = <FILE>){
        $lines .= $_;
    }
    close FILE;
    
    foreach $md (sort keys %config) {
        next if defined( $lvm_pvlist{ "/dev/$md" } );
        # ROOT_PARTITION
        if ($config{$md}{mountpoint} eq '/') {
            $lines =~ s#^(.*)ROOT_PARTITION=\S+(.*)$#$1ROOT_PARTITION="/dev/$md"$2#s;
        }
    }
    
    foreach my $vg (keys %lvm_config) {
        foreach my $lv (keys %{ $lvm_config{$vg}{lv} }) {
            if( $lvm_config{$vg}{lv}{$lv}{mountpoint} eq '/' ) {
                
                # /dev/mapper/ translates '-' to '--'
                my $lv_xlate = $lv;
                $lv_xlate =~ s/-/--/g;
                
                $lines =~ s#^(.*)ROOT_PARTITION=\S+(.*)$#$1ROOT_PARTITION="/dev/mapper/$vg-$lv_xlate"$2#s;
            }
        }
    }

    
    # SWAPLIST
    $swaplist =~ s/^\s*(.*?)\s*$/$1/;
    #        $lines =~ s#^(.*)SWAPLIST=\S+(.*)$#$1SWAPLIST="$swaplist"$2#s;
    $lines =~ s#^(.*)SWAPLIST=\".*?\"(.*)$#$1SWAPLIST="$swaplist"$2#s;
    
    # anyone would put swap on vlm??
    
    open FILE, ">$filename" || die __FILE__.'['.__LINE__."]: Can't open $filename for write (disk_var.sh)\n";
    print FILE $lines;
    close FILE;
}

# ======================================================================

print $0,'$Id: mountdisks.SW_RAID 1489 2007-02-14 16:05:48Z grin $', "\n";

$ENV{SW_RAID_CONFIG} || die "Variable SW_RAID_CONFIG non exists.\n";
print "Configuration of SW_RAID:\n$ENV{SW_RAID_CONFIG}\n";
parse_config($ENV{SW_RAID_CONFIG});

### lvm 
if( $ENV{LVM_VG_CONFIG} ) {
    $ENV{LVM_LV_CONFIG} || die "Variable LVM_LV_CONFIG doesn't exist.\n";
    parse_vg_config($ENV{LVM_VG_CONFIG});
    parse_lv_config($ENV{LVM_LV_CONFIG});
}

print Data::Dumper->Dump([\%config],['config']);
print Data::Dumper->Dump([\%lvm_config],['lvm_config']);

set_partition_type();
create_md();
create_lvm();
wait_to_init();
#print Data::Dumper->Dump([\%lvm_pvlist],['lvm_pvlist']);
mkfs();
update_fstab();
update_diskvar();

exit 0;


Personal tools