Source:SW RAID LVM
From FAIWiki
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;
