#
# (c) Jan Gehring <jan.gehring@gmail.com>
#
# vim: set ts=2 sw=2 tw=0:
# vim: set expandtab:


=head1 NAME

Rex::Commands::LVM - Get LVM Information

=head1 DESCRIPTION

With this module you can get information of your lvm setup.

Version <= 1.0: All these functions will not be reported.

All these functions are not idempotent.

=head1 SYNOPSIS

 use Rex::Commands::LVM;

 my @physical_devices = pvs;
 my @volume_groups = vgs;
 my @logical_volumes = lvs;



=head1 EXPORTED FUNCTIONS

=over 4

=cut


package Rex::Commands::LVM;

use strict;
use warnings;

require Rex::Exporter;
use base qw(Rex::Exporter);
use vars qw(@EXPORT);

@EXPORT = qw(pvs vgs lvs pvcreate vgcreate lvcreate vgextend);

use Rex::Commands::Run;

=item pvs

Get Information of all your physical volumes.

 use Data::Dumper;
 use Rex::Commands::LVM;

 task "lvm", sub {
   my @physical_volumes = pvs;

   for my $physical_volume (@physical_volumes) {
     say Dumper($physical_volume);
   }
 };

=cut

sub pvs {

  my @lines = run 'pvdisplay --units b --columns --separator "|" --noheadings';
  if($? != 0) {
    die("Error running pvdisplay");
  }

  my @ret;
  for my $line (@lines) {
    chomp $line;
    $line =~ s/^\s+//g;
    my ($phy_vol, $vol_group, $format, $attr, $psize, $pfree) = split(/\|/, $line);
    $pfree =~ s/B$//;
    $psize =~ s/B$//;

    push(@ret, {
      physical_volume => $phy_vol,
      volume_group => $vol_group,
      format => $format,
      attributes => $attr,
      size => $psize,
      free => $pfree,
    });
  }

  return @ret;

}

=item vgs

Get Information of all your volume groups.

 use Data::Dumper;
 use Rex::Commands::LVM;

 task "lvm", sub {
   my @volume_groups = vgs;

   for my $volume_group (@volume_groups) {
     say Dumper($volume_group);
   }
 };

=cut

sub vgs {

  my ($vg) = @_;


  my $cmd = 'vgdisplay --units b --columns --separator "|" --noheadings -o "pv_name,vg_name,vg_size,vg_free,vg_attr"';
  if($vg) {
    $cmd .= " $vg";
  }

  my @lines = run $cmd;
  if($? != 0) {
    die("Error running vgdisplay");
  }

  my @ret;
  for my $line (@lines) {
    chomp $line;
    $line =~ s/^\s+//g;
    my ($pv_name, $vg_name, $vg_size, $vg_free, $vg_attr) = split(/\|/, $line);
    $vg_free =~ s/B$//;
    $vg_size =~ s/B$//;

    push(@ret, {
      physical_volume => $pv_name,
      volume_group => $vg_name,
      size => $vg_size,
      free => $vg_free,
      attributes => $vg_attr,
    });
  }

  return @ret;

}

=item lvs

Get Information of all your logical volumes.

 use Data::Dumper;
 use Rex::Commands::LVM;

 task "lvm", sub {
   my @logical_volumes = lvs;

   for my $logical_volume (@logical_volumes) {
     say Dumper($logical_volume);
   }
 };

=cut

sub lvs {

  my ($vg) = @_;

  my $cmd = 'lvdisplay --units b --columns --separator "|" -o "lv_name,vg_name,lv_attr,lv_size" --noheading';
  if($vg) {
    $cmd .= " " . $vg;
  }

  my @lines = run $cmd;
  if($? != 0) {
    die("Error running lvdisplay");
  }

  my @ret;
  for my $line (@lines) {
    chomp $line;
    $line =~ s/^\s+//g;

    my($lv_name, $vg_name, $lv_attr, $lv_size) = split(/\|/, $line);
    $lv_size =~ s/B$//;
    push(@ret, {
      name => $lv_name,
      path => "/dev/$vg_name/$lv_name",
      attributes => $lv_attr,
      size => $lv_size,
    });
  }

  return @ret;
}


sub pvcreate {
  my ($dev) = @_;
  my $s = run "pvcreate $dev";
  if($? != 0) {
    die("Error creating pv.\n$s\n");
  }

  return 1;
}

sub vgcreate {
  my ($vgname, @devices) = @_;

  my $s = run "vgcreate $vgname " . join(" ", @devices);
  if($? != 0) {
    die("Error creating vg.\n$s\n");
  }

  return 1;
}

sub lvcreate {
  my ($lvname, %option) = @_;

  if(! exists $option{size} || ! exists $option{onvg}) {
    die("Missing parameter size or onvg.");
  }

  unless($lvname =~ m/^[a-z0-9\-_]+$/i) {
    die("Error in lvname. Allowed characters a-z, 0-9 and _- .");
  }

  my $size = $option{size};
  if($size =~ m/^[0-9]+$/) { $size .= "M"; }
  my $onvg = $option{onvg};

  my $s = run "lvcreate -n $lvname -L $size $onvg";

  my $lv_path = $option{onvg} . "/" . $lvname;

  if(exists $option{fstype}) {
    if(can_run("mkfs.$option{fstype}")) {
      Rex::Logger::info("Creating filesystem $option{fstype} on /dev/$lv_path");
      run "mkfs.$option{fstype} /dev/$lv_path";
    }
    elsif($option{fstype} eq "swap") {
      Rex::Logger::info("Creating swap space on /dev/$lv_path");
      run "mkswap -f /dev/$lv_path";
    }
    else {
      die("Can't format partition with $option{fstype}");
    }
  }

  if($? != 0) {
    die("Error creating lv.\n$s\n");
  }

  return $lv_path;
}

sub vgextend {
  my ($vgname, @devices) = @_;

  my $s = run "vgextend $vgname " . join(" ", @devices);

  if($? != 0) {
    die("Error extending vg.\n$s\n");
  }

  return 1;
}

=back

=cut


1;
