#!/usr/bin/perl

# show the distribution of LVs/LEs in the systems PVs
#
# Copyright 2005, 2008 Uli Martens <uli@youam.net>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# version 2 as published by the Free Software Foundation.
#
# 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

# ChangeLog:
# 2008-01-20 Uli Martens <uli@youam.net>
#   * fix off-by-two error for lv slice sizes
# 2005-04-11 Uli Martens <uli@youam.net>
#   * written

# Example Output:
# $ ~/bin/pvinfo
#         first/last PE    #PE       device                    first/last LE
# vg: vg
#  pv: /dev/md5
#    (      0 ..    295 (  296 )) -- /dev/vg/backup       (      0 ..    295 )
#    (    296 ..    807 (  512 )) -- /dev/vg/kyra         (      0 ..    511 )
# [...]


use strict;
use warnings;

# ---- vv config ----

my $pvdisplay_file = "pvdisplay |";
my $lvdisplay_file = "lvdisplay -m |";

my $lvname_width = 25;
# -------------------

my $all;

my $pv;
my $lv;
my $vg;

open PVD, $pvdisplay_file
	or die "can't get pvdisplay info: $!";

while (<PVD>) {
	if ( m/^  PV Name\s+(.*)$/ ) {
		$pv = $1;
		next;
	}
	if ( m/^  VG Name\s+(.*)$/ ) {
		$vg = $1;
		next;
	}
	if ( m/^  Total PE\s+(.*)$/ ) {
		$all->{$vg}->{$pv}->{size} = $1 -1;
		undef $vg;
		undef $pv;
		next;
	}
#	print;
}
close PVD;

open LVD, $lvdisplay_file
	or die "can't get lvdisplay info: $!";

VG: while (<LVD>) {
	if ( m/^  LV Name\s+(.*)$/ ) {
		$lv = $1;
		next;
	}
	if ( m/^  VG Name\s+(.*)$/ ) {
		$vg = $1;
		next;
	}
	if ( m/^  --- Segments ---$/ ) {
		my $from;
		my $to;
		my $pfrom;
		my $pto;
		my $pv;
		while (<LVD>) {
			if ( m/^  --- Logical volume ---/ ) {
				next VG;
			}
			if ( m/^  Logical extent (\d+) to (\d+):$/ ) {
				$from = $1;
				$to = $2;
				next;
			}
			if ( m/^    Physical volume\s+(.*)$/ ) {
				$pv = $1;
				next;
			}
			if ( m/^    Physical extents\s+(\d+) to (\d+)$/ ) {
				$pfrom = $1;
				$pto = $2;
				next;
			}
			
			if ( m/^\s*$/ and defined $pv ) {
				#print "GOT: $pv / $from / $to\n";
				$all->{$vg}->{$pv}->{lvs}->{$pfrom} = {
					from => $pfrom,
					to => $pto,
					lv => $lv,
					pv => $pv,
					pfrom => $from,
					pto => $to,
				};
				undef $pv;
				undef $from;
				undef $to;
			}
				
			#print ">> $_";
		}
	}
	#print "XX $_";
}

close LVD;

printf "     %17s  %5s       %*s  %17s\n",
	"first/last PE", "#PE", -$lvname_width, "device", "first/last LE";
for my $vg ( sort keys %{$all} ) {
	print "vg: $vg\n";
	for my $pv ( sort keys %{$all->{$vg}} ) {
		print "  pv: $pv\n";
		my $pelast = -1;
		for my $x ( sort { $a <=> $b } keys %{$all->{$vg}->{$pv}->{lvs}} ) {
			if ( $pelast + 1 != $all->{$vg}->{$pv}->{lvs}->{$x}->{from} ) {
				printf "    (%7i ..%7i (%5i ))\n",
					$pelast+1,
					$all->{$vg}->{$pv}->{lvs}->{$x}->{from} -1,
					$all->{$vg}->{$pv}->{lvs}->{$x}->{from} - $pelast -1;
			}
			printf "    (%7i ..%7i (%5i )) -- %*s (%7i ..%7i )\n",
				$all->{$vg}->{$pv}->{lvs}->{$x}->{from},
				$all->{$vg}->{$pv}->{lvs}->{$x}->{to},
				$all->{$vg}->{$pv}->{lvs}->{$x}->{to} - $all->{$vg}->{$pv}->{lvs}->{$x}->{from} +1,
				-$lvname_width,
				$all->{$vg}->{$pv}->{lvs}->{$x}->{lv},
				$all->{$vg}->{$pv}->{lvs}->{$x}->{pfrom},
				$all->{$vg}->{$pv}->{lvs}->{$x}->{pto};
			$pelast = $all->{$vg}->{$pv}->{lvs}->{$x}->{to};
		}
		if ( $pelast != $all->{$vg}->{$pv}->{size} ) {
			printf "    (%7i ..%7i (%5i ))\n",
				$pelast +1,
				$all->{$vg}->{$pv}->{size},
				$all->{$vg}->{$pv}->{size} - $pelast;
		}
	}
}